SRGBReadWritePixelsTest.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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. #include "include/core/SkCanvas.h"
  8. #include "include/core/SkSurface.h"
  9. #include "include/gpu/GrContext.h"
  10. #include "src/gpu/GrCaps.h"
  11. #include "src/gpu/GrContextPriv.h"
  12. #include "src/gpu/GrRenderTargetContext.h"
  13. #include "src/gpu/GrSurfaceContext.h"
  14. #include "src/gpu/SkGr.h"
  15. #include "tests/Test.h"
  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. /** 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. /** tests a conversion with an error tolerance */
  36. template <float (*CONVERT)(float)> static bool check_conversion(uint32_t input, uint32_t output,
  37. float error) {
  38. // alpha should always be exactly preserved.
  39. if ((input & 0xff000000) != (output & 0xff000000)) {
  40. return false;
  41. }
  42. for (int c = 0; c < 3; ++c) {
  43. uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8));
  44. float lower = SkTMax(0.f, (float) inputComponent - error);
  45. float upper = SkTMin(255.f, (float) inputComponent + error);
  46. lower = CONVERT(lower / 255.f);
  47. upper = CONVERT(upper / 255.f);
  48. SkASSERT(lower >= 0.f && lower <= 255.f);
  49. SkASSERT(upper >= 0.f && upper <= 255.f);
  50. uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8);
  51. if (outputComponent < SkScalarFloorToInt(lower * 255.f) ||
  52. outputComponent > SkScalarCeilToInt(upper * 255.f)) {
  53. return false;
  54. }
  55. }
  56. return true;
  57. }
  58. /** tests a forward and backward conversion with an error tolerance */
  59. template <float (*FORWARD)(float), float (*BACKWARD)(float)>
  60. static bool check_double_conversion(uint32_t input, uint32_t output, float error) {
  61. // alpha should always be exactly preserved.
  62. if ((input & 0xff000000) != (output & 0xff000000)) {
  63. return false;
  64. }
  65. for (int c = 0; c < 3; ++c) {
  66. uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8));
  67. float lower = SkTMax(0.f, (float) inputComponent - error);
  68. float upper = SkTMin(255.f, (float) inputComponent + error);
  69. lower = FORWARD(lower / 255.f);
  70. upper = FORWARD(upper / 255.f);
  71. SkASSERT(lower >= 0.f && lower <= 255.f);
  72. SkASSERT(upper >= 0.f && upper <= 255.f);
  73. uint8_t upperComponent = SkScalarCeilToInt(upper * 255.f);
  74. uint8_t lowerComponent = SkScalarFloorToInt(lower * 255.f);
  75. lower = SkTMax(0.f, (float) lowerComponent - error);
  76. upper = SkTMin(255.f, (float) upperComponent + error);
  77. lower = BACKWARD(lowerComponent / 255.f);
  78. upper = BACKWARD(upperComponent / 255.f);
  79. SkASSERT(lower >= 0.f && lower <= 255.f);
  80. SkASSERT(upper >= 0.f && upper <= 255.f);
  81. upperComponent = SkScalarCeilToInt(upper * 255.f);
  82. lowerComponent = SkScalarFloorToInt(lower * 255.f);
  83. uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8);
  84. if (outputComponent < lowerComponent || outputComponent > upperComponent) {
  85. return false;
  86. }
  87. }
  88. return true;
  89. }
  90. static bool check_srgb_to_linear_conversion(uint32_t srgb, uint32_t linear, float error) {
  91. return check_conversion<srgb_to_linear>(srgb, linear, error);
  92. }
  93. static bool check_linear_to_srgb_conversion(uint32_t linear, uint32_t srgb, float error) {
  94. return check_conversion<linear_to_srgb>(linear, srgb, error);
  95. }
  96. static bool check_linear_to_srgb_to_linear_conversion(uint32_t input, uint32_t output, float error) {
  97. return check_double_conversion<linear_to_srgb, srgb_to_linear>(input, output, error);
  98. }
  99. static bool check_srgb_to_linear_to_srgb_conversion(uint32_t input, uint32_t output, float error) {
  100. return check_double_conversion<srgb_to_linear, linear_to_srgb>(input, output, error);
  101. }
  102. static bool check_no_conversion(uint32_t input, uint32_t output, float error) {
  103. // This is a bit of a hack to check identity transformations that may lose precision.
  104. return check_srgb_to_linear_to_srgb_conversion(input, output, error);
  105. }
  106. typedef bool (*CheckFn) (uint32_t orig, uint32_t actual, float error);
  107. void read_and_check_pixels(skiatest::Reporter* reporter, GrSurfaceContext* context,
  108. uint32_t* origData,
  109. const SkImageInfo& dstInfo, CheckFn checker, float error,
  110. const char* subtestName) {
  111. int w = dstInfo.width();
  112. int h = dstInfo.height();
  113. SkAutoTMalloc<uint32_t> readData(w * h);
  114. memset(readData.get(), 0, sizeof(uint32_t) * w * h);
  115. if (!context->readPixels(dstInfo, readData.get(), 0, {0, 0})) {
  116. ERRORF(reporter, "Could not read pixels for %s.", subtestName);
  117. return;
  118. }
  119. for (int j = 0; j < h; ++j) {
  120. for (int i = 0; i < w; ++i) {
  121. uint32_t orig = origData[j * w + i];
  122. uint32_t read = readData[j * w + i];
  123. if (!checker(orig, read, error)) {
  124. ERRORF(reporter, "Original 0x%08x, read back as 0x%08x in %s at %d, %d).", orig,
  125. read, subtestName, i, j);
  126. return;
  127. }
  128. }
  129. }
  130. }
  131. namespace {
  132. enum class Encoding {
  133. kUntagged,
  134. kLinear,
  135. kSRGB,
  136. };
  137. }
  138. static sk_sp<SkColorSpace> encoding_as_color_space(Encoding encoding) {
  139. switch (encoding) {
  140. case Encoding::kUntagged: return nullptr;
  141. case Encoding::kLinear: return SkColorSpace::MakeSRGBLinear();
  142. case Encoding::kSRGB: return SkColorSpace::MakeSRGB();
  143. }
  144. return nullptr;
  145. }
  146. static const char* encoding_as_str(Encoding encoding) {
  147. switch (encoding) {
  148. case Encoding::kUntagged: return "untagged";
  149. case Encoding::kLinear: return "linear";
  150. case Encoding::kSRGB: return "sRGB";
  151. }
  152. return nullptr;
  153. }
  154. static constexpr int kW = 255;
  155. static constexpr int kH = 255;
  156. static std::unique_ptr<uint32_t[]> make_data() {
  157. std::unique_ptr<uint32_t[]> data(new uint32_t[kW * kH]);
  158. for (int j = 0; j < kH; ++j) {
  159. for (int i = 0; i < kW; ++i) {
  160. data[j * kW + i] = (0xFF << 24) | (i << 16) | (i << 8) | i;
  161. }
  162. }
  163. return data;
  164. }
  165. static sk_sp<GrSurfaceContext> make_surface_context(Encoding contextEncoding, GrContext* context,
  166. skiatest::Reporter* reporter) {
  167. auto surfaceContext = context->priv().makeDeferredRenderTargetContext(
  168. SkBackingFit::kExact, kW, kH, GrColorType::kRGBA_8888,
  169. encoding_as_color_space(contextEncoding), 1, GrMipMapped::kNo,
  170. kBottomLeft_GrSurfaceOrigin, nullptr, SkBudgeted::kNo, GrProtected::kNo);
  171. if (!surfaceContext) {
  172. ERRORF(reporter, "Could not create %s surface context.", encoding_as_str(contextEncoding));
  173. }
  174. return std::move(surfaceContext);
  175. }
  176. static void test_write_read(Encoding contextEncoding, Encoding writeEncoding, Encoding readEncoding,
  177. float error, CheckFn check, GrContext* context,
  178. skiatest::Reporter* reporter) {
  179. auto surfaceContext = make_surface_context(contextEncoding, context, reporter);
  180. if (!surfaceContext) {
  181. return;
  182. }
  183. auto writeII = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
  184. encoding_as_color_space(writeEncoding));
  185. auto data = make_data();
  186. if (!surfaceContext->writePixels(writeII, data.get(), 0, {0, 0})) {
  187. ERRORF(reporter, "Could not write %s to %s surface context.",
  188. encoding_as_str(writeEncoding), encoding_as_str(contextEncoding));
  189. return;
  190. }
  191. auto readII = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
  192. encoding_as_color_space(readEncoding));
  193. SkString testName;
  194. testName.printf("write %s data to a %s context and read as %s.", encoding_as_str(writeEncoding),
  195. encoding_as_str(contextEncoding), encoding_as_str(readEncoding));
  196. read_and_check_pixels(reporter, surfaceContext.get(), data.get(), readII, check, error,
  197. testName.c_str());
  198. }
  199. // Test all combinations of writePixels/readPixels where the surface context/write source/read dst
  200. // are sRGB, linear, or untagged RGBA_8888.
  201. DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SRGBReadWritePixels, reporter, ctxInfo) {
  202. GrContext* context = ctxInfo.grContext();
  203. if (!context->priv().caps()->isConfigRenderable(kSRGBA_8888_GrPixelConfig) &&
  204. !context->priv().caps()->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) {
  205. return;
  206. }
  207. // We allow more error on GPUs with lower precision shader variables.
  208. float error = context->priv().caps()->shaderCaps()->halfIs32Bits() ? 0.5f : 1.2f;
  209. // For the all-sRGB case, we allow a small error only for devices that have
  210. // precision variation because the sRGB data gets converted to linear and back in
  211. // the shader.
  212. float smallError = context->priv().caps()->shaderCaps()->halfIs32Bits() ? 0.0f : 1.f;
  213. ///////////////////////////////////////////////////////////////////////////////////////////////
  214. // Write sRGB data to a sRGB context - no conversion on the write.
  215. // back to sRGB - no conversion.
  216. test_write_read(Encoding::kSRGB, Encoding::kSRGB, Encoding::kSRGB, smallError,
  217. check_no_conversion, context, reporter);
  218. // Reading back to untagged should be a pass through with no conversion.
  219. test_write_read(Encoding::kSRGB, Encoding::kSRGB, Encoding::kUntagged, error,
  220. check_no_conversion, context, reporter);
  221. // Converts back to linear
  222. test_write_read(Encoding::kSRGB, Encoding::kSRGB, Encoding::kLinear, error,
  223. check_srgb_to_linear_conversion, context, reporter);
  224. // Untagged source data should be interpreted as sRGB.
  225. test_write_read(Encoding::kSRGB, Encoding::kUntagged, Encoding::kSRGB, smallError,
  226. check_no_conversion, context, reporter);
  227. ///////////////////////////////////////////////////////////////////////////////////////////////
  228. // Write linear data to a sRGB context. It gets converted to sRGB on write. The reads
  229. // are all the same as the above cases where the original data was untagged.
  230. test_write_read(Encoding::kSRGB, Encoding::kLinear, Encoding::kSRGB, error,
  231. check_linear_to_srgb_conversion, context, reporter);
  232. // When the dst buffer is untagged there should be no conversion on the read.
  233. test_write_read(Encoding::kSRGB, Encoding::kLinear, Encoding::kUntagged, error,
  234. check_linear_to_srgb_conversion, context, reporter);
  235. test_write_read(Encoding::kSRGB, Encoding::kLinear, Encoding::kLinear, error,
  236. check_linear_to_srgb_to_linear_conversion, context, reporter);
  237. ///////////////////////////////////////////////////////////////////////////////////////////////
  238. // Write data to an untagged context. The write does no conversion no matter what encoding the
  239. // src data has.
  240. for (auto writeEncoding : {Encoding::kSRGB, Encoding::kUntagged, Encoding::kLinear}) {
  241. // The read from untagged to sRGB also does no conversion.
  242. test_write_read(Encoding::kUntagged, writeEncoding, Encoding::kSRGB, error,
  243. check_no_conversion, context, reporter);
  244. // Reading untagged back as untagged should do no conversion.
  245. test_write_read(Encoding::kUntagged, writeEncoding, Encoding::kUntagged, error,
  246. check_no_conversion, context, reporter);
  247. // Reading untagged back as linear does convert (context is source, so treated as sRGB),
  248. // dst is tagged.
  249. test_write_read(Encoding::kUntagged, writeEncoding, Encoding::kLinear, error,
  250. check_srgb_to_linear_conversion, context, reporter);
  251. }
  252. ///////////////////////////////////////////////////////////////////////////////////////////////
  253. // Write sRGB data to a linear context - converts to sRGB on the write.
  254. // converts back to sRGB on read.
  255. test_write_read(Encoding::kLinear, Encoding::kSRGB, Encoding::kSRGB, error,
  256. check_srgb_to_linear_to_srgb_conversion, context, reporter);
  257. // Reading untagged data from linear currently does no conversion.
  258. test_write_read(Encoding::kLinear, Encoding::kSRGB, Encoding::kUntagged, error,
  259. check_srgb_to_linear_conversion, context, reporter);
  260. // Stays linear when read.
  261. test_write_read(Encoding::kLinear, Encoding::kSRGB, Encoding::kLinear, error,
  262. check_srgb_to_linear_conversion, context, reporter);
  263. // Untagged source data should be interpreted as sRGB.
  264. test_write_read(Encoding::kLinear, Encoding::kUntagged, Encoding::kSRGB, error,
  265. check_srgb_to_linear_to_srgb_conversion, context, reporter);
  266. ///////////////////////////////////////////////////////////////////////////////////////////////
  267. // Write linear data to a linear context. Does no conversion.
  268. // Reading to sRGB does a conversion.
  269. test_write_read(Encoding::kLinear, Encoding::kLinear, Encoding::kSRGB, error,
  270. check_linear_to_srgb_conversion, context, reporter);
  271. // Reading to untagged does no conversion.
  272. test_write_read(Encoding::kLinear, Encoding::kLinear, Encoding::kUntagged, error,
  273. check_no_conversion, context, reporter);
  274. // Stays linear when read.
  275. test_write_read(Encoding::kLinear, Encoding::kLinear, Encoding::kLinear, error,
  276. check_no_conversion, context, reporter);
  277. }