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