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