You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
3.1 KiB
105 lines
3.1 KiB
/* |
|
* Copyright 2016 Nu-book Inc. |
|
* Copyright 2019 Axel Waggersauser. |
|
*/ |
|
// SPDX-License-Identifier: Apache-2.0 |
|
|
|
#include "ImageLoader.h" |
|
|
|
#include "BinaryBitmap.h" |
|
#include "ImageView.h" |
|
|
|
#include <array> |
|
#include <cctype> |
|
#include <cstdlib> |
|
#include <fstream> |
|
#include <iterator> |
|
#include <map> |
|
#include <memory> |
|
#include <mutex> |
|
#include <stdexcept> |
|
#include <vector> |
|
|
|
#define STB_IMAGE_IMPLEMENTATION |
|
#include <stb_image.h> |
|
|
|
#include <webp/decode.h> |
|
|
|
namespace ZXing::Test { |
|
|
|
class STBImage : public ImageView |
|
{ |
|
std::unique_ptr<uint8_t[], void (*)(void*)> _memory = {nullptr, nullptr}; |
|
|
|
public: |
|
void load(const fs::path& imgPath) |
|
{ |
|
int width = 0, height = 0, channels = 0; |
|
int rowStride = 0; |
|
|
|
if (imgPath.extension() == ".webp") { |
|
std::ifstream file(imgPath, std::ios::binary); |
|
if (!file) |
|
throw std::runtime_error("Failed to read image: " + imgPath.string() + " (failed to open file)"); |
|
|
|
std::vector<uint8_t> compressedData(std::istreambuf_iterator<char>(file), {}); |
|
if (compressedData.empty()) |
|
throw std::runtime_error("Failed to read image: " + imgPath.string() + " (empty file)"); |
|
|
|
#if 0 |
|
// directly decoding to YUV is faster and makes more sense but a couple of tests fail with this, so we decode to RGB and convert to Lum instead |
|
channels = 1; |
|
uint8_t *u = nullptr, *v = nullptr; |
|
int uvStride = 0; |
|
_memory = std::unique_ptr<uint8_t[], void (*)(void*)>( |
|
WebPDecodeYUV(compressedData.data(), compressedData.size(), &width, &height, &u, &v, &rowStride, &uvStride), WebPFree); |
|
#else |
|
channels = 3; |
|
_memory = std::unique_ptr<uint8_t[], void (*)(void*)>( |
|
WebPDecodeRGB(compressedData.data(), compressedData.size(), &width, &height), WebPFree); |
|
#endif |
|
if (_memory == nullptr) |
|
throw std::runtime_error("Failed to read image: " + imgPath.string() + " (WebP decode failed)"); |
|
|
|
} else { |
|
_memory = std::unique_ptr<uint8_t[], void (*)(void*)>(stbi_load(imgPath.string().c_str(), &width, &height, &channels, 0), stbi_image_free); |
|
if (_memory == nullptr) |
|
throw std::runtime_error("Failed to read image: " + imgPath.string() + " (" + stbi_failure_reason() + ")"); |
|
rowStride = width * channels; |
|
} |
|
|
|
auto ImageFormatFromChannels = std::array{ImageFormat::None, ImageFormat::Lum, ImageFormat::LumA, ImageFormat::RGB, ImageFormat::RGBA}; |
|
ImageView::operator=({_memory.get(), width, height, ImageFormatFromChannels.at(channels), rowStride}); |
|
|
|
// preconvert from RGB -> Lum to do this only once instead of for each rotation |
|
if (_format == ImageFormat::RGB) { |
|
auto* img = _memory.get(); |
|
for (int i = 0; i < width * height; ++i) |
|
img[i] = RGBToLum(img[3 * i + 0], img[3 * i + 1], img[3 * i + 2]); |
|
ImageView::operator=({_memory.get(), width, height, ImageFormat::Lum}); |
|
} |
|
} |
|
|
|
operator bool() const { return _data; } |
|
}; |
|
|
|
std::map<fs::path, STBImage> cache; |
|
std::mutex cacheMutex; |
|
|
|
void ImageLoader::clearCache() |
|
{ |
|
std::scoped_lock lock(cacheMutex); |
|
cache.clear(); |
|
} |
|
|
|
const ImageView& ImageLoader::load(const fs::path& imgPath) |
|
{ |
|
std::scoped_lock lock(cacheMutex); |
|
auto& binImg = cache[imgPath]; |
|
if (!binImg) |
|
binImg.load(imgPath); |
|
|
|
return binImg; |
|
} |
|
|
|
} // namespace ZXing::Test
|
|
|