BakedOpDispatcher.cpp revision e29ce6f51d681af7649c0a7cddee97c471e43eb5
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 renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); 83} 84 85void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, 86 const MergedBakedOpList& opList) { 87 const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op)); 88 const BakedOpState& firstState = *(opList.states[0]); 89 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry( 90 firstOp.bitmap->pixelRef()); 91 92 // Batches will usually contain a small number of items so it's 93 // worth performing a first iteration to count the exact number 94 // of vertices we need in the new mesh 95 uint32_t totalVertices = 0; 96 97 for (size_t i = 0; i < opList.count; i++) { 98 const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op)); 99 100 // TODO: cache mesh lookups 101 const Patch* opMesh = renderer.caches().patchCache.get( 102 entry, op.bitmap->width(), op.bitmap->height(), 103 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); 104 totalVertices += opMesh->verticesCount; 105 } 106 107 const bool dirtyRenderTarget = renderer.offscreenRenderTarget(); 108 109 uint32_t indexCount = 0; 110 111 TextureVertex vertices[totalVertices]; 112 TextureVertex* vertex = &vertices[0]; 113 // Create a mesh that contains the transformed vertices for all the 114 // 9-patch objects that are part of the batch. Note that onDefer() 115 // enforces ops drawn by this function to have a pure translate or 116 // identity matrix 117 for (size_t i = 0; i < opList.count; i++) { 118 const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op)); 119 const BakedOpState& state = *opList.states[i]; 120 121 // TODO: cache mesh lookups 122 const Patch* opMesh = renderer.caches().patchCache.get( 123 entry, op.bitmap->width(), op.bitmap->height(), 124 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); 125 126 127 uint32_t vertexCount = opMesh->verticesCount; 128 if (vertexCount == 0) continue; 129 130 // We use the bounds to know where to translate our vertices 131 // Using patchOp->state.mBounds wouldn't work because these 132 // bounds are clipped 133 const float tx = floorf(state.computedState.transform.getTranslateX() 134 + op.unmappedBounds.left + 0.5f); 135 const float ty = floorf(state.computedState.transform.getTranslateY() 136 + op.unmappedBounds.top + 0.5f); 137 138 // Copy & transform all the vertices for the current operation 139 TextureVertex* opVertices = opMesh->vertices.get(); 140 for (uint32_t j = 0; j < vertexCount; j++, opVertices++) { 141 TextureVertex::set(vertex++, 142 opVertices->x + tx, opVertices->y + ty, 143 opVertices->u, opVertices->v); 144 } 145 146 // Dirty the current layer if possible. When the 9-patch does not 147 // contain empty quads we can take a shortcut and simply set the 148 // dirty rect to the object's bounds. 149 if (dirtyRenderTarget) { 150 if (!opMesh->hasEmptyQuads) { 151 renderer.dirtyRenderTarget(Rect(tx, ty, 152 tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.getHeight())); 153 } else { 154 const size_t count = opMesh->quads.size(); 155 for (size_t i = 0; i < count; i++) { 156 const Rect& quadBounds = opMesh->quads[i]; 157 const float x = tx + quadBounds.left; 158 const float y = ty + quadBounds.top; 159 renderer.dirtyRenderTarget(Rect(x, y, 160 x + quadBounds.getWidth(), y + quadBounds.getHeight())); 161 } 162 } 163 } 164 165 indexCount += opMesh->indexCount; 166 } 167 168 169 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap); 170 if (!texture) return; 171 const AutoTexture autoCleanup(texture); 172 173 // 9 patches are built for stretching - always filter 174 int textureFillFlags = TextureFillFlags::ForceFilter; 175 if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) { 176 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; 177 } 178 Glop glop; 179 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 180 .setRoundRectClipState(firstState.roundRectClipState) 181 .setMeshTexturedIndexedQuads(vertices, indexCount) 182 .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha) 183 .setTransform(Matrix4::identity(), TransformFlags::None) 184 .setModelViewIdentityEmptyBounds() 185 .build(); 186 renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); 187} 188 189static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer, 190 const TextOp& op, const BakedOpState& state) { 191 renderer.caches().textureState().activateTexture(0); 192 193 PaintUtils::TextShadow textShadow; 194 if (!PaintUtils::getTextShadow(op.paint, &textShadow)) { 195 LOG_ALWAYS_FATAL("failed to query shadow attributes"); 196 } 197 198 renderer.caches().dropShadowCache.setFontRenderer(fontRenderer); 199 ShadowTexture* texture = renderer.caches().dropShadowCache.get( 200 op.paint, (const char*) op.glyphs, 201 op.glyphCount, textShadow.radius, op.positions); 202 // If the drop shadow exceeds the max texture size or couldn't be 203 // allocated, skip drawing 204 if (!texture) return; 205 const AutoTexture autoCleanup(texture); 206 207 const float sx = op.x - texture->left + textShadow.dx; 208 const float sy = op.y - texture->top + textShadow.dy; 209 210 Glop glop; 211 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 212 .setRoundRectClipState(state.roundRectClipState) 213 .setMeshTexturedUnitQuad(nullptr) 214 .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha) 215 .setTransform(state.computedState.transform, TransformFlags::None) 216 .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height)) 217 .build(); 218 renderer.renderGlop(state, glop); 219} 220 221enum class TextRenderType { 222 Defer, 223 Flush 224}; 225 226static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state, 227 const Rect* renderClip, TextRenderType renderType) { 228 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); 229 230 if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) { 231 fontRenderer.setFont(op.paint, SkMatrix::I()); 232 renderTextShadow(renderer, fontRenderer, op, state); 233 } 234 235 float x = op.x; 236 float y = op.y; 237 const Matrix4& transform = state.computedState.transform; 238 const bool pureTranslate = transform.isPureTranslate(); 239 if (CC_LIKELY(pureTranslate)) { 240 x = floorf(x + transform.getTranslateX() + 0.5f); 241 y = floorf(y + transform.getTranslateY() + 0.5f); 242 fontRenderer.setFont(op.paint, SkMatrix::I()); 243 fontRenderer.setTextureFiltering(false); 244 } else if (CC_UNLIKELY(transform.isPerspective())) { 245 fontRenderer.setFont(op.paint, SkMatrix::I()); 246 fontRenderer.setTextureFiltering(true); 247 } else { 248 // We only pass a partial transform to the font renderer. That partial 249 // matrix defines how glyphs are rasterized. Typically we want glyphs 250 // to be rasterized at their final size on screen, which means the partial 251 // matrix needs to take the scale factor into account. 252 // When a partial matrix is used to transform glyphs during rasterization, 253 // the mesh is generated with the inverse transform (in the case of scale, 254 // the mesh is generated at 1.0 / scale for instance.) This allows us to 255 // apply the full transform matrix at draw time in the vertex shader. 256 // Applying the full matrix in the shader is the easiest way to handle 257 // rotation and perspective and allows us to always generated quads in the 258 // font renderer which greatly simplifies the code, clipping in particular. 259 float sx, sy; 260 transform.decomposeScale(sx, sy); 261 fontRenderer.setFont(op.paint, SkMatrix::MakeScale( 262 roundf(std::max(1.0f, sx)), 263 roundf(std::max(1.0f, sy)))); 264 fontRenderer.setTextureFiltering(true); 265 } 266 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); 267 268 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha; 269 SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint); 270 TextDrawFunctor functor(&renderer, &state, renderClip, 271 x, y, pureTranslate, alpha, mode, op.paint); 272 273 bool forceFinish = (renderType == TextRenderType::Flush); 274 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); 275 const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect : nullptr; 276 fontRenderer.renderPosText(op.paint, localOpClip, 277 (const char*) op.glyphs, op.glyphCount, x, y, 278 op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish); 279 280 if (mustDirtyRenderTarget) { 281 if (!pureTranslate) { 282 transform.mapRect(layerBounds); 283 } 284 renderer.dirtyRenderTarget(layerBounds); 285 } 286} 287 288void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer, 289 const MergedBakedOpList& opList) { 290 const Rect* clip = opList.clipSideFlags ? &opList.clip : nullptr; 291 for (size_t i = 0; i < opList.count; i++) { 292 const BakedOpState& state = *(opList.states[i]); 293 const TextOp& op = *(static_cast<const TextOp*>(state.op)); 294 TextRenderType renderType = (i + 1 == opList.count) 295 ? TextRenderType::Flush : TextRenderType::Defer; 296 renderTextOp(renderer, op, state, clip, renderType); 297 } 298} 299 300void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) { 301 LOG_ALWAYS_FATAL("unsupported operation"); 302} 303 304void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer&, const BeginLayerOp&, const BakedOpState&) { 305 LOG_ALWAYS_FATAL("unsupported operation"); 306} 307 308void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer&, const EndLayerOp&, const BakedOpState&) { 309 LOG_ALWAYS_FATAL("unsupported operation"); 310} 311 312void BakedOpDispatcher::onCirclePropsOp(BakedOpRenderer&, const CirclePropsOp&, const BakedOpState&) { 313 LOG_ALWAYS_FATAL("unsupported operation"); 314} 315 316void BakedOpDispatcher::onRoundRectPropsOp(BakedOpRenderer&, const RoundRectPropsOp&, const BakedOpState&) { 317 LOG_ALWAYS_FATAL("unsupported operation"); 318} 319 320namespace VertexBufferRenderFlags { 321 enum { 322 Offset = 0x1, 323 ShadowInterp = 0x2, 324 }; 325} 326 327static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state, 328 const VertexBuffer& vertexBuffer, float translateX, float translateY, 329 const SkPaint& paint, int vertexBufferRenderFlags) { 330 if (CC_LIKELY(vertexBuffer.getVertexCount())) { 331 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp; 332 const int transformFlags = TransformFlags::OffsetByFudgeFactor; 333 Glop glop; 334 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 335 .setRoundRectClipState(state.roundRectClipState) 336 .setMeshVertexBuffer(vertexBuffer, shadowInterp) 337 .setFillPaint(paint, state.alpha) 338 .setTransform(state.computedState.transform, transformFlags) 339 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds()) 340 .build(); 341 renderer.renderGlop(state, glop); 342 } 343} 344 345static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state, 346 const SkPath& path, const SkPaint& paint) { 347 VertexBuffer vertexBuffer; 348 // TODO: try clipping large paths to viewport 349 PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer); 350 renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0); 351} 352 353static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state, 354 PathTexture& texture, const RecordedOp& op) { 355 Rect dest(texture.width, texture.height); 356 dest.translate(texture.left + op.unmappedBounds.left - texture.offset, 357 texture.top + op.unmappedBounds.top - texture.offset); 358 Glop glop; 359 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 360 .setRoundRectClipState(state.roundRectClipState) 361 .setMeshTexturedUnitQuad(nullptr) 362 .setFillPathTexturePaint(texture, *(op.paint), state.alpha) 363 .setTransform(state.computedState.transform, TransformFlags::None) 364 .setModelViewMapUnitToRect(dest) 365 .build(); 366 renderer.renderGlop(state, glop); 367} 368 369SkRect getBoundsOfFill(const RecordedOp& op) { 370 SkRect bounds = op.unmappedBounds.toSkRect(); 371 if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) { 372 float outsetDistance = op.paint->getStrokeWidth() / 2; 373 bounds.outset(outsetDistance, outsetDistance); 374 } 375 return bounds; 376} 377 378void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) { 379 // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) 380 if (op.paint->getStyle() != SkPaint::kStroke_Style 381 || op.paint->getPathEffect() != nullptr 382 || op.useCenter) { 383 PathTexture* texture = renderer.caches().pathCache.getArc( 384 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), 385 op.startAngle, op.sweepAngle, op.useCenter, op.paint); 386 const AutoTexture holder(texture); 387 if (CC_LIKELY(holder.texture)) { 388 renderPathTexture(renderer, state, *texture, op); 389 } 390 } else { 391 SkRect rect = getBoundsOfFill(op); 392 SkPath path; 393 if (op.useCenter) { 394 path.moveTo(rect.centerX(), rect.centerY()); 395 } 396 path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter); 397 if (op.useCenter) { 398 path.close(); 399 } 400 renderConvexPath(renderer, state, path, *(op.paint)); 401 } 402} 403 404void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) { 405 Texture* texture = renderer.getTexture(op.bitmap); 406 if (!texture) return; 407 const AutoTexture autoCleanup(texture); 408 409 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) 410 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; 411 Glop glop; 412 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 413 .setRoundRectClipState(state.roundRectClipState) 414 .setMeshTexturedUnitQuad(texture->uvMapper) 415 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 416 .setTransform(state.computedState.transform, TransformFlags::None) 417 .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height)) 418 .build(); 419 renderer.renderGlop(state, glop); 420} 421 422void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) { 423 const static UvMapper defaultUvMapper; 424 const uint32_t elementCount = op.meshWidth * op.meshHeight * 6; 425 426 std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]); 427 ColorTextureVertex* vertex = &mesh[0]; 428 429 const int* colors = op.colors; 430 std::unique_ptr<int[]> tempColors; 431 if (!colors) { 432 uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1); 433 tempColors.reset(new int[colorsCount]); 434 memset(tempColors.get(), 0xff, colorsCount * sizeof(int)); 435 colors = tempColors.get(); 436 } 437 438 Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef()); 439 const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper); 440 441 for (int32_t y = 0; y < op.meshHeight; y++) { 442 for (int32_t x = 0; x < op.meshWidth; x++) { 443 uint32_t i = (y * (op.meshWidth + 1) + x) * 2; 444 445 float u1 = float(x) / op.meshWidth; 446 float u2 = float(x + 1) / op.meshWidth; 447 float v1 = float(y) / op.meshHeight; 448 float v2 = float(y + 1) / op.meshHeight; 449 450 mapper.map(u1, v1, u2, v2); 451 452 int ax = i + (op.meshWidth + 1) * 2; 453 int ay = ax + 1; 454 int bx = i; 455 int by = bx + 1; 456 int cx = i + 2; 457 int cy = cx + 1; 458 int dx = i + (op.meshWidth + 1) * 2 + 2; 459 int dy = dx + 1; 460 461 const float* vertices = op.vertices; 462 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); 463 ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]); 464 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); 465 466 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); 467 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); 468 ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]); 469 } 470 } 471 472 if (!texture) { 473 texture = renderer.caches().textureCache.get(op.bitmap); 474 if (!texture) { 475 return; 476 } 477 } 478 const AutoTexture autoCleanup(texture); 479 480 /* 481 * TODO: handle alpha_8 textures correctly by applying paint color, but *not* 482 * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh. 483 */ 484 const int textureFillFlags = TextureFillFlags::None; 485 Glop glop; 486 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 487 .setRoundRectClipState(state.roundRectClipState) 488 .setMeshColoredTexturedMesh(mesh.get(), elementCount) 489 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 490 .setTransform(state.computedState.transform, TransformFlags::None) 491 .setModelViewOffsetRect(0, 0, op.unmappedBounds) 492 .build(); 493 renderer.renderGlop(state, glop); 494} 495 496void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) { 497 Texture* texture = renderer.getTexture(op.bitmap); 498 if (!texture) return; 499 const AutoTexture autoCleanup(texture); 500 501 Rect uv(std::max(0.0f, op.src.left / texture->width), 502 std::max(0.0f, op.src.top / texture->height), 503 std::min(1.0f, op.src.right / texture->width), 504 std::min(1.0f, op.src.bottom / texture->height)); 505 506 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) 507 ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; 508 const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth()) 509 && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight()); 510 Glop glop; 511 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 512 .setRoundRectClipState(state.roundRectClipState) 513 .setMeshTexturedUvQuad(texture->uvMapper, uv) 514 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 515 .setTransform(state.computedState.transform, TransformFlags::None) 516 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds) 517 .build(); 518 renderer.renderGlop(state, glop); 519} 520 521void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) { 522 renderer.renderFunctor(op, state); 523} 524 525void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) { 526 VertexBuffer buffer; 527 PathTessellator::tessellateLines(op.points, op.floatCount, op.paint, 528 state.computedState.transform, buffer); 529 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset; 530 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags); 531} 532 533void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) { 534 if (op.paint->getPathEffect() != nullptr) { 535 PathTexture* texture = renderer.caches().pathCache.getOval( 536 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); 537 const AutoTexture holder(texture); 538 if (CC_LIKELY(holder.texture)) { 539 renderPathTexture(renderer, state, *texture, op); 540 } 541 } else { 542 SkPath path; 543 SkRect rect = getBoundsOfFill(op); 544 path.addOval(rect); 545 renderConvexPath(renderer, state, path, *(op.paint)); 546 } 547} 548 549void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) { 550 // 9 patches are built for stretching - always filter 551 int textureFillFlags = TextureFillFlags::ForceFilter; 552 if (op.bitmap->colorType() == kAlpha_8_SkColorType) { 553 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; 554 } 555 556 // TODO: avoid redoing the below work each frame: 557 AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef()); 558 const Patch* mesh = renderer.caches().patchCache.get( 559 entry, op.bitmap->width(), op.bitmap->height(), 560 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); 561 562 Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap); 563 if (!texture) return; 564 const AutoTexture autoCleanup(texture); 565 Glop glop; 566 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 567 .setRoundRectClipState(state.roundRectClipState) 568 .setMeshPatchQuads(*mesh) 569 .setMeshTexturedUnitQuad(texture->uvMapper) 570 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 571 .setTransform(state.computedState.transform, TransformFlags::None) 572 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top, 573 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight())) 574 .build(); 575 renderer.renderGlop(state, glop); 576} 577 578void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) { 579 PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint); 580 const AutoTexture holder(texture); 581 if (CC_LIKELY(holder.texture)) { 582 renderPathTexture(renderer, state, *texture, op); 583 } 584} 585 586void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) { 587 VertexBuffer buffer; 588 PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint, 589 state.computedState.transform, buffer); 590 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset; 591 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags); 592} 593 594// See SkPaintDefaults.h 595#define SkPaintDefaults_MiterLimit SkIntToScalar(4) 596 597void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) { 598 if (op.paint->getStyle() != SkPaint::kFill_Style) { 599 // only fill + default miter is supported by drawConvexPath, since others must handle joins 600 static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed"); 601 if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr 602 || op.paint->getStrokeJoin() != SkPaint::kMiter_Join 603 || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) { 604 PathTexture* texture = renderer.caches().pathCache.getRect( 605 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); 606 const AutoTexture holder(texture); 607 if (CC_LIKELY(holder.texture)) { 608 renderPathTexture(renderer, state, *texture, op); 609 } 610 } else { 611 SkPath path; 612 path.addRect(getBoundsOfFill(op)); 613 renderConvexPath(renderer, state, path, *(op.paint)); 614 } 615 } else { 616 if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) { 617 SkPath path; 618 path.addRect(op.unmappedBounds.toSkRect()); 619 renderConvexPath(renderer, state, path, *(op.paint)); 620 } else { 621 // render simple unit quad, no tessellation required 622 Glop glop; 623 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 624 .setRoundRectClipState(state.roundRectClipState) 625 .setMeshUnitQuad() 626 .setFillPaint(*op.paint, state.alpha) 627 .setTransform(state.computedState.transform, TransformFlags::None) 628 .setModelViewMapUnitToRect(op.unmappedBounds) 629 .build(); 630 renderer.renderGlop(state, glop); 631 } 632 } 633} 634 635void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) { 636 if (op.paint->getPathEffect() != nullptr) { 637 PathTexture* texture = renderer.caches().pathCache.getRoundRect( 638 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), 639 op.rx, op.ry, op.paint); 640 const AutoTexture holder(texture); 641 if (CC_LIKELY(holder.texture)) { 642 renderPathTexture(renderer, state, *texture, op); 643 } 644 } else { 645 const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect( 646 state.computedState.transform, *(op.paint), 647 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry); 648 renderVertexBuffer(renderer, state, *buffer, 649 op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0); 650 } 651} 652 653static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha, 654 const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) { 655 SkPaint paint; 656 paint.setAntiAlias(true); // want to use AlphaVertex 657 658 // The caller has made sure casterAlpha > 0. 659 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha; 660 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) { 661 ambientShadowAlpha = Properties::overrideAmbientShadowStrength; 662 } 663 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) { 664 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha)); 665 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0, 666 paint, VertexBufferRenderFlags::ShadowInterp); 667 } 668 669 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha; 670 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) { 671 spotShadowAlpha = Properties::overrideSpotShadowStrength; 672 } 673 if (spotShadowVertexBuffer && spotShadowAlpha > 0) { 674 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha)); 675 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0, 676 paint, VertexBufferRenderFlags::ShadowInterp); 677 } 678} 679 680void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) { 681 TessellationCache::vertexBuffer_pair_t buffers; 682 renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform, 683 op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath, 684 &op.shadowMatrixXY, &op.shadowMatrixZ, 685 op.lightCenter, renderer.getLightInfo().lightRadius, 686 buffers); 687 688 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second); 689} 690 691void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) { 692 Glop glop; 693 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 694 .setRoundRectClipState(state.roundRectClipState) 695 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4) 696 .setFillPaint(*op.paint, state.alpha) 697 .setTransform(state.computedState.transform, TransformFlags::None) 698 .setModelViewOffsetRect(0, 0, op.unmappedBounds) 699 .build(); 700 renderer.renderGlop(state, glop); 701} 702 703void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) { 704 const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr; 705 renderTextOp(renderer, op, state, clip, TextRenderType::Flush); 706} 707 708void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) { 709 OffscreenBuffer* buffer = *op.layerHandle; 710 711 float layerAlpha = op.alpha * state.alpha; 712 Glop glop; 713 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 714 .setRoundRectClipState(state.roundRectClipState) 715 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount) 716 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap) 717 .setTransform(state.computedState.transform, TransformFlags::None) 718 .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top, 719 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight())) 720 .build(); 721 renderer.renderGlop(state, glop); 722 723 if (op.destroy) { 724 renderer.renderState().layerPool().putOrDelete(buffer); 725 } 726} 727 728} // namespace uirenderer 729} // namespace android 730