Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2015 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 #include "BakedOpDispatcher.h"
     18 
     19 #include "BakedOpRenderer.h"
     20 #include "Caches.h"
     21 #include "DeferredLayerUpdater.h"
     22 #include "Glop.h"
     23 #include "GlopBuilder.h"
     24 #include "Patch.h"
     25 #include "PathTessellator.h"
     26 #include "renderstate/OffscreenBufferPool.h"
     27 #include "renderstate/RenderState.h"
     28 #include "utils/GLUtils.h"
     29 #include "VertexBuffer.h"
     30 
     31 #include <algorithm>
     32 #include <math.h>
     33 #include <SkPaintDefaults.h>
     34 #include <SkPathOps.h>
     35 
     36 namespace android {
     37 namespace uirenderer {
     38 
     39 static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds) {
     40     vertices[0] = { bounds.left,  bounds.top,    0, 0 };
     41     vertices[1] = { bounds.right, bounds.top,    1, 0 };
     42     vertices[2] = { bounds.left,  bounds.bottom, 0, 1 };
     43     vertices[3] = { bounds.right, bounds.bottom, 1, 1 };
     44 }
     45 
     46 void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
     47         const MergedBakedOpList& opList) {
     48 
     49     const BakedOpState& firstState = *(opList.states[0]);
     50     Bitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
     51 
     52     Texture* texture = renderer.caches().textureCache.get(bitmap);
     53     if (!texture) return;
     54     const AutoTexture autoCleanup(texture);
     55 
     56     TextureVertex vertices[opList.count * 4];
     57     for (size_t i = 0; i < opList.count; i++) {
     58         const BakedOpState& state = *(opList.states[i]);
     59         TextureVertex* rectVerts = &vertices[i * 4];
     60 
     61         // calculate unclipped bounds, since they'll determine texture coordinates
     62         Rect opBounds = state.op->unmappedBounds;
     63         state.computedState.transform.mapRect(opBounds);
     64         if (CC_LIKELY(state.computedState.transform.isPureTranslate())) {
     65             // pure translate, so snap (same behavior as onBitmapOp)
     66             opBounds.snapToPixelBoundaries();
     67         }
     68         storeTexturedRect(rectVerts, opBounds);
     69         renderer.dirtyRenderTarget(opBounds);
     70     }
     71 
     72     const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
     73             ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
     74     Glop glop;
     75     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
     76             .setRoundRectClipState(firstState.roundRectClipState)
     77             .setMeshTexturedIndexedQuads(vertices, opList.count * 6)
     78             .setFillTexturePaint(*texture, textureFillFlags, firstState.op->paint, firstState.alpha)
     79             .setTransform(Matrix4::identity(), TransformFlags::None)
     80             .setModelViewIdentityEmptyBounds()
     81             .build();
     82     ClipRect renderTargetClip(opList.clip);
     83     const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
     84     renderer.renderGlop(nullptr, clip, glop);
     85 }
     86 
     87 void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
     88         const MergedBakedOpList& opList) {
     89     const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op));
     90     const BakedOpState& firstState = *(opList.states[0]);
     91 
     92     // Batches will usually contain a small number of items so it's
     93     // worth performing a first iteration to count the exact number
     94     // of vertices we need in the new mesh
     95     uint32_t totalVertices = 0;
     96 
     97     for (size_t i = 0; i < opList.count; i++) {
     98         const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
     99 
    100         // TODO: cache mesh lookups
    101         const Patch* opMesh = renderer.caches().patchCache.get(
    102                 op.bitmap->width(), op.bitmap->height(),
    103                 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
    104         totalVertices += opMesh->verticesCount;
    105     }
    106 
    107     const bool dirtyRenderTarget = renderer.offscreenRenderTarget();
    108 
    109     uint32_t indexCount = 0;
    110 
    111     TextureVertex vertices[totalVertices];
    112     TextureVertex* vertex = &vertices[0];
    113     // Create a mesh that contains the transformed vertices for all the
    114     // 9-patch objects that are part of the batch. Note that onDefer()
    115     // enforces ops drawn by this function to have a pure translate or
    116     // identity matrix
    117     for (size_t i = 0; i < opList.count; i++) {
    118         const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
    119         const BakedOpState& state = *opList.states[i];
    120 
    121         // TODO: cache mesh lookups
    122         const Patch* opMesh = renderer.caches().patchCache.get(
    123                 op.bitmap->width(), op.bitmap->height(),
    124                 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
    125 
    126 
    127         uint32_t vertexCount = opMesh->verticesCount;
    128         if (vertexCount == 0) continue;
    129 
    130         // We use the bounds to know where to translate our vertices
    131         // Using patchOp->state.mBounds wouldn't work because these
    132         // bounds are clipped
    133         const float tx = floorf(state.computedState.transform.getTranslateX()
    134                 + op.unmappedBounds.left + 0.5f);
    135         const float ty = floorf(state.computedState.transform.getTranslateY()
    136                 + op.unmappedBounds.top + 0.5f);
    137 
    138         // Copy & transform all the vertices for the current operation
    139         TextureVertex* opVertices = opMesh->vertices.get();
    140         for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
    141             TextureVertex::set(vertex++,
    142                     opVertices->x + tx, opVertices->y + ty,
    143                     opVertices->u, opVertices->v);
    144         }
    145 
    146         // Dirty the current layer if possible. When the 9-patch does not
    147         // contain empty quads we can take a shortcut and simply set the
    148         // dirty rect to the object's bounds.
    149         if (dirtyRenderTarget) {
    150             if (!opMesh->hasEmptyQuads) {
    151                 renderer.dirtyRenderTarget(Rect(tx, ty,
    152                         tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.getHeight()));
    153             } else {
    154                 const size_t count = opMesh->quads.size();
    155                 for (size_t i = 0; i < count; i++) {
    156                     const Rect& quadBounds = opMesh->quads[i];
    157                     const float x = tx + quadBounds.left;
    158                     const float y = ty + quadBounds.top;
    159                     renderer.dirtyRenderTarget(Rect(x, y,
    160                             x + quadBounds.getWidth(), y + quadBounds.getHeight()));
    161                 }
    162             }
    163         }
    164 
    165         indexCount += opMesh->indexCount;
    166     }
    167 
    168 
    169     Texture* texture = renderer.caches().textureCache.get(firstOp.bitmap);
    170     if (!texture) return;
    171     const AutoTexture autoCleanup(texture);
    172 
    173     // 9 patches are built for stretching - always filter
    174     int textureFillFlags = TextureFillFlags::ForceFilter;
    175     if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) {
    176         textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
    177     }
    178     Glop glop;
    179     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    180             .setRoundRectClipState(firstState.roundRectClipState)
    181             .setMeshTexturedIndexedQuads(vertices, indexCount)
    182             .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha)
    183             .setTransform(Matrix4::identity(), TransformFlags::None)
    184             .setModelViewIdentityEmptyBounds()
    185             .build();
    186     ClipRect renderTargetClip(opList.clip);
    187     const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
    188     renderer.renderGlop(nullptr, clip, glop);
    189 }
    190 
    191 static void renderTextShadow(BakedOpRenderer& renderer,
    192         const TextOp& op, const BakedOpState& textOpState) {
    193     if (CC_LIKELY(!PaintUtils::hasTextShadow(op.paint))) return;
    194 
    195     FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
    196     fontRenderer.setFont(op.paint, SkMatrix::I());
    197     renderer.caches().textureState().activateTexture(0);
    198 
    199     PaintUtils::TextShadow textShadow;
    200     if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
    201         LOG_ALWAYS_FATAL("failed to query shadow attributes");
    202     }
    203 
    204     renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
    205     ShadowTexture* texture = renderer.caches().dropShadowCache.get(
    206             op.paint, op.glyphs, op.glyphCount, textShadow.radius, op.positions);
    207     // If the drop shadow exceeds the max texture size or couldn't be
    208     // allocated, skip drawing
    209     if (!texture) return;
    210     const AutoTexture autoCleanup(texture);
    211 
    212     const float sx = op.x - texture->left + textShadow.dx;
    213     const float sy = op.y - texture->top + textShadow.dy;
    214 
    215     Glop glop;
    216     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    217             .setRoundRectClipState(textOpState.roundRectClipState)
    218             .setMeshTexturedUnitQuad(nullptr)
    219             .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, textOpState.alpha)
    220             .setTransform(textOpState.computedState.transform, TransformFlags::None)
    221             .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
    222             .build();
    223 
    224     // Compute damage bounds and clip (since may differ from those in textOpState).
    225     // Bounds should be same as text op, but with dx/dy offset and radius outset
    226     // applied in local space.
    227     auto& transform = textOpState.computedState.transform;
    228     Rect shadowBounds = op.unmappedBounds; // STROKE
    229     const bool expandForStroke = op.paint->getStyle() != SkPaint::kFill_Style;
    230     if (expandForStroke) {
    231         shadowBounds.outset(op.paint->getStrokeWidth() * 0.5f);
    232     }
    233     shadowBounds.translate(textShadow.dx, textShadow.dy);
    234     shadowBounds.outset(textShadow.radius, textShadow.radius);
    235     transform.mapRect(shadowBounds);
    236     if (CC_UNLIKELY(expandForStroke &&
    237             (!transform.isPureTranslate() || op.paint->getStrokeWidth() < 1.0f))) {
    238         shadowBounds.outset(0.5f);
    239     }
    240 
    241     auto clipState = textOpState.computedState.clipState;
    242     if (clipState->mode != ClipMode::Rectangle
    243             || !clipState->rect.contains(shadowBounds)) {
    244         // need clip, so pass it and clip bounds
    245         shadowBounds.doIntersect(clipState->rect);
    246     } else {
    247         // don't need clip, ignore
    248         clipState = nullptr;
    249     }
    250 
    251     renderer.renderGlop(&shadowBounds, clipState, glop);
    252 }
    253 
    254 enum class TextRenderType {
    255     Defer,
    256     Flush
    257 };
    258 
    259 static void renderText(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state,
    260         const ClipBase* renderClip, TextRenderType renderType) {
    261     FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
    262     float x = op.x;
    263     float y = op.y;
    264     const Matrix4& transform = state.computedState.transform;
    265     const bool pureTranslate = transform.isPureTranslate();
    266     if (CC_LIKELY(pureTranslate)) {
    267         x = floorf(x + transform.getTranslateX() + 0.5f);
    268         y = floorf(y + transform.getTranslateY() + 0.5f);
    269         fontRenderer.setFont(op.paint, SkMatrix::I());
    270         fontRenderer.setTextureFiltering(false);
    271     } else if (CC_UNLIKELY(transform.isPerspective())) {
    272         fontRenderer.setFont(op.paint, SkMatrix::I());
    273         fontRenderer.setTextureFiltering(true);
    274     } else {
    275         // We only pass a partial transform to the font renderer. That partial
    276         // matrix defines how glyphs are rasterized. Typically we want glyphs
    277         // to be rasterized at their final size on screen, which means the partial
    278         // matrix needs to take the scale factor into account.
    279         // When a partial matrix is used to transform glyphs during rasterization,
    280         // the mesh is generated with the inverse transform (in the case of scale,
    281         // the mesh is generated at 1.0 / scale for instance.) This allows us to
    282         // apply the full transform matrix at draw time in the vertex shader.
    283         // Applying the full matrix in the shader is the easiest way to handle
    284         // rotation and perspective and allows us to always generated quads in the
    285         // font renderer which greatly simplifies the code, clipping in particular.
    286         float sx, sy;
    287         transform.decomposeScale(sx, sy);
    288         fontRenderer.setFont(op.paint, SkMatrix::MakeScale(
    289                 roundf(std::max(1.0f, sx)),
    290                 roundf(std::max(1.0f, sy))));
    291         fontRenderer.setTextureFiltering(true);
    292     }
    293     Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
    294 
    295     int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
    296     SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint);
    297     TextDrawFunctor functor(&renderer, &state, renderClip,
    298             x, y, pureTranslate, alpha, mode, op.paint);
    299 
    300     bool forceFinish = (renderType == TextRenderType::Flush);
    301     bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
    302     const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr;
    303     fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y,
    304             op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
    305 
    306     if (mustDirtyRenderTarget) {
    307         if (!pureTranslate) {
    308             transform.mapRect(layerBounds);
    309         }
    310         renderer.dirtyRenderTarget(layerBounds);
    311     }
    312 }
    313 
    314 void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
    315         const MergedBakedOpList& opList) {
    316     for (size_t i = 0; i < opList.count; i++) {
    317         const BakedOpState& state = *(opList.states[i]);
    318         const TextOp& op = *(static_cast<const TextOp*>(state.op));
    319         renderTextShadow(renderer, op, state);
    320     }
    321 
    322     ClipRect renderTargetClip(opList.clip);
    323     const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
    324     for (size_t i = 0; i < opList.count; i++) {
    325         const BakedOpState& state = *(opList.states[i]);
    326         const TextOp& op = *(static_cast<const TextOp*>(state.op));
    327         TextRenderType renderType = (i + 1 == opList.count)
    328                 ? TextRenderType::Flush : TextRenderType::Defer;
    329         renderText(renderer, op, state, clip, renderType);
    330     }
    331 }
    332 
    333 namespace VertexBufferRenderFlags {
    334     enum {
    335         Offset = 0x1,
    336         ShadowInterp = 0x2,
    337     };
    338 }
    339 
    340 static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
    341         const VertexBuffer& vertexBuffer, float translateX, float translateY,
    342         const SkPaint& paint, int vertexBufferRenderFlags) {
    343     if (CC_LIKELY(vertexBuffer.getVertexCount())) {
    344         bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
    345         const int transformFlags = vertexBufferRenderFlags & VertexBufferRenderFlags::Offset
    346                 ? TransformFlags::OffsetByFudgeFactor : 0;
    347 
    348         Glop glop;
    349         GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    350                 .setRoundRectClipState(state.roundRectClipState)
    351                 .setMeshVertexBuffer(vertexBuffer)
    352                 .setFillPaint(paint, state.alpha, shadowInterp)
    353                 .setTransform(state.computedState.transform, transformFlags)
    354                 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
    355                 .build();
    356         renderer.renderGlop(state, glop);
    357     }
    358 }
    359 
    360 static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
    361         const SkPath& path, const SkPaint& paint) {
    362     VertexBuffer vertexBuffer;
    363     // TODO: try clipping large paths to viewport
    364     PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
    365     renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
    366 }
    367 
    368 static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
    369         float xOffset, float yOffset, PathTexture& texture, const SkPaint& paint) {
    370     Rect dest(texture.width(), texture.height());
    371     dest.translate(xOffset + texture.left - texture.offset,
    372             yOffset + texture.top - texture.offset);
    373     Glop glop;
    374     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    375             .setRoundRectClipState(state.roundRectClipState)
    376             .setMeshTexturedUnitQuad(nullptr)
    377             .setFillPathTexturePaint(texture, paint, state.alpha)
    378             .setTransform(state.computedState.transform,  TransformFlags::None)
    379             .setModelViewMapUnitToRect(dest)
    380             .build();
    381     renderer.renderGlop(state, glop);
    382 }
    383 
    384 SkRect getBoundsOfFill(const RecordedOp& op) {
    385     SkRect bounds = op.unmappedBounds.toSkRect();
    386     if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
    387         float outsetDistance = op.paint->getStrokeWidth() / 2;
    388         bounds.outset(outsetDistance, outsetDistance);
    389     }
    390     return bounds;
    391 }
    392 
    393 void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
    394     // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
    395     if (op.paint->getStyle() != SkPaint::kStroke_Style
    396             || op.paint->getPathEffect() != nullptr
    397             || op.useCenter) {
    398         PathTexture* texture = renderer.caches().pathCache.getArc(
    399                 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
    400                 op.startAngle, op.sweepAngle, op.useCenter, op.paint);
    401         const AutoTexture holder(texture);
    402         if (CC_LIKELY(holder.texture)) {
    403             renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
    404                     *texture, *(op.paint));
    405         }
    406     } else {
    407         SkRect rect = getBoundsOfFill(op);
    408         SkPath path;
    409         if (op.useCenter) {
    410             path.moveTo(rect.centerX(), rect.centerY());
    411         }
    412         path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
    413         if (op.useCenter) {
    414             path.close();
    415         }
    416         renderConvexPath(renderer, state, path, *(op.paint));
    417     }
    418 }
    419 
    420 void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
    421     Texture* texture = renderer.getTexture(op.bitmap);
    422     if (!texture) return;
    423     const AutoTexture autoCleanup(texture);
    424 
    425     const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
    426             ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
    427     Glop glop;
    428     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    429             .setRoundRectClipState(state.roundRectClipState)
    430             .setMeshTexturedUnitQuad(texture->uvMapper)
    431             .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
    432             .setTransform(state.computedState.transform, TransformFlags::None)
    433             .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height()))
    434             .build();
    435     renderer.renderGlop(state, glop);
    436 }
    437 
    438 void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
    439     Texture* texture = renderer.caches().textureCache.get(op.bitmap);
    440     if (!texture) {
    441         return;
    442     }
    443     const AutoTexture autoCleanup(texture);
    444 
    445     const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
    446 
    447     std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
    448     ColorTextureVertex* vertex = &mesh[0];
    449 
    450     const int* colors = op.colors;
    451     std::unique_ptr<int[]> tempColors;
    452     if (!colors) {
    453         uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
    454         tempColors.reset(new int[colorsCount]);
    455         memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
    456         colors = tempColors.get();
    457     }
    458 
    459     for (int32_t y = 0; y < op.meshHeight; y++) {
    460         for (int32_t x = 0; x < op.meshWidth; x++) {
    461             uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
    462 
    463             float u1 = float(x) / op.meshWidth;
    464             float u2 = float(x + 1) / op.meshWidth;
    465             float v1 = float(y) / op.meshHeight;
    466             float v2 = float(y + 1) / op.meshHeight;
    467 
    468             int ax = i + (op.meshWidth + 1) * 2;
    469             int ay = ax + 1;
    470             int bx = i;
    471             int by = bx + 1;
    472             int cx = i + 2;
    473             int cy = cx + 1;
    474             int dx = i + (op.meshWidth + 1) * 2 + 2;
    475             int dy = dx + 1;
    476 
    477             const float* vertices = op.vertices;
    478             ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
    479             ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
    480             ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
    481 
    482             ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
    483             ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
    484             ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
    485         }
    486     }
    487 
    488     /*
    489      * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
    490      * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
    491      */
    492     const int textureFillFlags = TextureFillFlags::None;
    493     Glop glop;
    494     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    495             .setRoundRectClipState(state.roundRectClipState)
    496             .setMeshColoredTexturedMesh(mesh.get(), elementCount)
    497             .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
    498             .setTransform(state.computedState.transform,  TransformFlags::None)
    499             .setModelViewOffsetRect(0, 0, op.unmappedBounds)
    500             .build();
    501     renderer.renderGlop(state, glop);
    502 }
    503 
    504 void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) {
    505     Texture* texture = renderer.getTexture(op.bitmap);
    506     if (!texture) return;
    507     const AutoTexture autoCleanup(texture);
    508 
    509     Rect uv(std::max(0.0f, op.src.left / texture->width()),
    510             std::max(0.0f, op.src.top / texture->height()),
    511             std::min(1.0f, op.src.right / texture->width()),
    512             std::min(1.0f, op.src.bottom / texture->height()));
    513 
    514     const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
    515             ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
    516     const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth())
    517             && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
    518     Glop glop;
    519     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    520             .setRoundRectClipState(state.roundRectClipState)
    521             .setMeshTexturedUvQuad(texture->uvMapper, uv)
    522             .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
    523             .setTransform(state.computedState.transform, TransformFlags::None)
    524             .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
    525             .build();
    526     renderer.renderGlop(state, glop);
    527 }
    528 
    529 void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op, const BakedOpState& state) {
    530     SkPaint paint;
    531     paint.setColor(op.color);
    532     paint.setBlendMode(op.mode);
    533 
    534     Glop glop;
    535     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    536             .setRoundRectClipState(state.roundRectClipState)
    537             .setMeshUnitQuad()
    538             .setFillPaint(paint, state.alpha)
    539             .setTransform(Matrix4::identity(), TransformFlags::None)
    540             .setModelViewMapUnitToRect(state.computedState.clipState->rect)
    541             .build();
    542     renderer.renderGlop(state, glop);
    543 }
    544 
    545 void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) {
    546     renderer.renderFunctor(op, state);
    547 }
    548 
    549 void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
    550     VertexBuffer buffer;
    551     PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
    552             state.computedState.transform, buffer);
    553     int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
    554     renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
    555 }
    556 
    557 void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
    558     if (op.paint->getPathEffect() != nullptr) {
    559         PathTexture* texture = renderer.caches().pathCache.getOval(
    560                 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
    561         const AutoTexture holder(texture);
    562         if (CC_LIKELY(holder.texture)) {
    563             renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
    564                     *texture, *(op.paint));
    565         }
    566     } else {
    567         SkPath path;
    568         SkRect rect = getBoundsOfFill(op);
    569         path.addOval(rect);
    570 
    571         if (state.computedState.localProjectionPathMask != nullptr) {
    572             // Mask the ripple path by the local space projection mask in local space.
    573             // Note that this can create CCW paths.
    574             Op(path, *state.computedState.localProjectionPathMask, kIntersect_SkPathOp, &path);
    575         }
    576         renderConvexPath(renderer, state, path, *(op.paint));
    577     }
    578 }
    579 
    580 void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) {
    581     // 9 patches are built for stretching - always filter
    582     int textureFillFlags = TextureFillFlags::ForceFilter;
    583     if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
    584         textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
    585     }
    586 
    587     // TODO: avoid redoing the below work each frame:
    588     const Patch* mesh = renderer.caches().patchCache.get(
    589             op.bitmap->width(), op.bitmap->height(),
    590             op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
    591 
    592     Texture* texture = renderer.caches().textureCache.get(op.bitmap);
    593     if (CC_LIKELY(texture)) {
    594         const AutoTexture autoCleanup(texture);
    595         Glop glop;
    596         GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    597                 .setRoundRectClipState(state.roundRectClipState)
    598                 .setMeshPatchQuads(*mesh)
    599                 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
    600                 .setTransform(state.computedState.transform, TransformFlags::None)
    601                 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
    602                         Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
    603                 .build();
    604         renderer.renderGlop(state, glop);
    605     }
    606 }
    607 
    608 void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
    609     PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
    610     const AutoTexture holder(texture);
    611     if (CC_LIKELY(holder.texture)) {
    612         // Unlike other callers to renderPathTexture, no offsets are used because PathOp doesn't
    613         // have any translate built in, other than what's in the SkPath itself
    614         renderPathTexture(renderer, state, 0, 0, *texture, *(op.paint));
    615     }
    616 }
    617 
    618 void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
    619     VertexBuffer buffer;
    620     PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
    621             state.computedState.transform, buffer);
    622     int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
    623     renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
    624 }
    625 
    626 // See SkPaintDefaults.h
    627 #define SkPaintDefaults_MiterLimit SkIntToScalar(4)
    628 
    629 void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
    630     if (op.paint->getStyle() != SkPaint::kFill_Style) {
    631         // only fill + default miter is supported by drawConvexPath, since others must handle joins
    632         static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
    633         if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
    634                 || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
    635                 || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
    636              PathTexture* texture = renderer.caches().pathCache.getRect(
    637                      op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
    638              const AutoTexture holder(texture);
    639              if (CC_LIKELY(holder.texture)) {
    640                  renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
    641                          *texture, *(op.paint));
    642              }
    643         } else {
    644             SkPath path;
    645             path.addRect(getBoundsOfFill(op));
    646             renderConvexPath(renderer, state, path, *(op.paint));
    647         }
    648     } else {
    649         if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
    650             SkPath path;
    651             path.addRect(op.unmappedBounds.toSkRect());
    652             renderConvexPath(renderer, state, path, *(op.paint));
    653         } else {
    654             // render simple unit quad, no tessellation required
    655             Glop glop;
    656             GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    657                     .setRoundRectClipState(state.roundRectClipState)
    658                     .setMeshUnitQuad()
    659                     .setFillPaint(*op.paint, state.alpha)
    660                     .setTransform(state.computedState.transform, TransformFlags::None)
    661                     .setModelViewMapUnitToRect(op.unmappedBounds)
    662                     .build();
    663             renderer.renderGlop(state, glop);
    664         }
    665     }
    666 }
    667 
    668 void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
    669     if (op.paint->getPathEffect() != nullptr) {
    670         PathTexture* texture = renderer.caches().pathCache.getRoundRect(
    671                 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
    672                 op.rx, op.ry, op.paint);
    673         const AutoTexture holder(texture);
    674         if (CC_LIKELY(holder.texture)) {
    675             renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
    676                     *texture, *(op.paint));
    677         }
    678     } else {
    679         const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
    680                 state.computedState.transform, *(op.paint),
    681                 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
    682         renderVertexBuffer(renderer, state, *buffer,
    683                 op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
    684     }
    685 }
    686 
    687 static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
    688         const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
    689     SkPaint paint;
    690     paint.setAntiAlias(true); // want to use AlphaVertex
    691 
    692     // The caller has made sure casterAlpha > 0.
    693     uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
    694     if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
    695         ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
    696     }
    697     if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
    698         paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
    699         renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
    700                 paint, VertexBufferRenderFlags::ShadowInterp);
    701     }
    702 
    703     uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
    704     if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
    705         spotShadowAlpha = Properties::overrideSpotShadowStrength;
    706     }
    707     if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
    708         paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
    709         renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
    710                 paint, VertexBufferRenderFlags::ShadowInterp);
    711     }
    712 }
    713 
    714 void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
    715     TessellationCache::vertexBuffer_pair_t buffers = op.shadowTask->getResult();
    716     renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
    717 }
    718 
    719 void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
    720     Glop glop;
    721     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    722             .setRoundRectClipState(state.roundRectClipState)
    723             .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
    724             .setFillPaint(*op.paint, state.alpha)
    725             .setTransform(state.computedState.transform, TransformFlags::None)
    726             .setModelViewOffsetRect(0, 0, op.unmappedBounds)
    727             .build();
    728     renderer.renderGlop(state, glop);
    729 }
    730 
    731 void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
    732     renderTextShadow(renderer, op, state);
    733     renderText(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush);
    734 }
    735 
    736 void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) {
    737     // Note: can't trust clipSideFlags since we record with unmappedBounds == clip.
    738     // TODO: respect clipSideFlags, once we record with bounds
    739     auto renderTargetClip = state.computedState.clipState;
    740 
    741     FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
    742     fontRenderer.setFont(op.paint, SkMatrix::I());
    743     fontRenderer.setTextureFiltering(true);
    744 
    745     Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
    746 
    747     int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
    748     SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint);
    749     TextDrawFunctor functor(&renderer, &state, renderTargetClip,
    750             0.0f, 0.0f, false, alpha, mode, op.paint);
    751 
    752     bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
    753     const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
    754     if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount,
    755             op.path, op.hOffset, op.vOffset,
    756             mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
    757         if (mustDirtyRenderTarget) {
    758             // manually dirty render target, since TextDrawFunctor won't
    759             state.computedState.transform.mapRect(layerBounds);
    760             renderer.dirtyRenderTarget(layerBounds);
    761         }
    762     }
    763 }
    764 
    765 void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, const BakedOpState& state) {
    766     GlLayer* layer = static_cast<GlLayer*>(op.layerHandle->backingLayer());
    767     if (!layer) {
    768         return;
    769     }
    770     const bool tryToSnap = layer->getForceFilter();
    771     float alpha = (layer->getAlpha() / 255.0f) * state.alpha;
    772     Glop glop;
    773     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    774             .setRoundRectClipState(state.roundRectClipState)
    775             .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
    776             .setFillTextureLayer(*(layer), alpha)
    777             .setTransform(state.computedState.transform, TransformFlags::None)
    778             .setModelViewMapUnitToRectOptionalSnap(tryToSnap, Rect(layer->getWidth(), layer->getHeight()))
    779             .build();
    780     renderer.renderGlop(state, glop);
    781 }
    782 
    783 void renderRectForLayer(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state,
    784         int color, SkBlendMode mode, SkColorFilter* colorFilter) {
    785     SkPaint paint;
    786     paint.setColor(color);
    787     paint.setBlendMode(mode);
    788     paint.setColorFilter(sk_ref_sp(colorFilter));
    789     RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
    790     BakedOpDispatcher::onRectOp(renderer, rectOp, state);
    791 }
    792 
    793 void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
    794     // Note that we don't use op->paint in this function - it's never set on a LayerOp
    795     OffscreenBuffer* buffer = *op.layerHandle;
    796 
    797     if (CC_UNLIKELY(!buffer)) return;
    798 
    799     float layerAlpha = op.alpha * state.alpha;
    800     Glop glop;
    801     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    802             .setRoundRectClipState(state.roundRectClipState)
    803             .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
    804             .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
    805             .setTransform(state.computedState.transform, TransformFlags::None)
    806             .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
    807                     Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
    808             .build();
    809     renderer.renderGlop(state, glop);
    810 
    811     if (!buffer->hasRenderedSinceRepaint) {
    812         buffer->hasRenderedSinceRepaint = true;
    813         if (CC_UNLIKELY(Properties::debugLayersUpdates)) {
    814             // render debug layer highlight
    815             renderRectForLayer(renderer, op, state,
    816                     0x7f00ff00, SkBlendMode::kSrcOver, nullptr);
    817         } else if (CC_UNLIKELY(Properties::debugOverdraw)) {
    818             // render transparent to increment overdraw for repaint area
    819             renderRectForLayer(renderer, op, state,
    820                     SK_ColorTRANSPARENT, SkBlendMode::kSrcOver, nullptr);
    821         }
    822     }
    823 }
    824 
    825 void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) {
    826     LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!");
    827     *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds);
    828     LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed");
    829 }
    830 
    831 void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) {
    832     LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!");
    833     if (!state.computedState.clippedBounds.isEmpty()) {
    834         if (op.paint && op.paint->getAlpha() < 255) {
    835             SkPaint layerPaint;
    836             layerPaint.setAlpha(op.paint->getAlpha());
    837             layerPaint.setBlendMode(SkBlendMode::kDstIn);
    838             layerPaint.setColorFilter(sk_ref_sp(op.paint->getColorFilter()));
    839             RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint);
    840             BakedOpDispatcher::onRectOp(renderer, rectOp, state);
    841         }
    842 
    843         OffscreenBuffer& layer = **(op.layerHandle);
    844         auto mode = PaintUtils::getBlendModeDirect(op.paint);
    845         Glop glop;
    846         GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
    847                 .setRoundRectClipState(state.roundRectClipState)
    848                 .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates())
    849                 .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap)
    850                 .setTransform(state.computedState.transform, TransformFlags::None)
    851                 .setModelViewMapUnitToRect(state.computedState.clippedBounds)
    852                 .build();
    853         renderer.renderGlop(state, glop);
    854     }
    855     renderer.renderState().layerPool().putOrDelete(*op.layerHandle);
    856 }
    857 
    858 } // namespace uirenderer
    859 } // namespace android
    860