OpenGLRenderer.cpp revision 65ef909776c03417d8b597738da54ca211e37e4f
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 <stdlib.h>
20#include <stdint.h>
21#include <sys/types.h>
22
23#include <SkCanvas.h>
24#include <SkTypeface.h>
25
26#include <cutils/properties.h>
27#include <utils/Log.h>
28
29#include "OpenGLRenderer.h"
30#include "Properties.h"
31
32namespace android {
33namespace uirenderer {
34
35///////////////////////////////////////////////////////////////////////////////
36// Defines
37///////////////////////////////////////////////////////////////////////////////
38
39#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
40#define DEFAULT_LAYER_CACHE_SIZE 10.0f
41#define DEFAULT_PATCH_CACHE_SIZE 100
42#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
43
44// Converts a number of mega-bytes into bytes
45#define MB(s) s * 1024 * 1024
46
47// Generates simple and textured vertices
48#define SV(x, y) { { x, y } }
49#define FV(x, y, u, v) { { x, y }, { u, v } }
50
51///////////////////////////////////////////////////////////////////////////////
52// Globals
53///////////////////////////////////////////////////////////////////////////////
54
55static const SimpleVertex gDrawColorVertices[] = {
56        SV(0.0f, 0.0f),
57        SV(1.0f, 0.0f),
58        SV(0.0f, 1.0f),
59        SV(1.0f, 1.0f)
60};
61static const GLsizei gDrawColorVertexStride = sizeof(SimpleVertex);
62static const GLsizei gDrawColorVertexCount = 4;
63
64// This array is never used directly but used as a memcpy source in the
65// OpenGLRenderer constructor
66static const TextureVertex gDrawTextureVertices[] = {
67        FV(0.0f, 0.0f, 0.0f, 0.0f),
68        FV(1.0f, 0.0f, 1.0f, 0.0f),
69        FV(0.0f, 1.0f, 0.0f, 1.0f),
70        FV(1.0f, 1.0f, 1.0f, 1.0f)
71};
72static const GLsizei gDrawTextureVertexStride = sizeof(TextureVertex);
73static const GLsizei gDrawTextureVertexCount = 4;
74
75// In this array, the index of each Blender equals the value of the first
76// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
77static const Blender gBlends[] = {
78        { SkXfermode::kClear_Mode,   GL_ZERO,                 GL_ZERO },
79        { SkXfermode::kSrc_Mode,     GL_ONE,                  GL_ZERO },
80        { SkXfermode::kDst_Mode,     GL_ZERO,                 GL_ONE },
81        { SkXfermode::kSrcOver_Mode, GL_ONE,                  GL_ONE_MINUS_SRC_ALPHA },
82        { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA,  GL_ONE },
83        { SkXfermode::kSrcIn_Mode,   GL_DST_ALPHA,            GL_ZERO },
84        { SkXfermode::kDstIn_Mode,   GL_ZERO,                 GL_SRC_ALPHA },
85        { SkXfermode::kSrcOut_Mode,  GL_ONE_MINUS_DST_ALPHA,  GL_ZERO },
86        { SkXfermode::kDstOut_Mode,  GL_ZERO,                 GL_ONE_MINUS_SRC_ALPHA },
87        { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA,            GL_ONE_MINUS_SRC_ALPHA },
88        { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA,  GL_SRC_ALPHA },
89        { SkXfermode::kXor_Mode,     GL_ONE_MINUS_DST_ALPHA,  GL_ONE_MINUS_SRC_ALPHA }
90};
91
92static const GLint gTileModes[] = {
93        GL_CLAMP_TO_EDGE,   // == SkShader::kClamp_TileMode
94        GL_REPEAT,          // == SkShader::kRepeat_Mode
95        GL_MIRRORED_REPEAT  // == SkShader::kMirror_TileMode
96};
97
98///////////////////////////////////////////////////////////////////////////////
99// Constructors/destructor
100///////////////////////////////////////////////////////////////////////////////
101
102OpenGLRenderer::OpenGLRenderer():
103        mBlend(false), mLastSrcMode(GL_ZERO), mLastDstMode(GL_ZERO),
104        mTextureCache(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
105        mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)),
106        mGradientCache(MB(DEFAULT_GRADIENT_CACHE_SIZE)),
107        mPatchCache(DEFAULT_PATCH_CACHE_SIZE) {
108    LOGD("Create OpenGLRenderer");
109
110    char property[PROPERTY_VALUE_MAX];
111    if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
112        LOGD("  Setting texture cache size to %sMB", property);
113        mTextureCache.setMaxSize(MB(atof(property)));
114    } else {
115        LOGD("  Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
116    }
117
118    if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) {
119        LOGD("  Setting layer cache size to %sMB", property);
120        mLayerCache.setMaxSize(MB(atof(property)));
121    } else {
122        LOGD("  Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE);
123    }
124
125    if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
126        LOGD("  Setting gradient cache size to %sMB", property);
127        mLayerCache.setMaxSize(MB(atof(property)));
128    } else {
129        LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
130    }
131
132    mDrawColorProgram = new DrawColorProgram;
133    mDrawTextureProgram = new DrawTextureProgram;
134    mDrawTextProgram = new DrawTextProgram;
135    mDrawLinearGradientProgram = new DrawLinearGradientProgram;
136    mCurrentProgram = mDrawTextureProgram;
137
138    mShader = kShaderNone;
139    mShaderTileX = GL_CLAMP_TO_EDGE;
140    mShaderTileY = GL_CLAMP_TO_EDGE;
141    mShaderMatrix = NULL;
142    mShaderBitmap = NULL;
143
144    mLastTexture = 0;
145
146    memcpy(mDrawTextureVertices, gDrawTextureVertices, sizeof(gDrawTextureVertices));
147}
148
149OpenGLRenderer::~OpenGLRenderer() {
150    LOGD("Destroy OpenGLRenderer");
151
152    mTextureCache.clear();
153    mLayerCache.clear();
154    mGradientCache.clear();
155    mPatchCache.clear();
156}
157
158///////////////////////////////////////////////////////////////////////////////
159// Setup
160///////////////////////////////////////////////////////////////////////////////
161
162void OpenGLRenderer::setViewport(int width, int height) {
163    glViewport(0, 0, width, height);
164
165    mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
166
167    mWidth = width;
168    mHeight = height;
169    mFirstSnapshot.height = height;
170}
171
172void OpenGLRenderer::prepare() {
173    mSnapshot = &mFirstSnapshot;
174    mSaveCount = 0;
175
176    glDisable(GL_SCISSOR_TEST);
177
178    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
179    glClear(GL_COLOR_BUFFER_BIT);
180
181    glEnable(GL_SCISSOR_TEST);
182    glScissor(0, 0, mWidth, mHeight);
183
184    mSnapshot->clipRect.set(0.0f, 0.0f, mWidth, mHeight);
185}
186
187///////////////////////////////////////////////////////////////////////////////
188// State management
189///////////////////////////////////////////////////////////////////////////////
190
191int OpenGLRenderer::getSaveCount() const {
192    return mSaveCount;
193}
194
195int OpenGLRenderer::save(int flags) {
196    return saveSnapshot();
197}
198
199void OpenGLRenderer::restore() {
200    if (mSaveCount == 0) return;
201
202    if (restoreSnapshot()) {
203        setScissorFromClip();
204    }
205}
206
207void OpenGLRenderer::restoreToCount(int saveCount) {
208    if (saveCount <= 0 || saveCount > mSaveCount) return;
209
210    bool restoreClip = false;
211
212    while (mSaveCount != saveCount - 1) {
213        restoreClip |= restoreSnapshot();
214    }
215
216    if (restoreClip) {
217        setScissorFromClip();
218    }
219}
220
221int OpenGLRenderer::saveSnapshot() {
222    mSnapshot = new Snapshot(mSnapshot);
223    return ++mSaveCount;
224}
225
226bool OpenGLRenderer::restoreSnapshot() {
227    bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet;
228    bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer;
229    bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho;
230
231    sp<Snapshot> current = mSnapshot;
232    sp<Snapshot> previous = mSnapshot->previous;
233
234    if (restoreOrtho) {
235        mOrthoMatrix.load(current->orthoMatrix);
236    }
237
238    if (restoreLayer) {
239        composeLayer(current, previous);
240    }
241
242    mSnapshot = previous;
243    mSaveCount--;
244
245    return restoreClip;
246}
247
248void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
249    if (!current->layer) {
250        LOGE("Attempting to compose a layer that does not exist");
251        return;
252    }
253
254    // Unbind current FBO and restore previous one
255    // Most of the time, previous->fbo will be 0 to bind the default buffer
256    glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
257
258    // Restore the clip from the previous snapshot
259    const Rect& clip = previous->clipRect;
260    glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
261
262    Layer* layer = current->layer;
263
264    // Compute the correct texture coordinates for the FBO texture
265    // The texture is currently as big as the window but drawn with
266    // a quad of the appropriate size
267    const Rect& rect = layer->layer;
268
269    drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
270            layer->texture, layer->alpha, layer->mode, layer->blend);
271
272    LayerSize size(rect.getWidth(), rect.getHeight());
273    // Failing to add the layer to the cache should happen only if the
274    // layer is too large
275    if (!mLayerCache.put(size, layer)) {
276        LAYER_LOGD("Deleting layer");
277
278        glDeleteFramebuffers(1, &layer->fbo);
279        glDeleteTextures(1, &layer->texture);
280
281        delete layer;
282    }
283}
284
285///////////////////////////////////////////////////////////////////////////////
286// Layers
287///////////////////////////////////////////////////////////////////////////////
288
289int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
290        const SkPaint* p, int flags) {
291    int count = saveSnapshot();
292
293    int alpha = 255;
294    SkXfermode::Mode mode;
295
296    if (p) {
297        alpha = p->getAlpha();
298        const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
299        if (!isMode) {
300            // Assume SRC_OVER
301            mode = SkXfermode::kSrcOver_Mode;
302        }
303    } else {
304        mode = SkXfermode::kSrcOver_Mode;
305    }
306
307    createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags);
308
309    return count;
310}
311
312int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
313        int alpha, int flags) {
314    int count = saveSnapshot();
315    createLayer(mSnapshot, left, top, right, bottom, alpha, SkXfermode::kSrcOver_Mode, flags);
316    return count;
317}
318
319bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
320        float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
321
322    LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
323    LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize());
324
325    GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
326    LayerSize size(right - left, bottom - top);
327
328    Layer* layer = mLayerCache.get(size, previousFbo);
329    if (!layer) {
330        return false;
331    }
332
333    glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
334
335    // Clear the FBO
336    glDisable(GL_SCISSOR_TEST);
337    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
338    glClear(GL_COLOR_BUFFER_BIT);
339    glEnable(GL_SCISSOR_TEST);
340
341    // Save the layer in the snapshot
342    snapshot->flags |= Snapshot::kFlagIsLayer;
343    layer->mode = mode;
344    layer->alpha = alpha / 255.0f;
345    layer->layer.set(left, top, right, bottom);
346
347    snapshot->layer = layer;
348    snapshot->fbo = layer->fbo;
349
350    // Creates a new snapshot to draw into the FBO
351    saveSnapshot();
352    // TODO: This doesn't preserve other transformations (check Skia first)
353    mSnapshot->transform.loadTranslate(-left, -top, 0.0f);
354    mSnapshot->setClip(0.0f, 0.0f, right - left, bottom - top);
355    mSnapshot->height = bottom - top;
356    setScissorFromClip();
357
358    mSnapshot->flags = Snapshot::kFlagDirtyOrtho | Snapshot::kFlagClipSet;
359    mSnapshot->orthoMatrix.load(mOrthoMatrix);
360
361    // Change the ortho projection
362    mOrthoMatrix.loadOrtho(0.0f, right - left, bottom - top, 0.0f, 0.0f, 1.0f);
363
364    return true;
365}
366
367///////////////////////////////////////////////////////////////////////////////
368// Transforms
369///////////////////////////////////////////////////////////////////////////////
370
371void OpenGLRenderer::translate(float dx, float dy) {
372    mSnapshot->transform.translate(dx, dy, 0.0f);
373}
374
375void OpenGLRenderer::rotate(float degrees) {
376    mSnapshot->transform.rotate(degrees, 0.0f, 0.0f, 1.0f);
377}
378
379void OpenGLRenderer::scale(float sx, float sy) {
380    mSnapshot->transform.scale(sx, sy, 1.0f);
381}
382
383void OpenGLRenderer::setMatrix(SkMatrix* matrix) {
384    mSnapshot->transform.load(*matrix);
385}
386
387void OpenGLRenderer::getMatrix(SkMatrix* matrix) {
388    mSnapshot->transform.copyTo(*matrix);
389}
390
391void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
392    mat4 m(*matrix);
393    mSnapshot->transform.multiply(m);
394}
395
396///////////////////////////////////////////////////////////////////////////////
397// Clipping
398///////////////////////////////////////////////////////////////////////////////
399
400void OpenGLRenderer::setScissorFromClip() {
401    const Rect& clip = mSnapshot->clipRect;
402    glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight());
403}
404
405const Rect& OpenGLRenderer::getClipBounds() {
406    return mSnapshot->getLocalClip();
407}
408
409bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
410    Rect r(left, top, right, bottom);
411    mSnapshot->transform.mapRect(r);
412    return !mSnapshot->clipRect.intersects(r);
413}
414
415bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
416    bool clipped = mSnapshot->clip(left, top, right, bottom, op);
417    if (clipped) {
418        setScissorFromClip();
419    }
420    return !mSnapshot->clipRect.isEmpty();
421}
422
423///////////////////////////////////////////////////////////////////////////////
424// Drawing
425///////////////////////////////////////////////////////////////////////////////
426
427void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint) {
428    const float right = left + bitmap->width();
429    const float bottom = top + bitmap->height();
430
431    if (quickReject(left, top, right, bottom)) {
432        return;
433    }
434
435    const Texture* texture = mTextureCache.get(bitmap);
436    drawTextureRect(left, top, right, bottom, texture, paint);
437}
438
439void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) {
440    Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
441    const mat4 transform(*matrix);
442    transform.mapRect(r);
443
444    if (quickReject(r.left, r.top, r.right, r.bottom)) {
445        return;
446    }
447
448    const Texture* texture = mTextureCache.get(bitmap);
449    drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint);
450}
451
452void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
453         float srcLeft, float srcTop, float srcRight, float srcBottom,
454         float dstLeft, float dstTop, float dstRight, float dstBottom,
455         const SkPaint* paint) {
456    if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) {
457        return;
458    }
459
460    const Texture* texture = mTextureCache.get(bitmap);
461
462    const float width = texture->width;
463    const float height = texture->height;
464
465    const float u1 = srcLeft / width;
466    const float v1 = srcTop / height;
467    const float u2 = srcRight / width;
468    const float v2 = srcBottom / height;
469
470    resetDrawTextureTexCoords(u1, v1, u2, v2);
471
472    drawTextureRect(dstLeft, dstTop, dstRight, dstBottom, texture, paint);
473
474    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
475}
476
477void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
478        float left, float top, float right, float bottom, const SkPaint* paint) {
479    if (quickReject(left, top, right, bottom)) {
480        return;
481    }
482
483    const Texture* texture = mTextureCache.get(bitmap);
484
485    int alpha;
486    SkXfermode::Mode mode;
487    getAlphaAndMode(paint, &alpha, &mode);
488
489    Patch* mesh = mPatchCache.get(patch);
490    mesh->updateVertices(bitmap, left, top, right, bottom,
491            &patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs);
492
493    // Specify right and bottom as +1.0f from left/top to prevent scaling since the
494    // patch mesh already defines the final size
495    drawTextureMesh(left, top, left + 1.0f, top + 1.0f, texture->id, alpha / 255.0f,
496            mode, texture->blend, &mesh->vertices[0].position[0],
497            &mesh->vertices[0].texture[0], mesh->indices, mesh->indicesCount);
498}
499
500void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
501    const Rect& clip = mSnapshot->clipRect;
502    drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
503}
504
505void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* p) {
506    if (quickReject(left, top, right, bottom)) {
507        return;
508    }
509
510    SkXfermode::Mode mode;
511
512    const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
513    if (!isMode) {
514        // Assume SRC_OVER
515        mode = SkXfermode::kSrcOver_Mode;
516    }
517
518    // Skia draws using the color's alpha channel if < 255
519    // Otherwise, it uses the paint's alpha
520    int color = p->getColor();
521    if (((color >> 24) & 0xff) == 255) {
522        color |= p->getAlpha() << 24;
523    }
524
525    drawColorRect(left, top, right, bottom, color, mode);
526}
527
528void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
529        float x, float y, SkPaint* paint) {
530    if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
531        return;
532    }
533
534    float length;
535    switch (paint->getTextAlign()) {
536        case SkPaint::kCenter_Align:
537            length = paint->measureText(text, bytesCount);
538            x -= length / 2.0f;
539            break;
540        case SkPaint::kRight_Align:
541            length = paint->measureText(text, bytesCount);
542            x -= length;
543            break;
544        default:
545            break;
546    }
547
548    int alpha;
549    SkXfermode::Mode mode;
550    getAlphaAndMode(paint, &alpha, &mode);
551
552    uint32_t color = paint->getColor();
553    const GLfloat a = alpha / 255.0f;
554    const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
555    const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
556    const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
557
558    mModelView.loadIdentity();
559
560    useProgram(mDrawTextProgram);
561    mDrawTextProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
562
563    chooseBlending(true, mode);
564    bindTexture(mFontRenderer.getTexture(), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
565
566    // Always premultiplied
567    glUniform4f(mDrawTextProgram->color, r, g, b, a);
568
569    // TODO: Implement scale properly
570    const Rect& clip = mSnapshot->getLocalClip();
571
572    mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
573    mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
574
575    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
576}
577
578///////////////////////////////////////////////////////////////////////////////
579// Shaders
580///////////////////////////////////////////////////////////////////////////////
581
582void OpenGLRenderer::resetShader() {
583    mShader = OpenGLRenderer::kShaderNone;
584    mShaderKey = NULL;
585    mShaderBlend = false;
586    mShaderTileX = GL_CLAMP_TO_EDGE;
587    mShaderTileY = GL_CLAMP_TO_EDGE;
588}
589
590void OpenGLRenderer::setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX,
591        SkShader::TileMode tileY, SkMatrix* matrix, bool hasAlpha) {
592    mShader = OpenGLRenderer::kShaderBitmap;
593    mShaderBlend = hasAlpha;
594    mShaderBitmap = bitmap;
595    mShaderTileX = gTileModes[tileX];
596    mShaderTileY = gTileModes[tileY];
597    mShaderMatrix = matrix;
598}
599
600void OpenGLRenderer::setupLinearGradientShader(SkShader* shader, float* bounds, uint32_t* colors,
601        float* positions, int count, SkShader::TileMode tileMode, SkMatrix* matrix,
602        bool hasAlpha) {
603    // TODO: We should use a struct to describe each shader
604    mShader = OpenGLRenderer::kShaderLinearGradient;
605    mShaderKey = shader;
606    mShaderBlend = hasAlpha;
607    mShaderTileX = gTileModes[tileMode];
608    mShaderTileY = gTileModes[tileMode];
609    mShaderMatrix = matrix;
610    mShaderBounds = bounds;
611    mShaderColors = colors;
612    mShaderPositions = positions;
613    mShaderCount = count;
614}
615
616///////////////////////////////////////////////////////////////////////////////
617// Drawing implementation
618///////////////////////////////////////////////////////////////////////////////
619
620void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
621        int color, SkXfermode::Mode mode, bool ignoreTransform) {
622    // If a shader is set, preserve only the alpha
623    if (mShader != kShaderNone) {
624        color |= 0x00ffffff;
625    }
626
627    // Render using pre-multiplied alpha
628    const int alpha = (color >> 24) & 0xFF;
629    const GLfloat a = alpha / 255.0f;
630
631    switch (mShader) {
632        case OpenGLRenderer::kShaderBitmap:
633            drawBitmapShader(left, top, right, bottom, a, mode);
634            return;
635        case OpenGLRenderer::kShaderLinearGradient:
636            drawLinearGradientShader(left, top, right, bottom, a, mode);
637            return;
638        default:
639            break;
640    }
641
642    const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
643    const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
644    const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
645
646    // Pre-multiplication happens when setting the shader color
647    chooseBlending(alpha < 255 || mShaderBlend, mode);
648
649    mModelView.loadTranslate(left, top, 0.0f);
650    mModelView.scale(right - left, bottom - top, 1.0f);
651
652    if (!useProgram(mDrawColorProgram)) {
653        const GLvoid* p = &gDrawColorVertices[0].position[0];
654        glVertexAttribPointer(mDrawColorProgram->position, 2, GL_FLOAT, GL_FALSE,
655                gDrawColorVertexStride, p);
656    }
657
658    if (!ignoreTransform) {
659        mDrawColorProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
660    } else {
661        mat4 identity;
662        mDrawColorProgram->set(mOrthoMatrix, mModelView, identity);
663    }
664
665    glUniform4f(mDrawColorProgram->color, r, g, b, a);
666
667    glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
668}
669
670void OpenGLRenderer::drawLinearGradientShader(float left, float top, float right, float bottom,
671        float alpha, SkXfermode::Mode mode) {
672    Texture* texture = mGradientCache.get(mShaderKey);
673    if (!texture) {
674        SkShader::TileMode tileMode = SkShader::kClamp_TileMode;
675        switch (mShaderTileX) {
676            case GL_REPEAT:
677                tileMode = SkShader::kRepeat_TileMode;
678                break;
679            case GL_MIRRORED_REPEAT:
680                tileMode = SkShader::kMirror_TileMode;
681                break;
682        }
683
684        texture = mGradientCache.addLinearGradient(mShaderKey, mShaderBounds, mShaderColors,
685                mShaderPositions, mShaderCount, tileMode);
686    }
687
688    mModelView.loadTranslate(left, top, 0.0f);
689    mModelView.scale(right - left, bottom - top, 1.0f);
690
691    useProgram(mDrawLinearGradientProgram);
692    mDrawLinearGradientProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
693
694    chooseBlending(mShaderBlend || alpha < 1.0f, mode);
695    bindTexture(texture->id, mShaderTileX, mShaderTileY);
696
697    Rect start(mShaderBounds[0], mShaderBounds[1], mShaderBounds[2], mShaderBounds[3]);
698    if (mShaderMatrix) {
699        mat4 shaderMatrix(*mShaderMatrix);
700        shaderMatrix.mapRect(start);
701    }
702    mSnapshot->transform.mapRect(start);
703
704    const float gradientX = start.right - start.left;
705    const float gradientY = start.bottom - start.top;
706
707    mat4 screenSpace(mSnapshot->transform);
708    screenSpace.multiply(mModelView);
709
710    // Always premultiplied
711    glUniform4f(mDrawLinearGradientProgram->color, alpha, alpha, alpha, alpha);
712    glUniform2f(mDrawLinearGradientProgram->start, start.left, start.top);
713    glUniform2f(mDrawLinearGradientProgram->gradient, gradientX, gradientY);
714    glUniform1f(mDrawLinearGradientProgram->gradientLength,
715            1.0f / (gradientX * gradientX + gradientY * gradientY));
716    glUniformMatrix4fv(mDrawLinearGradientProgram->screenSpace, 1, GL_FALSE,
717            &screenSpace.data[0]);
718
719    glVertexAttribPointer(mDrawLinearGradientProgram->position, 2, GL_FLOAT, GL_FALSE,
720            gDrawTextureVertexStride, &mDrawTextureVertices[0].position[0]);
721
722    glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
723}
724
725void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float bottom,
726        float alpha, SkXfermode::Mode mode) {
727    const Texture* texture = mTextureCache.get(mShaderBitmap);
728
729    const float width = texture->width;
730    const float height = texture->height;
731
732    // This could be done in the vertex shader but we have only 4 vertices
733    float u1 = 0.0f;
734    float v1 = 0.0f;
735    float u2 = right - left;
736    float v2 = bottom - top;
737
738    // TODO: If the texture is not pow, use a shader to support repeat/mirror
739    if (mShaderMatrix) {
740        SkMatrix inverse;
741        mShaderMatrix->invert(&inverse);
742        mat4 m(inverse);
743        Rect r(u1, v1, u2, v2);
744        m.mapRect(r);
745
746        u1 = r.left;
747        u2 = r.right;
748        v1 = r.top;
749        v2 = r.bottom;
750    }
751
752    u1 /= width;
753    u2 /= width;
754    v1 /= height;
755    v2 /= height;
756
757    resetDrawTextureTexCoords(u1, v1, u2, v2);
758
759    drawTextureMesh(left, top, right, bottom, texture->id, alpha, mode, texture->blend,
760            &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
761
762    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
763}
764
765void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
766        const Texture* texture, const SkPaint* paint) {
767    int alpha;
768    SkXfermode::Mode mode;
769    getAlphaAndMode(paint, &alpha, &mode);
770
771    drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, texture->blend,
772            &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
773}
774
775void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
776        GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) {
777    drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend,
778            &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
779}
780
781void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
782        GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
783        GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount) {
784    mModelView.loadTranslate(left, top, 0.0f);
785    mModelView.scale(right - left, bottom - top, 1.0f);
786
787    useProgram(mDrawTextureProgram);
788    mDrawTextureProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
789
790    chooseBlending(blend || alpha < 1.0f, mode);
791    bindTexture(texture, mShaderTileX, mShaderTileY);
792
793    // Always premultiplied
794    glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
795
796    glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE,
797            gDrawTextureVertexStride, vertices);
798    glVertexAttribPointer(mDrawTextureProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
799            gDrawTextureVertexStride, texCoords);
800
801    if (!indices) {
802        glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
803    } else {
804        glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_SHORT, indices);
805    }
806}
807
808void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied) {
809    // In theory we should not blend if the mode is Src, but it's rare enough
810    // that it's not worth it
811    blend = blend || mode != SkXfermode::kSrcOver_Mode;
812    if (blend) {
813        if (!mBlend) {
814            glEnable(GL_BLEND);
815        }
816
817        GLenum sourceMode = gBlends[mode].src;
818        GLenum destMode = gBlends[mode].dst;
819        if (!isPremultiplied && sourceMode == GL_ONE) {
820            sourceMode = GL_SRC_ALPHA;
821        }
822
823        if (sourceMode != mLastSrcMode || destMode != mLastDstMode) {
824            glBlendFunc(sourceMode, destMode);
825            mLastSrcMode = sourceMode;
826            mLastDstMode = destMode;
827        }
828    } else if (mBlend) {
829        glDisable(GL_BLEND);
830    }
831    mBlend = blend;
832}
833
834bool OpenGLRenderer::useProgram(const sp<Program>& program) {
835    if (!program->isInUse()) {
836        mCurrentProgram->remove();
837        program->use();
838        mCurrentProgram = program;
839        return false;
840    }
841    return true;
842}
843
844void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
845    TextureVertex* v = &mDrawTextureVertices[0];
846    TextureVertex::setUV(v++, u1, v1);
847    TextureVertex::setUV(v++, u2, v1);
848    TextureVertex::setUV(v++, u1, v2);
849    TextureVertex::setUV(v++, u2, v2);
850}
851
852void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
853    if (paint) {
854        const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode);
855        if (!isMode) {
856            // Assume SRC_OVER
857            *mode = SkXfermode::kSrcOver_Mode;
858        }
859
860        // Skia draws using the color's alpha channel if < 255
861        // Otherwise, it uses the paint's alpha
862        int color = paint->getColor();
863        *alpha = (color >> 24) & 0xFF;
864        if (*alpha == 255) {
865            *alpha = paint->getAlpha();
866        }
867    } else {
868        *mode = SkXfermode::kSrcOver_Mode;
869        *alpha = 255;
870    }
871}
872
873void OpenGLRenderer::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT) {
874    if (texture != mLastTexture) {
875        glBindTexture(GL_TEXTURE_2D, texture);
876        mLastTexture = texture;
877    }
878    // TODO: Don't set the texture parameters every time
879    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
880    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
881}
882
883}; // namespace uirenderer
884}; // namespace android
885