GlopBuilder.cpp revision 48f650cb24e5b028deaff01baddc1d154f78d91a
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            static_cast<int>(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            static_cast<int>(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            static_cast<int>(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            static_cast<int>(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            static_cast<int>(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            static_cast<int>(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            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            static_cast<int>(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            static_cast<int>(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,
212        SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
213        const SkShader* shader, const SkColorFilter* colorFilter) {
214    if (mode != SkXfermode::kClear_Mode) {
215        float alpha = (SkColorGetA(color) / 255.0f) * alphaScale;
216        if (!shader) {
217            float colorScale = alpha / 255.0f;
218            mOutGlop->fill.color = {
219                    colorScale * SkColorGetR(color),
220                    colorScale * SkColorGetG(color),
221                    colorScale * SkColorGetB(color),
222                    alpha
223            };
224        } else {
225            mOutGlop->fill.color = { 1, 1, 1, alpha };
226        }
227    } else {
228        mOutGlop->fill.color = { 0, 0, 0, 1 };
229    }
230
231    mOutGlop->blend = { GL_ZERO, GL_ZERO };
232    if (mOutGlop->fill.color.a < 1.0f
233            || (mOutGlop->mesh.vertices.attribFlags & 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, modeUsage,
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 = (modeUsage == Blend::ModeOrderSwap::Swap);
251                // blending in shader, don't enable
252            } else {
253                // unsupported
254                Blend::getFactors(SkXfermode::kSrcOver_Mode, modeUsage,
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,
321                PaintUtils::getXfermode(paint->getXfermode()), Blend::ModeOrderSwap::NoSwap,
322                shader, paint->getColorFilter());
323    } else {
324        mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale };
325
326        if (alphaScale < 1.0f
327                || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha)
328                || texture.blend
329                || mOutGlop->roundRectClipState) {
330            Blend::getFactors(SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
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,
353            PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
354            paint.getShader(), paint.getColorFilter());
355    mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
356    return *this;
357}
358
359GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture,
360        const SkPaint& paint, float alphaScale) {
361    TRIGGER_STAGE(kFillStage);
362    REQUIRE_STAGES(kMeshStage);
363
364    //specify invalid filter/clamp, since these are always static for PathTextures
365    mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
366
367    setFill(paint.getColor(), alphaScale,
368            PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
369            paint.getShader(), paint.getColorFilter());
370
371    mDescription.hasAlpha8Texture = true;
372    mDescription.modulate = mOutGlop->fill.color.isNotBlack();
373    return *this;
374}
375
376GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor,
377        const SkPaint& paint, float alphaScale) {
378    TRIGGER_STAGE(kFillStage);
379    REQUIRE_STAGES(kMeshStage);
380
381    //specify invalid filter/clamp, since these are always static for ShadowTextures
382    mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
383
384    const int ALPHA_BITMASK = SK_ColorBLACK;
385    const int COLOR_BITMASK = ~ALPHA_BITMASK;
386    if ((shadowColor & ALPHA_BITMASK) == ALPHA_BITMASK) {
387        // shadow color is fully opaque: override its alpha with that of paint
388        shadowColor &= paint.getColor() | COLOR_BITMASK;
389    }
390
391    setFill(shadowColor, alphaScale,
392            PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
393            paint.getShader(), paint.getColorFilter());
394
395    mDescription.hasAlpha8Texture = true;
396    mDescription.modulate = mOutGlop->fill.color.isNotBlack();
397    return *this;
398}
399
400GlopBuilder& GlopBuilder::setFillBlack() {
401    TRIGGER_STAGE(kFillStage);
402    REQUIRE_STAGES(kMeshStage);
403
404    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
405    setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
406            nullptr, nullptr);
407    return *this;
408}
409
410GlopBuilder& GlopBuilder::setFillClear() {
411    TRIGGER_STAGE(kFillStage);
412    REQUIRE_STAGES(kMeshStage);
413
414    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
415    setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, Blend::ModeOrderSwap::NoSwap,
416            nullptr, nullptr);
417    return *this;
418}
419
420GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
421        float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage) {
422    TRIGGER_STAGE(kFillStage);
423    REQUIRE_STAGES(kMeshStage);
424
425    mOutGlop->fill.texture = { &texture,
426            GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
427    mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
428
429    setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter);
430
431    mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
432    return *this;
433}
434
435GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
436    TRIGGER_STAGE(kFillStage);
437    REQUIRE_STAGES(kMeshStage);
438
439    mOutGlop->fill.texture = { &(layer.getTexture()),
440            layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
441    mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
442
443    setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap,
444            nullptr, layer.getColorFilter());
445
446    mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
447    mDescription.hasTextureTransform = true;
448    return *this;
449}
450
451////////////////////////////////////////////////////////////////////////////////
452// Transform
453////////////////////////////////////////////////////////////////////////////////
454
455GlopBuilder& GlopBuilder::setTransform(const Matrix4& ortho,
456        const Matrix4& transform, bool fudgingOffset) {
457    TRIGGER_STAGE(kTransformStage);
458
459    mOutGlop->transform.ortho.load(ortho);
460    mOutGlop->transform.canvas.load(transform);
461    mOutGlop->transform.fudgingOffset = fudgingOffset;
462    return *this;
463}
464
465////////////////////////////////////////////////////////////////////////////////
466// ModelView
467////////////////////////////////////////////////////////////////////////////////
468
469GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) {
470    TRIGGER_STAGE(kModelViewStage);
471
472    mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
473    mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
474    mOutGlop->bounds = destination;
475    return *this;
476}
477
478GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination) {
479    TRIGGER_STAGE(kModelViewStage);
480    REQUIRE_STAGES(kTransformStage | kFillStage);
481
482    float left = destination.left;
483    float top = destination.top;
484
485    const Matrix4& canvasTransform = mOutGlop->transform.canvas;
486    if (CC_LIKELY(canvasTransform.isPureTranslate())) {
487        // snap by adjusting the model view matrix
488        const float translateX = canvasTransform.getTranslateX();
489        const float translateY = canvasTransform.getTranslateY();
490
491        left = (int) floorf(left + translateX + 0.5f) - translateX;
492        top = (int) floorf(top + translateY + 0.5f) - translateY;
493        mOutGlop->fill.texture.filter = GL_NEAREST;
494    }
495
496    mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f);
497    mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
498    mOutGlop->bounds = destination;
499    return *this;
500}
501
502GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) {
503    TRIGGER_STAGE(kModelViewStage);
504
505    mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
506    mOutGlop->bounds = source;
507    mOutGlop->bounds.translate(offsetX, offsetY);
508    return *this;
509}
510
511GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source) {
512    TRIGGER_STAGE(kModelViewStage);
513    REQUIRE_STAGES(kTransformStage | kFillStage);
514
515    const Matrix4& canvasTransform = mOutGlop->transform.canvas;
516    if (CC_LIKELY(canvasTransform.isPureTranslate())) {
517        // snap by adjusting the model view matrix
518        const float translateX = canvasTransform.getTranslateX();
519        const float translateY = canvasTransform.getTranslateY();
520
521        offsetX = (int) floorf(offsetX + translateX + source.left + 0.5f) - translateX - source.left;
522        offsetY = (int) floorf(offsetY + translateY + source.top + 0.5f) - translateY - source.top;
523        mOutGlop->fill.texture.filter = GL_NEAREST;
524    }
525
526    mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
527    mOutGlop->bounds = source;
528    mOutGlop->bounds.translate(offsetX, offsetY);
529    return *this;
530}
531
532////////////////////////////////////////////////////////////////////////////////
533// RoundRectClip
534////////////////////////////////////////////////////////////////////////////////
535
536GlopBuilder& GlopBuilder::setRoundRectClipState(const RoundRectClipState* roundRectClipState) {
537    TRIGGER_STAGE(kRoundRectClipStage);
538
539    mOutGlop->roundRectClipState = roundRectClipState;
540    mDescription.hasRoundRectClip = roundRectClipState != nullptr;
541    return *this;
542}
543
544////////////////////////////////////////////////////////////////////////////////
545// Build
546////////////////////////////////////////////////////////////////////////////////
547
548void verify(const ProgramDescription& description, const Glop& glop) {
549    if (glop.fill.texture.texture != nullptr) {
550        LOG_ALWAYS_FATAL_IF(((description.hasTexture && description.hasExternalTexture)
551                        || (!description.hasTexture && !description.hasExternalTexture)
552                        || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) == 0)),
553                "Texture %p, hT%d, hET %d, attribFlags %x",
554                glop.fill.texture.texture,
555                description.hasTexture, description.hasExternalTexture,
556                glop.mesh.vertices.attribFlags);
557    } else {
558        LOG_ALWAYS_FATAL_IF((description.hasTexture
559                        || description.hasExternalTexture
560                        || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) != 0)),
561                "No texture, hT%d, hET %d, attribFlags %x",
562                description.hasTexture, description.hasExternalTexture,
563                glop.mesh.vertices.attribFlags);
564    }
565
566    if ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kAlpha)
567            && glop.mesh.vertices.bufferObject) {
568        LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible");
569    }
570
571    if (description.hasTextureTransform != (glop.fill.texture.textureTransform != nullptr)) {
572        LOG_ALWAYS_FATAL("Texture transform incorrectly specified");
573    }
574}
575
576void GlopBuilder::build() {
577    REQUIRE_STAGES(kAllStages);
578    if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) {
579        if (mOutGlop->fill.texture.target == GL_TEXTURE_2D) {
580            mDescription.hasTexture = true;
581        } else {
582            mDescription.hasExternalTexture = true;
583        }
584
585    }
586    mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kColor;
587    mDescription.hasVertexAlpha = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha;
588
589    // serialize shader info into ShaderData
590    GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0;
591    SkiaShader::store(mCaches, mShader, mOutGlop->transform.modelView,
592            &textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData));
593
594    // duplicates ProgramCache's definition of color uniform presence
595    const bool singleColor = !mDescription.hasTexture
596            && !mDescription.hasExternalTexture
597            && !mDescription.hasGradient
598            && !mDescription.hasBitmap;
599    mOutGlop->fill.colorEnabled = mDescription.modulate || singleColor;
600
601    verify(mDescription, *mOutGlop);
602
603    // Final step: populate program and map bounds into render target space
604    mOutGlop->fill.program = mCaches.programCache.get(mDescription);
605    mOutGlop->transform.canvas.mapRect(mOutGlop->bounds);
606}
607
608} /* namespace uirenderer */
609} /* namespace android */
610