/* * 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 }; /////////////////////////////////////////////////////////////////////////////// // 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(Type type, SkShader* key, SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix, bool blend): mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend) { 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) { glBindTexture(GL_TEXTURE_2D, 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 = mTextureCache->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); // Assume linear here; we should really check the transform in // ::updateTransforms() but we don't have the texture object // available at that point. The optimization is not worth the // effort for now. 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); } void SkiaBitmapShader::updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot) { mat4 textureTransform; computeScreenSpaceMatrix(textureTransform, modelView); glUniformMatrix4fv(program->getUniform("textureTransform"), 1, GL_FALSE, &textureTransform.data[0]); } /////////////////////////////////////////////////////////////////////////////// // 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); } 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; return copy; } void SkiaLinearGradientShader::describe(ProgramDescription& description, const Extensions& extensions) { description.hasGradient = true; description.gradientType = ProgramDescription::kGradientLinear; } void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit) { GLuint textureSlot = (*textureUnit)++; Caches::getInstance().activeTexture(textureSlot); Texture* texture = mGradientCache->get(mColors, mPositions, mCount, mTileX); mat4 screenSpace; computeScreenSpaceMatrix(screenSpace, modelView); // Uniforms bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]); glUniform1i(program->getUniform("gradientSampler"), textureSlot); glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); } void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot) { 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; return copy; } void SkiaCircularGradientShader::describe(ProgramDescription& description, const Extensions& extensions) { description.hasGradient = true; description.gradientType = ProgramDescription::kGradientCircular; } /////////////////////////////////////////////////////////////////////////////// // 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); } 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) { } 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; return copy; } void SkiaSweepGradientShader::describe(ProgramDescription& description, const Extensions& extensions) { description.hasGradient = true; description.gradientType = ProgramDescription::kGradientSweep; } void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit) { GLuint textureSlot = (*textureUnit)++; Caches::getInstance().activeTexture(textureSlot); Texture* texture = mGradientCache->get(mColors, mPositions, mCount); mat4 screenSpace; computeScreenSpaceMatrix(screenSpace, modelView); // Uniforms bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]); glUniform1i(program->getUniform("gradientSampler"), textureSlot); glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); } void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot) { 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::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