diff --git a/examples/quads/main.js b/examples/quads/main.js index b5d99ca87c..0d14c58175 100644 --- a/examples/quads/main.js +++ b/examples/quads/main.js @@ -1,4 +1,4 @@ -/* globals $, geo, utils */ +/* globals utils */ var quadDebug = {}; @@ -85,6 +85,11 @@ $(function () { ur: {x: -138, y: 39}, opacity: 0.25, color: '#0000FF' + }, { + ll: {x: -88, y: 49}, + ur: {x: -58, y: 63}, + image: 'flower1.jpg', + crop: {left: 10, right: 240, top: 20, bottom: 260} }]; if (query.canvas === 'true') { // You can render a canvas on a quad, but only on the canvas and webgl diff --git a/src/canvas/quadFeature.js b/src/canvas/quadFeature.js index 0fb76c8b4b..d7e600d573 100644 --- a/src/canvas/quadFeature.js +++ b/src/canvas/quadFeature.js @@ -174,10 +174,17 @@ var canvas_quadFeature = function (arg) { if (!quad.crop) { context2d.drawImage(src, 0, 0); } else { - var cropx = Math.min(w, quad.crop.x), - cropy = Math.min(h, quad.crop.y); - if (cropx > 0 && cropy > 0) { - context2d.drawImage(src, 0, 0, cropx, cropy, 0, 0, cropx, cropy); + const cropw = Math.min(w, quad.crop.x || w), + croph = Math.min(h, quad.crop.y || h), + cropx0 = Math.max(0, quad.crop.left || 0), + cropy0 = Math.max(0, quad.crop.top || 0), + cropx1 = Math.min(w, quad.crop.right || w), + cropy1 = Math.min(h, quad.crop.bottom || h); + if (w && h && cropw > 0 && croph > 0 && cropx1 > cropx0 && cropy1 > cropy0) { + context2d.drawImage( + src, + cropx0, cropy0, Math.floor((cropx1 - cropx0) * cropw / w), Math.floor((cropy1 - cropy0) * croph / h), + 0, 0, cropw, croph); } } }); diff --git a/src/quadFeature.js b/src/quadFeature.js index fe76f2ab86..b7d90f14f5 100644 --- a/src/quadFeature.js +++ b/src/quadFeature.js @@ -10,6 +10,17 @@ var feature = require('./feature'); * @property {geo.geoPosition} [ur] Upper right coordinate. * @property {geo.geoPosition} [ll] Lower left coordinate. * @property {geo.geoPosition} [lr] Lower right coordinate. + * @property {object} [crop] Image tile crop size in image pixels. Areas + * beyond the width ``x`` and height ``y`` are transparent. ``left``, + * ``top``, ``right``, ``bottom`` extract a specific part of the image tile + * as the source and expand it to fill the conceptual space before any crop + * width and height are applied. + * @property {number} [crop.x] Width of image after crop. + * @property {number} [crop.y] Height of image after crop. + * @property {number} [crop.left] Left coordinate of image source. + * @property {number} [crop.top] Top coordinate of image source. + * @property {number} [crop.right] Right coordinate of image source. + * @property {number} [crop.bottom] Bottom coordinate of image source. */ /** diff --git a/src/webgl/quadFeature.js b/src/webgl/quadFeature.js index b080fcd53d..54221ac146 100644 --- a/src/webgl/quadFeature.js +++ b/src/webgl/quadFeature.js @@ -158,7 +158,7 @@ var webgl_quadFeature = function (arg) { * Build this feature. */ this._build = function () { - var mapper, mat, prog, srctex, unicrop, geom, context; + var mapper, mat, prog, srctex, unicrop, unicropsource, geom, context; if (!m_this.position()) { return; @@ -187,6 +187,9 @@ var webgl_quadFeature = function (arg) { unicrop = new vgl.uniform(context.FLOAT_VEC2, 'crop'); unicrop.set([1.0, 1.0]); prog.addUniform(unicrop); + unicropsource = new vgl.uniform(context.FLOAT_VEC4, 'cropsource'); + unicropsource.set([0.0, 0.0, 0.0, 0.0]); + prog.addUniform(unicropsource); prog.addShader(vgl.getCachedShader( context.VERTEX_SHADER, context, vertexShaderImage)); prog.addShader(vgl.getCachedShader( @@ -348,7 +351,9 @@ var webgl_quadFeature = function (arg) { var context = renderState.m_context, opacity, zOffset, - crop = {x: 1, y: 1}, quadcrop; + crop = {x: 1, y: 1}, quadcrop, + cropsrc = {x0: 0, y0: 0, x1: 1, y1: 1}, quadcropsrc, + w, h, quadw, quadh; context.bindBuffer(context.ARRAY_BUFFER, m_glBuffers.imgQuadsPosition); $.each(m_quads.imgQuads, function (idx, quad) { @@ -371,7 +376,18 @@ var webgl_quadFeature = function (arg) { if (!crop || quadcrop.x !== crop.x || quadcrop.y !== crop.y) { crop = quadcrop; context.uniform2fv(renderState.m_material.shaderProgram() - .uniformLocation('crop'), new Float32Array([crop.x, crop.y])); + .uniformLocation('crop'), new Float32Array([crop.x || 1, crop.y || 1])); + } + w = quad.image.width; + h = quad.image.height; + quadcropsrc = quad.crop || {left: 0, top: 0, right: w, bottom: h}; + if (!cropsrc || quadcropsrc.left !== cropsrc.left || quadcropsrc.top !== cropsrc.top || quadcropsrc.right !== cropsrc.right || quadcropsrc.bottom !== cropsrc.bottom || quadw !== w || quadh !== h) { + cropsrc = quadcropsrc; + quadw = w; + quadh = h; + context.uniform4fv(renderState.m_material.shaderProgram() + .uniformLocation('cropsource'), new Float32Array([ + cropsrc.left / w, cropsrc.top / h, cropsrc.right / w, cropsrc.bottom / h])); } context.bindBuffer(context.ARRAY_BUFFER, m_glBuffers.imgQuadsPosition); context.vertexAttribPointer(vgl.vertexAttributeKeys.Position, 3, diff --git a/src/webgl/quadFeatureImage.frag b/src/webgl/quadFeatureImage.frag index 2e4967f666..9676a553a2 100644 --- a/src/webgl/quadFeatureImage.frag +++ b/src/webgl/quadFeatureImage.frag @@ -13,4 +13,3 @@ void main(void) { color.w *= opacity; gl_FragColor = color; } - diff --git a/src/webgl/quadFeatureImage.vert b/src/webgl/quadFeatureImage.vert index d6fb8e9c64..1b5f21a8d8 100644 --- a/src/webgl/quadFeatureImage.vert +++ b/src/webgl/quadFeatureImage.vert @@ -6,10 +6,15 @@ uniform float zOffset; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; varying highp vec2 iTextureCoord; +uniform highp vec4 cropsource; void main(void) { gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1.0); gl_Position.z += zOffset; - iTextureCoord = textureCoord; + if (cropsource.p > cropsource.s && cropsource.q > cropsource.t && (cropsource.p < 1.0 || cropsource.s > 0.0 || cropsource.q < 1.0 || cropsource.t > 0.0)) { + iTextureCoord.s = textureCoord.s * (cropsource.p - cropsource.s) + cropsource.s; + iTextureCoord.t = textureCoord.t * (cropsource.q - cropsource.t) + cropsource.t; + } else { + iTextureCoord = textureCoord; + } } -