[gtk/image-loading: 4/12] Add code to save jpegs




commit e720f9d35d2ca66936f6d3378839fb3fdb8e1578
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Sep 14 17:40:29 2021 -0400

    Add code to save jpegs

 gdk/loaders/gdkjpeg.c        | 205 ++++++++++++++++++++++++++++++++++++++++---
 gdk/loaders/gdkjpegprivate.h |   2 +
 2 files changed, 196 insertions(+), 11 deletions(-)
---
diff --git a/gdk/loaders/gdkjpeg.c b/gdk/loaders/gdkjpeg.c
index 74a1c9623a..506a278aaa 100644
--- a/gdk/loaders/gdkjpeg.c
+++ b/gdk/loaders/gdkjpeg.c
@@ -70,7 +70,97 @@ output_message_handler (j_common_ptr cinfo)
 }
 
 /* }}} */
- /* {{{ Public API */
+/* {{{ Format conversion */
+
+static void
+convert_rgba_to_rgb (guchar *data,
+                     int     width,
+                     int     height,
+                     int     stride)
+{
+  gsize x, y;
+  guchar *src, *dest;
+
+  for (y = 0; y < height; y++)
+    {
+      src = data;
+      dest = data;
+
+      for (x = 0; x < width; x++)
+        {
+          guint32 pixel;
+
+          memcpy (&pixel, src, sizeof (guint32));
+
+          dest[0] = (pixel & 0x00ff0000) >> 16;
+          dest[1] = (pixel & 0x0000ff00) >>  8;
+          dest[2] = (pixel & 0x000000ff) >>  0;
+
+          dest += 3;
+          src += 4;
+        }
+
+      data += stride;
+    }
+}
+
+static void
+convert_grayscale_to_rgb (guchar *data,
+                          int     width,
+                          int     height,
+                          int     stride)
+{
+  gsize x, y;
+  guchar *dest, *src;
+
+  for (y = 0; y < height; y++)
+    {
+      src = data + width;
+      dest = data + 3 * width;
+      for (x = 0; x < width; x++)
+        {
+          dest -= 3;
+          src -= 1;
+          dest[0] = *src;
+          dest[1] = *src;
+          dest[2] = *src;
+        }
+      data += stride;
+    }
+}
+
+static void
+convert_cmyk_to_rgba (guchar *data,
+                      int     width,
+                      int     height,
+                      int     stride)
+{
+  gsize x, r;
+  guchar *dest;
+
+  for (r = 0; r < height; r++)
+    {
+      dest = data;
+      for (x = 0; x < width; x++)
+        {
+          int c, m, y, k;
+
+          c = dest[0];
+          m = dest[1];
+          y = dest[2];
+          k = dest[3];
+          dest[0] = k * c / 255;
+          dest[1] = k * m / 255;
+          dest[2] = k * y / 255;
+          dest[3] = 255;
+          dest += 4;
+        }
+      data += stride;
+    }
+}
+
+ /* }}} */
+/* {{{ Public API */
 
 GdkTexture *
 gdk_load_jpeg (GBytes  *input_bytes,
@@ -78,13 +168,12 @@ gdk_load_jpeg (GBytes  *input_bytes,
 {
   struct jpeg_decompress_struct info;
   struct error_handler_data jerr;
-  struct jpeg_error_mgr err;
-  int width, height;
-  int size;
+  guint width, height, stride;
   unsigned char *data;
   unsigned char *row[1];
   GBytes *bytes;
   GdkTexture *texture;
+  GdkMemoryFormat format;
 
   info.err = jpeg_std_error (&jerr.pub);
   jerr.pub.error_exit = fatal_error_handler;
@@ -97,7 +186,6 @@ gdk_load_jpeg (GBytes  *input_bytes,
       return NULL;
     }
 
-  info.err = jpeg_std_error (&err);
   jpeg_create_decompress (&info);
 
   jpeg_mem_src (&info,
@@ -110,8 +198,27 @@ gdk_load_jpeg (GBytes  *input_bytes,
   width = info.output_width;
   height = info.output_height;
 
-  size = width * height * 3;
-  data = g_try_malloc_n (width * 3, height);
+  switch ((int)info.out_color_space)
+    {
+    case JCS_GRAYSCALE:
+    case JCS_RGB:
+      stride = 3 * width;
+      data = g_try_malloc_n (stride, height);
+      format = GDK_MEMORY_R8G8B8;
+      break;
+    case JCS_CMYK:
+      stride = 4 * width;
+      data = g_try_malloc_n (stride, height);
+      format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
+      break;
+    default:
+      g_set_error (error,
+                   GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED,
+                   "Unsupported colorspace in jpeg (%d)", info.out_color_space);
+      jpeg_destroy_decompress (&info);
+      return NULL;
+    }
+
   if (!data)
     {
       g_set_error_literal (error,
@@ -123,24 +230,100 @@ gdk_load_jpeg (GBytes  *input_bytes,
 
   while (info.output_scanline < info.output_height)
     {
-       row[0] = (unsigned char *)(&data[3 *info.output_width * info.output_scanline]);
+       row[0] = (unsigned char *)(&data[stride * info.output_scanline]);
        jpeg_read_scanlines (&info, row, 1);
     }
 
+  switch ((int)info.out_color_space)
+    {
+    case JCS_GRAYSCALE:
+      convert_grayscale_to_rgb (data, width, height, stride);
+      format = GDK_MEMORY_R8G8B8;
+      break;
+    case JCS_RGB:
+      break;
+    case JCS_CMYK:
+      convert_cmyk_to_rgba (data, width, height, stride);
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
   jpeg_finish_decompress (&info);
   jpeg_destroy_decompress (&info);
 
-  bytes = g_bytes_new_take (data, size);
+  bytes = g_bytes_new_take (data, stride * height);
 
   texture = gdk_memory_texture_new (width, height,
-                                    GDK_MEMORY_R8G8B8,
-                                    bytes, width * 3);
+                                    format,
+                                    bytes, stride);
 
   g_bytes_unref (bytes);
 
   return texture;
 }
 
+GBytes *
+gdk_save_jpeg (GdkTexture *texture)
+{
+  struct jpeg_compress_struct info;
+  struct error_handler_data jerr;
+  struct jpeg_error_mgr err;
+  guchar *data = NULL;
+  gulong size = 0;
+  guchar *input = NULL;
+  guchar *row;
+  int width, height, stride;
+
+  width = gdk_texture_get_width (texture);
+  height = gdk_texture_get_height (texture);
+
+  info.err = jpeg_std_error (&jerr.pub);
+  jerr.pub.error_exit = fatal_error_handler;
+  jerr.pub.output_message = output_message_handler;
+  jerr.error = NULL;
+
+  if (sigsetjmp (jerr.setjmp_buffer, 1))
+    {
+      free (data);
+      g_free (input);
+      jpeg_destroy_compress (&info);
+      return NULL;
+    }
+
+  info.err = jpeg_std_error (&err);
+  jpeg_create_compress (&info);
+  info.image_width = width;
+  info.image_height = height;
+  info.input_components = 3;
+  info.in_color_space = JCS_RGB;
+
+  jpeg_set_defaults (&info);
+  jpeg_set_quality (&info, 75, TRUE);
+
+  jpeg_mem_dest (&info, &data, &size);
+
+  stride = width * 4;
+  input = g_malloc (stride * height);
+  gdk_texture_download (texture, input, stride);
+  convert_rgba_to_rgb (data, width, height, stride);
+
+  jpeg_start_compress (&info, TRUE);
+
+  while (info.next_scanline < info.image_height)
+    {
+      row = &input[info.next_scanline * stride];
+      jpeg_write_scanlines (&info, &row, 1);
+    }
+
+  jpeg_finish_compress (&info);
+
+  g_free (input);
+  jpeg_destroy_compress (&info);
+
+  return g_bytes_new_with_free_func (data, size, (GDestroyNotify) free, NULL);
+}
+
 /* }}} */
 
 /* vim:set foldmethod=marker expandtab: */
diff --git a/gdk/loaders/gdkjpegprivate.h b/gdk/loaders/gdkjpegprivate.h
index 4dcccbaf8d..28e7b90f28 100644
--- a/gdk/loaders/gdkjpegprivate.h
+++ b/gdk/loaders/gdkjpegprivate.h
@@ -26,6 +26,8 @@
 GdkTexture *gdk_load_jpeg         (GBytes           *bytes,
                                    GError          **error);
 
+GBytes     *gdk_save_jpeg         (GdkTexture     *texture);
+
 static inline gboolean
 gdk_is_jpeg (GBytes *bytes)
 {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]