blob: 80180068c44a707a70746f81f18d3b767e4e6382 [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 Craik678ff812016-03-01 13:27:54 -080033#include <SkPathOps.h>
Chris Craik9e7fcfd2015-11-25 13:27:33 -080034
35namespace android {
36namespace uirenderer {
37
Chris Craik15c3f192015-12-03 12:16:56 -080038static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds, const Rect& texCoord) {
39 vertices[0] = { bounds.left, bounds.top, texCoord.left, texCoord.top };
40 vertices[1] = { bounds.right, bounds.top, texCoord.right, texCoord.top };
41 vertices[2] = { bounds.left, bounds.bottom, texCoord.left, texCoord.bottom };
42 vertices[3] = { bounds.right, bounds.bottom, texCoord.right, texCoord.bottom };
43}
44
45void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
46 const MergedBakedOpList& opList) {
47
48 const BakedOpState& firstState = *(opList.states[0]);
49 const SkBitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
50
51 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(bitmap->pixelRef());
52 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(bitmap);
53 if (!texture) return;
54 const AutoTexture autoCleanup(texture);
55
56 TextureVertex vertices[opList.count * 4];
57 Rect texCoords(0, 0, 1, 1);
58 if (entry) {
59 entry->uvMapper.map(texCoords);
60 }
Chris Craik15c3f192015-12-03 12:16:56 -080061 for (size_t i = 0; i < opList.count; i++) {
62 const BakedOpState& state = *(opList.states[i]);
63 TextureVertex* rectVerts = &vertices[i * 4];
64 Rect opBounds = state.computedState.clippedBounds;
65 if (CC_LIKELY(state.computedState.transform.isPureTranslate())) {
66 // pure translate, so snap (same behavior as onBitmapOp)
67 opBounds.snapToPixelBoundaries();
68 }
69 storeTexturedRect(rectVerts, opBounds, texCoords);
70 renderer.dirtyRenderTarget(opBounds);
Chris Craik15c3f192015-12-03 12:16:56 -080071 }
72
73 const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
74 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
75 Glop glop;
76 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
77 .setRoundRectClipState(firstState.roundRectClipState)
78 .setMeshTexturedIndexedQuads(vertices, opList.count * 6)
79 .setFillTexturePaint(*texture, textureFillFlags, firstState.op->paint, firstState.alpha)
80 .setTransform(Matrix4::identity(), TransformFlags::None)
Chris Craikf09ff5a2015-12-08 17:21:58 -080081 .setModelViewIdentityEmptyBounds()
82 .build();
Chris Craike4db79d2015-12-22 16:32:23 -080083 ClipRect renderTargetClip(opList.clip);
84 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
85 renderer.renderGlop(nullptr, clip, glop);
Chris Craikf09ff5a2015-12-08 17:21:58 -080086}
87
88void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
89 const MergedBakedOpList& opList) {
90 const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op));
91 const BakedOpState& firstState = *(opList.states[0]);
92 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(
93 firstOp.bitmap->pixelRef());
94
95 // Batches will usually contain a small number of items so it's
96 // worth performing a first iteration to count the exact number
97 // of vertices we need in the new mesh
98 uint32_t totalVertices = 0;
99
100 for (size_t i = 0; i < opList.count; i++) {
101 const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
102
103 // TODO: cache mesh lookups
104 const Patch* opMesh = renderer.caches().patchCache.get(
105 entry, op.bitmap->width(), op.bitmap->height(),
106 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
107 totalVertices += opMesh->verticesCount;
108 }
109
110 const bool dirtyRenderTarget = renderer.offscreenRenderTarget();
111
112 uint32_t indexCount = 0;
113
114 TextureVertex vertices[totalVertices];
115 TextureVertex* vertex = &vertices[0];
116 // Create a mesh that contains the transformed vertices for all the
117 // 9-patch objects that are part of the batch. Note that onDefer()
118 // enforces ops drawn by this function to have a pure translate or
119 // identity matrix
120 for (size_t i = 0; i < opList.count; i++) {
121 const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
122 const BakedOpState& state = *opList.states[i];
123
124 // TODO: cache mesh lookups
125 const Patch* opMesh = renderer.caches().patchCache.get(
126 entry, op.bitmap->width(), op.bitmap->height(),
127 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
128
129
130 uint32_t vertexCount = opMesh->verticesCount;
131 if (vertexCount == 0) continue;
132
133 // We use the bounds to know where to translate our vertices
134 // Using patchOp->state.mBounds wouldn't work because these
135 // bounds are clipped
136 const float tx = floorf(state.computedState.transform.getTranslateX()
137 + op.unmappedBounds.left + 0.5f);
138 const float ty = floorf(state.computedState.transform.getTranslateY()
139 + op.unmappedBounds.top + 0.5f);
140
141 // Copy & transform all the vertices for the current operation
142 TextureVertex* opVertices = opMesh->vertices.get();
143 for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
144 TextureVertex::set(vertex++,
145 opVertices->x + tx, opVertices->y + ty,
146 opVertices->u, opVertices->v);
147 }
148
149 // Dirty the current layer if possible. When the 9-patch does not
150 // contain empty quads we can take a shortcut and simply set the
151 // dirty rect to the object's bounds.
152 if (dirtyRenderTarget) {
153 if (!opMesh->hasEmptyQuads) {
154 renderer.dirtyRenderTarget(Rect(tx, ty,
155 tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.getHeight()));
156 } else {
157 const size_t count = opMesh->quads.size();
158 for (size_t i = 0; i < count; i++) {
159 const Rect& quadBounds = opMesh->quads[i];
160 const float x = tx + quadBounds.left;
161 const float y = ty + quadBounds.top;
162 renderer.dirtyRenderTarget(Rect(x, y,
163 x + quadBounds.getWidth(), y + quadBounds.getHeight()));
164 }
165 }
166 }
167
168 indexCount += opMesh->indexCount;
169 }
170
171
172 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap);
173 if (!texture) return;
174 const AutoTexture autoCleanup(texture);
175
176 // 9 patches are built for stretching - always filter
177 int textureFillFlags = TextureFillFlags::ForceFilter;
178 if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) {
179 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
180 }
181 Glop glop;
182 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
183 .setRoundRectClipState(firstState.roundRectClipState)
184 .setMeshTexturedIndexedQuads(vertices, indexCount)
185 .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha)
186 .setTransform(Matrix4::identity(), TransformFlags::None)
187 .setModelViewIdentityEmptyBounds()
Chris Craik15c3f192015-12-03 12:16:56 -0800188 .build();
Chris Craike4db79d2015-12-22 16:32:23 -0800189 ClipRect renderTargetClip(opList.clip);
190 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
191 renderer.renderGlop(nullptr, clip, glop);
Chris Craik15c3f192015-12-03 12:16:56 -0800192}
193
194static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
195 const TextOp& op, const BakedOpState& state) {
196 renderer.caches().textureState().activateTexture(0);
197
198 PaintUtils::TextShadow textShadow;
199 if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
200 LOG_ALWAYS_FATAL("failed to query shadow attributes");
201 }
202
203 renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
204 ShadowTexture* texture = renderer.caches().dropShadowCache.get(
Chris Craike8c3c812016-02-05 20:10:50 -0800205 op.paint, op.glyphs, op.glyphCount, textShadow.radius, op.positions);
Chris Craik15c3f192015-12-03 12:16:56 -0800206 // 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 Craike8c3c812016-02-05 20:10:50 -0800280 fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y,
Chris Craik15c3f192015-12-03 12:16:56 -0800281 op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
282
283 if (mustDirtyRenderTarget) {
284 if (!pureTranslate) {
285 transform.mapRect(layerBounds);
286 }
287 renderer.dirtyRenderTarget(layerBounds);
288 }
289}
290
291void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
292 const MergedBakedOpList& opList) {
Chris Craike4db79d2015-12-22 16:32:23 -0800293 ClipRect renderTargetClip(opList.clip);
294 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
Chris Craik15c3f192015-12-03 12:16:56 -0800295 for (size_t i = 0; i < opList.count; i++) {
296 const BakedOpState& state = *(opList.states[i]);
297 const TextOp& op = *(static_cast<const TextOp*>(state.op));
298 TextRenderType renderType = (i + 1 == opList.count)
299 ? TextRenderType::Flush : TextRenderType::Defer;
300 renderTextOp(renderer, op, state, clip, renderType);
301 }
302}
303
Chris Craik386aa032015-12-07 17:08:25 -0800304namespace VertexBufferRenderFlags {
305 enum {
306 Offset = 0x1,
307 ShadowInterp = 0x2,
308 };
309}
310
311static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
312 const VertexBuffer& vertexBuffer, float translateX, float translateY,
313 const SkPaint& paint, int vertexBufferRenderFlags) {
314 if (CC_LIKELY(vertexBuffer.getVertexCount())) {
315 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
316 const int transformFlags = TransformFlags::OffsetByFudgeFactor;
317 Glop glop;
318 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
319 .setRoundRectClipState(state.roundRectClipState)
320 .setMeshVertexBuffer(vertexBuffer, shadowInterp)
321 .setFillPaint(paint, state.alpha)
322 .setTransform(state.computedState.transform, transformFlags)
323 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
324 .build();
325 renderer.renderGlop(state, glop);
326 }
327}
328
329static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
330 const SkPath& path, const SkPaint& paint) {
331 VertexBuffer vertexBuffer;
332 // TODO: try clipping large paths to viewport
333 PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
334 renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
335}
336
337static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
Chris Craike2822e42016-02-22 16:42:24 -0800338 float xOffset, float yOffset, PathTexture& texture, const SkPaint& paint) {
John Reck38e0c322015-11-10 12:19:17 -0800339 Rect dest(texture.width(), texture.height());
Chris Craike2822e42016-02-22 16:42:24 -0800340 dest.translate(xOffset + texture.left - texture.offset,
341 yOffset + texture.top - texture.offset);
Chris Craik386aa032015-12-07 17:08:25 -0800342 Glop glop;
343 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
344 .setRoundRectClipState(state.roundRectClipState)
345 .setMeshTexturedUnitQuad(nullptr)
Chris Craike2822e42016-02-22 16:42:24 -0800346 .setFillPathTexturePaint(texture, paint, state.alpha)
Chris Craik386aa032015-12-07 17:08:25 -0800347 .setTransform(state.computedState.transform, TransformFlags::None)
348 .setModelViewMapUnitToRect(dest)
349 .build();
350 renderer.renderGlop(state, glop);
351}
352
353SkRect getBoundsOfFill(const RecordedOp& op) {
354 SkRect bounds = op.unmappedBounds.toSkRect();
355 if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
356 float outsetDistance = op.paint->getStrokeWidth() / 2;
357 bounds.outset(outsetDistance, outsetDistance);
358 }
359 return bounds;
360}
361
362void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
363 // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
364 if (op.paint->getStyle() != SkPaint::kStroke_Style
365 || op.paint->getPathEffect() != nullptr
366 || op.useCenter) {
367 PathTexture* texture = renderer.caches().pathCache.getArc(
368 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
369 op.startAngle, op.sweepAngle, op.useCenter, op.paint);
370 const AutoTexture holder(texture);
371 if (CC_LIKELY(holder.texture)) {
Chris Craik0066a012016-02-29 13:34:43 -0800372 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
Chris Craike2822e42016-02-22 16:42:24 -0800373 *texture, *(op.paint));
Chris Craik386aa032015-12-07 17:08:25 -0800374 }
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)) {
Chris Craike2822e42016-02-22 16:42:24 -0800524 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.right,
525 *texture, *(op.paint));
Chris Craik386aa032015-12-07 17:08:25 -0800526 }
527 } else {
528 SkPath path;
529 SkRect rect = getBoundsOfFill(op);
530 path.addOval(rect);
Chris Craik678ff812016-03-01 13:27:54 -0800531
532 if (state.computedState.localProjectionPathMask != nullptr) {
533 // Mask the ripple path by the local space projection mask in local space.
534 // Note that this can create CCW paths.
535 Op(path, *state.computedState.localProjectionPathMask, kIntersect_SkPathOp, &path);
536 }
Chris Craik386aa032015-12-07 17:08:25 -0800537 renderConvexPath(renderer, state, path, *(op.paint));
538 }
539}
540
Chris Craikf09ff5a2015-12-08 17:21:58 -0800541void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) {
542 // 9 patches are built for stretching - always filter
543 int textureFillFlags = TextureFillFlags::ForceFilter;
544 if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
545 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
546 }
547
548 // TODO: avoid redoing the below work each frame:
549 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef());
550 const Patch* mesh = renderer.caches().patchCache.get(
551 entry, op.bitmap->width(), op.bitmap->height(),
552 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
553
554 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
Chris Craik89ceb5c2016-01-12 13:45:34 -0800555 if (CC_LIKELY(texture)) {
556 const AutoTexture autoCleanup(texture);
557 Glop glop;
558 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
559 .setRoundRectClipState(state.roundRectClipState)
560 .setMeshPatchQuads(*mesh)
561 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
562 .setTransform(state.computedState.transform, TransformFlags::None)
563 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
564 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
565 .build();
566 renderer.renderGlop(state, glop);
567 }
Chris Craikf09ff5a2015-12-08 17:21:58 -0800568}
569
Chris Craik386aa032015-12-07 17:08:25 -0800570void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
571 PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
572 const AutoTexture holder(texture);
573 if (CC_LIKELY(holder.texture)) {
Chris Craike2822e42016-02-22 16:42:24 -0800574 // Unlike other callers to renderPathTexture, no offsets are used because PathOp doesn't
575 // have any translate built in, other than what's in the SkPath itself
576 renderPathTexture(renderer, state, 0, 0, *texture, *(op.paint));
Chris Craik386aa032015-12-07 17:08:25 -0800577 }
578}
579
580void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
581 VertexBuffer buffer;
582 PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
583 state.computedState.transform, buffer);
584 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
585 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
586}
587
588// See SkPaintDefaults.h
589#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
590
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800591void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800592 if (op.paint->getStyle() != SkPaint::kFill_Style) {
593 // only fill + default miter is supported by drawConvexPath, since others must handle joins
594 static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
595 if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
596 || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
597 || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
598 PathTexture* texture = renderer.caches().pathCache.getRect(
599 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
600 const AutoTexture holder(texture);
601 if (CC_LIKELY(holder.texture)) {
Chris Craike2822e42016-02-22 16:42:24 -0800602 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
603 *texture, *(op.paint));
Chris Craik386aa032015-12-07 17:08:25 -0800604 }
605 } else {
606 SkPath path;
607 path.addRect(getBoundsOfFill(op));
608 renderConvexPath(renderer, state, path, *(op.paint));
609 }
610 } else {
611 if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
612 SkPath path;
613 path.addRect(op.unmappedBounds.toSkRect());
614 renderConvexPath(renderer, state, path, *(op.paint));
615 } else {
616 // render simple unit quad, no tessellation required
617 Glop glop;
618 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
619 .setRoundRectClipState(state.roundRectClipState)
620 .setMeshUnitQuad()
621 .setFillPaint(*op.paint, state.alpha)
622 .setTransform(state.computedState.transform, TransformFlags::None)
623 .setModelViewMapUnitToRect(op.unmappedBounds)
624 .build();
625 renderer.renderGlop(state, glop);
626 }
627 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800628}
629
Chris Craik386aa032015-12-07 17:08:25 -0800630void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
631 if (op.paint->getPathEffect() != nullptr) {
632 PathTexture* texture = renderer.caches().pathCache.getRoundRect(
633 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
634 op.rx, op.ry, op.paint);
635 const AutoTexture holder(texture);
636 if (CC_LIKELY(holder.texture)) {
Chris Craike2822e42016-02-22 16:42:24 -0800637 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
638 *texture, *(op.paint));
Chris Craik386aa032015-12-07 17:08:25 -0800639 }
640 } else {
641 const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
642 state.computedState.transform, *(op.paint),
643 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
644 renderVertexBuffer(renderer, state, *buffer,
645 op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800646 }
647}
648
649static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
650 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
651 SkPaint paint;
652 paint.setAntiAlias(true); // want to use AlphaVertex
653
654 // The caller has made sure casterAlpha > 0.
655 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
656 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
657 ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
658 }
659 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
660 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
661 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
662 paint, VertexBufferRenderFlags::ShadowInterp);
663 }
664
665 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
666 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
667 spotShadowAlpha = Properties::overrideSpotShadowStrength;
668 }
669 if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
670 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
671 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
672 paint, VertexBufferRenderFlags::ShadowInterp);
673 }
674}
675
676void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
Chris Craikd8165e82016-02-03 15:52:25 -0800677 TessellationCache::vertexBuffer_pair_t buffers = op.shadowTask->getResult();
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800678 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
679}
680
681void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
682 Glop glop;
683 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
684 .setRoundRectClipState(state.roundRectClipState)
685 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
686 .setFillPaint(*op.paint, state.alpha)
687 .setTransform(state.computedState.transform, TransformFlags::None)
688 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
689 .build();
690 renderer.renderGlop(state, glop);
691}
692
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800693void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
Chris Craike4db79d2015-12-22 16:32:23 -0800694 renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800695}
696
Chris Craikd7448e62015-12-15 10:34:36 -0800697void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) {
698 // Note: can't trust clipSideFlags since we record with unmappedBounds == clip.
699 // TODO: respect clipSideFlags, once we record with bounds
Chris Craike4db79d2015-12-22 16:32:23 -0800700 auto renderTargetClip = state.computedState.clipState;
Chris Craikd7448e62015-12-15 10:34:36 -0800701
702 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
703 fontRenderer.setFont(op.paint, SkMatrix::I());
704 fontRenderer.setTextureFiltering(true);
705
706 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
707
708 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
709 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
710 TextDrawFunctor functor(&renderer, &state, renderTargetClip,
711 0.0f, 0.0f, false, alpha, mode, op.paint);
712
713 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
714 const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
Chris Craike8c3c812016-02-05 20:10:50 -0800715 if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount,
Chris Craikd7448e62015-12-15 10:34:36 -0800716 op.path, op.hOffset, op.vOffset,
717 mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
718 if (mustDirtyRenderTarget) {
719 // manually dirty render target, since TextDrawFunctor won't
720 state.computedState.transform.mapRect(layerBounds);
721 renderer.dirtyRenderTarget(layerBounds);
722 }
723 }
724}
725
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800726void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, const BakedOpState& state) {
727 const bool tryToSnap = !op.layer->getForceFilter();
728 float alpha = (op.layer->getAlpha() / 255.0f) * state.alpha;
729 Glop glop;
730 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
731 .setRoundRectClipState(state.roundRectClipState)
732 .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
733 .setFillTextureLayer(*(op.layer), alpha)
734 .setTransform(state.computedState.transform, TransformFlags::None)
735 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, Rect(op.layer->getWidth(), op.layer->getHeight()))
736 .build();
737 renderer.renderGlop(state, glop);
738}
739
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800740void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800741 // 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 -0800742 OffscreenBuffer* buffer = *op.layerHandle;
743
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800744 if (CC_UNLIKELY(!buffer)) {
745 // Layer was not allocated, which can occur if there were no draw ops inside. We draw the
746 // equivalent by drawing a rect with the same layer properties (alpha/xfer/filter).
747 SkPaint paint;
748 paint.setAlpha(op.alpha * 255);
749 paint.setXfermodeMode(op.mode);
750 paint.setColorFilter(op.colorFilter);
751 RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
752 BakedOpDispatcher::onRectOp(renderer, rectOp, state);
753 } else {
754 float layerAlpha = op.alpha * state.alpha;
755 Glop glop;
756 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
757 .setRoundRectClipState(state.roundRectClipState)
758 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
759 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
760 .setTransform(state.computedState.transform, TransformFlags::None)
761 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
762 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
763 .build();
764 renderer.renderGlop(state, glop);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800765
Chris Craikc0f3f2f2016-02-02 16:10:32 -0800766 if (op.destroy) {
767 renderer.renderState().layerPool().putOrDelete(buffer);
768 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800769 }
770}
771
Chris Craikb87eadd2016-01-06 09:16:05 -0800772void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) {
Chris Craik7435eb12016-01-07 17:41:40 -0800773 LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!");
774 *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds);
775 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed");
Chris Craikb87eadd2016-01-06 09:16:05 -0800776}
777
778void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) {
Chris Craik7435eb12016-01-07 17:41:40 -0800779 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!");
780 if (!state.computedState.clippedBounds.isEmpty()) {
781 if (op.paint && op.paint->getAlpha() < 255) {
782 SkPaint layerPaint;
783 layerPaint.setAlpha(op.paint->getAlpha());
784 layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
785 layerPaint.setColorFilter(op.paint->getColorFilter());
786 RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint);
787 BakedOpDispatcher::onRectOp(renderer, rectOp, state);
788 }
789
790 OffscreenBuffer& layer = **(op.layerHandle);
791 auto mode = PaintUtils::getXfermodeDirect(op.paint);
792 Glop glop;
793 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
794 .setRoundRectClipState(state.roundRectClipState)
795 .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates())
796 .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap)
797 .setTransform(state.computedState.transform, TransformFlags::None)
798 .setModelViewMapUnitToRect(state.computedState.clippedBounds)
799 .build();
800 renderer.renderGlop(state, glop);
801 }
John Reck7db5ffb2016-01-15 13:17:09 -0800802 renderer.renderState().layerPool().putOrDelete(*op.layerHandle);
Chris Craikb87eadd2016-01-06 09:16:05 -0800803}
804
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800805} // namespace uirenderer
806} // namespace android