blob: 332a2044dbfb263d34d95d11c6de3eec3c195a48 [file] [log] [blame]
Chris Craik9e7fcfd2015-11-25 13:27:33 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "BakedOpDispatcher.h"
18
19#include "BakedOpRenderer.h"
20#include "Caches.h"
21#include "Glop.h"
22#include "GlopBuilder.h"
Chris Craikf09ff5a2015-12-08 17:21:58 -080023#include "Patch.h"
Chris Craik386aa032015-12-07 17:08:25 -080024#include "PathTessellator.h"
Chris Craik9e7fcfd2015-11-25 13:27:33 -080025#include "renderstate/OffscreenBufferPool.h"
26#include "renderstate/RenderState.h"
27#include "utils/GLUtils.h"
28#include "VertexBuffer.h"
29
30#include <algorithm>
31#include <math.h>
Chris Craik386aa032015-12-07 17:08:25 -080032#include <SkPaintDefaults.h>
Chris Craik9e7fcfd2015-11-25 13:27:33 -080033
34namespace android {
35namespace uirenderer {
36
Chris Craik15c3f192015-12-03 12:16:56 -080037static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds, const Rect& texCoord) {
38 vertices[0] = { bounds.left, bounds.top, texCoord.left, texCoord.top };
39 vertices[1] = { bounds.right, bounds.top, texCoord.right, texCoord.top };
40 vertices[2] = { bounds.left, bounds.bottom, texCoord.left, texCoord.bottom };
41 vertices[3] = { bounds.right, bounds.bottom, texCoord.right, texCoord.bottom };
42}
43
44void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
45 const MergedBakedOpList& opList) {
46
47 const BakedOpState& firstState = *(opList.states[0]);
48 const SkBitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
49
50 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(bitmap->pixelRef());
51 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(bitmap);
52 if (!texture) return;
53 const AutoTexture autoCleanup(texture);
54
55 TextureVertex vertices[opList.count * 4];
56 Rect texCoords(0, 0, 1, 1);
57 if (entry) {
58 entry->uvMapper.map(texCoords);
59 }
Chris Craik15c3f192015-12-03 12:16:56 -080060 for (size_t i = 0; i < opList.count; i++) {
61 const BakedOpState& state = *(opList.states[i]);
62 TextureVertex* rectVerts = &vertices[i * 4];
63 Rect opBounds = state.computedState.clippedBounds;
64 if (CC_LIKELY(state.computedState.transform.isPureTranslate())) {
65 // pure translate, so snap (same behavior as onBitmapOp)
66 opBounds.snapToPixelBoundaries();
67 }
68 storeTexturedRect(rectVerts, opBounds, texCoords);
69 renderer.dirtyRenderTarget(opBounds);
Chris Craik15c3f192015-12-03 12:16:56 -080070 }
71
72 const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
73 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
74 Glop glop;
75 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
76 .setRoundRectClipState(firstState.roundRectClipState)
77 .setMeshTexturedIndexedQuads(vertices, opList.count * 6)
78 .setFillTexturePaint(*texture, textureFillFlags, firstState.op->paint, firstState.alpha)
79 .setTransform(Matrix4::identity(), TransformFlags::None)
Chris Craikf09ff5a2015-12-08 17:21:58 -080080 .setModelViewIdentityEmptyBounds()
81 .build();
Chris Craike4db79d2015-12-22 16:32:23 -080082 ClipRect renderTargetClip(opList.clip);
83 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
84 renderer.renderGlop(nullptr, clip, glop);
Chris Craikf09ff5a2015-12-08 17:21:58 -080085}
86
87void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
88 const MergedBakedOpList& opList) {
89 const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op));
90 const BakedOpState& firstState = *(opList.states[0]);
91 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(
92 firstOp.bitmap->pixelRef());
93
94 // Batches will usually contain a small number of items so it's
95 // worth performing a first iteration to count the exact number
96 // of vertices we need in the new mesh
97 uint32_t totalVertices = 0;
98
99 for (size_t i = 0; i < opList.count; i++) {
100 const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
101
102 // TODO: cache mesh lookups
103 const Patch* opMesh = renderer.caches().patchCache.get(
104 entry, op.bitmap->width(), op.bitmap->height(),
105 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
106 totalVertices += opMesh->verticesCount;
107 }
108
109 const bool dirtyRenderTarget = renderer.offscreenRenderTarget();
110
111 uint32_t indexCount = 0;
112
113 TextureVertex vertices[totalVertices];
114 TextureVertex* vertex = &vertices[0];
115 // Create a mesh that contains the transformed vertices for all the
116 // 9-patch objects that are part of the batch. Note that onDefer()
117 // enforces ops drawn by this function to have a pure translate or
118 // identity matrix
119 for (size_t i = 0; i < opList.count; i++) {
120 const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
121 const BakedOpState& state = *opList.states[i];
122
123 // TODO: cache mesh lookups
124 const Patch* opMesh = renderer.caches().patchCache.get(
125 entry, op.bitmap->width(), op.bitmap->height(),
126 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
127
128
129 uint32_t vertexCount = opMesh->verticesCount;
130 if (vertexCount == 0) continue;
131
132 // We use the bounds to know where to translate our vertices
133 // Using patchOp->state.mBounds wouldn't work because these
134 // bounds are clipped
135 const float tx = floorf(state.computedState.transform.getTranslateX()
136 + op.unmappedBounds.left + 0.5f);
137 const float ty = floorf(state.computedState.transform.getTranslateY()
138 + op.unmappedBounds.top + 0.5f);
139
140 // Copy & transform all the vertices for the current operation
141 TextureVertex* opVertices = opMesh->vertices.get();
142 for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
143 TextureVertex::set(vertex++,
144 opVertices->x + tx, opVertices->y + ty,
145 opVertices->u, opVertices->v);
146 }
147
148 // Dirty the current layer if possible. When the 9-patch does not
149 // contain empty quads we can take a shortcut and simply set the
150 // dirty rect to the object's bounds.
151 if (dirtyRenderTarget) {
152 if (!opMesh->hasEmptyQuads) {
153 renderer.dirtyRenderTarget(Rect(tx, ty,
154 tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.getHeight()));
155 } else {
156 const size_t count = opMesh->quads.size();
157 for (size_t i = 0; i < count; i++) {
158 const Rect& quadBounds = opMesh->quads[i];
159 const float x = tx + quadBounds.left;
160 const float y = ty + quadBounds.top;
161 renderer.dirtyRenderTarget(Rect(x, y,
162 x + quadBounds.getWidth(), y + quadBounds.getHeight()));
163 }
164 }
165 }
166
167 indexCount += opMesh->indexCount;
168 }
169
170
171 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap);
172 if (!texture) return;
173 const AutoTexture autoCleanup(texture);
174
175 // 9 patches are built for stretching - always filter
176 int textureFillFlags = TextureFillFlags::ForceFilter;
177 if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) {
178 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
179 }
180 Glop glop;
181 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
182 .setRoundRectClipState(firstState.roundRectClipState)
183 .setMeshTexturedIndexedQuads(vertices, indexCount)
184 .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha)
185 .setTransform(Matrix4::identity(), TransformFlags::None)
186 .setModelViewIdentityEmptyBounds()
Chris Craik15c3f192015-12-03 12:16:56 -0800187 .build();
Chris Craike4db79d2015-12-22 16:32:23 -0800188 ClipRect renderTargetClip(opList.clip);
189 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
190 renderer.renderGlop(nullptr, clip, glop);
Chris Craik15c3f192015-12-03 12:16:56 -0800191}
192
193static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
194 const TextOp& op, const BakedOpState& state) {
195 renderer.caches().textureState().activateTexture(0);
196
197 PaintUtils::TextShadow textShadow;
198 if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
199 LOG_ALWAYS_FATAL("failed to query shadow attributes");
200 }
201
202 renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
203 ShadowTexture* texture = renderer.caches().dropShadowCache.get(
204 op.paint, (const char*) op.glyphs,
205 op.glyphCount, textShadow.radius, op.positions);
206 // If the drop shadow exceeds the max texture size or couldn't be
207 // allocated, skip drawing
208 if (!texture) return;
209 const AutoTexture autoCleanup(texture);
210
211 const float sx = op.x - texture->left + textShadow.dx;
212 const float sy = op.y - texture->top + textShadow.dy;
213
214 Glop glop;
215 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
216 .setRoundRectClipState(state.roundRectClipState)
217 .setMeshTexturedUnitQuad(nullptr)
218 .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
219 .setTransform(state.computedState.transform, TransformFlags::None)
220 .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
221 .build();
222 renderer.renderGlop(state, glop);
223}
224
225enum class TextRenderType {
226 Defer,
227 Flush
228};
229
230static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state,
Chris Craike4db79d2015-12-22 16:32:23 -0800231 const ClipBase* renderClip, TextRenderType renderType) {
Chris Craik15c3f192015-12-03 12:16:56 -0800232 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
233
234 if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
235 fontRenderer.setFont(op.paint, SkMatrix::I());
236 renderTextShadow(renderer, fontRenderer, op, state);
237 }
238
239 float x = op.x;
240 float y = op.y;
241 const Matrix4& transform = state.computedState.transform;
242 const bool pureTranslate = transform.isPureTranslate();
243 if (CC_LIKELY(pureTranslate)) {
244 x = floorf(x + transform.getTranslateX() + 0.5f);
245 y = floorf(y + transform.getTranslateY() + 0.5f);
246 fontRenderer.setFont(op.paint, SkMatrix::I());
247 fontRenderer.setTextureFiltering(false);
248 } else if (CC_UNLIKELY(transform.isPerspective())) {
249 fontRenderer.setFont(op.paint, SkMatrix::I());
250 fontRenderer.setTextureFiltering(true);
251 } else {
252 // We only pass a partial transform to the font renderer. That partial
253 // matrix defines how glyphs are rasterized. Typically we want glyphs
254 // to be rasterized at their final size on screen, which means the partial
255 // matrix needs to take the scale factor into account.
256 // When a partial matrix is used to transform glyphs during rasterization,
257 // the mesh is generated with the inverse transform (in the case of scale,
258 // the mesh is generated at 1.0 / scale for instance.) This allows us to
259 // apply the full transform matrix at draw time in the vertex shader.
260 // Applying the full matrix in the shader is the easiest way to handle
261 // rotation and perspective and allows us to always generated quads in the
262 // font renderer which greatly simplifies the code, clipping in particular.
263 float sx, sy;
264 transform.decomposeScale(sx, sy);
265 fontRenderer.setFont(op.paint, SkMatrix::MakeScale(
266 roundf(std::max(1.0f, sx)),
267 roundf(std::max(1.0f, sy))));
268 fontRenderer.setTextureFiltering(true);
269 }
270 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
271
272 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
273 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
274 TextDrawFunctor functor(&renderer, &state, renderClip,
275 x, y, pureTranslate, alpha, mode, op.paint);
276
277 bool forceFinish = (renderType == TextRenderType::Flush);
278 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
Chris Craike4db79d2015-12-22 16:32:23 -0800279 const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr;
Chris Craik15c3f192015-12-03 12:16:56 -0800280 fontRenderer.renderPosText(op.paint, localOpClip,
281 (const char*) op.glyphs, op.glyphCount, x, y,
282 op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
283
284 if (mustDirtyRenderTarget) {
285 if (!pureTranslate) {
286 transform.mapRect(layerBounds);
287 }
288 renderer.dirtyRenderTarget(layerBounds);
289 }
290}
291
292void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
293 const MergedBakedOpList& opList) {
Chris Craike4db79d2015-12-22 16:32:23 -0800294 ClipRect renderTargetClip(opList.clip);
295 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
Chris Craik15c3f192015-12-03 12:16:56 -0800296 for (size_t i = 0; i < opList.count; i++) {
297 const BakedOpState& state = *(opList.states[i]);
298 const TextOp& op = *(static_cast<const TextOp*>(state.op));
299 TextRenderType renderType = (i + 1 == opList.count)
300 ? TextRenderType::Flush : TextRenderType::Defer;
301 renderTextOp(renderer, op, state, clip, renderType);
302 }
303}
304
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800305void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
306 LOG_ALWAYS_FATAL("unsupported operation");
307}
308
Chris Craik15c3f192015-12-03 12:16:56 -0800309void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer&, const BeginLayerOp&, const BakedOpState&) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800310 LOG_ALWAYS_FATAL("unsupported operation");
311}
312
Chris Craik15c3f192015-12-03 12:16:56 -0800313void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer&, const EndLayerOp&, const BakedOpState&) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800314 LOG_ALWAYS_FATAL("unsupported operation");
315}
316
Chris Craik268a9c02015-12-09 18:05:12 -0800317void BakedOpDispatcher::onCirclePropsOp(BakedOpRenderer&, const CirclePropsOp&, const BakedOpState&) {
318 LOG_ALWAYS_FATAL("unsupported operation");
319}
320
321void BakedOpDispatcher::onRoundRectPropsOp(BakedOpRenderer&, const RoundRectPropsOp&, const BakedOpState&) {
322 LOG_ALWAYS_FATAL("unsupported operation");
323}
324
Chris Craik386aa032015-12-07 17:08:25 -0800325namespace VertexBufferRenderFlags {
326 enum {
327 Offset = 0x1,
328 ShadowInterp = 0x2,
329 };
330}
331
332static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
333 const VertexBuffer& vertexBuffer, float translateX, float translateY,
334 const SkPaint& paint, int vertexBufferRenderFlags) {
335 if (CC_LIKELY(vertexBuffer.getVertexCount())) {
336 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
337 const int transformFlags = TransformFlags::OffsetByFudgeFactor;
338 Glop glop;
339 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
340 .setRoundRectClipState(state.roundRectClipState)
341 .setMeshVertexBuffer(vertexBuffer, shadowInterp)
342 .setFillPaint(paint, state.alpha)
343 .setTransform(state.computedState.transform, transformFlags)
344 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
345 .build();
346 renderer.renderGlop(state, glop);
347 }
348}
349
350static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
351 const SkPath& path, const SkPaint& paint) {
352 VertexBuffer vertexBuffer;
353 // TODO: try clipping large paths to viewport
354 PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
355 renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
356}
357
358static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
359 PathTexture& texture, const RecordedOp& op) {
360 Rect dest(texture.width, texture.height);
361 dest.translate(texture.left + op.unmappedBounds.left - texture.offset,
362 texture.top + op.unmappedBounds.top - texture.offset);
363 Glop glop;
364 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
365 .setRoundRectClipState(state.roundRectClipState)
366 .setMeshTexturedUnitQuad(nullptr)
367 .setFillPathTexturePaint(texture, *(op.paint), state.alpha)
368 .setTransform(state.computedState.transform, TransformFlags::None)
369 .setModelViewMapUnitToRect(dest)
370 .build();
371 renderer.renderGlop(state, glop);
372}
373
374SkRect getBoundsOfFill(const RecordedOp& op) {
375 SkRect bounds = op.unmappedBounds.toSkRect();
376 if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
377 float outsetDistance = op.paint->getStrokeWidth() / 2;
378 bounds.outset(outsetDistance, outsetDistance);
379 }
380 return bounds;
381}
382
383void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
384 // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
385 if (op.paint->getStyle() != SkPaint::kStroke_Style
386 || op.paint->getPathEffect() != nullptr
387 || op.useCenter) {
388 PathTexture* texture = renderer.caches().pathCache.getArc(
389 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
390 op.startAngle, op.sweepAngle, op.useCenter, op.paint);
391 const AutoTexture holder(texture);
392 if (CC_LIKELY(holder.texture)) {
393 renderPathTexture(renderer, state, *texture, op);
394 }
395 } else {
396 SkRect rect = getBoundsOfFill(op);
397 SkPath path;
398 if (op.useCenter) {
399 path.moveTo(rect.centerX(), rect.centerY());
400 }
401 path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
402 if (op.useCenter) {
403 path.close();
404 }
405 renderConvexPath(renderer, state, path, *(op.paint));
406 }
407}
408
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800409void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800410 Texture* texture = renderer.getTexture(op.bitmap);
411 if (!texture) return;
412 const AutoTexture autoCleanup(texture);
413
414 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
415 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
416 Glop glop;
417 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
418 .setRoundRectClipState(state.roundRectClipState)
419 .setMeshTexturedUnitQuad(texture->uvMapper)
420 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
421 .setTransform(state.computedState.transform, TransformFlags::None)
Chris Craik5430ab22015-12-10 16:28:16 -0800422 .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height))
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800423 .build();
424 renderer.renderGlop(state, glop);
425}
426
Chris Craikf09ff5a2015-12-08 17:21:58 -0800427void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
428 const static UvMapper defaultUvMapper;
429 const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
430
431 std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
432 ColorTextureVertex* vertex = &mesh[0];
433
434 const int* colors = op.colors;
435 std::unique_ptr<int[]> tempColors;
436 if (!colors) {
437 uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
438 tempColors.reset(new int[colorsCount]);
439 memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
440 colors = tempColors.get();
441 }
442
443 Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef());
444 const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper);
445
446 for (int32_t y = 0; y < op.meshHeight; y++) {
447 for (int32_t x = 0; x < op.meshWidth; x++) {
448 uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
449
450 float u1 = float(x) / op.meshWidth;
451 float u2 = float(x + 1) / op.meshWidth;
452 float v1 = float(y) / op.meshHeight;
453 float v2 = float(y + 1) / op.meshHeight;
454
455 mapper.map(u1, v1, u2, v2);
456
457 int ax = i + (op.meshWidth + 1) * 2;
458 int ay = ax + 1;
459 int bx = i;
460 int by = bx + 1;
461 int cx = i + 2;
462 int cy = cx + 1;
463 int dx = i + (op.meshWidth + 1) * 2 + 2;
464 int dy = dx + 1;
465
466 const float* vertices = op.vertices;
467 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
468 ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
469 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
470
471 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
472 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
473 ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
474 }
475 }
476
477 if (!texture) {
478 texture = renderer.caches().textureCache.get(op.bitmap);
479 if (!texture) {
480 return;
481 }
482 }
483 const AutoTexture autoCleanup(texture);
484
485 /*
486 * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
487 * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
488 */
489 const int textureFillFlags = TextureFillFlags::None;
490 Glop glop;
491 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
492 .setRoundRectClipState(state.roundRectClipState)
493 .setMeshColoredTexturedMesh(mesh.get(), elementCount)
494 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
495 .setTransform(state.computedState.transform, TransformFlags::None)
496 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
497 .build();
498 renderer.renderGlop(state, glop);
499}
500
501void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) {
502 Texture* texture = renderer.getTexture(op.bitmap);
503 if (!texture) return;
504 const AutoTexture autoCleanup(texture);
505
506 Rect uv(std::max(0.0f, op.src.left / texture->width),
507 std::max(0.0f, op.src.top / texture->height),
508 std::min(1.0f, op.src.right / texture->width),
509 std::min(1.0f, op.src.bottom / texture->height));
510
511 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
512 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
513 const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth())
514 && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
515 Glop glop;
516 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
517 .setRoundRectClipState(state.roundRectClipState)
518 .setMeshTexturedUvQuad(texture->uvMapper, uv)
519 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
520 .setTransform(state.computedState.transform, TransformFlags::None)
521 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
522 .build();
523 renderer.renderGlop(state, glop);
524}
525
Chris Craike29ce6f2015-12-10 16:25:13 -0800526void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) {
527 renderer.renderFunctor(op, state);
528}
529
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800530void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800531 VertexBuffer buffer;
532 PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
533 state.computedState.transform, buffer);
534 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
535 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800536}
537
Chris Craik386aa032015-12-07 17:08:25 -0800538void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
539 if (op.paint->getPathEffect() != nullptr) {
540 PathTexture* texture = renderer.caches().pathCache.getOval(
541 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
542 const AutoTexture holder(texture);
543 if (CC_LIKELY(holder.texture)) {
544 renderPathTexture(renderer, state, *texture, op);
545 }
546 } else {
547 SkPath path;
548 SkRect rect = getBoundsOfFill(op);
549 path.addOval(rect);
550 renderConvexPath(renderer, state, path, *(op.paint));
551 }
552}
553
Chris Craikf09ff5a2015-12-08 17:21:58 -0800554void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) {
555 // 9 patches are built for stretching - always filter
556 int textureFillFlags = TextureFillFlags::ForceFilter;
557 if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
558 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
559 }
560
561 // TODO: avoid redoing the below work each frame:
562 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef());
563 const Patch* mesh = renderer.caches().patchCache.get(
564 entry, op.bitmap->width(), op.bitmap->height(),
565 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
566
567 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
568 if (!texture) return;
569 const AutoTexture autoCleanup(texture);
570 Glop glop;
571 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
572 .setRoundRectClipState(state.roundRectClipState)
573 .setMeshPatchQuads(*mesh)
574 .setMeshTexturedUnitQuad(texture->uvMapper)
575 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
576 .setTransform(state.computedState.transform, TransformFlags::None)
577 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
578 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
579 .build();
580 renderer.renderGlop(state, glop);
581}
582
Chris Craik386aa032015-12-07 17:08:25 -0800583void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
584 PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
585 const AutoTexture holder(texture);
586 if (CC_LIKELY(holder.texture)) {
587 renderPathTexture(renderer, state, *texture, op);
588 }
589}
590
591void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
592 VertexBuffer buffer;
593 PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
594 state.computedState.transform, buffer);
595 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
596 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
597}
598
599// See SkPaintDefaults.h
600#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
601
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800602void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800603 if (op.paint->getStyle() != SkPaint::kFill_Style) {
604 // only fill + default miter is supported by drawConvexPath, since others must handle joins
605 static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
606 if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
607 || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
608 || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
609 PathTexture* texture = renderer.caches().pathCache.getRect(
610 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
611 const AutoTexture holder(texture);
612 if (CC_LIKELY(holder.texture)) {
613 renderPathTexture(renderer, state, *texture, op);
614 }
615 } else {
616 SkPath path;
617 path.addRect(getBoundsOfFill(op));
618 renderConvexPath(renderer, state, path, *(op.paint));
619 }
620 } else {
621 if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
622 SkPath path;
623 path.addRect(op.unmappedBounds.toSkRect());
624 renderConvexPath(renderer, state, path, *(op.paint));
625 } else {
626 // render simple unit quad, no tessellation required
627 Glop glop;
628 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
629 .setRoundRectClipState(state.roundRectClipState)
630 .setMeshUnitQuad()
631 .setFillPaint(*op.paint, state.alpha)
632 .setTransform(state.computedState.transform, TransformFlags::None)
633 .setModelViewMapUnitToRect(op.unmappedBounds)
634 .build();
635 renderer.renderGlop(state, glop);
636 }
637 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800638}
639
Chris Craik386aa032015-12-07 17:08:25 -0800640void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
641 if (op.paint->getPathEffect() != nullptr) {
642 PathTexture* texture = renderer.caches().pathCache.getRoundRect(
643 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
644 op.rx, op.ry, op.paint);
645 const AutoTexture holder(texture);
646 if (CC_LIKELY(holder.texture)) {
647 renderPathTexture(renderer, state, *texture, op);
648 }
649 } else {
650 const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
651 state.computedState.transform, *(op.paint),
652 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
653 renderVertexBuffer(renderer, state, *buffer,
654 op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800655 }
656}
657
658static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
659 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
660 SkPaint paint;
661 paint.setAntiAlias(true); // want to use AlphaVertex
662
663 // The caller has made sure casterAlpha > 0.
664 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
665 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
666 ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
667 }
668 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
669 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
670 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
671 paint, VertexBufferRenderFlags::ShadowInterp);
672 }
673
674 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
675 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
676 spotShadowAlpha = Properties::overrideSpotShadowStrength;
677 }
678 if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
679 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
680 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
681 paint, VertexBufferRenderFlags::ShadowInterp);
682 }
683}
684
685void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
686 TessellationCache::vertexBuffer_pair_t buffers;
687 renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform,
688 op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath,
689 &op.shadowMatrixXY, &op.shadowMatrixZ,
690 op.lightCenter, renderer.getLightInfo().lightRadius,
691 buffers);
692
693 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
694}
695
696void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
697 Glop glop;
698 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
699 .setRoundRectClipState(state.roundRectClipState)
700 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
701 .setFillPaint(*op.paint, state.alpha)
702 .setTransform(state.computedState.transform, TransformFlags::None)
703 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
704 .build();
705 renderer.renderGlop(state, glop);
706}
707
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800708void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
Chris Craike4db79d2015-12-22 16:32:23 -0800709 renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800710}
711
Chris Craikd7448e62015-12-15 10:34:36 -0800712void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) {
713 // Note: can't trust clipSideFlags since we record with unmappedBounds == clip.
714 // TODO: respect clipSideFlags, once we record with bounds
Chris Craike4db79d2015-12-22 16:32:23 -0800715 auto renderTargetClip = state.computedState.clipState;
Chris Craikd7448e62015-12-15 10:34:36 -0800716
717 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
718 fontRenderer.setFont(op.paint, SkMatrix::I());
719 fontRenderer.setTextureFiltering(true);
720
721 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
722
723 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
724 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
725 TextDrawFunctor functor(&renderer, &state, renderTargetClip,
726 0.0f, 0.0f, false, alpha, mode, op.paint);
727
728 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
729 const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
730 if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip,
731 reinterpret_cast<const char*>(op.glyphs), op.glyphCount,
732 op.path, op.hOffset, op.vOffset,
733 mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
734 if (mustDirtyRenderTarget) {
735 // manually dirty render target, since TextDrawFunctor won't
736 state.computedState.transform.mapRect(layerBounds);
737 renderer.dirtyRenderTarget(layerBounds);
738 }
739 }
740}
741
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800742void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, const BakedOpState& state) {
743 const bool tryToSnap = !op.layer->getForceFilter();
744 float alpha = (op.layer->getAlpha() / 255.0f) * state.alpha;
745 Glop glop;
746 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
747 .setRoundRectClipState(state.roundRectClipState)
748 .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
749 .setFillTextureLayer(*(op.layer), alpha)
750 .setTransform(state.computedState.transform, TransformFlags::None)
751 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, Rect(op.layer->getWidth(), op.layer->getHeight()))
752 .build();
753 renderer.renderGlop(state, glop);
754}
755
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800756void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
757 OffscreenBuffer* buffer = *op.layerHandle;
758
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800759 float layerAlpha = op.alpha * state.alpha;
760 Glop glop;
761 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
762 .setRoundRectClipState(state.roundRectClipState)
763 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
764 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
765 .setTransform(state.computedState.transform, TransformFlags::None)
766 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
767 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
768 .build();
769 renderer.renderGlop(state, glop);
770
771 if (op.destroy) {
772 renderer.renderState().layerPool().putOrDelete(buffer);
773 }
774}
775
776} // namespace uirenderer
777} // namespace android