diff --git a/lib/OpenLayers/Layer/Bing.js b/lib/OpenLayers/Layer/Bing.js index a33202a724..703c2b5e43 100644 --- a/lib/OpenLayers/Layer/Bing.js +++ b/lib/OpenLayers/Layer/Bing.js @@ -107,6 +107,9 @@ OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { var newArgs = [name, null, options]; OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs); + this.tileOptions = OpenLayers.Util.extend({ + crossOriginKeyword: 'anonymous' + }, this.options.tileOptions); this.loadMetadata(); }, diff --git a/lib/OpenLayers/Layer/OSM.js b/lib/OpenLayers/Layer/OSM.js index 320c4540ef..69f411756b 100644 --- a/lib/OpenLayers/Layer/OSM.js +++ b/lib/OpenLayers/Layer/OSM.js @@ -73,6 +73,12 @@ OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { * layer option can be set in this object (e.g. * ). */ + initialize: function(name, url, options) { + OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); + this.tileOptions = OpenLayers.Util.extend({ + crossOriginKeyword: 'anonymous' + }, this.options && this.options.tileOptions); + }, /** * Method: clone diff --git a/lib/OpenLayers/Renderer/Canvas.js b/lib/OpenLayers/Renderer/Canvas.js index 4edaf92a7b..daf9e19e35 100644 --- a/lib/OpenLayers/Renderer/Canvas.js +++ b/lib/OpenLayers/Renderer/Canvas.js @@ -110,8 +110,7 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { * {Boolean} Whether or not the browser supports the renderer class */ supported: function() { - var canvas = document.createElement("canvas"); - return !!canvas.getContext; + return OpenLayers.CANVAS_SUPPORTED; }, /** diff --git a/lib/OpenLayers/Tile/Image.js b/lib/OpenLayers/Tile/Image.js index 23a7c9668a..dfbf1bda43 100644 --- a/lib/OpenLayers/Tile/Image.js +++ b/lib/OpenLayers/Tile/Image.js @@ -81,6 +81,23 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { */ maxGetUrlLength: null, + /** + * Property: canvasContext + * {CanvasRenderingContext2D} A canvas context associated with + * the tile image. + */ + canvasContext: null, + + /** + * APIProperty: crossOriginKeyword + * The value of the crossorigin keyword to use when loading images. This is + * only relevant when using for tiles from remote + * origins and should be set to either 'anonymous' or 'use-credentials' + * for servers that send Access-Control-Allow-Origin headers with their + * tiles. + */ + crossOriginKeyword: null, + /** TBD 3.0 - reorder the parameters to the init function to remove * URL. the getUrl() function on the layer gets called on * each draw(), so no need to specify it here. @@ -216,6 +233,7 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { } OpenLayers.Element.removeClass(img, "olImageLoadError"); } + this.canvasContext = null; }, /** @@ -297,6 +315,9 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { } else { OpenLayers.Event.observe(img, "load", load); OpenLayers.Event.observe(img, "error", load); + if (img.crossOrigin) { + img.crossOrigin = null; + } img.src = this.blankImageUrl; } } @@ -314,6 +335,10 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { img.style.visibility = 'hidden'; img.style.opacity = 0; if (url) { + // don't set crossOrigin if the url is a data URL + if (this.crossOriginKeyword && url.substr(0, 5 !== 'data:')) { + img.crossOrigin = this.crossOriginKeyword; + } img.src = url; } }, @@ -366,6 +391,7 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { img.style.opacity = this.layer.opacity; this.isLoading = false; + this.canvasContext = null; this.events.triggerEvent("loadend"); // IE<7 needs a reflow when the tiles are loaded because of the @@ -406,6 +432,38 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { } }, + /** + * APIMethod: getCanvasContext + * Returns a canvas context associated with the tile image (with + * the image drawn on it). + * Returns undefined if the browser does not support canvas, if + * the tile has no image or if it's currently loading. + * + * The function returns a canvas context instance but the + * underlying canvas is still available in the 'canvas' property: + * (code) + * var context = tile.getCanvasContext(); + * if (context) { + * var data = context.canvas.toDataURL('image/jpeg'); + * } + * (end) + * + * Returns: + * {Boolean} + */ + getCanvasContext: function() { + if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) { + if (!this.canvasContext) { + var canvas = document.createElement("canvas"); + canvas.width = this.size.w; + canvas.height = this.size.h; + this.canvasContext = canvas.getContext("2d"); + this.canvasContext.drawImage(this.imgDiv, 0, 0); + } + return this.canvasContext; + } + }, + CLASS_NAME: "OpenLayers.Tile.Image" }); diff --git a/lib/OpenLayers/Util.js b/lib/OpenLayers/Util.js index 2458a54b67..5ca8c35201 100644 --- a/lib/OpenLayers/Util.js +++ b/lib/OpenLayers/Util.js @@ -1435,6 +1435,15 @@ OpenLayers.IS_GECKO = (function() { return ua.indexOf("webkit") == -1 && ua.indexOf("gecko") != -1; })(); +/** + * Constant: CANVAS_SUPPORTED + * {Boolean} True if canvas 2d is supported. + */ +OpenLayers.CANVAS_SUPPORTED = (function() { + var elem = document.createElement('canvas'); + return !!(elem.getContext && elem.getContext('2d')); +})(); + /** * Constant: BROWSER_NAME * {String} diff --git a/tests/Tile/Image.html b/tests/Tile/Image.html index 0b42064265..f202f6dfe1 100644 --- a/tests/Tile/Image.html +++ b/tests/Tile/Image.html @@ -399,6 +399,31 @@ map.destroy(); }); } + + function test_getCanvasContext(t) { + if (!OpenLayers.CANVAS_SUPPORTED) { + t.plan(0); + } else { + t.plan(1); + + var map = new OpenLayers.Map('map'); + var layer = new OpenLayers.Layer.WMS("OpenLayers WMS", + "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); + map.addLayer(layer); + map.setCenter(new OpenLayers.LonLat(0, 0), 5); + + t.delay_call(5, function() { + var tile = layer.grid[0][0]; + if (tile.isLoading) { + t.ok(false, "test_getCanvasContext timeout"); + } else { + t.ok(tile.getCanvasContext() instanceof CanvasRenderingContext2D, + "getCanvasContext() returns CanvasRenderingContext2D instance"); + } + map.destroy(); + }); + } + }