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