diff --git a/src/libs/image/CMakeLists.txt b/src/libs/image/CMakeLists.txt index 15555911..3dcf7bd3 100644 --- a/src/libs/image/CMakeLists.txt +++ b/src/libs/image/CMakeLists.txt @@ -27,6 +27,9 @@ if (${LMS_IMAGE_BACKEND} STREQUAL "stb") target_sources(lmsimage PRIVATE impl/stb/Image.cpp impl/stb/RawImage.cpp + impl/stb/StbImage.cpp + impl/stb/StbImageResize.cpp + impl/stb/StbImageWrite.cpp ) target_compile_options(lmsimage PRIVATE "-DSTB_IMAGE_RESIZE_VERSION=${STB_IMAGE_RESIZE_VERSION}") target_include_directories(lmsimage PRIVATE ${STB_IMAGE_INCLUDE_DIR}) diff --git a/src/libs/image/impl/graphicsmagick/Image.cpp b/src/libs/image/impl/graphicsmagick/Image.cpp index fdc31958..8f4c7605 100644 --- a/src/libs/image/impl/graphicsmagick/Image.cpp +++ b/src/libs/image/impl/graphicsmagick/Image.cpp @@ -21,6 +21,8 @@ #include +#include + #include "core/ILogger.hpp" #include "core/ITraceLogger.hpp" #include "image/Exception.hpp" @@ -53,6 +55,28 @@ namespace lms::image return fileExtensions; } + ImageProperties probeImage(const std::filesystem::path& path) + { + LMS_SCOPED_TRACE_DETAILED("Image", "ProbeFile"); + + try + { + Magick::Image image; + image.ping(path.c_str()); + + ImageProperties properties; + properties.width = image.size().width(); + properties.height = image.size().height(); + + return properties; + } + catch (Magick::Exception& e) + { + LMS_LOG(COVER, ERROR, "Caught Magick exception: " << e.what()); + throw Exception{ std::string{ "Magick probe error: " } + e.what() }; + } + } + std::unique_ptr decodeImage(std::span encodedData) { LMS_SCOPED_TRACE_DETAILED("Image", "DecodeBuffer"); diff --git a/src/libs/image/impl/stb/Image.cpp b/src/libs/image/impl/stb/Image.cpp index 28e443d2..ec9432bf 100644 --- a/src/libs/image/impl/stb/Image.cpp +++ b/src/libs/image/impl/stb/Image.cpp @@ -21,14 +21,14 @@ #include -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include +#include "StbImage.hpp" +#include "StbImageWrite.hpp" #include "core/ITraceLogger.hpp" +#include "image/Exception.hpp" #include "EncodedImage.hpp" #include "RawImage.hpp" -#include "image/Exception.hpp" namespace lms::image { @@ -42,6 +42,24 @@ namespace lms::image return fileExtensions; } + ImageProperties probeImage(const std::filesystem::path& path) + { + LMS_SCOPED_TRACE_DETAILED("Image", "ProbeFile"); + + int x{}; + int y{}; + int comp{}; + + if (::stbi_info(path.c_str(), &x, &y, &comp) == 0) + throw StbiException{ "Probe failed" }; + + ImageProperties properties; + properties.width = x; + properties.height = y; + + return properties; + } + std::unique_ptr decodeImage(std::span encodedData) { LMS_SCOPED_TRACE_DETAILED("Image", "DecodeBuffer"); diff --git a/src/libs/image/impl/stb/RawImage.cpp b/src/libs/image/impl/stb/RawImage.cpp index 0f4871fa..f4c420af 100644 --- a/src/libs/image/impl/stb/RawImage.cpp +++ b/src/libs/image/impl/stb/RawImage.cpp @@ -19,22 +19,8 @@ #include "RawImage.hpp" -#define STB_IMAGE_IMPLEMENTATION -#define STB_IMAGE_RESIZE_IMPLEMENTATION - -#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL -#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM - -#define STBI_FAILURE_USERMSG - -#include -#if STB_IMAGE_RESIZE_VERSION == 1 - #include -#elif STB_IMAGE_RESIZE_VERSION == 2 - #include -#else - #error "Unhandled STB image resize version"! -#endif +#include "StbImage.hpp" +#include "StbImageResize.hpp" #include "core/ITraceLogger.hpp" #include "image/Exception.hpp" @@ -43,21 +29,7 @@ namespace lms::image::STB { namespace { - class StbiException : public Exception - { - public: - StbiException(std::string_view desc) - : Exception{ std::string{ desc } + ": " + getLastFailureReason() } - { - } - private: - static std::string getLastFailureReason() - { - const char* failureReason{ ::stbi_failure_reason() }; - return failureReason ? failureReason : "unknown reason"; - } - }; } // namespace RawImage::RawImage(std::span encodedData) diff --git a/src/libs/image/impl/stb/StbImage.cpp b/src/libs/image/impl/stb/StbImage.cpp new file mode 100644 index 00000000..7432daad --- /dev/null +++ b/src/libs/image/impl/stb/StbImage.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#define STB_IMAGE_IMPLEMENTATION +#include "StbImage.hpp" + +namespace lms::image +{ + StbiException::StbiException(std::string_view desc) + : Exception{ std::string{ desc } + ": " + getLastFailureReason() } + { + } + + std::string StbiException::getLastFailureReason() + { + const char* failureReason{ ::stbi_failure_reason() }; + return failureReason ? failureReason : "unknown reason"; + } +} // namespace lms::image \ No newline at end of file diff --git a/src/libs/image/impl/stb/StbImage.hpp b/src/libs/image/impl/stb/StbImage.hpp new file mode 100644 index 00000000..09bc88ac --- /dev/null +++ b/src/libs/image/impl/stb/StbImage.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ +#pragma once + +#define STBI_ONLY_JPEG +#define STBI_ONLY_PNG +#define STBI_ONLY_BMP +#define STBI_FAILURE_USERMSG + +#include + +#include + +#include "image/Exception.hpp" + +namespace lms::image +{ + class StbiException : public Exception + { + public: + StbiException(std::string_view desc); + + private: + static std::string getLastFailureReason(); + }; +} // namespace lms::image \ No newline at end of file diff --git a/src/libs/image/impl/stb/StbImageResize.cpp b/src/libs/image/impl/stb/StbImageResize.cpp new file mode 100644 index 00000000..35054deb --- /dev/null +++ b/src/libs/image/impl/stb/StbImageResize.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include "StbImageResize.hpp" diff --git a/src/libs/image/impl/stb/StbImageResize.hpp b/src/libs/image/impl/stb/StbImageResize.hpp new file mode 100644 index 00000000..53de1342 --- /dev/null +++ b/src/libs/image/impl/stb/StbImageResize.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM + +#if STB_IMAGE_RESIZE_VERSION == 1 + #include +#elif STB_IMAGE_RESIZE_VERSION == 2 + #include +#else + #error "Unhandled STB image resize version"! +#endif diff --git a/src/libs/image/impl/stb/StbImageWrite.cpp b/src/libs/image/impl/stb/StbImageWrite.cpp new file mode 100644 index 00000000..bca30deb --- /dev/null +++ b/src/libs/image/impl/stb/StbImageWrite.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "StbImageWrite.hpp" diff --git a/src/libs/image/impl/stb/StbImageWrite.hpp b/src/libs/image/impl/stb/StbImageWrite.hpp new file mode 100644 index 00000000..ff741442 --- /dev/null +++ b/src/libs/image/impl/stb/StbImageWrite.hpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ +#pragma once + +#include \ No newline at end of file diff --git a/src/libs/image/include/image/Image.hpp b/src/libs/image/include/image/Image.hpp index 39fc67f8..594d78fd 100644 --- a/src/libs/image/include/image/Image.hpp +++ b/src/libs/image/include/image/Image.hpp @@ -31,6 +31,9 @@ namespace lms::image void init(const std::filesystem::path& path); std::span getSupportedFileExtensions(); + // All these methods may throw Exception + ImageProperties probeImage(const std::filesystem::path& path); + std::unique_ptr decodeImage(std::span encodedData); std::unique_ptr decodeImage(const std::filesystem::path& path); diff --git a/src/libs/image/include/image/Types.hpp b/src/libs/image/include/image/Types.hpp index ccd56849..f513f969 100644 --- a/src/libs/image/include/image/Types.hpp +++ b/src/libs/image/include/image/Types.hpp @@ -22,4 +22,10 @@ namespace lms::image { using ImageSize = std::size_t; -} \ No newline at end of file + + struct ImageProperties + { + ImageSize width{}; + ImageSize height{}; + }; +} // namespace lms::image \ No newline at end of file diff --git a/src/libs/services/scanner/impl/scanners/ImageFileScanner.cpp b/src/libs/services/scanner/impl/scanners/ImageFileScanner.cpp index 8def9d37..d84a3c65 100644 --- a/src/libs/services/scanner/impl/scanners/ImageFileScanner.cpp +++ b/src/libs/services/scanner/impl/scanners/ImageFileScanner.cpp @@ -27,6 +27,7 @@ #include "database/MediaLibrary.hpp" #include "database/Session.hpp" #include "image/Exception.hpp" +#include "image/IRawImage.hpp" #include "image/Image.hpp" #include "IFileScanOperation.hpp" @@ -55,29 +56,18 @@ namespace lms::scanner const MediaLibraryInfo _mediaLibrary; db::Db& _db; - struct ImageInfo - { - std::size_t height{}; - std::size_t width{}; - }; - std::optional _parsedImageInfo; + std::optional _parsedImageProperties; }; void ImageFileScanOperation::scan() { try { - std::unique_ptr rawImage{ image::decodeImage(_file) }; - - ImageInfo imageInfo; - imageInfo.width = rawImage->getWidth(); - imageInfo.height = rawImage->getHeight(); - - _parsedImageInfo = imageInfo; + _parsedImageProperties = image::probeImage(_file); } catch (const image::Exception& e) { - _parsedImageInfo.reset(); + _parsedImageProperties.reset(); LMS_LOG(DBUPDATER, ERROR, "Cannot read image in file " << _file << ": " << e.what()); } } @@ -96,7 +86,7 @@ namespace lms::scanner db::Session& dbSession{ _db.getTLSSession() }; db::Image::pointer image{ db::Image::find(dbSession, _file) }; - if (!_parsedImageInfo) + if (!_parsedImageProperties) { if (image) { @@ -114,8 +104,8 @@ namespace lms::scanner image.modify()->setLastWriteTime(fileInfo->lastWriteTime); image.modify()->setFileSize(fileInfo->fileSize); - image.modify()->setHeight(_parsedImageInfo->height); - image.modify()->setWidth(_parsedImageInfo->width); + image.modify()->setHeight(_parsedImageProperties->height); + image.modify()->setWidth(_parsedImageProperties->width); db::MediaLibrary::pointer mediaLibrary{ db::MediaLibrary::find(dbSession, _mediaLibrary.id) }; // may be null if settings are updated in // => next scan will correct this image.modify()->setDirectory(utils::getOrCreateDirectory(dbSession, _file.parent_path(), mediaLibrary));