1 /*****************************************************************************
2 * This file is part of gfxprim library. *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
19 * Copyright (C) 2009-2014 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
25 JPEG 2000 image support using openjpeg library.
33 #include "../../config.h"
34 #include "core/GP_Debug.h"
35 #include "core/GP_GetPutPixel.h"
39 #define JP2_SIG "\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a"
40 #define JP2_SIG_LEN 12
42 int GP_MatchJP2(const void *buf
)
44 return !memcmp(buf
, JP2_SIG
, JP2_SIG_LEN
);
49 #include <openjpeg-2.0/openjpeg.h>
51 static void jp2_err_callback(const char *msg
, void *priv
)
54 GP_WARN("openjpeg: %s", msg
);
57 static void jp2_warn_callback(const char *msg
, void *priv
)
60 GP_WARN("openjpeg: %s", msg
);
63 static void jp2_info_callback(const char *msg
, void *priv
)
66 GP_DEBUG(1, "openjpeg: %s", msg
);
69 static const char *color_space_name(OPJ_COLOR_SPACE color_space
)
71 switch (color_space
) {
72 case OPJ_CLRSPC_UNKNOWN
:
74 case OPJ_CLRSPC_UNSPECIFIED
:
87 static OPJ_SIZE_T
jp2_io_read(void *buf
, OPJ_SIZE_T size
, void *io
)
90 ret
= GP_IORead(io
, buf
, size
);
98 static void fill_metadata(opj_image_t
*img
, GP_DataStorage
*storage
)
102 GP_DataStorageAddInt(storage
, NULL
, "Width", img
->x1
- img
->x0
);
103 GP_DataStorageAddInt(storage
, NULL
, "Height", img
->y1
- img
->y0
);
104 GP_DataStorageAddString(storage
, NULL
, "Color Space",
105 color_space_name(img
->color_space
));
106 GP_DataStorageAddInt(storage
, NULL
, "Samples per Pixel", img
->numcomps
);
108 for (i
= 0; i
< img
->numcomps
; i
++) {
110 GP_DataNode
*comp_node
;
112 snprintf(buf
, sizeof(buf
), "Channel %u", i
);
114 comp_node
= GP_DataStorageAddDict(storage
, NULL
, buf
);
116 GP_DataStorageAddInt(storage
, comp_node
, "Width", img
->comps
[i
].w
);
117 GP_DataStorageAddInt(storage
, comp_node
, "Height", img
->comps
[i
].h
);
118 GP_DataStorageAddInt(storage
, comp_node
, "Bits per Sample", img
->comps
[i
].prec
);
122 int GP_ReadJP2Ex(GP_IO
*io
, GP_Pixmap
**rimg
, GP_DataStorage
*storage
,
123 GP_ProgressCallback
*callback
)
125 opj_dparameters_t params
;
127 opj_stream_t
*stream
;
130 GP_PixelType pixel_type
;
131 GP_Pixmap
*res
= NULL
;
132 unsigned int i
, x
, y
;
133 int err
= 0, ret
= 1;
135 opj_set_default_decoder_parameters(¶ms
);
137 codec
= opj_create_decompress(OPJ_CODEC_JP2
);
140 GP_DEBUG(1, "opj_create_decompress failed");
145 opj_set_error_handler(codec
, jp2_err_callback
, NULL
);
146 opj_set_warning_handler(codec
, jp2_warn_callback
, NULL
);
147 opj_set_info_handler(codec
, jp2_info_callback
, callback
);
149 if (!opj_setup_decoder(codec
, ¶ms
)) {
150 GP_DEBUG(1, "opj_setup_decoder failed");
155 stream
= opj_stream_default_create(OPJ_TRUE
);
158 GP_DEBUG(1, "opj_stream_create_default_file_stream faled");
163 //TODO: Do we need seek and skip?
164 opj_stream_set_read_function(stream
, jp2_io_read
);
165 opj_stream_set_user_data(stream
, io
);
167 if (!opj_read_header(stream
, codec
, &img
)) {
168 GP_DEBUG(1, "opj_read_header failed");
174 fill_metadata(img
, storage
);
176 GP_DEBUG(1, "Have image %ux%u-%ux%u colorspace=%s numcomps=%u",
177 img
->x0
, img
->y0
, img
->x1
, img
->y1
,
178 color_space_name(img
->color_space
), img
->numcomps
);
184 * Try to match the image information into pixel type.
186 * Unfortunately the images I had have color_space set
187 * to unspecified yet they were RGB888.
189 for (i
= 0; i
< img
->numcomps
; i
++) {
190 opj_image_comp_t
*comp
= &img
->comps
[i
];
192 GP_DEBUG(2, "Component %u %ux%u bpp=%u",
193 i
, comp
->w
, comp
->h
, comp
->prec
);
195 if (comp
->w
!= img
->comps
[0].w
||
196 comp
->h
!= img
->comps
[0].h
) {
197 GP_DEBUG(1, "Component %u has different size", 1);
202 if (comp
->prec
!= 8) {
203 GP_DEBUG(1, "Component %u has different bpp", 1);
209 switch (img
->color_space
) {
210 case OPJ_CLRSPC_UNSPECIFIED
:
211 if (img
->numcomps
!= 3) {
212 GP_DEBUG(1, "Unexpected number of components");
216 pixel_type
= GP_PIXEL_RGB888
;
219 GP_DEBUG(1, "Unsupported colorspace");
224 GP_ProgressCallbackReport(callback
, 0, 100, 100);
226 if (!opj_decode(codec
, stream
, img
)) {
227 GP_DEBUG(1, "opj_decode failed");
232 res
= GP_PixmapAlloc(img
->comps
[0].w
, img
->comps
[0].h
, pixel_type
);
235 GP_DEBUG(1, "Malloc failed :(");
240 for (y
= 0; y
< res
->h
; y
++) {
241 for (x
= 0; x
< res
->w
; x
++) {
244 GP_Pixel p
= img
->comps
[0].data
[i
] << 16|
245 img
->comps
[1].data
[i
] << 8 |
246 img
->comps
[2].data
[i
];
248 GP_PutPixel_Raw_24BPP(res
, x
, y
, p
);
252 GP_ProgressCallbackDone(callback
);
256 opj_image_destroy(img
);
258 opj_stream_destroy(stream
);
260 opj_destroy_codec(codec
);
269 int GP_ReadJP2Ex(GP_IO
GP_UNUSED(*io
), GP_Pixmap
GP_UNUSED(**img
),
270 GP_DataStorage
GP_UNUSED(*storage
),
271 GP_ProgressCallback
GP_UNUSED(*callback
))
277 #endif /* HAVE_OPENJPEG */
279 GP_Pixmap
*GP_LoadJP2(const char *src_path
, GP_ProgressCallback
*callback
)
281 return GP_LoaderLoadImage(&GP_JP2
, src_path
, callback
);
284 GP_Pixmap
*GP_ReadJP2(GP_IO
*io
, GP_ProgressCallback
*callback
)
286 return GP_LoaderReadImage(&GP_JP2
, io
, callback
);
289 int GP_LoadJP2Ex(const char *src_path
, GP_Pixmap
**img
,
290 GP_DataStorage
*storage
, GP_ProgressCallback
*callback
)
292 return GP_LoaderLoadImageEx(&GP_JP2
, src_path
, img
, storage
, callback
);
295 struct GP_Loader GP_JP2
= {
297 .Read
= GP_ReadJP2Ex
,
298 #endif /* HAVE_OPENJPEG */
299 .Match
= GP_MatchJP2
,
301 .fmt_name
= "JPEG 2000",
302 .extensions
= {"jp2", "jpx", NULL
},