From 75d0b8fe04fd565ec3181508c4396e9af9660ce4 Mon Sep 17 00:00:00 2001 From: ippo615 Date: Wed, 4 Sep 2013 21:08:39 -0400 Subject: [PATCH] Added experimental filter section. I added the experimental folder to show some work on filters that can be applied to an entire layer. Multiple filters can be applied to a layer (in any order, multiple times). To hook into the layer I use: layer.on('draw', filterFunc); Eventually, I would like to move that to `layer.filterFunc` and automatically apply it after the draw. `filterFunc` looks like: function filterFunc(){ // Get pixel data and create a temporary pixel buffer for working var imageData = this.getContext().getImageData(0,0,this.getCanvas().width,this.getCanvas().height); var scratchData = this.getContext().createImageData(imageData); // Apply all filters here ColorStretch(imageData,scratchData,{}); // Copy the pixel data back this.getContext().putImageData(scratchData,0,0); } `ColorStretch` is an example of a filter. It takes 3 arguments: the original image data, image data to write the result to, and an options object. --- experimental/colorStretch.js | 74 ++++++++++++++ experimental/flip.js | 41 ++++++++ experimental/grayscale.js | 16 +++ experimental/index.html | 182 +++++++++++++++++++++++++++++++++++ experimental/invert.js | 15 +++ experimental/levels.js | 14 +++ experimental/mirror.js | 55 +++++++++++ experimental/noise.js | 27 ++++++ experimental/pixelate.js | 94 ++++++++++++++++++ experimental/threshold.js | 17 ++++ 10 files changed, 535 insertions(+) create mode 100644 experimental/colorStretch.js create mode 100644 experimental/flip.js create mode 100644 experimental/grayscale.js create mode 100644 experimental/index.html create mode 100644 experimental/invert.js create mode 100644 experimental/levels.js create mode 100644 experimental/mirror.js create mode 100644 experimental/noise.js create mode 100644 experimental/pixelate.js create mode 100644 experimental/threshold.js diff --git a/experimental/colorStretch.js b/experimental/colorStretch.js new file mode 100644 index 00000000..6323a2ec --- /dev/null +++ b/experimental/colorStretch.js @@ -0,0 +1,74 @@ +ColorStretch = (function () { + function remap(fromValue, fromMin, fromMax, toMin, toMax) { + + // Make sure min is less than max + var swap; + if (fromMin > fromMax) { + swap = fromMax; + fromMin = fromMax; + fromMin = swap; + } + if (toMin > toMax) { + swap = toMax; + toMin = toMax; + toMin = swap; + } + + // Compute the range of the data + var fromRange = fromMax - fromMin; + var toRange = toMax - toMin; + + // If either range is 0, then the value can only be mapped to 1 value + if (fromRange === 0) { + return toMin + toRange / 2; + } + if (toRange === 0) { + return toMin; + } + + // (1) untranslate, (2) unscale, (3) rescale, (4) retranslate + var toValue = (fromValue - fromMin) / fromRange; + toValue = (toRange * toValue) + toMin; + + return toValue; + } + + // 2 PASS! + var ColorStretch = function (src, dst, opt) { + var srcPixels = src.data, + dstPixels = dst.data, + nPixels = srcPixels.length, + i; + + // 1st Pass - find the min and max for each channel: + var rMin = srcPixels[0], rMax = rMin, r, + gMin = srcPixels[1], gMax = gMin, g, + bMin = srcPixels[3], bMax = bMin, b, + aMin = srcPixels[4], aMax = aMin, a; + for (i = 0; i < nPixels; i += 4) { + r = srcPixels[i + 0]; + if (r < rMin) { rMin = r; } else + if (r > rMax) { rMax = r; } + g = srcPixels[i + 1]; + if (g < gMin) { gMin = g; } else + if (g > gMax) { gMax = g; } + b = srcPixels[i + 2]; + if (b < bMin) { bMin = b; } else + if (b > bMax) { bMax = b; } + a = srcPixels[i + 3]; + if (a < aMin) { aMin = a; } else + if (a > aMax) { aMax = a; } + } + + // Pass 2 - remap everything to fill the full range + for (i = 0; i < nPixels; i += 1) { + dstPixels[i + 0] = remap(srcPixels[i + 0], rMin, rMax, 0, 255); + dstPixels[i + 1] = remap(srcPixels[i + 1], gMin, gMax, 0, 255); + dstPixels[i + 2] = remap(srcPixels[i + 2], bMin, bMax, 0, 255); + dstPixels[i + 3] = remap(srcPixels[i + 3], aMin, aMax, 0, 255); + } + + }; + + return ColorStretch; +})(); diff --git a/experimental/flip.js b/experimental/flip.js new file mode 100644 index 00000000..8c766e8e --- /dev/null +++ b/experimental/flip.js @@ -0,0 +1,41 @@ +FlipX = (function () { + var FlipX = function (src, dst, opt) { + var srcPixels = src.data, + dstPixels = dst.data, + xSize = src.width, + ySize = src.height, + i, m, x, y; + for (x = 0; x < xSize; x += 1) { + for (y = 0; y < ySize; y += 1) { + i = (y * xSize + x) * 4; // original + m = ((y + 1) * xSize - x) * 4; // flipped + dstPixels[m + 0] = srcPixels[i + 0]; + dstPixels[m + 1] = srcPixels[i + 1]; + dstPixels[m + 2] = srcPixels[i + 2]; + dstPixels[m + 3] = srcPixels[i + 3]; + } + } + }; + return FlipX; +})(); + +FlipY = (function () { + var FlipY = function (src, dst, opt) { + var srcPixels = src.data, + dstPixels = dst.data, + xSize = src.width, + ySize = src.height, + i, m, x, y; + for (x = 0; x < xSize; x += 1) { + for (y = 0; y < ySize; y += 1) { + i = (y * xSize + x) * 4; // original + m = ((ySize - y) * xSize + x) * 4; // flipped + dstPixels[m + 0] = srcPixels[i + 0]; + dstPixels[m + 1] = srcPixels[i + 1]; + dstPixels[m + 2] = srcPixels[i + 2]; + dstPixels[m + 3] = srcPixels[i + 3]; + } + } + }; + return FlipY; +})(); diff --git a/experimental/grayscale.js b/experimental/grayscale.js new file mode 100644 index 00000000..bff3fc04 --- /dev/null +++ b/experimental/grayscale.js @@ -0,0 +1,16 @@ +Grayscale = (function () { + var Grayscale = function (src, dst) { + var srcPixels = src.data, + dstPixels = dst.data, + nPixels = srcPixels.length, + i, brightness; + for (i = 0; i < nPixels; i += 4) { + brightness = 0.34 * srcPixels[i] + 0.5 * srcPixels[i + 1] + 0.16 * srcPixels[i + 2]; + dstPixels[i] = brightness; // r + dstPixels[i + 1] = brightness; // g + dstPixels[i + 2] = brightness; // b + dstPixels[i + 3] = srcPixels[i + 3]; // alpha + } + }; + return Grayscale; +})(); diff --git a/experimental/index.html b/experimental/index.html new file mode 100644 index 00000000..c5da5d35 --- /dev/null +++ b/experimental/index.html @@ -0,0 +1,182 @@ + + + + + + +
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/experimental/invert.js b/experimental/invert.js new file mode 100644 index 00000000..bc714247 --- /dev/null +++ b/experimental/invert.js @@ -0,0 +1,15 @@ +Invert = (function () { + var Invert = function (src, dst, opt) { + var srcPixels = src.data, + dstPixels = dst.data, + nPixels = srcPixels.length, + i; + for (i = 0; i < nPixels; i += 4) { + dstPixels[i+0] = 255 - srcPixels[i+0]; // r + dstPixels[i+1] = 255 - srcPixels[i+1]; // g + dstPixels[i+2] = 255 - srcPixels[i+2]; // b + dstPixels[i+3] = srcPixels[i+3]; // copy alpha + } + }; + return Invert; +})(); \ No newline at end of file diff --git a/experimental/levels.js b/experimental/levels.js new file mode 100644 index 00000000..4f8b824c --- /dev/null +++ b/experimental/levels.js @@ -0,0 +1,14 @@ +Levels = (function () { + var Levels = function (src, dst, opt) { + var nLevels = opt.levels || 2; + var srcPixels = src.data, + dstPixels = dst.data, + nPixels = srcPixels.length, + scale = (255 / nLevels), + i; + for (i = 0; i < nPixels; i += 1) { + dstPixels[i] = Math.floor(srcPixels[i] / scale) * scale; + } + }; + return Levels; +})(); \ No newline at end of file diff --git a/experimental/mirror.js b/experimental/mirror.js new file mode 100644 index 00000000..4f5fa734 --- /dev/null +++ b/experimental/mirror.js @@ -0,0 +1,55 @@ +MirrorX = (function () { + var MirrorX = function (src, dst, opt) { + var srcPixels = src.data, + dstPixels = dst.data, + xSize = src.width, + ySize = src.height, + xMid = Math.ceil(xSize / 2), + i, m, x, y; + for (x = 0; x <= xMid; x += 1) { + for (y = 0; y < ySize; y += 1) { + // copy the original + i = (y * xSize + x) * 4; + dstPixels[i + 0] = srcPixels[i + 0]; + dstPixels[i + 1] = srcPixels[i + 1]; + dstPixels[i + 2] = srcPixels[i + 2]; + dstPixels[i + 3] = srcPixels[i + 3]; + // copy the mirrored + m = (y * xSize + xSize - x) * 4; + dstPixels[m + 0] = srcPixels[i + 0]; + dstPixels[m + 1] = srcPixels[i + 1]; + dstPixels[m + 2] = srcPixels[i + 2]; + dstPixels[m + 3] = srcPixels[i + 3]; + } + } + }; + return MirrorX; +})(); + +MirrorY = (function () { + var MirrorY = function (src, dst, opt) { + var srcPixels = src.data, + dstPixels = dst.data, + xSize = src.width, + ySize = src.height, + yMid = Math.ceil(ySize / 2), + i, m, x, y; + for (x = 0; x < xSize; x += 1) { + for (y = 0; y <= yMid; y += 1) { + // copy the original + i = (y * xSize + x) * 4; + dstPixels[i + 0] = srcPixels[i + 0]; + dstPixels[i + 1] = srcPixels[i + 1]; + dstPixels[i + 2] = srcPixels[i + 2]; + dstPixels[i + 3] = srcPixels[i + 3]; + // copy the mirrored + m = ( (ySize-y) * xSize + x) * 4; + dstPixels[m + 0] = srcPixels[i + 0]; + dstPixels[m + 1] = srcPixels[i + 1]; + dstPixels[m + 2] = srcPixels[i + 2]; + dstPixels[m + 3] = srcPixels[i + 3]; + } + } + }; + return MirrorY; +})(); diff --git a/experimental/noise.js b/experimental/noise.js new file mode 100644 index 00000000..4b9c42ff --- /dev/null +++ b/experimental/noise.js @@ -0,0 +1,27 @@ +Noise = (function () { + var Noise = function (src, dst, opt) { + var amount = opt.amount || 32, + affectAlpha = opt.affectAlpha || 0; + var srcPixels = src.data, + dstPixels = dst.data, + nPixels = srcPixels.length, + half = amount / 2, + i; + if (affectAlpha) { + for (i = 0; i < nPixels; i += 4) { + dstPixels[i + 0] = srcPixels[i + 0] + half - 2 * half * Math.random(); + dstPixels[i + 1] = srcPixels[i + 1] + half - 2 * half * Math.random(); + dstPixels[i + 2] = srcPixels[i + 2] + half - 2 * half * Math.random(); + dstPixels[i + 3] = srcPixels[i + 3] + half - 2 * half * Math.random(); + } + } else { + for (i = 0; i < nPixels; i += 4) { + dstPixels[i + 0] = srcPixels[i + 0] + half - 2 * half * Math.random(); + dstPixels[i + 1] = srcPixels[i + 1] + half - 2 * half * Math.random(); + dstPixels[i + 2] = srcPixels[i + 2] + half - 2 * half * Math.random(); + dstPixels[i + 3] = srcPixels[i + 3]; + } + } + }; + return Noise; +})(); \ No newline at end of file diff --git a/experimental/pixelate.js b/experimental/pixelate.js new file mode 100644 index 00000000..be976281 --- /dev/null +++ b/experimental/pixelate.js @@ -0,0 +1,94 @@ +Pixelate = (function () { + var Pixelate = function (src, dst, opt) { + + var xBinSize = opt.width || 8, + yBinSize = opt.height || 8; + + var xSize = src.width, + ySize = src.height, + srcPixels = src.data, + dstPixels = dst.data, + x, y, i; + var pixelsPerBin = xBinSize * yBinSize, + reds = [], + greens = [], + blues = [], + alphas = [], + red, green, blue, alpha, + nBinsX = Math.floor(xSize / opt.width), + nBinsY = Math.floor(ySize / opt.height), + xBinStart, xBinEnd, yBinStart, yBinEnd, + xBin, yBin; + + for (xBin = 0; xBin < nBinsX; xBin += 1) { + + // Add a new 'row' + reds.push([]); + greens.push([]); + blues.push([]); + alphas.push([]); + + for (yBin = 0; yBin < nBinsY; yBin += 1) { + + // Initialize all bins to 0 + red = 0; + green = 0; + blue = 0; + alpha = 0; + + // Determine which pixels are included in this bin + xBinStart = xBin * xBinSize; + xBinEnd = xBinStart + xBinSize; + yBinStart = yBin * yBinSize; + yBinEnd = yBinStart + yBinSize; + + // Add all of the pixels to this bin! + for (x = xBinStart; x < xBinEnd; x += 1) { + for (y = yBinStart; y < yBinEnd; y += 1) { + i = (xSize * y + x) * 4; + red += srcPixels[i + 0]; + green += srcPixels[i + 1]; + blue += srcPixels[i + 2]; + alpha += srcPixels[i + 3]; + } + } + + // Make sure the pixels are between 0-255 + reds[xBin].push(red / pixelsPerBin); + greens[xBin].push(green / pixelsPerBin); + blues[xBin].push(blue / pixelsPerBin); + alphas[xBin].push(alpha / pixelsPerBin); + } + } + + // For each bin + for (xBin = 0; xBin < nBinsX; xBin += 1) { + for (yBin = 0; yBin < nBinsY; yBin += 1) { + xBinStart = xBin * xBinSize; + xBinEnd = xBinStart + xBinSize; + yBinStart = yBin * yBinSize; + yBinEnd = yBinStart + yBinSize; + + // Draw all of the pixels at the bin's average value + for (x = xBinStart; x < xBinEnd; x += 1) { + for (y = yBinStart; y < yBinEnd; y += 1) { + i = (xSize * y + x) * 4; + dstPixels[i + 0] = reds[xBin][yBin]; + dstPixels[i + 1] = greens[xBin][yBin]; + dstPixels[i + 2] = blues[xBin][yBin]; + dstPixels[i + 3] = alphas[xBin][yBin]; + } + } + } + } + + // I probably don't need to set these to null, but I want to make sure + // the garabage collector removes them + reds = null; + greens = null; + blues = null; + alphas = null; + }; + + return Pixelate; +})(); \ No newline at end of file diff --git a/experimental/threshold.js b/experimental/threshold.js new file mode 100644 index 00000000..21c130c5 --- /dev/null +++ b/experimental/threshold.js @@ -0,0 +1,17 @@ +Threshold = (function () { + var Threshold = function (src, dst, opt) { + var level = opt.level || 128; + var srcPixels = src.data, + dstPixels = dst.data, + nPixels = srcPixels.length, + i; + for (i = 0; i < nPixels; i += 1) { + if (srcPixels[i] < level) { + dstPixels[i] = 0; + } else { + dstPixels[i] = 255; + } + } + }; + return Threshold; +})(); \ No newline at end of file