summaryrefslogtreecommitdiffstats
path: root/libs/hwui/SkiaShader.cpp
diff options
context:
space:
mode:
authorChris Craik <ccraik@google.com>2015-02-13 17:47:21 -0800
committerChris Craik <ccraik@google.com>2015-02-17 15:45:46 -0800
commit922d3a7f6f8c1c05a996ee3e91e8cbadfff560c9 (patch)
tree9b8acab9755321ae0e7564d2250f2106117412cb /libs/hwui/SkiaShader.cpp
parent91d415d863de73430af100ebd1b4f5b3b795e8d1 (diff)
downloadframeworks_base-922d3a7f6f8c1c05a996ee3e91e8cbadfff560c9.zip
frameworks_base-922d3a7f6f8c1c05a996ee3e91e8cbadfff560c9.tar.gz
frameworks_base-922d3a7f6f8c1c05a996ee3e91e8cbadfff560c9.tar.bz2
Glop SkiaShader support
Change-Id: I894a0b62701bd02367ab970813e4c332147351a2
Diffstat (limited to 'libs/hwui/SkiaShader.cpp')
-rw-r--r--libs/hwui/SkiaShader.cpp301
1 files changed, 295 insertions, 6 deletions
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 9c929da..81531e8 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -16,17 +16,17 @@
#define LOG_TAG "OpenGLRenderer"
-#include <utils/Log.h>
-
-#include <SkMatrix.h>
+#include "SkiaShader.h"
#include "Caches.h"
#include "Extensions.h"
#include "Layer.h"
#include "Matrix.h"
-#include "SkiaShader.h"
#include "Texture.h"
+#include <SkMatrix.h>
+#include <utils/Log.h>
+
namespace android {
namespace uirenderer {
@@ -34,7 +34,7 @@ namespace uirenderer {
// Support
///////////////////////////////////////////////////////////////////////////////
-static const GLint gTileModes[] = {
+static const GLenum gTileModes[] = {
GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode
GL_REPEAT, // == SkShader::kRepeat_Mode
GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode
@@ -56,6 +56,10 @@ static inline void bindUniformColor(int slot, uint32_t color) {
a);
}
+static inline void bindUniformColor(int slot, FloatColor color) {
+ glUniform4fv(slot, 1, reinterpret_cast<const float*>(&color));
+}
+
static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
caches->textureState().bindTexture(texture->id);
texture->setWrapST(wrapS, wrapT);
@@ -270,7 +274,7 @@ void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
}
GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().textureState().activateTexture(textureSlot);
+ caches->textureState().activateTexture(textureSlot);
BitmapShaderInfo shaderInfo;
if (!bitmapShaderHelper(caches, nullptr, &shaderInfo, extensions, bitmap, xy)) {
@@ -470,5 +474,290 @@ void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix
SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB);
}
+///////////////////////////////////////////////////////////////////////////////
+// Store / apply
+///////////////////////////////////////////////////////////////////////////////
+
+bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData::GradientShaderData* outData) {
+ SkShader::GradientInfo gradInfo;
+ gradInfo.fColorCount = 0;
+ gradInfo.fColors = nullptr;
+ gradInfo.fColorOffsets = nullptr;
+
+ SkMatrix unitMatrix;
+ switch (shader.asAGradient(&gradInfo)) {
+ case SkShader::kLinear_GradientType:
+ description->gradientType = ProgramDescription::kGradientLinear;
+
+ toUnitMatrix(gradInfo.fPoint, &unitMatrix);
+ break;
+ case SkShader::kRadial_GradientType:
+ description->gradientType = ProgramDescription::kGradientCircular;
+
+ toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
+ gradInfo.fRadius[0], &unitMatrix);
+ break;
+ case SkShader::kSweep_GradientType:
+ description->gradientType = ProgramDescription::kGradientSweep;
+
+ toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
+ break;
+ default:
+ // Do nothing. This shader is unsupported.
+ return false;
+ }
+ description->hasGradient = true;
+ description->isSimpleGradient = isSimpleGradient(gradInfo);
+
+ computeScreenSpaceMatrix(outData->screenSpace, unitMatrix,
+ shader.getLocalMatrix(), modelViewMatrix);
+
+ // re-query shader to get full color / offset data
+ std::unique_ptr<SkColor[]> colorStorage(new SkColor[gradInfo.fColorCount]);
+ std::unique_ptr<SkScalar[]> colorOffsets(new SkScalar[gradInfo.fColorCount]);
+ gradInfo.fColors = &colorStorage[0];
+ gradInfo.fColorOffsets = &colorOffsets[0];
+ shader.asAGradient(&gradInfo);
+
+ if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
+ outData->gradientSampler = (*textureUnit)++;
+
+#ifndef SK_SCALAR_IS_FLOAT
+ #error Need to convert gradInfo.fColorOffsets to float!
+#endif
+ outData->gradientTexture = caches.gradientCache.get(
+ gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount);
+ outData->wrapST = gTileModes[gradInfo.fTileMode];
+ } else {
+ outData->gradientSampler = 0;
+ outData->gradientTexture = nullptr;
+
+ outData->startColor.set(gradInfo.fColors[0]);
+ outData->endColor.set(gradInfo.fColors[1]);
+ }
+
+ outData->ditherSampler = (*textureUnit)++;
+ return true;
+}
+
+void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) {
+ if (CC_UNLIKELY(data.gradientTexture)) {
+ caches.textureState().activateTexture(data.gradientSampler);
+ bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST);
+ glUniform1i(caches.program().getUniform("gradientSampler"), data.gradientSampler);
+ } else {
+ bindUniformColor(caches.program().getUniform("startColor"), data.startColor);
+ bindUniformColor(caches.program().getUniform("endColor"), data.endColor);
+ }
+
+ // TODO: remove sampler slot incrementing from dither.setupProgram,
+ // since this assignment of slots is done at store, not apply time
+ GLuint ditherSampler = data.ditherSampler;
+ caches.dither.setupProgram(caches.program(), &ditherSampler);
+ glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1,
+ GL_FALSE, &data.screenSpace.data[0]);
+}
+
+bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData::BitmapShaderData* outData) {
+ SkBitmap bitmap;
+ SkShader::TileMode xy[2];
+ if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) {
+ return false;
+ }
+
+ outData->bitmapTexture = caches.textureCache.get(&bitmap);
+ if (!outData->bitmapTexture) return false;
+
+ outData->bitmapSampler = (*textureUnit)++;
+
+ const float width = outData->bitmapTexture->width;
+ const float height = outData->bitmapTexture->height;
+
+ description->hasBitmap = true;
+ if (!caches.extensions().hasNPot()
+ && (!isPowerOfTwo(width) || !isPowerOfTwo(height))
+ && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode)) {
+ description->isBitmapNpot = true;
+ description->bitmapWrapS = gTileModes[xy[0]];
+ description->bitmapWrapT = gTileModes[xy[1]];
+
+ outData->wrapS = GL_CLAMP_TO_EDGE;
+ outData->wrapT = GL_CLAMP_TO_EDGE;
+ } else {
+ outData->wrapS = gTileModes[xy[0]];
+ outData->wrapT = gTileModes[xy[1]];
+ }
+
+ computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+ modelViewMatrix);
+ outData->textureDimension[0] = 1.0f / width;
+ outData->textureDimension[1] = 1.0f / height;
+
+ return true;
+}
+
+void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) {
+ caches.textureState().activateTexture(data.bitmapSampler);
+ bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT);
+ data.bitmapTexture->setFilter(GL_LINEAR);
+
+ glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
+ glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE,
+ &data.textureTransform.data[0]);
+ glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
+}
+
+SkiaShaderType getComposeSubType(const SkShader& shader) {
+ // First check for a gradient shader.
+ switch (shader.asAGradient(nullptr)) {
+ case SkShader::kNone_GradientType:
+ // Not a gradient shader. Fall through to check for other types.
+ break;
+ case SkShader::kLinear_GradientType:
+ case SkShader::kRadial_GradientType:
+ case SkShader::kSweep_GradientType:
+ return kGradient_SkiaShaderType;
+ default:
+ // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
+ return kNone_SkiaShaderType;
+ }
+
+ // The shader is not a gradient. Check for a bitmap shader.
+ if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) {
+ return kBitmap_SkiaShaderType;
+ }
+ return kNone_SkiaShaderType;
+}
+
+void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader,
+ const Matrix4& modelViewMatrix, GLuint* textureUnit,
+ ProgramDescription* description, SkiaShaderData* outData) {
+ LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix,
+ textureUnit, description, &outData->bitmapData),
+ "failed storing bitmap shader data");
+ LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix,
+ textureUnit, description, &outData->gradientData),
+ "failing storing gradient shader data");
+}
+
+bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData* outData) {
+
+ SkShader::ComposeRec rec;
+ if (!shader.asACompose(&rec)) return false;
+
+ const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA);
+ const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB);
+
+ // check that type enum values are the 2 flags that compose the kCompose value
+ if ((shaderAType & shaderBType) != 0) return false;
+ if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false;
+
+ mat4 transform;
+ computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix);
+ if (shaderAType == kBitmap_SkiaShaderType) {
+ description->isBitmapFirst = true;
+ storeCompose(caches, *rec.fShaderA, *rec.fShaderB,
+ transform, textureUnit, description, outData);
+ } else {
+ description->isBitmapFirst = false;
+ storeCompose(caches, *rec.fShaderB, *rec.fShaderA,
+ transform, textureUnit, description, outData);
+ }
+ if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) {
+ // TODO: Support other modes.
+ description->shadersMode = SkXfermode::kSrcOver_Mode;
+ }
+ return true;
+}
+
+bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData::LayerShaderData* outData) {
+ Layer* layer;
+ if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
+ return false;
+ }
+
+ description->hasBitmap = true;
+
+ outData->bitmapSampler = (*textureUnit)++;
+
+ const float width = layer->getWidth();
+ const float height = layer->getHeight();
+
+ computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+ modelViewMatrix);
+
+ outData->textureDimension[0] = 1.0f / width;
+ outData->textureDimension[1] = 1.0f / height;
+ return true;
+}
+
+void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) {
+ caches.textureState().activateTexture(data.bitmapSampler);
+
+ data.layer->bindTexture();
+ data.layer->setWrap(GL_CLAMP_TO_EDGE);
+ data.layer->setFilter(GL_LINEAR);
+
+ glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
+ glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1,
+ GL_FALSE, &data.textureTransform.data[0]);
+ glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
+}
+
+void SkiaShader::store(Caches& caches, const SkShader* shader, const Matrix4& modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData* outData) {
+ if (!shader) {
+ outData->skiaShaderType = kNone_SkiaShaderType;
+ return;
+ }
+
+ if (tryStoreGradient(caches, *shader, modelViewMatrix,
+ textureUnit, description, &outData->gradientData)) {
+ outData->skiaShaderType = kGradient_SkiaShaderType;
+ return;
+ }
+
+ if (tryStoreBitmap(caches, *shader, modelViewMatrix,
+ textureUnit, description, &outData->bitmapData)) {
+ outData->skiaShaderType = kBitmap_SkiaShaderType;
+ return;
+ }
+
+ if (tryStoreCompose(caches, *shader, modelViewMatrix,
+ textureUnit, description, outData)) {
+ outData->skiaShaderType = kCompose_SkiaShaderType;
+ return;
+ }
+
+ if (tryStoreLayer(caches, *shader, modelViewMatrix,
+ textureUnit, description, &outData->layerData)) {
+ outData->skiaShaderType = kLayer_SkiaShaderType;
+ }
+}
+
+void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) {
+ if (!data.skiaShaderType) return;
+
+ if (data.skiaShaderType & kGradient_SkiaShaderType) {
+ applyGradient(caches, data.gradientData);
+ }
+ if (data.skiaShaderType & kBitmap_SkiaShaderType) {
+ applyBitmap(caches, data.bitmapData);
+ }
+
+ if (data.skiaShaderType == kLayer_SkiaShaderType) {
+ applyLayer(caches, data.layerData);
+ }
+}
+
}; // namespace uirenderer
}; // namespace android