1/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8@header {
9    #include "GrClip.h"
10    #include "GrContext.h"
11    #include "GrContextPriv.h"
12    #include "GrProxyProvider.h"
13    #include "GrRenderTargetContext.h"
14}
15
16@class {
17    static bool TestForPreservingPMConversions(GrContext* context) {
18        static constexpr int kSize = 256;
19        static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
20        SkAutoTMalloc<uint32_t> data(kSize * kSize * 3);
21        uint32_t* srcData = data.get();
22        uint32_t* firstRead = data.get() + kSize * kSize;
23        uint32_t* secondRead = data.get() + 2 * kSize * kSize;
24
25        // Fill with every possible premultiplied A, color channel value. There will be 256-y
26        // duplicate values in row y. We set r, g, and b to the same value since they are handled
27        // identically.
28        for (int y = 0; y < kSize; ++y) {
29            for (int x = 0; x < kSize; ++x) {
30                uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[kSize*y + x]);
31                color[3] = y;
32                color[2] = SkTMin(x, y);
33                color[1] = SkTMin(x, y);
34                color[0] = SkTMin(x, y);
35            }
36        }
37
38        const SkImageInfo ii = SkImageInfo::Make(kSize, kSize,
39                                                 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
40
41        sk_sp<GrRenderTargetContext> readRTC(context->makeDeferredRenderTargetContext(
42                                                                              SkBackingFit::kExact,
43                                                                              kSize, kSize,
44                                                                              kConfig, nullptr));
45        sk_sp<GrRenderTargetContext> tempRTC(context->makeDeferredRenderTargetContext(
46                                                                              SkBackingFit::kExact,
47                                                                              kSize, kSize,
48                                                                              kConfig, nullptr));
49        if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) {
50            return false;
51        }
52        // Adding discard to appease vulkan validation warning about loading uninitialized data on
53        // draw
54        readRTC->discard();
55
56        GrSurfaceDesc desc;
57        desc.fOrigin = kTopLeft_GrSurfaceOrigin;
58        desc.fWidth = kSize;
59        desc.fHeight = kSize;
60        desc.fConfig = kConfig;
61
62        GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
63
64        sk_sp<GrTextureProxy> dataProxy = proxyProvider->createTextureProxy(desc, SkBudgeted::kYes,
65                                                                            data, 0);
66        if (!dataProxy) {
67            return false;
68        }
69
70        static const SkRect kRect = SkRect::MakeIWH(kSize, kSize);
71
72        // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
73        // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
74        // We then verify that two reads produced the same values.
75
76        GrPaint paint1;
77        GrPaint paint2;
78        GrPaint paint3;
79        std::unique_ptr<GrFragmentProcessor> pmToUPM(
80                new GrConfigConversionEffect(PMConversion::kToUnpremul));
81        std::unique_ptr<GrFragmentProcessor> upmToPM(
82                new GrConfigConversionEffect(PMConversion::kToPremul));
83
84        paint1.addColorTextureProcessor(dataProxy, SkMatrix::I());
85        paint1.addColorFragmentProcessor(pmToUPM->clone());
86        paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
87
88        readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect,
89                                kRect);
90        if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
91            return false;
92        }
93
94        // Adding discard to appease vulkan validation warning about loading uninitialized data on
95        // draw
96        tempRTC->discard();
97
98        paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), SkMatrix::I());
99        paint2.addColorFragmentProcessor(std::move(upmToPM));
100        paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
101
102        tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect,
103                                kRect);
104
105        paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), SkMatrix::I());
106        paint3.addColorFragmentProcessor(std::move(pmToUPM));
107        paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
108
109        readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kRect,
110                                kRect);
111
112        if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) {
113            return false;
114        }
115
116        for (int y = 0; y < kSize; ++y) {
117            for (int x = 0; x <= y; ++x) {
118                if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) {
119                    return false;
120                }
121            }
122        }
123
124        return true;
125    }
126}
127
128@make {
129    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> fp,
130                                                     PMConversion pmConversion) {
131        if (!fp) {
132            return nullptr;
133        }
134        std::unique_ptr<GrFragmentProcessor> ccFP(new GrConfigConversionEffect(pmConversion));
135        std::unique_ptr<GrFragmentProcessor> fpPipeline[] = { std::move(fp), std::move(ccFP) };
136        return GrFragmentProcessor::RunInSeries(fpPipeline, 2);
137    }
138}
139
140layout(key) in PMConversion pmConversion;
141
142@emitCode {
143    fragBuilder->forceHighPrecision();
144}
145
146void main() {
147    // Aggressively round to the nearest exact (N / 255) floating point value. This lets us find a
148    // round-trip preserving pair on some GPUs that do odd byte to float conversion.
149    sk_OutColor = floor(sk_InColor * 255 + 0.5) / 255;
150
151    @switch (pmConversion) {
152        case PMConversion::kToPremul:
153            sk_OutColor.rgb = floor(sk_OutColor.rgb * sk_OutColor.a * 255 + 0.5) / 255;
154            break;
155
156        case PMConversion::kToUnpremul:
157            sk_OutColor.rgb = sk_OutColor.a <= 0.0 ?
158                                          half3(0) :
159                                          floor(sk_OutColor.rgb / sk_OutColor.a * 255 + 0.5) / 255;
160            break;
161    }
162}
163
164@test(data) {
165    PMConversion pmConv = static_cast<PMConversion>(data->fRandom->nextULessThan(
166                                                             (int) PMConversion::kPMConversionCnt));
167    return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(pmConv));
168}
169