From 7cfa65b8c34721205cfab7de9708f3d051b44536 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 22 May 2018 16:05:32 +0200 Subject: [PATCH] Align patterns and gradients to a grid --- changelog/upgrade-notes.md | 4 +++ examples/canvas-gradient-pattern.html | 7 +++-- examples/canvas-gradient-pattern.js | 28 +++++++----------- src/ol/colorlike.js | 5 ++-- src/ol/render/canvas/Replay.js | 5 ++-- .../polygon-pattern-gradient-canvas.png | Bin 1304 -> 1700 bytes 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md index 9647079c34..15d26e5db0 100644 --- a/changelog/upgrade-notes.md +++ b/changelog/upgrade-notes.md @@ -2,6 +2,10 @@ ### Next release +#### `ol/style/Fill` with `CanvasGradient` or `CanvasPattern` + +The origin for gradients and patterns has changed from the top-left corner of the extent of the geometry being filled to 512 css pixel increments from map coordinate `[0, 0]`. This allows repeat patterns to be aligned properly with vector tiles. For seamless repeat patterns, width and height of the pattern image must be a factor of two (2, 4, 8, ..., 512). + #### Removal of the renderer option for maps The `renderer` option has been removed from the `Map` constructor. The purpose of this change is to avoid bundling code in your application that you do not need. Previously, code for both the Canvas and WebGL renderers was included in all applications - even though most people only use one renderer. The `Map` constructor now gives you a Canvas (2D) based renderer. If you want to try the WebGL renderer, you can import the constructor from `ol/WebGLMap`. diff --git a/examples/canvas-gradient-pattern.html b/examples/canvas-gradient-pattern.html index fed8ded8d2..f1f1f8ee9e 100644 --- a/examples/canvas-gradient-pattern.html +++ b/examples/canvas-gradient-pattern.html @@ -3,10 +3,11 @@ layout: example.html title: Styling feature with CanvasGradient or CanvasPattern shortdesc: Example showing the countries vector layer styled with patterns and gradients. docs: > - First this example creates a reusable [`CanvasPattern`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasPattern) + This example creates a [`CanvasPattern`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasPattern) and a [`CanvasGradient`](https://developer.mozilla.org/en/docs/Web/API/CanvasGradient). The countries are loaded from - a GeoJSON file. A style function determines for each country whether to use a fill with the pregenerated - CanvasGradient (rainbow colors) or a CanvasPattern (repeating stacked circles). + a GeoJSON file. A style function determines for each country whether to use a fill with the + CanvasGradient (rainbow colors) or a CanvasPattern (repeating stacked circles). **Note**: For seamless repeat patterns, + image width and height of the pattern image must be a factor of two (2, 4, 8, ..., 512). tags: "canvas, gradient, pattern, style" ---
diff --git a/examples/canvas-gradient-pattern.js b/examples/canvas-gradient-pattern.js index 3c8348f0dc..6e2e1018ae 100644 --- a/examples/canvas-gradient-pattern.js +++ b/examples/canvas-gradient-pattern.js @@ -1,6 +1,5 @@ import Map from '../src/ol/Map.js'; import View from '../src/ol/View.js'; -import {getWidth} from '../src/ol/extent.js'; import GeoJSON from '../src/ol/format/GeoJSON.js'; import {DEVICE_PIXEL_RATIO} from '../src/ol/has.js'; import VectorLayer from '../src/ol/layer/Vector.js'; @@ -16,14 +15,8 @@ const context = canvas.getContext('2d'); const pixelRatio = DEVICE_PIXEL_RATIO; // Generate a rainbow gradient -function gradient(feature, resolution) { - const extent = feature.getGeometry().getExtent(); - // Gradient starts on the left edge of each feature, and ends on the right. - // Coordinate origin is the top-left corner of the extent of the geometry, so - // we just divide the geometry's extent width by resolution and multiply with - // pixelRatio to match the renderer's pixel coordinate system. - const grad = context.createLinearGradient(0, 0, - getWidth(extent) / resolution * pixelRatio, 0); +const gradient = (function() { + const grad = context.createLinearGradient(0, 0, 512 * pixelRatio, 0); grad.addColorStop(0, 'red'); grad.addColorStop(1 / 6, 'orange'); grad.addColorStop(2 / 6, 'yellow'); @@ -32,24 +25,24 @@ function gradient(feature, resolution) { grad.addColorStop(5 / 6, 'blue'); grad.addColorStop(1, 'purple'); return grad; -} +})(); // Generate a canvasPattern with two circles on white background const pattern = (function() { - canvas.width = 11 * pixelRatio; - canvas.height = 11 * pixelRatio; + canvas.width = 8 * pixelRatio; + canvas.height = 8 * pixelRatio; // white background context.fillStyle = 'white'; context.fillRect(0, 0, canvas.width, canvas.height); // outer circle context.fillStyle = 'rgba(102, 0, 102, 0.5)'; context.beginPath(); - context.arc(5 * pixelRatio, 5 * pixelRatio, 4 * pixelRatio, 0, 2 * Math.PI); + context.arc(4 * pixelRatio, 4 * pixelRatio, 3 * pixelRatio, 0, 2 * Math.PI); context.fill(); // inner circle context.fillStyle = 'rgb(55, 0, 170)'; context.beginPath(); - context.arc(5 * pixelRatio, 5 * pixelRatio, 2 * pixelRatio, 0, 2 * Math.PI); + context.arc(4 * pixelRatio, 4 * pixelRatio, 1.5 * pixelRatio, 0, 2 * Math.PI); context.fill(); return context.createPattern(canvas, 'repeat'); }()); @@ -69,12 +62,11 @@ const style = new Style({ * which either contains the aboove gradient or pattern. * * @param {module:ol/Feature~Feature} feature The feature to style. - * @param {number} resolution Resolution. * @return {module:ol/style/Style} The style to use for the feature. */ -const getStackedStyle = function(feature, resolution) { +const getStackedStyle = function(feature) { const id = feature.getId(); - fill.setColor(id > 'J' ? gradient(feature, resolution) : pattern); + fill.setColor(id > 'J' ? gradient : pattern); return style; }; @@ -94,7 +86,7 @@ const map = new Map({ ], target: 'map', view: new View({ - center: fromLonLat([7, 52]), + center: fromLonLat([16, 48]), zoom: 3 }) }); diff --git a/src/ol/colorlike.js b/src/ol/colorlike.js index eb0b2d37cf..9e0c26b99b 100644 --- a/src/ol/colorlike.js +++ b/src/ol/colorlike.js @@ -8,8 +8,9 @@ import {toString} from './color.js'; * A type accepted by CanvasRenderingContext2D.fillStyle * or CanvasRenderingContext2D.strokeStyle. * Represents a color, pattern, or gradient. The origin for patterns and - * gradients as fill style is the top-left corner of the extent of the geometry - * being filled. + * gradients as fill style is an increment of 512 css pixels from map coordinate + * `[0, 0]`. For seamless repeat patterns, width and height of the pattern image + * must be a factor of two (2, 4, 8, ..., 512). * * @typedef {string|CanvasPattern|CanvasGradient} ColorLike * @api diff --git a/src/ol/render/canvas/Replay.js b/src/ol/render/canvas/Replay.js index a65db3fea1..e9e90e36cb 100644 --- a/src/ol/render/canvas/Replay.js +++ b/src/ol/render/canvas/Replay.js @@ -456,8 +456,9 @@ CanvasReplay.prototype.beginGeometry = function(geometry, feature) { */ CanvasReplay.prototype.fill_ = function(context) { if (this.fillOrigin_) { - const origin = applyTransform(this.renderedTransform_, this.fillOrigin_.slice()); - context.translate(origin[0], origin[1]); + const origin = applyTransform(this.renderedTransform_, [0, 0]); + const repeatSize = 512 * this.pixelRatio; + context.translate(origin[0] % repeatSize, origin[1] % repeatSize); context.rotate(this.viewRotation_); } context.fill(); diff --git a/test/rendering/ol/style/expected/polygon-pattern-gradient-canvas.png b/test/rendering/ol/style/expected/polygon-pattern-gradient-canvas.png index 4a2fb3412acafaed1be8f9fba9a5a28d3960aba3..769c63fcce388e1e703003eec906aa8e87b42daf 100644 GIT binary patch delta 1685 zcmV;G25R}33ZxB?BYy^KNklCa~U;ff?%< z73i+H-L1JBYrD0*YIc+6HraW7`aS1&&i8jF=qHcpCmi%&5Px+&cjBag8^HK+cfb)h z)VVv~wH|Qe1ZO_D^1w9_oO#gZf+Ij3o$p#makt{ag9~@yUAip|#2-juX ztZB&&*7N>zf1OEgakq>)swtA|17V4+@WH%XY$AH!8HD&qBd?~)0SXk z%UQluAac%F;&_ymk>{$Yp9q9S@8>(9R7SoWF3fe0TK}w(nqx!vZj?*+87p7r z@|G=ZX*5i&QNV(pI*6vMj64}v^#UGg$OIbVi%nULf6o`TPpqs5&3snh<_PJlq=X1TPb)Z2gRx%8ht8i$r{#ZVLqlO zWYmiH5oIY)ywF7H5(ll>`sw#7FkMFeHYNhSa>#uozDE=_aU3$M#aPF5VF4?(CK78`Sb>(6c-7RQ<+HtIdE2yGCL< zH11TZsH4J=MOoKI`eSB6gjFQkBg*b~9~pJZk$c}iS(L+U#Upz3+3^GmxkjRiS_fX4 zmUn#dJ+lp|ply7?A`m_LZ1X@2o`1csN0j4t4V!jy>+R`yC86iFc5#9^Y!@MYws{~? zW>NMQO(_3$gEWBb37wh7!+b{_rN|r_@S=TC6Q^y_5 z77=~6c_0QwdUHgXSX}(#k%v;t)$BZ5XWDf9F??BtWQtz-Z1X@2ibPuM(tlbeD7e1I zm6xpNe(P}-O(8wK*FM`kkZuk!R2F6Chm5W}!y&hj()HYLBiWN~W5kR;`E2t*jJe+s z$%l%4jQFvM12t|Ofo!~Uyh8TmNfh-m?hfZ^_Mt>nkgE-v81&>Xd%u`C#KHEHy5LuM0vFDqYegA1$U9&n= zzwBwNaZ{|66O+$252S~vmFBpZSW*4ivWt%P!8(MmwuzF@7YFjP|C=XlHvZ)|S2*6- z{!+&1lHYBC8>=R+)+$VwONfilj_0os(jk8E^-SN`g5Q&FomwySJAXufjhlIMMsdyk zT|5UAZCky)LsUI}7D%al6m3Ul*9)DEH(pO?uv{dw+3dl@(tcz`GI&&Eu|Aeg{*Ahk z6sZ!oV4Ba4Ct!C)<}sX)1NZD1tP@3;>a$G)(Hd6Q@i18=h`ZSk&yLJ)tN;E}joH7P fS?%{5Mg9g-djDXX!w$UV00000NkvXXu0mjf3IsT@ delta 1285 zcmV+g1^W7=4VVg$BYyi6QMx?~G}nrh0)I zvrwxkC{21{+L^5;*ljURA?@<*67`gy##j<=tR~c<3CW6qR91~0yf8wHQHhQGw2>59 z4BT|5Sz;;jc~Pi`aog_C*^NzjlXG|G>^!^Y%)IaSEwG^tWq(H8g*pyGum!o`oe| z!+CKGoq|BgjJo(B8nKVNz#Ig#1?FGCJOkz^IBbIlC%|NZt9!x49bk007^a(H`55B^ z411OlX2u{hh<_Q?%qV7l1U7HO1V00d>(R@N=wJdRJNCr`a{N93sZJEcihh^V^6RIi z=IF=eq0rg#&?g_2nZEwqfZK#o(hdXdE(509faNoCeh9Xd_J3=Zca4V1-yRK>9k^uX zJ{Jj~gpDn*jqMmWcEd9E!!(ZJR7(?@Oan&9fDvj&vw!J>FW`sYkOzUr9S}VX&PoX6 zuM3+_YtRfGI?2NWkI{gJJo!2udcNwp1oAo{f_>f*2!ELBbGDkj23VKk&fwpF^oZi*R<2(*Jh^Af}?fyz@}IFld*Dqp?vHHdiv?NG9Kra<1osnXRgv<7i4fWVW=KHzR= zM1$6}`o!59>)Lqd9zaiFBRVci5YYevyOcpVLDFGSDiA8Dhj~ddjW-BXr4+&mqS~T7 zfqyIcyJ3w;?sthmdz3>sLDat4!<)Bbd|0ya%YOLl(*lt$kk_*tPHcfaB;}EnMsyrX z8-x=?g+)1-{f0lUaACv|th{N3aDt?4QL+!yV^9D7mrqoPVuS z@miGqFH{;O9k?ddey1_@MQS-)rQ)$DZ|!>ibhU{EsfaxbOF_<7se4%z--}fZek&_{ zyBgo_nn#aYP0m)SyS)(qSVZaI1g6*83b(davAUeCQg;I3>nIG{n7rK|F%d0kgW8;} zQn5gI1_d0&*zHEoFmAX_sL$Cd<$eVD^3A8pB0uF$Ngb;tx`_1TY?YEhmO`7l7cS&o zky>xd1CvN!&Q>WIWaVPkO{s^t61I`voUKxKY=$l({W)8uVuD;nW^%Sl#Q@1%&Q>YM v9|pRak=cl