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