Skip to content

Commit

Permalink
TgBot++: imagep: Add version getter, make dependencies optional
Browse files Browse the repository at this point in the history
  • Loading branch information
Royna2544 committed Jul 1, 2024
1 parent da2aca9 commit 2588f30
Show file tree
Hide file tree
Showing 15 changed files with 185 additions and 79 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/macos_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
# https://github.com/Homebrew/homebrew-core/issues/169820#issuecomment-2080459578
- name: Set up dependencies
run: |
brew install boost ninja llvm webp libpng libjpeg opencv
brew install boost ninja llvm webp libpng
git submodule update --init
sed -i '' '/^#define _LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS/d' "$(brew --prefix llvm)"/include/c++/v1/__config_site
Expand Down
31 changes: 27 additions & 4 deletions cmake/tgbotpng.cmake
Original file line number Diff line number Diff line change
@@ -1,13 +1,36 @@
include(cmake/FindWebP.cmake)
find_package(PNG REQUIRED)
find_package(JPEG REQUIRED)
find_package(OpenCV REQUIRED core imgproc highgui)
find_package(PNG)
find_package(JPEG)
find_package(OpenCV COMPONENTS core imgproc highgui)

add_library_san(TgBotImgProc SHARED
src/imagep/ImageProcOpenCV.cpp
src/imagep/ImageTypeWEBP.cpp
src/imagep/ImageTypeJPEG.cpp
src/imagep/ImageTypePNG.cpp)
src/imagep/ImageTypePNG.cpp
src/imagep/ImageProcAll.cpp)

if (${WebP_FOUND})
target_compile_definitions(TgBotImgProc PRIVATE -DHAVE_LIBWEBP)
target_link_libraries(TgBotImgProc ${WebP_LIBRARIES})
message(STATUS "libWebP Present")
endif()
if (${JPEG_FOUND})
target_compile_definitions(TgBotImgProc PRIVATE -DHAVE_LIBJPEG)
target_link_libraries(TgBotImgProc ${JPEG_LIBRARIES})
message(STATUS "libJPEG Present")
endif()
if (${PNG_FOUND})
target_compile_definitions(TgBotImgProc PRIVATE -DHAVE_LIBPNG)
target_link_libraries(TgBotImgProc ${PNG_LIBRARIES})
message(STATUS "libPNG Present")
endif()
if (${OpenCV_FOUND})
target_compile_definitions(TgBotImgProc PRIVATE -DHAVE_OPENCV)
target_link_libraries(TgBotImgProc ${OpenCV_LIBRARIES})
message(STATUS "OpenCV Present")
endif()

target_include_directories(TgBotImgProc PUBLIC
${PNG_INCLUDE_DIR} ${WebP_INCLUDE_DIRS} ${JPEG_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS})
target_link_libraries(TgBotImgProc
Expand Down
69 changes: 10 additions & 59 deletions src/command_modules/rotatepic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <cctype>
#include <database/bot/TgBotDatabaseImpl.hpp>
#include <filesystem>
#include <imagep/ImageProcessingAll.hpp>
#include <imagep/ImageProcAll.hpp>
#include <memory>
#include <string>
#include <string_view>
Expand All @@ -24,15 +24,11 @@ struct ProcessImageParam {
};

namespace {

using ImageVariants = std::variant<PngImage, WebPImage, JPEGImage, OpenCVImage>;
template <int index>
bool tryToProcess(ImageVariants& images, ProcessImageParam param) {
auto& inst = std::get<index>(images);
if (inst.read(param.srcPath)) {
bool processPhotoFile(ProcessImageParam& param) {
ImageProcessingAll procAll(param.srcPath);
if (procAll.read()) {
LOG(INFO) << "Successfully read image";
LOG(INFO) << "Rotating image by " << param.rotation << " degrees";
switch (inst.rotate_image(param.rotation)) {
switch (procAll.rotate(param.rotation)) {
case PhotoBase::Result::kErrorInvalidArgument:
LOG(ERROR) << "Invalid rotation angle";
return false;
Expand All @@ -47,60 +43,15 @@ bool tryToProcess(ImageVariants& images, ProcessImageParam param) {
break;
}
if (param.greyscale) {
LOG(INFO) << "Converting image to greyscale";
inst.to_greyscale();
procAll.to_greyscale();
}
LOG(INFO) << "Writing image to " << param.destPath;
return inst.write(param.destPath);
} else {
LOG(ERROR) << "Counld't read or parse image";
return procAll.write(param.destPath);
}
return false;
}

bool processPhotoFile(ProcessImageParam& param) {
ImageVariants images;

// First try: PNG
LOG(INFO) << "First try: PNG";
images.emplace<PngImage>();
param.destPath = "output.png";
if (tryToProcess<0>(images, param)) {
LOG(INFO) << "PNG liked it. Wrote image: " << param.destPath;
return true;
}

// Second try: WebP
LOG(INFO) << "Second try: WebP";
images.emplace<WebPImage>();
param.destPath = "output.webp";
if (tryToProcess<1>(images, param)) {
LOG(INFO) << "WebP liked it. Wrote image: " << param.destPath;
return true;
}

// Third try: JPEG
LOG(INFO) << "Third try: JPEG";
images.emplace<JPEGImage>();
param.destPath = "output.jpg";
if (tryToProcess<2>(images, param)) {
LOG(INFO) << "JPEG liked it. Wrote image: " << param.destPath;
return true;
}

// Fourth try: OpenCV
LOG(INFO) << "Fourth try: OpenCV";
images.emplace<OpenCVImage>();
param.destPath = "output.png";
if (tryToProcess<3>(images, param)) {
LOG(INFO) << "OpenCV liked it. Wrote image: " << param.destPath;
return true;
}

// Failed to process
LOG(ERROR) << "No one liked it. Failed to process";
return false;
}
constexpr std::string_view kDownloadFile = "inpic.bin";
constexpr std::string_view kOutputFile = "outpic.png";

void rotateStickerCommand(const Bot& bot, const Message::Ptr message) {
MessageWrapper wrapper(bot, message);
Expand Down Expand Up @@ -152,7 +103,6 @@ void rotateStickerCommand(const Bot& bot, const Message::Ptr message) {
}

const auto file = bot.getApi().getFile(fileid.value());
constexpr std::string_view kDownloadFile = "inpic.bin";
if (!file) {
wrapper.sendMessageOnExit("Failed to download sticker file.");
return;
Expand All @@ -172,6 +122,7 @@ void rotateStickerCommand(const Bot& bot, const Message::Ptr message) {
params.srcPath = kDownloadFile.data();
params.greyscale = greyscale;
params.rotation = rotation;
params.destPath = kOutputFile.data();

if (processPhotoFile(params)) {
const auto infile =
Expand Down
2 changes: 2 additions & 0 deletions src/imagep/ImagePBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ struct PhotoBase {
file, [](FILE* f) { fclose(f); });
}

[[nodiscard]] virtual std::string version() const = 0;

protected:
/**
* @brief Rotates the image by the specified angle.
Expand Down
67 changes: 67 additions & 0 deletions src/imagep/ImageProcAll.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "ImageProcAll.hpp"

#include <filesystem>

#include "imagep/ImagePBase.hpp"

ImageProcessingAll::ImageProcessingAll(std::filesystem::path filename)
: _filename(std::move(filename)) {
#ifdef HAVE_OPENCV
impls.emplace_back(std::make_unique<OpenCVImage>());
#endif
#ifdef HAVE_LIBJPEG
impls.emplace_back(std::make_unique<JPEGImage>());
#endif
#ifdef HAVE_LIBPNG
impls.emplace_back(std::make_unique<PngImage>());
#endif
#ifdef HAVE_LIBWEBP
impls.emplace_back(std::make_unique<WebPImage>());
#endif
}

bool ImageProcessingAll::read() {
for (auto& impl : impls) {
if (impl->read(_filename)) {
// We found the backend suitable. Select one and dealloc others.
_impl = std::move(impl);
impls.clear();

LOG(INFO) << "Using implementation: " << _impl->version();
return true;
}
}
LOG(INFO) << "No backend was suitable to read";
return false;
}

PhotoBase::Result ImageProcessingAll::rotate(int angle) {
if (!_impl) {
LOG(ERROR) << "No backend selected for rotation";
return PhotoBase::Result::kErrorNoData;
}
DLOG(INFO) << "Calling impl->rotate with angle: " << angle;
return _impl->rotate_image(angle);
}

void ImageProcessingAll::to_greyscale() {
if (!_impl) {
LOG(ERROR) << "No backend selected for greyscale conversion";
return;
}
DLOG(INFO) << "Calling impl->to_greyscale";
_impl->to_greyscale();
}

bool ImageProcessingAll::write(const std::filesystem::path& filename) {
if (!_impl) {
LOG(ERROR) << "No backend selected for writing";
return false;
}
if (filename.empty()) {
LOG(ERROR) << "Filename is empty";
return false;
}
DLOG(INFO) << "Calling impl->write with filename: " << filename;
return _impl->write(filename);
}
31 changes: 31 additions & 0 deletions src/imagep/ImageProcAll.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <memory>

#ifdef HAVE_OPENCV
#include "ImageProcOpenCV.hpp"
#endif
#ifdef HAVE_LIBJPEG
#include "ImageTypeJPEG.hpp"
#endif
#ifdef HAVE_LIBPNG
#include "ImageTypePNG.hpp"
#endif
#ifdef HAVE_LIBWEBP
#include "ImageTypeWEBP.hpp"
#endif
#include "imagep/ImagePBase.hpp"

struct ImageProcessingAll {
bool read();
PhotoBase::Result rotate(int angle);
void to_greyscale();
bool write(const std::filesystem::path& filename);

explicit ImageProcessingAll(std::filesystem::path filename);

private:
std::filesystem::path _filename;
std::vector<std::unique_ptr<PhotoBase>> impls;
std::unique_ptr<PhotoBase> _impl;
};
5 changes: 5 additions & 0 deletions src/imagep/ImageProcOpenCV.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "ImageProcOpenCV.hpp"
#include <opencv2/core/utility.hpp>

bool OpenCVImage::read(const std::filesystem::path& filename) {
image = cv::imread(filename.string(), cv::IMREAD_UNCHANGED);
Expand Down Expand Up @@ -49,3 +50,7 @@ bool OpenCVImage::write(const std::filesystem::path& filename) {
}
return true;
}

std::string OpenCVImage::version() const {
return "OpenCV version: " + cv::getVersionString();
}
7 changes: 4 additions & 3 deletions src/imagep/ImageProcOpenCV.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#pragma once

#include "ImagePBase.hpp"

#include <opencv2/opencv.hpp>

#include "ImagePBase.hpp"

/**
* @brief Derived class for photo manipulation using OpenCV.
*
Expand All @@ -12,13 +12,14 @@
*/
class OpenCVImage : public PhotoBase {
public:
OpenCVImage() = default;
OpenCVImage() noexcept = default;
~OpenCVImage() override = default;

bool read(const std::filesystem::path& filename) override;
Result _rotate_image(int angle) override;
void to_greyscale() override;
bool write(const std::filesystem::path& filename) override;
std::string version() const override;

private:
cv::Mat image;
Expand Down
5 changes: 0 additions & 5 deletions src/imagep/ImageProcessingAll.hpp

This file was deleted.

8 changes: 8 additions & 0 deletions src/imagep/ImageTypeJPEG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,12 @@ bool JPEGImage::write(const std::filesystem::path& filename) {
fclose(outfile);

return true;
}

#define _STR(x) #x
#define STR(x) _STR(x)
#define LIBJPEG_TURBO_VERSION_STR STR(LIBJPEG_TURBO_VERSION)

std::string JPEGImage::version() const {
return "libjpeg version " LIBJPEG_TURBO_VERSION_STR;
}
3 changes: 2 additions & 1 deletion src/imagep/ImageTypeJPEG.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
*/
class JPEGImage : public PhotoBase {
public:
JPEGImage() = default;
JPEGImage() noexcept = default;
~JPEGImage() override = default;

bool read(const std::filesystem::path& filename) override;
Result _rotate_image(int angle) override;
void to_greyscale() override;
bool write(const std::filesystem::path& filename) override;
std::string version() const override;

private:
std::unique_ptr<unsigned char[]> image_data;
Expand Down
5 changes: 5 additions & 0 deletions src/imagep/ImageTypePNG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <cstddef>
#include <filesystem>
#include <string>

namespace {
void absl_warn_fn(png_structp png_ptr, png_const_charp error_message) {
Expand Down Expand Up @@ -258,3 +259,7 @@ bool PngImage::write(const std::filesystem::path& filename) {
contains_data = false;
return true;
}

std::string PngImage::version() const {
return PNG_LIBPNG_VER_STRING;
}
4 changes: 3 additions & 1 deletion src/imagep/ImageTypePNG.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include "ImagePBase.hpp"

struct PngImage : PhotoBase {
PngImage() = default;
PngImage() noexcept = default;
~PngImage() override = default;

struct MiniSharedMalloc {
Expand Down Expand Up @@ -77,6 +77,8 @@ struct PngImage : PhotoBase {
*/
bool write(const std::filesystem::path& filename) override;

std::string version() const override;

private:
using transform_fn_t =
std::function<void(png_uint_32 src_width, png_uint_32 src_height,
Expand Down
Loading

0 comments on commit 2588f30

Please sign in to comment.