Rename GP_Context -> GP_Pixmap
[gfxprim.git] / libs / loaders / GP_JP2.c
blob9f97d96b8a03d4f5658aaa9d51db244a531f2001
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
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. *
8 * *
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. *
13 * *
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 *
18 * *
19 * Copyright (C) 2009-2014 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
25 JPEG 2000 image support using openjpeg library.
29 #include <errno.h>
30 #include <string.h>
31 #include <stdio.h>
33 #include "../../config.h"
34 #include "core/GP_Debug.h"
35 #include "core/GP_GetPutPixel.h"
37 #include "GP_JP2.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);
47 #ifdef HAVE_OPENJPEG
49 #include <openjpeg-2.0/openjpeg.h>
51 static void jp2_err_callback(const char *msg, void *priv)
53 (void) priv;
54 GP_WARN("openjpeg: %s", msg);
57 static void jp2_warn_callback(const char *msg, void *priv)
59 (void) priv;
60 GP_WARN("openjpeg: %s", msg);
63 static void jp2_info_callback(const char *msg, void *priv)
65 (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:
73 return "Unknown";
74 case OPJ_CLRSPC_UNSPECIFIED:
75 return "Unspecified";
76 case OPJ_CLRSPC_SRGB:
77 return "sRGB";
78 case OPJ_CLRSPC_GRAY:
79 return "Grayscale";
80 case OPJ_CLRSPC_SYCC:
81 return "YUV";
82 default:
83 return "Invalid";
87 static OPJ_SIZE_T jp2_io_read(void *buf, OPJ_SIZE_T size, void *io)
89 ssize_t ret;
90 ret = GP_IORead(io, buf, size);
92 if (ret == 0)
93 return -1;
95 return ret;
98 static void fill_metadata(opj_image_t *img, GP_DataStorage *storage)
100 unsigned int i;
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++) {
109 char buf[32];
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;
126 opj_codec_t *codec;
127 opj_stream_t *stream;
128 opj_image_t *img;
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(&params);
137 codec = opj_create_decompress(OPJ_CODEC_JP2);
139 if (!codec) {
140 GP_DEBUG(1, "opj_create_decompress failed");
141 err = ENOMEM;
142 goto err0;
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, &params)) {
150 GP_DEBUG(1, "opj_setup_decoder failed");
151 err = ENOMEM;
152 goto err1;
155 stream = opj_stream_default_create(OPJ_TRUE);
157 if (!stream) {
158 GP_DEBUG(1, "opj_stream_create_default_file_stream faled");
159 err = ENOMEM;
160 goto err1;
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");
169 err = EINVAL;
170 goto err2;
173 if (storage)
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);
180 if (!rimg)
181 return 0;
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);
198 err = ENOSYS;
199 goto err3;
202 if (comp->prec != 8) {
203 GP_DEBUG(1, "Component %u has different bpp", 1);
204 err = ENOSYS;
205 goto err3;
209 switch (img->color_space) {
210 case OPJ_CLRSPC_UNSPECIFIED:
211 if (img->numcomps != 3) {
212 GP_DEBUG(1, "Unexpected number of components");
213 err = ENOSYS;
214 goto err3;
216 pixel_type = GP_PIXEL_RGB888;
217 break;
218 default:
219 GP_DEBUG(1, "Unsupported colorspace");
220 err = ENOSYS;
221 goto err3;
224 GP_ProgressCallbackReport(callback, 0, 100, 100);
226 if (!opj_decode(codec, stream, img)) {
227 GP_DEBUG(1, "opj_decode failed");
228 err = EINVAL;
229 goto err3;
232 res = GP_PixmapAlloc(img->comps[0].w, img->comps[0].h, pixel_type);
234 if (!res) {
235 GP_DEBUG(1, "Malloc failed :(");
236 err = ENOMEM;
237 goto err3;
240 for (y = 0; y < res->h; y++) {
241 for (x = 0; x < res->w; x++) {
242 i = y * 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);
253 *rimg = res;
254 ret = 0;
255 err3:
256 opj_image_destroy(img);
257 err2:
258 opj_stream_destroy(stream);
259 err1:
260 opj_destroy_codec(codec);
261 err0:
262 if (err)
263 errno = err;
264 return ret;
267 #else
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))
273 errno = ENOSYS;
274 return 1;
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 = {
296 #ifdef HAVE_OPENJPEG
297 .Read = GP_ReadJP2Ex,
298 #endif /* HAVE_OPENJPEG */
299 .Match = GP_MatchJP2,
301 .fmt_name = "JPEG 2000",
302 .extensions = {"jp2", "jpx", NULL},