Professional Print Solutions
C.C.S.
Color Correction Studio
Professional color correction for sublimation on colored substrates with live preview.

Color Correction Studio

Professional color correction for garment printing

📋 How to Use This Tool

📷 Step 1: First, take a photo of your fabric or material in normal room lighting to capture its true color
🖼️ Step 2: Upload your design or artwork that you want to print
⚙️ Step 3: The app will optimize your design and show you exactly how it will look when printed on your colored material
🖼️
Design Image
Your design to optimize for printing
🎨
Substrate Color
Reference image of substrate color

Original Design

No image uploaded

Substrate Color

Upload substrate reference

Processing Stats

No processing yet

🛠️ Color Correction Controls

75%
20%
130%
25%

👁️ Preview Modes

Upload images and select a preview mode
class ColorCorrectionStudio { constructor() { this.originalImage = null; this.substrateColor = { r: 60, g: 60, b: 60 }; this.currentMode = 'original'; this.processedData = { original: null, basic: null, printfile: null, simulation: null, difference: null }; this.settings = { inkOpacity: 0.75, transparencyThreshold: 0.20, colorBoost: 1.30, substrateBleed: 0.25 }; this.stats = { transparentPixels: 0, processedPixels: 0, avgTransparency: 0 }; this.initializeEventListeners(); this.setupDragAndDrop(); this.initializeCounters(); } initializeCounters() { let pageLoads = parseInt(localStorage.getItem('reddoorrip_colorcorrection_page_loads') || '0'); pageLoads++; localStorage.setItem('reddoorrip_colorcorrection_page_loads', pageLoads.toString()); document.getElementById('pageLoadCounter').textContent = pageLoads.toLocaleString(); const imageCount = parseInt(localStorage.getItem('reddoorrip_colorcorrection_images_processed') || '0'); document.getElementById('imageCounter').textContent = imageCount.toLocaleString(); const downloadCount = parseInt(localStorage.getItem('reddoorrip_colorcorrection_downloads') || '0'); document.getElementById('downloadCounter').textContent = downloadCount.toLocaleString(); } incrementImageCounter() { let imageCount = parseInt(localStorage.getItem('reddoorrip_colorcorrection_images_processed') || '0'); imageCount++; localStorage.setItem('reddoorrip_colorcorrection_images_processed', imageCount.toString()); document.getElementById('imageCounter').textContent = imageCount.toLocaleString(); } incrementDownloadCounter() { let downloadCount = parseInt(localStorage.getItem('reddoorrip_colorcorrection_downloads') || '0'); downloadCount++; localStorage.setItem('reddoorrip_colorcorrection_downloads', downloadCount.toString()); document.getElementById('downloadCounter').textContent = downloadCount.toLocaleString(); } initializeEventListeners() { document.getElementById('image1').addEventListener('change', (e) => this.handleImageUpload(e, 1)); document.getElementById('image2').addEventListener('change', (e) => this.handleImageUpload(e, 2)); ['inkOpacity', 'transparencyThreshold', 'colorBoost', 'substrateBleed'].forEach(control => { const slider = document.getElementById(control); const valueDisplay = document.getElementById(control + 'Value'); slider.addEventListener('input', (e) => { let value = parseInt(e.target.value); if (control === 'inkOpacity' || control === 'transparencyThreshold' || control === 'substrateBleed') { this.settings[control] = value / 100; valueDisplay.textContent = value + '%'; } else if (control === 'colorBoost') { this.settings[control] = value / 100; valueDisplay.textContent = value + '%'; } this.processImage(); }); }); document.querySelectorAll('.mode-btn').forEach(btn => { btn.addEventListener('click', (e) => { this.switchMode(e.target.dataset.mode); }); }); document.getElementById('downloadPrint').addEventListener('click', () => { this.downloadImage('printfile'); this.incrementDownloadCounter(); }); document.getElementById('downloadTransparent').addEventListener('click', () => { this.downloadImage('basic'); this.incrementDownloadCounter(); }); document.getElementById('downloadSimulation').addEventListener('click', () => { this.downloadImage('simulation'); this.incrementDownloadCounter(); }); } setupDragAndDrop() { ['upload1', 'upload2'].forEach((id, index) => { const uploadArea = document.getElementById(id); uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('dragover'); }); uploadArea.addEventListener('dragleave', () => { uploadArea.classList.remove('dragover'); }); uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.classList.remove('dragover'); const files = e.dataTransfer.files; if (files.length > 0) { const input = document.getElementById(`image${index + 1}`); input.files = files; this.handleImageUpload({ target: input }, index + 1); } }); }); } handleImageUpload(event, imageNumber) { incrementToolCounter(); const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { const img = new Image(); img.onload = () => { if (imageNumber === 1) { this.originalImage = img; document.getElementById('preview1').src = e.target.result; document.getElementById('preview1').style.display = 'block'; document.getElementById('placeholder1').style.display = 'none'; this.incrementImageCounter(); } else { this.calculateSubstrateColor(img); document.getElementById('preview2').src = e.target.result; document.getElementById('preview2').style.display = 'block'; } if (this.originalImage && this.substrateColor) { this.processImage(); } }; img.src = e.target.result; }; reader.readAsDataURL(file); } calculateSubstrateColor(img) { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; let r = 0, g = 0, b = 0; let pixelCount = 0; for (let i = 0; i < data.length; i += 4) { r += data[i]; g += data[i + 1]; b += data[i + 2]; pixelCount++; } this.substrateColor = { r: Math.round(r / pixelCount), g: Math.round(g / pixelCount), b: Math.round(b / pixelCount) }; const colorSample = document.getElementById('colorSample'); const colorHex = `rgb(${this.substrateColor.r}, ${this.substrateColor.g}, ${this.substrateColor.b})`; colorSample.style.background = colorHex; document.getElementById('colorText').textContent = `RGB(${this.substrateColor.r}, ${this.substrateColor.g}, ${this.substrateColor.b})`; } processImage() { if (!this.originalImage) return; const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = this.originalImage.width; canvas.height = this.originalImage.height; ctx.drawImage(this.originalImage, 0, 0); const originalData = ctx.getImageData(0, 0, canvas.width, canvas.height); this.processedData.original = this.createOriginalMode(originalData); this.processedData.basic = this.createBasicSubstrateMode(originalData); this.processedData.printfile = this.createPrintFile(originalData); this.processedData.simulation = this.createSubstrateSimulation(originalData); this.processedData.difference = this.createDifferenceView(originalData); this.updateStats(); this.updateDisplay(); document.getElementById('downloadPrint').disabled = false; document.getElementById('downloadTransparent').disabled = false; document.getElementById('downloadSimulation').disabled = false; } createOriginalMode(originalData) { return { data: new ImageData(new Uint8ClampedArray(originalData.data), originalData.width, originalData.height), width: originalData.width, height: originalData.height }; } createBasicSubstrateMode(originalData) { const data = new Uint8ClampedArray(originalData.data); const whiteThreshold = 242; for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; const isNearWhite = r >= whiteThreshold && g >= whiteThreshold && b >= whiteThreshold; if (isNearWhite) { data[i + 3] = 0; // Make transparent } } return { data: new ImageData(data, originalData.width, originalData.height), width: originalData.width, height: originalData.height }; } createPrintFile(originalData) { const data = new Uint8ClampedArray(originalData.data); this.stats.transparentPixels = 0; this.stats.processedPixels = 0; let totalTransparency = 0; for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; const originalAlpha = data[i + 3]; this.stats.processedPixels++; const luminance = 0.299 * r + 0.587 * g + 0.114 * b; const transparencyFromLuminance = luminance / 255; const adjustedTransparency = Math.max(0, transparencyFromLuminance - this.settings.transparencyThreshold); const alpha = (1 - adjustedTransparency) * (originalAlpha / 255); totalTransparency += (1 - alpha); if (alpha < 0.1) { this.stats.transparentPixels++; data[i + 3] = 0; } else { const factor = this.settings.colorBoost; const gray = (r + g + b) / 3; const boostedR = gray + (r - gray) * factor; const boostedG = gray + (g - gray) * factor; const boostedB = gray + (b - gray) * factor; data[i] = Math.max(0, Math.min(255, boostedR)); data[i + 1] = Math.max(0, Math.min(255, boostedG)); data[i + 2] = Math.max(0, Math.min(255, boostedB)); data[i + 3] = Math.round(alpha * 255); } } this.stats.avgTransparency = totalTransparency / this.stats.processedPixels; return { data: new ImageData(data, originalData.width, originalData.height), width: originalData.width, height: originalData.height }; } createSubstrateSimulation(originalData) { const printFile = this.processedData.printfile; if (!printFile) return null; const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = originalData.width; canvas.height = originalData.height; ctx.fillStyle = `rgb(${this.substrateColor.r}, ${this.substrateColor.g}, ${this.substrateColor.b})`; ctx.fillRect(0, 0, canvas.width, canvas.height); const substrateData = ctx.getImageData(0, 0, canvas.width, canvas.height); const simData = new Uint8ClampedArray(substrateData.data); const printData = printFile.data.data; for (let i = 0; i < simData.length; i += 4) { const inkR = printData[i]; const inkG = printData[i + 1]; const inkB = printData[i + 2]; const inkAlpha = printData[i + 3] / 255; const subR = this.substrateColor.r; const subG = this.substrateColor.g; const subB = this.substrateColor.b; const inkOpacity = this.settings.inkOpacity * inkAlpha; const substrateShow = 1 - inkOpacity + (this.settings.substrateBleed * inkOpacity); simData[i] = Math.round((inkR * inkOpacity) + (subR * substrateShow)); simData[i + 1] = Math.round((inkG * inkOpacity) + (subG * substrateShow)); simData[i + 2] = Math.round((inkB * inkOpacity) + (subB * substrateShow)); simData[i + 3] = 255; } return { data: new ImageData(simData, originalData.width, originalData.height), width: originalData.width, height: originalData.height }; } createDifferenceView(originalData) { const simulation = this.processedData.simulation; if (!simulation) return null; const diffData = new Uint8ClampedArray(originalData.data); const origData = originalData.data; const simData = simulation.data.data; for (let i = 0; i < diffData.length; i += 4) { const origR = origData[i]; const origG = origData[i + 1]; const origB = origData[i + 2]; const simR = simData[i]; const simG = simData[i + 1]; const simB = simData[i + 2]; const diffR = Math.abs(origR - simR); const diffG = Math.abs(origG - simG); const diffB = Math.abs(origB - simB); const totalDiff = (diffR + diffG + diffB) / 3; diffData[i] = Math.min(255, totalDiff * 2); diffData[i + 1] = Math.max(0, 255 - totalDiff); diffData[i + 2] = Math.max(0, 255 - totalDiff); diffData[i + 3] = 255; } return { data: new ImageData(diffData, originalData.width, originalData.height), width: originalData.width, height: originalData.height }; } switchMode(mode) { this.currentMode = mode; document.querySelectorAll('.mode-btn').forEach(btn => { btn.classList.remove('active'); }); document.querySelector(`[data-mode="${mode}"]`).classList.add('active'); this.updateDisplay(); } updateDisplay() { const canvas = document.getElementById('resultCanvas'); const ctx = canvas.getContext('2d'); const currentData = this.processedData[this.currentMode]; if (!currentData) { canvas.style.display = 'none'; document.getElementById('placeholderResult').style.display = 'block'; return; } canvas.width = currentData.width; canvas.height = currentData.height; canvas.style.display = 'block'; document.getElementById('placeholderResult').style.display = 'none'; if (this.currentMode === 'basic' || this.currentMode === 'printfile') { ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, canvas.width, canvas.height); const size = 20; for (let x = 0; x < canvas.width; x += size) { for (let y = 0; y < canvas.height; y += size) { if ((Math.floor(x/size) + Math.floor(y/size)) % 2) { ctx.fillStyle = '#f0f0f0'; ctx.fillRect(x, y, size, size); } } } } if (this.currentMode === 'basic') { // Show basic with substrate background ctx.fillStyle = `rgb(${this.substrateColor.r}, ${this.substrateColor.g}, ${this.substrateColor.b})`; ctx.fillRect(0, 0, canvas.width, canvas.height); const tempCanvas = document.createElement('canvas'); const tempCtx = tempCanvas.getContext('2d'); tempCanvas.width = canvas.width; tempCanvas.height = canvas.height; tempCtx.putImageData(currentData.data, 0, 0); ctx.drawImage(tempCanvas, 0, 0); } else { ctx.putImageData(currentData.data, 0, 0); } } updateStats() { const stats = document.getElementById('statsDisplay'); const transparencyPercent = ((this.stats.transparentPixels / this.stats.processedPixels) * 100).toFixed(1); const avgTransparencyPercent = (this.stats.avgTransparency * 100).toFixed(1); stats.innerHTML = ` Pixels processed: ${this.stats.processedPixels.toLocaleString()}
Transparent pixels: ${this.stats.transparentPixels.toLocaleString()} (${transparencyPercent}%)
Avg transparency: ${avgTransparencyPercent}%
Ink opacity: ${(this.settings.inkOpacity * 100).toFixed(0)}%
Color boost: ${(this.settings.colorBoost * 100).toFixed(0)}% `; } downloadImage(mode) { const data = this.processedData[mode]; if (!data) return; const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = data.width; canvas.height = data.height; if (mode === 'basic') { // Show with substrate background for basic mode ctx.fillStyle = `rgb(${this.substrateColor.r}, ${this.substrateColor.g}, ${this.substrateColor.b})`; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.putImageData(data.data, 0, 0); } else { ctx.putImageData(data.data, 0, 0); } const link = document.createElement('a'); const modeNames = { printfile: 'sublimation-print-file', basic: 'basic-substrate-preview', simulation: 'substrate-simulation' }; link.download = `${modeNames[mode]}.png`; link.href = canvas.toDataURL(); link.click(); } } // Initialize the app document.addEventListener('DOMContentLoaded', () => { new ColorCorrectionStudio(); }); })(); // Counter 3 increment function - tracks tool usage function incrementToolCounter() { fetch('/includes/visitor-tracking/increment_custom.php') .then(response => response.json()) .then(data => console.log('Tool counter incremented:', data)) .catch(error => console.error('Counter error:', error)); }
Footer Version: 2.0.2