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