BakedOpDispatcher.cpp revision e29ce6f51d681af7649c0a7cddee97c471e43eb5
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 "Glop.h"
22#include "GlopBuilder.h"
23#include "Patch.h"
24#include "PathTessellator.h"
25#include "renderstate/OffscreenBufferPool.h"
26#include "renderstate/RenderState.h"
27#include "utils/GLUtils.h"
28#include "VertexBuffer.h"
29
30#include <algorithm>
31#include <math.h>
32#include <SkPaintDefaults.h>
33
34namespace android {
35namespace uirenderer {
36
37static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds, const Rect& texCoord) {
38    vertices[0] = { bounds.left, bounds.top, texCoord.left, texCoord.top };
39    vertices[1] = { bounds.right, bounds.top, texCoord.right, texCoord.top };
40    vertices[2] = { bounds.left, bounds.bottom, texCoord.left, texCoord.bottom };
41    vertices[3] = { bounds.right, bounds.bottom, texCoord.right, texCoord.bottom };
42}
43
44void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
45        const MergedBakedOpList& opList) {
46
47    const BakedOpState& firstState = *(opList.states[0]);
48    const SkBitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
49
50    AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(bitmap->pixelRef());
51    Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(bitmap);
52    if (!texture) return;
53    const AutoTexture autoCleanup(texture);
54
55    TextureVertex vertices[opList.count * 4];
56    Rect texCoords(0, 0, 1, 1);
57    if (entry) {
58        entry->uvMapper.map(texCoords);
59    }
60    for (size_t i = 0; i < opList.count; i++) {
61        const BakedOpState& state = *(opList.states[i]);
62        TextureVertex* rectVerts = &vertices[i * 4];
63        Rect opBounds = state.computedState.clippedBounds;
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, texCoords);
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    renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
83}
84
85void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
86        const MergedBakedOpList& opList) {
87    const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op));
88    const BakedOpState& firstState = *(opList.states[0]);
89    AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(
90            firstOp.bitmap->pixelRef());
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                entry, 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                entry, 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 = entry ? entry->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    renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
187}
188
189static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
190        const TextOp& op, const BakedOpState& state) {
191    renderer.caches().textureState().activateTexture(0);
192
193    PaintUtils::TextShadow textShadow;
194    if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
195        LOG_ALWAYS_FATAL("failed to query shadow attributes");
196    }
197
198    renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
199    ShadowTexture* texture = renderer.caches().dropShadowCache.get(
200            op.paint, (const char*) op.glyphs,
201            op.glyphCount, textShadow.radius, op.positions);
202    // If the drop shadow exceeds the max texture size or couldn't be
203    // allocated, skip drawing
204    if (!texture) return;
205    const AutoTexture autoCleanup(texture);
206
207    const float sx = op.x - texture->left + textShadow.dx;
208    const float sy = op.y - texture->top + textShadow.dy;
209
210    Glop glop;
211    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
212            .setRoundRectClipState(state.roundRectClipState)
213            .setMeshTexturedUnitQuad(nullptr)
214            .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
215            .setTransform(state.computedState.transform, TransformFlags::None)
216            .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
217            .build();
218    renderer.renderGlop(state, glop);
219}
220
221enum class TextRenderType {
222    Defer,
223    Flush
224};
225
226static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state,
227        const Rect* renderClip, TextRenderType renderType) {
228    FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
229
230    if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
231        fontRenderer.setFont(op.paint, SkMatrix::I());
232        renderTextShadow(renderer, fontRenderer, op, state);
233    }
234
235    float x = op.x;
236    float y = op.y;
237    const Matrix4& transform = state.computedState.transform;
238    const bool pureTranslate = transform.isPureTranslate();
239    if (CC_LIKELY(pureTranslate)) {
240        x = floorf(x + transform.getTranslateX() + 0.5f);
241        y = floorf(y + transform.getTranslateY() + 0.5f);
242        fontRenderer.setFont(op.paint, SkMatrix::I());
243        fontRenderer.setTextureFiltering(false);
244    } else if (CC_UNLIKELY(transform.isPerspective())) {
245        fontRenderer.setFont(op.paint, SkMatrix::I());
246        fontRenderer.setTextureFiltering(true);
247    } else {
248        // We only pass a partial transform to the font renderer. That partial
249        // matrix defines how glyphs are rasterized. Typically we want glyphs
250        // to be rasterized at their final size on screen, which means the partial
251        // matrix needs to take the scale factor into account.
252        // When a partial matrix is used to transform glyphs during rasterization,
253        // the mesh is generated with the inverse transform (in the case of scale,
254        // the mesh is generated at 1.0 / scale for instance.) This allows us to
255        // apply the full transform matrix at draw time in the vertex shader.
256        // Applying the full matrix in the shader is the easiest way to handle
257        // rotation and perspective and allows us to always generated quads in the
258        // font renderer which greatly simplifies the code, clipping in particular.
259        float sx, sy;
260        transform.decomposeScale(sx, sy);
261        fontRenderer.setFont(op.paint, SkMatrix::MakeScale(
262                roundf(std::max(1.0f, sx)),
263                roundf(std::max(1.0f, sy))));
264        fontRenderer.setTextureFiltering(true);
265    }
266    Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
267
268    int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
269    SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
270    TextDrawFunctor functor(&renderer, &state, renderClip,
271            x, y, pureTranslate, alpha, mode, op.paint);
272
273    bool forceFinish = (renderType == TextRenderType::Flush);
274    bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
275    const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect : nullptr;
276    fontRenderer.renderPosText(op.paint, localOpClip,
277            (const char*) op.glyphs, op.glyphCount, x, y,
278            op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
279
280    if (mustDirtyRenderTarget) {
281        if (!pureTranslate) {
282            transform.mapRect(layerBounds);
283        }
284        renderer.dirtyRenderTarget(layerBounds);
285    }
286}
287
288void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
289        const MergedBakedOpList& opList) {
290    const Rect* clip = opList.clipSideFlags ? &opList.clip : nullptr;
291    for (size_t i = 0; i < opList.count; i++) {
292        const BakedOpState& state = *(opList.states[i]);
293        const TextOp& op = *(static_cast<const TextOp*>(state.op));
294        TextRenderType renderType = (i + 1 == opList.count)
295                ? TextRenderType::Flush : TextRenderType::Defer;
296        renderTextOp(renderer, op, state, clip, renderType);
297    }
298}
299
300void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
301    LOG_ALWAYS_FATAL("unsupported operation");
302}
303
304void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer&, const BeginLayerOp&, const BakedOpState&) {
305    LOG_ALWAYS_FATAL("unsupported operation");
306}
307
308void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer&, const EndLayerOp&, const BakedOpState&) {
309    LOG_ALWAYS_FATAL("unsupported operation");
310}
311
312void BakedOpDispatcher::onCirclePropsOp(BakedOpRenderer&, const CirclePropsOp&, const BakedOpState&) {
313    LOG_ALWAYS_FATAL("unsupported operation");
314}
315
316void BakedOpDispatcher::onRoundRectPropsOp(BakedOpRenderer&, const RoundRectPropsOp&, const BakedOpState&) {
317    LOG_ALWAYS_FATAL("unsupported operation");
318}
319
320namespace VertexBufferRenderFlags {
321    enum {
322        Offset = 0x1,
323        ShadowInterp = 0x2,
324    };
325}
326
327static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
328        const VertexBuffer& vertexBuffer, float translateX, float translateY,
329        const SkPaint& paint, int vertexBufferRenderFlags) {
330    if (CC_LIKELY(vertexBuffer.getVertexCount())) {
331        bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
332        const int transformFlags = TransformFlags::OffsetByFudgeFactor;
333        Glop glop;
334        GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
335                .setRoundRectClipState(state.roundRectClipState)
336                .setMeshVertexBuffer(vertexBuffer, shadowInterp)
337                .setFillPaint(paint, state.alpha)
338                .setTransform(state.computedState.transform, transformFlags)
339                .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
340                .build();
341        renderer.renderGlop(state, glop);
342    }
343}
344
345static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
346        const SkPath& path, const SkPaint& paint) {
347    VertexBuffer vertexBuffer;
348    // TODO: try clipping large paths to viewport
349    PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
350    renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
351}
352
353static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
354        PathTexture& texture, const RecordedOp& op) {
355    Rect dest(texture.width, texture.height);
356    dest.translate(texture.left + op.unmappedBounds.left - texture.offset,
357            texture.top + op.unmappedBounds.top - texture.offset);
358    Glop glop;
359    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
360            .setRoundRectClipState(state.roundRectClipState)
361            .setMeshTexturedUnitQuad(nullptr)
362            .setFillPathTexturePaint(texture, *(op.paint), state.alpha)
363            .setTransform(state.computedState.transform,  TransformFlags::None)
364            .setModelViewMapUnitToRect(dest)
365            .build();
366    renderer.renderGlop(state, glop);
367}
368
369SkRect getBoundsOfFill(const RecordedOp& op) {
370    SkRect bounds = op.unmappedBounds.toSkRect();
371    if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
372        float outsetDistance = op.paint->getStrokeWidth() / 2;
373        bounds.outset(outsetDistance, outsetDistance);
374    }
375    return bounds;
376}
377
378void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
379    // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
380    if (op.paint->getStyle() != SkPaint::kStroke_Style
381            || op.paint->getPathEffect() != nullptr
382            || op.useCenter) {
383        PathTexture* texture = renderer.caches().pathCache.getArc(
384                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
385                op.startAngle, op.sweepAngle, op.useCenter, op.paint);
386        const AutoTexture holder(texture);
387        if (CC_LIKELY(holder.texture)) {
388            renderPathTexture(renderer, state, *texture, op);
389        }
390    } else {
391        SkRect rect = getBoundsOfFill(op);
392        SkPath path;
393        if (op.useCenter) {
394            path.moveTo(rect.centerX(), rect.centerY());
395        }
396        path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
397        if (op.useCenter) {
398            path.close();
399        }
400        renderConvexPath(renderer, state, path, *(op.paint));
401    }
402}
403
404void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
405    Texture* texture = renderer.getTexture(op.bitmap);
406    if (!texture) return;
407    const AutoTexture autoCleanup(texture);
408
409    const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
410            ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
411    Glop glop;
412    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
413            .setRoundRectClipState(state.roundRectClipState)
414            .setMeshTexturedUnitQuad(texture->uvMapper)
415            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
416            .setTransform(state.computedState.transform, TransformFlags::None)
417            .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height))
418            .build();
419    renderer.renderGlop(state, glop);
420}
421
422void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
423    const static UvMapper defaultUvMapper;
424    const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
425
426    std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
427    ColorTextureVertex* vertex = &mesh[0];
428
429    const int* colors = op.colors;
430    std::unique_ptr<int[]> tempColors;
431    if (!colors) {
432        uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
433        tempColors.reset(new int[colorsCount]);
434        memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
435        colors = tempColors.get();
436    }
437
438    Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef());
439    const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper);
440
441    for (int32_t y = 0; y < op.meshHeight; y++) {
442        for (int32_t x = 0; x < op.meshWidth; x++) {
443            uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
444
445            float u1 = float(x) / op.meshWidth;
446            float u2 = float(x + 1) / op.meshWidth;
447            float v1 = float(y) / op.meshHeight;
448            float v2 = float(y + 1) / op.meshHeight;
449
450            mapper.map(u1, v1, u2, v2);
451
452            int ax = i + (op.meshWidth + 1) * 2;
453            int ay = ax + 1;
454            int bx = i;
455            int by = bx + 1;
456            int cx = i + 2;
457            int cy = cx + 1;
458            int dx = i + (op.meshWidth + 1) * 2 + 2;
459            int dy = dx + 1;
460
461            const float* vertices = op.vertices;
462            ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
463            ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
464            ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
465
466            ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
467            ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
468            ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
469        }
470    }
471
472    if (!texture) {
473        texture = renderer.caches().textureCache.get(op.bitmap);
474        if (!texture) {
475            return;
476        }
477    }
478    const AutoTexture autoCleanup(texture);
479
480    /*
481     * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
482     * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
483     */
484    const int textureFillFlags = TextureFillFlags::None;
485    Glop glop;
486    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
487            .setRoundRectClipState(state.roundRectClipState)
488            .setMeshColoredTexturedMesh(mesh.get(), elementCount)
489            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
490            .setTransform(state.computedState.transform,  TransformFlags::None)
491            .setModelViewOffsetRect(0, 0, op.unmappedBounds)
492            .build();
493    renderer.renderGlop(state, glop);
494}
495
496void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) {
497    Texture* texture = renderer.getTexture(op.bitmap);
498    if (!texture) return;
499    const AutoTexture autoCleanup(texture);
500
501    Rect uv(std::max(0.0f, op.src.left / texture->width),
502            std::max(0.0f, op.src.top / texture->height),
503            std::min(1.0f, op.src.right / texture->width),
504            std::min(1.0f, op.src.bottom / texture->height));
505
506    const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
507            ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
508    const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth())
509            && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
510    Glop glop;
511    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
512            .setRoundRectClipState(state.roundRectClipState)
513            .setMeshTexturedUvQuad(texture->uvMapper, uv)
514            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
515            .setTransform(state.computedState.transform, TransformFlags::None)
516            .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
517            .build();
518    renderer.renderGlop(state, glop);
519}
520
521void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) {
522    renderer.renderFunctor(op, state);
523}
524
525void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
526    VertexBuffer buffer;
527    PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
528            state.computedState.transform, buffer);
529    int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
530    renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
531}
532
533void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
534    if (op.paint->getPathEffect() != nullptr) {
535        PathTexture* texture = renderer.caches().pathCache.getOval(
536                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
537        const AutoTexture holder(texture);
538        if (CC_LIKELY(holder.texture)) {
539            renderPathTexture(renderer, state, *texture, op);
540        }
541    } else {
542        SkPath path;
543        SkRect rect = getBoundsOfFill(op);
544        path.addOval(rect);
545        renderConvexPath(renderer, state, path, *(op.paint));
546    }
547}
548
549void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) {
550    // 9 patches are built for stretching - always filter
551    int textureFillFlags = TextureFillFlags::ForceFilter;
552    if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
553        textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
554    }
555
556    // TODO: avoid redoing the below work each frame:
557    AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef());
558    const Patch* mesh = renderer.caches().patchCache.get(
559            entry, op.bitmap->width(), op.bitmap->height(),
560            op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
561
562    Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
563    if (!texture) return;
564    const AutoTexture autoCleanup(texture);
565    Glop glop;
566    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
567            .setRoundRectClipState(state.roundRectClipState)
568            .setMeshPatchQuads(*mesh)
569            .setMeshTexturedUnitQuad(texture->uvMapper)
570            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
571            .setTransform(state.computedState.transform, TransformFlags::None)
572            .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
573                    Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
574            .build();
575    renderer.renderGlop(state, glop);
576}
577
578void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
579    PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
580    const AutoTexture holder(texture);
581    if (CC_LIKELY(holder.texture)) {
582        renderPathTexture(renderer, state, *texture, op);
583    }
584}
585
586void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
587    VertexBuffer buffer;
588    PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
589            state.computedState.transform, buffer);
590    int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
591    renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
592}
593
594// See SkPaintDefaults.h
595#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
596
597void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
598    if (op.paint->getStyle() != SkPaint::kFill_Style) {
599        // only fill + default miter is supported by drawConvexPath, since others must handle joins
600        static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
601        if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
602                || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
603                || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
604             PathTexture* texture = renderer.caches().pathCache.getRect(
605                     op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
606             const AutoTexture holder(texture);
607             if (CC_LIKELY(holder.texture)) {
608                 renderPathTexture(renderer, state, *texture, op);
609             }
610        } else {
611            SkPath path;
612            path.addRect(getBoundsOfFill(op));
613            renderConvexPath(renderer, state, path, *(op.paint));
614        }
615    } else {
616        if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
617            SkPath path;
618            path.addRect(op.unmappedBounds.toSkRect());
619            renderConvexPath(renderer, state, path, *(op.paint));
620        } else {
621            // render simple unit quad, no tessellation required
622            Glop glop;
623            GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
624                    .setRoundRectClipState(state.roundRectClipState)
625                    .setMeshUnitQuad()
626                    .setFillPaint(*op.paint, state.alpha)
627                    .setTransform(state.computedState.transform, TransformFlags::None)
628                    .setModelViewMapUnitToRect(op.unmappedBounds)
629                    .build();
630            renderer.renderGlop(state, glop);
631        }
632    }
633}
634
635void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
636    if (op.paint->getPathEffect() != nullptr) {
637        PathTexture* texture = renderer.caches().pathCache.getRoundRect(
638                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
639                op.rx, op.ry, op.paint);
640        const AutoTexture holder(texture);
641        if (CC_LIKELY(holder.texture)) {
642            renderPathTexture(renderer, state, *texture, op);
643        }
644    } else {
645        const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
646                state.computedState.transform, *(op.paint),
647                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
648        renderVertexBuffer(renderer, state, *buffer,
649                op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
650    }
651}
652
653static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
654        const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
655    SkPaint paint;
656    paint.setAntiAlias(true); // want to use AlphaVertex
657
658    // The caller has made sure casterAlpha > 0.
659    uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
660    if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
661        ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
662    }
663    if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
664        paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
665        renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
666                paint, VertexBufferRenderFlags::ShadowInterp);
667    }
668
669    uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
670    if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
671        spotShadowAlpha = Properties::overrideSpotShadowStrength;
672    }
673    if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
674        paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
675        renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
676                paint, VertexBufferRenderFlags::ShadowInterp);
677    }
678}
679
680void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
681    TessellationCache::vertexBuffer_pair_t buffers;
682    renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform,
683            op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath,
684            &op.shadowMatrixXY, &op.shadowMatrixZ,
685            op.lightCenter, renderer.getLightInfo().lightRadius,
686            buffers);
687
688    renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
689}
690
691void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
692    Glop glop;
693    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
694            .setRoundRectClipState(state.roundRectClipState)
695            .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
696            .setFillPaint(*op.paint, state.alpha)
697            .setTransform(state.computedState.transform, TransformFlags::None)
698            .setModelViewOffsetRect(0, 0, op.unmappedBounds)
699            .build();
700    renderer.renderGlop(state, glop);
701}
702
703void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
704    const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr;
705    renderTextOp(renderer, op, state, clip, TextRenderType::Flush);
706}
707
708void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
709    OffscreenBuffer* buffer = *op.layerHandle;
710
711    float layerAlpha = op.alpha * state.alpha;
712    Glop glop;
713    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
714            .setRoundRectClipState(state.roundRectClipState)
715            .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
716            .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
717            .setTransform(state.computedState.transform, TransformFlags::None)
718            .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
719                    Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
720            .build();
721    renderer.renderGlop(state, glop);
722
723    if (op.destroy) {
724        renderer.renderState().layerPool().putOrDelete(buffer);
725    }
726}
727
728} // namespace uirenderer
729} // namespace android
730