[androidkit] Bind canvas to native Bitmap

Wrap native Bitmap pixels using JNIGraphis APIs.

Add finalizer to clean up.

Change-Id: I22ba54e65a9cdf498e97afefe8bcc6cd88db0c95
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/394816
Commit-Queue: Florin Malita <fmalita@google.com>
Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 4f151fa..37f3743 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2456,7 +2456,7 @@
       is_shared_library = true
 
       sources = [ "modules/androidkit/Canvas.cpp" ]
-      libs = []
+      libs = [ "jnigraphics" ]
 
       deps = [ ":skia" ]
     }
diff --git a/modules/androidkit/Canvas.cpp b/modules/androidkit/Canvas.cpp
index ee4f49a..803dbb1 100644
--- a/modules/androidkit/Canvas.cpp
+++ b/modules/androidkit/Canvas.cpp
@@ -5,38 +5,114 @@
  * found in the LICENSE file.
  */
 
-#include "include/core/SkBitmap.h"
 #include "include/core/SkCanvas.h"
 #include "include/core/SkImageInfo.h"
 #include "include/core/SkPaint.h"
+#include "include/core/SkSurface.h"
+
+#include <android/bitmap.h>
+#include <android/log.h>
 #include <jni.h>
 
+namespace {
+
+static SkColorType color_type(int32_t format) {
+    switch (format) {
+        case ANDROID_BITMAP_FORMAT_RGBA_8888: return kRGBA_8888_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGB_565:   return kRGB_565_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGBA_4444: return kARGB_4444_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGBA_F16:  return kRGBA_F16_SkColorType;
+        case ANDROID_BITMAP_FORMAT_A_8:       return kAlpha_8_SkColorType;
+        default: break;
+    }
+
+    return kUnknown_SkColorType;
+}
+
+static SkAlphaType alpha_type(int32_t flags) {
+    switch ((flags >> ANDROID_BITMAP_FLAGS_ALPHA_SHIFT) & ANDROID_BITMAP_FLAGS_ALPHA_MASK) {
+        case ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE:   return kOpaque_SkAlphaType;
+        case ANDROID_BITMAP_FLAGS_ALPHA_PREMUL:   return kPremul_SkAlphaType;
+        case ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL: return kUnpremul_SkAlphaType;
+        default: break;
+    }
+
+    return kUnknown_SkAlphaType;
+}
+
+SkPaint skpaint(JNIEnv* env, jobject jpaint) {
+    SkPaint paint;
+
+    // TODO: reflect jpaint
+    paint.setColor(0xff00ff00);
+
+    return paint;
+}
+
+class CanvasWrapper {
+ public:
+    CanvasWrapper(JNIEnv* env, jobject bitmap) {
+        AndroidBitmapInfo bm_info;
+        if (AndroidBitmap_getInfo(env, bitmap, &bm_info) != ANDROID_BITMAP_RESULT_SUCCESS) {
+            return;
+        }
+
+        const auto info = SkImageInfo::Make(bm_info.width, bm_info.height,
+                                            color_type(bm_info.format), alpha_type(bm_info.flags));
+
+        void* pixels;
+        if (AndroidBitmap_lockPixels(env, bitmap, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
+            return;
+        }
+
+        fSurface = SkSurface::MakeRasterDirect(info, pixels, bm_info.stride);
+        if (!fSurface) {
+            AndroidBitmap_unlockPixels(env, bitmap);
+        }
+    }
+
+    void unlockPixels(JNIEnv* env, jobject bitmap) {
+        if (fSurface) {
+            AndroidBitmap_unlockPixels(env, bitmap);
+        }
+    }
+
+    SkCanvas* canvas() const { return fSurface ? fSurface->getCanvas() : nullptr; }
+
+ private:
+    sk_sp<SkSurface> fSurface;
+};
+
+}  // namespace
 
 /*
  * Takes in a native instance of bitmap and returns a pointer to the raster canvas.
- *
- * The native instance of bitmap provided by Android is not an SkBitmap,
- * so we ignore it for now.
- *
  */
 extern "C" JNIEXPORT jlong
 JNICALL
-Java_org_skia_androidkit_Canvas_nInitRaster(JNIEnv* env, jobject, SkBitmap* /*bitmap*/) {
-    SkCanvas* canvas = new SkCanvas();
-    return (jlong) canvas;
+Java_org_skia_androidkit_Canvas_nCreateRaster(JNIEnv* env, jobject, jobject bitmap) {
+    return (jlong) new CanvasWrapper(env, bitmap);
 }
 
 extern "C" JNIEXPORT void
 JNICALL
-Java_org_skia_androidkit_Canvas_nDrawRect(JNIEnv* env, jobject, jlong canvasProxy,
-                                                        jfloat left, jfloat top, jfloat right,
-                                                        jfloat bottom, jlong paintProxy) {
-    SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasProxy);
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintProxy);
-    SkScalar left_ = SkFloatToScalar(left);
-    SkScalar top_ = SkFloatToScalar(top);
-    SkScalar right_ = SkFloatToScalar(right);
-    SkScalar bottom_ = SkFloatToScalar(bottom);
-
-    canvas->drawRect(SkRect{left_, top_, right_, bottom_}, *paint);
+Java_org_skia_androidkit_Canvas_nFinalize(JNIEnv* env, jobject,
+                                          jlong canvas_wrapper, jobject bitmap) {
+    auto* wrapper = reinterpret_cast<CanvasWrapper*>(canvas_wrapper);
+    wrapper->unlockPixels(env, bitmap);
+    delete wrapper;
 }
+
+extern "C" JNIEXPORT void
+JNICALL
+Java_org_skia_androidkit_Canvas_nDrawRect(JNIEnv* env, jobject, jlong canvas_wrapper,
+                                          jfloat left, jfloat top, jfloat right, jfloat bottom,
+                                          jobject paint) {
+    auto* canvas = reinterpret_cast<CanvasWrapper*>(canvas_wrapper)->canvas();
+    if (!canvas) {
+        return;
+    }
+
+    canvas->drawRect(SkRect::MakeLTRB(left, top, right, bottom), skpaint(env, paint));
+}
+
diff --git a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Canvas.java b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Canvas.java
index 29b87ed..b647679 100644
--- a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Canvas.java
+++ b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Canvas.java
@@ -4,8 +4,6 @@
 import android.graphics.Paint;
 import android.util.Log;
 
-import java.lang.reflect.Method;
-
 public class Canvas {
     private long mNativeCanvasWrapper;
     private Bitmap mBitmap;
@@ -16,30 +14,30 @@
         Log.d(TAG, "loading lib");
         System.loadLibrary("androidkit");
     }
+
     public Canvas(Bitmap bitmap) {
         if (!bitmap.isMutable()) {
             throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
         }
-        // --------- ugly class reflection to get the @hide method -----------
-        Object bitmapProxy = null;
-        try {
-            Class c = Class.forName("android.graphics.Bitmap");
-            Method m = c.getMethod("getNativeInstance");
-            bitmapProxy = m.invoke(bitmap);
-        } catch (Exception e) {
-            e.printStackTrace();
-            throw new RuntimeException(e);
-        }
-        mNativeCanvasWrapper = nInitRaster(Long.parseLong(bitmapProxy.toString()));
-        // --------------------------------------------------------------------
+
         mBitmap = bitmap;
+        mNativeCanvasWrapper = nCreateRaster(bitmap);
+    }
+
+    @Override
+    protected void finalize() throws Throwable
+    {
+        nFinalize(mNativeCanvasWrapper, mBitmap);
     }
 
     public void drawRect(float left, float right, float top, float bottom, Paint paint) {
-        //nDrawRect(mNativeCanvasWrapper, left, right, top, bottom, paint.getNativeInstance());
+        nDrawRect(mNativeCanvasWrapper, left, right, top, bottom, paint);
     }
 
-    private static native long nInitRaster(long bitmapHandle);
-    private static native long nDrawRect(long canvasProxy, float left, float right, float top, float bottom, long paintProxy);
+    private static native long nCreateRaster(Bitmap bitmap);
+    private static native void nFinalize(long canvasWrapper, Bitmap bitmap);
+
+    private static native void nDrawRect(long canvasWrapper,
+                                         float left, float right, float top, float bottom, Paint p);
 
 }
diff --git a/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java b/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
index a71e11e..7398306 100644
--- a/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
+++ b/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
@@ -1,16 +1,13 @@
 package org.skia.androidkitdemo1;
 
-// Will eventually be replaced with:
-// import org.skia.androidkit.Canvas;
 import android.app.Activity;
-import android.graphics.Canvas;
-
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.os.Bundle;
 import android.util.Log;
 import android.widget.ImageView;
+import org.skia.androidkit.Canvas;
 
 public class MainActivity extends Activity {
     private static final String TAG = "ANDROIDKIT DEMO";
@@ -29,4 +26,4 @@
         ImageView image = findViewById(R.id.image);
         image.setImageBitmap(bmp);
     }
-}
\ No newline at end of file
+}