From d4a5b2f48e0bf5236d15c5242eae8671daa30c85 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 9 Aug 2016 00:23:27 -0600 Subject: [PATCH] Separate out ol.style.IconImage --- src/ol/style/icon.js | 285 +------------------------------- src/ol/style/iconimage.js | 279 +++++++++++++++++++++++++++++++ test/spec/ol/style/icon.test.js | 15 +- 3 files changed, 293 insertions(+), 286 deletions(-) create mode 100644 src/ol/style/iconimage.js diff --git a/src/ol/style/icon.js b/src/ol/style/icon.js index 9483d44689..e1088b6892 100644 --- a/src/ol/style/icon.js +++ b/src/ol/style/icon.js @@ -3,11 +3,10 @@ goog.provide('ol.style.IconAnchorUnits'); goog.provide('ol.style.IconOrigin'); goog.provide('ol.style.iconImageCache'); +goog.require('ol'); goog.require('ol.events'); -goog.require('ol.events.EventTarget'); goog.require('ol.events.EventType'); goog.require('ol.color'); -goog.require('ol.dom'); goog.require('ol.style.Image'); goog.require('ol.style.ImageState'); @@ -126,9 +125,9 @@ ol.style.Icon = function(opt_options) { /** * @private - * @type {ol.style.IconImage_} + * @type {ol.style.IconImage} */ - this.iconImage_ = ol.style.IconImage_.get( + this.iconImage_ = ol.style.IconImage.get( image, /** @type {string} */ (src), imgSize, crossOrigin, imageState, color); /** @@ -364,278 +363,6 @@ ol.style.Icon.prototype.unlistenImageChange = function(listener, thisArg) { }; -/** - * @constructor - * @param {Image|HTMLCanvasElement} image Image. - * @param {string|undefined} src Src. - * @param {ol.Size} size Size. - * @param {?string} crossOrigin Cross origin. - * @param {ol.style.ImageState} imageState Image state. - * @param {ol.Color} color Color. - * @extends {ol.events.EventTarget} - * @private - */ -ol.style.IconImage_ = function(image, src, size, crossOrigin, imageState, - color) { - - ol.events.EventTarget.call(this); - - /** - * @private - * @type {Image|HTMLCanvasElement} - */ - this.hitDetectionImage_ = null; - - /** - * @private - * @type {Image|HTMLCanvasElement} - */ - this.image_ = !image ? new Image() : image; - - if (crossOrigin !== null) { - this.image_.crossOrigin = crossOrigin; - } - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = color ? - /** @type {HTMLCanvasElement} */ (document.createElement('CANVAS')) : - null; - - /** - * @private - * @type {ol.Color} - */ - this.color_ = color; - - /** - * @private - * @type {Array.} - */ - this.imageListenerKeys_ = null; - - /** - * @private - * @type {ol.style.ImageState} - */ - this.imageState_ = imageState; - - /** - * @private - * @type {ol.Size} - */ - this.size_ = size; - - /** - * @private - * @type {string|undefined} - */ - this.src_ = src; - - /** - * @private - * @type {boolean} - */ - this.tainting_ = false; - if (this.imageState_ == ol.style.ImageState.LOADED) { - this.determineTainting_(); - } - -}; -ol.inherits(ol.style.IconImage_, ol.events.EventTarget); - - -/** - * @param {Image|HTMLCanvasElement} image Image. - * @param {string} src Src. - * @param {ol.Size} size Size. - * @param {?string} crossOrigin Cross origin. - * @param {ol.style.ImageState} imageState Image state. - * @param {ol.Color} color Color. - * @return {ol.style.IconImage_} Icon image. - */ -ol.style.IconImage_.get = function(image, src, size, crossOrigin, imageState, - color) { - var iconImageCache = ol.style.iconImageCache; - var iconImage = iconImageCache.get(src, crossOrigin, color); - if (!iconImage) { - iconImage = new ol.style.IconImage_( - image, src, size, crossOrigin, imageState, color); - iconImageCache.set(src, crossOrigin, color, iconImage); - } - return iconImage; -}; - - -/** - * @private - */ -ol.style.IconImage_.prototype.determineTainting_ = function() { - var context = ol.dom.createCanvasContext2D(1, 1); - try { - context.drawImage(this.image_, 0, 0); - context.getImageData(0, 0, 1, 1); - } catch (e) { - this.tainting_ = true; - } -}; - - -/** - * @private - */ -ol.style.IconImage_.prototype.dispatchChangeEvent_ = function() { - this.dispatchEvent(ol.events.EventType.CHANGE); -}; - - -/** - * @private - */ -ol.style.IconImage_.prototype.handleImageError_ = function() { - this.imageState_ = ol.style.ImageState.ERROR; - this.unlistenImage_(); - this.dispatchChangeEvent_(); -}; - - -/** - * @private - */ -ol.style.IconImage_.prototype.handleImageLoad_ = function() { - this.imageState_ = ol.style.ImageState.LOADED; - if (this.size_) { - this.image_.width = this.size_[0]; - this.image_.height = this.size_[1]; - } - this.size_ = [this.image_.width, this.image_.height]; - this.unlistenImage_(); - this.determineTainting_(); - this.replaceColor_(); - this.dispatchChangeEvent_(); -}; - - -/** - * @param {number} pixelRatio Pixel ratio. - * @return {Image|HTMLCanvasElement} Image or Canvas element. - */ -ol.style.IconImage_.prototype.getImage = function(pixelRatio) { - return this.canvas_ ? this.canvas_ : this.image_; -}; - - -/** - * @return {ol.style.ImageState} Image state. - */ -ol.style.IconImage_.prototype.getImageState = function() { - return this.imageState_; -}; - - -/** - * @param {number} pixelRatio Pixel ratio. - * @return {Image|HTMLCanvasElement} Image element. - */ -ol.style.IconImage_.prototype.getHitDetectionImage = function(pixelRatio) { - if (!this.hitDetectionImage_) { - if (this.tainting_) { - var width = this.size_[0]; - var height = this.size_[1]; - var context = ol.dom.createCanvasContext2D(width, height); - context.fillRect(0, 0, width, height); - this.hitDetectionImage_ = context.canvas; - } else { - this.hitDetectionImage_ = this.image_; - } - } - return this.hitDetectionImage_; -}; - - -/** - * @return {ol.Size} Image size. - */ -ol.style.IconImage_.prototype.getSize = function() { - return this.size_; -}; - - -/** - * @return {string|undefined} Image src. - */ -ol.style.IconImage_.prototype.getSrc = function() { - return this.src_; -}; - - -/** - * Load not yet loaded URI. - */ -ol.style.IconImage_.prototype.load = function() { - if (this.imageState_ == ol.style.ImageState.IDLE) { - goog.DEBUG && console.assert(this.src_ !== undefined, - 'this.src_ must not be undefined'); - goog.DEBUG && console.assert(!this.imageListenerKeys_, - 'no listener keys existing'); - this.imageState_ = ol.style.ImageState.LOADING; - this.imageListenerKeys_ = [ - ol.events.listenOnce(this.image_, ol.events.EventType.ERROR, - this.handleImageError_, this), - ol.events.listenOnce(this.image_, ol.events.EventType.LOAD, - this.handleImageLoad_, this) - ]; - try { - this.image_.src = this.src_; - } catch (e) { - this.handleImageError_(); - } - } -}; - - -/** - * @private - */ -ol.style.IconImage_.prototype.replaceColor_ = function() { - if (this.tainting_ || this.color_ === null) { - return; - } - - this.canvas_.width = this.image_.width; - this.canvas_.height = this.image_.height; - - var ctx = this.canvas_.getContext('2d'); - ctx.drawImage(this.image_, 0, 0); - - var imgData = ctx.getImageData(0, 0, this.image_.width, this.image_.height); - var data = imgData.data; - var r = this.color_[0] / 255.0; - var g = this.color_[1] / 255.0; - var b = this.color_[2] / 255.0; - - for (var i = 0, ii = data.length; i < ii; i += 4) { - data[i] *= r; - data[i + 1] *= g; - data[i + 2] *= b; - } - ctx.putImageData(imgData, 0, 0); -}; - - -/** - * Discards event handlers which listen for load completion or errors. - * - * @private - */ -ol.style.IconImage_.prototype.unlistenImage_ = function() { - this.imageListenerKeys_.forEach(ol.events.unlistenByKey); - this.imageListenerKeys_ = null; -}; - - /** * @constructor * @private @@ -643,7 +370,7 @@ ol.style.IconImage_.prototype.unlistenImage_ = function() { ol.style.IconImageCache_ = function() { /** - * @type {Object.} + * @type {Object.} * @private */ this.cache_ = {}; @@ -708,7 +435,7 @@ ol.style.IconImageCache_.prototype.expire = function() { * @param {string} src Src. * @param {?string} crossOrigin Cross origin. * @param {ol.Color} color Color. - * @return {ol.style.IconImage_} Icon image. + * @return {ol.style.IconImage} Icon image. */ ol.style.IconImageCache_.prototype.get = function(src, crossOrigin, color) { var key = ol.style.IconImageCache_.getKey(src, crossOrigin, color); @@ -720,7 +447,7 @@ ol.style.IconImageCache_.prototype.get = function(src, crossOrigin, color) { * @param {string} src Src. * @param {?string} crossOrigin Cross origin. * @param {ol.Color} color Color. - * @param {ol.style.IconImage_} iconImage Icon image. + * @param {ol.style.IconImage} iconImage Icon image. */ ol.style.IconImageCache_.prototype.set = function(src, crossOrigin, color, iconImage) { diff --git a/src/ol/style/iconimage.js b/src/ol/style/iconimage.js new file mode 100644 index 0000000000..838aa198e5 --- /dev/null +++ b/src/ol/style/iconimage.js @@ -0,0 +1,279 @@ +goog.provide('ol.style.IconImage'); + +goog.require('ol'); +goog.require('ol.dom'); +goog.require('ol.events'); +goog.require('ol.events.EventTarget'); +goog.require('ol.events.EventType'); +goog.require('ol.style.ImageState'); + + +/** + * @constructor + * @param {Image|HTMLCanvasElement} image Image. + * @param {string|undefined} src Src. + * @param {ol.Size} size Size. + * @param {?string} crossOrigin Cross origin. + * @param {ol.style.ImageState} imageState Image state. + * @param {ol.Color} color Color. + * @extends {ol.events.EventTarget} + */ +ol.style.IconImage = function(image, src, size, crossOrigin, imageState, + color) { + + ol.events.EventTarget.call(this); + + /** + * @private + * @type {Image|HTMLCanvasElement} + */ + this.hitDetectionImage_ = null; + + /** + * @private + * @type {Image|HTMLCanvasElement} + */ + this.image_ = !image ? new Image() : image; + + if (crossOrigin !== null) { + this.image_.crossOrigin = crossOrigin; + } + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = color ? + /** @type {HTMLCanvasElement} */ (document.createElement('CANVAS')) : + null; + + /** + * @private + * @type {ol.Color} + */ + this.color_ = color; + + /** + * @private + * @type {Array.} + */ + this.imageListenerKeys_ = null; + + /** + * @private + * @type {ol.style.ImageState} + */ + this.imageState_ = imageState; + + /** + * @private + * @type {ol.Size} + */ + this.size_ = size; + + /** + * @private + * @type {string|undefined} + */ + this.src_ = src; + + /** + * @private + * @type {boolean} + */ + this.tainting_ = false; + if (this.imageState_ == ol.style.ImageState.LOADED) { + this.determineTainting_(); + } + +}; +ol.inherits(ol.style.IconImage, ol.events.EventTarget); + + +/** + * @param {Image|HTMLCanvasElement} image Image. + * @param {string} src Src. + * @param {ol.Size} size Size. + * @param {?string} crossOrigin Cross origin. + * @param {ol.style.ImageState} imageState Image state. + * @param {ol.Color} color Color. + * @return {ol.style.IconImage} Icon image. + */ +ol.style.IconImage.get = function(image, src, size, crossOrigin, imageState, + color) { + var iconImageCache = ol.style.iconImageCache; + var iconImage = iconImageCache.get(src, crossOrigin, color); + if (!iconImage) { + iconImage = new ol.style.IconImage( + image, src, size, crossOrigin, imageState, color); + iconImageCache.set(src, crossOrigin, color, iconImage); + } + return iconImage; +}; + + +/** + * @private + */ +ol.style.IconImage.prototype.determineTainting_ = function() { + var context = ol.dom.createCanvasContext2D(1, 1); + try { + context.drawImage(this.image_, 0, 0); + context.getImageData(0, 0, 1, 1); + } catch (e) { + this.tainting_ = true; + } +}; + + +/** + * @private + */ +ol.style.IconImage.prototype.dispatchChangeEvent_ = function() { + this.dispatchEvent(ol.events.EventType.CHANGE); +}; + + +/** + * @private + */ +ol.style.IconImage.prototype.handleImageError_ = function() { + this.imageState_ = ol.style.ImageState.ERROR; + this.unlistenImage_(); + this.dispatchChangeEvent_(); +}; + + +/** + * @private + */ +ol.style.IconImage.prototype.handleImageLoad_ = function() { + this.imageState_ = ol.style.ImageState.LOADED; + if (this.size_) { + this.image_.width = this.size_[0]; + this.image_.height = this.size_[1]; + } + this.size_ = [this.image_.width, this.image_.height]; + this.unlistenImage_(); + this.determineTainting_(); + this.replaceColor_(); + this.dispatchChangeEvent_(); +}; + + +/** + * @param {number} pixelRatio Pixel ratio. + * @return {Image|HTMLCanvasElement} Image or Canvas element. + */ +ol.style.IconImage.prototype.getImage = function(pixelRatio) { + return this.canvas_ ? this.canvas_ : this.image_; +}; + + +/** + * @return {ol.style.ImageState} Image state. + */ +ol.style.IconImage.prototype.getImageState = function() { + return this.imageState_; +}; + + +/** + * @param {number} pixelRatio Pixel ratio. + * @return {Image|HTMLCanvasElement} Image element. + */ +ol.style.IconImage.prototype.getHitDetectionImage = function(pixelRatio) { + if (!this.hitDetectionImage_) { + if (this.tainting_) { + var width = this.size_[0]; + var height = this.size_[1]; + var context = ol.dom.createCanvasContext2D(width, height); + context.fillRect(0, 0, width, height); + this.hitDetectionImage_ = context.canvas; + } else { + this.hitDetectionImage_ = this.image_; + } + } + return this.hitDetectionImage_; +}; + + +/** + * @return {ol.Size} Image size. + */ +ol.style.IconImage.prototype.getSize = function() { + return this.size_; +}; + + +/** + * @return {string|undefined} Image src. + */ +ol.style.IconImage.prototype.getSrc = function() { + return this.src_; +}; + + +/** + * Load not yet loaded URI. + */ +ol.style.IconImage.prototype.load = function() { + if (this.imageState_ == ol.style.ImageState.IDLE) { + goog.DEBUG && console.assert(this.src_ !== undefined, + 'this.src_ must not be undefined'); + goog.DEBUG && console.assert(!this.imageListenerKeys_, + 'no listener keys existing'); + this.imageState_ = ol.style.ImageState.LOADING; + this.imageListenerKeys_ = [ + ol.events.listenOnce(this.image_, ol.events.EventType.ERROR, + this.handleImageError_, this), + ol.events.listenOnce(this.image_, ol.events.EventType.LOAD, + this.handleImageLoad_, this) + ]; + try { + this.image_.src = this.src_; + } catch (e) { + this.handleImageError_(); + } + } +}; + + +/** + * @private + */ +ol.style.IconImage.prototype.replaceColor_ = function() { + if (this.tainting_ || this.color_ === null) { + return; + } + + this.canvas_.width = this.image_.width; + this.canvas_.height = this.image_.height; + + var ctx = this.canvas_.getContext('2d'); + ctx.drawImage(this.image_, 0, 0); + + var imgData = ctx.getImageData(0, 0, this.image_.width, this.image_.height); + var data = imgData.data; + var r = this.color_[0] / 255.0; + var g = this.color_[1] / 255.0; + var b = this.color_[2] / 255.0; + + for (var i = 0, ii = data.length; i < ii; i += 4) { + data[i] *= r; + data[i + 1] *= g; + data[i + 2] *= b; + } + ctx.putImageData(imgData, 0, 0); +}; + + +/** + * Discards event handlers which listen for load completion or errors. + * + * @private + */ +ol.style.IconImage.prototype.unlistenImage_ = function() { + this.imageListenerKeys_.forEach(ol.events.unlistenByKey); + this.imageListenerKeys_ = null; +}; diff --git a/test/spec/ol/style/icon.test.js b/test/spec/ol/style/icon.test.js index d3e6c07c8a..b2ec97dcf5 100644 --- a/test/spec/ol/style/icon.test.js +++ b/test/spec/ol/style/icon.test.js @@ -1,8 +1,9 @@ goog.provide('ol.test.style.Icon'); goog.provide('ol.test.style.IconImageCache'); +goog.require('ol'); goog.require('ol.events'); -goog.require('ol.style.iconImageCache'); +goog.require('ol.style.IconImage'); goog.require('ol.style.Icon'); @@ -19,7 +20,7 @@ describe('ol.style.Icon', function() { img: canvas, imgSize: size }); - expect(ol.style.IconImage_.get( + expect(ol.style.IconImage.get( canvas, ol.getUid(canvas), size, '').getImage()).to.eql(canvas); }); @@ -147,7 +148,7 @@ describe('ol.style.Icon', function() { // this image will be used for the icon. var cache = ol.style.iconImageCache; var src = 'test.png'; - var iconImage = new ol.style.IconImage_(null, 'test.png', imgSize); + var iconImage = new ol.style.IconImage(null, 'test.png', imgSize); cache.set(src, null, null, iconImage); var iconStyle = new ol.style.Icon({ @@ -190,7 +191,7 @@ describe('ol.style.IconImageCache', function() { for (i = 0; i < 4; ++i) { src = i + ''; - iconImage = new ol.style.IconImage_(src, null); + iconImage = new ol.style.IconImage(src, null); cache.set(src, null, null, iconImage); } @@ -200,7 +201,7 @@ describe('ol.style.IconImageCache', function() { expect(cache.cacheSize_).to.eql(4); src = '4'; - iconImage = new ol.style.IconImage_(src, null); + iconImage = new ol.style.IconImage(src, null); cache.set(src, null, null, iconImage); expect(cache.cacheSize_).to.eql(5); @@ -208,14 +209,14 @@ describe('ol.style.IconImageCache', function() { expect(cache.cacheSize_).to.eql(3); src = '0'; - iconImage = new ol.style.IconImage_(src, null); + iconImage = new ol.style.IconImage(src, null); ol.events.listen(iconImage, 'change', ol.nullFunction, false); cache.set(src, null, null, iconImage); expect(cache.cacheSize_).to.eql(4); src = '4'; - iconImage = new ol.style.IconImage_(src, null); + iconImage = new ol.style.IconImage(src, null); ol.events.listen(iconImage, 'change', ol.nullFunction, false); cache.set(src, null, null, iconImage);