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 "DeferredLayerUpdater.h" 22 #include "Glop.h" 23 #include "GlopBuilder.h" 24 #include "Patch.h" 25 #include "PathTessellator.h" 26 #include "VertexBuffer.h" 27 #include "renderstate/OffscreenBufferPool.h" 28 #include "renderstate/RenderState.h" 29 #include "utils/GLUtils.h" 30 31 #include <SkPaintDefaults.h> 32 #include <SkPathOps.h> 33 #include <math.h> 34 #include <algorithm> 35 36 namespace android { 37 namespace uirenderer { 38 39 static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds) { 40 vertices[0] = {bounds.left, bounds.top, 0, 0}; 41 vertices[1] = {bounds.right, bounds.top, 1, 0}; 42 vertices[2] = {bounds.left, bounds.bottom, 0, 1}; 43 vertices[3] = {bounds.right, bounds.bottom, 1, 1}; 44 } 45 46 void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, 47 const MergedBakedOpList& opList) { 48 const BakedOpState& firstState = *(opList.states[0]); 49 Bitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap; 50 51 Texture* texture = renderer.caches().textureCache.get(bitmap); 52 if (!texture) return; 53 const AutoTexture autoCleanup(texture); 54 55 TextureVertex vertices[opList.count * 4]; 56 for (size_t i = 0; i < opList.count; i++) { 57 const BakedOpState& state = *(opList.states[i]); 58 TextureVertex* rectVerts = &vertices[i * 4]; 59 60 // calculate unclipped bounds, since they'll determine texture coordinates 61 Rect opBounds = state.op->unmappedBounds; 62 state.computedState.transform.mapRect(opBounds); 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); 68 renderer.dirtyRenderTarget(opBounds); 69 } 70 71 const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType) 72 ? TextureFillFlags::IsAlphaMaskTexture 73 : 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 87 void 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 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 op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), 103 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 op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), 124 op.unmappedBounds.getHeight(), op.patch); 125 126 uint32_t vertexCount = opMesh->verticesCount; 127 if (vertexCount == 0) continue; 128 129 // We use the bounds to know where to translate our vertices 130 // Using patchOp->state.mBounds wouldn't work because these 131 // bounds are clipped 132 const float tx = floorf(state.computedState.transform.getTranslateX() + 133 op.unmappedBounds.left + 0.5f); 134 const float ty = floorf(state.computedState.transform.getTranslateY() + 135 op.unmappedBounds.top + 0.5f); 136 137 // Copy & transform all the vertices for the current operation 138 TextureVertex* opVertices = opMesh->vertices.get(); 139 for (uint32_t j = 0; j < vertexCount; j++, opVertices++) { 140 TextureVertex::set(vertex++, opVertices->x + tx, opVertices->y + ty, opVertices->u, 141 opVertices->v); 142 } 143 144 // Dirty the current layer if possible. When the 9-patch does not 145 // contain empty quads we can take a shortcut and simply set the 146 // dirty rect to the object's bounds. 147 if (dirtyRenderTarget) { 148 if (!opMesh->hasEmptyQuads) { 149 renderer.dirtyRenderTarget(Rect(tx, ty, tx + op.unmappedBounds.getWidth(), 150 ty + op.unmappedBounds.getHeight())); 151 } else { 152 const size_t count = opMesh->quads.size(); 153 for (size_t i = 0; i < count; i++) { 154 const Rect& quadBounds = opMesh->quads[i]; 155 const float x = tx + quadBounds.left; 156 const float y = ty + quadBounds.top; 157 renderer.dirtyRenderTarget( 158 Rect(x, y, x + quadBounds.getWidth(), y + quadBounds.getHeight())); 159 } 160 } 161 } 162 163 indexCount += opMesh->indexCount; 164 } 165 166 Texture* texture = renderer.caches().textureCache.get(firstOp.bitmap); 167 if (!texture) return; 168 const AutoTexture autoCleanup(texture); 169 170 // 9 patches are built for stretching - always filter 171 int textureFillFlags = TextureFillFlags::ForceFilter; 172 if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) { 173 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; 174 } 175 Glop glop; 176 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 177 .setRoundRectClipState(firstState.roundRectClipState) 178 .setMeshTexturedIndexedQuads(vertices, indexCount) 179 .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha) 180 .setTransform(Matrix4::identity(), TransformFlags::None) 181 .setModelViewIdentityEmptyBounds() 182 .build(); 183 ClipRect renderTargetClip(opList.clip); 184 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; 185 renderer.renderGlop(nullptr, clip, glop); 186 } 187 188 static void renderTextShadow(BakedOpRenderer& renderer, const TextOp& op, 189 const BakedOpState& textOpState) { 190 if (CC_LIKELY(!PaintUtils::hasTextShadow(op.paint))) return; 191 192 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); 193 fontRenderer.setFont(op.paint, SkMatrix::I()); 194 renderer.caches().textureState().activateTexture(0); 195 196 PaintUtils::TextShadow textShadow; 197 if (!PaintUtils::getTextShadow(op.paint, &textShadow)) { 198 LOG_ALWAYS_FATAL("failed to query shadow attributes"); 199 } 200 201 renderer.caches().dropShadowCache.setFontRenderer(fontRenderer); 202 ShadowTexture* texture = renderer.caches().dropShadowCache.get( 203 op.paint, op.glyphs, op.glyphCount, textShadow.radius, op.positions); 204 // If the drop shadow exceeds the max texture size or couldn't be 205 // allocated, skip drawing 206 if (!texture) return; 207 const AutoTexture autoCleanup(texture); 208 209 const float sx = op.x - texture->left + textShadow.dx; 210 const float sy = op.y - texture->top + textShadow.dy; 211 212 Glop glop; 213 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 214 .setRoundRectClipState(textOpState.roundRectClipState) 215 .setMeshTexturedUnitQuad(nullptr) 216 .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, textOpState.alpha) 217 .setTransform(textOpState.computedState.transform, TransformFlags::None) 218 .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height())) 219 .build(); 220 221 // Compute damage bounds and clip (since may differ from those in textOpState). 222 // Bounds should be same as text op, but with dx/dy offset and radius outset 223 // applied in local space. 224 auto& transform = textOpState.computedState.transform; 225 Rect shadowBounds = op.unmappedBounds; // STROKE 226 const bool expandForStroke = op.paint->getStyle() != SkPaint::kFill_Style; 227 if (expandForStroke) { 228 shadowBounds.outset(op.paint->getStrokeWidth() * 0.5f); 229 } 230 shadowBounds.translate(textShadow.dx, textShadow.dy); 231 shadowBounds.outset(textShadow.radius, textShadow.radius); 232 transform.mapRect(shadowBounds); 233 if (CC_UNLIKELY(expandForStroke && 234 (!transform.isPureTranslate() || op.paint->getStrokeWidth() < 1.0f))) { 235 shadowBounds.outset(0.5f); 236 } 237 238 auto clipState = textOpState.computedState.clipState; 239 if (clipState->mode != ClipMode::Rectangle || !clipState->rect.contains(shadowBounds)) { 240 // need clip, so pass it and clip bounds 241 shadowBounds.doIntersect(clipState->rect); 242 } else { 243 // don't need clip, ignore 244 clipState = nullptr; 245 } 246 247 renderer.renderGlop(&shadowBounds, clipState, glop); 248 } 249 250 enum class TextRenderType { Defer, Flush }; 251 252 static void renderText(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state, 253 const ClipBase* renderClip, TextRenderType renderType) { 254 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); 255 float x = op.x; 256 float y = op.y; 257 const Matrix4& transform = state.computedState.transform; 258 const bool pureTranslate = transform.isPureTranslate(); 259 if (CC_LIKELY(pureTranslate)) { 260 x = floorf(x + transform.getTranslateX() + 0.5f); 261 y = floorf(y + transform.getTranslateY() + 0.5f); 262 fontRenderer.setFont(op.paint, SkMatrix::I()); 263 fontRenderer.setTextureFiltering(false); 264 } else if (CC_UNLIKELY(transform.isPerspective())) { 265 fontRenderer.setFont(op.paint, SkMatrix::I()); 266 fontRenderer.setTextureFiltering(true); 267 } else { 268 // We only pass a partial transform to the font renderer. That partial 269 // matrix defines how glyphs are rasterized. Typically we want glyphs 270 // to be rasterized at their final size on screen, which means the partial 271 // matrix needs to take the scale factor into account. 272 // When a partial matrix is used to transform glyphs during rasterization, 273 // the mesh is generated with the inverse transform (in the case of scale, 274 // the mesh is generated at 1.0 / scale for instance.) This allows us to 275 // apply the full transform matrix at draw time in the vertex shader. 276 // Applying the full matrix in the shader is the easiest way to handle 277 // rotation and perspective and allows us to always generated quads in the 278 // font renderer which greatly simplifies the code, clipping in particular. 279 float sx, sy; 280 transform.decomposeScale(sx, sy); 281 fontRenderer.setFont(op.paint, SkMatrix::MakeScale(roundf(std::max(1.0f, sx)), 282 roundf(std::max(1.0f, sy)))); 283 fontRenderer.setTextureFiltering(true); 284 } 285 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); 286 287 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha; 288 SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint); 289 TextDrawFunctor functor(&renderer, &state, renderClip, x, y, pureTranslate, alpha, mode, 290 op.paint); 291 292 bool forceFinish = (renderType == TextRenderType::Flush); 293 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); 294 const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr; 295 fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y, op.positions, 296 mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, 297 forceFinish); 298 299 if (mustDirtyRenderTarget) { 300 if (!pureTranslate) { 301 transform.mapRect(layerBounds); 302 } 303 renderer.dirtyRenderTarget(layerBounds); 304 } 305 } 306 307 void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer, 308 const MergedBakedOpList& opList) { 309 for (size_t i = 0; i < opList.count; i++) { 310 const BakedOpState& state = *(opList.states[i]); 311 const TextOp& op = *(static_cast<const TextOp*>(state.op)); 312 renderTextShadow(renderer, op, state); 313 } 314 315 ClipRect renderTargetClip(opList.clip); 316 const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; 317 for (size_t i = 0; i < opList.count; i++) { 318 const BakedOpState& state = *(opList.states[i]); 319 const TextOp& op = *(static_cast<const TextOp*>(state.op)); 320 TextRenderType renderType = 321 (i + 1 == opList.count) ? TextRenderType::Flush : TextRenderType::Defer; 322 renderText(renderer, op, state, clip, renderType); 323 } 324 } 325 326 namespace VertexBufferRenderFlags { 327 enum { 328 Offset = 0x1, 329 ShadowInterp = 0x2, 330 }; 331 } 332 333 static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state, 334 const VertexBuffer& vertexBuffer, float translateX, float translateY, 335 const SkPaint& paint, int vertexBufferRenderFlags) { 336 if (CC_LIKELY(vertexBuffer.getVertexCount())) { 337 bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp; 338 const int transformFlags = vertexBufferRenderFlags & VertexBufferRenderFlags::Offset 339 ? TransformFlags::OffsetByFudgeFactor 340 : 0; 341 342 Glop glop; 343 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 344 .setRoundRectClipState(state.roundRectClipState) 345 .setMeshVertexBuffer(vertexBuffer) 346 .setFillPaint(paint, state.alpha, shadowInterp) 347 .setTransform(state.computedState.transform, transformFlags) 348 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds()) 349 .build(); 350 renderer.renderGlop(state, glop); 351 } 352 } 353 354 static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state, 355 const SkPath& path, const SkPaint& paint) { 356 VertexBuffer vertexBuffer; 357 // TODO: try clipping large paths to viewport 358 PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer); 359 renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0); 360 } 361 362 static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state, float xOffset, 363 float yOffset, PathTexture& texture, const SkPaint& paint) { 364 Rect dest(texture.width(), texture.height()); 365 dest.translate(xOffset + texture.left - texture.offset, yOffset + texture.top - texture.offset); 366 Glop glop; 367 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 368 .setRoundRectClipState(state.roundRectClipState) 369 .setMeshTexturedUnitQuad(nullptr) 370 .setFillPathTexturePaint(texture, paint, state.alpha) 371 .setTransform(state.computedState.transform, TransformFlags::None) 372 .setModelViewMapUnitToRect(dest) 373 .build(); 374 renderer.renderGlop(state, glop); 375 } 376 377 SkRect getBoundsOfFill(const RecordedOp& op) { 378 SkRect bounds = op.unmappedBounds.toSkRect(); 379 if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) { 380 float outsetDistance = op.paint->getStrokeWidth() / 2; 381 bounds.outset(outsetDistance, outsetDistance); 382 } 383 return bounds; 384 } 385 386 void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, 387 const BakedOpState& state) { 388 // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) 389 if (op.paint->getStyle() != SkPaint::kStroke_Style || op.paint->getPathEffect() != nullptr || 390 op.useCenter) { 391 PathTexture* texture = renderer.caches().pathCache.getArc( 392 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.startAngle, 393 op.sweepAngle, op.useCenter, op.paint); 394 const AutoTexture holder(texture); 395 if (CC_LIKELY(holder.texture)) { 396 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, 397 *texture, *(op.paint)); 398 } 399 } else { 400 SkRect rect = getBoundsOfFill(op); 401 SkPath path; 402 if (op.useCenter) { 403 path.moveTo(rect.centerX(), rect.centerY()); 404 } 405 path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter); 406 if (op.useCenter) { 407 path.close(); 408 } 409 renderConvexPath(renderer, state, path, *(op.paint)); 410 } 411 } 412 413 void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, 414 const BakedOpState& state) { 415 Texture* texture = renderer.getTexture(op.bitmap); 416 if (!texture) return; 417 const AutoTexture autoCleanup(texture); 418 419 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) 420 ? TextureFillFlags::IsAlphaMaskTexture 421 : TextureFillFlags::None; 422 Glop glop; 423 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 424 .setRoundRectClipState(state.roundRectClipState) 425 .setMeshTexturedUnitQuad(texture->uvMapper) 426 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 427 .setTransform(state.computedState.transform, TransformFlags::None) 428 .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height())) 429 .build(); 430 renderer.renderGlop(state, glop); 431 } 432 433 void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, 434 const BakedOpState& state) { 435 Texture* texture = renderer.caches().textureCache.get(op.bitmap); 436 if (!texture) { 437 return; 438 } 439 const AutoTexture autoCleanup(texture); 440 441 const uint32_t elementCount = op.meshWidth * op.meshHeight * 6; 442 443 std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]); 444 ColorTextureVertex* vertex = &mesh[0]; 445 446 const int* colors = op.colors; 447 std::unique_ptr<int[]> tempColors; 448 if (!colors) { 449 uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1); 450 tempColors.reset(new int[colorsCount]); 451 memset(tempColors.get(), 0xff, colorsCount * sizeof(int)); 452 colors = tempColors.get(); 453 } 454 455 for (int32_t y = 0; y < op.meshHeight; y++) { 456 for (int32_t x = 0; x < op.meshWidth; x++) { 457 uint32_t i = (y * (op.meshWidth + 1) + x) * 2; 458 459 float u1 = float(x) / op.meshWidth; 460 float u2 = float(x + 1) / op.meshWidth; 461 float v1 = float(y) / op.meshHeight; 462 float v2 = float(y + 1) / op.meshHeight; 463 464 int ax = i + (op.meshWidth + 1) * 2; 465 int ay = ax + 1; 466 int bx = i; 467 int by = bx + 1; 468 int cx = i + 2; 469 int cy = cx + 1; 470 int dx = i + (op.meshWidth + 1) * 2 + 2; 471 int dy = dx + 1; 472 473 const float* vertices = op.vertices; 474 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); 475 ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]); 476 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); 477 478 ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); 479 ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); 480 ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]); 481 } 482 } 483 484 /* 485 * TODO: handle alpha_8 textures correctly by applying paint color, but *not* 486 * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh. 487 */ 488 const int textureFillFlags = TextureFillFlags::None; 489 Glop glop; 490 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 491 .setRoundRectClipState(state.roundRectClipState) 492 .setMeshColoredTexturedMesh(mesh.get(), elementCount) 493 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 494 .setTransform(state.computedState.transform, TransformFlags::None) 495 .setModelViewOffsetRect(0, 0, op.unmappedBounds) 496 .build(); 497 renderer.renderGlop(state, glop); 498 } 499 500 void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, 501 const BakedOpState& state) { 502 Texture* texture = renderer.getTexture(op.bitmap); 503 if (!texture) return; 504 const AutoTexture autoCleanup(texture); 505 506 Rect uv(std::max(0.0f, op.src.left / texture->width()), 507 std::max(0.0f, op.src.top / texture->height()), 508 std::min(1.0f, op.src.right / texture->width()), 509 std::min(1.0f, op.src.bottom / texture->height())); 510 511 const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) 512 ? TextureFillFlags::IsAlphaMaskTexture 513 : TextureFillFlags::None; 514 const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth()) && 515 MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight()); 516 Glop glop; 517 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 518 .setRoundRectClipState(state.roundRectClipState) 519 .setMeshTexturedUvQuad(texture->uvMapper, uv) 520 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 521 .setTransform(state.computedState.transform, TransformFlags::None) 522 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds) 523 .build(); 524 renderer.renderGlop(state, glop); 525 } 526 527 void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op, 528 const BakedOpState& state) { 529 SkPaint paint; 530 paint.setColor(op.color); 531 paint.setBlendMode(op.mode); 532 533 Glop glop; 534 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 535 .setRoundRectClipState(state.roundRectClipState) 536 .setMeshUnitQuad() 537 .setFillPaint(paint, state.alpha) 538 .setTransform(Matrix4::identity(), TransformFlags::None) 539 .setModelViewMapUnitToRect(state.computedState.clipState->rect) 540 .build(); 541 renderer.renderGlop(state, glop); 542 } 543 544 void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, 545 const BakedOpState& state) { 546 renderer.renderFunctor(op, state); 547 } 548 549 void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, 550 const BakedOpState& state) { 551 VertexBuffer buffer; 552 PathTessellator::tessellateLines(op.points, op.floatCount, op.paint, 553 state.computedState.transform, buffer); 554 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset; 555 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags); 556 } 557 558 void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, 559 const BakedOpState& state) { 560 if (op.paint->getPathEffect() != nullptr) { 561 PathTexture* texture = renderer.caches().pathCache.getOval( 562 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); 563 const AutoTexture holder(texture); 564 if (CC_LIKELY(holder.texture)) { 565 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, 566 *texture, *(op.paint)); 567 } 568 } else { 569 SkPath path; 570 SkRect rect = getBoundsOfFill(op); 571 path.addOval(rect); 572 573 if (state.computedState.localProjectionPathMask != nullptr) { 574 // Mask the ripple path by the local space projection mask in local space. 575 // Note that this can create CCW paths. 576 Op(path, *state.computedState.localProjectionPathMask, kIntersect_SkPathOp, &path); 577 } 578 renderConvexPath(renderer, state, path, *(op.paint)); 579 } 580 } 581 582 void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, 583 const BakedOpState& state) { 584 // 9 patches are built for stretching - always filter 585 int textureFillFlags = TextureFillFlags::ForceFilter; 586 if (op.bitmap->colorType() == kAlpha_8_SkColorType) { 587 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; 588 } 589 590 // TODO: avoid redoing the below work each frame: 591 const Patch* mesh = renderer.caches().patchCache.get(op.bitmap->width(), op.bitmap->height(), 592 op.unmappedBounds.getWidth(), 593 op.unmappedBounds.getHeight(), op.patch); 594 595 Texture* texture = renderer.caches().textureCache.get(op.bitmap); 596 if (CC_LIKELY(texture)) { 597 const AutoTexture autoCleanup(texture); 598 Glop glop; 599 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 600 .setRoundRectClipState(state.roundRectClipState) 601 .setMeshPatchQuads(*mesh) 602 .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) 603 .setTransform(state.computedState.transform, TransformFlags::None) 604 .setModelViewOffsetRectSnap( 605 op.unmappedBounds.left, op.unmappedBounds.top, 606 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight())) 607 .build(); 608 renderer.renderGlop(state, glop); 609 } 610 } 611 612 void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, 613 const BakedOpState& state) { 614 PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint); 615 const AutoTexture holder(texture); 616 if (CC_LIKELY(holder.texture)) { 617 // Unlike other callers to renderPathTexture, no offsets are used because PathOp doesn't 618 // have any translate built in, other than what's in the SkPath itself 619 renderPathTexture(renderer, state, 0, 0, *texture, *(op.paint)); 620 } 621 } 622 623 void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, 624 const BakedOpState& state) { 625 VertexBuffer buffer; 626 PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint, 627 state.computedState.transform, buffer); 628 int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset; 629 renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags); 630 } 631 632 // See SkPaintDefaults.h 633 #define SkPaintDefaults_MiterLimit SkIntToScalar(4) 634 635 void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, 636 const BakedOpState& state) { 637 if (op.paint->getStyle() != SkPaint::kFill_Style) { 638 // only fill + default miter is supported by drawConvexPath, since others must handle joins 639 static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed"); 640 if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr || 641 op.paint->getStrokeJoin() != SkPaint::kMiter_Join || 642 op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) { 643 PathTexture* texture = renderer.caches().pathCache.getRect( 644 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); 645 const AutoTexture holder(texture); 646 if (CC_LIKELY(holder.texture)) { 647 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, 648 *texture, *(op.paint)); 649 } 650 } else { 651 SkPath path; 652 path.addRect(getBoundsOfFill(op)); 653 renderConvexPath(renderer, state, path, *(op.paint)); 654 } 655 } else { 656 if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) { 657 SkPath path; 658 path.addRect(op.unmappedBounds.toSkRect()); 659 renderConvexPath(renderer, state, path, *(op.paint)); 660 } else { 661 // render simple unit quad, no tessellation required 662 Glop glop; 663 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 664 .setRoundRectClipState(state.roundRectClipState) 665 .setMeshUnitQuad() 666 .setFillPaint(*op.paint, state.alpha) 667 .setTransform(state.computedState.transform, TransformFlags::None) 668 .setModelViewMapUnitToRect(op.unmappedBounds) 669 .build(); 670 renderer.renderGlop(state, glop); 671 } 672 } 673 } 674 675 void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, 676 const BakedOpState& state) { 677 if (op.paint->getPathEffect() != nullptr) { 678 PathTexture* texture = renderer.caches().pathCache.getRoundRect( 679 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry, 680 op.paint); 681 const AutoTexture holder(texture); 682 if (CC_LIKELY(holder.texture)) { 683 renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, 684 *texture, *(op.paint)); 685 } 686 } else { 687 const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect( 688 state.computedState.transform, *(op.paint), op.unmappedBounds.getWidth(), 689 op.unmappedBounds.getHeight(), op.rx, op.ry); 690 renderVertexBuffer(renderer, state, *buffer, op.unmappedBounds.left, op.unmappedBounds.top, 691 *(op.paint), 0); 692 } 693 } 694 695 static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha, 696 const VertexBuffer* ambientShadowVertexBuffer, 697 const VertexBuffer* spotShadowVertexBuffer) { 698 SkPaint paint; 699 paint.setAntiAlias(true); // want to use AlphaVertex 700 701 // The caller has made sure casterAlpha > 0. 702 uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha; 703 if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) { 704 ambientShadowAlpha = Properties::overrideAmbientShadowStrength; 705 } 706 if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) { 707 paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha)); 708 renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0, paint, 709 VertexBufferRenderFlags::ShadowInterp); 710 } 711 712 uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha; 713 if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) { 714 spotShadowAlpha = Properties::overrideSpotShadowStrength; 715 } 716 if (spotShadowVertexBuffer && spotShadowAlpha > 0) { 717 paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha)); 718 renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0, paint, 719 VertexBufferRenderFlags::ShadowInterp); 720 } 721 } 722 723 void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, 724 const BakedOpState& state) { 725 TessellationCache::vertexBuffer_pair_t buffers = op.shadowTask->getResult(); 726 renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second); 727 } 728 729 void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, 730 const BakedOpState& state) { 731 Glop glop; 732 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 733 .setRoundRectClipState(state.roundRectClipState) 734 .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4) 735 .setFillPaint(*op.paint, state.alpha) 736 .setTransform(state.computedState.transform, TransformFlags::None) 737 .setModelViewOffsetRect(0, 0, op.unmappedBounds) 738 .build(); 739 renderer.renderGlop(state, glop); 740 } 741 742 void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, 743 const BakedOpState& state) { 744 renderTextShadow(renderer, op, state); 745 renderText(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush); 746 } 747 748 void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, 749 const BakedOpState& state) { 750 // Note: can't trust clipSideFlags since we record with unmappedBounds == clip. 751 // TODO: respect clipSideFlags, once we record with bounds 752 auto renderTargetClip = state.computedState.clipState; 753 754 FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); 755 fontRenderer.setFont(op.paint, SkMatrix::I()); 756 fontRenderer.setTextureFiltering(true); 757 758 Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); 759 760 int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha; 761 SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint); 762 TextDrawFunctor functor(&renderer, &state, renderTargetClip, 0.0f, 0.0f, false, alpha, mode, 763 op.paint); 764 765 bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); 766 const Rect localSpaceClip = state.computedState.computeLocalSpaceClip(); 767 if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount, op.path, 768 op.hOffset, op.vOffset, 769 mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) { 770 if (mustDirtyRenderTarget) { 771 // manually dirty render target, since TextDrawFunctor won't 772 state.computedState.transform.mapRect(layerBounds); 773 renderer.dirtyRenderTarget(layerBounds); 774 } 775 } 776 } 777 778 void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, 779 const BakedOpState& state) { 780 GlLayer* layer = static_cast<GlLayer*>(op.layerHandle->backingLayer()); 781 if (!layer) { 782 return; 783 } 784 const bool tryToSnap = layer->getForceFilter(); 785 float alpha = (layer->getAlpha() / 255.0f) * state.alpha; 786 Glop glop; 787 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 788 .setRoundRectClipState(state.roundRectClipState) 789 .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO 790 .setFillTextureLayer(*(layer), alpha) 791 .setTransform(state.computedState.transform, TransformFlags::None) 792 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, 793 Rect(layer->getWidth(), layer->getHeight())) 794 .build(); 795 renderer.renderGlop(state, glop); 796 } 797 798 void renderRectForLayer(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state, 799 int color, SkBlendMode mode, SkColorFilter* colorFilter) { 800 SkPaint paint; 801 paint.setColor(color); 802 paint.setBlendMode(mode); 803 paint.setColorFilter(sk_ref_sp(colorFilter)); 804 RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint); 805 BakedOpDispatcher::onRectOp(renderer, rectOp, state); 806 } 807 808 void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, 809 const BakedOpState& state) { 810 // Note that we don't use op->paint in this function - it's never set on a LayerOp 811 OffscreenBuffer* buffer = *op.layerHandle; 812 813 if (CC_UNLIKELY(!buffer)) return; 814 815 float layerAlpha = op.alpha * state.alpha; 816 Glop glop; 817 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 818 .setRoundRectClipState(state.roundRectClipState) 819 .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount) 820 .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, 821 Blend::ModeOrderSwap::NoSwap) 822 .setTransform(state.computedState.transform, TransformFlags::None) 823 .setModelViewOffsetRectSnap( 824 op.unmappedBounds.left, op.unmappedBounds.top, 825 Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight())) 826 .build(); 827 renderer.renderGlop(state, glop); 828 829 if (!buffer->hasRenderedSinceRepaint) { 830 buffer->hasRenderedSinceRepaint = true; 831 if (CC_UNLIKELY(Properties::debugLayersUpdates)) { 832 // render debug layer highlight 833 renderRectForLayer(renderer, op, state, 0x7f00ff00, SkBlendMode::kSrcOver, nullptr); 834 } else if (CC_UNLIKELY(Properties::debugOverdraw)) { 835 // render transparent to increment overdraw for repaint area 836 renderRectForLayer(renderer, op, state, SK_ColorTRANSPARENT, SkBlendMode::kSrcOver, 837 nullptr); 838 } 839 } 840 } 841 842 void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, 843 const BakedOpState& state) { 844 LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!"); 845 *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds); 846 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed"); 847 } 848 849 void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, 850 const BakedOpState& state) { 851 LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!"); 852 if (!state.computedState.clippedBounds.isEmpty()) { 853 if (op.paint && op.paint->getAlpha() < 255) { 854 SkPaint layerPaint; 855 layerPaint.setAlpha(op.paint->getAlpha()); 856 layerPaint.setBlendMode(SkBlendMode::kDstIn); 857 layerPaint.setColorFilter(sk_ref_sp(op.paint->getColorFilter())); 858 RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, 859 &layerPaint); 860 BakedOpDispatcher::onRectOp(renderer, rectOp, state); 861 } 862 863 OffscreenBuffer& layer = **(op.layerHandle); 864 auto mode = PaintUtils::getBlendModeDirect(op.paint); 865 Glop glop; 866 GlopBuilder(renderer.renderState(), renderer.caches(), &glop) 867 .setRoundRectClipState(state.roundRectClipState) 868 .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates()) 869 .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap) 870 .setTransform(state.computedState.transform, TransformFlags::None) 871 .setModelViewMapUnitToRect(state.computedState.clippedBounds) 872 .build(); 873 renderer.renderGlop(state, glop); 874 } 875 renderer.renderState().layerPool().putOrDelete(*op.layerHandle); 876 } 877 878 } // namespace uirenderer 879 } // namespace android 880