From e82135abb7db7c35177fc7f07d3847b7a6a7986e Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Wed, 11 Mar 2020 11:00:51 -0700 Subject: [PATCH] Adjust patterns added with `addImage` by the asset pixel ratio (#9372) * Adjust *-pattern images based on their asset pixel ratio Instead of using the device pixel ratio, use the image asset pixel ratio Fix for issue #8020 * EOF * Add render tests * Rebase from master Update baseline for line-pattern to take into account https://github.com/mapbox/mapbox-gl-js/pull/9266 --- src/data/array_types.js | 22 +++-- src/data/bucket/pattern_attributes.js | 4 +- src/data/program_configuration.js | 40 +++++--- src/render/program/fill_extrusion_program.js | 5 +- src/render/program/fill_program.js | 10 +- src/render/program/line_program.js | 9 +- src/render/program/pattern.js | 8 +- .../fill_extrusion_pattern.fragment.glsl | 4 + .../fill_extrusion_pattern.vertex.glsl | 17 ++-- src/shaders/fill_outline_pattern.vertex.glsl | 17 ++-- src/shaders/fill_pattern.vertex.glsl | 17 ++-- src/shaders/line_pattern.fragment.glsl | 17 ++-- src/shaders/line_pattern.vertex.glsl | 6 +- .../1.5x-on-1x-add-image/expected.png | Bin 0 -> 25729 bytes .../1.5x-on-1x-add-image/style.json | 93 ++++++++++++++++++ .../3x-on-2x-add-image/expected.png | Bin 0 -> 3888 bytes .../3x-on-2x-add-image/style.json | 79 +++++++++++++++ .../3x-on-2x-add-image/expected.png | Bin 0 -> 2173 bytes .../3x-on-2x-add-image/style.json | 66 +++++++++++++ 19 files changed, 344 insertions(+), 70 deletions(-) create mode 100644 test/integration/render-tests/fill-extrusion-pattern/1.5x-on-1x-add-image/expected.png create mode 100644 test/integration/render-tests/fill-extrusion-pattern/1.5x-on-1x-add-image/style.json create mode 100644 test/integration/render-tests/fill-pattern/3x-on-2x-add-image/expected.png create mode 100644 test/integration/render-tests/fill-pattern/3x-on-2x-add-image/style.json create mode 100644 test/integration/render-tests/line-pattern/3x-on-2x-add-image/expected.png create mode 100644 test/integration/render-tests/line-pattern/3x-on-2x-add-image/style.json diff --git a/src/data/array_types.js b/src/data/array_types.js index 35dca5c6ec8..c502d723a53 100644 --- a/src/data/array_types.js +++ b/src/data/array_types.js @@ -150,10 +150,11 @@ register('StructArrayLayout2i4ub8', StructArrayLayout2i4ub8); /** * Implementation of the StructArray layout: * [0]: Uint16[8] + * [16]: Uint8[2] * * @private */ -class StructArrayLayout8ui16 extends StructArray { +class StructArrayLayout8ui2ub18 extends StructArray { uint8: Uint8Array; uint16: Uint16Array; @@ -162,14 +163,15 @@ class StructArrayLayout8ui16 extends StructArray { this.uint16 = new Uint16Array(this.arrayBuffer); } - emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number) { + emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number) { const i = this.length; this.resize(i + 1); - return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7); + return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); } - emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number) { - const o2 = i * 8; + emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number) { + const o2 = i * 9; + const o1 = i * 18; this.uint16[o2 + 0] = v0; this.uint16[o2 + 1] = v1; this.uint16[o2 + 2] = v2; @@ -178,12 +180,14 @@ class StructArrayLayout8ui16 extends StructArray { this.uint16[o2 + 5] = v5; this.uint16[o2 + 6] = v6; this.uint16[o2 + 7] = v7; + this.uint8[o1 + 16] = v8; + this.uint8[o1 + 17] = v9; return i; } } -StructArrayLayout8ui16.prototype.bytesPerElement = 16; -register('StructArrayLayout8ui16', StructArrayLayout8ui16); +StructArrayLayout8ui2ub18.prototype.bytesPerElement = 18; +register('StructArrayLayout8ui2ub18', StructArrayLayout8ui2ub18); /** * Implementation of the StructArray layout: @@ -1054,7 +1058,7 @@ export { StructArrayLayout4i8, StructArrayLayout2i4i12, StructArrayLayout2i4ub8, - StructArrayLayout8ui16, + StructArrayLayout8ui2ub18, StructArrayLayout4i4ui4i24, StructArrayLayout3f12, StructArrayLayout1ul4, @@ -1078,7 +1082,7 @@ export { StructArrayLayout2i4i12 as FillExtrusionLayoutArray, StructArrayLayout2i4 as HeatmapLayoutArray, StructArrayLayout2i4ub8 as LineLayoutArray, - StructArrayLayout8ui16 as PatternLayoutArray, + StructArrayLayout8ui2ub18 as PatternLayoutArray, StructArrayLayout4i4ui4i24 as SymbolLayoutArray, StructArrayLayout3f12 as SymbolDynamicLayoutArray, StructArrayLayout1ul4 as SymbolOpacityArray, diff --git a/src/data/bucket/pattern_attributes.js b/src/data/bucket/pattern_attributes.js index a46d42cc057..f47b64ca115 100644 --- a/src/data/bucket/pattern_attributes.js +++ b/src/data/bucket/pattern_attributes.js @@ -4,5 +4,7 @@ import {createLayout} from '../../util/struct_array'; export default createLayout([ // [tl.x, tl.y, br.x, br.y] {name: 'a_pattern_from', components: 4, type: 'Uint16'}, - {name: 'a_pattern_to', components: 4, type: 'Uint16'} + {name: 'a_pattern_to', components: 4, type: 'Uint16'}, + {name: 'a_pixel_ratio_from', components: 1, type: 'Uint8'}, + {name: 'a_pixel_ratio_to', components: 1, type: 'Uint8'}, ]); diff --git a/src/data/program_configuration.js b/src/data/program_configuration.js index 22e9cba0d3f..cd7110dd9f6 100644 --- a/src/data/program_configuration.js +++ b/src/data/program_configuration.js @@ -86,7 +86,7 @@ interface AttributeBinder { interface UniformBinder { uniformNames: Array; setUniform(uniform: Uniform<*>, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue<*>, uniformName: string): void; - getBinding(context: Context, location: WebGLUniformLocation): $Shape>; + getBinding(context: Context, location: WebGLUniformLocation, name: string): $Shape>; } class ConstantBinder implements UniformBinder { @@ -104,7 +104,7 @@ class ConstantBinder implements UniformBinder { uniform.set(currentValue.constantOr(this.value)); } - getBinding(context: Context, location: WebGLUniformLocation): $Shape> { + getBinding(context: Context, location: WebGLUniformLocation, _: string): $Shape> { return (this.type === 'color') ? new UniformColor(context, location) : new Uniform1f(context, location); @@ -115,27 +115,37 @@ class CrossFadedConstantBinder implements UniformBinder { uniformNames: Array; patternFrom: ?Array; patternTo: ?Array; + pixelRatioFrom: number; + pixelRatioTo: number; constructor(value: mixed, names: Array) { this.uniformNames = names.map(name => `u_${name}`); this.patternFrom = null; this.patternTo = null; + this.pixelRatioFrom = 1.0; + this.pixelRatioTo = 1.0; } setConstantPatternPositions(posTo: ImagePosition, posFrom: ImagePosition) { - this.patternTo = posTo.tlbr; + this.pixelRatioFrom = posFrom.pixelRatio; + this.pixelRatioTo = posTo.pixelRatio; this.patternFrom = posFrom.tlbr; + this.patternTo = posTo.tlbr; } setUniform(uniform: Uniform<*>, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue, uniformName: string) { const pos = uniformName === 'u_pattern_to' ? this.patternTo : - uniformName === 'u_pattern_from' ? this.patternFrom : null; + uniformName === 'u_pattern_from' ? this.patternFrom : + uniformName === 'u_pixel_ratio_to' ? this.pixelRatioTo : + uniformName === 'u_pixel_ratio_from' ? this.pixelRatioFrom : null; if (pos) uniform.set(pos); } - getBinding(context: Context, location: WebGLUniformLocation): $Shape> { - return new Uniform4f(context, location); + getBinding(context: Context, location: WebGLUniformLocation, name: string): $Shape> { + return name.startsWith('u_pattern') ? + new Uniform4f(context, location) : + new Uniform1f(context, location); } } @@ -283,7 +293,7 @@ class CompositeExpressionBinder implements AttributeBinder, UniformBinder { uniform.set(factor); } - getBinding(context: Context, location: WebGLUniformLocation): Uniform1f { + getBinding(context: Context, location: WebGLUniformLocation, _: string): Uniform1f { return new Uniform1f(context, location); } } @@ -345,11 +355,15 @@ class CrossFadedCompositeBinder implements AttributeBinder { for (let i = start; i < end; i++) { this.zoomInPaintVertexArray.emplace(i, imageMid.tl[0], imageMid.tl[1], imageMid.br[0], imageMid.br[1], - imageMin.tl[0], imageMin.tl[1], imageMin.br[0], imageMin.br[1] + imageMin.tl[0], imageMin.tl[1], imageMin.br[0], imageMin.br[1], + imageMid.pixelRatio, + imageMin.pixelRatio, ); this.zoomOutPaintVertexArray.emplace(i, imageMid.tl[0], imageMid.tl[1], imageMid.br[0], imageMid.br[1], - imageMax.tl[0], imageMax.tl[1], imageMax.br[0], imageMax.br[1] + imageMax.tl[0], imageMax.tl[1], imageMax.br[0], imageMax.br[1], + imageMid.pixelRatio, + imageMax.pixelRatio, ); } } @@ -503,7 +517,7 @@ export default class ProgramConfiguration { if (binder instanceof ConstantBinder || binder instanceof CrossFadedConstantBinder || binder instanceof CompositeExpressionBinder) { for (const name of binder.uniformNames) { if (locations[name]) { - const binding = binder.getBinding(context, locations[name]); + const binding = binder.getBinding(context, locations[name], name); uniforms.push({name, property, binding}); } } @@ -620,9 +634,9 @@ function paintAttributeNames(property, type) { 'text-halo-width': ['halo_width'], 'icon-halo-width': ['halo_width'], 'line-gap-width': ['gapwidth'], - 'line-pattern': ['pattern_to', 'pattern_from'], - 'fill-pattern': ['pattern_to', 'pattern_from'], - 'fill-extrusion-pattern': ['pattern_to', 'pattern_from'], + 'line-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'], + 'fill-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'], + 'fill-extrusion-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'], }; return attributeNameExceptions[property] || [property.replace(`${type}-`, '').replace(/-/g, '_')]; diff --git a/src/render/program/fill_extrusion_program.js b/src/render/program/fill_extrusion_program.js index ab0bd31039e..39dec5cf055 100644 --- a/src/render/program/fill_extrusion_program.js +++ b/src/render/program/fill_extrusion_program.js @@ -6,7 +6,6 @@ import { Uniform1f, Uniform2f, Uniform3f, - Uniform4f, UniformMatrix4f } from '../uniform_binding'; @@ -41,7 +40,7 @@ export type FillExtrusionPatternUniformsType = {| 'u_image': Uniform1i, 'u_pixel_coord_upper': Uniform2f, 'u_pixel_coord_lower': Uniform2f, - 'u_scale': Uniform4f, + 'u_scale': Uniform3f, 'u_fade': Uniform1f, 'u_opacity': Uniform1f |}; @@ -67,7 +66,7 @@ const fillExtrusionPatternUniforms = (context: Context, locations: UniformLocati 'u_texsize': new Uniform2f(context, locations.u_texsize), 'u_pixel_coord_upper': new Uniform2f(context, locations.u_pixel_coord_upper), 'u_pixel_coord_lower': new Uniform2f(context, locations.u_pixel_coord_lower), - 'u_scale': new Uniform4f(context, locations.u_scale), + 'u_scale': new Uniform3f(context, locations.u_scale), 'u_fade': new Uniform1f(context, locations.u_fade), 'u_opacity': new Uniform1f(context, locations.u_opacity) }); diff --git a/src/render/program/fill_program.js b/src/render/program/fill_program.js index 3c2b79e0c2c..8bc83d141f8 100644 --- a/src/render/program/fill_program.js +++ b/src/render/program/fill_program.js @@ -5,7 +5,7 @@ import { Uniform1i, Uniform1f, Uniform2f, - Uniform4f, + Uniform3f, UniformMatrix4f } from '../uniform_binding'; import {extend} from '../../util/util'; @@ -32,7 +32,7 @@ export type FillPatternUniformsType = {| 'u_image': Uniform1i, 'u_pixel_coord_upper': Uniform2f, 'u_pixel_coord_lower': Uniform2f, - 'u_scale': Uniform4f, + 'u_scale': Uniform3f, 'u_fade': Uniform1f |}; @@ -44,7 +44,7 @@ export type FillOutlinePatternUniformsType = {| 'u_image': Uniform1i, 'u_pixel_coord_upper': Uniform2f, 'u_pixel_coord_lower': Uniform2f, - 'u_scale': Uniform4f, + 'u_scale': Uniform3f, 'u_fade': Uniform1f |}; @@ -58,7 +58,7 @@ const fillPatternUniforms = (context: Context, locations: UniformLocations): Fil 'u_texsize': new Uniform2f(context, locations.u_texsize), 'u_pixel_coord_upper': new Uniform2f(context, locations.u_pixel_coord_upper), 'u_pixel_coord_lower': new Uniform2f(context, locations.u_pixel_coord_lower), - 'u_scale': new Uniform4f(context, locations.u_scale), + 'u_scale': new Uniform3f(context, locations.u_scale), 'u_fade': new Uniform1f(context, locations.u_fade) }); @@ -75,7 +75,7 @@ const fillOutlinePatternUniforms = (context: Context, locations: UniformLocation 'u_texsize': new Uniform2f(context, locations.u_texsize), 'u_pixel_coord_upper': new Uniform2f(context, locations.u_pixel_coord_upper), 'u_pixel_coord_lower': new Uniform2f(context, locations.u_pixel_coord_lower), - 'u_scale': new Uniform4f(context, locations.u_scale), + 'u_scale': new Uniform3f(context, locations.u_scale), 'u_fade': new Uniform1f(context, locations.u_fade) }); diff --git a/src/render/program/line_program.js b/src/render/program/line_program.js index 78d38e207e3..4ab16c3e5c8 100644 --- a/src/render/program/line_program.js +++ b/src/render/program/line_program.js @@ -4,7 +4,7 @@ import { Uniform1i, Uniform1f, Uniform2f, - Uniform4f, + Uniform3f, UniformMatrix4f } from '../uniform_binding'; import pixelsToTileUnits from '../../source/pixels_to_tile_units'; @@ -42,7 +42,7 @@ export type LinePatternUniformsType = {| 'u_device_pixel_ratio': Uniform1f, 'u_units_to_pixels': Uniform2f, 'u_image': Uniform1i, - 'u_scale': Uniform4f, + 'u_scale': Uniform3f, 'u_fade': Uniform1f |}; @@ -82,7 +82,7 @@ const linePatternUniforms = (context: Context, locations: UniformLocations): Lin 'u_device_pixel_ratio': new Uniform1f(context, locations.u_device_pixel_ratio), 'u_image': new Uniform1i(context, locations.u_image), 'u_units_to_pixels': new Uniform2f(context, locations.u_units_to_pixels), - 'u_scale': new Uniform4f(context, locations.u_scale), + 'u_scale': new Uniform3f(context, locations.u_scale), 'u_fade': new Uniform1f(context, locations.u_fade) }); @@ -143,8 +143,7 @@ const linePatternUniformValues = ( 'u_ratio': 1 / pixelsToTileUnits(tile, 1, transform.zoom), 'u_device_pixel_ratio': browser.devicePixelRatio, 'u_image': 0, - // this assumes all images in the icon atlas texture have the same pixel ratio - 'u_scale': [browser.devicePixelRatio, tileZoomRatio, crossfade.fromScale, crossfade.toScale], + 'u_scale': [tileZoomRatio, crossfade.fromScale, crossfade.toScale], 'u_fade': crossfade.t, 'u_units_to_pixels': [ 1 / transform.pixelsToGLUnits[0], diff --git a/src/render/program/pattern.js b/src/render/program/pattern.js index d33718abc9f..283fca5d9e2 100644 --- a/src/render/program/pattern.js +++ b/src/render/program/pattern.js @@ -5,10 +5,9 @@ import { Uniform1i, Uniform1f, Uniform2f, - Uniform4f + Uniform3f } from '../uniform_binding'; import pixelsToTileUnits from '../../source/pixels_to_tile_units'; -import browser from '../../util/browser'; import type Painter from '../painter'; import type {OverscaledTileID} from '../../source/tile_id'; @@ -39,7 +38,7 @@ export type PatternUniformsType = {| // pattern uniforms: 'u_image': Uniform1i, 'u_texsize': Uniform2f, - 'u_scale': Uniform4f, + 'u_scale': Uniform3f, 'u_fade': Uniform1f, 'u_pixel_coord_upper': Uniform2f, 'u_pixel_coord_lower': Uniform2f @@ -60,8 +59,7 @@ function patternUniformValues(crossfade: CrossfadeParameters, painter: Painter, return { 'u_image': 0, 'u_texsize': tile.imageAtlasTexture.size, - // this assumes all images in the icon atlas texture have the same pixel ratio - 'u_scale': [browser.devicePixelRatio, tileRatio, crossfade.fromScale, crossfade.toScale], + 'u_scale': [tileRatio, crossfade.fromScale, crossfade.toScale], 'u_fade': crossfade.t, // split the pixel coord into two pairs of 16 bit numbers. The glsl spec only guarantees 16 bits of precision. 'u_pixel_coord_upper': [pixelX >> 16, pixelY >> 16], diff --git a/src/shaders/fill_extrusion_pattern.fragment.glsl b/src/shaders/fill_extrusion_pattern.fragment.glsl index f3243d74b71..1edd25eac45 100644 --- a/src/shaders/fill_extrusion_pattern.fragment.glsl +++ b/src/shaders/fill_extrusion_pattern.fragment.glsl @@ -11,12 +11,16 @@ varying vec4 v_lighting; #pragma mapbox: define lowp float height #pragma mapbox: define lowp vec4 pattern_from #pragma mapbox: define lowp vec4 pattern_to +#pragma mapbox: define lowp float pixel_ratio_from +#pragma mapbox: define lowp float pixel_ratio_to void main() { #pragma mapbox: initialize lowp float base #pragma mapbox: initialize lowp float height #pragma mapbox: initialize mediump vec4 pattern_from #pragma mapbox: initialize mediump vec4 pattern_to + #pragma mapbox: initialize lowp float pixel_ratio_from + #pragma mapbox: initialize lowp float pixel_ratio_to vec2 pattern_tl_a = pattern_from.xy; vec2 pattern_br_a = pattern_from.zw; diff --git a/src/shaders/fill_extrusion_pattern.vertex.glsl b/src/shaders/fill_extrusion_pattern.vertex.glsl index 2761d56b6a9..2428580f9ca 100644 --- a/src/shaders/fill_extrusion_pattern.vertex.glsl +++ b/src/shaders/fill_extrusion_pattern.vertex.glsl @@ -2,7 +2,7 @@ uniform mat4 u_matrix; uniform vec2 u_pixel_coord_upper; uniform vec2 u_pixel_coord_lower; uniform float u_height_factor; -uniform vec4 u_scale; +uniform vec3 u_scale; uniform float u_vertical_gradient; uniform lowp float u_opacity; @@ -21,28 +21,31 @@ varying vec4 v_lighting; #pragma mapbox: define lowp float height #pragma mapbox: define lowp vec4 pattern_from #pragma mapbox: define lowp vec4 pattern_to +#pragma mapbox: define lowp float pixel_ratio_from +#pragma mapbox: define lowp float pixel_ratio_to void main() { #pragma mapbox: initialize lowp float base #pragma mapbox: initialize lowp float height #pragma mapbox: initialize mediump vec4 pattern_from #pragma mapbox: initialize mediump vec4 pattern_to + #pragma mapbox: initialize lowp float pixel_ratio_from + #pragma mapbox: initialize lowp float pixel_ratio_to vec2 pattern_tl_a = pattern_from.xy; vec2 pattern_br_a = pattern_from.zw; vec2 pattern_tl_b = pattern_to.xy; vec2 pattern_br_b = pattern_to.zw; - float pixelRatio = u_scale.x; - float tileRatio = u_scale.y; - float fromScale = u_scale.z; - float toScale = u_scale.w; + float tileRatio = u_scale.x; + float fromScale = u_scale.y; + float toScale = u_scale.z; vec3 normal = a_normal_ed.xyz; float edgedistance = a_normal_ed.w; - vec2 display_size_a = vec2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); - vec2 display_size_b = vec2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); + vec2 display_size_a = (pattern_br_a - pattern_tl_a) / pixel_ratio_from; + vec2 display_size_b = (pattern_br_b - pattern_tl_b) / pixel_ratio_to; base = max(0.0, base); height = max(0.0, height); diff --git a/src/shaders/fill_outline_pattern.vertex.glsl b/src/shaders/fill_outline_pattern.vertex.glsl index ac5ed0c423f..8d47ca56638 100644 --- a/src/shaders/fill_outline_pattern.vertex.glsl +++ b/src/shaders/fill_outline_pattern.vertex.glsl @@ -2,7 +2,7 @@ uniform mat4 u_matrix; uniform vec2 u_world; uniform vec2 u_pixel_coord_upper; uniform vec2 u_pixel_coord_lower; -uniform vec4 u_scale; +uniform vec3 u_scale; attribute vec2 a_pos; @@ -13,26 +13,29 @@ varying vec2 v_pos; #pragma mapbox: define lowp float opacity #pragma mapbox: define lowp vec4 pattern_from #pragma mapbox: define lowp vec4 pattern_to +#pragma mapbox: define lowp float pixel_ratio_from +#pragma mapbox: define lowp float pixel_ratio_to void main() { #pragma mapbox: initialize lowp float opacity #pragma mapbox: initialize mediump vec4 pattern_from #pragma mapbox: initialize mediump vec4 pattern_to + #pragma mapbox: initialize lowp float pixel_ratio_from + #pragma mapbox: initialize lowp float pixel_ratio_to vec2 pattern_tl_a = pattern_from.xy; vec2 pattern_br_a = pattern_from.zw; vec2 pattern_tl_b = pattern_to.xy; vec2 pattern_br_b = pattern_to.zw; - float pixelRatio = u_scale.x; - float tileRatio = u_scale.y; - float fromScale = u_scale.z; - float toScale = u_scale.w; + float tileRatio = u_scale.x; + float fromScale = u_scale.y; + float toScale = u_scale.z; gl_Position = u_matrix * vec4(a_pos, 0, 1); - vec2 display_size_a = vec2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); - vec2 display_size_b = vec2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); + vec2 display_size_a = (pattern_br_a - pattern_tl_a) / pixel_ratio_from; + vec2 display_size_b = (pattern_br_b - pattern_tl_b) / pixel_ratio_to; v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, fromScale * display_size_a, tileRatio, a_pos); v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, toScale * display_size_b, tileRatio, a_pos); diff --git a/src/shaders/fill_pattern.vertex.glsl b/src/shaders/fill_pattern.vertex.glsl index a3be3320186..e6f75d441c7 100644 --- a/src/shaders/fill_pattern.vertex.glsl +++ b/src/shaders/fill_pattern.vertex.glsl @@ -1,7 +1,7 @@ uniform mat4 u_matrix; uniform vec2 u_pixel_coord_upper; uniform vec2 u_pixel_coord_lower; -uniform vec4 u_scale; +uniform vec3 u_scale; attribute vec2 a_pos; @@ -11,24 +11,27 @@ varying vec2 v_pos_b; #pragma mapbox: define lowp float opacity #pragma mapbox: define lowp vec4 pattern_from #pragma mapbox: define lowp vec4 pattern_to +#pragma mapbox: define lowp float pixel_ratio_from +#pragma mapbox: define lowp float pixel_ratio_to void main() { #pragma mapbox: initialize lowp float opacity #pragma mapbox: initialize mediump vec4 pattern_from #pragma mapbox: initialize mediump vec4 pattern_to + #pragma mapbox: initialize lowp float pixel_ratio_from + #pragma mapbox: initialize lowp float pixel_ratio_to vec2 pattern_tl_a = pattern_from.xy; vec2 pattern_br_a = pattern_from.zw; vec2 pattern_tl_b = pattern_to.xy; vec2 pattern_br_b = pattern_to.zw; - float pixelRatio = u_scale.x; - float tileZoomRatio = u_scale.y; - float fromScale = u_scale.z; - float toScale = u_scale.w; + float tileZoomRatio = u_scale.x; + float fromScale = u_scale.y; + float toScale = u_scale.z; - vec2 display_size_a = vec2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); - vec2 display_size_b = vec2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); + vec2 display_size_a = (pattern_br_a - pattern_tl_a) / pixel_ratio_from; + vec2 display_size_b = (pattern_br_b - pattern_tl_b) / pixel_ratio_to; gl_Position = u_matrix * vec4(a_pos, 0, 1); v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, fromScale * display_size_a, tileZoomRatio, a_pos); diff --git a/src/shaders/line_pattern.fragment.glsl b/src/shaders/line_pattern.fragment.glsl index 21f086358c0..c9b1adc4004 100644 --- a/src/shaders/line_pattern.fragment.glsl +++ b/src/shaders/line_pattern.fragment.glsl @@ -1,7 +1,7 @@ uniform lowp float u_device_pixel_ratio; uniform vec2 u_texsize; uniform float u_fade; -uniform mediump vec4 u_scale; +uniform mediump vec3 u_scale; uniform sampler2D u_image; @@ -13,12 +13,16 @@ varying float v_width; #pragma mapbox: define lowp vec4 pattern_from #pragma mapbox: define lowp vec4 pattern_to +#pragma mapbox: define lowp float pixel_ratio_from +#pragma mapbox: define lowp float pixel_ratio_to #pragma mapbox: define lowp float blur #pragma mapbox: define lowp float opacity void main() { #pragma mapbox: initialize mediump vec4 pattern_from #pragma mapbox: initialize mediump vec4 pattern_to + #pragma mapbox: initialize lowp float pixel_ratio_from + #pragma mapbox: initialize lowp float pixel_ratio_to #pragma mapbox: initialize lowp float blur #pragma mapbox: initialize lowp float opacity @@ -28,13 +32,12 @@ void main() { vec2 pattern_tl_b = pattern_to.xy; vec2 pattern_br_b = pattern_to.zw; - float pixelRatio = u_scale.x; - float tileZoomRatio = u_scale.y; - float fromScale = u_scale.z; - float toScale = u_scale.w; + float tileZoomRatio = u_scale.x; + float fromScale = u_scale.y; + float toScale = u_scale.z; - vec2 display_size_a = (pattern_br_a - pattern_tl_a) / pixelRatio; - vec2 display_size_b = (pattern_br_b - pattern_tl_b) / pixelRatio; + vec2 display_size_a = (pattern_br_a - pattern_tl_a) / pixel_ratio_from; + vec2 display_size_b = (pattern_br_b - pattern_tl_b) / pixel_ratio_to; vec2 pattern_size_a = vec2(display_size_a.x * fromScale / tileZoomRatio, display_size_a.y); vec2 pattern_size_b = vec2(display_size_b.x * toScale / tileZoomRatio, display_size_b.y); diff --git a/src/shaders/line_pattern.vertex.glsl b/src/shaders/line_pattern.vertex.glsl index 0b217ccdfff..fff2daa110a 100644 --- a/src/shaders/line_pattern.vertex.glsl +++ b/src/shaders/line_pattern.vertex.glsl @@ -32,6 +32,8 @@ varying float v_width; #pragma mapbox: define lowp float floorwidth #pragma mapbox: define lowp vec4 pattern_from #pragma mapbox: define lowp vec4 pattern_to +#pragma mapbox: define lowp float pixel_ratio_from +#pragma mapbox: define lowp float pixel_ratio_to void main() { #pragma mapbox: initialize lowp float blur @@ -42,6 +44,8 @@ void main() { #pragma mapbox: initialize lowp float floorwidth #pragma mapbox: initialize mediump vec4 pattern_from #pragma mapbox: initialize mediump vec4 pattern_to + #pragma mapbox: initialize lowp float pixel_ratio_from + #pragma mapbox: initialize lowp float pixel_ratio_to // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. @@ -50,7 +54,7 @@ void main() { vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE; - // float tileRatio = u_scale.y; + // float tileRatio = u_scale.x; vec2 pos = floor(a_pos_normal * 0.5); // x is 1 if it's a round cap, 0 otherwise diff --git a/test/integration/render-tests/fill-extrusion-pattern/1.5x-on-1x-add-image/expected.png b/test/integration/render-tests/fill-extrusion-pattern/1.5x-on-1x-add-image/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..554e1afa3a2e9e4eb9ea81cbcd6ebfb0a4a04f7e GIT binary patch literal 25729 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 literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/fill-extrusion-pattern/1.5x-on-1x-add-image/style.json b/test/integration/render-tests/fill-extrusion-pattern/1.5x-on-1x-add-image/style.json new file mode 100644 index 00000000000..833132a7a65 --- /dev/null +++ b/test/integration/render-tests/fill-extrusion-pattern/1.5x-on-1x-add-image/style.json @@ -0,0 +1,93 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 256, + "width": 256, + "pixelRatio": 1, + "operations": [ + [ + "wait" + ], + [ + "addImage", + "pattern", + "./image/marker.png", + { + "pixelRatio": 1.5 + } + ], + [ + "wait" + ], + [ + "addSource", + "geojson", + { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "property": 20 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -0.0001, + -0.0001 + ], + [ + -0.0001, + 0.0001 + ], + [ + 0.0001, + 0.0001 + ], + [ + 0.0001, + -0.0001 + ], + [ + -0.0001, + -0.0001 + ] + ] + ] + } + } + ] + } + } + ], + [ + "wait" + ], + [ + "addLayer", + { + "id": "extrusion", + "type": "fill-extrusion", + "source": "geojson", + "paint": { + "fill-extrusion-pattern": "pattern", + "fill-extrusion-height": 10 + } + } + ], + [ + "wait" + ] + ] + } + }, + "pitch": 60, + "zoom": 19, + "sources": {}, + "layers": [] +} diff --git a/test/integration/render-tests/fill-pattern/3x-on-2x-add-image/expected.png b/test/integration/render-tests/fill-pattern/3x-on-2x-add-image/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..3116105e8eb2073d8348d9484f49ae833d0ce237 GIT binary patch literal 3888 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 literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/line-pattern/3x-on-2x-add-image/style.json b/test/integration/render-tests/line-pattern/3x-on-2x-add-image/style.json new file mode 100644 index 00000000000..425c467c887 --- /dev/null +++ b/test/integration/render-tests/line-pattern/3x-on-2x-add-image/style.json @@ -0,0 +1,66 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 64, + "height": 32, + "pixelRatio": 2, + "operations": [ + [ + "wait" + ], + [ + "addImage", + "pattern", + "./image/marker.png", + { + "pixelRatio": 3 + } + ], + [ + "wait" + ], + [ + "addSource", + "geojson", + { + "type": "geojson", + "data": { + "type": "LineString", + "coordinates": [ + [ + -20, + 0 + ], + [ + 20, + 0 + ] + ] + } + } + ], + [ + "wait" + ], + [ + "addLayer", + { + "id": "a", + "type": "line", + "source": "geojson", + "paint": { + "line-width": 8, + "line-pattern": "pattern" + } + } + ], + [ + "wait" + ] + ] + } + }, + "sources": {}, + "layers": [] +}