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