blob: f1c89b89504816230dc8077537c703f0943c57ac [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 Craik386aa032015-12-07 17:08:25 -0800312namespace VertexBufferRenderFlags {
313 enum {
314 Offset = 0x1,
315 ShadowInterp = 0x2,
316 };
317}
318
319static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
320 const VertexBuffer& vertexBuffer, float translateX, float translateY,
321 const SkPaint& paint, int vertexBufferRenderFlags) {
322 if (CC_LIKELY(vertexBuffer.getVertexCount())) {
323 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
324 const int transformFlags = TransformFlags::OffsetByFudgeFactor;
325 Glop glop;
326 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
327 .setRoundRectClipState(state.roundRectClipState)
328 .setMeshVertexBuffer(vertexBuffer, shadowInterp)
329 .setFillPaint(paint, state.alpha)
330 .setTransform(state.computedState.transform, transformFlags)
331 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
332 .build();
333 renderer.renderGlop(state, glop);
334 }
335}
336
337static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
338 const SkPath& path, const SkPaint& paint) {
339 VertexBuffer vertexBuffer;
340 // TODO: try clipping large paths to viewport
341 PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
342 renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
343}
344
345static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
346 PathTexture& texture, const RecordedOp& op) {
347 Rect dest(texture.width, texture.height);
348 dest.translate(texture.left + op.unmappedBounds.left - texture.offset,
349 texture.top + op.unmappedBounds.top - texture.offset);
350 Glop glop;
351 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
352 .setRoundRectClipState(state.roundRectClipState)
353 .setMeshTexturedUnitQuad(nullptr)
354 .setFillPathTexturePaint(texture, *(op.paint), state.alpha)
355 .setTransform(state.computedState.transform, TransformFlags::None)
356 .setModelViewMapUnitToRect(dest)
357 .build();
358 renderer.renderGlop(state, glop);
359}
360
361SkRect getBoundsOfFill(const RecordedOp& op) {
362 SkRect bounds = op.unmappedBounds.toSkRect();
363 if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
364 float outsetDistance = op.paint->getStrokeWidth() / 2;
365 bounds.outset(outsetDistance, outsetDistance);
366 }
367 return bounds;
368}
369
370void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
371 // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
372 if (op.paint->getStyle() != SkPaint::kStroke_Style
373 || op.paint->getPathEffect() != nullptr
374 || op.useCenter) {
375 PathTexture* texture = renderer.caches().pathCache.getArc(
376 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
377 op.startAngle, op.sweepAngle, op.useCenter, op.paint);
378 const AutoTexture holder(texture);
379 if (CC_LIKELY(holder.texture)) {
380 renderPathTexture(renderer, state, *texture, op);
381 }
382 } else {
383 SkRect rect = getBoundsOfFill(op);
384 SkPath path;
385 if (op.useCenter) {
386 path.moveTo(rect.centerX(), rect.centerY());
387 }
388 path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
389 if (op.useCenter) {
390 path.close();
391 }
392 renderConvexPath(renderer, state, path, *(op.paint));
393 }
394}
395
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800396void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800397 Texture* texture = renderer.getTexture(op.bitmap);
398 if (!texture) return;
399 const AutoTexture autoCleanup(texture);
400
401 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
402 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
403 Glop glop;
404 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
405 .setRoundRectClipState(state.roundRectClipState)
406 .setMeshTexturedUnitQuad(texture->uvMapper)
407 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
408 .setTransform(state.computedState.transform, TransformFlags::None)
409 .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
410 .build();
411 renderer.renderGlop(state, glop);
412}
413
Chris Craikf09ff5a2015-12-08 17:21:58 -0800414void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
415 const static UvMapper defaultUvMapper;
416 const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
417
418 std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
419 ColorTextureVertex* vertex = &mesh[0];
420
421 const int* colors = op.colors;
422 std::unique_ptr<int[]> tempColors;
423 if (!colors) {
424 uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
425 tempColors.reset(new int[colorsCount]);
426 memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
427 colors = tempColors.get();
428 }
429
430 Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef());
431 const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper);
432
433 for (int32_t y = 0; y < op.meshHeight; y++) {
434 for (int32_t x = 0; x < op.meshWidth; x++) {
435 uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
436
437 float u1 = float(x) / op.meshWidth;
438 float u2 = float(x + 1) / op.meshWidth;
439 float v1 = float(y) / op.meshHeight;
440 float v2 = float(y + 1) / op.meshHeight;
441
442 mapper.map(u1, v1, u2, v2);
443
444 int ax = i + (op.meshWidth + 1) * 2;
445 int ay = ax + 1;
446 int bx = i;
447 int by = bx + 1;
448 int cx = i + 2;
449 int cy = cx + 1;
450 int dx = i + (op.meshWidth + 1) * 2 + 2;
451 int dy = dx + 1;
452
453 const float* vertices = op.vertices;
454 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
455 ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
456 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
457
458 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
459 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
460 ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
461 }
462 }
463
464 if (!texture) {
465 texture = renderer.caches().textureCache.get(op.bitmap);
466 if (!texture) {
467 return;
468 }
469 }
470 const AutoTexture autoCleanup(texture);
471
472 /*
473 * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
474 * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
475 */
476 const int textureFillFlags = TextureFillFlags::None;
477 Glop glop;
478 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
479 .setRoundRectClipState(state.roundRectClipState)
480 .setMeshColoredTexturedMesh(mesh.get(), elementCount)
481 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
482 .setTransform(state.computedState.transform, TransformFlags::None)
483 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
484 .build();
485 renderer.renderGlop(state, glop);
486}
487
488void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) {
489 Texture* texture = renderer.getTexture(op.bitmap);
490 if (!texture) return;
491 const AutoTexture autoCleanup(texture);
492
493 Rect uv(std::max(0.0f, op.src.left / texture->width),
494 std::max(0.0f, op.src.top / texture->height),
495 std::min(1.0f, op.src.right / texture->width),
496 std::min(1.0f, op.src.bottom / texture->height));
497
498 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
499 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
500 const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth())
501 && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
502 Glop glop;
503 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
504 .setRoundRectClipState(state.roundRectClipState)
505 .setMeshTexturedUvQuad(texture->uvMapper, uv)
506 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
507 .setTransform(state.computedState.transform, TransformFlags::None)
508 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
509 .build();
510 renderer.renderGlop(state, glop);
511}
512
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800513void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800514 VertexBuffer buffer;
515 PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
516 state.computedState.transform, buffer);
517 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
518 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800519}
520
Chris Craik386aa032015-12-07 17:08:25 -0800521void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
522 if (op.paint->getPathEffect() != nullptr) {
523 PathTexture* texture = renderer.caches().pathCache.getOval(
524 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
525 const AutoTexture holder(texture);
526 if (CC_LIKELY(holder.texture)) {
527 renderPathTexture(renderer, state, *texture, op);
528 }
529 } else {
530 SkPath path;
531 SkRect rect = getBoundsOfFill(op);
532 path.addOval(rect);
533 renderConvexPath(renderer, state, path, *(op.paint));
534 }
535}
536
Chris Craikf09ff5a2015-12-08 17:21:58 -0800537void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) {
538 // 9 patches are built for stretching - always filter
539 int textureFillFlags = TextureFillFlags::ForceFilter;
540 if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
541 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
542 }
543
544 // TODO: avoid redoing the below work each frame:
545 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef());
546 const Patch* mesh = renderer.caches().patchCache.get(
547 entry, op.bitmap->width(), op.bitmap->height(),
548 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
549
550 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
551 if (!texture) return;
552 const AutoTexture autoCleanup(texture);
553 Glop glop;
554 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
555 .setRoundRectClipState(state.roundRectClipState)
556 .setMeshPatchQuads(*mesh)
557 .setMeshTexturedUnitQuad(texture->uvMapper)
558 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
559 .setTransform(state.computedState.transform, TransformFlags::None)
560 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
561 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
562 .build();
563 renderer.renderGlop(state, glop);
564}
565
Chris Craik386aa032015-12-07 17:08:25 -0800566void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
567 PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
568 const AutoTexture holder(texture);
569 if (CC_LIKELY(holder.texture)) {
570 renderPathTexture(renderer, state, *texture, op);
571 }
572}
573
574void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
575 VertexBuffer buffer;
576 PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
577 state.computedState.transform, buffer);
578 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
579 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
580}
581
582// See SkPaintDefaults.h
583#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
584
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800585void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800586 if (op.paint->getStyle() != SkPaint::kFill_Style) {
587 // only fill + default miter is supported by drawConvexPath, since others must handle joins
588 static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
589 if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
590 || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
591 || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
592 PathTexture* texture = renderer.caches().pathCache.getRect(
593 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
594 const AutoTexture holder(texture);
595 if (CC_LIKELY(holder.texture)) {
596 renderPathTexture(renderer, state, *texture, op);
597 }
598 } else {
599 SkPath path;
600 path.addRect(getBoundsOfFill(op));
601 renderConvexPath(renderer, state, path, *(op.paint));
602 }
603 } else {
604 if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
605 SkPath path;
606 path.addRect(op.unmappedBounds.toSkRect());
607 renderConvexPath(renderer, state, path, *(op.paint));
608 } else {
609 // render simple unit quad, no tessellation required
610 Glop glop;
611 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
612 .setRoundRectClipState(state.roundRectClipState)
613 .setMeshUnitQuad()
614 .setFillPaint(*op.paint, state.alpha)
615 .setTransform(state.computedState.transform, TransformFlags::None)
616 .setModelViewMapUnitToRect(op.unmappedBounds)
617 .build();
618 renderer.renderGlop(state, glop);
619 }
620 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800621}
622
Chris Craik386aa032015-12-07 17:08:25 -0800623void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
624 if (op.paint->getPathEffect() != nullptr) {
625 PathTexture* texture = renderer.caches().pathCache.getRoundRect(
626 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
627 op.rx, op.ry, op.paint);
628 const AutoTexture holder(texture);
629 if (CC_LIKELY(holder.texture)) {
630 renderPathTexture(renderer, state, *texture, op);
631 }
632 } else {
633 const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
634 state.computedState.transform, *(op.paint),
635 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
636 renderVertexBuffer(renderer, state, *buffer,
637 op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800638 }
639}
640
641static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
642 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
643 SkPaint paint;
644 paint.setAntiAlias(true); // want to use AlphaVertex
645
646 // The caller has made sure casterAlpha > 0.
647 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
648 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
649 ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
650 }
651 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
652 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
653 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
654 paint, VertexBufferRenderFlags::ShadowInterp);
655 }
656
657 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
658 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
659 spotShadowAlpha = Properties::overrideSpotShadowStrength;
660 }
661 if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
662 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
663 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
664 paint, VertexBufferRenderFlags::ShadowInterp);
665 }
666}
667
668void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
669 TessellationCache::vertexBuffer_pair_t buffers;
670 renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform,
671 op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath,
672 &op.shadowMatrixXY, &op.shadowMatrixZ,
673 op.lightCenter, renderer.getLightInfo().lightRadius,
674 buffers);
675
676 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
677}
678
679void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
680 Glop glop;
681 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
682 .setRoundRectClipState(state.roundRectClipState)
683 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
684 .setFillPaint(*op.paint, state.alpha)
685 .setTransform(state.computedState.transform, TransformFlags::None)
686 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
687 .build();
688 renderer.renderGlop(state, glop);
689}
690
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800691void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
Chris Craik15c3f192015-12-03 12:16:56 -0800692 const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr;
693 renderTextOp(renderer, op, state, clip, TextRenderType::Flush);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800694}
695
696void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
697 OffscreenBuffer* buffer = *op.layerHandle;
698
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800699 float layerAlpha = op.alpha * state.alpha;
700 Glop glop;
701 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
702 .setRoundRectClipState(state.roundRectClipState)
703 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
704 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
705 .setTransform(state.computedState.transform, TransformFlags::None)
706 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
707 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
708 .build();
709 renderer.renderGlop(state, glop);
710
711 if (op.destroy) {
712 renderer.renderState().layerPool().putOrDelete(buffer);
713 }
714}
715
716} // namespace uirenderer
717} // namespace android