8#if defined(MXVK_WITH_JPEG) || defined(WITH_JPEG)
25 struct JpegErrorManager {
27 std::jmp_buf jump_buffer{};
30 using JpegErrorPtr = JpegErrorManager *;
32 void JpegErrorExit(j_common_ptr cinfo) {
33 auto *err =
reinterpret_cast<JpegErrorPtr
>(cinfo->err);
34 (*cinfo->err->output_message)(cinfo);
35 std::longjmp(err->jump_buffer, 1);
38 [[nodiscard]]
bool WriteJpegFromRgb24Surface(
const SDL_Surface *surface,
const char *filename,
const int quality) {
39 if (surface ==
nullptr || filename ==
nullptr) {
40 std::cerr <<
"mxvk: Invalid JPEG save parameters.\n";
44 const int clamped_quality = std::clamp(quality, 0, 100);
46 FILE *file = std::fopen(filename,
"wb");
47 if (file ==
nullptr) {
48 std::cerr <<
"mxvk: Failed to open JPEG output: " << filename <<
"\n";
52 jpeg_compress_struct cinfo{};
53 jpeg_error_mgr jerr{};
54 cinfo.err = jpeg_std_error(&jerr);
55 jpeg_create_compress(&cinfo);
56 jpeg_stdio_dest(&cinfo, file);
58 cinfo.image_width = surface->w;
59 cinfo.image_height = surface->h;
60 cinfo.input_components = 3;
61 cinfo.in_color_space = JCS_RGB;
63 jpeg_set_defaults(&cinfo);
64 jpeg_set_quality(&cinfo, clamped_quality, TRUE);
65 jpeg_start_compress(&cinfo, TRUE);
67 while (cinfo.next_scanline < cinfo.image_height) {
68 JSAMPROW row =
static_cast<JSAMPROW
>(
static_cast<void *
>(
static_cast<Uint8 *
>(surface->pixels) + (cinfo.next_scanline * surface->pitch)));
69 jpeg_write_scanlines(&cinfo, &row, 1);
72 jpeg_finish_compress(&cinfo);
73 jpeg_destroy_compress(&cinfo);
80 SDL_Surface *VK_JPEG::Load(
const char *filename) {
81 if (filename ==
nullptr) {
85 FILE *file = std::fopen(filename,
"rb");
86 if (file ==
nullptr) {
90 jpeg_decompress_struct cinfo{};
91 JpegErrorManager jerr{};
92 cinfo.err = jpeg_std_error(&jerr.pub);
93 jerr.pub.error_exit = JpegErrorExit;
95 if (setjmp(jerr.jump_buffer) != 0) {
96 jpeg_destroy_decompress(&cinfo);
101 jpeg_create_decompress(&cinfo);
102 jpeg_stdio_src(&cinfo, file);
103 jpeg_read_header(&cinfo, TRUE);
104 jpeg_start_decompress(&cinfo);
106 const int width =
static_cast<int>(cinfo.output_width);
107 const int height =
static_cast<int>(cinfo.output_height);
108 if (width <= 0 || height <= 0) {
109 jpeg_finish_decompress(&cinfo);
110 jpeg_destroy_decompress(&cinfo);
117 SDL_Surface *rgb_surface = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGB24);
118 if (rgb_surface ==
nullptr) {
119 jpeg_finish_decompress(&cinfo);
120 jpeg_destroy_decompress(&cinfo);
125 while (cinfo.output_scanline < cinfo.output_height) {
126 JSAMPROW row =
static_cast<JSAMPROW
>(
static_cast<void *
>(
static_cast<Uint8 *
>(rgb_surface->pixels) + (cinfo.output_scanline * rgb_surface->pitch)));
127 jpeg_read_scanlines(&cinfo, &row, 1);
130 jpeg_finish_decompress(&cinfo);
131 jpeg_destroy_decompress(&cinfo);
134 SDL_Surface *rgba_surface = SDL_ConvertSurface(rgb_surface, SDL_PIXELFORMAT_RGBA32);
135 SDL_DestroySurface(rgb_surface);
139 bool VK_JPEG::SaveTexture(SDL_Texture *texture, SDL_Renderer *renderer,
const char *filename,
const int quality) {
140 if (texture ==
nullptr || renderer ==
nullptr || filename ==
nullptr) {
141 std::cerr <<
"mxvk: Invalid SaveTexture JPEG parameters.\n";
145 SDL_Texture *previous_target = SDL_GetRenderTarget(renderer);
146 if (!SDL_SetRenderTarget(renderer, texture)) {
147 std::cerr <<
"mxvk: Failed to set render target for JPEG save: " << SDL_GetError() <<
"\n";
151 SDL_Surface *captured = SDL_RenderReadPixels(renderer,
nullptr);
153 if (!SDL_SetRenderTarget(renderer, previous_target)) {
154 std::cerr <<
"mxvk: Failed to restore render target after JPEG save: " << SDL_GetError() <<
"\n";
157 if (captured ==
nullptr) {
158 std::cerr <<
"mxvk: Failed to read texture pixels for JPEG save: " << SDL_GetError() <<
"\n";
162 std::unique_ptr<SDL_Surface,
decltype(&SDL_DestroySurface)> captured_surface(captured, &SDL_DestroySurface);
163 return SaveSurface(captured_surface.get(), filename, quality);
166 bool VK_JPEG::SaveSurface(
const SDL_Surface *surface,
const char *filename,
const int quality) {
167 if (surface ==
nullptr || filename ==
nullptr) {
171 std::unique_ptr<SDL_Surface,
decltype(&SDL_DestroySurface)> rgb_surface(
nullptr, &SDL_DestroySurface);
172 const SDL_Surface *source_surface = surface;
174 if (surface->format != SDL_PIXELFORMAT_RGB24) {
175 SDL_Surface *converted = SDL_ConvertSurface(
const_cast<SDL_Surface *
>(surface), SDL_PIXELFORMAT_RGB24);
176 if (converted ==
nullptr) {
177 std::cerr <<
"mxvk: Failed to convert surface to RGB24 for JPEG save: " << SDL_GetError() <<
"\n";
180 rgb_surface.reset(converted);
181 source_surface = rgb_surface.get();
184 return WriteJpegFromRgb24Surface(source_surface, filename, quality);
187 SDL_Surface *LoadJPEG(
const char *filename) {
return VK_JPEG::Load(filename); }
189 bool SaveJPEG(SDL_Texture *texture, SDL_Renderer *renderer,
const char *filename,
const int quality) {
return VK_JPEG::SaveTexture(texture, renderer, filename, quality); }
JPEG image loading and saving utilities.
Utilities for loading and saving PNG images.