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