@Override protected void draw(final Canvas canvas, final MapView mapView, final boolean shadow) { if (shadow) { return; } final int size = mPoints.size(); if (size < 2) { // nothing to paint return; } final Projection pj = mapView.getProjection(); // 180° in longitude in pixels, for avoiding lines > 180° in length final int halfMapSize = TileSystem.MapSize(mapView.getProjection().getZoomLevel()) / 2; // southern Limit of the map in Pixels, for detecting map crossing lines final int southLimit = pj.toPixelsFromMercator(0, halfMapSize * 2, null).y - 1; final int northLimit = southLimit - halfMapSize * 2 + 1; // calculate clipBounds in screen coordinates for removing "invisible" line segments final Rect clipBounds = new Rect(0, 0, mapView.getWidth() - 1, mapView.getHeight() - 1); // take into account map orientation: if (mapView.getMapOrientation() != 0.0f) GeometryMath.getBoundingBoxForRotatatedRectangle( clipBounds, mapView.getMapOrientation(), clipBounds); // area of visible line segments Rect pathExtension = new Rect( clipBounds); // used for decision: canvas.draw (fast) or bitmap draw (slow, avoiding // OpenGLRenderer problem) boolean lineVisible = false; // precompute new points to the intermediate projection. precomputePoints(pj); final Point projectedPoint0 = mPoints.get(0); // points from the points list final Point screenPoint0 = pj.toPixelsFromProjected(projectedPoint0, mTempPoint1); // points on screen if (screenPoint0.y < northLimit) screenPoint0.y += 2 * halfMapSize; Point projectedPoint1; Point screenPoint1; mPath.rewind(); for (int i = 1; i < size; i++) { // compute next points projectedPoint1 = mPoints.get(i); screenPoint1 = pj.toPixelsFromProjected(projectedPoint1, this.mTempPoint2); if (screenPoint1.y < northLimit) screenPoint1.y += 2 * halfMapSize; if (Math.abs(screenPoint1.x - screenPoint0.x) + Math.abs(screenPoint1.y - screenPoint0.y) <= 1 && screenPoint0.y != southLimit && screenPoint1.y != southLimit) { // do not skip points crossing maps! // skip this point, too close to previous point continue; } // check for lines exceeding 180° in longitude, cut line into two segments if ((Math.abs(screenPoint1.x - screenPoint0.x) > halfMapSize) // check for lines crossing the southern limit || (screenPoint1.y > southLimit) != (screenPoint0.y > southLimit)) { // handle x and y coordinates separately int x0 = screenPoint0.x; int y0 = screenPoint0.y; int x1 = screenPoint1.x; int y1 = screenPoint1.y; // first check x if (Math.abs(screenPoint1.x - screenPoint0.x) > halfMapSize) { // x has to be adjusted if (screenPoint1.x < mapView.getWidth() / 2) { // screenPoint1 is left of screenPoint0 x1 += halfMapSize * 2; // set x1 360° east of screenPoint1 x0 -= halfMapSize * 2; // set x0 360° west of screenPoint0 } else { x1 -= halfMapSize * 2; x0 += halfMapSize * 2; } } // now check y if ((screenPoint1.y > southLimit) != (screenPoint0.y > southLimit)) { // line is crossing from one map to the other if (screenPoint1.y > southLimit) { // screenPoint1 was switched to map below y1 -= halfMapSize * 2; // set y1 into northern map y0 += halfMapSize * 2; // set y0 into map below } else { y1 += halfMapSize * 2; y0 -= halfMapSize * 2; } } // create rectangle of line segment, ensure top < bottom and right < left to obtain valid // rectangle! mLineBounds.set( Math.min(screenPoint0.x, x1), Math.min(screenPoint0.y, y1), Math.max(screenPoint0.x, x1), Math.max(screenPoint0.y, y1)); // check whether this line segment is visible if (Rect.intersects(clipBounds, mLineBounds)) { mPath.moveTo(screenPoint0.x, screenPoint0.y); mPath.lineTo(x1, y1); pathExtension.union( mLineBounds); // caution! buggy for invalid rectangles (top > bottom or right > left) // ! lineVisible = true; } screenPoint0.x = x0; screenPoint0.y = y0; } // end of line break check // check, whether this line segment is visible, ensure top < bottom and right < left to // obtain valid rectangle! mLineBounds.set( Math.min(screenPoint0.x, screenPoint1.x), Math.min(screenPoint0.y, screenPoint1.y), Math.max(screenPoint0.x, screenPoint1.x), Math.max(screenPoint0.y, screenPoint1.y)); if (Rect.intersects(clipBounds, mLineBounds)) { mPath.moveTo(screenPoint0.x, screenPoint0.y); mPath.lineTo(screenPoint1.x, screenPoint1.y); pathExtension.union(mLineBounds); lineVisible = true; } // update starting point to next position screenPoint0.x = screenPoint1.x; screenPoint0.y = screenPoint1.y; } // send only visible lines to canvas, not segments outside screen area if (lineVisible) { // check, whether visible line segments cover less than 2048 pixels rectangle. if (Math.max(pathExtension.width(), pathExtension.height()) <= 2048) { // Use fast canvas.drawPath method. canvas.drawPath(mPath, mPaint); } else { // fixing W/OpenGLRenderer(29239): Shape path too large to be rendered into a texture. // This will occur, if memory exceeds 2040 pixels => Path will disappear. // Draw path into temporary bitmap in screen size, then send bitmap to screen canvas. // <application android:hardwareAccelerated="false" > does not fix the OpenGLRenderer // problem! // Line (80, 80) to (-80, 80) will disappear at zoomLevel 4, Line (90, 90) to (-90, 90) at // zoomLevel 3 final Rect dest = new Rect(0, 0, mapView.getWidth() - 1, mapView.getHeight() - 1); // dest.set(0, 0, mapView.getWidth()-1, mapView.getHeight()-1); // final Bitmap.Config conf = Bitmap.Config.ARGB_8888; // 8 bit Aplpha, RGB if (mBmp == null || mBmp.getWidth() != mapView.getWidth() || mBmp.getHeight() != mapView.getHeight()) { // create Bitmap only once if needed mBmp = Bitmap.createBitmap( mapView.getWidth(), mapView.getHeight(), Bitmap.Config.ARGB_8888); // this most time consuming step, app. 30 ms! } else mBmp.eraseColor( Color .TRANSPARENT); // recycle bitmap and erase the old bitmap, much faster 0.6 .. 0.9 // ms final Canvas bmpcanvas = new Canvas(mBmp); // Draw path to bitmap according to actual screen rotation if (mapView.getMapOrientation() != 0.0f) bmpcanvas.rotate( mapView.getMapOrientation(), mapView.getWidth() / 2, mapView.getHeight() / 2); bmpcanvas.drawPath(mPath, mPaint); // Draw bitmap according to actual screen rotation, derotate canvas if necessary if (mapView.getMapOrientation() != 0.0f) canvas.rotate( -mapView.getMapOrientation(), mapView.getWidth() / 2, mapView.getHeight() / 2); canvas.drawBitmap(mBmp, dest, dest, null); // restore canvas rotation if necessary if (mapView.getMapOrientation() != 0.0f) canvas.rotate( mapView.getMapOrientation(), mapView.getWidth() / 2, mapView.getHeight() / 2); } } }
protected void drawOld(final Canvas canvas, final MapView mapView, final boolean shadow) { if (shadow) { return; } final int size = this.mPoints.size(); if (size < 2) { // nothing to paint return; } final Projection pj = mapView.getProjection(); // precompute new points to the intermediate projection. precomputePoints(pj); Point screenPoint0 = null; // points on screen Point screenPoint1; Point projectedPoint0; // points from the points list Point projectedPoint1; // clipping rectangle in the intermediate projection, to avoid performing projection. BoundingBoxE6 boundingBox = pj.getBoundingBox(); Point topLeft = pj.toProjectedPixels(boundingBox.getLatNorthE6(), boundingBox.getLonWestE6(), null); Point bottomRight = pj.toProjectedPixels(boundingBox.getLatSouthE6(), boundingBox.getLonEastE6(), null); final Rect clipBounds = new Rect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); // take into account map orientation: if (mapView.getMapOrientation() != 0.0f) GeometryMath.getBoundingBoxForRotatatedRectangle( clipBounds, mapView.getMapOrientation(), clipBounds); mPath.rewind(); projectedPoint0 = this.mPoints.get(size - 1); mLineBounds.set(projectedPoint0.x, projectedPoint0.y, projectedPoint0.x, projectedPoint0.y); for (int i = size - 2; i >= 0; i--) { // compute next points projectedPoint1 = this.mPoints.get(i); mLineBounds.union(projectedPoint1.x, projectedPoint1.y); if (!Rect.intersects(clipBounds, mLineBounds)) { // skip this line, move to next point projectedPoint0 = projectedPoint1; screenPoint0 = null; continue; } // the starting point may be not calculated, because previous segment was out of clip // bounds if (screenPoint0 == null) { screenPoint0 = pj.toPixelsFromProjected(projectedPoint0, this.mTempPoint1); mPath.moveTo(screenPoint0.x, screenPoint0.y); } screenPoint1 = pj.toPixelsFromProjected(projectedPoint1, this.mTempPoint2); // skip this point, too close to previous point if (Math.abs(screenPoint1.x - screenPoint0.x) + Math.abs(screenPoint1.y - screenPoint0.y) <= 1) { continue; } mPath.lineTo(screenPoint1.x, screenPoint1.y); // update starting point to next position projectedPoint0 = projectedPoint1; screenPoint0.x = screenPoint1.x; screenPoint0.y = screenPoint1.y; mLineBounds.set(projectedPoint0.x, projectedPoint0.y, projectedPoint0.x, projectedPoint0.y); } canvas.drawPath(mPath, mPaint); }