1/*
2 * Copyright 2012 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#include "Test.h"
9
10// This test is specific to the GPU backend.
11#if SK_SUPPORT_GPU
12
13#include "GrContext.h"
14#include "GrContextPriv.h"
15#include "GrProxyProvider.h"
16#include "GrResourceProvider.h"
17#include "GrSurfaceContext.h"
18#include "GrSurfaceProxy.h"
19#include "GrTextureProxy.h"
20#include "SkCanvas.h"
21#include "SkSurface.h"
22
23// This was made indivisible by 4 to ensure we test setting GL_PACK_ALIGNMENT properly.
24static const int X_SIZE = 13;
25static const int Y_SIZE = 13;
26
27static void validate_alpha_data(skiatest::Reporter* reporter, int w, int h, const uint8_t* actual,
28                                size_t actualRowBytes, const uint8_t* expected, SkString extraMsg,
29                                GrPixelConfig config) {
30    for (int y = 0; y < h; ++y) {
31        for (int x = 0; x < w; ++x) {
32            uint8_t a = actual[y * actualRowBytes + x];
33            uint8_t e = expected[y * w + x];
34            if (kRGBA_1010102_GrPixelConfig == config) {
35                // This config only preserves two bits of alpha
36                a >>= 6;
37                e >>= 6;
38            }
39            if (e != a) {
40                ERRORF(reporter,
41                       "Failed alpha readback. Expected: 0x%02x, Got: 0x%02x at (%d,%d), %s",
42                       e, a, x, y, extraMsg.c_str());
43                return;
44            }
45        }
46    }
47}
48
49DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadWriteAlpha, reporter, ctxInfo) {
50    GrContext* context = ctxInfo.grContext();
51    GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
52
53    unsigned char alphaData[X_SIZE * Y_SIZE];
54
55    static const int kClearValue = 0x2;
56
57    bool match;
58    static const size_t kRowBytes[] = {0, X_SIZE, X_SIZE + 1, 2 * X_SIZE - 1};
59    {
60        GrSurfaceDesc desc;
61        desc.fFlags     = kNone_GrSurfaceFlags;
62        desc.fOrigin    = kTopLeft_GrSurfaceOrigin;
63        desc.fConfig    = kAlpha_8_GrPixelConfig;    // it is a single channel texture
64        desc.fWidth     = X_SIZE;
65        desc.fHeight    = Y_SIZE;
66
67        // We are initializing the texture with zeros here
68        memset(alphaData, 0, X_SIZE * Y_SIZE);
69
70        sk_sp<GrTextureProxy> proxy = proxyProvider->createTextureProxy(desc, SkBudgeted::kNo,
71                                                                        alphaData, 0);
72        if (!proxy) {
73            ERRORF(reporter, "Could not create alpha texture.");
74            return;
75        }
76        sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext(
77                                                                  std::move(proxy)));
78
79        const SkImageInfo ii = SkImageInfo::MakeA8(X_SIZE, Y_SIZE);
80        sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii));
81
82        // create a distinctive texture
83        for (int y = 0; y < Y_SIZE; ++y) {
84            for (int x = 0; x < X_SIZE; ++x) {
85                alphaData[y * X_SIZE + x] = y*X_SIZE+x;
86            }
87        }
88
89        for (auto rowBytes : kRowBytes) {
90
91            // upload the texture (do per-rowbytes iteration because we may overwrite below).
92            bool result = sContext->writePixels(ii, alphaData, 0, 0, 0);
93            REPORTER_ASSERT(reporter, result, "Initial A8 writePixels failed");
94
95            size_t nonZeroRowBytes = rowBytes ? rowBytes : X_SIZE;
96            size_t bufLen = nonZeroRowBytes * Y_SIZE;
97            std::unique_ptr<uint8_t[]> readback(new uint8_t[bufLen]);
98            // clear readback to something non-zero so we can detect readback failures
99            memset(readback.get(), kClearValue, bufLen);
100
101            // read the texture back
102            result = sContext->readPixels(ii, readback.get(), rowBytes, 0, 0);
103            REPORTER_ASSERT(reporter, result, "Initial A8 readPixels failed");
104
105            // make sure the original & read back versions match
106            SkString msg;
107            msg.printf("rb:%d A8", SkToU32(rowBytes));
108            validate_alpha_data(reporter, X_SIZE, Y_SIZE, readback.get(), nonZeroRowBytes,
109                                alphaData, msg, kAlpha_8_GrPixelConfig);
110
111            // Now try writing to a single channel surface (if we could create one).
112            if (surf) {
113                SkCanvas* canvas = surf->getCanvas();
114
115                SkPaint paint;
116
117                const SkRect rect = SkRect::MakeLTRB(-10, -10, X_SIZE + 10, Y_SIZE + 10);
118
119                paint.setColor(SK_ColorWHITE);
120
121                canvas->drawRect(rect, paint);
122
123                // Workaround for a bug in old GCC/glibc used in our Chromecast toolchain:
124                // error: call to '__warn_memset_zero_len' declared with attribute warning:
125                //        memset used with constant zero length parameter; this could be due
126                //        to transposed parameters
127                // See also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61294
128                if (bufLen > 0) {
129                    memset(readback.get(), kClearValue, bufLen);
130                }
131                result = surf->readPixels(ii, readback.get(), nonZeroRowBytes, 0, 0);
132                REPORTER_ASSERT(reporter, result, "A8 readPixels after clear failed");
133
134                match = true;
135                for (int y = 0; y < Y_SIZE && match; ++y) {
136                    for (int x = 0; x < X_SIZE && match; ++x) {
137                        uint8_t rbValue = readback.get()[y * nonZeroRowBytes + x];
138                        if (0xFF != rbValue) {
139                            ERRORF(reporter,
140                                   "Failed alpha readback after clear. Expected: 0xFF, Got: 0x%02x"
141                                   " at (%d,%d), rb:%d", rbValue, x, y, SkToU32(rowBytes));
142                            match = false;
143                        }
144                    }
145                }
146            }
147        }
148    }
149
150    static const GrPixelConfig kRGBAConfigs[] {
151        kRGBA_8888_GrPixelConfig,
152        kBGRA_8888_GrPixelConfig,
153        kSRGBA_8888_GrPixelConfig,
154        kRGBA_1010102_GrPixelConfig,
155    };
156
157    for (int y = 0; y < Y_SIZE; ++y) {
158        for (int x = 0; x < X_SIZE; ++x) {
159            alphaData[y * X_SIZE + x] = y*X_SIZE+x;
160        }
161    }
162
163    const SkImageInfo dstInfo = SkImageInfo::Make(X_SIZE, Y_SIZE,
164                                                  kAlpha_8_SkColorType,
165                                                  kPremul_SkAlphaType);
166
167    // Attempt to read back just alpha from a RGBA/BGRA texture. Once with a texture-only src and
168    // once with a render target.
169    for (auto config : kRGBAConfigs) {
170        for (int rt = 0; rt < 2; ++rt) {
171            GrSurfaceDesc desc;
172            desc.fFlags     = rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
173            desc.fOrigin    = rt ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
174            desc.fConfig    = config;
175            desc.fWidth     = X_SIZE;
176            desc.fHeight    = Y_SIZE;
177
178            uint32_t rgbaData[X_SIZE * Y_SIZE];
179            // Make the alpha channel of the rgba texture come from alphaData.
180            for (int y = 0; y < Y_SIZE; ++y) {
181                for (int x = 0; x < X_SIZE; ++x) {
182                    rgbaData[y * X_SIZE + x] = GrColorPackRGBA(6, 7, 8, alphaData[y * X_SIZE + x]);
183                }
184            }
185
186            sk_sp<GrTextureProxy> proxy = proxyProvider->createTextureProxy(desc, SkBudgeted::kNo,
187                                                                            rgbaData, 0);
188            if (!proxy) {
189                // We always expect to be able to create a RGBA texture
190                if (!rt  && kRGBA_8888_GrPixelConfig == desc.fConfig) {
191                    ERRORF(reporter, "Failed to create RGBA texture.");
192                }
193                continue;
194            }
195
196            sk_sp<SkColorSpace> colorSpace;
197            if (GrPixelConfigIsSRGB(proxy->config())) {
198                colorSpace = SkColorSpace::MakeSRGB();
199            }
200            sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
201                    std::move(proxy), std::move(colorSpace));
202
203            for (auto rowBytes : kRowBytes) {
204                size_t nonZeroRowBytes = rowBytes ? rowBytes : X_SIZE;
205
206                std::unique_ptr<uint8_t[]> readback(new uint8_t[nonZeroRowBytes * Y_SIZE]);
207                // Clear so we don't accidentally see values from previous iteration.
208                memset(readback.get(), kClearValue, nonZeroRowBytes * Y_SIZE);
209
210                // read the texture back
211                bool result = sContext->readPixels(dstInfo, readback.get(), rowBytes, 0, 0);
212                REPORTER_ASSERT(reporter, result, "8888 readPixels failed");
213
214                // make sure the original & read back versions match
215                SkString msg;
216                msg.printf("rt:%d, rb:%d 8888", rt, SkToU32(rowBytes));
217                validate_alpha_data(reporter, X_SIZE, Y_SIZE, readback.get(), nonZeroRowBytes,
218                                    alphaData, msg, config);
219            }
220        }
221    }
222}
223
224#endif
225