diff --git a/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java b/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java index 20429ec5a3..738894fc5d 100644 --- a/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java +++ b/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java @@ -47,17 +47,17 @@ import android.view.WindowManager; public class OsmandRenderer { private static final Log log = LogUtil.getLog(OsmandRenderer.class); - + private final int clFillScreen = Color.rgb(241, 238, 232); - + private TextPaint paintText; private Paint paint; - + private Paint paintFillEmpty; private Paint paintIcon; - + public static final int TILE_SIZE = 256; - + private Map dashEffect = new LinkedHashMap(); private Map shaders = new LinkedHashMap(); private Map cachedIcons = new LinkedHashMap(); @@ -65,17 +65,16 @@ public class OsmandRenderer { private final Context context; private DisplayMetrics dm; - + private int[] shadowarray; private int shadownum; - + private static class TextDrawInfo { - + public TextDrawInfo(String text){ this.text = text; } - public void fillProperties(RenderingContext rc, float centerX, float centerY){ this.centerX = centerX + rc.textDx; this.centerY = centerY + rc.textDy; @@ -106,37 +105,37 @@ public class OsmandRenderer { int textOrder = 20; } - + private static class IconDrawInfo { float x = 0; float y = 0; int resId; } - + /*package*/ static class RenderingContext { public boolean interrupted = false; public boolean nightMode = false; public boolean highResMode = false; public float mapTextSize = 1; - + List textToDraw = new ArrayList(); List iconsToDraw = new ArrayList(); - + float leftX; float topY; int width; int height; - + int zoom; float rotate; float tileDivisor; - + // debug purpose int pointCount = 0; int pointInsideCount = 0; int visible = 0; int allObjects = 0; - + // use to calculate points PointF tempPoint = new PointF(); float cosRotateTileSize; @@ -156,15 +155,16 @@ public class OsmandRenderer { boolean textBold; int textShield = 0; int textOrder = -1; - String renderingDebugInfo; + String renderingDebugInfo; + RenderingPaintProperties main = new RenderingPaintProperties(); RenderingPaintProperties second = new RenderingPaintProperties(); RenderingPaintProperties third = new RenderingPaintProperties(); RenderingPaintProperties[] adds = null; - - + + public void clearText() { showAnotherText = null; showTextOnPath = false; @@ -179,10 +179,10 @@ public class OsmandRenderer { textBold = false; textShield = 0; } - - + + } - + /* package*/ static class RenderingPaintProperties { int color; float strokeWidth; @@ -192,7 +192,7 @@ public class OsmandRenderer { PathEffect pathEffect; Shader shader; Cap cap; - + public void emptyLine(){ color = 0; strokeWidth = 0; @@ -203,7 +203,7 @@ public class OsmandRenderer { shadowColor = 0; shadowRadius = 0; } - + public void updatePaint(Paint p){ p.setStyle(fillArea ? Style.FILL_AND_STROKE : Style.STROKE); p.setColor(color); @@ -218,7 +218,7 @@ public class OsmandRenderer { p.setPathEffect(pathEffect); } } - + public void emptyArea(){ color = 0; strokeWidth = 0; @@ -230,7 +230,7 @@ public class OsmandRenderer { shadowRadius = 0; } } - + public OsmandRenderer(Context context) { this.context = context; @@ -255,7 +255,7 @@ public class OsmandRenderer { WindowManager wmgr = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); wmgr.getDefaultDisplay().getMetrics(dm); } - + public PathEffect getDashEffect(String dashes){ if(!dashEffect.containsKey(dashes)){ String[] ds = dashes.split("_"); //$NON-NLS-1$ @@ -267,7 +267,7 @@ public class OsmandRenderer { } return dashEffect.get(dashes); } - + public Shader getShader(int resId){ if(shaders.get(resId) == null){ Shader sh = new BitmapShader( @@ -276,19 +276,19 @@ public class OsmandRenderer { } return shaders.get(resId); } - + private void put(TFloatObjectHashMap map, Float k, int v, int init){ if(!map.containsKey(k)){ map.put(k, new TIntArrayList()); } map.get(k).add(v); } - - + + public Bitmap generateNewBitmap(RenderingContext rc, List objects, Bitmap bmp, boolean useEnglishNames, BaseOsmandRender renderer, List notifyList) { long now = System.currentTimeMillis(); - + // fill area Canvas cv = new Canvas(bmp); if(renderer != null){ @@ -298,7 +298,7 @@ public class OsmandRenderer { } } cv.drawRect(0, 0, bmp.getWidth(), bmp.getHeight(), paintFillEmpty); - + // put in order map int sz = objects.size(); int init = sz / 4; @@ -337,45 +337,45 @@ public class OsmandRenderer { } } } - + if (objects != null && !objects.isEmpty() && rc.width > 0 && rc.height > 0) { // init rendering context rc.tileDivisor = (int) (1 << (31 - rc.zoom)); rc.cosRotateTileSize = FloatMath.cos((float) Math.toRadians(rc.rotate)) * TILE_SIZE; rc.sinRotateTileSize = FloatMath.sin((float) Math.toRadians(rc.rotate)) * TILE_SIZE; - + //int shadow = 0; // no shadow (minumum CPU) //int shadow = 1; // classic shadow (the implementaton in master) //int shadow = 2; // blur shadow (most CPU, but still reasonable) int shadow = 3; // solid border (CPU use like classic version or even smaller) boolean repeat = false; - + float[] keys = orderMap.keys(); Arrays.sort(keys); int objCount = 0; - + shadowarray = new int[keys.length]; shadownum = 0; - + for (int k = 0; k < keys.length; k++) { - + if(repeat == true && shadowarray[shadownum] == k){ shadownum++; continue; } - + TIntArrayList list = orderMap.get(keys[k]); - + for (int j = 0; j < list.size(); j++) { int i = list.get(j); int ind = i >> 8; - int l = i & 0xff; - BinaryMapDataObject obj = objects.get(ind); + int l = i & 0xff; + BinaryMapDataObject obj = objects.get(ind); - // show text only for main type - drawObj(obj, renderer, cv, rc, l, l == 0, shadow, k); - - objCount++; + // show text only for main type + drawObj(obj, renderer, cv, rc, l, l == 0, shadow, k); + + objCount++; } if(objCount > 25){ notifyListeners(notifyList); @@ -384,7 +384,7 @@ public class OsmandRenderer { if(rc.interrupted){ return null; } - + // order = 57 should be set as limit for shadows if(keys[k] > 57 && repeat == false && shadow > 1){ shadow = 0; @@ -395,9 +395,9 @@ public class OsmandRenderer { } notifyListeners(notifyList); long beforeIconTextTime = System.currentTimeMillis() - now; - + int skewConstant = (int) getDensityValue(rc, 16); - + int iconsW = rc.width / skewConstant ; int iconsH = rc.height / skewConstant; int[] alreadyDrawnIcons = new int[iconsW * iconsH / 32]; @@ -436,9 +436,9 @@ public class OsmandRenderer { "(%s points, %s points inside, %s objects visile from %s)",//$NON-NLS-1$ time, time - beforeIconTextTime,rc.pointCount, rc.pointInsideCount, rc.visible, rc.allObjects); log.info(rc.renderingDebugInfo); - + } - + return bmp; } @@ -462,7 +462,7 @@ public class OsmandRenderer { } } private final static boolean findAllTextIntersections = true; - + private float getDensityValue(RenderingContext rc, float val) { if (rc.highResMode && dm.density > 1) { return val * dm.density * rc.mapTextSize; @@ -480,7 +480,7 @@ public class OsmandRenderer { public int compare(RectF object1, RectF object2) { return Float.compare(object1.left, object2.left); } - + }; // 1. Sort text using text order @@ -503,8 +503,8 @@ public class OsmandRenderer { if(useEnglishNames){ text.text = Junidecode.unidecode(text.text); } - - + + // sest text size before finding intersection (it is used there) float textSize = getDensityValue(rc, text.textSize); paintText.setTextSize(textSize); @@ -512,14 +512,14 @@ public class OsmandRenderer { paintText.setColor(text.textColor); // align center y text.centerY += (-paintText.ascent()); - + // calculate if there is intersection boolean intersects = findTextIntersection(rc, boundsNotPathIntersect, boundsPathIntersect, c, text); if(intersects){ continue nextText; } - - + + if(text.drawOnPath != null){ if(text.textShadow > 0){ paintText.setColor(Color.WHITE); @@ -544,7 +544,7 @@ public class OsmandRenderer { , paintIcon); } } - + drawWrappedText(cv, text, textSize); } } @@ -556,7 +556,7 @@ public class OsmandRenderer { // set maximum for all text text.textWrap = 40; } - + if(text.text.length() > text.textWrap){ int start = 0; int end = text.text.length(); @@ -584,13 +584,13 @@ public class OsmandRenderer { limit += (start - pos) - 1; } line++; - + } } else { drawTextOnCanvas(cv, text.text, text.centerX, text.centerY, paintText, text.textShadow); } } - + private void drawTextOnCanvas(Canvas cv, String text, float centerX, float centerY, Paint paint, float textShadow){ if(textShadow > 0){ int c = paintText.getColor(); @@ -605,18 +605,18 @@ public class OsmandRenderer { } cv.drawText(text, centerX, centerY, paint); } - + private boolean findTextIntersection(RenderingContext rc, List boundsNotPathIntersect, List boundsPathIntersect, Comparator c, TextDrawInfo text) { boolean horizontalWayDisplay = (text.pathRotate > 45 && text.pathRotate < 135) || (text.pathRotate > 225 && text.pathRotate < 315); // text.minDistance = 0; float textWidth = paintText.measureText(text.text) + (!horizontalWayDisplay ? 0 : text.minDistance); - // Paint.ascent is negative, so negate it. + // Paint.ascent is negative, so negate it. int ascent = (int) Math.ceil(-paintText.ascent()); int descent = (int) Math.ceil(paintText.descent()); float textHeight = ascent + descent + (horizontalWayDisplay ? 0 : text.minDistance) + getDensityValue(rc, 5); - + RectF bounds = new RectF(); if(text.drawOnPath == null || horizontalWayDisplay){ bounds.set(text.centerX - textWidth / 2, text.centerY - textHeight / 2 , @@ -661,9 +661,9 @@ public class OsmandRenderer { st = 0; } // test functionality -// cv.drawRect(bounds, paint); -// cv.drawText(text.text.substring(0, Math.min(5, text.text.length())), bounds.centerX(), bounds.centerY(), paint); - + // cv.drawRect(bounds, paint); + // cv.drawText(text.text.substring(0, Math.min(5, text.text.length())), bounds.centerX(), bounds.centerY(), paint); + for (int j = st; j < e; j++) { RectF b = boundsIntersect.get(j); float x = Math.min(bounds.right, b.right) - Math.max(b.left, bounds.left); @@ -673,21 +673,21 @@ public class OsmandRenderer { } } // store in list sorted by left boundary -// if(text.minDistance > 0){ -// if (verticalText) { -// bounds.set(bounds.left + text.minDistance / 2, bounds.top, -// bounds.right - text.minDistance / 2, bounds.bottom); -// } else { -// bounds.set(bounds.left, bounds.top + text.minDistance / 2, bounds.right, -// bounds.bottom - text.minDistance / 2); -// } -// } - boundsIntersect.add(index, bounds); + // if(text.minDistance > 0){ + // if (verticalText) { + // bounds.set(bounds.left + text.minDistance / 2, bounds.top, + // bounds.right - text.minDistance / 2, bounds.bottom); + // } else { + // bounds.set(bounds.left, bounds.top + text.minDistance / 2, bounds.right, + // bounds.bottom - text.minDistance / 2); + // } + // } + boundsIntersect.add(index, bounds); } return false; } - + protected void drawObj(BinaryMapDataObject obj, BaseOsmandRender render, Canvas canvas, RenderingContext rc, int l, boolean renderText , int shadow, int index) { rc.allObjects++; @@ -715,8 +715,8 @@ public class OsmandRenderer { } } - - + + private PointF calcPoint(BinaryMapDataObject o, int ind, RenderingContext rc){ rc.pointCount ++; float tx = o.getPoint31XTile(ind) / rc.tileDivisor; @@ -732,7 +732,7 @@ public class OsmandRenderer { } return rc.tempPoint; } - + private PointF calcMultiPolygonPoint(MultyPolygon o, int i, int b, RenderingContext rc){ rc.pointCount ++; float tx = o.getPoint31XTile(i, b)/ rc.tileDivisor; @@ -749,16 +749,16 @@ public class OsmandRenderer { return rc.tempPoint; } - - - - + + + + public void clearCachedResources(){ cachedIcons.clear(); shaders.clear(); } - + private void drawMultiPolygon(BinaryMapDataObject obj, BaseOsmandRender render, Canvas canvas, RenderingContext rc) { String tag = ((MultyPolygon)obj).getTag(); String value = ((MultyPolygon)obj).getValue(); @@ -768,7 +768,7 @@ public class OsmandRenderer { rc.main.emptyArea(); rc.second.emptyLine(); rc.main.color = Color.rgb(245, 245, 245); - + boolean rendered = render.renderPolygon(tag, value, rc.zoom, rc, this, rc.nightMode); if(!rendered){ return; @@ -799,18 +799,18 @@ public class OsmandRenderer { rc.main.updatePaint(paint); canvas.drawPath(path, paint); // for test purpose -// rc.second.strokeWidth = 1.5f; -// rc.second.color = Color.BLACK; - + // rc.second.strokeWidth = 1.5f; + // rc.second.color = Color.BLACK; + if (rc.second.strokeWidth != 0) { rc.second.updatePaint(paint); canvas.drawPath(path, paint); } } - - + + private void drawPolygon(BinaryMapDataObject obj, BaseOsmandRender render, Canvas canvas, RenderingContext rc, TagValuePair pair) { - + if(render == null || pair == null){ return; } @@ -821,7 +821,7 @@ public class OsmandRenderer { rc.main.emptyArea(); rc.second.emptyLine(); // rc.main.color = Color.rgb(245, 245, 245); - + boolean rendered = render.renderPolygon(pair.tag, pair.value, zoom, rc, this, rc.nightMode); if(!rendered){ return; @@ -889,13 +889,13 @@ public class OsmandRenderer { rc.textToDraw.add(info); } } - - + + private void drawPoint(BinaryMapDataObject obj, BaseOsmandRender render, Canvas canvas, RenderingContext rc, TagValuePair pair, boolean renderText) { if(render == null || pair == null){ return; } - + Integer resId = render.getPointIcon(pair.tag, pair.value, rc.zoom, rc.nightMode); String name = null; if (renderText) { @@ -916,7 +916,7 @@ public class OsmandRenderer { ps.x /= len; ps.y /= len; } - + if(resId != null && resId != 0){ IconDrawInfo ico = new IconDrawInfo(); ico.x = ps.x; @@ -927,34 +927,56 @@ public class OsmandRenderer { if (name != null) { drawPointText(render, rc, pair, ps.x, ps.y, name); } - + } - -private void drawPolylineWithShadow(Canvas canvas, Path path, int shadow, int shadowRadius, int index){ - // option shadow = 1 ,2 don't need any changes in first draw - // option shadow = 0 without any shadows - if(shadow == 0) paint.setShadowLayer(0, 0, 0, 0); - // option shadow = 3 with solid border - if(shadow == 3){ - paint.setShadowLayer(0, 0, 0, 0); - paint.setStrokeWidth(paint.getStrokeWidth() + 2); - paint.setColor(0xffbababa); - } - canvas.drawPath(path, paint); - - //check for shadow and save index in array - if(shadowRadius > 0 && shadow > 1){ - if(shadownum == 0){ - shadowarray[shadownum] = index; - shadownum++; + + private void drawPolylineWithShadow(Canvas canvas, Path path, int shadow, int shadowRadius, int index){ + //if(paint.getStrokeCap() == Paint.Cap.ROUND)paint.setStrokeCap(Paint.Cap.SQUARE); + //if(paint.getStrokeCap() == Paint.Cap.ROUND)paint.setStrokeCap(Paint.Cap.BUTT); + + if(paint.getPathEffect() != null){ + paint.setStrokeCap(Paint.Cap.BUTT); } + + // no shadow + if(shadow == 0){ + paint.setShadowLayer(0, 0, 0, 0); + canvas.drawPath(path, paint); + } + + //classic ugly shadows + if(shadow == 1){ + canvas.drawPath(path, paint); + } + + // blurred shadows + if(shadow == 2){ + if(paint.getPathEffect() == null) paint.setColor(0xffffffff); + canvas.drawPath(path, paint); + } + + // option shadow = 3 with solid border + if(shadow == 3 && shadowRadius > 0){ + paint.setShadowLayer(0, 0, 0, 0); + paint.setStrokeWidth(paint.getStrokeWidth() + 2); + if(paint.getPathEffect() == null) paint.setColor(0xffbababa); + canvas.drawPath(path, paint); + } + + + //check for shadow and save index in array + if(shadowRadius > 0 && shadow > 1){ + if(shadownum == 0){ + shadowarray[shadownum] = index; + shadownum++; + } if (shadowarray[shadownum-1] != index){ - shadowarray[shadownum] = index; - shadownum++; + shadowarray[shadownum] = index; + shadownum++; } } } - + private void drawPolyline(BinaryMapDataObject obj, BaseOsmandRender render, Canvas canvas, RenderingContext rc, TagValuePair pair, int layer, int shadow, int index) { if(render == null || pair == null){ @@ -975,10 +997,10 @@ private void drawPolylineWithShadow(Canvas canvas, Path path, int shadow, int sh if(rc.zoom >= 16 && "highway".equals(pair.tag) && MapRenderingTypes.isOneWayWay(obj.getHighwayAttributes())){ //$NON-NLS-1$ rc.adds = getOneWayProperties(); } - - + + rc.visible++; - + Path path = null; float pathRotate = 0; float roadLength = 0; @@ -989,7 +1011,7 @@ private void drawPolylineWithShadow(Canvas canvas, Path path, int shadow, int sh float yMid = 0; PointF middlePoint = new PointF(); int middle = obj.getPointsLength() / 2; - + for (int i = 0; i < length ; i++) { PointF p = calcPoint(obj, i, rc); if(i == 0 || i == length -1){ @@ -1058,9 +1080,9 @@ private void drawPolylineWithShadow(Canvas canvas, Path path, int shadow, int sh text.fillProperties(rc, middlePoint.x, middlePoint.y); text.pathRotate = pathRotate; rc.textToDraw.add(text); - + } - + if(name != null && name.trim().length() > 0){ rc.clearText(); name = render.renderObjectText(name, pair.tag, pair.value, rc, false, rc.nightMode); @@ -1093,7 +1115,7 @@ private void drawPolylineWithShadow(Canvas canvas, Path path, int shadow, int sh } } } - + } } } @@ -1112,28 +1134,28 @@ private void drawPolylineWithShadow(Canvas canvas, Path path, int shadow, int sh oneWay[0].color = 0xff6c70d5; oneWay[0].strokeWidth = 1; oneWay[0].pathEffect = arrowDashEffect1; - + oneWay[1] = new RenderingPaintProperties(); oneWay[1].emptyLine(); oneWay[1].color = 0xff6c70d5; oneWay[1].strokeWidth = 2; oneWay[1].pathEffect = arrowDashEffect2; - + oneWay[2] = new RenderingPaintProperties(); oneWay[2].emptyLine(); oneWay[2].color = 0xff6c70d5; oneWay[2].strokeWidth = 3; oneWay[2].pathEffect = arrowDashEffect3; - + oneWay[3] = new RenderingPaintProperties(); oneWay[3].emptyLine(); oneWay[3].color = 0xff6c70d5; oneWay[3].strokeWidth = 4; oneWay[3].pathEffect = arrowDashEffect4; - + } return oneWay; } - + }