diff options
| -rw-r--r-- | api/current.xml | 15 | ||||
| -rw-r--r-- | core/java/android/app/Activity.java | 16 | ||||
| -rw-r--r-- | core/java/android/app/Fragment.java | 2 | ||||
| -rw-r--r-- | core/java/android/app/LoaderManager.java | 129 | ||||
| -rw-r--r-- | core/java/android/view/GLES20Canvas.java | 39 | ||||
| -rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 19 | ||||
| -rw-r--r-- | graphics/java/android/graphics/Shader.java | 19 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 154 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.h | 57 | ||||
| -rw-r--r-- | libs/hwui/Snapshot.h | 12 | ||||
| -rw-r--r-- | libs/hwui/shaders/drawColor.frag | 2 | ||||
| -rw-r--r-- | libs/hwui/shaders/drawTexture.frag | 4 | ||||
| -rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 9 | ||||
| -rw-r--r-- | tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java | 12 | ||||
| -rw-r--r-- | tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java | 131 |
15 files changed, 503 insertions, 117 deletions
diff --git a/api/current.xml b/api/current.xml index 7131eb9..355b3cd 100644 --- a/api/current.xml +++ b/api/current.xml @@ -28537,9 +28537,8 @@ </parameter> </method> </class> -<class name="LoaderManager" - extends="java.lang.Object" - abstract="false" +<interface name="LoaderManager" + abstract="true" static="false" final="false" deprecated="not deprecated" @@ -28547,7 +28546,7 @@ > <method name="getLoader" return="android.content.Loader<D>" - abstract="false" + abstract="true" native="false" synchronized="false" static="false" @@ -28560,7 +28559,7 @@ </method> <method name="initLoader" return="android.content.Loader<D>" - abstract="false" + abstract="true" native="false" synchronized="false" static="false" @@ -28577,7 +28576,7 @@ </method> <method name="restartLoader" return="android.content.Loader<D>" - abstract="false" + abstract="true" native="false" synchronized="false" static="false" @@ -28594,7 +28593,7 @@ </method> <method name="stopLoader" return="void" - abstract="false" + abstract="true" native="false" synchronized="false" static="false" @@ -28605,7 +28604,7 @@ <parameter name="id" type="int"> </parameter> </method> -</class> +</interface> <interface name="LoaderManager.LoaderCallbacks" abstract="true" static="true" diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 91e4cd5..1cdd423 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -651,7 +651,7 @@ public class Activity extends ContextThemeWrapper Object activity; HashMap<String, Object> children; ArrayList<Fragment> fragments; - SparseArray<LoaderManager> loaders; + SparseArray<LoaderManagerImpl> loaders; } /* package */ NonConfigurationInstances mLastNonConfigurationInstances; @@ -669,8 +669,8 @@ public class Activity extends ContextThemeWrapper final FragmentManager mFragments = new FragmentManager(); - SparseArray<LoaderManager> mAllLoaderManagers; - LoaderManager mLoaderManager; + SparseArray<LoaderManagerImpl> mAllLoaderManagers; + LoaderManagerImpl mLoaderManager; private static final class ManagedCursor { ManagedCursor(Cursor cursor) { @@ -779,13 +779,13 @@ public class Activity extends ContextThemeWrapper return mLoaderManager; } - LoaderManager getLoaderManager(int index, boolean started) { + LoaderManagerImpl getLoaderManager(int index, boolean started) { if (mAllLoaderManagers == null) { - mAllLoaderManagers = new SparseArray<LoaderManager>(); + mAllLoaderManagers = new SparseArray<LoaderManagerImpl>(); } - LoaderManager lm = mAllLoaderManagers.get(index); + LoaderManagerImpl lm = mAllLoaderManagers.get(index); if (lm == null) { - lm = new LoaderManager(started); + lm = new LoaderManagerImpl(started); mAllLoaderManagers.put(index, lm); } return lm; @@ -1554,7 +1554,7 @@ public class Activity extends ContextThemeWrapper // prune out any loader managers that were already stopped, so // have nothing useful to retain. for (int i=mAllLoaderManagers.size()-1; i>=0; i--) { - LoaderManager lm = mAllLoaderManagers.valueAt(i); + LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i); if (lm.mRetaining) { retainLoaders = true; } else { diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 51cce5e..daf6ce0 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -213,7 +213,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener // The View generated for this fragment. View mView; - LoaderManager mLoaderManager; + LoaderManagerImpl mLoaderManager; boolean mStarted; /** diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index 7600899..c9fdfba 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -17,29 +17,82 @@ package android.app; import android.content.Loader; -import android.content.Loader.OnLoadCompleteListener; import android.os.Bundle; import android.util.SparseArray; /** - * Object associated with an {@link Activity} or {@link Fragment} for managing + * Interface associated with an {@link Activity} or {@link Fragment} for managing * one or more {@link android.content.Loader} instances associated with it. */ -public class LoaderManager { - final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>(); - final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(); - boolean mStarted; - boolean mRetaining; - boolean mRetainingStarted; - +public interface LoaderManager { /** * Callback interface for a client to interact with the manager. */ public interface LoaderCallbacks<D> { + /** + * Instantiate and return a new Loader for the given ID. + * + * @param id The ID whose loader is to be created. + * @param args Any arguments supplied by the caller. + * @return Return a new Loader instance that is ready to start loading. + */ public Loader<D> onCreateLoader(int id, Bundle args); + + /** + * Called when a previously created loader has finished its load. + * @param loader The Loader that has finished. + * @param data The data generated by the Loader. + */ public void onLoadFinished(Loader<D> loader, D data); } + /** + * Ensures a loader is initialized and active. If the loader doesn't + * already exist, one is created and (if the activity/fragment is currently + * started) starts the loader. Otherwise the last created + * loader is re-used. + * + * <p>In either case, the given callback is associated with the loader, and + * will be called as the loader state changes. If at the point of call + * the caller is in its started state, and the requested loader + * already exists and has generated its data, then + * callback. {@link LoaderCallbacks#onLoadFinished} will + * be called immediately (inside of this function), so you must be prepared + * for this to happen. + */ + public <D> Loader<D> initLoader(int id, Bundle args, + LoaderManager.LoaderCallbacks<D> callback); + + /** + * Creates a new loader in this manager, registers the callbacks to it, + * and (if the activity/fragment is currently started) starts loading it. + * If a loader with the same id has previously been + * started it will automatically be destroyed when the new loader completes + * its work. The callback will be delivered before the old loader + * is destroyed. + */ + public <D> Loader<D> restartLoader(int id, Bundle args, + LoaderManager.LoaderCallbacks<D> callback); + + /** + * Stops and removes the loader with the given ID. + */ + public void stopLoader(int id); + + /** + * Return the Loader with the given id or null if no matching Loader + * is found. + */ + public <D> Loader<D> getLoader(int id); +} + +class LoaderManagerImpl implements LoaderManager { + final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>(); + final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(); + boolean mStarted; + boolean mRetaining; + boolean mRetainingStarted; + final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> { final int mId; final Bundle mArgs; @@ -67,12 +120,19 @@ public class LoaderManager { return; } + if (mStarted) { + // If loader already started, don't restart. + return; + } + if (mLoader == null && mCallbacks != null) { mLoader = mCallbacks.onCreateLoader(mId, mArgs); } if (mLoader != null) { - mLoader.registerListener(mId, this); - mListenerRegistered = true; + if (!mListenerRegistered) { + mLoader.registerListener(mId, this); + mListenerRegistered = true; + } mLoader.startLoading(); mStarted = true; } @@ -144,7 +204,9 @@ public class LoaderManager { if (info != null) { Loader<Object> oldLoader = info.mLoader; if (oldLoader != null) { - oldLoader.unregisterListener(info); + if (info.mListenerRegistered) { + oldLoader.unregisterListener(info); + } oldLoader.destroy(); } mInactiveLoaders.remove(mId); @@ -152,7 +214,7 @@ public class LoaderManager { } } - LoaderManager(boolean started) { + LoaderManagerImpl(boolean started) { mStarted = started; } @@ -163,27 +225,14 @@ public class LoaderManager { Loader<Object> loader = callback.onCreateLoader(id, args); info.mLoader = (Loader<Object>)loader; if (mStarted) { - // The activity will start all existing loaders in it's onStart(), so only start them - // here if we're past that point of the activitiy's life cycle - loader.registerListener(id, info); - loader.startLoading(); + // The activity will start all existing loaders in it's onStart(), + // so only start them here if we're past that point of the activitiy's + // life cycle + info.start(); } return info; } - /** - * Ensures a loader is initialized an active. If the loader doesn't - * already exist, one is created and started. Otherwise the last created - * loader is re-used. - * - * <p>In either case, the given callback is associated with the loader, and - * will be called as the loader state changes. If at the point of call - * the caller is in its started state, and the requested loader - * already exists and has generated its data, then - * callback. {@link LoaderCallbacks#onLoadFinished} will - * be called immediately (inside of this function), so you must be prepared - * for this to happen. - */ @SuppressWarnings("unchecked") public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) { LoaderInfo info = mLoaders.get(id); @@ -203,13 +252,6 @@ public class LoaderManager { return (Loader<D>)info.mLoader; } - /** - * Create a new loader in this manager, registers the callbacks to it, - * and starts it loading. If a loader with the same id has previously been - * started it will automatically be destroyed when the new loader completes - * its work. The callback will be delivered before the old loader - * is destroyed. - */ @SuppressWarnings("unchecked") public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) { LoaderInfo info = mLoaders.get(id); @@ -231,26 +273,15 @@ public class LoaderManager { return (Loader<D>)info.mLoader; } - /** - * Stops and removes the loader with the given ID. - */ public void stopLoader(int id) { int idx = mLoaders.indexOfKey(id); if (idx >= 0) { LoaderInfo info = mLoaders.valueAt(idx); mLoaders.removeAt(idx); - Loader<Object> loader = info.mLoader; - if (loader != null) { - loader.unregisterListener(info); - loader.destroy(); - } + info.destroy(); } } - /** - * Return the Loader with the given id or null if no matching Loader - * is found. - */ @SuppressWarnings("unchecked") public <D> Loader<D> getLoader(int id) { LoaderInfo loaderInfo = mLoaders.get(id); diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 0ad3c0b..a2ab8bd 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -17,16 +17,21 @@ package android.view; import android.graphics.Bitmap; +import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.DrawFilter; +import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Picture; import android.graphics.PorterDuff; +import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; +import android.graphics.Shader; +import android.graphics.SweepGradient; import javax.microedition.khronos.opengles.GL; @@ -369,6 +374,7 @@ class GLES20Canvas extends Canvas { @Override public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) { + // Shaders are ignored when drawing patches final int nativePaint = paint == null ? 0 : paint.mNativePaint; nDrawPatch(mRenderer, bitmap.mNativeBitmap, chunks, dst.left, dst.top, dst.right, dst.bottom, nativePaint); @@ -379,6 +385,7 @@ class GLES20Canvas extends Canvas { @Override public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { + // Shaders are ignored when drawing bitmaps final int nativePaint = paint == null ? 0 : paint.mNativePaint; nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint); } @@ -387,6 +394,7 @@ class GLES20Canvas extends Canvas { @Override public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { + // Shaders are ignored when drawing bitmaps final int nativePaint = paint == null ? 0 : paint.mNativePaint; nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint); } @@ -395,6 +403,7 @@ class GLES20Canvas extends Canvas { @Override public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { + // Shaders are ignored when drawing bitmaps 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, nativePaint @@ -403,6 +412,7 @@ class GLES20Canvas extends Canvas { @Override public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) { + // Shaders are ignored when drawing bitmaps 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, nativePaint @@ -416,6 +426,7 @@ class GLES20Canvas extends Canvas { @Override public void drawBitmap(int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, Paint paint) { + // Shaders are ignored when drawing bitmaps final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config); final int nativePaint = paint == null ? 0 : paint.mNativePaint; @@ -426,6 +437,7 @@ class GLES20Canvas extends Canvas { @Override public void drawBitmap(int[] colors, int offset, int stride, int x, int y, int width, int height, boolean hasAlpha, Paint paint) { + // Shaders are ignored when drawing bitmaps drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint); } @@ -532,7 +544,9 @@ class GLES20Canvas extends Canvas { @Override public void drawRect(float left, float top, float right, float bottom, Paint paint) { + boolean hasShader = setupShader(paint); nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint); + if (hasShader) nResetShader(mRenderer); } private native void nDrawRect(int renderer, float left, float top, float right, float bottom, @@ -555,7 +569,7 @@ class GLES20Canvas extends Canvas { @Override public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) { - throw new UnsupportedOperationException(); + // TODO: Implement } @Override @@ -607,4 +621,27 @@ class GLES20Canvas extends Canvas { int indexOffset, int indexCount, Paint paint) { // TODO: Implement } + + private boolean setupShader(Paint paint) { + final Shader shader = paint.getShader(); + if (shader != null) { + if (shader instanceof BitmapShader) { + final BitmapShader bs = (BitmapShader) shader; + nSetupBitmapShader(mRenderer, bs.native_instance, bs.mBitmap.mNativeBitmap, + bs.mTileX, bs.mTileY, bs.mLocalMatrix); + return true; + } else if (shader instanceof LinearGradient) { + // TODO: Implement + } else if (shader instanceof RadialGradient) { + // TODO: Implement + } else if (shader instanceof SweepGradient) { + // TODO: Implement + } + } + return false; + } + + private native void nSetupBitmapShader(int renderer, int shader, int bitmap, + int tileX, int tileY, int matrix); + private native void nResetShader(int renderer); } diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 1bbac71..dbf482e 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -221,6 +221,22 @@ static void android_view_GLES20Canvas_drawRect(JNIEnv* env, jobject canvas, } // ---------------------------------------------------------------------------- +// Shaders +// ---------------------------------------------------------------------------- + +static void android_view_GLES20Canvas_resetShader(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer) { + renderer->resetShader(); +} + +static void android_view_GLES20Canvas_setupBitmapShader(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkShader* shader, SkBitmap* bitmap, + SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix) { + renderer->setupBitmapShader(bitmap, tileX, tileY, matrix, + (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0); +} + +// ---------------------------------------------------------------------------- // JNI Glue // ---------------------------------------------------------------------------- @@ -259,6 +275,9 @@ static JNINativeMethod gMethods[] = { { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor }, { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect }, + { "nResetShader", "(I)V", (void*) android_view_GLES20Canvas_resetShader }, + { "nSetupBitmapShader", "(IIIIII)V", (void*) android_view_GLES20Canvas_setupBitmapShader }, + { "nGetClipBounds", "(ILandroid/graphics/Rect;)Z", (void*) android_view_GLES20Canvas_getClipBounds }, }; diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java index ae0304e..03eb55b 100644 --- a/graphics/java/android/graphics/Shader.java +++ b/graphics/java/android/graphics/Shader.java @@ -23,9 +23,19 @@ package android.graphics; * drawn with that paint will get its color(s) from the shader. */ public class Shader { + /** + * Local matrix native instance. + * + * @hide + */ + public int mLocalMatrix; - // this is set by subclasses, but don't make it public - /* package */ int native_instance; + /** + * This is set by subclasses, but don't make it public. + * + * @hide + */ + public int native_instance; public enum TileMode { /** @@ -64,11 +74,12 @@ public class Shader { * @param localM The shader's new local matrix, or null to specify identity */ public void setLocalMatrix(Matrix localM) { - nativeSetLocalMatrix(native_instance, - localM != null ? localM.native_instance : 0); + mLocalMatrix = localM != null ? localM.native_instance : 0; + nativeSetLocalMatrix(native_instance, mLocalMatrix); } protected void finalize() throws Throwable { + super.finalize(); nativeDestructor(native_instance); } diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 12b0dea..decfecf 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -90,6 +90,12 @@ static const Blender gBlends[] = { { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA } }; +static const GLint gTileModes[] = { + GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode + GL_REPEAT, // == SkShader::kRepeat_Mode + GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode +}; + /////////////////////////////////////////////////////////////////////////////// // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// @@ -116,9 +122,15 @@ OpenGLRenderer::OpenGLRenderer(): LOGD(" Using default layer cache size of %dMB", DEFAULT_LAYER_CACHE_SIZE); } - mDrawColorShader = new DrawColorProgram; - mDrawTextureShader = new DrawTextureProgram; - mCurrentShader = mDrawTextureShader; + mDrawColorProgram = new DrawColorProgram; + mDrawTextureProgram = new DrawTextureProgram; + mCurrentProgram = mDrawTextureProgram; + + mShader = kShaderNone; + mShaderTileX = SkShader::kClamp_TileMode; + mShaderTileY = SkShader::kClamp_TileMode; + mShaderMatrix = NULL; + mShaderBitmap = NULL; memcpy(mDrawTextureVertices, gDrawTextureVertices, sizeof(gDrawTextureVertices)); } @@ -295,7 +307,7 @@ int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bot bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) { - LAYER_LOGD("Requesting layer %dx%d", size.width, size.height); + LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top); LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize()); GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0; @@ -327,7 +339,7 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, saveSnapshot(); // TODO: This doesn't preserve other transformations (check Skia first) mSnapshot->transform.loadTranslate(-left, -top, 0.0f); - mSnapshot->clipRect.set(left, top, right, bottom); + mSnapshot->setClip(left, top, right, bottom); mSnapshot->height = bottom - top; setScissorFromClip(); @@ -512,7 +524,7 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, // Skia draws using the color's alpha channel if < 255 // Otherwise, it uses the paint's alpha int color = p->getColor(); - if (((color >> 24) & 0xFF) == 255) { + if (((color >> 24) & 0xff) == 255) { color |= p->getAlpha() << 24; } @@ -520,42 +532,117 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, } /////////////////////////////////////////////////////////////////////////////// +// Shaders +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::resetShader() { + mShader = OpenGLRenderer::kShaderNone; + mShaderBlend = false; + mShaderTileX = SkShader::kClamp_TileMode; + mShaderTileY = SkShader::kClamp_TileMode; +} + +void OpenGLRenderer::setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX, + SkShader::TileMode tileY, SkMatrix* matrix, bool hasAlpha) { + mShader = kShaderBitmap; + mShaderBlend = hasAlpha; + mShaderBitmap = bitmap; + mShaderTileX = tileX; + mShaderTileY = tileY; + mShaderMatrix = matrix; +} + +/////////////////////////////////////////////////////////////////////////////// // Drawing implementation /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, int color, SkXfermode::Mode mode, bool ignoreTransform) { + // If a shader is set, preserve only the alpha + if (mShader != kShaderNone) { + color |= 0x00ffffff; + } + + // Render using pre-multiplied alpha const int alpha = (color >> 24) & 0xFF; - const GLfloat a = alpha / 255.0f; - const GLfloat r = ((color >> 16) & 0xFF) / 255.0f; - const GLfloat g = ((color >> 8) & 0xFF) / 255.0f; - const GLfloat b = ((color ) & 0xFF) / 255.0f; + const GLfloat a = alpha / 255.0f; + const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f; + const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f; + const GLfloat b = a * ((color ) & 0xFF) / 255.0f; + + switch (mShader) { + case kShaderBitmap: + drawBitmapShader(left, top, right, bottom, a, mode); + return; + default: + break; + } // Pre-multiplication happens when setting the shader color - chooseBlending(alpha < 255, mode); + chooseBlending(alpha < 255 || mShaderBlend, mode); mModelView.loadTranslate(left, top, 0.0f); mModelView.scale(right - left, bottom - top, 1.0f); - const bool inUse = useShader(mDrawColorShader); + // TODO: Pick the program matching the current shader + sp<DrawColorProgram> program = mDrawColorProgram; + if (!useProgram(program)) { + const GLvoid* p = &gDrawColorVertices[0].position[0]; + glVertexAttribPointer(program->position, 2, GL_FLOAT, GL_FALSE, + gDrawColorVertexStride, p); + } + if (!ignoreTransform) { - mDrawColorShader->set(mOrthoMatrix, mModelView, mSnapshot->transform); + program->set(mOrthoMatrix, mModelView, mSnapshot->transform); } else { mat4 identity; - mDrawColorShader->set(mOrthoMatrix, mModelView, identity); + program->set(mOrthoMatrix, mModelView, identity); } - if (!inUse) { - const GLvoid* p = &gDrawColorVertices[0].position[0]; - glVertexAttribPointer(mDrawColorShader->position, 2, GL_FLOAT, GL_FALSE, - gDrawColorVertexStride, p); - } - // Render using pre-multiplied alpha - glUniform4f(mDrawColorShader->color, r * a, g * a, b * a, a); + glUniform4f(program->color, r, g, b, a); glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount); } +void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float bottom, + float alpha, SkXfermode::Mode mode) { + const Texture* texture = mTextureCache.get(mShaderBitmap); + + const float width = texture->width; + const float height = texture->height; + + // This could be done in the vertex shader but we have only 4 vertices + float u1 = 0.0f; + float v1 = 0.0f; + float u2 = right - left; + float v2 = bottom - top; + + if (mShaderMatrix) { + SkMatrix inverse; + mShaderMatrix->invert(&inverse); + mat4 m(inverse); + Rect r(u1, v1, u2, v2); + m.mapRect(r); + + u1 = r.left; + u2 = r.right; + v1 = r.top; + v2 = r.bottom; + } + + u1 /= width; + u2 /= width; + v1 /= height; + v2 /= height; + + resetDrawTextureTexCoords(u1, v1, u2, v2); + + drawTextureMesh(left, top, right, bottom, texture->id, alpha, mode, texture->blend, + &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL); + + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); +} + void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, const Texture* texture, const SkPaint* paint) { int alpha; @@ -578,21 +665,22 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b mModelView.loadTranslate(left, top, 0.0f); mModelView.scale(right - left, bottom - top, 1.0f); - useShader(mDrawTextureShader); - mDrawTextureShader->set(mOrthoMatrix, mModelView, mSnapshot->transform); + useProgram(mDrawTextureProgram); + mDrawTextureProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform); chooseBlending(blend || alpha < 1.0f, mode); + // TODO: Only bind/set parameters when needed glBindTexture(GL_TEXTURE_2D, texture); - - // TODO handle tiling and filtering here + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileModes[mShaderTileX]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileModes[mShaderTileY]); // Always premultiplied - glUniform4f(mDrawTextureShader->color, alpha, alpha, alpha, alpha); + glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha); - glVertexAttribPointer(mDrawTextureShader->position, 2, GL_FLOAT, GL_FALSE, + glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE, gDrawTextureVertexStride, vertices); - glVertexAttribPointer(mDrawTextureShader->texCoords, 2, GL_FLOAT, GL_FALSE, + glVertexAttribPointer(mDrawTextureProgram->texCoords, 2, GL_FLOAT, GL_FALSE, gDrawTextureVertexStride, texCoords); if (!indices) { @@ -630,11 +718,11 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPr mBlend = blend; } -bool OpenGLRenderer::useShader(const sp<Program>& shader) { - if (!shader->isInUse()) { - mCurrentShader->remove(); - shader->use(); - mCurrentShader = shader; +bool OpenGLRenderer::useProgram(const sp<Program>& program) { + if (!program->isInUse()) { + mCurrentProgram->remove(); + program->use(); + mCurrentProgram = program; return false; } return true; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 8083038..2a96432 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -23,6 +23,7 @@ #include <SkBitmap.h> #include <SkMatrix.h> #include <SkPaint.h> +#include <SkShader.h> #include <SkXfermode.h> #include <utils/RefBase.h> @@ -98,8 +99,23 @@ public: void drawColor(int color, SkXfermode::Mode mode); void drawRect(float left, float top, float right, float bottom, const SkPaint* paint); + void resetShader(); + void setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX, SkShader::TileMode tileY, + SkMatrix* matrix, bool hasAlpha); + private: /** + * Type of Skia shader in use. + */ + enum ShaderType { + kShaderNone, + kShaderBitmap, + kShaderLinearGradient, + kShaderCircularGradient, + kShaderSweepGradient + }; + + /** * Saves the current state of the renderer as a new snapshot. * The new snapshot is saved in mSnapshot and the previous snapshot * is linked from mSnapshot->previous. @@ -217,6 +233,19 @@ private: GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0); /** + * Fills the specified rectangle with the currently set bitmap shader. + * + * @param left The left coordinate of the rectangle + * @param top The top coordinate of the rectangle + * @param right The right coordinate of the rectangle + * @param bottom The bottom coordinate of the rectangle + * @param alpha An additional translucency parameter, between 0.0f and 1.0f + * @param mode The blending mode + */ + void drawBitmapShader(float left, float top, float right, float bottom, float alpha, + SkXfermode::Mode mode); + + /** * Resets the texture coordinates stored in mDrawTextureVertices. Setting the values * back to default is achieved by calling: * @@ -246,14 +275,16 @@ private: inline void chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied = true); /** - * Use the specified shader with the current GL context. If the shader is already - * in use, it will not be bound again. If it is not in use, the current shader is - * marked unused and the specified shader becomes used and becomes the new - * current shader. + * Use the specified program with the current GL context. If the program is already + * in use, it will not be bound again. If it is not in use, the current program is + * marked unused and the specified program becomes used and becomes the new + * current program. * - * @return true If the specified shader was already in use, false otherwise. + * @param program The program to use + * + * @return true If the specified program was already in use, false otherwise. */ - inline bool useShader(const sp<Program>& shader); + inline bool useProgram(const sp<Program>& program); // Dimensions of the drawing surface int mWidth, mHeight; @@ -272,9 +303,9 @@ private: sp<Snapshot> mSnapshot; // Shaders - sp<Program> mCurrentShader; - sp<DrawColorProgram> mDrawColorShader; - sp<DrawTextureProgram> mDrawTextureShader; + sp<Program> mCurrentProgram; + sp<DrawColorProgram> mDrawColorProgram; + sp<DrawTextureProgram> mDrawTextureProgram; // Used to draw textured quads TextureVertex mDrawTextureVertices[4]; @@ -284,6 +315,14 @@ private: GLenum mLastSrcMode; GLenum mLastDstMode; + // Skia shader + ShaderType mShader; + bool mShaderBlend; + SkBitmap* mShaderBitmap; + SkShader::TileMode mShaderTileX; + SkShader::TileMode mShaderTileY; + SkMatrix* mShaderMatrix; + // Various caches TextureCache mTextureCache; LayerCache mLayerCache; diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 7265d91..32fee32 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -106,6 +106,18 @@ public: } /** + * Sets the current clip. + */ + void setClip(float left, float top, float right, float bottom) { + clipRect.set(left, top, right, bottom); + if (flags & kFlagDirtyTransform) { + flags &= ~kFlagDirtyTransform; + mappedClip.set(clipRect); + transform.mapRect(mappedClip); + } + } + + /** * Height of the framebuffer the snapshot is rendering into. */ int height; diff --git a/libs/hwui/shaders/drawColor.frag b/libs/hwui/shaders/drawColor.frag index 0628850..1d6cb8b 100644 --- a/libs/hwui/shaders/drawColor.frag +++ b/libs/hwui/shaders/drawColor.frag @@ -1,5 +1,7 @@ SHADER_SOURCE(gDrawColorFragmentShader, +precision mediump float; + uniform vec4 color; void main(void) { diff --git a/libs/hwui/shaders/drawTexture.frag b/libs/hwui/shaders/drawTexture.frag index 0f2aa91..8390d8e 100644 --- a/libs/hwui/shaders/drawTexture.frag +++ b/libs/hwui/shaders/drawTexture.frag @@ -1,6 +1,8 @@ SHADER_SOURCE(gDrawTextureFragmentShader, -varying mediump vec2 outTexCoords; +precision mediump float; + +varying vec2 outTexCoords; uniform vec4 color; uniform sampler2D sampler; diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index cb894f1..098359c 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -96,6 +96,15 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + + <activity + android:name="ShadersActivity" + android:label="_Shaders"> + <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/src/com/google/android/test/hwui/BitmapsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java index dfc8a71..cfa8d3c 100644 --- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java +++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java @@ -25,9 +25,11 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.os.Bundle; +import android.view.Gravity; import android.view.View; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; +import android.widget.FrameLayout; @SuppressWarnings({"UnusedDeclaration"}) public class BitmapsActivity extends Activity { @@ -35,7 +37,9 @@ public class BitmapsActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final BitmapsView view = new BitmapsView(this); - setContentView(view); + final FrameLayout layout = new FrameLayout(this); + layout.addView(view, new FrameLayout.LayoutParams(480, 800, Gravity.CENTER)); + setContentView(layout); ScaleAnimation a = new ScaleAnimation(1.0f, 2.0f, 1.0f, 2.0f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f, @@ -50,6 +54,7 @@ public class BitmapsActivity extends Activity { private Paint mBitmapPaint; private final Bitmap mBitmap1; private final Bitmap mBitmap2; + private final PorterDuffXfermode mDstIn; BitmapsView(Context c) { super(c); @@ -58,6 +63,7 @@ public class BitmapsActivity extends Activity { mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2); mBitmapPaint = new Paint(); + mDstIn = new PorterDuffXfermode(PorterDuff.Mode.DST_IN); } @Override @@ -81,10 +87,10 @@ public class BitmapsActivity extends Activity { 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)); + mBitmapPaint.setXfermode(mDstIn); canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mBitmapPaint); - mBitmapPaint = new Paint(); + mBitmapPaint.reset(); } } } diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java new file mode 100644 index 0000000..851a06c --- /dev/null +++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java @@ -0,0 +1,131 @@ +/* + * 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.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Shader; +import android.os.Bundle; +import android.view.View; + +@SuppressWarnings({"UnusedDeclaration"}) +public class ShadersActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(new ShadersView(this)); + } + + static class ShadersView extends View { + private BitmapShader mRepeatShader; + private BitmapShader mTranslatedShader; + private BitmapShader mScaledShader; + private int mTexWidth; + private int mTexHeight; + private Paint mPaint; + private float mDrawWidth; + private float mDrawHeight; + private LinearGradient mHorGradient; + private LinearGradient mDiagGradient; + private LinearGradient mVertGradient; + + ShadersView(Context c) { + super(c); + + Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1); + mTexWidth = texture.getWidth(); + mTexHeight = texture.getHeight(); + mDrawWidth = mTexWidth * 2.2f; + mDrawHeight = mTexHeight * 1.2f; + + mRepeatShader = new BitmapShader(texture, Shader.TileMode.REPEAT, + Shader.TileMode.REPEAT); + + mTranslatedShader = new BitmapShader(texture, Shader.TileMode.REPEAT, + Shader.TileMode.REPEAT); + Matrix m1 = new Matrix(); + m1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f); + mTranslatedShader.setLocalMatrix(m1); + + mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR, + Shader.TileMode.MIRROR); + Matrix m2 = new Matrix(); + m2.setScale(0.5f, 0.5f); + mScaledShader.setLocalMatrix(m2); + + mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f, + Color.RED, Color.GREEN, Shader.TileMode.REPEAT); + + mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 1.5f, mDrawHeight, + Color.BLUE, Color.MAGENTA, Shader.TileMode.CLAMP); + + mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f, + Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR); + + mPaint = new Paint(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.drawRGB(255, 255, 255); + + // Bitmap shaders + canvas.save(); + canvas.translate(40.0f, 40.0f); + + mPaint.setShader(mRepeatShader); + canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint); + + canvas.translate(0.0f, 40.0f + mDrawHeight); + mPaint.setShader(mTranslatedShader); + canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint); + + canvas.translate(0.0f, 40.0f + mDrawHeight); + mPaint.setShader(mScaledShader); + canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint); + + canvas.restore(); + + // Gradients + canvas.save(); + canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f); + + mPaint.setShader(mHorGradient); + canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint); + + canvas.translate(0.0f, 40.0f + mDrawHeight); + mPaint.setShader(mDiagGradient); + canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint); + + canvas.translate(0.0f, 40.0f + mDrawHeight); + mPaint.setShader(mVertGradient); + canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint); + + canvas.restore(); + } + } +} |
