blob: e3a5f3eeeac1aa816a27f0d1c9867ed4717bfd98 [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();
Chris Craike4db79d2015-12-22 16:32:23 -080082 ClipRect renderTargetClip(opList.clip);
83 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
84 renderer.renderGlop(nullptr, clip, glop);
Chris Craikf09ff5a2015-12-08 17:21:58 -080085}
86
87void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
88 const MergedBakedOpList& opList) {
89 const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op));
90 const BakedOpState& firstState = *(opList.states[0]);
91 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(
92 firstOp.bitmap->pixelRef());
93
94 // Batches will usually contain a small number of items so it's
95 // worth performing a first iteration to count the exact number
96 // of vertices we need in the new mesh
97 uint32_t totalVertices = 0;
98
99 for (size_t i = 0; i < opList.count; i++) {
100 const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
101
102 // TODO: cache mesh lookups
103 const Patch* opMesh = renderer.caches().patchCache.get(
104 entry, op.bitmap->width(), op.bitmap->height(),
105 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
106 totalVertices += opMesh->verticesCount;
107 }
108
109 const bool dirtyRenderTarget = renderer.offscreenRenderTarget();
110
111 uint32_t indexCount = 0;
112
113 TextureVertex vertices[totalVertices];
114 TextureVertex* vertex = &vertices[0];
115 // Create a mesh that contains the transformed vertices for all the
116 // 9-patch objects that are part of the batch. Note that onDefer()
117 // enforces ops drawn by this function to have a pure translate or
118 // identity matrix
119 for (size_t i = 0; i < opList.count; i++) {
120 const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
121 const BakedOpState& state = *opList.states[i];
122
123 // TODO: cache mesh lookups
124 const Patch* opMesh = renderer.caches().patchCache.get(
125 entry, op.bitmap->width(), op.bitmap->height(),
126 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
127
128
129 uint32_t vertexCount = opMesh->verticesCount;
130 if (vertexCount == 0) continue;
131
132 // We use the bounds to know where to translate our vertices
133 // Using patchOp->state.mBounds wouldn't work because these
134 // bounds are clipped
135 const float tx = floorf(state.computedState.transform.getTranslateX()
136 + op.unmappedBounds.left + 0.5f);
137 const float ty = floorf(state.computedState.transform.getTranslateY()
138 + op.unmappedBounds.top + 0.5f);
139
140 // Copy & transform all the vertices for the current operation
141 TextureVertex* opVertices = opMesh->vertices.get();
142 for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
143 TextureVertex::set(vertex++,
144 opVertices->x + tx, opVertices->y + ty,
145 opVertices->u, opVertices->v);
146 }
147
148 // Dirty the current layer if possible. When the 9-patch does not
149 // contain empty quads we can take a shortcut and simply set the
150 // dirty rect to the object's bounds.
151 if (dirtyRenderTarget) {
152 if (!opMesh->hasEmptyQuads) {
153 renderer.dirtyRenderTarget(Rect(tx, ty,
154 tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.getHeight()));
155 } else {
156 const size_t count = opMesh->quads.size();
157 for (size_t i = 0; i < count; i++) {
158 const Rect& quadBounds = opMesh->quads[i];
159 const float x = tx + quadBounds.left;
160 const float y = ty + quadBounds.top;
161 renderer.dirtyRenderTarget(Rect(x, y,
162 x + quadBounds.getWidth(), y + quadBounds.getHeight()));
163 }
164 }
165 }
166
167 indexCount += opMesh->indexCount;
168 }
169
170
171 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap);
172 if (!texture) return;
173 const AutoTexture autoCleanup(texture);
174
175 // 9 patches are built for stretching - always filter
176 int textureFillFlags = TextureFillFlags::ForceFilter;
177 if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) {
178 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
179 }
180 Glop glop;
181 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
182 .setRoundRectClipState(firstState.roundRectClipState)
183 .setMeshTexturedIndexedQuads(vertices, indexCount)
184 .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha)
185 .setTransform(Matrix4::identity(), TransformFlags::None)
186 .setModelViewIdentityEmptyBounds()
Chris Craik15c3f192015-12-03 12:16:56 -0800187 .build();
Chris Craike4db79d2015-12-22 16:32:23 -0800188 ClipRect renderTargetClip(opList.clip);
189 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
190 renderer.renderGlop(nullptr, clip, glop);
Chris Craik15c3f192015-12-03 12:16:56 -0800191}
192
193static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
194 const TextOp& op, const BakedOpState& state) {
195 renderer.caches().textureState().activateTexture(0);
196
197 PaintUtils::TextShadow textShadow;
198 if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
199 LOG_ALWAYS_FATAL("failed to query shadow attributes");
200 }
201
202 renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
203 ShadowTexture* texture = renderer.caches().dropShadowCache.get(
204 op.paint, (const char*) op.glyphs,
205 op.glyphCount, textShadow.radius, op.positions);
206 // If the drop shadow exceeds the max texture size or couldn't be
207 // allocated, skip drawing
208 if (!texture) return;
209 const AutoTexture autoCleanup(texture);
210
211 const float sx = op.x - texture->left + textShadow.dx;
212 const float sy = op.y - texture->top + textShadow.dy;
213
214 Glop glop;
215 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
216 .setRoundRectClipState(state.roundRectClipState)
217 .setMeshTexturedUnitQuad(nullptr)
218 .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
219 .setTransform(state.computedState.transform, TransformFlags::None)
John Reck38e0c322015-11-10 12:19:17 -0800220 .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
Chris Craik15c3f192015-12-03 12:16:56 -0800221 .build();
222 renderer.renderGlop(state, glop);
223}
224
225enum class TextRenderType {
226 Defer,
227 Flush
228};
229
230static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state,
Chris Craike4db79d2015-12-22 16:32:23 -0800231 const ClipBase* renderClip, TextRenderType renderType) {
Chris Craik15c3f192015-12-03 12:16:56 -0800232 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
233
234 if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
235 fontRenderer.setFont(op.paint, SkMatrix::I());
236 renderTextShadow(renderer, fontRenderer, op, state);
237 }
238
239 float x = op.x;
240 float y = op.y;
241 const Matrix4& transform = state.computedState.transform;
242 const bool pureTranslate = transform.isPureTranslate();
243 if (CC_LIKELY(pureTranslate)) {
244 x = floorf(x + transform.getTranslateX() + 0.5f);
245 y = floorf(y + transform.getTranslateY() + 0.5f);
246 fontRenderer.setFont(op.paint, SkMatrix::I());
247 fontRenderer.setTextureFiltering(false);
248 } else if (CC_UNLIKELY(transform.isPerspective())) {
249 fontRenderer.setFont(op.paint, SkMatrix::I());
250 fontRenderer.setTextureFiltering(true);
251 } else {
252 // We only pass a partial transform to the font renderer. That partial
253 // matrix defines how glyphs are rasterized. Typically we want glyphs
254 // to be rasterized at their final size on screen, which means the partial
255 // matrix needs to take the scale factor into account.
256 // When a partial matrix is used to transform glyphs during rasterization,
257 // the mesh is generated with the inverse transform (in the case of scale,
258 // the mesh is generated at 1.0 / scale for instance.) This allows us to
259 // apply the full transform matrix at draw time in the vertex shader.
260 // Applying the full matrix in the shader is the easiest way to handle
261 // rotation and perspective and allows us to always generated quads in the
262 // font renderer which greatly simplifies the code, clipping in particular.
263 float sx, sy;
264 transform.decomposeScale(sx, sy);
265 fontRenderer.setFont(op.paint, SkMatrix::MakeScale(
266 roundf(std::max(1.0f, sx)),
267 roundf(std::max(1.0f, sy))));
268 fontRenderer.setTextureFiltering(true);
269 }
270 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
271
272 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
273 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
274 TextDrawFunctor functor(&renderer, &state, renderClip,
275 x, y, pureTranslate, alpha, mode, op.paint);
276
277 bool forceFinish = (renderType == TextRenderType::Flush);
278 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
Chris Craike4db79d2015-12-22 16:32:23 -0800279 const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr;
Chris Craik15c3f192015-12-03 12:16:56 -0800280 fontRenderer.renderPosText(op.paint, localOpClip,
281 (const char*) op.glyphs, op.glyphCount, x, y,
282 op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
283
284 if (mustDirtyRenderTarget) {
285 if (!pureTranslate) {
286 transform.mapRect(layerBounds);
287 }
288 renderer.dirtyRenderTarget(layerBounds);
289 }
290}
291
292void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
293 const MergedBakedOpList& opList) {
Chris Craike4db79d2015-12-22 16:32:23 -0800294 ClipRect renderTargetClip(opList.clip);
295 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
Chris Craik15c3f192015-12-03 12:16:56 -0800296 for (size_t i = 0; i < opList.count; i++) {
297 const BakedOpState& state = *(opList.states[i]);
298 const TextOp& op = *(static_cast<const TextOp*>(state.op));
299 TextRenderType renderType = (i + 1 == opList.count)
300 ? TextRenderType::Flush : TextRenderType::Defer;
301 renderTextOp(renderer, op, state, clip, renderType);
302 }
303}
304
Chris Craik386aa032015-12-07 17:08:25 -0800305namespace VertexBufferRenderFlags {
306 enum {
307 Offset = 0x1,
308 ShadowInterp = 0x2,
309 };
310}
311
312static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
313 const VertexBuffer& vertexBuffer, float translateX, float translateY,
314 const SkPaint& paint, int vertexBufferRenderFlags) {
315 if (CC_LIKELY(vertexBuffer.getVertexCount())) {
316 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
317 const int transformFlags = TransformFlags::OffsetByFudgeFactor;
318 Glop glop;
319 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
320 .setRoundRectClipState(state.roundRectClipState)
321 .setMeshVertexBuffer(vertexBuffer, shadowInterp)
322 .setFillPaint(paint, state.alpha)
323 .setTransform(state.computedState.transform, transformFlags)
324 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
325 .build();
326 renderer.renderGlop(state, glop);
327 }
328}
329
330static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
331 const SkPath& path, const SkPaint& paint) {
332 VertexBuffer vertexBuffer;
333 // TODO: try clipping large paths to viewport
334 PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
335 renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
336}
337
338static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
339 PathTexture& texture, const RecordedOp& op) {
John Reck38e0c322015-11-10 12:19:17 -0800340 Rect dest(texture.width(), texture.height());
Chris Craikf9b36782016-01-11 13:24:36 -0800341 dest.translate(texture.left - texture.offset,
342 texture.top - texture.offset);
Chris Craik386aa032015-12-07 17:08:25 -0800343 Glop glop;
344 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
345 .setRoundRectClipState(state.roundRectClipState)
346 .setMeshTexturedUnitQuad(nullptr)
347 .setFillPathTexturePaint(texture, *(op.paint), state.alpha)
348 .setTransform(state.computedState.transform, TransformFlags::None)
349 .setModelViewMapUnitToRect(dest)
350 .build();
351 renderer.renderGlop(state, glop);
352}
353
354SkRect getBoundsOfFill(const RecordedOp& op) {
355 SkRect bounds = op.unmappedBounds.toSkRect();
356 if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
357 float outsetDistance = op.paint->getStrokeWidth() / 2;
358 bounds.outset(outsetDistance, outsetDistance);
359 }
360 return bounds;
361}
362
363void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
364 // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
365 if (op.paint->getStyle() != SkPaint::kStroke_Style
366 || op.paint->getPathEffect() != nullptr
367 || op.useCenter) {
368 PathTexture* texture = renderer.caches().pathCache.getArc(
369 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
370 op.startAngle, op.sweepAngle, op.useCenter, op.paint);
371 const AutoTexture holder(texture);
372 if (CC_LIKELY(holder.texture)) {
373 renderPathTexture(renderer, state, *texture, op);
374 }
375 } else {
376 SkRect rect = getBoundsOfFill(op);
377 SkPath path;
378 if (op.useCenter) {
379 path.moveTo(rect.centerX(), rect.centerY());
380 }
381 path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
382 if (op.useCenter) {
383 path.close();
384 }
385 renderConvexPath(renderer, state, path, *(op.paint));
386 }
387}
388
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800389void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800390 Texture* texture = renderer.getTexture(op.bitmap);
391 if (!texture) return;
392 const AutoTexture autoCleanup(texture);
393
394 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
395 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
396 Glop glop;
397 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
398 .setRoundRectClipState(state.roundRectClipState)
399 .setMeshTexturedUnitQuad(texture->uvMapper)
400 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
401 .setTransform(state.computedState.transform, TransformFlags::None)
John Reck38e0c322015-11-10 12:19:17 -0800402 .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height()))
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800403 .build();
404 renderer.renderGlop(state, glop);
405}
406
Chris Craikf09ff5a2015-12-08 17:21:58 -0800407void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
408 const static UvMapper defaultUvMapper;
409 const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
410
411 std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
412 ColorTextureVertex* vertex = &mesh[0];
413
414 const int* colors = op.colors;
415 std::unique_ptr<int[]> tempColors;
416 if (!colors) {
417 uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
418 tempColors.reset(new int[colorsCount]);
419 memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
420 colors = tempColors.get();
421 }
422
423 Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef());
424 const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper);
425
426 for (int32_t y = 0; y < op.meshHeight; y++) {
427 for (int32_t x = 0; x < op.meshWidth; x++) {
428 uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
429
430 float u1 = float(x) / op.meshWidth;
431 float u2 = float(x + 1) / op.meshWidth;
432 float v1 = float(y) / op.meshHeight;
433 float v2 = float(y + 1) / op.meshHeight;
434
435 mapper.map(u1, v1, u2, v2);
436
437 int ax = i + (op.meshWidth + 1) * 2;
438 int ay = ax + 1;
439 int bx = i;
440 int by = bx + 1;
441 int cx = i + 2;
442 int cy = cx + 1;
443 int dx = i + (op.meshWidth + 1) * 2 + 2;
444 int dy = dx + 1;
445
446 const float* vertices = op.vertices;
447 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
448 ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
449 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
450
451 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
452 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
453 ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
454 }
455 }
456
457 if (!texture) {
458 texture = renderer.caches().textureCache.get(op.bitmap);
459 if (!texture) {
460 return;
461 }
462 }
463 const AutoTexture autoCleanup(texture);
464
465 /*
466 * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
467 * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
468 */
469 const int textureFillFlags = TextureFillFlags::None;
470 Glop glop;
471 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
472 .setRoundRectClipState(state.roundRectClipState)
473 .setMeshColoredTexturedMesh(mesh.get(), elementCount)
474 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
475 .setTransform(state.computedState.transform, TransformFlags::None)
476 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
477 .build();
478 renderer.renderGlop(state, glop);
479}
480
481void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) {
482 Texture* texture = renderer.getTexture(op.bitmap);
483 if (!texture) return;
484 const AutoTexture autoCleanup(texture);
485
John Reck38e0c322015-11-10 12:19:17 -0800486 Rect uv(std::max(0.0f, op.src.left / texture->width()),
487 std::max(0.0f, op.src.top / texture->height()),
488 std::min(1.0f, op.src.right / texture->width()),
489 std::min(1.0f, op.src.bottom / texture->height()));
Chris Craikf09ff5a2015-12-08 17:21:58 -0800490
491 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
492 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
493 const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth())
494 && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
495 Glop glop;
496 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
497 .setRoundRectClipState(state.roundRectClipState)
498 .setMeshTexturedUvQuad(texture->uvMapper, uv)
499 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
500 .setTransform(state.computedState.transform, TransformFlags::None)
501 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
502 .build();
503 renderer.renderGlop(state, glop);
504}
505
Chris Craike29ce6f2015-12-10 16:25:13 -0800506void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) {
507 renderer.renderFunctor(op, state);
508}
509
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800510void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800511 VertexBuffer buffer;
512 PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
513 state.computedState.transform, buffer);
514 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
515 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800516}
517
Chris Craik386aa032015-12-07 17:08:25 -0800518void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
519 if (op.paint->getPathEffect() != nullptr) {
520 PathTexture* texture = renderer.caches().pathCache.getOval(
521 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
522 const AutoTexture holder(texture);
523 if (CC_LIKELY(holder.texture)) {
524 renderPathTexture(renderer, state, *texture, op);
525 }
526 } else {
527 SkPath path;
528 SkRect rect = getBoundsOfFill(op);
529 path.addOval(rect);
530 renderConvexPath(renderer, state, path, *(op.paint));
531 }
532}
533
Chris Craikf09ff5a2015-12-08 17:21:58 -0800534void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) {
535 // 9 patches are built for stretching - always filter
536 int textureFillFlags = TextureFillFlags::ForceFilter;
537 if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
538 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
539 }
540
541 // TODO: avoid redoing the below work each frame:
542 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef());
543 const Patch* mesh = renderer.caches().patchCache.get(
544 entry, op.bitmap->width(), op.bitmap->height(),
545 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
546
547 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
Chris Craik89ceb5c2016-01-12 13:45:34 -0800548 if (CC_LIKELY(texture)) {
549 const AutoTexture autoCleanup(texture);
550 Glop glop;
551 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
552 .setRoundRectClipState(state.roundRectClipState)
553 .setMeshPatchQuads(*mesh)
554 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
555 .setTransform(state.computedState.transform, TransformFlags::None)
556 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
557 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
558 .build();
559 renderer.renderGlop(state, glop);
560 }
Chris Craikf09ff5a2015-12-08 17:21:58 -0800561}
562
Chris Craik386aa032015-12-07 17:08:25 -0800563void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
564 PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
565 const AutoTexture holder(texture);
566 if (CC_LIKELY(holder.texture)) {
567 renderPathTexture(renderer, state, *texture, op);
568 }
569}
570
571void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
572 VertexBuffer buffer;
573 PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
574 state.computedState.transform, buffer);
575 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
576 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
577}
578
579// See SkPaintDefaults.h
580#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
581
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800582void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800583 if (op.paint->getStyle() != SkPaint::kFill_Style) {
584 // only fill + default miter is supported by drawConvexPath, since others must handle joins
585 static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
586 if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
587 || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
588 || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
589 PathTexture* texture = renderer.caches().pathCache.getRect(
590 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
591 const AutoTexture holder(texture);
592 if (CC_LIKELY(holder.texture)) {
593 renderPathTexture(renderer, state, *texture, op);
594 }
595 } else {
596 SkPath path;
597 path.addRect(getBoundsOfFill(op));
598 renderConvexPath(renderer, state, path, *(op.paint));
599 }
600 } else {
601 if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
602 SkPath path;
603 path.addRect(op.unmappedBounds.toSkRect());
604 renderConvexPath(renderer, state, path, *(op.paint));
605 } else {
606 // render simple unit quad, no tessellation required
607 Glop glop;
608 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
609 .setRoundRectClipState(state.roundRectClipState)
610 .setMeshUnitQuad()
611 .setFillPaint(*op.paint, state.alpha)
612 .setTransform(state.computedState.transform, TransformFlags::None)
613 .setModelViewMapUnitToRect(op.unmappedBounds)
614 .build();
615 renderer.renderGlop(state, glop);
616 }
617 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800618}
619
Chris Craik386aa032015-12-07 17:08:25 -0800620void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
621 if (op.paint->getPathEffect() != nullptr) {
622 PathTexture* texture = renderer.caches().pathCache.getRoundRect(
623 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
624 op.rx, op.ry, op.paint);
625 const AutoTexture holder(texture);
626 if (CC_LIKELY(holder.texture)) {
627 renderPathTexture(renderer, state, *texture, op);
628 }
629 } else {
630 const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
631 state.computedState.transform, *(op.paint),
632 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
633 renderVertexBuffer(renderer, state, *buffer,
634 op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800635 }
636}
637
638static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
639 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
640 SkPaint paint;
641 paint.setAntiAlias(true); // want to use AlphaVertex
642
643 // The caller has made sure casterAlpha > 0.
644 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
645 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
646 ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
647 }
648 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
649 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
650 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
651 paint, VertexBufferRenderFlags::ShadowInterp);
652 }
653
654 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
655 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
656 spotShadowAlpha = Properties::overrideSpotShadowStrength;
657 }
658 if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
659 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
660 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
661 paint, VertexBufferRenderFlags::ShadowInterp);
662 }
663}
664
665void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
Chris Craikd8165e82016-02-03 15:52:25 -0800666 TessellationCache::vertexBuffer_pair_t buffers = op.shadowTask->getResult();
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800667 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
668}
669
670void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
671 Glop glop;
672 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
673 .setRoundRectClipState(state.roundRectClipState)
674 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
675 .setFillPaint(*op.paint, state.alpha)
676 .setTransform(state.computedState.transform, TransformFlags::None)
677 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
678 .build();
679 renderer.renderGlop(state, glop);
680}
681
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800682void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
Chris Craike4db79d2015-12-22 16:32:23 -0800683 renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800684}
685
Chris Craikd7448e62015-12-15 10:34:36 -0800686void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) {
687 // Note: can't trust clipSideFlags since we record with unmappedBounds == clip.
688 // TODO: respect clipSideFlags, once we record with bounds
Chris Craike4db79d2015-12-22 16:32:23 -0800689 auto renderTargetClip = state.computedState.clipState;
Chris Craikd7448e62015-12-15 10:34:36 -0800690
691 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
692 fontRenderer.setFont(op.paint, SkMatrix::I());
693 fontRenderer.setTextureFiltering(true);
694
695 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
696
697 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
698 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
699 TextDrawFunctor functor(&renderer, &state, renderTargetClip,
700 0.0f, 0.0f, false, alpha, mode, op.paint);
701
702 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
703 const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
704 if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip,
705 reinterpret_cast<const char*>(op.glyphs), op.glyphCount,
706 op.path, op.hOffset, op.vOffset,
707 mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
708 if (mustDirtyRenderTarget) {
709 // manually dirty render target, since TextDrawFunctor won't
710 state.computedState.transform.mapRect(layerBounds);
711 renderer.dirtyRenderTarget(layerBounds);
712 }
713 }
714}
715
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800716void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, const BakedOpState& state) {
717 const bool tryToSnap = !op.layer->getForceFilter();
718 float alpha = (op.layer->getAlpha() / 255.0f) * state.alpha;
719 Glop glop;
720 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
721 .setRoundRectClipState(state.roundRectClipState)
722 .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
723 .setFillTextureLayer(*(op.layer), alpha)
724 .setTransform(state.computedState.transform, TransformFlags::None)
725 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, Rect(op.layer->getWidth(), op.layer->getHeight()))
726 .build();
727 renderer.renderGlop(state, glop);
728}
729
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800730void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800731 // Note that we don't use op->paint in this function - it's never set on a LayerOp
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800732 OffscreenBuffer* buffer = *op.layerHandle;
733
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800734 if (CC_UNLIKELY(!buffer)) {
735 // Layer was not allocated, which can occur if there were no draw ops inside. We draw the
736 // equivalent by drawing a rect with the same layer properties (alpha/xfer/filter).
737 SkPaint paint;
738 paint.setAlpha(op.alpha * 255);
739 paint.setXfermodeMode(op.mode);
740 paint.setColorFilter(op.colorFilter);
741 RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
742 BakedOpDispatcher::onRectOp(renderer, rectOp, state);
743 } else {
744 float layerAlpha = op.alpha * state.alpha;
745 Glop glop;
746 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
747 .setRoundRectClipState(state.roundRectClipState)
748 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
749 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
750 .setTransform(state.computedState.transform, TransformFlags::None)
751 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
752 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
753 .build();
754 renderer.renderGlop(state, glop);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800755
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800756 if (op.destroy) {
757 renderer.renderState().layerPool().putOrDelete(buffer);
758 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800759 }
760}
761
Chris Craikb87eadd2016-01-06 09:16:05 -0800762void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) {
Chris Craik7435eb12016-01-07 17:41:40 -0800763 LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!");
764 *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds);
765 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed");
Chris Craikb87eadd2016-01-06 09:16:05 -0800766}
767
768void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) {
Chris Craik7435eb12016-01-07 17:41:40 -0800769 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!");
770 if (!state.computedState.clippedBounds.isEmpty()) {
771 if (op.paint && op.paint->getAlpha() < 255) {
772 SkPaint layerPaint;
773 layerPaint.setAlpha(op.paint->getAlpha());
774 layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
775 layerPaint.setColorFilter(op.paint->getColorFilter());
776 RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint);
777 BakedOpDispatcher::onRectOp(renderer, rectOp, state);
778 }
779
780 OffscreenBuffer& layer = **(op.layerHandle);
781 auto mode = PaintUtils::getXfermodeDirect(op.paint);
782 Glop glop;
783 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
784 .setRoundRectClipState(state.roundRectClipState)
785 .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates())
786 .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap)
787 .setTransform(state.computedState.transform, TransformFlags::None)
788 .setModelViewMapUnitToRect(state.computedState.clippedBounds)
789 .build();
790 renderer.renderGlop(state, glop);
791 }
John Reck7db5ffb2016-01-15 13:17:09 -0800792 renderer.renderState().layerPool().putOrDelete(*op.layerHandle);
Chris Craikb87eadd2016-01-06 09:16:05 -0800793}
794
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800795} // namespace uirenderer
796} // namespace android