mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-02-02 02:13:02 -05:00
261 lines
8.1 KiB
C++
261 lines
8.1 KiB
C++
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md
|
|
|
|
#include "ImageManager.hpp"
|
|
#include "ImageMap.hpp"
|
|
#include "VecMap.hpp"
|
|
#include "PixMap.hpp"
|
|
#include "DrawCommand.hpp"
|
|
#include "Renderer.hpp"
|
|
#include <log/log.hpp>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <list>
|
|
|
|
namespace
|
|
{
|
|
auto getFileSize(const std::string &path) -> std::uintmax_t
|
|
{
|
|
try {
|
|
return std::filesystem::file_size(path);
|
|
}
|
|
catch (const std::filesystem::filesystem_error &e) {
|
|
return 0;
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace gui
|
|
{
|
|
ImageManager::ImageManager()
|
|
{
|
|
addFallbackImage();
|
|
}
|
|
|
|
ImageManager::~ImageManager()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
auto ImageManager::init(const std::filesystem::path &baseDirectory) -> bool
|
|
{
|
|
// Load images from specified folder
|
|
loadImageMaps(baseDirectory);
|
|
return true;
|
|
}
|
|
|
|
auto ImageManager::clear() -> void
|
|
{
|
|
for (auto imageMap : imageMaps) {
|
|
LOG_INFO("Deleting image '%s'", imageMap->getName().c_str());
|
|
delete imageMap;
|
|
}
|
|
imageMaps.clear();
|
|
}
|
|
|
|
auto ImageManager::getInstance() -> ImageManager &
|
|
{
|
|
static ImageManager instance;
|
|
return instance;
|
|
}
|
|
|
|
auto ImageManager::getImageMap(std::uint32_t id) const -> ImageMap *
|
|
{
|
|
if (id >= imageMaps.size()) {
|
|
#if DEBUG_MISSING_ASSETS == 1
|
|
LOG_ERROR("Unable to find an image by id %" PRIu32, id);
|
|
#endif
|
|
return imageMaps[fallbackImageId];
|
|
}
|
|
return imageMaps[id];
|
|
}
|
|
|
|
auto ImageManager::getImageMapID(const std::string &name, ImageTypeSpecifier specifier) -> std::uint32_t
|
|
{
|
|
const auto &searchName = checkAndAddSpecifierToName(name, specifier);
|
|
|
|
for (auto i = 0U; i < imageMaps.size(); ++i) {
|
|
if (imageMaps[i]->getName() == searchName) {
|
|
return i;
|
|
}
|
|
}
|
|
#if DEBUG_MISSING_ASSETS == 1
|
|
LOG_ERROR("Unable to find an image '%s', using default fallback image instead", name.c_str());
|
|
#endif
|
|
return fallbackImageId;
|
|
}
|
|
|
|
auto ImageManager::getImageMapList(const std::string &ext1, const std::string &ext2) const
|
|
-> std::pair<std::vector<std::string>, std::vector<std::string>>
|
|
{
|
|
std::vector<std::string> ext1MapFiles;
|
|
std::vector<std::string> ext2MapFiles;
|
|
|
|
LOG_INFO("Scanning extensions '%s' '%s' in images folder '%s'", ext1.c_str(), ext2.c_str(), mapFolder.c_str());
|
|
|
|
for (const auto &entry : std::filesystem::recursive_directory_iterator(mapFolder)) {
|
|
if (!entry.is_directory()) {
|
|
if (entry.path().extension() == ext1) {
|
|
ext1MapFiles.push_back(entry.path().string());
|
|
}
|
|
else if (entry.path().extension() == ext2) {
|
|
ext2MapFiles.push_back(entry.path().string());
|
|
}
|
|
}
|
|
}
|
|
LOG_INFO("Total number of images: %zu", ext2MapFiles.size() + ext1MapFiles.size());
|
|
return {ext1MapFiles, ext2MapFiles};
|
|
}
|
|
|
|
auto ImageManager::loadPixMap(const std::filesystem::path &filename) -> ImageMap *
|
|
{
|
|
const auto fileSize = getFileSize(filename);
|
|
if (fileSize == 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto imageData = std::make_unique<std::uint8_t[]>(fileSize);
|
|
|
|
std::ifstream input(filename, std::ios::in | std::ifstream::binary);
|
|
if (!input.is_open()) {
|
|
LOG_FATAL("Failed to open file '%s'", filename.c_str());
|
|
return nullptr;
|
|
}
|
|
if (!input.read(reinterpret_cast<char *>(imageData.get()), static_cast<std::streamsize>(fileSize))) {
|
|
return nullptr;
|
|
}
|
|
const auto bytesRead = static_cast<std::uintmax_t>(input.gcount());
|
|
if (bytesRead != fileSize) {
|
|
LOG_FATAL("Failed to read from file '%s', expected %" PRIuMAX "B, got %" PRIuMAX "B",
|
|
filename.c_str(),
|
|
fileSize,
|
|
bytesRead);
|
|
return nullptr;
|
|
}
|
|
|
|
// Allocate memory for new pixmap
|
|
auto pixMap = new (std::nothrow) PixMap();
|
|
if (pixMap == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (pixMap->load(imageData.get(), fileSize) != gui::Status::GUI_SUCCESS) {
|
|
delete pixMap;
|
|
return nullptr;
|
|
}
|
|
|
|
// Set name, id and push it to vector
|
|
const auto &pixMapName = filename.stem();
|
|
pixMap->setName(pixMapName);
|
|
pixMap->setID(imageMaps.size());
|
|
imageMaps.push_back(pixMap);
|
|
|
|
return pixMap;
|
|
}
|
|
|
|
auto ImageManager::loadVecMap(const std::filesystem::path &filename) -> ImageMap *
|
|
{
|
|
const auto fileSize = getFileSize(filename);
|
|
if (fileSize == 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto imageData = std::make_unique<std::uint8_t[]>(fileSize);
|
|
|
|
std::ifstream input(filename, std::ios::in | std::ifstream::binary);
|
|
if (!input.is_open()) {
|
|
LOG_FATAL("Failed to open file '%s'", filename.c_str());
|
|
return nullptr;
|
|
}
|
|
if (!input.read(reinterpret_cast<char *>(imageData.get()), static_cast<std::streamsize>(fileSize))) {
|
|
return nullptr;
|
|
}
|
|
const auto bytesRead = static_cast<std::uintmax_t>(input.gcount());
|
|
if (bytesRead != fileSize) {
|
|
LOG_FATAL("Failed to read from file '%s', expected %" PRIuMAX "B, got %" PRIuMAX "B",
|
|
filename.c_str(),
|
|
fileSize,
|
|
bytesRead);
|
|
return nullptr;
|
|
}
|
|
|
|
auto vecMap = new (std::nothrow) VecMap();
|
|
if (vecMap == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (vecMap->load(imageData.get(), fileSize) != gui::Status::GUI_SUCCESS) {
|
|
delete vecMap;
|
|
return nullptr;
|
|
}
|
|
|
|
// Set name, id and push it to vector
|
|
const auto &pixMapName = filename.stem();
|
|
vecMap->setName(pixMapName);
|
|
vecMap->setID(imageMaps.size());
|
|
imageMaps.push_back(vecMap);
|
|
|
|
return vecMap;
|
|
}
|
|
|
|
auto ImageManager::addFallbackImage() -> void
|
|
{
|
|
const std::string fallbackImageName{"FallbackImage"};
|
|
|
|
auto fallbackImage = createFallbackImage();
|
|
fallbackImageId = imageMaps.size();
|
|
fallbackImage->setID(fallbackImageId);
|
|
fallbackImage->setName(fallbackImageName);
|
|
imageMaps.push_back(fallbackImage);
|
|
}
|
|
|
|
auto ImageManager::loadImageMaps(const std::filesystem::path &baseDirectory) -> void
|
|
{
|
|
mapFolder = baseDirectory / "images";
|
|
auto [pixMapFiles, vecMapFiles] = getImageMapList(".mpi", ".vpi");
|
|
|
|
for (const auto &mapName : pixMapFiles) {
|
|
loadPixMap(mapName);
|
|
}
|
|
for (const auto &mapName : vecMapFiles) {
|
|
loadVecMap(mapName);
|
|
}
|
|
}
|
|
|
|
auto ImageManager::checkAndAddSpecifierToName(const std::string &name, ImageTypeSpecifier specifier) -> std::string
|
|
{
|
|
if (specifier != ImageTypeSpecifier::None) {
|
|
return name + specifierMap[specifier];
|
|
}
|
|
return name;
|
|
}
|
|
|
|
auto ImageManager::createFallbackImage() const -> ImageMap *
|
|
{
|
|
// Creation of square with crossed lines as fallback image
|
|
constexpr auto squareWidth = 15;
|
|
|
|
DrawRectangle rectangle;
|
|
rectangle.origin = {0, 0};
|
|
rectangle.width = squareWidth;
|
|
rectangle.height = squareWidth;
|
|
rectangle.areaX = 0;
|
|
rectangle.areaY = 0;
|
|
rectangle.areaW = squareWidth;
|
|
rectangle.areaH = squareWidth;
|
|
|
|
DrawLine line1;
|
|
line1.start = {0, 0};
|
|
line1.end = {squareWidth, squareWidth};
|
|
|
|
DrawLine line2;
|
|
line2.start = {squareWidth - 1, 0};
|
|
line2.end = {0, squareWidth - 1};
|
|
|
|
Context renderContext(squareWidth, squareWidth);
|
|
Renderer().render(renderContext, rectangle, line1, line2);
|
|
|
|
return new PixMap(squareWidth, squareWidth, renderContext.getData());
|
|
}
|
|
} // namespace gui
|