BakedOpDispatcher.cpp revision 0066a01d7ceb9db89fe5edb97e06f62db1c52a74
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 "Patch.h" 24#include "PathTessellator.h" 25#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> 32#include <SkPaintDefaults.h> 33 34namespace android { 35namespace uirenderer { 36 37static 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 } 60 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); 70 } 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) 80 .setModelViewIdentityEmptyBounds() 81 .build(); 82 ClipRect renderTargetClip(opList.clip); 83 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; 84 renderer.renderGlop(nullptr, clip, glop); 85} 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() 187 .build(); 188 ClipRect renderTargetClip(opList.clip); 189 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; 190 renderer.renderGlop(nullptr, clip, glop); 191} 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, op.glyphs, op.glyphCount, textShadow.radius, op.positions); 205 // If the drop shadow exceeds the max texture size or couldn't be 206 // allocated, skip drawing 207 if (!texture) return; 208 const AutoTexture autoCleanup(texture); 209 210 const float sx = op.x - texture->left + textShadow.dx; 211 const float sy = op.y - texture->top + textShadow.dy; 212 213 Glop glop; 214 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 215 .setRoundRectClipState(state.roundRectClipState) 216 .setMeshTexturedUnitQuad(nullptr) 217 .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha) 218 .setTransform(state.computedState.transform, TransformFlags::None) 219 .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height())) 220 .build(); 221 renderer.renderGlop(state, glop); 222} 223 224enum class TextRenderType { 225 Defer, 226 Flush 227}; 228 229static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state, 230 const ClipBase* renderClip, TextRenderType renderType) { 231 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); 232 233 if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) { 234 fontRenderer.setFont(op.paint, SkMatrix::I()); 235 renderTextShadow(renderer, fontRenderer, op, state); 236 } 237 238 float x = op.x; 239 float y = op.y; 240 const Matrix4& transform = state.computedState.transform; 241 const bool pureTranslate = transform.isPureTranslate(); 242 if (CC_LIKELY(pureTranslate)) { 243 x = floorf(x + transform.getTranslateX() + 0.5f); 244 y = floorf(y + transform.getTranslateY() + 0.5f); 245 fontRenderer.setFont(op.paint, SkMatrix::I()); 246 fontRenderer.setTextureFiltering(false); 247 } else if (CC_UNLIKELY(transform.isPerspective())) { 248 fontRenderer.setFont(op.paint, SkMatrix::I()); 249 fontRenderer.setTextureFiltering(true); 250 } else { 251 // We only pass a partial transform to the font renderer. That partial 252 // matrix defines how glyphs are rasterized. Typically we want glyphs 253 // to be rasterized at their final size on screen, which means the partial 254 // matrix needs to take the scale factor into account. 255 // When a partial matrix is used to transform glyphs during rasterization, 256 // the mesh is generated with the inverse transform (in the case of scale, 257 // the mesh is generated at 1.0 / scale for instance.) This allows us to 258 // apply the full transform matrix at draw time in the vertex shader. 259 // Applying the full matrix in the shader is the easiest way to handle 260 // rotation and perspective and allows us to always generated quads in the 261 // font renderer which greatly simplifies the code, clipping in particular. 262 float sx, sy; 263 transform.decomposeScale(sx, sy); 264 fontRenderer.setFont(op.paint, SkMatrix::MakeScale( 265 roundf(std::max(1.0f, sx)), 266 roundf(std::max(1.0f, sy)))); 267 fontRenderer.setTextureFiltering(true); 268 } 269 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); 270 271 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha; 272 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint); 273 TextDrawFunctor functor(&renderer, &state, renderClip, 274 x, y, pureTranslate, alpha, mode, op.paint); 275 276 bool forceFinish = (renderType == TextRenderType::Flush); 277 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); 278 const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr; 279 fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y, 280 op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish); 281 282 if (mustDirtyRenderTarget) { 283 if (!pureTranslate) { 284 transform.mapRect(layerBounds); 285 } 286 renderer.dirtyRenderTarget(layerBounds); 287 } 288} 289 290void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer, 291 const MergedBakedOpList& opList) { 292 ClipRect renderTargetClip(opList.clip); 293 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; 294 for (size_t i = 0; i < opList.count; i++) { 295 const BakedOpState& state = *(opList.states[i]); 296 const TextOp& op = *(static_cast<const TextOp*>(state.op)); 297 TextRenderType renderType = (i + 1 == opList.count) 298 ? TextRenderType::Flush : TextRenderType::Defer; 299 renderTextOp(renderer, op, state, clip, renderType); 300 } 301} 302 303namespace VertexBufferRenderFlags { 304 enum { 305 Offset = 0x1, 306 ShadowInterp = 0x2, 307 }; 308} 309 310static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state, 311 const VertexBuffer& vertexBuffer, float translateX, float translateY, 312 const SkPaint& paint, int vertexBufferRenderFlags) { 313 if (CC_LIKELY(vertexBuffer.getVertexCount())) { 314 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp; 315 const int transformFlags = TransformFlags::OffsetByFudgeFactor; 316 Glop glop; 317 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 318 .setRoundRectClipState(state.roundRectClipState) 319 .setMeshVertexBuffer(vertexBuffer, shadowInterp) 320 .setFillPaint(paint, state.alpha) 321 .setTransform(state.computedState.transform, transformFlags) 322 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds()) 323 .build(); 324 renderer.renderGlop(state, glop); 325 } 326} 327 328static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state, 329 const SkPath& path, const SkPaint& paint) { 330 VertexBuffer vertexBuffer; 331 // TODO: try clipping large paths to viewport 332 PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer); 333 renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0); 334} 335 336static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state, 337 float xOffset, float yOffset, PathTexture& texture, const SkPaint& paint) { 338 Rect dest(texture.width(), texture.height()); 339 dest.translate(xOffset + texture.left - texture.offset, 340 yOffset + texture.top - texture.offset); 341 Glop glop; 342 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 343 .setRoundRectClipState(state.roundRectClipState) 344 .setMeshTexturedUnitQuad(nullptr) 345 .setFillPathTexturePaint(texture, paint, state.alpha) 346 .setTransform(state.computedState.transform, TransformFlags::None) 347 .setModelViewMapUnitToRect(dest) 348 .build(); 349 renderer.renderGlop(state, glop); 350} 351 352SkRect getBoundsOfFill(const RecordedOp& op) { 353 SkRect bounds = op.unmappedBounds.toSkRect(); 354 if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) { 355 float outsetDistance = op.paint->getStrokeWidth() / 2; 356 bounds.outset(outsetDistance, outsetDistance); 357 } 358 return bounds; 359} 360 361void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) { 362 // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) 363 if (op.paint->getStyle() != SkPaint::kStroke_Style 364 || op.paint->getPathEffect() != nullptr 365 || op.useCenter) { 366 PathTexture* texture = renderer.caches().pathCache.getArc( 367 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), 368 op.startAngle, op.sweepAngle, op.useCenter, op.paint); 369 const AutoTexture holder(texture); 370 if (CC_LIKELY(holder.texture)) { 371 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, 372 *texture, *(op.paint)); 373 } 374 } else { 375 SkRect rect = getBoundsOfFill(op); 376 SkPath path; 377 if (op.useCenter) { 378 path.moveTo(rect.centerX(), rect.centerY()); 379 } 380 path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter); 381 if (op.useCenter) { 382 path.close(); 383 } 384 renderConvexPath(renderer, state, path, *(op.paint)); 385 } 386} 387 388void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) { 389 Texture* texture = renderer.getTexture(op.bitmap); 390 if (!texture) return; 391 const AutoTexture autoCleanup(texture); 392 393 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) 394 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; 395 Glop glop; 396 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 397 .setRoundRectClipState(state.roundRectClipState) 398 .setMeshTexturedUnitQuad(texture->uvMapper) 399 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 400 .setTransform(state.computedState.transform, TransformFlags::None) 401 .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height())) 402 .build(); 403 renderer.renderGlop(state, glop); 404} 405 406void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) { 407 const static UvMapper defaultUvMapper; 408 const uint32_t elementCount = op.meshWidth * op.meshHeight * 6; 409 410 std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]); 411 ColorTextureVertex* vertex = &mesh[0]; 412 413 const int* colors = op.colors; 414 std::unique_ptr<int[]> tempColors; 415 if (!colors) { 416 uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1); 417 tempColors.reset(new int[colorsCount]); 418 memset(tempColors.get(), 0xff, colorsCount * sizeof(int)); 419 colors = tempColors.get(); 420 } 421 422 Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef()); 423 const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper); 424 425 for (int32_t y = 0; y < op.meshHeight; y++) { 426 for (int32_t x = 0; x < op.meshWidth; x++) { 427 uint32_t i = (y * (op.meshWidth + 1) + x) * 2; 428 429 float u1 = float(x) / op.meshWidth; 430 float u2 = float(x + 1) / op.meshWidth; 431 float v1 = float(y) / op.meshHeight; 432 float v2 = float(y + 1) / op.meshHeight; 433 434 mapper.map(u1, v1, u2, v2); 435 436 int ax = i + (op.meshWidth + 1) * 2; 437 int ay = ax + 1; 438 int bx = i; 439 int by = bx + 1; 440 int cx = i + 2; 441 int cy = cx + 1; 442 int dx = i + (op.meshWidth + 1) * 2 + 2; 443 int dy = dx + 1; 444 445 const float* vertices = op.vertices; 446 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); 447 ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]); 448 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); 449 450 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); 451 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); 452 ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]); 453 } 454 } 455 456 if (!texture) { 457 texture = renderer.caches().textureCache.get(op.bitmap); 458 if (!texture) { 459 return; 460 } 461 } 462 const AutoTexture autoCleanup(texture); 463 464 /* 465 * TODO: handle alpha_8 textures correctly by applying paint color, but *not* 466 * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh. 467 */ 468 const int textureFillFlags = TextureFillFlags::None; 469 Glop glop; 470 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 471 .setRoundRectClipState(state.roundRectClipState) 472 .setMeshColoredTexturedMesh(mesh.get(), elementCount) 473 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 474 .setTransform(state.computedState.transform, TransformFlags::None) 475 .setModelViewOffsetRect(0, 0, op.unmappedBounds) 476 .build(); 477 renderer.renderGlop(state, glop); 478} 479 480void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) { 481 Texture* texture = renderer.getTexture(op.bitmap); 482 if (!texture) return; 483 const AutoTexture autoCleanup(texture); 484 485 Rect uv(std::max(0.0f, op.src.left / texture->width()), 486 std::max(0.0f, op.src.top / texture->height()), 487 std::min(1.0f, op.src.right / texture->width()), 488 std::min(1.0f, op.src.bottom / texture->height())); 489 490 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) 491 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; 492 const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth()) 493 && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight()); 494 Glop glop; 495 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 496 .setRoundRectClipState(state.roundRectClipState) 497 .setMeshTexturedUvQuad(texture->uvMapper, uv) 498 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 499 .setTransform(state.computedState.transform, TransformFlags::None) 500 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds) 501 .build(); 502 renderer.renderGlop(state, glop); 503} 504 505void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) { 506 renderer.renderFunctor(op, state); 507} 508 509void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) { 510 VertexBuffer buffer; 511 PathTessellator::tessellateLines(op.points, op.floatCount, op.paint, 512 state.computedState.transform, buffer); 513 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset; 514 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags); 515} 516 517void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) { 518 if (op.paint->getPathEffect() != nullptr) { 519 PathTexture* texture = renderer.caches().pathCache.getOval( 520 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); 521 const AutoTexture holder(texture); 522 if (CC_LIKELY(holder.texture)) { 523 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.right, 524 *texture, *(op.paint)); 525 } 526 } else { 527 SkPath path; 528 SkRect rect = getBoundsOfFill(op); 529 path.addOval(rect); 530 renderConvexPath(renderer, state, path, *(op.paint)); 531 } 532} 533 534void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) { 535 // 9 patches are built for stretching - always filter 536 int textureFillFlags = TextureFillFlags::ForceFilter; 537 if (op.bitmap->colorType() == kAlpha_8_SkColorType) { 538 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; 539 } 540 541 // TODO: avoid redoing the below work each frame: 542 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef()); 543 const Patch* mesh = renderer.caches().patchCache.get( 544 entry, op.bitmap->width(), op.bitmap->height(), 545 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); 546 547 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap); 548 if (CC_LIKELY(texture)) { 549 const AutoTexture autoCleanup(texture); 550 Glop glop; 551 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 552 .setRoundRectClipState(state.roundRectClipState) 553 .setMeshPatchQuads(*mesh) 554 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 555 .setTransform(state.computedState.transform, TransformFlags::None) 556 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top, 557 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight())) 558 .build(); 559 renderer.renderGlop(state, glop); 560 } 561} 562 563void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) { 564 PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint); 565 const AutoTexture holder(texture); 566 if (CC_LIKELY(holder.texture)) { 567 // Unlike other callers to renderPathTexture, no offsets are used because PathOp doesn't 568 // have any translate built in, other than what's in the SkPath itself 569 renderPathTexture(renderer, state, 0, 0, *texture, *(op.paint)); 570 } 571} 572 573void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) { 574 VertexBuffer buffer; 575 PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint, 576 state.computedState.transform, buffer); 577 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset; 578 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags); 579} 580 581// See SkPaintDefaults.h 582#define SkPaintDefaults_MiterLimit SkIntToScalar(4) 583 584void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) { 585 if (op.paint->getStyle() != SkPaint::kFill_Style) { 586 // only fill + default miter is supported by drawConvexPath, since others must handle joins 587 static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed"); 588 if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr 589 || op.paint->getStrokeJoin() != SkPaint::kMiter_Join 590 || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) { 591 PathTexture* texture = renderer.caches().pathCache.getRect( 592 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); 593 const AutoTexture holder(texture); 594 if (CC_LIKELY(holder.texture)) { 595 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, 596 *texture, *(op.paint)); 597 } 598 } else { 599 SkPath path; 600 path.addRect(getBoundsOfFill(op)); 601 renderConvexPath(renderer, state, path, *(op.paint)); 602 } 603 } else { 604 if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) { 605 SkPath path; 606 path.addRect(op.unmappedBounds.toSkRect()); 607 renderConvexPath(renderer, state, path, *(op.paint)); 608 } else { 609 // render simple unit quad, no tessellation required 610 Glop glop; 611 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 612 .setRoundRectClipState(state.roundRectClipState) 613 .setMeshUnitQuad() 614 .setFillPaint(*op.paint, state.alpha) 615 .setTransform(state.computedState.transform, TransformFlags::None) 616 .setModelViewMapUnitToRect(op.unmappedBounds) 617 .build(); 618 renderer.renderGlop(state, glop); 619 } 620 } 621} 622 623void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) { 624 if (op.paint->getPathEffect() != nullptr) { 625 PathTexture* texture = renderer.caches().pathCache.getRoundRect( 626 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), 627 op.rx, op.ry, op.paint); 628 const AutoTexture holder(texture); 629 if (CC_LIKELY(holder.texture)) { 630 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, 631 *texture, *(op.paint)); 632 } 633 } else { 634 const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect( 635 state.computedState.transform, *(op.paint), 636 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry); 637 renderVertexBuffer(renderer, state, *buffer, 638 op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0); 639 } 640} 641 642static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha, 643 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) { 644 SkPaint paint; 645 paint.setAntiAlias(true); // want to use AlphaVertex 646 647 // The caller has made sure casterAlpha > 0. 648 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha; 649 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) { 650 ambientShadowAlpha = Properties::overrideAmbientShadowStrength; 651 } 652 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) { 653 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha)); 654 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0, 655 paint, VertexBufferRenderFlags::ShadowInterp); 656 } 657 658 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha; 659 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) { 660 spotShadowAlpha = Properties::overrideSpotShadowStrength; 661 } 662 if (spotShadowVertexBuffer && spotShadowAlpha > 0) { 663 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha)); 664 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0, 665 paint, VertexBufferRenderFlags::ShadowInterp); 666 } 667} 668 669void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) { 670 TessellationCache::vertexBuffer_pair_t buffers = op.shadowTask->getResult(); 671 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second); 672} 673 674void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) { 675 Glop glop; 676 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 677 .setRoundRectClipState(state.roundRectClipState) 678 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4) 679 .setFillPaint(*op.paint, state.alpha) 680 .setTransform(state.computedState.transform, TransformFlags::None) 681 .setModelViewOffsetRect(0, 0, op.unmappedBounds) 682 .build(); 683 renderer.renderGlop(state, glop); 684} 685 686void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) { 687 renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush); 688} 689 690void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) { 691 // Note: can't trust clipSideFlags since we record with unmappedBounds == clip. 692 // TODO: respect clipSideFlags, once we record with bounds 693 auto renderTargetClip = state.computedState.clipState; 694 695 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); 696 fontRenderer.setFont(op.paint, SkMatrix::I()); 697 fontRenderer.setTextureFiltering(true); 698 699 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); 700 701 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha; 702 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint); 703 TextDrawFunctor functor(&renderer, &state, renderTargetClip, 704 0.0f, 0.0f, false, alpha, mode, op.paint); 705 706 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); 707 const Rect localSpaceClip = state.computedState.computeLocalSpaceClip(); 708 if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount, 709 op.path, op.hOffset, op.vOffset, 710 mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) { 711 if (mustDirtyRenderTarget) { 712 // manually dirty render target, since TextDrawFunctor won't 713 state.computedState.transform.mapRect(layerBounds); 714 renderer.dirtyRenderTarget(layerBounds); 715 } 716 } 717} 718 719void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, const BakedOpState& state) { 720 const bool tryToSnap = !op.layer->getForceFilter(); 721 float alpha = (op.layer->getAlpha() / 255.0f) * state.alpha; 722 Glop glop; 723 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 724 .setRoundRectClipState(state.roundRectClipState) 725 .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO 726 .setFillTextureLayer(*(op.layer), alpha) 727 .setTransform(state.computedState.transform, TransformFlags::None) 728 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, Rect(op.layer->getWidth(), op.layer->getHeight())) 729 .build(); 730 renderer.renderGlop(state, glop); 731} 732 733void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) { 734 // Note that we don't use op->paint in this function - it's never set on a LayerOp 735 OffscreenBuffer* buffer = *op.layerHandle; 736 737 if (CC_UNLIKELY(!buffer)) { 738 // Layer was not allocated, which can occur if there were no draw ops inside. We draw the 739 // equivalent by drawing a rect with the same layer properties (alpha/xfer/filter). 740 SkPaint paint; 741 paint.setAlpha(op.alpha * 255); 742 paint.setXfermodeMode(op.mode); 743 paint.setColorFilter(op.colorFilter); 744 RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint); 745 BakedOpDispatcher::onRectOp(renderer, rectOp, state); 746 } else { 747 float layerAlpha = op.alpha * state.alpha; 748 Glop glop; 749 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 750 .setRoundRectClipState(state.roundRectClipState) 751 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount) 752 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap) 753 .setTransform(state.computedState.transform, TransformFlags::None) 754 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top, 755 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight())) 756 .build(); 757 renderer.renderGlop(state, glop); 758 759 if (op.destroy) { 760 renderer.renderState().layerPool().putOrDelete(buffer); 761 } 762 } 763} 764 765void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) { 766 LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!"); 767 *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds); 768 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed"); 769} 770 771void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) { 772 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!"); 773 if (!state.computedState.clippedBounds.isEmpty()) { 774 if (op.paint && op.paint->getAlpha() < 255) { 775 SkPaint layerPaint; 776 layerPaint.setAlpha(op.paint->getAlpha()); 777 layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode); 778 layerPaint.setColorFilter(op.paint->getColorFilter()); 779 RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint); 780 BakedOpDispatcher::onRectOp(renderer, rectOp, state); 781 } 782 783 OffscreenBuffer& layer = **(op.layerHandle); 784 auto mode = PaintUtils::getXfermodeDirect(op.paint); 785 Glop glop; 786 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 787 .setRoundRectClipState(state.roundRectClipState) 788 .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates()) 789 .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap) 790 .setTransform(state.computedState.transform, TransformFlags::None) 791 .setModelViewMapUnitToRect(state.computedState.clippedBounds) 792 .build(); 793 renderer.renderGlop(state, glop); 794 } 795 renderer.renderState().layerPool().putOrDelete(*op.layerHandle); 796} 797 798} // namespace uirenderer 799} // namespace android 800