1 /*
2 * Copyright 2017 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 "tests/TestUtils.h"
9
10 #include "include/encode/SkPngEncoder.h"
11 #include "include/utils/SkBase64.h"
12 #include "src/core/SkUtils.h"
13 #include "src/gpu/GrContextPriv.h"
14 #include "src/gpu/GrDrawingManager.h"
15 #include "src/gpu/GrGpu.h"
16 #include "src/gpu/GrSurfaceContext.h"
17 #include "src/gpu/GrSurfaceProxy.h"
18 #include "src/gpu/GrTextureContext.h"
19 #include "src/gpu/GrTextureProxy.h"
20 #include "src/gpu/SkGr.h"
21
test_read_pixels(skiatest::Reporter * reporter,GrSurfaceContext * srcContext,uint32_t expectedPixelValues[],const char * testName)22 void test_read_pixels(skiatest::Reporter* reporter,
23 GrSurfaceContext* srcContext, uint32_t expectedPixelValues[],
24 const char* testName) {
25 int pixelCnt = srcContext->width() * srcContext->height();
26 SkAutoTMalloc<uint32_t> pixels(pixelCnt);
27 memset(pixels.get(), 0, sizeof(uint32_t)*pixelCnt);
28
29 SkImageInfo ii = SkImageInfo::Make(srcContext->width(), srcContext->height(),
30 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
31 bool read = srcContext->readPixels(ii, pixels.get(), 0, {0, 0});
32 if (!read) {
33 ERRORF(reporter, "%s: Error reading from texture.", testName);
34 }
35
36 for (int i = 0; i < pixelCnt; ++i) {
37 if (pixels.get()[i] != expectedPixelValues[i]) {
38 ERRORF(reporter, "%s: Error, pixel value %d should be 0x%08x, got 0x%08x.",
39 testName, i, expectedPixelValues[i], pixels.get()[i]);
40 break;
41 }
42 }
43 }
44
test_write_pixels(skiatest::Reporter * reporter,GrSurfaceContext * dstContext,bool expectedToWork,const char * testName)45 void test_write_pixels(skiatest::Reporter* reporter,
46 GrSurfaceContext* dstContext, bool expectedToWork,
47 const char* testName) {
48 int pixelCnt = dstContext->width() * dstContext->height();
49 SkAutoTMalloc<uint32_t> pixels(pixelCnt);
50 for (int y = 0; y < dstContext->width(); ++y) {
51 for (int x = 0; x < dstContext->height(); ++x) {
52 pixels.get()[y * dstContext->width() + x] =
53 SkColorToPremulGrColor(SkColorSetARGB(2*y, x, y, x + y));
54 }
55 }
56
57 SkImageInfo ii = SkImageInfo::Make(dstContext->width(), dstContext->height(),
58 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
59 bool write = dstContext->writePixels(ii, pixels.get(), 0, {0, 0});
60 if (!write) {
61 if (expectedToWork) {
62 ERRORF(reporter, "%s: Error writing to texture.", testName);
63 }
64 return;
65 }
66
67 if (write && !expectedToWork) {
68 ERRORF(reporter, "%s: writePixels succeeded when it wasn't supposed to.", testName);
69 return;
70 }
71
72 test_read_pixels(reporter, dstContext, pixels.get(), testName);
73 }
74
test_copy_from_surface(skiatest::Reporter * reporter,GrContext * context,GrSurfaceProxy * proxy,GrColorType colorType,uint32_t expectedPixelValues[],const char * testName)75 void test_copy_from_surface(skiatest::Reporter* reporter, GrContext* context, GrSurfaceProxy* proxy,
76 GrColorType colorType, uint32_t expectedPixelValues[],
77 const char* testName) {
78 sk_sp<GrTextureProxy> dstProxy = GrSurfaceProxy::Copy(context, proxy, GrMipMapped::kNo,
79 SkBackingFit::kExact, SkBudgeted::kYes);
80 SkASSERT(dstProxy);
81
82 sk_sp<GrSurfaceContext> dstContext = context->priv().makeWrappedSurfaceContext(
83 std::move(dstProxy), colorType, kPremul_SkAlphaType);
84 SkASSERT(dstContext.get());
85
86 test_read_pixels(reporter, dstContext.get(), expectedPixelValues, testName);
87 }
88
fill_pixel_data(int width,int height,GrColor * data)89 void fill_pixel_data(int width, int height, GrColor* data) {
90 for (int j = 0; j < height; ++j) {
91 for (int i = 0; i < width; ++i) {
92 unsigned int red = (unsigned int)(256.f * (i / (float)width));
93 unsigned int green = (unsigned int)(256.f * (j / (float)height));
94 data[i + j * width] = GrColorPackRGBA(red - (red >> 8), green - (green >> 8),
95 0xff, 0xff);
96 }
97 }
98 }
99
create_backend_texture(GrContext * context,GrBackendTexture * backendTex,const SkImageInfo & ii,const SkColor4f & color,GrMipMapped mipMapped,GrRenderable renderable)100 bool create_backend_texture(GrContext* context, GrBackendTexture* backendTex,
101 const SkImageInfo& ii, const SkColor4f& color,
102 GrMipMapped mipMapped, GrRenderable renderable) {
103 // TODO: use the color-init version of createBackendTexture once Metal supports it.
104 #if 0
105 *backendTex = context->createBackendTexture(ii.width(), ii.height(), ii.colorType(),
106 color, mipMapped, renderable);
107 #else
108 SkBitmap bm;
109 bm.allocPixels(ii);
110 sk_memset32(bm.getAddr32(0, 0), color.toSkColor(), ii.width() * ii.height());
111
112 SkASSERT(GrMipMapped::kNo == mipMapped);
113 *backendTex = context->priv().createBackendTexture(&bm.pixmap(), 1, renderable,
114 GrProtected::kNo);
115 #endif
116
117 return backendTex->isValid();
118 }
119
delete_backend_texture(GrContext * context,const GrBackendTexture & backendTex)120 void delete_backend_texture(GrContext* context, const GrBackendTexture& backendTex) {
121 GrFlushInfo flushInfo;
122 flushInfo.fFlags = kSyncCpu_GrFlushFlag;
123 context->flush(flushInfo);
124 context->deleteBackendTexture(backendTex);
125 }
126
does_full_buffer_contain_correct_color(const GrColor * srcBuffer,const GrColor * dstBuffer,int width,int height)127 bool does_full_buffer_contain_correct_color(const GrColor* srcBuffer,
128 const GrColor* dstBuffer,
129 int width,
130 int height) {
131 const GrColor* srcPtr = srcBuffer;
132 const GrColor* dstPtr = dstBuffer;
133 for (int j = 0; j < height; ++j) {
134 for (int i = 0; i < width; ++i) {
135 if (srcPtr[i] != dstPtr[i]) {
136 return false;
137 }
138 }
139 srcPtr += width;
140 dstPtr += width;
141 }
142 return true;
143 }
144
bitmap_to_base64_data_uri(const SkBitmap & bitmap,SkString * dst)145 bool bitmap_to_base64_data_uri(const SkBitmap& bitmap, SkString* dst) {
146 SkPixmap pm;
147 if (!bitmap.peekPixels(&pm)) {
148 dst->set("peekPixels failed");
149 return false;
150 }
151
152 // We're going to embed this PNG in a data URI, so make it as small as possible
153 SkPngEncoder::Options options;
154 options.fFilterFlags = SkPngEncoder::FilterFlag::kAll;
155 options.fZLibLevel = 9;
156
157 SkDynamicMemoryWStream wStream;
158 if (!SkPngEncoder::Encode(&wStream, pm, options)) {
159 dst->set("SkPngEncoder::Encode failed");
160 return false;
161 }
162
163 sk_sp<SkData> pngData = wStream.detachAsData();
164 size_t len = SkBase64::Encode(pngData->data(), pngData->size(), nullptr);
165
166 // The PNG can be almost arbitrarily large. We don't want to fill our logs with enormous URLs.
167 // Infra says these can be pretty big, as long as we're only outputting them on failure.
168 static const size_t kMaxBase64Length = 1024 * 1024;
169 if (len > kMaxBase64Length) {
170 dst->printf("Encoded image too large (%u bytes)", static_cast<uint32_t>(len));
171 return false;
172 }
173
174 dst->resize(len);
175 SkBase64::Encode(pngData->data(), pngData->size(), dst->writable_str());
176 dst->prepend("data:image/png;base64,");
177 return true;
178 }
179
compare_pixels(const GrPixelInfo & infoA,const char * a,size_t rowBytesA,const GrPixelInfo & infoB,const char * b,size_t rowBytesB,const float tolRGBA[4],std::function<ComparePixmapsErrorReporter> & error)180 bool compare_pixels(const GrPixelInfo& infoA, const char* a, size_t rowBytesA,
181 const GrPixelInfo& infoB, const char* b, size_t rowBytesB,
182 const float tolRGBA[4], std::function<ComparePixmapsErrorReporter>& error) {
183 if (infoA.width() != infoB.width() || infoA.height() != infoB.height()) {
184 static constexpr float kDummyDiffs[4] = {};
185 error(-1, -1, kDummyDiffs);
186 return false;
187 }
188
189 SkAlphaType floatAlphaType = infoA.alphaType();
190 // If one is premul and the other is unpremul we do the comparison in premul space.
191 if ((infoA.alphaType() == kPremul_SkAlphaType ||
192 infoB.alphaType() == kPremul_SkAlphaType) &&
193 (infoA.alphaType() == kUnpremul_SkAlphaType ||
194 infoB.alphaType() == kUnpremul_SkAlphaType)) {
195 floatAlphaType = kPremul_SkAlphaType;
196 }
197 sk_sp<SkColorSpace> floatCS;
198 if (SkColorSpace::Equals(infoA.colorSpace(), infoB.colorSpace())) {
199 floatCS = infoA.refColorSpace();
200 } else {
201 floatCS = SkColorSpace::MakeSRGBLinear();
202 }
203 GrPixelInfo floatInfo(GrColorType::kRGBA_F32, floatAlphaType, std::move(floatCS),
204 infoA.width(), infoA.height());
205
206 size_t floatBpp = GrColorTypeBytesPerPixel(GrColorType::kRGBA_F32);
207 size_t floatRowBytes = floatBpp * infoA.width();
208 std::unique_ptr<char[]> floatA(new char[floatRowBytes * infoA.height()]);
209 std::unique_ptr<char[]> floatB(new char[floatRowBytes * infoA.height()]);
210 SkAssertResult(GrConvertPixels(floatInfo, floatA.get(), floatRowBytes, infoA, a, rowBytesA));
211 SkAssertResult(GrConvertPixels(floatInfo, floatB.get(), floatRowBytes, infoB, b, rowBytesB));
212
213 auto at = [floatBpp, floatRowBytes](const char* floatBuffer, int x, int y) {
214 return reinterpret_cast<const float*>(floatBuffer + y * floatRowBytes + x * floatBpp);
215 };
216 for (int y = 0; y < infoA.height(); ++y) {
217 for (int x = 0; x < infoA.width(); ++x) {
218 const float* rgbaA = at(floatA.get(), x, y);
219 const float* rgbaB = at(floatB.get(), x, y);
220 float diffs[4];
221 bool bad = false;
222 for (int i = 0; i < 4; ++i) {
223 diffs[i] = rgbaB[i] - rgbaA[i];
224 if (std::abs(diffs[i]) > std::abs(tolRGBA[i])) {
225 bad = true;
226 }
227 }
228 if (bad) {
229 error(x, y, diffs);
230 return false;
231 }
232 }
233 }
234 return true;
235 }
236
compare_pixels(const SkPixmap & a,const SkPixmap & b,const float tolRGBA[4],std::function<ComparePixmapsErrorReporter> & error)237 bool compare_pixels(const SkPixmap& a, const SkPixmap& b, const float tolRGBA[4],
238 std::function<ComparePixmapsErrorReporter>& error) {
239 return compare_pixels(a.info(), static_cast<const char*>(a.addr()), a.rowBytes(),
240 b.info(), static_cast<const char*>(b.addr()), b.rowBytes(),
241 tolRGBA, error);
242 }
243
244 #include "src/utils/SkCharToGlyphCache.h"
245
hash_to_glyph(uint32_t value)246 static SkGlyphID hash_to_glyph(uint32_t value) {
247 return SkToU16(((value >> 16) ^ value) & 0xFFFF);
248 }
249
250 namespace {
251 class UnicharGen {
252 SkUnichar fU;
253 const int fStep;
254 public:
UnicharGen(int step)255 UnicharGen(int step) : fU(0), fStep(step) {}
256
next()257 SkUnichar next() {
258 fU += fStep;
259 return fU;
260 }
261 };
262 }
263
DEF_TEST(chartoglyph_cache,reporter)264 DEF_TEST(chartoglyph_cache, reporter) {
265 SkCharToGlyphCache cache;
266 const int step = 3;
267
268 UnicharGen gen(step);
269 for (int i = 0; i < 500; ++i) {
270 SkUnichar c = gen.next();
271 SkGlyphID glyph = hash_to_glyph(c);
272
273 int index = cache.findGlyphIndex(c);
274 if (index >= 0) {
275 index = cache.findGlyphIndex(c);
276 }
277 REPORTER_ASSERT(reporter, index < 0);
278 cache.insertCharAndGlyph(~index, c, glyph);
279
280 UnicharGen gen2(step);
281 for (int j = 0; j <= i; ++j) {
282 c = gen2.next();
283 glyph = hash_to_glyph(c);
284 index = cache.findGlyphIndex(c);
285 if ((unsigned)index != glyph) {
286 index = cache.findGlyphIndex(c);
287 }
288 REPORTER_ASSERT(reporter, (unsigned)index == glyph);
289 }
290 }
291 }
292