1 /********************************************************************************
2 *                                                                               *
3 *                    J P E G - 2 0 0 0   I n p u t / O u t p u t                *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2009,2021 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (at your option) any later version.                                           *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "FXArray.h"
26 #include "FXHash.h"
27 #include "FXElement.h"
28 #include "FXStream.h"
29 #ifdef HAVE_JP2_H
30 #include "openjpeg.h"
31 #endif
32 
33 
34 /*
35   Notes:
36   - Support for JPEG 2000 image file compression.
37 */
38 
39 // Contents of signature box
40 #define SIGNATURE       0x0d0a870a              // Value for signature box
41 
42 // File level boxes
43 #define BOX_JP          0x6a502020              // File signature box
44 #define BOX_FTYP        0x66747970              // File type box
45 #define BOX_JP2H        0x6a703268              // JP2 Header box
46 #define BOX_MHDR        0x6D686472              // Compound image header box
47 #define BOX_DBTL        0x6474626c		// Data reference box
48 #define BOX_URL         0x75726c20		// URL box
49 #define BOX_PCOL        0x70636F6C              // Page collection box
50 #define BOX_LBL         0x6C626C20              // Label box
51 #define BOX_PAGT        0x70616774              // Page table box
52 #define BOX_SDAT        0x73646174              // Shared data box
53 #define BOX_SREF        0x73726566              // Shared reference box
54 #define BOX_PAGE        0x70616765              // Page box
55 #define BOX_PHDR        0x70686472              // Page header box
56 #define BOX_RES         0x72657320              // Resolution box
57 #define BOX_BCLR        0x62636C72		// Base color box
58 #define BOX_LOBJ        0x6C6F626A		// Layout object box
59 #define BOX_LHDR        0x6C686472		// Layout object header box
60 #define BOX_OBJC        0x6F626A63		// Object box
61 #define BOX_OHDR        0x6F686472		// Object header box
62 #define BOX_SCAL        0x7363616C		// Object scale box
63 #define BOX_FTBL        0x6672626C              // Fragment table box
64 #define BOX_FLST        0x666C7374              // Fragment list box
65 #define BOX_MDAT        0x6D646174              // Media data box
66 #define BOX_JP2C        0x6a703263		// Contiguous codestream box
67 #define BOX_RESC        0x72657363              // Capture resolution box
68 #define BOX_RESD        0x72657364              // Default display resolution box
69 #define BOX_IHDR        0x69686472		// Image Header box
70 #define BOX_PCLR        0x70636C72		// Palette box
71 #define BOX_COLR        0x636f6c72		// Colour specification box
72 #define BOX_CMAP        0x636D6170              // Component mapping box
73 #define BOX_FTBL        0x6672626C              // Fragment table box
74 #define BOX_FREE        0x66726565		// Free box
75 #define BOX_JP2         0x6a703220              // JP2 box
76 #define BOX_BPCC        0x62706363		// Bits per component box
77 #define BOX_XML         0x786d6c20              // XML box
78 #define BOX_RREQ        0x72726571              // RREQ box
79 
80 using namespace FX;
81 
82 /*******************************************************************************/
83 
84 namespace FX {
85 
86 
87 #ifndef FXLOADJP2
88 extern FXAPI FXbool fxcheckJP2(FXStream& store);
89 extern FXAPI FXbool fxloadJP2(FXStream& store,FXColor*& data,FXint& width,FXint& height,FXint& quality);
90 extern FXAPI FXbool fxsaveJP2(FXStream& store,const FXColor* data,FXint width,FXint height,FXint quality);
91 #endif
92 
93 
94 #undef HAVE_JP2_H
95 #ifdef HAVE_JP2_H
96 
97 /*******************************************************************************/
98 
99 
100 // Report error
j2k_error_callback(const char * msg,void * client_data)101 void j2k_error_callback(const char *msg, void *client_data){
102   FXTRACE((100,"fxjp2io: error: %s.\n",msg));
103   }
104 
105 
106 // Report warning
j2k_warning_callback(const char * msg,void * client_data)107 void j2k_warning_callback(const char *msg, void *client_data){
108   FXTRACE((100,"fxjp2io: warning: %s.\n",msg));
109   }
110 
111 
112 // Report info
j2k_info_callback(const char * msg,void * client_data)113 void j2k_info_callback(const char *msg, void *client_data){
114   FXTRACE((100,"fxjp2io: info: %s.\n",msg));
115   }
116 
117 
118 // Check if stream contains a JPG
fxcheckJP2(FXStream & store)119 FXbool fxcheckJP2(FXStream& store){
120   FXuchar ss[12];
121   store.load(ss,12);
122   store.position(-12,FXFromCurrent);
123   return ss[0]==0 && ss[1]==0 && ss[2]==0 && ss[3]==12 && ss[4]=='j' && ss[5]=='P' && ss[6]==' ' && ss[7]==' ' && ss[8]==0x0D && ss[9]==0x0A && ss[10]==0x87 && ss[11]==0x0A;
124   }
125 
126 
127 // Load a JPEG image
fxloadJP2(FXStream & store,FXColor * & data,FXint & width,FXint & height,FXint &)128 FXbool fxloadJP2(FXStream& store,FXColor*& data,FXint& width,FXint& height,FXint&){
129   FXint x,y,cw,rsh,gsh,bsh,ash,rof,gof,bof,aof;
130   FXuchar r,g,b,a;
131   FXbool swap=store.swapBytes();
132   FXlong pos=store.position();
133   FXbool result=false;
134   FXuint box[4];
135   FXlong boxsize;
136   FXuint size;
137   FXuchar *ptr;
138 
139   // Null out
140   data=NULL;
141   width=0;
142   height=0;
143 
144   // Switch big-endian to grab header
145   store.setBigEndian(true);
146 
147   // Grab signature
148   store.load(box,3);
149 
150   // Check signature, bail quickly if no match
151   if(box[0]==12 && box[1]==BOX_JP && box[2]==SIGNATURE){
152 
153     // Figure size
154     store.position(0,FXFromEnd);
155     size=store.position()-pos;
156     store.position(pos);
157 
158     FXTRACE((100,"fxloadJP2: file size=%d\n",size));
159 
160     // Allocate chunk for file data
161     if(allocElms(ptr,size)){
162 
163       // Load entire file
164       store.load(ptr,size);
165 
166       // Create decompressor
167       opj_dinfo_t *decompressor=opj_create_decompress(CODEC_JP2);
168       if(decompressor){
169         opj_dparameters_t     parameters;
170         opj_event_mgr_t       event_mgr;
171         opj_cio_t            *cio=NULL;
172         opj_image_t          *image=NULL;
173 
174         // Set up callbacks
175         event_mgr.error_handler=j2k_error_callback;
176         event_mgr.warning_handler=j2k_warning_callback;
177         event_mgr.info_handler=j2k_info_callback;
178 
179         // Set event manager
180         opj_set_event_mgr((opj_common_ptr)decompressor,&event_mgr,NULL);
181 
182         // Initialize decompression parameters
183         opj_set_default_decoder_parameters(&parameters);
184 
185         // Setup the decoder decoding parameters using user parameters
186         opj_setup_decoder(decompressor,&parameters);
187 
188         // Open a byte stream */
189         cio=opj_cio_open((opj_common_ptr)decompressor,ptr,size);
190         if(cio){
191 
192           // Decode the stream and fill the image structure
193           image=opj_decode(decompressor,cio);
194           if(image){
195 
196             // Image size
197             width=image->x1-image->x0;
198             height=image->y1-image->y0;
199 
200             FXTRACE((100,"fxloadJP2: width=%d height=%d numcomps=%d color_space=%d\n",width,height,image->numcomps,image->color_space));
201 
202             // Only support GREY, RGB, and RGBA
203             if(((image->numcomps==1) && (image->color_space==CLRSPC_GRAY)) || ((image->numcomps==3 || image->numcomps==4) && (image->color_space==CLRSPC_SRGB))){
204 
205               // Allocate image data
206               if(allocElms(data,width*height)){
207                 rof=gof=bof=aof=rsh=gsh=bsh=ash=0;
208                 switch(image->numcomps){
209                   case 1:
210                     if(image->comps[0].sgnd) gof=1<<(image->comps[0].prec-1);
211                     gsh=image->comps[0].prec-8;
212                     cw=image->comps[0].w;
213                     for(y=0; y<height; ++y){
214                       for(x=0; x<width; ++x){
215                         g=(image->comps[0].data[y*cw+x]+gof)>>gsh;
216                         data[y*width+x]=FXRGB(g,g,g);
217                         }
218                       }
219                     break;
220                   case 3:
221                     if(image->comps[0].sgnd) rof=1<<(image->comps[0].prec-1);
222                     if(image->comps[1].sgnd) gof=1<<(image->comps[1].prec-1);
223                     if(image->comps[2].sgnd) bof=1<<(image->comps[2].prec-1);
224                     rsh=image->comps[0].prec-8;
225                     gsh=image->comps[1].prec-8;
226                     bsh=image->comps[2].prec-8;
227                     cw=image->comps[0].w;
228                     for(y=0; y<height; ++y){
229                       for(x=0; x<width; ++x){
230                         r=(image->comps[0].data[y*cw+x]+rof)>>rsh;
231                         g=(image->comps[1].data[y*cw+x]+gof)>>gsh;
232                         b=(image->comps[2].data[y*cw+x]+bof)>>bsh;
233                         data[y*width+x]=FXRGB(r,g,b);
234                         }
235                       }
236                     break;
237                   default:
238                     if(image->comps[0].sgnd) rof=1<<(image->comps[0].prec-1);
239                     if(image->comps[1].sgnd) gof=1<<(image->comps[1].prec-1);
240                     if(image->comps[2].sgnd) bof=1<<(image->comps[2].prec-1);
241                     if(image->comps[3].sgnd) aof=1<<(image->comps[3].prec-1);
242                     rsh=image->comps[0].prec-8;
243                     gsh=image->comps[1].prec-8;
244                     bsh=image->comps[2].prec-8;
245                     ash=image->comps[3].prec-8;
246                     cw=image->comps[0].w;
247                     for(y=0; y<height; ++y){
248                       for(x=0; x<width; ++x){
249                         r=(image->comps[0].data[y*cw+x]+rof)>>rsh;
250                         g=(image->comps[1].data[y*cw+x]+gof)>>gsh;
251                         b=(image->comps[2].data[y*cw+x]+bof)>>bsh;
252                         a=(image->comps[3].data[y*cw+x]+aof)>>ash;
253                         data[y*width+x]=FXRGBA(r,g,b,a);
254                         }
255                       }
256                     break;
257                   }
258                 result=true;
259                 }
260               }
261             opj_image_destroy(image);
262             }
263           opj_cio_close(cio);
264           }
265         opj_destroy_decompress(decompressor);
266         }
267       freeElms(ptr);
268       }
269     }
270   store.swapBytes(swap);
271   return result;
272   }
273 
274 
275 /*******************************************************************************/
276 
277 
278 // Save a JPEG image
fxsaveJP2(FXStream & store,const FXColor * data,FXint width,FXint height,FXint quality)279 FXbool fxsaveJP2(FXStream& store,const FXColor* data,FXint width,FXint height,FXint quality){
280   FXint x,y,c,p;
281   FXbool result=false;
282 
283   // Must make sense
284   if(data && 0<width && 0<height){
285     opj_cinfo_t* compressor=opj_create_compress(CODEC_JP2);
286     if(compressor){
287       opj_event_mgr_t       event_mgr;
288       opj_cparameters_t     parameters;
289       opj_cio_t            *cio=NULL;
290       opj_image_t          *image=NULL;
291       opj_image_cmptparm_t  components[3];
292       FXColor               color;
293 
294       // Set up callbacks
295       event_mgr.error_handler=j2k_error_callback;
296       event_mgr.warning_handler=j2k_warning_callback;
297       event_mgr.info_handler=j2k_info_callback;
298 
299       // Set event manager
300       opj_set_event_mgr((opj_common_ptr)compressor,&event_mgr,NULL);
301 
302       // Set encoding parameters to default values
303       opj_set_default_encoder_parameters(&parameters);
304 
305 //      parameters.tcp_rates[0]=((100-quality)/90.0f*99.0f)+1;
306       parameters.tcp_rates[0]=16;
307       parameters.tcp_numlayers=1;
308       parameters.cp_disto_alloc=1;
309 
310       // Set up parameters
311       for(c=0; c<3; c++){
312         components[c].dx=parameters.subsampling_dx;
313         components[c].dy=parameters.subsampling_dy;
314         components[c].w=width;
315         components[c].h=height;
316         components[c].x0=0;
317         components[c].y0=0;
318         components[c].prec=8;
319         components[c].bpp=8;
320         components[c].sgnd=0;
321         }
322 
323       // Create image
324       image=opj_image_create(3,components,CLRSPC_SRGB);
325       if(image){
326 
327 	/* set image offset and reference grid */
328 	image->x0=parameters.image_offset_x0;
329 	image->y0=parameters.image_offset_y0;
330 	image->x1=parameters.image_offset_x0+(width-1)*parameters.subsampling_dx+1;
331 	image->y1=parameters.image_offset_y0+(height-1)*parameters.subsampling_dy+1;
332 
333         // Setup encoder for image
334         opj_setup_encoder(compressor,&parameters,image);
335 
336         // Fill image buffers
337 	for(y=p=0; y<height; ++y){
338           for(x=0; x<width; ++x){
339             color=data[y*width+x];
340             image->comps[0].data[p]=FXREDVAL(color);
341             image->comps[1].data[p]=FXGREENVAL(color);
342             image->comps[2].data[p]=FXBLUEVAL(color);
343             p++;
344             }
345           }
346 
347         // Open code stream
348         cio=opj_cio_open((opj_common_ptr)compressor,NULL,0);
349         if(cio){
350 
351           // Encode the image
352           result=opj_encode(compressor,cio,image,NULL);
353 
354           // Encoded properly
355           if(result){
356 
357             // Write to store
358             store.save(cio->buffer,cio_tell(cio));
359 
360             // Check if write was successful
361             if(store.status()!=FXStreamOK) result=false;
362             }
363 
364           // Close stream
365           opj_cio_close(cio);
366           }
367 
368         // Destroy image
369         opj_image_destroy(image);
370         }
371       opj_destroy_compress(compressor);
372       }
373     }
374   return result;
375   }
376 
377 /*******************************************************************************/
378 
379 #else
380 
381 // Check if stream contains a JPG
fxcheckJP2(FXStream &)382 FXbool fxcheckJP2(FXStream&){
383   return false;
384   }
385 
386 
387 // Stub routine
fxloadJP2(FXStream &,FXColor * & data,FXint & width,FXint & height,FXint & quality)388 FXbool fxloadJP2(FXStream&,FXColor*& data,FXint& width,FXint& height,FXint& quality){
389   static const FXColor color[2]={FXRGB(0,0,0),FXRGB(255,255,255)};
390   static const FXuchar image_bits[]={
391    0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x80, 0xfd, 0xff, 0xff, 0xbf,
392    0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0xa0,
393    0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0xa0,
394    0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0xa0,
395    0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0xa0,
396    0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0xa0, 0xc5, 0xe7, 0xc3, 0xa1,
397    0x45, 0x44, 0x24, 0xa2, 0x05, 0x44, 0x04, 0xa2, 0x05, 0x44, 0x04, 0xa2,
398    0x05, 0x44, 0x04, 0xa1, 0x05, 0xc4, 0xc3, 0xa0, 0x05, 0x44, 0x20, 0xa0,
399    0x45, 0x44, 0x20, 0xa2, 0x85, 0xe3, 0xe0, 0xa3, 0x05, 0x00, 0x00, 0xa0,
400    0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0xa0, 0xfd, 0xff, 0xff, 0xbf,
401    0x01, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff};
402   allocElms(data,32*32);
403   for(FXint p=0; p<32*32; p++){
404     data[p]=color[(image_bits[p>>3]>>(p&7))&1];
405     }
406   width=32;
407   height=32;
408   quality=75;
409   return true;
410   }
411 
412 
413 // Stub routine
fxsaveJP2(FXStream &,const FXColor *,FXint,FXint,FXint)414 FXbool fxsaveJP2(FXStream&,const FXColor*,FXint,FXint,FXint){
415   return false;
416   }
417 
418 #endif
419 
420 }
421