diff --git a/gdk/gdkjpeg.c b/gdk/gdkjpeg.c new file mode 100644 index 0000000000..e2f26fb9f6 --- /dev/null +++ b/gdk/gdkjpeg.c @@ -0,0 +1,234 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2021 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gdkjpeg.h" + +#include "gdktexture.h" +#include "gdkmemorytextureprivate.h" +#include +#include +#include + +/* {{{ IO handling */ + +#define BUF_SIZE 65536 + +typedef struct { + struct jpeg_source_mgr pub; + JOCTET buffer[BUF_SIZE]; + long skip_next; + GInputStream *stream; +} my_source_mgr; + +static void +init_source (j_decompress_ptr info) +{ + my_source_mgr *src = (my_source_mgr *) info->src; + src->skip_next = 0; +} + +static void +term_source (j_decompress_ptr info) +{ +} + +static boolean +fill_input_buffer (j_decompress_ptr info) +{ + my_source_mgr *src = (my_source_mgr *) info->src; + size_t nbytes; + + nbytes = g_input_stream_read (src->stream, src->buffer, BUF_SIZE, NULL, NULL); + + if (nbytes <= 0) + { + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + + return TRUE; +} + +static void +skip_input_data (j_decompress_ptr info, + long num_bytes) +{ + my_source_mgr *src = (my_source_mgr *) info->src; + + if (num_bytes > 0) + { + while (num_bytes > src->pub.bytes_in_buffer) + { + num_bytes -= src->pub.bytes_in_buffer; + fill_input_buffer (info); + } + + src->pub.next_input_byte += num_bytes; + src->pub.bytes_in_buffer -= num_bytes; + } +} + +/* }}} */ +/* {{{ Error handling */ + +struct error_handler_data { + struct jpeg_error_mgr pub; + sigjmp_buf setjmp_buffer; + GError **error; +}; + +static void +fatal_error_handler (j_common_ptr cinfo) +{ + struct error_handler_data *errmgr; + + errmgr = (struct error_handler_data *) cinfo->err; + + siglongjmp (errmgr->setjmp_buffer, 1); + + g_assert_not_reached (); +} + +static void +output_message_handler (j_common_ptr cinfo) +{ + /* do nothing */ +} + +/* }}} */ + /* {{{ Public API */ + +GdkTexture * +gdk_load_jpeg (GInputStream *stream, + GError **error) +{ + struct jpeg_decompress_struct info; + struct error_handler_data jerr; + struct jpeg_error_mgr err; + my_source_mgr src; + int width, height; + int size; + unsigned char *data; + unsigned char *row[1]; + GBytes *bytes; + GdkTexture *texture; + + info.err = jpeg_std_error (&jerr.pub); + jerr.pub.error_exit = fatal_error_handler; + jerr.pub.output_message = output_message_handler; + jerr.error = error; + + if (sigsetjmp (jerr.setjmp_buffer, 1)) + { + jpeg_destroy_decompress (&info); + return NULL; + } + + info.err = jpeg_std_error (&err); + jpeg_create_decompress (&info); + + src.pub.init_source = init_source; + src.pub.fill_input_buffer = fill_input_buffer; + src.pub.skip_input_data = skip_input_data; + src.pub.resync_to_restart = jpeg_resync_to_restart; + src.pub.term_source = term_source; + src.pub.bytes_in_buffer = 0; + src.pub.next_input_byte = NULL; + src.stream = stream; + + info.src = (struct jpeg_source_mgr *)&src; + + jpeg_read_header (&info, TRUE); + jpeg_start_decompress (&info); + + width = info.output_width; + height = info.output_height; + + size = width * height * 3; + data = g_malloc (size); + + while (info.output_scanline < info.output_height) + { + row[0] = (unsigned char *)(&data[3 *info.output_width * info.output_scanline]); + jpeg_read_scanlines (&info, row, 1); + } + + jpeg_finish_decompress (&info); + jpeg_destroy_decompress (&info); + + bytes = g_bytes_new_take (data, size); + + texture = gdk_memory_texture_new (width, height, + GDK_MEMORY_R8G8B8, + bytes, width * 3); + + g_bytes_unref (bytes); + + return texture; +} + +/* }}} */ +/* {{{ Async code */ + +static void +load_jpeg_in_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GInputStream *stream = source_object; + GdkTexture *texture; + GError *error = NULL; + + texture = gdk_load_jpeg (stream, &error); + + if (texture) + g_task_return_pointer (task, texture, g_object_unref); + else + g_task_return_error (task, error); +} + +void +gdk_load_jpeg_async (GInputStream *stream, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (stream, cancellable, callback, user_data); + g_task_run_in_thread (task, load_jpeg_in_thread); + g_object_unref (task); +} + +GdkTexture * +gdk_load_jpeg_finish (GAsyncResult *result, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (result), error); +} + +/* }}} */ + +/* vim:set foldmethod=marker expandtab: */ diff --git a/gdk/gdkjpeg.h b/gdk/gdkjpeg.h new file mode 100644 index 0000000000..3b865b1fb6 --- /dev/null +++ b/gdk/gdkjpeg.h @@ -0,0 +1,35 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2021 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GDK_JPEG_H__ +#define __GDK_JPEG_H__ + +#include "gdkmemorytexture.h" +#include + +GdkTexture *gdk_load_jpeg (GInputStream *stream, + GError **error); + +void gdk_load_jpeg_async (GInputStream *stream, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GdkTexture *gdk_load_jpeg_finish (GAsyncResult *result, + GError **error); + +#endif diff --git a/gdk/meson.build b/gdk/meson.build index d1295af5c8..da8e5324a9 100644 --- a/gdk/meson.build +++ b/gdk/meson.build @@ -52,6 +52,7 @@ gdk_public_sources = files([ 'gdkdragsurface.c', 'gdkpng.c', 'gdktiff.c', + 'gdkjpeg.c', ]) gdk_public_headers = files([ @@ -256,7 +257,7 @@ endif libgdk = static_library('gdk', sources: [gdk_sources, gdk_backends_gen_headers, gdkconfig], - dependencies: gdk_deps + [libgtk_css_dep, png_dep, tiff_dep], + dependencies: gdk_deps + [libgtk_css_dep, png_dep, tiff_dep, jpeg_dep], link_with: [libgtk_css], include_directories: [confinc, gdkx11_inc, wlinc], c_args: libgdk_c_args + common_cflags, diff --git a/meson.build b/meson.build index f00b571944..8d5344c34c 100644 --- a/meson.build +++ b/meson.build @@ -392,6 +392,7 @@ pixbuf_dep = dependency('gdk-pixbuf-2.0', version: gdk_pixbuf_req, default_options: ['png=enabled', 'jpeg=enabled', 'builtin_loaders=png,jpeg', 'man=false']) png_dep = dependency('libpng16') tiff_dep = dependency('libtiff-4') +jpeg_dep = dependency('libjpeg') epoxy_dep = dependency('epoxy', version: epoxy_req, fallback: ['libepoxy', 'libepoxy_dep']) harfbuzz_dep = dependency('harfbuzz', version: '>= 2.1.0', required: false,