diff --git a/mapbox-gl-js b/mapbox-gl-js index 579abbad9fc..de365184e13 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 579abbad9fc8b83a2a2c1de114b2a77472bee52d +Subproject commit de365184e13c08fb42bbd93a08abfc8598294994 diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index d79a65c61e2..18d2c24aee5 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -27,7 +27,7 @@ void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) cons matrix::scale(matrix, matrix, s / util::EXTENT, s / util::EXTENT, 1); } -void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const { +void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ, bool aligned) const { if (size.isEmpty()) { return; } @@ -63,8 +63,8 @@ void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const { matrix::rotate_z(projMatrix, projMatrix, getAngle() + getNorthOrientationAngle()); - matrix::translate(projMatrix, projMatrix, pixel_x() - size.width / 2.0f, - pixel_y() - size.height / 2.0f, 0); + const double dx = pixel_x() - size.width / 2.0f, dy = pixel_y() - size.height / 2.0f; + matrix::translate(projMatrix, projMatrix, dx, dy, 0); if (axonometric) { // mat[11] controls perspective @@ -77,6 +77,21 @@ void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const { matrix::scale(projMatrix, projMatrix, 1, 1, 1.0 / Projection::getMetersPerPixelAtLatitude(getLatLng(LatLng::Unwrapped).latitude(), getZoom())); + + // Make a second projection matrix that is aligned to a pixel grid for rendering raster tiles. + // We're rounding the (floating point) x/y values to achieve to avoid rendering raster images to fractional + // coordinates. Additionally, we adjust by half a pixel in either direction in case that viewport dimension + // is an odd integer to preserve rendering to the pixel grid. We're rotating this shift based on the angle + // of the transformation so that 0°, 90°, 180°, and 270° rasters are crisp, and adjust the shift so that + // it is always <= 0.5 pixels. + if (aligned) { + const float xShift = float(size.width % 2) / 2, yShift = float(size.height % 2) / 2; + const double angleCos = std::cos(angle), angleSin = std::sin(angle); + double devNull; + const float dxa = -std::modf(dx, &devNull) + angleCos * xShift + angleSin * yShift; + const float dya = -std::modf(dy, &devNull) + angleCos * yShift + angleSin * xShift; + matrix::translate(projMatrix, projMatrix, dxa > 0.5 ? dxa - 1 : dxa, dya > 0.5 ? dya - 1 : dya, 0); + } } #pragma mark - Dimensions diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 0dd6d5a15e0..451802034d6 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -25,7 +25,7 @@ class TransformState { // Matrix void matrixFor(mat4&, const UnwrappedTileID&) const; - void getProjMatrix(mat4& matrix, uint16_t nearZ = 1) const; + void getProjMatrix(mat4& matrix, uint16_t nearZ = 1, bool aligned = false) const; // Dimensions Size getSize() const; diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp index 06616d90e58..b41b2ac560a 100644 --- a/src/mbgl/renderer/layers/render_raster_layer.cpp +++ b/src/mbgl/renderer/layers/render_raster_layer.cpp @@ -137,13 +137,13 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) { // Draw only the parts of the tile that aren't drawn by another tile in the layer. - draw(tile.matrix, + draw(parameters.matrixForTile(tile.id, true), *bucket.vertexBuffer, *bucket.indexBuffer, bucket.segments); } else { // Draw the full tile. - draw(tile.matrix, + draw(parameters.matrixForTile(tile.id, true), parameters.staticData.rasterVertexBuffer, parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.rasterSegments); diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp index 58dd5597a5c..a7f621eb616 100644 --- a/src/mbgl/renderer/paint_parameters.cpp +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -35,6 +35,10 @@ PaintParameters::PaintParameters(gl::Context& context_, // Update the default matrices to the current viewport dimensions. state.getProjMatrix(projMatrix); + // Also compute a projection matrix that aligns with the current pixel grid, taking into account + // odd viewport sizes. + state.getProjMatrix(alignedProjMatrix, 1, true); + // Calculate a second projection matrix with the near plane clipped to 100 so as // not to waste lots of depth buffer precision on very close empty space, for layer // types (fill-extrusion) that use the depth buffer to emulate real-world space. @@ -47,10 +51,10 @@ PaintParameters::PaintParameters(gl::Context& context_, } } -mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID) { +mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID, bool aligned) const { mat4 matrix; state.matrixFor(matrix, tileID); - matrix::multiply(matrix, projMatrix, matrix); + matrix::multiply(matrix, aligned ? alignedProjMatrix : projMatrix, matrix); return matrix; } diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index 5c934c2239b..41f46ae34e5 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -62,9 +62,10 @@ class PaintParameters { gl::StencilMode stencilModeForClipping(const ClipID&) const; gl::ColorMode colorModeForRenderPass() const; - mat4 matrixForTile(const UnwrappedTileID&); + mat4 matrixForTile(const UnwrappedTileID&, bool aligned = false) const; mat4 projMatrix; + mat4 alignedProjMatrix; mat4 nearClippedProjMatrix; int numSublayers = 3; diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp index 35b80cd9982..31a5916a341 100644 --- a/src/mbgl/renderer/sources/render_image_source.cpp +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -41,7 +41,7 @@ void RenderImageSource::startRender(PaintParameters& parameters) { mat4 matrix; matrix::identity(matrix); parameters.state.matrixFor(matrix, tileIds[i]); - matrix::multiply(matrix, parameters.projMatrix, matrix); + matrix::multiply(matrix, parameters.alignedProjMatrix, matrix); matrices.push_back(matrix); }