1d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary/* 2d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary * Copyright 2017 Google Inc. 3d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary * 4d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary * Use of this source code is governed by a BSD-style license that can be 5d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary * found in the LICENSE file. 6d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary */ 7d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 8d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include "gm_knowledge.h" 9d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 10d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include <cfloat> 112a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary#include <cstdlib> 12d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include <fstream> 132a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary#include <mutex> 14d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include <sstream> 15d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include <string> 16d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include <vector> 17d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 18d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include "../../src/core/SkStreamPriv.h" 192a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary#include "../../src/core/SkTSort.h" 20d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include "SkBitmap.h" 21d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include "SkCodec.h" 22d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include "SkOSFile.h" 232a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary#include "SkOSPath.h" 24d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include "SkPngEncoder.h" 25d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include "SkStream.h" 26d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 27d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#include "skqp_asset_manager.h" 28d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 29a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary#define IMAGES_DIRECTORY_PATH "images" 30d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#define PATH_MAX_PNG "max.png" 31d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#define PATH_MIN_PNG "min.png" 32d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#define PATH_IMG_PNG "image.png" 33d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#define PATH_ERR_PNG "errors.png" 34d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary#define PATH_REPORT "report.html" 35a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary#define PATH_CSV "out.csv" 36d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 372331c82e0d10ee519d9afb3f9e85485c6cf0b3c3Hal Canary#ifndef SK_SKQP_GLOBAL_ERROR_TOLERANCE 382331c82e0d10ee519d9afb3f9e85485c6cf0b3c3Hal Canary#define SK_SKQP_GLOBAL_ERROR_TOLERANCE 0 392331c82e0d10ee519d9afb3f9e85485c6cf0b3c3Hal Canary#endif 402331c82e0d10ee519d9afb3f9e85485c6cf0b3c3Hal Canary 41d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary//////////////////////////////////////////////////////////////////////////////// 42d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 43d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canarystatic int get_error(uint32_t value, uint32_t value_max, uint32_t value_min) { 44d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary int error = 0; 45d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary for (int j : {0, 8, 16, 24}) { 46d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary uint8_t v = (value >> j) & 0xFF, 47d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary vmin = (value_min >> j) & 0xFF, 48d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary vmax = (value_max >> j) & 0xFF; 49d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary if (v > vmax) { 50d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary error = std::max(v - vmax, error); 51d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } else if (v < vmin) { 52d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary error = std::max(vmin - v, error); 53d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 54d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 552331c82e0d10ee519d9afb3f9e85485c6cf0b3c3Hal Canary return std::max(0, error - SK_SKQP_GLOBAL_ERROR_TOLERANCE); 56d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary} 57d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 5851494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canarystatic int get_error_with_nearby(int x, int y, const SkPixmap& pm, 5951494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary const SkPixmap& pm_max, const SkPixmap& pm_min) { 6051494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary struct NearbyPixels { 6151494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary const int x, y, w, h; 6251494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary struct Iter { 6351494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary const int x, y, w, h; 6451494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary int8_t curr; 6551494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary SkIPoint operator*() const { return this->get(); } 6651494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary SkIPoint get() const { 6751494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary switch (curr) { 6851494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary case 0: return {x - 1, y - 1}; 6951494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary case 1: return {x , y - 1}; 7051494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary case 2: return {x + 1, y - 1}; 7151494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary case 3: return {x - 1, y }; 7251494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary case 4: return {x + 1, y }; 7351494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary case 5: return {x - 1, y + 1}; 7451494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary case 6: return {x , y + 1}; 7551494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary case 7: return {x + 1, y + 1}; 7651494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary default: SkASSERT(false); return {0, 0}; 7751494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary } 7851494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary } 7951494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary void skipBad() { 80c126191fd1f540f7318516a965c85a5bff07dad1Hal Canary while (curr < 8) { 8151494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary SkIPoint p = this->get(); 8251494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary if (p.x() >= 0 && p.y() >= 0 && p.x() < w && p.y() < h) { 8351494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary return; 8451494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary } 8551494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary ++curr; 86c126191fd1f540f7318516a965c85a5bff07dad1Hal Canary } 8751494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary curr = -1; 8851494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary } 8951494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary void operator++() { 9051494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary if (-1 == curr) { return; } 9151494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary ++curr; 9251494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary this->skipBad(); 9351494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary } 9451494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary bool operator!=(const Iter& other) const { return curr != other.curr; } 9551494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary }; 9651494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary Iter begin() const { Iter i{x, y, w, h, 0}; i.skipBad(); return i; } 9751494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary Iter end() const { return Iter{x, y, w, h, -1}; } 9851494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary }; 9951494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary 10051494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary uint32_t c = *pm.addr32(x, y); 10151494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary int error = get_error(c, *pm_max.addr32(x, y), *pm_min.addr32(x, y)); 10251494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary for (SkIPoint p : NearbyPixels{x, y, pm.width(), pm.height()}) { 10351494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary if (error == 0) { 10451494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary return 0; 10551494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary } 10651494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary error = SkTMin(error, get_error( 10751494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary c, *pm_max.addr32(p.x(), p.y()), *pm_min.addr32(p.x(), p.y()))); 10851494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary } 10951494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary return error; 11051494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary} 11151494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary 112d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canarystatic float set_error_code(gmkb::Error* error_out, gmkb::Error error) { 113d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkASSERT(error != gmkb::Error::kNone); 114d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary if (error_out) { 115d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary *error_out = error; 116d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 117d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary return FLT_MAX; 118d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary} 119d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 120d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canarystatic bool WritePixmapToFile(const SkPixmap& pixmap, const char* path) { 121d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkFILEWStream wStream(path); 122d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkPngEncoder::Options options; 123d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary options.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore; 124d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary return wStream.isValid() && SkPngEncoder::Encode(&wStream, pixmap, options); 125d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary} 126d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 127d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canaryconstexpr SkColorType kColorType = kRGBA_8888_SkColorType; 128d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canaryconstexpr SkAlphaType kAlphaType = kUnpremul_SkAlphaType; 129d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 130d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canarystatic SkPixmap rgba8888_to_pixmap(const uint32_t* pixels, int width, int height) { 131d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkImageInfo info = SkImageInfo::Make(width, height, kColorType, kAlphaType); 132d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary return SkPixmap(info, pixels, width * sizeof(uint32_t)); 133d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary} 134d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 135d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canarystatic bool copy(skqp::AssetManager* mgr, const char* path, const char* dst) { 136d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary if (mgr) { 137d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary if (auto stream = mgr->open(path)) { 138d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkFILEWStream wStream(dst); 139d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary return wStream.isValid() && SkStreamCopy(&wStream, stream.get()); 140d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 141d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 142d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary return false; 143d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary} 144d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 145d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canarystatic SkBitmap ReadPngRgba8888FromFile(skqp::AssetManager* assetManager, const char* path) { 146d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkBitmap bitmap; 147d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary if (auto codec = SkCodec::MakeFromStream(assetManager->open(path))) { 148d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkISize size = codec->getInfo().dimensions(); 149d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkASSERT(!size.isEmpty()); 150d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), kColorType, kAlphaType); 151d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary bitmap.allocPixels(info); 152d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkASSERT(bitmap.rowBytes() == (unsigned)bitmap.width() * sizeof(uint32_t)); 153b69a2f66448603c26b0265201cec7161cf6d6f75Hal Canary if (SkCodec::kSuccess != codec->getPixels(bitmap.pixmap())) { 154d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary bitmap.reset(); 155d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 156d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 157d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary return bitmap; 158d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary} 159d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 1602a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canarynamespace { 1612a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canarystruct Run { 1622a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary SkString fBackend; 1632a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary SkString fGM; 1642a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary int fMaxerror; 1652a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary int fBadpixels; 1662a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary}; 1672a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary} // namespace 1682a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary 1692a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canarystatic std::vector<Run> gErrors; 1702a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canarystatic std::mutex gMutex; 1712a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary 172a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canarystatic SkString make_path(const SkString& images_directory, 173a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary const char* backend, 174a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary const char* gm_name, 175a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary const char* thing) { 176a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary auto path = SkStringPrintf("%s_%s_%s", backend, gm_name, thing); 177a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary return SkOSPath::Join(images_directory.c_str(), path.c_str()); 178a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary} 179a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary 180a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary 181d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canarynamespace gmkb { 182d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canaryfloat Check(const uint32_t* pixels, 183d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary int width, 184d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary int height, 185d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary const char* name, 186d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary const char* backend, 187d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary skqp::AssetManager* assetManager, 188d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary const char* report_directory_path, 189d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary Error* error_out) { 190a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary if (report_directory_path && report_directory_path[0]) { 191a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary SkASSERT_RELEASE(sk_isdir(report_directory_path)); 192a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary } 193d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary if (width <= 0 || height <= 0) { 194d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary return set_error_code(error_out, Error::kBadInput); 195d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 196a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary constexpr char PATH_ROOT[] = "gmkb"; 197a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary SkString img_path = SkOSPath::Join(PATH_ROOT, name); 198a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary SkString max_path = SkOSPath::Join(img_path.c_str(), PATH_MAX_PNG); 199a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary SkString min_path = SkOSPath::Join(img_path.c_str(), PATH_MIN_PNG); 200d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkBitmap max_image = ReadPngRgba8888FromFile(assetManager, max_path.c_str()); 201d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkBitmap min_image = ReadPngRgba8888FromFile(assetManager, min_path.c_str()); 202a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary if (max_image.isNull() || min_image.isNull()) { 203a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary // No data. 204a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary if (error_out) { 205a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary *error_out = Error::kNone; 206a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary } 207a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary return 0; 208d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 209d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary if (max_image.width() != min_image.width() || 210d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary max_image.height() != min_image.height()) 211d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary { 212d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary return set_error_code(error_out, Error::kBadData); 213d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 214d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary if (max_image.width() != width || max_image.height() != height) { 215d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary return set_error_code(error_out, Error::kBadInput); 216d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 21751494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary 218d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary int badness = 0; 219d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary int badPixelCount = 0; 22051494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary SkPixmap pm(SkImageInfo::Make(width, height, kColorType, kAlphaType), 22151494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary pixels, width * sizeof(uint32_t)); 22251494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary SkPixmap pm_max = max_image.pixmap(); 22351494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary SkPixmap pm_min = min_image.pixmap(); 22451494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary for (int y = 0; y < pm.height(); ++y) { 22551494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary for (int x = 0; x < pm.width(); ++x) { 22651494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary int error = get_error_with_nearby(x, y, pm, pm_max, pm_min) ; 22751494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary if (error > 0) { 22851494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary badness = SkTMax(error, badness); 22951494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary ++badPixelCount; 23051494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary } 231d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 232d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 23351494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary 2343de24bf20cd9bff6ecbe8d262b596658642ab7c3Hal Canary if (badness == 0) { 2353de24bf20cd9bff6ecbe8d262b596658642ab7c3Hal Canary std::lock_guard<std::mutex> lock(gMutex); 2363de24bf20cd9bff6ecbe8d262b596658642ab7c3Hal Canary gErrors.push_back(Run{SkString(backend), SkString(name), 0, 0}); 2373de24bf20cd9bff6ecbe8d262b596658642ab7c3Hal Canary } 238d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary if (report_directory_path && badness > 0 && report_directory_path[0] != '\0') { 239d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary if (!backend) { 240d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary backend = "skia"; 241d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 242a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary SkString images_directory = SkOSPath::Join(report_directory_path, IMAGES_DIRECTORY_PATH); 243a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary sk_mkdir(images_directory.c_str()); 244a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary 245a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary SkString image_path = make_path(images_directory, backend, name, PATH_IMG_PNG); 246a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary SkString error_path = make_path(images_directory, backend, name, PATH_ERR_PNG); 247a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary SkString max_path_out = make_path(images_directory, backend, name, PATH_MAX_PNG); 248a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary SkString min_path_out = make_path(images_directory, backend, name, PATH_MIN_PNG); 249a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary 250d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkAssertResult(WritePixmapToFile(rgba8888_to_pixmap(pixels, width, height), 251a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary image_path.c_str())); 252a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary 253d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary SkBitmap errorBitmap; 254d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary errorBitmap.allocPixels(SkImageInfo::Make(width, height, kColorType, kAlphaType)); 25551494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary for (int y = 0; y < pm.height(); ++y) { 25651494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary for (int x = 0; x < pm.width(); ++x) { 25751494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary int error = get_error_with_nearby(x, y, pm, pm_max, pm_min); 25851494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary *errorBitmap.getAddr32(x, y) = 25951494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary error > 0 ? 0xFF000000 + (unsigned)error : 0xFFFFFFFF; 26051494f6615b8bd637caf0fd6d0d577ea19d50948Hal Canary } 261d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 262b69a2f66448603c26b0265201cec7161cf6d6f75Hal Canary SkAssertResult(WritePixmapToFile(errorBitmap.pixmap(), error_path.c_str())); 263d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 264d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary (void)copy(assetManager, max_path.c_str(), max_path_out.c_str()); 265d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary (void)copy(assetManager, min_path.c_str(), min_path_out.c_str()); 266d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary 2672a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary std::lock_guard<std::mutex> lock(gMutex); 2682a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary gErrors.push_back(Run{SkString(backend), SkString(name), badness, badPixelCount}); 2692a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary } 2702a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary if (error_out) { 2712a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary *error_out = Error::kNone; 2722a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary } 2732a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary return (float)badness; 2742a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary} 2752a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary 276c341d3829d47e1688677981874c1a27ff00096f7Hal Canarystatic constexpr char kDocHead[] = 277c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "<!doctype html>\n" 278c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "<html lang=\"en\">\n" 279c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "<head>\n" 280c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "<meta charset=\"UTF-8\">\n" 281c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "<title>SkQP Report</title>\n" 282c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "<style>\n" 283b0c427b1f286524aa3bfd497056b0626c84a20b9Hal Canary "img { max-width:48%; border:1px green solid;\n" 2849c17391cef1b0903a3240a50557e20fe68a7f42dHal Canary " image-rendering: pixelated;\n" 285b0c427b1f286524aa3bfd497056b0626c84a20b9Hal Canary " background-image:url('data:image/png;base64,iVBORw0KGgoA" 286c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "AAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAAXNSR0IArs4c6QAAAAJiS0dEAP+H" 287c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "j8y/AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAB3RJTUUH3gUBEi4DGRAQYgAAAB1J" 288c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "REFUGNNjfMoAAVJQmokBDdBHgPE/lPFsYN0BABdaAwN6tehMAAAAAElFTkSuQmCC" 289c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "'); }\n" 290c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "</style>\n" 291c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "<script>\n" 292c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "function ce(t) { return document.createElement(t); }\n" 293c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "function ct(n) { return document.createTextNode(n); }\n" 294c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "function ac(u,v) { return u.appendChild(v); }\n" 295c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "function br(u) { ac(u, ce(\"br\")); }\n" 296c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "function ma(s, c) { var a = ce(\"a\"); a.href = s; ac(a, c); return a; }\n" 297c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "function f(backend, gm, e1, e2) {\n" 298c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " var b = ce(\"div\");\n" 299c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " var x = ce(\"h2\");\n" 300c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " var t = backend + \"/\" + gm;\n" 301c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " ac(x, ct(t));\n" 302c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " ac(b, x);\n" 303c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " ac(b, ct(\"backend: \" + backend));\n" 304c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " br(b);\n" 305c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " ac(b, ct(\"gm name: \" + gm));\n" 306c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " br(b);\n" 307c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " ac(b, ct(\"maximum error: \" + e1));\n" 308c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " br(b);\n" 309c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " ac(b, ct(\"bad pixel counts: \" + e2));\n" 310c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " br(b);\n" 311a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary " var q = \"" IMAGES_DIRECTORY_PATH "/\" + backend + \"_\" + gm + \"_\";\n" 312c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " var i = ce(\"img\");\n" 313a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary " i.src = q + \"" PATH_IMG_PNG "\";\n" 314c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " i.alt = \"img\";\n" 315c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " ac(b, ma(i.src, i));\n" 316c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " i = ce(\"img\");\n" 317a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary " i.src = q + \"" PATH_ERR_PNG "\";\n" 318a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary " i.alt = \"err\";\n" 319c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " ac(b, ma(i.src, i));\n" 320c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " br(b);\n" 321b0c427b1f286524aa3bfd497056b0626c84a20b9Hal Canary " ac(b, ct(\"Expectation: \"));\n" 322a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary " ac(b, ma(q + \"" PATH_MAX_PNG "\", ct(\"max\")));\n" 323181ec2f02f2efa822b0fba35feb74fc0ba3945f1Hal Canary " ac(b, ct(\" | \"));\n" 324a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary " ac(b, ma(q + \"" PATH_MIN_PNG "\", ct(\"min\")));\n" 325c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " ac(b, ce(\"hr\"));\n" 32675454f58f53fcd52dad64147dcb688d45ad04895Hal Canary " b.id = backend + \":\" + gm;\n" 327c341d3829d47e1688677981874c1a27ff00096f7Hal Canary " ac(document.body, b);\n" 32875454f58f53fcd52dad64147dcb688d45ad04895Hal Canary " l = ce(\"li\");\n" 329a3ca5f857a36ec16ce666c56adfa5c5800bb979aHal Canary " ac(l, ct(\"[\" + e1 + \"] \"));\n" 33075454f58f53fcd52dad64147dcb688d45ad04895Hal Canary " ac(l, ma(\"#\" + backend +\":\"+ gm , ct(t)));\n" 33175454f58f53fcd52dad64147dcb688d45ad04895Hal Canary " ac(document.getElementById(\"toc\"), l);\n" 332c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "}\n" 333181ec2f02f2efa822b0fba35feb74fc0ba3945f1Hal Canary "function main() {\n"; 334c341d3829d47e1688677981874c1a27ff00096f7Hal Canary 33575454f58f53fcd52dad64147dcb688d45ad04895Hal Canarystatic constexpr char kDocMiddle[] = 336c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "}\n" 337c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "</script>\n" 338c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "</head>\n" 339181ec2f02f2efa822b0fba35feb74fc0ba3945f1Hal Canary "<body onload=\"main()\">\n" 34075454f58f53fcd52dad64147dcb688d45ad04895Hal Canary "<h1>SkQP Report</h1>\n"; 34175454f58f53fcd52dad64147dcb688d45ad04895Hal Canary 34275454f58f53fcd52dad64147dcb688d45ad04895Hal Canarystatic constexpr char kDocTail[] = 34375454f58f53fcd52dad64147dcb688d45ad04895Hal Canary "<ul id=\"toc\"></ul>\n" 34475454f58f53fcd52dad64147dcb688d45ad04895Hal Canary "<hr>\n" 345b0c427b1f286524aa3bfd497056b0626c84a20b9Hal Canary "<p>Left image: test result<br>\n" 346b0c427b1f286524aa3bfd497056b0626c84a20b9Hal Canary "Right image: errors (white = no error, black = smallest error, red = biggest error)</p>\n" 347c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "<hr>\n" 348c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "</body>\n" 349c341d3829d47e1688677981874c1a27ff00096f7Hal Canary "</html>\n"; 350c341d3829d47e1688677981874c1a27ff00096f7Hal Canary 351c341d3829d47e1688677981874c1a27ff00096f7Hal Canarystatic void write(SkWStream* wStream, const SkString& text) { 352c341d3829d47e1688677981874c1a27ff00096f7Hal Canary wStream->write(text.c_str(), text.size()); 353c341d3829d47e1688677981874c1a27ff00096f7Hal Canary} 354c341d3829d47e1688677981874c1a27ff00096f7Hal Canary 35575454f58f53fcd52dad64147dcb688d45ad04895Hal Canaryenum class Backend { 35675454f58f53fcd52dad64147dcb688d45ad04895Hal Canary kUnknown, 35775454f58f53fcd52dad64147dcb688d45ad04895Hal Canary kGLES, 35875454f58f53fcd52dad64147dcb688d45ad04895Hal Canary kVulkan, 35975454f58f53fcd52dad64147dcb688d45ad04895Hal Canary}; 36075454f58f53fcd52dad64147dcb688d45ad04895Hal Canary 36175454f58f53fcd52dad64147dcb688d45ad04895Hal Canarystatic Backend get_backend(const SkString& s) { 36275454f58f53fcd52dad64147dcb688d45ad04895Hal Canary if (s.equals("gles")) { 36375454f58f53fcd52dad64147dcb688d45ad04895Hal Canary return Backend::kGLES; 36475454f58f53fcd52dad64147dcb688d45ad04895Hal Canary } else if (s.equals("vk")) { 36575454f58f53fcd52dad64147dcb688d45ad04895Hal Canary return Backend::kVulkan; 36675454f58f53fcd52dad64147dcb688d45ad04895Hal Canary } 36775454f58f53fcd52dad64147dcb688d45ad04895Hal Canary return Backend::kUnknown; 36875454f58f53fcd52dad64147dcb688d45ad04895Hal Canary} 36975454f58f53fcd52dad64147dcb688d45ad04895Hal Canary 37075454f58f53fcd52dad64147dcb688d45ad04895Hal Canary 3712a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canarybool MakeReport(const char* report_directory_path) { 37275454f58f53fcd52dad64147dcb688d45ad04895Hal Canary int glesErrorCount = 0, vkErrorCount = 0, gles = 0, vk = 0; 37375454f58f53fcd52dad64147dcb688d45ad04895Hal Canary 374a9de760a217cf48c974d6c51b4ba88f08c269bbeHal Canary SkASSERT_RELEASE(sk_isdir(report_directory_path)); 3753de24bf20cd9bff6ecbe8d262b596658642ab7c3Hal Canary std::lock_guard<std::mutex> lock(gMutex); 376c341d3829d47e1688677981874c1a27ff00096f7Hal Canary SkFILEWStream csvOut(SkOSPath::Join(report_directory_path, PATH_CSV).c_str()); 377c341d3829d47e1688677981874c1a27ff00096f7Hal Canary SkFILEWStream htmOut(SkOSPath::Join(report_directory_path, PATH_REPORT).c_str()); 378c341d3829d47e1688677981874c1a27ff00096f7Hal Canary SkASSERT_RELEASE(csvOut.isValid()); 379c341d3829d47e1688677981874c1a27ff00096f7Hal Canary if (!csvOut.isValid() || !htmOut.isValid()) { 3802a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary return false; 3812a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary } 382c341d3829d47e1688677981874c1a27ff00096f7Hal Canary htmOut.writeText(kDocHead); 3832a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary for (const Run& run : gErrors) { 38475454f58f53fcd52dad64147dcb688d45ad04895Hal Canary auto backend = get_backend(run.fBackend); 38575454f58f53fcd52dad64147dcb688d45ad04895Hal Canary switch (backend) { 38675454f58f53fcd52dad64147dcb688d45ad04895Hal Canary case Backend::kGLES: ++gles; break; 38775454f58f53fcd52dad64147dcb688d45ad04895Hal Canary case Backend::kVulkan: ++vk; break; 38875454f58f53fcd52dad64147dcb688d45ad04895Hal Canary default: break; 38975454f58f53fcd52dad64147dcb688d45ad04895Hal Canary } 390c341d3829d47e1688677981874c1a27ff00096f7Hal Canary write(&csvOut, SkStringPrintf("\"%s\",\"%s\",%d,%d\n", 391c341d3829d47e1688677981874c1a27ff00096f7Hal Canary run.fBackend.c_str(), run.fGM.c_str(), 392c341d3829d47e1688677981874c1a27ff00096f7Hal Canary run.fMaxerror, run.fBadpixels)); 393c341d3829d47e1688677981874c1a27ff00096f7Hal Canary if (run.fMaxerror == 0 && run.fBadpixels == 0) { 3943de24bf20cd9bff6ecbe8d262b596658642ab7c3Hal Canary continue; 3953de24bf20cd9bff6ecbe8d262b596658642ab7c3Hal Canary } 396c341d3829d47e1688677981874c1a27ff00096f7Hal Canary write(&htmOut, SkStringPrintf(" f(\"%s\", \"%s\", %d, %d);\n", 397c341d3829d47e1688677981874c1a27ff00096f7Hal Canary run.fBackend.c_str(), run.fGM.c_str(), 398c341d3829d47e1688677981874c1a27ff00096f7Hal Canary run.fMaxerror, run.fBadpixels)); 39975454f58f53fcd52dad64147dcb688d45ad04895Hal Canary switch (backend) { 40075454f58f53fcd52dad64147dcb688d45ad04895Hal Canary case Backend::kGLES: ++glesErrorCount; break; 40175454f58f53fcd52dad64147dcb688d45ad04895Hal Canary case Backend::kVulkan: ++vkErrorCount; break; 40275454f58f53fcd52dad64147dcb688d45ad04895Hal Canary default: break; 40375454f58f53fcd52dad64147dcb688d45ad04895Hal Canary } 404d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary } 40575454f58f53fcd52dad64147dcb688d45ad04895Hal Canary htmOut.writeText(kDocMiddle); 40675454f58f53fcd52dad64147dcb688d45ad04895Hal Canary write(&htmOut, SkStringPrintf("<p>gles errors: %d (of %d)</br>\n" 40775454f58f53fcd52dad64147dcb688d45ad04895Hal Canary "vk errors: %d (of %d)</p>\n", 40875454f58f53fcd52dad64147dcb688d45ad04895Hal Canary glesErrorCount, gles, vkErrorCount, vk)); 409c341d3829d47e1688677981874c1a27ff00096f7Hal Canary htmOut.writeText(kDocTail); 4102a7f0aa9ebfd66c902dcf1a0ca86ced9fde60d5aHal Canary return true; 411d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary} 412d7b3845f3d3f3498c2adc542b4b20003ac7d3ab0Hal Canary} // namespace gmkb 413