/* * 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 "Caches.h" #include "SkiaShader.h" #include "Texture.h" #include "Matrix.h" namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// // Support /////////////////////////////////////////////////////////////////////////////// static const GLint gTileModes[] = { GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode GL_REPEAT, // == SkShader::kRepeat_Mode GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode }; /** * This function does not work for n == 0. */ static inline bool isPowerOfTwo(unsigned int n) { return !(n & (n - 1)); } static inline void bindUniformColor(int slot, uint32_t color) { const float a = ((color >> 24) & 0xff) / 255.0f; glUniform4f(slot, a * ((color >> 16) & 0xff) / 255.0f, a * ((color >> 8) & 0xff) / 255.0f, a * ((color ) & 0xff) / 255.0f, a); } /////////////////////////////////////////////////////////////////////////////// // Base shader /////////////////////////////////////////////////////////////////////////////// void SkiaShader::copyFrom(const SkiaShader& shader) { mType = shader.mType; mKey = shader.mKey; mTileX = shader.mTileX; mTileY = shader.mTileY; mBlend = shader.mBlend; mUnitMatrix = shader.mUnitMatrix; mShaderMatrix = shader.mShaderMatrix; mGenerationId = shader.mGenerationId; } SkiaShader::SkiaShader(): mCaches(NULL) { } SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix, bool blend): mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend), mCaches(NULL) { setMatrix(matrix); mGenerationId = 0; } 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(Texture* texture, GLenum wrapS, GLenum wrapT) { mCaches->bindTexture(texture->id); texture->setWrapST(wrapS, wrapT); } void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) { screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix); screenSpace.multiply(modelView); } /////////////////////////////////////////////////////////////////////////////// // 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), mTexture(NULL) { updateLocalMatrix(matrix); } SkiaShader* SkiaBitmapShader::copy() { SkiaBitmapShader* copy = new SkiaBitmapShader(); copy->copyFrom(*this); copy->mBitmap = mBitmap; return copy; } void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) { Texture* texture = mCaches->textureCache.get(mBitmap); if (!texture) return; mTexture = texture; 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)) && (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) { description.isBitmapNpot = true; description.bitmapWrapS = gTileModes[mTileX]; description.bitmapWrapT = gTileModes[mTileY]; mWrapS = GL_CLAMP_TO_EDGE; mWrapT = GL_CLAMP_TO_EDGE; } else { mWrapS = gTileModes[mTileX]; mWrapT = gTileModes[mTileY]; } } void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit) { GLuint textureSlot = (*textureUnit)++; Caches::getInstance().activeTexture(textureSlot); Texture* texture = mTexture; mTexture = NULL; if (!texture) return; const AutoTexture autoCleanup(texture); const float width = texture->width; const float height = texture->height; mat4 textureTransform; computeScreenSpaceMatrix(textureTransform, modelView); // Uniforms bindTexture(texture, mWrapS, mWrapT); texture->setFilter(GL_LINEAR); 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 /////////////////////////////////////////////////////////////////////////////// static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { SkVector vec = pts[1] - pts[0]; const float mag = vec.length(); const float inv = mag ? 1.0f / mag : 0; vec.scale(inv); matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); matrix->postTranslate(-pts[0].fX, -pts[0].fY); matrix->postScale(inv, inv); } 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) { SkPoint points[2]; points[0].set(bounds[0], bounds[1]); points[1].set(bounds[2], bounds[3]); SkMatrix unitMatrix; toUnitMatrix(points, &unitMatrix); mUnitMatrix.load(unitMatrix); updateLocalMatrix(matrix); mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode; } SkiaLinearGradientShader::~SkiaLinearGradientShader() { delete[] mBounds; delete[] mColors; delete[] mPositions; } SkiaShader* SkiaLinearGradientShader::copy() { SkiaLinearGradientShader* copy = new SkiaLinearGradientShader(); copy->copyFrom(*this); copy->mBounds = new float[4]; memcpy(copy->mBounds, mBounds, sizeof(float) * 4); copy->mColors = new uint32_t[mCount]; memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount); copy->mPositions = new float[mCount]; memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); copy->mCount = mCount; copy->mIsSimple = mIsSimple; return copy; } void SkiaLinearGradientShader::describe(ProgramDescription& description, const Extensions& extensions) { description.hasGradient = true; description.gradientType = ProgramDescription::kGradientLinear; description.isSimpleGradient = mIsSimple; } void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit) { if (CC_UNLIKELY(!mIsSimple)) { GLuint textureSlot = (*textureUnit)++; Caches::getInstance().activeTexture(textureSlot); Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount); // Uniforms bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]); glUniform1i(program->getUniform("gradientSampler"), textureSlot); } else { bindUniformColor(program->getUniform("startColor"), mColors[0]); bindUniformColor(program->getUniform("endColor"), mColors[1]); } Caches::getInstance().dither.setupProgram(program, textureUnit); mat4 screenSpace; computeScreenSpaceMatrix(screenSpace, modelView); glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); } /////////////////////////////////////////////////////////////////////////////// // Circular gradient shader /////////////////////////////////////////////////////////////////////////////// static void toCircularUnitMatrix(const float x, const float y, const float radius, SkMatrix* matrix) { const float inv = 1.0f / radius; matrix->setTranslate(-x, -y); matrix->postScale(inv, inv); } SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend): SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key, tileMode, matrix, blend) { SkMatrix unitMatrix; toCircularUnitMatrix(x, y, radius, &unitMatrix); mUnitMatrix.load(unitMatrix); updateLocalMatrix(matrix); } SkiaShader* SkiaCircularGradientShader::copy() { SkiaCircularGradientShader* copy = new SkiaCircularGradientShader(); copy->copyFrom(*this); copy->mColors = new uint32_t[mCount]; memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount); copy->mPositions = new float[mCount]; memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); copy->mCount = mCount; copy->mIsSimple = mIsSimple; return copy; } void SkiaCircularGradientShader::describe(ProgramDescription& description, const Extensions& extensions) { description.hasGradient = true; description.gradientType = ProgramDescription::kGradientCircular; description.isSimpleGradient = mIsSimple; } /////////////////////////////////////////////////////////////////////////////// // Sweep gradient shader /////////////////////////////////////////////////////////////////////////////// static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) { matrix->setTranslate(-x, -y); } SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend): SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, matrix, blend), mColors(colors), mPositions(positions), mCount(count) { SkMatrix unitMatrix; toSweepUnitMatrix(x, y, &unitMatrix); mUnitMatrix.load(unitMatrix); updateLocalMatrix(matrix); mIsSimple = count == 2; } SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend): SkiaShader(type, key, tileMode, tileMode, matrix, blend), mColors(colors), mPositions(positions), mCount(count) { mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode; } SkiaSweepGradientShader::~SkiaSweepGradientShader() { delete[] mColors; delete[] mPositions; } SkiaShader* SkiaSweepGradientShader::copy() { SkiaSweepGradientShader* copy = new SkiaSweepGradientShader(); copy->copyFrom(*this); copy->mColors = new uint32_t[mCount]; memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount); copy->mPositions = new float[mCount]; memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); copy->mCount = mCount; copy->mIsSimple = mIsSimple; return copy; } void SkiaSweepGradientShader::describe(ProgramDescription& description, const Extensions& extensions) { description.hasGradient = true; description.gradientType = ProgramDescription::kGradientSweep; description.isSimpleGradient = mIsSimple; } void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit) { if (CC_UNLIKELY(!mIsSimple)) { GLuint textureSlot = (*textureUnit)++; Caches::getInstance().activeTexture(textureSlot); Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount); // Uniforms bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]); glUniform1i(program->getUniform("gradientSampler"), textureSlot); } else { bindUniformColor(program->getUniform("startColor"), mColors[0]); bindUniformColor(program->getUniform("endColor"), mColors[1]); } mCaches->dither.setupProgram(program, textureUnit); mat4 screenSpace; computeScreenSpaceMatrix(screenSpace, modelView); 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), mCleanup(false) { } SkiaComposeShader::~SkiaComposeShader() { if (mCleanup) { delete mFirst; delete mSecond; } } SkiaShader* SkiaComposeShader::copy() { SkiaComposeShader* copy = new SkiaComposeShader(); copy->copyFrom(*this); copy->mFirst = mFirst->copy(); copy->mSecond = mSecond->copy(); copy->mMode = mMode; copy->cleanup(); return copy; } 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) { // Apply this compose shader's local transform and pass it down to // the child shaders. They will in turn apply their local transform // to this matrix. mat4 transform; computeScreenSpaceMatrix(transform, modelView); mFirst->setupProgram(program, transform, snapshot, textureUnit); mSecond->setupProgram(program, transform, snapshot, textureUnit); } }; // namespace uirenderer }; // namespace android