blob: fde12dd261f5801a663e8800c2af22768f1d4785 [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"
23#include "renderstate/OffscreenBufferPool.h"
24#include "renderstate/RenderState.h"
25#include "utils/GLUtils.h"
26#include "VertexBuffer.h"
27
28#include <algorithm>
29#include <math.h>
30
31namespace android {
32namespace uirenderer {
33
Chris Craik15c3f192015-12-03 12:16:56 -080034static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds, const Rect& texCoord) {
35 vertices[0] = { bounds.left, bounds.top, texCoord.left, texCoord.top };
36 vertices[1] = { bounds.right, bounds.top, texCoord.right, texCoord.top };
37 vertices[2] = { bounds.left, bounds.bottom, texCoord.left, texCoord.bottom };
38 vertices[3] = { bounds.right, bounds.bottom, texCoord.right, texCoord.bottom };
39}
40
41void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
42 const MergedBakedOpList& opList) {
43
44 const BakedOpState& firstState = *(opList.states[0]);
45 const SkBitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
46
47 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(bitmap->pixelRef());
48 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(bitmap);
49 if (!texture) return;
50 const AutoTexture autoCleanup(texture);
51
52 TextureVertex vertices[opList.count * 4];
53 Rect texCoords(0, 0, 1, 1);
54 if (entry) {
55 entry->uvMapper.map(texCoords);
56 }
57 // init to non-empty, so we can safely expandtoCoverRect
58 Rect totalBounds = firstState.computedState.clippedBounds;
59 for (size_t i = 0; i < opList.count; i++) {
60 const BakedOpState& state = *(opList.states[i]);
61 TextureVertex* rectVerts = &vertices[i * 4];
62 Rect opBounds = state.computedState.clippedBounds;
63 if (CC_LIKELY(state.computedState.transform.isPureTranslate())) {
64 // pure translate, so snap (same behavior as onBitmapOp)
65 opBounds.snapToPixelBoundaries();
66 }
67 storeTexturedRect(rectVerts, opBounds, texCoords);
68 renderer.dirtyRenderTarget(opBounds);
69
70 totalBounds.expandToCover(opBounds);
71 }
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)
81 .setModelViewOffsetRect(0, 0, totalBounds) // don't snap here, we snap per-quad above
82 .build();
83 renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
84}
85
86static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
87 const TextOp& op, const BakedOpState& state) {
88 renderer.caches().textureState().activateTexture(0);
89
90 PaintUtils::TextShadow textShadow;
91 if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
92 LOG_ALWAYS_FATAL("failed to query shadow attributes");
93 }
94
95 renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
96 ShadowTexture* texture = renderer.caches().dropShadowCache.get(
97 op.paint, (const char*) op.glyphs,
98 op.glyphCount, textShadow.radius, op.positions);
99 // If the drop shadow exceeds the max texture size or couldn't be
100 // allocated, skip drawing
101 if (!texture) return;
102 const AutoTexture autoCleanup(texture);
103
104 const float sx = op.x - texture->left + textShadow.dx;
105 const float sy = op.y - texture->top + textShadow.dy;
106
107 Glop glop;
108 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
109 .setRoundRectClipState(state.roundRectClipState)
110 .setMeshTexturedUnitQuad(nullptr)
111 .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
112 .setTransform(state.computedState.transform, TransformFlags::None)
113 .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
114 .build();
115 renderer.renderGlop(state, glop);
116}
117
118enum class TextRenderType {
119 Defer,
120 Flush
121};
122
123static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state,
124 const Rect* renderClip, TextRenderType renderType) {
125 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
126
127 if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
128 fontRenderer.setFont(op.paint, SkMatrix::I());
129 renderTextShadow(renderer, fontRenderer, op, state);
130 }
131
132 float x = op.x;
133 float y = op.y;
134 const Matrix4& transform = state.computedState.transform;
135 const bool pureTranslate = transform.isPureTranslate();
136 if (CC_LIKELY(pureTranslate)) {
137 x = floorf(x + transform.getTranslateX() + 0.5f);
138 y = floorf(y + transform.getTranslateY() + 0.5f);
139 fontRenderer.setFont(op.paint, SkMatrix::I());
140 fontRenderer.setTextureFiltering(false);
141 } else if (CC_UNLIKELY(transform.isPerspective())) {
142 fontRenderer.setFont(op.paint, SkMatrix::I());
143 fontRenderer.setTextureFiltering(true);
144 } else {
145 // We only pass a partial transform to the font renderer. That partial
146 // matrix defines how glyphs are rasterized. Typically we want glyphs
147 // to be rasterized at their final size on screen, which means the partial
148 // matrix needs to take the scale factor into account.
149 // When a partial matrix is used to transform glyphs during rasterization,
150 // the mesh is generated with the inverse transform (in the case of scale,
151 // the mesh is generated at 1.0 / scale for instance.) This allows us to
152 // apply the full transform matrix at draw time in the vertex shader.
153 // Applying the full matrix in the shader is the easiest way to handle
154 // rotation and perspective and allows us to always generated quads in the
155 // font renderer which greatly simplifies the code, clipping in particular.
156 float sx, sy;
157 transform.decomposeScale(sx, sy);
158 fontRenderer.setFont(op.paint, SkMatrix::MakeScale(
159 roundf(std::max(1.0f, sx)),
160 roundf(std::max(1.0f, sy))));
161 fontRenderer.setTextureFiltering(true);
162 }
163 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
164
165 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
166 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
167 TextDrawFunctor functor(&renderer, &state, renderClip,
168 x, y, pureTranslate, alpha, mode, op.paint);
169
170 bool forceFinish = (renderType == TextRenderType::Flush);
171 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
172 const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect : nullptr;
173 fontRenderer.renderPosText(op.paint, localOpClip,
174 (const char*) op.glyphs, op.glyphCount, x, y,
175 op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
176
177 if (mustDirtyRenderTarget) {
178 if (!pureTranslate) {
179 transform.mapRect(layerBounds);
180 }
181 renderer.dirtyRenderTarget(layerBounds);
182 }
183}
184
185void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
186 const MergedBakedOpList& opList) {
187 const Rect* clip = opList.clipSideFlags ? &opList.clip : nullptr;
188 for (size_t i = 0; i < opList.count; i++) {
189 const BakedOpState& state = *(opList.states[i]);
190 const TextOp& op = *(static_cast<const TextOp*>(state.op));
191 TextRenderType renderType = (i + 1 == opList.count)
192 ? TextRenderType::Flush : TextRenderType::Defer;
193 renderTextOp(renderer, op, state, clip, renderType);
194 }
195}
196
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800197void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
198 LOG_ALWAYS_FATAL("unsupported operation");
199}
200
Chris Craik15c3f192015-12-03 12:16:56 -0800201void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer&, const BeginLayerOp&, const BakedOpState&) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800202 LOG_ALWAYS_FATAL("unsupported operation");
203}
204
Chris Craik15c3f192015-12-03 12:16:56 -0800205void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer&, const EndLayerOp&, const BakedOpState&) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800206 LOG_ALWAYS_FATAL("unsupported operation");
207}
208
209void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800210 Texture* texture = renderer.getTexture(op.bitmap);
211 if (!texture) return;
212 const AutoTexture autoCleanup(texture);
213
214 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
215 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
216 Glop glop;
217 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
218 .setRoundRectClipState(state.roundRectClipState)
219 .setMeshTexturedUnitQuad(texture->uvMapper)
220 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
221 .setTransform(state.computedState.transform, TransformFlags::None)
222 .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
223 .build();
224 renderer.renderGlop(state, glop);
225}
226
227void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
228 LOG_ALWAYS_FATAL("todo");
229}
230
231void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
232 Glop glop;
233 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
234 .setRoundRectClipState(state.roundRectClipState)
235 .setMeshUnitQuad()
236 .setFillPaint(*op.paint, state.alpha)
237 .setTransform(state.computedState.transform, TransformFlags::None)
238 .setModelViewMapUnitToRect(op.unmappedBounds)
239 .build();
240 renderer.renderGlop(state, glop);
241}
242
243namespace VertexBufferRenderFlags {
244 enum {
245 Offset = 0x1,
246 ShadowInterp = 0x2,
247 };
248}
249
250static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
251 const VertexBuffer& vertexBuffer, float translateX, float translateY,
252 SkPaint& paint, int vertexBufferRenderFlags) {
253 if (CC_LIKELY(vertexBuffer.getVertexCount())) {
254 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
255 const int transformFlags = TransformFlags::OffsetByFudgeFactor;
256 Glop glop;
257 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
258 .setRoundRectClipState(state.roundRectClipState)
259 .setMeshVertexBuffer(vertexBuffer, shadowInterp)
260 .setFillPaint(paint, state.alpha)
261 .setTransform(state.computedState.transform, transformFlags)
262 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
263 .build();
264 renderer.renderGlop(state, glop);
265 }
266}
267
268static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
269 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
270 SkPaint paint;
271 paint.setAntiAlias(true); // want to use AlphaVertex
272
273 // The caller has made sure casterAlpha > 0.
274 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
275 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
276 ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
277 }
278 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
279 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
280 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
281 paint, VertexBufferRenderFlags::ShadowInterp);
282 }
283
284 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
285 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
286 spotShadowAlpha = Properties::overrideSpotShadowStrength;
287 }
288 if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
289 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
290 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
291 paint, VertexBufferRenderFlags::ShadowInterp);
292 }
293}
294
295void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
296 TessellationCache::vertexBuffer_pair_t buffers;
297 renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform,
298 op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath,
299 &op.shadowMatrixXY, &op.shadowMatrixZ,
300 op.lightCenter, renderer.getLightInfo().lightRadius,
301 buffers);
302
303 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
304}
305
306void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
307 Glop glop;
308 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
309 .setRoundRectClipState(state.roundRectClipState)
310 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
311 .setFillPaint(*op.paint, state.alpha)
312 .setTransform(state.computedState.transform, TransformFlags::None)
313 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
314 .build();
315 renderer.renderGlop(state, glop);
316}
317
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800318void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
Chris Craik15c3f192015-12-03 12:16:56 -0800319 const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr;
320 renderTextOp(renderer, op, state, clip, TextRenderType::Flush);
Chris Craik9e7fcfd2015-11-25 13:27:33 -0800321}
322
323void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
324 OffscreenBuffer* buffer = *op.layerHandle;
325
326 // TODO: extend this to handle HW layers & paint properties which
327 // reside in node.properties().layerProperties()
328 float layerAlpha = op.alpha * state.alpha;
329 Glop glop;
330 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
331 .setRoundRectClipState(state.roundRectClipState)
332 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
333 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
334 .setTransform(state.computedState.transform, TransformFlags::None)
335 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
336 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
337 .build();
338 renderer.renderGlop(state, glop);
339
340 if (op.destroy) {
341 renderer.renderState().layerPool().putOrDelete(buffer);
342 }
343}
344
345} // namespace uirenderer
346} // namespace android