This patch adds the RSaveRawImage() function to the WRaster lib
to be able to save image structure in memory.
The WRaster lib version is bumped.
---
configure.ac | 4 +-
wrlib/ChangeLog | 2 +
wrlib/imgformat.h | 2 +
wrlib/load.c | 5 ++
wrlib/save.c | 21 ++++++-
wrlib/save_jpeg.c | 139 ++++++++++++++++++++++++++++++++++++++-------
wrlib/save_png.c | 126 ++++++++++++++++++++++++++++++++++------
wrlib/
wraster.h.in | 3 +
8 files changed, 263 insertions(+), 39 deletions(-)
diff --git a/
configure.ac b/
configure.acindex e0f860a7..ad67fc91 100644
--- a/
configure.ac+++ b/
configure.ac@@ -71,9 +71,9 @@ dnl 6. If any interfaces have been removed or changed since the last
dnl public release, then set age to 0.
dnl
dnl libwraster
-WRASTER_CURRENT=7
+WRASTER_CURRENT=8
WRASTER_REVISION=0
-WRASTER_AGE=1
+WRASTER_AGE=2
WRASTER_VERSION=$WRASTER_CURRENT:$WRASTER_REVISION:$WRASTER_AGE
AC_SUBST(WRASTER_VERSION)
dnl
diff --git a/wrlib/ChangeLog b/wrlib/ChangeLog
index d6d99681..21e13784 100644
--- a/wrlib/ChangeLog
+++ b/wrlib/ChangeLog
@@ -1,3 +1,5 @@
+- added RSaveRawImage()
+
- added RSaveTitledImage()
- removed obsoleted RDestroyImage()
diff --git a/wrlib/imgformat.h b/wrlib/imgformat.h
index 209b375f..a903bc3f 100644
--- a/wrlib/imgformat.h
+++ b/wrlib/imgformat.h
@@ -90,10 +90,12 @@ Bool RSaveXPM(RImage *image, const char *filename);
#ifdef USE_PNG
Bool RSavePNG(RImage *image, const char *filename, char *title);
+Bool RSaveRawPNG(RImage *image, char *title, unsigned char **out_buf, size_t *out_size);
#endif
#ifdef USE_JPEG
Bool RSaveJPEG(RImage *image, const char *filename, char *title);
+Bool RSaveRawJPEG(RImage *image, char *title, unsigned char **out_buf, size_t *out_size);
#endif
/*
diff --git a/wrlib/load.c b/wrlib/load.c
index ef4e3b94..0f824201 100644
--- a/wrlib/load.c
+++ b/wrlib/load.c
@@ -162,6 +162,11 @@ RImage *RLoadImage(RContext *context, const char *file, int index)
assert(file != NULL);
+
/* just to suppress the compilation warning as index is only used with TIFF and GIF */
+#if !defined(USE_TIFF) && !defined(USE_GIF)
+
(void)index;
+#endif
+
if (RImageCacheSize < 0)
init_cache();
diff --git a/wrlib/save.c b/wrlib/save.c
index eee8ce54..840f002d 100644
--- a/wrlib/save.c
+++ b/wrlib/save.c
@@ -3,7 +3,7 @@
* Raster graphics library
*
* Copyright (c) 1998-2003 Alfredo K. Kojima
- * Copyright (c) 2013-2023 Window Maker Team
+ * Copyright (c) 2013-2025 Window Maker Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -40,6 +40,25 @@ Bool RSaveImage(RImage *image, const char *filename, const char *format)
return RSaveTitledImage(image, filename, format, NULL);
}
+Bool RSaveRawImage(RImage *image, const char *format, unsigned char **out_buf, size_t *out_size)
+{
+#ifdef USE_PNG
+
if (strcasecmp(format, "PNG") == 0)
+
return RSaveRawPNG(image, NULL, out_buf, out_size);
+#endif
+
+#ifdef USE_JPEG
+
if (strcasecmp(format, "JPG") == 0)
+
return RSaveRawJPEG(image, NULL, out_buf, out_size);
+
+
if (strcasecmp(format, "JPEG") == 0)
+
return RSaveRawJPEG(image, NULL, out_buf, out_size);
+#endif
+
+
RErrorCode = RERR_BADFORMAT;
+
return False;
+}
+
Bool RSaveTitledImage(RImage *image, const char *filename, const char *format, char *title)
{
#ifdef USE_PNG
diff --git a/wrlib/save_jpeg.c b/wrlib/save_jpeg.c
index 5320ba47..33a6d178 100644
--- a/wrlib/save_jpeg.c
+++ b/wrlib/save_jpeg.c
@@ -2,7 +2,7 @@
*
* Raster graphics library
*
- * Copyright (c) 2023 Window Maker Team
+ * Copyright (c) 2023-2025 Window Maker Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -33,39 +33,92 @@
#include "imgformat.h"
#include "wr_i18n.h"
+/* Structure for JPEG memory destination */
+struct jpeg_mem_data {
+
unsigned char **out_buf;
+
size_t *out_size;
+
unsigned char *buffer;
+
size_t buffer_size;
+};
+
+/* JPEG memory destination methods */
+static void jpeg_init_mem_destination(j_compress_ptr cinfo)
+{
+
struct jpeg_mem_data *dest = (struct jpeg_mem_data *)cinfo->client_data;
+
dest->buffer_size = 32768; /* Initial buffer size */
+
dest->buffer = malloc(dest->buffer_size);
+
if (!dest->buffer) {
+
/* Memory allocation failed - will be caught by caller */
+
dest->buffer_size = 0;
+
return;
+
}
+
cinfo->dest->next_output_byte = dest->buffer;
+
cinfo->dest->free_in_buffer = dest->buffer_size;
+}
+
+static boolean jpeg_empty_mem_output_buffer(j_compress_ptr cinfo)
+{
+
struct jpeg_mem_data *dest = (struct jpeg_mem_data *)cinfo->client_data;
+
size_t old_size = dest->buffer_size;
+
dest->buffer_size *= 2;
+
dest->buffer = realloc(dest->buffer, dest->buffer_size);
+
if (!dest->buffer) {
+
/* Memory allocation failed - signal error */
+
dest->buffer_size = 0;
+
return FALSE;
+
}
+
cinfo->dest->next_output_byte = dest->buffer + old_size;
+
cinfo->dest->free_in_buffer = dest->buffer_size - old_size;
+
return TRUE;
+}
+
+static void jpeg_term_mem_destination(j_compress_ptr cinfo)
+{
+
struct jpeg_mem_data *dest = (struct jpeg_mem_data *)cinfo->client_data;
+
*dest->out_size = dest->buffer_size - cinfo->dest->free_in_buffer;
+
*dest->out_buf = dest->buffer;
+
/* Don't free dest->buffer here - caller will free it */
+}
+
/*
- * Save RImage to JPEG image
+ * Save RImage to JPEG data in memory
*/
-Bool RSaveJPEG(RImage *img, const char *filename, char *title)
+Bool RSaveRawJPEG(RImage *img, char *title, unsigned char **out_buf, size_t *out_size)
{
-
FILE *file;
-
int x, y, img_depth;
+
int x, y;
char *buffer;
RColor pixel;
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
+
struct jpeg_destination_mgr jpeg_dest;
+
struct jpeg_mem_data mem_data;
JSAMPROW row_pointer;
-
file = fopen(filename, "wb");
-
if (!file) {
-
RErrorCode = RERR_OPEN;
-
return False;
-
}
-
-
if (img->format == RRGBAFormat)
-
img_depth = 4;
-
else
-
img_depth = 3;
+
*out_buf = NULL;
+
*out_size = 0;
/* collect separate RGB values to a buffer */
buffer = malloc(sizeof(char) * 3 * img->width * img->height);
+
if (!buffer) {
+
RErrorCode = RERR_NOMEMORY;
+
return False;
+
}
+
for (y = 0; y < img->height; y++) {
for (x = 0; x < img->width; x++) {
RGetPixel(img, x, y, &pixel);
-
buffer[y*img->width*3+x*3+0] = (char)(pixel.red);
-
buffer[y*img->width*3+x*3+1] = (char)(pixel.green);
-
buffer[y*img->width*3+x*3+2] = (char)(pixel.blue);
+
/* Handle transparent pixels by converting them to white
+
* since JPEG doesn't support transparency */
+
if (pixel.alpha == 0) {
+
buffer[y*img->width*3+x*3+0] = (char)255; /* white red */
+
buffer[y*img->width*3+x*3+1] = (char)255; /* white green */
+
buffer[y*img->width*3+x*3+2] = (char)255; /* white blue */
+
} else {
+
buffer[y*img->width*3+x*3+0] = (char)(pixel.red);
+
buffer[y*img->width*3+x*3+1] = (char)(pixel.green);
+
buffer[y*img->width*3+x*3+2] = (char)(pixel.blue);
+
}
}
}
@@ -74,7 +127,17 @@ Bool RSaveJPEG(RImage *img, const char *filename, char *title)
/* Initialize cinfo structure */
jpeg_create_compress(&cinfo);
-
jpeg_stdio_dest(&cinfo, file);
+
+
/* Set up custom memory destination */
+
mem_data.out_buf = out_buf;
+
mem_data.out_size = out_size;
+
jpeg_dest.init_destination = jpeg_init_mem_destination;
+
jpeg_dest.empty_output_buffer = jpeg_empty_mem_output_buffer;
+
jpeg_dest.term_destination = jpeg_term_mem_destination;
+
cinfo.dest = &jpeg_dest;
+
cinfo.dest->next_output_byte = NULL;
+
cinfo.dest->free_in_buffer = 0;
+
cinfo.client_data = &mem_data;
cinfo.image_width = img->width;
cinfo.image_height = img->height;
@@ -90,15 +153,51 @@ Bool RSaveJPEG(RImage *img, const char *filename, char *title)
jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET*)title, strlen(title));
while (cinfo.next_scanline < cinfo.image_height) {
-
row_pointer = (JSAMPROW) &buffer[cinfo.next_scanline * img_depth * img->width];
+
row_pointer = (JSAMPROW) &buffer[cinfo.next_scanline * 3 * img->width];
jpeg_write_scanlines(&cinfo, &row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
+
jpeg_destroy_compress(&cinfo);
/* Clean */
free(buffer);
+
+
return True;
+}
+
+/*
+ * Save RImage to JPEG file
+ */
+
+Bool RSaveJPEG(RImage *img, const char *filename, char *title)
+{
+
FILE *file;
+
unsigned char *jpeg_data;
+
size_t jpeg_size;
+
size_t written;
+
+
/* Generate JPEG data in memory */
+
if (!RSaveRawJPEG(img, title, &jpeg_data, &jpeg_size)) {
+
return False;
+
}
+
+
/* Write to file */
+
file = fopen(filename, "wb");
+
if (!file) {
+
free(jpeg_data);
+
RErrorCode = RERR_OPEN;
+
return False;
+
}
+
+
written = fwrite(jpeg_data, 1, jpeg_size, file);
fclose(file);
+
free(jpeg_data);
+
+
if (written != jpeg_size) {
+
RErrorCode = RERR_WRITE;
+
return False;
+
}
return True;
}
diff --git a/wrlib/save_png.c b/wrlib/save_png.c
index 40d3b996..c8504118 100644
--- a/wrlib/save_png.c
+++ b/wrlib/save_png.c
@@ -2,7 +2,7 @@
*
* Raster graphics library
*
- * Copyright (c) 2023 Window Maker Team
+ * Copyright (c) 2023-2025 Window Maker Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -33,12 +33,51 @@
#include "imgformat.h"
#include "wr_i18n.h"
+/* Structure to hold PNG data in memory */
+struct png_mem_data {
+
unsigned char *buffer;
+
size_t size;
+
size_t capacity;
+};
+
+/* Callback function to write PNG data to memory buffer */
+static void png_write_to_memory(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+
struct png_mem_data *p = (struct png_mem_data *)png_get_io_ptr(png_ptr);
+
size_t new_size = p->size + length;
+
+
/* Expand buffer if necessary */
+
if (new_size > p->capacity) {
+
size_t new_capacity = p->capacity ? p->capacity * 2 : 8192;
+
while (new_capacity < new_size)
+
new_capacity *= 2;
+
+
unsigned char *new_buffer = realloc(p->buffer, new_capacity);
+
if (!new_buffer) {
+
png_error(png_ptr, "Out of memory");
+
return;
+
}
+
p->buffer = new_buffer;
+
p->capacity = new_capacity;
+
}
+
+
/* Copy data to buffer */
+
memcpy(p->buffer + p->size, data, length);
+
p->size += length;
+}
+
+/* Dummy flush function for memory I/O */
+static void png_flush_memory(png_structp png_ptr)
+{
+
/* No-op for memory I/O */
+
(void)png_ptr;
+}
+
/*
- * Save RImage to PNG image
+ * Save RImage to PNG data in memory
*/
-Bool RSavePNG(RImage *img, const char *filename, char *title)
+Bool RSaveRawPNG(RImage *img, char *title, unsigned char **out_buf, size_t *out_size)
{
-
FILE *file;
png_structp png_ptr;
png_infop png_info_ptr;
png_bytep png_row;
@@ -46,17 +85,14 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
int x, y;
int width = img->width;
int height = img->height;
+
struct png_mem_data png_data = {NULL, 0, 0};
-
file = fopen(filename, "wb");
-
if (file == NULL) {
-
RErrorCode = RERR_OPEN;
-
return False;
-
}
+
*out_buf = NULL;
+
*out_size = 0;
/* Initialize write structure */
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
-
fclose(file);
RErrorCode = RERR_NOMEMORY;
return False;
}
@@ -64,19 +100,22 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
/* Initialize info structure */
png_info_ptr = png_create_info_struct(png_ptr);
if (png_info_ptr == NULL) {
-
fclose(file);
+
png_destroy_write_struct(&png_ptr, NULL);
RErrorCode = RERR_NOMEMORY;
return False;
}
/* Setup Exception handling */
-
if (setjmp(png_jmpbuf (png_ptr))) {
-
fclose(file);
+
if (setjmp(png_jmpbuf(png_ptr))) {
+
if (png_data.buffer)
+
free(png_data.buffer);
+
png_destroy_write_struct(&png_ptr, &png_info_ptr);
RErrorCode = RERR_INTERNAL;
return False;
}
-
png_init_io(png_ptr, file);
+
/* Set up memory I/O */
+
png_set_write_fn(png_ptr, &png_data, png_write_to_memory, png_flush_memory);
/* Write header (8 bit colour depth) */
png_set_IHDR(png_ptr, png_info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
@@ -96,6 +135,13 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
/* Allocate memory for one row (3 bytes per pixel - RGB) */
png_row = (png_bytep) malloc(3 * width * sizeof(png_byte));
+
if (!png_row) {
+
if (png_data.buffer)
+
free(png_data.buffer);
+
png_destroy_write_struct(&png_ptr, &png_info_ptr);
+
RErrorCode = RERR_NOMEMORY;
+
return False;
+
}
/* Write image data */
for (y = 0; y < height; y++) {
@@ -114,8 +160,7 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
/* End write */
png_write_end(png_ptr, NULL);
-
/* Clean */
-
fclose(file);
+
/* Clean up structures */
if (png_info_ptr != NULL)
png_free_data(png_ptr, png_info_ptr, PNG_FREE_ALL, -1);
if (png_ptr != NULL)
@@ -123,5 +168,54 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
if (png_row != NULL)
free(png_row);
+
/* Return the buffer */
+
*out_buf = png_data.buffer;
+
*out_size = png_data.size;
+
+
return True;
+}
+
+/*
+ * Save RImage to PNG image
+ */
+Bool RSavePNG(RImage *img, const char *filename, char *title)
+{
+
FILE *file;
+
unsigned char *png_data = NULL;
+
size_t png_size = 0;
+
size_t written;
+
+
if (!img || !filename) {
+
RErrorCode = RERR_BADIMAGEFILE;
+
return False;
+
}
+
+
/* Use RSaveRawPNG to generate PNG data in memory */
+
if (!RSaveRawPNG(img, title, &png_data, &png_size)) {
+
/* Error code already set by RSaveRawPNG */
+
return False;
+
}
+
+
/* Open file for writing */
+
file = fopen(filename, "wb");
+
if (file == NULL) {
+
free(png_data);
+
RErrorCode = RERR_OPEN;
+
return False;
+
}
+
+
/* Write PNG data to file */
+
written = fwrite(png_data, 1, png_size, file);
+
fclose(file);
+
+
/* Check if all data was written */
+
if (written != png_size) {
+
free(png_data);
+
RErrorCode = RERR_WRITE;
+
return False;
+
}
+
+
/* Clean up */
+
free(png_data);
return True;
}
diff --git a/wrlib/
wraster.h.in b/wrlib/
wraster.h.inindex 6f6e5df4..6414455e 100644
--- a/wrlib/
wraster.h.in+++ b/wrlib/
wraster.h.in@@ -391,6 +391,9 @@ RImage *RGetImageFromXPMData(RContext *context, char **xpmData)
Bool RSaveImage(RImage *image, const char *filename, const char *format)
__wrlib_nonnull(1, 2, 3);
+Bool RSaveRawImage(RImage *image, const char *format, unsigned char **out_buf, size_t *out_size)
+
__wrlib_nonnull(1, 2, 3, 4);
+
Bool RSaveTitledImage(RImage *image, const char *filename, const char *format, char *title)
__wrlib_nonnull(1, 2, 3);
--
2.43.0