blob: 1aab3c79dcfd20817853122747786393c50c2c8a [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,
Chris Craike2822e42016-02-22 16:42:24 -0800337 float xOffset, float yOffset, PathTexture& texture, const SkPaint& paint) {
John Reck38e0c322015-11-10 12:19:17 -0800338 Rect dest(texture.width(), texture.height());
Chris Craike2822e42016-02-22 16:42:24 -0800339 dest.translate(xOffset + texture.left - texture.offset,
340 yOffset + 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)
Chris Craike2822e42016-02-22 16:42:24 -0800345 .setFillPathTexturePaint(texture, paint, state.alpha)
Chris Craik386aa032015-12-07 17:08:25 -0800346 .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)) {
Chris Craik0066a012016-02-29 13:34:43 -0800371 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
Chris Craike2822e42016-02-22 16:42:24 -0800372 *texture, *(op.paint));
Chris Craik386aa032015-12-07 17:08:25 -0800373 }
374 } else {
375 SkRect rect = getBoundsOfFill(op);
376 SkPath path;
377 if (op.useCenter) {
378 path.moveTo(rect.centerX(), rect.centerY());
379 }
380 path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
381 if (op.useCenter) {
382 path.close();
383 }
384 renderConvexPath(renderer, state, path, *(op.paint));
385 }
386}
387
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800388void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800389 Texture* texture = renderer.getTexture(op.bitmap);
390 if (!texture) return;
391 const AutoTexture autoCleanup(texture);
392
393 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
394 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
395 Glop glop;
396 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
397 .setRoundRectClipState(state.roundRectClipState)
398 .setMeshTexturedUnitQuad(texture->uvMapper)
399 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
400 .setTransform(state.computedState.transform, TransformFlags::None)
John Reck38e0c322015-11-10 12:19:17 -0800401 .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height()))
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800402 .build();
403 renderer.renderGlop(state, glop);
404}
405
Chris Craikf09ff5a2015-12-08 17:21:58 -0800406void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
407 const static UvMapper defaultUvMapper;
408 const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
409
410 std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
411 ColorTextureVertex* vertex = &mesh[0];
412
413 const int* colors = op.colors;
414 std::unique_ptr<int[]> tempColors;
415 if (!colors) {
416 uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
417 tempColors.reset(new int[colorsCount]);
418 memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
419 colors = tempColors.get();
420 }
421
422 Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef());
423 const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper);
424
425 for (int32_t y = 0; y < op.meshHeight; y++) {
426 for (int32_t x = 0; x < op.meshWidth; x++) {
427 uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
428
429 float u1 = float(x) / op.meshWidth;
430 float u2 = float(x + 1) / op.meshWidth;
431 float v1 = float(y) / op.meshHeight;
432 float v2 = float(y + 1) / op.meshHeight;
433
434 mapper.map(u1, v1, u2, v2);
435
436 int ax = i + (op.meshWidth + 1) * 2;
437 int ay = ax + 1;
438 int bx = i;
439 int by = bx + 1;
440 int cx = i + 2;
441 int cy = cx + 1;
442 int dx = i + (op.meshWidth + 1) * 2 + 2;
443 int dy = dx + 1;
444
445 const float* vertices = op.vertices;
446 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
447 ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
448 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
449
450 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
451 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
452 ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
453 }
454 }
455
456 if (!texture) {
457 texture = renderer.caches().textureCache.get(op.bitmap);
458 if (!texture) {
459 return;
460 }
461 }
462 const AutoTexture autoCleanup(texture);
463
464 /*
465 * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
466 * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
467 */
468 const int textureFillFlags = TextureFillFlags::None;
469 Glop glop;
470 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
471 .setRoundRectClipState(state.roundRectClipState)
472 .setMeshColoredTexturedMesh(mesh.get(), elementCount)
473 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
474 .setTransform(state.computedState.transform, TransformFlags::None)
475 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
476 .build();
477 renderer.renderGlop(state, glop);
478}
479
480void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) {
481 Texture* texture = renderer.getTexture(op.bitmap);
482 if (!texture) return;
483 const AutoTexture autoCleanup(texture);
484
John Reck38e0c322015-11-10 12:19:17 -0800485 Rect uv(std::max(0.0f, op.src.left / texture->width()),
486 std::max(0.0f, op.src.top / texture->height()),
487 std::min(1.0f, op.src.right / texture->width()),
488 std::min(1.0f, op.src.bottom / texture->height()));
Chris Craikf09ff5a2015-12-08 17:21:58 -0800489
490 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
491 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
492 const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth())
493 && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
494 Glop glop;
495 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
496 .setRoundRectClipState(state.roundRectClipState)
497 .setMeshTexturedUvQuad(texture->uvMapper, uv)
498 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
499 .setTransform(state.computedState.transform, TransformFlags::None)
500 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
501 .build();
502 renderer.renderGlop(state, glop);
503}
504
Chris Craike29ce6f2015-12-10 16:25:13 -0800505void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) {
506 renderer.renderFunctor(op, state);
507}
508
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800509void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800510 VertexBuffer buffer;
511 PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
512 state.computedState.transform, buffer);
513 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
514 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800515}
516
Chris Craik386aa032015-12-07 17:08:25 -0800517void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
518 if (op.paint->getPathEffect() != nullptr) {
519 PathTexture* texture = renderer.caches().pathCache.getOval(
520 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
521 const AutoTexture holder(texture);
522 if (CC_LIKELY(holder.texture)) {
Chris Craike2822e42016-02-22 16:42:24 -0800523 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.right,
524 *texture, *(op.paint));
Chris Craik386aa032015-12-07 17:08:25 -0800525 }
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)) {
Chris Craike2822e42016-02-22 16:42:24 -0800567 // Unlike other callers to renderPathTexture, no offsets are used because PathOp doesn't
568 // have any translate built in, other than what's in the SkPath itself
569 renderPathTexture(renderer, state, 0, 0, *texture, *(op.paint));
Chris Craik386aa032015-12-07 17:08:25 -0800570 }
571}
572
573void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
574 VertexBuffer buffer;
575 PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
576 state.computedState.transform, buffer);
577 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
578 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
579}
580
581// See SkPaintDefaults.h
582#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
583
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800584void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800585 if (op.paint->getStyle() != SkPaint::kFill_Style) {
586 // only fill + default miter is supported by drawConvexPath, since others must handle joins
587 static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
588 if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
589 || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
590 || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
591 PathTexture* texture = renderer.caches().pathCache.getRect(
592 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
593 const AutoTexture holder(texture);
594 if (CC_LIKELY(holder.texture)) {
Chris Craike2822e42016-02-22 16:42:24 -0800595 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
596 *texture, *(op.paint));
Chris Craik386aa032015-12-07 17:08:25 -0800597 }
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)) {
Chris Craike2822e42016-02-22 16:42:24 -0800630 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
631 *texture, *(op.paint));
Chris Craik386aa032015-12-07 17:08:25 -0800632 }
633 } else {
634 const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
635 state.computedState.transform, *(op.paint),
636 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
637 renderVertexBuffer(renderer, state, *buffer,
638 op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800639 }
640}
641
642static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
643 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
644 SkPaint paint;
645 paint.setAntiAlias(true); // want to use AlphaVertex
646
647 // The caller has made sure casterAlpha > 0.
648 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
649 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
650 ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
651 }
652 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
653 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
654 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
655 paint, VertexBufferRenderFlags::ShadowInterp);
656 }
657
658 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
659 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
660 spotShadowAlpha = Properties::overrideSpotShadowStrength;
661 }
662 if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
663 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
664 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
665 paint, VertexBufferRenderFlags::ShadowInterp);
666 }
667}
668
669void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
Chris Craikd8165e82016-02-03 15:52:25 -0800670 TessellationCache::vertexBuffer_pair_t buffers = op.shadowTask->getResult();
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800671 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
672}
673
674void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
675 Glop glop;
676 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
677 .setRoundRectClipState(state.roundRectClipState)
678 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
679 .setFillPaint(*op.paint, state.alpha)
680 .setTransform(state.computedState.transform, TransformFlags::None)
681 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
682 .build();
683 renderer.renderGlop(state, glop);
684}
685
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800686void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
Chris Craike4db79d2015-12-22 16:32:23 -0800687 renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800688}
689
Chris Craikd7448e62015-12-15 10:34:36 -0800690void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) {
691 // Note: can't trust clipSideFlags since we record with unmappedBounds == clip.
692 // TODO: respect clipSideFlags, once we record with bounds
Chris Craike4db79d2015-12-22 16:32:23 -0800693 auto renderTargetClip = state.computedState.clipState;
Chris Craikd7448e62015-12-15 10:34:36 -0800694
695 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
696 fontRenderer.setFont(op.paint, SkMatrix::I());
697 fontRenderer.setTextureFiltering(true);
698
699 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
700
701 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
702 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
703 TextDrawFunctor functor(&renderer, &state, renderTargetClip,
704 0.0f, 0.0f, false, alpha, mode, op.paint);
705
706 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
707 const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
Chris Craike8c3c812016-02-05 20:10:50 -0800708 if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount,
Chris Craikd7448e62015-12-15 10:34:36 -0800709 op.path, op.hOffset, op.vOffset,
710 mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
711 if (mustDirtyRenderTarget) {
712 // manually dirty render target, since TextDrawFunctor won't
713 state.computedState.transform.mapRect(layerBounds);
714 renderer.dirtyRenderTarget(layerBounds);
715 }
716 }
717}
718
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800719void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, const BakedOpState& state) {
720 const bool tryToSnap = !op.layer->getForceFilter();
721 float alpha = (op.layer->getAlpha() / 255.0f) * state.alpha;
722 Glop glop;
723 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
724 .setRoundRectClipState(state.roundRectClipState)
725 .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
726 .setFillTextureLayer(*(op.layer), alpha)
727 .setTransform(state.computedState.transform, TransformFlags::None)
728 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, Rect(op.layer->getWidth(), op.layer->getHeight()))
729 .build();
730 renderer.renderGlop(state, glop);
731}
732
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800733void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800734 // 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 -0800735 OffscreenBuffer* buffer = *op.layerHandle;
736
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800737 if (CC_UNLIKELY(!buffer)) {
738 // Layer was not allocated, which can occur if there were no draw ops inside. We draw the
739 // equivalent by drawing a rect with the same layer properties (alpha/xfer/filter).
740 SkPaint paint;
741 paint.setAlpha(op.alpha * 255);
742 paint.setXfermodeMode(op.mode);
743 paint.setColorFilter(op.colorFilter);
744 RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
745 BakedOpDispatcher::onRectOp(renderer, rectOp, state);
746 } else {
747 float layerAlpha = op.alpha * state.alpha;
748 Glop glop;
749 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
750 .setRoundRectClipState(state.roundRectClipState)
751 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
752 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
753 .setTransform(state.computedState.transform, TransformFlags::None)
754 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
755 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
756 .build();
757 renderer.renderGlop(state, glop);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800758
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800759 if (op.destroy) {
760 renderer.renderState().layerPool().putOrDelete(buffer);
761 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800762 }
763}
764
Chris Craikb87eadd2016-01-06 09:16:05 -0800765void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) {
Chris Craik7435eb12016-01-07 17:41:40 -0800766 LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!");
767 *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds);
768 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed");
Chris Craikb87eadd2016-01-06 09:16:05 -0800769}
770
771void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) {
Chris Craik7435eb12016-01-07 17:41:40 -0800772 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!");
773 if (!state.computedState.clippedBounds.isEmpty()) {
774 if (op.paint && op.paint->getAlpha() < 255) {
775 SkPaint layerPaint;
776 layerPaint.setAlpha(op.paint->getAlpha());
777 layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
778 layerPaint.setColorFilter(op.paint->getColorFilter());
779 RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint);
780 BakedOpDispatcher::onRectOp(renderer, rectOp, state);
781 }
782
783 OffscreenBuffer& layer = **(op.layerHandle);
784 auto mode = PaintUtils::getXfermodeDirect(op.paint);
785 Glop glop;
786 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
787 .setRoundRectClipState(state.roundRectClipState)
788 .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates())
789 .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap)
790 .setTransform(state.computedState.transform, TransformFlags::None)
791 .setModelViewMapUnitToRect(state.computedState.clippedBounds)
792 .build();
793 renderer.renderGlop(state, glop);
794 }
John Reck7db5ffb2016-01-15 13:17:09 -0800795 renderer.renderState().layerPool().putOrDelete(*op.layerHandle);
Chris Craikb87eadd2016-01-06 09:16:05 -0800796}
797
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800798} // namespace uirenderer
799} // namespace android