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