blob: 0f0768f08a5ed0f1868ab768a218aa553467721c [file] [log] [blame]
Chris Craik9e7fcfd2015-11-25 13:27:33 -08001/*
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"
Chris Craikf09ff5a2015-12-08 17:21:58 -080023#include "Patch.h"
Chris Craik386aa032015-12-07 17:08:25 -080024#include "PathTessellator.h"
Chris Craik9e7fcfd2015-11-25 13:27:33 -080025#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>
Chris Craik386aa032015-12-07 17:08:25 -080032#include <SkPaintDefaults.h>
Chris Craik9e7fcfd2015-11-25 13:27:33 -080033
34namespace android {
35namespace uirenderer {
36
Chris Craik15c3f192015-12-03 12:16:56 -080037static 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 }
Chris Craik15c3f192015-12-03 12:16:56 -080060 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);
Chris Craik15c3f192015-12-03 12:16:56 -080070 }
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)
Chris Craikf09ff5a2015-12-08 17:21:58 -080080 .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()
Chris Craik15c3f192015-12-03 12:16:56 -0800185 .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
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800300void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
301 LOG_ALWAYS_FATAL("unsupported operation");
302}
303
Chris Craik15c3f192015-12-03 12:16:56 -0800304void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer&, const BeginLayerOp&, const BakedOpState&) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800305 LOG_ALWAYS_FATAL("unsupported operation");
306}
307
Chris Craik15c3f192015-12-03 12:16:56 -0800308void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer&, const EndLayerOp&, const BakedOpState&) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800309 LOG_ALWAYS_FATAL("unsupported operation");
310}
311
Chris Craik268a9c02015-12-09 18:05:12 -0800312void 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
Chris Craik386aa032015-12-07 17:08:25 -0800320namespace 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
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800404void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800405 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)
Chris Craik5430ab22015-12-10 16:28:16 -0800417 .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height))
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800418 .build();
419 renderer.renderGlop(state, glop);
420}
421
Chris Craikf09ff5a2015-12-08 17:21:58 -0800422void 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
Chris Craike29ce6f2015-12-10 16:25:13 -0800521void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) {
522 renderer.renderFunctor(op, state);
523}
524
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800525void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800526 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);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800531}
532
Chris Craik386aa032015-12-07 17:08:25 -0800533void 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
Chris Craikf09ff5a2015-12-08 17:21:58 -0800549void 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
Chris Craik386aa032015-12-07 17:08:25 -0800578void 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
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800597void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800598 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 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800633}
634
Chris Craik386aa032015-12-07 17:08:25 -0800635void 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);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800650 }
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
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800703void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
Chris Craik15c3f192015-12-03 12:16:56 -0800704 const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr;
705 renderTextOp(renderer, op, state, clip, TextRenderType::Flush);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800706}
707
Chris Craikd7448e62015-12-15 10:34:36 -0800708void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) {
709 // Note: can't trust clipSideFlags since we record with unmappedBounds == clip.
710 // TODO: respect clipSideFlags, once we record with bounds
711 const Rect* renderTargetClip = &state.computedState.clipRect;
712
713 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
714 fontRenderer.setFont(op.paint, SkMatrix::I());
715 fontRenderer.setTextureFiltering(true);
716
717 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
718
719 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
720 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
721 TextDrawFunctor functor(&renderer, &state, renderTargetClip,
722 0.0f, 0.0f, false, alpha, mode, op.paint);
723
724 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
725 const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
726 if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip,
727 reinterpret_cast<const char*>(op.glyphs), op.glyphCount,
728 op.path, op.hOffset, op.vOffset,
729 mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
730 if (mustDirtyRenderTarget) {
731 // manually dirty render target, since TextDrawFunctor won't
732 state.computedState.transform.mapRect(layerBounds);
733 renderer.dirtyRenderTarget(layerBounds);
734 }
735 }
736}
737
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800738void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, const BakedOpState& state) {
739 const bool tryToSnap = !op.layer->getForceFilter();
740 float alpha = (op.layer->getAlpha() / 255.0f) * state.alpha;
741 Glop glop;
742 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
743 .setRoundRectClipState(state.roundRectClipState)
744 .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
745 .setFillTextureLayer(*(op.layer), alpha)
746 .setTransform(state.computedState.transform, TransformFlags::None)
747 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, Rect(op.layer->getWidth(), op.layer->getHeight()))
748 .build();
749 renderer.renderGlop(state, glop);
750}
751
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800752void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
753 OffscreenBuffer* buffer = *op.layerHandle;
754
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800755 float layerAlpha = op.alpha * state.alpha;
756 Glop glop;
757 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
758 .setRoundRectClipState(state.roundRectClipState)
759 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
760 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
761 .setTransform(state.computedState.transform, TransformFlags::None)
762 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
763 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
764 .build();
765 renderer.renderGlop(state, glop);
766
767 if (op.destroy) {
768 renderer.renderState().layerPool().putOrDelete(buffer);
769 }
770}
771
772} // namespace uirenderer
773} // namespace android