{{>simple-image-filters-source-html}}

<script type="text/javascript">
    YUI().use(
        'cssbase',
        'cssbutton',
        'cssfonts',
        'cssgrids',
        'cssreset',
        'gallery-composite-image-canvas',
        function (Y) {
            'use strict';

            // Get a reference to the form nodes.
            var blurHeightLabelNode = Y.one('#blurHeightLabel'),
                blurHeightNode = Y.one('#blurHeight'),
                blurWidthLabelNode = Y.one('#blurWidthLabel'),
                blurWidthNode = Y.one('#blurWidth'),
                displayNode = Y.one('#display'),
                selectFilterNode = Y.one('#selectFilter'),
                selectImageNode = Y.one('#selectImage'),

                createImageFromImgNode = function (imgNode) {
                    // Create a canvas to draw the image on.
                    var canvasNode = Y.Node.create(
                            '<canvas height="' + imgNode.get('height') + '" width="' + imgNode.get('width') + '">' +
                            '</canvas>'
                        );

                    // Draw the image.
                    canvasNode.getDOMNode().getContext('2d').drawImage(imgNode.getDOMNode(), 0, 0);

                    // Create and return an image object from the canvas.
                    return Y.Composite.Image.fromCanvas(canvasNode);
                },

                createImageFromUrl = function (url, callbackFunction) {
                    // Create an img node to fetch the file.
                    var imgNode = Y.Node.create('<img alt="' + url + '" src="' + url + '" />');

                    // Wait for the file to load.
                    imgNode.on('load', function () {
                        // Create the image object from the img node and pass it to the callback function.
                        callbackFunction(createImageFromImgNode(imgNode));
                    });
                },

                horizontalBlur = function (image, blurWidth) {
                    var blue,
                        blurLength,
                        blurPixelCount,
                        blurX,
                        clone = image.clone(),
                        dimensions = image.dimensions,
                        green,
                        halfBlurWidth = Math.floor(blurWidth / 2),
                        height = dimensions[1],
                        pixelIndex = 0,
                        red,
                        width = dimensions[0],
                        x,
                        y;

                    // Loop through the pixels of the image.  Start at the upper left corner and travel to the right.
                    // When the right edge of the image is reached, move down a row and start over from the left.
                    for (y = 0; y < height; y += 1) {
                        for (x = 0; x < width; x += 1) {
                            if (x <= halfBlurWidth) {
                                // If we're near the left edge of the image, get the sum of the pixel values
                                // by looping through and adding.
                                red = 0;
                                green = 0;
                                blue = 0;
                                blurPixelCount = 0;

                                // So close to the left edge, there aren't enough pixels to the left to use the full
                                // blur width.  Start at column 0 and continue until we either reach the right side of
                                // blur width or the right side of the image.
                                for (
                                    blurX = 0, blurLength = Math.min(x + halfBlurWidth, width - 1);
                                    blurX <= blurLength;
                                    blurPixelCount += 1, blurX += 1
                                ) {
                                    // Add up the color channel values for each pixel we visit.
                                    // The values are read from a clone of the image.  We can't read the values from
                                    // this image since we are manipulating those values as we loop through them.
                                    red += clone.getValue([
                                        blurX,
                                        y
                                    ], 0);
                                    green += clone.getValue([
                                        blurX,
                                        y
                                    ], 1);
                                    blue += clone.getValue([
                                        blurX,
                                        y
                                    ], 2);
                                }
                            } else {
                                // Since we are not near the left edge, we have access to the sums calculated for the
                                // blur width of the previous pixel.  We can subtract the values from the leftmost pixel.
                                blurX = x - halfBlurWidth - 1;

                                red -= clone.getValue([
                                    blurX,
                                    y
                                ], 0);
                                green -= clone.getValue([
                                    blurX,
                                    y
                                ], 1);
                                blue -= clone.getValue([
                                    blurX,
                                    y
                                ], 2);

                                if (x + halfBlurWidth < width) {
                                    // If we are not near the right edge of the image, we can add the values from the
                                    // next pixel to the right.
                                    blurX = x + halfBlurWidth;

                                    red += clone.getValue([
                                        blurX,
                                        y
                                    ], 0);
                                    green += clone.getValue([
                                        blurX,
                                        y
                                    ], 1);
                                    blue += clone.getValue([
                                        blurX,
                                        y
                                    ], 2);
                                } else {
                                    // As we approach the right edge, there aren't enough pixels to the right to use the
                                    // full blur width.
                                    blurPixelCount -= 1;
                                }
                            }

                            // Take the sum divided by the number of pixels to produce the average.
                            // Set the new pixel values.
                            image
                                .setValue(pixelIndex, 0, Math.round(red / blurPixelCount))
                                .setValue(pixelIndex, 1, Math.round(green / blurPixelCount))
                                .setValue(pixelIndex, 2, Math.round(blue / blurPixelCount));

                            // We are looping through the x,y pixel locations but it is much more efficient to access
                            // values by pixel index instead of by pixel location.  In this case we are looping through
                            // the pixels in the same order they are stored internally, so keeping a simple pixel index
                            // counter improves our efficiency.
                            pixelIndex += 1;
                        }
                    }

                    return image;
                },

                verticalBlur = function (image, blurHeight) {
                    var blue,
                        blurLength,
                        blurPixelCount,
                        blurY,
                        clone = image.clone(),
                        dimensions = image.dimensions,
                        green,
                        halfBlurHeight = Math.floor(blurHeight / 2),
                        height = dimensions[1],
                        pixelCount = image.pixelCount,
                        pixelIndex = 0,
                        red,
                        width = dimensions[0],
                        x,
                        y;

                    // Loop through the pixels of the image.  Start at the upper left corner and travel downward.
                    // When the bottom edge of the image is reached, move to the right one column and start over from
                    // the top.
                    for (x = 0; x < width; x += 1) {
                        for (y = 0; y < height; y += 1) {
                            if (y <= halfBlurHeight) {
                                // If we're near the top edge of the image, get the sum of the pixel values
                                // by looping through and adding.
                                red = 0;
                                green = 0;
                                blue = 0;
                                blurPixelCount = 0;

                                // So close to the top edge, there aren't enough pixels above to use the full blur
                                // height.  Start at row 0 and continue until we either reach the bottom of the blur
                                // height or the bottom edge of the image.
                                for (
                                    blurY = 0, blurLength = Math.min(y + halfBlurHeight, height - 1);
                                    blurY <= blurLength;
                                    blurPixelCount += 1, blurY += 1
                                ) {
                                    // Add up the color channel values for each pixel we visit.
                                    // The values are read from a clone of the image.  We can't read the values from
                                    // this image since we are manipulating those values as we loop through them.
                                    red += clone.getValue([
                                        x,
                                        blurY
                                    ], 0);
                                    green += clone.getValue([
                                        x,
                                        blurY
                                    ], 1);
                                    blue += clone.getValue([
                                        x,
                                        blurY
                                    ], 2);
                                }
                            } else {
                                // Since we are not near the top edge, we have access to the sums calculated for the
                                // blur height of the previous pixel.  We can subtract the values from the topmost
                                // pixel.
                                blurY = y - halfBlurHeight - 1;

                                red -= clone.getValue([
                                    x,
                                    blurY
                                ], 0);
                                green -= clone.getValue([
                                    x,
                                    blurY
                                ], 1);
                                blue -= clone.getValue([
                                    x,
                                    blurY
                                ], 2);

                                if (y + halfBlurHeight < height) {
                                    // If we are not near the bottom edge of the image, we can add the values from the
                                    // next pixel below.
                                    blurY = y + halfBlurHeight;

                                    red += clone.getValue([
                                        x,
                                        blurY
                                    ], 0);
                                    green += clone.getValue([
                                        x,
                                        blurY
                                    ], 1);
                                    blue += clone.getValue([
                                        x,
                                        blurY
                                    ], 2);
                                } else {
                                    // As we approach the bottom edge, there aren't enough pixels below to use the full
                                    // blur width.
                                    blurPixelCount -= 1;
                                }
                            }

                            // Take the sum divided by the number of pixels to produce the average.
                            // Set the new pixel values.
                            image
                                .setValue(pixelIndex, 0, Math.round(red / blurPixelCount))
                                .setValue(pixelIndex, 1, Math.round(green / blurPixelCount))
                                .setValue(pixelIndex, 2, Math.round(blue / blurPixelCount));

                            // We are looping through the x,y pixel locations but it is much more efficient to access
                            // values by pixel index instead of by pixel location.  In this case we are not looping
                            // through the pixels in the same order they are stored internally; even so, it's not too
                            // difficult to keep track of the pixel index to improve our efficiency.
                            pixelIndex += width;

                            if (pixelIndex >= pixelCount) {
                                pixelIndex -= pixelCount - 1;
                            }
                        }
                    }

                    return image;
                },

                boxBlur = function (image, blurWidth, blurHeight) {
                    return horizontalBlur(verticalBlur(image, blurHeight), blurWidth);
                },

                sobelEdgeDetect = function (image) {
                    var blue,
                        blueX,
                        blueY,
                        clone = image.clone(),
                        dimensions = image.dimensions,
                        green,
                        greenX,
                        greenY,
                        height = dimensions[1],
                        heightMinusOne = height - 1,
                        pixelLocation,
                        pixelIndex = 0,
                        red,
                        redX,
                        redY,
                        width = dimensions[0],
                        widthMinusOne = width - 1,
                        x,
                        y;

                    for (y = 0; y < height; y += 1) {
                        for (x = 0; x < width; x += 1) {
                            if (x === 0 || x === widthMinusOne || y === 0 || y === heightMinusOne) {
                                red = 0;
                                green = 0;
                                blue = 0;
                            } else {
                                redX = 0;
                                redY = 0;
                                greenX = 0;
                                greenY = 0;
                                blueX = 0;
                                blueY = 0;

                                pixelLocation = [
                                    x - 1,
                                    y - 1
                                ];

                                red = clone.getValue(pixelLocation, 0);
                                green = clone.getValue(pixelLocation, 1);
                                blue = clone.getValue(pixelLocation, 2);

                                redX -= red;
                                redY += red;
                                greenX -= green;
                                greenY += green;
                                blueX -= blue;
                                blueY += blue;

                                pixelLocation[0] += 1;

                                redY += 2 * clone.getValue(pixelLocation, 0);
                                greenY += 2 * clone.getValue(pixelLocation, 1);
                                blueY += 2 * clone.getValue(pixelLocation, 2);

                                pixelLocation[0] += 1;

                                red = clone.getValue(pixelLocation, 0);
                                green = clone.getValue(pixelLocation, 1);
                                blue = clone.getValue(pixelLocation, 2);

                                redX += red;
                                redY += red;
                                greenX += green;
                                greenY += green;
                                blueX += blue;
                                blueY += blue;

                                pixelLocation[1] += 1;

                                redX += 2 * clone.getValue(pixelLocation, 0);
                                greenX += 2 * clone.getValue(pixelLocation, 1);
                                blueX += 2 * clone.getValue(pixelLocation, 2);

                                pixelLocation[1] += 1;

                                red = clone.getValue(pixelLocation, 0);
                                green = clone.getValue(pixelLocation, 1);
                                blue = clone.getValue(pixelLocation, 2);

                                redX += red;
                                redY -= red;
                                greenX += green;
                                greenY -= green;
                                blueX += blue;
                                blueY -= blue;

                                pixelLocation[0] -= 1;

                                redY -= 2 * clone.getValue(pixelLocation, 0);
                                greenY -= 2 * clone.getValue(pixelLocation, 1);
                                blueY -= 2 * clone.getValue(pixelLocation, 2);

                                pixelLocation[0] -= 1;

                                red = clone.getValue(pixelLocation, 0);
                                green = clone.getValue(pixelLocation, 1);
                                blue = clone.getValue(pixelLocation, 2);

                                redX -= red;
                                redY -= red;
                                greenX -= green;
                                greenY -= green;
                                blueX -= blue;
                                blueY -= blue;

                                pixelLocation[1] -= 1;

                                redX -= 2 * clone.getValue(pixelLocation, 0);
                                greenX -= 2 * clone.getValue(pixelLocation, 1);
                                blueX -= 2 * clone.getValue(pixelLocation, 2);

                                red = Math.sqrt(redX * redX + redY * redY);
                                green = Math.sqrt(greenX * greenX + greenY * greenY);
                                blue = Math.sqrt(blueX * blueX + blueY * blueY);
                            }

                            image
                                .setValue(pixelIndex, 0, Math.max(Math.min(Math.round(red), 255), 0))
                                .setValue(pixelIndex, 1, Math.max(Math.min(Math.round(green), 255), 0))
                                .setValue(pixelIndex, 2, Math.max(Math.min(Math.round(blue), 255), 0));

                            pixelIndex += 1;
                        }
                    }

                    return image;
                };

            // Update the UI to present specific filter options.
            selectFilterNode.on('change', function () {
                var selectedFilter = selectFilterNode.get('value');

                if (selectedFilter === 'Horizontal Blur' || selectedFilter === 'Box Blur') {
                    blurWidthLabelNode.removeClass('display-none');
                    blurWidthNode.removeClass('display-none');
                } else {
                    blurWidthLabelNode.addClass('display-none');
                    blurWidthNode.addClass('display-none');
                }

                if (selectedFilter === 'Vertical Blur' || selectedFilter === 'Box Blur') {
                    blurHeightLabelNode.removeClass('display-none');
                    blurHeightNode.removeClass('display-none');
                } else {
                    blurHeightLabelNode.addClass('display-none');
                    blurHeightNode.addClass('display-none');
                }
            });

            // Handle clear images button click.
            Y.one('#clearImagesButton').on('click', function () {
                displayNode.empty();
            });

            // Handle render image button click.
            Y.one('#renderImageButton').on('click', function () {
                // Gather values from the form.
                var blurHeight = +blurHeightNode.get('value'),
                    blurWidth = +blurWidthNode.get('value'),
                    selectedFilter = selectFilterNode.get('value'),
                    selectedImage = selectImageNode.get('value'),

                    // Create the node where the image will be rendered.
                    resultNode = Y.Node.create(
                        '<div class="yui3-u">' +
                            '<h3>' +
                                selectedFilter +
                            '</h3>' +
                            '<h6>' +
                                selectedImage +
                            '</h6>' +
                            '<p class="loading">' +
                                'Loading&hellip;' +
                            '</p>' +
                        '</div>'
                    ).appendTo(displayNode);

                // Create an instance of Y.Canvas.Image.
                createImageFromUrl('{{componentAssets}}/' + selectedImage, function (image) {
                    // Apply the appropriate filter.
                    switch (selectedFilter) {
                        case 'Horizontal Blur':
                            horizontalBlur(image, blurWidth);
                            break;
                        case 'Vertical Blur':
                            verticalBlur(image, blurHeight);
                            break;
                        case 'Box Blur':
                            boxBlur(image, blurWidth, blurHeight);
                            break;
                        case 'Sobel Edge Detect':
                            sobelEdgeDetect(image);
                            break;
                    }

                    // Remove the loading text and render the image to a canvas.
                    resultNode.one('.loading').replace(image.toCanvas());
                });
            });
        }
    );
</script>