Loading libs/hwui/BakedOpDispatcher.cpp +234 −5 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include "Caches.h" #include "Glop.h" #include "GlopBuilder.h" #include "Patch.h" #include "PathTessellator.h" #include "renderstate/OffscreenBufferPool.h" #include "renderstate/RenderState.h" Loading Loading @@ -56,8 +57,6 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, if (entry) { entry->uvMapper.map(texCoords); } // init to non-empty, so we can safely expandtoCoverRect Rect totalBounds = firstState.computedState.clippedBounds; for (size_t i = 0; i < opList.count; i++) { const BakedOpState& state = *(opList.states[i]); TextureVertex* rectVerts = &vertices[i * 4]; Loading @@ -68,8 +67,6 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, } storeTexturedRect(rectVerts, opBounds, texCoords); renderer.dirtyRenderTarget(opBounds); totalBounds.expandToCover(opBounds); } const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType) Loading @@ -80,7 +77,111 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, .setMeshTexturedIndexedQuads(vertices, opList.count * 6) .setFillTexturePaint(*texture, textureFillFlags, firstState.op->paint, firstState.alpha) .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewOffsetRect(0, 0, totalBounds) // don't snap here, we snap per-quad above .setModelViewIdentityEmptyBounds() .build(); renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); } void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, const MergedBakedOpList& opList) { const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op)); const BakedOpState& firstState = *(opList.states[0]); AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry( firstOp.bitmap->pixelRef()); // Batches will usually contain a small number of items so it's // worth performing a first iteration to count the exact number // of vertices we need in the new mesh uint32_t totalVertices = 0; for (size_t i = 0; i < opList.count; i++) { const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op)); // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( entry, op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); totalVertices += opMesh->verticesCount; } const bool dirtyRenderTarget = renderer.offscreenRenderTarget(); uint32_t indexCount = 0; TextureVertex vertices[totalVertices]; TextureVertex* vertex = &vertices[0]; // Create a mesh that contains the transformed vertices for all the // 9-patch objects that are part of the batch. Note that onDefer() // enforces ops drawn by this function to have a pure translate or // identity matrix for (size_t i = 0; i < opList.count; i++) { const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op)); const BakedOpState& state = *opList.states[i]; // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( entry, op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); uint32_t vertexCount = opMesh->verticesCount; if (vertexCount == 0) continue; // We use the bounds to know where to translate our vertices // Using patchOp->state.mBounds wouldn't work because these // bounds are clipped const float tx = floorf(state.computedState.transform.getTranslateX() + op.unmappedBounds.left + 0.5f); const float ty = floorf(state.computedState.transform.getTranslateY() + op.unmappedBounds.top + 0.5f); // Copy & transform all the vertices for the current operation TextureVertex* opVertices = opMesh->vertices.get(); for (uint32_t j = 0; j < vertexCount; j++, opVertices++) { TextureVertex::set(vertex++, opVertices->x + tx, opVertices->y + ty, opVertices->u, opVertices->v); } // Dirty the current layer if possible. When the 9-patch does not // contain empty quads we can take a shortcut and simply set the // dirty rect to the object's bounds. if (dirtyRenderTarget) { if (!opMesh->hasEmptyQuads) { renderer.dirtyRenderTarget(Rect(tx, ty, tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.getHeight())); } else { const size_t count = opMesh->quads.size(); for (size_t i = 0; i < count; i++) { const Rect& quadBounds = opMesh->quads[i]; const float x = tx + quadBounds.left; const float y = ty + quadBounds.top; renderer.dirtyRenderTarget(Rect(x, y, x + quadBounds.getWidth(), y + quadBounds.getHeight())); } } } indexCount += opMesh->indexCount; } Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); // 9 patches are built for stretching - always filter int textureFillFlags = TextureFillFlags::ForceFilter; if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) { textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; } Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(firstState.roundRectClipState) .setMeshTexturedIndexedQuads(vertices, indexCount) .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha) .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); } Loading Loading @@ -310,6 +411,105 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op renderer.renderGlop(state, glop); } void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) { const static UvMapper defaultUvMapper; const uint32_t elementCount = op.meshWidth * op.meshHeight * 6; std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]); ColorTextureVertex* vertex = &mesh[0]; const int* colors = op.colors; std::unique_ptr<int[]> tempColors; if (!colors) { uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1); tempColors.reset(new int[colorsCount]); memset(tempColors.get(), 0xff, colorsCount * sizeof(int)); colors = tempColors.get(); } Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef()); const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper); for (int32_t y = 0; y < op.meshHeight; y++) { for (int32_t x = 0; x < op.meshWidth; x++) { uint32_t i = (y * (op.meshWidth + 1) + x) * 2; float u1 = float(x) / op.meshWidth; float u2 = float(x + 1) / op.meshWidth; float v1 = float(y) / op.meshHeight; float v2 = float(y + 1) / op.meshHeight; mapper.map(u1, v1, u2, v2); int ax = i + (op.meshWidth + 1) * 2; int ay = ax + 1; int bx = i; int by = bx + 1; int cx = i + 2; int cy = cx + 1; int dx = i + (op.meshWidth + 1) * 2 + 2; int dy = dx + 1; const float* vertices = op.vertices; ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]); ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]); } } if (!texture) { texture = renderer.caches().textureCache.get(op.bitmap); if (!texture) { return; } } const AutoTexture autoCleanup(texture); /* * TODO: handle alpha_8 textures correctly by applying paint color, but *not* * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh. */ const int textureFillFlags = TextureFillFlags::None; Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshColoredTexturedMesh(mesh.get(), elementCount) .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewOffsetRect(0, 0, op.unmappedBounds) .build(); renderer.renderGlop(state, glop); } void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) { Texture* texture = renderer.getTexture(op.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); Rect uv(std::max(0.0f, op.src.left / texture->width), std::max(0.0f, op.src.top / texture->height), std::min(1.0f, op.src.right / texture->width), std::min(1.0f, op.src.bottom / texture->height)); const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth()) && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight()); Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshTexturedUvQuad(texture->uvMapper, uv) .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds) .build(); renderer.renderGlop(state, glop); } void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) { VertexBuffer buffer; PathTessellator::tessellateLines(op.points, op.floatCount, op.paint, Loading @@ -334,6 +534,35 @@ void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, co } } void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) { // 9 patches are built for stretching - always filter int textureFillFlags = TextureFillFlags::ForceFilter; if (op.bitmap->colorType() == kAlpha_8_SkColorType) { textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; } // TODO: avoid redoing the below work each frame: AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef()); const Patch* mesh = renderer.caches().patchCache.get( entry, op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshPatchQuads(*mesh) .setMeshTexturedUnitQuad(texture->uvMapper) .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top, Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight())) .build(); renderer.renderGlop(state, glop); } void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) { PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint); const AutoTexture holder(texture); Loading libs/hwui/FontRenderer.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha) .setTransform(bakedState->computedState.transform, transformFlags) .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0)) .setModelViewIdentityEmptyBounds() .build(); // Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer renderer->renderGlop(nullptr, clip, glop); Loading libs/hwui/Glop.h +1 −1 Original line number Diff line number Diff line Loading @@ -64,7 +64,7 @@ namespace TransformFlags { // Canvas transform isn't applied to the mesh at draw time, //since it's already built in. MeshIgnoresCanvasTransform = 1 << 1, MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove }; }; Loading libs/hwui/GlopBuilder.h +4 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,10 @@ public: return setModelViewOffsetRect(offsetX, offsetY, source); } } GlopBuilder& setModelViewIdentityEmptyBounds() { // pass empty rect since not needed for damage / snap return setModelViewOffsetRect(0, 0, Rect()); } GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState); Loading libs/hwui/OpReorderer.cpp +29 −0 Original line number Diff line number Diff line Loading @@ -757,6 +757,18 @@ void OpReorderer::onBitmapOp(const BitmapOp& op) { } } void OpReorderer::onBitmapMeshOp(const BitmapMeshOp& op) { BakedOpState* bakedState = tryBakeOpState(op); if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); } void OpReorderer::onBitmapRectOp(const BitmapRectOp& op) { BakedOpState* bakedState = tryBakeOpState(op); if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); } void OpReorderer::onLinesOp(const LinesOp& op) { batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices; onStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced); Loading @@ -766,6 +778,23 @@ void OpReorderer::onOvalOp(const OvalOp& op) { onStrokeableOp(op, tessBatchId(op)); } void OpReorderer::onPatchOp(const PatchOp& op) { BakedOpState* bakedState = tryBakeOpState(op); if (!bakedState) return; // quick rejected if (bakedState->computedState.transform.isPureTranslate() && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode) { mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID(); // TODO: AssetAtlas in mergeId // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId); } else { // Use Bitmap batchId since Bitmap+Patch use same shader currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); } } void OpReorderer::onPathOp(const PathOp& op) { onStrokeableOp(op, OpBatchType::Bitmap); } Loading Loading
libs/hwui/BakedOpDispatcher.cpp +234 −5 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include "Caches.h" #include "Glop.h" #include "GlopBuilder.h" #include "Patch.h" #include "PathTessellator.h" #include "renderstate/OffscreenBufferPool.h" #include "renderstate/RenderState.h" Loading Loading @@ -56,8 +57,6 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, if (entry) { entry->uvMapper.map(texCoords); } // init to non-empty, so we can safely expandtoCoverRect Rect totalBounds = firstState.computedState.clippedBounds; for (size_t i = 0; i < opList.count; i++) { const BakedOpState& state = *(opList.states[i]); TextureVertex* rectVerts = &vertices[i * 4]; Loading @@ -68,8 +67,6 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, } storeTexturedRect(rectVerts, opBounds, texCoords); renderer.dirtyRenderTarget(opBounds); totalBounds.expandToCover(opBounds); } const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType) Loading @@ -80,7 +77,111 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, .setMeshTexturedIndexedQuads(vertices, opList.count * 6) .setFillTexturePaint(*texture, textureFillFlags, firstState.op->paint, firstState.alpha) .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewOffsetRect(0, 0, totalBounds) // don't snap here, we snap per-quad above .setModelViewIdentityEmptyBounds() .build(); renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); } void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, const MergedBakedOpList& opList) { const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op)); const BakedOpState& firstState = *(opList.states[0]); AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry( firstOp.bitmap->pixelRef()); // Batches will usually contain a small number of items so it's // worth performing a first iteration to count the exact number // of vertices we need in the new mesh uint32_t totalVertices = 0; for (size_t i = 0; i < opList.count; i++) { const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op)); // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( entry, op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); totalVertices += opMesh->verticesCount; } const bool dirtyRenderTarget = renderer.offscreenRenderTarget(); uint32_t indexCount = 0; TextureVertex vertices[totalVertices]; TextureVertex* vertex = &vertices[0]; // Create a mesh that contains the transformed vertices for all the // 9-patch objects that are part of the batch. Note that onDefer() // enforces ops drawn by this function to have a pure translate or // identity matrix for (size_t i = 0; i < opList.count; i++) { const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op)); const BakedOpState& state = *opList.states[i]; // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( entry, op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); uint32_t vertexCount = opMesh->verticesCount; if (vertexCount == 0) continue; // We use the bounds to know where to translate our vertices // Using patchOp->state.mBounds wouldn't work because these // bounds are clipped const float tx = floorf(state.computedState.transform.getTranslateX() + op.unmappedBounds.left + 0.5f); const float ty = floorf(state.computedState.transform.getTranslateY() + op.unmappedBounds.top + 0.5f); // Copy & transform all the vertices for the current operation TextureVertex* opVertices = opMesh->vertices.get(); for (uint32_t j = 0; j < vertexCount; j++, opVertices++) { TextureVertex::set(vertex++, opVertices->x + tx, opVertices->y + ty, opVertices->u, opVertices->v); } // Dirty the current layer if possible. When the 9-patch does not // contain empty quads we can take a shortcut and simply set the // dirty rect to the object's bounds. if (dirtyRenderTarget) { if (!opMesh->hasEmptyQuads) { renderer.dirtyRenderTarget(Rect(tx, ty, tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.getHeight())); } else { const size_t count = opMesh->quads.size(); for (size_t i = 0; i < count; i++) { const Rect& quadBounds = opMesh->quads[i]; const float x = tx + quadBounds.left; const float y = ty + quadBounds.top; renderer.dirtyRenderTarget(Rect(x, y, x + quadBounds.getWidth(), y + quadBounds.getHeight())); } } } indexCount += opMesh->indexCount; } Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); // 9 patches are built for stretching - always filter int textureFillFlags = TextureFillFlags::ForceFilter; if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) { textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; } Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(firstState.roundRectClipState) .setMeshTexturedIndexedQuads(vertices, indexCount) .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha) .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); } Loading Loading @@ -310,6 +411,105 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op renderer.renderGlop(state, glop); } void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) { const static UvMapper defaultUvMapper; const uint32_t elementCount = op.meshWidth * op.meshHeight * 6; std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]); ColorTextureVertex* vertex = &mesh[0]; const int* colors = op.colors; std::unique_ptr<int[]> tempColors; if (!colors) { uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1); tempColors.reset(new int[colorsCount]); memset(tempColors.get(), 0xff, colorsCount * sizeof(int)); colors = tempColors.get(); } Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef()); const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper); for (int32_t y = 0; y < op.meshHeight; y++) { for (int32_t x = 0; x < op.meshWidth; x++) { uint32_t i = (y * (op.meshWidth + 1) + x) * 2; float u1 = float(x) / op.meshWidth; float u2 = float(x + 1) / op.meshWidth; float v1 = float(y) / op.meshHeight; float v2 = float(y + 1) / op.meshHeight; mapper.map(u1, v1, u2, v2); int ax = i + (op.meshWidth + 1) * 2; int ay = ax + 1; int bx = i; int by = bx + 1; int cx = i + 2; int cy = cx + 1; int dx = i + (op.meshWidth + 1) * 2 + 2; int dy = dx + 1; const float* vertices = op.vertices; ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]); ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]); } } if (!texture) { texture = renderer.caches().textureCache.get(op.bitmap); if (!texture) { return; } } const AutoTexture autoCleanup(texture); /* * TODO: handle alpha_8 textures correctly by applying paint color, but *not* * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh. */ const int textureFillFlags = TextureFillFlags::None; Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshColoredTexturedMesh(mesh.get(), elementCount) .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewOffsetRect(0, 0, op.unmappedBounds) .build(); renderer.renderGlop(state, glop); } void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) { Texture* texture = renderer.getTexture(op.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); Rect uv(std::max(0.0f, op.src.left / texture->width), std::max(0.0f, op.src.top / texture->height), std::min(1.0f, op.src.right / texture->width), std::min(1.0f, op.src.bottom / texture->height)); const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth()) && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight()); Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshTexturedUvQuad(texture->uvMapper, uv) .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds) .build(); renderer.renderGlop(state, glop); } void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) { VertexBuffer buffer; PathTessellator::tessellateLines(op.points, op.floatCount, op.paint, Loading @@ -334,6 +534,35 @@ void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, co } } void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) { // 9 patches are built for stretching - always filter int textureFillFlags = TextureFillFlags::ForceFilter; if (op.bitmap->colorType() == kAlpha_8_SkColorType) { textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; } // TODO: avoid redoing the below work each frame: AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef()); const Patch* mesh = renderer.caches().patchCache.get( entry, op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshPatchQuads(*mesh) .setMeshTexturedUnitQuad(texture->uvMapper) .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top, Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight())) .build(); renderer.renderGlop(state, glop); } void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) { PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint); const AutoTexture holder(texture); Loading
libs/hwui/FontRenderer.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha) .setTransform(bakedState->computedState.transform, transformFlags) .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0)) .setModelViewIdentityEmptyBounds() .build(); // Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer renderer->renderGlop(nullptr, clip, glop); Loading
libs/hwui/Glop.h +1 −1 Original line number Diff line number Diff line Loading @@ -64,7 +64,7 @@ namespace TransformFlags { // Canvas transform isn't applied to the mesh at draw time, //since it's already built in. MeshIgnoresCanvasTransform = 1 << 1, MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove }; }; Loading
libs/hwui/GlopBuilder.h +4 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,10 @@ public: return setModelViewOffsetRect(offsetX, offsetY, source); } } GlopBuilder& setModelViewIdentityEmptyBounds() { // pass empty rect since not needed for damage / snap return setModelViewOffsetRect(0, 0, Rect()); } GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState); Loading
libs/hwui/OpReorderer.cpp +29 −0 Original line number Diff line number Diff line Loading @@ -757,6 +757,18 @@ void OpReorderer::onBitmapOp(const BitmapOp& op) { } } void OpReorderer::onBitmapMeshOp(const BitmapMeshOp& op) { BakedOpState* bakedState = tryBakeOpState(op); if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); } void OpReorderer::onBitmapRectOp(const BitmapRectOp& op) { BakedOpState* bakedState = tryBakeOpState(op); if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); } void OpReorderer::onLinesOp(const LinesOp& op) { batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices; onStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced); Loading @@ -766,6 +778,23 @@ void OpReorderer::onOvalOp(const OvalOp& op) { onStrokeableOp(op, tessBatchId(op)); } void OpReorderer::onPatchOp(const PatchOp& op) { BakedOpState* bakedState = tryBakeOpState(op); if (!bakedState) return; // quick rejected if (bakedState->computedState.transform.isPureTranslate() && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode) { mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID(); // TODO: AssetAtlas in mergeId // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId); } else { // Use Bitmap batchId since Bitmap+Patch use same shader currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); } } void OpReorderer::onPathOp(const PathOp& op) { onStrokeableOp(op, OpBatchType::Bitmap); } Loading