| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 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 | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 10 | #include "GrCaps.h" | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 11 | #include "GrContext.h" | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 12 | #include "GrContextPriv.h" | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 13 | #include "GrSurfaceContext.h" | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 14 | #include "SkCanvas.h" | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 15 | #include "SkGr.h" | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 16 | #include "SkSurface.h" | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 17 |  | 
 | 18 | // using anonymous namespace because these functions are used as template params. | 
 | 19 | namespace { | 
 | 20 | /** convert 0..1 srgb value to 0..1 linear */ | 
 | 21 | float srgb_to_linear(float srgb) { | 
 | 22 |     if (srgb <= 0.04045f) { | 
 | 23 |         return srgb / 12.92f; | 
 | 24 |     } else { | 
 | 25 |         return powf((srgb + 0.055f) / 1.055f, 2.4f); | 
 | 26 |     } | 
 | 27 | } | 
 | 28 |  | 
 | 29 | /** convert 0..1 linear value to 0..1 srgb */ | 
 | 30 | float linear_to_srgb(float linear) { | 
 | 31 |     if (linear <= 0.0031308) { | 
 | 32 |         return linear * 12.92f; | 
 | 33 |     } else { | 
 | 34 |         return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f; | 
 | 35 |     } | 
 | 36 | } | 
 | 37 | } | 
 | 38 |  | 
 | 39 | /** tests a conversion with an error tolerance */ | 
 | 40 | template <float (*CONVERT)(float)> static bool check_conversion(uint32_t input, uint32_t output, | 
 | 41 |                                                                 float error) { | 
 | 42 |     // alpha should always be exactly preserved. | 
 | 43 |     if ((input & 0xff000000) != (output & 0xff000000)) { | 
 | 44 |         return false; | 
 | 45 |     } | 
 | 46 |  | 
 | 47 |     for (int c = 0; c < 3; ++c) { | 
 | 48 |         uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8)); | 
 | 49 |         float lower = SkTMax(0.f, (float) inputComponent - error); | 
 | 50 |         float upper = SkTMin(255.f, (float) inputComponent + error); | 
 | 51 |         lower = CONVERT(lower / 255.f); | 
 | 52 |         upper = CONVERT(upper / 255.f); | 
 | 53 |         SkASSERT(lower >= 0.f && lower <= 255.f); | 
 | 54 |         SkASSERT(upper >= 0.f && upper <= 255.f); | 
 | 55 |         uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8); | 
 | 56 |         if (outputComponent < SkScalarFloorToInt(lower * 255.f) || | 
 | 57 |             outputComponent > SkScalarCeilToInt(upper * 255.f)) { | 
 | 58 |             return false; | 
 | 59 |         } | 
 | 60 |     } | 
 | 61 |     return true; | 
 | 62 | } | 
 | 63 |  | 
 | 64 | /** tests a forward and backward conversion with an error tolerance */ | 
 | 65 | template <float (*FORWARD)(float), float (*BACKWARD)(float)> | 
 | 66 | static bool check_double_conversion(uint32_t input, uint32_t output, float error) { | 
 | 67 |     // alpha should always be exactly preserved. | 
 | 68 |     if ((input & 0xff000000) != (output & 0xff000000)) { | 
 | 69 |         return false; | 
 | 70 |     } | 
 | 71 |  | 
 | 72 |     for (int c = 0; c < 3; ++c) { | 
 | 73 |         uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8)); | 
 | 74 |         float lower = SkTMax(0.f, (float) inputComponent - error); | 
 | 75 |         float upper = SkTMin(255.f, (float) inputComponent + error); | 
 | 76 |         lower = FORWARD(lower / 255.f); | 
 | 77 |         upper = FORWARD(upper / 255.f); | 
 | 78 |         SkASSERT(lower >= 0.f && lower <= 255.f); | 
 | 79 |         SkASSERT(upper >= 0.f && upper <= 255.f); | 
 | 80 |         uint8_t upperComponent = SkScalarCeilToInt(upper * 255.f); | 
 | 81 |         uint8_t lowerComponent = SkScalarFloorToInt(lower * 255.f); | 
 | 82 |         lower = SkTMax(0.f, (float) lowerComponent - error); | 
 | 83 |         upper = SkTMin(255.f, (float) upperComponent + error); | 
 | 84 |         lower = BACKWARD(lowerComponent / 255.f); | 
 | 85 |         upper = BACKWARD(upperComponent / 255.f); | 
 | 86 |         SkASSERT(lower >= 0.f && lower <= 255.f); | 
 | 87 |         SkASSERT(upper >= 0.f && upper <= 255.f); | 
 | 88 |         upperComponent = SkScalarCeilToInt(upper * 255.f); | 
 | 89 |         lowerComponent = SkScalarFloorToInt(lower * 255.f); | 
 | 90 |  | 
 | 91 |         uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8); | 
 | 92 |         if (outputComponent < lowerComponent || outputComponent > upperComponent) { | 
 | 93 |             return false; | 
 | 94 |         } | 
 | 95 |     } | 
 | 96 |     return true; | 
 | 97 | } | 
 | 98 |  | 
 | 99 | static bool check_srgb_to_linear_conversion(uint32_t srgb, uint32_t linear, float error) { | 
 | 100 |     return check_conversion<srgb_to_linear>(srgb, linear, error); | 
 | 101 | } | 
 | 102 |  | 
 | 103 | static bool check_linear_to_srgb_conversion(uint32_t linear, uint32_t srgb, float error) { | 
 | 104 |     return check_conversion<linear_to_srgb>(linear, srgb, error); | 
 | 105 | } | 
 | 106 |  | 
 | 107 | static bool check_linear_to_srgb_to_linear_conversion(uint32_t input, uint32_t output, float error) { | 
 | 108 |     return check_double_conversion<linear_to_srgb, srgb_to_linear>(input, output, error); | 
 | 109 | } | 
 | 110 |  | 
 | 111 | static bool check_srgb_to_linear_to_srgb_conversion(uint32_t input, uint32_t output, float error) { | 
 | 112 |     return check_double_conversion<srgb_to_linear, linear_to_srgb>(input, output, error); | 
 | 113 | } | 
 | 114 |  | 
 | 115 | typedef bool (*CheckFn) (uint32_t orig, uint32_t actual, float error); | 
 | 116 |  | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 117 | void read_and_check_pixels(skiatest::Reporter* reporter, GrSurfaceContext* context, | 
 | 118 |                            uint32_t* origData, | 
 | 119 |                            const SkImageInfo& dstInfo, CheckFn checker, float error, | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 120 |                            const char* subtestName) { | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 121 |     int w = dstInfo.width(); | 
 | 122 |     int h = dstInfo.height(); | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 123 |     SkAutoTMalloc<uint32_t> readData(w * h); | 
 | 124 |     memset(readData.get(), 0, sizeof(uint32_t) * w * h); | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 125 |  | 
 | 126 |     if (!context->readPixels(dstInfo, readData.get(), 0, 0, 0)) { | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 127 |         ERRORF(reporter, "Could not read pixels for %s.", subtestName); | 
 | 128 |         return; | 
 | 129 |     } | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 130 |  | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 131 |     for (int j = 0; j < h; ++j) { | 
 | 132 |         for (int i = 0; i < w; ++i) { | 
 | 133 |             uint32_t orig = origData[j * w + i]; | 
 | 134 |             uint32_t read = readData[j * w + i]; | 
 | 135 |  | 
 | 136 |             if (!checker(orig, read, error)) { | 
 | 137 |                 ERRORF(reporter, "Expected 0x%08x, read back as 0x%08x in %s at %d, %d).", | 
 | 138 |                        orig, read, subtestName, i, j); | 
 | 139 |                 return; | 
 | 140 |             } | 
 | 141 |         } | 
 | 142 |     } | 
 | 143 | } | 
 | 144 |  | 
 | 145 | // TODO: Add tests for copySurface between srgb/linear textures. Add tests for unpremul/premul | 
 | 146 | // conversion during read/write along with srgb/linear conversions. | 
| egdaniel | ab527a5 | 2016-06-28 08:07:26 -0700 | [diff] [blame] | 147 | DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SRGBReadWritePixels, reporter, ctxInfo) { | 
| bsalomon | 8b7451a | 2016-05-11 06:33:06 -0700 | [diff] [blame] | 148 |     GrContext* context = ctxInfo.grContext(); | 
| benjaminwagner | 7d974f5 | 2015-10-19 13:55:55 -0700 | [diff] [blame] | 149 | #if defined(GOOGLE3) | 
 | 150 |     // Stack frame size is limited in GOOGLE3. | 
 | 151 |     static const int kW = 63; | 
 | 152 |     static const int kH = 63; | 
 | 153 | #else | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 154 |     static const int kW = 255; | 
 | 155 |     static const int kH = 255; | 
| benjaminwagner | 7d974f5 | 2015-10-19 13:55:55 -0700 | [diff] [blame] | 156 | #endif | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 157 |     uint32_t origData[kW * kH]; | 
 | 158 |     for (int j = 0; j < kH; ++j) { | 
 | 159 |         for (int i = 0; i < kW; ++i) { | 
 | 160 |             origData[j * kW + i] = (j << 24) | (i << 16) | (i << 8) | i; | 
 | 161 |         } | 
 | 162 |     } | 
 | 163 |  | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 164 |     const SkImageInfo iiSRGBA = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, | 
 | 165 |                                                   kPremul_SkAlphaType, | 
 | 166 |                                                   SkColorSpace::MakeSRGB()); | 
 | 167 |     const SkImageInfo iiRGBA = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, | 
 | 168 |                                                  kPremul_SkAlphaType); | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 169 |     GrSurfaceDesc desc; | 
 | 170 |     desc.fFlags = kRenderTarget_GrSurfaceFlag; | 
| Robert Phillips | e78b725 | 2017-04-06 07:59:41 -0400 | [diff] [blame] | 171 |     desc.fOrigin = kBottomLeft_GrSurfaceOrigin; | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 172 |     desc.fWidth = kW; | 
 | 173 |     desc.fHeight = kH; | 
 | 174 |     desc.fConfig = kSRGBA_8888_GrPixelConfig; | 
 | 175 |     if (context->caps()->isConfigRenderable(desc.fConfig, false) && | 
 | 176 |         context->caps()->isConfigTexturable(desc.fConfig)) { | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 177 |  | 
 | 178 |         sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeDeferredSurfaceContext( | 
| Greg Daniel | 65c7f66 | 2017-10-30 13:39:09 -0400 | [diff] [blame] | 179 |                                                                              desc, GrMipMapped::kNo, | 
 | 180 |                                                                              SkBackingFit::kExact, | 
 | 181 |                                                                              SkBudgeted::kNo); | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 182 |         if (!sContext) { | 
 | 183 |             ERRORF(reporter, "Could not create SRGBA surface context."); | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 184 |             return; | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 185 |         } | 
 | 186 |  | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 187 |         float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f  : 0.5f; | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 188 |  | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 189 |         // Write srgba data and read as srgba and then as rgba | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 190 |         if (sContext->writePixels(iiSRGBA, origData, 0, 0, 0)) { | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 191 |             // For the all-srgba case, we allow a small error only for devices that have | 
 | 192 |             // precision variation because the srgba data gets converted to linear and back in | 
 | 193 |             // the shader. | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 194 |             float smallError = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.f : 0.0f; | 
 | 195 |             read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA, | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 196 |                                   check_srgb_to_linear_to_srgb_conversion, smallError, | 
 | 197 |                                   "write/read srgba to srgba texture"); | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 198 |             read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA, | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 199 |                                   check_srgb_to_linear_conversion, error, | 
 | 200 |                                   "write srgba/read rgba with srgba texture"); | 
 | 201 |         } else { | 
 | 202 |             ERRORF(reporter, "Could not write srgba data to srgba texture."); | 
 | 203 |         } | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 204 |  | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 205 |         // Now verify that we can write linear data | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 206 |         if (sContext->writePixels(iiRGBA, origData, 0, 0, 0)) { | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 207 |             // We allow more error on GPUs with lower precision shader variables. | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 208 |             read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA, | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 209 |                                   check_linear_to_srgb_conversion, error, | 
 | 210 |                                   "write rgba/read srgba with srgba texture"); | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 211 |             read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA, | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 212 |                                   check_linear_to_srgb_to_linear_conversion, error, | 
 | 213 |                                   "write/read rgba with srgba texture"); | 
 | 214 |         } else { | 
 | 215 |             ERRORF(reporter, "Could not write rgba data to srgba texture."); | 
 | 216 |         } | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 217 |  | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 218 |         desc.fConfig = kRGBA_8888_GrPixelConfig; | 
| Greg Daniel | 65c7f66 | 2017-10-30 13:39:09 -0400 | [diff] [blame] | 219 |         sContext = context->contextPriv().makeDeferredSurfaceContext(desc, | 
 | 220 |                                                                      GrMipMapped::kNo, | 
 | 221 |                                                                      SkBackingFit::kExact, | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 222 |                                                                      SkBudgeted::kNo); | 
 | 223 |         if (!sContext) { | 
 | 224 |             ERRORF(reporter, "Could not create RGBA surface context."); | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 225 |             return; | 
 | 226 |         } | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 227 |  | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 228 |         // Write srgba data to a rgba texture and read back as srgba and rgba | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 229 |         if (sContext->writePixels(iiSRGBA, origData, 0, 0, 0)) { | 
| Brian Osman | 7ab6a7f | 2017-04-25 15:01:46 -0400 | [diff] [blame] | 230 | #if 0 | 
 | 231 |             // We don't support this conversion (read from untagged source into tagged destination. | 
 | 232 |             // If we decide there is a meaningful way to implement this, restore this test. | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 233 |             read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA, | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 234 |                                   check_srgb_to_linear_to_srgb_conversion, error, | 
 | 235 |                                   "write/read srgba to rgba texture"); | 
| Brian Osman | 7ab6a7f | 2017-04-25 15:01:46 -0400 | [diff] [blame] | 236 | #endif | 
 | 237 |             // We expect the sRGB -> linear write to do no sRGB conversion (to match the behavior of | 
 | 238 |             // drawing tagged sources). skbug.com/6547. So the data we read should still contain | 
 | 239 |             // sRGB encoded values. | 
 | 240 |             // | 
 | 241 |             // srgb_to_linear_to_srgb is a proxy for the expected identity transform. | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 242 |             read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA, | 
| Brian Osman | 7ab6a7f | 2017-04-25 15:01:46 -0400 | [diff] [blame] | 243 |                                   check_srgb_to_linear_to_srgb_conversion, error, | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 244 |                                   "write srgba/read rgba to rgba texture"); | 
 | 245 |         } else { | 
 | 246 |             ERRORF(reporter, "Could not write srgba data to rgba texture."); | 
 | 247 |         } | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 248 |  | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 249 |         // Write rgba data to a rgba texture and read back as srgba | 
| Robert Phillips | 301431d | 2017-03-29 12:08:49 -0400 | [diff] [blame] | 250 |         if (sContext->writePixels(iiRGBA, origData, 0, 0, 0)) { | 
 | 251 |             read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA, | 
| kkinnunen | 1530283 | 2015-12-01 04:35:26 -0800 | [diff] [blame] | 252 |                                   check_linear_to_srgb_conversion, 1.2f, | 
 | 253 |                                   "write rgba/read srgba to rgba texture"); | 
 | 254 |         } else { | 
 | 255 |             ERRORF(reporter, "Could not write rgba data to rgba texture."); | 
 | 256 |         } | 
| bsalomon | 16921ec | 2015-07-30 15:34:56 -0700 | [diff] [blame] | 257 |     } | 
 | 258 | } | 
 | 259 | #endif |