1 //  ----------------------------------------------------------------------------
2 //  MODULE    : PTileFlashPix
3 //  LANGUAGE  : C++
4 //  CREATOR   : Philippe BOSSUT
5 //  CREAT. DATE : Wednesday, March 20, 1996
6 //  DESCRIPTION :
7 //  COMMENTS  :
8 //      SCCSID      : @(#)ptil_fpx.cpp  1.11 12:24:18 08 Jul 1997
9 //  ----------------------------------------------------------------------------
10 //  Copyright (c) 1999 Digital Imaging Group, Inc.
11 //  For conditions of distribution and use, see copyright notice
12 //  in Flashpix.h
13 //  ----------------------------------------------------------------------------
14 
15 //  ----------------------------------------------------------------------------
16   #include "ptil_fpx.h"
17 //  ----------------------------------------------------------------------------
18 
19 //  Includes
20 //  --------
21 
22 #ifndef Memoire_h
23   #include  "b_memory.h"
24 #endif
25 
26 #ifndef Debug_h
27   #include  "debug.h"
28 #endif
29 
30 #ifndef FPXBaselineIO_h
31   #include "fpxlibio.h"
32 #endif
33 
34 #ifndef FPXBaselineView_h
35   #include "fpxlib.h"
36 #endif
37 
38 
39 #ifndef OLECommun_h
40   #include "olecomm.h"
41 #endif
42 
43 #ifndef OLECore_h
44   #include "olecore.h"
45 #endif
46 
47 #ifndef OLEStorages_h
48   #include  "olestorg.h"
49 #endif
50 
51 #ifndef OLEHeaderStream_h
52   #include  "olehstrm.h"
53 #endif
54 
55 #ifndef OLEProperties_h
56   #include  "oleprop.h"
57 #endif
58 
59 #ifndef OLEPropertySet_h
60   #include  "oleprops.h"
61 #endif
62 
63 #ifndef OLEFiles_h
64   #include  "olefiles.h"
65 #endif
66 
67 
68 #ifndef FlashPixUtils_h
69   #include "fpxutils.h"
70 #endif
71 
72 
73 #ifndef AdvancedIVUE_h
74   #include "viewimg.h"
75 #endif
76 
77 #ifndef ColorTwist_h
78   #include "coltwist.h"
79 #endif
80 
81 
82 #ifndef FileFlashPixIO_h
83   #include "f_fpxio.h"
84 #endif
85 
86 #ifndef PResolutionFlashPix_h
87   #include "pres_fpx.h"
88 #endif
89 
90 #ifndef FlashPixFormat_h
91   #include "fpxformt.h"
92 #endif
93 
94 #ifndef CompressorJPEG_h
95     #include  "cp_jpeg.h"
96 #endif
97 #ifndef Compresseur32Vers24_h
98     #include  "cp32to24.h"
99 #endif
100 
101 #ifndef SwapBytes_h
102   #include "swapbyte.h"
103 #endif
104 
105 //  Constants
106 //  ---------
107 
108 //  Variables
109 //  ---------
110 Boolean     gContrastVal = 0;
111 unsigned char gContrastLut[256];
112 
113 //  ----------------------------------------------------------------------------
114 //  Internal Functions
115 //  ----------------------------------------------------------------------------
116 
117 //  ----------------------------------------------------------------------------
118 //  Member Functions
119 //  ----------------------------------------------------------------------------
120 
121 //  ----------------------------------------------------------------------------
122 //  Methods of the "PTileFlashPix" class
123 //
124 //  Manage a tile of a sub-image (generaly a tile is 256 x 256 pixels)
125 //  ----------------------------------------------------------------------------
126 
PTileFlashPix()127 PTileFlashPix::PTileFlashPix()
128 {
129   pixelsSpace     = NON_AUTHORIZED_SPACE;
130   rawPixelsSpace    = NON_AUTHORIZED_SPACE;
131   dirtyCount      = -1;         // This value set when pixels read
132   tileInitialize    = FALSE;
133   viewingParamApplied = FALSE;
134 
135   //Initialize compression options
136   compression     = 0;
137   compressionSubtype  = 0;
138   idCodec       = ConvertCompressionOption();
139 }
140 
141 
142 //  ----------------------------------------------------------------------------
143 // suppress the 'pixels' buffer from the list of buffer in memory and delete it
~PTileFlashPix()144 PTileFlashPix::~PTileFlashPix()
145 {
146 }
147 
148 
149 //  ----------------------------------------------------------------------------
150 // Initialize a tile with file assignment
151 // Use this constructor in Read mode
InitializeRead(PResolutionLevel * father,long pixelOffset,long sizetile,long id,long theCompression,long theCompressionSubtype)152 void PTileFlashPix::InitializeRead (PResolutionLevel* father, long pixelOffset,
153           long sizetile, long id, long theCompression, long theCompressionSubtype)
154 {
155   // Call the parent method
156   PTile::InitializeRead (father, pixelOffset, sizetile, id, theCompression);
157 
158   // Set the compression type and subtype for tile
159   compression     = theCompression;
160   compressionSubtype  = theCompressionSubtype;
161   idCodec       = ConvertCompressionOption();
162 
163   // Set quality factor
164   qualityFactor = ((PResolutionFlashPix *)fatherSubImage)->qualityFactor;
165 
166   // Set the number of channels
167   nbChannels = ((PResolutionFlashPix *)fatherSubImage)->nbChannels;
168 
169   // Will have to expand the channels onto 32 bits per pixel if no compression
170   if ((idCodec == TLC_Aucun) && (((PResolutionFlashPix*)fatherSubImage)->nbChannels != 4))
171     idCodec = TLC_32Vers24;
172 }
173 
174 
175 //  ----------------------------------------------------------------------------
176 // Convert the FPX compression code to index of compressor
ConvertCompressionOption()177 TLC_IdCodec PTileFlashPix::ConvertCompressionOption ()
178 {
179   TLC_IdCodec index;
180 
181 
182   switch ((FPXCompressionOption)compression) {
183     case NONE:
184         index = TLC_Aucun;
185       break;
186       case SINGLE_COLOR:
187         index = TLC_SingleColor;
188       break;
189       case JPEG_UNSPECIFIED:
190       case JPEG_BY_QUALITY:
191       case JPEG_BY_TABLE_GROUP:
192         index = TLC_JPEG;
193       break;
194 
195     // For FlashPix, currently assume valid compressions are only these above modes, anything else is
196     // considered invalid and no compressur for it
197 
198     default:
199         index = TLC_INVALID;
200       break;
201   }
202   return index;
203 }
204 
205 
206 //  ----------------------------------------------------------------------------
207 // Write the data passed as parameters directly in the file
208 // The passed tile data is written to the file, no compression is performed.
209 // Returns: FPX_FILE_WRITE_ERROR
210 //
WriteRawTile(FPXCompressionOption compressOption,unsigned char compressQuality,long compressSubtype,unsigned int dataLength,void * data)211 FPXStatus PTileFlashPix::WriteRawTile (FPXCompressionOption   compressOption,
212                                        unsigned char          compressQuality,
213                                        long                   compressSubtype,
214                                        unsigned int          dataLength,
215                                        void*                  data)
216 {
217   Boolean   wasLocked, ok;
218   FPXStatus   status = FPX_OK;
219 
220 
221   assert(data);
222 
223   // Set the number of channels
224   nbChannels = ((PResolutionFlashPix *)fatherSubImage)->nbChannels;
225 
226   wasLocked = IsLocked();
227   Lock();
228   OLEHeaderStream *subStreamData = ((PResolutionFlashPix*)fatherSubImage)->GetSubStreamData();
229 
230   // If the tile has never been written, reset the compression with the one passed in parameter
231   if ( posPixelFic < 0 && !tileInitialize ) {
232     compression         = compressOption;
233     idCodec             = ConvertCompressionOption();
234     qualityFactor       = compressQuality;
235     compressionSubtype  = compressSubtype;
236   }
237 
238   // TEMPORARY IN THIS VERSION: when no compression, should compress 32->nbChannel
239   if ((idCodec == TLC_Aucun) && (!fatherSubImage->fatherFile->existAlphaChannel))
240     idCodec = TLC_32Vers24;
241 
242   // Compute position of the tile in file
243   // CAUTION : go to the end of the file if the new tile is bigger than the old one
244   // NO GARBAGE COLLECTION WITHIN THE FILE
245 
246   if ((posPixelFic < 0) || ((unsigned long) tileSize < dataLength)) {
247     ok = subStreamData->GetEndOfFile(&posPixelFic);
248   }
249 
250   if (ok) {
251     // Set the tilesize as size of compressed data
252     tileSize = dataLength;
253 
254     // Go to the right position in the stream...
255     ok = subStreamData->Seek(posPixelFic);
256 
257     // ... then write the pixels or the compressed tile (at last...)
258     if (ok)
259       if (subStreamData->Write((unsigned char *)data,tileSize) == 0)
260         ok = false;
261   }
262 
263   if (!ok)
264     status = FPX_FILE_WRITE_ERROR;
265 
266   // Clean up tile and release memory
267   if (wasLocked == false)
268     UnLock();
269 
270   if (ok) {
271     // Update the global modification flag
272     PFileFlashPixIO* father = (PFileFlashPixIO*)(fatherSubImage->fatherFile);
273     father->tilesHasBeenModified = TRUE;
274   }
275   return status;
276 }
277 
278 
279 // --------------------------------------------------------------------------
280 // Write the tile in 'rawPixels' to the disk file. Use compression if necessary.
281 //
282 // Returns: FPX_MEMORY_ALLOCATION_FAILED
283 //      FPX_ERROR           - if no 'rawPixels' buffer exists in memory
284 //      FPX_FILE_WRITE_ERROR
285 //      FPX_INVALID_COMPRESSION_ERROR - if no decompressor available
286 //
Write()287 FPXStatus PTileFlashPix::Write()
288 {
289   register long   TILE_WIDTH = fatherSubImage->fatherFile->tileWidth;
290   long      sizeCompressed;       // Size of data after compression
291 
292   ptr_Compresseur monCompresseur;
293 
294   Ptr           buffer = NULL;
295   Pixel        *entireTile;
296   Boolean       wasLocked;
297   FPXStatus     status = FPX_OK;
298 
299   FPXBaselineColorSpace base = ((PResolutionFlashPix*)fatherSubImage)->baseSpace;
300   FPXBaselineColorSpace used = ((PFileFlashPixIO*)fatherSubImage->fatherFile)->usedSpace;
301 
302   // Set the number of channels
303   nbChannels = ((PResolutionFlashPix *)fatherSubImage)->nbChannels;
304 
305   if( rawPixels == NULL) {
306     assert( rawPixels);
307     return FPX_ERROR;
308   }
309 
310   // Allocate an intermediate tile if we have to pad or convert the tile
311   if ((width < TILE_WIDTH)  || (height < TILE_WIDTH) || (used != base)) {
312     FastCallocArray( entireTile, Pixel, TILE_WIDTH * TILE_WIDTH);
313 
314     if (entireTile == NULL)
315       return FPX_MEMORY_ALLOCATION_FAILED;
316 
317     short i, j;
318     Pixel *source = rawPixels;
319     Pixel *dest   = entireTile;
320 
321     // Copy line by line and pad (padding conform to spec p.22)
322     for (i = 0; i < height; i++, source+=width, dest+=TILE_WIDTH) {
323       // Copy the raw pixels of the row
324       memcpy (dest, source, width*sizeof(Pixel));
325 
326       // Pad the rest of the line with the value of the last pixel
327       Pixel *pt = dest + width;
328       Pixel padPixel = *(source + width - 1);
329 
330       for (j = width; j < TILE_WIDTH; j++, pt++)
331         *pt = padPixel;
332     }
333 
334     // Pad the remaining lines with a copy of the last line
335     Pixel *pt = dest - TILE_WIDTH;
336 
337     for (i = height; i < TILE_WIDTH; i++, dest+=TILE_WIDTH)
338       memcpy (dest, pt, TILE_WIDTH*sizeof(Pixel));
339   } else
340     entireTile = rawPixels;
341 
342   wasLocked = IsLocked();
343   Lock();
344 
345   PFlashPixFile* fileFPX = (PFlashPixFile*)(fatherSubImage->fatherFile->filePtr);
346   OLEHeaderStream *subStreamData = ((PResolutionFlashPix*)fatherSubImage)->GetSubStreamData();
347 
348 // PTCH_104 - colorspace conversion is now in PTile::WriteRectangle()
349 //  if (used != base)
350 //  {
351 //    ConvertPixelBuffer((unsigned char*)(entireTile),TILE_WIDTH*TILE_WIDTH,used,base);
352 //    pixelsSpace = base;
353 //  }
354 
355   // If the tile has never been written, reset the compression with the one of the file
356   if ( (posPixelFic < 0) && !tileInitialize ) {
357 
358     compression   = (long)((PResolutionFlashPix *)fatherSubImage)->compression;
359     idCodec     = ConvertCompressionOption();
360 
361     // Set quality factor
362     qualityFactor = ((PResolutionFlashPix *)fatherSubImage)->qualityFactor;
363   }
364 
365   // TEMPORARY IN THIS VERSION: single color compression is performed on tiles which consists
366   // of only one color
367   Pixel singleColorPixel = fatherSubImage->fatherFile->backgroundBase;    // default color value of this tile
368 
369   if ( (compression == SINGLE_COLOR) && !tileInitialize )
370   {
371     // Check to see if all pixels in this tile are same. If so apply single color compression,
372     // otherwise no compression is applied
373     if ( !IsTileAllSamePixel(entireTile, (short) TILE_WIDTH,(short) TILE_WIDTH, &singleColorPixel) ) {
374       compression = NONE;
375       idCodec   = ConvertCompressionOption();
376     }
377     else
378     {
379       // Only rotate color components if opacity is not last in order   PTCH_202
380       if (!((base == SPACE_32_BITS_RGBA)                //  PTCH_202
381         ||  (base == SPACE_32_BITS_YCCA))) {              //  PTCH_202
382         // Shift Pixels so that compsubtype gets written
383         // as RGBA not ARGB
384         // This should actually be the same channel order
385         // as specified in Subimage color of Image Contents
386         Pixel tmpPixel;
387 
388         tmpPixel.alpha = singleColorPixel.rouge;
389         tmpPixel.rouge = singleColorPixel.vert;
390         tmpPixel.vert  = singleColorPixel.bleu;
391 
392         if( nbChannels > 3)
393           tmpPixel.bleu = singleColorPixel.alpha;
394         else
395           tmpPixel.bleu = 0x00;
396 
397         singleColorPixel = tmpPixel;
398       }                               //  PTCH_202
399     }
400   }
401 
402   // TEMPORARY IN THIS VERSION: when no compression, should compress 32->nbChannel
403 
404   if ((idCodec == TLC_Aucun) && (!fatherSubImage->fatherFile->existAlphaChannel))
405     idCodec = TLC_32Vers24;
406 
407   compressionSubtype = 0; // subtype is 0 for all cases except TLC_SingleColor.
408 
409   switch(idCodec) {
410     case TLC_Aucun: // If no Compression
411       // Set the tilesize
412       sizeCompressed = TILE_WIDTH * TILE_WIDTH * sizeof(Pixel);
413 
414       // Set the output buffer to write
415       buffer = (Ptr)(entireTile);
416       break;
417 
418     case TLC_SingleColor: // Single color compression
419       // Set the tile values for a single color tile (see spec p.46-48)
420       posPixelFic     = 0;
421       tileSize      = 0;
422       compression     = SINGLE_COLOR;
423       compressionSubtype  = (long)(singleColorPixel);
424 #ifndef IN_LITTLE_ENDIAN
425     compressionSubtype = (long) SwapBytes( (int32)compressionSubtype );
426 #endif
427       // Update local management values
428       sizeCompressed    = 0;
429       buffer        = NULL;
430       break;
431 
432     case TLC_JPEG: // JPEG compression
433       // Set compression subtype ( The subtype is only used by jpeg compression )
434       compressionSubtype  = ((PResolutionFlashPix *)fatherSubImage)->compressionSubtype;
435 
436       // If channel is less than 4, compression the data
437       if ( nbChannels < 4 ) {
438         // Get the TLC_32Vers24 compressor
439         monCompresseur = (*tousLesCodecs)[TLC_32Vers24];
440         monCompresseur->Lock();       // Lock buffers used by the compressor
441 
442         // In the case of simple compression, we have to set the number of channels and the direction
443         // of the compression. This is optimized only for the most common cases.
444         Boolean leftShift = false;
445         if ((base == SPACE_32_BITS_RGBA) || (base == SPACE_32_BITS_YCCA)) {
446           leftShift   = true;
447         }
448         ((obj_Compresseur32Vers24 *)monCompresseur)->SetCompressionParameters(nbChannels,leftShift);
449 
450         if(!monCompresseur->Compresse((Ptr)entireTile,(short) TILE_WIDTH,
451             (short)TILE_WIDTH, &buffer, &tileSize)) {
452           fileFPX->CompressionError();  // Compression failed => Signal error
453           posPixelFic = -1;       // Compression failed => Delete reference pointer
454           monCompresseur->UnLock();   // Unlock buffers used by the compressor
455           assert (false);
456 
457           if ((width < TILE_WIDTH)  || (height < TILE_WIDTH) || (used != base))
458             FastDeleteArray( entireTile, Pixel);
459           status = FPX_MEMORY_ALLOCATION_FAILED;  // CHG_FILE_ERR - the only error that occurs
460           goto RETURN;
461         }
462 
463         LockDecompress ();
464         AllocDecompress(tileSize);
465         if(decompressBuffer == NULL) {        // CHG_FILE_ERR - check for allocation failure
466           status = FPX_MEMORY_ALLOCATION_FAILED;
467           goto RETURN;
468         }
469         memcpy(decompressBuffer, buffer, tileSize);
470       } else
471         decompressBuffer = (Ptr)entireTile;
472 
473       // Get the jpeg compressor
474       monCompresseur = (*tousLesCodecs)[idCodec];
475 
476       unsigned char internalConv;
477 
478       if(base == SPACE_32_BITS_RGBA || base == SPACE_32_BITS_RGB ||
479          base == SPACE_32_BITS_ARGB) {
480         internalConv = 1;
481       }
482       else {
483         internalConv = 0;
484         // clear the internal rotation bits from compression subtype
485         compressionSubtype &= 0xFF00FFFF; // Clear chroma subsample flags
486       }
487 
488 
489       // Set the jpeg parameters
490       if ( (status = (FPXStatus)((PCompressorJPEG *)monCompresseur)->SetCompressionParameters(
491                       GET_InterleaveType(compressionSubtype),
492                       GET_ChromaSubSample(compressionSubtype),
493                       // GET_InternalColorConv(compressionSubtype),
494                       internalConv,
495                       compressionSubtype,
496                       qualityFactor,
497                       nbChannels,
498                       NULL,
499                       0,
500                       FALSE ))) {
501         fileFPX->CompressionError();  // Compression failed => Signal error
502         posPixelFic = -1;       // Compression failed => Delete reference pointer
503         assert (false);
504 
505         if ((width < TILE_WIDTH)  || (height < TILE_WIDTH) || (used != base))
506           FastDeleteArray(entireTile,Pixel);
507         status = (FPXStatus)jpegErrorToFPXerror( status);
508         goto RETURN;
509       }
510 
511       // Do the JPEG compression
512 
513       if ( ((PCompressorJPEG *)monCompresseur)->Compress((unsigned char *)decompressBuffer,
514             (short)TILE_WIDTH, (short)TILE_WIDTH, (unsigned char **)&buffer,
515             &sizeCompressed))  {
516         fileFPX->CompressionError();  // Compression failed => Signal error
517         posPixelFic = -1;       // Compression failed => Delete reference pointer
518         assert (false);
519 
520         if ((width < TILE_WIDTH)  || (height < TILE_WIDTH) || (used != base))
521           FastDeleteArray(entireTile,Pixel);
522         status = FPX_MEMORY_ALLOCATION_FAILED;  // CHG_FILE_ERR - this is the only Compresse error
523         goto RETURN;
524       }
525       if ( nbChannels < 4 )
526         UnLockDecompress ();        // Unflag decompress buffer
527 
528       // Write jpeg header to the image content property set
529       WriteHeader(fileFPX, ((PCompressorJPEG *)monCompresseur)->GetHeaderBuf(),
530           ((PCompressorJPEG *)monCompresseur)->GetHeaderSize());
531       break;
532 
533     default:
534       // Get the corresponding compressor
535       monCompresseur = (*tousLesCodecs)[idCodec];
536       monCompresseur->Lock();         // Lock buffers used by the compressor
537 
538       if (idCodec == TLC_32Vers24) {
539         // In the case of simple compression, we have to set the number of channels and the direction
540         // of the compression. This is optimized only for the most common cases.
541         long nbChan = ((PResolutionFlashPix*)fatherSubImage)->nbChannels;
542         Boolean leftShift = false;
543         if ((base == SPACE_32_BITS_RGBA) || (base == SPACE_32_BITS_YCCA)) {
544           leftShift   = true;
545         }
546         ((obj_Compresseur32Vers24 *)monCompresseur)->SetCompressionParameters(nbChan,leftShift);
547       }
548 
549       if (!monCompresseur->Compresse((Ptr)entireTile,(short) TILE_WIDTH,
550         (short) TILE_WIDTH, &buffer, &sizeCompressed)) {
551         fileFPX->CompressionError();    // Compression failed => Signal error
552         posPixelFic = -1;         // Compression failed => Delete reference pointer
553         monCompresseur->UnLock();     // Unlock buffers used by the compressor
554         assert (false);
555 
556         if ((width < TILE_WIDTH)  || (height < TILE_WIDTH) || (used != base))
557           FastDeleteArray(entireTile,Pixel);
558         status = FPX_MEMORY_ALLOCATION_FAILED;  // CHG_FILE_ERR - this is the only Compresse error
559         goto RETURN;
560       }
561       break;
562   }
563 
564   // Compute position of the tile in file
565   // CAUTION : go to the end of the file if the new tile is bigger than the old one
566   // NO GARBAGE COLLECTION WITHIN THE FILE
567   if ((posPixelFic < 0) || (tileSize < sizeCompressed)) {
568     if (subStreamData->GetEndOfFile( &posPixelFic) == 0)
569       status = FPX_FILE_READ_ERROR;
570   }
571 
572   if ((status == FPX_OK) && buffer) {
573     tileSize = sizeCompressed;          // Set tilesize as size of compressed data
574 
575     if (subStreamData->Seek(posPixelFic) == 0)  // Go to the right position in the stream
576       status = FPX_FILE_READ_ERROR;
577     else                    // Write the pixels or the compressed tile
578       if (subStreamData->Write((unsigned char *)buffer,tileSize) == 0)
579         status = subStreamData->getFPXStatus();
580   }
581 
582   // Clean up tile and release memory
583   if ((idCodec != TLC_Aucun) && (idCodec != TLC_SingleColor)) { // If Compression
584     ptr_Compresseur monCompresseur = (*tousLesCodecs)[idCodec];
585     monCompresseur->UnLock();                 // Unlock buffers used by the compressor
586   }
587 
588 #ifdef Memoire_Debug
589   VISU2 "Writing this tile successfull\n" FIN
590 #endif
591 
592  RETURN:
593   if (wasLocked == false)
594     UnLock();
595 
596   if ((width < TILE_WIDTH)  || (height < TILE_WIDTH) || (used != base))
597     FastDeleteArray(entireTile,Pixel);
598 
599   if (status == FPX_OK) {
600     // Update the global modification flag
601     PFileFlashPixIO* father = (PFileFlashPixIO*)(fatherSubImage->fatherFile);
602     father->tilesHasBeenModified = TRUE;
603   }
604   return (status);
605 }
606 
607 
608 //  ----------------------------------------------------------------------------
609 // Read the data from the file directly in the passed parameters
610 // This returns the raw, possibly compressed tile data as stored in the file
611 // Returns: FPX_MEMORY_ALLOCATION_FAILED
612 //      FPX_ERROR           - if tile is missing in the file
613 //      FPX_FILE_READ_ERROR
614 //
ReadRawTile(FPXCompressionOption * compressOption,unsigned char * compressQuality,long * compressSubtype,unsigned int * dataLength,void ** data)615 FPXStatus PTileFlashPix::ReadRawTile (FPXCompressionOption*   compressOption,
616                  unsigned char*     compressQuality,
617                  long*          compressSubtype,
618                  unsigned int*     dataLength,
619                  void**         data)
620 {
621   // Declare some management variable
622 
623   PFlashPixFile *fileFPX = NULL;
624   Boolean     wasLocked = IsLocked();
625   FPXStatus   status = FPX_OK;
626 
627 
628   // Init the passed parameters with default values
629   *compressOption   = NONE;
630   *compressQuality  = 0;
631   *compressSubtype  = 0;
632   *dataLength     = 0;
633   *data         = NULL;
634 
635   // If the tile has never been written, return an error
636   if (posPixelFic < 0) {
637     status = FPX_ERROR;
638     goto exit;
639   }
640 
641   // Test if the file is already open
642   if (fatherSubImage->fatherFile->filePtr)
643     fileFPX = (PFlashPixFile*)fatherSubImage->fatherFile->filePtr;
644 
645   else {
646     // This should never happen, however, we try to open the file
647     assert(false);    // beeps...
648 
649     // Open the FPX file
650     PFileFlashPixIO* parentFile = (PFileFlashPixIO*)(fatherSubImage->fatherFile);
651 
652     // These calls are flacky because the pointed values may be obsolete by now... may crash...
653     if (parentFile->owningStorage)
654       fileFPX = new PFlashPixFile (parentFile->owningStorage,parentFile->storageName,mode_Lecture);
655     else
656       fileFPX = new PFlashPixFile (parentFile->fileName,parentFile->storageName,mode_Lecture);
657 
658     // If error while opening : signal error and exit with error
659     if (fileFPX->Erreur() != FPX_OK) {
660       parentFile->UpdateErrorCount();
661       fileFPX->SignaleErreur();
662       status = FPX_FILE_READ_ERROR;
663       goto exit;
664     }
665   }
666 
667   // dk 6aug96.  need block so subStreamData goes out of scope before exit
668   {
669       OLEHeaderStream *subStreamData = ((PResolutionFlashPix*)fatherSubImage)->GetSubStreamData();
670 
671       if (IVUE_System->GetErrorsList() != NULL) {
672       status = FPX_FILE_READ_ERROR;
673       goto exit;
674       }
675 
676       Lock();
677 
678       // Allocate the required memory
679       char* buffer;
680 
681       FastAllocArray(buffer, char, tileSize);
682       if (buffer == NULL) {
683       status = FPX_MEMORY_ALLOCATION_FAILED;
684       goto exit;
685       }
686 
687       // Read pixels data (at last...)
688       if (subStreamData->Seek(posPixelFic) == false) {
689       FastDeleteArray(buffer, char);
690       status = FPX_FILE_READ_ERROR;
691       goto exit;
692       }
693 
694       if (subStreamData->Read(buffer,tileSize) == 0) {
695       FastDeleteArray(buffer, char);
696       status = FPX_FILE_READ_ERROR;
697       goto exit;
698       }
699 
700       // Update the other parameters
701       *data      = buffer;
702       *dataLength      = tileSize;
703       *compressOption  = (FPXCompressionOption)(compression);
704       *compressQuality = qualityFactor;
705       *compressSubtype = compressionSubtype;
706   }
707 
708 exit:
709   // Close the file if IVUE opened in read only
710   if (!fatherSubImage->fatherFile->filePtr)
711     delete fileFPX;
712   if (wasLocked == false)
713     UnLock();
714   return status;
715 }
716 
717 
718 // ---------------------------------------------------------------------------------
719 // Read a tile's raw data and decompress it. The data is placed in 'rawPixels'.
720 // If 'rawPixels' contains data, then the data is lost.
721 //
722 // Returns: FPX_MEMORY_ALLOCATION_FAILED
723 //      FPX_FILE_READ_ERROR
724 //      FPX_INVALID_COMPRESSION_ERROR - if no decompressor available
725 //
ReadRawPixels()726 FPXStatus PTileFlashPix::ReadRawPixels()
727 {
728   ptr_Compresseur monDecompresseur;
729   PFlashPixFile *fileFPX;
730   Pixel       *entireTile;
731   register long   TILE_WIDTH = fatherSubImage->fatherFile->tileWidth;
732   Boolean     wasLocked = false;
733   FPXStatus   status = FPX_OK;
734   OLEHeaderStream *subStreamData = NULL;
735 
736   FPXBaselineColorSpace base = ((PResolutionFlashPix*)fatherSubImage)->baseSpace;
737   FPXBaselineColorSpace used = ((PFileFlashPixIO*)fatherSubImage->fatherFile)->usedSpace;
738 
739   // Allocate pixel buffer, if it's hasn't been done yet.
740   if (rawPixels == NULL)
741     if (AllocateRawPixels())
742       return FPX_MEMORY_ALLOCATION_FAILED;
743   rawPixelsSpace = base;
744 
745   // If the tile has never been written (i.e., it's not in the file), then fill it with the
746   //  background color and return. The file should always have all the tiles, unless the
747   //  file is in the process of creation.
748   if ((posPixelFic < 0) && (idCodec != TLC_SingleColor)) {  // PTCH_NEGONE
749     // If there was an error, fill the tile with the background color.
750     Pixel  BACKGROUND = fatherSubImage->fatherFile->backgroundUsed;
751     Pixel* pt = rawPixels;
752     long i = width * height;
753 
754     while (i--)
755       *pt++ = BACKGROUND;
756     return FPX_OK;
757   }
758 
759   // Test that the file is already open
760   if (fatherSubImage->fatherFile->filePtr)
761     fileFPX = (PFlashPixFile*)fatherSubImage->fatherFile->filePtr;
762   else {
763     // This should never happen, however, we try to open the file
764     assert(false);        // beeps...
765 
766     // Open the FPX file
767     PFileFlashPixIO* parentFile = (PFileFlashPixIO*)(fatherSubImage->fatherFile);
768 
769     // These calls are flacky because the pointed values may be obsolete by now... may crash...
770     if (parentFile->owningStorage)
771       fileFPX = new PFlashPixFile (parentFile->owningStorage,parentFile->storageName,mode_Lecture);
772     else
773       fileFPX = new PFlashPixFile (parentFile->fileName,parentFile->storageName,mode_Lecture);
774 
775     // If error while opening : signal error and exit with error
776     if (fileFPX->Erreur() != FPX_OK) {
777       parentFile->UpdateErrorCount();
778       fileFPX->SignaleErreur();
779       freshPixels = 0;
780       status = FPX_FILE_READ_ERROR;
781       goto RETURN;
782     }
783   }
784 
785   subStreamData = ((PResolutionFlashPix*)fatherSubImage)->GetSubStreamData();
786   if (IVUE_System->GetErrorsList() != NULL) {
787     status = FPX_FILE_READ_ERROR;
788     goto RETURN;
789   }
790   wasLocked = IsLocked();
791   Lock();
792 
793   // This is a left or bottom edge tile and is not full size, then allocate a temporary
794   //  buffer of full tile size.
795   if ((width < TILE_WIDTH)  || (height < TILE_WIDTH)) {
796     FastCallocArray( entireTile, Pixel, TILE_WIDTH * TILE_WIDTH);
797     if (entireTile == NULL) {
798       status = FPX_MEMORY_ALLOCATION_FAILED;
799       goto RETURN;
800     }
801   } else
802     entireTile = rawPixels;
803 
804   // Read the data from the file and decompress it into 'entireTile'
805   switch(idCodec) {
806     case TLC_Aucun:       // If no Compression
807       // Read pixels data (at last...)
808       if (subStreamData->Seek(posPixelFic) == false)
809         status = subStreamData->getFPXStatus();
810       else if (subStreamData->Read((unsigned char*)entireTile,tileSize) == 0)
811         status = subStreamData->getFPXStatus();
812       break;
813 
814     case TLC_SingleColor: {   // Fill the tile with the single color info
815       Pixel tmpPixel;
816       Pixel *pt = entireTile;
817       long  nbPixels = TILE_WIDTH*TILE_WIDTH;
818 
819 #ifndef IN_LITTLE_ENDIAN
820     compressionSubtype = (long) SwapBytes( (int32)compressionSubtype );
821 #endif
822       unsigned char *tmpptr = (unsigned char*)&compressionSubtype;
823 
824       long nbChan = ((PResolutionFlashPix*)fatherSubImage)->nbChannels;
825 
826       if(nbChan == 4) {
827         tmpPixel.alpha = tmpptr[0];
828         tmpPixel.rouge = tmpptr[1];
829         tmpPixel.vert  = tmpptr[2];
830         tmpPixel.bleu  = tmpptr[3];
831       }
832       else {
833         tmpPixel.alpha = tmpptr[3];
834         tmpPixel.rouge = tmpptr[0];
835         tmpPixel.vert  = tmpptr[1];
836         tmpPixel.bleu  = tmpptr[2];
837       }
838       while (nbPixels--)
839         *pt++ = tmpPixel;
840 #ifndef IN_LITTLE_ENDIAN
841     compressionSubtype = (long) SwapBytes( (int32)compressionSubtype );
842 #endif
843       break; }
844 
845 
846     case TLC_JPEG: {        // JPEG decompression
847       unsigned char*  jpegHeader = ((PResolutionFlashPix*)fatherSubImage)->jpegHeader;
848       unsigned long   headerSize = ((PResolutionFlashPix*)fatherSubImage)->headerSize;
849 
850       decompressorIsMissing = true;       // look for the decompressor
851 
852       // Get the corresponding compressor
853       monDecompresseur = (*tousLesCodecs)[idCodec];
854 
855       if (monDecompresseur != NULL)
856         // Read jpeg header to the image content property set
857         if ( ReadHeader(fileFPX, &jpegHeader, &headerSize) ) {
858           ((PResolutionFlashPix*)fatherSubImage)->jpegHeader = jpegHeader;
859           ((PResolutionFlashPix*)fatherSubImage)->headerSize = headerSize;
860           decompressorIsMissing = false;
861         }
862       if (!decompressorIsMissing) {     // If decompressor is present
863         LockDecompress ();          // Flag decompress buffer in use
864         AllocDecompress(tileSize);      // Allocate a buffer for compressed data
865 
866         if (!decompressBuffer)        // If no buffer allocated�
867           decompressorIsMissing = true; // �decompression failed
868         else {
869           // Read pixels data (at last...)
870           if (subStreamData->Seek( posPixelFic) == false) {
871             status = FPX_FILE_READ_ERROR;
872             goto RETURN;
873           }
874           if (subStreamData->Read( (unsigned char*)decompressBuffer, tileSize) == 0) {
875             status = FPX_FILE_READ_ERROR;
876             goto RETURN;
877           }
878           // Set the jpeg parameters
879           if ( (status = (FPXStatus)((PCompressorJPEG *)monDecompresseur)->SetCompressionParameters(
880                   GET_InterleaveType(compressionSubtype),
881                   GET_ChromaSubSample(compressionSubtype),
882                   GET_InternalColorConv(compressionSubtype),
883                   compressionSubtype,
884                   qualityFactor,
885                   nbChannels,
886                   jpegHeader,
887                   headerSize,
888                   TRUE ))) {
889             status = (FPXStatus)jpegErrorToFPXerror( status);
890             goto RETURN;
891           }
892 
893           // Do the JPEG decompression
894           if ( (status = (FPXStatus)((PCompressorJPEG *)monDecompresseur)->Decompress((unsigned char *)entireTile,
895               (short) TILE_WIDTH,(short) TILE_WIDTH, (unsigned char*)decompressBuffer, tileSize) ) ) {
896             status = (FPXStatus)jpegErrorToFPXerror( status);
897             goto RETURN;
898           }
899 
900           // Convert to 4-channel colorspace
901           if ( nbChannels < 4 ) {
902             // Get a pointer to the simple expansion function
903             monDecompresseur = (*tousLesCodecs)[TLC_32Vers24];
904 
905             // Determine the alignment direction
906             Boolean leftShift = false;
907 
908             if (((base == SPACE_32_BITS_RGB) && (used == SPACE_32_BITS_RGBA)) ||
909               ((base == SPACE_32_BITS_YCC) && (used == SPACE_32_BITS_YCCA))) {
910               leftShift   = true;
911               rawPixelsSpace = used;
912             }
913 
914             // Set the expansion parameters
915             ((obj_Compresseur32Vers24 *)monDecompresseur)->SetCompressionParameters(nbChannels,leftShift);
916 
917             LockDecompress ();          // Flag decompress buffer in use
918             AllocDecompress(TILE_WIDTH * TILE_WIDTH * nbChannels);      // Allocate a buffer for compressed data
919             if(decompressBuffer == NULL) {
920               status = FPX_MEMORY_ALLOCATION_FAILED;
921               goto RETURN;
922             }
923             // Expand the buffer
924             memcpy( decompressBuffer, entireTile, TILE_WIDTH * TILE_WIDTH * nbChannels);
925             monDecompresseur->Decompresse((Ptr)entireTile,(short) TILE_WIDTH,
926                   (short) TILE_WIDTH, decompressBuffer,
927                   TILE_WIDTH * TILE_WIDTH * nbChannels);
928             UnLockDecompress ();        // Unflag decompress buffer
929           }
930         }
931         UnLockDecompress ();            // Unflag decompress buffer
932       }
933       break;
934     }
935     default:
936     {
937       decompressorIsMissing = true;         // look for the decompressor
938 
939       // Check if the tile is valid
940       if ( (unsigned long) compression == 0xFFFFFFFF )
941         break;
942 
943       monDecompresseur = (*tousLesCodecs)[idCodec];
944 
945       if (monDecompresseur != NULL)
946         if ( monDecompresseur->DecompresseurPresent() )
947           decompressorIsMissing = false;
948 
949       if (!decompressorIsMissing) {     // If decompressor is present
950         if (idCodec == TLC_32Vers24) {
951 
952           // In the case of simple expansion, we have to set the number of channels and the direction
953           // of the expansion. This is optimized only for the most common cases.
954           long  nbChan = ((PResolutionFlashPix*)fatherSubImage)->nbChannels;
955           Boolean leftShift = false;
956 
957           if (((base == SPACE_32_BITS_RGB) && (used == SPACE_32_BITS_RGBA)) ||
958             ((base == SPACE_32_BITS_YCC) && (used == SPACE_32_BITS_YCCA))) {
959             leftShift   = true;
960             rawPixelsSpace = used;
961           }
962           ((obj_Compresseur32Vers24 *)monDecompresseur)->SetCompressionParameters(nbChan,leftShift);
963         }
964 
965         LockDecompress ();          // Flag decompress buffer in use
966         AllocDecompress(tileSize);      // Allocate a buffer for compressed data
967 
968         if (!decompressBuffer)        // If no buffer allocated�
969           decompressorIsMissing = true; // �decompression failed
970         else {
971           // Read pixels data (at last...)
972           if (subStreamData->Seek(posPixelFic) == false) {
973             status = FPX_FILE_READ_ERROR; // CHG_FILE_ERR - better error report
974             goto RETURN;
975           }
976           if (subStreamData->Read((unsigned char*)decompressBuffer,tileSize) == 0) {
977             status = FPX_FILE_READ_ERROR; // CHG_FILE_ERR - better error report
978             goto RETURN;
979           }
980           if ( !monDecompresseur->Decompresse((Ptr)entireTile,(short) TILE_WIDTH,
981                 (short) TILE_WIDTH, decompressBuffer, tileSize) )
982             decompressorIsMissing = true; // Decompression failed
983         }
984         UnLockDecompress ();        // Unflag decompress buffer
985       }
986       break;
987     }
988   }
989 
990   // If the tile is not full-sized, then copy the pertinent pixels into 'rawPixels'
991   if ((width < TILE_WIDTH)  || (height < TILE_WIDTH)) {
992     short i;
993     Pixel *source = entireTile;
994     Pixel *dest = rawPixels;
995 
996     for (i = 0; i < height; i++, source += TILE_WIDTH, dest += width)
997       memcpy (dest, source, width * sizeof(Pixel));
998     FastDeleteArray( entireTile, Pixel);
999   }
1000 
1001   // Invert alpha channel if requested by user
1002   if (fatherSubImage->fatherFile->inverseAlphaChannel && invertLUT) {
1003     unsigned char* pt = (unsigned char*)(rawPixels);
1004     long i, j;
1005 
1006     for (i = 0; i < height; i++)
1007       for (j = 0; j < width; j++, pt+=4)
1008         *pt = invertLUT[*pt];
1009   }
1010 
1011  RETURN:
1012   freshPixels = 0;                // No modified pixels in buffer yet
1013   pixelsStale = true;               // Buffer has not been modified yet
1014 
1015   if (wasLocked == false)
1016     UnLock();
1017 
1018   if (status != FPX_OK) {
1019     if (decompressorIsMissing)
1020       fileFPX->CompressionError();        // Signal error
1021 
1022     int32 *pt = (int32*)(rawPixels);
1023     int32 i, j, pat_i, pat_j;
1024 
1025     // CHG_FILE_ERR - fixed the following loops so only height x width pixels are
1026     //        written into the rawPixels buffer (not TILE_WIDTH x TILE_WIDTH)
1027     for (i = 0, pat_i = 0; i < height; i++, pat_i++) {
1028       if (pat_i == 8)
1029         pat_i = 0;
1030       for (j = 0, pat_j = 8 - pat_i; j < width; j++, pat_j++, pt++) {
1031         if (pat_j == 8) pat_j = 0;
1032         switch (pat_j) {
1033           case 0 :
1034           case 4 :
1035             *pt = 0x7F7F7F; break;
1036           case 1 :
1037           case 2 :
1038           case 3 :
1039             *pt = 0xFFFFFF; break;
1040           case 5 :
1041           case 6 :
1042           case 7 :
1043             *pt = 0; break;
1044         }
1045       }
1046     }
1047   }
1048 
1049   // Close the file if IVUE opened in read only
1050   if (!fatherSubImage->fatherFile->filePtr)
1051     delete fileFPX;
1052 
1053   return status;
1054 }
1055 
1056 
1057 // ---------------------------------------------------------------------------------
1058 // Return a tile's display-modified data. If necessary, this will read the raw data in
1059 //  from the file. If the 'rawPixels' have already been read and decompressed, then
1060 //  file reading is not necessary.
1061 // The data returned has contrast, filtering and colortwist applied to it. This data
1062 //  is cached in 'pixels' and, if missing, is created during this call.
1063 //
1064 // Returns: FPX_MEMORY_ALLOCATION_FAILED
1065 //      FPX_COLOR_CONVERSION_ERROR    - due to filtering problems
1066 //      FPX_INVALID_COMPRESSION_ERROR - if no decompressor available
1067 //
Read()1068 FPXStatus PTileFlashPix::Read()
1069 {
1070   ViewImage     *imageParam = fatherSubImage->fatherFile->imageParam;
1071   Boolean     wasLocked = IsLocked();
1072   FPXStatus   status = FPX_OK;
1073 
1074   // Check if the pixels are already cached, or if they have been modified
1075   //  FPXBaselineColorSpace base = ((PResolutionFlashPix*)fatherSubImage)->baseSpace;
1076   FPXBaselineColorSpace used = ((PFileFlashPixIO*)fatherSubImage->fatherFile)->usedSpace;
1077 
1078 
1079   // Determine if we need to read new 'rawPixels' or create a new set of 'pixels'
1080   Boolean   needNewPixels = false;
1081 
1082   if (pixels == NULL)           // If the 'pixels' buffer has not been allocated,
1083     needNewPixels = true;       //  then a new set of pixels needs to be made
1084   else if (pixelsStale)         // Otherwise, if pixels contains old image data
1085     needNewPixels = true;       //  then a new set of pixels needs to be made
1086 
1087   if (imageParam) {           // If there are display-modifications for image
1088     if (imageParam->GetDirtyCount()   //  and the # times modifications have been set
1089           != dirtyCount)      //  does not match the count when pixels were created
1090       needNewPixels = true;     //  then a new set of pixels needs to be made
1091   }
1092   if (needNewPixels == true)        // If a new set of 'pixels' needs to be made
1093     if (rawPixels == NULL)        //  and we don't have any raw pixels cached
1094       if ((status = ReadRawPixels())) //  then read-in it's  uncompressed data
1095         return status;
1096                       // If there are no viewing/display modifications
1097   if (needNewPixels == false) {     //  and we don't need to update pixels
1098     TouchRawPixelsBuffer( );      //  then we can use 'rawPixels'
1099     return FPX_OK;
1100   }
1101 
1102   // A new set of 'pixels' needs to be made. Make sure there is memory available
1103   Lock();
1104   if (pixels == NULL)
1105     if (AllocatePixels()) {
1106       if (wasLocked == false)
1107         UnLock();
1108       return FPX_MEMORY_ALLOCATION_FAILED;
1109     }
1110   dirtyCount = fatherSubImage->fatherFile->imageParam->GetDirtyCount();
1111   viewingParamApplied = fatherSubImage->fatherFile->applyParam;
1112 
1113   // Copy rawPixels into pixels. Set it's timestamp.
1114   memcpy (pixels, rawPixels, width * height * sizeof(Pixel));
1115   pixelsSpace = rawPixelsSpace;
1116   TouchPixelsBuffer( );
1117 
1118   // Take the viewing display parameters into account
1119   if(imageParam && viewingParamApplied) {
1120 
1121     if (imageParam->HasFilteringValue())      // Apply filtering (sharpen/blur)
1122       if ((status = ApplyFilter( rawPixelsSpace)) != FPX_OK)
1123         return status;
1124 
1125     if (imageParam->HasColorTwist()) {        // Apply colortwist to 'pixels'
1126       PColorTwist colorTwist;
1127 
1128       imageParam->GetColorTwist( &colorTwist);
1129 
1130       switch (pixelsSpace) {
1131         case  SPACE_32_BITS_RGB:
1132         case  SPACE_32_BITS_ARGB:
1133         case  SPACE_32_BITS_RGBA: {
1134           PColorTwist RGBtorgb (RGB8_to_rgb);
1135           PColorTwist rgbtoycc (rgb_to_ycc);
1136           PColorTwist ycctorgb (ycc_to_rgb);
1137           PColorTwist rgbtoRGB (rgb_to_RGB8);
1138 
1139           colorTwist = rgbtoRGB * ycctorgb * colorTwist * rgbtoycc * RGBtorgb;
1140           break;  }
1141         case  SPACE_32_BITS_YCC:
1142         case  SPACE_32_BITS_M:
1143         case  SPACE_32_BITS_MA: {
1144           PColorTwist YCC8toycc (YCC8_to_ycc);
1145           PColorTwist ycctoYCC8 (ycc_to_YCC8);
1146 
1147           colorTwist =  ycctoYCC8 * colorTwist * YCC8toycc;
1148 
1149           // CHG_MONO_TINT - FlashPix spec states that monochrome images are tinted using
1150           //  the colorTwist matrix. To preserve color information, if the 'used' space
1151           //  (i.e., the space used by the calling applic) has color, then we will set the
1152           //  'pixelSpace' (the tile buffer after color transforms) to YCC. The matrix we
1153           //  just created will give YCC output. There is a final colorspace conversion
1154           //  at the end of this section that will do RGB->YCC conversion if necessary.
1155           if ((used != SPACE_32_BITS_M) && (used != SPACE_32_BITS_MA)) {
1156             if (pixelsSpace == SPACE_32_BITS_M)
1157               pixelsSpace = SPACE_32_BITS_YCC;
1158             else if (pixelsSpace == SPACE_32_BITS_MA)
1159               pixelsSpace = SPACE_32_BITS_YCCA;
1160           }
1161           break; }
1162         case  SPACE_32_BITS_YCCA:
1163         case  SPACE_32_BITS_AYCC: {
1164           PColorTwist YCC8toycc (YCC8_to_ycc);
1165           PColorTwist ycctoYCC8 (ycc_to_YCC8);
1166 
1167           colorTwist =  ycctoYCC8 * colorTwist * YCC8toycc;
1168           colorTwist.UseAlphaChannel();
1169           break; }
1170       default:
1171         {
1172         }
1173       }
1174       colorTwist.ApplyToPixelBuffer( pixels, pixelsSpace, width * height);
1175     }
1176 
1177     if (imageParam->HasContrastValue()) {         // Apply contrast to 'pixels'
1178       float   contrastValue;
1179 
1180       // Since contrast is applied in RGB space, if the pixels are currently in a YCC space
1181       //  but are to be returned in RGB or monochrome space, then apply that conversion
1182       //  now.
1183       if ((pixelsSpace == SPACE_32_BITS_YCC)
1184       || (pixelsSpace == SPACE_32_BITS_YCCA)
1185       || (pixelsSpace == SPACE_32_BITS_AYCC))
1186         if (!((used == SPACE_32_BITS_YCC)
1187           ||  (used == SPACE_32_BITS_YCCA)
1188           ||  (used == SPACE_32_BITS_AYCC))) {
1189             // If need be, preserve the opacity component for clippint the
1190             //  contrast-adjusted component(s)
1191             FPXBaselineColorSpace tmpPixelSpace = used;
1192 
1193           if ((pixelsSpace == SPACE_32_BITS_YCCA)
1194           || (pixelsSpace == SPACE_32_BITS_AYCC))
1195             if (used == SPACE_32_BITS_RGB)
1196               tmpPixelSpace = SPACE_32_BITS_ARGB;
1197 
1198           ConvertPixelBuffer((unsigned char*)(pixels), width * height,
1199                   pixelsSpace, tmpPixelSpace);
1200           pixelsSpace = tmpPixelSpace;
1201         }
1202       imageParam->GetContrast( &contrastValue);
1203       Contrast( contrastValue, pixelsSpace, pixels, width * height);
1204     }
1205   }
1206 
1207   // Do the color conversion
1208   if (pixelsSpace != used) {
1209     ConvertPixelBuffer((unsigned char*)(pixels), width * height,pixelsSpace, used);
1210     pixelsSpace = used;
1211   }
1212 
1213   if (wasLocked == false)
1214     UnLock();
1215   pixelsStale = false;
1216 
1217 
1218 // VISU2 "Read tile %d of the image %s\n", identifier, fatherSubImage->fatherFile->fileName.nom.Texte() FIN
1219 
1220   return (FPX_OK);
1221 }
1222 
1223 
1224 
1225 // --------------------------------------------------------------------------
1226 // This method makes a version of the raw pixels which is padded on all four
1227 //  sides by 'padLen'. If there are neighboring tiles, then the pad data is
1228 //  obtained from them. If this is an edge tile, then the padding is made by
1229 //  pixel replication (as per the FlashPix specification).
1230 // NOTE: This code assumes that 'padLen' is never greater than the width of a
1231 //  full tile (which is currently 64x64 Pixels). RIght & bottom edge conditions
1232 //  are handled properly for neighboring tiles which are not full-size.
1233 //
makePaddedRawPixels(long padLen,Pixel ** paddedRawPixels)1234 FPXStatus PTileFlashPix::makePaddedRawPixels (long padLen, Pixel **paddedRawPixels)
1235 {
1236   long      x, y;
1237   Pixel     *srcP, *dstP;
1238   long      srcStride,          // Source Pixel pointer increment
1239           padWidth,         // Width (in Pixels) of padded tile being filled
1240           srcCnt,           // # of rows or cols source tile can contribute
1241           numTiles,         // Number of tiles in this resolution
1242           neighborID;         // (zero-based) identifier of a tile
1243   PTileFlashPix *tileArr,         // Tile list for this resolution
1244           *srcTile;         // Neighboring tile for padding source
1245   Boolean     wasLocked;
1246   FPXStatus   status = FPX_OK;
1247 
1248   wasLocked = IsLocked();           // Remember if this tile was initially locked
1249   Lock();                   // Lock this tile so it's 'rawPixels' memory
1250                         //  does not get re-allocated
1251 
1252   FastAllocArray( *paddedRawPixels, Pixel, (width + (2 * padLen)) * (height + (2 * padLen)));
1253   if (*paddedRawPixels == NULL) {
1254     status = FPX_MEMORY_ALLOCATION_FAILED;
1255     goto RETURN;
1256   }
1257 
1258   padWidth = width + (2L * padLen);
1259   tileArr  = (PTileFlashPix*)fatherSubImage->tiles;
1260   numTiles = fatherSubImage->nbTilesW * fatherSubImage->nbTilesH;
1261 
1262 
1263   // Surrounding tile positions for notes:  |0|1|2|
1264   //                      |3|x|4|
1265   //                      |5|6|7|
1266   //  where 'x' is the position of the tile to be padded.
1267 
1268   // ------------------  Do top pad fill -- Position [1]
1269   neighborID = identifier - fatherSubImage->nbTilesW;
1270   if( neighborID >= 0) {            // If there is a neighboring tile above
1271 
1272     srcTile = tileArr + neighborID;     // Get it's address
1273     if( srcTile == NULL)          // If the tile doesn't exist
1274       return FPX_ERROR;         //  then we're in big trouble
1275     srcTile->TouchRawPixelsBuffer( );   // Set access time stamp
1276     srcP = srcTile->rawPixels;        // Get the address of it's raw pixels
1277 
1278     if( srcP == NULL) {           // If there aren't any raw pixels in memory
1279       if( (status = srcTile->ReadRawPixels()) != FPX_OK)  // Read-in neighbor tile's raw pixels
1280         return status;
1281       srcP = srcTile->rawPixels;
1282     }
1283     // Calc where the desired data is located in the source tile and set the read increment
1284     //  to be a full line of pixels
1285     srcP += ((srcTile->height - padLen) * srcTile->width);
1286     srcStride = srcTile->width;
1287   }
1288   else {                    // Else there is no neighbor tile above,
1289     srcP = rawPixels;           //  so just point to the top line of the target tile
1290     srcStride = 0;              //  and set increment to 0 se we keep copying the
1291   }                     //  same line over and over to form padding
1292 
1293   dstP = *paddedRawPixels + padLen;     // Skip over left pad area of destination buffer
1294   for( y = 0; y < padLen; y++) {        //  and copy data into it
1295     memcpy( dstP, srcP, width * sizeof(Pixel) );
1296     srcP += srcStride;
1297     dstP += padWidth;
1298   }
1299 
1300   // ------------------  Copy source into center of padded dest -- Position [x]
1301   srcP = rawPixels;             // Copy from the stored raw pixels
1302   srcStride = width;
1303   dstP = *paddedRawPixels + (padWidth * padLen) + padLen;
1304   for( y = 0; y < height; y++) {
1305     memcpy( dstP, srcP, width * sizeof(Pixel) );
1306     srcP += srcStride;
1307     dstP += padWidth;
1308   }
1309 
1310   // ------------------  Do bottom pad fill -- Position [6]
1311   dstP = *paddedRawPixels + ((height + padLen) * padWidth) + padLen;
1312   neighborID = identifier + fatherSubImage->nbTilesW;
1313 
1314   if( neighborID < numTiles) {        // If there is a neighboring tile below
1315 
1316     srcTile = tileArr + neighborID;     // Get it's address
1317     if( srcTile == NULL)          // If the tile doesn't exist
1318       return FPX_ERROR;         //  then we're in big trouble
1319     srcTile->TouchRawPixelsBuffer( );   // Set access time stamp
1320     srcP = srcTile->rawPixels;        // Get the address of it's raw pixels
1321 
1322     if( srcP == NULL) {           // If there aren't any raw pixels in memory
1323       if( (status = srcTile->ReadRawPixels()) != FPX_OK)  // Read-in neighbor tile's raw pixels
1324         return status;
1325       srcP = srcTile->rawPixels;
1326     }
1327     srcStride = srcTile->width;
1328     srcCnt = srcTile->height;
1329   }
1330   else {                    // No tile below. Replicate last line of tile.
1331     srcP = dstP - padWidth;         // Back one line's length from destination Pixel
1332     srcStride = 0;              // Never really used for this case
1333     srcCnt = 0;               // Set to 0 so that srcP does not get incremented
1334   }
1335 
1336   for( y = 0; y < padLen; y++) {
1337     memcpy( dstP, srcP, width * sizeof(Pixel) );
1338     dstP += padWidth;
1339     srcCnt--;
1340     if( srcCnt > 0)
1341       srcP += srcStride;
1342   }
1343 
1344 
1345   // ------------------  Start left fills
1346   if( (identifier % fatherSubImage->nbTilesW) == 0) {
1347     // -------------  Do left fills -- Positions [0], [3] and [5]
1348     // The tile being padded is at the left edge, so all we have to do is to copy/extend
1349     //  the leftmost pixels that have already been loaded into the padded buffer to fill
1350     //  the pad area along the left side.
1351     dstP = *paddedRawPixels;              // 1st pixel in buffer
1352     for (y = 0; y < height + (2L * padLen); y++) {
1353       srcP = dstP + padLen;             // 1st pixel in line copied from src
1354       for (x = 0; x < padLen; x++)
1355         *dstP++ = *srcP;
1356       dstP += (padWidth - padLen);          // 1st pixel in line
1357     }
1358   }
1359   else {
1360     // -------------- Do left-top fill  -- Position [0]
1361     dstP = *paddedRawPixels;          // Start at top-left of destination buffer
1362     neighborID = identifier - fatherSubImage->nbTilesW - 1;
1363     if( neighborID >= 0) {            // If there is a neighboring tile above
1364 
1365       srcTile = tileArr + neighborID;     // Get it's address
1366       if( srcTile == NULL)          // If the tile doesn't exist
1367         return FPX_ERROR;         //  then we're in big trouble
1368       srcTile->TouchRawPixelsBuffer( );   // Set access time stamp
1369       srcP = srcTile->rawPixels;        // Get the address of it's raw pixels
1370 
1371       if( srcP == NULL) {           // If there aren't any raw pixels in memory
1372         if( (status = srcTile->ReadRawPixels()) != FPX_OK)  // Read-in neighbor tile's raw pixels
1373           return status;
1374         srcP = srcTile->rawPixels;
1375       }
1376       // Calc where the desired data is located in the source tile and set the read increment
1377       //  to be a full line of pixels
1378       srcP += ((srcTile->height - padLen) * srcTile->width);
1379       srcP += (srcTile->width - padLen);
1380       srcStride = srcTile->width - padLen;
1381 
1382       // Copy the data over
1383       for( y = 0; y < padLen; y++) {
1384         for (x = 0; x < padLen; x++)
1385           *dstP++ = *srcP++;
1386         srcP += srcStride;
1387         dstP += padWidth - padLen;
1388       }
1389     }
1390     else {
1391       // There is no neighbor tile above and to the left. All we can do is replicate the
1392       //  top-left pixel of the target tile to fill this area.
1393       srcP = rawPixels;           //  so just point to the top line of the target tile
1394       for( y = 0; y < padLen; y++) {
1395         for( x = 0; x < padLen; x++)
1396           *dstP++ = *srcP;
1397         dstP += (padWidth - padLen);
1398       }
1399     }
1400 
1401     // -------------- Do left-middle fill  -- Position [3]
1402     dstP = *paddedRawPixels + (padWidth * padLen);
1403     neighborID = identifier - 1;
1404 
1405     srcTile = tileArr + neighborID;     // Get it's address
1406     if( srcTile == NULL)          // If the tile doesn't exist
1407       return FPX_ERROR;         //  then we're in big trouble
1408     srcTile->TouchRawPixelsBuffer( );   // Set access time stamp
1409     srcP = srcTile->rawPixels;        // Get the address of it's raw pixels
1410 
1411     if( srcP == NULL) {           // If there aren't any raw pixels in memory
1412       if( (status = srcTile->ReadRawPixels()) != FPX_OK)  // Read-in neighbor tile's raw pixels
1413         return status;
1414       srcP = srcTile->rawPixels;
1415     }
1416     // Calc where the desired data is located in the source tile and set the read increment
1417     //  to be a full line of pixels
1418     srcP += (srcTile->width - padLen);
1419     srcStride = srcTile->width - padLen;
1420 
1421     // Copy the data over
1422     for( y = 0; y < height; y++) {
1423       for (x = 0; x < padLen; x++)
1424         *dstP++ = *srcP++;
1425       srcP += srcStride;
1426       dstP += (padWidth - padLen);
1427     }
1428 
1429     // -------------- Do left-bottom fill  -- Position [5]
1430     dstP = *paddedRawPixels + ((height + padLen) * padWidth);
1431     neighborID = identifier + (fatherSubImage->nbTilesW - 1);
1432 
1433     if( neighborID < numTiles) {        // If there is a neighboring tile below
1434 
1435       srcTile = tileArr + neighborID;     // Get it's address
1436       if( srcTile == NULL)          // If the tile doesn't exist
1437         return FPX_ERROR;         //  then we're in big trouble
1438       srcTile->TouchRawPixelsBuffer( );   // Set access time stamp
1439       srcP = srcTile->rawPixels;        // Get the address of it's raw pixels
1440 
1441       if( srcP == NULL) {           // If there aren't any raw pixels in memory
1442         if( (status = srcTile->ReadRawPixels()) != FPX_OK)  // Read-in neighbor tile's raw pixels
1443           return status;
1444         srcP = srcTile->rawPixels;
1445       }
1446       srcP += (srcTile->width - padLen);
1447       srcStride = srcTile->width - padLen;
1448       srcCnt = srcTile->height;
1449       for( y = 0; y < padLen; y++) {
1450         for (x = 0; x < padLen; x++)
1451           *dstP++ = *srcP++;
1452         dstP += (padWidth - padLen);
1453         srcCnt--;
1454         if( srcCnt > 0)           // If there are more lines of src data
1455           srcP += srcStride;        //  advance to next src line
1456         else                // Else
1457           srcP -= padLen;         //  Move back and replicate last line
1458       }
1459     }
1460     else {                    // No tile below. Replicate last pixel
1461       srcP = dstP - padWidth;         // of last line of target
1462       for( y = 0; y < padLen; y++) {
1463         for (x = 0; x < padLen; x++)
1464           *dstP++ = *srcP;
1465         dstP += (padWidth - padLen);
1466       }
1467     }
1468 
1469   }
1470 
1471 
1472   // ------------------  Start right fills
1473   if( ((identifier + 1) % fatherSubImage->nbTilesW) == 0) {
1474     // -------------  Do right fills -- Positions [2], [4] and [7]
1475     // The tile being padded is at the right edge, so all we have to do is to copy/extend
1476     //  the rightmost pixels that have already been loaded into the padded buffer to fill
1477     //  the pad area along the right side.
1478     dstP = *paddedRawPixels + padWidth - padLen;
1479     for (y = 0; y < height + (2L * padLen); y++) {
1480       srcP = dstP - 1;
1481       for (x = 0; x < padLen; x++)
1482         *dstP++ = *srcP;
1483       dstP += (padWidth - padLen);
1484     }
1485   }
1486   else {
1487     // The tiles to the right may not be as wide as 'padLen'. So the next 2 sections will
1488     //  copy whatever piel data is available.  At the end of this section the rightmost
1489     //  pixels in the padded buffer will be replicated, if need be, to fill the remainder.
1490     long  srcLen = padLen;
1491 
1492     // -------------- Do right-top fill  -- Position [2]
1493     dstP = *paddedRawPixels + (padLen + width);
1494     neighborID = identifier - fatherSubImage->nbTilesW + 1;
1495     if( neighborID >= 0) {            // If there is a neighboring tile above
1496 
1497       srcTile = tileArr + neighborID;     // Get it's address
1498       if( srcTile == NULL)          // If the tile doesn't exist
1499         return FPX_ERROR;         //  then we're in big trouble
1500       srcTile->TouchRawPixelsBuffer( );   // Set access time stamp
1501       srcP = srcTile->rawPixels;        // Get the address of it's raw pixels
1502 
1503       if( srcP == NULL) {           // If there aren't any raw pixels in memory
1504         if( (status = srcTile->ReadRawPixels()) != FPX_OK)  // Read-in neighbor tile's raw pixels
1505           return status;
1506         srcP = srcTile->rawPixels;
1507       }
1508 
1509       srcP += ((srcTile->height - padLen) * srcTile->width);
1510       if( srcLen > srcTile->width)
1511         srcLen = srcTile->width;
1512       srcStride = srcTile->width - srcLen;
1513 
1514       // Copy the data over
1515       for( y = 0; y < padLen; y++) {
1516         for( x = 0; x < srcLen; x++)
1517           *dstP++ = *srcP++;
1518         srcP += srcStride;
1519         dstP += (padWidth - srcLen);
1520       }
1521     }
1522     else {
1523       // There is no neighbor tile above and to the right. All we can do is replicate the
1524       //  top-right pixel of the target tile to fill this area.
1525       srcP = rawPixels + (width - 1);     //  Point to last pixel of top source line tile
1526       for( y = 0; y < padLen; y++) {
1527         for( x = 0; x < padLen; x++)
1528           *dstP++ = *srcP;
1529         dstP += (padWidth - padLen);
1530       }
1531     }
1532 
1533     // -------------- Do right-middle fill  -- Position [4]
1534     dstP = *paddedRawPixels + (padLen * padWidth) + width + padLen;
1535     neighborID = identifier + 1;
1536 
1537     srcTile = tileArr + neighborID;       // Get it's address
1538     if( srcTile == NULL)            // If the tile doesn't exist
1539       return FPX_ERROR;           //  then we're in big trouble
1540     srcTile->TouchRawPixelsBuffer( );   // Set access time stamp
1541     srcP = srcTile->rawPixels;          // Get the address of it's raw pixels
1542 
1543     if( srcP == NULL) {             // If there aren't any raw pixels in memory
1544       if( (status = srcTile->ReadRawPixels()) != FPX_OK)  // Read-in neighbor tile's raw pixels
1545         return status;
1546       srcP = srcTile->rawPixels;
1547     }
1548 
1549     if( srcLen > srcTile->width)
1550       srcLen = srcTile->width;
1551     srcStride = srcTile->width - srcLen;
1552 
1553     // Copy the data over
1554     for( y = 0; y < height; y++) {
1555       for( x = 0; x < srcLen; x++)
1556         *dstP++ = *srcP++;
1557       srcP += srcStride;
1558       dstP += (padWidth - srcLen);
1559     }
1560 
1561     // -------------- Do right-bottom fill  -- Position [7]
1562     dstP = *paddedRawPixels + ((height + padLen) * padWidth) + padLen + width;
1563     neighborID = identifier + fatherSubImage->nbTilesW + 1;
1564     if( neighborID < numTiles) {        // If there is a neighboring tile above
1565 
1566       srcTile = tileArr + neighborID;     // Get it's address
1567       if( srcTile == NULL)          // If the tile doesn't exist
1568         return FPX_ERROR;         //  then we're in big trouble
1569       srcTile->TouchRawPixelsBuffer( );   // Set access time stamp
1570       srcP = srcTile->rawPixels;        // Get the address of it's raw pixels
1571 
1572       if( srcP == NULL) {           // If there aren't any raw pixels in memory
1573         if( (status = srcTile->ReadRawPixels()) != FPX_OK)  // Read-in neighbor tile's raw pixels
1574           return status;
1575         srcP = srcTile->rawPixels;
1576       }
1577 
1578       if( srcLen > srcTile->width)
1579         srcLen = srcTile->width;
1580       srcStride = srcTile->width - srcLen;
1581 
1582       // Copy the data over
1583       for( y = 0; y < padLen; y++) {
1584         for( x = 0; x < srcLen; x++)
1585           *dstP++ = *srcP++;
1586         srcP += srcStride;
1587         dstP += (padWidth - srcLen);
1588       }
1589     }
1590     else {
1591       // There is no neighbor tile below and to the right. All we can do is replicate the
1592       //  bottom-right pixel of the target tile to fill this area.
1593       srcP = rawPixels + (width * height)- 1; //  Point to last pixel of last source line tile
1594       for( y = 0; y < padLen; y++) {
1595         for( x = 0; x < srcLen; x++)  // only need to copy 'srcLen' here
1596           *dstP++ = *srcP;
1597         dstP += (padWidth - srcLen);
1598       }
1599     }
1600 
1601     // As promised, if there was insufficient pixels in the tiles on the right, then
1602     //  some replication must be performed to fill the remaining pad area on the right.
1603     if( srcLen != padLen) {
1604       padLen -= srcLen;
1605       dstP = *paddedRawPixels + (padWidth - padLen);
1606       for (y = 0; y < height + (2L * padLen); y++) {
1607         srcP = dstP - 1;
1608         for (x = 0; x < padLen; x++)
1609           *dstP++ = *srcP;
1610         dstP += (padWidth - padLen);
1611       }
1612     }
1613   }
1614 
1615  RETURN:
1616   if (wasLocked == false)
1617     UnLock();
1618   return status;
1619 }
1620 
1621 
1622 // ------------------------------------------------------------------------------
1623 // Read jpeg header from image content property set
1624 //
ReadHeader(PFlashPixFile * filePtr,unsigned char ** pJpegHeader,unsigned long * headerSize)1625 Boolean PTileFlashPix::ReadHeader(PFlashPixFile* filePtr, unsigned char** pJpegHeader,
1626       unsigned long* headerSize )
1627 {
1628   OLEProperty*  aProp;
1629   OLEBlob     jpegTable;
1630 
1631 
1632   // Get jpeg table index
1633   unsigned char JPEGtableSelector = GET_JPEGTablesIndex(compressionSubtype);
1634 
1635   // Convert the index into property ID for jpeg table
1636   unsigned long   PID_jpegTableIndex = PID_JPEGTables(JPEGtableSelector);
1637 
1638   // If jpeg table index is 0, then the header is stored with tile data
1639   if ( !JPEGtableSelector )
1640     return TRUE;
1641 
1642   // If no jpeg table for this resolution, read it from image content property set
1643   if ( !*pJpegHeader ) {
1644 
1645     // Find the jpeg table specified by JPEGtableSelector in the image content property set
1646     if (filePtr->GetImageContentProperty (PID_jpegTableIndex, &aProp)) {
1647 
1648       // Get the jpeg table
1649       if ( !(jpegTable = (BLOB *)(*aProp)) )
1650         return FALSE;
1651 
1652       *headerSize = jpegTable.ReadVT_VECTOR(pJpegHeader);
1653 
1654       // Set the table index and flag
1655       ((PResolutionFlashPix*)fatherSubImage)->compressTableGroup = JPEGtableSelector;
1656     }
1657     else
1658       return FALSE;
1659   }
1660   else
1661     // If table index is different from this resolution, read it from image content property set
1662     if ( JPEGtableSelector != ((PResolutionFlashPix*)fatherSubImage)->compressTableGroup ) {
1663 
1664       // Delete header if it already exists
1665       delete *pJpegHeader;
1666 
1667       // Find the jpeg table specified by JPEGtableSelector in the image content property set
1668 
1669       if (filePtr->GetImageContentProperty (PID_jpegTableIndex, &aProp)) {
1670 
1671         // Get the jpeg table
1672         if ( !(jpegTable = (BLOB *)(*aProp)) )
1673           return FALSE;
1674 
1675         *headerSize = jpegTable.ReadVT_VECTOR(pJpegHeader);
1676       }
1677       else
1678         return FALSE;
1679 
1680       // Set the jpeg table index for this resolution
1681       ((PResolutionFlashPix*)fatherSubImage)->compressTableGroup = JPEGtableSelector;
1682     }
1683   return TRUE;
1684 }
1685 
1686 
1687 // ------------------------------------------------------------------------------
1688 // Write jpeg table to image content property set.
1689 //
WriteHeader(PFlashPixFile * filePtr,unsigned char * jpegHeader,unsigned long headerSize)1690 Boolean PTileFlashPix::WriteHeader(PFlashPixFile* filePtr,
1691                                    unsigned char* jpegHeader,
1692                                    unsigned long headerSize )
1693 {
1694   OLEProperty*  aProp;
1695   OLEBlob     jpegTable;
1696 
1697   // Get jpeg table index.
1698   // compressTableGroup is type unsigned char so it has a range of 0 - 255
1699   unsigned char JPEGtableSelector = ((PResolutionFlashPix*)fatherSubImage)->compressTableGroup;
1700 
1701   // If jpeg table index is 0, then the header is stored with tile data
1702   if ( !JPEGtableSelector )
1703     return true;
1704 
1705   // Convert the index into property ID for jpeg table
1706   unsigned long   PID_jpegTableIndex = PID_JPEGTables(JPEGtableSelector);
1707 
1708   // If jpeg table for this index doesn't exist, write it
1709   if ( !filePtr->GetImageContentProperty (PID_jpegTableIndex, &aProp) ) {
1710 
1711     // Copy the stream in jpeg table into blob
1712     jpegTable.WriteVT_VECTOR(jpegHeader, headerSize);
1713 
1714     // Save the jpeg table into image content property set
1715     if (filePtr->SetImageContentProperty (PID_jpegTableIndex, TYP_JPEGTables, &aProp)) {
1716       *aProp = jpegTable.GetBlob();
1717     }
1718     else
1719       return false;
1720 
1721     // Save the maximum jpeg table index
1722     if ( !filePtr->GetImageContentProperty (PID_MaxJPEGTables, &aProp) )
1723       if (filePtr->SetImageContentProperty (PID_MaxJPEGTables, TYP_MaxJPEGTables, &aProp))
1724         *aProp = (int32_t)JPEGtableSelector;
1725       else
1726         return false;
1727     else {
1728       int32_t tempMaxIndex = (int32_t)(*aProp);
1729       tempMaxIndex = (tempMaxIndex < JPEGtableSelector) ? JPEGtableSelector : tempMaxIndex;
1730       *aProp = (int32_t)tempMaxIndex;
1731     }
1732     filePtr->Commit();
1733   }
1734   return true;
1735 }
1736 
1737 
1738 // ------------------------------------------------------------------------------
1739 // Does contrast correction
1740 // Note that if there is an opacity component, this routine assumes that the
1741 //  other components have been pre-multiplied.  In this case, the output of the
1742 //  non-opacity component(s) is clamped to opacity. A more proper method would
1743 //  be to reconstruct the original, non-premultiplied component(s) by multiplying
1744 //  by the reciprocal of opacity, then performing the contrast adjustment and
1745 //  then re-applying the opacity. This is not done here dur to the additional
1746 //  multiplies that are required.
1747 // Note that contrast is not (and should not be) applied to the opacity channel.
1748 //
Contrast(double k,FPXBaselineColorSpace colorSpace,Pixel * pixels,long count)1749 FPXStatus PTileFlashPix::Contrast (
1750       double    k,            // Contrast factor (e.g. 1.2 = +20%contrast)
1751       FPXBaselineColorSpace colorSpace,   // Which channels are which?
1752       Pixel     *pixels,          // array of 32-bit pixels
1753       long    count )         // No. of pixels in array
1754 {
1755   unsigned char   *lookup;
1756   unsigned char   *red_pixel, *green_pixel, *blue_pixel, *opac_pixel;
1757   FPXBaselineColorSpace tempSpace;
1758   Boolean     monochrome;
1759 
1760 
1761   // Set up lookup table, if need be
1762   if (gContrastVal != k) {
1763     double  p = (float)0.43;    // Center of contrast spread, per FPX specification
1764     double  contrasted;
1765     int   i;
1766 
1767     for (i = 0; i < 256; i++) {
1768       contrasted = p * pow( ((double)i / 256.0) / p, k);
1769       if (contrasted >= 1.0)
1770         contrasted = (float)0.999;    // Truncate to 0... 255
1771       gContrastLut[i] = (unsigned char)(contrasted * 256.0);
1772     }
1773     gContrastVal = (Boolean)k;
1774   }
1775   lookup = gContrastLut;
1776 
1777   // Determine channel #'s in color space
1778   // Alpha channel is not contrasted, but if present it's address is loaded for
1779   //  use in clipping later.
1780   // Note that the 'red_pixel' pointer is used for a monochrome component
1781   opac_pixel = 0;
1782   tempSpace = colorSpace;
1783   switch(pixelsSpace) {
1784     case  SPACE_32_BITS_ARGB: // The 24 bits are stored in the LSB part of the long
1785       opac_pixel  = ((unsigned char*)pixels);
1786     case  SPACE_32_BITS_RGB: {
1787       red_pixel   = ((unsigned char*)pixels) + 1;
1788       green_pixel = ((unsigned char*)pixels) + 2;
1789       blue_pixel  = ((unsigned char*)pixels) + 3;
1790       monochrome  = false;
1791       } break;
1792 
1793     case  SPACE_32_BITS_RGBA: {
1794       red_pixel   = ((unsigned char*)pixels);
1795       green_pixel = ((unsigned char*)pixels) + 1;
1796       blue_pixel  = ((unsigned char*)pixels) + 2;
1797       opac_pixel  = ((unsigned char*)pixels) + 3;
1798       monochrome  = false;
1799       } break;
1800 
1801     case  SPACE_32_BITS_AM:
1802       opac_pixel = ((unsigned char*)pixels) + 2;
1803     case  SPACE_32_BITS_M: {
1804       red_pixel  = ((unsigned char*)pixels) + 3;
1805       monochrome = true;
1806       } break;
1807 
1808     case  SPACE_32_BITS_MA: {
1809       red_pixel  = ((unsigned char*)pixels) + 2;
1810       opac_pixel = ((unsigned char*)pixels) + 3;
1811       monochrome = true;
1812       } break;
1813 
1814     case  SPACE_32_BITS_AYCC:
1815       opac_pixel  = ((unsigned char*)pixels);
1816     case  SPACE_32_BITS_YCC: {
1817       tempSpace = SPACE_32_BITS_ARGB;
1818       red_pixel   = ((unsigned char*)pixels) + 1;
1819       green_pixel = ((unsigned char*)pixels) + 2;
1820       blue_pixel  = ((unsigned char*)pixels) + 3;
1821       monochrome  = false;
1822       } break;
1823 
1824     case  SPACE_32_BITS_YCCA: {
1825       tempSpace = SPACE_32_BITS_RGBA;
1826       red_pixel   = ((unsigned char*)pixels);
1827       green_pixel = ((unsigned char*)pixels) + 1;
1828       blue_pixel  = ((unsigned char*)pixels) + 2;
1829       opac_pixel  = ((unsigned char*)pixels) + 3;
1830       monochrome  = false;
1831       } break;
1832 
1833     default:
1834       return FPX_COLOR_CONVERSION_ERROR;  // Invalid color space
1835     }
1836 
1837   // If need be, convert from 'colorSpace' into an RGB 'tempSpace'
1838   if (tempSpace != colorSpace)
1839     ConvertPixelBuffer((unsigned char*)(pixels), width * height, colorSpace, tempSpace);
1840 
1841   if (monochrome) {
1842     if (opac_pixel) {             // If there is an opacity
1843       while (count--) {           //  component, then the new
1844         *red_pixel = lookup[*red_pixel];  //  mono value must be clipped
1845         if (*red_pixel > *opac_pixel)
1846           *red_pixel = *opac_pixel;
1847         red_pixel  += 4;
1848         opac_pixel += 4;
1849       }
1850     } else {
1851       while (count--) {           //  If there is no opacity
1852         *red_pixel = lookup[*red_pixel];  //  component, then just blast
1853         red_pixel += 4;           //  through
1854       }
1855     }
1856   } else { // RGB
1857     if (opac_pixel) {             // If there is an opacity
1858       while (count--) {           //  values must be clipped to it
1859         *red_pixel   = lookup[  *red_pixel];
1860         if (*red_pixel > *opac_pixel)
1861           *red_pixel = *opac_pixel;
1862         *green_pixel = lookup[*green_pixel];
1863         if (*green_pixel > *opac_pixel)
1864           *green_pixel = *opac_pixel;
1865         *blue_pixel  = lookup[ *blue_pixel];
1866         if (*blue_pixel > *opac_pixel)
1867           *blue_pixel = *opac_pixel;
1868 
1869         red_pixel   += 4;
1870         green_pixel += 4;
1871         blue_pixel  += 4;
1872         opac_pixel  += 4;
1873       }
1874     } else {
1875       while (count--) {           // If there is no opacity
1876         *red_pixel   = lookup[  *red_pixel];//  no clipping is required
1877         *green_pixel = lookup[*green_pixel];
1878         *blue_pixel  = lookup[ *blue_pixel];
1879 
1880         red_pixel   += 4;
1881         green_pixel += 4;
1882         blue_pixel  += 4;
1883       }
1884     }
1885   }
1886 
1887   // If need be, convert from an RGB 'tempSpace' back into 'colorSpace'
1888   if (tempSpace != colorSpace)
1889     ConvertPixelBuffer((unsigned char*)(pixels), width * height, tempSpace, colorSpace);
1890 
1891   return FPX_OK;
1892 }
1893 
1894 
1895 //  ----------------------------------------------------------------------------
1896 //  External Functions
1897 //  ----------------------------------------------------------------------------
1898 
1899 
1900 //  - EOF ----------------------------------------------------------------------
1901 
1902