| /* | |
| * Copyright (C) 2015 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include "GlopBuilder.h" | |
| #include "Caches.h" | |
| #include "Glop.h" | |
| #include "Layer.h" | |
| #include "Matrix.h" | |
| #include "Patch.h" | |
| #include "PathCache.h" | |
| #include "renderstate/MeshState.h" | |
| #include "renderstate/RenderState.h" | |
| #include "SkiaShader.h" | |
| #include "Texture.h" | |
| #include "utils/PaintUtils.h" | |
| #include "VertexBuffer.h" | |
| #include <GLES2/gl2.h> | |
| #include <SkPaint.h> | |
| #define DEBUG_GLOP_BUILDER 0 | |
| #if DEBUG_GLOP_BUILDER | |
| #define TRIGGER_STAGE(stageFlag) \ | |
| LOG_ALWAYS_FATAL_IF((stageFlag) & mStageFlags, "Stage %d cannot be run twice", (stageFlag)); \ | |
| mStageFlags = static_cast<StageFlags>(mStageFlags | (stageFlag)) | |
| #define REQUIRE_STAGES(requiredFlags) \ | |
| LOG_ALWAYS_FATAL_IF((mStageFlags & (requiredFlags)) != (requiredFlags), \ | |
| "not prepared for current stage") | |
| #else | |
| #define TRIGGER_STAGE(stageFlag) ((void)0) | |
| #define REQUIRE_STAGES(requiredFlags) ((void)0) | |
| #endif | |
| namespace android { | |
| namespace uirenderer { | |
| static void setUnitQuadTextureCoords(Rect uvs, TextureVertex* quadVertex) { | |
| quadVertex[0] = {0, 0, uvs.left, uvs.top}; | |
| quadVertex[1] = {1, 0, uvs.right, uvs.top}; | |
| quadVertex[2] = {0, 1, uvs.left, uvs.bottom}; | |
| quadVertex[3] = {1, 1, uvs.right, uvs.bottom}; | |
| } | |
| GlopBuilder::GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop) | |
| : mRenderState(renderState) | |
| , mCaches(caches) | |
| , mShader(nullptr) | |
| , mOutGlop(outGlop) { | |
| mStageFlags = kInitialStage; | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // Mesh | |
| //////////////////////////////////////////////////////////////////////////////// | |
| GlopBuilder& GlopBuilder::setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount) { | |
| TRIGGER_STAGE(kMeshStage); | |
| mOutGlop->mesh.primitiveMode = GL_TRIANGLES; | |
| mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; | |
| mOutGlop->mesh.vertices = { | |
| vbo, | |
| VertexAttribFlags::TextureCoord, | |
| nullptr, (const void*) kMeshTextureOffset, nullptr, | |
| kTextureVertexStride }; | |
| mOutGlop->mesh.elementCount = elementCount; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setMeshUnitQuad() { | |
| TRIGGER_STAGE(kMeshStage); | |
| mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; | |
| mOutGlop->mesh.indices = { 0, nullptr }; | |
| mOutGlop->mesh.vertices = { | |
| mRenderState.meshState().getUnitQuadVBO(), | |
| VertexAttribFlags::None, | |
| nullptr, nullptr, nullptr, | |
| kTextureVertexStride }; | |
| mOutGlop->mesh.elementCount = 4; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setMeshTexturedUnitQuad(const UvMapper* uvMapper) { | |
| if (uvMapper) { | |
| // can't use unit quad VBO, so build UV vertices manually | |
| return setMeshTexturedUvQuad(uvMapper, Rect(1, 1)); | |
| } | |
| TRIGGER_STAGE(kMeshStage); | |
| mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; | |
| mOutGlop->mesh.indices = { 0, nullptr }; | |
| mOutGlop->mesh.vertices = { | |
| mRenderState.meshState().getUnitQuadVBO(), | |
| VertexAttribFlags::TextureCoord, | |
| nullptr, (const void*) kMeshTextureOffset, nullptr, | |
| kTextureVertexStride }; | |
| mOutGlop->mesh.elementCount = 4; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setMeshTexturedUvQuad(const UvMapper* uvMapper, Rect uvs) { | |
| TRIGGER_STAGE(kMeshStage); | |
| if (CC_UNLIKELY(uvMapper)) { | |
| uvMapper->map(uvs); | |
| } | |
| setUnitQuadTextureCoords(uvs, &mOutGlop->mesh.mappedVertices[0]); | |
| const TextureVertex* textureVertex = mOutGlop->mesh.mappedVertices; | |
| mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; | |
| mOutGlop->mesh.indices = { 0, nullptr }; | |
| mOutGlop->mesh.vertices = { | |
| 0, | |
| VertexAttribFlags::TextureCoord, | |
| &textureVertex[0].x, &textureVertex[0].u, nullptr, | |
| kTextureVertexStride }; | |
| mOutGlop->mesh.elementCount = 4; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setMeshIndexedQuads(Vertex* vertexData, int quadCount) { | |
| TRIGGER_STAGE(kMeshStage); | |
| mOutGlop->mesh.primitiveMode = GL_TRIANGLES; | |
| mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; | |
| mOutGlop->mesh.vertices = { | |
| 0, | |
| VertexAttribFlags::None, | |
| vertexData, nullptr, nullptr, | |
| kVertexStride }; | |
| mOutGlop->mesh.elementCount = 6 * quadCount; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount) { | |
| TRIGGER_STAGE(kMeshStage); | |
| mOutGlop->mesh.primitiveMode = GL_TRIANGLES; | |
| mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; | |
| mOutGlop->mesh.vertices = { | |
| 0, | |
| VertexAttribFlags::TextureCoord, | |
| &vertexData[0].x, &vertexData[0].u, nullptr, | |
| kTextureVertexStride }; | |
| mOutGlop->mesh.elementCount = elementCount; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount) { | |
| TRIGGER_STAGE(kMeshStage); | |
| mOutGlop->mesh.primitiveMode = GL_TRIANGLES; | |
| mOutGlop->mesh.indices = { 0, nullptr }; | |
| mOutGlop->mesh.vertices = { | |
| 0, | |
| VertexAttribFlags::TextureCoord | VertexAttribFlags::Color, | |
| &vertexData[0].x, &vertexData[0].u, &vertexData[0].r, | |
| kColorTextureVertexStride }; | |
| mOutGlop->mesh.elementCount = elementCount; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer) { | |
| TRIGGER_STAGE(kMeshStage); | |
| const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags(); | |
| bool alphaVertex = flags & VertexBuffer::kAlpha; | |
| bool indices = flags & VertexBuffer::kIndices; | |
| mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; | |
| mOutGlop->mesh.indices = { 0, vertexBuffer.getIndices() }; | |
| mOutGlop->mesh.vertices = { | |
| 0, | |
| alphaVertex ? VertexAttribFlags::Alpha : VertexAttribFlags::None, | |
| vertexBuffer.getBuffer(), nullptr, nullptr, | |
| alphaVertex ? kAlphaVertexStride : kVertexStride }; | |
| mOutGlop->mesh.elementCount = indices | |
| ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount(); | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setMeshPatchQuads(const Patch& patch) { | |
| TRIGGER_STAGE(kMeshStage); | |
| mOutGlop->mesh.primitiveMode = GL_TRIANGLES; | |
| mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; | |
| mOutGlop->mesh.vertices = { | |
| mCaches.patchCache.getMeshBuffer(), | |
| VertexAttribFlags::TextureCoord, | |
| (void*)patch.positionOffset, (void*)patch.textureOffset, nullptr, | |
| kTextureVertexStride }; | |
| mOutGlop->mesh.elementCount = patch.indexCount; | |
| return *this; | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // Fill | |
| //////////////////////////////////////////////////////////////////////////////// | |
| void GlopBuilder::setFill(int color, float alphaScale, | |
| SkBlendMode mode, Blend::ModeOrderSwap modeUsage, | |
| const SkShader* shader, const SkColorFilter* colorFilter) { | |
| if (mode != SkBlendMode::kClear) { | |
| if (!shader) { | |
| FloatColor c; | |
| c.set(color); | |
| c.r *= alphaScale; | |
| c.g *= alphaScale; | |
| c.b *= alphaScale; | |
| c.a *= alphaScale; | |
| mOutGlop->fill.color = c; | |
| } else { | |
| float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; | |
| mOutGlop->fill.color = { 1, 1, 1, alpha }; | |
| } | |
| } else { | |
| mOutGlop->fill.color = { 0, 0, 0, 1 }; | |
| } | |
| mOutGlop->blend = { GL_ZERO, GL_ZERO }; | |
| if (mOutGlop->fill.color.a < 1.0f | |
| || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha) | |
| || (mOutGlop->fill.texture.texture && mOutGlop->fill.texture.texture->blend) | |
| || mOutGlop->roundRectClipState | |
| || PaintUtils::isBlendedShader(shader) | |
| || PaintUtils::isBlendedColorFilter(colorFilter) | |
| || mode != SkBlendMode::kSrcOver) { | |
| if (CC_LIKELY(mode <= SkBlendMode::kScreen)) { | |
| Blend::getFactors(mode, modeUsage, | |
| &mOutGlop->blend.src, &mOutGlop->blend.dst); | |
| } else { | |
| // These blend modes are not supported by OpenGL directly and have | |
| // to be implemented using shaders. Since the shader will perform | |
| // the blending, don't enable GL blending off here | |
| // If the blend mode cannot be implemented using shaders, fall | |
| // back to the default SrcOver blend mode instead | |
| if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) { | |
| mDescription.framebufferMode = (SkXfermode::Mode)mode; | |
| mDescription.swapSrcDst = (modeUsage == Blend::ModeOrderSwap::Swap); | |
| // blending in shader, don't enable | |
| } else { | |
| // unsupported | |
| Blend::getFactors(SkBlendMode::kSrcOver, modeUsage, | |
| &mOutGlop->blend.src, &mOutGlop->blend.dst); | |
| } | |
| } | |
| } | |
| mShader = shader; // shader resolved in ::build() | |
| if (colorFilter) { | |
| SkColor color; | |
| SkXfermode::Mode xmode; | |
| SkScalar srcColorMatrix[20]; | |
| if (colorFilter->asColorMode(&color, &xmode)) { | |
| mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Blend; | |
| mDescription.colorMode = xmode; | |
| mOutGlop->fill.filter.color.set(color); | |
| } else if (colorFilter->asColorMatrix(srcColorMatrix)) { | |
| mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Matrix; | |
| float* colorMatrix = mOutGlop->fill.filter.matrix.matrix; | |
| memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float)); | |
| memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float)); | |
| memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float)); | |
| memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float)); | |
| // Skia uses the range [0..255] for the addition vector, but we need | |
| // the [0..1] range to apply the vector in GLSL | |
| float* colorVector = mOutGlop->fill.filter.matrix.vector; | |
| colorVector[0] = EOCF(srcColorMatrix[4] / 255.0f); | |
| colorVector[1] = EOCF(srcColorMatrix[9] / 255.0f); | |
| colorVector[2] = EOCF(srcColorMatrix[14] / 255.0f); | |
| colorVector[3] = srcColorMatrix[19] / 255.0f; // alpha is linear | |
| } else { | |
| LOG_ALWAYS_FATAL("unsupported ColorFilter"); | |
| } | |
| } else { | |
| mOutGlop->fill.filterMode = ProgramDescription::ColorFilterMode::None; | |
| } | |
| } | |
| GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, | |
| const int textureFillFlags, const SkPaint* paint, float alphaScale) { | |
| TRIGGER_STAGE(kFillStage); | |
| REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); | |
| GLenum filter = (textureFillFlags & TextureFillFlags::ForceFilter) | |
| ? GL_LINEAR : PaintUtils::getFilter(paint); | |
| mOutGlop->fill.texture = { &texture, | |
| GL_TEXTURE_2D, filter, GL_CLAMP_TO_EDGE, nullptr }; | |
| if (paint) { | |
| int color = paint->getColor(); | |
| SkShader* shader = paint->getShader(); | |
| if (!(textureFillFlags & TextureFillFlags::IsAlphaMaskTexture)) { | |
| // Texture defines color, so disable shaders, and reset all non-alpha color channels | |
| color |= 0x00FFFFFF; | |
| shader = nullptr; | |
| } | |
| setFill(color, alphaScale, | |
| paint->getBlendMode(), Blend::ModeOrderSwap::NoSwap, | |
| shader, paint->getColorFilter()); | |
| } else { | |
| mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale }; | |
| if (alphaScale < 1.0f | |
| || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha) | |
| || texture.blend | |
| || mOutGlop->roundRectClipState) { | |
| Blend::getFactors(SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap, | |
| &mOutGlop->blend.src, &mOutGlop->blend.dst); | |
| } else { | |
| mOutGlop->blend = { GL_ZERO, GL_ZERO }; | |
| } | |
| } | |
| if (textureFillFlags & TextureFillFlags::IsAlphaMaskTexture) { | |
| mDescription.modulate = mOutGlop->fill.color.isNotBlack(); | |
| mDescription.hasAlpha8Texture = true; | |
| } else { | |
| mDescription.modulate = mOutGlop->fill.color.a < 1.0f; | |
| } | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale, bool shadowInterp) { | |
| TRIGGER_STAGE(kFillStage); | |
| REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); | |
| if (CC_LIKELY(!shadowInterp)) { | |
| mOutGlop->fill.texture = { | |
| nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; | |
| } else { | |
| mOutGlop->fill.texture = { | |
| mCaches.textureState().getShadowLutTexture(), GL_TEXTURE_2D, | |
| GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; | |
| } | |
| setFill(paint.getColor(), alphaScale, | |
| paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, | |
| paint.getShader(), paint.getColorFilter()); | |
| mDescription.useShadowAlphaInterp = shadowInterp; | |
| mDescription.modulate = mOutGlop->fill.color.a < 1.0f; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture, | |
| const SkPaint& paint, float alphaScale) { | |
| TRIGGER_STAGE(kFillStage); | |
| REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); | |
| //specify invalid filter/clamp, since these are always static for PathTextures | |
| mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; | |
| setFill(paint.getColor(), alphaScale, | |
| paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, | |
| paint.getShader(), paint.getColorFilter()); | |
| mDescription.hasAlpha8Texture = true; | |
| mDescription.modulate = mOutGlop->fill.color.isNotBlack(); | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor, | |
| const SkPaint& paint, float alphaScale) { | |
| TRIGGER_STAGE(kFillStage); | |
| REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); | |
| //specify invalid filter/clamp, since these are always static for ShadowTextures | |
| mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; | |
| const int ALPHA_BITMASK = SK_ColorBLACK; | |
| const int COLOR_BITMASK = ~ALPHA_BITMASK; | |
| if ((shadowColor & ALPHA_BITMASK) == ALPHA_BITMASK) { | |
| // shadow color is fully opaque: override its alpha with that of paint | |
| shadowColor &= paint.getColor() | COLOR_BITMASK; | |
| } | |
| setFill(shadowColor, alphaScale, | |
| paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, | |
| paint.getShader(), paint.getColorFilter()); | |
| mDescription.hasAlpha8Texture = true; | |
| mDescription.modulate = mOutGlop->fill.color.isNotBlack(); | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setFillBlack() { | |
| TRIGGER_STAGE(kFillStage); | |
| REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); | |
| mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; | |
| setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap, | |
| nullptr, nullptr); | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setFillClear() { | |
| TRIGGER_STAGE(kFillStage); | |
| REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); | |
| mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; | |
| setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kClear, Blend::ModeOrderSwap::NoSwap, | |
| nullptr, nullptr); | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter, | |
| float alpha, SkBlendMode mode, Blend::ModeOrderSwap modeUsage) { | |
| TRIGGER_STAGE(kFillStage); | |
| REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); | |
| mOutGlop->fill.texture = { &texture, | |
| GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr }; | |
| setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter); | |
| mDescription.modulate = mOutGlop->fill.color.a < 1.0f; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) { | |
| TRIGGER_STAGE(kFillStage); | |
| REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); | |
| mOutGlop->fill.texture = { &(layer.getTexture()), | |
| layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() }; | |
| setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap, | |
| nullptr, layer.getColorFilter()); | |
| mDescription.modulate = mOutGlop->fill.color.a < 1.0f; | |
| mDescription.hasTextureTransform = true; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& textureTransform) { | |
| TRIGGER_STAGE(kFillStage); | |
| REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); | |
| mOutGlop->fill.texture = { &texture, | |
| GL_TEXTURE_EXTERNAL_OES, GL_LINEAR, GL_CLAMP_TO_EDGE, | |
| &textureTransform }; | |
| setFill(SK_ColorWHITE, 1.0f, SkBlendMode::kSrc, Blend::ModeOrderSwap::NoSwap, | |
| nullptr, nullptr); | |
| mDescription.modulate = mOutGlop->fill.color.a < 1.0f; | |
| mDescription.hasTextureTransform = true; | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setGammaCorrection(bool enabled) { | |
| REQUIRE_STAGES(kFillStage); | |
| mDescription.hasGammaCorrection = enabled; | |
| return *this; | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // Transform | |
| //////////////////////////////////////////////////////////////////////////////// | |
| GlopBuilder& GlopBuilder::setTransform(const Matrix4& canvas, const int transformFlags) { | |
| TRIGGER_STAGE(kTransformStage); | |
| mOutGlop->transform.canvas = canvas; | |
| mOutGlop->transform.transformFlags = transformFlags; | |
| return *this; | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // ModelView | |
| //////////////////////////////////////////////////////////////////////////////// | |
| GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) { | |
| TRIGGER_STAGE(kModelViewStage); | |
| mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f); | |
| mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f); | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination) { | |
| TRIGGER_STAGE(kModelViewStage); | |
| REQUIRE_STAGES(kTransformStage | kFillStage); | |
| float left = destination.left; | |
| float top = destination.top; | |
| const Matrix4& meshTransform = mOutGlop->transform.meshTransform(); | |
| if (CC_LIKELY(meshTransform.isPureTranslate())) { | |
| // snap by adjusting the model view matrix | |
| const float translateX = meshTransform.getTranslateX(); | |
| const float translateY = meshTransform.getTranslateY(); | |
| left = (int) floorf(left + translateX + 0.5f) - translateX; | |
| top = (int) floorf(top + translateY + 0.5f) - translateY; | |
| mOutGlop->fill.texture.filter = GL_NEAREST; | |
| } | |
| mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f); | |
| mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f); | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) { | |
| TRIGGER_STAGE(kModelViewStage); | |
| mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f); | |
| return *this; | |
| } | |
| GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source) { | |
| TRIGGER_STAGE(kModelViewStage); | |
| REQUIRE_STAGES(kTransformStage | kFillStage); | |
| const Matrix4& meshTransform = mOutGlop->transform.meshTransform(); | |
| if (CC_LIKELY(meshTransform.isPureTranslate())) { | |
| // snap by adjusting the model view matrix | |
| const float translateX = meshTransform.getTranslateX(); | |
| const float translateY = meshTransform.getTranslateY(); | |
| offsetX = (int) floorf(offsetX + translateX + source.left + 0.5f) - translateX - source.left; | |
| offsetY = (int) floorf(offsetY + translateY + source.top + 0.5f) - translateY - source.top; | |
| mOutGlop->fill.texture.filter = GL_NEAREST; | |
| } | |
| mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f); | |
| return *this; | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // RoundRectClip | |
| //////////////////////////////////////////////////////////////////////////////// | |
| GlopBuilder& GlopBuilder::setRoundRectClipState(const RoundRectClipState* roundRectClipState) { | |
| TRIGGER_STAGE(kRoundRectClipStage); | |
| mOutGlop->roundRectClipState = roundRectClipState; | |
| mDescription.hasRoundRectClip = roundRectClipState != nullptr; | |
| return *this; | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // Build | |
| //////////////////////////////////////////////////////////////////////////////// | |
| void verify(const ProgramDescription& description, const Glop& glop) { | |
| if (glop.fill.texture.texture != nullptr) { | |
| LOG_ALWAYS_FATAL_IF(((description.hasTexture && description.hasExternalTexture) | |
| || (!description.hasTexture | |
| && !description.hasExternalTexture | |
| && !description.useShadowAlphaInterp) | |
| || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0 | |
| && !description.useShadowAlphaInterp)), | |
| "Texture %p, hT%d, hET %d, attribFlags %x", | |
| glop.fill.texture.texture, | |
| description.hasTexture, description.hasExternalTexture, | |
| glop.mesh.vertices.attribFlags); | |
| } else { | |
| LOG_ALWAYS_FATAL_IF((description.hasTexture | |
| || description.hasExternalTexture | |
| || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) != 0)), | |
| "No texture, hT%d, hET %d, attribFlags %x", | |
| description.hasTexture, description.hasExternalTexture, | |
| glop.mesh.vertices.attribFlags); | |
| } | |
| if ((glop.mesh.vertices.attribFlags & VertexAttribFlags::Alpha) | |
| && glop.mesh.vertices.bufferObject) { | |
| LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible"); | |
| } | |
| if (description.hasTextureTransform != (glop.fill.texture.textureTransform != nullptr)) { | |
| LOG_ALWAYS_FATAL("Texture transform incorrectly specified"); | |
| } | |
| } | |
| void GlopBuilder::build() { | |
| REQUIRE_STAGES(kAllStages); | |
| if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) { | |
| if (mOutGlop->fill.texture.target == GL_TEXTURE_2D) { | |
| mDescription.hasTexture = true; | |
| } else { | |
| mDescription.hasExternalTexture = true; | |
| } | |
| } | |
| mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Color; | |
| mDescription.hasVertexAlpha = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha; | |
| // Enable debug highlight when what we're about to draw is tested against | |
| // the stencil buffer and if stencil highlight debugging is on | |
| mDescription.hasDebugHighlight = !Properties::debugOverdraw | |
| && Properties::debugStencilClip == StencilClipDebug::ShowHighlight | |
| && mRenderState.stencil().isTestEnabled(); | |
| // serialize shader info into ShaderData | |
| GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0; | |
| if (CC_LIKELY(!mShader)) { | |
| mOutGlop->fill.skiaShaderData.skiaShaderType = kNone_SkiaShaderType; | |
| } else { | |
| Matrix4 shaderMatrix; | |
| if (mOutGlop->transform.transformFlags & TransformFlags::MeshIgnoresCanvasTransform) { | |
| // canvas level transform was built into the modelView and geometry, | |
| // so the shader matrix must reverse this | |
| shaderMatrix.loadInverse(mOutGlop->transform.canvas); | |
| shaderMatrix.multiply(mOutGlop->transform.modelView); | |
| } else { | |
| shaderMatrix = mOutGlop->transform.modelView; | |
| } | |
| SkiaShader::store(mCaches, *mShader, shaderMatrix, | |
| &textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData)); | |
| } | |
| // duplicates ProgramCache's definition of color uniform presence | |
| const bool singleColor = !mDescription.hasTexture | |
| && !mDescription.hasExternalTexture | |
| && !mDescription.hasGradient | |
| && !mDescription.hasBitmap; | |
| mOutGlop->fill.colorEnabled = mDescription.modulate || singleColor; | |
| verify(mDescription, *mOutGlop); | |
| // Final step: populate program and map bounds into render target space | |
| mOutGlop->fill.program = mCaches.programCache.get(mDescription); | |
| } | |
| void GlopBuilder::dump(const Glop& glop) { | |
| ALOGD("Glop Mesh"); | |
| const Glop::Mesh& mesh = glop.mesh; | |
| ALOGD(" primitive mode: %d", mesh.primitiveMode); | |
| ALOGD(" indices: buffer obj %x, indices %p", mesh.indices.bufferObject, mesh.indices.indices); | |
| const Glop::Mesh::Vertices& vertices = glop.mesh.vertices; | |
| ALOGD(" vertices: buffer obj %x, flags %x, pos %p, tex %p, clr %p, stride %d", | |
| vertices.bufferObject, vertices.attribFlags, | |
| vertices.position, vertices.texCoord, vertices.color, vertices.stride); | |
| ALOGD(" element count: %d", mesh.elementCount); | |
| ALOGD("Glop Fill"); | |
| const Glop::Fill& fill = glop.fill; | |
| ALOGD(" program %p", fill.program); | |
| if (fill.texture.texture) { | |
| ALOGD(" texture %p, target %d, filter %d, clamp %d", | |
| fill.texture.texture, fill.texture.target, fill.texture.filter, fill.texture.clamp); | |
| if (fill.texture.textureTransform) { | |
| fill.texture.textureTransform->dump("texture transform"); | |
| } | |
| } | |
| ALOGD_IF(fill.colorEnabled, " color (argb) %.2f %.2f %.2f %.2f", | |
| fill.color.a, fill.color.r, fill.color.g, fill.color.b); | |
| ALOGD_IF(fill.filterMode != ProgramDescription::ColorFilterMode::None, | |
| " filterMode %d", (int)fill.filterMode); | |
| ALOGD_IF(fill.skiaShaderData.skiaShaderType, " shader type %d", | |
| fill.skiaShaderData.skiaShaderType); | |
| ALOGD("Glop transform"); | |
| glop.transform.modelView.dump(" model view"); | |
| glop.transform.canvas.dump(" canvas"); | |
| ALOGD_IF(glop.transform.transformFlags, " transformFlags 0x%x", glop.transform.transformFlags); | |
| ALOGD_IF(glop.roundRectClipState, "Glop RRCS %p", glop.roundRectClipState); | |
| ALOGD("Glop blend %d %d", glop.blend.src, glop.blend.dst); | |
| } | |
| } /* namespace uirenderer */ | |
| } /* namespace android */ |