1 /* IIP Server: Kakadu JPEG2000 handler
2
3
4 Initial development supported by Moravian Library in Brno (Moravska zemska
5 knihovna v Brne, http://www.mzk.cz/) R&D grant MK00009494301 & Old
6 Maps Online (http://www.oldmapsonline.org/) from the Ministry of
7 Culture of the Czech Republic.
8
9
10 Copyright (C) 2009-2019 IIPImage.
11 Author: Ruven Pillay
12
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 3 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software Foundation,
25 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
26 */
27
28
29 #include "KakaduImage.h"
30 #include <kdu_compressed.h>
31 #include <cmath>
32 #include <sstream>
33
34 // Required for get_nprocs_conf() on Linux
35 #ifdef NPROCS
36 #include <sys/sysinfo.h>
37 #endif
38
39 // On Mac OS X, define our own get_nprocs_conf()
40 #if defined (__APPLE__) || defined(__FreeBSD__)
41 #include <pthread.h>
42 #include <sys/sysctl.h>
get_nprocs_conf()43 unsigned int get_nprocs_conf(){
44 int numProcessors = 0;
45 size_t size = sizeof(numProcessors);
46 int returnCode = sysctlbyname("hw.ncpu", &numProcessors, &size, NULL, 0);
47 if( returnCode != 0 ) return 1;
48 else return (unsigned int)numProcessors;
49 }
50 #define NPROCS
51 #endif
52
53
54 #include "Timer.h"
55 //#define DEBUG 1
56
57
58 using namespace std;
59
60
openImage()61 void KakaduImage::openImage()
62 {
63 string filename = getFileName( currentX, currentY );
64
65 // Update our timestamp
66 updateTimestamp( filename );
67
68 // Set our error handlers
69 kdu_customize_warnings(&pretty_cout);
70 kdu_customize_errors(&pretty_cerr);
71
72 #ifdef DEBUG
73 Timer timer;
74 timer.start();
75 #endif
76
77 // Open the JPX or JP2 file
78 try{
79 src.open( filename.c_str(), true );
80 if( jpx_input.open( &src, false ) != 1 ) throw 1;
81 }
82 catch (...){
83 throw file_error( "Kakadu :: Unable to open '"+filename+"'"); // Rethrow the exception
84 }
85
86
87 // Get our JPX codestream
88 try{
89 jpx_stream = jpx_input.access_codestream(0);
90 if( !jpx_stream.exists() ) throw 1;
91 }
92 catch (...){
93 throw file_error( "Kakadu :: No codestream in file '"+filename+"'"); // Rethrow exception
94 }
95
96
97 // Open the underlying JPEG2000 codestream
98 input = NULL;
99 input = jpx_stream.open_stream();
100
101 // Create codestream
102 codestream.create(input);
103 if( !codestream.exists() ) throw file_error( "Kakadu :: Unable to create codestream for '"+filename+"'"); // Throw exception
104
105 // Set up the cache size and allow restarting
106 //codestream.augment_cache_threshold(1024);
107
108 // Set Kakadu read mode
109 switch( kdu_readmode ) {
110 case KDU_FUSSY:
111 codestream.set_fussy();
112 break;
113 case KDU_RESILIENT:
114 codestream.set_resilient();
115 break;
116 case KDU_FAST:
117 default:
118 codestream.set_fast();
119 }
120
121 codestream.set_persistent();
122 // codestream.enable_restart();
123
124 // Load our metadata if not already loaded
125 if( bpc == 0 ) loadImageInfo( currentX, currentY );
126
127 #ifdef DEBUG
128 logfile << "Kakadu :: openImage() :: " << timer.getTime() << " microseconds" << endl;
129 #endif
130
131 }
132
133
loadImageInfo(int seq,int ang)134 void KakaduImage::loadImageInfo( int seq, int ang )
135 {
136 jp2_channels j2k_channels;
137 jp2_palette j2k_palette;
138 jp2_resolution j2k_resolution;
139 jp2_colour j2k_colour;
140 kdu_coords layer_size;
141 jpx_layer_source jpx_layer;
142
143 // Malformed images can throw exceptions here with older versions of Kakadu
144 try{
145 jpx_layer = jpx_input.access_layer(0);
146 }
147 catch( ... ){
148 throw file_error( "Kakadu :: Core Exception Caught During Metadata Extraction"); // Rethrow the exception
149 }
150
151 j2k_channels = jpx_layer.access_channels();
152 j2k_resolution = jpx_layer.access_resolution();
153 j2k_colour = jpx_layer.access_colour(0);
154 layer_size = jpx_layer.get_layer_size();
155
156 image_widths.push_back(layer_size.x);
157 image_heights.push_back(layer_size.y);
158 channels = codestream.get_num_components();
159 numResolutions = codestream.get_min_dwt_levels();
160 bpc = codestream.get_bit_depth(0,true);
161
162 unsigned int w = layer_size.x;
163 unsigned int h = layer_size.y;
164
165 #ifdef DEBUG
166 logfile << "Kakadu :: DWT Levels: " << numResolutions << endl;
167 logfile << "Kakadu :: Resolution : " << w << "x" << h << endl;
168 #endif
169
170 // Loop through each resolution and calculate the image dimensions -
171 // We calculate ourselves rather than relying on get_dims() to force a similar
172 // behaviour to TIFF with resolutions at floor(x/2) rather than Kakadu's default ceil(x/2)
173 for( unsigned int c=1; c<numResolutions; c++ ){
174 // codestream.apply_input_restrictions(0,0,c,1,NULL,KDU_WANT_OUTPUT_COMPONENTS);
175 // kdu_dims layers;
176 // codestream.get_dims(0,layers,true);
177 // image_widths.push_back(layers.size.x);
178 // image_heights.push_back(layers.size.y);
179 w = floor( w/2.0 );
180 h = floor( h/2.0 );
181 image_widths.push_back(w);
182 image_heights.push_back(h);
183 #ifdef DEBUG
184 logfile << "Kakadu :: Resolution : " << w << "x" << h << endl;
185 #endif
186 }
187
188 // If we don't have enough resolutions to fit a whole image into a single tile
189 // we need to generate them ourselves virtually. Fortunately, the
190 // kdu_region_decompressor function is able to handle the downsampling for us for one extra level.
191 // Extra downsampling has to be done ourselves
192 unsigned int n = 1;
193 w = image_widths[0];
194 h = image_heights[0];
195 while( (w>tile_width) || (h>tile_height) ){
196 n++;
197 w = floor( w/2.0 );
198 h = floor( h/2.0 );
199 if( n > numResolutions ){
200 image_widths.push_back(w);
201 image_heights.push_back(h);
202 }
203 }
204
205 if( n > numResolutions ){
206 #ifdef DEBUG
207 logfile << "Kakadu :: Warning! Insufficient resolution levels in JPEG2000 stream. Will generate " << n-numResolutions << " extra levels dynamically -" << endl
208 << "Kakadu :: However, you are advised to regenerate the file with at least " << n << " levels" << endl;
209 #endif
210 }
211
212 if( n > numResolutions ) virtual_levels = n-numResolutions-1;
213 numResolutions = n;
214
215
216 // Check for a palette and LUT - only used for bilevel images for now
217 int cmp, plt, stream_id,format=0;
218 #if defined(KDU_MAJOR_VERSION) && (KDU_MAJOR_VERSION >= 7) && (KDU_MINOR_VERSION >= 8)
219 // API change for get_colour_mapping in Kakadu 7.8
220 j2k_channels.get_colour_mapping(0,cmp,plt,stream_id,format);
221 #else
222 j2k_channels.get_colour_mapping(0,cmp,plt,stream_id);
223 #endif
224
225 j2k_palette = jpx_stream.access_palette();
226
227 if( j2k_palette.exists() && j2k_palette.get_num_luts()>0 ){
228 int entries = j2k_palette.get_num_entries();
229 float *lt = new float[entries];
230 j2k_palette.get_lut(0,lt); // Note that we extract only first LUT
231 // Force to unsigned format, scale to 8 bit and load these into our LUT vector
232 for( int n=0; n<entries; n++ ){
233 lut.push_back((int)((lt[n]+0.5)*255));
234 }
235 delete[] lt;
236 #ifdef DEBUG
237 logfile << "Kakadu :: Palette with " << j2k_palette.get_num_luts() << " LUT and " << entries
238 << " entries/LUT with values " << lut[0] << "," << lut[1] << endl;
239 #endif
240 }
241
242
243 // Extract any ICC profile and add it to our metadata map
244 int icc_length = 0;
245 const char* icc = (const char*) j2k_colour.get_icc_profile( &icc_length );
246 if( icc_length > 0 ) metadata["icc"] = string( icc, icc_length );
247
248
249 // Set our colour space - we let Kakadu automatically handle CIELAB->sRGB conversion for the time being
250 if( channels == 1 ){
251 colourspace = (bpc==1)? BINARY : GREYSCALE;
252 }
253 else{
254 jp2_colour_space cs = j2k_colour.get_space();
255 if( cs == JP2_sRGB_SPACE || cs == JP2_iccRGB_SPACE || cs == JP2_esRGB_SPACE || cs == JP2_CIELab_SPACE ) colourspace = sRGB;
256 //else if ( cs == JP2_CIELab_SPACE ) colourspace = CIELAB;
257 else {
258 #ifdef DEBUG
259 logfile << "WARNING : colour space not found, setting sRGB colour space value" << endl;
260 #endif
261 colourspace = sRGB;
262 }
263 }
264
265
266 // Get the number of quality layers - must first open a tile, however
267 kdu_tile kt = codestream.open_tile(kdu_coords(0,0),NULL);
268 quality_layers = codestream.get_max_tile_layers();
269 #ifdef DEBUG
270 string cs;
271 switch( j2k_colour.get_space() ){
272 case JP2_sRGB_SPACE:
273 cs = "JP2_sRGB_SPACE";
274 break;
275 case JP2_sLUM_SPACE:
276 cs = "JP2_sLUM_SPACE";
277 break;
278 case JP2_CIELab_SPACE:
279 cs = "JP2_CIELab_SPACE";
280 break;
281 default:
282 cs = j2k_colour.get_space();
283 break;
284 }
285 logfile << "Kakadu :: " << bpc << " bit data" << endl
286 << "Kakadu :: " << channels << " channels" << endl
287 << "Kakadu :: colour space: " << cs << endl
288 << "Kakadu :: " << quality_layers << " quality layers detected" << endl;
289 #endif
290 kt.close();
291
292 // For bilevel images, force channels to 1 as we sometimes come across such images which claim 3 channels
293 if( bpc == 1 ) channels = 1;
294
295 // Get the max and min values for our data type
296 //double sminvalue[4], smaxvalue[4];
297 for( unsigned int i=0; i<channels; i++ ){
298 min.push_back( 0.0 );
299 if( bpc > 8 && bpc <= 16 ) max.push_back( 65535.0 );
300 else max.push_back( 255.0 );
301 }
302
303 isSet = true;
304 }
305
306
307 // Close our image descriptors
closeImage()308 void KakaduImage::closeImage()
309 {
310 #ifdef DEBUG
311 Timer timer;
312 timer.start();
313 #endif
314
315 // Close our codestream - need to make sure it exists or it'll crash
316 if( codestream.exists() ) codestream.destroy();
317
318 // Close our JP2 family and JPX files
319 src.close();
320 jpx_input.close();
321
322 #ifdef DEBUG
323 logfile << "Kakadu :: closeImage() :: " << timer.getTime() << " microseconds" << endl;
324 #endif
325 }
326
327
328 // Get an individual tile
getTile(int seq,int ang,unsigned int res,int layers,unsigned int tile)329 RawTile KakaduImage::getTile( int seq, int ang, unsigned int res, int layers, unsigned int tile )
330 {
331
332 // Scale up our output bit depth to the nearest factor of 8
333 unsigned obpc = bpc;
334 if( bpc <= 16 && bpc > 8 ) obpc = 16;
335 else if( bpc <= 8 ) obpc = 8;
336
337 #ifdef DEBUG
338 Timer timer;
339 timer.start();
340 #endif
341
342 if( res > numResolutions ){
343 ostringstream tile_no;
344 tile_no << "Kakadu :: Asked for non-existent resolution: " << res;
345 throw file_error( tile_no.str() );
346 }
347
348 int vipsres = ( numResolutions - 1 ) - res;
349
350 unsigned int tw = tile_width;
351 unsigned int th = tile_height;
352
353
354 // Get the width and height for last row and column tiles
355 unsigned int rem_x = image_widths[vipsres] % tile_width;
356 unsigned int rem_y = image_heights[vipsres] % tile_height;
357
358
359 // Calculate the number of tiles in each direction
360 unsigned int ntlx = (image_widths[vipsres] / tw) + (rem_x == 0 ? 0 : 1);
361 unsigned int ntly = (image_heights[vipsres] / th) + (rem_y == 0 ? 0 : 1);
362
363 if( tile >= ntlx*ntly ){
364 ostringstream tile_no;
365 tile_no << "Kakadu :: Asked for non-existent tile: " << tile;
366 throw file_error( tile_no.str() );
367 }
368
369 // Alter the tile size if it's in the last column
370 if( ( tile % ntlx == ntlx - 1 ) && ( rem_x != 0 ) ) {
371 tw = rem_x;
372 }
373
374 // Alter the tile size if it's in the bottom row
375 if( ( tile / ntlx == ntly - 1 ) && rem_y != 0 ) {
376 th = rem_y;
377 }
378
379
380 // Calculate the pixel offsets for this tile
381 int xoffset = (tile % ntlx) * tile_width;
382 int yoffset = (unsigned int) floor((double)(tile/ntlx)) * tile_height;
383
384 #ifdef DEBUG
385 logfile << "Kakadu :: Tile size: " << tw << "x" << th << "@" << channels << endl;
386 #endif
387
388
389 // Create our Rawtile object and initialize with data
390 RawTile rawtile( tile, res, seq, ang, tw, th, channels, obpc );
391
392
393 // Create our raw tile buffer and initialize some values
394 if( obpc == 16 ) rawtile.data = new unsigned short[tw*th*channels];
395 else if( obpc == 8 ) rawtile.data = new unsigned char[tw*th*channels];
396 else throw file_error( "Kakadu :: Unsupported number of bits" );
397
398 rawtile.dataLength = tw*th*channels*(obpc/8);
399 rawtile.filename = getImagePath();
400 rawtile.timestamp = timestamp;
401
402 // Process the tile
403 process( res, layers, xoffset, yoffset, tw, th, rawtile.data );
404
405
406 #ifdef DEBUG
407 logfile << "Kakadu :: bytes parsed: " << codestream.get_total_bytes(true) << endl;
408 logfile << "Kakadu :: getTile() :: " << timer.getTime() << " microseconds" << endl;
409 #endif
410
411 return rawtile;
412
413 }
414
415
416 // Get an entire region and not just a tile
getRegion(int seq,int ang,unsigned int res,int layers,int x,int y,unsigned int w,unsigned int h)417 RawTile KakaduImage::getRegion( int seq, int ang, unsigned int res, int layers, int x, int y, unsigned int w, unsigned int h )
418 {
419 // Scale up our output bit depth to the nearest factor of 8
420 unsigned int obpc = bpc;
421 if( bpc <= 16 && bpc > 8 ) obpc = 16;
422 else if( bpc <= 8 ) obpc = 8;
423
424 #ifdef DEBUG
425 Timer timer;
426 timer.start();
427 #endif
428
429 RawTile rawtile( 0, res, seq, ang, w, h, channels, obpc );
430
431 if( obpc == 16 ) rawtile.data = new unsigned short[w*h*channels];
432 else if( obpc == 8 ) rawtile.data = new unsigned char[w*h*channels];
433 else throw file_error( "Kakadu :: Unsupported number of bits" );
434
435 rawtile.dataLength = w*h*channels*(obpc/8);
436 rawtile.filename = getImagePath();
437 rawtile.timestamp = timestamp;
438
439 process( res, layers, x, y, w, h, rawtile.data );
440
441 #ifdef DEBUG
442 logfile << "Kakadu :: getRegion() :: " << timer.getTime() << " microseconds" << endl;
443 #endif
444
445 return rawtile;
446
447 }
448
449
450 // Main processing function
process(unsigned int res,int layers,int xoffset,int yoffset,unsigned int tw,unsigned int th,void * d)451 void KakaduImage::process( unsigned int res, int layers, int xoffset, int yoffset, unsigned int tw, unsigned int th, void *d )
452 {
453
454 // Scale up our output bit depth to the nearest factor of 8
455 unsigned int obpc = bpc;
456 if( bpc <= 16 && bpc > 8 ) obpc = 16;
457 else if( bpc <= 8 ) obpc = 8;
458
459 int vipsres = ( numResolutions - 1 ) - res;
460
461 // Handle virtual resolutions
462 if( res < virtual_levels ){
463 unsigned int factor = 1 << (virtual_levels-res);
464 xoffset *= factor;
465 yoffset *= factor;
466 tw *= factor;
467 th *= factor;
468 vipsres = numResolutions - 1 - virtual_levels;
469 #ifdef DEBUG
470 logfile << "Kakadu :: using smallest existing resolution " << virtual_levels << endl;
471 #endif
472 }
473
474 // Set the number of layers to half of the number of detected layers if we have not set the
475 // layers parameter manually. If layers is set to less than 0, use all layers.
476 if( layers < 0 ) layers = quality_layers;
477 else if( layers == 0 ) layers = ceil( quality_layers/2.0 );
478
479 // Also make sure we have at least 1 layer
480 if( layers < 1 ) layers = 1;
481
482
483 // Set up the bounding box for our tile
484 kdu_dims image_dims, canvas_dims;
485 canvas_dims.pos = kdu_coords( xoffset, yoffset );
486 canvas_dims.size = kdu_coords( tw, th );
487
488 // Check our codestream status - throw exception for malformed codestreams
489 if( !codestream.exists() ) throw file_error( "Kakadu :: Malformed JPEG2000 - unable to access codestream");
490
491 // Apply our resolution restrictions to calculate the rendering zone on the highest resolution
492 // canvas
493 codestream.apply_input_restrictions( 0,0,vipsres,layers,&canvas_dims,KDU_WANT_OUTPUT_COMPONENTS );
494 codestream.map_region( 0, canvas_dims, image_dims, true );
495
496
497 // Create some worker threads
498 #ifdef NPROCS
499 int num_threads = get_nprocs_conf();
500 #else
501 int num_threads = 0;
502 #endif
503
504
505 kdu_thread_env env, *env_ref = NULL;
506 if( num_threads > 0 ){
507 env.create();
508 for (int nt=0; nt < num_threads; nt++){
509 // Unable to create all the threads requested
510 if( !env.add_thread() ) num_threads = nt;
511 }
512 env_ref = &env;
513 }
514
515
516
517 #ifdef DEBUG
518 logfile << "Kakadu :: decompressor init with " << num_threads << " threads" << endl;
519 logfile << "Kakadu :: decoding " << layers << " quality layers" << endl;
520 #endif
521
522
523 // Setup tile and stripe buffers
524 void *buffer = NULL;
525 void *stripe_buffer = NULL;
526 int *stripe_heights = NULL;
527
528 try{
529
530 // Note that we set max channels rather than leave the default to strip off alpha channels
531 codestream.apply_input_restrictions( 0, channels, vipsres, layers, &image_dims, KDU_WANT_OUTPUT_COMPONENTS );
532
533 decompressor.start( codestream, false, true, env_ref, NULL );
534
535 stripe_heights = new int[channels];
536 codestream.get_dims(0,comp_dims,true);
537
538 #ifdef DEBUG
539 logfile << "Kakadu :: decompressor starting" << endl;
540
541 logfile << "Kakadu :: requested region on high resolution canvas: position: "
542 << image_dims.pos.x << "x" << image_dims.pos.y
543 << ". size: " << image_dims.size.x << "x" << image_dims.size.y << endl;
544
545 logfile << "Kakadu :: mapped resolution region size: " << comp_dims.size.x << "x" << comp_dims.size.y << endl;
546 logfile << "Kakadu :: About to pull stripes" << endl;
547 #endif
548
549 // Make sure we don't have zero or negative sized images
550 if( comp_dims.size.x <= 0 || comp_dims.size.y <= 0 ){
551 #ifdef DEBUG
552 logfile << "Kakadu :: Error: region of zero size requested" << endl;
553 #endif
554 throw 1;
555 }
556
557 int index = 0;
558 bool continues = true;
559
560 // Get our stripe heights so that we can allocate our stripe buffer
561 // Assume that first stripe height is largest
562 decompressor.get_recommended_stripe_heights( comp_dims.size.y,
563 1024, stripe_heights, NULL );
564
565 #ifdef DEBUG
566 logfile << "Kakadu :: Allocating memory for stripe height " << stripe_heights[0] << endl;
567 #endif
568
569 // Create our buffers
570
571 if( obpc == 16 ){
572 stripe_buffer = new kdu_uint16[tw*stripe_heights[0]*channels];
573 buffer = new unsigned short[tw*th*channels];
574 }
575 else if( obpc == 8 ){
576 stripe_buffer = new kdu_byte[tw*stripe_heights[0]*channels];
577 buffer = new unsigned char[tw*th*channels];
578 }
579
580 // Keep track of changes in stripe heights
581 int previous_stripe_heights = stripe_heights[0];
582
583
584 while( continues ){
585
586
587 decompressor.get_recommended_stripe_heights( comp_dims.size.y,
588 1024, stripe_heights, NULL );
589
590
591 // If we have a larger stripe height, allocate new memory for this
592 if( stripe_heights[0] > previous_stripe_heights ){
593
594 // First delete then re-allocate our buffers
595 delete_buffer( stripe_buffer );
596 if( obpc == 16 ){
597 stripe_buffer = new kdu_uint16[tw*stripe_heights[0]*channels];
598 }
599 else if( obpc == 8 ){
600 stripe_buffer = new kdu_byte[tw*stripe_heights[0]*channels];
601 }
602
603 #ifdef DEBUG
604 logfile << "Kakadu :: Stripe height increase: re-allocating memory for height " << stripe_heights[0] << endl;
605 #endif
606 }
607
608 // Check for zero height, which can occur with incorrect position or size parameters
609 if( stripe_heights[0] == 0 ){
610 #ifdef DEBUG
611 logfile << "Kakadu :: Error: Zero stripe height" << endl;
612 #endif
613 throw 1;
614 }
615
616
617 if( obpc == 16 ){
618 // Set these to false to get unsigned 16 bit values
619 bool s[3] = {false,false,false};
620 continues = decompressor.pull_stripe( (kdu_int16*) stripe_buffer, stripe_heights, NULL, NULL, NULL, NULL, s );
621 }
622 else if( obpc == 8 ){
623 continues = decompressor.pull_stripe( (kdu_byte*) stripe_buffer, stripe_heights, NULL, NULL, NULL );
624 }
625
626
627 #ifdef DEBUG
628 logfile << "Kakadu :: stripe pulled" << endl;
629 #endif
630
631 // Copy the data into the supplied buffer
632 void *b1, *b2;
633 if( obpc == 16 ){
634 b1 = &( ((kdu_uint16*)stripe_buffer)[0] );
635 b2 = &( ((unsigned short*)buffer)[index] );
636 }
637 else{ // if( obpc == 8 ){
638 b1 = &( ((kdu_byte*)stripe_buffer)[0] );
639 b2 = &( ((unsigned char*)buffer)[index] );
640
641 /* Handle 1 bit bilevel images, which we output scaled to 8 bits
642 - ideally we would do this in the Kakadu pull_stripe function,
643 but the precisions parameter seems not to work as expected.
644 When requesting OUTPUT_COMPONENTS, data is provided as 0 or 128,
645 so simply scale this up to [0,255]
646 */
647 if( bpc == 1 ){
648
649 unsigned int k = tw * stripe_heights[0] * channels;
650
651 // Deal with inverted LUTs - we should really handle LUTs more generally, however
652 if( !lut.empty() && lut[0]>lut[1] ){
653 for( unsigned int n=0; n<k; n++ ){
654 ((kdu_byte*)stripe_buffer)[n] = ~(-((kdu_byte*)stripe_buffer)[n] >> 8);
655 }
656 }
657 else{
658 for( unsigned int n=0; n<k; n++ ){
659 ((kdu_byte*)stripe_buffer)[n] = (-((kdu_byte*)stripe_buffer)[n] >> 8);
660 }
661 }
662 }
663 }
664
665 memcpy( b2, b1, tw * stripe_heights[0] * channels * (obpc/8) );
666
667 // Advance our output buffer pointer
668 index += tw * stripe_heights[0] * channels;
669
670 #ifdef DEBUG
671 logfile << "Kakadu :: stripe complete with height " << stripe_heights[0] << endl;
672 #endif
673
674 }
675
676
677 if( !decompressor.finish() ){
678 throw file_error( "Kakadu :: Error indicated by finish()" );
679 }
680
681
682 // Shrink virtual resolution tiles
683 if( res < virtual_levels ){
684
685 #ifdef DEBUG
686 logfile << "Kakadu :: resizing tile to virtual resolution with factor " << (1 << (virtual_levels-res)) << endl;
687 #endif
688
689 unsigned int n = 0;
690 unsigned int factor = 1 << (virtual_levels-res);
691 for( unsigned int j=0; j<th; j+=factor ){
692 for( unsigned int i=0; i<tw; i+=factor ){
693 for( unsigned int k=0; k<channels; k++ ){
694 // Handle 16 and 8 bit data
695 if( obpc==16 ){
696 ((unsigned short*)d)[n++] = ((unsigned short*)buffer)[j*tw*channels + i*channels + k];
697 }
698 else if( obpc==8 ){
699 ((unsigned char*)d)[n++] = ((unsigned char*)buffer)[j*tw*channels + i*channels + k];
700 }
701 }
702 }
703 }
704 }
705 else memcpy( d, buffer, tw*th*channels * (obpc/8) );
706
707 // Delete our local buffer
708 delete_buffer( buffer );
709
710 #ifdef DEBUG
711 logfile << "Kakadu :: decompressor completed" << endl;
712 #endif
713
714
715 }
716 catch (...){
717 // Shut down our decompressor, delete our buffers, destroy our threads and codestream before rethrowing the exception
718 decompressor.finish();
719 if( env.exists() ) env.destroy();
720 delete_buffer( stripe_buffer );
721 delete_buffer( buffer );
722 if( stripe_heights ) delete[] stripe_heights;
723 throw file_error( "Kakadu :: Core Exception Caught"); // Rethrow the exception
724 }
725
726
727 // Destroy our threads
728 if( env.exists() ) env.destroy();
729
730 // Delete our stripe buffer
731 delete_buffer( stripe_buffer );
732 if( stripe_heights ){
733 delete[] stripe_heights;
734 stripe_heights = NULL;
735 }
736
737 }
738
739
740 // Delete our buffers
delete_buffer(void * buffer)741 void KakaduImage::delete_buffer( void* buffer ){
742 if( buffer ){
743 if( bpc <= 16 && bpc > 8 ) delete[] (kdu_uint16*) buffer;
744 else if( bpc<=8 ) delete[] (kdu_byte*) buffer;
745 }
746
747
748 }
749