OpenGLRenderer.cpp revision b458942bb6e6cf13c68341dda35ef5cee060f5ae
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
19#include <stdlib.h>
20#include <stdint.h>
21#include <sys/types.h>
22
23#include <SkCanvas.h>
24#include <SkTypeface.h>
25
26#include <utils/Log.h>
27#include <utils/StopWatch.h>
28
29#include <private/hwui/DrawGlInfo.h>
30
31#include <ui/Rect.h>
32
33#include "OpenGLRenderer.h"
34#include "DeferredDisplayList.h"
35#include "DisplayListRenderer.h"
36#include "Fence.h"
37#include "PathTessellator.h"
38#include "Properties.h"
39#include "ShadowTessellator.h"
40#include "Vector.h"
41#include "VertexBuffer.h"
42
43namespace android {
44namespace uirenderer {
45
46///////////////////////////////////////////////////////////////////////////////
47// Defines
48///////////////////////////////////////////////////////////////////////////////
49
50#define RAD_TO_DEG (180.0f / 3.14159265f)
51#define MIN_ANGLE 0.001f
52
53#define ALPHA_THRESHOLD 0
54
55#define FILTER(paint) (!paint || paint->isFilterBitmap() ? GL_LINEAR : GL_NEAREST)
56
57///////////////////////////////////////////////////////////////////////////////
58// Globals
59///////////////////////////////////////////////////////////////////////////////
60
61/**
62 * Structure mapping Skia xfermodes to OpenGL blending factors.
63 */
64struct Blender {
65    SkXfermode::Mode mode;
66    GLenum src;
67    GLenum dst;
68}; // struct Blender
69
70// In this array, the index of each Blender equals the value of the first
71// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
72static const Blender gBlends[] = {
73    { SkXfermode::kClear_Mode,    GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
74    { SkXfermode::kSrc_Mode,      GL_ONE,                 GL_ZERO },
75    { SkXfermode::kDst_Mode,      GL_ZERO,                GL_ONE },
76    { SkXfermode::kSrcOver_Mode,  GL_ONE,                 GL_ONE_MINUS_SRC_ALPHA },
77    { SkXfermode::kDstOver_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_ONE },
78    { SkXfermode::kSrcIn_Mode,    GL_DST_ALPHA,           GL_ZERO },
79    { SkXfermode::kDstIn_Mode,    GL_ZERO,                GL_SRC_ALPHA },
80    { SkXfermode::kSrcOut_Mode,   GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
81    { SkXfermode::kDstOut_Mode,   GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
82    { SkXfermode::kSrcATop_Mode,  GL_DST_ALPHA,           GL_ONE_MINUS_SRC_ALPHA },
83    { SkXfermode::kDstATop_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
84    { SkXfermode::kXor_Mode,      GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
85    { SkXfermode::kPlus_Mode,     GL_ONE,                 GL_ONE },
86    { SkXfermode::kModulate_Mode, GL_ZERO,                GL_SRC_COLOR },
87    { SkXfermode::kScreen_Mode,   GL_ONE,                 GL_ONE_MINUS_SRC_COLOR }
88};
89
90// This array contains the swapped version of each SkXfermode. For instance
91// this array's SrcOver blending mode is actually DstOver. You can refer to
92// createLayer() for more information on the purpose of this array.
93static const Blender gBlendsSwap[] = {
94    { SkXfermode::kClear_Mode,    GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
95    { SkXfermode::kSrc_Mode,      GL_ZERO,                GL_ONE },
96    { SkXfermode::kDst_Mode,      GL_ONE,                 GL_ZERO },
97    { SkXfermode::kSrcOver_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_ONE },
98    { SkXfermode::kDstOver_Mode,  GL_ONE,                 GL_ONE_MINUS_SRC_ALPHA },
99    { SkXfermode::kSrcIn_Mode,    GL_ZERO,                GL_SRC_ALPHA },
100    { SkXfermode::kDstIn_Mode,    GL_DST_ALPHA,           GL_ZERO },
101    { SkXfermode::kSrcOut_Mode,   GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
102    { SkXfermode::kDstOut_Mode,   GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
103    { SkXfermode::kSrcATop_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
104    { SkXfermode::kDstATop_Mode,  GL_DST_ALPHA,           GL_ONE_MINUS_SRC_ALPHA },
105    { SkXfermode::kXor_Mode,      GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
106    { SkXfermode::kPlus_Mode,     GL_ONE,                 GL_ONE },
107    { SkXfermode::kModulate_Mode, GL_DST_COLOR,           GL_ZERO },
108    { SkXfermode::kScreen_Mode,   GL_ONE_MINUS_DST_COLOR, GL_ONE }
109};
110
111///////////////////////////////////////////////////////////////////////////////
112// Functions
113///////////////////////////////////////////////////////////////////////////////
114
115template<typename T>
116static inline T min(T a, T b) {
117    return a < b ? a : b;
118}
119
120///////////////////////////////////////////////////////////////////////////////
121// Constructors/destructor
122///////////////////////////////////////////////////////////////////////////////
123
124OpenGLRenderer::OpenGLRenderer():
125        mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
126    // *set* draw modifiers to be 0
127    memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
128    mDrawModifiers.mOverrideLayerAlpha = 1.0f;
129
130    memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
131
132    mFirstSnapshot = new Snapshot;
133    mFrameStarted = false;
134    mCountOverdraw = false;
135
136    mScissorOptimizationDisabled = false;
137}
138
139OpenGLRenderer::~OpenGLRenderer() {
140    // The context has already been destroyed at this point, do not call
141    // GL APIs. All GL state should be kept in Caches.h
142}
143
144void OpenGLRenderer::initProperties() {
145    char property[PROPERTY_VALUE_MAX];
146    if (property_get(PROPERTY_DISABLE_SCISSOR_OPTIMIZATION, property, "false")) {
147        mScissorOptimizationDisabled = !strcasecmp(property, "true");
148        INIT_LOGD("  Scissor optimization %s",
149                mScissorOptimizationDisabled ? "disabled" : "enabled");
150    } else {
151        INIT_LOGD("  Scissor optimization enabled");
152    }
153}
154
155///////////////////////////////////////////////////////////////////////////////
156// Setup
157///////////////////////////////////////////////////////////////////////////////
158
159void OpenGLRenderer::setViewport(int width, int height) {
160    initViewport(width, height);
161
162    glDisable(GL_DITHER);
163    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
164
165    glEnableVertexAttribArray(Program::kBindingPosition);
166}
167
168void OpenGLRenderer::initViewport(int width, int height) {
169    if (mCaches.propertyEnable3d) {
170        // TODO: make view proj app configurable
171        float dist = std::max(width, height) * 1.5;
172        dist *= mCaches.propertyCameraDistance;
173        Matrix4 projection;
174        projection.loadFrustum(-width / 2, -height / 2, width / 2, height / 2, dist, 0);
175        Matrix4 view;
176        view.loadLookAt(0, 0, dist,
177                0, 0, 0,
178                0, 1, 0);
179        mViewProjMatrix.loadMultiply(projection, view);
180        mViewProjMatrix.translate(-width/2, -height/2);
181    } else {
182        mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1);
183    }
184
185    mWidth = width;
186    mHeight = height;
187
188    mFirstSnapshot->height = height;
189    mFirstSnapshot->viewport.set(0, 0, width, height);
190}
191
192void OpenGLRenderer::setupFrameState(float left, float top,
193        float right, float bottom, bool opaque) {
194    mCaches.clearGarbage();
195
196    mOpaque = opaque;
197    mSnapshot = new Snapshot(mFirstSnapshot,
198            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
199    mSnapshot->fbo = getTargetFbo();
200    mSaveCount = 1;
201
202    mSnapshot->setClip(left, top, right, bottom);
203    mTilingClip.set(left, top, right, bottom);
204}
205
206status_t OpenGLRenderer::startFrame() {
207    if (mFrameStarted) return DrawGlInfo::kStatusDone;
208    mFrameStarted = true;
209
210    mDirtyClip = true;
211
212    discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
213
214    glViewport(0, 0, mWidth, mHeight);
215
216    // Functors break the tiling extension in pretty spectacular ways
217    // This ensures we don't use tiling when a functor is going to be
218    // invoked during the frame
219    mSuppressTiling = mCaches.hasRegisteredFunctors();
220
221    startTiling(mSnapshot, true);
222
223    debugOverdraw(true, true);
224
225    return clear(mTilingClip.left, mTilingClip.top,
226            mTilingClip.right, mTilingClip.bottom, mOpaque);
227}
228
229status_t OpenGLRenderer::prepare(bool opaque) {
230    return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
231}
232
233status_t OpenGLRenderer::prepareDirty(float left, float top,
234        float right, float bottom, bool opaque) {
235
236    setupFrameState(left, top, right, bottom, opaque);
237
238    // Layer renderers will start the frame immediately
239    // The framebuffer renderer will first defer the display list
240    // for each layer and wait until the first drawing command
241    // to start the frame
242    if (mSnapshot->fbo == 0) {
243        syncState();
244        updateLayers();
245    } else {
246        return startFrame();
247    }
248
249    return DrawGlInfo::kStatusDone;
250}
251
252void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) {
253    // If we know that we are going to redraw the entire framebuffer,
254    // perform a discard to let the driver know we don't need to preserve
255    // the back buffer for this frame.
256    if (mExtensions.hasDiscardFramebuffer() &&
257            left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) {
258        const bool isFbo = getTargetFbo() == 0;
259        const GLenum attachments[] = {
260                isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0,
261                isFbo ? (const GLenum) GL_STENCIL_EXT : (const GLenum) GL_STENCIL_ATTACHMENT };
262        glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
263    }
264}
265
266status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
267    if (!opaque || mCountOverdraw) {
268        mCaches.enableScissor();
269        mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
270        glClear(GL_COLOR_BUFFER_BIT);
271        return DrawGlInfo::kStatusDrew;
272    }
273
274    mCaches.resetScissor();
275    return DrawGlInfo::kStatusDone;
276}
277
278void OpenGLRenderer::syncState() {
279    if (mCaches.blend) {
280        glEnable(GL_BLEND);
281    } else {
282        glDisable(GL_BLEND);
283    }
284}
285
286void OpenGLRenderer::startTiling(const sp<Snapshot>& s, bool opaque) {
287    if (!mSuppressTiling) {
288        Rect* clip = &mTilingClip;
289        if (s->flags & Snapshot::kFlagFboTarget) {
290            clip = &(s->layer->clipRect);
291        }
292
293        startTiling(*clip, s->height, opaque);
294    }
295}
296
297void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque) {
298    if (!mSuppressTiling) {
299        mCaches.startTiling(clip.left, windowHeight - clip.bottom,
300                clip.right - clip.left, clip.bottom - clip.top, opaque);
301    }
302}
303
304void OpenGLRenderer::endTiling() {
305    if (!mSuppressTiling) mCaches.endTiling();
306}
307
308void OpenGLRenderer::finish() {
309    renderOverdraw();
310    endTiling();
311
312    // When finish() is invoked on FBO 0 we've reached the end
313    // of the current frame
314    if (getTargetFbo() == 0) {
315        mCaches.pathCache.trim();
316    }
317
318    if (!suppressErrorChecks()) {
319#if DEBUG_OPENGL
320        GLenum status = GL_NO_ERROR;
321        while ((status = glGetError()) != GL_NO_ERROR) {
322            ALOGD("GL error from OpenGLRenderer: 0x%x", status);
323            switch (status) {
324                case GL_INVALID_ENUM:
325                    ALOGE("  GL_INVALID_ENUM");
326                    break;
327                case GL_INVALID_VALUE:
328                    ALOGE("  GL_INVALID_VALUE");
329                    break;
330                case GL_INVALID_OPERATION:
331                    ALOGE("  GL_INVALID_OPERATION");
332                    break;
333                case GL_OUT_OF_MEMORY:
334                    ALOGE("  Out of memory!");
335                    break;
336            }
337        }
338#endif
339
340#if DEBUG_MEMORY_USAGE
341        mCaches.dumpMemoryUsage();
342#else
343        if (mCaches.getDebugLevel() & kDebugMemory) {
344            mCaches.dumpMemoryUsage();
345        }
346#endif
347    }
348
349    if (mCountOverdraw) {
350        countOverdraw();
351    }
352
353    mFrameStarted = false;
354}
355
356void OpenGLRenderer::interrupt() {
357    if (mCaches.currentProgram) {
358        if (mCaches.currentProgram->isInUse()) {
359            mCaches.currentProgram->remove();
360            mCaches.currentProgram = NULL;
361        }
362    }
363    mCaches.resetActiveTexture();
364    mCaches.unbindMeshBuffer();
365    mCaches.unbindIndicesBuffer();
366    mCaches.resetVertexPointers();
367    mCaches.disableTexCoordsVertexArray();
368    debugOverdraw(false, false);
369}
370
371void OpenGLRenderer::resume() {
372    sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
373    glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
374    glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
375    debugOverdraw(true, false);
376
377    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
378
379    mCaches.scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
380    mCaches.enableScissor();
381    mCaches.resetScissor();
382    dirtyClip();
383
384    mCaches.activeTexture(0);
385    mCaches.resetBoundTextures();
386
387    mCaches.blend = true;
388    glEnable(GL_BLEND);
389    glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
390    glBlendEquation(GL_FUNC_ADD);
391}
392
393void OpenGLRenderer::resumeAfterLayer() {
394    sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
395    glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
396    glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
397    debugOverdraw(true, false);
398
399    mCaches.resetScissor();
400    dirtyClip();
401}
402
403void OpenGLRenderer::detachFunctor(Functor* functor) {
404    mFunctors.remove(functor);
405}
406
407void OpenGLRenderer::attachFunctor(Functor* functor) {
408    mFunctors.add(functor);
409}
410
411status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
412    status_t result = DrawGlInfo::kStatusDone;
413    size_t count = mFunctors.size();
414
415    if (count > 0) {
416        interrupt();
417        SortedVector<Functor*> functors(mFunctors);
418        mFunctors.clear();
419
420        DrawGlInfo info;
421        info.clipLeft = 0;
422        info.clipTop = 0;
423        info.clipRight = 0;
424        info.clipBottom = 0;
425        info.isLayer = false;
426        info.width = 0;
427        info.height = 0;
428        memset(info.transform, 0, sizeof(float) * 16);
429
430        for (size_t i = 0; i < count; i++) {
431            Functor* f = functors.itemAt(i);
432            result |= (*f)(DrawGlInfo::kModeProcess, &info);
433
434            if (result & DrawGlInfo::kStatusDraw) {
435                Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
436                dirty.unionWith(localDirty);
437            }
438
439            if (result & DrawGlInfo::kStatusInvoke) {
440                mFunctors.add(f);
441            }
442        }
443        resume();
444    }
445
446    return result;
447}
448
449status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
450    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
451
452    detachFunctor(functor);
453
454
455    Rect clip(*mSnapshot->clipRect);
456    clip.snapToPixelBoundaries();
457
458    // Since we don't know what the functor will draw, let's dirty
459    // tne entire clip region
460    if (hasLayer()) {
461        dirtyLayerUnchecked(clip, getRegion());
462    }
463
464    DrawGlInfo info;
465    info.clipLeft = clip.left;
466    info.clipTop = clip.top;
467    info.clipRight = clip.right;
468    info.clipBottom = clip.bottom;
469    info.isLayer = hasLayer();
470    info.width = getSnapshot()->viewport.getWidth();
471    info.height = getSnapshot()->height;
472    getSnapshot()->transform->copyTo(&info.transform[0]);
473
474    bool dirtyClip = mDirtyClip;
475    // setup GL state for functor
476    if (mDirtyClip) {
477        setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
478    }
479    if (mCaches.enableScissor() || dirtyClip) {
480        setScissorFromClip();
481    }
482    interrupt();
483
484    // call functor immediately after GL state setup
485    status_t result = (*functor)(DrawGlInfo::kModeDraw, &info);
486
487    if (result != DrawGlInfo::kStatusDone) {
488        Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
489        dirty.unionWith(localDirty);
490
491        if (result & DrawGlInfo::kStatusInvoke) {
492            mFunctors.add(functor);
493        }
494    }
495
496    resume();
497    return result | DrawGlInfo::kStatusDrew;
498}
499
500///////////////////////////////////////////////////////////////////////////////
501// Debug
502///////////////////////////////////////////////////////////////////////////////
503
504void OpenGLRenderer::eventMark(const char* name) const {
505    mCaches.eventMark(0, name);
506}
507
508void OpenGLRenderer::startMark(const char* name) const {
509    mCaches.startMark(0, name);
510}
511
512void OpenGLRenderer::endMark() const {
513    mCaches.endMark();
514}
515
516void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
517    if (mCaches.debugOverdraw && getTargetFbo() == 0) {
518        if (clear) {
519            mCaches.disableScissor();
520            mCaches.stencil.clear();
521        }
522        if (enable) {
523            mCaches.stencil.enableDebugWrite();
524        } else {
525            mCaches.stencil.disable();
526        }
527    }
528}
529
530void OpenGLRenderer::renderOverdraw() {
531    if (mCaches.debugOverdraw && getTargetFbo() == 0) {
532        const Rect* clip = &mTilingClip;
533
534        mCaches.enableScissor();
535        mCaches.setScissor(clip->left, mFirstSnapshot->height - clip->bottom,
536                clip->right - clip->left, clip->bottom - clip->top);
537
538        // 1x overdraw
539        mCaches.stencil.enableDebugTest(2);
540        drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);
541
542        // 2x overdraw
543        mCaches.stencil.enableDebugTest(3);
544        drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);
545
546        // 3x overdraw
547        mCaches.stencil.enableDebugTest(4);
548        drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);
549
550        // 4x overdraw and higher
551        mCaches.stencil.enableDebugTest(4, true);
552        drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);
553
554        mCaches.stencil.disable();
555    }
556}
557
558void OpenGLRenderer::countOverdraw() {
559    size_t count = mWidth * mHeight;
560    uint32_t* buffer = new uint32_t[count];
561    glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]);
562
563    size_t total = 0;
564    for (size_t i = 0; i < count; i++) {
565        total += buffer[i] & 0xff;
566    }
567
568    mOverdraw = total / float(count);
569
570    delete[] buffer;
571}
572
573///////////////////////////////////////////////////////////////////////////////
574// Layers
575///////////////////////////////////////////////////////////////////////////////
576
577bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
578    if (layer->deferredUpdateScheduled && layer->renderer &&
579            layer->displayList && layer->displayList->isRenderable()) {
580        ATRACE_CALL();
581
582        Rect& dirty = layer->dirtyRect;
583
584        if (inFrame) {
585            endTiling();
586            debugOverdraw(false, false);
587        }
588
589        if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
590            layer->render();
591        } else {
592            layer->defer();
593        }
594
595        if (inFrame) {
596            resumeAfterLayer();
597            startTiling(mSnapshot);
598        }
599
600        layer->debugDrawUpdate = mCaches.debugLayersUpdates;
601        layer->hasDrawnSinceUpdate = false;
602
603        return true;
604    }
605
606    return false;
607}
608
609void OpenGLRenderer::updateLayers() {
610    // If draw deferring is enabled this method will simply defer
611    // the display list of each individual layer. The layers remain
612    // in the layer updates list which will be cleared by flushLayers().
613    int count = mLayerUpdates.size();
614    if (count > 0) {
615        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
616            startMark("Layer Updates");
617        } else {
618            startMark("Defer Layer Updates");
619        }
620
621        // Note: it is very important to update the layers in order
622        for (int i = 0; i < count; i++) {
623            Layer* layer = mLayerUpdates.itemAt(i);
624            updateLayer(layer, false);
625            if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
626                mCaches.resourceCache.decrementRefcount(layer);
627            }
628        }
629
630        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
631            mLayerUpdates.clear();
632            glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
633        }
634        endMark();
635    }
636}
637
638void OpenGLRenderer::flushLayers() {
639    int count = mLayerUpdates.size();
640    if (count > 0) {
641        startMark("Apply Layer Updates");
642        char layerName[12];
643
644        // Note: it is very important to update the layers in order
645        for (int i = 0; i < count; i++) {
646            sprintf(layerName, "Layer #%d", i);
647            startMark(layerName);
648
649            ATRACE_BEGIN("flushLayer");
650            Layer* layer = mLayerUpdates.itemAt(i);
651            layer->flush();
652            ATRACE_END();
653
654            mCaches.resourceCache.decrementRefcount(layer);
655
656            endMark();
657        }
658
659        mLayerUpdates.clear();
660        glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
661
662        endMark();
663    }
664}
665
666void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
667    if (layer) {
668        // Make sure we don't introduce duplicates.
669        // SortedVector would do this automatically but we need to respect
670        // the insertion order. The linear search is not an issue since
671        // this list is usually very short (typically one item, at most a few)
672        for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
673            if (mLayerUpdates.itemAt(i) == layer) {
674                return;
675            }
676        }
677        mLayerUpdates.push_back(layer);
678        mCaches.resourceCache.incrementRefcount(layer);
679    }
680}
681
682void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
683    if (layer) {
684        for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
685            if (mLayerUpdates.itemAt(i) == layer) {
686                mLayerUpdates.removeAt(i);
687                mCaches.resourceCache.decrementRefcount(layer);
688                break;
689            }
690        }
691    }
692}
693
694void OpenGLRenderer::clearLayerUpdates() {
695    size_t count = mLayerUpdates.size();
696    if (count > 0) {
697        mCaches.resourceCache.lock();
698        for (size_t i = 0; i < count; i++) {
699            mCaches.resourceCache.decrementRefcountLocked(mLayerUpdates.itemAt(i));
700        }
701        mCaches.resourceCache.unlock();
702        mLayerUpdates.clear();
703    }
704}
705
706void OpenGLRenderer::flushLayerUpdates() {
707    syncState();
708    updateLayers();
709    flushLayers();
710    // Wait for all the layer updates to be executed
711    AutoFence fence;
712}
713
714///////////////////////////////////////////////////////////////////////////////
715// State management
716///////////////////////////////////////////////////////////////////////////////
717
718int OpenGLRenderer::getSaveCount() const {
719    return mSaveCount;
720}
721
722int OpenGLRenderer::save(int flags) {
723    return saveSnapshot(flags);
724}
725
726void OpenGLRenderer::restore() {
727    if (mSaveCount > 1) {
728        restoreSnapshot();
729    }
730}
731
732void OpenGLRenderer::restoreToCount(int saveCount) {
733    if (saveCount < 1) saveCount = 1;
734
735    while (mSaveCount > saveCount) {
736        restoreSnapshot();
737    }
738}
739
740int OpenGLRenderer::saveSnapshot(int flags) {
741    mSnapshot = new Snapshot(mSnapshot, flags);
742    return mSaveCount++;
743}
744
745bool OpenGLRenderer::restoreSnapshot() {
746    bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet;
747    bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer;
748    bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho;
749
750    sp<Snapshot> current = mSnapshot;
751    sp<Snapshot> previous = mSnapshot->previous;
752
753    if (restoreOrtho) {
754        Rect& r = previous->viewport;
755        glViewport(r.left, r.top, r.right, r.bottom);
756        mViewProjMatrix.load(current->orthoMatrix);
757    }
758
759    mSaveCount--;
760    mSnapshot = previous;
761
762    if (restoreClip) {
763        dirtyClip();
764    }
765
766    if (restoreLayer) {
767        endMark(); // Savelayer
768        startMark("ComposeLayer");
769        composeLayer(current, previous);
770        endMark();
771    }
772
773    return restoreClip;
774}
775
776///////////////////////////////////////////////////////////////////////////////
777// Layers
778///////////////////////////////////////////////////////////////////////////////
779
780int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
781        int alpha, SkXfermode::Mode mode, int flags) {
782    const int count = saveSnapshot(flags);
783
784    if (!mSnapshot->isIgnored()) {
785        createLayer(left, top, right, bottom, alpha, mode, flags);
786    }
787
788    return count;
789}
790
791void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) {
792    const Rect untransformedBounds(bounds);
793
794    currentTransform().mapRect(bounds);
795
796    // Layers only make sense if they are in the framebuffer's bounds
797    if (bounds.intersect(*mSnapshot->clipRect)) {
798        // We cannot work with sub-pixels in this case
799        bounds.snapToPixelBoundaries();
800
801        // When the layer is not an FBO, we may use glCopyTexImage so we
802        // need to make sure the layer does not extend outside the bounds
803        // of the framebuffer
804        if (!bounds.intersect(mSnapshot->previous->viewport)) {
805            bounds.setEmpty();
806        } else if (fboLayer) {
807            clip.set(bounds);
808            mat4 inverse;
809            inverse.loadInverse(currentTransform());
810            inverse.mapRect(clip);
811            clip.snapToPixelBoundaries();
812            if (clip.intersect(untransformedBounds)) {
813                clip.translate(-untransformedBounds.left, -untransformedBounds.top);
814                bounds.set(untransformedBounds);
815            } else {
816                clip.setEmpty();
817            }
818        }
819    } else {
820        bounds.setEmpty();
821    }
822}
823
824void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip,
825        bool fboLayer, int alpha) {
826    if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
827            bounds.getHeight() > mCaches.maxTextureSize ||
828            (fboLayer && clip.isEmpty())) {
829        mSnapshot->empty = fboLayer;
830    } else {
831        mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
832    }
833}
834
835int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom,
836        int alpha, SkXfermode::Mode mode, int flags) {
837    const int count = saveSnapshot(flags);
838
839    if (!mSnapshot->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
840        // initialize the snapshot as though it almost represents an FBO layer so deferred draw
841        // operations will be able to store and restore the current clip and transform info, and
842        // quick rejection will be correct (for display lists)
843
844        Rect bounds(left, top, right, bottom);
845        Rect clip;
846        calculateLayerBoundsAndClip(bounds, clip, true);
847        updateSnapshotIgnoreForLayer(bounds, clip, true, alpha);
848
849        if (!mSnapshot->isIgnored()) {
850            mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
851            mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
852            mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
853        }
854    }
855
856    return count;
857}
858
859
860/**
861 * Layers are viewed by Skia are slightly different than layers in image editing
862 * programs (for instance.) When a layer is created, previously created layers
863 * and the frame buffer still receive every drawing command. For instance, if a
864 * layer is created and a shape intersecting the bounds of the layers and the
865 * framebuffer is draw, the shape will be drawn on both (unless the layer was
866 * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
867 *
868 * A way to implement layers is to create an FBO for each layer, backed by an RGBA
869 * texture. Unfortunately, this is inefficient as it requires every primitive to
870 * be drawn n + 1 times, where n is the number of active layers. In practice this
871 * means, for every primitive:
872 *   - Switch active frame buffer
873 *   - Change viewport, clip and projection matrix
874 *   - Issue the drawing
875 *
876 * Switching rendering target n + 1 times per drawn primitive is extremely costly.
877 * To avoid this, layers are implemented in a different way here, at least in the
878 * general case. FBOs are used, as an optimization, when the "clip to layer" flag
879 * is set. When this flag is set we can redirect all drawing operations into a
880 * single FBO.
881 *
882 * This implementation relies on the frame buffer being at least RGBA 8888. When
883 * a layer is created, only a texture is created, not an FBO. The content of the
884 * frame buffer contained within the layer's bounds is copied into this texture
885 * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
886 * buffer and drawing continues as normal. This technique therefore treats the
887 * frame buffer as a scratch buffer for the layers.
888 *
889 * To compose the layers back onto the frame buffer, each layer texture
890 * (containing the original frame buffer data) is drawn as a simple quad over
891 * the frame buffer. The trick is that the quad is set as the composition
892 * destination in the blending equation, and the frame buffer becomes the source
893 * of the composition.
894 *
895 * Drawing layers with an alpha value requires an extra step before composition.
896 * An empty quad is drawn over the layer's region in the frame buffer. This quad
897 * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the
898 * quad is used to multiply the colors in the frame buffer. This is achieved by
899 * changing the GL blend functions for the GL_FUNC_ADD blend equation to
900 * GL_ZERO, GL_SRC_ALPHA.
901 *
902 * Because glCopyTexImage2D() can be slow, an alternative implementation might
903 * be use to draw a single clipped layer. The implementation described above
904 * is correct in every case.
905 *
906 * (1) The frame buffer is actually not cleared right away. To allow the GPU
907 *     to potentially optimize series of calls to glCopyTexImage2D, the frame
908 *     buffer is left untouched until the first drawing operation. Only when
909 *     something actually gets drawn are the layers regions cleared.
910 */
911bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
912        int alpha, SkXfermode::Mode mode, int flags) {
913    LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
914    LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
915
916    const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
917
918    // Window coordinates of the layer
919    Rect clip;
920    Rect bounds(left, top, right, bottom);
921    calculateLayerBoundsAndClip(bounds, clip, fboLayer);
922    updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, alpha);
923
924    // Bail out if we won't draw in this snapshot
925    if (mSnapshot->isIgnored()) {
926        return false;
927    }
928
929    mCaches.activeTexture(0);
930    Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight());
931    if (!layer) {
932        return false;
933    }
934
935    layer->setAlpha(alpha, mode);
936    layer->layer.set(bounds);
937    layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()),
938            bounds.getWidth() / float(layer->getWidth()), 0.0f);
939    layer->setColorFilter(mDrawModifiers.mColorFilter);
940    layer->setBlend(true);
941    layer->setDirty(false);
942
943    // Save the layer in the snapshot
944    mSnapshot->flags |= Snapshot::kFlagIsLayer;
945    mSnapshot->layer = layer;
946
947    startMark("SaveLayer");
948    if (fboLayer) {
949        return createFboLayer(layer, bounds, clip);
950    } else {
951        // Copy the framebuffer into the layer
952        layer->bindTexture();
953        if (!bounds.isEmpty()) {
954            if (layer->isEmpty()) {
955                // Workaround for some GL drivers. When reading pixels lying outside
956                // of the window we should get undefined values for those pixels.
957                // Unfortunately some drivers will turn the entire target texture black
958                // when reading outside of the window.
959                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->getWidth(), layer->getHeight(),
960                        0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
961                layer->setEmpty(false);
962            }
963
964            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left,
965                    mSnapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight());
966
967            // Enqueue the buffer coordinates to clear the corresponding region later
968            mLayers.push(new Rect(bounds));
969        }
970    }
971
972    return true;
973}
974
975bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
976    layer->clipRect.set(clip);
977    layer->setFbo(mCaches.fboCache.get());
978
979    mSnapshot->region = &mSnapshot->layer->region;
980    mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer |
981            Snapshot::kFlagDirtyOrtho;
982    mSnapshot->fbo = layer->getFbo();
983    mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
984    mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
985    mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
986    mSnapshot->height = bounds.getHeight();
987    mSnapshot->orthoMatrix.load(mViewProjMatrix);
988
989    endTiling();
990    debugOverdraw(false, false);
991    // Bind texture to FBO
992    glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
993    layer->bindTexture();
994
995    // Initialize the texture if needed
996    if (layer->isEmpty()) {
997        layer->allocateTexture();
998        layer->setEmpty(false);
999    }
1000
1001    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
1002            layer->getTexture(), 0);
1003
1004    startTiling(mSnapshot, true);
1005
1006    // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
1007    mCaches.enableScissor();
1008    mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
1009            clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
1010    glClear(GL_COLOR_BUFFER_BIT);
1011
1012    dirtyClip();
1013
1014    // Change the ortho projection
1015    glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
1016
1017    // TODO: determine best way to support 3d drawing within HW layers
1018    mViewProjMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
1019
1020    return true;
1021}
1022
1023/**
1024 * Read the documentation of createLayer() before doing anything in this method.
1025 */
1026void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
1027    if (!current->layer) {
1028        ALOGE("Attempting to compose a layer that does not exist");
1029        return;
1030    }
1031
1032    Layer* layer = current->layer;
1033    const Rect& rect = layer->layer;
1034    const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
1035
1036    bool clipRequired = false;
1037    calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
1038            &clipRequired, false); // safely ignore return, should never be rejected
1039    mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
1040
1041    if (fboLayer) {
1042        endTiling();
1043
1044        // Detach the texture from the FBO
1045        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
1046
1047        layer->removeFbo(false);
1048
1049        // Unbind current FBO and restore previous one
1050        glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
1051        debugOverdraw(true, false);
1052
1053        startTiling(previous);
1054    }
1055
1056    if (!fboLayer && layer->getAlpha() < 255) {
1057        drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
1058                layer->getAlpha() << 24, SkXfermode::kDstIn_Mode, true);
1059        // Required below, composeLayerRect() will divide by 255
1060        layer->setAlpha(255);
1061    }
1062
1063    mCaches.unbindMeshBuffer();
1064
1065    mCaches.activeTexture(0);
1066
1067    // When the layer is stored in an FBO, we can save a bit of fillrate by
1068    // drawing only the dirty region
1069    if (fboLayer) {
1070        dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform);
1071        if (layer->getColorFilter()) {
1072            setupColorFilter(layer->getColorFilter());
1073        }
1074        composeLayerRegion(layer, rect);
1075        if (layer->getColorFilter()) {
1076            resetColorFilter();
1077        }
1078    } else if (!rect.isEmpty()) {
1079        dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
1080
1081        save(0);
1082        // the layer contains screen buffer content that shouldn't be alpha modulated
1083        // (and any necessary alpha modulation was handled drawing into the layer)
1084        mSnapshot->alpha = 1.0f;
1085        composeLayerRect(layer, rect, true);
1086        restore();
1087    }
1088
1089    dirtyClip();
1090
1091    // Failing to add the layer to the cache should happen only if the layer is too large
1092    if (!mCaches.layerCache.put(layer)) {
1093        LAYER_LOGD("Deleting layer");
1094        Caches::getInstance().resourceCache.decrementRefcount(layer);
1095    }
1096}
1097
1098void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
1099    float alpha = getLayerAlpha(layer);
1100
1101    setupDraw();
1102    if (layer->getRenderTarget() == GL_TEXTURE_2D) {
1103        setupDrawWithTexture();
1104    } else {
1105        setupDrawWithExternalTexture();
1106    }
1107    setupDrawTextureTransform();
1108    setupDrawColor(alpha, alpha, alpha, alpha);
1109    setupDrawColorFilter();
1110    setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode());
1111    setupDrawProgram();
1112    setupDrawPureColorUniforms();
1113    setupDrawColorFilterUniforms();
1114    if (layer->getRenderTarget() == GL_TEXTURE_2D) {
1115        setupDrawTexture(layer->getTexture());
1116    } else {
1117        setupDrawExternalTexture(layer->getTexture());
1118    }
1119    if (currentTransform().isPureTranslate() &&
1120            layer->getWidth() == (uint32_t) rect.getWidth() &&
1121            layer->getHeight() == (uint32_t) rect.getHeight()) {
1122        const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
1123        const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
1124
1125        layer->setFilter(GL_NEAREST);
1126        setupDrawModelView(kModelViewMode_TranslateAndScale, false,
1127                x, y, x + rect.getWidth(), y + rect.getHeight(), true);
1128    } else {
1129        layer->setFilter(GL_LINEAR);
1130        setupDrawModelView(kModelViewMode_TranslateAndScale, false,
1131                rect.left, rect.top, rect.right, rect.bottom);
1132    }
1133    setupDrawTextureTransformUniforms(layer->getTexTransform());
1134    setupDrawMesh(&mMeshVertices[0].x, &mMeshVertices[0].u);
1135
1136    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
1137}
1138
1139void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
1140    if (!layer->isTextureLayer()) {
1141        const Rect& texCoords = layer->texCoords;
1142        resetDrawTextureTexCoords(texCoords.left, texCoords.top,
1143                texCoords.right, texCoords.bottom);
1144
1145        float x = rect.left;
1146        float y = rect.top;
1147        bool simpleTransform = currentTransform().isPureTranslate() &&
1148                layer->getWidth() == (uint32_t) rect.getWidth() &&
1149                layer->getHeight() == (uint32_t) rect.getHeight();
1150
1151        if (simpleTransform) {
1152            // When we're swapping, the layer is already in screen coordinates
1153            if (!swap) {
1154                x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
1155                y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
1156            }
1157
1158            layer->setFilter(GL_NEAREST, true);
1159        } else {
1160            layer->setFilter(GL_LINEAR, true);
1161        }
1162
1163        float alpha = getLayerAlpha(layer);
1164        bool blend = layer->isBlend() || alpha < 1.0f;
1165        drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(),
1166                layer->getTexture(), alpha, layer->getMode(), blend,
1167                &mMeshVertices[0].x, &mMeshVertices[0].u,
1168                GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform);
1169
1170        resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
1171    } else {
1172        resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f);
1173        drawTextureLayer(layer, rect);
1174        resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
1175    }
1176}
1177
1178/**
1179 * Issues the command X, and if we're composing a save layer to the fbo or drawing a newly updated
1180 * hardware layer with overdraw debug on, draws again to the stencil only, so that these draw
1181 * operations are correctly counted twice for overdraw. NOTE: assumes composeLayerRegion only used
1182 * by saveLayer's restore
1183 */
1184#define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) {                             \
1185        DRAW_COMMAND;                                                            \
1186        if (CC_UNLIKELY(mCaches.debugOverdraw && getTargetFbo() == 0 && COND)) { \
1187            glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);                 \
1188            DRAW_COMMAND;                                                        \
1189            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);                     \
1190        }                                                                        \
1191    }
1192
1193#define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
1194
1195void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
1196    if (layer->region.isRect()) {
1197        layer->setRegionAsRect();
1198
1199        DRAW_DOUBLE_STENCIL(composeLayerRect(layer, layer->regionRect));
1200
1201        layer->region.clear();
1202        return;
1203    }
1204
1205    if (CC_LIKELY(!layer->region.isEmpty())) {
1206        size_t count;
1207        const android::Rect* rects;
1208        Region safeRegion;
1209        if (CC_LIKELY(hasRectToRectTransform())) {
1210            rects = layer->region.getArray(&count);
1211        } else {
1212            safeRegion = Region::createTJunctionFreeRegion(layer->region);
1213            rects = safeRegion.getArray(&count);
1214        }
1215
1216        const float alpha = getLayerAlpha(layer);
1217        const float texX = 1.0f / float(layer->getWidth());
1218        const float texY = 1.0f / float(layer->getHeight());
1219        const float height = rect.getHeight();
1220
1221        setupDraw();
1222
1223        // We must get (and therefore bind) the region mesh buffer
1224        // after we setup drawing in case we need to mess with the
1225        // stencil buffer in setupDraw()
1226        TextureVertex* mesh = mCaches.getRegionMesh();
1227        uint32_t numQuads = 0;
1228
1229        setupDrawWithTexture();
1230        setupDrawColor(alpha, alpha, alpha, alpha);
1231        setupDrawColorFilter();
1232        setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode(), false);
1233        setupDrawProgram();
1234        setupDrawDirtyRegionsDisabled();
1235        setupDrawPureColorUniforms();
1236        setupDrawColorFilterUniforms();
1237        setupDrawTexture(layer->getTexture());
1238        if (currentTransform().isPureTranslate()) {
1239            const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
1240            const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
1241
1242            layer->setFilter(GL_NEAREST);
1243            setupDrawModelView(kModelViewMode_Translate, false,
1244                    x, y, x + rect.getWidth(), y + rect.getHeight(), true);
1245        } else {
1246            layer->setFilter(GL_LINEAR);
1247            setupDrawModelView(kModelViewMode_Translate, false,
1248                    rect.left, rect.top, rect.right, rect.bottom);
1249        }
1250        setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
1251
1252        for (size_t i = 0; i < count; i++) {
1253            const android::Rect* r = &rects[i];
1254
1255            const float u1 = r->left * texX;
1256            const float v1 = (height - r->top) * texY;
1257            const float u2 = r->right * texX;
1258            const float v2 = (height - r->bottom) * texY;
1259
1260            // TODO: Reject quads outside of the clip
1261            TextureVertex::set(mesh++, r->left, r->top, u1, v1);
1262            TextureVertex::set(mesh++, r->right, r->top, u2, v1);
1263            TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
1264            TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
1265
1266            numQuads++;
1267
1268            if (numQuads >= gMaxNumberOfQuads) {
1269                DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
1270                                GL_UNSIGNED_SHORT, NULL));
1271                numQuads = 0;
1272                mesh = mCaches.getRegionMesh();
1273            }
1274        }
1275
1276        if (numQuads > 0) {
1277            DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
1278                            GL_UNSIGNED_SHORT, NULL));
1279        }
1280
1281#if DEBUG_LAYERS_AS_REGIONS
1282        drawRegionRectsDebug(layer->region);
1283#endif
1284
1285        layer->region.clear();
1286    }
1287}
1288
1289#if DEBUG_LAYERS_AS_REGIONS
1290void OpenGLRenderer::drawRegionRectsDebug(const Region& region) {
1291    size_t count;
1292    const android::Rect* rects = region.getArray(&count);
1293
1294    uint32_t colors[] = {
1295            0x7fff0000, 0x7f00ff00,
1296            0x7f0000ff, 0x7fff00ff,
1297    };
1298
1299    int offset = 0;
1300    int32_t top = rects[0].top;
1301
1302    for (size_t i = 0; i < count; i++) {
1303        if (top != rects[i].top) {
1304            offset ^= 0x2;
1305            top = rects[i].top;
1306        }
1307
1308        Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
1309        drawColorRect(r.left, r.top, r.right, r.bottom, colors[offset + (i & 0x1)],
1310                SkXfermode::kSrcOver_Mode);
1311    }
1312}
1313#endif
1314
1315void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
1316        SkXfermode::Mode mode, bool dirty) {
1317    Vector<float> rects;
1318
1319    SkRegion::Iterator it(region);
1320    while (!it.done()) {
1321        const SkIRect& r = it.rect();
1322        rects.push(r.fLeft);
1323        rects.push(r.fTop);
1324        rects.push(r.fRight);
1325        rects.push(r.fBottom);
1326        it.next();
1327    }
1328
1329    drawColorRects(rects.array(), rects.size(), color, mode, true, dirty, false);
1330}
1331
1332void OpenGLRenderer::dirtyLayer(const float left, const float top,
1333        const float right, const float bottom, const mat4 transform) {
1334    if (hasLayer()) {
1335        Rect bounds(left, top, right, bottom);
1336        transform.mapRect(bounds);
1337        dirtyLayerUnchecked(bounds, getRegion());
1338    }
1339}
1340
1341void OpenGLRenderer::dirtyLayer(const float left, const float top,
1342        const float right, const float bottom) {
1343    if (hasLayer()) {
1344        Rect bounds(left, top, right, bottom);
1345        dirtyLayerUnchecked(bounds, getRegion());
1346    }
1347}
1348
1349void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
1350    if (bounds.intersect(*mSnapshot->clipRect)) {
1351        bounds.snapToPixelBoundaries();
1352        android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
1353        if (!dirty.isEmpty()) {
1354            region->orSelf(dirty);
1355        }
1356    }
1357}
1358
1359void OpenGLRenderer::issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount) {
1360    GLsizei elementsCount = quadsCount * 6;
1361    while (elementsCount > 0) {
1362        GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
1363
1364        setupDrawIndexedVertices(&mesh[0].x);
1365        glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL);
1366
1367        elementsCount -= drawCount;
1368        // Though there are 4 vertices in a quad, we use 6 indices per
1369        // quad to draw with GL_TRIANGLES
1370        mesh += (drawCount / 6) * 4;
1371    }
1372}
1373
1374void OpenGLRenderer::clearLayerRegions() {
1375    const size_t count = mLayers.size();
1376    if (count == 0) return;
1377
1378    if (!mSnapshot->isIgnored()) {
1379        // Doing several glScissor/glClear here can negatively impact
1380        // GPUs with a tiler architecture, instead we draw quads with
1381        // the Clear blending mode
1382
1383        // The list contains bounds that have already been clipped
1384        // against their initial clip rect, and the current clip
1385        // is likely different so we need to disable clipping here
1386        bool scissorChanged = mCaches.disableScissor();
1387
1388        Vertex mesh[count * 4];
1389        Vertex* vertex = mesh;
1390
1391        for (uint32_t i = 0; i < count; i++) {
1392            Rect* bounds = mLayers.itemAt(i);
1393
1394            Vertex::set(vertex++, bounds->left, bounds->top);
1395            Vertex::set(vertex++, bounds->right, bounds->top);
1396            Vertex::set(vertex++, bounds->left, bounds->bottom);
1397            Vertex::set(vertex++, bounds->right, bounds->bottom);
1398
1399            delete bounds;
1400        }
1401        // We must clear the list of dirty rects before we
1402        // call setupDraw() to prevent stencil setup to do
1403        // the same thing again
1404        mLayers.clear();
1405
1406        setupDraw(false);
1407        setupDrawColor(0.0f, 0.0f, 0.0f, 1.0f);
1408        setupDrawBlending(true, SkXfermode::kClear_Mode);
1409        setupDrawProgram();
1410        setupDrawPureColorUniforms();
1411        setupDrawModelView(kModelViewMode_Translate, false,
1412                0.0f, 0.0f, 0.0f, 0.0f, true);
1413
1414        issueIndexedQuadDraw(&mesh[0], count);
1415
1416        if (scissorChanged) mCaches.enableScissor();
1417    } else {
1418        for (uint32_t i = 0; i < count; i++) {
1419            delete mLayers.itemAt(i);
1420        }
1421        mLayers.clear();
1422    }
1423}
1424
1425///////////////////////////////////////////////////////////////////////////////
1426// State Deferral
1427///////////////////////////////////////////////////////////////////////////////
1428
1429bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
1430    const Rect& currentClip = *(mSnapshot->clipRect);
1431    const mat4& currentMatrix = *(mSnapshot->transform);
1432
1433    if (stateDeferFlags & kStateDeferFlag_Draw) {
1434        // state has bounds initialized in local coordinates
1435        if (!state.mBounds.isEmpty()) {
1436            currentMatrix.mapRect(state.mBounds);
1437            Rect clippedBounds(state.mBounds);
1438            // NOTE: if we ever want to use this clipping info to drive whether the scissor
1439            // is used, it should more closely duplicate the quickReject logic (in how it uses
1440            // snapToPixelBoundaries)
1441
1442            if(!clippedBounds.intersect(currentClip)) {
1443                // quick rejected
1444                return true;
1445            }
1446
1447            state.mClipSideFlags = kClipSide_None;
1448            if (!currentClip.contains(state.mBounds)) {
1449                int& flags = state.mClipSideFlags;
1450                // op partially clipped, so record which sides are clipped for clip-aware merging
1451                if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left;
1452                if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top;
1453                if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right;
1454                if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
1455            }
1456            state.mBounds.set(clippedBounds);
1457        } else {
1458            // Empty bounds implies size unknown. Label op as conservatively clipped to disable
1459            // overdraw avoidance (since we don't know what it overlaps)
1460            state.mClipSideFlags = kClipSide_ConservativeFull;
1461            state.mBounds.set(currentClip);
1462        }
1463    }
1464
1465    state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
1466    if (state.mClipValid) {
1467        state.mClip.set(currentClip);
1468    }
1469
1470    // Transform, drawModifiers, and alpha always deferred, since they are used by state operations
1471    // (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything)
1472    state.mMatrix.load(currentMatrix);
1473    state.mDrawModifiers = mDrawModifiers;
1474    state.mAlpha = mSnapshot->alpha;
1475    return false;
1476}
1477
1478void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
1479    currentTransform().load(state.mMatrix);
1480    mDrawModifiers = state.mDrawModifiers;
1481    mSnapshot->alpha = state.mAlpha;
1482
1483    if (state.mClipValid && !skipClipRestore) {
1484        mSnapshot->setClip(state.mClip.left, state.mClip.top,
1485                state.mClip.right, state.mClip.bottom);
1486        dirtyClip();
1487    }
1488}
1489
1490/**
1491 * Merged multidraw (such as in drawText and drawBitmaps rely on the fact that no clipping is done
1492 * in the draw path. Instead, clipping is done ahead of time - either as a single clip rect (when at
1493 * least one op is clipped), or disabled entirely (because no merged op is clipped)
1494 *
1495 * This method should be called when restoreDisplayState() won't be restoring the clip
1496 */
1497void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
1498    if (clipRect != NULL) {
1499        mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
1500    } else {
1501        mSnapshot->setClip(0, 0, mWidth, mHeight);
1502    }
1503    dirtyClip();
1504    mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled);
1505}
1506
1507///////////////////////////////////////////////////////////////////////////////
1508// Transforms
1509///////////////////////////////////////////////////////////////////////////////
1510
1511void OpenGLRenderer::translate(float dx, float dy, float dz) {
1512    currentTransform().translate(dx, dy, dz);
1513}
1514
1515void OpenGLRenderer::rotate(float degrees) {
1516    currentTransform().rotate(degrees, 0.0f, 0.0f, 1.0f);
1517}
1518
1519void OpenGLRenderer::scale(float sx, float sy) {
1520    currentTransform().scale(sx, sy, 1.0f);
1521}
1522
1523void OpenGLRenderer::skew(float sx, float sy) {
1524    currentTransform().skew(sx, sy);
1525}
1526
1527void OpenGLRenderer::setMatrix(SkMatrix* matrix) {
1528    if (matrix) {
1529        currentTransform().load(*matrix);
1530    } else {
1531        currentTransform().loadIdentity();
1532    }
1533}
1534
1535bool OpenGLRenderer::hasRectToRectTransform() {
1536    return CC_LIKELY(currentTransform().rectToRect());
1537}
1538
1539void OpenGLRenderer::getMatrix(SkMatrix* matrix) const {
1540    currentTransform().copyTo(*matrix);
1541}
1542
1543void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
1544    mat4 transform(*matrix);
1545    currentTransform().multiply(transform);
1546}
1547
1548///////////////////////////////////////////////////////////////////////////////
1549// Clipping
1550///////////////////////////////////////////////////////////////////////////////
1551
1552void OpenGLRenderer::setScissorFromClip() {
1553    Rect clip(*mSnapshot->clipRect);
1554    clip.snapToPixelBoundaries();
1555
1556    if (mCaches.setScissor(clip.left, mSnapshot->height - clip.bottom,
1557            clip.getWidth(), clip.getHeight())) {
1558        mDirtyClip = false;
1559    }
1560}
1561
1562void OpenGLRenderer::ensureStencilBuffer() {
1563    // Thanks to the mismatch between EGL and OpenGL ES FBO we
1564    // cannot attach a stencil buffer to fbo0 dynamically. Let's
1565    // just hope we have one when hasLayer() returns false.
1566    if (hasLayer()) {
1567        attachStencilBufferToLayer(mSnapshot->layer);
1568    }
1569}
1570
1571void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
1572    // The layer's FBO is already bound when we reach this stage
1573    if (!layer->getStencilRenderBuffer()) {
1574        // GL_QCOM_tiled_rendering doesn't like it if a renderbuffer
1575        // is attached after we initiated tiling. We must turn it off,
1576        // attach the new render buffer then turn tiling back on
1577        endTiling();
1578
1579        RenderBuffer* buffer = mCaches.renderBufferCache.get(
1580                Stencil::getSmallestStencilFormat(), layer->getWidth(), layer->getHeight());
1581        layer->setStencilRenderBuffer(buffer);
1582
1583        startTiling(layer->clipRect, layer->layer.getHeight());
1584    }
1585}
1586
1587void OpenGLRenderer::setStencilFromClip() {
1588    if (!mCaches.debugOverdraw) {
1589        if (!mSnapshot->clipRegion->isEmpty()) {
1590            // NOTE: The order here is important, we must set dirtyClip to false
1591            //       before any draw call to avoid calling back into this method
1592            mDirtyClip = false;
1593
1594            ensureStencilBuffer();
1595
1596            mCaches.stencil.enableWrite();
1597
1598            // Clear the stencil but first make sure we restrict drawing
1599            // to the region's bounds
1600            bool resetScissor = mCaches.enableScissor();
1601            if (resetScissor) {
1602                // The scissor was not set so we now need to update it
1603                setScissorFromClip();
1604            }
1605            mCaches.stencil.clear();
1606            if (resetScissor) mCaches.disableScissor();
1607
1608            // NOTE: We could use the region contour path to generate a smaller mesh
1609            //       Since we are using the stencil we could use the red book path
1610            //       drawing technique. It might increase bandwidth usage though.
1611
1612            // The last parameter is important: we are not drawing in the color buffer
1613            // so we don't want to dirty the current layer, if any
1614            drawRegionRects(*mSnapshot->clipRegion, 0xff000000, SkXfermode::kSrc_Mode, false);
1615
1616            mCaches.stencil.enableTest();
1617
1618            // Draw the region used to generate the stencil if the appropriate debug
1619            // mode is enabled
1620            if (mCaches.debugStencilClip == Caches::kStencilShowRegion) {
1621                drawRegionRects(*mSnapshot->clipRegion, 0x7f0000ff, SkXfermode::kSrcOver_Mode);
1622            }
1623        } else {
1624            mCaches.stencil.disable();
1625        }
1626    }
1627}
1628
1629const Rect& OpenGLRenderer::getClipBounds() const {
1630    return mSnapshot->getLocalClip();
1631}
1632
1633/**
1634 * Calculates whether content drawn within the passed bounds would be outside of, or intersect with
1635 * the clipRect. Does not modify the scissor.
1636 *
1637 * @param clipRequired if not null, will be set to true if element intersects clip
1638 *         (and wasn't rejected)
1639 *
1640 * @param snapOut if set, the geometry will be treated as having an AA ramp.
1641 *         See Rect::snapGeometryToPixelBoundaries()
1642 */
1643bool OpenGLRenderer::calculateQuickRejectForScissor(float left, float top,
1644        float right, float bottom, bool* clipRequired, bool snapOut) const {
1645    if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
1646        return true;
1647    }
1648
1649    Rect r(left, top, right, bottom);
1650    currentTransform().mapRect(r);
1651    r.snapGeometryToPixelBoundaries(snapOut);
1652
1653    Rect clipRect(*mSnapshot->clipRect);
1654    clipRect.snapToPixelBoundaries();
1655
1656    if (!clipRect.intersects(r)) return true;
1657
1658    // clip is required if geometry intersects clip rect
1659    if (clipRequired) *clipRequired = !clipRect.contains(r);
1660    return false;
1661}
1662
1663/**
1664 * Returns false if drawing won't be clipped out.
1665 *
1666 * Makes the decision conservatively, by rounding out the mapped rect before comparing with the
1667 * clipRect. To be used when perfect, pixel accuracy is not possible (esp. with tessellation) but
1668 * rejection is still desired.
1669 *
1670 * This function, unlike quickRejectSetupScissor, should be used where precise geometry information
1671 * isn't known (esp. when geometry adjusts based on scale). Generally, this will be first pass
1672 * rejection where precise rejection isn't important, or precise information isn't available.
1673 */
1674bool OpenGLRenderer::quickRejectConservative(float left, float top,
1675        float right, float bottom) const {
1676    if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
1677        return true;
1678    }
1679
1680    Rect r(left, top, right, bottom);
1681    currentTransform().mapRect(r);
1682    r.roundOut(); // rounded out to be conservative
1683
1684    Rect clipRect(*mSnapshot->clipRect);
1685    clipRect.snapToPixelBoundaries();
1686
1687    if (!clipRect.intersects(r)) return true;
1688
1689    return false;
1690}
1691
1692/**
1693 * Returns false and sets scissor enable based upon bounds if drawing won't be clipped out.
1694 *
1695 * @param paint if not null, the bounds will be expanded to account for stroke depending on paint
1696 *         style, and tessellated AA ramp
1697 */
1698bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom,
1699        SkPaint* paint) {
1700    bool clipRequired = false;
1701    bool snapOut = paint && paint->isAntiAlias();
1702
1703    if (paint && paint->getStyle() != SkPaint::kFill_Style) {
1704        float outset = paint->getStrokeWidth() * 0.5f;
1705        left -= outset;
1706        top -= outset;
1707        right += outset;
1708        bottom += outset;
1709    }
1710
1711    if (calculateQuickRejectForScissor(left, top, right, bottom, &clipRequired, snapOut)) {
1712        return true;
1713    }
1714
1715    if (!isRecording()) {
1716        // not quick rejected, so enable the scissor if clipRequired
1717        mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
1718    }
1719    return false;
1720}
1721
1722void OpenGLRenderer::debugClip() {
1723#if DEBUG_CLIP_REGIONS
1724    if (!isRecording() && !mSnapshot->clipRegion->isEmpty()) {
1725        drawRegionRects(*mSnapshot->clipRegion, 0x7f00ff00, SkXfermode::kSrcOver_Mode);
1726    }
1727#endif
1728}
1729
1730bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
1731    if (CC_LIKELY(currentTransform().rectToRect())) {
1732        bool clipped = mSnapshot->clip(left, top, right, bottom, op);
1733        if (clipped) {
1734            dirtyClip();
1735        }
1736        return !mSnapshot->clipRect->isEmpty();
1737    }
1738
1739    SkPath path;
1740    path.addRect(left, top, right, bottom);
1741
1742    return OpenGLRenderer::clipPath(&path, op);
1743}
1744
1745bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
1746    SkMatrix transform;
1747    currentTransform().copyTo(transform);
1748
1749    SkPath transformed;
1750    path->transform(transform, &transformed);
1751
1752    SkRegion clip;
1753    if (!mSnapshot->previous->clipRegion->isEmpty()) {
1754        clip.setRegion(*mSnapshot->previous->clipRegion);
1755    } else {
1756        if (mSnapshot->previous == mFirstSnapshot) {
1757            clip.setRect(0, 0, mWidth, mHeight);
1758        } else {
1759            Rect* bounds = mSnapshot->previous->clipRect;
1760            clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
1761        }
1762    }
1763
1764    SkRegion region;
1765    region.setPath(transformed, clip);
1766
1767    bool clipped = mSnapshot->clipRegionTransformed(region, op);
1768    if (clipped) {
1769        dirtyClip();
1770    }
1771    return !mSnapshot->clipRect->isEmpty();
1772}
1773
1774bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
1775    bool clipped = mSnapshot->clipRegionTransformed(*region, op);
1776    if (clipped) {
1777        dirtyClip();
1778    }
1779    return !mSnapshot->clipRect->isEmpty();
1780}
1781
1782Rect* OpenGLRenderer::getClipRect() {
1783    return mSnapshot->clipRect;
1784}
1785
1786///////////////////////////////////////////////////////////////////////////////
1787// Drawing commands
1788///////////////////////////////////////////////////////////////////////////////
1789
1790void OpenGLRenderer::setupDraw(bool clear) {
1791    // TODO: It would be best if we could do this before quickRejectSetupScissor()
1792    //       changes the scissor test state
1793    if (clear) clearLayerRegions();
1794    // Make sure setScissor & setStencil happen at the beginning of
1795    // this method
1796    if (mDirtyClip) {
1797        if (mCaches.scissorEnabled) {
1798            setScissorFromClip();
1799        }
1800        setStencilFromClip();
1801    }
1802
1803    mDescription.reset();
1804
1805    mSetShaderColor = false;
1806    mColorSet = false;
1807    mColorA = mColorR = mColorG = mColorB = 0.0f;
1808    mTextureUnit = 0;
1809    mTrackDirtyRegions = true;
1810
1811    // Enable debug highlight when what we're about to draw is tested against
1812    // the stencil buffer and if stencil highlight debugging is on
1813    mDescription.hasDebugHighlight = !mCaches.debugOverdraw &&
1814            mCaches.debugStencilClip == Caches::kStencilShowHighlight &&
1815            mCaches.stencil.isTestEnabled();
1816
1817    mDescription.emulateStencil = mCountOverdraw;
1818}
1819
1820void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
1821    mDescription.hasTexture = true;
1822    mDescription.hasAlpha8Texture = isAlpha8;
1823}
1824
1825void OpenGLRenderer::setupDrawWithTextureAndColor(bool isAlpha8) {
1826    mDescription.hasTexture = true;
1827    mDescription.hasColors = true;
1828    mDescription.hasAlpha8Texture = isAlpha8;
1829}
1830
1831void OpenGLRenderer::setupDrawWithExternalTexture() {
1832    mDescription.hasExternalTexture = true;
1833}
1834
1835void OpenGLRenderer::setupDrawNoTexture() {
1836    mCaches.disableTexCoordsVertexArray();
1837}
1838
1839void OpenGLRenderer::setupDrawAA() {
1840    mDescription.isAA = true;
1841}
1842
1843void OpenGLRenderer::setupDrawColor(int color, int alpha) {
1844    mColorA = alpha / 255.0f;
1845    mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
1846    mColorG = mColorA * ((color >>  8) & 0xFF) / 255.0f;
1847    mColorB = mColorA * ((color      ) & 0xFF) / 255.0f;
1848    mColorSet = true;
1849    mSetShaderColor = mDescription.setColorModulate(mColorA);
1850}
1851
1852void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) {
1853    mColorA = alpha / 255.0f;
1854    mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
1855    mColorG = mColorA * ((color >>  8) & 0xFF) / 255.0f;
1856    mColorB = mColorA * ((color      ) & 0xFF) / 255.0f;
1857    mColorSet = true;
1858    mSetShaderColor = mDescription.setAlpha8ColorModulate(mColorR, mColorG, mColorB, mColorA);
1859}
1860
1861void OpenGLRenderer::setupDrawTextGamma(const SkPaint* paint) {
1862    mCaches.fontRenderer->describe(mDescription, paint);
1863}
1864
1865void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) {
1866    mColorA = a;
1867    mColorR = r;
1868    mColorG = g;
1869    mColorB = b;
1870    mColorSet = true;
1871    mSetShaderColor = mDescription.setColorModulate(a);
1872}
1873
1874void OpenGLRenderer::setupDrawShader() {
1875    if (mDrawModifiers.mShader) {
1876        mDrawModifiers.mShader->describe(mDescription, mExtensions);
1877    }
1878}
1879
1880void OpenGLRenderer::setupDrawColorFilter() {
1881    if (mDrawModifiers.mColorFilter) {
1882        mDrawModifiers.mColorFilter->describe(mDescription, mExtensions);
1883    }
1884}
1885
1886void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) {
1887    if (mColorSet && mode == SkXfermode::kClear_Mode) {
1888        mColorA = 1.0f;
1889        mColorR = mColorG = mColorB = 0.0f;
1890        mSetShaderColor = mDescription.modulate = true;
1891    }
1892}
1893
1894void OpenGLRenderer::setupDrawBlending(SkXfermode::Mode mode, bool swapSrcDst) {
1895    // When the blending mode is kClear_Mode, we need to use a modulate color
1896    // argb=1,0,0,0
1897    accountForClear(mode);
1898    bool blend = (mColorSet && mColorA < 1.0f) ||
1899            (mDrawModifiers.mShader && mDrawModifiers.mShader->blend());
1900    chooseBlending(blend, mode, mDescription, swapSrcDst);
1901}
1902
1903void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool swapSrcDst) {
1904    // When the blending mode is kClear_Mode, we need to use a modulate color
1905    // argb=1,0,0,0
1906    accountForClear(mode);
1907    blend |= (mColorSet && mColorA < 1.0f) ||
1908            (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
1909            (mDrawModifiers.mColorFilter && mDrawModifiers.mColorFilter->blend());
1910    chooseBlending(blend, mode, mDescription, swapSrcDst);
1911}
1912
1913void OpenGLRenderer::setupDrawProgram() {
1914    useProgram(mCaches.programCache.get(mDescription));
1915}
1916
1917void OpenGLRenderer::setupDrawDirtyRegionsDisabled() {
1918    mTrackDirtyRegions = false;
1919}
1920
1921void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset,
1922        float left, float top, float right, float bottom, bool ignoreTransform) {
1923    mModelView.loadTranslate(left, top, 0.0f);
1924    if (mode == kModelViewMode_TranslateAndScale) {
1925        mModelView.scale(right - left, bottom - top, 1.0f);
1926    }
1927
1928    bool dirty = right - left > 0.0f && bottom - top > 0.0f;
1929    if (!ignoreTransform) {
1930        mCaches.currentProgram->set(mViewProjMatrix, mModelView, currentTransform(), offset);
1931        if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, currentTransform());
1932    } else {
1933        mCaches.currentProgram->set(mViewProjMatrix, mModelView, mat4::identity(), offset);
1934        if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom);
1935    }
1936}
1937
1938void OpenGLRenderer::setupDrawColorUniforms() {
1939    if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
1940        mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
1941    }
1942}
1943
1944void OpenGLRenderer::setupDrawPureColorUniforms() {
1945    if (mSetShaderColor) {
1946        mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
1947    }
1948}
1949
1950void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
1951    if (mDrawModifiers.mShader) {
1952        if (ignoreTransform) {
1953            // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform()
1954            // because it was built into modelView / the geometry, and the SkiaShader needs to
1955            // compensate.
1956            mat4 modelViewWithoutTransform;
1957            modelViewWithoutTransform.loadInverse(currentTransform());
1958            modelViewWithoutTransform.multiply(mModelView);
1959            mModelView.load(modelViewWithoutTransform);
1960        }
1961        mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
1962                mModelView, *mSnapshot, &mTextureUnit);
1963    }
1964}
1965
1966void OpenGLRenderer::setupDrawColorFilterUniforms() {
1967    if (mDrawModifiers.mColorFilter) {
1968        mDrawModifiers.mColorFilter->setupProgram(mCaches.currentProgram);
1969    }
1970}
1971
1972void OpenGLRenderer::setupDrawTextGammaUniforms() {
1973    mCaches.fontRenderer->setupProgram(mDescription, mCaches.currentProgram);
1974}
1975
1976void OpenGLRenderer::setupDrawSimpleMesh() {
1977    bool force = mCaches.bindMeshBuffer();
1978    mCaches.bindPositionVertexPointer(force, 0);
1979    mCaches.unbindIndicesBuffer();
1980}
1981
1982void OpenGLRenderer::setupDrawTexture(GLuint texture) {
1983    if (texture) bindTexture(texture);
1984    mTextureUnit++;
1985    mCaches.enableTexCoordsVertexArray();
1986}
1987
1988void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
1989    bindExternalTexture(texture);
1990    mTextureUnit++;
1991    mCaches.enableTexCoordsVertexArray();
1992}
1993
1994void OpenGLRenderer::setupDrawTextureTransform() {
1995    mDescription.hasTextureTransform = true;
1996}
1997
1998void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
1999    glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1,
2000            GL_FALSE, &transform.data[0]);
2001}
2002
2003void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
2004    bool force = false;
2005    if (!vertices || vbo) {
2006        force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
2007    } else {
2008        force = mCaches.unbindMeshBuffer();
2009    }
2010
2011    mCaches.bindPositionVertexPointer(force, vertices);
2012    if (mCaches.currentProgram->texCoords >= 0) {
2013        mCaches.bindTexCoordsVertexPointer(force, texCoords);
2014    }
2015
2016    mCaches.unbindIndicesBuffer();
2017}
2018
2019void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors) {
2020    bool force = mCaches.unbindMeshBuffer();
2021    GLsizei stride = sizeof(ColorTextureVertex);
2022
2023    mCaches.bindPositionVertexPointer(force, vertices, stride);
2024    if (mCaches.currentProgram->texCoords >= 0) {
2025        mCaches.bindTexCoordsVertexPointer(force, texCoords, stride);
2026    }
2027    int slot = mCaches.currentProgram->getAttrib("colors");
2028    if (slot >= 0) {
2029        glEnableVertexAttribArray(slot);
2030        glVertexAttribPointer(slot, 4, GL_FLOAT, GL_FALSE, stride, colors);
2031    }
2032
2033    mCaches.unbindIndicesBuffer();
2034}
2035
2036void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
2037    bool force = false;
2038    // If vbo is != 0 we want to treat the vertices parameter as an offset inside
2039    // a VBO. However, if vertices is set to NULL and vbo == 0 then we want to
2040    // use the default VBO found in Caches
2041    if (!vertices || vbo) {
2042        force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
2043    } else {
2044        force = mCaches.unbindMeshBuffer();
2045    }
2046    mCaches.bindIndicesBuffer();
2047
2048    mCaches.bindPositionVertexPointer(force, vertices);
2049    if (mCaches.currentProgram->texCoords >= 0) {
2050        mCaches.bindTexCoordsVertexPointer(force, texCoords);
2051    }
2052}
2053
2054void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) {
2055    bool force = mCaches.unbindMeshBuffer();
2056    mCaches.bindIndicesBuffer();
2057    mCaches.bindPositionVertexPointer(force, vertices, gVertexStride);
2058}
2059
2060///////////////////////////////////////////////////////////////////////////////
2061// Drawing
2062///////////////////////////////////////////////////////////////////////////////
2063
2064status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
2065        int32_t replayFlags) {
2066    status_t status;
2067
2068    if (mCaches.propertyDirtyViewport) {
2069        // force recalc of view/proj matrices
2070        setViewport(mWidth, mHeight);
2071        mCaches.propertyDirtyViewport = false;
2072    }
2073
2074    // All the usual checks and setup operations (quickReject, setupDraw, etc.)
2075    // will be performed by the display list itself
2076    if (displayList && displayList->isRenderable()) {
2077        // compute 3d ordering
2078        displayList->computeOrdering();
2079        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
2080            status = startFrame();
2081            ReplayStateStruct replayStruct(*this, dirty, replayFlags);
2082            displayList->replay(replayStruct, 0);
2083            return status | replayStruct.mDrawGlStatus;
2084        }
2085
2086        bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
2087        DeferredDisplayList deferredList(*(mSnapshot->clipRect), avoidOverdraw);
2088        DeferStateStruct deferStruct(deferredList, *this, replayFlags);
2089        displayList->defer(deferStruct, 0);
2090
2091        flushLayers();
2092        status = startFrame();
2093
2094        return deferredList.flush(*this, dirty) | status;
2095    }
2096
2097    return DrawGlInfo::kStatusDone;
2098}
2099
2100void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint) {
2101    int alpha;
2102    SkXfermode::Mode mode;
2103    getAlphaAndMode(paint, &alpha, &mode);
2104
2105    int color = paint != NULL ? paint->getColor() : 0;
2106
2107    float x = left;
2108    float y = top;
2109
2110    texture->setWrap(GL_CLAMP_TO_EDGE, true);
2111
2112    bool ignoreTransform = false;
2113    if (currentTransform().isPureTranslate()) {
2114        x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
2115        y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
2116        ignoreTransform = true;
2117
2118        texture->setFilter(GL_NEAREST, true);
2119    } else {
2120        texture->setFilter(FILTER(paint), true);
2121    }
2122
2123    // No need to check for a UV mapper on the texture object, only ARGB_8888
2124    // bitmaps get packed in the atlas
2125    drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
2126            paint != NULL, color, alpha, mode, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
2127            GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
2128}
2129
2130/**
2131 * Important note: this method is intended to draw batches of bitmaps and
2132 * will not set the scissor enable or dirty the current layer, if any.
2133 * The caller is responsible for properly dirtying the current layer.
2134 */
2135status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
2136        TextureVertex* vertices, bool pureTranslate, const Rect& bounds, SkPaint* paint) {
2137    mCaches.activeTexture(0);
2138    Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
2139    if (!texture) return DrawGlInfo::kStatusDone;
2140
2141    const AutoTexture autoCleanup(texture);
2142
2143    int alpha;
2144    SkXfermode::Mode mode;
2145    getAlphaAndMode(paint, &alpha, &mode);
2146
2147    texture->setWrap(GL_CLAMP_TO_EDGE, true);
2148    texture->setFilter(pureTranslate ? GL_NEAREST : FILTER(paint), true);
2149
2150    const float x = (int) floorf(bounds.left + 0.5f);
2151    const float y = (int) floorf(bounds.top + 0.5f);
2152    if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
2153        int color = paint != NULL ? paint->getColor() : 0;
2154        drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
2155                texture->id, paint != NULL, color, alpha, mode,
2156                &vertices[0].x, &vertices[0].u,
2157                GL_TRIANGLES, bitmapCount * 6, true,
2158                kModelViewMode_Translate, false);
2159    } else {
2160        drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
2161                texture->id, alpha / 255.0f, mode, texture->blend,
2162                &vertices[0].x, &vertices[0].u,
2163                GL_TRIANGLES, bitmapCount * 6, false, true, 0,
2164                kModelViewMode_Translate, false);
2165    }
2166
2167    return DrawGlInfo::kStatusDrew;
2168}
2169
2170status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
2171    const float right = left + bitmap->width();
2172    const float bottom = top + bitmap->height();
2173
2174    if (quickRejectSetupScissor(left, top, right, bottom)) {
2175        return DrawGlInfo::kStatusDone;
2176    }
2177
2178    mCaches.activeTexture(0);
2179    Texture* texture = getTexture(bitmap);
2180    if (!texture) return DrawGlInfo::kStatusDone;
2181    const AutoTexture autoCleanup(texture);
2182
2183    if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
2184        drawAlphaBitmap(texture, left, top, paint);
2185    } else {
2186        drawTextureRect(left, top, right, bottom, texture, paint);
2187    }
2188
2189    return DrawGlInfo::kStatusDrew;
2190}
2191
2192status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
2193    Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
2194    const mat4 transform(*matrix);
2195    transform.mapRect(r);
2196
2197    if (quickRejectSetupScissor(r.left, r.top, r.right, r.bottom)) {
2198        return DrawGlInfo::kStatusDone;
2199    }
2200
2201    mCaches.activeTexture(0);
2202    Texture* texture = getTexture(bitmap);
2203    if (!texture) return DrawGlInfo::kStatusDone;
2204    const AutoTexture autoCleanup(texture);
2205
2206    // This could be done in a cheaper way, all we need is pass the matrix
2207    // to the vertex shader. The save/restore is a bit overkill.
2208    save(SkCanvas::kMatrix_SaveFlag);
2209    concatMatrix(matrix);
2210    if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
2211        drawAlphaBitmap(texture, 0.0f, 0.0f, paint);
2212    } else {
2213        drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint);
2214    }
2215    restore();
2216
2217    return DrawGlInfo::kStatusDrew;
2218}
2219
2220status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
2221    const float right = left + bitmap->width();
2222    const float bottom = top + bitmap->height();
2223
2224    if (quickRejectSetupScissor(left, top, right, bottom)) {
2225        return DrawGlInfo::kStatusDone;
2226    }
2227
2228    mCaches.activeTexture(0);
2229    Texture* texture = mCaches.textureCache.getTransient(bitmap);
2230    const AutoTexture autoCleanup(texture);
2231
2232    if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
2233        drawAlphaBitmap(texture, left, top, paint);
2234    } else {
2235        drawTextureRect(left, top, right, bottom, texture, paint);
2236    }
2237
2238    return DrawGlInfo::kStatusDrew;
2239}
2240
2241status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
2242        float* vertices, int* colors, SkPaint* paint) {
2243    if (!vertices || mSnapshot->isIgnored()) {
2244        return DrawGlInfo::kStatusDone;
2245    }
2246
2247    // TODO: use quickReject on bounds from vertices
2248    mCaches.enableScissor();
2249
2250    float left = FLT_MAX;
2251    float top = FLT_MAX;
2252    float right = FLT_MIN;
2253    float bottom = FLT_MIN;
2254
2255    const uint32_t count = meshWidth * meshHeight * 6;
2256
2257    ColorTextureVertex mesh[count];
2258    ColorTextureVertex* vertex = mesh;
2259
2260    bool cleanupColors = false;
2261    if (!colors) {
2262        uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1);
2263        colors = new int[colorsCount];
2264        memset(colors, 0xff, colorsCount * sizeof(int));
2265        cleanupColors = true;
2266    }
2267
2268    mCaches.activeTexture(0);
2269    Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap);
2270    const UvMapper& mapper(getMapper(texture));
2271
2272    for (int32_t y = 0; y < meshHeight; y++) {
2273        for (int32_t x = 0; x < meshWidth; x++) {
2274            uint32_t i = (y * (meshWidth + 1) + x) * 2;
2275
2276            float u1 = float(x) / meshWidth;
2277            float u2 = float(x + 1) / meshWidth;
2278            float v1 = float(y) / meshHeight;
2279            float v2 = float(y + 1) / meshHeight;
2280
2281            mapper.map(u1, v1, u2, v2);
2282
2283            int ax = i + (meshWidth + 1) * 2;
2284            int ay = ax + 1;
2285            int bx = i;
2286            int by = bx + 1;
2287            int cx = i + 2;
2288            int cy = cx + 1;
2289            int dx = i + (meshWidth + 1) * 2 + 2;
2290            int dy = dx + 1;
2291
2292            ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
2293            ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
2294            ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
2295
2296            ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
2297            ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
2298            ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
2299
2300            left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx])));
2301            top = fminf(top, fminf(vertices[ay], fminf(vertices[by], vertices[cy])));
2302            right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx])));
2303            bottom = fmaxf(bottom, fmaxf(vertices[ay], fmaxf(vertices[by], vertices[cy])));
2304        }
2305    }
2306
2307    if (quickRejectSetupScissor(left, top, right, bottom)) {
2308        if (cleanupColors) delete[] colors;
2309        return DrawGlInfo::kStatusDone;
2310    }
2311
2312    if (!texture) {
2313        texture = mCaches.textureCache.get(bitmap);
2314        if (!texture) {
2315            if (cleanupColors) delete[] colors;
2316            return DrawGlInfo::kStatusDone;
2317        }
2318    }
2319    const AutoTexture autoCleanup(texture);
2320
2321    texture->setWrap(GL_CLAMP_TO_EDGE, true);
2322    texture->setFilter(FILTER(paint), true);
2323
2324    int alpha;
2325    SkXfermode::Mode mode;
2326    getAlphaAndMode(paint, &alpha, &mode);
2327
2328    float a = alpha / 255.0f;
2329
2330    if (hasLayer()) {
2331        dirtyLayer(left, top, right, bottom, currentTransform());
2332    }
2333
2334    setupDraw();
2335    setupDrawWithTextureAndColor();
2336    setupDrawColor(a, a, a, a);
2337    setupDrawColorFilter();
2338    setupDrawBlending(true, mode, false);
2339    setupDrawProgram();
2340    setupDrawDirtyRegionsDisabled();
2341    setupDrawModelView(kModelViewMode_TranslateAndScale, false, 0.0f, 0.0f, 1.0f, 1.0f);
2342    setupDrawTexture(texture->id);
2343    setupDrawPureColorUniforms();
2344    setupDrawColorFilterUniforms();
2345    setupDrawMesh(&mesh[0].x, &mesh[0].u, &mesh[0].r);
2346
2347    glDrawArrays(GL_TRIANGLES, 0, count);
2348
2349    int slot = mCaches.currentProgram->getAttrib("colors");
2350    if (slot >= 0) {
2351        glDisableVertexAttribArray(slot);
2352    }
2353
2354    if (cleanupColors) delete[] colors;
2355
2356    return DrawGlInfo::kStatusDrew;
2357}
2358
2359status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
2360         float srcLeft, float srcTop, float srcRight, float srcBottom,
2361         float dstLeft, float dstTop, float dstRight, float dstBottom,
2362         SkPaint* paint) {
2363    if (quickRejectSetupScissor(dstLeft, dstTop, dstRight, dstBottom)) {
2364        return DrawGlInfo::kStatusDone;
2365    }
2366
2367    mCaches.activeTexture(0);
2368    Texture* texture = getTexture(bitmap);
2369    if (!texture) return DrawGlInfo::kStatusDone;
2370    const AutoTexture autoCleanup(texture);
2371
2372    const float width = texture->width;
2373    const float height = texture->height;
2374
2375    float u1 = fmax(0.0f, srcLeft / width);
2376    float v1 = fmax(0.0f, srcTop / height);
2377    float u2 = fmin(1.0f, srcRight / width);
2378    float v2 = fmin(1.0f, srcBottom / height);
2379
2380    getMapper(texture).map(u1, v1, u2, v2);
2381
2382    mCaches.unbindMeshBuffer();
2383    resetDrawTextureTexCoords(u1, v1, u2, v2);
2384
2385    int alpha;
2386    SkXfermode::Mode mode;
2387    getAlphaAndMode(paint, &alpha, &mode);
2388
2389    texture->setWrap(GL_CLAMP_TO_EDGE, true);
2390
2391    float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft);
2392    float scaleY = (dstBottom - dstTop) / (srcBottom - srcTop);
2393
2394    bool scaled = scaleX != 1.0f || scaleY != 1.0f;
2395    // Apply a scale transform on the canvas only when a shader is in use
2396    // Skia handles the ratio between the dst and src rects as a scale factor
2397    // when a shader is set
2398    bool useScaleTransform = mDrawModifiers.mShader && scaled;
2399    bool ignoreTransform = false;
2400
2401    if (CC_LIKELY(currentTransform().isPureTranslate() && !useScaleTransform)) {
2402        float x = (int) floorf(dstLeft + currentTransform().getTranslateX() + 0.5f);
2403        float y = (int) floorf(dstTop + currentTransform().getTranslateY() + 0.5f);
2404
2405        dstRight = x + (dstRight - dstLeft);
2406        dstBottom = y + (dstBottom - dstTop);
2407
2408        dstLeft = x;
2409        dstTop = y;
2410
2411        texture->setFilter(scaled ? FILTER(paint) : GL_NEAREST, true);
2412        ignoreTransform = true;
2413    } else {
2414        texture->setFilter(FILTER(paint), true);
2415    }
2416
2417    if (CC_UNLIKELY(useScaleTransform)) {
2418        save(SkCanvas::kMatrix_SaveFlag);
2419        translate(dstLeft, dstTop);
2420        scale(scaleX, scaleY);
2421
2422        dstLeft = 0.0f;
2423        dstTop = 0.0f;
2424
2425        dstRight = srcRight - srcLeft;
2426        dstBottom = srcBottom - srcTop;
2427    }
2428
2429    if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
2430        int color = paint ? paint->getColor() : 0;
2431        drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom,
2432                texture->id, paint != NULL, color, alpha, mode,
2433                &mMeshVertices[0].x, &mMeshVertices[0].u,
2434                GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
2435    } else {
2436        drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom,
2437                texture->id, alpha / 255.0f, mode, texture->blend,
2438                &mMeshVertices[0].x, &mMeshVertices[0].u,
2439                GL_TRIANGLE_STRIP, gMeshCount, false, ignoreTransform);
2440    }
2441
2442    if (CC_UNLIKELY(useScaleTransform)) {
2443        restore();
2444    }
2445
2446    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
2447
2448    return DrawGlInfo::kStatusDrew;
2449}
2450
2451status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
2452        float left, float top, float right, float bottom, SkPaint* paint) {
2453    if (quickRejectSetupScissor(left, top, right, bottom)) {
2454        return DrawGlInfo::kStatusDone;
2455    }
2456
2457    AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
2458    const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(),
2459            right - left, bottom - top, patch);
2460
2461    return drawPatch(bitmap, mesh, entry, left, top, right, bottom, paint);
2462}
2463
2464status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
2465        float left, float top, float right, float bottom, SkPaint* paint) {
2466    if (quickRejectSetupScissor(left, top, right, bottom)) {
2467        return DrawGlInfo::kStatusDone;
2468    }
2469
2470    if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
2471        mCaches.activeTexture(0);
2472        Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
2473        if (!texture) return DrawGlInfo::kStatusDone;
2474        const AutoTexture autoCleanup(texture);
2475
2476        texture->setWrap(GL_CLAMP_TO_EDGE, true);
2477        texture->setFilter(GL_LINEAR, true);
2478
2479        int alpha;
2480        SkXfermode::Mode mode;
2481        getAlphaAndMode(paint, &alpha, &mode);
2482
2483        const bool pureTranslate = currentTransform().isPureTranslate();
2484        // Mark the current layer dirty where we are going to draw the patch
2485        if (hasLayer() && mesh->hasEmptyQuads) {
2486            const float offsetX = left + currentTransform().getTranslateX();
2487            const float offsetY = top + currentTransform().getTranslateY();
2488            const size_t count = mesh->quads.size();
2489            for (size_t i = 0; i < count; i++) {
2490                const Rect& bounds = mesh->quads.itemAt(i);
2491                if (CC_LIKELY(pureTranslate)) {
2492                    const float x = (int) floorf(bounds.left + offsetX + 0.5f);
2493                    const float y = (int) floorf(bounds.top + offsetY + 0.5f);
2494                    dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight());
2495                } else {
2496                    dirtyLayer(left + bounds.left, top + bounds.top,
2497                            left + bounds.right, top + bounds.bottom, currentTransform());
2498                }
2499            }
2500        }
2501
2502        bool ignoreTransform = false;
2503        if (CC_LIKELY(pureTranslate)) {
2504            const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
2505            const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
2506
2507            right = x + right - left;
2508            bottom = y + bottom - top;
2509            left = x;
2510            top = y;
2511            ignoreTransform = true;
2512        }
2513        drawIndexedTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
2514                mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
2515                GL_TRIANGLES, mesh->indexCount, false, ignoreTransform,
2516                mCaches.patchCache.getMeshBuffer(), kModelViewMode_Translate, !mesh->hasEmptyQuads);
2517    }
2518
2519    return DrawGlInfo::kStatusDrew;
2520}
2521
2522/**
2523 * Important note: this method is intended to draw batches of 9-patch objects and
2524 * will not set the scissor enable or dirty the current layer, if any.
2525 * The caller is responsible for properly dirtying the current layer.
2526 */
2527status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry,
2528        TextureVertex* vertices, uint32_t indexCount, SkPaint* paint) {
2529    mCaches.activeTexture(0);
2530    Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
2531    if (!texture) return DrawGlInfo::kStatusDone;
2532    const AutoTexture autoCleanup(texture);
2533
2534    texture->setWrap(GL_CLAMP_TO_EDGE, true);
2535    texture->setFilter(GL_LINEAR, true);
2536
2537    int alpha;
2538    SkXfermode::Mode mode;
2539    getAlphaAndMode(paint, &alpha, &mode);
2540
2541    drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f,
2542            mode, texture->blend, &vertices[0].x, &vertices[0].u,
2543            GL_TRIANGLES, indexCount, false, true, 0, kModelViewMode_Translate, false);
2544
2545    return DrawGlInfo::kStatusDrew;
2546}
2547
2548status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
2549        bool useOffset) {
2550    // not missing call to quickReject/dirtyLayer, always done at a higher level
2551
2552    if (!vertexBuffer.getVertexCount()) {
2553        // no vertices to draw
2554        return DrawGlInfo::kStatusDone;
2555    }
2556
2557    int color = paint->getColor();
2558    SkXfermode::Mode mode = getXfermode(paint->getXfermode());
2559    bool isAA = paint->isAntiAlias();
2560
2561    setupDraw();
2562    setupDrawNoTexture();
2563    if (isAA) setupDrawAA();
2564    setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
2565    setupDrawColorFilter();
2566    setupDrawShader();
2567    setupDrawBlending(isAA, mode);
2568    setupDrawProgram();
2569    setupDrawModelView(kModelViewMode_Translate, useOffset, 0, 0, 0, 0);
2570    setupDrawColorUniforms();
2571    setupDrawColorFilterUniforms();
2572    setupDrawShaderUniforms();
2573
2574    const void* vertices = vertexBuffer.getBuffer();
2575    bool force = mCaches.unbindMeshBuffer();
2576    mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride);
2577    mCaches.resetTexCoordsVertexPointer();
2578    mCaches.unbindIndicesBuffer();
2579
2580    int alphaSlot = -1;
2581    if (isAA) {
2582        void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset;
2583        alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
2584
2585        // TODO: avoid enable/disable in back to back uses of the alpha attribute
2586        glEnableVertexAttribArray(alphaSlot);
2587        glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
2588    }
2589
2590    glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
2591
2592    if (isAA) {
2593        glDisableVertexAttribArray(alphaSlot);
2594    }
2595
2596    return DrawGlInfo::kStatusDrew;
2597}
2598
2599/**
2600 * Renders a convex path via tessellation. For AA paths, this function uses a similar approach to
2601 * that of AA lines in the drawLines() function.  We expand the convex path by a half pixel in
2602 * screen space in all directions. However, instead of using a fragment shader to compute the
2603 * translucency of the color from its position, we simply use a varying parameter to define how far
2604 * a given pixel is from the edge. For non-AA paths, the expansion and alpha varying are not used.
2605 *
2606 * Doesn't yet support joins, caps, or path effects.
2607 */
2608status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) {
2609    VertexBuffer vertexBuffer;
2610    // TODO: try clipping large paths to viewport
2611    PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer);
2612
2613    if (hasLayer()) {
2614        SkRect bounds = path.getBounds();
2615        PathTessellator::expandBoundsForStroke(bounds, paint);
2616        dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
2617    }
2618
2619    return drawVertexBuffer(vertexBuffer, paint);
2620}
2621
2622/**
2623 * We create tristrips for the lines much like shape stroke tessellation, using a per-vertex alpha
2624 * and additional geometry for defining an alpha slope perimeter.
2625 *
2626 * Using GL_LINES can be difficult because the rasterization rules for those lines produces some
2627 * unexpected results, and may vary between hardware devices. Previously we used a varying-base
2628 * in-shader alpha region, but found it to be taxing on some GPUs.
2629 *
2630 * TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce
2631 * memory transfer by removing need for degenerate vertices.
2632 */
2633status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
2634    if (mSnapshot->isIgnored() || count < 4) return DrawGlInfo::kStatusDone;
2635
2636    count &= ~0x3; // round down to nearest four
2637
2638    VertexBuffer buffer;
2639    SkRect bounds;
2640    PathTessellator::tessellateLines(points, count, paint, mSnapshot->transform, bounds, buffer);
2641
2642    // can't pass paint, since style would be checked for outset. outset done by tessellation.
2643    if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
2644        return DrawGlInfo::kStatusDone;
2645    }
2646
2647    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
2648
2649    bool useOffset = !paint->isAntiAlias();
2650    return drawVertexBuffer(buffer, paint, useOffset);
2651}
2652
2653status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
2654    if (mSnapshot->isIgnored() || count < 2) return DrawGlInfo::kStatusDone;
2655
2656    count &= ~0x1; // round down to nearest two
2657
2658    VertexBuffer buffer;
2659    SkRect bounds;
2660    PathTessellator::tessellatePoints(points, count, paint, mSnapshot->transform, bounds, buffer);
2661
2662    // can't pass paint, since style would be checked for outset. outset done by tessellation.
2663    if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
2664        return DrawGlInfo::kStatusDone;
2665    }
2666
2667    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
2668
2669    bool useOffset = !paint->isAntiAlias();
2670    return drawVertexBuffer(buffer, paint, useOffset);
2671}
2672
2673status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
2674    // No need to check against the clip, we fill the clip region
2675    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
2676
2677    Rect& clip(*mSnapshot->clipRect);
2678    clip.snapToPixelBoundaries();
2679
2680    drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
2681
2682    return DrawGlInfo::kStatusDrew;
2683}
2684
2685status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* texture,
2686        SkPaint* paint) {
2687    if (!texture) return DrawGlInfo::kStatusDone;
2688    const AutoTexture autoCleanup(texture);
2689
2690    const float x = left + texture->left - texture->offset;
2691    const float y = top + texture->top - texture->offset;
2692
2693    drawPathTexture(texture, x, y, paint);
2694
2695    return DrawGlInfo::kStatusDrew;
2696}
2697
2698status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
2699        float rx, float ry, SkPaint* p) {
2700    if (mSnapshot->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) ||
2701            (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
2702        return DrawGlInfo::kStatusDone;
2703    }
2704
2705    if (p->getPathEffect() != 0) {
2706        mCaches.activeTexture(0);
2707        const PathTexture* texture = mCaches.pathCache.getRoundRect(
2708                right - left, bottom - top, rx, ry, p);
2709        return drawShape(left, top, texture, p);
2710    }
2711
2712    SkPath path;
2713    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
2714    if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
2715        float outset = p->getStrokeWidth() / 2;
2716        rect.outset(outset, outset);
2717        rx += outset;
2718        ry += outset;
2719    }
2720    path.addRoundRect(rect, rx, ry);
2721    return drawConvexPath(path, p);
2722}
2723
2724status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) {
2725    if (mSnapshot->isIgnored() || quickRejectSetupScissor(x - radius, y - radius,
2726            x + radius, y + radius, p) ||
2727            (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
2728        return DrawGlInfo::kStatusDone;
2729    }
2730    if (p->getPathEffect() != 0) {
2731        mCaches.activeTexture(0);
2732        const PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
2733        return drawShape(x - radius, y - radius, texture, p);
2734    }
2735
2736    SkPath path;
2737    if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
2738        path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
2739    } else {
2740        path.addCircle(x, y, radius);
2741    }
2742    return drawConvexPath(path, p);
2743}
2744
2745status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
2746        SkPaint* p) {
2747    if (mSnapshot->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) ||
2748            (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
2749        return DrawGlInfo::kStatusDone;
2750    }
2751
2752    if (p->getPathEffect() != 0) {
2753        mCaches.activeTexture(0);
2754        const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
2755        return drawShape(left, top, texture, p);
2756    }
2757
2758    SkPath path;
2759    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
2760    if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
2761        rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
2762    }
2763    path.addOval(rect);
2764    return drawConvexPath(path, p);
2765}
2766
2767status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
2768        float startAngle, float sweepAngle, bool useCenter, SkPaint* p) {
2769    if (mSnapshot->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) ||
2770            (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
2771        return DrawGlInfo::kStatusDone;
2772    }
2773
2774    if (fabs(sweepAngle) >= 360.0f) {
2775        return drawOval(left, top, right, bottom, p);
2776    }
2777
2778    // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
2779    if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
2780        mCaches.activeTexture(0);
2781        const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
2782                startAngle, sweepAngle, useCenter, p);
2783        return drawShape(left, top, texture, p);
2784    }
2785
2786    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
2787    if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
2788        rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
2789    }
2790
2791    SkPath path;
2792    if (useCenter) {
2793        path.moveTo(rect.centerX(), rect.centerY());
2794    }
2795    path.arcTo(rect, startAngle, sweepAngle, !useCenter);
2796    if (useCenter) {
2797        path.close();
2798    }
2799    return drawConvexPath(path, p);
2800}
2801
2802// See SkPaintDefaults.h
2803#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
2804
2805status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) {
2806    if (mSnapshot->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) ||
2807            (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) {
2808        return DrawGlInfo::kStatusDone;
2809    }
2810
2811    if (p->getStyle() != SkPaint::kFill_Style) {
2812        // only fill style is supported by drawConvexPath, since others have to handle joins
2813        if (p->getPathEffect() != 0 || p->getStrokeJoin() != SkPaint::kMiter_Join ||
2814                p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
2815            mCaches.activeTexture(0);
2816            const PathTexture* texture =
2817                    mCaches.pathCache.getRect(right - left, bottom - top, p);
2818            return drawShape(left, top, texture, p);
2819        }
2820
2821        SkPath path;
2822        SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
2823        if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
2824            rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
2825        }
2826        path.addRect(rect);
2827        return drawConvexPath(path, p);
2828    }
2829
2830    if (p->isAntiAlias() && !currentTransform().isSimple()) {
2831        SkPath path;
2832        path.addRect(left, top, right, bottom);
2833        return drawConvexPath(path, p);
2834    } else {
2835        drawColorRect(left, top, right, bottom, p->getColor(), getXfermode(p->getXfermode()));
2836        return DrawGlInfo::kStatusDrew;
2837    }
2838}
2839
2840void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
2841        const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
2842        float x, float y) {
2843    mCaches.activeTexture(0);
2844
2845    // NOTE: The drop shadow will not perform gamma correction
2846    //       if shader-based correction is enabled
2847    mCaches.dropShadowCache.setFontRenderer(fontRenderer);
2848    const ShadowTexture* shadow = mCaches.dropShadowCache.get(
2849            paint, text, bytesCount, count, mDrawModifiers.mShadowRadius, positions);
2850    // If the drop shadow exceeds the max texture size or couldn't be
2851    // allocated, skip drawing
2852    if (!shadow) return;
2853    const AutoTexture autoCleanup(shadow);
2854
2855    const float sx = x - shadow->left + mDrawModifiers.mShadowDx;
2856    const float sy = y - shadow->top + mDrawModifiers.mShadowDy;
2857
2858    const int shadowAlpha = ((mDrawModifiers.mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
2859    int shadowColor = mDrawModifiers.mShadowColor;
2860    if (mDrawModifiers.mShader) {
2861        shadowColor = 0xffffffff;
2862    }
2863
2864    setupDraw();
2865    setupDrawWithTexture(true);
2866    setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
2867    setupDrawColorFilter();
2868    setupDrawShader();
2869    setupDrawBlending(true, mode);
2870    setupDrawProgram();
2871    setupDrawModelView(kModelViewMode_TranslateAndScale, false,
2872            sx, sy, sx + shadow->width, sy + shadow->height);
2873    setupDrawTexture(shadow->id);
2874    setupDrawPureColorUniforms();
2875    setupDrawColorFilterUniforms();
2876    setupDrawShaderUniforms();
2877    setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
2878
2879    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
2880}
2881
2882bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
2883    float alpha = (mDrawModifiers.mHasShadow ? 1.0f : paint->getAlpha()) * mSnapshot->alpha;
2884    return alpha == 0.0f && getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
2885}
2886
2887status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
2888        const float* positions, SkPaint* paint) {
2889    if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
2890        return DrawGlInfo::kStatusDone;
2891    }
2892
2893    // NOTE: Skia does not support perspective transform on drawPosText yet
2894    if (!currentTransform().isSimple()) {
2895        return DrawGlInfo::kStatusDone;
2896    }
2897
2898    mCaches.enableScissor();
2899
2900    float x = 0.0f;
2901    float y = 0.0f;
2902    const bool pureTranslate = currentTransform().isPureTranslate();
2903    if (pureTranslate) {
2904        x = (int) floorf(x + currentTransform().getTranslateX() + 0.5f);
2905        y = (int) floorf(y + currentTransform().getTranslateY() + 0.5f);
2906    }
2907
2908    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2909    fontRenderer.setFont(paint, mat4::identity());
2910
2911    int alpha;
2912    SkXfermode::Mode mode;
2913    getAlphaAndMode(paint, &alpha, &mode);
2914
2915    if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
2916        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
2917                alpha, mode, 0.0f, 0.0f);
2918    }
2919
2920    // Pick the appropriate texture filtering
2921    bool linearFilter = currentTransform().changesBounds();
2922    if (pureTranslate && !linearFilter) {
2923        linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
2924    }
2925    fontRenderer.setTextureFiltering(linearFilter);
2926
2927    const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip();
2928    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
2929
2930    const bool hasActiveLayer = hasLayer();
2931
2932    TextSetupFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
2933    if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
2934            positions, hasActiveLayer ? &bounds : NULL, &functor)) {
2935        if (hasActiveLayer) {
2936            if (!pureTranslate) {
2937                currentTransform().mapRect(bounds);
2938            }
2939            dirtyLayerUnchecked(bounds, getRegion());
2940        }
2941    }
2942
2943    return DrawGlInfo::kStatusDrew;
2944}
2945
2946mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const {
2947    mat4 fontTransform;
2948    if (CC_LIKELY(transform.isPureTranslate())) {
2949        fontTransform = mat4::identity();
2950    } else {
2951        if (CC_UNLIKELY(transform.isPerspective())) {
2952            fontTransform = mat4::identity();
2953        } else {
2954            float sx, sy;
2955            currentTransform().decomposeScale(sx, sy);
2956            fontTransform.loadScale(sx, sy, 1.0f);
2957        }
2958    }
2959    return fontTransform;
2960}
2961
2962status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
2963        const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
2964        DrawOpMode drawOpMode) {
2965
2966    if (drawOpMode == kDrawOpMode_Immediate) {
2967        // The checks for corner-case ignorable text and quick rejection is only done for immediate
2968        // drawing as ops from DeferredDisplayList are already filtered for these
2969        if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint) ||
2970                quickRejectSetupScissor(bounds)) {
2971            return DrawGlInfo::kStatusDone;
2972        }
2973    }
2974
2975    const float oldX = x;
2976    const float oldY = y;
2977
2978    const mat4& transform = currentTransform();
2979    const bool pureTranslate = transform.isPureTranslate();
2980
2981    if (CC_LIKELY(pureTranslate)) {
2982        x = (int) floorf(x + transform.getTranslateX() + 0.5f);
2983        y = (int) floorf(y + transform.getTranslateY() + 0.5f);
2984    }
2985
2986    int alpha;
2987    SkXfermode::Mode mode;
2988    getAlphaAndMode(paint, &alpha, &mode);
2989
2990    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
2991
2992    if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
2993        fontRenderer.setFont(paint, mat4::identity());
2994        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
2995                alpha, mode, oldX, oldY);
2996    }
2997
2998    const bool hasActiveLayer = hasLayer();
2999
3000    // We only pass a partial transform to the font renderer. That partial
3001    // matrix defines how glyphs are rasterized. Typically we want glyphs
3002    // to be rasterized at their final size on screen, which means the partial
3003    // matrix needs to take the scale factor into account.
3004    // When a partial matrix is used to transform glyphs during rasterization,
3005    // the mesh is generated with the inverse transform (in the case of scale,
3006    // the mesh is generated at 1.0 / scale for instance.) This allows us to
3007    // apply the full transform matrix at draw time in the vertex shader.
3008    // Applying the full matrix in the shader is the easiest way to handle
3009    // rotation and perspective and allows us to always generated quads in the
3010    // font renderer which greatly simplifies the code, clipping in particular.
3011    mat4 fontTransform = findBestFontTransform(transform);
3012    fontRenderer.setFont(paint, fontTransform);
3013
3014    // Pick the appropriate texture filtering
3015    bool linearFilter = !pureTranslate || fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
3016    fontRenderer.setTextureFiltering(linearFilter);
3017
3018    // TODO: Implement better clipping for scaled/rotated text
3019    const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect;
3020    Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
3021
3022    bool status;
3023    TextSetupFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
3024
3025    // don't call issuedrawcommand, do it at end of batch
3026    bool forceFinish = (drawOpMode != kDrawOpMode_Defer);
3027    if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
3028        SkPaint paintCopy(*paint);
3029        paintCopy.setTextAlign(SkPaint::kLeft_Align);
3030        status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
3031                positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
3032    } else {
3033        status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
3034                positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
3035    }
3036
3037    if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) {
3038        if (!pureTranslate) {
3039            transform.mapRect(layerBounds);
3040        }
3041        dirtyLayerUnchecked(layerBounds, getRegion());
3042    }
3043
3044    drawTextDecorations(totalAdvance, oldX, oldY, paint);
3045
3046    return DrawGlInfo::kStatusDrew;
3047}
3048
3049status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
3050        float hOffset, float vOffset, SkPaint* paint) {
3051    if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
3052        return DrawGlInfo::kStatusDone;
3053    }
3054
3055    // TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
3056    mCaches.enableScissor();
3057
3058    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
3059    fontRenderer.setFont(paint, mat4::identity());
3060    fontRenderer.setTextureFiltering(true);
3061
3062    int alpha;
3063    SkXfermode::Mode mode;
3064    getAlphaAndMode(paint, &alpha, &mode);
3065    TextSetupFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
3066
3067    const Rect* clip = &mSnapshot->getLocalClip();
3068    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
3069
3070    const bool hasActiveLayer = hasLayer();
3071
3072    if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
3073            hOffset, vOffset, hasActiveLayer ? &bounds : NULL, &functor)) {
3074        if (hasActiveLayer) {
3075            currentTransform().mapRect(bounds);
3076            dirtyLayerUnchecked(bounds, getRegion());
3077        }
3078    }
3079
3080    return DrawGlInfo::kStatusDrew;
3081}
3082
3083status_t OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
3084    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
3085
3086    mCaches.activeTexture(0);
3087
3088    const PathTexture* texture = mCaches.pathCache.get(path, paint);
3089    if (!texture) return DrawGlInfo::kStatusDone;
3090    const AutoTexture autoCleanup(texture);
3091
3092    const float x = texture->left - texture->offset;
3093    const float y = texture->top - texture->offset;
3094
3095    drawPathTexture(texture, x, y, paint);
3096
3097    return DrawGlInfo::kStatusDrew;
3098}
3099
3100status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
3101    if (!layer) {
3102        return DrawGlInfo::kStatusDone;
3103    }
3104
3105    mat4* transform = NULL;
3106    if (layer->isTextureLayer()) {
3107        transform = &layer->getTransform();
3108        if (!transform->isIdentity()) {
3109            save(0);
3110            currentTransform().multiply(*transform);
3111        }
3112    }
3113
3114    bool clipRequired = false;
3115    const bool rejected = calculateQuickRejectForScissor(x, y,
3116            x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, false);
3117
3118    if (rejected) {
3119        if (transform && !transform->isIdentity()) {
3120            restore();
3121        }
3122        return DrawGlInfo::kStatusDone;
3123    }
3124
3125    updateLayer(layer, true);
3126
3127    mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
3128    mCaches.activeTexture(0);
3129
3130    if (CC_LIKELY(!layer->region.isEmpty())) {
3131        SkiaColorFilter* oldFilter = mDrawModifiers.mColorFilter;
3132        mDrawModifiers.mColorFilter = layer->getColorFilter();
3133
3134        if (layer->region.isRect()) {
3135            DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
3136                    composeLayerRect(layer, layer->regionRect));
3137        } else if (layer->mesh) {
3138            const float a = getLayerAlpha(layer);
3139            setupDraw();
3140            setupDrawWithTexture();
3141            setupDrawColor(a, a, a, a);
3142            setupDrawColorFilter();
3143            setupDrawBlending(layer->isBlend() || a < 1.0f, layer->getMode(), false);
3144            setupDrawProgram();
3145            setupDrawPureColorUniforms();
3146            setupDrawColorFilterUniforms();
3147            setupDrawTexture(layer->getTexture());
3148            if (CC_LIKELY(currentTransform().isPureTranslate())) {
3149                int tx = (int) floorf(x + currentTransform().getTranslateX() + 0.5f);
3150                int ty = (int) floorf(y + currentTransform().getTranslateY() + 0.5f);
3151
3152                layer->setFilter(GL_NEAREST);
3153                setupDrawModelView(kModelViewMode_Translate, false, tx, ty,
3154                        tx + layer->layer.getWidth(), ty + layer->layer.getHeight(), true);
3155            } else {
3156                layer->setFilter(GL_LINEAR);
3157                setupDrawModelView(kModelViewMode_Translate, false, x, y,
3158                        x + layer->layer.getWidth(), y + layer->layer.getHeight());
3159            }
3160
3161            TextureVertex* mesh = &layer->mesh[0];
3162            GLsizei elementsCount = layer->meshElementCount;
3163
3164            while (elementsCount > 0) {
3165                GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
3166
3167                setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
3168                DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
3169                        glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL));
3170
3171                elementsCount -= drawCount;
3172                // Though there are 4 vertices in a quad, we use 6 indices per
3173                // quad to draw with GL_TRIANGLES
3174                mesh += (drawCount / 6) * 4;
3175            }
3176
3177#if DEBUG_LAYERS_AS_REGIONS
3178            drawRegionRectsDebug(layer->region);
3179#endif
3180        }
3181
3182        mDrawModifiers.mColorFilter = oldFilter;
3183
3184        if (layer->debugDrawUpdate) {
3185            layer->debugDrawUpdate = false;
3186            drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(),
3187                    0x7f00ff00, SkXfermode::kSrcOver_Mode);
3188        }
3189    }
3190    layer->hasDrawnSinceUpdate = true;
3191
3192    if (transform && !transform->isIdentity()) {
3193        restore();
3194    }
3195
3196    return DrawGlInfo::kStatusDrew;
3197}
3198
3199///////////////////////////////////////////////////////////////////////////////
3200// Shaders
3201///////////////////////////////////////////////////////////////////////////////
3202
3203void OpenGLRenderer::resetShader() {
3204    mDrawModifiers.mShader = NULL;
3205}
3206
3207void OpenGLRenderer::setupShader(SkiaShader* shader) {
3208    mDrawModifiers.mShader = shader;
3209    if (mDrawModifiers.mShader) {
3210        mDrawModifiers.mShader->setCaches(mCaches);
3211    }
3212}
3213
3214///////////////////////////////////////////////////////////////////////////////
3215// Color filters
3216///////////////////////////////////////////////////////////////////////////////
3217
3218void OpenGLRenderer::resetColorFilter() {
3219    mDrawModifiers.mColorFilter = NULL;
3220}
3221
3222void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) {
3223    mDrawModifiers.mColorFilter = filter;
3224}
3225
3226///////////////////////////////////////////////////////////////////////////////
3227// Drop shadow
3228///////////////////////////////////////////////////////////////////////////////
3229
3230void OpenGLRenderer::resetShadow() {
3231    mDrawModifiers.mHasShadow = false;
3232}
3233
3234void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
3235    mDrawModifiers.mHasShadow = true;
3236    mDrawModifiers.mShadowRadius = radius;
3237    mDrawModifiers.mShadowDx = dx;
3238    mDrawModifiers.mShadowDy = dy;
3239    mDrawModifiers.mShadowColor = color;
3240}
3241
3242///////////////////////////////////////////////////////////////////////////////
3243// Draw filters
3244///////////////////////////////////////////////////////////////////////////////
3245
3246void OpenGLRenderer::resetPaintFilter() {
3247    // when clearing the PaintFilter, the masks should also be cleared for simple DrawModifier
3248    // comparison, see MergingDrawBatch::canMergeWith
3249    mDrawModifiers.mHasDrawFilter = false;
3250    mDrawModifiers.mPaintFilterClearBits = 0;
3251    mDrawModifiers.mPaintFilterSetBits = 0;
3252}
3253
3254void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
3255    mDrawModifiers.mHasDrawFilter = true;
3256    mDrawModifiers.mPaintFilterClearBits = clearBits & SkPaint::kAllFlags;
3257    mDrawModifiers.mPaintFilterSetBits = setBits & SkPaint::kAllFlags;
3258}
3259
3260SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) {
3261    if (CC_LIKELY(!mDrawModifiers.mHasDrawFilter || !paint)) {
3262        return paint;
3263    }
3264
3265    uint32_t flags = paint->getFlags();
3266
3267    mFilteredPaint = *paint;
3268    mFilteredPaint.setFlags((flags & ~mDrawModifiers.mPaintFilterClearBits) |
3269            mDrawModifiers.mPaintFilterSetBits);
3270
3271    return &mFilteredPaint;
3272}
3273
3274///////////////////////////////////////////////////////////////////////////////
3275// Drawing implementation
3276///////////////////////////////////////////////////////////////////////////////
3277
3278Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) {
3279    Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap);
3280    if (!texture) {
3281        return mCaches.textureCache.get(bitmap);
3282    }
3283    return texture;
3284}
3285
3286void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
3287        float x, float y, SkPaint* paint) {
3288    if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) {
3289        return;
3290    }
3291
3292    int alpha;
3293    SkXfermode::Mode mode;
3294    getAlphaAndMode(paint, &alpha, &mode);
3295
3296    setupDraw();
3297    setupDrawWithTexture(true);
3298    setupDrawAlpha8Color(paint->getColor(), alpha);
3299    setupDrawColorFilter();
3300    setupDrawShader();
3301    setupDrawBlending(true, mode);
3302    setupDrawProgram();
3303    setupDrawModelView(kModelViewMode_TranslateAndScale, false,
3304            x, y, x + texture->width, y + texture->height);
3305    setupDrawTexture(texture->id);
3306    setupDrawPureColorUniforms();
3307    setupDrawColorFilterUniforms();
3308    setupDrawShaderUniforms();
3309    setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
3310
3311    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
3312}
3313
3314// Same values used by Skia
3315#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
3316#define kStdUnderline_Offset    (1.0f / 9.0f)
3317#define kStdUnderline_Thickness (1.0f / 18.0f)
3318
3319void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y, SkPaint* paint) {
3320    // Handle underline and strike-through
3321    uint32_t flags = paint->getFlags();
3322    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
3323        SkPaint paintCopy(*paint);
3324
3325        if (CC_LIKELY(underlineWidth > 0.0f)) {
3326            const float textSize = paintCopy.getTextSize();
3327            const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
3328
3329            const float left = x;
3330            float top = 0.0f;
3331
3332            int linesCount = 0;
3333            if (flags & SkPaint::kUnderlineText_Flag) linesCount++;
3334            if (flags & SkPaint::kStrikeThruText_Flag) linesCount++;
3335
3336            const int pointsCount = 4 * linesCount;
3337            float points[pointsCount];
3338            int currentPoint = 0;
3339
3340            if (flags & SkPaint::kUnderlineText_Flag) {
3341                top = y + textSize * kStdUnderline_Offset;
3342                points[currentPoint++] = left;
3343                points[currentPoint++] = top;
3344                points[currentPoint++] = left + underlineWidth;
3345                points[currentPoint++] = top;
3346            }
3347
3348            if (flags & SkPaint::kStrikeThruText_Flag) {
3349                top = y + textSize * kStdStrikeThru_Offset;
3350                points[currentPoint++] = left;
3351                points[currentPoint++] = top;
3352                points[currentPoint++] = left + underlineWidth;
3353                points[currentPoint++] = top;
3354            }
3355
3356            paintCopy.setStrokeWidth(strokeWidth);
3357
3358            drawLines(&points[0], pointsCount, &paintCopy);
3359        }
3360    }
3361}
3362
3363status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint) {
3364    if (mSnapshot->isIgnored()) {
3365        return DrawGlInfo::kStatusDone;
3366    }
3367
3368    int color = paint->getColor();
3369    // If a shader is set, preserve only the alpha
3370    if (mDrawModifiers.mShader) {
3371        color |= 0x00ffffff;
3372    }
3373    SkXfermode::Mode mode = getXfermode(paint->getXfermode());
3374
3375    return drawColorRects(rects, count, color, mode);
3376}
3377
3378status_t OpenGLRenderer::drawShadow(const mat4& casterTransform, float casterAlpha,
3379        float width, float height) {
3380    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
3381
3382    // For now, always and scissor
3383    // TODO: use quickReject
3384    mCaches.enableScissor();
3385
3386    SkPaint paint;
3387    paint.setColor(mCaches.propertyShadowStrength << 24);
3388    paint.setAntiAlias(true); // want to use AlphaVertex
3389
3390    VertexBuffer shadowVertexBuffer;
3391    ShadowTessellator::tessellateAmbientShadow(width, height, casterTransform,
3392            shadowVertexBuffer);
3393    return drawVertexBuffer(shadowVertexBuffer, &paint);
3394}
3395
3396status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
3397        SkXfermode::Mode mode, bool ignoreTransform, bool dirty, bool clip) {
3398    if (count == 0) {
3399        return DrawGlInfo::kStatusDone;
3400    }
3401
3402    float left = FLT_MAX;
3403    float top = FLT_MAX;
3404    float right = FLT_MIN;
3405    float bottom = FLT_MIN;
3406
3407    Vertex mesh[count];
3408    Vertex* vertex = mesh;
3409
3410    for (int index = 0; index < count; index += 4) {
3411        float l = rects[index + 0];
3412        float t = rects[index + 1];
3413        float r = rects[index + 2];
3414        float b = rects[index + 3];
3415
3416        Vertex::set(vertex++, l, t);
3417        Vertex::set(vertex++, r, t);
3418        Vertex::set(vertex++, l, b);
3419        Vertex::set(vertex++, r, b);
3420
3421        left = fminf(left, l);
3422        top = fminf(top, t);
3423        right = fmaxf(right, r);
3424        bottom = fmaxf(bottom, b);
3425    }
3426
3427    if (clip && quickRejectSetupScissor(left, top, right, bottom)) {
3428        return DrawGlInfo::kStatusDone;
3429    }
3430
3431    setupDraw();
3432    setupDrawNoTexture();
3433    setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
3434    setupDrawShader();
3435    setupDrawColorFilter();
3436    setupDrawBlending(mode);
3437    setupDrawProgram();
3438    setupDrawDirtyRegionsDisabled();
3439    setupDrawModelView(kModelViewMode_Translate, false,
3440            0.0f, 0.0f, 0.0f, 0.0f, ignoreTransform);
3441    setupDrawColorUniforms();
3442    setupDrawShaderUniforms();
3443    setupDrawColorFilterUniforms();
3444
3445    if (dirty && hasLayer()) {
3446        dirtyLayer(left, top, right, bottom, currentTransform());
3447    }
3448
3449    issueIndexedQuadDraw(&mesh[0], count / 4);
3450
3451    return DrawGlInfo::kStatusDrew;
3452}
3453
3454void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
3455        int color, SkXfermode::Mode mode, bool ignoreTransform) {
3456    // If a shader is set, preserve only the alpha
3457    if (mDrawModifiers.mShader) {
3458        color |= 0x00ffffff;
3459    }
3460
3461    setupDraw();
3462    setupDrawNoTexture();
3463    setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
3464    setupDrawShader();
3465    setupDrawColorFilter();
3466    setupDrawBlending(mode);
3467    setupDrawProgram();
3468    setupDrawModelView(kModelViewMode_TranslateAndScale, false,
3469            left, top, right, bottom, ignoreTransform);
3470    setupDrawColorUniforms();
3471    setupDrawShaderUniforms(ignoreTransform);
3472    setupDrawColorFilterUniforms();
3473    setupDrawSimpleMesh();
3474
3475    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
3476}
3477
3478void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
3479        Texture* texture, SkPaint* paint) {
3480    int alpha;
3481    SkXfermode::Mode mode;
3482    getAlphaAndMode(paint, &alpha, &mode);
3483
3484    texture->setWrap(GL_CLAMP_TO_EDGE, true);
3485
3486    GLvoid* vertices = (GLvoid*) NULL;
3487    GLvoid* texCoords = (GLvoid*) gMeshTextureOffset;
3488
3489    if (texture->uvMapper) {
3490        vertices = &mMeshVertices[0].x;
3491        texCoords = &mMeshVertices[0].u;
3492
3493        Rect uvs(0.0f, 0.0f, 1.0f, 1.0f);
3494        texture->uvMapper->map(uvs);
3495
3496        resetDrawTextureTexCoords(uvs.left, uvs.top, uvs.right, uvs.bottom);
3497    }
3498
3499    if (CC_LIKELY(currentTransform().isPureTranslate())) {
3500        const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
3501        const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
3502
3503        texture->setFilter(GL_NEAREST, true);
3504        drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
3505                alpha / 255.0f, mode, texture->blend, vertices, texCoords,
3506                GL_TRIANGLE_STRIP, gMeshCount, false, true);
3507    } else {
3508        texture->setFilter(FILTER(paint), true);
3509        drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
3510                texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount);
3511    }
3512
3513    if (texture->uvMapper) {
3514        resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
3515    }
3516}
3517
3518void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
3519        GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) {
3520    drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend,
3521            (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount);
3522}
3523
3524void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
3525        GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
3526        GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
3527        bool swapSrcDst, bool ignoreTransform, GLuint vbo,
3528        ModelViewMode modelViewMode, bool dirty) {
3529
3530    setupDraw();
3531    setupDrawWithTexture();
3532    setupDrawColor(alpha, alpha, alpha, alpha);
3533    setupDrawColorFilter();
3534    setupDrawBlending(blend, mode, swapSrcDst);
3535    setupDrawProgram();
3536    if (!dirty) setupDrawDirtyRegionsDisabled();
3537    setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform);
3538    setupDrawTexture(texture);
3539    setupDrawPureColorUniforms();
3540    setupDrawColorFilterUniforms();
3541    setupDrawMesh(vertices, texCoords, vbo);
3542
3543    glDrawArrays(drawMode, 0, elementsCount);
3544}
3545
3546void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, float bottom,
3547        GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
3548        GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
3549        bool swapSrcDst, bool ignoreTransform, GLuint vbo,
3550        ModelViewMode modelViewMode, bool dirty) {
3551
3552    setupDraw();
3553    setupDrawWithTexture();
3554    setupDrawColor(alpha, alpha, alpha, alpha);
3555    setupDrawColorFilter();
3556    setupDrawBlending(blend, mode, swapSrcDst);
3557    setupDrawProgram();
3558    if (!dirty) setupDrawDirtyRegionsDisabled();
3559    setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform);
3560    setupDrawTexture(texture);
3561    setupDrawPureColorUniforms();
3562    setupDrawColorFilterUniforms();
3563    setupDrawMeshIndices(vertices, texCoords, vbo);
3564
3565    glDrawElements(drawMode, elementsCount, GL_UNSIGNED_SHORT, NULL);
3566}
3567
3568void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
3569        GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
3570        GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
3571        bool ignoreTransform, ModelViewMode modelViewMode, bool dirty) {
3572
3573    setupDraw();
3574    setupDrawWithTexture(true);
3575    if (hasColor) {
3576        setupDrawAlpha8Color(color, alpha);
3577    }
3578    setupDrawColorFilter();
3579    setupDrawShader();
3580    setupDrawBlending(true, mode);
3581    setupDrawProgram();
3582    if (!dirty) setupDrawDirtyRegionsDisabled();
3583    setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform);
3584    setupDrawTexture(texture);
3585    setupDrawPureColorUniforms();
3586    setupDrawColorFilterUniforms();
3587    setupDrawShaderUniforms(ignoreTransform);
3588    setupDrawMesh(vertices, texCoords);
3589
3590    glDrawArrays(drawMode, 0, elementsCount);
3591}
3592
3593void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
3594        ProgramDescription& description, bool swapSrcDst) {
3595    if (mCountOverdraw) {
3596        if (!mCaches.blend) glEnable(GL_BLEND);
3597        if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) {
3598            glBlendFunc(GL_ONE, GL_ONE);
3599        }
3600
3601        mCaches.blend = true;
3602        mCaches.lastSrcMode = GL_ONE;
3603        mCaches.lastDstMode = GL_ONE;
3604
3605        return;
3606    }
3607
3608    blend = blend || mode != SkXfermode::kSrcOver_Mode;
3609
3610    if (blend) {
3611        // These blend modes are not supported by OpenGL directly and have
3612        // to be implemented using shaders. Since the shader will perform
3613        // the blending, turn blending off here
3614        // If the blend mode cannot be implemented using shaders, fall
3615        // back to the default SrcOver blend mode instead
3616        if (CC_UNLIKELY(mode > SkXfermode::kScreen_Mode)) {
3617            if (CC_UNLIKELY(mExtensions.hasFramebufferFetch())) {
3618                description.framebufferMode = mode;
3619                description.swapSrcDst = swapSrcDst;
3620
3621                if (mCaches.blend) {
3622                    glDisable(GL_BLEND);
3623                    mCaches.blend = false;
3624                }
3625
3626                return;
3627            } else {
3628                mode = SkXfermode::kSrcOver_Mode;
3629            }
3630        }
3631
3632        if (!mCaches.blend) {
3633            glEnable(GL_BLEND);
3634        }
3635
3636        GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src;
3637        GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst;
3638
3639        if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
3640            glBlendFunc(sourceMode, destMode);
3641            mCaches.lastSrcMode = sourceMode;
3642            mCaches.lastDstMode = destMode;
3643        }
3644    } else if (mCaches.blend) {
3645        glDisable(GL_BLEND);
3646    }
3647    mCaches.blend = blend;
3648}
3649
3650bool OpenGLRenderer::useProgram(Program* program) {
3651    if (!program->isInUse()) {
3652        if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove();
3653        program->use();
3654        mCaches.currentProgram = program;
3655        return false;
3656    }
3657    return true;
3658}
3659
3660void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
3661    TextureVertex* v = &mMeshVertices[0];
3662    TextureVertex::setUV(v++, u1, v1);
3663    TextureVertex::setUV(v++, u2, v1);
3664    TextureVertex::setUV(v++, u1, v2);
3665    TextureVertex::setUV(v++, u2, v2);
3666}
3667
3668void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const {
3669    getAlphaAndModeDirect(paint, alpha,  mode);
3670    if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
3671        // if drawing a layer, ignore the paint's alpha
3672        *alpha = mDrawModifiers.mOverrideLayerAlpha * 255;
3673    }
3674    *alpha *= mSnapshot->alpha;
3675}
3676
3677float OpenGLRenderer::getLayerAlpha(Layer* layer) const {
3678    float alpha;
3679    if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
3680        alpha = mDrawModifiers.mOverrideLayerAlpha;
3681    } else {
3682        alpha = layer->getAlpha() / 255.0f;
3683    }
3684    return alpha * mSnapshot->alpha;
3685}
3686
3687}; // namespace uirenderer
3688}; // namespace android
3689