Fix #610: Support heic image format
[geeqie.git] / src / image_load_heif.c
1 /*
2  * Copyright (C) 20019 - The Geeqie Team
3  *
4  * Author: Colin Clark
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include "main.h"
22
23 #include "image-load.h"
24 #include "image_load_heif.h"
25
26 #ifdef HAVE_HEIF
27 #include <libheif/heif.h>
28
29 typedef struct _ImageLoaderHEIF ImageLoaderHEIF;
30 struct _ImageLoaderHEIF {
31         ImageLoaderBackendCbAreaUpdated area_updated_cb;
32         ImageLoaderBackendCbSize size_cb;
33         ImageLoaderBackendCbAreaPrepared area_prepared_cb;
34         gpointer data;
35         GdkPixbuf *pixbuf;
36         guint requested_width;
37         guint requested_height;
38         gboolean abort;
39 };
40
41 static void free_buffer(guchar *pixels, gpointer data)
42 {
43         g_free (pixels);
44 }
45
46 static gboolean image_loader_heif_load(gpointer loader, const guchar *buf, gsize count, GError **error)
47 {
48         ImageLoaderHEIF *ld = (ImageLoaderHEIF *) loader;
49         struct heif_context* ctx;
50         struct heif_image* img;
51         struct heif_error error_code;
52         struct heif_image_handle* handle;
53         guint8* data;
54         gint width, height;
55         gint stride;
56         gboolean alpha;
57
58         ctx = heif_context_alloc();
59
60         error_code = heif_context_read_from_memory_without_copy(ctx, buf, count, NULL);
61         if (error_code.code)
62                 {
63                 log_printf("warning: heif reader error: %s\n", error_code.message);
64                 heif_context_free(ctx);
65                 return FALSE;
66                 }
67
68         // get a handle to the primary image
69         error_code = heif_context_get_primary_image_handle(ctx, &handle);
70         if (error_code.code)
71                 {
72                 log_printf("warning: heif reader error: %s\n", error_code.message);
73                 heif_context_free(ctx);
74                 return FALSE;
75                 }
76
77         // decode the image and convert colorspace to RGB, saved as 24bit interleaved
78         error_code = heif_decode_image(handle, &img, heif_colorspace_RGB, heif_chroma_interleaved_24bit, NULL);
79         if (error_code.code)
80                 {
81                 log_printf("warning: heif reader error: %s\n", error_code.message);
82                 heif_context_free(ctx);
83                 return FALSE;
84                 }
85
86         data = heif_image_get_plane(img, heif_channel_interleaved, &stride);
87
88         height = heif_image_get_height(img,heif_channel_interleaved);
89         width = heif_image_get_width(img,heif_channel_interleaved);
90         alpha = heif_image_handle_has_alpha_channel(handle);
91
92         ld->pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, alpha, 8, width, height, stride, free_buffer, NULL);
93
94         ld->area_updated_cb(loader, 0, 0, width, height, ld->data);
95
96         heif_context_free(ctx);
97
98         return TRUE;
99 }
100
101 static gpointer image_loader_heif_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
102 {
103         ImageLoaderHEIF *loader = g_new0(ImageLoaderHEIF, 1);
104         loader->area_updated_cb = area_updated_cb;
105         loader->size_cb = size_cb;
106         loader->area_prepared_cb = area_prepared_cb;
107         loader->data = data;
108         return (gpointer) loader;
109 }
110
111 static void image_loader_heif_set_size(gpointer loader, int width, int height)
112 {
113         ImageLoaderHEIF *ld = (ImageLoaderHEIF *) loader;
114         ld->requested_width = width;
115         ld->requested_height = height;
116 }
117
118 static GdkPixbuf* image_loader_heif_get_pixbuf(gpointer loader)
119 {
120         ImageLoaderHEIF *ld = (ImageLoaderHEIF *) loader;
121         return ld->pixbuf;
122 }
123
124 static gchar* image_loader_heif_get_format_name(gpointer loader)
125 {
126         return g_strdup("heif");
127 }
128
129 static gchar** image_loader_heif_get_format_mime_types(gpointer loader)
130 {
131         static gchar *mime[] = {"image/heic", NULL};
132         return g_strdupv(mime);
133 }
134
135 static gboolean image_loader_heif_close(gpointer loader, GError **error)
136 {
137         return TRUE;
138 }
139
140 static void image_loader_heif_abort(gpointer loader)
141 {
142         ImageLoaderHEIF *ld = (ImageLoaderHEIF *) loader;
143         ld->abort = TRUE;
144 }
145
146 static void image_loader_heif_free(gpointer loader)
147 {
148         ImageLoaderHEIF *ld = (ImageLoaderHEIF *) loader;
149         if (ld->pixbuf) g_object_unref(ld->pixbuf);
150         g_free(ld);
151 }
152
153 void image_loader_backend_set_heif(ImageLoaderBackend *funcs)
154 {
155         funcs->loader_new = image_loader_heif_new;
156         funcs->set_size = image_loader_heif_set_size;
157         funcs->load = image_loader_heif_load;
158         funcs->write = NULL;
159         funcs->get_pixbuf = image_loader_heif_get_pixbuf;
160         funcs->close = image_loader_heif_close;
161         funcs->abort = image_loader_heif_abort;
162         funcs->free = image_loader_heif_free;
163         funcs->get_format_name = image_loader_heif_get_format_name;
164         funcs->get_format_mime_types = image_loader_heif_get_format_mime_types;
165 }
166
167 #endif
168 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */