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