Refactor Skia shaders handling.
With this change, Skia shaders can easily be applied to any mesh. This change also
supports ComposeShader. For instance, this can be used to blend a gradient and a
bitmap togehter and paint a string of text with the result.
Change-Id: I701c2f9cf7f89b2ff58005e8a1d0d80ccf4a4aea
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index a9714c7..fe1b524 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -11,6 +11,7 @@
PatchCache.cpp \
Program.cpp \
ProgramCache.cpp \
+ SkiaShader.cpp \
TextureCache.cpp
LOCAL_C_INCLUDES += \
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cc8e6bc..187e9d8 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -41,6 +41,8 @@
#define DEFAULT_PATCH_CACHE_SIZE 100
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
+#define REQUIRED_TEXTURE_UNITS_COUNT 3
+
// Converts a number of mega-bytes into bytes
#define MB(s) s * 1024 * 1024
@@ -88,16 +90,10 @@
{ 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
-};
-
static const GLenum gTextureUnits[] = {
- GL_TEXTURE0, // Bitmap or text
- GL_TEXTURE1, // Gradient
- GL_TEXTURE2 // Bitmap shader
+ GL_TEXTURE0,
+ GL_TEXTURE1,
+ GL_TEXTURE2
};
///////////////////////////////////////////////////////////////////////////////
@@ -135,12 +131,7 @@
}
mCurrentProgram = NULL;
-
- mShader = kShaderNone;
- mShaderTileX = GL_CLAMP_TO_EDGE;
- mShaderTileY = GL_CLAMP_TO_EDGE;
- mShaderMatrix = NULL;
- mShaderBitmap = NULL;
+ mShader = NULL;
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
@@ -560,16 +551,21 @@
mModelView.loadIdentity();
+ GLuint textureUnit = 0;
+
ProgramDescription description;
description.hasTexture = true;
description.hasAlpha8Texture = true;
+ if (mShader) {
+ mShader->describe(description, mExtensions);
+ }
useProgram(mProgramCache.get(description));
mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
chooseBlending(true, mode);
- bindTexture(mFontRenderer.getTexture(), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0);
- glUniform1i(mCurrentProgram->getUniform("sampler"), 0);
+ bindTexture(mFontRenderer.getTexture(), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
+ glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
glEnableVertexAttribArray(texCoordsSlot);
@@ -577,6 +573,12 @@
// Always premultiplied
glUniform4f(mCurrentProgram->color, r, g, b, a);
+ textureUnit++;
+ // Setup attributes and uniforms required by the shaders
+ if (mShader) {
+ mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
+ }
+
// TODO: Implement scale properly
const Rect& clip = mSnapshot->getLocalClip();
mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
@@ -591,36 +593,14 @@
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::resetShader() {
- mShader = OpenGLRenderer::kShaderNone;
- mShaderKey = NULL;
- mShaderBlend = false;
- mShaderTileX = GL_CLAMP_TO_EDGE;
- mShaderTileY = GL_CLAMP_TO_EDGE;
+ mShader = NULL;
}
-void OpenGLRenderer::setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX,
- SkShader::TileMode tileY, SkMatrix* matrix, bool hasAlpha) {
- mShader = OpenGLRenderer::kShaderBitmap;
- mShaderBlend = hasAlpha;
- mShaderBitmap = bitmap;
- mShaderTileX = gTileModes[tileX];
- mShaderTileY = gTileModes[tileY];
- mShaderMatrix = matrix;
-}
-
-void OpenGLRenderer::setupLinearGradientShader(SkShader* shader, float* bounds, uint32_t* colors,
- float* positions, int count, SkShader::TileMode tileMode, SkMatrix* matrix, bool hasAlpha) {
- // TODO: We should use a struct to describe each shader
- mShader = OpenGLRenderer::kShaderLinearGradient;
- mShaderKey = shader;
- mShaderBlend = hasAlpha;
- mShaderTileX = gTileModes[tileMode];
- mShaderTileY = gTileModes[tileMode];
- mShaderMatrix = matrix;
- mShaderBounds = bounds;
- mShaderColors = colors;
- mShaderPositions = positions;
- mShaderCount = count;
+void OpenGLRenderer::setupShader(SkiaShader* shader) {
+ mShader = shader;
+ if (mShader) {
+ mShader->set(&mTextureCache, &mGradientCache);
+ }
}
///////////////////////////////////////////////////////////////////////////////
@@ -630,165 +610,52 @@
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) {
+ if (mShader) {
color |= 0x00ffffff;
}
// Render using pre-multiplied alpha
const int alpha = (color >> 24) & 0xFF;
const GLfloat a = alpha / 255.0f;
-
- switch (mShader) {
- case OpenGLRenderer::kShaderBitmap:
- drawBitmapShader(left, top, right, bottom, a, mode);
- return;
- case OpenGLRenderer::kShaderLinearGradient:
- drawLinearGradientShader(left, top, right, bottom, a, mode);
- return;
- default:
- break;
- }
-
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;
- // Pre-multiplication happens when setting the shader color
- chooseBlending(alpha < 255 || mShaderBlend, mode);
+ GLuint textureUnit = 0;
- mModelView.loadTranslate(left, top, 0.0f);
- mModelView.scale(right - left, bottom - top, 1.0f);
+ // Setup the blending mode
+ chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode);
+ // Describe the required shaders
ProgramDescription description;
- Program* program = mProgramCache.get(description);
- if (!useProgram(program)) {
- const GLvoid* vertices = &mMeshVertices[0].position[0];
- const GLvoid* texCoords = &mMeshVertices[0].texture[0];
-
- glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
- gMeshStride, vertices);
+ if (mShader) {
+ mShader->describe(description, mExtensions);
}
+ // Build and use the appropriate shader
+ useProgram(mProgramCache.get(description));
+
+ // Setup attributes
+ glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+ gMeshStride, &mMeshVertices[0].position[0]);
+
+ // Setup uniforms
+ mModelView.loadTranslate(left, top, 0.0f);
+ mModelView.scale(right - left, bottom - top, 1.0f);
if (!ignoreTransform) {
mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
} else {
mat4 identity;
mCurrentProgram->set(mOrthoMatrix, mModelView, identity);
}
-
glUniform4f(mCurrentProgram->color, r, g, b, a);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-}
-
-void OpenGLRenderer::drawLinearGradientShader(float left, float top, float right, float bottom,
- float alpha, SkXfermode::Mode mode) {
- Texture* texture = mGradientCache.get(mShaderKey);
- if (!texture) {
- SkShader::TileMode tileMode = SkShader::kClamp_TileMode;
- switch (mShaderTileX) {
- case GL_REPEAT:
- tileMode = SkShader::kRepeat_TileMode;
- break;
- case GL_MIRRORED_REPEAT:
- tileMode = SkShader::kMirror_TileMode;
- break;
- }
-
- texture = mGradientCache.addLinearGradient(mShaderKey, mShaderBounds, mShaderColors,
- mShaderPositions, mShaderCount, tileMode);
+ // Setup attributes and uniforms required by the shaders
+ if (mShader) {
+ mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
}
- ProgramDescription description;
- description.hasGradient = true;
-
- mModelView.loadTranslate(left, top, 0.0f);
- mModelView.scale(right - left, bottom - top, 1.0f);
-
- useProgram(mProgramCache.get(description));
- mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
-
- chooseBlending(mShaderBlend || alpha < 1.0f, mode);
- bindTexture(texture->id, mShaderTileX, mShaderTileY, 0);
- glUniform1i(mCurrentProgram->getUniform("gradientSampler"), 0);
-
- Rect start(mShaderBounds[0], mShaderBounds[1], mShaderBounds[2], mShaderBounds[3]);
- if (mShaderMatrix) {
- mat4 shaderMatrix(*mShaderMatrix);
- shaderMatrix.mapRect(start);
- }
- mSnapshot->transform.mapRect(start);
-
- const float gradientX = start.right - start.left;
- const float gradientY = start.bottom - start.top;
-
- mat4 screenSpace(mSnapshot->transform);
- screenSpace.multiply(mModelView);
-
- // Always premultiplied
- glUniform4f(mCurrentProgram->color, alpha, alpha, alpha, alpha);
- glUniform2f(mCurrentProgram->getUniform("gradientStart"), start.left, start.top);
- glUniform2f(mCurrentProgram->getUniform("gradient"), gradientX, gradientY);
- glUniform1f(mCurrentProgram->getUniform("gradientLength"),
- 1.0f / (gradientX * gradientX + gradientY * gradientY));
- glUniformMatrix4fv(mCurrentProgram->getUniform("screenSpace"), 1, GL_FALSE,
- &screenSpace.data[0]);
-
- glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
- gMeshStride, &mMeshVertices[0].position[0]);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-}
-
-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;
-
- mModelView.loadTranslate(left, top, 0.0f);
- mModelView.scale(right - left, bottom - top, 1.0f);
-
- mat4 textureTransform;
- if (mShaderMatrix) {
- SkMatrix inverse;
- mShaderMatrix->invert(&inverse);
- textureTransform.load(inverse);
- textureTransform.multiply(mModelView);
- } else {
- textureTransform.load(mModelView);
- }
-
- ProgramDescription description;
- description.hasBitmap = true;
- // The driver does not support non-power of two mirrored/repeated
- // textures, so do it ourselves
- if (!mExtensions.hasNPot()) {
- description.isBitmapNpot = true;
- description.bitmapWrapS = mShaderTileX;
- description.bitmapWrapT = mShaderTileY;
- }
-
- useProgram(mProgramCache.get(description));
- mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
-
- chooseBlending(texture->blend || alpha < 1.0f, mode);
-
- // Texture
- bindTexture(texture->id, mShaderTileX, mShaderTileY, 0);
- glUniform1i(mCurrentProgram->getUniform("bitmapSampler"), 0);
- glUniformMatrix4fv(mCurrentProgram->getUniform("textureTransform"), 1,
- GL_FALSE, &textureTransform.data[0]);
- glUniform2f(mCurrentProgram->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
-
- // Always premultiplied
- glUniform4f(mCurrentProgram->color, alpha, alpha, alpha, alpha);
-
- // Mesh
- glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
- gMeshStride, &mMeshVertices[0].position[0]);
-
+ // Draw the mesh
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
}
@@ -823,7 +690,7 @@
chooseBlending(blend || alpha < 1.0f, mode);
// Texture
- bindTexture(texture, mShaderTileX, mShaderTileY, 0);
+ bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0);
glUniform1i(mCurrentProgram->getUniform("sampler"), 0);
// Always premultiplied
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 937ff08..dc0f50f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -42,6 +42,7 @@
#include "Vertex.h"
#include "FontRenderer.h"
#include "ProgramCache.h"
+#include "SkiaShader.h"
namespace android {
namespace uirenderer {
@@ -50,8 +51,6 @@
// Renderer
///////////////////////////////////////////////////////////////////////////////
-#define REQUIRED_TEXTURE_UNITS_COUNT 3
-
/**
* OpenGL renderer used to draw accelerated 2D graphics. The API is a
* simplified version of Skia's Canvas API.
@@ -94,27 +93,12 @@
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);
- void setupLinearGradientShader(SkShader* shader, float* bounds, uint32_t* colors,
- float* positions, int count, SkShader::TileMode tileMode,
- SkMatrix* matrix, bool hasAlpha);
+ void setupShader(SkiaShader* shader);
void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint);
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.
@@ -232,32 +216,6 @@
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);
-
- /**
- * Fills the specified rectangle with the currently set linear gradient 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 drawLinearGradientShader(float left, float top, float right, float bottom, float alpha,
- SkXfermode::Mode mode);
-
- /**
* Resets the texture coordinates stored in mMeshVertices. Setting the values
* back to default is achieved by calling:
*
@@ -321,6 +279,7 @@
// Shaders
Program* mCurrentProgram;
+ SkiaShader* mShader;
// Used to draw textured quads
TextureVertex mMeshVertices[4];
@@ -330,21 +289,6 @@
GLenum mLastSrcMode;
GLenum mLastDstMode;
- // Skia shaders
- ShaderType mShader;
- SkShader* mShaderKey;
- bool mShaderBlend;
- GLenum mShaderTileX;
- GLenum mShaderTileY;
- SkMatrix* mShaderMatrix;
- // Bitmaps
- SkBitmap* mShaderBitmap;
- // Gradients
- float* mShaderBounds;
- uint32_t* mShaderColors;
- float* mShaderPositions;
- int mShaderCount;
-
// GL extensions
Extensions mExtensions;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index c9e2d2e..23923f6 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -106,9 +106,9 @@
const char* gFS_Main_FetchBitmapNpot =
" vec4 bitmapColor = texture2D(bitmapSampler, wrap(outBitmapTexCoords));\n";
const char* gFS_Main_BlendShadersBG =
- " fragColor = blendShaders(bitmapColor, gradientColor)";
-const char* gFS_Main_BlendShadersGB =
" fragColor = blendShaders(gradientColor, bitmapColor)";
+const char* gFS_Main_BlendShadersGB =
+ " fragColor = blendShaders(bitmapColor, gradientColor)";
const char* gFS_Main_BlendShaders_Modulate =
" * fragColor.a;\n";
const char* gFS_Main_GradientShader_Modulate =
@@ -144,23 +144,23 @@
// Dst
"return dst;\n",
// SrcOver
- "return vec4(src.rgb + (1.0 - src.a) * dst.rgb, src.a + dst.a - src.a * dst.a);\n",
+ "return src + dst * (1.0 - src.a);\n",
// DstOver
- "return vec4(dst.rgb + (1.0 - dst.a) * src.rgb, src.a + dst.a - src.a * dst.a);\n",
+ "return dst + src * (1.0 - dst.a);\n",
// SrcIn
- "return vec4(src.rgb * dst.a, src.a * dst.a);\n",
+ "return src * dst.a;\n",
// DstIn
- "return vec4(dst.rgb * src.a, src.a * dst.a);\n",
+ "return dst * src.a;\n",
// SrcOut
- "return vec4(src.rgb * (1.0 - dst.a), src.a * (1.0 - dst.a));\n",
+ "return src * (1.0 - dst.a);\n",
// DstOut
- "return vec4(dst.rgb * (1.0 - src.a), dst.a * (1.0 - src.a));\n",
+ "return dst * (1.0 - src.a);\n",
// SrcAtop
"return vec4(src.rgb * dst.a + (1.0 - src.a) * dst.rgb, dst.a);\n",
// DstAtop
"return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n",
// Xor
- "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, "
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, 1.0, "
"src.a + dst.a - 2.0 * src.a * dst.a);\n",
};
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 5a6ec33..d60f6ce 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -19,6 +19,7 @@
#include <utils/KeyedVector.h>
#include <utils/Log.h>
+#include <utils/String8.h>
#include <GLES2/gl2.h>
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
new file mode 100644
index 0000000..fedb56c
--- /dev/null
+++ b/libs/hwui/SkiaShader.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Log.h>
+
+#include <SkMatrix.h>
+
+#include "SkiaShader.h"
+#include "Texture.h"
+#include "Matrix.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Support
+///////////////////////////////////////////////////////////////////////////////
+
+static const GLenum gTextureUnitsMap[] = {
+ GL_TEXTURE0,
+ GL_TEXTURE1,
+ GL_TEXTURE2
+};
+
+static const GLint gTileModes[] = {
+ GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode
+ GL_REPEAT, // == SkShader::kRepeat_Mode
+ GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Base shader
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
+ SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
+ mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mMatrix(matrix), mBlend(blend) {
+}
+
+SkiaShader::~SkiaShader() {
+}
+
+void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) {
+}
+
+void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+ GLuint* textureUnit) {
+}
+
+void SkiaShader::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit) {
+ glActiveTexture(gTextureUnitsMap[textureUnit]);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Bitmap shader
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
+ SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
+ SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap) {
+}
+
+SkiaBitmapShader::~SkiaBitmapShader() {
+}
+
+void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
+ const Texture* texture = mTextureCache->get(mBitmap);
+
+ const float width = texture->width;
+ const float height = texture->height;
+
+ description.hasBitmap = true;
+ // The driver does not support non-power of two mirrored/repeated
+ // textures, so do it ourselves
+ if (!extensions.hasNPot() && !isPowerOfTwo(width) && !isPowerOfTwo(height)) {
+ description.isBitmapNpot = true;
+ description.bitmapWrapS = gTileModes[mTileX];
+ description.bitmapWrapT = gTileModes[mTileY];
+ }
+}
+
+void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
+ const Snapshot& snapshot, GLuint* textureUnit) {
+ GLuint textureSlot = (*textureUnit)++;
+ glActiveTexture(gTextureUnitsMap[textureSlot]);
+ const Texture* texture = mTextureCache->get(mBitmap);
+
+ const float width = texture->width;
+ const float height = texture->height;
+
+ mat4 textureTransform;
+ if (mMatrix) {
+ SkMatrix inverse;
+ mMatrix->invert(&inverse);
+ textureTransform.load(inverse);
+ textureTransform.multiply(modelView);
+ } else {
+ textureTransform.load(modelView);
+ }
+
+ // Uniforms
+ bindTexture(texture->id, gTileModes[mTileX], gTileModes[mTileY], textureSlot);
+ glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
+ glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
+ GL_FALSE, &textureTransform.data[0]);
+ glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Linear gradient shader
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
+ float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
+ SkMatrix* matrix, bool blend):
+ SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
+ mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
+}
+
+SkiaLinearGradientShader::~SkiaLinearGradientShader() {
+ delete mBounds;
+ delete mColors;
+ delete mPositions;
+}
+
+void SkiaLinearGradientShader::describe(ProgramDescription& description,
+ const Extensions& extensions) {
+ description.hasGradient = true;
+}
+
+void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
+ const Snapshot& snapshot, GLuint* textureUnit) {
+ GLuint textureSlot = (*textureUnit)++;
+ glActiveTexture(gTextureUnitsMap[textureSlot]);
+
+ Texture* texture = mGradientCache->get(mKey);
+ if (!texture) {
+ texture = mGradientCache->addLinearGradient(mKey, mBounds, mColors, mPositions,
+ mCount, mTileX);
+ }
+
+ Rect start(mBounds[0], mBounds[1], mBounds[2], mBounds[3]);
+ if (mMatrix) {
+ mat4 shaderMatrix(*mMatrix);
+ shaderMatrix.mapRect(start);
+ }
+ snapshot.transform.mapRect(start);
+
+ const float gradientX = start.right - start.left;
+ const float gradientY = start.bottom - start.top;
+
+ mat4 screenSpace(snapshot.transform);
+ screenSpace.multiply(modelView);
+
+ // Uniforms
+ bindTexture(texture->id, gTileModes[mTileX], gTileModes[mTileY], textureSlot);
+ glUniform1i(program->getUniform("gradientSampler"), textureSlot);
+ glUniform2f(program->getUniform("gradientStart"), start.left, start.top);
+ glUniform2f(program->getUniform("gradient"), gradientX, gradientY);
+ glUniform1f(program->getUniform("gradientLength"),
+ 1.0f / (gradientX * gradientX + gradientY * gradientY));
+ glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Compose shader
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
+ SkXfermode::Mode mode, SkShader* key):
+ SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
+ NULL, first->blend() || second->blend()), mFirst(first), mSecond(second), mMode(mode) {
+}
+
+SkiaComposeShader::~SkiaComposeShader() {
+ delete mFirst;
+ delete mSecond;
+}
+
+void SkiaComposeShader::set(TextureCache* textureCache, GradientCache* gradientCache) {
+ SkiaShader::set(textureCache, gradientCache);
+ mFirst->set(textureCache, gradientCache);
+ mSecond->set(textureCache, gradientCache);
+}
+
+void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
+ mFirst->describe(description, extensions);
+ mSecond->describe(description, extensions);
+ if (mFirst->type() == kBitmap) {
+ description.isBitmapFirst = true;
+ }
+ description.shadersMode = mMode;
+}
+
+void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
+ const Snapshot& snapshot, GLuint* textureUnit) {
+ mFirst->setupProgram(program, modelView, snapshot, textureUnit);
+ mSecond->setupProgram(program, modelView, snapshot, textureUnit);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
new file mode 100644
index 0000000..b5e6aeb
--- /dev/null
+++ b/libs/hwui/SkiaShader.h
@@ -0,0 +1,163 @@
+/*
+ * 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 SKIA_SHADER_H
+#define SKIA_SHADER_H
+
+#include <SkShader.h>
+#include <SkXfermode.h>
+
+#include <GLES2/gl2.h>
+
+#include "Extensions.h"
+#include "ProgramCache.h"
+#include "TextureCache.h"
+#include "GradientCache.h"
+#include "Snapshot.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Base shader
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Represents a Skia shader. A shader will modify the GL context and active
+ * program to recreate the original effect.
+ */
+struct SkiaShader {
+ /**
+ * Type of Skia shader in use.
+ */
+ enum Type {
+ kNone,
+ kBitmap,
+ kLinearGradient,
+ kCircularGradient,
+ kSweepGradient,
+ kCompose
+ };
+
+ SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, SkShader::TileMode tileY,
+ SkMatrix* matrix, bool blend);
+ virtual ~SkiaShader();
+
+ virtual void describe(ProgramDescription& description, const Extensions& extensions);
+ virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+ GLuint* textureUnit);
+
+ inline bool blend() const {
+ return mBlend;
+ }
+
+ Type type() const {
+ return mType;
+ }
+
+ virtual void set(TextureCache* textureCache, GradientCache* gradientCache) {
+ mTextureCache = textureCache;
+ mGradientCache = gradientCache;
+ }
+
+ void setMatrix(SkMatrix* matrix) {
+ mMatrix = matrix;
+ }
+
+protected:
+ inline void bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit);
+
+ Type mType;
+ SkShader* mKey;
+ SkShader::TileMode mTileX;
+ SkShader::TileMode mTileY;
+ SkMatrix* mMatrix;
+ bool mBlend;
+
+ TextureCache* mTextureCache;
+ GradientCache* mGradientCache;
+}; // struct SkiaShader
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Implementations
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A shader that draws a bitmap.
+ */
+struct SkiaBitmapShader: public SkiaShader {
+ SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
+ SkShader::TileMode tileY, SkMatrix* matrix, bool blend);
+ ~SkiaBitmapShader();
+
+ void describe(ProgramDescription& description, const Extensions& extensions);
+ void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+ GLuint* textureUnit);
+
+private:
+ /**
+ * This method does not work for n == 0.
+ */
+ inline bool isPowerOfTwo(unsigned int n) {
+ return !(n & (n - 1));
+ }
+
+ SkBitmap* mBitmap;
+}; // struct SkiaBitmapShader
+
+/**
+ * A shader that draws a linear gradient.
+ */
+struct SkiaLinearGradientShader: public SkiaShader {
+ SkiaLinearGradientShader(float* bounds, uint32_t* colors, float* positions, int count,
+ SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
+ ~SkiaLinearGradientShader();
+
+ void describe(ProgramDescription& description, const Extensions& extensions);
+ void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+ GLuint* textureUnit);
+
+private:
+ float* mBounds;
+ uint32_t* mColors;
+ float* mPositions;
+ int mCount;
+}; // struct SkiaLinearGradientShader
+
+/**
+ * A shader that draws two shaders, composited with an xfermode.
+ */
+struct SkiaComposeShader: public SkiaShader {
+ SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode, SkShader* key);
+ ~SkiaComposeShader();
+
+ void set(TextureCache* textureCache, GradientCache* gradientCache);
+
+ void describe(ProgramDescription& description, const Extensions& extensions);
+ void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+ GLuint* textureUnit);
+
+private:
+ SkiaShader* mFirst;
+ SkiaShader* mSecond;
+ SkXfermode::Mode mMode;
+}; // struct SkiaComposeShader
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // SKIA_SHADER_H