BakedOpDispatcher.cpp revision 9e7fcfda28fde747ba4e026772007cea77374e16
1/* 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 265