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