
Link: https://saitama.net.tr/filtre/
<!DOCTYPE html>
<html lang="tr-TR">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title>Görüntü Renk Düzeltme Özel Filtreler</title>
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<style>
/* ===== Global Renkler (Modern Stil) ===== */
:root{
/* Ana Renkler */
--primary:#1A73E8;
--primary-hover:#1967D2;
--primary-light:rgba(26,115,232,0.08);
--success:#34A853;
--success-hover:#2E9249;
--danger:#EA4335;
--warning:#FBBC05;
/* Nötr Renkler */
--bg:#F8F9FA;
--card:#FFFFFF;
--border:rgba(0,0,0,0.12);
--txt:#202124;
--txt-secondary:#5F6368;
--txt-disabled:#9AA0A6;
/* Gölgeler */
--shadow-sm:0 1px 2px rgba(0,0,0,0.05);
--shadow:0 2px 6px rgba(0,0,0,0.08);
--shadow-lg:0 4px 12px rgba(0,0,0,0.12);
/* Köşe Radyusu */
--radius-sm:4px;
--radius:8px;
--radius-lg:12px;
--radius-xl:16px;
/* Boşluklar */
--space-xxs:2px;
--space-xs:4px;
--space-sm:8px;
--space-md:16px;
--space-lg:24px;
--space-xl:32px;
--space-xxl:48px;
/* Animasyonlar */
--transition-fast:0.15s ease-out;
--transition:0.25s ease-out;
}
@media(prefers-color-scheme:dark){
:root{
--primary:#8AB4F8;
--primary-hover:#9CC3FF;
--primary-light:rgba(138,180,248,0.08);
--success:#81C995;
--success-hover:#8FD5A3;
--danger:#F28B82;
--warning:#FDD663;
--bg:#202124;
--card:#2D2F31;
--border:rgba(255,255,255,0.12);
--txt:#E8EAED;
--txt-secondary:#BBBEC3;
--txt-disabled:#80868B;
--shadow-sm:0 1px 2px rgba(0,0,0,0.25);
--shadow:0 2px 6px rgba(0,0,0,0.35);
--shadow-lg:0 4px 12px rgba(0,0,0,0.45);
}
}
* {
margin:0;
padding:0;
box-sizing:border-box;
-webkit-tap-highlight-color:transparent;
}
body {
font-family:'Inter',-apple-system,BlinkMacSystemFont,'SF Pro Text',
'Helvetica Neue',sans-serif;
background:var(--bg);
color:var(--txt);
line-height:1.5;
-webkit-font-smoothing:antialiased;
padding:env(safe-area-inset-top) env(safe-area-inset-right)
env(safe-area-inset-bottom) env(safe-area-inset-left);
transition:background var(--transition);
}
.container {
max-width:1200px;
margin:0 auto;
padding:var(--space-md);
}
/* Başlık */
.header {
text-align:center;
margin-bottom:var(--space-xl);
padding:var(--space-lg) 0 var(--space-xl);
}
.header h1 {
font-size:2.5rem;
font-weight:700;
margin-bottom:var(--space-sm);
letter-spacing:-0.02em;
background:linear-gradient(135deg, var(--primary), var(--success));
-webkit-background-clip:text;
background-clip:text;
color:transparent;
}
.header p {
font-size:1.125rem;
color:var(--txt-secondary);
max-width:600px;
margin:0 auto;
opacity:0.9;
}
/* Kart */
.card {
background:var(--card);
border-radius:var(--radius-lg);
box-shadow:var(--shadow-lg);
overflow:hidden;
margin-bottom:var(--space-xl);
border:1px solid var(--border);
backdrop-filter:blur(20px);
-webkit-backdrop-filter:blur(20px);
transition:box-shadow var(--transition), background var(--transition), border var(--transition);
}
.card:hover {
box-shadow:var(--shadow-lg);
}
/* Karşılaştırma Alanı */
.compare-box {
position:relative;
width:100%;
min-height:280px;
overflow:hidden;
background:#111;
border-radius:var(--radius-lg) var(--radius-lg) 0 0;
}
.compare-img {
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
object-fit:contain;
opacity:0;
transition:opacity 0.3s ease;
}
.compare-img.loaded {
opacity:1;
}
.compare-img.corr {
clip-path:inset(0 0 0 50%);
}
.img-labels {
position:absolute;
top:var(--space-md);
left:0;
width:100%;
display:flex;
justify-content:space-between;
padding:0 var(--space-md);
z-index:5;
pointer-events:none;
}
.img-tag {
font-size:0.813rem;
font-weight:600;
color:#fff;
background:rgba(0,0,0,0.6);
padding:8px 14px;
border-radius:var(--radius-xl);
display:flex;
align-items:center;
gap:6px;
backdrop-filter:blur(4px);
-webkit-backdrop-filter:blur(4px);
box-shadow:0 2px 8px rgba(0,0,0,0.2);
transition:transform var(--transition-fast), background var(--transition-fast);
}
.img-tag:hover {
transform:translateY(-2px);
background:rgba(0,0,0,0.75);
}
.img-tag::before {
content:"";
display:inline-block;
width:8px;
height:8px;
border-radius:50%;
}
.img-tag.orig::before {
background:var(--danger);
}
.img-tag.corr::before {
background:var(--success);
}
.placeholder {
position:absolute;
inset:0;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
color:var(--txt-secondary);
background:rgba(0,0,0,0.05);
transition:opacity 0.3s ease;
}
.placeholder svg {
width:52px;
height:52px;
margin-bottom:var(--space-md);
opacity:0.6;
fill:none;
stroke:currentColor;
stroke-width:1.5;
stroke-linecap:round;
stroke-linejoin:round;
}
.placeholder span {
font-size:0.938rem;
font-weight:500;
}
/* Kaydırıcı */
.slider {
position:absolute;
top:0;
left:50%;
transform:translateX(-50%);
width:44px;
height:100%;
cursor:col-resize;
z-index:6;
display:flex;
align-items:center;
justify-content:center;
transition:left var(--transition-fast);
}
.slider-line {
position:absolute;
width:2px;
height:100%;
background:#fff;
box-shadow:0 0 8px rgba(0,0,0,0.5);
}
.slider-btn {
width:38px;
height:38px;
border-radius:50%;
background:#fff;
box-shadow:0 2px 12px rgba(0,0,0,0.5);
position:relative;
display:flex;
align-items:center;
justify-content:center;
transition:transform var(--transition-fast);
}
.slider:hover .slider-btn {
transform:scale(1.1);
}
.slider-btn::before, .slider-btn::after {
content:"";
position:absolute;
width:10px;
height:10px;
border:solid 2px #555;
border-width:0 0 2px 2px;
}
.slider-btn::before {
transform:translateX(-4px) rotate(45deg);
}
.slider-btn::after {
transform:translateX(4px) rotate(225deg);
}
/* Kontrol Paneli */
.controls {
padding:var(--space-xl);
}
.section-title {
font-size:1.125rem;
font-weight:600;
margin-bottom:var(--space-md);
color:var(--txt);
display:flex;
align-items:center;
gap:var(--space-xs);
}
.section-title svg {
width:20px;
height:20px;
stroke:var(--primary);
}
.input-set {
display:flex;
gap:var(--space-md);
margin-bottom:var(--space-lg);
}
.file-wrap {
flex:1;
position:relative;
}
.file-hidden {
position:absolute;
inset:0;
opacity:0;
cursor:pointer;
z-index:2;
}
.btn, .file-vis {
height:48px;
border:none;
border-radius:var(--radius);
font-size:0.938rem;
font-weight:600;
cursor:pointer;
transition:all var(--transition-fast);
padding:0 var(--space-md);
display:flex;
align-items:center;
justify-content:center;
gap:var(--space-xs);
}
.file-vis {
width:100%;
background:var(--primary);
color:#fff;
}
.file-vis:hover {
background:var(--primary-hover);
transform:translateY(-2px);
box-shadow:var(--shadow);
}
.btn.grey {
background:var(--bg);
color:var(--txt);
border:1px solid var(--border);
}
.btn.grey:hover {
background:var(--card);
transform:translateY(-2px);
box-shadow:var(--shadow);
}
.btn.blue {
background:var(--primary);
color:#fff;
}
.btn.blue:hover {
background:var(--primary-hover);
transform:translateY(-2px);
box-shadow:var(--shadow);
}
.btn.green {
background:var(--success);
color:#fff;
}
.btn.green:hover {
background:var(--success-hover);
transform:translateY(-2px);
box-shadow:var(--shadow);
}
.btn-icon {
width:20px;
height:20px;
stroke:currentColor;
fill:none;
stroke-width:2;
stroke-linecap:round;
stroke-linejoin:round;
}
/* Sürgü Kontrolleri */
.adjustment-section {
margin-top:var(--space-xl);
margin-bottom:var(--space-lg);
}
.range-set {
margin-bottom:var(--space-md);
position:relative;
}
.range-head {
display:flex;
justify-content:space-between;
align-items:center;
margin-bottom:var(--space-xs);
}
.range-head span:first-child {
font-weight:500;
font-size:0.938rem;
}
.range-head span:last-child {
font-variant-numeric:tabular-nums;
color:var(--txt-secondary);
font-size:0.875rem;
background:var(--primary-light);
padding:2px 8px;
border-radius:var(--radius-sm);
transition:background 0.2s;
}
.range {
width:100%;
height:5px;
-webkit-appearance:none;
background:var(--border);
border-radius:var(--radius-xl);
outline:none;
margin:var(--space-sm) 0;
cursor:pointer;
}
.range::-webkit-slider-thumb {
-webkit-appearance:none;
width:18px;
height:18px;
border-radius:50%;
background:var(--primary);
box-shadow:0 1px 4px rgba(0,0,0,0.25);
cursor:pointer;
transition:transform 0.2s;
}
.range::-webkit-slider-thumb:hover {
transform:scale(1.2);
}
.range::-moz-range-thumb {
width:18px;
height:18px;
border-radius:50%;
background:var(--primary);
box-shadow:0 1px 4px rgba(0,0,0,0.25);
cursor:pointer;
border:none;
transition:transform 0.2s;
}
.range::-moz-range-thumb:hover {
transform:scale(1.2);
}
.btn-group {
display:flex;
gap:var(--space-md);
margin-top:var(--space-xl);
flex-wrap:wrap;
}
/* Filtre Butonları */
.filter-btn-group {
display:flex;
gap:var(--space-sm);
margin-bottom:var(--space-lg);
flex-wrap:wrap;
}
.filter-btn {
padding:8px 16px;
background:var(--bg);
color:var(--txt);
font-size:0.875rem;
font-weight:500;
border-radius:var(--radius);
border:1px solid var(--border);
cursor:pointer;
transition:all var(--transition-fast);
}
.filter-btn:hover {
transform:translateY(-2px);
box-shadow:var(--shadow);
}
.filter-btn.active {
background:var(--primary);
color:#fff;
border-color:var(--primary);
}
/* Alt Bilgi */
.footer {
text-align:center;
margin-top:var(--space-xxl);
padding:var(--space-lg) var(--space-md);
font-size:0.813rem;
color:var(--txt-secondary);
}
.footer a {
color:var(--primary);
text-decoration:none;
}
.footer a:hover {
text-decoration:underline;
}
@media(max-width:768px) {
.header h1 {
font-size:1.75rem;
}
.header p {
font-size:0.938rem;
}
.input-set {
flex-direction:column;
}
.controls {
padding:var(--space-lg);
}
.btn, .file-vis {
height:44px;
}
.btn-group {
flex-direction:column;
}
.btn-group .btn {
width:100%;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Görüntü Renk Düzeltme ve Filtreler</h1>
<p>Akıllı renk sapması düzeltme, gerçek renkleri yenileme ve özel sanatsal filtre efektleri</p>
</div>
<!-- ==================== Karşılaştırma Görüntüsü ==================== -->
<div class="card">
<div class="compare-box" id="compareBox">
<div class="img-labels">
<div class="img-tag orig">Orijinal</div>
<div class="img-tag corr">İşlenmiş</div>
</div>
<img id="imgOrig" class="compare-img"/>
<div id="phOrig" class="placeholder">
<!-- Kamera İkonu -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
<circle cx="8.5" cy="8.5" r="1.5"/>
<polyline points="21 15 16 10 5 21"/>
</svg>
<span>Lütfen bir görüntü seçin veya yapıştırın</span>
</div>
<!-- Düzeltme / Filtre Sonucu -->
<img id="imgCorr" class="compare-img corr" style="display:none">
<canvas id="canvasCorr" class="compare-img corr" style="display:none"></canvas>
<div id="phCorr" class="placeholder">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
<span>"Otomatik Düzelt" düğmesine tıklayın</span>
</div>
<!-- Kaydırıcı -->
<div class="slider" id="splitSlider">
<div class="slider-line"></div>
<div class="slider-btn"></div>
</div>
</div>
</div>
<!-- ==================== Kontrol Paneli ==================== -->
<div class="card controls">
<div class="section-title">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
<circle cx="8.5" cy="8.5" r="1.5"/>
<polyline points="21 15 16 10 5 21"/>
</svg>
Görüntü İçe Aktar
</div>
<div class="input-set">
<div class="file-wrap">
<input type="file" accept="image/*" id="fileIn" class="file-hidden">
<button class="file-vis">
<svg class="btn-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="17 8 12 3 7 8"/>
<line x1="12" y1="3" x2="12" y2="15"/>
</svg>
Görüntü Seç
</button>
</div>
<button id="btnPaste" class="btn grey">
<svg class="btn-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
</svg>
Görüntü Yapıştır
</button>
</div>
<!-- Ayarlama Parametreleri -->
<div class="adjustment-section">
<div class="section-title">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
Temel Ayarlar
</div>
<div class="range-set">
<div class="range-head"><span>Renk Sıcaklığı</span><span id="valT">0</span></div>
<input type="range" id="rangeT" min="-100" max="100" value="0" class="range">
</div>
<div class="range-set">
<div class="range-head"><span>Doygunluk</span><span id="valS">100</span></div>
<input type="range" id="rangeS" min="0" max="200" value="100" class="range">
</div>
<div class="range-set">
<div class="range-head"><span>Kontrast</span><span id="valC">100</span></div>
<input type="range" id="rangeC" min="0" max="200" value="100" class="range">
</div>
<div class="range-set">
<div class="range-head"><span>Parlaklık</span><span id="valB">100</span></div>
<input type="range" id="rangeB" min="0" max="200" value="100" class="range">
</div>
</div>
<!-- ====== İçe Aktarılabilir Sanatsal Filtreler ====== -->
<div class="section-title">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>
</svg>
Sanatsal Filtreler
</div>
<!-- Filtre İçe Aktar Butonu -->
<div class="file-wrap" style="margin-bottom:var(--space-md)">
<input id="filePreset" type="file" accept=".json" class="file-hidden">
<button id="btnPreset" class="file-vis">
<svg class="btn-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M12 3v18M3 12h18"/>
</svg>
Özel Filtre İçe Aktar
</button>
</div>
<!-- Filtre Buton Grubu -->
<div class="filter-btn-group" id="filterGroup"></div>
<!-- İşlem Butonları -->
<div class="btn-group">
<button id="btnAuto" class="btn blue">
<svg class="btn-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
Otomatik Düzelt
</button>
<button id="btnReset" class="btn grey">
<svg class="btn-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M3 2v6h6"/>
<path d="M3 13a9 9 0 1 0 3-7.7L3 8"/>
</svg>
Sıfırla
</button>
<button id="btnDL" class="btn green">
<svg class="btn-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
Sonucu İndir
</button>
</div>
</div>
<div class="footer">
©2025 LINUX DO Görüntü Renk Düzeltme Aracı | <a href="#" target="_blank">Kullanım Kılavuzu</a>
</div>
</div>
<!-- ============ JavaScript ============ -->
<script>
/* ========= DOM Referansları ========= */
const fileIn = document.getElementById('fileIn'),
btnPaste= document.getElementById('btnPaste'),
btnAuto = document.getElementById('btnAuto'),
btnReset= document.getElementById('btnReset'),
btnDL = document.getElementById('btnDL'),
imgOrig = document.getElementById('imgOrig'),
imgCorr = document.getElementById('imgCorr'),
cvCorr = document.getElementById('canvasCorr'),
ctxCorr = cvCorr.getContext('2d'),
phOrig = document.getElementById('phOrig'),
phCorr = document.getElementById('phCorr'),
split = document.getElementById('splitSlider'),
compareBox = document.getElementById('compareBox'),
filterGroup = document.getElementById('filterGroup'),
filePreset = document.getElementById('filePreset'),
btnPreset = document.getElementById('btnPreset'),
ranges = {
T: document.getElementById('rangeT'),
S: document.getElementById('rangeS'),
C: document.getElementById('rangeC'),
B: document.getElementById('rangeB')
},
vals = {
T: document.getElementById('valT'),
S: document.getElementById('valS'),
C: document.getElementById('valC'),
B: document.getElementById('valB')
};
let clipPos = 50, // Kaydırıcı Pozisyonu
imgURL = '', // Orijinal Görüntü URL'si
baseData = null, // "Düzeltilmiş" Piksel Önbelleği
currentFilter = 'none'; // Mevcut Filtre
/* ========= 0. Dahili + Özel Filtre Tablosu ========= */
const filterDefs = {
none : { label:'Hiçbiri', mode:'css', css:'' },
cool : { label:'Soğuk', mode:'pixel', func:filterCool },
vintage: { label:'Vintage', mode:'pixel', func:filterVintage },
comic : { label:'Çizgi Roman', mode:'pixel', func:filterComic }
};
/* ========= 1. Sürgü Değerleri → CSS-Filter String'i ========= */
function sliderFilterStr(){
const t = +ranges.T.value,
s = +ranges.S.value,
c = +ranges.C.value,
b = +ranges.B.value;
const tF = t<0 ? `sepia(${Math.abs(t)/100}) hue-rotate(180deg)`
: `sepia(${t/100})`;
return `${tF} saturate(${s}%) contrast(${c}%) brightness(${b}%)`;
}
/* ========= 2. Filtre Buton Çubuğunu Oluştur ========= */
function rebuildFilterBar(){
filterGroup.innerHTML='';
Object.entries(filterDefs).forEach(([id,def])=>{
const btn=document.createElement('button');
btn.className='btn filter-btn';
btn.dataset.filter=id;
btn.textContent=def.label;
filterGroup.appendChild(btn);
});
setActiveBtn();
}
rebuildFilterBar();
/* ========= 3. Mevcut Aktif Butonu Ayarla ========= */
function setActiveBtn(){
document.querySelectorAll('.filter-btn').forEach(btn=>{
btn.classList.toggle('active', btn.dataset.filter === currentFilter);
});
}
/* ========= 4. Dosya/Yapıştırma ile Görüntü Yükleme ========= */
fileIn.addEventListener('change',e=>{
if(e.target.files.length){
imgURL = URL.createObjectURL(e.target.files[0]);
loadImage(imgURL);
}
});
btnPaste.addEventListener('click',async ()=>{
try{
const items = await navigator.clipboard.read();
for(const it of items){
for(const t of it.types){
if(t.startsWith('image/')){
const blob = await it.getType(t);
imgURL = URL.createObjectURL(blob);
loadImage(imgURL);
return;
}
}
}
alert('Panoda görüntü bulunamadı');
}catch(e){
alert('Tarayıcı pano API\'sini desteklemiyor');
}
});
/* ========= 5. Orijinal Görüntüyü Yükle & Kutu Boyutunu Ayarla ========= */
function loadImage(url){
imgOrig.crossOrigin='anonymous';
imgOrig.onload = ()=>{
imgOrig.classList.add('loaded');
phOrig.style.display='none';
resizeBox();
reset(false);
};
imgOrig.onerror = ()=>{
alert('Görüntü yüklenemedi');
imgOrig.classList.remove('loaded');
};
imgOrig.src = url;
}
window.addEventListener('resize', resizeBox);
function resizeBox(){
if(!imgOrig.naturalWidth) return;
const r = imgOrig.naturalHeight / imgOrig.naturalWidth;
compareBox.style.height =
Math.min(window.innerHeight * .8,
compareBox.offsetWidth * r) + 'px';
}
/* ========= 6. Kaydırıcı Sürükleme ========= */
split.addEventListener('mousedown', startDrag);
split.addEventListener('touchstart', startDrag, {passive:true});
function startDrag(e){
e.preventDefault();
document.addEventListener('mousemove', moveDrag);
document.addEventListener('mouseup', stopDrag);
document.addEventListener('touchmove', moveDrag, {passive:true});
document.addEventListener('touchend', stopDrag);
document.addEventListener('touchcancel', stopDrag);
}
function moveDrag(e){
const rect = compareBox.getBoundingClientRect();
const x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
clipPos = Math.max(0, Math.min(100, (x/rect.width)*100));
updateClip();
}
function stopDrag(){
document.removeEventListener('mousemove', moveDrag);
document.removeEventListener('mouseup', stopDrag);
document.removeEventListener('touchmove', moveDrag);
document.removeEventListener('touchend', stopDrag);
document.removeEventListener('touchcancel', stopDrag);
}
function updateClip(){
split.style.left = clipPos + '%';
document.querySelectorAll('.corr').forEach(el=>{
el.style.clipPath = `inset(0 0 0 ${clipPos}%)`;
});
}
/* ========= 7. Otomatik LAB Düzeltme ========= */
btnAuto.addEventListener('click', ()=>{
if(!imgOrig.naturalWidth){ alert('Lütfen önce bir görüntü yükleyin'); return; }
/* 1. 800px'i geçmeyecek şekilde ölçeklendir */
const maxSide = 800;
const scale = Math.min(1, maxSide / Math.max(imgOrig.naturalWidth,
imgOrig.naturalHeight));
cvCorr.width = Math.round(imgOrig.naturalWidth * scale);
cvCorr.height = Math.round(imgOrig.naturalHeight * scale);
ctxCorr.drawImage(imgOrig, 0, 0, cvCorr.width, cvCorr.height);
/* 2. Basit gray-world + LAB a/b nötralizasyonu */
const imgData = ctxCorr.getImageData(0,0,cvCorr.width,cvCorr.height),
d = imgData.data, len = d.length/4;
let aSum=0, bSum=0;
for(let i=0;i<d.length;i+=4){
const [,a,b] = rgb2lab(d[i], d[i+1], d[i+2]);
aSum += a; bSum += b;
}
const aAvg = aSum/len, bAvg = bSum/len, k = 1;
for(let i=0;i<d.length;i+=4){
let [L,a,b] = rgb2lab(d[i], d[i+1], d[i+2]);
a -= k * aAvg; b -= k * bAvg;
const [r,g,bl] = lab2rgb(L,a,b);
d[i]=r; d[i+1]=g; d[i+2]=bl;
}
ctxCorr.putImageData(imgData,0,0);
/* 3. Temel piksel verisini kaydet, filtreleri sıfırla */
baseData = ctxCorr.getImageData(0,0,cvCorr.width,cvCorr.height);
currentFilter = 'none';
setActiveBtn();
/* 4. Sonucu göster */
imgCorr.src = cvCorr.toDataURL('image/png');
imgCorr.style.display='block';
imgCorr.classList.add('loaded');
cvCorr.style.display='none';
phCorr.style.display='none';
updateClip();
// Başarı animasyonu ekle
btnAuto.classList.add('success');
setTimeout(() => btnAuto.classList.remove('success'), 1000);
});
/* ========= 8. Dört Sürgü ========= */
Object.entries(ranges).forEach(([k,el])=>{
el.addEventListener('input',()=>{
vals[k].textContent = el.value;
vals[k].style.opacity = 1;
setTimeout(() => {
vals[k].style.opacity = 0.8;
}, 200);
applyFilter(); // CSS güncelle
});
});
/* ========= 9. Filtre Buton Olayları ========= */
filterGroup.addEventListener('click', e=>{
const btn = e.target.closest('.filter-btn');
if(!btn) return;
currentFilter = btn.dataset.filter;
setActiveBtn();
applyNamedFilter();
});
/* ========= 10. CSS Sürgülerini Uygula ========= */
function applyFilter(){
const def = filterDefs[currentFilter] || filterDefs.none;
const finalCss = (def.mode==='css'?def.css+' ':'') + sliderFilterStr();
imgCorr.style.filter = cvCorr.style.filter = finalCss;
}
/* ========= 11. Belirtilen Filtreyi Uygula ========= */
function applyNamedFilter(){
if(!baseData){
alert('Lütfen önce "Otomatik Düzelt" işlemini yapın');
return;
}
const def = filterDefs[currentFilter] || filterDefs.none;
/* ---- pixel tipi ---- */
if(def.mode==='pixel' && typeof def.func === 'function'){
let imgData = new ImageData(
new Uint8ClampedArray(baseData.data),
baseData.width, baseData.height);
def.func(imgData, imgData.data, imgData.width, imgData.height);
ctxCorr.putImageData(imgData, 0, 0);
imgCorr.src = cvCorr.toDataURL('image/png');
imgCorr.style.display='block';
phCorr.style.display='none';
}
/* ---- css tipi ---- */
if(def.mode==='css'){
ctxCorr.putImageData(baseData,0,0);
imgCorr.src = cvCorr.toDataURL('image/png');
}
applyFilter();
updateClip();
}
/* ========= 12. Filtre JSON İçe Aktarma ========= */
btnPreset.addEventListener('click',()=>filePreset.click());
filePreset.addEventListener('change',e=>{
if(!e.target.files.length) return;
const fr=new FileReader();
fr.onload=()=>{
try{
const json=JSON.parse(fr.result);
loadPreset(json);
rebuildFilterBar();
// Başarı bildirimi ekle
const toast = document.createElement('div');
toast.className = 'toast';
toast.textContent = 'Filtre başarıyla içe aktarıldı';
document.body.appendChild(toast);
setTimeout(() => {
toast.style.opacity = 0;
setTimeout(() => toast.remove(), 500);
}, 2000);
}catch(err){
alert('JSON ayrıştırma hatası: '+err.message);
}
};
fr.readAsText(e.target.files[0],'utf-8');
});
function loadPreset(list){
(Array.isArray(list)?list:[list]).forEach(f=>{
if(!f.id) return;
if(f.mode==='css' && f.css){
filterDefs[f.id] = {label:f.label||f.id,mode:'css',css:f.css};
}else if(f.mode==='pixel'){
let fn=null;
if(f.funcName && typeof window[f.funcName]==='function'){
fn = window[f.funcName];
}else if(f.script){
fn = new Function('imgData','data','width','height',f.script);
}
if(fn){
filterDefs[f.id]={label:f.label||f.id,mode:'pixel',func:fn};
}
}
});
}
/* ========= 13. İndirme ========= */
btnDL.addEventListener('click', ()=>{
if(imgCorr.src){
const a=document.createElement('a');
a.href=imgCorr.src;
a.download='filtrelenmis_goruntu.png';
a.click();
// İndirme animasyonu ekle
btnDL.classList.add('active');
setTimeout(() => btnDL.classList.remove('active'), 1000);
}else alert('İndirilecek görüntü yok');
});
/* ========= 14. Sıfırlama ========= */
btnReset.addEventListener('click', ()=>reset(false));
function reset(keepCorr){
ranges.T.value=0;
ranges.S.value=ranges.C.value=ranges.B.value=100;
vals.T.textContent=0;
vals.S.textContent=vals.C.textContent=vals.B.textContent=100;
currentFilter='none';
setActiveBtn();
applyFilter();
if(!keepCorr){
imgCorr.style.display='none';
imgCorr.classList.remove('loaded');
cvCorr.style.display='none';
phCorr.style.display='flex';
imgCorr.src='';
baseData=null;
}
clipPos=50;
updateClip();
}
/* ========= 15. Piksel Filtre Algoritmaları ========= */
function clamp(v){return v<0?0:(v>255?255:v);}
/* ----- Soğuk ----- */
function filterCool(imgData){
const d=imgData.data;
for(let i=0;i<d.length;i+=4){
let r=d[i]*1.1, g=d[i+1], b=d[i+2]*1.2;
const lum=r+g+b;
if(lum<200) b+=10;
d[i]=clamp(r); d[i+1]=clamp(g); d[i+2]=clamp(b);
}
return imgData;
}
/* ----- Vintage ----- */
function filterVintage(imgData){
const d=imgData.data, w=imgData.width, h=imgData.height;
const cx=w/2, cy=h/2, maxD=Math.hypot(cx,cy);
for(let y=0;y<h;y++){
for(let x=0;x<w;x++){
const idx=(y*w+x)*4;
const r=d[idx], g=d[idx+1], b=d[idx+2];
let tr=0.393*r+0.769*g+0.189*b;
let tg=0.349*r+0.686*g+0.168*b;
let tb=0.272*r+0.534*g+0.131*b;
tr*=0.8; tg*=0.8; tb*=0.8;
const v=1-0.5*Math.hypot(x-cx,y-cy)/maxD;
tr*=v; tg*=v; tb*=v;
const n=(Math.random()-.5)*20;
d[idx]=clamp(tr+n); d[idx+1]=clamp(tg+n); d[idx+2]=clamp(tb+n);
}
}
return imgData;
}
/* ----- Çizgi Roman ----- */
function filterComic(imgData){
const w=imgData.width,h=imgData.height,src=imgData.data;
const gray=new Uint8Array(w*h),edge=new Uint8Array(w*h);
for(let i=0,j=0;i<src.length;i+=4,j++){
gray[j]=0.299*src[i]+0.587*src[i+1]+0.114*src[i+2];
}
for(let y=1;y<h-1;y++){
for(let x=1;x<w-1;x++){
const i=y*w+x;
const gx=-gray[i-w-1]-2*gray[i-1]-gray[i+w-1]+gray[i-w+1]+2*gray[i+1]+gray[i+w+1];
const gy=-gray[i-w-1]-2*gray[i-w]-gray[i-w+1]+gray[i+w-1]+2*gray[i+w]+gray[i+w+1];
edge[i]=(Math.abs(gx)+Math.abs(gy))>250?255:0;
}
}
const lvl=v=>[0,85,170,255][Math.floor(v/64)];
for(let i=0,j=0;i<src.length;i+=4,j++){
if(edge[j]){src[i]=src[i+1]=src[i+2]=0;}
else{
src[i]=lvl(src[i]); src[i+1]=lvl(src[i+1]); src[i+2]=lvl(src[i+2]);
}
}
return imgData;
}
/* ========= 16. Renk Uzayı Araçları ========= */
const W = {xn:0.95047,yn:1,zn:1.08883};
const srgb2lin = x=> (x/=255)<=0.04045?x/12.92:Math.pow((x+0.055)/1.055,2.4);
const lin2srgb = x=>{
x=Math.max(0,Math.min(1,x));
return Math.round((x<=0.0031308?12.92*x:1.055*Math.pow(x,1/2.4)-0.055)*255);
};
function rgb2xyz(r,g,b){
r=srgb2lin(r);g=srgb2lin(g);b=srgb2lin(b);
return [0.4124*r+0.3576*g+0.1805*b,
0.2126*r+0.7152*g+0.0722*b,
0.0193*r+0.1192*g+0.9505*b];
}
function xyz2rgb(x,y,z){
const r=3.2406*x-1.5372*y-0.4986*z,
g=-0.9689*x+1.8758*y+0.0415*z,
b=0.0557*x-0.2040*y+1.0570*z;
return [lin2srgb(r),lin2srgb(g),lin2srgb(b)];
}
function xyz2lab(x,y,z){
const f=t=>(t>0.008856)?Math.cbrt(t):(7.787*t+16/116);
const fx=f(x/W.xn), fy=f(y/W.yn), fz=f(z/W.zn);
return [116*fy-16,500*(fx-fy),200*(fy-fz)];
}
function lab2xyz(L,a,b){
const fy=(L+16)/116,fx=a/500+fy,fz=fy-b/200;
const f=t=>{const t3=t*t*t;return (t3>0.008856)?t3:(t-16/116)/7.787};
return [W.xn*f(fx), W.yn*f(fy), W.zn*f(fz)];
}
function rgb2lab(r,g,b){return xyz2lab(...rgb2xyz(r,g,b))}
function lab2rgb(L,a,b){return xyz2rgb(...lab2xyz(L,a,b))}
/* ========= 17. Toast Bildirim Stillerini Ekle ========= */
const toastStyle = document.createElement('style');
toastStyle.textContent = `
.toast {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.8);
color: white;
padding: 10px 20px;
border-radius: 4px;
font-size: 14px;
z-index: 1000;
opacity: 1;
transition: opacity 0.5s;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.btn.active, .btn.success {
transform: scale(1.05);
transition: transform 0.2s;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
`;
document.head.appendChild(toastStyle);
/* ========= Başlangıç İşlemleri ========= */
updateClip();
applyFilter();
// Sağ tık menüsünü devre dışı bırak, kullanıcı deneyimini artır
document.addEventListener('contextmenu', e => e.preventDefault());
// Görüntüler yüklendiğinde sınıf ekle
imgOrig.addEventListener('load', () => {
imgOrig.classList.add('loaded');
});
imgCorr.addEventListener('load', () => {
imgCorr.classList.add('loaded');
});
</script>
</body>
</html>