skqp_model.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /*
  2. * Copyright 2018 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 "tools/skqp/src/skqp.h"
  8. #include "tools/skqp/src/skqp_model.h"
  9. #include "include/codec/SkCodec.h"
  10. #include "include/core/SkBitmap.h"
  11. #include "include/core/SkStream.h"
  12. #include "src/utils/SkOSPath.h"
  13. #include <limits.h>
  14. #ifndef SK_SKQP_GLOBAL_ERROR_TOLERANCE
  15. #define SK_SKQP_GLOBAL_ERROR_TOLERANCE 0
  16. #endif
  17. ////////////////////////////////////////////////////////////////////////////////
  18. static inline uint32_t color(const SkPixmap& pm, SkIPoint p) {
  19. return *pm.addr32(p.x(), p.y());
  20. }
  21. static inline bool inside(SkIPoint point, SkISize dimensions) {
  22. return (unsigned)point.x() < (unsigned)dimensions.width() &&
  23. (unsigned)point.y() < (unsigned)dimensions.height();
  24. }
  25. SkQP::RenderOutcome skqp::Check(const SkPixmap& minImg,
  26. const SkPixmap& maxImg,
  27. const SkPixmap& img,
  28. unsigned tolerance,
  29. SkBitmap* errorOut) {
  30. SkQP::RenderOutcome result;
  31. SkISize dim = img.info().dimensions();
  32. SkASSERT(minImg.info().dimensions() == dim);
  33. SkASSERT(maxImg.info().dimensions() == dim);
  34. static const SkIPoint kNeighborhood[9] = {
  35. { 0, 0}, // ordered by closest pixels first.
  36. {-1, 0}, { 1, 0}, { 0, -1}, { 0, 1},
  37. {-1, -1}, { 1, -1}, {-1, 1}, { 1, 1},
  38. };
  39. for (int y = 0; y < dim.height(); ++y) {
  40. for (int x = 0; x < dim.width(); ++x) {
  41. const SkIPoint xy{x, y};
  42. const uint32_t c = color(img, xy);
  43. int error = INT_MAX;
  44. // loop over neighborhood (halo);
  45. for (SkIPoint delta : kNeighborhood) {
  46. SkIPoint point = xy + delta;
  47. if (inside(point, dim)) { // skip out of pixmap bounds.
  48. int err = 0;
  49. // loop over four color channels.
  50. // Return Manhattan distance in channel-space.
  51. for (int component : {0, 8, 16, 24}) {
  52. uint8_t v = (c >> component) & 0xFF,
  53. vmin = (color(minImg, point) >> component) & 0xFF,
  54. vmax = (color(maxImg, point) >> component) & 0xFF;
  55. err = SkMax32(err, SkMax32((int)v - (int)vmax, (int)vmin - (int)v));
  56. }
  57. error = SkMin32(error, err);
  58. }
  59. }
  60. if (error > (int)tolerance) {
  61. ++result.fBadPixelCount;
  62. result.fTotalError += error;
  63. result.fMaxError = SkMax32(error, result.fMaxError);
  64. if (errorOut) {
  65. if (!errorOut->getPixels()) {
  66. errorOut->allocPixels(SkImageInfo::Make(
  67. dim.width(), dim.height(),
  68. kBGRA_8888_SkColorType,
  69. kOpaque_SkAlphaType));
  70. errorOut->eraseColor(SK_ColorWHITE);
  71. }
  72. SkASSERT((unsigned)error < 256);
  73. *(errorOut->getAddr32(x, y)) = SkColorSetARGB(0xFF, (uint8_t)error, 0, 0);
  74. }
  75. }
  76. }
  77. }
  78. return result;
  79. }
  80. static SkBitmap decode(sk_sp<SkData> data) {
  81. SkBitmap bitmap;
  82. if (auto codec = SkCodec::MakeFromData(std::move(data))) {
  83. SkISize size = codec->getInfo().dimensions();
  84. SkASSERT(!size.isEmpty());
  85. SkImageInfo info = SkImageInfo::Make(size.width(), size.height(),
  86. skqp::kColorType, skqp::kAlphaType);
  87. bitmap.allocPixels(info);
  88. if (SkCodec::kSuccess != codec->getPixels(bitmap.pixmap())) {
  89. bitmap.reset();
  90. }
  91. }
  92. return bitmap;
  93. }
  94. skqp::ModelResult skqp::CheckAgainstModel(const char* name,
  95. const SkPixmap& pm,
  96. SkQPAssetManager* mgr) {
  97. skqp::ModelResult result;
  98. if (pm.colorType() != kColorType || pm.alphaType() != kAlphaType) {
  99. result.fErrorString = "Model failed: source image format.";
  100. return result;
  101. }
  102. if (pm.info().isEmpty()) {
  103. result.fErrorString = "Model failed: empty source image";
  104. return result;
  105. }
  106. constexpr char PATH_ROOT[] = "gmkb";
  107. SkString img_path = SkOSPath::Join(PATH_ROOT, name);
  108. SkString max_path = SkOSPath::Join(img_path.c_str(), kMaxPngPath);
  109. SkString min_path = SkOSPath::Join(img_path.c_str(), kMinPngPath);
  110. result.fMaxPng = mgr->open(max_path.c_str());
  111. result.fMinPng = mgr->open(min_path.c_str());
  112. SkBitmap max_image = decode(result.fMaxPng);
  113. SkBitmap min_image = decode(result.fMinPng);
  114. if (max_image.isNull() || min_image.isNull()) {
  115. result.fErrorString = "Model missing";
  116. return result;
  117. }
  118. if (max_image.info().dimensions() != min_image.info().dimensions()) {
  119. result.fErrorString = "Model has mismatched data.";
  120. return result;
  121. }
  122. if (max_image.info().dimensions() != pm.info().dimensions()) {
  123. result.fErrorString = "Model data does not match source size.";
  124. return result;
  125. }
  126. result.fOutcome = Check(min_image.pixmap(),
  127. max_image.pixmap(),
  128. pm,
  129. SK_SKQP_GLOBAL_ERROR_TOLERANCE,
  130. &result.fErrors);
  131. return result;
  132. }