Fix deprecation warning for poppler >= 0.82
[geeqie.git] / src / image-load-j2k.cc
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 <config.h>
22
23 #if HAVE_J2K
24
25 #include "image-load-j2k.h"
26
27 #include <cstdlib>
28 #include <cstring>
29
30 #include <gdk-pixbuf/gdk-pixbuf.h>
31 #include <glib-object.h>
32 #include <glib.h>
33 #include <openjpeg.h>
34
35 #include "debug.h"
36 #include "image-load.h"
37 #include "intl.h"
38 #include "misc.h"
39
40 namespace
41 {
42
43 struct ImageLoaderJ2K {
44         ImageLoaderBackendCbAreaUpdated area_updated_cb;
45         ImageLoaderBackendCbSize size_cb;
46         ImageLoaderBackendCbAreaPrepared area_prepared_cb;
47         gpointer data;
48         GdkPixbuf *pixbuf;
49         guint requested_width;
50         guint requested_height;
51         gboolean abort;
52 };
53
54 void free_buffer(guchar *pixels, gpointer)
55 {
56         g_free (pixels);
57 }
58
59 struct opj_buffer_info_t {
60         OPJ_BYTE* buf;
61         OPJ_BYTE* cur;
62         OPJ_SIZE_T len;
63 };
64
65 OPJ_SIZE_T opj_read_from_buffer (void* pdst, OPJ_SIZE_T len, opj_buffer_info_t* psrc)
66 {
67         OPJ_SIZE_T n = psrc->buf + psrc->len - psrc->cur;
68
69         if (n)
70                 {
71                 if (n > len)
72                         n = len;
73
74                 memcpy (pdst, psrc->cur, n);
75                 psrc->cur += n;
76                 }
77         else
78                 n = static_cast<OPJ_SIZE_T>(-1);
79
80         return n;
81 }
82
83 OPJ_SIZE_T opj_write_to_buffer (void* p_buffer, OPJ_SIZE_T p_nb_bytes,
84                      opj_buffer_info_t* p_source_buffer)
85 {
86         void* pbuf = p_source_buffer->buf;
87         void* pcur = p_source_buffer->cur;
88
89         OPJ_SIZE_T len = p_source_buffer->len;
90
91         if (0 == len)
92                 len = 1;
93
94         OPJ_SIZE_T dist = static_cast<guchar *>(pcur) - static_cast<guchar *>(pbuf);
95         OPJ_SIZE_T n = len - dist;
96         g_assert (dist <= len);
97
98         while (n < p_nb_bytes)
99                 {
100                 len *= 2;
101                 n = len - dist;
102                 }
103
104         if (len != p_source_buffer->len)
105                 {
106                 pbuf = malloc (len);
107
108                 if (nullptr == pbuf)
109                         return static_cast<OPJ_SIZE_T>(-1);
110
111                 if (p_source_buffer->buf)
112                         {
113                         memcpy (pbuf, p_source_buffer->buf, dist);
114                         free (p_source_buffer->buf);
115                         }
116
117                 p_source_buffer->buf = static_cast<OPJ_BYTE *>(pbuf);
118                 p_source_buffer->cur = static_cast<guchar *>(pbuf) + dist;
119                 p_source_buffer->len = len;
120                 }
121
122         memcpy (p_source_buffer->cur, p_buffer, p_nb_bytes);
123         p_source_buffer->cur += p_nb_bytes;
124
125         return p_nb_bytes;
126 }
127
128 OPJ_SIZE_T opj_skip_from_buffer (OPJ_SIZE_T len, opj_buffer_info_t* psrc)
129 {
130         OPJ_SIZE_T n = psrc->buf + psrc->len - psrc->cur;
131
132         if (n)
133                 {
134                 if (n > len)
135                         n = len;
136
137                 psrc->cur += len;
138                 }
139         else
140                 n = static_cast<OPJ_SIZE_T>(-1);
141
142         return n;
143 }
144
145 OPJ_BOOL opj_seek_from_buffer (OPJ_OFF_T len, opj_buffer_info_t* psrc)
146 {
147         OPJ_SIZE_T n = psrc->len;
148
149         if (n > static_cast<gulong>(len))
150                 n = len;
151
152         psrc->cur = psrc->buf + n;
153
154         return OPJ_TRUE;
155 }
156
157 opj_stream_t* OPJ_CALLCONV opj_stream_create_buffer_stream (opj_buffer_info_t* psrc, OPJ_BOOL input)
158 {
159         if (!psrc)
160                 return nullptr;
161
162         opj_stream_t* ps = opj_stream_default_create (input);
163
164         if (nullptr == ps)
165                 return nullptr;
166
167         opj_stream_set_user_data        (ps, psrc, nullptr);
168         opj_stream_set_user_data_length (ps, psrc->len);
169
170         if (input)
171                 opj_stream_set_read_function (
172                     ps, reinterpret_cast<opj_stream_read_fn>(opj_read_from_buffer));
173         else
174                 opj_stream_set_write_function(
175                     ps,reinterpret_cast<opj_stream_write_fn>(opj_write_to_buffer));
176
177         opj_stream_set_skip_function (
178             ps, reinterpret_cast<opj_stream_skip_fn>(opj_skip_from_buffer));
179
180         opj_stream_set_seek_function (
181             ps, reinterpret_cast<opj_stream_seek_fn>(opj_seek_from_buffer));
182
183         return ps;
184 }
185
186 gboolean image_loader_j2k_write(gpointer loader, const guchar *buf, gsize &chunk_size, gsize count, GError **)
187 {
188         auto ld = static_cast<ImageLoaderJ2K *>(loader);
189         opj_stream_t *stream;
190         opj_codec_t *codec;
191         opj_dparameters_t parameters;
192         opj_image_t *image;
193         gint width;
194         gint height;
195         gint num_components;
196         gint i;
197         gint j;
198         gint k;
199         guchar *pixels;
200         gint  bytes_per_pixel;
201         opj_buffer_info_t *decode_buffer;
202     guchar *buf_copy;
203
204         stream = nullptr;
205         codec = nullptr;
206         image = nullptr;
207
208         buf_copy = static_cast<guchar *>(g_malloc(count));
209         memcpy(buf_copy, buf, count);
210
211         decode_buffer = g_new0(opj_buffer_info_t, 1);
212         decode_buffer->buf = buf_copy;
213         decode_buffer->len = count;
214         decode_buffer->cur = buf_copy;
215
216         stream = opj_stream_create_buffer_stream(decode_buffer, OPJ_TRUE);
217
218         if (!stream)
219                 {
220                 log_printf(_("Could not open file for reading"));
221                 return FALSE;
222                 }
223
224         if (memcmp(buf_copy + 20, "jp2", 3) == 0)
225                 {
226                 codec = opj_create_decompress(OPJ_CODEC_JP2);
227                 }
228         else
229                 {
230                 log_printf(_("Unknown jpeg2000 decoder type"));
231                 return FALSE;
232                 }
233
234         opj_set_default_decoder_parameters(&parameters);
235         if (opj_setup_decoder (codec, &parameters) != OPJ_TRUE)
236                 {
237                 log_printf(_("Couldn't set parameters on decoder for file."));
238                 return FALSE;
239                 }
240
241         opj_codec_set_threads(codec, get_cpu_cores());
242
243         if (opj_read_header(stream, codec, &image) != OPJ_TRUE)
244                 {
245                 log_printf(_("Couldn't read JP2 header from file"));
246                 return FALSE;
247                 }
248
249         if (opj_decode(codec, stream, image) != OPJ_TRUE)
250                 {
251                 log_printf(_("Couldn't decode JP2 image in file"));
252                 return FALSE;
253                 }
254
255         if (opj_end_decompress(codec, stream) != OPJ_TRUE)
256                 {
257                 log_printf(_("Couldn't decompress JP2 image in file"));
258                 return FALSE;
259                 }
260
261         num_components = image->numcomps;
262         if (num_components != 3)
263                 {
264                 log_printf(_("JP2 image not rgb"));
265                 return FALSE;
266                 }
267
268         width = image->comps[0].w;
269         height = image->comps[0].h;
270
271         bytes_per_pixel = 3;
272
273         pixels = g_new0(guchar, width * bytes_per_pixel * height);
274         for (i = 0; i < height; i++)
275                 {
276                 for (j = 0; j < num_components; j++)
277                         {
278                         for (k = 0; k < width; k++)
279                                 {
280                                 pixels[(k * bytes_per_pixel + j) + (i * width * bytes_per_pixel)] =   image->comps[j].data[i * width + k];
281                                 }
282                         }
283                 }
284
285         ld->pixbuf = gdk_pixbuf_new_from_data(pixels, GDK_COLORSPACE_RGB, FALSE , 8, width, height, width * bytes_per_pixel, free_buffer, nullptr);
286
287         ld->area_updated_cb(loader, 0, 0, width, height, ld->data);
288
289         g_free(decode_buffer);
290         g_free(buf_copy);
291         if (image)
292                 opj_image_destroy (image);
293         if (codec)
294                 opj_destroy_codec (codec);
295         if (stream)
296                 opj_stream_destroy (stream);
297
298         chunk_size = count;
299         return TRUE;
300 }
301
302 gpointer image_loader_j2k_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
303 {
304         auto loader = g_new0(ImageLoaderJ2K, 1);
305         loader->area_updated_cb = area_updated_cb;
306         loader->size_cb = size_cb;
307         loader->area_prepared_cb = area_prepared_cb;
308         loader->data = data;
309         return loader;
310 }
311
312 void image_loader_j2k_set_size(gpointer loader, int width, int height)
313 {
314         auto ld = static_cast<ImageLoaderJ2K *>(loader);
315         ld->requested_width = width;
316         ld->requested_height = height;
317 }
318
319 GdkPixbuf* image_loader_j2k_get_pixbuf(gpointer loader)
320 {
321         auto ld = static_cast<ImageLoaderJ2K *>(loader);
322         return ld->pixbuf;
323 }
324
325 gchar* image_loader_j2k_get_format_name(gpointer)
326 {
327         return g_strdup("j2k");
328 }
329
330 gchar** image_loader_j2k_get_format_mime_types(gpointer)
331 {
332         static const gchar *mime[] = {"image/jp2", nullptr};
333         return g_strdupv(const_cast<gchar **>(mime));
334 }
335
336 gboolean image_loader_j2k_close(gpointer, GError **)
337 {
338         return TRUE;
339 }
340
341 void image_loader_j2k_abort(gpointer loader)
342 {
343         auto ld = static_cast<ImageLoaderJ2K *>(loader);
344         ld->abort = TRUE;
345 }
346
347 void image_loader_j2k_free(gpointer loader)
348 {
349         auto ld = static_cast<ImageLoaderJ2K *>(loader);
350         if (ld->pixbuf) g_object_unref(ld->pixbuf);
351         g_free(ld);
352 }
353
354 } // namespace
355
356 void image_loader_backend_set_j2k(ImageLoaderBackend *funcs)
357 {
358         funcs->loader_new = image_loader_j2k_new;
359         funcs->set_size = image_loader_j2k_set_size;
360         funcs->write = image_loader_j2k_write;
361         funcs->get_pixbuf = image_loader_j2k_get_pixbuf;
362         funcs->close = image_loader_j2k_close;
363         funcs->abort = image_loader_j2k_abort;
364         funcs->free = image_loader_j2k_free;
365         funcs->get_format_name = image_loader_j2k_get_format_name;
366         funcs->get_format_mime_types = image_loader_j2k_get_format_mime_types;
367 }
368
369 #endif
370 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */