/* This file contains proprietary software owned by Motorola Mobility, Inc.
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved.
*/ var Montage = require("montage").Montage; var Component = require("ui/component").Component; var dom = require("ui/dom"); var Point = require("core/geometry/point").Point; var Effect = require("effect/effect").Effect; var DesaturateEffect = require("effect/desaturate-effect").DesaturateEffect; var InvertEffect = require("effect/invert-effect").InvertEffect; var SepiaEffect = require("effect/sepia-effect").SepiaEffect; var MultiplyEffect = require("effect/multiply-effect").MultiplyEffect; exports.PhotoEditor = Montage.create(Component, { __image: { enumerable: false, value: null }, _image: { enumerable: false, get: function() { return this.__image; }, set: function(value) { if (this.__image === value) { return; } if (this.__image) { this.__image.element.removeEventListener("load", this, false); } this.__image = value; this.__image.element.identifier = "editorImage"; if (this.__image) { this.__image.element.addEventListener("load", this, false); } } }, _canvas: { enumerable: false, value: null }, _pointerIdentifier: { enumerable: false, value: null }, prepareForActivationEvents: { value: function() { if (window.Touch) { this._canvas.addEventListener("touchstart", this, false); } else { this._canvas.addEventListener("mousedown", this, false); } } }, handleMousedown: { value: function(event) { event.preventDefault(); this._pointerIdentifier = "mouse"; this._canvas.addEventListener("mousemove", this, false); document.addEventListener("mouseup", this, false); this._pickColor(event.clientX, event.clientY); this.needsDraw = true; } }, handleTouchstart: { value: function(event) { if (this._pointerIdentifier) { return; } event.preventDefault(); var pickTouch = event.changedTouches[0]; this._pointerIdentifier = pickTouch.identifier; this._canvas.addEventListener("touchmove", this, false); document.addEventListener("touchend", this, false); document.addEventListener("touchcancel", this, false); this._pickColor(pickTouch.clientX, pickTouch.clientY); } }, handleMouseup: { value: function() { this._pointerIdentifier = null; this._canvas.removeEventListener("mousemove", this, false); document.removeEventListener("mouseup", this, false); var colorPickEvent = document.createEvent("CustomEvent"); colorPickEvent.initCustomEvent("colorpickend", true, true, null); document.application.dispatchEvent(colorPickEvent); this.needsDraw = true; } }, handleMousemove: { enumerable: false, value: function(event) { if (!this._pointerIdentifier) { return; } this._pickColor(event.clientX, event.clientY); } }, handleTouchmove: { enumerable: false, value: function(event) { var i = 0, iTouch, foundTouch = null for(; (iTouch = event.changedTouches[i]); i++) { if (iTouch.identifier === this._pointerIdentifier) { foundTouch = iTouch; break; } } if (!foundTouch) { return; } this._pickColor(foundTouch.clientX, foundTouch.clientY); } }, handleTouchend: { value: function() { var i = 0, iTouch, foundTouch = null for(; (iTouch = event.changedTouches[i]); i++) { if (iTouch.identifier === this._pointerIdentifier) { foundTouch = iTouch; break; } } if (!foundTouch) { return; } this._pointerIdentifier = null; var colorPickEvent = document.createEvent("CustomEvent"); colorPickEvent.initCustomEvent("colorpickend", true, true, null); document.application.dispatchEvent(colorPickEvent); } }, _pickColor: { value: function(x, y) { var gridExtent = 20, halfGridExtent = 10, canvas = this._canvas, context = canvas.getContext('2d'), canvasPoint = dom.convertPointFromPageToNode(canvas, Point.create().init(x, y)), pickedPixel = context.getImageData(canvasPoint.x, canvasPoint.y, 1, 1), focusGrid = context.getImageData(canvasPoint.x - halfGridExtent, canvasPoint.y - halfGridExtent, gridExtent, gridExtent), colorPickEvent; colorPickEvent = document.createEvent("CustomEvent"); colorPickEvent.initCustomEvent("colorpick", true, true, null); colorPickEvent.color = pickedPixel.data; colorPickEvent.focusGrid = focusGrid; colorPickEvent.clientX = x; colorPickEvent.clientY = y; colorPickEvent.canvasX = canvasPoint.x; colorPickEvent.canvasY = canvasPoint.y; document.application.dispatchEvent(colorPickEvent); } }, _src: { enumerable: false, value: null }, src: { enumerable: false, get: function() { return this._src; }, set: function(value) { if (value === this._src) { return; } this._src = value; this._needToRefreshImageData = true; } }, handleEditorImageLoad: { enumerable: false, value: function(event) { this.needsDraw = true; } }, _width: { enumerable: false, value: null }, _height: { enumerable: false, value: null }, willDraw: { value: function() { this._width = this._image.element.offsetWidth; this._height = this._image.element.offsetHeight; } }, // TODO Eventually we need to maintain a stack of effects to apply to the image inside the editor // I don't want to complicate this for the demo right now though _inverted: { enumerable: false, value: false }, inverted: { enumerable: false, get: function() { return this._inverted; }, set: function(value) { if (value === this._inverted) { return; } this._inverted = value; this.needsDraw = true; } }, _desaturated: { enumerable: false, value: false }, desaturated: { enumerable: false, get: function() { return this._desaturated; }, set: function(value) { if (value === this._desaturated) { return; } this._desaturated = value; this.needsDraw = true; } }, _sepiaToned: { enumerable: false, value: false }, sepia: { enumerable: false, get: function() { return this._sepiaToned; }, set: function(value) { if (value === this._sepiaToned) { return; } this._sepiaToned = value; this.needsDraw = true; } }, _multiplyEffect: { enumerable: false, value: false }, multiply: { enumerable: false, get: function() { return this._multiplyEffect; }, set: function(value) { if (value === this._multiplyEffect) { return; } this._multiplyEffect = value; this.needsDraw = true; } }, _multiplyMultiplier: { enumerable: false, value: 1 }, multiplier: { enumerable: false, get: function() { return this._multiplyMultiplier; }, set: function(value) { if (value === this._multiplyMultiplier) { return; } this._multiplyMultiplier = value; if (this._multiplyEffect) { this.needsDraw = true; } } }, prepareForDraw: { value: function() { // TODO this is a workaround for a problem with our deserialization in iOS concerning // canvas elements. Debugging points to some issue with adoptNode. Either way, // if we don't do this it takes two draw cycles to actually get the canvas rendering. var newCanvas = this._canvas.cloneNode(true); this.element.replaceChild(newCanvas, this._canvas); this._canvas = newCanvas; } }, draw: { value: function() { // Don't draw unless we have something to actually draw if (!this._width || !this._height) { return; } // TODO should only draw the canvas if the canvas data is dirty // flipping classnames should be cheap if (this._pointerIdentifier) { this.element.classList.add("pickingColor"); } else { this.element.classList.remove("pickingColor"); } var canvas = this._canvas, image = this._image.element, context; canvas.width = this._width; canvas.height = this._height; context = canvas.getContext('2d'); context.drawImage(image, 0, 0); var imgd = context.getImageData(0, 0, this._width, this._height), pixels = imgd.data, pixelCount = pixels.length; if (this.inverted) { InvertEffect.applyEffect(pixels, pixelCount); } if (this.desaturated) { DesaturateEffect.applyEffect(pixels, pixelCount); } if (this.sepia) { SepiaEffect.applyEffect(pixels, pixelCount); } if (this.multiply) { MultiplyEffect.applyEffect(pixels, pixelCount, this.multiplier); } context.putImageData(imgd, 0, 0); } } });