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