#ifndef _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ #define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ #include #include "Bitmap.h" #include "SkBitmap.h" #include "SkBRDAllocator.h" #include "SkCodec.h" #include "SkPixelRef.h" #include "SkMallocPixelRef.h" #include "SkPoint.h" #include "SkRect.h" #include "SkColorSpace.h" #include #include #include "graphics_jni_helpers.h" class SkBitmapRegionDecoder; class SkCanvas; namespace android { class Paint; struct Typeface; } class GraphicsJNI { public: // This enum must keep these int values, to match the int values // in the java Bitmap.Config enum. enum LegacyBitmapConfig { kNo_LegacyBitmapConfig = 0, kA8_LegacyBitmapConfig = 1, kIndex8_LegacyBitmapConfig = 2, kRGB_565_LegacyBitmapConfig = 3, kARGB_4444_LegacyBitmapConfig = 4, kARGB_8888_LegacyBitmapConfig = 5, kRGBA_16F_LegacyBitmapConfig = 6, kHardware_LegacyBitmapConfig = 7, kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig }; static void setJavaVM(JavaVM* javaVM); /** returns a pointer to the JavaVM provided when we initialized the module */ static JavaVM* getJavaVM() { return mJavaVM; } /** return a pointer to the JNIEnv for this thread */ static JNIEnv* getJNIEnv(); /** create a JNIEnv* for this thread or assert if one already exists */ static JNIEnv* attachJNIEnv(const char* envName); /** detach the current thread from the JavaVM */ static void detachJNIEnv(); // returns true if an exception is set (and dumps it out to the Log) static bool hasException(JNIEnv*); static void get_jrect(JNIEnv*, jobject jrect, int* L, int* T, int* R, int* B); static void set_jrect(JNIEnv*, jobject jrect, int L, int T, int R, int B); static SkIRect* jrect_to_irect(JNIEnv*, jobject jrect, SkIRect*); static void irect_to_jrect(const SkIRect&, JNIEnv*, jobject jrect); static SkRect* jrectf_to_rect(JNIEnv*, jobject jrectf, SkRect*); static SkRect* jrect_to_rect(JNIEnv*, jobject jrect, SkRect*); static void rect_to_jrectf(const SkRect&, JNIEnv*, jobject jrectf); static void set_jpoint(JNIEnv*, jobject jrect, int x, int y); static SkIPoint* jpoint_to_ipoint(JNIEnv*, jobject jpoint, SkIPoint* point); static void ipoint_to_jpoint(const SkIPoint& point, JNIEnv*, jobject jpoint); static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point); static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf); ANDROID_API static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); static android::Bitmap* getNativeBitmap(JNIEnv*, jobject bitmap); static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes, bool* isHardware); static SkRegion* getNativeRegion(JNIEnv*, jobject region); /* * LegacyBitmapConfig is the old enum in Skia that matched the enum int values * in Bitmap.Config. Skia no longer supports this config, but has replaced it * with SkColorType. These routines convert between the two. */ static SkColorType legacyBitmapConfigToColorType(jint legacyConfig); static jint colorTypeToLegacyBitmapConfig(SkColorType colorType); /** Return the corresponding native colorType from the java Config enum, or kUnknown_SkColorType if the java object is null. */ static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig); static AndroidBitmapFormat getFormatFromConfig(JNIEnv* env, jobject jconfig); static jobject getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format); static bool isHardwareConfig(JNIEnv* env, jobject jconfig); static jint hardwareLegacyBitmapConfig(); static jobject createRegion(JNIEnv* env, SkRegion* region); static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); /** * Given a bitmap we natively allocate a memory block to store the contents * of that bitmap. The memory is then attached to the bitmap via an * SkPixelRef, which ensures that upon deletion the appropriate caches * are notified. */ static bool allocatePixels(JNIEnv* env, SkBitmap* bitmap); /** Copy the colors in colors[] to the bitmap, convert to the correct format along the way. Whether to use premultiplied pixels is determined by dstBitmap's alphaType. */ static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset, int srcStride, int x, int y, int width, int height, SkBitmap* dstBitmap); /** * Convert the native SkColorSpace retrieved from ColorSpace.Rgb.getNativeInstance(). * * This will never throw an Exception. If the ColorSpace is one that Skia cannot * use, ColorSpace.Rgb.getNativeInstance() would have thrown an Exception. It may, * however, be nullptr, which may be acceptable. */ static sk_sp getNativeColorSpace(jlong colorSpaceHandle); /** * Return the android.graphics.ColorSpace Java object that corresponds to decodeColorSpace * and decodeColorType. * * This may create a new object if none of the Named ColorSpaces match. */ static jobject getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, SkColorType decodeColorType); /** * Convert from a Java @ColorLong to an SkColor4f that Skia can use directly. * * This ignores the encoded ColorSpace, besides checking to see if it is sRGB, * which is encoded differently. The color space should be passed down separately * via ColorSpace#getNativeInstance(), and converted with getNativeColorSpace(), * above. */ static SkColor4f convertColorLong(jlong color); private: /* JNI JavaVM pointer */ static JavaVM* mJavaVM; }; class HeapAllocator : public SkBRDAllocator { public: HeapAllocator() { }; ~HeapAllocator() { }; virtual bool allocPixelRef(SkBitmap* bitmap) override; /** * Fetches the backing allocation object. Must be called! */ android::Bitmap* getStorageObjAndReset() { return mStorage.release(); }; SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kYes_ZeroInitialized; } private: sk_sp mStorage; }; /** * Allocator to handle reusing bitmaps for BitmapRegionDecoder. * * The BitmapRegionDecoder documentation states that, if it is * provided, the recycled bitmap will always be reused, clipping * the decoded output to fit in the recycled bitmap if necessary. * This allocator implements that behavior. * * Skia's SkBitmapRegionDecoder expects the memory that * is allocated to be large enough to decode the entire region * that is requested. It will decode directly into the memory * that is provided. * * FIXME: BUG:25465958 * If the recycled bitmap is not large enough for the decode * requested, meaning that a clip is required, we will allocate * enough memory for Skia to perform the decode, and then copy * from the decoded output into the recycled bitmap. * * If the recycled bitmap is large enough for the decode requested, * we will provide that memory for Skia to decode directly into. * * This allocator should only be used for a single allocation. * After we reuse the recycledBitmap once, it is dangerous to * reuse it again, given that it still may be in use from our * first allocation. */ class RecyclingClippingPixelAllocator : public SkBRDAllocator { public: RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap, size_t recycledBytes); ~RecyclingClippingPixelAllocator(); virtual bool allocPixelRef(SkBitmap* bitmap) override; /** * Must be called! * * In the event that the recycled bitmap is not large enough for * the allocation requested, we will allocate memory on the heap * instead. As a final step, once we are done using this memory, * we will copy the contents of the heap memory into the recycled * bitmap's memory, clipping as necessary. */ void copyIfNecessary(); /** * Indicates that this allocator does not allocate zero initialized * memory. */ SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kNo_ZeroInitialized; } private: android::Bitmap* mRecycledBitmap; const size_t mRecycledBytes; SkBitmap* mSkiaBitmap; bool mNeedsCopy; }; class AshmemPixelAllocator : public SkBitmap::Allocator { public: explicit AshmemPixelAllocator(JNIEnv* env); ~AshmemPixelAllocator() { }; virtual bool allocPixelRef(SkBitmap* bitmap); android::Bitmap* getStorageObjAndReset() { return mStorage.release(); }; private: JavaVM* mJavaVM; sk_sp mStorage; }; enum JNIAccess { kRO_JNIAccess, kRW_JNIAccess }; class AutoJavaFloatArray { public: AutoJavaFloatArray(JNIEnv* env, jfloatArray array, int minLength = 0, JNIAccess = kRW_JNIAccess); ~AutoJavaFloatArray(); float* ptr() const { return fPtr; } int length() const { return fLen; } private: JNIEnv* fEnv; jfloatArray fArray; float* fPtr; int fLen; int fReleaseMode; }; class AutoJavaIntArray { public: AutoJavaIntArray(JNIEnv* env, jintArray array, int minLength = 0); ~AutoJavaIntArray(); jint* ptr() const { return fPtr; } int length() const { return fLen; } private: JNIEnv* fEnv; jintArray fArray; jint* fPtr; int fLen; }; class AutoJavaShortArray { public: AutoJavaShortArray(JNIEnv* env, jshortArray array, int minLength = 0, JNIAccess = kRW_JNIAccess); ~AutoJavaShortArray(); jshort* ptr() const { return fPtr; } int length() const { return fLen; } private: JNIEnv* fEnv; jshortArray fArray; jshort* fPtr; int fLen; int fReleaseMode; }; class AutoJavaByteArray { public: AutoJavaByteArray(JNIEnv* env, jbyteArray array, int minLength = 0); ~AutoJavaByteArray(); jbyte* ptr() const { return fPtr; } int length() const { return fLen; } private: JNIEnv* fEnv; jbyteArray fArray; jbyte* fPtr; int fLen; }; void doThrowNPE(JNIEnv* env); void doThrowAIOOBE(JNIEnv* env); // Array Index Out Of Bounds Exception void doThrowIAE(JNIEnv* env, const char* msg = NULL); // Illegal Argument void doThrowRE(JNIEnv* env, const char* msg = NULL); // Runtime void doThrowISE(JNIEnv* env, const char* msg = NULL); // Illegal State void doThrowOOME(JNIEnv* env, const char* msg = NULL); // Out of memory void doThrowIOE(JNIEnv* env, const char* msg = NULL); // IO Exception #define NPE_CHECK_RETURN_ZERO(env, object) \ do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0) #define NPE_CHECK_RETURN_VOID(env, object) \ do { if (NULL == (object)) { doThrowNPE(env); return; } } while (0) #endif // _ANDROID_GRAPHICS_GRAPHICS_JNI_H_