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