Add hooks for drawBitmap().

Change-Id: I58e962c3a8b2bc75c2605fe369ad3002579d86e0

Add texture cache.

Change-Id: I1c0e5581d228869e114438258a1014e33e024ad7
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index aafd3ff..5a2e99d 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -42,6 +42,9 @@
     
     private int mWidth;
     private int mHeight;
+    
+    private final float[] mPoint = new float[2];
+    private final float[] mLine = new float[4];
 
     ///////////////////////////////////////////////////////////////////////////
     // Constructors
@@ -64,7 +67,7 @@
             nDestroyRenderer(mRenderer);
         }
     }
-    
+
     private native void nDestroyRenderer(int renderer);
 
     ///////////////////////////////////////////////////////////////////////////
@@ -358,24 +361,45 @@
 
     @Override
     public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
-        // TODO: Implement
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint,
+                bitmap.getDensity(), mDensity, mScreenDensity);
     }
 
     @Override
     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
-        // TODO: Implement
+        final float width = bitmap.getWidth();
+        final float height = bitmap.getHeight();
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, 0.0f, 0.0f, width, height,
+                0.0f, 0.0f, width, height, matrix.native_instance, nativePaint,
+                bitmap.getDensity(), mDensity, mScreenDensity);
     }
 
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
-        // TODO: Implement
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
+                dst.left, dst.top, dst.right, dst.bottom, 0, nativePaint,
+                bitmap.getDensity(), mDensity, mScreenDensity);
     }
 
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
-        // TODO: Implement
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
+                dst.left, dst.top, dst.right, dst.bottom, 0, nativePaint,
+                bitmap.getDensity(), mDensity, mScreenDensity);
     }
 
+    private native void nDrawBitmap(int renderer, int bitmap, float left, float top, int paint,
+            int bitmapDensity, int canvasDensity, int screenDensity);
+
+    private native void nDrawBitmap(int renderer, int bitmap,
+            float srcLeft, float srcTop, float srcRight, float srcBottom,
+            float left, float top, float right, float bottom, int matrix, int paint,
+            int bitmapDensity, int canvasDensity, int screenDensity);
+
     @Override
     public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
             int width, int height, boolean hasAlpha, Paint paint) {
@@ -386,14 +410,12 @@
     @Override
     public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
             int width, int height, boolean hasAlpha, Paint paint) {
-
-        // TODO: Implement
+        drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint);
     }
 
     @Override
     public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
             int vertOffset, int[] colors, int colorOffset, Paint paint) {
-
         throw new UnsupportedOperationException();
     }
 
@@ -416,7 +438,11 @@
 
     @Override
     public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
-        // TODO: Implement
+        mLine[0] = startX;
+        mLine[1] = startY;
+        mLine[2] = stopX;
+        mLine[3] = stopY;
+        drawLines(mLine, 0, 1, paint);
     }
 
     @Override
@@ -426,7 +452,7 @@
 
     @Override
     public void drawLines(float[] pts, Paint paint) {
-        // TODO: Implement
+        drawLines(pts, 0, pts.length / 4, paint);
     }
 
     @Override
@@ -461,7 +487,9 @@
 
     @Override
     public void drawPoint(float x, float y, Paint paint) {
-        // TODO: Implement
+        mPoint[0] = x;
+        mPoint[1] = y;
+        drawPoints(mPoint, 0, 1, paint);
     }
 
     @Override
@@ -471,7 +499,7 @@
 
     @Override
     public void drawPoints(float[] pts, Paint paint) {
-        // TODO: Implement
+        drawPoints(pts, 0, pts.length / 2, paint);
     }
 
     @Override
@@ -529,13 +557,12 @@
 
     @Override
     public void drawText(String text, float x, float y, Paint paint) {
-        // TODO: Implement
+        drawText(text, 0, text.length(), x, y, paint);
     }
 
     @Override
     public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset,
             float vOffset, Paint paint) {
-
         throw new UnsupportedOperationException();
     }
 
@@ -547,14 +574,12 @@
     @Override
     public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
             float x, float y, int dir, Paint paint) {
-        
         throw new UnsupportedOperationException();
     }
 
     @Override
     public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
             float x, float y, int dir, Paint paint) {
-
         throw new UnsupportedOperationException();
     }
 
@@ -562,7 +587,6 @@
     public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
             float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
             int indexOffset, int indexCount, Paint paint) {
-
         throw new UnsupportedOperationException();
     }
 }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index c7fe31b..a1ee3d3 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -76,10 +76,12 @@
      * @param attachInfo AttachInfo tied to the specified view.
      * @param translator Translator used to draw applications in compatibility mode.
      * @param yoff The vertical offset for the drawing.
+     * @param density The density of the application
      * @param scalingRequired Whether drawing should be scaled.
      */
     abstract void draw(View view, View.AttachInfo attachInfo,
-            CompatibilityInfo.Translator translator, int yoff, boolean scalingRequired);
+            CompatibilityInfo.Translator translator, int yoff, int density,
+            boolean scalingRequired);
 
     /**
      * Initializes the hardware renderer for the specified surface and setup the
@@ -370,7 +372,7 @@
 
         @Override
         void draw(View view, View.AttachInfo attachInfo, CompatibilityInfo.Translator translator,
-                int yoff, boolean scalingRequired) {
+                int yoff, int density, boolean scalingRequired) {
 
             if (canDraw()) {
                 attachInfo.mDrawingTime = SystemClock.uptimeMillis();
@@ -386,6 +388,7 @@
                     if (translator != null) {
                         translator.translateCanvas(canvas);
                     }
+                    canvas.setDensity(density);
                     canvas.setScreenDensity(scalingRequired ? DisplayMetrics.DENSITY_DEVICE : 0);
     
                     view.draw(canvas);
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 09c1793..e63ecef 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1264,7 +1264,7 @@
         
         if (mHwRenderer != null && mHwRenderer.isEnabled()) {
             if (!dirty.isEmpty()) {
-                mHwRenderer.draw(mView, mAttachInfo, mTranslator, yoff, scalingRequired);
+                mHwRenderer.draw(mView, mAttachInfo, mTranslator, yoff, mDensity, scalingRequired);
             }
 
             if (scrolling) {
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index fd9ce44..bd3b18d 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -18,6 +18,7 @@
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 
+#include <SkBitmap.h>
 #include <SkCanvas.h>
 #include <SkMatrix.h>
 #include <SkPaint.h>
@@ -44,11 +45,11 @@
 // Constructors
 // ----------------------------------------------------------------------------
 
-static OpenGLRenderer* android_view_GLES20Renderer_createRenderer(JNIEnv* env, jobject canvas) {
+static OpenGLRenderer* android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject canvas) {
     return new OpenGLRenderer;
 }
 
-static void android_view_GLES20Renderer_destroyRenderer(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer) {
     delete renderer;
 }
@@ -57,12 +58,12 @@
 // Setup
 // ----------------------------------------------------------------------------
 
-static void android_view_GLES20Renderer_setViewport(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_setViewport(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jint width, jint height) {
     renderer->setViewport(width, height);
 }
 
-static void android_view_GLES20Renderer_prepare(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_prepare(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer) {
     renderer->prepare();
 }
@@ -71,22 +72,22 @@
 // State
 // ----------------------------------------------------------------------------
 
-static jint android_view_GLES20Renderer_save(JNIEnv* env, jobject canvas, OpenGLRenderer* renderer,
+static jint android_view_GLES20Canvas_save(JNIEnv* env, jobject canvas, OpenGLRenderer* renderer,
         jint flags) {
     return renderer->save(flags);
 }
 
-static jint android_view_GLES20Renderer_getSaveCount(JNIEnv* env, jobject canvas,
+static jint android_view_GLES20Canvas_getSaveCount(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer) {
     return renderer->getSaveCount();
 }
 
-static void android_view_GLES20Renderer_restore(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_restore(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer) {
     renderer->restore();
 }
 
-static void android_view_GLES20Renderer_restoreToCount(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_restoreToCount(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jint saveCount) {
     renderer->restoreToCount(saveCount);
 }
@@ -95,13 +96,13 @@
 // Layers
 // ----------------------------------------------------------------------------
 
-static jint android_view_GLES20Renderer_saveLayer(JNIEnv* env, jobject canvas,
+static jint android_view_GLES20Canvas_saveLayer(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
         SkPaint* paint, jint saveFlags) {
     return renderer->saveLayer(left, top, right, bottom, paint, saveFlags);
 }
 
-static jint android_view_GLES20Renderer_saveLayerAlpha(JNIEnv* env, jobject canvas,
+static jint android_view_GLES20Canvas_saveLayerAlpha(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
         jint alpha, jint saveFlags) {
     return renderer->saveLayerAlpha(left, top, right, bottom, alpha, saveFlags);
@@ -111,25 +112,24 @@
 // Clipping
 // ----------------------------------------------------------------------------
 
-static bool android_view_GLES20Renderer_quickReject(JNIEnv* env, jobject canvas,
+static bool android_view_GLES20Canvas_quickReject(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
         SkCanvas::EdgeType edge) {
     return renderer->quickReject(left, top, right, bottom);
 }
 
-static bool android_view_GLES20Renderer_clipRectF(JNIEnv* env, jobject canvas,
+static bool android_view_GLES20Canvas_clipRectF(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom) {
     return renderer->clipRect(left, top, right, bottom);
 }
 
-static bool android_view_GLES20Renderer_clipRect(JNIEnv* env, jobject canvas,
+static bool android_view_GLES20Canvas_clipRect(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jint left, jint top, jint right, jint bottom) {
     return renderer->clipRect(float(left), float(top), float(right), float(bottom));
 }
 
-static bool android_view_GLES20Renderer_getClipBounds(JNIEnv* env, jobject canvas,
+static bool android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jobject rect) {
-
     const android::uirenderer::Rect& bounds(renderer->getClipBounds());
 
     env->CallVoidMethod(rect, gRectClassInfo.set,
@@ -142,32 +142,32 @@
 // Transforms
 // ----------------------------------------------------------------------------
 
-static void android_view_GLES20Renderer_translate(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_translate(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jfloat dx, jfloat dy) {
     renderer->translate(dx, dy);
 }
 
-static void android_view_GLES20Renderer_rotate(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_rotate(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jfloat degrees) {
     renderer->rotate(degrees);
 }
 
-static void android_view_GLES20Renderer_scale(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_scale(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jfloat sx, jfloat sy) {
     renderer->scale(sx, sy);
 }
 
-static void android_view_GLES20Renderer_setMatrix(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_setMatrix(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, SkMatrix* matrix) {
     renderer->setMatrix(matrix);
 }
 
-static void android_view_GLES20Renderer_getMatrix(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_getMatrix(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, SkMatrix* matrix) {
     renderer->getMatrix(matrix);
 }
 
-static void android_view_GLES20Renderer_concatMatrix(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_concatMatrix(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, SkMatrix* matrix) {
     renderer->concatMatrix(matrix);
 }
@@ -176,12 +176,37 @@
 // Drawing
 // ----------------------------------------------------------------------------
 
-static void android_view_GLES20Renderer_drawColor(JNIEnv* env, jobject canvas,
-        OpenGLRenderer* renderer, jint color, jint mode) {
-    renderer->drawColor(color, (SkXfermode::Mode) mode);
+static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkBitmap* bitmap, float left, float top,
+        SkPaint* paint, jint bitmapDensity, jint canvasDensity,jint screenDensity) {
+    if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
+        renderer->drawBitmap(bitmap, left, top, paint);
+    } else {
+        renderer->save(0);
+        const float scale = canvasDensity / float(bitmapDensity);
+        renderer->translate(left, top);
+        renderer->scale(scale, scale);
+        renderer->drawBitmap(bitmap, left, top, paint);
+        renderer->restore();
+    }
 }
 
-static void android_view_GLES20Renderer_drawRect(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkBitmap* bitmap,
+        float srcLeft, float srcTop, float srcRight, float srcBottom,
+        float dstLeft, float dstTop, float dstRight, float dstBottom,
+        SkMatrix* matrix, SkPaint* paint,
+        jint bitmapDenstiy, jint canvasDensity, jint screenDensity) {
+    // TODO: Implement!
+    LOGE("Not implemented: drawBitmap(IIFFFFFFFFIIIII)V");
+}
+
+static void android_view_GLES20Canvas_drawColor(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jint color, SkXfermode::Mode mode) {
+    renderer->drawColor(color, mode);
+}
+
+static void android_view_GLES20Canvas_drawRect(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
         SkPaint* paint) {
     renderer->drawRect(left, top, right, bottom, paint);
@@ -194,36 +219,38 @@
 const char* const kClassPathName = "android/view/GLES20Canvas";
 
 static JNINativeMethod gMethods[] = {
-    {   "nCreateRenderer",    "()I",        (void*) android_view_GLES20Renderer_createRenderer },
-    {   "nDestroyRenderer",   "(I)V",       (void*) android_view_GLES20Renderer_destroyRenderer },
-    {   "nSetViewport",       "(III)V",     (void*) android_view_GLES20Renderer_setViewport },
-    {   "nPrepare",           "(I)V",       (void*) android_view_GLES20Renderer_prepare },
+    {   "nCreateRenderer",    "()I",                (void*) android_view_GLES20Canvas_createRenderer },
+    {   "nDestroyRenderer",   "(I)V",               (void*) android_view_GLES20Canvas_destroyRenderer },
+    {   "nSetViewport",       "(III)V",             (void*) android_view_GLES20Canvas_setViewport },
+    {   "nPrepare",           "(I)V",               (void*) android_view_GLES20Canvas_prepare },
 
-    {   "nSave",              "(II)I",      (void*) android_view_GLES20Renderer_save },
-    {   "nRestore",           "(I)V",       (void*) android_view_GLES20Renderer_restore },
-    {   "nRestoreToCount",    "(II)V",      (void*) android_view_GLES20Renderer_restoreToCount },
-    {   "nGetSaveCount",      "(I)I",       (void*) android_view_GLES20Renderer_getSaveCount },
+    {   "nSave",              "(II)I",              (void*) android_view_GLES20Canvas_save },
+    {   "nRestore",           "(I)V",               (void*) android_view_GLES20Canvas_restore },
+    {   "nRestoreToCount",    "(II)V",              (void*) android_view_GLES20Canvas_restoreToCount },
+    {   "nGetSaveCount",      "(I)I",               (void*) android_view_GLES20Canvas_getSaveCount },
 
-    {   "nSaveLayer",         "(IFFFFII)I", (void*) android_view_GLES20Renderer_saveLayer },
-    {   "nSaveLayerAlpha",    "(IFFFFII)I", (void*) android_view_GLES20Renderer_saveLayerAlpha },
+    {   "nSaveLayer",         "(IFFFFII)I",         (void*) android_view_GLES20Canvas_saveLayer },
+    {   "nSaveLayerAlpha",    "(IFFFFII)I",         (void*) android_view_GLES20Canvas_saveLayerAlpha },
 
-    {   "nQuickReject",       "(IFFFFI)Z",  (void*) android_view_GLES20Renderer_quickReject },
-    {   "nClipRect",          "(IFFFF)Z",   (void*) android_view_GLES20Renderer_clipRectF },
-    {   "nClipRect",          "(IIIII)Z",   (void*) android_view_GLES20Renderer_clipRect },
+    {   "nQuickReject",       "(IFFFFI)Z",          (void*) android_view_GLES20Canvas_quickReject },
+    {   "nClipRect",          "(IFFFF)Z",           (void*) android_view_GLES20Canvas_clipRectF },
+    {   "nClipRect",          "(IIIII)Z",           (void*) android_view_GLES20Canvas_clipRect },
 
-    {   "nTranslate",         "(IFF)V",     (void*) android_view_GLES20Renderer_translate },
-    {   "nRotate",            "(IF)V",      (void*) android_view_GLES20Renderer_rotate },
-    {   "nScale",             "(IFF)V",     (void*) android_view_GLES20Renderer_scale },
+    {   "nTranslate",         "(IFF)V",             (void*) android_view_GLES20Canvas_translate },
+    {   "nRotate",            "(IF)V",              (void*) android_view_GLES20Canvas_rotate },
+    {   "nScale",             "(IFF)V",             (void*) android_view_GLES20Canvas_scale },
 
-    {   "nSetMatrix",         "(II)V",      (void*) android_view_GLES20Renderer_setMatrix },
-    {   "nGetMatrix",         "(II)V",      (void*) android_view_GLES20Renderer_getMatrix },
-    {   "nConcatMatrix",      "(II)V",      (void*) android_view_GLES20Renderer_concatMatrix },
+    {   "nSetMatrix",         "(II)V",              (void*) android_view_GLES20Canvas_setMatrix },
+    {   "nGetMatrix",         "(II)V",              (void*) android_view_GLES20Canvas_getMatrix },
+    {   "nConcatMatrix",      "(II)V",              (void*) android_view_GLES20Canvas_concatMatrix },
 
-    {   "nDrawColor",         "(III)V",     (void*) android_view_GLES20Renderer_drawColor },
-    {   "nDrawRect",          "(IFFFFI)V",  (void*) android_view_GLES20Renderer_drawRect },
+    {   "nDrawBitmap",        "(IIFFIIII)V",        (void*) android_view_GLES20Canvas_drawBitmap },
+    {   "nDrawBitmap",        "(IIFFFFFFFFIIIII)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
+    {   "nDrawColor",         "(III)V",             (void*) android_view_GLES20Canvas_drawColor },
+    {   "nDrawRect",          "(IFFFFI)V",          (void*) android_view_GLES20Canvas_drawRect },
 
     {   "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
-            (void*) android_view_GLES20Renderer_getClipBounds },
+            (void*) android_view_GLES20Canvas_getClipBounds },
 };
 
 #define FIND_CLASS(var, className) \
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index e261cfa..537dd3a 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -35,9 +35,13 @@
      */
     public static final int DENSITY_NONE = 0;
     
-    // Note:  mNativeBitmap is used by FaceDetector_jni.cpp
-    // Don't change/rename without updating FaceDetector_jni.cpp
-    private final int mNativeBitmap;
+    /**
+     * Note:  mNativeBitmap is used by FaceDetector_jni.cpp
+     * Don't change/rename without updating FaceDetector_jni.cpp
+     * 
+     * @hide
+     */
+    public final int mNativeBitmap;
 
     private final boolean mIsMutable;
     private byte[] mNinePatchChunk;   // may be null
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 3a0c18c..3b2210b 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -45,13 +45,19 @@
     private GL      mGL;        // if not null, mBitmap must be null
     
     // optional field set by the caller
-    private DrawFilter  mDrawFilter;
+    private DrawFilter mDrawFilter;
 
-    // Package-scoped for quick access.
-    /*package*/ int mDensity = Bitmap.DENSITY_NONE;
+    /**
+     * @hide
+     */
+    protected int mDensity = Bitmap.DENSITY_NONE;
 
-    // Used to determine when compatibility scaling is in effect.
-    private int mScreenDensity = Bitmap.DENSITY_NONE;
+    /**
+     * Used to determine when compatibility scaling is in effect.
+     * 
+     * @hide
+     */
+    protected int mScreenDensity = Bitmap.DENSITY_NONE;
     
     // Used by native code
     @SuppressWarnings({"UnusedDeclaration"})
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index fd76811..95ebda1 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -4,7 +4,8 @@
 LOCAL_SRC_FILES:= \
 	Matrix.cpp \
 	OpenGLRenderer.cpp \
-	Program.cpp
+	Program.cpp \
+	TextureCache.cpp
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
diff --git a/libs/hwui/GenerationCache.h b/libs/hwui/GenerationCache.h
new file mode 100644
index 0000000..c4ff587
--- /dev/null
+++ b/libs/hwui/GenerationCache.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_GENERATION_CACHE_H
+#define ANDROID_UI_GENERATION_CACHE_H
+
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace uirenderer {
+
+template<typename EntryKey, typename EntryValue>
+class OnEntryRemoved {
+public:
+    virtual ~OnEntryRemoved() { };
+    virtual void operator()(EntryKey key, EntryValue value) = 0;
+}; // class OnEntryRemoved
+
+template<typename K, typename V>
+class GenerationCache {
+public:
+    GenerationCache(unsigned int maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) { };
+    ~GenerationCache() { clear(); };
+
+    void setOnEntryRemovedListener(OnEntryRemoved<K*, V*>* listener);
+
+    void clear();
+
+    bool contains(K* key) const;
+    const V* get(K* key);
+    void put(K* key, V* value);
+    const V* remove(K* key);
+
+    unsigned int size() const;
+
+private:
+    void removeOldest();
+
+    template<typename EntryKey, typename EntryValue>
+    struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > {
+        Entry() { }
+        Entry(const Entry<EntryKey, EntryValue>& e):
+                key(e.key), value(e.value), parent(e.parent), child(e.child) { }
+        Entry(sp<Entry<EntryKey, EntryValue> > e):
+                key(e->key), value(e->value), parent(e->parent), child(e->child) { }
+
+        EntryKey key;
+        EntryValue value;
+
+        sp<Entry<EntryKey, EntryValue> > parent;
+        sp<Entry<EntryKey, EntryValue> > child;
+    }; // struct Entry
+
+    void addToCache(sp<Entry<K*, V*> > entry, K* key, V* value);
+    void attachToCache(sp<Entry<K*, V*> > entry);
+    void detachFromCache(sp<Entry<K*, V*> > entry);
+
+    unsigned int mMaxCapacity;
+
+    OnEntryRemoved<K*, V*>* mListener;
+
+    KeyedVector<K*, sp<Entry<K*, V*> > > mCache;
+
+    sp<Entry<K*, V*> > mOldest;
+    sp<Entry<K*, V*> > mYougest;
+}; // class GenerationCache
+
+template<typename K, typename V>
+unsigned int GenerationCache<K, V>::size() const {
+    return mCache.size();
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K*, V*>* listener) {
+    mListener = listener;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::clear() {
+    if (mListener) {
+        while (mCache.size() > 0) {
+            removeOldest();
+        }
+    } else {
+        mCache.clear();
+    }
+    mYougest.clear();
+    mOldest.clear();
+}
+
+template<typename K, typename V>
+bool GenerationCache<K, V>::contains(K* key) const {
+    return mCache.indexOfKey(key) >= 0;
+}
+
+template<typename K, typename V>
+const V* GenerationCache<K, V>::get(K* key) {
+    ssize_t index = mCache.indexOfKey(key);
+    if (index >= 0) {
+        sp<Entry<K*, V*> > entry = mCache.valueAt(index);
+        if (entry.get()) {
+            detachFromCache(entry);
+            attachToCache(entry);
+            return entry->value;
+        }
+    }
+
+    return NULL;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::put(K* key, V* value) {
+    if (mCache.size() >= mMaxCapacity) {
+        removeOldest();
+    }
+
+    ssize_t index = mCache.indexOfKey(key);
+    if (index >= 0) {
+        sp<Entry<K*, V*> > entry = mCache.valueAt(index);
+        detachFromCache(entry);
+        addToCache(entry, key, value);
+    } else {
+        sp<Entry<K*, V*> > entry = new Entry<K*, V*>;
+        addToCache(entry, key, value);
+    }
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::addToCache(sp<Entry<K*, V*> > entry, K* key, V* value) {
+    entry->key = key;
+    entry->value = value;
+    mCache.add(key, entry);
+    attachToCache(entry);
+}
+
+template<typename K, typename V>
+const V* GenerationCache<K, V>::remove(K* key) {
+    ssize_t index = mCache.indexOfKey(key);
+    if (index >= 0) {
+        sp<Entry<K*, V*> > entry = mCache.valueAt(index);
+        if (mListener) {
+            (*mListener)(entry->key, entry->value);
+        }
+        mCache.removeItemsAt(index, 1);
+        detachFromCache(entry);
+    }
+
+    return NULL;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::removeOldest() {
+    if (mOldest.get()) {
+        remove(mOldest->key);
+    }
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::attachToCache(sp<Entry<K*, V*> > entry) {
+    if (!mYougest.get()) {
+        mYougest = mOldest = entry;
+    } else {
+        entry->parent = mYougest;
+        mYougest->child = entry;
+        mYougest = entry;
+    }
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::detachFromCache(sp<Entry<K*, V*> > entry) {
+    if (entry->parent.get()) {
+        entry->parent->child = entry->child;
+    }
+
+    if (entry->child.get()) {
+        entry->child->parent = entry->parent;
+    }
+
+    if (mOldest == entry) {
+        mOldest = entry->child;
+    }
+
+    if (mYougest == entry) {
+        mYougest = entry->parent;
+    }
+
+    entry->parent.clear();
+    entry->child.clear();
+}
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_GENERATION_CACHE_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index aa992c4..d8023859 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -33,6 +33,8 @@
 // Defines
 ///////////////////////////////////////////////////////////////////////////////
 
+#define MAX_TEXTURE_COUNT 128
+
 #define SV(x, y) { { x, y } }
 #define FV(x, y, u, v) { { x, y }, { u, v } }
 
@@ -81,7 +83,7 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-OpenGLRenderer::OpenGLRenderer() {
+OpenGLRenderer::OpenGLRenderer(): mTextureCache(MAX_TEXTURE_COUNT) {
     LOGD("Create OpenGLRenderer");
 
     mDrawColorShader = new DrawColorProgram;
@@ -92,6 +94,8 @@
 
 OpenGLRenderer::~OpenGLRenderer() {
     LOGD("Destroy OpenGLRenderer");
+
+    mTextureCache.clear();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -374,6 +378,10 @@
 // Drawing
 ///////////////////////////////////////////////////////////////////////////////
 
+void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, float left, float top, const SkPaint* paint) {
+    LOGD("Drawing bitmap!");
+}
+
 void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
     const Rect& clip = mSnapshot->clipRect;
     drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 1af73c1..e5cc98c 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -20,7 +20,9 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
+#include <SkBitmap.h>
 #include <SkMatrix.h>
+#include <SkPaint.h>
 #include <SkXfermode.h>
 
 #include <utils/RefBase.h>
@@ -29,6 +31,8 @@
 #include "Program.h"
 #include "Rect.h"
 #include "Snapshot.h"
+#include "Texture.h"
+#include "TextureCache.h"
 
 namespace android {
 namespace uirenderer {
@@ -98,6 +102,7 @@
     bool quickReject(float left, float top, float right, float bottom);
     bool clipRect(float left, float top, float right, float bottom);
 
+    void drawBitmap(const SkBitmap* bitmap, float left, float top, const SkPaint* paint);
     void drawColor(int color, SkXfermode::Mode mode);
     void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
 
@@ -218,6 +223,9 @@
 
     // Used to draw textured quads
     TextureVertex mDrawTextureVertices[4];
+
+    // Used to cache all drawBitmap textures
+    TextureCache mTextureCache;
 }; // class OpenGLRenderer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
new file mode 100644
index 0000000..b3b17ca
--- /dev/null
+++ b/libs/hwui/Texture.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_TEXTURE_H
+#define ANDROID_UI_TEXTURE_H
+
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Represents an OpenGL texture.
+ */
+struct Texture {
+    /**
+     * Name of the texture.
+     */
+    GLuint id;
+    /**
+     * Indicates whether the texture requires blending.
+     */
+    bool blend;
+    /**
+     * Width of the backing bitmap.
+     */
+    unsigned int width;
+    /**
+     * Height of the backing bitmap.
+     */
+    unsigned int height;
+}; // struct Texture
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_TEXTURE_H
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
new file mode 100644
index 0000000..b4a57ee
--- /dev/null
+++ b/libs/hwui/TextureCache.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <GLES2/gl2.h>
+
+#include "TextureCache.h"
+
+namespace android {
+namespace uirenderer {
+
+TextureCache::TextureCache(unsigned int maxEntries): mCache(maxEntries) {
+    mCache.setOnEntryRemovedListener(this);
+}
+
+TextureCache::~TextureCache() {
+    mCache.clear();
+}
+
+void TextureCache::operator()(SkBitmap* key, Texture* value) {
+    LOGD("Entry removed");
+    if (value) {
+        glDeleteTextures(1, &value->id);
+        delete value;
+    }
+}
+
+Texture* TextureCache::get(SkBitmap* bitmap) {
+    Texture* texture = mCache.get(bitmap);
+    if (!texture) {
+        texture = generateTexture(bitmap);
+        mCache.put(bitmap, texture);
+    }
+    return texture;
+}
+
+Texture* TextureCache::remove(SkBitmap* bitmap) {
+    return mCache.remove(bitmap);
+}
+
+void TextureCache::clear() {
+    mCache.clear();
+}
+
+Texture* TextureCache::generateTexture(SkBitmap* bitmap) {
+    Texture* texture = new Texture;
+
+    texture->width = bitmap->width();
+    texture->height = bitmap->height();
+
+    glGenTextures(1, &texture->id);
+    glBindTexture(GL_TEXTURE_2D, texture->id);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+    switch (bitmap->getConfig()) {
+    case SkBitmap::kRGB_565_Config:
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, texture->width, texture->height,
+                0, GL_RGB565, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
+        break;
+    case SkBitmap::kARGB_8888_Config:
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->width, texture->height,
+                0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+        break;
+    }
+
+    return texture;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
new file mode 100644
index 0000000..42328f4
--- /dev/null
+++ b/libs/hwui/TextureCache.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_TEXTURE_CACHE_H
+#define ANDROID_UI_TEXTURE_CACHE_H
+
+#include <SkBitmap.h>
+
+#include "Texture.h"
+#include "GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+class TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> {
+public:
+    TextureCache(unsigned int maxEntries);
+    ~TextureCache();
+
+    void operator()(SkBitmap* key, Texture* value);
+
+    Texture* get(SkBitmap* bitmap);
+    Texture* remove(SkBitmap* bitmap);
+    void clear();
+
+private:
+    Texture* generateTexture(SkBitmap* bitmap);
+
+    GenerationCache<SkBitmap, Texture> mCache;
+}; // class TextureCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_TEXTURE_CACHE_H
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 30ac8d6..f9b2823 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -50,5 +50,15 @@
             </intent-filter>
         </activity>
         
+        <activity
+                android:name="BitmapsActivity"
+                android:label="_Bitmaps"
+                android:theme="@android:style/Theme.Translucent">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+                
     </application>
 </manifest>
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
new file mode 100644
index 0000000..92851f3
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png b/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png
new file mode 100644
index 0000000..3258ee7
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png
Binary files differ
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java
new file mode 100644
index 0000000..85e7bf0
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BitmapsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(new BitmapsView(this));
+    }
+
+    static class BitmapsView extends View {
+        private Paint mBitmapPaint;
+        private final Bitmap mBitmap1;
+        private final Bitmap mBitmap2;
+
+        BitmapsView(Context c) {
+            super(c);
+
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
+
+            mBitmapPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.translate(120.0f, 50.0f);
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+
+            canvas.translate(0.0f, mBitmap1.getHeight());
+            canvas.translate(0.0f, 25.0f);
+            canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, null);
+            
+            mBitmapPaint.setAlpha(127);
+            canvas.translate(0.0f, mBitmap2.getHeight());
+            canvas.translate(0.0f, 25.0f);
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+            
+            mBitmapPaint.setAlpha(255);
+            canvas.translate(0.0f, mBitmap1.getHeight());
+            canvas.translate(0.0f, 25.0f);
+            mBitmapPaint.setColor(0xffff0000);
+            canvas.drawRect(0.0f, 0.0f, mBitmap2.getWidth(), mBitmap2.getHeight(), mBitmapPaint);
+            mBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
+            canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mBitmapPaint);
+
+            mBitmapPaint = new Paint();
+        }
+    }
+}