GlopBuilder.cpp revision 8820fd1d82acaefda98ae73ccf61413d5044f9f3
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#include "GlopBuilder.h"
17
18#include "Caches.h"
19#include "Glop.h"
20#include "Matrix.h"
21#include "Patch.h"
22#include "renderstate/MeshState.h"
23#include "renderstate/RenderState.h"
24#include "SkiaShader.h"
25#include "Texture.h"
26#include "utils/PaintUtils.h"
27#include "VertexBuffer.h"
28
29#include <GLES2/gl2.h>
30#include <SkPaint.h>
31
32namespace android {
33namespace uirenderer {
34
35#define TRIGGER_STAGE(stageFlag) \
36    LOG_ALWAYS_FATAL_IF((stageFlag) & mStageFlags, "Stage %d cannot be run twice", (stageFlag)); \
37    mStageFlags = static_cast<StageFlags>(mStageFlags | (stageFlag))
38
39#define REQUIRE_STAGES(requiredFlags) \
40    LOG_ALWAYS_FATAL_IF((mStageFlags & (requiredFlags)) != (requiredFlags), \
41            "not prepared for current stage")
42
43static void setUnitQuadTextureCoords(Rect uvs, TextureVertex* quadVertex) {
44    quadVertex[0] = {0, 0, uvs.left, uvs.top};
45    quadVertex[1] = {1, 0, uvs.right, uvs.top};
46    quadVertex[2] = {0, 1, uvs.left, uvs.bottom};
47    quadVertex[3] = {1, 1, uvs.right, uvs.bottom};
48}
49
50GlopBuilder::GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop)
51        : mRenderState(renderState)
52        , mCaches(caches)
53        , mShader(nullptr)
54        , mOutGlop(outGlop) {
55    mStageFlags = kInitialStage;
56}
57
58////////////////////////////////////////////////////////////////////////////////
59// Mesh
60////////////////////////////////////////////////////////////////////////////////
61
62GlopBuilder& GlopBuilder::setMeshUnitQuad() {
63    TRIGGER_STAGE(kMeshStage);
64
65    mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
66    mOutGlop->mesh.indices = { 0, nullptr };
67    mOutGlop->mesh.vertices = {
68            mRenderState.meshState().getUnitQuadVBO(),
69            VertexAttribFlags::kNone,
70            nullptr, nullptr, nullptr,
71            kTextureVertexStride };
72    mOutGlop->mesh.elementCount = 4;
73    return *this;
74}
75
76GlopBuilder& GlopBuilder::setMeshTexturedUnitQuad(const UvMapper* uvMapper) {
77    if (uvMapper) {
78        // can't use unit quad VBO, so build UV vertices manually
79        return setMeshTexturedUvQuad(uvMapper, Rect(0, 0, 1, 1));
80    }
81
82    TRIGGER_STAGE(kMeshStage);
83
84    mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
85    mOutGlop->mesh.indices = { 0, nullptr };
86    mOutGlop->mesh.vertices = {
87            mRenderState.meshState().getUnitQuadVBO(),
88            VertexAttribFlags::kTextureCoord,
89            nullptr, (const void*) kMeshTextureOffset, nullptr,
90            kTextureVertexStride };
91    mOutGlop->mesh.elementCount = 4;
92    return *this;
93}
94
95GlopBuilder& GlopBuilder::setMeshTexturedUvQuad(const UvMapper* uvMapper, Rect uvs) {
96    TRIGGER_STAGE(kMeshStage);
97
98    if (CC_UNLIKELY(uvMapper)) {
99        uvMapper->map(uvs);
100    }
101    setUnitQuadTextureCoords(uvs, &mOutGlop->mesh.mappedVertices[0]);
102
103    const TextureVertex* textureVertex = mOutGlop->mesh.mappedVertices;
104    mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
105    mOutGlop->mesh.indices = { 0, nullptr };
106    mOutGlop->mesh.vertices = {
107            0,
108            VertexAttribFlags::kTextureCoord,
109            &textureVertex[0].x, &textureVertex[0].u, nullptr,
110            kTextureVertexStride };
111    mOutGlop->mesh.elementCount = 4;
112    return *this;
113}
114
115GlopBuilder& GlopBuilder::setMeshIndexedQuads(Vertex* vertexData, int quadCount) {
116    TRIGGER_STAGE(kMeshStage);
117
118    mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
119    mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
120    mOutGlop->mesh.vertices = {
121            0,
122            VertexAttribFlags::kNone,
123            vertexData, nullptr, nullptr,
124            kVertexStride };
125    mOutGlop->mesh.elementCount = 6 * quadCount;
126    return *this;
127}
128
129GlopBuilder& GlopBuilder::setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount) {
130    TRIGGER_STAGE(kMeshStage);
131
132    mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
133    mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
134    mOutGlop->mesh.vertices = {
135            0,
136            VertexAttribFlags::kTextureCoord,
137            &vertexData[0].x, &vertexData[0].u, nullptr,
138            kTextureVertexStride };
139    mOutGlop->mesh.elementCount = elementCount;
140    return *this;
141}
142
143GlopBuilder& GlopBuilder::setMeshTexturedMesh(TextureVertex* vertexData, int elementCount) {
144    TRIGGER_STAGE(kMeshStage);
145
146    mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
147    mOutGlop->mesh.indices = { 0, nullptr };
148    mOutGlop->mesh.vertices = {
149            0,
150            VertexAttribFlags::kTextureCoord,
151            &vertexData[0].x, &vertexData[0].u, nullptr,
152            kTextureVertexStride };
153    mOutGlop->mesh.elementCount = elementCount;
154    return *this;
155}
156
157GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount) {
158    TRIGGER_STAGE(kMeshStage);
159
160    mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
161    mOutGlop->mesh.indices = { 0, nullptr };
162    mOutGlop->mesh.vertices = {
163            0,
164            static_cast<VertexAttribFlags>(VertexAttribFlags::kTextureCoord | VertexAttribFlags::kColor),
165            &vertexData[0].x, &vertexData[0].u, &vertexData[0].r,
166            kColorTextureVertexStride };
167    mOutGlop->mesh.elementCount = elementCount;
168    return *this;
169}
170
171GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp) {
172    TRIGGER_STAGE(kMeshStage);
173
174    const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags();
175
176    bool alphaVertex = flags & VertexBuffer::kAlpha;
177    bool indices = flags & VertexBuffer::kIndices;
178
179    mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
180    mOutGlop->mesh.indices = { 0, vertexBuffer.getIndices() };
181    mOutGlop->mesh.vertices = {
182            0,
183            alphaVertex ? VertexAttribFlags::kAlpha : VertexAttribFlags::kNone,
184            vertexBuffer.getBuffer(), nullptr, nullptr,
185            alphaVertex ? kAlphaVertexStride : kVertexStride };
186    mOutGlop->mesh.elementCount = indices
187                ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount();
188
189    mDescription.useShadowAlphaInterp = shadowInterp;
190    return *this;
191}
192
193GlopBuilder& GlopBuilder::setMeshPatchQuads(const Patch& patch) {
194    TRIGGER_STAGE(kMeshStage);
195
196    mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
197    mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
198    mOutGlop->mesh.vertices = {
199            mCaches.patchCache.getMeshBuffer(),
200            VertexAttribFlags::kTextureCoord,
201            (void*)patch.positionOffset, (void*)patch.textureOffset, nullptr,
202            kTextureVertexStride };
203    mOutGlop->mesh.elementCount = patch.indexCount;
204    return *this;
205}
206
207////////////////////////////////////////////////////////////////////////////////
208// Fill
209////////////////////////////////////////////////////////////////////////////////
210
211void GlopBuilder::setFill(int color, float alphaScale, SkXfermode::Mode mode,
212        const SkShader* shader, const SkColorFilter* colorFilter) {
213    if (mode != SkXfermode::kClear_Mode) {
214        float alpha = (SkColorGetA(color) / 255.0f) * alphaScale;
215        if (!shader) {
216            float colorScale = alpha / 255.0f;
217            mOutGlop->fill.color = {
218                    colorScale * SkColorGetR(color),
219                    colorScale * SkColorGetG(color),
220                    colorScale * SkColorGetB(color),
221                    alpha
222            };
223        } else {
224            mOutGlop->fill.color = { 1, 1, 1, alpha };
225        }
226    } else {
227        mOutGlop->fill.color = { 0, 0, 0, 1 };
228    }
229    const bool SWAP_SRC_DST = false;
230
231    mOutGlop->blend = { GL_ZERO, GL_ZERO };
232    if (mOutGlop->fill.color.a < 1.0f
233            || (mOutGlop->mesh.vertices.flags & VertexAttribFlags::kAlpha)
234            || (mOutGlop->fill.texture.texture && mOutGlop->fill.texture.texture->blend)
235            || mOutGlop->roundRectClipState
236            || PaintUtils::isBlendedShader(shader)
237            || PaintUtils::isBlendedColorFilter(colorFilter)
238            || mode != SkXfermode::kSrcOver_Mode) {
239        if (CC_LIKELY(mode <= SkXfermode::kScreen_Mode)) {
240            Blend::getFactors(mode, SWAP_SRC_DST,
241                    &mOutGlop->blend.src, &mOutGlop->blend.dst);
242        } else {
243            // These blend modes are not supported by OpenGL directly and have
244            // to be implemented using shaders. Since the shader will perform
245            // the blending, don't enable GL blending off here
246            // If the blend mode cannot be implemented using shaders, fall
247            // back to the default SrcOver blend mode instead
248            if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) {
249                mDescription.framebufferMode = mode;
250                mDescription.swapSrcDst = SWAP_SRC_DST;
251                // blending in shader, don't enable
252            } else {
253                // unsupported
254                Blend::getFactors(SkXfermode::kSrcOver_Mode, SWAP_SRC_DST,
255                        &mOutGlop->blend.src, &mOutGlop->blend.dst);
256            }
257        }
258    }
259    mShader = shader; // shader resolved in ::build()
260
261    if (colorFilter) {
262        SkColor color;
263        SkXfermode::Mode mode;
264        SkScalar srcColorMatrix[20];
265        if (colorFilter->asColorMode(&color, &mode)) {
266            mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorBlend;
267            mDescription.colorMode = mode;
268
269            const float alpha = SkColorGetA(color) / 255.0f;
270            float colorScale = alpha / 255.0f;
271            mOutGlop->fill.filter.color = {
272                    colorScale * SkColorGetR(color),
273                    colorScale * SkColorGetG(color),
274                    colorScale * SkColorGetB(color),
275                    alpha,
276            };
277        } else if (colorFilter->asColorMatrix(srcColorMatrix)) {
278            mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorMatrix;
279
280            float* colorMatrix = mOutGlop->fill.filter.matrix.matrix;
281            memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float));
282            memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float));
283            memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float));
284            memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float));
285
286            // Skia uses the range [0..255] for the addition vector, but we need
287            // the [0..1] range to apply the vector in GLSL
288            float* colorVector = mOutGlop->fill.filter.matrix.vector;
289            colorVector[0] = srcColorMatrix[4] / 255.0f;
290            colorVector[1] = srcColorMatrix[9] / 255.0f;
291            colorVector[2] = srcColorMatrix[14] / 255.0f;
292            colorVector[3] = srcColorMatrix[19] / 255.0f;
293        } else {
294            LOG_ALWAYS_FATAL("unsupported ColorFilter");
295        }
296    } else {
297        mOutGlop->fill.filterMode = ProgramDescription::kColorNone;
298    }
299}
300
301GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, int textureFillFlags,
302        const SkPaint* paint, float alphaScale) {
303    TRIGGER_STAGE(kFillStage);
304    REQUIRE_STAGES(kMeshStage);
305
306    GLenum filter = (textureFillFlags & TextureFillFlags::kForceFilter)
307            ? GL_LINEAR : PaintUtils::getFilter(paint);
308    mOutGlop->fill.texture = { &texture,
309            GL_TEXTURE_2D, filter, GL_CLAMP_TO_EDGE, nullptr };
310
311    if (paint) {
312        int color = paint->getColor();
313        SkShader* shader = paint->getShader();
314
315        if (!(textureFillFlags & TextureFillFlags::kIsAlphaMaskTexture)) {
316            // Texture defines color, so disable shaders, and reset all non-alpha color channels
317            color |= 0x00FFFFFF;
318            shader = nullptr;
319        }
320        setFill(color, alphaScale, PaintUtils::getXfermode(paint->getXfermode()),
321                shader, paint->getColorFilter());
322    } else {
323        mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale };
324
325        const bool SWAP_SRC_DST = false;
326        if (alphaScale < 1.0f
327                || (mOutGlop->mesh.vertices.flags & VertexAttribFlags::kAlpha)
328                || texture.blend
329                || mOutGlop->roundRectClipState) {
330            Blend::getFactors(SkXfermode::kSrcOver_Mode, SWAP_SRC_DST,
331                    &mOutGlop->blend.src, &mOutGlop->blend.dst);
332        } else {
333            mOutGlop->blend = { GL_ZERO, GL_ZERO };
334        }
335    }
336
337    if (textureFillFlags & TextureFillFlags::kIsAlphaMaskTexture) {
338        mDescription.modulate = mOutGlop->fill.color.isNotBlack();
339        mDescription.hasAlpha8Texture = true;
340    } else {
341        mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
342    }
343    return *this;
344}
345
346GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale) {
347    TRIGGER_STAGE(kFillStage);
348    REQUIRE_STAGES(kMeshStage);
349
350    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
351
352    setFill(paint.getColor(), alphaScale, PaintUtils::getXfermode(paint.getXfermode()),
353            paint.getShader(), paint.getColorFilter());
354    mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
355    return *this;
356}
357
358GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture,
359        const SkPaint& paint, float alphaScale) {
360    TRIGGER_STAGE(kFillStage);
361    REQUIRE_STAGES(kMeshStage);
362
363    //specify invalid filter/clamp, since these are always static for PathTextures
364    mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
365
366    setFill(paint.getColor(), alphaScale, PaintUtils::getXfermode(paint.getXfermode()),
367            paint.getShader(), paint.getColorFilter());
368
369    mDescription.hasAlpha8Texture = true;
370    mDescription.modulate = mOutGlop->fill.color.isNotBlack();
371    return *this;
372}
373
374GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor,
375        const SkPaint& paint, float alphaScale) {
376    TRIGGER_STAGE(kFillStage);
377    REQUIRE_STAGES(kMeshStage);
378
379    //specify invalid filter/clamp, since these are always static for ShadowTextures
380    mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
381
382    const int ALPHA_BITMASK = SK_ColorBLACK;
383    const int COLOR_BITMASK = ~ALPHA_BITMASK;
384    if ((shadowColor & ALPHA_BITMASK) == ALPHA_BITMASK) {
385        // shadow color is fully opaque: override its alpha with that of paint
386        shadowColor &= paint.getColor() | COLOR_BITMASK;
387    }
388
389    setFill(shadowColor, alphaScale, PaintUtils::getXfermode(paint.getXfermode()),
390            paint.getShader(), paint.getColorFilter());
391
392    mDescription.hasAlpha8Texture = true;
393    mDescription.modulate = mOutGlop->fill.color.isNotBlack();
394    return *this;
395}
396
397GlopBuilder& GlopBuilder::setFillBlack() {
398    TRIGGER_STAGE(kFillStage);
399    REQUIRE_STAGES(kMeshStage);
400
401    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
402    setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, nullptr, nullptr);
403    return *this;
404}
405
406GlopBuilder& GlopBuilder::setFillClear() {
407    TRIGGER_STAGE(kFillStage);
408    REQUIRE_STAGES(kMeshStage);
409
410    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
411    setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, nullptr, nullptr);
412    return *this;
413}
414
415GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
416        float alpha, SkXfermode::Mode mode) {
417    TRIGGER_STAGE(kFillStage);
418    REQUIRE_STAGES(kMeshStage);
419
420    mOutGlop->fill.texture = { &texture,
421            GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
422    mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
423
424    setFill(SK_ColorWHITE, alpha, mode, nullptr, colorFilter);
425
426    mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
427    return *this;
428}
429
430GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
431    TRIGGER_STAGE(kFillStage);
432    REQUIRE_STAGES(kMeshStage);
433
434    mOutGlop->fill.texture = { &(layer.getTexture()),
435            layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
436    mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
437
438    setFill(SK_ColorWHITE, alpha, layer.getMode(), nullptr, layer.getColorFilter());
439
440    mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
441    mDescription.hasTextureTransform = true;
442    return *this;
443}
444
445////////////////////////////////////////////////////////////////////////////////
446// Transform
447////////////////////////////////////////////////////////////////////////////////
448
449GlopBuilder& GlopBuilder::setTransform(const Matrix4& ortho,
450        const Matrix4& transform, bool fudgingOffset) {
451    TRIGGER_STAGE(kTransformStage);
452
453    mOutGlop->transform.ortho.load(ortho);
454    mOutGlop->transform.canvas.load(transform);
455    mOutGlop->transform.fudgingOffset = fudgingOffset;
456    return *this;
457}
458
459////////////////////////////////////////////////////////////////////////////////
460// ModelView
461////////////////////////////////////////////////////////////////////////////////
462
463GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) {
464    TRIGGER_STAGE(kModelViewStage);
465
466    mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
467    mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
468    mOutGlop->bounds = destination;
469    return *this;
470}
471
472GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination) {
473    TRIGGER_STAGE(kModelViewStage);
474    REQUIRE_STAGES(kTransformStage | kFillStage);
475
476    float left = destination.left;
477    float top = destination.top;
478
479    const Matrix4& canvasTransform = mOutGlop->transform.canvas;
480    if (CC_LIKELY(canvasTransform.isPureTranslate())) {
481        // snap by adjusting the model view matrix
482        const float translateX = canvasTransform.getTranslateX();
483        const float translateY = canvasTransform.getTranslateY();
484
485        left = (int) floorf(left + translateX + 0.5f) - translateX;
486        top = (int) floorf(top + translateY + 0.5f) - translateY;
487        mOutGlop->fill.texture.filter = GL_NEAREST;
488    }
489
490    mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f);
491    mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
492    mOutGlop->bounds = destination;
493    return *this;
494}
495
496GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) {
497    TRIGGER_STAGE(kModelViewStage);
498
499    mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
500    mOutGlop->bounds = source;
501    mOutGlop->bounds.translate(offsetX, offsetY);
502    return *this;
503}
504
505GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source) {
506    TRIGGER_STAGE(kModelViewStage);
507    REQUIRE_STAGES(kTransformStage | kFillStage);
508
509    const Matrix4& canvasTransform = mOutGlop->transform.canvas;
510    if (CC_LIKELY(canvasTransform.isPureTranslate())) {
511        // snap by adjusting the model view matrix
512        const float translateX = canvasTransform.getTranslateX();
513        const float translateY = canvasTransform.getTranslateY();
514
515        offsetX = (int) floorf(offsetX + translateX + source.left + 0.5f) - translateX - source.left;
516        offsetY = (int) floorf(offsetY + translateY + source.top + 0.5f) - translateY - source.top;
517        mOutGlop->fill.texture.filter = GL_NEAREST;
518    }
519
520    mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
521    mOutGlop->bounds = source;
522    mOutGlop->bounds.translate(offsetX, offsetY);
523    return *this;
524}
525
526////////////////////////////////////////////////////////////////////////////////
527// RoundRectClip
528////////////////////////////////////////////////////////////////////////////////
529
530GlopBuilder& GlopBuilder::setRoundRectClipState(const RoundRectClipState* roundRectClipState) {
531    TRIGGER_STAGE(kRoundRectClipStage);
532
533    mOutGlop->roundRectClipState = roundRectClipState;
534    mDescription.hasRoundRectClip = roundRectClipState != nullptr;
535    return *this;
536}
537
538////////////////////////////////////////////////////////////////////////////////
539// Build
540////////////////////////////////////////////////////////////////////////////////
541
542void verify(const ProgramDescription& description, const Glop& glop) {
543    bool hasTexture = glop.fill.texture.texture != nullptr;
544    LOG_ALWAYS_FATAL_IF(description.hasTexture && description.hasExternalTexture);
545    LOG_ALWAYS_FATAL_IF((description.hasTexture || description.hasExternalTexture )!= hasTexture);
546    LOG_ALWAYS_FATAL_IF((glop.mesh.vertices.flags & VertexAttribFlags::kTextureCoord) != hasTexture);
547
548    if ((glop.mesh.vertices.flags & VertexAttribFlags::kAlpha) && glop.mesh.vertices.bufferObject) {
549        LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible");
550    }
551
552    if (description.hasTextureTransform != (glop.fill.texture.textureTransform != nullptr)) {
553        LOG_ALWAYS_FATAL("Texture transform incorrectly specified");
554    }
555}
556
557void GlopBuilder::build() {
558    REQUIRE_STAGES(kAllStages);
559    if (mOutGlop->mesh.vertices.flags & VertexAttribFlags::kTextureCoord) {
560        mDescription.hasTexture = mOutGlop->fill.texture.target == GL_TEXTURE_2D;
561        mDescription.hasExternalTexture = mOutGlop->fill.texture.target == GL_TEXTURE_EXTERNAL_OES;
562    }
563    mDescription.hasColors = mOutGlop->mesh.vertices.flags & VertexAttribFlags::kColor;
564    mDescription.hasVertexAlpha = mOutGlop->mesh.vertices.flags & VertexAttribFlags::kAlpha;
565
566    // serialize shader info into ShaderData
567    GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0;
568    SkiaShader::store(mCaches, mShader, mOutGlop->transform.modelView,
569            &textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData));
570
571    // duplicates ProgramCache's definition of color uniform presence
572    const bool singleColor = !mDescription.hasTexture
573            && !mDescription.hasExternalTexture
574            && !mDescription.hasGradient
575            && !mDescription.hasBitmap;
576    mOutGlop->fill.colorEnabled = mDescription.modulate || singleColor;
577
578    verify(mDescription, *mOutGlop);
579
580    // Final step: populate program and map bounds into render target space
581    mOutGlop->fill.program = mCaches.programCache.get(mDescription);
582    mOutGlop->transform.canvas.mapRect(mOutGlop->bounds);
583}
584
585} /* namespace uirenderer */
586} /* namespace android */
587