blob: 3d35dd5b1fe815efb8d51cfb1ff8fb3bdd42df2f [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 Craik386aa032015-12-07 17:08:25 -080023#include "PathTessellator.h"
Chris Craik9e7fcfd2015-11-25 13:27:33 -080024#include "renderstate/OffscreenBufferPool.h"
25#include "renderstate/RenderState.h"
26#include "utils/GLUtils.h"
27#include "VertexBuffer.h"
28
29#include <algorithm>
30#include <math.h>
Chris Craik386aa032015-12-07 17:08:25 -080031#include <SkPaintDefaults.h>
Chris Craik9e7fcfd2015-11-25 13:27:33 -080032
33namespace android {
34namespace uirenderer {
35
Chris Craik15c3f192015-12-03 12:16:56 -080036static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds, const Rect& texCoord) {
37 vertices[0] = { bounds.left, bounds.top, texCoord.left, texCoord.top };
38 vertices[1] = { bounds.right, bounds.top, texCoord.right, texCoord.top };
39 vertices[2] = { bounds.left, bounds.bottom, texCoord.left, texCoord.bottom };
40 vertices[3] = { bounds.right, bounds.bottom, texCoord.right, texCoord.bottom };
41}
42
43void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
44 const MergedBakedOpList& opList) {
45
46 const BakedOpState& firstState = *(opList.states[0]);
47 const SkBitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
48
49 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(bitmap->pixelRef());
50 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(bitmap);
51 if (!texture) return;
52 const AutoTexture autoCleanup(texture);
53
54 TextureVertex vertices[opList.count * 4];
55 Rect texCoords(0, 0, 1, 1);
56 if (entry) {
57 entry->uvMapper.map(texCoords);
58 }
59 // init to non-empty, so we can safely expandtoCoverRect
60 Rect totalBounds = firstState.computedState.clippedBounds;
61 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);
71
72 totalBounds.expandToCover(opBounds);
73 }
74
75 const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
76 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
77 Glop glop;
78 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
79 .setRoundRectClipState(firstState.roundRectClipState)
80 .setMeshTexturedIndexedQuads(vertices, opList.count * 6)
81 .setFillTexturePaint(*texture, textureFillFlags, firstState.op->paint, firstState.alpha)
82 .setTransform(Matrix4::identity(), TransformFlags::None)
83 .setModelViewOffsetRect(0, 0, totalBounds) // don't snap here, we snap per-quad above
84 .build();
85 renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
86}
87
88static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
89 const TextOp& op, const BakedOpState& state) {
90 renderer.caches().textureState().activateTexture(0);
91
92 PaintUtils::TextShadow textShadow;
93 if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
94 LOG_ALWAYS_FATAL("failed to query shadow attributes");
95 }
96
97 renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
98 ShadowTexture* texture = renderer.caches().dropShadowCache.get(
99 op.paint, (const char*) op.glyphs,
100 op.glyphCount, textShadow.radius, op.positions);
101 // If the drop shadow exceeds the max texture size or couldn't be
102 // allocated, skip drawing
103 if (!texture) return;
104 const AutoTexture autoCleanup(texture);
105
106 const float sx = op.x - texture->left + textShadow.dx;
107 const float sy = op.y - texture->top + textShadow.dy;
108
109 Glop glop;
110 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
111 .setRoundRectClipState(state.roundRectClipState)
112 .setMeshTexturedUnitQuad(nullptr)
113 .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
114 .setTransform(state.computedState.transform, TransformFlags::None)
115 .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
116 .build();
117 renderer.renderGlop(state, glop);
118}
119
120enum class TextRenderType {
121 Defer,
122 Flush
123};
124
125static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state,
126 const Rect* renderClip, TextRenderType renderType) {
127 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
128
129 if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
130 fontRenderer.setFont(op.paint, SkMatrix::I());
131 renderTextShadow(renderer, fontRenderer, op, state);
132 }
133
134 float x = op.x;
135 float y = op.y;
136 const Matrix4& transform = state.computedState.transform;
137 const bool pureTranslate = transform.isPureTranslate();
138 if (CC_LIKELY(pureTranslate)) {
139 x = floorf(x + transform.getTranslateX() + 0.5f);
140 y = floorf(y + transform.getTranslateY() + 0.5f);
141 fontRenderer.setFont(op.paint, SkMatrix::I());
142 fontRenderer.setTextureFiltering(false);
143 } else if (CC_UNLIKELY(transform.isPerspective())) {
144 fontRenderer.setFont(op.paint, SkMatrix::I());
145 fontRenderer.setTextureFiltering(true);
146 } else {
147 // We only pass a partial transform to the font renderer. That partial
148 // matrix defines how glyphs are rasterized. Typically we want glyphs
149 // to be rasterized at their final size on screen, which means the partial
150 // matrix needs to take the scale factor into account.
151 // When a partial matrix is used to transform glyphs during rasterization,
152 // the mesh is generated with the inverse transform (in the case of scale,
153 // the mesh is generated at 1.0 / scale for instance.) This allows us to
154 // apply the full transform matrix at draw time in the vertex shader.
155 // Applying the full matrix in the shader is the easiest way to handle
156 // rotation and perspective and allows us to always generated quads in the
157 // font renderer which greatly simplifies the code, clipping in particular.
158 float sx, sy;
159 transform.decomposeScale(sx, sy);
160 fontRenderer.setFont(op.paint, SkMatrix::MakeScale(
161 roundf(std::max(1.0f, sx)),
162 roundf(std::max(1.0f, sy))));
163 fontRenderer.setTextureFiltering(true);
164 }
165 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
166
167 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
168 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
169 TextDrawFunctor functor(&renderer, &state, renderClip,
170 x, y, pureTranslate, alpha, mode, op.paint);
171
172 bool forceFinish = (renderType == TextRenderType::Flush);
173 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
174 const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect : nullptr;
175 fontRenderer.renderPosText(op.paint, localOpClip,
176 (const char*) op.glyphs, op.glyphCount, x, y,
177 op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
178
179 if (mustDirtyRenderTarget) {
180 if (!pureTranslate) {
181 transform.mapRect(layerBounds);
182 }
183 renderer.dirtyRenderTarget(layerBounds);
184 }
185}
186
187void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
188 const MergedBakedOpList& opList) {
189 const Rect* clip = opList.clipSideFlags ? &opList.clip : nullptr;
190 for (size_t i = 0; i < opList.count; i++) {
191 const BakedOpState& state = *(opList.states[i]);
192 const TextOp& op = *(static_cast<const TextOp*>(state.op));
193 TextRenderType renderType = (i + 1 == opList.count)
194 ? TextRenderType::Flush : TextRenderType::Defer;
195 renderTextOp(renderer, op, state, clip, renderType);
196 }
197}
198
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800199void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
200 LOG_ALWAYS_FATAL("unsupported operation");
201}
202
Chris Craik15c3f192015-12-03 12:16:56 -0800203void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer&, const BeginLayerOp&, const BakedOpState&) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800204 LOG_ALWAYS_FATAL("unsupported operation");
205}
206
Chris Craik15c3f192015-12-03 12:16:56 -0800207void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer&, const EndLayerOp&, const BakedOpState&) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800208 LOG_ALWAYS_FATAL("unsupported operation");
209}
210
Chris Craik386aa032015-12-07 17:08:25 -0800211namespace VertexBufferRenderFlags {
212 enum {
213 Offset = 0x1,
214 ShadowInterp = 0x2,
215 };
216}
217
218static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
219 const VertexBuffer& vertexBuffer, float translateX, float translateY,
220 const SkPaint& paint, int vertexBufferRenderFlags) {
221 if (CC_LIKELY(vertexBuffer.getVertexCount())) {
222 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
223 const int transformFlags = TransformFlags::OffsetByFudgeFactor;
224 Glop glop;
225 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
226 .setRoundRectClipState(state.roundRectClipState)
227 .setMeshVertexBuffer(vertexBuffer, shadowInterp)
228 .setFillPaint(paint, state.alpha)
229 .setTransform(state.computedState.transform, transformFlags)
230 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
231 .build();
232 renderer.renderGlop(state, glop);
233 }
234}
235
236static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
237 const SkPath& path, const SkPaint& paint) {
238 VertexBuffer vertexBuffer;
239 // TODO: try clipping large paths to viewport
240 PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
241 renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
242}
243
244static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
245 PathTexture& texture, const RecordedOp& op) {
246 Rect dest(texture.width, texture.height);
247 dest.translate(texture.left + op.unmappedBounds.left - texture.offset,
248 texture.top + op.unmappedBounds.top - texture.offset);
249 Glop glop;
250 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
251 .setRoundRectClipState(state.roundRectClipState)
252 .setMeshTexturedUnitQuad(nullptr)
253 .setFillPathTexturePaint(texture, *(op.paint), state.alpha)
254 .setTransform(state.computedState.transform, TransformFlags::None)
255 .setModelViewMapUnitToRect(dest)
256 .build();
257 renderer.renderGlop(state, glop);
258}
259
260SkRect getBoundsOfFill(const RecordedOp& op) {
261 SkRect bounds = op.unmappedBounds.toSkRect();
262 if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
263 float outsetDistance = op.paint->getStrokeWidth() / 2;
264 bounds.outset(outsetDistance, outsetDistance);
265 }
266 return bounds;
267}
268
269void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
270 // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
271 if (op.paint->getStyle() != SkPaint::kStroke_Style
272 || op.paint->getPathEffect() != nullptr
273 || op.useCenter) {
274 PathTexture* texture = renderer.caches().pathCache.getArc(
275 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
276 op.startAngle, op.sweepAngle, op.useCenter, op.paint);
277 const AutoTexture holder(texture);
278 if (CC_LIKELY(holder.texture)) {
279 renderPathTexture(renderer, state, *texture, op);
280 }
281 } else {
282 SkRect rect = getBoundsOfFill(op);
283 SkPath path;
284 if (op.useCenter) {
285 path.moveTo(rect.centerX(), rect.centerY());
286 }
287 path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
288 if (op.useCenter) {
289 path.close();
290 }
291 renderConvexPath(renderer, state, path, *(op.paint));
292 }
293}
294
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800295void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800296 Texture* texture = renderer.getTexture(op.bitmap);
297 if (!texture) return;
298 const AutoTexture autoCleanup(texture);
299
300 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
301 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
302 Glop glop;
303 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
304 .setRoundRectClipState(state.roundRectClipState)
305 .setMeshTexturedUnitQuad(texture->uvMapper)
306 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
307 .setTransform(state.computedState.transform, TransformFlags::None)
308 .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
309 .build();
310 renderer.renderGlop(state, glop);
311}
312
313void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800314 VertexBuffer buffer;
315 PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
316 state.computedState.transform, buffer);
317 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
318 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800319}
320
Chris Craik386aa032015-12-07 17:08:25 -0800321void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
322 if (op.paint->getPathEffect() != nullptr) {
323 PathTexture* texture = renderer.caches().pathCache.getOval(
324 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
325 const AutoTexture holder(texture);
326 if (CC_LIKELY(holder.texture)) {
327 renderPathTexture(renderer, state, *texture, op);
328 }
329 } else {
330 SkPath path;
331 SkRect rect = getBoundsOfFill(op);
332 path.addOval(rect);
333 renderConvexPath(renderer, state, path, *(op.paint));
334 }
335}
336
337void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
338 PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
339 const AutoTexture holder(texture);
340 if (CC_LIKELY(holder.texture)) {
341 renderPathTexture(renderer, state, *texture, op);
342 }
343}
344
345void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
346 VertexBuffer buffer;
347 PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
348 state.computedState.transform, buffer);
349 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
350 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
351}
352
353// See SkPaintDefaults.h
354#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
355
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800356void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
Chris Craik386aa032015-12-07 17:08:25 -0800357 if (op.paint->getStyle() != SkPaint::kFill_Style) {
358 // only fill + default miter is supported by drawConvexPath, since others must handle joins
359 static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
360 if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
361 || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
362 || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
363 PathTexture* texture = renderer.caches().pathCache.getRect(
364 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
365 const AutoTexture holder(texture);
366 if (CC_LIKELY(holder.texture)) {
367 renderPathTexture(renderer, state, *texture, op);
368 }
369 } else {
370 SkPath path;
371 path.addRect(getBoundsOfFill(op));
372 renderConvexPath(renderer, state, path, *(op.paint));
373 }
374 } else {
375 if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
376 SkPath path;
377 path.addRect(op.unmappedBounds.toSkRect());
378 renderConvexPath(renderer, state, path, *(op.paint));
379 } else {
380 // render simple unit quad, no tessellation required
381 Glop glop;
382 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
383 .setRoundRectClipState(state.roundRectClipState)
384 .setMeshUnitQuad()
385 .setFillPaint(*op.paint, state.alpha)
386 .setTransform(state.computedState.transform, TransformFlags::None)
387 .setModelViewMapUnitToRect(op.unmappedBounds)
388 .build();
389 renderer.renderGlop(state, glop);
390 }
391 }
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800392}
393
Chris Craik386aa032015-12-07 17:08:25 -0800394void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
395 if (op.paint->getPathEffect() != nullptr) {
396 PathTexture* texture = renderer.caches().pathCache.getRoundRect(
397 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
398 op.rx, op.ry, op.paint);
399 const AutoTexture holder(texture);
400 if (CC_LIKELY(holder.texture)) {
401 renderPathTexture(renderer, state, *texture, op);
402 }
403 } else {
404 const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
405 state.computedState.transform, *(op.paint),
406 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
407 renderVertexBuffer(renderer, state, *buffer,
408 op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800409 }
410}
411
412static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
413 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
414 SkPaint paint;
415 paint.setAntiAlias(true); // want to use AlphaVertex
416
417 // The caller has made sure casterAlpha > 0.
418 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
419 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
420 ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
421 }
422 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
423 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
424 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
425 paint, VertexBufferRenderFlags::ShadowInterp);
426 }
427
428 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
429 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
430 spotShadowAlpha = Properties::overrideSpotShadowStrength;
431 }
432 if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
433 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
434 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
435 paint, VertexBufferRenderFlags::ShadowInterp);
436 }
437}
438
439void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
440 TessellationCache::vertexBuffer_pair_t buffers;
441 renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform,
442 op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath,
443 &op.shadowMatrixXY, &op.shadowMatrixZ,
444 op.lightCenter, renderer.getLightInfo().lightRadius,
445 buffers);
446
447 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
448}
449
450void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
451 Glop glop;
452 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
453 .setRoundRectClipState(state.roundRectClipState)
454 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
455 .setFillPaint(*op.paint, state.alpha)
456 .setTransform(state.computedState.transform, TransformFlags::None)
457 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
458 .build();
459 renderer.renderGlop(state, glop);
460}
461
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800462void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
Chris Craik15c3f192015-12-03 12:16:56 -0800463 const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr;
464 renderTextOp(renderer, op, state, clip, TextRenderType::Flush);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800465}
466
467void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
468 OffscreenBuffer* buffer = *op.layerHandle;
469
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800470 float layerAlpha = op.alpha * state.alpha;
471 Glop glop;
472 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
473 .setRoundRectClipState(state.roundRectClipState)
474 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
475 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
476 .setTransform(state.computedState.transform, TransformFlags::None)
477 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
478 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
479 .build();
480 renderer.renderGlop(state, glop);
481
482 if (op.destroy) {
483 renderer.renderState().layerPool().putOrDelete(buffer);
484 }
485}
486
487} // namespace uirenderer
488} // namespace android