encode-jpx.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. // Copyright (C) 2004-2025 Artifex Software, Inc.
  2. //
  3. // This file is part of MuPDF.
  4. //
  5. // MuPDF is free software: you can redistribute it and/or modify it under the
  6. // terms of the GNU Affero General Public License as published by the Free
  7. // Software Foundation, either version 3 of the License, or (at your option)
  8. // any later version.
  9. //
  10. // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
  11. // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  12. // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. // details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
  17. //
  18. // Alternative licensing terms are available from the licensor.
  19. // For commercial licensing, see <https://www.artifex.com/> or contact
  20. // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
  21. // CA 94129, USA, for further information.
  22. #include "mupdf/fitz.h"
  23. #include <openjpeg.h>
  24. #if FZ_ENABLE_JPX
  25. static opj_image_t *
  26. image_from_pixmap(fz_context *ctx, fz_pixmap *pix)
  27. {
  28. opj_image_cmptparm_t cmptparm[FZ_MAX_COLORS] = { 0 };
  29. OPJ_INT32 *data[FZ_MAX_COLORS];
  30. int i;
  31. opj_image_t *image;
  32. OPJ_COLOR_SPACE cs;
  33. if (pix->alpha || pix->s)
  34. fz_throw(ctx, FZ_ERROR_ARGUMENT, "No spots/alpha for JPX encode");
  35. if (fz_colorspace_is_cmyk(ctx, pix->colorspace))
  36. cs = OPJ_CLRSPC_CMYK;
  37. else if (fz_colorspace_is_rgb(ctx, pix->colorspace))
  38. cs = OPJ_CLRSPC_SRGB;
  39. else if (fz_colorspace_is_gray(ctx, pix->colorspace))
  40. cs = OPJ_CLRSPC_GRAY;
  41. else
  42. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Invalid colorspace for JPX encode");
  43. /* Create image */
  44. for (i = 0; i < pix->n; ++i)
  45. {
  46. cmptparm[i].prec = 8;
  47. cmptparm[i].sgnd = 0;
  48. cmptparm[i].dx = 1;
  49. cmptparm[i].dy = 1;
  50. cmptparm[i].w = (OPJ_UINT32)pix->w;
  51. cmptparm[i].h = (OPJ_UINT32)pix->h;
  52. }
  53. image = opj_image_create(pix->n, &cmptparm[0], cs);
  54. if (image == NULL)
  55. fz_throw(ctx, FZ_ERROR_LIBRARY, "OPJ image creation failed");
  56. image->x0 = 0;
  57. image->y0 = 0;
  58. image->x1 = pix->w;
  59. image->y1 = pix->h;
  60. for (i = 0; i < pix->n; ++i)
  61. data[i] = image->comps[i].data;
  62. {
  63. int w = pix->w;
  64. int stride = pix->stride;
  65. int n = pix->n;
  66. int x, y, k;
  67. unsigned char *s = pix->samples;
  68. for (y = pix->h; y > 0; y--)
  69. {
  70. unsigned char *s2 = s;
  71. s += stride;
  72. for (k = 0; k < n; k++)
  73. {
  74. unsigned char *s3 = s2++;
  75. OPJ_INT32 *d = data[k];
  76. data[k] += w;
  77. for (x = w; x > 0; x--)
  78. {
  79. *d++ = (*s3);
  80. s3 += n;
  81. }
  82. }
  83. }
  84. }
  85. return image;
  86. }
  87. typedef struct
  88. {
  89. fz_context *ctx; /* Safe */
  90. fz_output *out;
  91. } my_stream;
  92. static void
  93. close_stm(void *user_data)
  94. {
  95. my_stream *stm = (my_stream *)user_data;
  96. /* Nothing to see here. */
  97. fz_close_output(stm->ctx, stm->out);
  98. }
  99. static OPJ_SIZE_T
  100. write_stm(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
  101. {
  102. my_stream *stm = (my_stream *)p_user_data;
  103. fz_try(stm->ctx)
  104. fz_write_data(stm->ctx, stm->out, p_buffer, p_nb_bytes);
  105. fz_catch(stm->ctx)
  106. return (OPJ_SIZE_T)-1;
  107. return p_nb_bytes;
  108. }
  109. static OPJ_OFF_T skip_stm(OPJ_OFF_T p_nb_bytes, void *p_user_data)
  110. {
  111. my_stream *stm = (my_stream *)p_user_data;
  112. (void)stm;
  113. return -1;
  114. }
  115. static OPJ_BOOL seek_stm(OPJ_OFF_T p_nb_bytes, void *p_user_data)
  116. {
  117. my_stream *stm = (my_stream *)p_user_data;
  118. (void)stm;
  119. return 0;
  120. }
  121. static void
  122. info_callback(const char *msg, void *client_data)
  123. {
  124. fz_context *ctx = (fz_context *)client_data;
  125. fz_warn(ctx, "INFO: %s", msg);
  126. }
  127. static void
  128. warning_callback(const char *msg, void *client_data)
  129. {
  130. fz_context *ctx = (fz_context *)client_data;
  131. fz_warn(ctx, "WARNING: %s", msg);
  132. }
  133. static void
  134. error_callback(const char *msg, void *client_data)
  135. {
  136. fz_context *ctx = (fz_context *)client_data;
  137. fz_warn(ctx, "ERROR: %s", msg);
  138. }
  139. void
  140. fz_write_pixmap_as_jpx(fz_context *ctx, fz_output *out, fz_pixmap *pix, int q)
  141. {
  142. opj_cparameters_t parameters; /* compression parameters */
  143. opj_stream_t *l_stream = 00;
  144. opj_codec_t* l_codec = 00;
  145. opj_image_t *image = NULL;
  146. OPJ_BOOL bSuccess;
  147. my_stream stm;
  148. fz_var(image);
  149. opj_lock(ctx);
  150. fz_try(ctx)
  151. {
  152. image = image_from_pixmap(ctx, pix);
  153. stm.ctx = ctx;
  154. stm.out = out;
  155. /* set encoding parameters to default values */
  156. opj_set_default_encoder_parameters(&parameters);
  157. /* Decide if MCT should be used */
  158. /* mct = 1 -> rgb data should be converted to ycc */
  159. parameters.tcp_mct = (pix->n >= 3) ? 1 : 0;
  160. parameters.irreversible = 1;
  161. /* JPEG-2000 codestream */
  162. l_codec = opj_create_compress(OPJ_CODEC_J2K);
  163. /* Use OPJ_CODEC_JP2 for JPEG 2000 compressed image data, but that requires seeking. */
  164. /* catch events using our callbacks and give a local context */
  165. opj_set_info_handler(l_codec, info_callback, ctx);
  166. opj_set_warning_handler(l_codec, warning_callback, ctx);
  167. opj_set_error_handler(l_codec, error_callback, ctx);
  168. /* We encode using tiles. */
  169. parameters.cp_tx0 = 0;
  170. parameters.cp_ty0 = 0;
  171. parameters.tile_size_on = OPJ_TRUE;
  172. parameters.cp_tdx = 256;
  173. parameters.cp_tdy = 256;
  174. /* Shrink the tile so it's not more than twice the width/height of the image. */
  175. while (parameters.cp_tdx>>1 >= pix->w)
  176. parameters.cp_tdx >>= 1;
  177. while (parameters.cp_tdy>>1 >= pix->h)
  178. parameters.cp_tdy >>= 1;
  179. /* The tile size must not be smaller than that given by numresolution. */
  180. if (parameters.cp_tdx < 1<<(parameters.numresolution-1))
  181. parameters.cp_tdx = 1<<(parameters.numresolution-1);
  182. if (parameters.cp_tdy < 1<<(parameters.numresolution-1))
  183. parameters.cp_tdy = 1<<(parameters.numresolution-1);
  184. /* FIXME: Calculate layers here? */
  185. /* My understanding of the suggestion that I've been given, is that we should pick
  186. * layers to be the largest integer, such that (1<<layers) * tile_size >= w */
  187. {
  188. int layers = 0;
  189. while (pix->w>>(layers+1) >= parameters.cp_tdx &&
  190. pix->h>>(layers+1) >= parameters.cp_tdy)
  191. layers++;
  192. /* But putting layers into parameters.tcp_numlayers causes a crash... */
  193. }
  194. if (q == 100)
  195. {
  196. /* Lossless compression requested! */
  197. }
  198. else if (pix->w < 2*parameters.cp_tdx && pix->h < 2*parameters.cp_tdy)
  199. {
  200. /* We only compress lossily if the image is larger than the tilesize, otherwise work losslessly. */
  201. }
  202. else
  203. {
  204. /* 20:1 compression is reasonable */
  205. parameters.tcp_numlayers = 1;
  206. parameters.tcp_rates[0] = (100-q);
  207. parameters.cp_disto_alloc = 1;
  208. }
  209. if (! opj_setup_encoder(l_codec, &parameters, image))
  210. {
  211. opj_destroy_codec(l_codec);
  212. opj_image_destroy(image);
  213. fz_throw(ctx, FZ_ERROR_LIBRARY, "OpenJPEG encoder setup failed");
  214. }
  215. /* open a byte stream for writing and allocate memory for all tiles */
  216. l_stream = opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE, OPJ_FALSE);
  217. if (!l_stream)
  218. {
  219. opj_destroy_codec(l_codec);
  220. opj_image_destroy(image);
  221. fz_throw(ctx, FZ_ERROR_LIBRARY, "OpenJPEG encoder setup failed (stream creation)");
  222. }
  223. opj_stream_set_user_data(l_stream, &stm, close_stm);
  224. opj_stream_set_user_data_length(l_stream, 0);
  225. //opj_stream_set_read_function(l_stream, opj_read_from_file);
  226. opj_stream_set_write_function(l_stream, write_stm);
  227. opj_stream_set_skip_function(l_stream, skip_stm);
  228. opj_stream_set_seek_function(l_stream, seek_stm);
  229. /* encode the image */
  230. bSuccess = opj_start_compress(l_codec, image, l_stream);
  231. if (!bSuccess)
  232. {
  233. opj_destroy_codec(l_codec);
  234. opj_image_destroy(image);
  235. fz_throw(ctx, FZ_ERROR_LIBRARY, "OpenJPEG encode failed");
  236. }
  237. bSuccess = bSuccess && opj_encode(l_codec, l_stream);
  238. bSuccess = bSuccess && opj_end_compress(l_codec, l_stream);
  239. opj_stream_destroy(l_stream);
  240. /* free remaining compression structures */
  241. opj_destroy_codec(l_codec);
  242. /* free image data */
  243. opj_image_destroy(image);
  244. if (!bSuccess)
  245. fz_throw(ctx, FZ_ERROR_LIBRARY, "Encoding failed");
  246. }
  247. fz_always(ctx)
  248. opj_unlock(ctx);
  249. fz_catch(ctx)
  250. fz_rethrow(ctx);
  251. }
  252. void
  253. fz_save_pixmap_as_jpx(fz_context *ctx, fz_pixmap *pixmap, const char *filename, int q)
  254. {
  255. fz_output *out = fz_new_output_with_path(ctx, filename, 0);
  256. fz_try(ctx)
  257. {
  258. fz_write_pixmap_as_jpx(ctx, out, pixmap, q);
  259. fz_close_output(ctx, out);
  260. }
  261. fz_always(ctx)
  262. {
  263. fz_drop_output(ctx, out);
  264. }
  265. fz_catch(ctx)
  266. {
  267. fz_rethrow(ctx);
  268. }
  269. }
  270. static fz_buffer *
  271. jpx_from_pixmap(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params, int quality, int drop)
  272. {
  273. fz_buffer *buf = NULL;
  274. fz_output *out = NULL;
  275. fz_var(buf);
  276. fz_var(out);
  277. fz_try(ctx)
  278. {
  279. buf = fz_new_buffer(ctx, 1024);
  280. out = fz_new_output_with_buffer(ctx, buf);
  281. fz_write_pixmap_as_jpx(ctx, out, pix, quality);
  282. fz_close_output(ctx, out);
  283. }
  284. fz_always(ctx)
  285. {
  286. if (drop)
  287. fz_drop_pixmap(ctx, pix);
  288. fz_drop_output(ctx, out);
  289. }
  290. fz_catch(ctx)
  291. {
  292. fz_drop_buffer(ctx, buf);
  293. fz_rethrow(ctx);
  294. }
  295. return buf;
  296. }
  297. fz_buffer *
  298. fz_new_buffer_from_image_as_jpx(fz_context *ctx, fz_image *image, fz_color_params color_params, int quality)
  299. {
  300. fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL);
  301. return jpx_from_pixmap(ctx, pix, color_params, quality, 1);
  302. }
  303. fz_buffer *
  304. fz_new_buffer_from_pixmap_as_jpx(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params, int quality)
  305. {
  306. return jpx_from_pixmap(ctx, pix, color_params, quality, 0);
  307. }
  308. #else
  309. void
  310. fz_write_pixmap_as_jpx(fz_context *ctx, fz_output *out, fz_pixmap *pix, int q)
  311. {
  312. fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "JPX support disabled");
  313. }
  314. #endif