From 7fac2e18339f765320d759e8d4c090f92431959e Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Fri, 16 Jul 2010 17:10:13 -0700 Subject: [PATCH] Add plumbing to support gradients in OpenGL renderer. The LinearGradient class keeps a copy of the various parameters that define the gradient. The copies are native arrays to avoid copying Java arrays on every draw call. The gradient code path is implemented until OpenGLRenderer::drawRect() (see TODO.) The actual gradient implementation will be added in a latter change. Change-Id: I9300d250ef5e2e9c2e097c3116ee71dfc9d752d8 --- core/java/android/view/GLES20Canvas.java | 11 ++- core/jni/android/graphics/Shader.cpp | 75 +++++++++++++++++-- core/jni/android_view_GLES20Canvas.cpp | 9 ++- .../java/android/graphics/LinearGradient.java | 41 ++++++++-- libs/hwui/OpenGLRenderer.cpp | 19 ++++- libs/hwui/OpenGLRenderer.h | 11 ++- libs/hwui/Snapshot.h | 32 ++++---- 7 files changed, 161 insertions(+), 37 deletions(-) diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 676aae3aa5134..ef9709ee88815 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -631,7 +631,10 @@ class GLES20Canvas extends Canvas { bs.mTileX, bs.mTileY, bs.mLocalMatrix); return true; } else if (shader instanceof LinearGradient) { - // TODO: Implement + final LinearGradient ls = (LinearGradient) shader; + nSetupLinearShader(mRenderer, ls.native_instance, ls.bounds, ls.colors, + ls.positions, ls.tileMode, ls.mLocalMatrix); + return true; } else if (shader instanceof RadialGradient) { // TODO: Implement } else if (shader instanceof SweepGradient) { @@ -640,8 +643,10 @@ class GLES20Canvas extends Canvas { } return false; } - + + private native void nSetupLinearShader(int renderer, int shader, int bounds, + int colors, int positions, int tileMode, int localMatrix); private native void nSetupBitmapShader(int renderer, int shader, int bitmap, int tileX, int tileY, int matrix); - private native void nResetShader(int renderer); + private native void nResetShader(int renderer); } diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index b09c62b7d2b3e..eb600c44a0072 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -8,6 +8,13 @@ #include "SkTemplates.h" #include "SkXfermode.h" +static struct { + jclass clazz; + jfieldID bounds; + jfieldID colors; + jfieldID positions; +} gLinearGradientClassInfo; + static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) { if (NULL == ptr) { doThrowIAE(env); @@ -77,7 +84,14 @@ static SkShader* BitmapShader_constructor(JNIEnv* env, jobject, const SkBitmap* /////////////////////////////////////////////////////////////////////////////////////////////// -static SkShader* LinearGradient_create1(JNIEnv* env, jobject, +static void LinearGradient_destructor(JNIEnv* env, jobject o, SkShader* shader) +{ + delete reinterpret_cast(env->GetIntField(o, gLinearGradientClassInfo.bounds)); + delete reinterpret_cast(env->GetIntField(o, gLinearGradientClassInfo.colors)); + delete reinterpret_cast(env->GetIntField(o, gLinearGradientClassInfo.positions)); +} + +static SkShader* LinearGradient_create1(JNIEnv* env, jobject o, float x0, float y0, float x1, float y1, jintArray colorArray, jfloatArray posArray, int tileMode) { @@ -90,15 +104,31 @@ static SkShader* LinearGradient_create1(JNIEnv* env, jobject, SkAutoSTMalloc<8, SkScalar> storage(posArray ? count : 0); SkScalar* pos = NULL; - + + jfloat* storedBounds = new jfloat[4]; + storedBounds[0] = x0; storedBounds[1] = y0; + storedBounds[2] = x1; storedBounds[3] = y1; + jfloat* storedPositions = new jfloat[count]; + jint* storedColors = new jint[count]; + memcpy(storedColors, colorValues, count); + if (posArray) { AutoJavaFloatArray autoPos(env, posArray, count); const float* posValues = autoPos.ptr(); pos = (SkScalar*)storage.get(); - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < count; i++) { pos[i] = SkFloatToScalar(posValues[i]); + storedPositions[i] = posValues[i]; + } + } else { + storedPositions[0] = 0.0f; + storedPositions[1] = 1.0f; } - + + env->SetIntField(o, gLinearGradientClassInfo.bounds, reinterpret_cast(storedBounds)); + env->SetIntField(o, gLinearGradientClassInfo.colors, reinterpret_cast(storedColors)); + env->SetIntField(o, gLinearGradientClassInfo.positions, reinterpret_cast(storedPositions)); + SkShader* shader = SkGradientShader::CreateLinear(pts, reinterpret_cast(colorValues), pos, count, @@ -109,7 +139,7 @@ static SkShader* LinearGradient_create1(JNIEnv* env, jobject, return shader; } -static SkShader* LinearGradient_create2(JNIEnv* env, jobject, +static SkShader* LinearGradient_create2(JNIEnv* env, jobject o, float x0, float y0, float x1, float y1, int color0, int color1, int tileMode) { @@ -120,6 +150,22 @@ static SkShader* LinearGradient_create2(JNIEnv* env, jobject, SkColor colors[2]; colors[0] = color0; colors[1] = color1; + + float* storedBounds = new float[4]; + storedBounds[0] = x0; storedBounds[1] = y0; + storedBounds[2] = x1; storedBounds[3] = y1; + + float* storedPositions = new float[2]; + storedPositions[0] = 0.0f; + storedPositions[1] = 1.0f; + + uint32_t* storedColors = new uint32_t[2]; + storedColors[0] = color0; + storedColors[1] = color1; + + env->SetIntField(o, gLinearGradientClassInfo.bounds, reinterpret_cast(storedBounds)); + env->SetIntField(o, gLinearGradientClassInfo.colors, reinterpret_cast(storedColors)); + env->SetIntField(o, gLinearGradientClassInfo.positions, reinterpret_cast(storedPositions)); SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode); ThrowIAE_IfNull(env, s); @@ -254,8 +300,9 @@ static JNINativeMethod gBitmapShaderMethods[] = { }; static JNINativeMethod gLinearGradientMethods[] = { - { "nativeCreate1", "(FFFF[I[FI)I", (void*)LinearGradient_create1 }, - { "nativeCreate2", "(FFFFIII)I", (void*)LinearGradient_create2 } + { "nativeDestructor", "(I)V", (void*)LinearGradient_destructor }, + { "nativeCreate1", "(FFFF[I[FI)I", (void*)LinearGradient_create1 }, + { "nativeCreate2", "(FFFFIII)I", (void*)LinearGradient_create2 } }; static JNINativeMethod gRadialGradientMethods[] = { @@ -278,6 +325,15 @@ static JNINativeMethod gComposeShaderMethods[] = { #define REG(env, name, array) \ result = android::AndroidRuntime::registerNativeMethods(env, name, array, SK_ARRAY_COUNT(array)); \ if (result < 0) return result + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldType) \ + var = env->GetFieldID(clazz, fieldName, fieldType); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_graphics_Shader(JNIEnv* env); int register_android_graphics_Shader(JNIEnv* env) @@ -292,6 +348,11 @@ int register_android_graphics_Shader(JNIEnv* env) REG(env, "android/graphics/SweepGradient", gSweepGradientMethods); REG(env, "android/graphics/ComposeShader", gComposeShaderMethods); + FIND_CLASS(gLinearGradientClassInfo.clazz, "android/graphics/LinearGradient"); + GET_FIELD_ID(gLinearGradientClassInfo.bounds, gLinearGradientClassInfo.clazz, "bounds", "I"); + GET_FIELD_ID(gLinearGradientClassInfo.colors, gLinearGradientClassInfo.clazz, "colors", "I"); + GET_FIELD_ID(gLinearGradientClassInfo.positions, gLinearGradientClassInfo.clazz, "positions", "I"); + return result; } diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index e5aa5dd074355..f952702343dee 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -201,7 +201,6 @@ static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject canv static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject canvas, OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray chunks, float left, float top, float right, float bottom, SkPaint* paint) { - jbyte* storage = env->GetByteArrayElements(chunks, NULL); Res_png_9patch* patch = reinterpret_cast(storage); Res_png_9patch::deserialize(patch); @@ -239,6 +238,13 @@ static void android_view_GLES20Canvas_setupBitmapShader(JNIEnv* env, jobject can (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0); } +static void android_view_GLES20Canvas_setupLinearShader(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkShader* shader, float* bounds, uint32_t* colors, + float* positions, SkShader::TileMode tileMode, SkMatrix* matrix) { + renderer->setupLinearGradientShader(bounds, colors, positions, tileMode, matrix, + (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0); +} + // ---------------------------------------------------------------------------- // JNI Glue // ---------------------------------------------------------------------------- @@ -280,6 +286,7 @@ static JNINativeMethod gMethods[] = { { "nResetShader", "(I)V", (void*) android_view_GLES20Canvas_resetShader }, { "nSetupBitmapShader", "(IIIIII)V", (void*) android_view_GLES20Canvas_setupBitmapShader }, + { "nSetupLinearShader", "(IIIIIII)V", (void*) android_view_GLES20Canvas_setupLinearShader }, { "nGetClipBounds", "(ILandroid/graphics/Rect;)Z", (void*) android_view_GLES20Canvas_getClipBounds }, diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java index e3db105951269..172e19bb3ead4 100644 --- a/graphics/java/android/graphics/LinearGradient.java +++ b/graphics/java/android/graphics/LinearGradient.java @@ -17,6 +17,27 @@ package android.graphics; public class LinearGradient extends Shader { + /** + * These fields are manipulated by the JNI layer, don't touch! + * @hide + */ + public int bounds; + /** + * @hide + */ + public int colors; + /** + * @hide + */ + public int positions; + /** + * @hide + */ + public int count; + /** + * @hide + */ + public int tileMode; /** Create a shader that draws a linear gradient along a line. @param x0 The x-coordinate for the start of the gradient line @@ -38,6 +59,8 @@ public class LinearGradient extends Shader { throw new IllegalArgumentException("color and position arrays must be of equal length"); } native_instance = nativeCreate1(x0, y0, x1, y1, colors, positions, tile.nativeInt); + count = colors.length; + tileMode = tile.nativeInt; } /** Create a shader that draws a linear gradient along a line. @@ -52,12 +75,18 @@ public class LinearGradient extends Shader { public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile) { native_instance = nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt); + count = 2; + tileMode = tile.nativeInt; + } + + protected void finalize() throws Throwable { + super.finalize(); + nativeDestructor(native_instance); } - - private static native int nativeCreate1(float x0, float y0, float x1, float y1, - int colors[], float positions[], int tileMode); - private static native int nativeCreate2(float x0, float y0, float x1, float y1, - int color0, int color1, int tileMode); + private native void nativeDestructor(int native_shader); + private native int nativeCreate1(float x0, float y0, float x1, float y1, + int colors[], float positions[], int tileMode); + private native int nativeCreate2(float x0, float y0, float x1, float y1, + int color0, int color1, int tileMode); } - diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 0bf0e8942a40b..143156331293e 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -528,7 +528,7 @@ void OpenGLRenderer::resetShader() { void OpenGLRenderer::setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix, bool hasAlpha) { - mShader = kShaderBitmap; + mShader = OpenGLRenderer::kShaderBitmap; mShaderBlend = hasAlpha; mShaderBitmap = bitmap; mShaderTileX = tileX; @@ -536,6 +536,18 @@ void OpenGLRenderer::setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tile mShaderMatrix = matrix; } +void OpenGLRenderer::setupLinearGradientShader(float* bounds, uint32_t* colors, + float* positions, SkShader::TileMode tileMode, SkMatrix* matrix, bool hasAlpha) { + mShader = OpenGLRenderer::kShaderLinearGradient; + mShaderBlend = hasAlpha; + mShaderTileX = tileMode; + mShaderTileY = tileMode; + mShaderMatrix = matrix; + mShaderBounds = bounds; + mShaderColors = colors; + mShaderPositions = positions; +} + /////////////////////////////////////////////////////////////////////////////// // Drawing implementation /////////////////////////////////////////////////////////////////////////////// @@ -555,9 +567,12 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot const GLfloat b = a * ((color ) & 0xFF) / 255.0f; switch (mShader) { - case kShaderBitmap: + case OpenGLRenderer::kShaderBitmap: drawBitmapShader(left, top, right, bottom, a, mode); return; + case OpenGLRenderer::kShaderLinearGradient: + // TODO: Generate gradient texture, set appropriate shader + break; default: break; } diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 235931c7a93f6..b3cef88ba8ea8 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -103,6 +103,8 @@ public: void resetShader(); void setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix, bool hasAlpha); + void setupLinearGradientShader(float* bounds, uint32_t* colors, float* positions, + SkShader::TileMode tileMode, SkMatrix* matrix, bool hasAlpha); private: /** @@ -319,13 +321,18 @@ private: GLenum mLastSrcMode; GLenum mLastDstMode; - // Skia shader + // Skia shaders ShaderType mShader; bool mShaderBlend; - SkBitmap* mShaderBitmap; SkShader::TileMode mShaderTileX; SkShader::TileMode mShaderTileY; SkMatrix* mShaderMatrix; + // Bitmaps + SkBitmap* mShaderBitmap; + // Gradients + float* mShaderBounds; + uint32_t* mShaderColors; + float* mShaderPositions; // Various caches TextureCache mTextureCache; diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 96dfab97cb864..3316c7e2e25d9 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -90,22 +90,22 @@ public: transform.mapRect(r); switch (op) { - case SkRegion::kDifference_Op: - break; - case SkRegion::kIntersect_Op: - clipped = clipRect.intersect(r); - break; - case SkRegion::kUnion_Op: - clipped = clipRect.unionWith(r); - break; - case SkRegion::kXOR_Op: - break; - case SkRegion::kReverseDifference_Op: - break; - case SkRegion::kReplace_Op: - clipRect.set(r); - clipped = true; - break; + case SkRegion::kDifference_Op: + break; + case SkRegion::kIntersect_Op: + clipped = clipRect.intersect(r); + break; + case SkRegion::kUnion_Op: + clipped = clipRect.unionWith(r); + break; + case SkRegion::kXOR_Op: + break; + case SkRegion::kReverseDifference_Op: + break; + case SkRegion::kReplace_Op: + clipRect.set(r); + clipped = true; + break; } if (clipped) {