| /** | |
| * Copyright 2022 Rive Inc. | |
| */ | |
| #ifdef TESTING | |
| #else | |
| #include "imagediff_arguments.hpp" | |
| #include "image_cmp.hpp" | |
| #include "SkImage.h" | |
| #include "SkData.h" | |
| #include "SkPixmap.h" | |
| #include "common/write_png_file.hpp" | |
| #include <assert.h> | |
| #include <string> | |
| #include <stdio.h> | |
| class WFile | |
| { | |
| FILE* m_File = nullptr; | |
| bool m_DoClose; | |
| public: | |
| WFile(FILE* existingFile) : m_File(existingFile) { m_DoClose = false; } | |
| WFile(const char path[], const char mode[]) : m_File(fopen(path, mode)) | |
| { | |
| m_DoClose = true; | |
| } | |
| ~WFile() { this->close(); } | |
| bool isOpen() const { return m_File != nullptr; } | |
| bool open(const char path[], const char mode[]) | |
| { | |
| this->close(); | |
| m_File = fopen(path, mode); | |
| return m_File != nullptr; | |
| } | |
| void close() | |
| { | |
| if (m_DoClose && m_File) | |
| { | |
| fclose(m_File); | |
| } | |
| m_File = nullptr; | |
| } | |
| void write(const void* data, size_t length) | |
| { | |
| assert(m_File); | |
| fwrite(data, 1, length, m_File); | |
| } | |
| void write(const std::string& str) { this->write(str.c_str(), str.size()); } | |
| void writeln(const std::string& str) | |
| { | |
| this->write(str); | |
| this->write("\n"); | |
| } | |
| }; | |
| static sk_sp<SkImage> file_to_image(const char path[]) | |
| { | |
| auto data = SkData::MakeFromFileName(path); | |
| if (!data) | |
| { | |
| return nullptr; | |
| } | |
| auto img = SkImage::MakeFromEncoded(data); | |
| if (img) | |
| { | |
| img = img->makeRasterImage(); | |
| } | |
| return img; | |
| } | |
| static void write_file(const char dir[], | |
| const char name[], | |
| const char suffix[], | |
| sk_sp<SkImage> img) | |
| { | |
| auto path = std::string(dir) + std::string("/") + std::string(name) + | |
| std::string(suffix); | |
| SkColorInfo colorInfo(kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); | |
| std::vector<uint8_t> pixels(img->height() * img->width() * 4); | |
| img->readPixels( | |
| nullptr, | |
| SkPixmap(SkImageInfo::Make({img->width(), img->height()}, colorInfo), | |
| pixels.data(), | |
| img->width() * 4), | |
| 0, | |
| 0); | |
| // printf("creating %s\n", path.c_str()); | |
| WritePNGFile(pixels.data(), | |
| img->width(), | |
| img->height(), | |
| false, | |
| path.c_str(), | |
| PNGCompression::fast_rle); | |
| } | |
| void image_diff(const char name[], | |
| const char pathA[], | |
| const char pathB[], | |
| const char statusPath[], | |
| const char diffDir[]) | |
| { | |
| WFile status = | |
| statusPath && statusPath[0] ? WFile(statusPath, "a") : WFile(stdout); | |
| if (!status.isOpen()) | |
| { | |
| throw "failed to open status file"; | |
| } | |
| status.write(std::string(name) + "\t"); | |
| auto imgA = file_to_image(pathA); | |
| auto imgB = file_to_image(pathB); | |
| if (!imgA || !imgB) | |
| { | |
| status.writeln("failed"); | |
| return; | |
| } | |
| const auto cmp = image_cmp(imgA, imgB); | |
| if (!cmp.images_loaded) | |
| { | |
| status.writeln("failed"); | |
| return; | |
| } | |
| if (cmp.identical()) | |
| { | |
| status.writeln("identical"); | |
| return; | |
| } | |
| if (!cmp.dimensions_equal) | |
| { | |
| status.writeln("sizemismatch"); | |
| return; | |
| } | |
| status.writeln(std::to_string(cmp.max_component_diff) + '\t' + | |
| std::to_string(cmp.ave_component_diff) + '\t' + | |
| std::to_string(cmp.pixel_diffcount) + '\t' + | |
| std::to_string(imgA->height() * imgA->width())); | |
| if (diffDir && diffDir[0]) | |
| { | |
| auto diff0 = make_diff_image(imgA, imgB, compute_diff0); | |
| auto diff1 = make_diff_image(imgA, imgB, compute_diff1); | |
| write_file(diffDir, name, ".diff0.png", diff0); | |
| write_file(diffDir, name, ".diff1.png", diff1); | |
| } | |
| } | |
| int main(int argc, const char* argv[]) | |
| { | |
| try | |
| { | |
| ImageDiffArguments args(argc, argv); | |
| image_diff(args.name().c_str(), | |
| args.golden().c_str(), | |
| args.candidate().c_str(), | |
| args.status().c_str(), | |
| args.output().c_str()); | |
| } | |
| catch (const args::Completion& e) | |
| { | |
| return 0; | |
| } | |
| catch (const args::Help&) | |
| { | |
| return 0; | |
| } | |
| catch (const args::ParseError& e) | |
| { | |
| return 1; | |
| } | |
| catch (args::ValidationError e) | |
| { | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| #endif |