1 /* 2 * Copyright 2015 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 #if SK_SUPPORT_GPU 10 #include "GrCaps.h" 11 #include "GrContext.h" 12 #include "GrResourceProvider.h" 13 #include "SkCanvas.h" 14 #include "SkSurface.h" 15 16 // using anonymous namespace because these functions are used as template params. 17 namespace { 18 /** convert 0..1 srgb value to 0..1 linear */ 19 float srgb_to_linear(float srgb) { 20 if (srgb <= 0.04045f) { 21 return srgb / 12.92f; 22 } else { 23 return powf((srgb + 0.055f) / 1.055f, 2.4f); 24 } 25 } 26 27 /** convert 0..1 linear value to 0..1 srgb */ 28 float linear_to_srgb(float linear) { 29 if (linear <= 0.0031308) { 30 return linear * 12.92f; 31 } else { 32 return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f; 33 } 34 } 35 } 36 37 /** tests a conversion with an error tolerance */ 38 template <float (*CONVERT)(float)> static bool check_conversion(uint32_t input, uint32_t output, 39 float error) { 40 // alpha should always be exactly preserved. 41 if ((input & 0xff000000) != (output & 0xff000000)) { 42 return false; 43 } 44 45 for (int c = 0; c < 3; ++c) { 46 uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8)); 47 float lower = SkTMax(0.f, (float) inputComponent - error); 48 float upper = SkTMin(255.f, (float) inputComponent + error); 49 lower = CONVERT(lower / 255.f); 50 upper = CONVERT(upper / 255.f); 51 SkASSERT(lower >= 0.f && lower <= 255.f); 52 SkASSERT(upper >= 0.f && upper <= 255.f); 53 uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8); 54 if (outputComponent < SkScalarFloorToInt(lower * 255.f) || 55 outputComponent > SkScalarCeilToInt(upper * 255.f)) { 56 return false; 57 } 58 } 59 return true; 60 } 61 62 /** tests a forward and backward conversion with an error tolerance */ 63 template <float (*FORWARD)(float), float (*BACKWARD)(float)> 64 static bool check_double_conversion(uint32_t input, uint32_t output, float error) { 65 // alpha should always be exactly preserved. 66 if ((input & 0xff000000) != (output & 0xff000000)) { 67 return false; 68 } 69 70 for (int c = 0; c < 3; ++c) { 71 uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8)); 72 float lower = SkTMax(0.f, (float) inputComponent - error); 73 float upper = SkTMin(255.f, (float) inputComponent + error); 74 lower = FORWARD(lower / 255.f); 75 upper = FORWARD(upper / 255.f); 76 SkASSERT(lower >= 0.f && lower <= 255.f); 77 SkASSERT(upper >= 0.f && upper <= 255.f); 78 uint8_t upperComponent = SkScalarCeilToInt(upper * 255.f); 79 uint8_t lowerComponent = SkScalarFloorToInt(lower * 255.f); 80 lower = SkTMax(0.f, (float) lowerComponent - error); 81 upper = SkTMin(255.f, (float) upperComponent + error); 82 lower = BACKWARD(lowerComponent / 255.f); 83 upper = BACKWARD(upperComponent / 255.f); 84 SkASSERT(lower >= 0.f && lower <= 255.f); 85 SkASSERT(upper >= 0.f && upper <= 255.f); 86 upperComponent = SkScalarCeilToInt(upper * 255.f); 87 lowerComponent = SkScalarFloorToInt(lower * 255.f); 88 89 uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8); 90 if (outputComponent < lowerComponent || outputComponent > upperComponent) { 91 return false; 92 } 93 } 94 return true; 95 } 96 97 static bool check_srgb_to_linear_conversion(uint32_t srgb, uint32_t linear, float error) { 98 return check_conversion<srgb_to_linear>(srgb, linear, error); 99 } 100 101 static bool check_linear_to_srgb_conversion(uint32_t linear, uint32_t srgb, float error) { 102 return check_conversion<linear_to_srgb>(linear, srgb, error); 103 } 104 105 static bool check_linear_to_srgb_to_linear_conversion(uint32_t input, uint32_t output, float error) { 106 return check_double_conversion<linear_to_srgb, srgb_to_linear>(input, output, error); 107 } 108 109 static bool check_srgb_to_linear_to_srgb_conversion(uint32_t input, uint32_t output, float error) { 110 return check_double_conversion<srgb_to_linear, linear_to_srgb>(input, output, error); 111 } 112 113 typedef bool (*CheckFn) (uint32_t orig, uint32_t actual, float error); 114 115 void read_and_check_pixels(skiatest::Reporter* reporter, GrTexture* texture, uint32_t* origData, 116 GrPixelConfig readConfig, CheckFn checker, float error, 117 const char* subtestName) { 118 int w = texture->width(); 119 int h = texture->height(); 120 SkAutoTMalloc<uint32_t> readData(w * h); 121 memset(readData.get(), 0, sizeof(uint32_t) * w * h); 122 if (!texture->readPixels(0, 0, w, h, readConfig, readData.get())) { 123 ERRORF(reporter, "Could not read pixels for %s.", subtestName); 124 return; 125 } 126 for (int j = 0; j < h; ++j) { 127 for (int i = 0; i < w; ++i) { 128 uint32_t orig = origData[j * w + i]; 129 uint32_t read = readData[j * w + i]; 130 131 if (!checker(orig, read, error)) { 132 ERRORF(reporter, "Expected 0x%08x, read back as 0x%08x in %s at %d, %d).", 133 orig, read, subtestName, i, j); 134 return; 135 } 136 } 137 } 138 } 139 140 // TODO: Add tests for copySurface between srgb/linear textures. Add tests for unpremul/premul 141 // conversion during read/write along with srgb/linear conversions. 142 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SRGBReadWritePixels, reporter, ctxInfo) { 143 GrContext* context = ctxInfo.grContext(); 144 #if defined(GOOGLE3) 145 // Stack frame size is limited in GOOGLE3. 146 static const int kW = 63; 147 static const int kH = 63; 148 #else 149 static const int kW = 255; 150 static const int kH = 255; 151 #endif 152 uint32_t origData[kW * kH]; 153 for (int j = 0; j < kH; ++j) { 154 for (int i = 0; i < kW; ++i) { 155 origData[j * kW + i] = (j << 24) | (i << 16) | (i << 8) | i; 156 } 157 } 158 159 GrSurfaceDesc desc; 160 desc.fFlags = kRenderTarget_GrSurfaceFlag; 161 desc.fWidth = kW; 162 desc.fHeight = kH; 163 desc.fConfig = kSRGBA_8888_GrPixelConfig; 164 if (context->caps()->isConfigRenderable(desc.fConfig, false) && 165 context->caps()->isConfigTexturable(desc.fConfig)) { 166 sk_sp<GrTexture> tex(context->resourceProvider()->createTexture(desc, SkBudgeted::kNo)); 167 if (!tex) { 168 ERRORF(reporter, "Could not create SRGBA texture."); 169 return; 170 } 171 172 float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f : 0.5f; 173 174 // Write srgba data and read as srgba and then as rgba 175 if (tex->writePixels(0, 0, kW, kH, kSRGBA_8888_GrPixelConfig, origData)) { 176 // For the all-srgba case, we allow a small error only for devices that have 177 // precision variation because the srgba data gets converted to linear and back in 178 // the shader. 179 float smallError = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.f : 180 0.0f; 181 read_and_check_pixels(reporter, tex.get(), origData, kSRGBA_8888_GrPixelConfig, 182 check_srgb_to_linear_to_srgb_conversion, smallError, 183 "write/read srgba to srgba texture"); 184 read_and_check_pixels(reporter, tex.get(), origData, kRGBA_8888_GrPixelConfig, 185 check_srgb_to_linear_conversion, error, 186 "write srgba/read rgba with srgba texture"); 187 } else { 188 ERRORF(reporter, "Could not write srgba data to srgba texture."); 189 } 190 191 // Now verify that we can write linear data 192 if (tex->writePixels(0, 0, kW, kH, kRGBA_8888_GrPixelConfig, origData)) { 193 // We allow more error on GPUs with lower precision shader variables. 194 read_and_check_pixels(reporter, tex.get(), origData, kSRGBA_8888_GrPixelConfig, 195 check_linear_to_srgb_conversion, error, 196 "write rgba/read srgba with srgba texture"); 197 read_and_check_pixels(reporter, tex.get(), origData, kRGBA_8888_GrPixelConfig, 198 check_linear_to_srgb_to_linear_conversion, error, 199 "write/read rgba with srgba texture"); 200 } else { 201 ERRORF(reporter, "Could not write rgba data to srgba texture."); 202 } 203 204 desc.fConfig = kRGBA_8888_GrPixelConfig; 205 tex.reset(context->resourceProvider()->createTexture(desc, SkBudgeted::kNo)); 206 if (!tex) { 207 ERRORF(reporter, "Could not create RGBA texture."); 208 return; 209 } 210 211 // Write srgba data to a rgba texture and read back as srgba and rgba 212 if (tex->writePixels(0, 0, kW, kH, kSRGBA_8888_GrPixelConfig, origData)) { 213 read_and_check_pixels(reporter, tex.get(), origData, kSRGBA_8888_GrPixelConfig, 214 check_srgb_to_linear_to_srgb_conversion, error, 215 "write/read srgba to rgba texture"); 216 read_and_check_pixels(reporter, tex.get(), origData, kRGBA_8888_GrPixelConfig, 217 check_srgb_to_linear_conversion, error, 218 "write srgba/read rgba to rgba texture"); 219 } else { 220 ERRORF(reporter, "Could not write srgba data to rgba texture."); 221 } 222 223 // Write rgba data to a rgba texture and read back as srgba 224 if (tex->writePixels(0, 0, kW, kH, kRGBA_8888_GrPixelConfig, origData)) { 225 read_and_check_pixels(reporter, tex.get(), origData, kSRGBA_8888_GrPixelConfig, 226 check_linear_to_srgb_conversion, 1.2f, 227 "write rgba/read srgba to rgba texture"); 228 } else { 229 ERRORF(reporter, "Could not write rgba data to rgba texture."); 230 } 231 } 232 } 233 #endif 234