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