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/SkAutoPixmapStorage.h" 13#include "src/core/SkUtils.h" 14#include "src/gpu/GrDirectContextPriv.h" 15#include "src/gpu/GrDrawingManager.h" 16#include "src/gpu/GrGpu.h" 17#include "src/gpu/GrImageInfo.h" 18#include "src/gpu/GrRecordingContextPriv.h" 19#include "src/gpu/GrSurfaceProxy.h" 20#include "src/gpu/GrTextureProxy.h" 21#include "src/gpu/SkGr.h" 22#include "src/gpu/SurfaceContext.h" 23 24void TestReadPixels(skiatest::Reporter* reporter, 25 GrDirectContext* dContext, 26 skgpu::SurfaceContext* srcContext, 27 uint32_t expectedPixelValues[], 28 const char* testName) { 29 int pixelCnt = srcContext->width() * srcContext->height(); 30 SkImageInfo ii = SkImageInfo::Make(srcContext->dimensions(), 31 kRGBA_8888_SkColorType, 32 kPremul_SkAlphaType); 33 SkAutoPixmapStorage pm; 34 pm.alloc(ii); 35 pm.erase(SK_ColorTRANSPARENT); 36 37 bool read = srcContext->readPixels(dContext, pm, {0, 0}); 38 if (!read) { 39 ERRORF(reporter, "%s: Error reading from texture.", testName); 40 } 41 42 for (int i = 0; i < pixelCnt; ++i) { 43 if (pm.addr32()[i] != expectedPixelValues[i]) { 44 ERRORF(reporter, "%s: Error, pixel value %d should be 0x%08x, got 0x%08x.", 45 testName, i, expectedPixelValues[i], pm.addr32()[i]); 46 break; 47 } 48 } 49} 50 51void TestWritePixels(skiatest::Reporter* reporter, 52 GrDirectContext* dContext, 53 skgpu::SurfaceContext* dstContext, 54 bool expectedToWork, 55 const char* testName) { 56 SkImageInfo ii = SkImageInfo::Make(dstContext->dimensions(), 57 kRGBA_8888_SkColorType, 58 kPremul_SkAlphaType); 59 SkAutoPixmapStorage pm; 60 pm.alloc(ii); 61 for (int y = 0; y < dstContext->height(); ++y) { 62 for (int x = 0; x < dstContext->width(); ++x) { 63 *pm.writable_addr32(x, y) = SkColorToPremulGrColor(SkColorSetARGB(2*y, x, y, x + y)); 64 } 65 } 66 67 bool write = dstContext->writePixels(dContext, pm, {0, 0}); 68 if (!write) { 69 if (expectedToWork) { 70 ERRORF(reporter, "%s: Error writing to texture.", testName); 71 } 72 return; 73 } 74 75 if (write && !expectedToWork) { 76 ERRORF(reporter, "%s: writePixels succeeded when it wasn't supposed to.", testName); 77 return; 78 } 79 80 TestReadPixels(reporter, dContext, dstContext, pm.writable_addr32(0, 0), testName); 81} 82 83void TestCopyFromSurface(skiatest::Reporter* reporter, 84 GrDirectContext* dContext, 85 sk_sp<GrSurfaceProxy> proxy, 86 GrSurfaceOrigin origin, 87 GrColorType colorType, 88 uint32_t expectedPixelValues[], 89 const char* testName) { 90 auto copy = GrSurfaceProxy::Copy(dContext, std::move(proxy), origin, GrMipmapped::kNo, 91 SkBackingFit::kExact, SkBudgeted::kYes); 92 SkASSERT(copy && copy->asTextureProxy()); 93 auto swizzle = dContext->priv().caps()->getReadSwizzle(copy->backendFormat(), colorType); 94 GrSurfaceProxyView view(std::move(copy), origin, swizzle); 95 auto dstContext = dContext->priv().makeSC(std::move(view), 96 {colorType, kPremul_SkAlphaType, nullptr}); 97 SkASSERT(dstContext); 98 99 TestReadPixels(reporter, dContext, dstContext.get(), expectedPixelValues, testName); 100} 101 102bool BipmapToBase64DataURI(const SkBitmap& bitmap, SkString* dst) { 103 SkPixmap pm; 104 if (!bitmap.peekPixels(&pm)) { 105 dst->set("peekPixels failed"); 106 return false; 107 } 108 109 // We're going to embed this PNG in a data URI, so make it as small as possible 110 SkPngEncoder::Options options; 111 options.fFilterFlags = SkPngEncoder::FilterFlag::kAll; 112 options.fZLibLevel = 9; 113 114 SkDynamicMemoryWStream wStream; 115 if (!SkPngEncoder::Encode(&wStream, pm, options)) { 116 dst->set("SkPngEncoder::Encode failed"); 117 return false; 118 } 119 120 sk_sp<SkData> pngData = wStream.detachAsData(); 121 size_t len = SkBase64::Encode(pngData->data(), pngData->size(), nullptr); 122 123 // The PNG can be almost arbitrarily large. We don't want to fill our logs with enormous URLs. 124 // Infra says these can be pretty big, as long as we're only outputting them on failure. 125 static const size_t kMaxBase64Length = 1024 * 1024; 126 if (len > kMaxBase64Length) { 127 dst->printf("Encoded image too large (%u bytes)", static_cast<uint32_t>(len)); 128 return false; 129 } 130 131 dst->resize(len); 132 SkBase64::Encode(pngData->data(), pngData->size(), dst->writable_str()); 133 dst->prepend("data:image/png;base64,"); 134 return true; 135} 136 137static bool compare_colors(int x, int y, 138 const float rgbaA[], 139 const float rgbaB[], 140 const float tolRGBA[4], 141 std::function<ComparePixmapsErrorReporter>& error) { 142 float diffs[4]; 143 bool bad = false; 144 for (int i = 0; i < 4; ++i) { 145 diffs[i] = rgbaB[i] - rgbaA[i]; 146 if (std::abs(diffs[i]) > std::abs(tolRGBA[i])) { 147 bad = true; 148 } 149 } 150 if (bad) { 151 error(x, y, diffs); 152 return false; 153 } 154 return true; 155} 156 157bool ComparePixels(const GrCPixmap& a, 158 const GrCPixmap& b, 159 const float tolRGBA[4], 160 std::function<ComparePixmapsErrorReporter>& error) { 161 if (a.dimensions() != b.dimensions()) { 162 static constexpr float kEmptyDiffs[4] = {}; 163 error(-1, -1, kEmptyDiffs); 164 return false; 165 } 166 167 SkAlphaType floatAlphaType = a.alphaType(); 168 // If one is premul and the other is unpremul we do the comparison in premul space. 169 if ((a.alphaType() == kPremul_SkAlphaType || b.alphaType() == kPremul_SkAlphaType) && 170 (a.alphaType() == kUnpremul_SkAlphaType || b.alphaType() == kUnpremul_SkAlphaType)) { 171 floatAlphaType = kPremul_SkAlphaType; 172 } 173 sk_sp<SkColorSpace> floatCS; 174 if (SkColorSpace::Equals(a.colorSpace(), b.colorSpace())) { 175 floatCS = a.refColorSpace(); 176 } else { 177 floatCS = SkColorSpace::MakeSRGBLinear(); 178 } 179 GrImageInfo floatInfo(GrColorType::kRGBA_F32, 180 floatAlphaType, 181 std::move(floatCS), 182 a.dimensions()); 183 184 GrPixmap floatA = GrPixmap::Allocate(floatInfo); 185 GrPixmap floatB = GrPixmap::Allocate(floatInfo); 186 SkAssertResult(GrConvertPixels(floatA, a)); 187 SkAssertResult(GrConvertPixels(floatB, b)); 188 189 SkASSERT(floatA.rowBytes() == floatB.rowBytes()); 190 auto at = [rb = floatA.rowBytes()](const void* base, int x, int y) { 191 return SkTAddOffset<const float>(base, y*rb + x*sizeof(float)*4); 192 }; 193 194 for (int y = 0; y < floatA.height(); ++y) { 195 for (int x = 0; x < floatA.width(); ++x) { 196 const float* rgbaA = at(floatA.addr(), x, y); 197 const float* rgbaB = at(floatB.addr(), x, y); 198 if (!compare_colors(x, y, rgbaA, rgbaB, tolRGBA, error)) { 199 return false; 200 } 201 } 202 } 203 return true; 204} 205 206bool CheckSolidPixels(const SkColor4f& col, 207 const SkPixmap& pixmap, 208 const float tolRGBA[4], 209 std::function<ComparePixmapsErrorReporter>& error) { 210 size_t floatBpp = GrColorTypeBytesPerPixel(GrColorType::kRGBA_F32); 211 212 // First convert 'col' to be compatible with 'pixmap' 213 GrPixmap colorPixmap; 214 { 215 sk_sp<SkColorSpace> srcCS = SkColorSpace::MakeSRGBLinear(); 216 GrImageInfo srcInfo(GrColorType::kRGBA_F32, 217 kUnpremul_SkAlphaType, 218 std::move(srcCS), 219 {1, 1}); 220 GrCPixmap srcPixmap(srcInfo, col.vec(), floatBpp); 221 GrImageInfo dstInfo = 222 srcInfo.makeAlphaType(pixmap.alphaType()).makeColorSpace(pixmap.refColorSpace()); 223 colorPixmap = GrPixmap::Allocate(dstInfo); 224 SkAssertResult(GrConvertPixels(colorPixmap, srcPixmap)); 225 } 226 227 size_t floatRowBytes = floatBpp * pixmap.width(); 228 std::unique_ptr<char[]> floatB(new char[floatRowBytes * pixmap.height()]); 229 // Then convert 'pixmap' to RGBA_F32 230 GrPixmap f32Pixmap = GrPixmap::Allocate(pixmap.info().makeColorType(kRGBA_F32_SkColorType)); 231 SkAssertResult(GrConvertPixels(f32Pixmap, pixmap)); 232 233 for (int y = 0; y < f32Pixmap.height(); ++y) { 234 for (int x = 0; x < f32Pixmap.width(); ++x) { 235 auto rgbaA = SkTAddOffset<const float>(f32Pixmap.addr(), 236 f32Pixmap.rowBytes()*y + floatBpp*x); 237 auto rgbaB = static_cast<const float*>(colorPixmap.addr()); 238 if (!compare_colors(x, y, rgbaA, rgbaB, tolRGBA, error)) { 239 return false; 240 } 241 } 242 } 243 return true; 244} 245 246void CheckSingleThreadedProxyRefs(skiatest::Reporter* reporter, 247 GrSurfaceProxy* proxy, 248 int32_t expectedProxyRefs, 249 int32_t expectedBackingRefs) { 250 int32_t actualBackingRefs = proxy->testingOnly_getBackingRefCnt(); 251 252 REPORTER_ASSERT(reporter, proxy->refCntGreaterThan(expectedProxyRefs - 1) && 253 !proxy->refCntGreaterThan(expectedProxyRefs)); 254 REPORTER_ASSERT(reporter, actualBackingRefs == expectedBackingRefs); 255} 256 257std::unique_ptr<skgpu::SurfaceContext> CreateSurfaceContext(GrRecordingContext* rContext, 258 const GrImageInfo& info, 259 SkBackingFit fit, 260 GrSurfaceOrigin origin, 261 GrRenderable renderable, 262 int sampleCount, 263 GrMipmapped mipmapped, 264 GrProtected isProtected, 265 SkBudgeted budgeted) { 266 GrBackendFormat format = rContext->priv().caps()->getDefaultBackendFormat(info.colorType(), 267 renderable); 268 return rContext->priv().makeSC(info, 269 format, 270 fit, 271 origin, 272 renderable, 273 sampleCount, 274 mipmapped, 275 isProtected, 276 budgeted); 277} 278 279#include "src/utils/SkCharToGlyphCache.h" 280 281static SkGlyphID hash_to_glyph(uint32_t value) { 282 return SkToU16(((value >> 16) ^ value) & 0xFFFF); 283} 284 285namespace { 286class UnicharGen { 287 SkUnichar fU; 288 const int fStep; 289public: 290 UnicharGen(int step) : fU(0), fStep(step) {} 291 292 SkUnichar next() { 293 fU += fStep; 294 return fU; 295 } 296}; 297} // namespace 298 299DEF_TEST(chartoglyph_cache, reporter) { 300 SkCharToGlyphCache cache; 301 const int step = 3; 302 303 UnicharGen gen(step); 304 for (int i = 0; i < 500; ++i) { 305 SkUnichar c = gen.next(); 306 SkGlyphID glyph = hash_to_glyph(c); 307 308 int index = cache.findGlyphIndex(c); 309 if (index >= 0) { 310 index = cache.findGlyphIndex(c); 311 } 312 REPORTER_ASSERT(reporter, index < 0); 313 cache.insertCharAndGlyph(~index, c, glyph); 314 315 UnicharGen gen2(step); 316 for (int j = 0; j <= i; ++j) { 317 c = gen2.next(); 318 glyph = hash_to_glyph(c); 319 index = cache.findGlyphIndex(c); 320 if ((unsigned)index != glyph) { 321 index = cache.findGlyphIndex(c); 322 } 323 REPORTER_ASSERT(reporter, (unsigned)index == glyph); 324 } 325 } 326} 327