blob: b56b1e4c5f9387b83451748dce643ba904a8c4fb [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
34void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
35 LOG_ALWAYS_FATAL("unsupported operation");
36}
37
38void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
39 LOG_ALWAYS_FATAL("unsupported operation");
40}
41
42void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
43 LOG_ALWAYS_FATAL("unsupported operation");
44}
45
46void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
47 renderer.caches().textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
48 Texture* texture = renderer.getTexture(op.bitmap);
49 if (!texture) return;
50 const AutoTexture autoCleanup(texture);
51
52 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
53 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
54 Glop glop;
55 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
56 .setRoundRectClipState(state.roundRectClipState)
57 .setMeshTexturedUnitQuad(texture->uvMapper)
58 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
59 .setTransform(state.computedState.transform, TransformFlags::None)
60 .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
61 .build();
62 renderer.renderGlop(state, glop);
63}
64
65void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
66 LOG_ALWAYS_FATAL("todo");
67}
68
69void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
70 Glop glop;
71 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
72 .setRoundRectClipState(state.roundRectClipState)
73 .setMeshUnitQuad()
74 .setFillPaint(*op.paint, state.alpha)
75 .setTransform(state.computedState.transform, TransformFlags::None)
76 .setModelViewMapUnitToRect(op.unmappedBounds)
77 .build();
78 renderer.renderGlop(state, glop);
79}
80
81namespace VertexBufferRenderFlags {
82 enum {
83 Offset = 0x1,
84 ShadowInterp = 0x2,
85 };
86}
87
88static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
89 const VertexBuffer& vertexBuffer, float translateX, float translateY,
90 SkPaint& paint, int vertexBufferRenderFlags) {
91 if (CC_LIKELY(vertexBuffer.getVertexCount())) {
92 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
93 const int transformFlags = TransformFlags::OffsetByFudgeFactor;
94 Glop glop;
95 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
96 .setRoundRectClipState(state.roundRectClipState)
97 .setMeshVertexBuffer(vertexBuffer, shadowInterp)
98 .setFillPaint(paint, state.alpha)
99 .setTransform(state.computedState.transform, transformFlags)
100 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
101 .build();
102 renderer.renderGlop(state, glop);
103 }
104}
105
106static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
107 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
108 SkPaint paint;
109 paint.setAntiAlias(true); // want to use AlphaVertex
110
111 // The caller has made sure casterAlpha > 0.
112 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
113 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
114 ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
115 }
116 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
117 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
118 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
119 paint, VertexBufferRenderFlags::ShadowInterp);
120 }
121
122 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
123 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
124 spotShadowAlpha = Properties::overrideSpotShadowStrength;
125 }
126 if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
127 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
128 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
129 paint, VertexBufferRenderFlags::ShadowInterp);
130 }
131}
132
133void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
134 TessellationCache::vertexBuffer_pair_t buffers;
135 renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform,
136 op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath,
137 &op.shadowMatrixXY, &op.shadowMatrixZ,
138 op.lightCenter, renderer.getLightInfo().lightRadius,
139 buffers);
140
141 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
142}
143
144void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
145 Glop glop;
146 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
147 .setRoundRectClipState(state.roundRectClipState)
148 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
149 .setFillPaint(*op.paint, state.alpha)
150 .setTransform(state.computedState.transform, TransformFlags::None)
151 .setModelViewOffsetRect(0, 0, op.unmappedBounds)
152 .build();
153 renderer.renderGlop(state, glop);
154}
155
156static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
157 const TextOp& op, const BakedOpState& state) {
158 renderer.caches().textureState().activateTexture(0);
159
160 PaintUtils::TextShadow textShadow;
161 if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
162 LOG_ALWAYS_FATAL("failed to query shadow attributes");
163 }
164
165 renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
166 ShadowTexture* texture = renderer.caches().dropShadowCache.get(
167 op.paint, (const char*) op.glyphs,
168 op.glyphCount, textShadow.radius, op.positions);
169 // If the drop shadow exceeds the max texture size or couldn't be
170 // allocated, skip drawing
171 if (!texture) return;
172 const AutoTexture autoCleanup(texture);
173
174 const float sx = op.x - texture->left + textShadow.dx;
175 const float sy = op.y - texture->top + textShadow.dy;
176
177 Glop glop;
178 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
179 .setRoundRectClipState(state.roundRectClipState)
180 .setMeshTexturedUnitQuad(nullptr)
181 .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
182 .setTransform(state.computedState.transform, TransformFlags::None)
183 .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
184 .build();
185 renderer.renderGlop(state, glop);
186}
187
188void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
189 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
190
191 if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
192 fontRenderer.setFont(op.paint, SkMatrix::I());
193 renderTextShadow(renderer, fontRenderer, op, state);
194 }
195
196 float x = op.x;
197 float y = op.y;
198 const Matrix4& transform = state.computedState.transform;
199 const bool pureTranslate = transform.isPureTranslate();
200 if (CC_LIKELY(pureTranslate)) {
201 x = floorf(x + transform.getTranslateX() + 0.5f);
202 y = floorf(y + transform.getTranslateY() + 0.5f);
203 fontRenderer.setFont(op.paint, SkMatrix::I());
204 fontRenderer.setTextureFiltering(false);
205 } else if (CC_UNLIKELY(transform.isPerspective())) {
206 fontRenderer.setFont(op.paint, SkMatrix::I());
207 fontRenderer.setTextureFiltering(true);
208 } else {
209 // We only pass a partial transform to the font renderer. That partial
210 // matrix defines how glyphs are rasterized. Typically we want glyphs
211 // to be rasterized at their final size on screen, which means the partial
212 // matrix needs to take the scale factor into account.
213 // When a partial matrix is used to transform glyphs during rasterization,
214 // the mesh is generated with the inverse transform (in the case of scale,
215 // the mesh is generated at 1.0 / scale for instance.) This allows us to
216 // apply the full transform matrix at draw time in the vertex shader.
217 // Applying the full matrix in the shader is the easiest way to handle
218 // rotation and perspective and allows us to always generated quads in the
219 // font renderer which greatly simplifies the code, clipping in particular.
220 float sx, sy;
221 transform.decomposeScale(sx, sy);
222 fontRenderer.setFont(op.paint, SkMatrix::MakeScale(
223 roundf(std::max(1.0f, sx)),
224 roundf(std::max(1.0f, sy))));
225 fontRenderer.setTextureFiltering(true);
226 }
227
228 // TODO: Implement better clipping for scaled/rotated text
229 const Rect* clip = !pureTranslate ? nullptr : &state.computedState.clipRect;
230 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
231
232 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
233 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
234 TextDrawFunctor functor(&renderer, &state, x, y, pureTranslate, alpha, mode, op.paint);
235
236 bool hasActiveLayer = false; // TODO
237 fontRenderer.renderPosText(op.paint, clip, (const char*) op.glyphs, op.glyphCount, x, y,
238 op.positions, hasActiveLayer ? &layerBounds : nullptr, &functor, true); // TODO: merging
239}
240
241void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
242 OffscreenBuffer* buffer = *op.layerHandle;
243
244 // TODO: extend this to handle HW layers & paint properties which
245 // reside in node.properties().layerProperties()
246 float layerAlpha = op.alpha * state.alpha;
247 Glop glop;
248 GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
249 .setRoundRectClipState(state.roundRectClipState)
250 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
251 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
252 .setTransform(state.computedState.transform, TransformFlags::None)
253 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
254 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
255 .build();
256 renderer.renderGlop(state, glop);
257
258 if (op.destroy) {
259 renderer.renderState().layerPool().putOrDelete(buffer);
260 }
261}
262
263} // namespace uirenderer
264} // namespace android