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