提供avif格式转换

This commit is contained in:
zsyg
2025-07-19 20:13:18 +08:00
committed by GitHub
parent 0e90195f9a
commit a445bbdc2d
31 changed files with 1025 additions and 99 deletions

View File

@@ -10,6 +10,9 @@ set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 设置MSYS2库搜索路径
set(MSYS2_LIB_DIR "C:/msys64/ucrt64/lib")
# 查找依赖库(静态版本)
find_package(PkgConfig REQUIRED)
pkg_check_modules(TIFF REQUIRED libtiff-4)
@@ -18,12 +21,30 @@ find_library(TIFF_STATIC_LIB NAMES libtiff.a PATHS ${TIFF_LIBRARY_DIRS})
pkg_check_modules(WEBP REQUIRED libwebp)
find_library(WEBP_STATIC_LIB NAMES libwebp.a PATHS ${WEBP_LIBRARY_DIRS})
pkg_check_modules(AVIF REQUIRED libavif)
find_library(AVIF_STATIC_LIB NAMES libavif.a PATHS ${AVIF_LIBRARY_DIRS})
# 使用find_package查找FLTK(静态版本)
find_package(FLTK REQUIRED)
find_library(FLTK_STATIC_LIB NAMES libfltk.a PATHS ${FLTK_LIBRARY_DIRS})
# 查找其他依赖库
find_library(JPEG_STATIC_LIB NAMES libjpeg.a PATHS ${MSYS2_LIB_DIR})
find_library(ZLIB_STATIC_LIB NAMES libz.a PATHS ${MSYS2_LIB_DIR})
find_library(ZSTD_STATIC_LIB NAMES libzstd.a PATHS ${MSYS2_LIB_DIR})
find_library(SHARPYUV_STATIC_LIB NAMES libsharpyuv.a PATHS ${MSYS2_LIB_DIR})
find_library(JBIG_STATIC_LIB NAMES libjbig.a PATHS ${MSYS2_LIB_DIR})
find_library(LZMA_STATIC_LIB NAMES liblzma.a PATHS ${MSYS2_LIB_DIR})
find_library(LIBDEFLATE_STATIC_LIB NAMES libdeflate.a PATHS ${MSYS2_LIB_DIR})
find_library(LERC_STATIC_LIB NAMES liblerc.a PATHS ${MSYS2_LIB_DIR})
find_library(AOM_STATIC_LIB NAMES libaom.a PATHS ${MSYS2_LIB_DIR})
find_library(YUV_STATIC_LIB NAMES libyuv.a PATHS ${MSYS2_LIB_DIR})
find_library(SVTAV1_ENC_STATIC_LIB NAMES libSvtAv1Enc.a PATHS ${MSYS2_LIB_DIR})
find_library(RAV1E_STATIC_LIB NAMES librav1e.a PATHS ${MSYS2_LIB_DIR})
find_library(DAV1D_STATIC_LIB NAMES libdav1d.a PATHS ${MSYS2_LIB_DIR})
# 包含头文件目录
include_directories(include ${TIFF_INCLUDE_DIRS} ${WEBP_INCLUDE_DIRS} ${FLTK_INCLUDE_DIRS})
include_directories(include ${TIFF_INCLUDE_DIRS} ${WEBP_INCLUDE_DIRS} ${FLTK_INCLUDE_DIRS} ${AVIF_INCLUDE_DIRS})
# 收集所有源文件
file(GLOB SOURCES "src/*.cpp")
@@ -31,16 +52,6 @@ file(GLOB SOURCES "src/*.cpp")
# 创建可执行文件(设置为WIN32应用程序避免控制台窗口)
add_executable(ImageFormatConverter WIN32 ${SOURCES})
# 查找更多依赖库
find_library(JPEG_STATIC_LIB NAMES libjpeg.a)
find_library(ZLIB_STATIC_LIB NAMES libz.a)
find_library(ZSTD_STATIC_LIB NAMES libzstd.a)
find_library(SHARPYUV_STATIC_LIB NAMES libsharpyuv.a)
find_library(JBIG_STATIC_LIB NAMES libjbig.a)
find_library(LZMA_STATIC_LIB NAMES liblzma.a)
find_library(LIBDEFLATE_STATIC_LIB NAMES libdeflate.a)
find_library(LERC_STATIC_LIB NAMES liblerc.a)
# 链接静态库
target_link_libraries(ImageFormatConverter
${TIFF_STATIC_LIB}
@@ -54,7 +65,13 @@ target_link_libraries(ImageFormatConverter
${LZMA_STATIC_LIB}
${LIBDEFLATE_STATIC_LIB}
${LERC_STATIC_LIB}
-lstdc++ -lgcc -lwinpthread -lcomctl32 -lole32 -luuid -lws2_32)
${AVIF_STATIC_LIB}
${AOM_STATIC_LIB}
${YUV_STATIC_LIB}
${SVTAV1_ENC_STATIC_LIB}
${RAV1E_STATIC_LIB}
${DAV1D_STATIC_LIB}
-lstdc++ -lgcc -lwinpthread -lcomctl32 -lole32 -luuid -lws2_32 -lntdll)
# 设置输出目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)

View File

@@ -1,30 +0,0 @@
CXX = g++
CXXFLAGS = -I"C:/msys64/ucrt64/include" -I"./include" -std=c++17 -Wall -mwindows
LDFLAGS = -L"C:/msys64/ucrt64/lib" -lfltk -lfltk_images -ltiff -ljpeg -lz -lzstd -lwebp -llerc -ljbig -llzma -ldeflate -lsharpyuv -lcomctl32 -lgdi32 -lole32 -luuid -lws2_32 -lwinspool -lcomdlg32 -static
SRC = src/main.cpp src/gui.cpp src/utils.cpp src/stb_impl.cpp \
src/png_to_jpg.cpp src/jpg_to_png.cpp src/image_loader.cpp \
src/bmp_to_png.cpp src/bmp_to_jpg.cpp src/png_to_bmp.cpp \
src/jpg_to_bmp.cpp src/tiff_to_bmp.cpp src/bmp_to_tiff.cpp \
src/tiff_to_png.cpp src/png_to_tiff.cpp src/tiff_to_jpg.cpp \
src/jpg_to_tiff.cpp src/webp_to_jpg.cpp src/webp_to_png.cpp \
src/webp_to_bmp.cpp src/webp_to_tiff.cpp src/jpg_to_webp.cpp \
src/png_to_webp.cpp src/bmp_to_webp.cpp src/tiff_to_webp.cpp \
src/jpeg_to_png.cpp src/jpeg_to_bmp.cpp src/jpeg_to_tiff.cpp \
src/jpeg_to_webp.cpp src/png_to_jpeg.cpp src/bmp_to_jpeg.cpp \
src/tiff_to_jpeg.cpp src/webp_to_jpeg.cpp
OBJ = $(SRC:src/%.cpp=obj/%.o)
TARGET = image_converter
all: $(TARGET)
$(TARGET): $(OBJ)
$(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS)
obj/%.o: src/%.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -f $(OBJ) $(TARGET)
.PHONY: all clean

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToBmpConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToJpegConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToJpgConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToPngConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToTiffConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToWebpConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class BmpToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -18,6 +18,7 @@ enum class ImageFormat {
JPG,
TIFF,
WEBP,
AVIF,
UNKNOWN
};

View File

@@ -7,7 +7,7 @@ public:
static ImageData load(const std::string& path);
static bool save_png(const std::string& path, const ImageData& data);
static bool save_jpg(const std::string& path, const ImageData& data, int quality = 90);
static void validate_image(const unsigned char* data, int width, int height);
private:
static void validate_image(const unsigned char* data, int width, int height);
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class JpegToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class JpgToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class PngToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class TiffToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class WebpToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,31 @@
#include "avif_to_bmp.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <stb/stb_image_write.h>
bool AvifToBmpConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载AVIF图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 保存为BMP
return stbi_write_bmp(output_path.c_str(),
data.width,
data.height,
data.channels,
data.pixels.get());
}
bool AvifToBmpConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}

View File

@@ -0,0 +1,32 @@
#include "avif_to_jpeg.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <stb/stb_image_write.h>
bool AvifToJpegConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载AVIF图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 保存为JPEG(默认质量90)
return stbi_write_jpg(output_path.c_str(),
data.width,
data.height,
data.channels,
data.pixels.get(),
90);
}
bool AvifToJpegConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}

View File

@@ -0,0 +1,32 @@
#include "avif_to_jpg.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <stb/stb_image_write.h>
bool AvifToJpgConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载AVIF图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 保存为JPG(默认质量90)
return stbi_write_jpg(output_path.c_str(),
data.width,
data.height,
data.channels,
data.pixels.get(),
90);
}
bool AvifToJpgConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}

View File

@@ -0,0 +1,33 @@
#include "avif_to_png.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <stb/stb_image_write.h>
bool AvifToPngConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载AVIF图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 保存为PNG
return stbi_write_png(output_path.c_str(),
data.width,
data.height,
data.channels,
data.pixels.get(),
data.width * data.channels);
}
bool AvifToPngConverter::validate(const ImageData& data) {
// 确保是有效的图像数据
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}

View File

@@ -0,0 +1,65 @@
#include "avif_to_tiff.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <tiffio.h>
bool AvifToTiffConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载AVIF图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 创建TIFF文件
TIFF* tif = TIFFOpen(output_path.c_str(), "w");
if (!tif) {
return false;
}
// 设置TIFF标签
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, data.width);
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, data.height);
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, data.channels);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC,
(data.channels == 1) ? PHOTOMETRIC_MINISBLACK :
(data.channels == 3) ? PHOTOMETRIC_RGB : PHOTOMETRIC_RGB);
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
// 写入图像数据
tsize_t linebytes = data.width * data.channels;
unsigned char* buf = nullptr;
if (TIFFScanlineSize(tif) == linebytes) {
buf = (unsigned char*)_TIFFmalloc(linebytes);
} else {
buf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(tif));
}
for (uint32_t row = 0; row < data.height; row++) {
memcpy(buf, &data.pixels.get()[row * linebytes], linebytes);
if (TIFFWriteScanline(tif, buf, row, 0) < 0) {
_TIFFfree(buf);
TIFFClose(tif);
return false;
}
}
// 清理资源
_TIFFfree(buf);
TIFFClose(tif);
return true;
}
bool AvifToTiffConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 1 || data.channels == 3 || data.channels == 4);
}

View File

@@ -0,0 +1,50 @@
#include "avif_to_webp.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <webp/encode.h>
bool AvifToWebpConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载AVIF图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 保存为WEBP
FILE* f = fopen(output_path.c_str(), "wb");
if (!f) {
return false;
}
uint8_t* output = nullptr;
size_t size = 0;
if (data.channels == 3) {
size = WebPEncodeRGB(data.pixels.get(), data.width, data.height,
data.width * 3, 90, &output);
} else if (data.channels == 4) {
size = WebPEncodeRGBA(data.pixels.get(), data.width, data.height,
data.width * 4, 90, &output);
}
if (size == 0 || !output) {
fclose(f);
return false;
}
fwrite(output, 1, size, f);
fclose(f);
WebPFree(output);
return true;
}
bool AvifToWebpConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}

View File

@@ -0,0 +1,71 @@
#include "bmp_to_avif.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <stb/stb_image.h>
bool BmpToAvifConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载BMP图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 创建AVIF编码器
avifEncoder* encoder = avifEncoderCreate();
encoder->maxThreads = 4;
encoder->minQuantizer = 20;
encoder->maxQuantizer = 20;
encoder->speed = 6;
// 创建AVIF图像
avifImage* image = avifImageCreate(data.width, data.height, 8, AVIF_PIXEL_FORMAT_YUV420);
avifRGBImage rgbImage;
avifRGBImageSetDefaults(&rgbImage, image);
rgbImage.pixels = data.pixels.get();
rgbImage.rowBytes = data.width * data.channels;
rgbImage.format = (data.channels == 3) ? AVIF_RGB_FORMAT_RGB : AVIF_RGB_FORMAT_RGBA;
// 转换RGB到YUV
if (avifImageRGBToYUV(image, &rgbImage) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 编码AVIF图像
avifRWData output = AVIF_DATA_EMPTY;
if (avifEncoderWrite(encoder, image, &output) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 保存AVIF文件
FILE* f = fopen(output_path.c_str(), "wb");
if (!f) {
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
fwrite(output.data, 1, output.size, f);
fclose(f);
// 清理资源
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return true;
}
bool BmpToAvifConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}

View File

@@ -28,6 +28,18 @@
#include "bmp_to_jpeg.hpp"
#include "tiff_to_jpeg.hpp"
#include "webp_to_jpeg.hpp"
#include "avif_to_png.hpp"
#include "avif_to_jpg.hpp"
#include "avif_to_jpeg.hpp"
#include "avif_to_bmp.hpp"
#include "avif_to_tiff.hpp"
#include "avif_to_webp.hpp"
#include "png_to_avif.hpp"
#include "jpg_to_avif.hpp"
#include "jpeg_to_avif.hpp"
#include "bmp_to_avif.hpp"
#include "tiff_to_avif.hpp"
#include "webp_to_avif.hpp"
#include <FL/Fl_File_Chooser.H>
#include <FL/fl_ask.H>
#include <stdexcept>
@@ -77,6 +89,18 @@ MainWindow::MainWindow(int w, int h, const char* title)
format_choice->add("BMP to JPEG");
format_choice->add("TIFF to JPEG");
format_choice->add("WEBP to JPEG");
format_choice->add("AVIF to PNG");
format_choice->add("AVIF to JPG");
format_choice->add("AVIF to JPEG");
format_choice->add("AVIF to BMP");
format_choice->add("AVIF to TIFF");
format_choice->add("AVIF to WEBP");
format_choice->add("PNG to AVIF");
format_choice->add("JPG to AVIF");
format_choice->add("JPEG to AVIF");
format_choice->add("BMP to AVIF");
format_choice->add("TIFF to AVIF");
format_choice->add("WEBP to AVIF");
format_choice->value(0);
convert_btn = new Fl_Button(150, 150, 100, 30, "转换");
@@ -202,6 +226,42 @@ void MainWindow::convert_cb(Fl_Widget* w, void* data) {
case 27: // WEBP to JPEG
success = WebpToJpegConverter::convert(input, output);
break;
case 28: // AVIF to PNG
success = AvifToPngConverter().convert(input, output);
break;
case 29: // AVIF to JPG
success = AvifToJpgConverter().convert(input, output);
break;
case 30: // AVIF to JPEG
success = AvifToJpegConverter().convert(input, output);
break;
case 31: // AVIF to BMP
success = AvifToBmpConverter().convert(input, output);
break;
case 32: // AVIF to TIFF
success = AvifToTiffConverter().convert(input, output);
break;
case 33: // AVIF to WEBP
success = AvifToWebpConverter().convert(input, output);
break;
case 34: // PNG to AVIF
success = PngToAvifConverter().convert(input, output);
break;
case 35: // JPG to AVIF
success = JpgToAvifConverter().convert(input, output);
break;
case 36: // JPEG to AVIF
success = JpegToAvifConverter().convert(input, output);
break;
case 37: // BMP to AVIF
success = BmpToAvifConverter().convert(input, output);
break;
case 38: // TIFF to AVIF
success = TiffToAvifConverter().convert(input, output);
break;
case 39: // WEBP to AVIF
success = WebpToAvifConverter().convert(input, output);
break;
}
if (!success) throw std::runtime_error("转换失败");
} catch (const std::exception& e) {

View File

@@ -3,11 +3,25 @@
#include <stb/stb_image.h>
#include <stb/stb_image_write.h>
#include <webp/decode.h>
#include <avif/avif.h>
#include <fstream>
#include <stdexcept>
#include <iostream>
#include <cstring>
static bool is_avif_file(const std::string& path) {
std::ifstream file(path, std::ios::binary);
if (!file) return false;
char header[12];
if (!file.read(header, 12)) return false;
return !memcmp(header, "\0\0\0 ftypavif", 12) ||
!memcmp(header, "\0\0\0 ftypavis", 12);
}
static bool is_webp_file(const std::string& path) {
std::ifstream file(path, std::ios::binary);
if (!file) return false;
@@ -18,11 +32,74 @@ static bool is_webp_file(const std::string& path) {
return !memcmp(header, "RIFF", 4) && !memcmp(header + 8, "WEBP", 4);
}
static ImageData load_avif(const std::string& path) {
ImageData data;
avifDecoder* decoder = avifDecoderCreate();
if (!decoder) {
throw std::runtime_error("无法创建AVIF解码器");
}
avifResult result = avifDecoderSetIOFile(decoder, path.c_str());
if (result != AVIF_RESULT_OK) {
avifDecoderDestroy(decoder);
throw std::runtime_error("无法读取AVIF文件");
}
result = avifDecoderParse(decoder);
if (result != AVIF_RESULT_OK) {
avifDecoderDestroy(decoder);
throw std::runtime_error("无效的AVIF图像");
}
result = avifDecoderNextImage(decoder);
if (result != AVIF_RESULT_OK) {
avifDecoderDestroy(decoder);
throw std::runtime_error("无法解码AVIF图像");
}
data.width = decoder->image->width;
data.height = decoder->image->height;
data.channels = 4; // AVIF解码为RGBA
// 分配内存并转换图像数据
uint8_t* rgba_pixels = new uint8_t[data.width * data.height * 4];
avifRGBImage rgb;
avifRGBImageSetDefaults(&rgb, decoder->image);
rgb.format = AVIF_RGB_FORMAT_RGBA;
rgb.depth = 8;
rgb.pixels = rgba_pixels;
rgb.rowBytes = data.width * 4;
if (avifImageYUVToRGB(decoder->image, &rgb) != AVIF_RESULT_OK) {
delete[] rgba_pixels;
avifDecoderDestroy(decoder);
throw std::runtime_error("AVIF颜色空间转换失败");
}
avifDecoderDestroy(decoder);
// 验证图像数据
try {
ImageLoader::validate_image(rgba_pixels, data.width, data.height);
} catch (...) {
delete[] rgba_pixels;
throw;
}
data.pixels = std::unique_ptr<unsigned char, void(*)(void*)>(rgba_pixels, [](void* p) { delete[] static_cast<uint8_t*>(p); });
return data;
}
ImageData ImageLoader::load(const std::string& path) {
ImageData data;
// 检查是否为AVIF格式
if (is_avif_file(path)) {
return load_avif(path);
}
// 检查是否为WebP格式
if (is_webp_file(path)) {
else if (is_webp_file(path)) {
// 读取WebP文件数据
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file) {
@@ -65,7 +142,7 @@ ImageData ImageLoader::load(const std::string& path) {
// 验证图像数据
try {
validate_image(rgb_pixels, data.width, data.height);
ImageLoader::validate_image(rgb_pixels, data.width, data.height);
} catch (...) {
delete[] rgb_pixels;
throw;
@@ -75,7 +152,7 @@ ImageData ImageLoader::load(const std::string& path) {
} else {
// 验证图像数据
try {
validate_image(rgba_pixels, data.width, data.height);
ImageLoader::validate_image(rgba_pixels, data.width, data.height);
} catch (...) {
WebPFree(rgba_pixels);
throw;
@@ -96,7 +173,7 @@ ImageData ImageLoader::load(const std::string& path) {
// 验证图像数据
try {
validate_image(pixels, data.width, data.height);
ImageLoader::validate_image(pixels, data.width, data.height);
} catch (...) {
stbi_image_free(pixels);
throw;

View File

@@ -0,0 +1,71 @@
#include "jpeg_to_avif.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <stb/stb_image.h>
bool JpegToAvifConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载JPEG图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 创建AVIF编码器
avifEncoder* encoder = avifEncoderCreate();
encoder->maxThreads = 4;
encoder->minQuantizer = 20;
encoder->maxQuantizer = 20;
encoder->speed = 6;
// 创建AVIF图像
avifImage* image = avifImageCreate(data.width, data.height, 8, AVIF_PIXEL_FORMAT_YUV420);
avifRGBImage rgbImage;
avifRGBImageSetDefaults(&rgbImage, image);
rgbImage.pixels = data.pixels.get();
rgbImage.rowBytes = data.width * data.channels;
rgbImage.format = (data.channels == 3) ? AVIF_RGB_FORMAT_RGB : AVIF_RGB_FORMAT_RGBA;
// 转换RGB到YUV
if (avifImageRGBToYUV(image, &rgbImage) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 编码AVIF图像
avifRWData output = AVIF_DATA_EMPTY;
if (avifEncoderWrite(encoder, image, &output) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 保存AVIF文件
FILE* f = fopen(output_path.c_str(), "wb");
if (!f) {
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
fwrite(output.data, 1, output.size, f);
fclose(f);
// 清理资源
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return true;
}
bool JpegToAvifConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}

View File

@@ -0,0 +1,71 @@
#include "jpg_to_avif.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <stb/stb_image.h>
bool JpgToAvifConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载JPG图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 创建AVIF编码器
avifEncoder* encoder = avifEncoderCreate();
encoder->maxThreads = 4;
encoder->minQuantizer = 20;
encoder->maxQuantizer = 20;
encoder->speed = 6;
// 创建AVIF图像
avifImage* image = avifImageCreate(data.width, data.height, 8, AVIF_PIXEL_FORMAT_YUV420);
avifRGBImage rgbImage;
avifRGBImageSetDefaults(&rgbImage, image);
rgbImage.pixels = data.pixels.get();
rgbImage.rowBytes = data.width * data.channels;
rgbImage.format = (data.channels == 3) ? AVIF_RGB_FORMAT_RGB : AVIF_RGB_FORMAT_RGBA;
// 转换RGB到YUV
if (avifImageRGBToYUV(image, &rgbImage) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 编码AVIF图像
avifRWData output = AVIF_DATA_EMPTY;
if (avifEncoderWrite(encoder, image, &output) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 保存AVIF文件
FILE* f = fopen(output_path.c_str(), "wb");
if (!f) {
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
fwrite(output.data, 1, output.size, f);
fclose(f);
// 清理资源
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return true;
}
bool JpgToAvifConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}

View File

@@ -0,0 +1,71 @@
#include "png_to_avif.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <stb/stb_image.h>
bool PngToAvifConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载PNG图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 创建AVIF编码器
avifEncoder* encoder = avifEncoderCreate();
encoder->maxThreads = 4;
encoder->minQuantizer = 20;
encoder->maxQuantizer = 20;
encoder->speed = 6;
// 创建AVIF图像
avifImage* image = avifImageCreate(data.width, data.height, 8, AVIF_PIXEL_FORMAT_YUV420);
avifRGBImage rgbImage;
avifRGBImageSetDefaults(&rgbImage, image);
rgbImage.pixels = data.pixels.get();
rgbImage.rowBytes = data.width * data.channels;
rgbImage.format = (data.channels == 3) ? AVIF_RGB_FORMAT_RGB : AVIF_RGB_FORMAT_RGBA;
// 转换RGB到YUV
if (avifImageRGBToYUV(image, &rgbImage) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 编码AVIF图像
avifRWData output = AVIF_DATA_EMPTY;
if (avifEncoderWrite(encoder, image, &output) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 保存AVIF文件
FILE* f = fopen(output_path.c_str(), "wb");
if (!f) {
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
fwrite(output.data, 1, output.size, f);
fclose(f);
// 清理资源
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return true;
}
bool PngToAvifConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}

View File

@@ -0,0 +1,71 @@
#include "tiff_to_avif.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <tiffio.h>
bool TiffToAvifConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载TIFF图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 创建AVIF编码器
avifEncoder* encoder = avifEncoderCreate();
encoder->maxThreads = 4;
encoder->minQuantizer = 20;
encoder->maxQuantizer = 20;
encoder->speed = 6;
// 创建AVIF图像
avifImage* image = avifImageCreate(data.width, data.height, 8, AVIF_PIXEL_FORMAT_YUV420);
avifRGBImage rgbImage;
avifRGBImageSetDefaults(&rgbImage, image);
rgbImage.pixels = data.pixels.get();
rgbImage.rowBytes = data.width * data.channels;
rgbImage.format = (data.channels == 3) ? AVIF_RGB_FORMAT_RGB : AVIF_RGB_FORMAT_RGBA;
// 转换RGB到YUV
if (avifImageRGBToYUV(image, &rgbImage) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 编码AVIF图像
avifRWData output = AVIF_DATA_EMPTY;
if (avifEncoderWrite(encoder, image, &output) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 保存AVIF文件
FILE* f = fopen(output_path.c_str(), "wb");
if (!f) {
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
fwrite(output.data, 1, output.size, f);
fclose(f);
// 清理资源
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return true;
}
bool TiffToAvifConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 1 || data.channels == 3 || data.channels == 4);
}

View File

@@ -0,0 +1,71 @@
#include "webp_to_avif.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <webp/decode.h>
bool WebpToAvifConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载WEBP图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 创建AVIF编码器
avifEncoder* encoder = avifEncoderCreate();
encoder->maxThreads = 4;
encoder->minQuantizer = 20;
encoder->maxQuantizer = 20;
encoder->speed = 6;
// 创建AVIF图像
avifImage* image = avifImageCreate(data.width, data.height, 8, AVIF_PIXEL_FORMAT_YUV420);
avifRGBImage rgbImage;
avifRGBImageSetDefaults(&rgbImage, image);
rgbImage.pixels = data.pixels.get();
rgbImage.rowBytes = data.width * data.channels;
rgbImage.format = (data.channels == 3) ? AVIF_RGB_FORMAT_RGB : AVIF_RGB_FORMAT_RGBA;
// 转换RGB到YUV
if (avifImageRGBToYUV(image, &rgbImage) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 编码AVIF图像
avifRWData output = AVIF_DATA_EMPTY;
if (avifEncoderWrite(encoder, image, &output) != AVIF_RESULT_OK) {
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
// 保存AVIF文件
FILE* f = fopen(output_path.c_str(), "wb");
if (!f) {
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return false;
}
fwrite(output.data, 1, output.size, f);
fclose(f);
// 清理资源
avifRWDataFree(&output);
avifEncoderDestroy(encoder);
avifImageDestroy(image);
return true;
}
bool WebpToAvifConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}