Skip to content

Commit

Permalink
fix anti-aliasing for fill-patterns
Browse files Browse the repository at this point in the history
update package.json with new test-suite sha, rename imageOffset -> setPattern

include distance interpolation for opacity

fix linting tests

refactor image offset calculations

use distance from line to calculate alpha, change to multiplication for combining u_opacity and alpha

add u_world to use_program for outline pattern, refactor using new imageOffset function

refactor draw_fill, fix conditionals
  • Loading branch information
Molly Lloyd committed Apr 4, 2016
1 parent 3f86fbf commit 6a985a6
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 61 deletions.
152 changes: 92 additions & 60 deletions js/render/draw_fill.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,48 @@ function draw(painter, source, layer, coords) {
}

// Draw stroke
if (!painter.isOpaquePass && layer.paint['fill-antialias'] && !(layer.paint['fill-pattern'] && !strokeColor)) {
var outlineProgram = painter.useProgram('outline');
painter.lineWidth(2);
painter.depthMask(false);

if (strokeColor) {
// If we defined a different color for the fill outline, we are
// going to ignore the bits in 0x07 and just care about the global
// clipping mask.
painter.setDepthSublayer(2);

if (!painter.isOpaquePass && layer.paint['fill-antialias']) {
if (strokeColor || !layer.paint['fill-pattern']) {
var outlineProgram = painter.useProgram('outline');
painter.lineWidth(2);
painter.depthMask(false);

if (strokeColor) {
// If we defined a different color for the fill outline, we are
// going to ignore the bits in 0x07 and just care about the global
// clipping mask.
painter.setDepthSublayer(2);
} else {
// Otherwise, we only want to drawFill the antialiased parts that are
// *outside* the current shape. This is important in case the fill
// or stroke color is translucent. If we wouldn't clip to outside
// the current shape, some pixels from the outline stroke overlapped
// the (non-antialiased) fill.
painter.setDepthSublayer(0);
}
gl.uniform2f(outlineProgram.u_world, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.uniform4fv(outlineProgram.u_color, strokeColor ? strokeColor : color);

for (var j = 0; j < coords.length; j++) {
drawStroke(painter, source, layer, coords[j]);
}
} else {
var outlinePatternProgram = painter.useProgram('outlinepattern');
painter.lineWidth(2);
painter.depthMask(false);
// Otherwise, we only want to drawFill the antialiased parts that are
// *outside* the current shape. This is important in case the fill
// or stroke color is translucent. If we wouldn't clip to outside
// the current shape, some pixels from the outline stroke overlapped
// the (non-antialiased) fill.
painter.setDepthSublayer(0);
}

gl.uniform2f(outlineProgram.u_world, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.uniform4fv(outlineProgram.u_color, strokeColor ? strokeColor : color);
gl.uniform2f(outlinePatternProgram.u_world, gl.drawingBufferWidth, gl.drawingBufferHeight);

for (var j = 0; j < coords.length; j++) {
drawStroke(painter, source, layer, coords[j]);
for (var k = 0; k < coords.length; k++) {
drawStroke(painter, source, layer, coords[k]);
}
}

}
}

Expand Down Expand Up @@ -125,49 +141,8 @@ function drawFill(painter, source, layer, coord) {

if (image) {
// Draw texture fill
var imagePosA = painter.spriteAtlas.getPosition(image.from, true);
var imagePosB = painter.spriteAtlas.getPosition(image.to, true);
if (!imagePosA || !imagePosB) return;

program = painter.useProgram('pattern', posMatrix);
gl.uniform1i(program.u_image, 0);
gl.uniform2fv(program.u_pattern_tl_a, imagePosA.tl);
gl.uniform2fv(program.u_pattern_br_a, imagePosA.br);
gl.uniform2fv(program.u_pattern_tl_b, imagePosB.tl);
gl.uniform2fv(program.u_pattern_br_b, imagePosB.br);
gl.uniform1f(program.u_opacity, opacity);
gl.uniform1f(program.u_mix, image.t);

var imageSizeScaledA = [
(imagePosA.size[0] * image.fromScale),
(imagePosA.size[1] * image.fromScale)
];
var imageSizeScaledB = [
(imagePosB.size[0] * image.toScale),
(imagePosB.size[1] * image.toScale)
];

gl.uniform2fv(program.u_patternscale_a, [
1 / pixelsToTileUnits(tile, imageSizeScaledA[0], painter.transform.tileZoom),
1 / pixelsToTileUnits(tile, imageSizeScaledA[1], painter.transform.tileZoom)
]);

gl.uniform2fv(program.u_patternscale_b, [
1 / pixelsToTileUnits(tile, imageSizeScaledB[0], painter.transform.tileZoom),
1 / pixelsToTileUnits(tile, imageSizeScaledB[1], painter.transform.tileZoom)
]);

var tileSizeAtNearestZoom = tile.tileSize * Math.pow(2, painter.transform.tileZoom - tile.coord.z);

// shift images to match at tile boundaries
var offsetAx = ((tileSizeAtNearestZoom / imageSizeScaledA[0]) % 1) * (tile.coord.x + coord.w * Math.pow(2, tile.coord.z));
var offsetAy = ((tileSizeAtNearestZoom / imageSizeScaledA[1]) % 1) * tile.coord.y;

var offsetBx = ((tileSizeAtNearestZoom / imageSizeScaledB[0]) % 1) * (tile.coord.x + coord.w * Math.pow(2, tile.coord.z));
var offsetBy = ((tileSizeAtNearestZoom / imageSizeScaledB[1]) % 1) * tile.coord.y;

gl.uniform2fv(program.u_offset_a, [offsetAx, offsetAy]);
gl.uniform2fv(program.u_offset_b, [offsetBx, offsetBy]);
setPattern(image, opacity, tile, coord, painter, program);

gl.activeTexture(gl.TEXTURE0);
painter.spriteAtlas.bind(gl, true);
Expand All @@ -194,7 +169,10 @@ function drawStroke(painter, source, layer, coord) {

var gl = painter.gl;
var elementGroups = bucket.elementGroups.fill;
var program = painter.useProgram('outline');

var image = layer.paint['fill-pattern'];
var opacity = layer.paint['fill-opacity'];
var program = image ? painter.useProgram('outlinepattern') : painter.useProgram('outline');

painter.setPosMatrix(painter.translatePosMatrix(
painter.calculatePosMatrix(coord, source.maxzoom),
Expand All @@ -203,6 +181,8 @@ function drawStroke(painter, source, layer, coord) {
layer.paint['fill-translate-anchor']
));

if (image) { setPattern(image, opacity, tile, coord, painter, program); }

// Draw all buffers
var vertex = bucket.buffers.fillVertex;
var elements = bucket.buffers.fillSecondElement;
Expand All @@ -221,3 +201,55 @@ function drawStroke(painter, source, layer, coord) {
gl.drawElements(gl.LINES, count, gl.UNSIGNED_SHORT, elementOffset);
}
}


function setPattern(image, opacity, tile, coord, painter, program) {
var gl = painter.gl;

var imagePosA = painter.spriteAtlas.getPosition(image.from, true);
var imagePosB = painter.spriteAtlas.getPosition(image.to, true);
if (!imagePosA || !imagePosB) return;


gl.uniform1i(program.u_image, 0);
gl.uniform2fv(program.u_pattern_tl_a, imagePosA.tl);
gl.uniform2fv(program.u_pattern_br_a, imagePosA.br);
gl.uniform2fv(program.u_pattern_tl_b, imagePosB.tl);
gl.uniform2fv(program.u_pattern_br_b, imagePosB.br);
gl.uniform1f(program.u_opacity, opacity);
gl.uniform1f(program.u_mix, image.t);

var imageSizeScaledA = [
(imagePosA.size[0] * image.fromScale),
(imagePosA.size[1] * image.fromScale)
];
var imageSizeScaledB = [
(imagePosB.size[0] * image.toScale),
(imagePosB.size[1] * image.toScale)
];

gl.uniform2fv(program.u_patternscale_a, [
1 / pixelsToTileUnits(tile, imageSizeScaledA[0], painter.transform.tileZoom),
1 / pixelsToTileUnits(tile, imageSizeScaledA[1], painter.transform.tileZoom)
]);

gl.uniform2fv(program.u_patternscale_b, [
1 / pixelsToTileUnits(tile, imageSizeScaledB[0], painter.transform.tileZoom),
1 / pixelsToTileUnits(tile, imageSizeScaledB[1], painter.transform.tileZoom)
]);

var tileSizeAtNearestZoom = tile.tileSize * Math.pow(2, painter.transform.tileZoom - tile.coord.z);

// shift images to match at tile boundaries
var offsetAx = ((tileSizeAtNearestZoom / imageSizeScaledA[0]) % 1) * (tile.coord.x + coord.w * Math.pow(2, tile.coord.z));
var offsetAy = ((tileSizeAtNearestZoom / imageSizeScaledA[1]) % 1) * tile.coord.y;

var offsetBx = ((tileSizeAtNearestZoom / imageSizeScaledB[0]) % 1) * (tile.coord.x + coord.w * Math.pow(2, tile.coord.z));
var offsetBy = ((tileSizeAtNearestZoom / imageSizeScaledB[1]) % 1) * tile.coord.y;

gl.uniform2fv(program.u_offset_a, [offsetAx, offsetAy]);
gl.uniform2fv(program.u_offset_b, [offsetBx, offsetBy]);

gl.activeTexture(gl.TEXTURE0);
painter.spriteAtlas.bind(gl, true);
}
6 changes: 6 additions & 0 deletions js/render/painter/use_program.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ var definitions = {
attributeNames: ['a_pos'],
uniformNames: ['u_matrix', 'u_color', 'u_world']
},
outlinepattern: {
fragmentSource: fs.readFileSync(path.join(__dirname, '../../../shaders/outlinepattern.fragment.glsl'), 'utf8'),
vertexSource: fs.readFileSync(path.join(__dirname, '../../../shaders/outlinepattern.vertex.glsl'), 'utf8'),
attributeNames: ['a_pos'],
uniformNames: ['u_matrix', 'u_world', 'u_pattern_tl_a', 'u_pattern_br_a', 'u_pattern_tl_b', 'u_pattern_br_b', 'u_mix', 'u_patternscale_a', 'u_patternscale_b', 'u_opacity', 'u_image', 'u_offset_a', 'u_offset_b']
},
pattern: {
fragmentSource: fs.readFileSync(path.join(__dirname, '../../../shaders/pattern.fragment.glsl'), 'utf8'),
vertexSource: fs.readFileSync(path.join(__dirname, '../../../shaders/pattern.vertex.glsl'), 'utf8'),
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"express": "^4.13.4",
"gl": "^2.1.5",
"istanbul": "^0.4.2",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#a42cd0cb818356d91fc6b61e2b64801e57486ac0",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#d974ec6b3748a258f8ddd7528e049493390177b4",
"nyc": "^6.1.1",
"sinon": "^1.15.4",
"st": "^1.0.0",
Expand Down
32 changes: 32 additions & 0 deletions shaders/outlinepattern.fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
precision mediump float;

uniform float u_opacity;
uniform vec2 u_pattern_tl_a;
uniform vec2 u_pattern_br_a;
uniform vec2 u_pattern_tl_b;
uniform vec2 u_pattern_br_b;
uniform float u_mix;

uniform sampler2D u_image;

varying vec2 v_pos_a;
varying vec2 v_pos_b;
varying vec2 v_pos;

void main() {
vec2 imagecoord = mod(v_pos_a, 1.0);
vec2 pos = mix(u_pattern_tl_a, u_pattern_br_a, imagecoord);
vec4 color1 = texture2D(u_image, pos);

vec2 imagecoord_b = mod(v_pos_b, 1.0);
vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b);
vec4 color2 = texture2D(u_image, pos2);

// find distance to outline for alpha interpolation

float dist = length(v_pos - gl_FragCoord.xy);
float alpha = smoothstep(1.0, 0.0, dist);


gl_FragColor = mix(color1, color2, u_mix) * alpha * u_opacity;
}
23 changes: 23 additions & 0 deletions shaders/outlinepattern.vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
precision highp float;

uniform vec2 u_patternscale_a;
uniform vec2 u_patternscale_b;
uniform vec2 u_offset_a;
uniform vec2 u_offset_b;

attribute vec2 a_pos;

uniform mat4 u_matrix;
uniform vec2 u_world;

varying vec2 v_pos_a;
varying vec2 v_pos_b;
varying vec2 v_pos;


void main() {
gl_Position = u_matrix * vec4(a_pos, 0, 1);
v_pos_a = u_patternscale_a * a_pos + u_offset_a;
v_pos_b = u_patternscale_b * a_pos + u_offset_b;
v_pos = (gl_Position.xy/gl_Position.w + 1.0) / 2.0 * u_world;
}

0 comments on commit 6a985a6

Please sign in to comment.