diff --git a/OsmAnd/src/net/osmand/plus/views/Renderable.java b/OsmAnd/src/net/osmand/plus/views/Renderable.java index 5053f46a69..27d741d964 100644 --- a/OsmAnd/src/net/osmand/plus/views/Renderable.java +++ b/OsmAnd/src/net/osmand/plus/views/Renderable.java @@ -4,6 +4,7 @@ import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.PointF; import android.graphics.Shader; import net.osmand.GPXUtilities; @@ -67,6 +68,7 @@ public class Renderable { protected double zoom = -1; protected AsynchronousResampler culler = null; // The currently active resampler protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use + protected Paint borderPaint; protected GradientScaleType scaleType = null; protected GpxGeometryWay geometryWay; @@ -88,6 +90,10 @@ public class Renderable { paint.setStrokeWidth(p.getStrokeWidth()); } + public void setBorderPaint(@NonNull Paint paint) { + borderPaint = paint; + } + public void setGradientScaleType(GradientScaleType type) { this.scaleType = type; } @@ -177,33 +183,74 @@ public class Renderable { protected void drawGradient(List pts, Paint p, Canvas canvas, RotatedTileBox tileBox) { QuadRect tileBounds = tileBox.getLatLonBounds(); - Path path = new Path(); + Path currentPath = new Path(); + Path nextPath = new Path(); Paint paint = new Paint(this.paint); - WptPt prevPt = pts.get(0); + + WptPt prevWpt = pts.get(0); + WptPt currWpt = pts.get(1); + + PointF prevXY = new PointF(); + PointF currXY = new PointF(); + PointF nextXY = new PointF(); + + boolean currLineVisible = arePointsInsideTile(prevWpt, currWpt, tileBounds); + boolean nextLineVisible; + + if (currLineVisible) { + pixXYFromWptPt(tileBox, prevXY, prevWpt); + pixXYFromWptPt(tileBox, currXY, currWpt); + canvas.drawPath(pathFromStartEnd(currentPath, prevXY, currXY), borderPaint); + } + for (int i = 1; i < pts.size(); i++) { - WptPt currentPt = pts.get(i); - if (arePointsInsideTile(currentPt, prevPt, tileBounds)) { - float startX = tileBox.getPixXFromLatLon(prevPt.lat, prevPt.lon); - float startY = tileBox.getPixYFromLatLon(prevPt.lat, prevPt.lon); - float endX = tileBox.getPixXFromLatLon(currentPt.lat, currentPt.lon); - float endY = tileBox.getPixYFromLatLon(currentPt.lat, currentPt.lon); - int prevColor = prevPt.getColor(scaleType.toColorizationType()); - int currentColor = currentPt.getColor(scaleType.toColorizationType()); - LinearGradient gradient = new LinearGradient(startX, startY, endX, endY, prevColor, currentColor, Shader.TileMode.CLAMP); - paint.setShader(gradient); - path.reset(); - path.moveTo(startX, startY); - path.lineTo(endX, endY); - canvas.drawPath(path, paint); + currWpt = pts.get(i); + WptPt nextWpt = i + 1 == pts.size() ? null : pts.get(i + 1); + + nextLineVisible = arePointsInsideTile(currWpt, nextWpt, tileBounds); + if (nextWpt != null && nextLineVisible) { + pixXYFromWptPt(tileBox, currXY, currWpt); + pixXYFromWptPt(tileBox, nextXY, nextWpt); + canvas.drawPath(pathFromStartEnd(nextPath, currXY, nextXY), borderPaint); } - prevPt = currentPt; + + if (currLineVisible) { + int prevColor = prevWpt.getColor(scaleType.toColorizationType()); + int currentColor = currWpt.getColor(scaleType.toColorizationType()); + LinearGradient gradient = new LinearGradient(prevXY.x, prevXY.y, currXY.x, currXY.y, + prevColor, currentColor, Shader.TileMode.CLAMP); + paint.setShader(gradient); + canvas.drawPath(currentPath, paint); + } + + prevWpt = currWpt; + currentPath.set(nextPath); + prevXY.set(currXY); + currXY.set(nextXY); + currLineVisible = nextLineVisible; } } protected boolean arePointsInsideTile(WptPt first, WptPt second, QuadRect tileBounds) { + if (first == null || second == null) { + return false; + } return Math.min(first.lon, second.lon) < tileBounds.right && Math.max(first.lon, second.lon) > tileBounds.left && Math.min(first.lat, second.lat) < tileBounds.top && Math.max(first.lat, second.lat) > tileBounds.bottom; } + + protected PointF pixXYFromWptPt(RotatedTileBox tileBox, PointF pointF, WptPt wptPt) { + pointF.x = tileBox.getPixXFromLatLon(wptPt.lat, wptPt.lon); + pointF.y = tileBox.getPixYFromLatLon(wptPt.lat, wptPt.lon); + return pointF; + } + + protected Path pathFromStartEnd(Path path, PointF start, PointF end) { + path.reset(); + path.moveTo(start.x, start.y); + path.lineTo(end.x, end.y); + return path; + } } public static class StandardTrack extends RenderableSegment { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index c55fb675fd..c16d687614 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -95,6 +95,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM private OsmandMapTileView view; private Paint paint; + private Paint borderPaint; private Paint shadowPaint; private Paint paintIcon; @@ -190,6 +191,13 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM paint = new Paint(); paint.setStyle(Style.STROKE); paint.setAntiAlias(true); + + borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + borderPaint.setStyle(Style.STROKE); + borderPaint.setStrokeJoin(Paint.Join.ROUND); + borderPaint.setStrokeCap(Paint.Cap.ROUND); + borderPaint.setColor(0x80000000); + shadowPaint = new Paint(); shadowPaint.setStyle(Style.STROKE); shadowPaint.setAntiAlias(true); @@ -341,6 +349,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM } paint.setColor(color == 0 ? cachedColor : color); paint.setStrokeWidth(getTrackWidth(width, defaultTrackWidth)); + borderPaint.setStrokeWidth(paint.getStrokeWidth() + AndroidUtils.dpToPx(view.getContext(), 2)); } private void acquireTrackWidth(String widthKey, RenderingRulesStorage rrs, RenderingRuleSearchRequest req, RenderingContext rc) { @@ -700,6 +709,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM updatePaints(color, width, selectedGpxFile.isRoutePoints(), currentTrack, settings, tileBox); if (ts.renderer instanceof Renderable.RenderableSegment) { Renderable.RenderableSegment renderableSegment = (Renderable.RenderableSegment) ts.renderer; + renderableSegment.setBorderPaint(borderPaint); renderableSegment.setGradientScaleType(scaleType); renderableSegment.drawSegment(view.getZoom(), paint, canvas, tileBox); }