package org.osmdroid.views.overlay;
import org.osmdroid.views.MapView;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PorterDuff.Mode;
import android.os.Build;
import android.util.Log;
import org.osmdroid.api.IMapView;
/**
* This will allow an {@link Overlay} that is not HW acceleration compatible to work in a HW
* accelerated MapView. It will create a screen-sized backing Bitmap for the Overlay to draw to and
* then will draw the Bitmap to the HW accelerated canvas. Due to the extra work, it does not draw
* the shadow layer. If the Canvas passed into the Overlay is not HW accelerated or if
* {@link #isUsingBackingBitmap()} returns false then it draws normally (including the shadow layer)
* without the backing Bitmap. <br>
* <br>
* TODO:
* <ol>
* <li>Implement a flag to determine if the drawing has actually changed. If not, then reuse the
* last frame's backing bitmap. This will prevent having to re-upload the bitmap texture to GPU.</li>
* </ol>
*/
public abstract class NonAcceleratedOverlay extends Overlay {
private Bitmap mBackingBitmap;
private Canvas mBackingCanvas;
private final Matrix mBackingMatrix = new Matrix();
private final Matrix mCanvasIdentityMatrix = new Matrix();
/**
* A delegate for {@link #draw(Canvas, MapView, boolean)}.
*/
protected abstract void onDraw(Canvas c, MapView osmv, boolean shadow);
/** Use {@link #NonAcceleratedOverlay()} instead */
@Deprecated
public NonAcceleratedOverlay(Context ctx) {
super(ctx);
}
public NonAcceleratedOverlay() {
super();
}
/**
* Override if you really want access to the original (possibly) accelerated canvas.
*/
protected void onDraw(Canvas c, Canvas acceleratedCanvas, MapView osmv, boolean shadow) {
onDraw(c, osmv, shadow);
}
/**
* Allow forcing this overlay to skip drawing using backing Bitmap by returning false.
*/
public boolean isUsingBackingBitmap() {
return true;
}
@Override
public void onDetach(MapView mapView) {
mBackingBitmap = null;
mBackingCanvas = null;
super.onDetach(mapView);
}
@Override
public final void draw(Canvas c, MapView osmv, boolean shadow) {
// First check to see if we want to use the backing bitmap
final boolean atLeastHoneycomb = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
if (isUsingBackingBitmap() && atLeastHoneycomb && c.isHardwareAccelerated()) {
// Drawing a shadow layer would require a second backing Bitmap due to the way HW
// accelerated drawBitmap works. One could extend this Overlay to implement that if
// needed.
if (shadow)
return;
// If we don't have any drawing area, then don't draw
if (c.getWidth() == 0 || c.getHeight() == 0)
return;
if (mBackingBitmap == null || mBackingBitmap.getWidth() != c.getWidth()
|| mBackingBitmap.getHeight() != c.getHeight()) {
mBackingBitmap = null;
mBackingCanvas = null;
try {
mBackingBitmap = Bitmap.createBitmap(c.getWidth(), c.getHeight(),
Config.ARGB_8888);
} catch (OutOfMemoryError e) {
Log.e(IMapView.LOGTAG,"OutOfMemoryError creating backing bitmap in NonAcceleratedOverlay.");
System.gc();
return;
}
mBackingCanvas = new Canvas(mBackingBitmap);
}
mBackingCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
c.getMatrix(mBackingMatrix);
mBackingCanvas.setMatrix(mBackingMatrix);
onDraw(mBackingCanvas, c, osmv, shadow);
c.save();
c.getMatrix(mCanvasIdentityMatrix);
mCanvasIdentityMatrix.invert(mCanvasIdentityMatrix);
c.concat(mCanvasIdentityMatrix);
c.drawBitmap(mBackingBitmap, 0, 0, null);
c.restore();
} else
onDraw(c, c, osmv, shadow);
}
}