SkiaShader.cpp revision d21b6e1fe337b35f62cf2028e9bd0637fd009a75
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
19#include <utils/Log.h>
20
21#include <SkMatrix.h>
22
23#include "SkiaShader.h"
24#include "Texture.h"
25#include "Matrix.h"
26
27namespace android {
28namespace uirenderer {
29
30///////////////////////////////////////////////////////////////////////////////
31// Support
32///////////////////////////////////////////////////////////////////////////////
33
34static const GLenum gTextureUnitsMap[] = {
35        GL_TEXTURE0,
36        GL_TEXTURE1,
37        GL_TEXTURE2
38};
39
40static const GLint gTileModes[] = {
41        GL_CLAMP_TO_EDGE,   // == SkShader::kClamp_TileMode
42        GL_REPEAT,          // == SkShader::kRepeat_Mode
43        GL_MIRRORED_REPEAT  // == SkShader::kMirror_TileMode
44};
45
46///////////////////////////////////////////////////////////////////////////////
47// Base shader
48///////////////////////////////////////////////////////////////////////////////
49
50void SkiaShader::copyFrom(const SkiaShader& shader) {
51    mType = shader.mType;
52    mKey = shader.mKey;
53    mTileX = shader.mTileX;
54    mTileY = shader.mTileY;
55    mBlend = shader.mBlend;
56    mUnitMatrix = shader.mUnitMatrix;
57    mShaderMatrix = shader.mShaderMatrix;
58    mGenerationId = shader.mGenerationId;
59}
60
61SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
62        SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
63        mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend) {
64    setMatrix(matrix);
65    mGenerationId = 0;
66}
67
68SkiaShader::~SkiaShader() {
69}
70
71void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) {
72}
73
74void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
75        GLuint* textureUnit) {
76}
77
78void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) {
79    glBindTexture(GL_TEXTURE_2D, texture->id);
80    texture->setWrapST(wrapS, wrapT);
81}
82
83void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) {
84    screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix);
85    screenSpace.multiply(modelView);
86}
87
88///////////////////////////////////////////////////////////////////////////////
89// Bitmap shader
90///////////////////////////////////////////////////////////////////////////////
91
92SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
93        SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
94        SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
95    updateLocalMatrix(matrix);
96}
97
98SkiaShader* SkiaBitmapShader::copy() {
99    SkiaBitmapShader* copy = new SkiaBitmapShader();
100    copy->copyFrom(*this);
101    copy->mBitmap = mBitmap;
102    return copy;
103}
104
105void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
106    Texture* texture = mTextureCache->get(mBitmap);
107    if (!texture) return;
108    mTexture = texture;
109
110    const float width = texture->width;
111    const float height = texture->height;
112
113    description.hasBitmap = true;
114    // The driver does not support non-power of two mirrored/repeated
115    // textures, so do it ourselves
116    if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
117            (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
118        description.isBitmapNpot = true;
119        description.bitmapWrapS = gTileModes[mTileX];
120        description.bitmapWrapT = gTileModes[mTileY];
121        mWrapS = GL_CLAMP_TO_EDGE;
122        mWrapT = GL_CLAMP_TO_EDGE;
123    } else {
124        mWrapS = gTileModes[mTileX];
125        mWrapT = gTileModes[mTileY];
126    }
127}
128
129void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
130        const Snapshot& snapshot, GLuint* textureUnit) {
131    GLuint textureSlot = (*textureUnit)++;
132    glActiveTexture(gTextureUnitsMap[textureSlot]);
133
134    Texture* texture = mTexture;
135    mTexture = NULL;
136    if (!texture) return;
137    const AutoTexture autoCleanup(texture);
138
139    const float width = texture->width;
140    const float height = texture->height;
141
142    mat4 textureTransform;
143    computeScreenSpaceMatrix(textureTransform, modelView);
144
145    // Uniforms
146    bindTexture(texture, mWrapS, mWrapT);
147    // Assume linear here; we should really check the transform in
148    // ::updateTransforms() but we don't have the texture object
149    // available at that point. The optimization is not worth the
150    // effort for now.
151    texture->setFilter(GL_LINEAR);
152
153    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
154    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
155            GL_FALSE, &textureTransform.data[0]);
156    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
157}
158
159void SkiaBitmapShader::updateTransforms(Program* program, const mat4& modelView,
160        const Snapshot& snapshot) {
161    mat4 textureTransform;
162    computeScreenSpaceMatrix(textureTransform, modelView);
163    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
164            GL_FALSE, &textureTransform.data[0]);
165}
166
167///////////////////////////////////////////////////////////////////////////////
168// Linear gradient shader
169///////////////////////////////////////////////////////////////////////////////
170
171static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
172    SkVector vec = pts[1] - pts[0];
173    const float mag = vec.length();
174    const float inv = mag ? 1.0f / mag : 0;
175
176    vec.scale(inv);
177    matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
178    matrix->postTranslate(-pts[0].fX, -pts[0].fY);
179    matrix->postScale(inv, inv);
180}
181
182SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
183        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
184        SkMatrix* matrix, bool blend):
185        SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
186        mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
187    SkPoint points[2];
188    points[0].set(bounds[0], bounds[1]);
189    points[1].set(bounds[2], bounds[3]);
190
191    SkMatrix unitMatrix;
192    toUnitMatrix(points, &unitMatrix);
193    mUnitMatrix.load(unitMatrix);
194
195    updateLocalMatrix(matrix);
196}
197
198SkiaLinearGradientShader::~SkiaLinearGradientShader() {
199    delete[] mBounds;
200    delete[] mColors;
201    delete[] mPositions;
202}
203
204SkiaShader* SkiaLinearGradientShader::copy() {
205    SkiaLinearGradientShader* copy = new SkiaLinearGradientShader();
206    copy->copyFrom(*this);
207    copy->mBounds = new float[4];
208    memcpy(copy->mBounds, mBounds, sizeof(float) * 4);
209    copy->mColors = new uint32_t[mCount];
210    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
211    copy->mPositions = new float[mCount];
212    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
213    copy->mCount = mCount;
214    return copy;
215}
216
217void SkiaLinearGradientShader::describe(ProgramDescription& description,
218        const Extensions& extensions) {
219    description.hasGradient = true;
220    description.gradientType = ProgramDescription::kGradientLinear;
221}
222
223void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
224        const Snapshot& snapshot, GLuint* textureUnit) {
225    GLuint textureSlot = (*textureUnit)++;
226    glActiveTexture(gTextureUnitsMap[textureSlot]);
227
228    Texture* texture = mGradientCache->get(mColors, mPositions, mCount, mTileX);
229
230    mat4 screenSpace;
231    computeScreenSpaceMatrix(screenSpace, modelView);
232
233    // Uniforms
234    bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
235    glUniform1i(program->getUniform("gradientSampler"), textureSlot);
236    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
237}
238
239void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& modelView,
240        const Snapshot& snapshot) {
241    mat4 screenSpace;
242    computeScreenSpaceMatrix(screenSpace, modelView);
243    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
244}
245
246///////////////////////////////////////////////////////////////////////////////
247// Circular gradient shader
248///////////////////////////////////////////////////////////////////////////////
249
250static void toCircularUnitMatrix(const float x, const float y, const float radius,
251        SkMatrix* matrix) {
252    const float inv = 1.0f / radius;
253    matrix->setTranslate(-x, -y);
254    matrix->postScale(inv, inv);
255}
256
257SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
258        uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
259        SkMatrix* matrix, bool blend):
260        SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key,
261                tileMode, matrix, blend) {
262    SkMatrix unitMatrix;
263    toCircularUnitMatrix(x, y, radius, &unitMatrix);
264    mUnitMatrix.load(unitMatrix);
265
266    updateLocalMatrix(matrix);
267}
268
269SkiaShader* SkiaCircularGradientShader::copy() {
270    SkiaCircularGradientShader* copy = new SkiaCircularGradientShader();
271    copy->copyFrom(*this);
272    copy->mColors = new uint32_t[mCount];
273    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
274    copy->mPositions = new float[mCount];
275    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
276    copy->mCount = mCount;
277    return copy;
278}
279
280void SkiaCircularGradientShader::describe(ProgramDescription& description,
281        const Extensions& extensions) {
282    description.hasGradient = true;
283    description.gradientType = ProgramDescription::kGradientCircular;
284}
285
286///////////////////////////////////////////////////////////////////////////////
287// Sweep gradient shader
288///////////////////////////////////////////////////////////////////////////////
289
290static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
291    matrix->setTranslate(-x, -y);
292}
293
294SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
295        float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
296        SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
297                SkShader::kClamp_TileMode, matrix, blend),
298        mColors(colors), mPositions(positions), mCount(count) {
299    SkMatrix unitMatrix;
300    toSweepUnitMatrix(x, y, &unitMatrix);
301    mUnitMatrix.load(unitMatrix);
302
303    updateLocalMatrix(matrix);
304}
305
306SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors,
307        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
308        SkMatrix* matrix, bool blend):
309        SkiaShader(type, key, tileMode, tileMode, matrix, blend),
310        mColors(colors), mPositions(positions), mCount(count) {
311}
312
313SkiaSweepGradientShader::~SkiaSweepGradientShader() {
314    delete[] mColors;
315    delete[] mPositions;
316}
317
318SkiaShader* SkiaSweepGradientShader::copy() {
319    SkiaSweepGradientShader* copy = new SkiaSweepGradientShader();
320    copy->copyFrom(*this);
321    copy->mColors = new uint32_t[mCount];
322    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
323    copy->mPositions = new float[mCount];
324    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
325    copy->mCount = mCount;
326    return copy;
327}
328
329void SkiaSweepGradientShader::describe(ProgramDescription& description,
330        const Extensions& extensions) {
331    description.hasGradient = true;
332    description.gradientType = ProgramDescription::kGradientSweep;
333}
334
335void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
336        const Snapshot& snapshot, GLuint* textureUnit) {
337    GLuint textureSlot = (*textureUnit)++;
338    glActiveTexture(gTextureUnitsMap[textureSlot]);
339
340    Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
341
342    mat4 screenSpace;
343    computeScreenSpaceMatrix(screenSpace, modelView);
344
345    // Uniforms
346    bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
347    glUniform1i(program->getUniform("gradientSampler"), textureSlot);
348    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
349}
350
351void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& modelView,
352        const Snapshot& snapshot) {
353    mat4 screenSpace;
354    computeScreenSpaceMatrix(screenSpace, modelView);
355    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
356}
357
358///////////////////////////////////////////////////////////////////////////////
359// Compose shader
360///////////////////////////////////////////////////////////////////////////////
361
362SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
363        SkXfermode::Mode mode, SkShader* key):
364        SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
365        NULL, first->blend() || second->blend()),
366        mFirst(first), mSecond(second), mMode(mode), mCleanup(false) {
367}
368
369SkiaComposeShader::~SkiaComposeShader() {
370    if (mCleanup) {
371        delete mFirst;
372        delete mSecond;
373    }
374}
375
376SkiaShader* SkiaComposeShader::copy() {
377    SkiaComposeShader* copy = new SkiaComposeShader();
378    copy->copyFrom(*this);
379    copy->mFirst = mFirst->copy();
380    copy->mSecond = mSecond->copy();
381    copy->mMode = mMode;
382    copy->cleanup();
383    return copy;
384}
385
386void SkiaComposeShader::set(TextureCache* textureCache, GradientCache* gradientCache) {
387    SkiaShader::set(textureCache, gradientCache);
388    mFirst->set(textureCache, gradientCache);
389    mSecond->set(textureCache, gradientCache);
390}
391
392void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
393    mFirst->describe(description, extensions);
394    mSecond->describe(description, extensions);
395    if (mFirst->type() == kBitmap) {
396        description.isBitmapFirst = true;
397    }
398    description.shadersMode = mMode;
399}
400
401void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
402        const Snapshot& snapshot, GLuint* textureUnit) {
403    mFirst->setupProgram(program, modelView, snapshot, textureUnit);
404    mSecond->setupProgram(program, modelView, snapshot, textureUnit);
405}
406
407}; // namespace uirenderer
408}; // namespace android
409