Home | History | Annotate | Download | only in coders
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %                        PPPP    AAA   L      M   M                           %
      7 %                        P   P  A   A  L      MM MM                           %
      8 %                        PPPP   AAAAA  L      M M M                           %
      9 %                        P      A   A  L      M   M                           %
     10 %                        P      A   A  LLLLL  M   M                           %
     11 %                                                                             %
     12 %                                                                             %
     13 %                          Read/Write Palm Pixmap.                            %
     14 %                                                                             %
     15 %                                                                             %
     16 %                              Software Design                                %
     17 %                            Christopher R. Hawks                             %
     18 %                               December 2001                                 %
     19 %                                                                             %
     20 %  Copyright 1999-2004 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    http://www.imagemagick.org/script/license.php                            %
     27 %                                                                             %
     28 %  Unless required by applicable law or agreed to in writing, software        %
     29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
     30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
     31 %  See the License for the specific language governing permissions and        %
     32 %  limitations under the License.                                             %
     33 %                                                                             %
     34 %                                                                             %
     35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     36 %
     37 %  Based on pnmtopalm by Bill Janssen and ppmtobmp by Ian Goldberg.
     38 %
     39 */
     40 
     41 /*
     43   Include declarations.
     44 */
     45 #include "MagickCore/studio.h"
     46 #include "MagickCore/attribute.h"
     47 #include "MagickCore/blob.h"
     48 #include "MagickCore/blob-private.h"
     49 #include "MagickCore/cache.h"
     50 #include "MagickCore/color.h"
     51 #include "MagickCore/colormap.h"
     52 #include "MagickCore/colormap-private.h"
     53 #include "MagickCore/color-private.h"
     54 #include "MagickCore/colorspace.h"
     55 #include "MagickCore/colorspace-private.h"
     56 #include "MagickCore/constitute.h"
     57 #include "MagickCore/exception.h"
     58 #include "MagickCore/histogram.h"
     59 #include "MagickCore/image.h"
     60 #include "MagickCore/image-private.h"
     61 #include "MagickCore/list.h"
     62 #include "MagickCore/magick.h"
     63 #include "MagickCore/memory_.h"
     64 #include "MagickCore/monitor.h"
     65 #include "MagickCore/monitor-private.h"
     66 #include "MagickCore/paint.h"
     67 #include "MagickCore/pixel-accessor.h"
     68 #include "MagickCore/property.h"
     69 #include "MagickCore/quantize.h"
     70 #include "MagickCore/quantum-private.h"
     71 #include "MagickCore/static.h"
     72 #include "MagickCore/string_.h"
     73 #include "MagickCore/module.h"
     74 #include "MagickCore/utility.h"
     75 
     76 /*
     78   Define declarations.
     79 */
     80 #define PALM_IS_COMPRESSED_FLAG  0x8000
     81 #define PALM_HAS_COLORMAP_FLAG  0x4000
     82 #define PALM_HAS_FOUR_BYTE_FIELD  0x0200
     83 #define PALM_HAS_TRANSPARENCY_FLAG  0x2000
     84 #define PALM_IS_INDIRECT  0x1000
     85 #define PALM_IS_FOR_SCREEN  0x0800
     86 #define PALM_IS_DIRECT_COLOR  0x0400
     87 #define PALM_COMPRESSION_SCANLINE  0x00
     88 #define PALM_COMPRESSION_RLE  0x01
     89 #define PALM_COMPRESSION_NONE  0xFF
     90 
     91 /*
     93  The 256 color system palette for Palm Computing Devices.
     94 */
     95 static const unsigned char
     96   PalmPalette[256][3] =
     97   {
     98     {255, 255,255}, {255, 204,255}, {255, 153,255}, {255, 102,255},
     99     {255,  51,255}, {255,   0,255}, {255, 255,204}, {255, 204,204},
    100     {255, 153,204}, {255, 102,204}, {255,  51,204}, {255,   0,204},
    101     {255, 255,153}, {255, 204,153}, {255, 153,153}, {255, 102,153},
    102     {255,  51,153}, {255,   0,153}, {204, 255,255}, {204, 204,255},
    103     {204, 153,255}, {204, 102,255}, {204,  51,255}, {204,   0,255},
    104     {204, 255,204}, {204, 204,204}, {204, 153,204}, {204, 102,204},
    105     {204,  51,204}, {204,   0,204}, {204, 255,153}, {204, 204,153},
    106     {204, 153,153}, {204, 102,153}, {204,  51,153}, {204,   0,153},
    107     {153, 255,255}, {153, 204,255}, {153, 153,255}, {153, 102,255},
    108     {153,  51,255}, {153,   0,255}, {153, 255,204}, {153, 204,204},
    109     {153, 153,204}, {153, 102,204}, {153,  51,204}, {153,   0,204},
    110     {153, 255,153}, {153, 204,153}, {153, 153,153}, {153, 102,153},
    111     {153,  51,153}, {153,   0,153}, {102, 255,255}, {102, 204,255},
    112     {102, 153,255}, {102, 102,255}, {102,  51,255}, {102,   0,255},
    113     {102, 255,204}, {102, 204,204}, {102, 153,204}, {102, 102,204},
    114     {102,  51,204}, {102,   0,204}, {102, 255,153}, {102, 204,153},
    115     {102, 153,153}, {102, 102,153}, {102,  51,153}, {102,   0,153},
    116     { 51, 255,255}, { 51, 204,255}, { 51, 153,255}, { 51, 102,255},
    117     { 51,  51,255}, { 51,   0,255}, { 51, 255,204}, { 51, 204,204},
    118     { 51, 153,204}, { 51, 102,204}, { 51,  51,204}, { 51,   0,204},
    119     { 51, 255,153}, { 51, 204,153}, { 51, 153,153}, { 51, 102,153},
    120     { 51,  51,153}, { 51,   0,153}, {  0, 255,255}, {  0, 204,255},
    121     {  0, 153,255}, {  0, 102,255}, {  0,  51,255}, {  0,   0,255},
    122     {  0, 255,204}, {  0, 204,204}, {  0, 153,204}, {  0, 102,204},
    123     {  0,  51,204}, {  0,   0,204}, {  0, 255,153}, {  0, 204,153},
    124     {  0, 153,153}, {  0, 102,153}, {  0,  51,153}, {  0,   0,153},
    125     {255, 255,102}, {255, 204,102}, {255, 153,102}, {255, 102,102},
    126     {255,  51,102}, {255,   0,102}, {255, 255, 51}, {255, 204, 51},
    127     {255, 153, 51}, {255, 102, 51}, {255,  51, 51}, {255,   0, 51},
    128     {255, 255,  0}, {255, 204,  0}, {255, 153,  0}, {255, 102,  0},
    129     {255,  51,  0}, {255,   0,  0}, {204, 255,102}, {204, 204,102},
    130     {204, 153,102}, {204, 102,102}, {204,  51,102}, {204,   0,102},
    131     {204, 255, 51}, {204, 204, 51}, {204, 153, 51}, {204, 102, 51},
    132     {204,  51, 51}, {204,   0, 51}, {204, 255,  0}, {204, 204,  0},
    133     {204, 153,  0}, {204, 102,  0}, {204,  51,  0}, {204,   0,  0},
    134     {153, 255,102}, {153, 204,102}, {153, 153,102}, {153, 102,102},
    135     {153,  51,102}, {153,   0,102}, {153, 255, 51}, {153, 204, 51},
    136     {153, 153, 51}, {153, 102, 51}, {153,  51, 51}, {153,   0, 51},
    137     {153, 255,  0}, {153, 204,  0}, {153, 153,  0}, {153, 102,  0},
    138     {153,  51,  0}, {153,   0,  0}, {102, 255,102}, {102, 204,102},
    139     {102, 153,102}, {102, 102,102}, {102,  51,102}, {102,   0,102},
    140     {102, 255, 51}, {102, 204, 51}, {102, 153, 51}, {102, 102, 51},
    141     {102,  51, 51}, {102,   0, 51}, {102, 255,  0}, {102, 204,  0},
    142     {102, 153,  0}, {102, 102,  0}, {102,  51,  0}, {102,   0,  0},
    143     { 51, 255,102}, { 51, 204,102}, { 51, 153,102}, { 51, 102,102},
    144     { 51,  51,102}, { 51,   0,102}, { 51, 255, 51}, { 51, 204, 51},
    145     { 51, 153, 51}, { 51, 102, 51}, { 51,  51, 51}, { 51,   0, 51},
    146     { 51, 255,  0}, { 51, 204,  0}, { 51, 153,  0}, { 51, 102,  0},
    147     { 51,  51,  0}, { 51,   0,  0}, {  0, 255,102}, {  0, 204,102},
    148     {  0, 153,102}, {  0, 102,102}, {  0,  51,102}, {  0,   0,102},
    149     {  0, 255, 51}, {  0, 204, 51}, {  0, 153, 51}, {  0, 102, 51},
    150     {  0,  51, 51}, {  0,   0, 51}, {  0, 255,  0}, {  0, 204,  0},
    151     {  0, 153,  0}, {  0, 102,  0}, {  0,  51,  0}, { 17,  17, 17},
    152     { 34,  34, 34}, { 68,  68, 68}, { 85,  85, 85}, {119, 119,119},
    153     {136, 136,136}, {170, 170,170}, {187, 187,187}, {221, 221,221},
    154     {238, 238,238}, {192, 192,192}, {128,   0,  0}, {128,   0,128},
    155     {  0, 128,  0}, {  0, 128,128}, {  0,   0,  0}, {  0,   0,  0},
    156     {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0},
    157     {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0},
    158     {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0},
    159     {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0},
    160     {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0},
    161     {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0}, {  0,   0,  0}
    162   };
    163 
    164 /*
    166   Forward declarations.
    167 */
    168 static MagickBooleanType
    169   WritePALMImage(const ImageInfo *,Image *,ExceptionInfo *);
    170 
    171 /*
    173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    174 %                                                                             %
    175 %                                                                             %
    176 %                                                                             %
    177 %   F i n d C o l o r                                                         %
    178 %                                                                             %
    179 %                                                                             %
    180 %                                                                             %
    181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    182 %
    183 %  FindColor() returns the index of the matching entry from PalmPalette for a
    184 %  given PixelInfo.
    185 %
    186 %  The format of the FindColor method is:
    187 %
    188 %      int FindColor(PixelInfo *pixel)
    189 %
    190 %  A description of each parameter follows:
    191 %
    192 %    o int: the index of the matching color or -1 if not found/
    193 %
    194 %    o pixel: a pointer to the PixelInfo to be matched.
    195 %
    196 */
    197 static ssize_t FindColor(PixelInfo *packet)
    198 {
    199   register ssize_t
    200     i;
    201 
    202   for (i=0; i < 256; i++)
    203     if (ScaleQuantumToChar(ClampToQuantum(packet->red)) == PalmPalette[i][0] &&
    204         ScaleQuantumToChar(ClampToQuantum(packet->green)) == PalmPalette[i][1] &&
    205         ScaleQuantumToChar(ClampToQuantum(packet->blue)) == PalmPalette[i][2])
    206       return(i);
    207   return(-1);
    208 }
    209 
    210 /*
    212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    213 %                                                                             %
    214 %                                                                             %
    215 %                                                                             %
    216 %   R e a d P A L M I m a g e                                                 %
    217 %                                                                             %
    218 %                                                                             %
    219 %                                                                             %
    220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    221 %
    222 %  ReadPALMImage() reads an image of raw bites in LSB order and returns it.  It
    223 %  allocates the memory necessary for the new Image structure and returns a
    224 %  pointer to the new image.
    225 %
    226 %  The format of the ReadPALMImage method is:
    227 %
    228 %      Image *ReadPALMImage(const ImageInfo *image_info,
    229 %        ExceptionInfo *exception)
    230 %
    231 %  A description of each parameter follows:
    232 %
    233 %    o image_info: Specifies a pointer to an ImageInfo structure.
    234 %
    235 %    o exception: return any errors or warnings in this structure.
    236 %
    237 */
    238 static Image *ReadPALMImage(const ImageInfo *image_info,
    239   ExceptionInfo *exception)
    240 {
    241   Image
    242     *image;
    243 
    244   MagickBooleanType
    245     status;
    246 
    247   MagickOffsetType
    248     totalOffset,
    249     seekNextDepth;
    250 
    251   PixelInfo
    252     transpix;
    253 
    254   Quantum
    255     index;
    256 
    257   register ssize_t
    258     i,
    259     x;
    260 
    261   register Quantum
    262     *q;
    263 
    264   size_t
    265     bytes_per_row,
    266     flags,
    267     bits_per_pixel,
    268     version,
    269     nextDepthOffset,
    270     transparentIndex,
    271     compressionType,
    272     byte,
    273     mask,
    274     redbits,
    275     greenbits,
    276     bluebits,
    277     one,
    278     pad,
    279     size,
    280     bit;
    281 
    282   ssize_t
    283     count,
    284     y;
    285 
    286   unsigned char
    287     *lastrow,
    288     *one_row,
    289     *ptr;
    290 
    291   unsigned short
    292     color16;
    293 
    294   /*
    295     Open image file.
    296   */
    297   assert(image_info != (const ImageInfo *) NULL);
    298   assert(image_info->signature == MagickCoreSignature);
    299   if (image_info->debug != MagickFalse)
    300     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
    301       image_info->filename);
    302   assert(exception != (ExceptionInfo *) NULL);
    303   assert(exception->signature == MagickCoreSignature);
    304   image=AcquireImage(image_info,exception);
    305   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    306   if (status == MagickFalse)
    307     {
    308       (void) DestroyImageList(image);
    309       return((Image *) NULL);
    310     }
    311   totalOffset=0;
    312   do
    313   {
    314     image->columns=ReadBlobMSBShort(image);
    315     image->rows=ReadBlobMSBShort(image);
    316     if (EOFBlob(image) != MagickFalse)
    317       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    318     if ((image->columns == 0) || (image->rows == 0))
    319       ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
    320     status=SetImageExtent(image,image->columns,image->rows,exception);
    321     if (status == MagickFalse)
    322       return(DestroyImageList(image));
    323     bytes_per_row=ReadBlobMSBShort(image);
    324     flags=ReadBlobMSBShort(image);
    325     bits_per_pixel=(size_t) ReadBlobByte(image);
    326     if ((bits_per_pixel != 1) && (bits_per_pixel != 2) &&
    327         (bits_per_pixel != 4) && (bits_per_pixel != 8) &&
    328         (bits_per_pixel != 16))
    329       ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
    330     version=(size_t) ReadBlobByte(image);
    331     if ((version != 0) && (version != 1) && (version != 2))
    332       ThrowReaderException(CorruptImageError,"FileFormatVersionMismatch");
    333     nextDepthOffset=(size_t) ReadBlobMSBShort(image);
    334     transparentIndex=(size_t) ReadBlobByte(image);
    335     compressionType=(size_t) ReadBlobByte(image);
    336     if ((compressionType != PALM_COMPRESSION_NONE) &&
    337         (compressionType != PALM_COMPRESSION_SCANLINE ) &&
    338         (compressionType != PALM_COMPRESSION_RLE))
    339       ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression");
    340     pad=ReadBlobMSBShort(image);
    341     (void) pad;
    342     /*
    343       Initialize image colormap.
    344     */
    345     one=1;
    346     if ((bits_per_pixel < 16) &&
    347         (AcquireImageColormap(image,one << bits_per_pixel,exception) == MagickFalse))
    348       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    349     GetPixelInfo(image,&transpix);
    350     if (bits_per_pixel == 16)  /* Direct Color */
    351       {
    352         redbits=(size_t) ReadBlobByte(image);  /* # of bits of red */
    353         (void) redbits;
    354         greenbits=(size_t) ReadBlobByte(image);  /* # of bits of green */
    355         (void) greenbits;
    356         bluebits=(size_t) ReadBlobByte(image);  /* # of bits of blue */
    357         (void) bluebits;
    358         ReadBlobByte(image);  /* reserved by Palm */
    359         ReadBlobByte(image);  /* reserved by Palm */
    360         transpix.red=(double) (QuantumRange*ReadBlobByte(image)/31);
    361         transpix.green=(double) (QuantumRange*ReadBlobByte(image)/63);
    362         transpix.blue=(double) (QuantumRange*ReadBlobByte(image)/31);
    363       }
    364     if (bits_per_pixel == 8)
    365       {
    366         ssize_t
    367           index;
    368 
    369         if (flags & PALM_HAS_COLORMAP_FLAG)
    370           {
    371             count=(ssize_t) ReadBlobMSBShort(image);
    372             for (i=0; i < (ssize_t) count; i++)
    373             {
    374               ReadBlobByte(image);
    375               index=ConstrainColormapIndex(image,255-i,exception);
    376               image->colormap[index].red=(MagickRealType)
    377                 ScaleCharToQuantum((unsigned char) ReadBlobByte(image));
    378               image->colormap[index].green=(MagickRealType)
    379                 ScaleCharToQuantum((unsigned char) ReadBlobByte(image));
    380               image->colormap[index].blue=(MagickRealType)
    381                 ScaleCharToQuantum((unsigned char) ReadBlobByte(image));
    382           }
    383         }
    384       else
    385         for (i=0; i < (ssize_t) (1L << bits_per_pixel); i++)
    386         {
    387           index=ConstrainColormapIndex(image,255-i,exception);
    388           image->colormap[index].red=(MagickRealType)
    389             ScaleCharToQuantum(PalmPalette[i][0]);
    390           image->colormap[index].green=(MagickRealType)
    391             ScaleCharToQuantum(PalmPalette[i][1]);
    392           image->colormap[index].blue=(MagickRealType)
    393             ScaleCharToQuantum(PalmPalette[i][2]);
    394         }
    395       }
    396     if (flags & PALM_IS_COMPRESSED_FLAG)
    397       size=ReadBlobMSBShort(image);
    398     (void) size;
    399     image->storage_class=DirectClass;
    400     if (bits_per_pixel < 16)
    401       {
    402         image->storage_class=PseudoClass;
    403         image->depth=8;
    404       }
    405     if (image_info->ping != MagickFalse)
    406       {
    407         (void) CloseBlob(image);
    408         return(image);
    409       }
    410     status=SetImageExtent(image,image->columns,image->rows,exception);
    411     if (status == MagickFalse)
    412       return(DestroyImageList(image));
    413     one_row=(unsigned char *) AcquireQuantumMemory(MagickMax(bytes_per_row,
    414       2*image->columns),sizeof(*one_row));
    415     if (one_row == (unsigned char *) NULL)
    416       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    417     lastrow=(unsigned char *) NULL;
    418     if (compressionType == PALM_COMPRESSION_SCANLINE) {
    419       lastrow=(unsigned char *) AcquireQuantumMemory(MagickMax(bytes_per_row,
    420         2*image->columns),sizeof(*lastrow));
    421     if (lastrow == (unsigned char *) NULL)
    422       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    423     }
    424     mask=(size_t) (1U << bits_per_pixel)-1;
    425     for (y=0; y < (ssize_t) image->rows; y++)
    426     {
    427       if ((flags & PALM_IS_COMPRESSED_FLAG) == 0)
    428         {
    429           /* TODO move out of loop! */
    430           image->compression=NoCompression;
    431           count=ReadBlob(image,bytes_per_row,one_row);
    432           if (count != (ssize_t) bytes_per_row)
    433             break;
    434         }
    435       else
    436         {
    437           if (compressionType == PALM_COMPRESSION_RLE)
    438             {
    439               /* TODO move out of loop! */
    440               image->compression=RLECompression;
    441               for (i=0; i < (ssize_t) bytes_per_row; )
    442               {
    443                 count=(ssize_t) ReadBlobByte(image);
    444                 if (count < 0)
    445                   break;
    446                 count=MagickMin(count,(ssize_t) bytes_per_row-i);
    447                 byte=(size_t) ReadBlobByte(image);
    448                 (void) ResetMagickMemory(one_row+i,(int) byte,(size_t) count);
    449                 i+=count;
    450               }
    451           }
    452         else
    453           if (compressionType == PALM_COMPRESSION_SCANLINE)
    454             {
    455               size_t
    456                 one;
    457 
    458               /* TODO move out of loop! */
    459               one=1;
    460               image->compression=FaxCompression;
    461               for (i=0; i < (ssize_t) bytes_per_row; i+=8)
    462               {
    463                 count=(ssize_t) ReadBlobByte(image);
    464                 if (count < 0)
    465                   break;
    466                 byte=(size_t) MagickMin((ssize_t) bytes_per_row-i,8);
    467                 for (bit=0; bit < byte; bit++)
    468                 {
    469                   if ((y == 0) || (count & (one << (7 - bit))))
    470                     one_row[i+bit]=(unsigned char) ReadBlobByte(image);
    471                   else
    472                     one_row[i+bit]=lastrow[i+bit];
    473                 }
    474               }
    475               (void) CopyMagickMemory(lastrow, one_row, bytes_per_row);
    476             }
    477         }
    478       ptr=one_row;
    479       q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
    480       if (q == (Quantum *) NULL)
    481         break;
    482       if (bits_per_pixel == 16)
    483         {
    484           if (image->columns > (2*bytes_per_row))
    485             ThrowReaderException(CorruptImageError,"CorruptImage");
    486           for (x=0; x < (ssize_t) image->columns; x++)
    487           {
    488             color16=(*ptr++ << 8);
    489             color16|=(*ptr++);
    490             SetPixelRed(image,(Quantum) ((QuantumRange*((color16 >> 11) &
    491               0x1f))/0x1f),q);
    492             SetPixelGreen(image,(Quantum) ((QuantumRange*((color16 >> 5) &
    493               0x3f))/0x3f),q);
    494             SetPixelBlue(image,(Quantum) ((QuantumRange*((color16 >> 0) &
    495               0x1f))/0x1f),q);
    496             SetPixelAlpha(image,OpaqueAlpha,q);
    497             q+=GetPixelChannels(image);
    498           }
    499         }
    500       else
    501         {
    502           bit=8-bits_per_pixel;
    503           for (x=0; x < (ssize_t) image->columns; x++)
    504           {
    505             if ((size_t) (ptr-one_row) >= bytes_per_row)
    506               ThrowReaderException(CorruptImageError,"CorruptImage");
    507             index=(Quantum) (mask-(((*ptr) & (mask << bit)) >> bit));
    508             SetPixelIndex(image,index,q);
    509             SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
    510             if (bit)
    511               bit-=bits_per_pixel;
    512             else
    513               {
    514                 ptr++;
    515                 bit=8-bits_per_pixel;
    516               }
    517             q+=GetPixelChannels(image);
    518           }
    519           if (SyncAuthenticPixels(image,exception) == MagickFalse)
    520             break;
    521         }
    522         if (image->previous == (Image *) NULL)
    523           {
    524             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
    525               image->rows);
    526             if (status == MagickFalse)
    527               break;
    528           }
    529       }
    530     if (flags & PALM_HAS_TRANSPARENCY_FLAG)
    531       {
    532         ssize_t index=ConstrainColormapIndex(image,(ssize_t) (mask-
    533           transparentIndex),exception);
    534         if (bits_per_pixel != 16)
    535           transpix=image->colormap[index];
    536         (void) TransparentPaintImage(image,&transpix,(Quantum) TransparentAlpha,
    537           MagickFalse,exception);
    538       }
    539     one_row=(unsigned char *) RelinquishMagickMemory(one_row);
    540     if (compressionType == PALM_COMPRESSION_SCANLINE)
    541       lastrow=(unsigned char *) RelinquishMagickMemory(lastrow);
    542     if (EOFBlob(image) != MagickFalse)
    543       {
    544         ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
    545           image->filename);
    546         break;
    547       }
    548     /*
    549       Proceed to next image. Copied from coders/pnm.c
    550     */
    551     if (image_info->number_scenes != 0)
    552       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
    553         break;
    554     if (nextDepthOffset != 0)
    555       {
    556         /*
    557           Skip to next image.
    558         */
    559         totalOffset+=(MagickOffsetType) (nextDepthOffset*4);
    560         if (totalOffset >= (MagickOffsetType) GetBlobSize(image))
    561           ThrowReaderException(CorruptImageError,"ImproperImageHeader")
    562         else
    563           seekNextDepth=SeekBlob(image,totalOffset,SEEK_SET);
    564         if (seekNextDepth != totalOffset)
    565           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    566         /*
    567           Allocate next image structure. Copied from coders/pnm.c
    568         */
    569         AcquireNextImage(image_info,image,exception);
    570         if (GetNextImageInList(image) == (Image *) NULL)
    571           {
    572             (void) DestroyImageList(image);
    573             return((Image *) NULL);
    574           }
    575         image=SyncNextImageInList(image);
    576         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
    577           GetBlobSize(image));
    578         if (status == MagickFalse)
    579           break;
    580       }
    581   } while (nextDepthOffset != 0);
    582   (void) CloseBlob(image);
    583   return(GetFirstImageInList(image));
    584 }
    585 
    586 /*
    588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    589 %                                                                             %
    590 %                                                                             %
    591 %                                                                             %
    592 %   R e g i s t e r P A L M I m a g e                                         %
    593 %                                                                             %
    594 %                                                                             %
    595 %                                                                             %
    596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    597 %
    598 %  RegisterPALMImage() adds properties for the PALM image format to the list of
    599 %  supported formats.  The properties include the image format tag, a method to
    600 %  read and/or write the format, whether the format supports the saving of more
    601 %  than one frame to the same file or blob, whether the format supports native
    602 %  in-memory I/O, and a brief description of the format.
    603 %
    604 %  The format of the RegisterPALMImage method is:
    605 %
    606 %      size_t RegisterPALMImage(void)
    607 %
    608 */
    609 ModuleExport size_t RegisterPALMImage(void)
    610 {
    611   MagickInfo
    612     *entry;
    613 
    614   entry=AcquireMagickInfo("PALM","PALM","Palm pixmap");
    615   entry->decoder=(DecodeImageHandler *) ReadPALMImage;
    616   entry->encoder=(EncodeImageHandler *) WritePALMImage;
    617   entry->flags|=CoderSeekableStreamFlag;
    618   (void) RegisterMagickInfo(entry);
    619   return(MagickImageCoderSignature);
    620 }
    621 
    622 /*
    624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    625 %                                                                             %
    626 %                                                                             %
    627 %                                                                             %
    628 %   U n r e g i s t e r P A L M I m a g e                                     %
    629 %                                                                             %
    630 %                                                                             %
    631 %                                                                             %
    632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    633 %
    634 %  UnregisterPALMImage() removes format registrations made by the PALM
    635 %  module from the list of supported formats.
    636 %
    637 %  The format of the UnregisterPALMImage method is:
    638 %
    639 %      UnregisterPALMImage(void)
    640 %
    641 */
    642 ModuleExport void UnregisterPALMImage(void)
    643 {
    644   (void) UnregisterMagickInfo("PALM");
    645 }
    646 
    647 /*
    649 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    650 %                                                                             %
    651 %                                                                             %
    652 %                                                                             %
    653 %   W r i t e P A L M I m a g e                                               %
    654 %                                                                             %
    655 %                                                                             %
    656 %                                                                             %
    657 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    658 %
    659 %  WritePALMImage() writes an image of raw bits in LSB order to a file.
    660 %
    661 %  The format of the WritePALMImage method is:
    662 %
    663 %      MagickBooleanType WritePALMImage(const ImageInfo *image_info,
    664 %        Image *image,ExceptionInfo *exception)
    665 %
    666 %  A description of each parameter follows.
    667 %
    668 %    o image_info: Specifies a pointer to an ImageInfo structure.
    669 %
    670 %    o image:  A pointer to a Image structure.
    671 %
    672 %    o exception: return any errors or warnings in this structure.
    673 %
    674 */
    675 static MagickBooleanType WritePALMImage(const ImageInfo *image_info,
    676   Image *image,ExceptionInfo *exception)
    677 {
    678   MagickBooleanType
    679     status;
    680 
    681   MagickOffsetType
    682     currentOffset,
    683     offset,
    684     scene;
    685 
    686   MagickSizeType
    687     cc;
    688 
    689   PixelInfo
    690     transpix;
    691 
    692   QuantizeInfo
    693     *quantize_info;
    694 
    695   register ssize_t
    696     x;
    697 
    698   register const Quantum
    699     *p;
    700 
    701   register Quantum
    702     *q;
    703 
    704   ssize_t
    705     y;
    706 
    707   size_t
    708     count,
    709     bits_per_pixel,
    710     bytes_per_row,
    711     nextDepthOffset,
    712     one;
    713 
    714   unsigned char
    715     bit,
    716     byte,
    717     color,
    718     *lastrow,
    719     *one_row,
    720     *ptr,
    721     version;
    722 
    723   unsigned int
    724     transparentIndex;
    725 
    726   unsigned short
    727     color16,
    728     flags;
    729 
    730   /*
    731     Open output image file.
    732   */
    733   assert(image_info != (const ImageInfo *) NULL);
    734   assert(image_info->signature == MagickCoreSignature);
    735   assert(image != (Image *) NULL);
    736   assert(image->signature == MagickCoreSignature);
    737   if (image->debug != MagickFalse)
    738     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    739   assert(exception != (ExceptionInfo *) NULL);
    740   assert(exception->signature == MagickCoreSignature);
    741   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
    742   if (status == MagickFalse)
    743     return(status);
    744   quantize_info=AcquireQuantizeInfo(image_info);
    745   flags=0;
    746   currentOffset=0;
    747   transparentIndex=0;
    748   transpix.red=0.0;
    749   transpix.green=0.0;
    750   transpix.blue=0.0;
    751   transpix.alpha=0.0;
    752   one=1;
    753   version=0;
    754   scene=0;
    755   do
    756   {
    757     (void) TransformImageColorspace(image,sRGBColorspace,exception);
    758     count=GetNumberColors(image,NULL,exception);
    759     for (bits_per_pixel=1;  (one << bits_per_pixel) < count; bits_per_pixel*=2) ;
    760     if (image_info->depth > 100)
    761       bits_per_pixel=image_info->depth-100;
    762     if (bits_per_pixel < 16)
    763       (void) TransformImageColorspace(image,image->colorspace,exception);
    764     if (bits_per_pixel < 8)
    765       {
    766         (void) TransformImageColorspace(image,GRAYColorspace,exception);
    767         (void) SetImageType(image,PaletteType,exception);
    768         (void) SortColormapByIntensity(image,exception);
    769       }
    770     if ((image->storage_class == PseudoClass) && (image->colors > 256))
    771       (void) SetImageStorageClass(image,DirectClass,exception);
    772     if (image->storage_class == PseudoClass)
    773       flags|=PALM_HAS_COLORMAP_FLAG;
    774     else
    775       flags|=PALM_IS_DIRECT_COLOR;
    776     (void) WriteBlobMSBShort(image,(unsigned short) image->columns); /* width */
    777     (void) WriteBlobMSBShort(image,(unsigned short) image->rows);  /* height */
    778     bytes_per_row=((image->columns+(16/bits_per_pixel-1))/(16/
    779       bits_per_pixel))*2;
    780     (void) WriteBlobMSBShort(image,(unsigned short) bytes_per_row);
    781     if ((image_info->compression == RLECompression) ||
    782         (image_info->compression == FaxCompression))
    783       flags|=PALM_IS_COMPRESSED_FLAG;
    784     (void) WriteBlobMSBShort(image, flags);
    785     (void) WriteBlobByte(image,(unsigned char) bits_per_pixel);
    786     if (bits_per_pixel > 1)
    787       version=1;
    788     if ((image_info->compression == RLECompression) ||
    789         (image_info->compression == FaxCompression))
    790       version=2;
    791     (void) WriteBlobByte(image,version);
    792     (void) WriteBlobMSBShort(image,0);  /* nextDepthOffset */
    793     (void) WriteBlobByte(image,(unsigned char) transparentIndex);
    794     if (image_info->compression == RLECompression)
    795       (void) WriteBlobByte(image,PALM_COMPRESSION_RLE);
    796     else
    797       if (image_info->compression == FaxCompression)
    798         (void) WriteBlobByte(image,PALM_COMPRESSION_SCANLINE);
    799       else
    800         (void) WriteBlobByte(image,PALM_COMPRESSION_NONE);
    801     (void) WriteBlobMSBShort(image,0);  /* reserved */
    802     offset=16;
    803     if (bits_per_pixel == 16)
    804       {
    805         (void) WriteBlobByte(image,5);  /* # of bits of red */
    806         (void) WriteBlobByte(image,6);  /* # of bits of green */
    807         (void) WriteBlobByte(image,5);  /* # of bits of blue */
    808         (void) WriteBlobByte(image,0);  /* reserved by Palm */
    809         (void) WriteBlobMSBLong(image,0);  /* no transparent color, YET */
    810         offset+=8;
    811       }
    812     if (bits_per_pixel == 8)
    813       {
    814         if (flags & PALM_HAS_COLORMAP_FLAG)  /* Write out colormap */
    815           {
    816             quantize_info->dither_method=IdentifyPaletteImage(image,exception)
    817               == MagickFalse ? RiemersmaDitherMethod : NoDitherMethod;
    818             quantize_info->number_colors=image->colors;
    819             (void) QuantizeImage(quantize_info,image,exception);
    820             (void) WriteBlobMSBShort(image,(unsigned short) image->colors);
    821             for (count = 0; count < image->colors; count++)
    822             {
    823               (void) WriteBlobByte(image,(unsigned char) count);
    824               (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum(
    825                 image->colormap[count].red)));
    826               (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum(
    827                 image->colormap[count].green)));
    828               (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum(
    829                 image->colormap[count].blue)));
    830             }
    831             offset+=2+count*4;
    832           }
    833       else  /* Map colors to Palm standard colormap */
    834         {
    835           Image
    836             *affinity_image;
    837 
    838           affinity_image=ConstituteImage(256,1,"RGB",CharPixel,&PalmPalette,
    839             exception);
    840           (void) TransformImageColorspace(affinity_image,
    841             affinity_image->colorspace,exception);
    842           (void) RemapImage(quantize_info,image,affinity_image,exception);
    843           for (y=0; y < (ssize_t) image->rows; y++)
    844           {
    845             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
    846             if (q == (Quantum *) NULL)
    847               break;
    848             for (x=0; x < (ssize_t) image->columns; x++)
    849             {
    850               SetPixelIndex(image,(Quantum) FindColor(&image->colormap[(ssize_t)
    851                 GetPixelIndex(image,q)]),q);
    852               q+=GetPixelChannels(image);
    853             }
    854           }
    855           affinity_image=DestroyImage(affinity_image);
    856         }
    857       }
    858     if (flags & PALM_IS_COMPRESSED_FLAG)
    859       (void) WriteBlobMSBShort(image,0);  /* fill in size later */
    860     lastrow=(unsigned char *) NULL;
    861     if (image_info->compression == FaxCompression)
    862       lastrow=(unsigned char *) AcquireQuantumMemory(bytes_per_row,
    863         sizeof(*lastrow));
    864       /* TODO check whether memory really was acquired? */
    865     one_row=(unsigned char *) AcquireQuantumMemory(bytes_per_row,
    866       sizeof(*one_row));
    867     if (one_row == (unsigned char *) NULL)
    868       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    869     for (y=0; y < (ssize_t) image->rows; y++)
    870     {
    871       ptr=one_row;
    872       (void) ResetMagickMemory(ptr,0,bytes_per_row);
    873       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    874       if (p == (const Quantum *) NULL)
    875         break;
    876       if (bits_per_pixel == 16)
    877         {
    878           for (x=0; x < (ssize_t) image->columns; x++)
    879           {
    880             color16=(unsigned short) ((((31*(size_t) GetPixelRed(image,p))/
    881               (size_t) QuantumRange) << 11) | (((63*(size_t)
    882               GetPixelGreen(image,p))/(size_t) QuantumRange) << 5) |
    883               ((31*(size_t) GetPixelBlue(image,p))/(size_t) QuantumRange));
    884             if (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)
    885               {
    886                 transpix.red=(MagickRealType) GetPixelRed(image,p);
    887                 transpix.green=(MagickRealType) GetPixelGreen(image,p);
    888                 transpix.blue=(MagickRealType) GetPixelBlue(image,p);
    889                 transpix.alpha=(MagickRealType) GetPixelAlpha(image,p);
    890                 flags|=PALM_HAS_TRANSPARENCY_FLAG;
    891               }
    892             *ptr++=(unsigned char) ((color16 >> 8) & 0xff);
    893             *ptr++=(unsigned char) (color16 & 0xff);
    894             p+=GetPixelChannels(image);
    895           }
    896         }
    897       else
    898         {
    899           byte=0x00;
    900           bit=(unsigned char) (8-bits_per_pixel);
    901           for (x=0; x < (ssize_t) image->columns; x++)
    902           {
    903             if (bits_per_pixel >= 8)
    904               color=(unsigned char) GetPixelIndex(image,p);
    905             else
    906               color=(unsigned char) (GetPixelIndex(image,p)*
    907                 ((one << bits_per_pixel)-1)/MagickMax(1*image->colors-1,1));
    908             byte|=color << bit;
    909             if (bit != 0)
    910               bit-=(unsigned char) bits_per_pixel;
    911             else
    912               {
    913                 *ptr++=byte;
    914                 byte=0x00;
    915                 bit=(unsigned char) (8-bits_per_pixel);
    916               }
    917             p+=GetPixelChannels(image);
    918           }
    919           if ((image->columns % (8/bits_per_pixel)) != 0)
    920             *ptr++=byte;
    921         }
    922       if (image_info->compression == RLECompression)
    923         {
    924           x=0;
    925           while (x < (ssize_t) bytes_per_row)
    926           {
    927             byte=one_row[x];
    928             count=1;
    929             while ((one_row[++x] == byte) && (count < 255) &&
    930                    (x < (ssize_t) bytes_per_row))
    931               count++;
    932             (void) WriteBlobByte(image,(unsigned char) count);
    933             (void) WriteBlobByte(image,(unsigned char) byte);
    934           }
    935         }
    936       else
    937         if (image_info->compression == FaxCompression)
    938           {
    939             char
    940               tmpbuf[8],
    941               *tptr;
    942 
    943             for (x = 0;  x < (ssize_t) bytes_per_row;  x += 8)
    944             {
    945               tptr = tmpbuf;
    946               for (bit=0, byte=0; bit < (unsigned char) MagickMin(8,(ssize_t) bytes_per_row-x); bit++)
    947               {
    948                 if ((y == 0) || (lastrow[x + bit] != one_row[x + bit]))
    949                   {
    950                     byte |= (1 << (7 - bit));
    951                     *tptr++ = (char) one_row[x + bit];
    952                   }
    953               }
    954               (void) WriteBlobByte(image, byte);
    955               (void) WriteBlob(image,tptr-tmpbuf,(unsigned char *) tmpbuf);
    956             }
    957             (void) CopyMagickMemory(lastrow,one_row,bytes_per_row);
    958           }
    959         else
    960           (void) WriteBlob(image,bytes_per_row,one_row);
    961       }
    962     if (flags & PALM_HAS_TRANSPARENCY_FLAG)
    963       {
    964         offset=SeekBlob(image,currentOffset+6,SEEK_SET);
    965         (void) WriteBlobMSBShort(image,flags);
    966         offset=SeekBlob(image,currentOffset+12,SEEK_SET);
    967         (void) WriteBlobByte(image,(unsigned char) transparentIndex);  /* trans index */
    968       }
    969     if (bits_per_pixel == 16)
    970       {
    971         offset=SeekBlob(image,currentOffset+20,SEEK_SET);
    972         (void) WriteBlobByte(image,0);  /* reserved by Palm */
    973         (void) WriteBlobByte(image,(unsigned char) ((31*transpix.red)/QuantumRange));
    974         (void) WriteBlobByte(image,(unsigned char) ((63*transpix.green)/QuantumRange));
    975         (void) WriteBlobByte(image,(unsigned char) ((31*transpix.blue)/QuantumRange));
    976       }
    977     if (flags & PALM_IS_COMPRESSED_FLAG)  /* fill in size now */
    978       {
    979         offset=SeekBlob(image,currentOffset+offset,SEEK_SET);
    980         (void) WriteBlobMSBShort(image,(unsigned short) (GetBlobSize(image)-
    981           currentOffset-offset));
    982       }
    983     if (one_row != (unsigned char *) NULL)
    984       one_row=(unsigned char *) RelinquishMagickMemory(one_row);
    985     if (lastrow != (unsigned char *) NULL)
    986       lastrow=(unsigned char *) RelinquishMagickMemory(lastrow);
    987     if (GetNextImageInList(image) == (Image *) NULL)
    988       break;
    989     /* padding to 4 byte word */
    990     for (cc=(GetBlobSize(image)) % 4; cc > 0; cc--)
    991       (void) WriteBlobByte(image,0);
    992     /* write nextDepthOffset and return to end of image */
    993     offset=SeekBlob(image,currentOffset+10,SEEK_SET);
    994     nextDepthOffset=(size_t) ((GetBlobSize(image)-currentOffset)/4);
    995     (void) WriteBlobMSBShort(image,(unsigned short) nextDepthOffset);
    996     currentOffset=(MagickOffsetType) GetBlobSize(image);
    997     offset=SeekBlob(image,currentOffset,SEEK_SET);
    998     image=SyncNextImageInList(image);
    999     status=SetImageProgress(image,SaveImagesTag,scene++,
   1000       GetImageListLength(image));
   1001     if (status == MagickFalse)
   1002       break;
   1003   } while (image_info->adjoin != MagickFalse);
   1004   quantize_info=DestroyQuantizeInfo(quantize_info);
   1005   (void) CloseBlob(image);
   1006   return(MagickTrue);
   1007 }
   1008