OpenGLRenderer.cpp revision d84aaa23a53b57834b0722ad36c460833dc4823c
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 <SkPathMeasure.h>
25#include <SkTypeface.h>
26
27#include <utils/Log.h>
28#include <utils/StopWatch.h>
29
30#include <private/hwui/DrawGlInfo.h>
31
32#include <ui/Rect.h>
33
34#include "OpenGLRenderer.h"
35#include "DisplayListRenderer.h"
36#include "Vector.h"
37
38namespace android {
39namespace uirenderer {
40
41///////////////////////////////////////////////////////////////////////////////
42// Defines
43///////////////////////////////////////////////////////////////////////////////
44
45#define RAD_TO_DEG (180.0f / 3.14159265f)
46#define MIN_ANGLE 0.001f
47
48#define ALPHA_THRESHOLD 0
49
50#define FILTER(paint) (paint && paint->isFilterBitmap() ? GL_LINEAR : GL_NEAREST)
51
52///////////////////////////////////////////////////////////////////////////////
53// Globals
54///////////////////////////////////////////////////////////////////////////////
55
56/**
57 * Structure mapping Skia xfermodes to OpenGL blending factors.
58 */
59struct Blender {
60    SkXfermode::Mode mode;
61    GLenum src;
62    GLenum dst;
63}; // struct Blender
64
65// In this array, the index of each Blender equals the value of the first
66// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
67static const Blender gBlends[] = {
68    { SkXfermode::kClear_Mode,    GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
69    { SkXfermode::kSrc_Mode,      GL_ONE,                 GL_ZERO },
70    { SkXfermode::kDst_Mode,      GL_ZERO,                GL_ONE },
71    { SkXfermode::kSrcOver_Mode,  GL_ONE,                 GL_ONE_MINUS_SRC_ALPHA },
72    { SkXfermode::kDstOver_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_ONE },
73    { SkXfermode::kSrcIn_Mode,    GL_DST_ALPHA,           GL_ZERO },
74    { SkXfermode::kDstIn_Mode,    GL_ZERO,                GL_SRC_ALPHA },
75    { SkXfermode::kSrcOut_Mode,   GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
76    { SkXfermode::kDstOut_Mode,   GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
77    { SkXfermode::kSrcATop_Mode,  GL_DST_ALPHA,           GL_ONE_MINUS_SRC_ALPHA },
78    { SkXfermode::kDstATop_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
79    { SkXfermode::kXor_Mode,      GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
80    { SkXfermode::kPlus_Mode,     GL_ONE,                 GL_ONE },
81    { SkXfermode::kMultiply_Mode, GL_ZERO,                GL_SRC_COLOR },
82    { SkXfermode::kScreen_Mode,   GL_ONE,                 GL_ONE_MINUS_SRC_COLOR }
83};
84
85// This array contains the swapped version of each SkXfermode. For instance
86// this array's SrcOver blending mode is actually DstOver. You can refer to
87// createLayer() for more information on the purpose of this array.
88static const Blender gBlendsSwap[] = {
89    { SkXfermode::kClear_Mode,    GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
90    { SkXfermode::kSrc_Mode,      GL_ZERO,                GL_ONE },
91    { SkXfermode::kDst_Mode,      GL_ONE,                 GL_ZERO },
92    { SkXfermode::kSrcOver_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_ONE },
93    { SkXfermode::kDstOver_Mode,  GL_ONE,                 GL_ONE_MINUS_SRC_ALPHA },
94    { SkXfermode::kSrcIn_Mode,    GL_ZERO,                GL_SRC_ALPHA },
95    { SkXfermode::kDstIn_Mode,    GL_DST_ALPHA,           GL_ZERO },
96    { SkXfermode::kSrcOut_Mode,   GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
97    { SkXfermode::kDstOut_Mode,   GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
98    { SkXfermode::kSrcATop_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
99    { SkXfermode::kDstATop_Mode,  GL_DST_ALPHA,           GL_ONE_MINUS_SRC_ALPHA },
100    { SkXfermode::kXor_Mode,      GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
101    { SkXfermode::kPlus_Mode,     GL_ONE,                 GL_ONE },
102    { SkXfermode::kMultiply_Mode, GL_DST_COLOR,           GL_ZERO },
103    { SkXfermode::kScreen_Mode,   GL_ONE_MINUS_DST_COLOR, GL_ONE }
104};
105
106///////////////////////////////////////////////////////////////////////////////
107// Constructors/destructor
108///////////////////////////////////////////////////////////////////////////////
109
110OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) {
111    mShader = NULL;
112    mColorFilter = NULL;
113    mHasShadow = false;
114    mHasDrawFilter = false;
115
116    memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
117
118    mFirstSnapshot = new Snapshot;
119}
120
121OpenGLRenderer::~OpenGLRenderer() {
122    // The context has already been destroyed at this point, do not call
123    // GL APIs. All GL state should be kept in Caches.h
124}
125
126///////////////////////////////////////////////////////////////////////////////
127// Debug
128///////////////////////////////////////////////////////////////////////////////
129
130void OpenGLRenderer::startMark(const char* name) const {
131    mCaches.startMark(0, name);
132}
133
134void OpenGLRenderer::endMark() const {
135    mCaches.endMark();
136}
137
138///////////////////////////////////////////////////////////////////////////////
139// Setup
140///////////////////////////////////////////////////////////////////////////////
141
142bool OpenGLRenderer::isDeferred() {
143    return false;
144}
145
146void OpenGLRenderer::setViewport(int width, int height) {
147    mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
148
149    mWidth = width;
150    mHeight = height;
151
152    mFirstSnapshot->height = height;
153    mFirstSnapshot->viewport.set(0, 0, width, height);
154
155    glDisable(GL_DITHER);
156    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
157
158    glEnableVertexAttribArray(Program::kBindingPosition);
159}
160
161int OpenGLRenderer::prepare(bool opaque) {
162    return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
163}
164
165int OpenGLRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) {
166    mCaches.clearGarbage();
167
168    mSnapshot = new Snapshot(mFirstSnapshot,
169            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
170    mSnapshot->fbo = getTargetFbo();
171    mSaveCount = 1;
172
173    mSnapshot->setClip(left, top, right, bottom);
174    mDirtyClip = opaque;
175
176    syncState();
177
178    if (!opaque) {
179        mCaches.enableScissor();
180        mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
181        glClear(GL_COLOR_BUFFER_BIT);
182        return DrawGlInfo::kStatusDrew;
183    } else {
184        mCaches.resetScissor();
185    }
186
187    return DrawGlInfo::kStatusDone;
188}
189
190void OpenGLRenderer::syncState() {
191    glViewport(0, 0, mWidth, mHeight);
192
193    if (mCaches.blend) {
194        glEnable(GL_BLEND);
195    } else {
196        glDisable(GL_BLEND);
197    }
198}
199
200void OpenGLRenderer::finish() {
201#if DEBUG_OPENGL
202    GLenum status = GL_NO_ERROR;
203    while ((status = glGetError()) != GL_NO_ERROR) {
204        ALOGD("GL error from OpenGLRenderer: 0x%x", status);
205        switch (status) {
206            case GL_INVALID_ENUM:
207                ALOGE("  GL_INVALID_ENUM");
208                break;
209            case GL_INVALID_VALUE:
210                ALOGE("  GL_INVALID_VALUE");
211                break;
212            case GL_INVALID_OPERATION:
213                ALOGE("  GL_INVALID_OPERATION");
214                break;
215            case GL_OUT_OF_MEMORY:
216                ALOGE("  Out of memory!");
217                break;
218        }
219    }
220#endif
221#if DEBUG_MEMORY_USAGE
222    mCaches.dumpMemoryUsage();
223#else
224    if (mCaches.getDebugLevel() & kDebugMemory) {
225        mCaches.dumpMemoryUsage();
226    }
227#endif
228}
229
230void OpenGLRenderer::interrupt() {
231    if (mCaches.currentProgram) {
232        if (mCaches.currentProgram->isInUse()) {
233            mCaches.currentProgram->remove();
234            mCaches.currentProgram = NULL;
235        }
236    }
237    mCaches.unbindMeshBuffer();
238    mCaches.unbindIndicesBuffer();
239    mCaches.resetVertexPointers();
240    mCaches.disbaleTexCoordsVertexArray();
241}
242
243void OpenGLRenderer::resume() {
244    sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
245
246    glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
247    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
248
249    mCaches.scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
250    mCaches.enableScissor();
251    mCaches.resetScissor();
252    dirtyClip();
253
254    mCaches.activeTexture(0);
255    glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
256
257    mCaches.blend = true;
258    glEnable(GL_BLEND);
259    glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
260    glBlendEquation(GL_FUNC_ADD);
261}
262
263void OpenGLRenderer::detachFunctor(Functor* functor) {
264    mFunctors.remove(functor);
265}
266
267void OpenGLRenderer::attachFunctor(Functor* functor) {
268    mFunctors.add(functor);
269}
270
271status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
272    status_t result = DrawGlInfo::kStatusDone;
273    size_t count = mFunctors.size();
274
275    if (count > 0) {
276        SortedVector<Functor*> functors(mFunctors);
277        mFunctors.clear();
278
279        DrawGlInfo info;
280        info.clipLeft = 0;
281        info.clipTop = 0;
282        info.clipRight = 0;
283        info.clipBottom = 0;
284        info.isLayer = false;
285        info.width = 0;
286        info.height = 0;
287        memset(info.transform, 0, sizeof(float) * 16);
288
289        for (size_t i = 0; i < count; i++) {
290            Functor* f = functors.itemAt(i);
291            result |= (*f)(DrawGlInfo::kModeProcess, &info);
292
293            if (result & DrawGlInfo::kStatusDraw) {
294                Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
295                dirty.unionWith(localDirty);
296            }
297
298            if (result & DrawGlInfo::kStatusInvoke) {
299                mFunctors.add(f);
300            }
301        }
302    }
303
304    mCaches.activeTexture(0);
305
306    return result;
307}
308
309status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
310    interrupt();
311    detachFunctor(functor);
312
313    mCaches.enableScissor();
314    if (mDirtyClip) {
315        setScissorFromClip();
316    }
317
318    Rect clip(*mSnapshot->clipRect);
319    clip.snapToPixelBoundaries();
320
321    // Since we don't know what the functor will draw, let's dirty
322    // tne entire clip region
323    if (hasLayer()) {
324        dirtyLayerUnchecked(clip, getRegion());
325    }
326
327    DrawGlInfo info;
328    info.clipLeft = clip.left;
329    info.clipTop = clip.top;
330    info.clipRight = clip.right;
331    info.clipBottom = clip.bottom;
332    info.isLayer = hasLayer();
333    info.width = getSnapshot()->viewport.getWidth();
334    info.height = getSnapshot()->height;
335    getSnapshot()->transform->copyTo(&info.transform[0]);
336
337    status_t result = (*functor)(DrawGlInfo::kModeDraw, &info) | DrawGlInfo::kStatusDrew;
338
339    if (result != DrawGlInfo::kStatusDone) {
340        Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
341        dirty.unionWith(localDirty);
342
343        if (result & DrawGlInfo::kStatusInvoke) {
344            mFunctors.add(functor);
345        }
346    }
347
348    resume();
349    return result;
350}
351
352///////////////////////////////////////////////////////////////////////////////
353// State management
354///////////////////////////////////////////////////////////////////////////////
355
356int OpenGLRenderer::getSaveCount() const {
357    return mSaveCount;
358}
359
360int OpenGLRenderer::save(int flags) {
361    return saveSnapshot(flags);
362}
363
364void OpenGLRenderer::restore() {
365    if (mSaveCount > 1) {
366        restoreSnapshot();
367    }
368}
369
370void OpenGLRenderer::restoreToCount(int saveCount) {
371    if (saveCount < 1) saveCount = 1;
372
373    while (mSaveCount > saveCount) {
374        restoreSnapshot();
375    }
376}
377
378int OpenGLRenderer::saveSnapshot(int flags) {
379    mSnapshot = new Snapshot(mSnapshot, flags);
380    return mSaveCount++;
381}
382
383bool OpenGLRenderer::restoreSnapshot() {
384    bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet;
385    bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer;
386    bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho;
387
388    sp<Snapshot> current = mSnapshot;
389    sp<Snapshot> previous = mSnapshot->previous;
390
391    if (restoreOrtho) {
392        Rect& r = previous->viewport;
393        glViewport(r.left, r.top, r.right, r.bottom);
394        mOrthoMatrix.load(current->orthoMatrix);
395    }
396
397    mSaveCount--;
398    mSnapshot = previous;
399
400    if (restoreClip) {
401        dirtyClip();
402    }
403
404    if (restoreLayer) {
405        composeLayer(current, previous);
406    }
407
408    return restoreClip;
409}
410
411///////////////////////////////////////////////////////////////////////////////
412// Layers
413///////////////////////////////////////////////////////////////////////////////
414
415int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
416        SkPaint* p, int flags) {
417    const GLuint previousFbo = mSnapshot->fbo;
418    const int count = saveSnapshot(flags);
419
420    if (!mSnapshot->isIgnored()) {
421        int alpha = 255;
422        SkXfermode::Mode mode;
423
424        if (p) {
425            alpha = p->getAlpha();
426            if (!mCaches.extensions.hasFramebufferFetch()) {
427                const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
428                if (!isMode) {
429                    // Assume SRC_OVER
430                    mode = SkXfermode::kSrcOver_Mode;
431                }
432            } else {
433                mode = getXfermode(p->getXfermode());
434            }
435        } else {
436            mode = SkXfermode::kSrcOver_Mode;
437        }
438
439        createLayer(left, top, right, bottom, alpha, mode, flags, previousFbo);
440    }
441
442    return count;
443}
444
445int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
446        int alpha, int flags) {
447    if (alpha >= 255) {
448        return saveLayer(left, top, right, bottom, NULL, flags);
449    } else {
450        SkPaint paint;
451        paint.setAlpha(alpha);
452        return saveLayer(left, top, right, bottom, &paint, flags);
453    }
454}
455
456/**
457 * Layers are viewed by Skia are slightly different than layers in image editing
458 * programs (for instance.) When a layer is created, previously created layers
459 * and the frame buffer still receive every drawing command. For instance, if a
460 * layer is created and a shape intersecting the bounds of the layers and the
461 * framebuffer is draw, the shape will be drawn on both (unless the layer was
462 * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
463 *
464 * A way to implement layers is to create an FBO for each layer, backed by an RGBA
465 * texture. Unfortunately, this is inefficient as it requires every primitive to
466 * be drawn n + 1 times, where n is the number of active layers. In practice this
467 * means, for every primitive:
468 *   - Switch active frame buffer
469 *   - Change viewport, clip and projection matrix
470 *   - Issue the drawing
471 *
472 * Switching rendering target n + 1 times per drawn primitive is extremely costly.
473 * To avoid this, layers are implemented in a different way here, at least in the
474 * general case. FBOs are used, as an optimization, when the "clip to layer" flag
475 * is set. When this flag is set we can redirect all drawing operations into a
476 * single FBO.
477 *
478 * This implementation relies on the frame buffer being at least RGBA 8888. When
479 * a layer is created, only a texture is created, not an FBO. The content of the
480 * frame buffer contained within the layer's bounds is copied into this texture
481 * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
482 * buffer and drawing continues as normal. This technique therefore treats the
483 * frame buffer as a scratch buffer for the layers.
484 *
485 * To compose the layers back onto the frame buffer, each layer texture
486 * (containing the original frame buffer data) is drawn as a simple quad over
487 * the frame buffer. The trick is that the quad is set as the composition
488 * destination in the blending equation, and the frame buffer becomes the source
489 * of the composition.
490 *
491 * Drawing layers with an alpha value requires an extra step before composition.
492 * An empty quad is drawn over the layer's region in the frame buffer. This quad
493 * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the
494 * quad is used to multiply the colors in the frame buffer. This is achieved by
495 * changing the GL blend functions for the GL_FUNC_ADD blend equation to
496 * GL_ZERO, GL_SRC_ALPHA.
497 *
498 * Because glCopyTexImage2D() can be slow, an alternative implementation might
499 * be use to draw a single clipped layer. The implementation described above
500 * is correct in every case.
501 *
502 * (1) The frame buffer is actually not cleared right away. To allow the GPU
503 *     to potentially optimize series of calls to glCopyTexImage2D, the frame
504 *     buffer is left untouched until the first drawing operation. Only when
505 *     something actually gets drawn are the layers regions cleared.
506 */
507bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
508        int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo) {
509    LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
510    LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
511
512    const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
513
514    // Window coordinates of the layer
515    Rect clip;
516    Rect bounds(left, top, right, bottom);
517    Rect untransformedBounds(bounds);
518    mSnapshot->transform->mapRect(bounds);
519
520    // Layers only make sense if they are in the framebuffer's bounds
521    if (bounds.intersect(*mSnapshot->clipRect)) {
522        // We cannot work with sub-pixels in this case
523        bounds.snapToPixelBoundaries();
524
525        // When the layer is not an FBO, we may use glCopyTexImage so we
526        // need to make sure the layer does not extend outside the bounds
527        // of the framebuffer
528        if (!bounds.intersect(mSnapshot->previous->viewport)) {
529            bounds.setEmpty();
530        } else if (fboLayer) {
531            clip.set(bounds);
532            mat4 inverse;
533            inverse.loadInverse(*mSnapshot->transform);
534            inverse.mapRect(clip);
535            clip.snapToPixelBoundaries();
536            if (clip.intersect(untransformedBounds)) {
537                clip.translate(-left, -top);
538                bounds.set(untransformedBounds);
539            } else {
540                clip.setEmpty();
541            }
542        }
543    } else {
544        bounds.setEmpty();
545    }
546
547    if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
548            bounds.getHeight() > mCaches.maxTextureSize ||
549            (fboLayer && clip.isEmpty())) {
550        mSnapshot->empty = fboLayer;
551    } else {
552        mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
553    }
554
555    // Bail out if we won't draw in this snapshot
556    if (mSnapshot->invisible || mSnapshot->empty) {
557        return false;
558    }
559
560    mCaches.activeTexture(0);
561    Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight());
562    if (!layer) {
563        return false;
564    }
565
566    layer->setAlpha(alpha, mode);
567    layer->layer.set(bounds);
568    layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()),
569            bounds.getWidth() / float(layer->getWidth()), 0.0f);
570    layer->setColorFilter(mColorFilter);
571    layer->setBlend(true);
572
573    // Save the layer in the snapshot
574    mSnapshot->flags |= Snapshot::kFlagIsLayer;
575    mSnapshot->layer = layer;
576
577    if (fboLayer) {
578        return createFboLayer(layer, bounds, clip, previousFbo);
579    } else {
580        // Copy the framebuffer into the layer
581        layer->bindTexture();
582        if (!bounds.isEmpty()) {
583            if (layer->isEmpty()) {
584                glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
585                        bounds.left, mSnapshot->height - bounds.bottom,
586                        layer->getWidth(), layer->getHeight(), 0);
587                layer->setEmpty(false);
588            } else {
589                glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left,
590                        mSnapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight());
591            }
592
593            // Enqueue the buffer coordinates to clear the corresponding region later
594            mLayers.push(new Rect(bounds));
595        }
596    }
597
598    return true;
599}
600
601bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo) {
602    layer->setFbo(mCaches.fboCache.get());
603
604    mSnapshot->region = &mSnapshot->layer->region;
605    mSnapshot->flags |= Snapshot::kFlagFboTarget;
606
607    mSnapshot->flags |= Snapshot::kFlagIsFboLayer;
608    mSnapshot->fbo = layer->getFbo();
609    mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
610    mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
611    mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
612    mSnapshot->height = bounds.getHeight();
613    mSnapshot->flags |= Snapshot::kFlagDirtyOrtho;
614    mSnapshot->orthoMatrix.load(mOrthoMatrix);
615
616    // Bind texture to FBO
617    glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
618    layer->bindTexture();
619
620    // Initialize the texture if needed
621    if (layer->isEmpty()) {
622        layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
623        layer->setEmpty(false);
624    }
625
626    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
627            layer->getTexture(), 0);
628
629#if DEBUG_LAYERS_AS_REGIONS
630    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
631    if (status != GL_FRAMEBUFFER_COMPLETE) {
632        ALOGE("Framebuffer incomplete (GL error code 0x%x)", status);
633
634        glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
635        layer->deleteTexture();
636        mCaches.fboCache.put(layer->getFbo());
637
638        delete layer;
639
640        return false;
641    }
642#endif
643
644    // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
645    mCaches.enableScissor();
646    mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
647            clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
648    glClear(GL_COLOR_BUFFER_BIT);
649
650    dirtyClip();
651
652    // Change the ortho projection
653    glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
654    mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
655
656    return true;
657}
658
659/**
660 * Read the documentation of createLayer() before doing anything in this method.
661 */
662void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
663    if (!current->layer) {
664        ALOGE("Attempting to compose a layer that does not exist");
665        return;
666    }
667
668    const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
669
670    if (fboLayer) {
671        // Detach the texture from the FBO
672        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
673
674        // Unbind current FBO and restore previous one
675        glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
676    }
677
678    Layer* layer = current->layer;
679    const Rect& rect = layer->layer;
680
681    if (!fboLayer && layer->getAlpha() < 255) {
682        drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
683                layer->getAlpha() << 24, SkXfermode::kDstIn_Mode, true);
684        // Required below, composeLayerRect() will divide by 255
685        layer->setAlpha(255);
686    }
687
688    mCaches.unbindMeshBuffer();
689
690    mCaches.activeTexture(0);
691
692    // When the layer is stored in an FBO, we can save a bit of fillrate by
693    // drawing only the dirty region
694    if (fboLayer) {
695        dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform);
696        if (layer->getColorFilter()) {
697            setupColorFilter(layer->getColorFilter());
698        }
699        composeLayerRegion(layer, rect);
700        if (layer->getColorFilter()) {
701            resetColorFilter();
702        }
703    } else if (!rect.isEmpty()) {
704        dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
705        composeLayerRect(layer, rect, true);
706    }
707
708    if (fboLayer) {
709        // Note: No need to use glDiscardFramebufferEXT() since we never
710        //       create/compose layers that are not on screen with this
711        //       code path
712        // See LayerRenderer::destroyLayer(Layer*)
713
714        // Put the FBO name back in the cache, if it doesn't fit, it will be destroyed
715        mCaches.fboCache.put(current->fbo);
716        layer->setFbo(0);
717    }
718
719    dirtyClip();
720
721    // Failing to add the layer to the cache should happen only if the layer is too large
722    if (!mCaches.layerCache.put(layer)) {
723        LAYER_LOGD("Deleting layer");
724        layer->deleteTexture();
725        delete layer;
726    }
727}
728
729void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
730    float alpha = layer->getAlpha() / 255.0f;
731
732    mat4& transform = layer->getTransform();
733    if (!transform.isIdentity()) {
734        save(0);
735        mSnapshot->transform->multiply(transform);
736    }
737
738    setupDraw();
739    if (layer->getRenderTarget() == GL_TEXTURE_2D) {
740        setupDrawWithTexture();
741    } else {
742        setupDrawWithExternalTexture();
743    }
744    setupDrawTextureTransform();
745    setupDrawColor(alpha, alpha, alpha, alpha);
746    setupDrawColorFilter();
747    setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode());
748    setupDrawProgram();
749    setupDrawPureColorUniforms();
750    setupDrawColorFilterUniforms();
751    if (layer->getRenderTarget() == GL_TEXTURE_2D) {
752        setupDrawTexture(layer->getTexture());
753    } else {
754        setupDrawExternalTexture(layer->getTexture());
755    }
756    if (mSnapshot->transform->isPureTranslate() &&
757            layer->getWidth() == (uint32_t) rect.getWidth() &&
758            layer->getHeight() == (uint32_t) rect.getHeight()) {
759        const float x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f);
760        const float y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f);
761
762        layer->setFilter(GL_NEAREST);
763        setupDrawModelView(x, y, x + rect.getWidth(), y + rect.getHeight(), true);
764    } else {
765        layer->setFilter(GL_LINEAR);
766        setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom);
767    }
768    setupDrawTextureTransformUniforms(layer->getTexTransform());
769    setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]);
770
771    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
772
773    finishDrawTexture();
774
775    if (!transform.isIdentity()) {
776        restore();
777    }
778}
779
780void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
781    if (!layer->isTextureLayer()) {
782        const Rect& texCoords = layer->texCoords;
783        resetDrawTextureTexCoords(texCoords.left, texCoords.top,
784                texCoords.right, texCoords.bottom);
785
786        float x = rect.left;
787        float y = rect.top;
788        bool simpleTransform = mSnapshot->transform->isPureTranslate() &&
789                layer->getWidth() == (uint32_t) rect.getWidth() &&
790                layer->getHeight() == (uint32_t) rect.getHeight();
791
792        if (simpleTransform) {
793            // When we're swapping, the layer is already in screen coordinates
794            if (!swap) {
795                x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f);
796                y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f);
797            }
798
799            layer->setFilter(GL_NEAREST, true);
800        } else {
801            layer->setFilter(GL_LINEAR, true);
802        }
803
804        drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(),
805                layer->getTexture(), layer->getAlpha() / 255.0f,
806                layer->getMode(), layer->isBlend(),
807                &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
808                GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform);
809
810        resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
811    } else {
812        resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f);
813        drawTextureLayer(layer, rect);
814        resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
815    }
816}
817
818void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
819    if (layer->region.isRect()) {
820        layer->setRegionAsRect();
821
822        composeLayerRect(layer, layer->regionRect);
823
824        layer->region.clear();
825        return;
826    }
827
828    // TODO: See LayerRenderer.cpp::generateMesh() for important
829    //       information about this implementation
830    if (CC_LIKELY(!layer->region.isEmpty())) {
831        size_t count;
832        const android::Rect* rects = layer->region.getArray(&count);
833
834        const float alpha = layer->getAlpha() / 255.0f;
835        const float texX = 1.0f / float(layer->getWidth());
836        const float texY = 1.0f / float(layer->getHeight());
837        const float height = rect.getHeight();
838
839        TextureVertex* mesh = mCaches.getRegionMesh();
840        GLsizei numQuads = 0;
841
842        setupDraw();
843        setupDrawWithTexture();
844        setupDrawColor(alpha, alpha, alpha, alpha);
845        setupDrawColorFilter();
846        setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode(), false);
847        setupDrawProgram();
848        setupDrawDirtyRegionsDisabled();
849        setupDrawPureColorUniforms();
850        setupDrawColorFilterUniforms();
851        setupDrawTexture(layer->getTexture());
852        if (mSnapshot->transform->isPureTranslate()) {
853            const float x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f);
854            const float y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f);
855
856            layer->setFilter(GL_NEAREST);
857            setupDrawModelViewTranslate(x, y, x + rect.getWidth(), y + rect.getHeight(), true);
858        } else {
859            layer->setFilter(GL_LINEAR);
860            setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom);
861        }
862        setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]);
863
864        for (size_t i = 0; i < count; i++) {
865            const android::Rect* r = &rects[i];
866
867            const float u1 = r->left * texX;
868            const float v1 = (height - r->top) * texY;
869            const float u2 = r->right * texX;
870            const float v2 = (height - r->bottom) * texY;
871
872            // TODO: Reject quads outside of the clip
873            TextureVertex::set(mesh++, r->left, r->top, u1, v1);
874            TextureVertex::set(mesh++, r->right, r->top, u2, v1);
875            TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
876            TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
877
878            numQuads++;
879
880            if (numQuads >= REGION_MESH_QUAD_COUNT) {
881                glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL);
882                numQuads = 0;
883                mesh = mCaches.getRegionMesh();
884            }
885        }
886
887        if (numQuads > 0) {
888            glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL);
889        }
890
891        finishDrawTexture();
892
893#if DEBUG_LAYERS_AS_REGIONS
894        drawRegionRects(layer->region);
895#endif
896
897        layer->region.clear();
898    }
899}
900
901void OpenGLRenderer::drawRegionRects(const Region& region) {
902#if DEBUG_LAYERS_AS_REGIONS
903    size_t count;
904    const android::Rect* rects = region.getArray(&count);
905
906    uint32_t colors[] = {
907            0x7fff0000, 0x7f00ff00,
908            0x7f0000ff, 0x7fff00ff,
909    };
910
911    int offset = 0;
912    int32_t top = rects[0].top;
913
914    for (size_t i = 0; i < count; i++) {
915        if (top != rects[i].top) {
916            offset ^= 0x2;
917            top = rects[i].top;
918        }
919
920        Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
921        drawColorRect(r.left, r.top, r.right, r.bottom, colors[offset + (i & 0x1)],
922                SkXfermode::kSrcOver_Mode);
923    }
924#endif
925}
926
927void OpenGLRenderer::dirtyLayer(const float left, const float top,
928        const float right, const float bottom, const mat4 transform) {
929    if (hasLayer()) {
930        Rect bounds(left, top, right, bottom);
931        transform.mapRect(bounds);
932        dirtyLayerUnchecked(bounds, getRegion());
933    }
934}
935
936void OpenGLRenderer::dirtyLayer(const float left, const float top,
937        const float right, const float bottom) {
938    if (hasLayer()) {
939        Rect bounds(left, top, right, bottom);
940        dirtyLayerUnchecked(bounds, getRegion());
941    }
942}
943
944void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
945    if (bounds.intersect(*mSnapshot->clipRect)) {
946        bounds.snapToPixelBoundaries();
947        android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
948        if (!dirty.isEmpty()) {
949            region->orSelf(dirty);
950        }
951    }
952}
953
954void OpenGLRenderer::clearLayerRegions() {
955    const size_t count = mLayers.size();
956    if (count == 0) return;
957
958    if (!mSnapshot->isIgnored()) {
959        // Doing several glScissor/glClear here can negatively impact
960        // GPUs with a tiler architecture, instead we draw quads with
961        // the Clear blending mode
962
963        // The list contains bounds that have already been clipped
964        // against their initial clip rect, and the current clip
965        // is likely different so we need to disable clipping here
966        bool scissorChanged = mCaches.disableScissor();
967
968        Vertex mesh[count * 6];
969        Vertex* vertex = mesh;
970
971        for (uint32_t i = 0; i < count; i++) {
972            Rect* bounds = mLayers.itemAt(i);
973
974            Vertex::set(vertex++, bounds->left, bounds->bottom);
975            Vertex::set(vertex++, bounds->left, bounds->top);
976            Vertex::set(vertex++, bounds->right, bounds->top);
977            Vertex::set(vertex++, bounds->left, bounds->bottom);
978            Vertex::set(vertex++, bounds->right, bounds->top);
979            Vertex::set(vertex++, bounds->right, bounds->bottom);
980
981            delete bounds;
982        }
983
984        setupDraw(false);
985        setupDrawColor(0.0f, 0.0f, 0.0f, 1.0f);
986        setupDrawBlending(true, SkXfermode::kClear_Mode);
987        setupDrawProgram();
988        setupDrawPureColorUniforms();
989        setupDrawModelViewTranslate(0.0f, 0.0f, 0.0f, 0.0f, true);
990        setupDrawVertices(&mesh[0].position[0]);
991
992        glDrawArrays(GL_TRIANGLES, 0, count * 6);
993
994        if (scissorChanged) mCaches.enableScissor();
995    } else {
996        for (uint32_t i = 0; i < count; i++) {
997            delete mLayers.itemAt(i);
998        }
999    }
1000
1001    mLayers.clear();
1002}
1003
1004///////////////////////////////////////////////////////////////////////////////
1005// Transforms
1006///////////////////////////////////////////////////////////////////////////////
1007
1008void OpenGLRenderer::translate(float dx, float dy) {
1009    mSnapshot->transform->translate(dx, dy, 0.0f);
1010}
1011
1012void OpenGLRenderer::rotate(float degrees) {
1013    mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
1014}
1015
1016void OpenGLRenderer::scale(float sx, float sy) {
1017    mSnapshot->transform->scale(sx, sy, 1.0f);
1018}
1019
1020void OpenGLRenderer::skew(float sx, float sy) {
1021    mSnapshot->transform->skew(sx, sy);
1022}
1023
1024void OpenGLRenderer::setMatrix(SkMatrix* matrix) {
1025    if (matrix) {
1026        mSnapshot->transform->load(*matrix);
1027    } else {
1028        mSnapshot->transform->loadIdentity();
1029    }
1030}
1031
1032void OpenGLRenderer::getMatrix(SkMatrix* matrix) {
1033    mSnapshot->transform->copyTo(*matrix);
1034}
1035
1036void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
1037    SkMatrix transform;
1038    mSnapshot->transform->copyTo(transform);
1039    transform.preConcat(*matrix);
1040    mSnapshot->transform->load(transform);
1041}
1042
1043///////////////////////////////////////////////////////////////////////////////
1044// Clipping
1045///////////////////////////////////////////////////////////////////////////////
1046
1047void OpenGLRenderer::setScissorFromClip() {
1048    Rect clip(*mSnapshot->clipRect);
1049    clip.snapToPixelBoundaries();
1050
1051    if (mCaches.setScissor(clip.left, mSnapshot->height - clip.bottom,
1052            clip.getWidth(), clip.getHeight())) {
1053        mDirtyClip = false;
1054    }
1055}
1056
1057const Rect& OpenGLRenderer::getClipBounds() {
1058    return mSnapshot->getLocalClip();
1059}
1060
1061bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom) {
1062    if (mSnapshot->isIgnored()) {
1063        return true;
1064    }
1065
1066    Rect r(left, top, right, bottom);
1067    mSnapshot->transform->mapRect(r);
1068    r.snapToPixelBoundaries();
1069
1070    Rect clipRect(*mSnapshot->clipRect);
1071    clipRect.snapToPixelBoundaries();
1072
1073    return !clipRect.intersects(r);
1074}
1075
1076bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
1077    if (mSnapshot->isIgnored()) {
1078        return true;
1079    }
1080
1081    Rect r(left, top, right, bottom);
1082    mSnapshot->transform->mapRect(r);
1083    r.snapToPixelBoundaries();
1084
1085    Rect clipRect(*mSnapshot->clipRect);
1086    clipRect.snapToPixelBoundaries();
1087
1088    bool rejected = !clipRect.intersects(r);
1089    if (!isDeferred() && !rejected) {
1090        mCaches.setScissorEnabled(!clipRect.contains(r));
1091    }
1092
1093    return rejected;
1094}
1095
1096bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
1097    bool clipped = mSnapshot->clip(left, top, right, bottom, op);
1098    if (clipped) {
1099        dirtyClip();
1100    }
1101    return !mSnapshot->clipRect->isEmpty();
1102}
1103
1104Rect* OpenGLRenderer::getClipRect() {
1105    return mSnapshot->clipRect;
1106}
1107
1108///////////////////////////////////////////////////////////////////////////////
1109// Drawing commands
1110///////////////////////////////////////////////////////////////////////////////
1111
1112void OpenGLRenderer::setupDraw(bool clear) {
1113    // TODO: It would be best if we could do this before quickReject()
1114    //       changes the scissor test state
1115    if (clear) clearLayerRegions();
1116    if (mDirtyClip) {
1117        setScissorFromClip();
1118    }
1119    mDescription.reset();
1120    mSetShaderColor = false;
1121    mColorSet = false;
1122    mColorA = mColorR = mColorG = mColorB = 0.0f;
1123    mTextureUnit = 0;
1124    mTrackDirtyRegions = true;
1125}
1126
1127void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
1128    mDescription.hasTexture = true;
1129    mDescription.hasAlpha8Texture = isAlpha8;
1130}
1131
1132void OpenGLRenderer::setupDrawWithExternalTexture() {
1133    mDescription.hasExternalTexture = true;
1134}
1135
1136void OpenGLRenderer::setupDrawNoTexture() {
1137    mCaches.disbaleTexCoordsVertexArray();
1138}
1139
1140void OpenGLRenderer::setupDrawAALine() {
1141    mDescription.isAA = true;
1142}
1143
1144void OpenGLRenderer::setupDrawPoint(float pointSize) {
1145    mDescription.isPoint = true;
1146    mDescription.pointSize = pointSize;
1147}
1148
1149void OpenGLRenderer::setupDrawColor(int color) {
1150    setupDrawColor(color, (color >> 24) & 0xFF);
1151}
1152
1153void OpenGLRenderer::setupDrawColor(int color, int alpha) {
1154    mColorA = alpha / 255.0f;
1155    // Second divide of a by 255 is an optimization, allowing us to simply multiply
1156    // the rgb values by a instead of also dividing by 255
1157    const float a = mColorA / 255.0f;
1158    mColorR = a * ((color >> 16) & 0xFF);
1159    mColorG = a * ((color >>  8) & 0xFF);
1160    mColorB = a * ((color      ) & 0xFF);
1161    mColorSet = true;
1162    mSetShaderColor = mDescription.setColor(mColorR, mColorG, mColorB, mColorA);
1163}
1164
1165void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) {
1166    mColorA = alpha / 255.0f;
1167    // Double-divide of a by 255 is an optimization, allowing us to simply multiply
1168    // the rgb values by a instead of also dividing by 255
1169    const float a = mColorA / 255.0f;
1170    mColorR = a * ((color >> 16) & 0xFF);
1171    mColorG = a * ((color >>  8) & 0xFF);
1172    mColorB = a * ((color      ) & 0xFF);
1173    mColorSet = true;
1174    mSetShaderColor = mDescription.setAlpha8Color(mColorR, mColorG, mColorB, mColorA);
1175}
1176
1177void OpenGLRenderer::setupDrawTextGamma(const SkPaint* paint) {
1178    mCaches.fontRenderer->describe(mDescription, paint);
1179}
1180
1181void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) {
1182    mColorA = a;
1183    mColorR = r;
1184    mColorG = g;
1185    mColorB = b;
1186    mColorSet = true;
1187    mSetShaderColor = mDescription.setColor(r, g, b, a);
1188}
1189
1190void OpenGLRenderer::setupDrawShader() {
1191    if (mShader) {
1192        mShader->describe(mDescription, mCaches.extensions);
1193    }
1194}
1195
1196void OpenGLRenderer::setupDrawColorFilter() {
1197    if (mColorFilter) {
1198        mColorFilter->describe(mDescription, mCaches.extensions);
1199    }
1200}
1201
1202void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) {
1203    if (mColorSet && mode == SkXfermode::kClear_Mode) {
1204        mColorA = 1.0f;
1205        mColorR = mColorG = mColorB = 0.0f;
1206        mSetShaderColor = mDescription.modulate = true;
1207    }
1208}
1209
1210void OpenGLRenderer::setupDrawBlending(SkXfermode::Mode mode, bool swapSrcDst) {
1211    // When the blending mode is kClear_Mode, we need to use a modulate color
1212    // argb=1,0,0,0
1213    accountForClear(mode);
1214    chooseBlending((mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode,
1215            mDescription, swapSrcDst);
1216}
1217
1218void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool swapSrcDst) {
1219    // When the blending mode is kClear_Mode, we need to use a modulate color
1220    // argb=1,0,0,0
1221    accountForClear(mode);
1222    chooseBlending(blend || (mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode,
1223            mDescription, swapSrcDst);
1224}
1225
1226void OpenGLRenderer::setupDrawProgram() {
1227    useProgram(mCaches.programCache.get(mDescription));
1228}
1229
1230void OpenGLRenderer::setupDrawDirtyRegionsDisabled() {
1231    mTrackDirtyRegions = false;
1232}
1233
1234void OpenGLRenderer::setupDrawModelViewTranslate(float left, float top, float right, float bottom,
1235        bool ignoreTransform) {
1236    mModelView.loadTranslate(left, top, 0.0f);
1237    if (!ignoreTransform) {
1238        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
1239        if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
1240    } else {
1241        mCaches.currentProgram->set(mOrthoMatrix, mModelView, mIdentity);
1242        if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom);
1243    }
1244}
1245
1246void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) {
1247    mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform, offset);
1248}
1249
1250void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom,
1251        bool ignoreTransform, bool ignoreModelView) {
1252    if (!ignoreModelView) {
1253        mModelView.loadTranslate(left, top, 0.0f);
1254        mModelView.scale(right - left, bottom - top, 1.0f);
1255    } else {
1256        mModelView.loadIdentity();
1257    }
1258    bool dirty = right - left > 0.0f && bottom - top > 0.0f;
1259    if (!ignoreTransform) {
1260        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
1261        if (mTrackDirtyRegions && dirty) {
1262            dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
1263        }
1264    } else {
1265        mCaches.currentProgram->set(mOrthoMatrix, mModelView, mIdentity);
1266        if (mTrackDirtyRegions && dirty) dirtyLayer(left, top, right, bottom);
1267    }
1268}
1269
1270void OpenGLRenderer::setupDrawPointUniforms() {
1271    int slot = mCaches.currentProgram->getUniform("pointSize");
1272    glUniform1f(slot, mDescription.pointSize);
1273}
1274
1275void OpenGLRenderer::setupDrawColorUniforms() {
1276    if ((mColorSet && !mShader) || (mShader && mSetShaderColor)) {
1277        mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
1278    }
1279}
1280
1281void OpenGLRenderer::setupDrawPureColorUniforms() {
1282    if (mSetShaderColor) {
1283        mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
1284    }
1285}
1286
1287void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
1288    if (mShader) {
1289        if (ignoreTransform) {
1290            mModelView.loadInverse(*mSnapshot->transform);
1291        }
1292        mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &mTextureUnit);
1293    }
1294}
1295
1296void OpenGLRenderer::setupDrawShaderIdentityUniforms() {
1297    if (mShader) {
1298        mShader->setupProgram(mCaches.currentProgram, mIdentity, *mSnapshot, &mTextureUnit);
1299    }
1300}
1301
1302void OpenGLRenderer::setupDrawColorFilterUniforms() {
1303    if (mColorFilter) {
1304        mColorFilter->setupProgram(mCaches.currentProgram);
1305    }
1306}
1307
1308void OpenGLRenderer::setupDrawTextGammaUniforms() {
1309    mCaches.fontRenderer->setupProgram(mDescription, mCaches.currentProgram);
1310}
1311
1312void OpenGLRenderer::setupDrawSimpleMesh() {
1313    bool force = mCaches.bindMeshBuffer();
1314    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, 0);
1315    mCaches.unbindIndicesBuffer();
1316}
1317
1318void OpenGLRenderer::setupDrawTexture(GLuint texture) {
1319    bindTexture(texture);
1320    mTextureUnit++;
1321    mCaches.enableTexCoordsVertexArray();
1322}
1323
1324void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
1325    bindExternalTexture(texture);
1326    mTextureUnit++;
1327    mCaches.enableTexCoordsVertexArray();
1328}
1329
1330void OpenGLRenderer::setupDrawTextureTransform() {
1331    mDescription.hasTextureTransform = true;
1332}
1333
1334void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
1335    glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1,
1336            GL_FALSE, &transform.data[0]);
1337}
1338
1339void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
1340    bool force = false;
1341    if (!vertices) {
1342        force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
1343    } else {
1344        force = mCaches.unbindMeshBuffer();
1345    }
1346
1347    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, vertices);
1348    if (mCaches.currentProgram->texCoords >= 0) {
1349        mCaches.bindTexCoordsVertexPointer(force, mCaches.currentProgram->texCoords, texCoords);
1350    }
1351
1352    mCaches.unbindIndicesBuffer();
1353}
1354
1355void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords) {
1356    bool force = mCaches.unbindMeshBuffer();
1357    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, vertices);
1358    if (mCaches.currentProgram->texCoords >= 0) {
1359        mCaches.bindTexCoordsVertexPointer(force, mCaches.currentProgram->texCoords, texCoords);
1360    }
1361}
1362
1363void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
1364    bool force = mCaches.unbindMeshBuffer();
1365    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
1366            vertices, gVertexStride);
1367    mCaches.unbindIndicesBuffer();
1368}
1369
1370/**
1371 * Sets up the shader to draw an AA line. We draw AA lines with quads, where there is an
1372 * outer boundary that fades out to 0. The variables set in the shader define the proportion of
1373 * the width and length of the primitive occupied by the AA region. The vtxWidth and vtxLength
1374 * attributes (one per vertex) are values from zero to one that tells the fragment
1375 * shader where the fragment is in relation to the line width/length overall; these values are
1376 * then used to compute the proper color, based on whether the fragment lies in the fading AA
1377 * region of the line.
1378 * Note that we only pass down the width values in this setup function. The length coordinates
1379 * are set up for each individual segment.
1380 */
1381void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
1382        GLvoid* lengthCoords, float boundaryWidthProportion, int& widthSlot, int& lengthSlot) {
1383    bool force = mCaches.unbindMeshBuffer();
1384    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
1385            vertices, gAAVertexStride);
1386    mCaches.resetTexCoordsVertexPointer();
1387    mCaches.unbindIndicesBuffer();
1388
1389    widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
1390    glEnableVertexAttribArray(widthSlot);
1391    glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords);
1392
1393    lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
1394    glEnableVertexAttribArray(lengthSlot);
1395    glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords);
1396
1397    int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth");
1398    glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
1399}
1400
1401void OpenGLRenderer::finishDrawAALine(const int widthSlot, const int lengthSlot) {
1402    glDisableVertexAttribArray(widthSlot);
1403    glDisableVertexAttribArray(lengthSlot);
1404}
1405
1406void OpenGLRenderer::finishDrawTexture() {
1407}
1408
1409///////////////////////////////////////////////////////////////////////////////
1410// Drawing
1411///////////////////////////////////////////////////////////////////////////////
1412
1413status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList,
1414        Rect& dirty, int32_t flags, uint32_t level) {
1415
1416    // All the usual checks and setup operations (quickReject, setupDraw, etc.)
1417    // will be performed by the display list itself
1418    if (displayList && displayList->isRenderable()) {
1419        return displayList->replay(*this, dirty, flags, level);
1420    }
1421
1422    return DrawGlInfo::kStatusDone;
1423}
1424
1425void OpenGLRenderer::outputDisplayList(DisplayList* displayList, uint32_t level) {
1426    if (displayList) {
1427        displayList->output(*this, level);
1428    }
1429}
1430
1431void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint) {
1432    int alpha;
1433    SkXfermode::Mode mode;
1434    getAlphaAndMode(paint, &alpha, &mode);
1435
1436    float x = left;
1437    float y = top;
1438
1439    GLenum filter = GL_LINEAR;
1440    bool ignoreTransform = false;
1441    if (mSnapshot->transform->isPureTranslate()) {
1442        x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
1443        y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
1444        ignoreTransform = true;
1445        filter = GL_NEAREST;
1446    } else {
1447        filter = FILTER(paint);
1448    }
1449
1450    setupDraw();
1451    setupDrawWithTexture(true);
1452    if (paint) {
1453        setupDrawAlpha8Color(paint->getColor(), alpha);
1454    }
1455    setupDrawColorFilter();
1456    setupDrawShader();
1457    setupDrawBlending(true, mode);
1458    setupDrawProgram();
1459    setupDrawModelView(x, y, x + texture->width, y + texture->height, ignoreTransform);
1460
1461    setupDrawTexture(texture->id);
1462    texture->setWrap(GL_CLAMP_TO_EDGE);
1463    texture->setFilter(filter);
1464
1465    setupDrawPureColorUniforms();
1466    setupDrawColorFilterUniforms();
1467    setupDrawShaderUniforms();
1468    setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
1469
1470    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
1471
1472    finishDrawTexture();
1473}
1474
1475status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
1476    const float right = left + bitmap->width();
1477    const float bottom = top + bitmap->height();
1478
1479    if (quickReject(left, top, right, bottom)) {
1480        return DrawGlInfo::kStatusDone;
1481    }
1482
1483    mCaches.activeTexture(0);
1484    Texture* texture = mCaches.textureCache.get(bitmap);
1485    if (!texture) return DrawGlInfo::kStatusDone;
1486    const AutoTexture autoCleanup(texture);
1487
1488    if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
1489        drawAlphaBitmap(texture, left, top, paint);
1490    } else {
1491        drawTextureRect(left, top, right, bottom, texture, paint);
1492    }
1493
1494    return DrawGlInfo::kStatusDrew;
1495}
1496
1497status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
1498    Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
1499    const mat4 transform(*matrix);
1500    transform.mapRect(r);
1501
1502    if (quickReject(r.left, r.top, r.right, r.bottom)) {
1503        return DrawGlInfo::kStatusDone;
1504    }
1505
1506    mCaches.activeTexture(0);
1507    Texture* texture = mCaches.textureCache.get(bitmap);
1508    if (!texture) return DrawGlInfo::kStatusDone;
1509    const AutoTexture autoCleanup(texture);
1510
1511    // This could be done in a cheaper way, all we need is pass the matrix
1512    // to the vertex shader. The save/restore is a bit overkill.
1513    save(SkCanvas::kMatrix_SaveFlag);
1514    concatMatrix(matrix);
1515    drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint);
1516    restore();
1517
1518    return DrawGlInfo::kStatusDrew;
1519}
1520
1521status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
1522    const float right = left + bitmap->width();
1523    const float bottom = top + bitmap->height();
1524
1525    if (quickReject(left, top, right, bottom)) {
1526        return DrawGlInfo::kStatusDone;
1527    }
1528
1529    mCaches.activeTexture(0);
1530    Texture* texture = mCaches.textureCache.getTransient(bitmap);
1531    const AutoTexture autoCleanup(texture);
1532
1533    drawTextureRect(left, top, right, bottom, texture, paint);
1534
1535    return DrawGlInfo::kStatusDrew;
1536}
1537
1538status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
1539        float* vertices, int* colors, SkPaint* paint) {
1540    // TODO: Do a quickReject
1541    if (!vertices || mSnapshot->isIgnored()) {
1542        return DrawGlInfo::kStatusDone;
1543    }
1544
1545    mCaches.activeTexture(0);
1546    Texture* texture = mCaches.textureCache.get(bitmap);
1547    if (!texture) return DrawGlInfo::kStatusDone;
1548    const AutoTexture autoCleanup(texture);
1549
1550    texture->setWrap(GL_CLAMP_TO_EDGE, true);
1551    texture->setFilter(FILTER(paint), true);
1552
1553    int alpha;
1554    SkXfermode::Mode mode;
1555    getAlphaAndMode(paint, &alpha, &mode);
1556
1557    const uint32_t count = meshWidth * meshHeight * 6;
1558
1559    float left = FLT_MAX;
1560    float top = FLT_MAX;
1561    float right = FLT_MIN;
1562    float bottom = FLT_MIN;
1563
1564    const bool hasActiveLayer = hasLayer();
1565
1566    // TODO: Support the colors array
1567    TextureVertex mesh[count];
1568    TextureVertex* vertex = mesh;
1569    for (int32_t y = 0; y < meshHeight; y++) {
1570        for (int32_t x = 0; x < meshWidth; x++) {
1571            uint32_t i = (y * (meshWidth + 1) + x) * 2;
1572
1573            float u1 = float(x) / meshWidth;
1574            float u2 = float(x + 1) / meshWidth;
1575            float v1 = float(y) / meshHeight;
1576            float v2 = float(y + 1) / meshHeight;
1577
1578            int ax = i + (meshWidth + 1) * 2;
1579            int ay = ax + 1;
1580            int bx = i;
1581            int by = bx + 1;
1582            int cx = i + 2;
1583            int cy = cx + 1;
1584            int dx = i + (meshWidth + 1) * 2 + 2;
1585            int dy = dx + 1;
1586
1587            TextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2);
1588            TextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1);
1589            TextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1);
1590
1591            TextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2);
1592            TextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1);
1593            TextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2);
1594
1595            if (hasActiveLayer) {
1596                // TODO: This could be optimized to avoid unnecessary ops
1597                left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx])));
1598                top = fminf(top, fminf(vertices[ay], fminf(vertices[by], vertices[cy])));
1599                right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx])));
1600                bottom = fmaxf(bottom, fmaxf(vertices[ay], fmaxf(vertices[by], vertices[cy])));
1601            }
1602        }
1603    }
1604
1605    if (hasActiveLayer) {
1606        dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
1607    }
1608
1609    drawTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f,
1610            mode, texture->blend, &mesh[0].position[0], &mesh[0].texture[0],
1611            GL_TRIANGLES, count, false, false, 0, false, false);
1612
1613    return DrawGlInfo::kStatusDrew;
1614}
1615
1616status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
1617         float srcLeft, float srcTop, float srcRight, float srcBottom,
1618         float dstLeft, float dstTop, float dstRight, float dstBottom,
1619         SkPaint* paint) {
1620    if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) {
1621        return DrawGlInfo::kStatusDone;
1622    }
1623
1624    mCaches.activeTexture(0);
1625    Texture* texture = mCaches.textureCache.get(bitmap);
1626    if (!texture) return DrawGlInfo::kStatusDone;
1627    const AutoTexture autoCleanup(texture);
1628
1629    const float width = texture->width;
1630    const float height = texture->height;
1631
1632    const float u1 = fmax(0.0f, srcLeft / width);
1633    const float v1 = fmax(0.0f, srcTop / height);
1634    const float u2 = fmin(1.0f, srcRight / width);
1635    const float v2 = fmin(1.0f, srcBottom / height);
1636
1637    mCaches.unbindMeshBuffer();
1638    resetDrawTextureTexCoords(u1, v1, u2, v2);
1639
1640    int alpha;
1641    SkXfermode::Mode mode;
1642    getAlphaAndMode(paint, &alpha, &mode);
1643
1644    texture->setWrap(GL_CLAMP_TO_EDGE, true);
1645
1646    if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) {
1647        const float x = (int) floorf(dstLeft + mSnapshot->transform->getTranslateX() + 0.5f);
1648        const float y = (int) floorf(dstTop + mSnapshot->transform->getTranslateY() + 0.5f);
1649
1650        GLenum filter = GL_NEAREST;
1651        // Enable linear filtering if the source rectangle is scaled
1652        if (srcRight - srcLeft != dstRight - dstLeft || srcBottom - srcTop != dstBottom - dstTop) {
1653            filter = FILTER(paint);
1654        }
1655
1656        texture->setFilter(filter, true);
1657        drawTextureMesh(x, y, x + (dstRight - dstLeft), y + (dstBottom - dstTop),
1658                texture->id, alpha / 255.0f, mode, texture->blend,
1659                &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
1660                GL_TRIANGLE_STRIP, gMeshCount, false, true);
1661    } else {
1662        texture->setFilter(FILTER(paint), true);
1663        drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom, texture->id, alpha / 255.0f,
1664                mode, texture->blend, &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
1665                GL_TRIANGLE_STRIP, gMeshCount);
1666    }
1667
1668    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
1669
1670    return DrawGlInfo::kStatusDrew;
1671}
1672
1673status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
1674        const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
1675        float left, float top, float right, float bottom, SkPaint* paint) {
1676    int alpha;
1677    SkXfermode::Mode mode;
1678    getAlphaAndModeDirect(paint, &alpha, &mode);
1679
1680    return drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors,
1681            left, top, right, bottom, alpha, mode);
1682}
1683
1684status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
1685        const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
1686        float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode) {
1687    if (quickReject(left, top, right, bottom)) {
1688        return DrawGlInfo::kStatusDone;
1689    }
1690
1691    alpha *= mSnapshot->alpha;
1692
1693    mCaches.activeTexture(0);
1694    Texture* texture = mCaches.textureCache.get(bitmap);
1695    if (!texture) return DrawGlInfo::kStatusDone;
1696    const AutoTexture autoCleanup(texture);
1697    texture->setWrap(GL_CLAMP_TO_EDGE, true);
1698    texture->setFilter(GL_LINEAR, true);
1699
1700    const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
1701            right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
1702
1703    if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
1704        const bool pureTranslate = mSnapshot->transform->isPureTranslate();
1705        // Mark the current layer dirty where we are going to draw the patch
1706        if (hasLayer() && mesh->hasEmptyQuads) {
1707            const float offsetX = left + mSnapshot->transform->getTranslateX();
1708            const float offsetY = top + mSnapshot->transform->getTranslateY();
1709            const size_t count = mesh->quads.size();
1710            for (size_t i = 0; i < count; i++) {
1711                const Rect& bounds = mesh->quads.itemAt(i);
1712                if (CC_LIKELY(pureTranslate)) {
1713                    const float x = (int) floorf(bounds.left + offsetX + 0.5f);
1714                    const float y = (int) floorf(bounds.top + offsetY + 0.5f);
1715                    dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight());
1716                } else {
1717                    dirtyLayer(left + bounds.left, top + bounds.top,
1718                            left + bounds.right, top + bounds.bottom, *mSnapshot->transform);
1719                }
1720            }
1721        }
1722
1723        if (CC_LIKELY(pureTranslate)) {
1724            const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
1725            const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
1726
1727            drawTextureMesh(x, y, x + right - left, y + bottom - top, texture->id, alpha / 255.0f,
1728                    mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
1729                    GL_TRIANGLES, mesh->verticesCount, false, true, mesh->meshBuffer,
1730                    true, !mesh->hasEmptyQuads);
1731        } else {
1732            drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
1733                    mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
1734                    GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer,
1735                    true, !mesh->hasEmptyQuads);
1736        }
1737    }
1738
1739    return DrawGlInfo::kStatusDrew;
1740}
1741
1742/**
1743 * This function uses a similar approach to that of AA lines in the drawLines() function.
1744 * We expand the rectangle by a half pixel in screen space on all sides, and use a fragment
1745 * shader to compute the translucency of the color, determined by whether a given pixel is
1746 * within that boundary region and how far into the region it is.
1747 */
1748void OpenGLRenderer::drawAARect(float left, float top, float right, float bottom,
1749        int color, SkXfermode::Mode mode) {
1750    float inverseScaleX = 1.0f;
1751    float inverseScaleY = 1.0f;
1752
1753    // The quad that we use needs to account for scaling.
1754    if (CC_UNLIKELY(!mSnapshot->transform->isPureTranslate())) {
1755        Matrix4 *mat = mSnapshot->transform;
1756        float m00 = mat->data[Matrix4::kScaleX];
1757        float m01 = mat->data[Matrix4::kSkewY];
1758        float m02 = mat->data[2];
1759        float m10 = mat->data[Matrix4::kSkewX];
1760        float m11 = mat->data[Matrix4::kScaleX];
1761        float m12 = mat->data[6];
1762        float scaleX = sqrt(m00 * m00 + m01 * m01);
1763        float scaleY = sqrt(m10 * m10 + m11 * m11);
1764        inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
1765        inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
1766    }
1767
1768    float boundarySizeX = .5 * inverseScaleX;
1769    float boundarySizeY = .5 * inverseScaleY;
1770
1771    // Adjust the rect by the AA boundary padding
1772    left -= boundarySizeX;
1773    right += boundarySizeX;
1774    top -= boundarySizeY;
1775    bottom += boundarySizeY;
1776
1777    if (!quickReject(left, top, right, bottom)) {
1778        setupDraw();
1779        setupDrawNoTexture();
1780        setupDrawAALine();
1781        setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
1782        setupDrawColorFilter();
1783        setupDrawShader();
1784        setupDrawBlending(true, mode);
1785        setupDrawProgram();
1786        setupDrawModelViewIdentity(true);
1787        setupDrawColorUniforms();
1788        setupDrawColorFilterUniforms();
1789        setupDrawShaderIdentityUniforms();
1790
1791        AAVertex rects[4];
1792        AAVertex* aaVertices = &rects[0];
1793        void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
1794        void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
1795
1796        int widthSlot;
1797        int lengthSlot;
1798
1799        float width = right - left;
1800        float height = bottom - top;
1801
1802        float boundaryWidthProportion = .5 - ((width != 0) ? (2 * boundarySizeX) / width : 0);
1803        float boundaryHeightProportion = .5 - ((height != 0) ? (2 * boundarySizeY) / height : 0);
1804        setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
1805                boundaryWidthProportion, widthSlot, lengthSlot);
1806
1807        int boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
1808        glUniform1f(boundaryLengthSlot, boundaryHeightProportion);
1809
1810        AAVertex::set(aaVertices++, left, bottom, 1, 1);
1811        AAVertex::set(aaVertices++, left, top, 1, 0);
1812        AAVertex::set(aaVertices++, right, bottom, 0, 1);
1813        AAVertex::set(aaVertices++, right, top, 0, 0);
1814        dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
1815
1816        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1817
1818        finishDrawAALine(widthSlot, lengthSlot);
1819    }
1820}
1821
1822/**
1823 * We draw lines as quads (tristrips). Using GL_LINES can be difficult because the rasterization
1824 * rules for those lines produces some unexpected results, and may vary between hardware devices.
1825 * The basics of lines-as-quads is easy; we simply find the normal to the line and position the
1826 * corners of the quads on either side of each line endpoint, separated by the strokeWidth
1827 * of the line. Hairlines are more involved because we need to account for transform scaling
1828 * to end up with a one-pixel-wide line in screen space..
1829 * Anti-aliased lines add another factor to the approach. We use a specialized fragment shader
1830 * in combination with values that we calculate and pass down in this method. The basic approach
1831 * is that the quad we create contains both the core line area plus a bounding area in which
1832 * the translucent/AA pixels are drawn. The values we calculate tell the shader what
1833 * proportion of the width and the length of a given segment is represented by the boundary
1834 * region. The quad ends up being exactly .5 pixel larger in all directions than the non-AA quad.
1835 * The bounding region is actually 1 pixel wide on all sides (half pixel on the outside, half pixel
1836 * on the inside). This ends up giving the result we want, with pixels that are completely
1837 * 'inside' the line area being filled opaquely and the other pixels being filled according to
1838 * how far into the boundary region they are, which is determined by shader interpolation.
1839 */
1840status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
1841    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
1842
1843    const bool isAA = paint->isAntiAlias();
1844    // We use half the stroke width here because we're going to position the quad
1845    // corner vertices half of the width away from the line endpoints
1846    float halfStrokeWidth = paint->getStrokeWidth() * 0.5f;
1847    // A stroke width of 0 has a special meaning in Skia:
1848    // it draws a line 1 px wide regardless of current transform
1849    bool isHairLine = paint->getStrokeWidth() == 0.0f;
1850
1851    float inverseScaleX = 1.0f;
1852    float inverseScaleY = 1.0f;
1853    bool scaled = false;
1854
1855    int alpha;
1856    SkXfermode::Mode mode;
1857
1858    int generatedVerticesCount = 0;
1859    int verticesCount = count;
1860    if (count > 4) {
1861        // Polyline: account for extra vertices needed for continuous tri-strip
1862        verticesCount += (count - 4);
1863    }
1864
1865    if (isHairLine || isAA) {
1866        // The quad that we use for AA and hairlines needs to account for scaling. For hairlines
1867        // the line on the screen should always be one pixel wide regardless of scale. For
1868        // AA lines, we only want one pixel of translucent boundary around the quad.
1869        if (CC_UNLIKELY(!mSnapshot->transform->isPureTranslate())) {
1870            Matrix4 *mat = mSnapshot->transform;
1871            float m00 = mat->data[Matrix4::kScaleX];
1872            float m01 = mat->data[Matrix4::kSkewY];
1873            float m02 = mat->data[2];
1874            float m10 = mat->data[Matrix4::kSkewX];
1875            float m11 = mat->data[Matrix4::kScaleX];
1876            float m12 = mat->data[6];
1877
1878            float scaleX = sqrtf(m00 * m00 + m01 * m01);
1879            float scaleY = sqrtf(m10 * m10 + m11 * m11);
1880
1881            inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
1882            inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
1883
1884            if (inverseScaleX != 1.0f || inverseScaleY != 1.0f) {
1885                scaled = true;
1886            }
1887        }
1888    }
1889
1890    getAlphaAndMode(paint, &alpha, &mode);
1891
1892    mCaches.enableScissor();
1893
1894    setupDraw();
1895    setupDrawNoTexture();
1896    if (isAA) {
1897        setupDrawAALine();
1898    }
1899    setupDrawColor(paint->getColor(), alpha);
1900    setupDrawColorFilter();
1901    setupDrawShader();
1902    setupDrawBlending(isAA, mode);
1903    setupDrawProgram();
1904    setupDrawModelViewIdentity(true);
1905    setupDrawColorUniforms();
1906    setupDrawColorFilterUniforms();
1907    setupDrawShaderIdentityUniforms();
1908
1909    if (isHairLine) {
1910        // Set a real stroke width to be used in quad construction
1911        halfStrokeWidth = isAA? 1 : .5;
1912    } else if (isAA && !scaled) {
1913        // Expand boundary to enable AA calculations on the quad border
1914        halfStrokeWidth += .5f;
1915    }
1916
1917    int widthSlot;
1918    int lengthSlot;
1919
1920    Vertex lines[verticesCount];
1921    Vertex* vertices = &lines[0];
1922
1923    AAVertex wLines[verticesCount];
1924    AAVertex* aaVertices = &wLines[0];
1925
1926    if (CC_UNLIKELY(!isAA)) {
1927        setupDrawVertices(vertices);
1928    } else {
1929        void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
1930        void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
1931        // innerProportion is the ratio of the inner (non-AA) part of the line to the total
1932        // AA stroke width (the base stroke width expanded by a half pixel on either side).
1933        // This value is used in the fragment shader to determine how to fill fragments.
1934        // We will need to calculate the actual width proportion on each segment for
1935        // scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
1936        float boundaryWidthProportion = .5 - 1 / (2 * halfStrokeWidth);
1937        setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
1938                boundaryWidthProportion, widthSlot, lengthSlot);
1939    }
1940
1941    AAVertex* prevAAVertex = NULL;
1942    Vertex* prevVertex = NULL;
1943
1944    int boundaryLengthSlot = -1;
1945    int boundaryWidthSlot = -1;
1946
1947    for (int i = 0; i < count; i += 4) {
1948        // a = start point, b = end point
1949        vec2 a(points[i], points[i + 1]);
1950        vec2 b(points[i + 2], points[i + 3]);
1951
1952        float length = 0;
1953        float boundaryLengthProportion = 0;
1954        float boundaryWidthProportion = 0;
1955
1956        // Find the normal to the line
1957        vec2 n = (b - a).copyNormalized() * halfStrokeWidth;
1958        if (isHairLine) {
1959            if (isAA) {
1960                float wideningFactor;
1961                if (fabs(n.x) >= fabs(n.y)) {
1962                    wideningFactor = fabs(1.0f / n.x);
1963                } else {
1964                    wideningFactor = fabs(1.0f / n.y);
1965                }
1966                n *= wideningFactor;
1967            }
1968
1969            if (scaled) {
1970                n.x *= inverseScaleX;
1971                n.y *= inverseScaleY;
1972            }
1973        } else if (scaled) {
1974            // Extend n by .5 pixel on each side, post-transform
1975            vec2 extendedN = n.copyNormalized();
1976            extendedN /= 2;
1977            extendedN.x *= inverseScaleX;
1978            extendedN.y *= inverseScaleY;
1979
1980            float extendedNLength = extendedN.length();
1981            // We need to set this value on the shader prior to drawing
1982            boundaryWidthProportion = extendedNLength / (halfStrokeWidth + extendedNLength);
1983            n += extendedN;
1984        }
1985
1986        float x = n.x;
1987        n.x = -n.y;
1988        n.y = x;
1989
1990        // aa lines expand the endpoint vertices to encompass the AA boundary
1991        if (isAA) {
1992            vec2 abVector = (b - a);
1993            length = abVector.length();
1994            abVector.normalize();
1995
1996            if (scaled) {
1997                abVector.x *= inverseScaleX;
1998                abVector.y *= inverseScaleY;
1999                float abLength = abVector.length();
2000                boundaryLengthProportion = .5 - abLength / (length + abLength);
2001            } else {
2002                boundaryLengthProportion = .5 - .5 / (length + 1);
2003            }
2004
2005            abVector /= 2;
2006            a -= abVector;
2007            b += abVector;
2008        }
2009
2010        // Four corners of the rectangle defining a thick line
2011        vec2 p1 = a - n;
2012        vec2 p2 = a + n;
2013        vec2 p3 = b + n;
2014        vec2 p4 = b - n;
2015
2016
2017        const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x)));
2018        const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x)));
2019        const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y)));
2020        const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y)));
2021
2022        if (!quickRejectNoScissor(left, top, right, bottom)) {
2023            if (!isAA) {
2024                if (prevVertex != NULL) {
2025                    // Issue two repeat vertices to create degenerate triangles to bridge
2026                    // between the previous line and the new one. This is necessary because
2027                    // we are creating a single triangle_strip which will contain
2028                    // potentially discontinuous line segments.
2029                    Vertex::set(vertices++, prevVertex->position[0], prevVertex->position[1]);
2030                    Vertex::set(vertices++, p1.x, p1.y);
2031                    generatedVerticesCount += 2;
2032                }
2033
2034                Vertex::set(vertices++, p1.x, p1.y);
2035                Vertex::set(vertices++, p2.x, p2.y);
2036                Vertex::set(vertices++, p4.x, p4.y);
2037                Vertex::set(vertices++, p3.x, p3.y);
2038
2039                prevVertex = vertices - 1;
2040                generatedVerticesCount += 4;
2041            } else {
2042                if (!isHairLine && scaled) {
2043                    // Must set width proportions per-segment for scaled non-hairlines to use the
2044                    // correct AA boundary dimensions
2045                    if (boundaryWidthSlot < 0) {
2046                        boundaryWidthSlot =
2047                                mCaches.currentProgram->getUniform("boundaryWidth");
2048                    }
2049
2050                    glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
2051                }
2052
2053                if (boundaryLengthSlot < 0) {
2054                    boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
2055                }
2056
2057                glUniform1f(boundaryLengthSlot, boundaryLengthProportion);
2058
2059                if (prevAAVertex != NULL) {
2060                    // Issue two repeat vertices to create degenerate triangles to bridge
2061                    // between the previous line and the new one. This is necessary because
2062                    // we are creating a single triangle_strip which will contain
2063                    // potentially discontinuous line segments.
2064                    AAVertex::set(aaVertices++,prevAAVertex->position[0],
2065                            prevAAVertex->position[1], prevAAVertex->width, prevAAVertex->length);
2066                    AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
2067                    generatedVerticesCount += 2;
2068                }
2069
2070                AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
2071                AAVertex::set(aaVertices++, p1.x, p1.y, 1, 0);
2072                AAVertex::set(aaVertices++, p3.x, p3.y, 0, 1);
2073                AAVertex::set(aaVertices++, p2.x, p2.y, 0, 0);
2074
2075                prevAAVertex = aaVertices - 1;
2076                generatedVerticesCount += 4;
2077            }
2078
2079            dirtyLayer(a.x == b.x ? left - 1 : left, a.y == b.y ? top - 1 : top,
2080                    a.x == b.x ? right: right, a.y == b.y ? bottom: bottom,
2081                    *mSnapshot->transform);
2082        }
2083    }
2084
2085    if (generatedVerticesCount > 0) {
2086       glDrawArrays(GL_TRIANGLE_STRIP, 0, generatedVerticesCount);
2087    }
2088
2089    if (isAA) {
2090        finishDrawAALine(widthSlot, lengthSlot);
2091    }
2092
2093    return DrawGlInfo::kStatusDrew;
2094}
2095
2096status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
2097    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
2098
2099    // TODO: The paint's cap style defines whether the points are square or circular
2100    // TODO: Handle AA for round points
2101
2102    // A stroke width of 0 has a special meaning in Skia:
2103    // it draws an unscaled 1px point
2104    float strokeWidth = paint->getStrokeWidth();
2105    const bool isHairLine = paint->getStrokeWidth() == 0.0f;
2106    if (isHairLine) {
2107        // Now that we know it's hairline, we can set the effective width, to be used later
2108        strokeWidth = 1.0f;
2109    }
2110    const float halfWidth = strokeWidth / 2;
2111    int alpha;
2112    SkXfermode::Mode mode;
2113    getAlphaAndMode(paint, &alpha, &mode);
2114
2115    int verticesCount = count >> 1;
2116    int generatedVerticesCount = 0;
2117
2118    TextureVertex pointsData[verticesCount];
2119    TextureVertex* vertex = &pointsData[0];
2120
2121    // TODO: We should optimize this method to not generate vertices for points
2122    // that lie outside of the clip.
2123    mCaches.enableScissor();
2124
2125    setupDraw();
2126    setupDrawNoTexture();
2127    setupDrawPoint(strokeWidth);
2128    setupDrawColor(paint->getColor(), alpha);
2129    setupDrawColorFilter();
2130    setupDrawShader();
2131    setupDrawBlending(mode);
2132    setupDrawProgram();
2133    setupDrawModelViewIdentity(true);
2134    setupDrawColorUniforms();
2135    setupDrawColorFilterUniforms();
2136    setupDrawPointUniforms();
2137    setupDrawShaderIdentityUniforms();
2138    setupDrawMesh(vertex);
2139
2140    for (int i = 0; i < count; i += 2) {
2141        TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
2142        generatedVerticesCount++;
2143
2144        float left = points[i] - halfWidth;
2145        float right = points[i] + halfWidth;
2146        float top = points[i + 1] - halfWidth;
2147        float bottom = points [i + 1] + halfWidth;
2148
2149        dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
2150    }
2151
2152    glDrawArrays(GL_POINTS, 0, generatedVerticesCount);
2153
2154    return DrawGlInfo::kStatusDrew;
2155}
2156
2157status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
2158    // No need to check against the clip, we fill the clip region
2159    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
2160
2161    Rect& clip(*mSnapshot->clipRect);
2162    clip.snapToPixelBoundaries();
2163
2164    drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
2165
2166    return DrawGlInfo::kStatusDrew;
2167}
2168
2169status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* texture,
2170        SkPaint* paint) {
2171    if (!texture) return DrawGlInfo::kStatusDone;
2172    const AutoTexture autoCleanup(texture);
2173
2174    const float x = left + texture->left - texture->offset;
2175    const float y = top + texture->top - texture->offset;
2176
2177    drawPathTexture(texture, x, y, paint);
2178
2179    return DrawGlInfo::kStatusDrew;
2180}
2181
2182status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
2183        float rx, float ry, SkPaint* paint) {
2184    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
2185
2186    mCaches.activeTexture(0);
2187    const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect(
2188            right - left, bottom - top, rx, ry, paint);
2189    return drawShape(left, top, texture, paint);
2190}
2191
2192status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
2193    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
2194
2195    mCaches.activeTexture(0);
2196    const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, paint);
2197    return drawShape(x - radius, y - radius, texture, paint);
2198}
2199
2200status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
2201        SkPaint* paint) {
2202    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
2203
2204    mCaches.activeTexture(0);
2205    const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, paint);
2206    return drawShape(left, top, texture, paint);
2207}
2208
2209status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
2210        float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
2211    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
2212
2213    if (fabs(sweepAngle) >= 360.0f) {
2214        return drawOval(left, top, right, bottom, paint);
2215    }
2216
2217    mCaches.activeTexture(0);
2218    const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top,
2219            startAngle, sweepAngle, useCenter, paint);
2220    return drawShape(left, top, texture, paint);
2221}
2222
2223status_t OpenGLRenderer::drawRectAsShape(float left, float top, float right, float bottom,
2224        SkPaint* paint) {
2225    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
2226
2227    mCaches.activeTexture(0);
2228    const PathTexture* texture = mCaches.rectShapeCache.getRect(right - left, bottom - top, paint);
2229    return drawShape(left, top, texture, paint);
2230}
2231
2232status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) {
2233    if (p->getStyle() != SkPaint::kFill_Style) {
2234        return drawRectAsShape(left, top, right, bottom, p);
2235    }
2236
2237    if (quickReject(left, top, right, bottom)) {
2238        return DrawGlInfo::kStatusDone;
2239    }
2240
2241    SkXfermode::Mode mode;
2242    if (!mCaches.extensions.hasFramebufferFetch()) {
2243        const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
2244        if (!isMode) {
2245            // Assume SRC_OVER
2246            mode = SkXfermode::kSrcOver_Mode;
2247        }
2248    } else {
2249        mode = getXfermode(p->getXfermode());
2250    }
2251
2252    int color = p->getColor();
2253    if (p->isAntiAlias() && !mSnapshot->transform->isSimple()) {
2254        drawAARect(left, top, right, bottom, color, mode);
2255    } else {
2256        drawColorRect(left, top, right, bottom, color, mode);
2257    }
2258
2259    return DrawGlInfo::kStatusDrew;
2260}
2261
2262void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
2263        const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
2264        float x, float y) {
2265    mCaches.activeTexture(0);
2266
2267    // NOTE: The drop shadow will not perform gamma correction
2268    //       if shader-based correction is enabled
2269    mCaches.dropShadowCache.setFontRenderer(fontRenderer);
2270    const ShadowTexture* shadow = mCaches.dropShadowCache.get(
2271            paint, text, bytesCount, count, mShadowRadius, positions);
2272    const AutoTexture autoCleanup(shadow);
2273
2274    const float sx = x - shadow->left + mShadowDx;
2275    const float sy = y - shadow->top + mShadowDy;
2276
2277    const int shadowAlpha = ((mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
2278    int shadowColor = mShadowColor;
2279    if (mShader) {
2280        shadowColor = 0xffffffff;
2281    }
2282
2283    setupDraw();
2284    setupDrawWithTexture(true);
2285    setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
2286    setupDrawColorFilter();
2287    setupDrawShader();
2288    setupDrawBlending(true, mode);
2289    setupDrawProgram();
2290    setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height);
2291    setupDrawTexture(shadow->id);
2292    setupDrawPureColorUniforms();
2293    setupDrawColorFilterUniforms();
2294    setupDrawShaderUniforms();
2295    setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
2296
2297    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
2298}
2299
2300status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
2301        const float* positions, SkPaint* paint) {
2302    if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
2303            (paint->getAlpha() * mSnapshot->alpha == 0 && paint->getXfermode() == NULL)) {
2304        return DrawGlInfo::kStatusDone;
2305    }
2306
2307    // NOTE: Skia does not support perspective transform on drawPosText yet
2308    if (!mSnapshot->transform->isSimple()) {
2309        return DrawGlInfo::kStatusDone;
2310    }
2311
2312    float x = 0.0f;
2313    float y = 0.0f;
2314    const bool pureTranslate = mSnapshot->transform->isPureTranslate();
2315    if (pureTranslate) {
2316        x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
2317        y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
2318    }
2319
2320    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2321    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
2322            paint->getTextSize());
2323
2324    int alpha;
2325    SkXfermode::Mode mode;
2326    getAlphaAndMode(paint, &alpha, &mode);
2327
2328    if (CC_UNLIKELY(mHasShadow)) {
2329        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
2330                0.0f, 0.0f);
2331    }
2332
2333    // Pick the appropriate texture filtering
2334    bool linearFilter = mSnapshot->transform->changesBounds();
2335    if (pureTranslate && !linearFilter) {
2336        linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
2337    }
2338
2339    mCaches.activeTexture(0);
2340    setupDraw();
2341    setupDrawTextGamma(paint);
2342    setupDrawDirtyRegionsDisabled();
2343    setupDrawWithTexture(true);
2344    setupDrawAlpha8Color(paint->getColor(), alpha);
2345    setupDrawColorFilter();
2346    setupDrawShader();
2347    setupDrawBlending(true, mode);
2348    setupDrawProgram();
2349    setupDrawModelView(x, y, x, y, pureTranslate, true);
2350    setupDrawTexture(fontRenderer.getTexture(linearFilter));
2351    setupDrawPureColorUniforms();
2352    setupDrawColorFilterUniforms();
2353    setupDrawShaderUniforms(pureTranslate);
2354    setupDrawTextGammaUniforms();
2355
2356    const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip();
2357    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
2358
2359    const bool hasActiveLayer = hasLayer();
2360
2361    if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
2362            positions, hasActiveLayer ? &bounds : NULL)) {
2363        if (hasActiveLayer) {
2364            if (!pureTranslate) {
2365                mSnapshot->transform->mapRect(bounds);
2366            }
2367            dirtyLayerUnchecked(bounds, getRegion());
2368        }
2369    }
2370
2371    return DrawGlInfo::kStatusDrew;
2372}
2373
2374status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
2375        float x, float y, const float* positions, SkPaint* paint, float length) {
2376    if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
2377            (paint->getAlpha() * mSnapshot->alpha == 0 && paint->getXfermode() == NULL)) {
2378        return DrawGlInfo::kStatusDone;
2379    }
2380
2381    if (length < 0.0f) length = paint->measureText(text, bytesCount);
2382    switch (paint->getTextAlign()) {
2383        case SkPaint::kCenter_Align:
2384            x -= length / 2.0f;
2385            break;
2386        case SkPaint::kRight_Align:
2387            x -= length;
2388            break;
2389        default:
2390            break;
2391    }
2392
2393    SkPaint::FontMetrics metrics;
2394    paint->getFontMetrics(&metrics, 0.0f);
2395    if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
2396        return DrawGlInfo::kStatusDone;
2397    }
2398
2399    const float oldX = x;
2400    const float oldY = y;
2401    const bool pureTranslate = mSnapshot->transform->isPureTranslate();
2402    if (CC_LIKELY(pureTranslate)) {
2403        x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
2404        y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
2405    }
2406
2407#if DEBUG_GLYPHS
2408    ALOGD("OpenGLRenderer drawText() with FontID=%d",
2409            SkTypeface::UniqueID(paint->getTypeface()));
2410#endif
2411
2412    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2413    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
2414            paint->getTextSize());
2415
2416    int alpha;
2417    SkXfermode::Mode mode;
2418    getAlphaAndMode(paint, &alpha, &mode);
2419
2420    if (CC_UNLIKELY(mHasShadow)) {
2421        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
2422                oldX, oldY);
2423    }
2424
2425    // Pick the appropriate texture filtering
2426    bool linearFilter = mSnapshot->transform->changesBounds();
2427    if (pureTranslate && !linearFilter) {
2428        linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
2429    }
2430
2431    // The font renderer will always use texture unit 0
2432    mCaches.activeTexture(0);
2433    setupDraw();
2434    setupDrawTextGamma(paint);
2435    setupDrawDirtyRegionsDisabled();
2436    setupDrawWithTexture(true);
2437    setupDrawAlpha8Color(paint->getColor(), alpha);
2438    setupDrawColorFilter();
2439    setupDrawShader();
2440    setupDrawBlending(true, mode);
2441    setupDrawProgram();
2442    setupDrawModelView(x, y, x, y, pureTranslate, true);
2443    // See comment above; the font renderer must use texture unit 0
2444    // assert(mTextureUnit == 0)
2445    setupDrawTexture(fontRenderer.getTexture(linearFilter));
2446    setupDrawPureColorUniforms();
2447    setupDrawColorFilterUniforms();
2448    setupDrawShaderUniforms(pureTranslate);
2449    setupDrawTextGammaUniforms();
2450
2451    const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip();
2452    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
2453
2454    const bool hasActiveLayer = hasLayer();
2455
2456    bool status;
2457    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
2458        SkPaint paintCopy(*paint);
2459        paintCopy.setTextAlign(SkPaint::kLeft_Align);
2460        status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
2461            positions, hasActiveLayer ? &bounds : NULL);
2462    } else {
2463        status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
2464            positions, hasActiveLayer ? &bounds : NULL);
2465    }
2466
2467    if (status && hasActiveLayer) {
2468        if (!pureTranslate) {
2469            mSnapshot->transform->mapRect(bounds);
2470        }
2471        dirtyLayerUnchecked(bounds, getRegion());
2472    }
2473
2474    drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
2475
2476    return DrawGlInfo::kStatusDrew;
2477}
2478
2479status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
2480        float hOffset, float vOffset, SkPaint* paint) {
2481    if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
2482            (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
2483        return DrawGlInfo::kStatusDone;
2484    }
2485
2486    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2487    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
2488            paint->getTextSize());
2489
2490    int alpha;
2491    SkXfermode::Mode mode;
2492    getAlphaAndMode(paint, &alpha, &mode);
2493
2494    mCaches.activeTexture(0);
2495    setupDraw();
2496    setupDrawTextGamma(paint);
2497    setupDrawDirtyRegionsDisabled();
2498    setupDrawWithTexture(true);
2499    setupDrawAlpha8Color(paint->getColor(), alpha);
2500    setupDrawColorFilter();
2501    setupDrawShader();
2502    setupDrawBlending(true, mode);
2503    setupDrawProgram();
2504    setupDrawModelView(0.0f, 0.0f, 0.0f, 0.0f, false, true);
2505    setupDrawTexture(fontRenderer.getTexture(true));
2506    setupDrawPureColorUniforms();
2507    setupDrawColorFilterUniforms();
2508    setupDrawShaderUniforms(false);
2509    setupDrawTextGammaUniforms();
2510
2511    const Rect* clip = &mSnapshot->getLocalClip();
2512    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
2513
2514    const bool hasActiveLayer = hasLayer();
2515
2516    if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
2517            hOffset, vOffset, hasActiveLayer ? &bounds : NULL)) {
2518        if (hasActiveLayer) {
2519            mSnapshot->transform->mapRect(bounds);
2520            dirtyLayerUnchecked(bounds, getRegion());
2521        }
2522    }
2523
2524    return DrawGlInfo::kStatusDrew;
2525}
2526
2527status_t OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
2528    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
2529
2530    mCaches.activeTexture(0);
2531
2532    // TODO: Perform early clip test before we rasterize the path
2533    const PathTexture* texture = mCaches.pathCache.get(path, paint);
2534    if (!texture) return DrawGlInfo::kStatusDone;
2535    const AutoTexture autoCleanup(texture);
2536
2537    const float x = texture->left - texture->offset;
2538    const float y = texture->top - texture->offset;
2539
2540    drawPathTexture(texture, x, y, paint);
2541
2542    return DrawGlInfo::kStatusDrew;
2543}
2544
2545status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) {
2546    if (!layer || quickReject(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight())) {
2547        return DrawGlInfo::kStatusDone;
2548    }
2549
2550    bool debugLayerUpdate = false;
2551
2552    if (layer->deferredUpdateScheduled && layer->renderer && layer->displayList) {
2553        OpenGLRenderer* renderer = layer->renderer;
2554        Rect& dirty = layer->dirtyRect;
2555
2556        interrupt();
2557        renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
2558        renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, !layer->isBlend());
2559        renderer->drawDisplayList(layer->displayList, dirty, DisplayList::kReplayFlag_ClipChildren);
2560        renderer->finish();
2561        resume();
2562
2563        dirty.setEmpty();
2564        layer->deferredUpdateScheduled = false;
2565        layer->renderer = NULL;
2566        layer->displayList = NULL;
2567
2568        debugLayerUpdate = mCaches.debugLayersUpdates;
2569    }
2570
2571    mCaches.activeTexture(0);
2572
2573    int alpha;
2574    SkXfermode::Mode mode;
2575    getAlphaAndMode(paint, &alpha, &mode);
2576
2577    layer->setAlpha(alpha, mode);
2578
2579    if (CC_LIKELY(!layer->region.isEmpty())) {
2580        if (layer->region.isRect()) {
2581            composeLayerRect(layer, layer->regionRect);
2582        } else if (layer->mesh) {
2583            const float a = alpha / 255.0f;
2584
2585            setupDraw();
2586            setupDrawWithTexture();
2587            setupDrawColor(a, a, a, a);
2588            setupDrawColorFilter();
2589            setupDrawBlending(layer->isBlend() || a < 1.0f, layer->getMode(), false);
2590            setupDrawProgram();
2591            setupDrawPureColorUniforms();
2592            setupDrawColorFilterUniforms();
2593            setupDrawTexture(layer->getTexture());
2594            if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) {
2595                int tx = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
2596                int ty = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
2597
2598                layer->setFilter(GL_NEAREST);
2599                setupDrawModelViewTranslate(tx, ty,
2600                        tx + layer->layer.getWidth(), ty + layer->layer.getHeight(), true);
2601            } else {
2602                layer->setFilter(GL_LINEAR);
2603                setupDrawModelViewTranslate(x, y,
2604                        x + layer->layer.getWidth(), y + layer->layer.getHeight());
2605            }
2606            setupDrawMesh(&layer->mesh[0].position[0], &layer->mesh[0].texture[0]);
2607
2608            glDrawElements(GL_TRIANGLES, layer->meshElementCount,
2609                    GL_UNSIGNED_SHORT, layer->meshIndices);
2610
2611            finishDrawTexture();
2612
2613#if DEBUG_LAYERS_AS_REGIONS
2614            drawRegionRects(layer->region);
2615#endif
2616        }
2617
2618        if (debugLayerUpdate) {
2619            drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(),
2620                    0x7f00ff00, SkXfermode::kSrcOver_Mode);
2621        }
2622    }
2623
2624    return DrawGlInfo::kStatusDrew;
2625}
2626
2627///////////////////////////////////////////////////////////////////////////////
2628// Shaders
2629///////////////////////////////////////////////////////////////////////////////
2630
2631void OpenGLRenderer::resetShader() {
2632    mShader = NULL;
2633}
2634
2635void OpenGLRenderer::setupShader(SkiaShader* shader) {
2636    mShader = shader;
2637    if (mShader) {
2638        mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
2639    }
2640}
2641
2642///////////////////////////////////////////////////////////////////////////////
2643// Color filters
2644///////////////////////////////////////////////////////////////////////////////
2645
2646void OpenGLRenderer::resetColorFilter() {
2647    mColorFilter = NULL;
2648}
2649
2650void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) {
2651    mColorFilter = filter;
2652}
2653
2654///////////////////////////////////////////////////////////////////////////////
2655// Drop shadow
2656///////////////////////////////////////////////////////////////////////////////
2657
2658void OpenGLRenderer::resetShadow() {
2659    mHasShadow = false;
2660}
2661
2662void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
2663    mHasShadow = true;
2664    mShadowRadius = radius;
2665    mShadowDx = dx;
2666    mShadowDy = dy;
2667    mShadowColor = color;
2668}
2669
2670///////////////////////////////////////////////////////////////////////////////
2671// Draw filters
2672///////////////////////////////////////////////////////////////////////////////
2673
2674void OpenGLRenderer::resetPaintFilter() {
2675    mHasDrawFilter = false;
2676}
2677
2678void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
2679    mHasDrawFilter = true;
2680    mPaintFilterClearBits = clearBits & SkPaint::kAllFlags;
2681    mPaintFilterSetBits = setBits & SkPaint::kAllFlags;
2682}
2683
2684SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) {
2685    if (CC_LIKELY(!mHasDrawFilter || !paint)) return paint;
2686
2687    uint32_t flags = paint->getFlags();
2688
2689    mFilteredPaint = *paint;
2690    mFilteredPaint.setFlags((flags & ~mPaintFilterClearBits) | mPaintFilterSetBits);
2691
2692    return &mFilteredPaint;
2693}
2694
2695///////////////////////////////////////////////////////////////////////////////
2696// Drawing implementation
2697///////////////////////////////////////////////////////////////////////////////
2698
2699void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
2700        float x, float y, SkPaint* paint) {
2701    if (quickReject(x, y, x + texture->width, y + texture->height)) {
2702        return;
2703    }
2704
2705    int alpha;
2706    SkXfermode::Mode mode;
2707    getAlphaAndMode(paint, &alpha, &mode);
2708
2709    setupDraw();
2710    setupDrawWithTexture(true);
2711    setupDrawAlpha8Color(paint->getColor(), alpha);
2712    setupDrawColorFilter();
2713    setupDrawShader();
2714    setupDrawBlending(true, mode);
2715    setupDrawProgram();
2716    setupDrawModelView(x, y, x + texture->width, y + texture->height);
2717    setupDrawTexture(texture->id);
2718    setupDrawPureColorUniforms();
2719    setupDrawColorFilterUniforms();
2720    setupDrawShaderUniforms();
2721    setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
2722
2723    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
2724
2725    finishDrawTexture();
2726}
2727
2728// Same values used by Skia
2729#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
2730#define kStdUnderline_Offset    (1.0f / 9.0f)
2731#define kStdUnderline_Thickness (1.0f / 18.0f)
2732
2733void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float length,
2734        float x, float y, SkPaint* paint) {
2735    // Handle underline and strike-through
2736    uint32_t flags = paint->getFlags();
2737    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
2738        SkPaint paintCopy(*paint);
2739        float underlineWidth = length;
2740        // If length is > 0.0f, we already measured the text for the text alignment
2741        if (length <= 0.0f) {
2742            underlineWidth = paintCopy.measureText(text, bytesCount);
2743        }
2744
2745        if (CC_LIKELY(underlineWidth > 0.0f)) {
2746            const float textSize = paintCopy.getTextSize();
2747            const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
2748
2749            const float left = x;
2750            float top = 0.0f;
2751
2752            int linesCount = 0;
2753            if (flags & SkPaint::kUnderlineText_Flag) linesCount++;
2754            if (flags & SkPaint::kStrikeThruText_Flag) linesCount++;
2755
2756            const int pointsCount = 4 * linesCount;
2757            float points[pointsCount];
2758            int currentPoint = 0;
2759
2760            if (flags & SkPaint::kUnderlineText_Flag) {
2761                top = y + textSize * kStdUnderline_Offset;
2762                points[currentPoint++] = left;
2763                points[currentPoint++] = top;
2764                points[currentPoint++] = left + underlineWidth;
2765                points[currentPoint++] = top;
2766            }
2767
2768            if (flags & SkPaint::kStrikeThruText_Flag) {
2769                top = y + textSize * kStdStrikeThru_Offset;
2770                points[currentPoint++] = left;
2771                points[currentPoint++] = top;
2772                points[currentPoint++] = left + underlineWidth;
2773                points[currentPoint++] = top;
2774            }
2775
2776            paintCopy.setStrokeWidth(strokeWidth);
2777
2778            drawLines(&points[0], pointsCount, &paintCopy);
2779        }
2780    }
2781}
2782
2783void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
2784        int color, SkXfermode::Mode mode, bool ignoreTransform) {
2785    // If a shader is set, preserve only the alpha
2786    if (mShader) {
2787        color |= 0x00ffffff;
2788    }
2789
2790    setupDraw();
2791    setupDrawNoTexture();
2792    setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
2793    setupDrawShader();
2794    setupDrawColorFilter();
2795    setupDrawBlending(mode);
2796    setupDrawProgram();
2797    setupDrawModelView(left, top, right, bottom, ignoreTransform);
2798    setupDrawColorUniforms();
2799    setupDrawShaderUniforms(ignoreTransform);
2800    setupDrawColorFilterUniforms();
2801    setupDrawSimpleMesh();
2802
2803    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
2804}
2805
2806void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
2807        Texture* texture, SkPaint* paint) {
2808    int alpha;
2809    SkXfermode::Mode mode;
2810    getAlphaAndMode(paint, &alpha, &mode);
2811
2812    texture->setWrap(GL_CLAMP_TO_EDGE, true);
2813
2814    if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) {
2815        const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
2816        const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
2817
2818        texture->setFilter(GL_NEAREST, true);
2819        drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
2820                alpha / 255.0f, mode, texture->blend, (GLvoid*) NULL,
2821                (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, false, true);
2822    } else {
2823        texture->setFilter(FILTER(paint), true);
2824        drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
2825                texture->blend, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
2826                GL_TRIANGLE_STRIP, gMeshCount);
2827    }
2828}
2829
2830void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
2831        GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) {
2832    drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend,
2833            (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount);
2834}
2835
2836void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
2837        GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
2838        GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
2839        bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) {
2840
2841    setupDraw();
2842    setupDrawWithTexture();
2843    setupDrawColor(alpha, alpha, alpha, alpha);
2844    setupDrawColorFilter();
2845    setupDrawBlending(blend, mode, swapSrcDst);
2846    setupDrawProgram();
2847    if (!dirty) {
2848        setupDrawDirtyRegionsDisabled();
2849    }
2850    if (!ignoreScale) {
2851        setupDrawModelView(left, top, right, bottom, ignoreTransform);
2852    } else {
2853        setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
2854    }
2855    setupDrawPureColorUniforms();
2856    setupDrawColorFilterUniforms();
2857    setupDrawTexture(texture);
2858    setupDrawMesh(vertices, texCoords, vbo);
2859
2860    glDrawArrays(drawMode, 0, elementsCount);
2861
2862    finishDrawTexture();
2863}
2864
2865void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
2866        ProgramDescription& description, bool swapSrcDst) {
2867    blend = blend || mode != SkXfermode::kSrcOver_Mode;
2868
2869    if (blend) {
2870        // These blend modes are not supported by OpenGL directly and have
2871        // to be implemented using shaders. Since the shader will perform
2872        // the blending, turn blending off here
2873        // If the blend mode cannot be implemented using shaders, fall
2874        // back to the default SrcOver blend mode instead
2875        if (CC_UNLIKELY(mode > SkXfermode::kScreen_Mode)) {
2876            if (CC_UNLIKELY(mCaches.extensions.hasFramebufferFetch())) {
2877                description.framebufferMode = mode;
2878                description.swapSrcDst = swapSrcDst;
2879
2880                if (mCaches.blend) {
2881                    glDisable(GL_BLEND);
2882                    mCaches.blend = false;
2883                }
2884
2885                return;
2886            } else {
2887                mode = SkXfermode::kSrcOver_Mode;
2888            }
2889        }
2890
2891        if (!mCaches.blend) {
2892            glEnable(GL_BLEND);
2893        }
2894
2895        GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src;
2896        GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst;
2897
2898        if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
2899            glBlendFunc(sourceMode, destMode);
2900            mCaches.lastSrcMode = sourceMode;
2901            mCaches.lastDstMode = destMode;
2902        }
2903    } else if (mCaches.blend) {
2904        glDisable(GL_BLEND);
2905    }
2906    mCaches.blend = blend;
2907}
2908
2909bool OpenGLRenderer::useProgram(Program* program) {
2910    if (!program->isInUse()) {
2911        if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove();
2912        program->use();
2913        mCaches.currentProgram = program;
2914        return false;
2915    }
2916    return true;
2917}
2918
2919void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
2920    TextureVertex* v = &mMeshVertices[0];
2921    TextureVertex::setUV(v++, u1, v1);
2922    TextureVertex::setUV(v++, u2, v1);
2923    TextureVertex::setUV(v++, u1, v2);
2924    TextureVertex::setUV(v++, u2, v2);
2925}
2926
2927void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
2928    getAlphaAndModeDirect(paint, alpha,  mode);
2929    *alpha *= mSnapshot->alpha;
2930}
2931
2932}; // namespace uirenderer
2933}; // namespace android
2934