| /* |
| * 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 "Matrix.h" |
| #include "Patch.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> |
| |
| namespace android { |
| namespace uirenderer { |
| |
| #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") |
| |
| 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::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(0, 0, 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::setMeshTexturedMesh(TextureVertex* vertexData, int elementCount) { |
| TRIGGER_STAGE(kMeshStage); |
| |
| mOutGlop->mesh.primitiveMode = GL_TRIANGLES; |
| mOutGlop->mesh.indices = { 0, 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, bool shadowInterp) { |
| 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(); |
| |
| mDescription.useShadowAlphaInterp = shadowInterp; |
| 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, |
| SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage, |
| const SkShader* shader, const SkColorFilter* colorFilter) { |
| if (mode != SkXfermode::kClear_Mode) { |
| float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; |
| if (!shader) { |
| float colorScale = alpha / 255.0f; |
| mOutGlop->fill.color = { |
| colorScale * SkColorGetR(color), |
| colorScale * SkColorGetG(color), |
| colorScale * SkColorGetB(color), |
| alpha |
| }; |
| } else { |
| 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 != SkXfermode::kSrcOver_Mode) { |
| if (CC_LIKELY(mode <= SkXfermode::kScreen_Mode)) { |
| 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 = mode; |
| mDescription.swapSrcDst = (modeUsage == Blend::ModeOrderSwap::Swap); |
| // blending in shader, don't enable |
| } else { |
| // unsupported |
| Blend::getFactors(SkXfermode::kSrcOver_Mode, modeUsage, |
| &mOutGlop->blend.src, &mOutGlop->blend.dst); |
| } |
| } |
| } |
| mShader = shader; // shader resolved in ::build() |
| |
| if (colorFilter) { |
| SkColor color; |
| SkXfermode::Mode mode; |
| SkScalar srcColorMatrix[20]; |
| if (colorFilter->asColorMode(&color, &mode)) { |
| mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorBlend; |
| mDescription.colorMode = mode; |
| |
| const float alpha = SkColorGetA(color) / 255.0f; |
| float colorScale = alpha / 255.0f; |
| mOutGlop->fill.filter.color = { |
| colorScale * SkColorGetR(color), |
| colorScale * SkColorGetG(color), |
| colorScale * SkColorGetB(color), |
| alpha, |
| }; |
| } else if (colorFilter->asColorMatrix(srcColorMatrix)) { |
| mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorMatrix; |
| |
| 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] = srcColorMatrix[4] / 255.0f; |
| colorVector[1] = srcColorMatrix[9] / 255.0f; |
| colorVector[2] = srcColorMatrix[14] / 255.0f; |
| colorVector[3] = srcColorMatrix[19] / 255.0f; |
| } else { |
| LOG_ALWAYS_FATAL("unsupported ColorFilter"); |
| } |
| } else { |
| mOutGlop->fill.filterMode = ProgramDescription::kColorNone; |
| } |
| } |
| |
| GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, |
| const int textureFillFlags, const SkPaint* paint, float alphaScale) { |
| TRIGGER_STAGE(kFillStage); |
| REQUIRE_STAGES(kMeshStage); |
| |
| 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, |
| PaintUtils::getXfermode(paint->getXfermode()), 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(SkXfermode::kSrcOver_Mode, 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) { |
| TRIGGER_STAGE(kFillStage); |
| REQUIRE_STAGES(kMeshStage); |
| |
| mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; |
| |
| setFill(paint.getColor(), alphaScale, |
| PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap, |
| paint.getShader(), paint.getColorFilter()); |
| 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); |
| |
| //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, |
| PaintUtils::getXfermode(paint.getXfermode()), 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); |
| |
| //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, |
| PaintUtils::getXfermode(paint.getXfermode()), 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); |
| |
| mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; |
| setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap, |
| nullptr, nullptr); |
| return *this; |
| } |
| |
| GlopBuilder& GlopBuilder::setFillClear() { |
| TRIGGER_STAGE(kFillStage); |
| REQUIRE_STAGES(kMeshStage); |
| |
| mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; |
| setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, Blend::ModeOrderSwap::NoSwap, |
| nullptr, nullptr); |
| return *this; |
| } |
| |
| GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter, |
| float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage) { |
| TRIGGER_STAGE(kFillStage); |
| REQUIRE_STAGES(kMeshStage); |
| |
| mOutGlop->fill.texture = { &texture, |
| GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr }; |
| mOutGlop->fill.color = { alpha, alpha, alpha, alpha }; |
| |
| 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); |
| |
| mOutGlop->fill.texture = { &(layer.getTexture()), |
| layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() }; |
| mOutGlop->fill.color = { alpha, alpha, alpha, alpha }; |
| |
| 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; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Transform |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| void GlopBuilder::setTransform(const Matrix4& ortho, const Matrix4& canvas, |
| const int transformFlags) { |
| TRIGGER_STAGE(kTransformStage); |
| |
| mOutGlop->transform.ortho.load(ortho); |
| mOutGlop->transform.canvas.load(canvas); |
| mOutGlop->transform.transformFlags = transformFlags; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // 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); |
| mOutGlop->bounds = destination; |
| 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); |
| mOutGlop->bounds = destination; |
| return *this; |
| } |
| |
| GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) { |
| TRIGGER_STAGE(kModelViewStage); |
| |
| mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f); |
| mOutGlop->bounds = source; |
| mOutGlop->bounds.translate(offsetX, offsetY); |
| 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); |
| mOutGlop->bounds = source; |
| mOutGlop->bounds.translate(offsetX, offsetY); |
| 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) |
| || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0)), |
| "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.load(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); |
| mOutGlop->transform.meshTransform().mapRect(mOutGlop->bounds); |
| } |
| |
| } /* namespace uirenderer */ |
| } /* namespace android */ |