blob: f83e1faf9c8ae662e34819b9b2dd89e49d313eea [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(
Chris Craike8c3c812016-02-05 20:10:50 -0800204 op.paint, op.glyphs, op.glyphCount, textShadow.radius, op.positions);
Chris Craik15c3f192015-12-03 12:16:56 -0800205 // If the drop shadow exceeds the max texture size or couldn't be
206 // allocated, skip drawing
207 if (!texture) return;
208 const AutoTexture autoCleanup(texture);
209
210 const float sx = op.x - texture->left + textShadow.dx;
211 const float sy = op.y - texture->top + textShadow.dy;
212
213 Glop glop;
214 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
215 .setRoundRectClipState(state.roundRectClipState)
216 .setMeshTexturedUnitQuad(nullptr)
217 .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
218 .setTransform(state.computedState.transform, TransformFlags::None)
John Reck38e0c322015-11-10 12:19:17 -0800219 .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
Chris Craik15c3f192015-12-03 12:16:56 -0800220 .build();
221 renderer.renderGlop(state, glop);
222}
223
224enum class TextRenderType {
225 Defer,
226 Flush
227};
228
229static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state,
Chris Craike4db79d2015-12-22 16:32:23 -0800230 const ClipBase* renderClip, TextRenderType renderType) {
Chris Craik15c3f192015-12-03 12:16:56 -0800231 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
232
233 if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
234 fontRenderer.setFont(op.paint, SkMatrix::I());
235 renderTextShadow(renderer, fontRenderer, op, state);
236 }
237
238 float x = op.x;
239 float y = op.y;
240 const Matrix4& transform = state.computedState.transform;
241 const bool pureTranslate = transform.isPureTranslate();
242 if (CC_LIKELY(pureTranslate)) {
243 x = floorf(x + transform.getTranslateX() + 0.5f);
244 y = floorf(y + transform.getTranslateY() + 0.5f);
245 fontRenderer.setFont(op.paint, SkMatrix::I());
246 fontRenderer.setTextureFiltering(false);
247 } else if (CC_UNLIKELY(transform.isPerspective())) {
248 fontRenderer.setFont(op.paint, SkMatrix::I());
249 fontRenderer.setTextureFiltering(true);
250 } else {
251 // We only pass a partial transform to the font renderer. That partial
252 // matrix defines how glyphs are rasterized. Typically we want glyphs
253 // to be rasterized at their final size on screen, which means the partial
254 // matrix needs to take the scale factor into account.
255 // When a partial matrix is used to transform glyphs during rasterization,
256 // the mesh is generated with the inverse transform (in the case of scale,
257 // the mesh is generated at 1.0 / scale for instance.) This allows us to
258 // apply the full transform matrix at draw time in the vertex shader.
259 // Applying the full matrix in the shader is the easiest way to handle
260 // rotation and perspective and allows us to always generated quads in the
261 // font renderer which greatly simplifies the code, clipping in particular.
262 float sx, sy;
263 transform.decomposeScale(sx, sy);
264 fontRenderer.setFont(op.paint, SkMatrix::MakeScale(
265 roundf(std::max(1.0f, sx)),
266 roundf(std::max(1.0f, sy))));
267 fontRenderer.setTextureFiltering(true);
268 }
269 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
270
271 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
272 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
273 TextDrawFunctor functor(&renderer, &state, renderClip,
274 x, y, pureTranslate, alpha, mode, op.paint);
275
276 bool forceFinish = (renderType == TextRenderType::Flush);
277 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
Chris Craike4db79d2015-12-22 16:32:23 -0800278 const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr;
Chris Craike8c3c812016-02-05 20:10:50 -0800279 fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y,
Chris Craik15c3f192015-12-03 12:16:56 -0800280 op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
281
282 if (mustDirtyRenderTarget) {
283 if (!pureTranslate) {
284 transform.mapRect(layerBounds);
285 }
286 renderer.dirtyRenderTarget(layerBounds);
287 }
288}
289
290void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
291 const MergedBakedOpList& opList) {
Chris Craike4db79d2015-12-22 16:32:23 -0800292 ClipRect renderTargetClip(opList.clip);
293 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
Chris Craik15c3f192015-12-03 12:16:56 -0800294 for (size_t i = 0; i < opList.count; i++) {
295 const BakedOpState& state = *(opList.states[i]);
296 const TextOp& op = *(static_cast<const TextOp*>(state.op));
297 TextRenderType renderType = (i + 1 == opList.count)
298 ? TextRenderType::Flush : TextRenderType::Defer;
299 renderTextOp(renderer, op, state, clip, renderType);
300 }
301}
302
Chris Craik386aa032015-12-07 17:08:25 -0800303namespace VertexBufferRenderFlags {
304 enum {
305 Offset = 0x1,
306 ShadowInterp = 0x2,
307 };
308}
309
310static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
311 const VertexBuffer& vertexBuffer, float translateX, float translateY,
312 const SkPaint& paint, int vertexBufferRenderFlags) {
313 if (CC_LIKELY(vertexBuffer.getVertexCount())) {
314 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
315 const int transformFlags = TransformFlags::OffsetByFudgeFactor;
316 Glop glop;
317 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
318 .setRoundRectClipState(state.roundRectClipState)
319 .setMeshVertexBuffer(vertexBuffer, shadowInterp)
320 .setFillPaint(paint, state.alpha)
321 .setTransform(state.computedState.transform, transformFlags)
322 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
323 .build();
324 renderer.renderGlop(state, glop);
325 }
326}
327
328static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
329 const SkPath& path, const SkPaint& paint) {
330 VertexBuffer vertexBuffer;
331 // TODO: try clipping large paths to viewport
332 PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
333 renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
334}
335
336static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
337 PathTexture& texture, const RecordedOp& op) {
John Reck38e0c322015-11-10 12:19:17 -0800338 Rect dest(texture.width(), texture.height());
Chris Craikf9b36782016-01-11 13:24:36 -0800339 dest.translate(texture.left - texture.offset,
340 texture.top - texture.offset);
Chris Craik386aa032015-12-07 17:08:25 -0800341 Glop glop;
342 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
343 .setRoundRectClipState(state.roundRectClipState)
344 .setMeshTexturedUnitQuad(nullptr)
345 .setFillPathTexturePaint(texture, *(op.paint), state.alpha)
346 .setTransform(state.computedState.transform, TransformFlags::None)
347 .setModelViewMapUnitToRect(dest)
348 .build();
349 renderer.renderGlop(state, glop);
350}
351
352SkRect getBoundsOfFill(const RecordedOp& op) {
353 SkRect bounds = op.unmappedBounds.toSkRect();
354 if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
355 float outsetDistance = op.paint->getStrokeWidth() / 2;
356 bounds.outset(outsetDistance, outsetDistance);
357 }
358 return bounds;
359}
360
361void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
362 // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
363 if (op.paint->getStyle() != SkPaint::kStroke_Style
364 || op.paint->getPathEffect() != nullptr
365 || op.useCenter) {
366 PathTexture* texture = renderer.caches().pathCache.getArc(
367 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
368 op.startAngle, op.sweepAngle, op.useCenter, op.paint);
369 const AutoTexture holder(texture);
370 if (CC_LIKELY(holder.texture)) {
371 renderPathTexture(renderer, state, *texture, op);
372 }
373 } else {
374 SkRect rect = getBoundsOfFill(op);
375 SkPath path;
376 if (op.useCenter) {
377 path.moveTo(rect.centerX(), rect.centerY());
378 }
379 path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
380 if (op.useCenter) {
381 path.close();
382 }
383 renderConvexPath(renderer, state, path, *(op.paint));
384 }
385}
386
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800387void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800388 Texture* texture = renderer.getTexture(op.bitmap);
389 if (!texture) return;
390 const AutoTexture autoCleanup(texture);
391
392 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
393 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
394 Glop glop;
395 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
396 .setRoundRectClipState(state.roundRectClipState)
397 .setMeshTexturedUnitQuad(texture->uvMapper)
398 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
399 .setTransform(state.computedState.transform, TransformFlags::None)
John Reck38e0c322015-11-10 12:19:17 -0800400 .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height()))
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800401 .build();
402 renderer.renderGlop(state, glop);
403}
404
Chris Craikf09ff5a2015-12-08 17:21:58 -0800405void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
406 const static UvMapper defaultUvMapper;
407 const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
408
409 std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
410 ColorTextureVertex* vertex = &mesh[0];
411
412 const int* colors = op.colors;
413 std::unique_ptr<int[]> tempColors;
414 if (!colors) {
415 uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
416 tempColors.reset(new int[colorsCount]);
417 memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
418 colors = tempColors.get();
419 }
420
421 Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef());
422 const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper);
423
424 for (int32_t y = 0; y < op.meshHeight; y++) {
425 for (int32_t x = 0; x < op.meshWidth; x++) {
426 uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
427
428 float u1 = float(x) / op.meshWidth;
429 float u2 = float(x + 1) / op.meshWidth;
430 float v1 = float(y) / op.meshHeight;
431 float v2 = float(y + 1) / op.meshHeight;
432
433 mapper.map(u1, v1, u2, v2);
434
435 int ax = i + (op.meshWidth + 1) * 2;
436 int ay = ax + 1;
437 int bx = i;
438 int by = bx + 1;
439 int cx = i + 2;
440 int cy = cx + 1;
441 int dx = i + (op.meshWidth + 1) * 2 + 2;
442 int dy = dx + 1;
443
444 const float* vertices = op.vertices;
445 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
446 ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
447 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
448
449 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
450 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
451 ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
452 }
453 }
454
455 if (!texture) {
456 texture = renderer.caches().textureCache.get(op.bitmap);
457 if (!texture) {
458 return;
459 }
460 }
461 const AutoTexture autoCleanup(texture);
462
463 /*
464 * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
465 * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
466 */
467 const int textureFillFlags = TextureFillFlags::None;
468 Glop glop;
469 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
470 .setRoundRectClipState(state.roundRectClipState)
471 .setMeshColoredTexturedMesh(mesh.get(), elementCount)
472 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
473 .setTransform(state.computedState.transform, TransformFlags::None)
474 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
475 .build();
476 renderer.renderGlop(state, glop);
477}
478
479void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) {
480 Texture* texture = renderer.getTexture(op.bitmap);
481 if (!texture) return;
482 const AutoTexture autoCleanup(texture);
483
John Reck38e0c322015-11-10 12:19:17 -0800484 Rect uv(std::max(0.0f, op.src.left / texture->width()),
485 std::max(0.0f, op.src.top / texture->height()),
486 std::min(1.0f, op.src.right / texture->width()),
487 std::min(1.0f, op.src.bottom / texture->height()));
Chris Craikf09ff5a2015-12-08 17:21:58 -0800488
489 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
490 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
491 const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth())
492 && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
493 Glop glop;
494 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
495 .setRoundRectClipState(state.roundRectClipState)
496 .setMeshTexturedUvQuad(texture->uvMapper, uv)
497 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
498 .setTransform(state.computedState.transform, TransformFlags::None)
499 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
500 .build();
501 renderer.renderGlop(state, glop);
502}
503
Chris Craike29ce6f2015-12-10 16:25:13 -0800504void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) {
505 renderer.renderFunctor(op, state);
506}
507
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800508void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800509 VertexBuffer buffer;
510 PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
511 state.computedState.transform, buffer);
512 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
513 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800514}
515
Chris Craik386aa032015-12-07 17:08:25 -0800516void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
517 if (op.paint->getPathEffect() != nullptr) {
518 PathTexture* texture = renderer.caches().pathCache.getOval(
519 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
520 const AutoTexture holder(texture);
521 if (CC_LIKELY(holder.texture)) {
522 renderPathTexture(renderer, state, *texture, op);
523 }
524 } else {
525 SkPath path;
526 SkRect rect = getBoundsOfFill(op);
527 path.addOval(rect);
528 renderConvexPath(renderer, state, path, *(op.paint));
529 }
530}
531
Chris Craikf09ff5a2015-12-08 17:21:58 -0800532void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) {
533 // 9 patches are built for stretching - always filter
534 int textureFillFlags = TextureFillFlags::ForceFilter;
535 if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
536 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
537 }
538
539 // TODO: avoid redoing the below work each frame:
540 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef());
541 const Patch* mesh = renderer.caches().patchCache.get(
542 entry, op.bitmap->width(), op.bitmap->height(),
543 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
544
545 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
Chris Craik89ceb5c2016-01-12 13:45:34 -0800546 if (CC_LIKELY(texture)) {
547 const AutoTexture autoCleanup(texture);
548 Glop glop;
549 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
550 .setRoundRectClipState(state.roundRectClipState)
551 .setMeshPatchQuads(*mesh)
552 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
553 .setTransform(state.computedState.transform, TransformFlags::None)
554 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
555 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
556 .build();
557 renderer.renderGlop(state, glop);
558 }
Chris Craikf09ff5a2015-12-08 17:21:58 -0800559}
560
Chris Craik386aa032015-12-07 17:08:25 -0800561void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
562 PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
563 const AutoTexture holder(texture);
564 if (CC_LIKELY(holder.texture)) {
565 renderPathTexture(renderer, state, *texture, op);
566 }
567}
568
569void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
570 VertexBuffer buffer;
571 PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
572 state.computedState.transform, buffer);
573 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
574 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
575}
576
577// See SkPaintDefaults.h
578#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
579
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800580void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800581 if (op.paint->getStyle() != SkPaint::kFill_Style) {
582 // only fill + default miter is supported by drawConvexPath, since others must handle joins
583 static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
584 if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
585 || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
586 || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
587 PathTexture* texture = renderer.caches().pathCache.getRect(
588 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
589 const AutoTexture holder(texture);
590 if (CC_LIKELY(holder.texture)) {
591 renderPathTexture(renderer, state, *texture, op);
592 }
593 } else {
594 SkPath path;
595 path.addRect(getBoundsOfFill(op));
596 renderConvexPath(renderer, state, path, *(op.paint));
597 }
598 } else {
599 if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
600 SkPath path;
601 path.addRect(op.unmappedBounds.toSkRect());
602 renderConvexPath(renderer, state, path, *(op.paint));
603 } else {
604 // render simple unit quad, no tessellation required
605 Glop glop;
606 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
607 .setRoundRectClipState(state.roundRectClipState)
608 .setMeshUnitQuad()
609 .setFillPaint(*op.paint, state.alpha)
610 .setTransform(state.computedState.transform, TransformFlags::None)
611 .setModelViewMapUnitToRect(op.unmappedBounds)
612 .build();
613 renderer.renderGlop(state, glop);
614 }
615 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800616}
617
Chris Craik386aa032015-12-07 17:08:25 -0800618void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
619 if (op.paint->getPathEffect() != nullptr) {
620 PathTexture* texture = renderer.caches().pathCache.getRoundRect(
621 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
622 op.rx, op.ry, op.paint);
623 const AutoTexture holder(texture);
624 if (CC_LIKELY(holder.texture)) {
625 renderPathTexture(renderer, state, *texture, op);
626 }
627 } else {
628 const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
629 state.computedState.transform, *(op.paint),
630 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
631 renderVertexBuffer(renderer, state, *buffer,
632 op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800633 }
634}
635
636static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
637 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
638 SkPaint paint;
639 paint.setAntiAlias(true); // want to use AlphaVertex
640
641 // The caller has made sure casterAlpha > 0.
642 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
643 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
644 ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
645 }
646 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
647 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
648 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
649 paint, VertexBufferRenderFlags::ShadowInterp);
650 }
651
652 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
653 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
654 spotShadowAlpha = Properties::overrideSpotShadowStrength;
655 }
656 if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
657 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
658 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
659 paint, VertexBufferRenderFlags::ShadowInterp);
660 }
661}
662
663void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
Chris Craikd8165e82016-02-03 15:52:25 -0800664 TessellationCache::vertexBuffer_pair_t buffers = op.shadowTask->getResult();
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800665 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
666}
667
668void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
669 Glop glop;
670 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
671 .setRoundRectClipState(state.roundRectClipState)
672 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
673 .setFillPaint(*op.paint, state.alpha)
674 .setTransform(state.computedState.transform, TransformFlags::None)
675 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
676 .build();
677 renderer.renderGlop(state, glop);
678}
679
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800680void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
Chris Craike4db79d2015-12-22 16:32:23 -0800681 renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800682}
683
Chris Craikd7448e62015-12-15 10:34:36 -0800684void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) {
685 // Note: can't trust clipSideFlags since we record with unmappedBounds == clip.
686 // TODO: respect clipSideFlags, once we record with bounds
Chris Craike4db79d2015-12-22 16:32:23 -0800687 auto renderTargetClip = state.computedState.clipState;
Chris Craikd7448e62015-12-15 10:34:36 -0800688
689 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
690 fontRenderer.setFont(op.paint, SkMatrix::I());
691 fontRenderer.setTextureFiltering(true);
692
693 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
694
695 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
696 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
697 TextDrawFunctor functor(&renderer, &state, renderTargetClip,
698 0.0f, 0.0f, false, alpha, mode, op.paint);
699
700 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
701 const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
Chris Craike8c3c812016-02-05 20:10:50 -0800702 if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount,
Chris Craikd7448e62015-12-15 10:34:36 -0800703 op.path, op.hOffset, op.vOffset,
704 mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
705 if (mustDirtyRenderTarget) {
706 // manually dirty render target, since TextDrawFunctor won't
707 state.computedState.transform.mapRect(layerBounds);
708 renderer.dirtyRenderTarget(layerBounds);
709 }
710 }
711}
712
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800713void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, const BakedOpState& state) {
714 const bool tryToSnap = !op.layer->getForceFilter();
715 float alpha = (op.layer->getAlpha() / 255.0f) * state.alpha;
716 Glop glop;
717 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
718 .setRoundRectClipState(state.roundRectClipState)
719 .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
720 .setFillTextureLayer(*(op.layer), alpha)
721 .setTransform(state.computedState.transform, TransformFlags::None)
722 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, Rect(op.layer->getWidth(), op.layer->getHeight()))
723 .build();
724 renderer.renderGlop(state, glop);
725}
726
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800727void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800728 // 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 -0800729 OffscreenBuffer* buffer = *op.layerHandle;
730
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800731 if (CC_UNLIKELY(!buffer)) {
732 // Layer was not allocated, which can occur if there were no draw ops inside. We draw the
733 // equivalent by drawing a rect with the same layer properties (alpha/xfer/filter).
734 SkPaint paint;
735 paint.setAlpha(op.alpha * 255);
736 paint.setXfermodeMode(op.mode);
737 paint.setColorFilter(op.colorFilter);
738 RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
739 BakedOpDispatcher::onRectOp(renderer, rectOp, state);
740 } else {
741 float layerAlpha = op.alpha * state.alpha;
742 Glop glop;
743 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
744 .setRoundRectClipState(state.roundRectClipState)
745 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
746 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
747 .setTransform(state.computedState.transform, TransformFlags::None)
748 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
749 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
750 .build();
751 renderer.renderGlop(state, glop);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800752
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800753 if (op.destroy) {
754 renderer.renderState().layerPool().putOrDelete(buffer);
755 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800756 }
757}
758
Chris Craikb87eadd2016-01-06 09:16:05 -0800759void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) {
Chris Craik7435eb12016-01-07 17:41:40 -0800760 LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!");
761 *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds);
762 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed");
Chris Craikb87eadd2016-01-06 09:16:05 -0800763}
764
765void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) {
Chris Craik7435eb12016-01-07 17:41:40 -0800766 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!");
767 if (!state.computedState.clippedBounds.isEmpty()) {
768 if (op.paint && op.paint->getAlpha() < 255) {
769 SkPaint layerPaint;
770 layerPaint.setAlpha(op.paint->getAlpha());
771 layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
772 layerPaint.setColorFilter(op.paint->getColorFilter());
773 RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint);
774 BakedOpDispatcher::onRectOp(renderer, rectOp, state);
775 }
776
777 OffscreenBuffer& layer = **(op.layerHandle);
778 auto mode = PaintUtils::getXfermodeDirect(op.paint);
779 Glop glop;
780 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
781 .setRoundRectClipState(state.roundRectClipState)
782 .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates())
783 .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap)
784 .setTransform(state.computedState.transform, TransformFlags::None)
785 .setModelViewMapUnitToRect(state.computedState.clippedBounds)
786 .build();
787 renderer.renderGlop(state, glop);
788 }
John Reck7db5ffb2016-01-15 13:17:09 -0800789 renderer.renderState().layerPool().putOrDelete(*op.layerHandle);
Chris Craikb87eadd2016-01-06 09:16:05 -0800790}
791
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800792} // namespace uirenderer
793} // namespace android