/*************************************************************************** * Copyright (C) 2007 by Tobias Koenig * * * * Based on code written by Bill Janssen 2002 * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #include #include #include #include /* This code requires the Independent JPEG Group libjpeg library, version 6b or later */ extern "C" { #include "jpeglib.h" } #include "unpluck.h" #include "image.h" #define GET_FUNCTION_CODE_TYPE(x) (((x)>>3) & 0x1F) #define GET_FUNCTION_CODE_DATALEN(x) ((x) & 0x7) #define CELLS(row,col) cells[row*cols+col] /***********************************************************************/ /***********************************************************************/ /***** *****/ /***** Code to decode the Palm image format to JPEG *****/ /***** *****/ /***********************************************************************/ /***********************************************************************/ #define READ_BIGENDIAN_SHORT(p) (((p)[0] << 8)|((p)[1])) #define READ_BIGENDIAN_LONG(p) (((p)[0] << 24)|((p)[1] << 16)|((p)[2] << 8)|((p)[3])) #define PALM_IS_COMPRESSED_FLAG 0x8000 #define PALM_HAS_COLORMAP_FLAG 0x4000 #define PALM_HAS_TRANSPARENCY_FLAG 0x2000 #define PALM_DIRECT_COLOR_FLAG 0x0400 #define PALM_4_BYTE_FIELD_FLAG 0x0200 #define PALM_COMPRESSION_SCANLINE 0x00 #define PALM_COMPRESSION_RLE 0x01 #define PALM_COMPRESSION_PACKBITS 0x02 #define PALM_COMPRESSION_NONE 0xFF #define PALM_COLORMAP_SIZE 232 typedef struct { unsigned char red; unsigned char green; unsigned char blue; } ColorMapEntry; static ColorMapEntry Palm8BitColormap[] = { {255, 255, 255}, {255, 204, 255}, {255, 153, 255}, {255, 102, 255}, {255, 51, 255}, {255, 0, 255}, {255, 255, 204}, {255, 204, 204}, {255, 153, 204}, {255, 102, 204}, {255, 51, 204}, {255, 0, 204}, {255, 255, 153}, {255, 204, 153}, {255, 153, 153}, {255, 102, 153}, {255, 51, 153}, {255, 0, 153}, {204, 255, 255}, {204, 204, 255}, {204, 153, 255}, {204, 102, 255}, {204, 51, 255}, {204, 0, 255}, {204, 255, 204}, {204, 204, 204}, {204, 153, 204}, {204, 102, 204}, {204, 51, 204}, {204, 0, 204}, {204, 255, 153}, {204, 204, 153}, {204, 153, 153}, {204, 102, 153}, {204, 51, 153}, {204, 0, 153}, {153, 255, 255}, {153, 204, 255}, {153, 153, 255}, {153, 102, 255}, {153, 51, 255}, {153, 0, 255}, {153, 255, 204}, {153, 204, 204}, {153, 153, 204}, {153, 102, 204}, {153, 51, 204}, {153, 0, 204}, {153, 255, 153}, {153, 204, 153}, {153, 153, 153}, {153, 102, 153}, {153, 51, 153}, {153, 0, 153}, {102, 255, 255}, {102, 204, 255}, {102, 153, 255}, {102, 102, 255}, {102, 51, 255}, {102, 0, 255}, {102, 255, 204}, {102, 204, 204}, {102, 153, 204}, {102, 102, 204}, {102, 51, 204}, {102, 0, 204}, {102, 255, 153}, {102, 204, 153}, {102, 153, 153}, {102, 102, 153}, {102, 51, 153}, {102, 0, 153}, { 51, 255, 255}, { 51, 204, 255}, { 51, 153, 255}, { 51, 102, 255}, { 51, 51, 255}, { 51, 0, 255}, { 51, 255, 204}, { 51, 204, 204}, { 51, 153, 204}, { 51, 102, 204}, { 51, 51, 204}, { 51, 0, 204}, { 51, 255, 153}, { 51, 204, 153}, { 51, 153, 153}, { 51, 102, 153}, { 51, 51, 153}, { 51, 0, 153}, { 0, 255, 255}, { 0, 204, 255}, { 0, 153, 255}, { 0, 102, 255}, { 0, 51, 255}, { 0, 0, 255}, { 0, 255, 204}, { 0, 204, 204}, { 0, 153, 204}, { 0, 102, 204}, { 0, 51, 204}, { 0, 0, 204}, { 0, 255, 153}, { 0, 204, 153}, { 0, 153, 153}, { 0, 102, 153}, { 0, 51, 153}, { 0, 0, 153}, {255, 255, 102}, {255, 204, 102}, {255, 153, 102}, {255, 102, 102}, {255, 51, 102}, {255, 0, 102}, {255, 255, 51}, {255, 204, 51}, {255, 153, 51}, {255, 102, 51}, {255, 51, 51}, {255, 0, 51}, {255, 255, 0}, {255, 204, 0}, {255, 153, 0}, {255, 102, 0}, {255, 51, 0}, {255, 0, 0}, {204, 255, 102}, {204, 204, 102}, {204, 153, 102}, {204, 102, 102}, {204, 51, 102}, {204, 0, 102}, {204, 255, 51}, {204, 204, 51}, {204, 153, 51}, {204, 102, 51}, {204, 51, 51}, {204, 0, 51}, {204, 255, 0}, {204, 204, 0}, {204, 153, 0}, {204, 102, 0}, {204, 51, 0}, {204, 0, 0}, {153, 255, 102}, {153, 204, 102}, {153, 153, 102}, {153, 102, 102}, {153, 51, 102}, {153, 0, 102}, {153, 255, 51}, {153, 204, 51}, {153, 153, 51}, {153, 102, 51}, {153, 51, 51}, {153, 0, 51}, {153, 255, 0}, {153, 204, 0}, {153, 153, 0}, {153, 102, 0}, {153, 51, 0}, {153, 0, 0}, {102, 255, 102}, {102, 204, 102}, {102, 153, 102}, {102, 102, 102}, {102, 51, 102}, {102, 0, 102}, {102, 255, 51}, {102, 204, 51}, {102, 153, 51}, {102, 102, 51}, {102, 51, 51}, {102, 0, 51}, {102, 255, 0}, {102, 204, 0}, {102, 153, 0}, {102, 102, 0}, {102, 51, 0}, {102, 0, 0}, { 51, 255, 102}, { 51, 204, 102}, { 51, 153, 102}, { 51, 102, 102}, { 51, 51, 102}, { 51, 0, 102}, { 51, 255, 51}, { 51, 204, 51}, { 51, 153, 51}, { 51, 102, 51}, { 51, 51, 51}, { 51, 0, 51}, { 51, 255, 0}, { 51, 204, 0}, { 51, 153, 0}, { 51, 102, 0}, { 51, 51, 0}, { 51, 0, 0}, { 0, 255, 102}, { 0, 204, 102}, { 0, 153, 102}, { 0, 102, 102}, { 0, 51, 102}, { 0, 0, 102}, { 0, 255, 51}, { 0, 204, 51}, { 0, 153, 51}, { 0, 102, 51}, { 0, 51, 51}, { 0, 0, 51}, { 0, 255, 0}, { 0, 204, 0}, { 0, 153, 0}, { 0, 102, 0}, { 0, 51, 0}, { 17, 17, 17}, { 34, 34, 34}, { 68, 68, 68}, { 85, 85, 85}, {119, 119, 119}, {136, 136, 136}, {170, 170, 170}, {187, 187, 187}, {221, 221, 221}, {238, 238, 238}, {192, 192, 192}, {128, 0, 0}, {128, 0, 128}, { 0, 128, 0}, { 0, 128, 128}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0} }; static ColorMapEntry Palm1BitColormap[] = { {255, 255, 255}, { 0, 0, 0} }; static ColorMapEntry Palm2BitColormap[] = { {255, 255, 255}, {192, 192, 192}, {128, 128, 128}, { 0, 0, 0} }; static ColorMapEntry Palm4BitColormap[] = { {255, 255, 255}, {238, 238, 238}, {221, 221, 221}, {204, 204, 204}, {187, 187, 187}, {170, 170, 170}, {153, 153, 153}, {136, 136, 136}, {119, 119, 119}, {102, 102, 102}, { 85, 85, 85}, { 68, 68, 68}, { 51, 51, 51}, { 34, 34, 34}, { 17, 17, 17}, { 0, 0, 0} }; bool TranscribePalmImageToJPEG ( unsigned char *image_bytes_in, QImage &image ) { unsigned int width; unsigned int height; unsigned int bytes_per_row; unsigned int flags; unsigned int next_depth_offset; unsigned int bits_per_pixel; unsigned int version; unsigned int transparent_index; unsigned int compression_type; unsigned int i; unsigned int j; unsigned int inval; unsigned int inbit; unsigned int mask; unsigned int incount; unsigned int palm_red_bits = 0; unsigned int palm_green_bits = 0; unsigned int palm_blue_bits = 0; unsigned char* palm_ptr; unsigned char* x_ptr; unsigned char* imagedata = 0; unsigned char* inbyte; unsigned char* rowbuf; unsigned char* lastrow; unsigned char* imagedatastart; unsigned char* palmimage; ColorMapEntry *colormap; JSAMPLE* jpeg_row; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; JSAMPROW row_pointer[1];/* pointer to JSAMPLE row[s] */ palmimage = image_bytes_in; width = READ_BIGENDIAN_SHORT (palmimage + 0); height = READ_BIGENDIAN_SHORT (palmimage + 2); bytes_per_row = READ_BIGENDIAN_SHORT (palmimage + 4); flags = READ_BIGENDIAN_SHORT (palmimage + 6); bits_per_pixel = palmimage[8]; version = palmimage[9]; next_depth_offset = READ_BIGENDIAN_SHORT (palmimage + 10); transparent_index = palmimage[12]; compression_type = palmimage[13]; /* bytes 14 and 15 are reserved by Palm and always 0 */ if (compression_type == PALM_COMPRESSION_PACKBITS) { return false; } else if ((compression_type != PALM_COMPRESSION_NONE) && (compression_type != PALM_COMPRESSION_RLE) && (compression_type != PALM_COMPRESSION_SCANLINE)) { return false; } /* as of PalmOS 4.0, there are 6 different kinds of Palm pixmaps: 1, 2, or 4 bit grayscale 8-bit StaticColor using the Palm standard colormap 8-bit PseudoColor using a user-specified colormap 16-bit DirectColor using 5 bits for red, 6 for green, and 5 for blue Each of these can be compressed with one of four compression schemes, "RLE", "Scanline", "PackBits", or none. We begin by constructing the colormap. */ if (flags & PALM_HAS_COLORMAP_FLAG) { return false; } else if (bits_per_pixel == 1) { colormap = Palm1BitColormap; imagedatastart = palmimage + 16; } else if (bits_per_pixel == 2) { colormap = Palm2BitColormap; imagedatastart = palmimage + 16; } else if (bits_per_pixel == 4) { colormap = Palm4BitColormap; imagedatastart = palmimage + 16; } else if (bits_per_pixel == 8) { colormap = Palm8BitColormap; imagedatastart = palmimage + 16; } else if (bits_per_pixel == 16 && (flags & PALM_DIRECT_COLOR_FLAG)) { colormap = NULL; palm_red_bits = palmimage[16]; palm_green_bits = palmimage[17]; palm_blue_bits = palmimage[18]; if (palm_blue_bits > 8 || palm_green_bits > 8 || palm_red_bits > 8) { return false; } if (bits_per_pixel > (8 * sizeof (unsigned long))) { return false; } imagedatastart = palmimage + 24; } else { return false; } QTemporaryFile tempFile; tempFile.open(); FILE *outfile = fopen( QFile::encodeName( tempFile.fileName() ), "w" ); if ( !outfile ) return false; /* now create the JPEG image row buffer */ jpeg_row = (JSAMPLE *) malloc (sizeof (JSAMPLE) * (width * 3)); /* Use standard JPEG error processing */ cinfo.err = jpeg_std_error (&jerr); /* Initialize the JPEG compression object. */ jpeg_create_compress (&cinfo); jpeg_stdio_dest (&cinfo, outfile); cinfo.image_width = width; /* image width and height, in pixels */ cinfo.image_height = height; cinfo.input_components = 3; /* # of color components per pixel */ cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ jpeg_set_defaults (&cinfo); jpeg_set_quality (&cinfo, 100, true /* limit to baseline-JPEG values */ ); row_pointer[0] = &jpeg_row[0]; jpeg_start_compress (&cinfo, true); /* row by row, uncompress the Palm image and copy it to the JPEG buffer */ rowbuf = (unsigned char *) malloc (bytes_per_row * width); lastrow = (unsigned char *) malloc (bytes_per_row * width); for (i = 0, palm_ptr = imagedatastart, x_ptr = imagedata; i < height; ++i) { /* first, uncompress the Palm image */ if ((flags & PALM_IS_COMPRESSED_FLAG) && (compression_type == PALM_COMPRESSION_RLE)) { for (j = 0; j < bytes_per_row;) { incount = *palm_ptr++; inval = *palm_ptr++; if (incount + j <= bytes_per_row * width) { memset (rowbuf + j, inval, incount); j += incount; } else { free (rowbuf); free (lastrow); free (jpeg_row); jpeg_destroy_compress (&cinfo); fclose( outfile ); return false; } } } else if ((flags & PALM_IS_COMPRESSED_FLAG) && (compression_type == PALM_COMPRESSION_SCANLINE)) { for (j = 0; j < bytes_per_row; j += 8) { incount = *palm_ptr++; inval = ((bytes_per_row - j) < 8) ? (bytes_per_row - j) : 8; for (inbit = 0; inbit < inval; inbit += 1) { if (incount & (1 << (7 - inbit))) rowbuf[j + inbit] = *palm_ptr++; else rowbuf[j + inbit] = lastrow[j + inbit]; } } memcpy (lastrow, rowbuf, bytes_per_row); } else if (((flags & PALM_IS_COMPRESSED_FLAG) && (compression_type == PALM_COMPRESSION_NONE)) || (flags & PALM_IS_COMPRESSED_FLAG) == 0) { memcpy (rowbuf, palm_ptr, bytes_per_row); palm_ptr += bytes_per_row; } /* next, write it to the GDK bitmap */ if (colormap) { mask = (1 << bits_per_pixel) - 1; for (inbit = 8 - bits_per_pixel, inbyte = rowbuf, j = 0; j < width; ++j) { inval = ((*inbyte) & (mask << inbit)) >> inbit; /* correct for oddity of the 8-bit color Palm pixmap... */ if ((bits_per_pixel == 8) && (inval == 0xFF)) inval = 231; /* now lookup the correct color and set the pixel in the GTK bitmap */ jpeg_row[(j * 3) + 0] = colormap[inval].red; jpeg_row[(j * 3) + 1] = colormap[inval].green; jpeg_row[(j * 3) + 2] = colormap[inval].blue; if (!inbit) { ++inbyte; inbit = 8 - bits_per_pixel; } else { inbit -= bits_per_pixel; } } } else if (!colormap && bits_per_pixel == 16) { for (inbyte = rowbuf, j = 0; j < width; ++j) { inval = (inbyte[0] << 8) | inbyte[1]; jpeg_row[(j * 3) + 0] = (inval >> (bits_per_pixel - palm_red_bits)) & ((1 << palm_red_bits) - 1); jpeg_row[(j * 3) + 1] = (inval >> palm_blue_bits) & ((1 << palm_green_bits) - 1); jpeg_row[(j * 3) + 2] = (inval >> 0) & ((1 << palm_blue_bits) - 1); inbyte += 2; } } (void) jpeg_write_scanlines (&cinfo, row_pointer, 1); } free (rowbuf); free (lastrow); free (jpeg_row); jpeg_finish_compress (&cinfo); jpeg_destroy_compress (&cinfo); fclose( outfile ); return image.load( tempFile.fileName() ); } typedef struct { unsigned int width; unsigned int height; unsigned int bytes_per_row; unsigned int flags; unsigned int next_depth_offset; unsigned int bits_per_pixel; unsigned int version; unsigned int transparent_index; unsigned int compression_type; unsigned int palm_red_bits; unsigned int palm_green_bits; unsigned int palm_blue_bits; unsigned char* bytes; } PALMPIX; bool TranscribeMultiImageRecord ( plkr_Document* doc, QImage &image, unsigned char* bytes ) { unsigned char* pbytes = 0; unsigned char* outbytes = 0; unsigned char* outptr = 0; unsigned char* ptr = &bytes[12]; plkr_DataRecordType ptype; PALMPIX* cells = 0; PALMPIX* acell = 0; unsigned int record_id = 0; int plen = 0; unsigned int x = 0; unsigned int y = 0; unsigned int cols = 0; unsigned int rows = 0; unsigned int width = 0; unsigned int height = 0; unsigned int bytes_per_row = 0; unsigned int flags = 0; unsigned int bits_per_pixel = 0; unsigned int version = 0; unsigned int transparent_index = 0; unsigned int compression_type = 0; unsigned int palm_red_bits = 0; unsigned int palm_green_bits = 0; unsigned int palm_blue_bits = 0; unsigned int outlen = 0; unsigned int offset = 0; bool status = true; cols = (bytes[8] << 8) + bytes[9]; rows = (bytes[10] << 8) + bytes[11]; cells = (PALMPIX *) calloc (cols * rows, sizeof (PALMPIX)); height = 0; for (y = 0; y < rows; y++) { width = 0; bytes_per_row = 0; for (x = 0; x < cols; x++) { acell = &CELLS (y, x); record_id = (ptr[0] << 8) + ptr[1]; ptr += 2; pbytes = plkr_GetRecordBytes (doc, record_id, &plen, &ptype); if (pbytes == NULL) { free (cells); return false; } pbytes += 8; acell->width = READ_BIGENDIAN_SHORT (&pbytes[0]); width += acell->width; acell->height = READ_BIGENDIAN_SHORT (&pbytes[2]); acell->bytes_per_row = READ_BIGENDIAN_SHORT (&pbytes[4]); bytes_per_row += acell->bytes_per_row; acell->flags = READ_BIGENDIAN_SHORT (&pbytes[6]); flags = acell->flags; acell->bits_per_pixel = pbytes[8]; bits_per_pixel = acell->bits_per_pixel; acell->version = pbytes[9]; version = acell->version; acell->next_depth_offset = READ_BIGENDIAN_SHORT (&pbytes[10]); acell->transparent_index = pbytes[12]; transparent_index = acell->transparent_index; acell->compression_type = pbytes[13]; compression_type = acell->compression_type; if (acell->flags & PALM_HAS_COLORMAP_FLAG) { free (cells); return false; } acell->bytes = pbytes + 16; offset = 16; if (acell->bits_per_pixel == 16 && (acell->flags & PALM_DIRECT_COLOR_FLAG)) { acell->palm_red_bits = pbytes[16]; palm_red_bits = acell->palm_red_bits; acell->palm_green_bits = pbytes[17]; palm_green_bits = acell->palm_green_bits; acell->palm_blue_bits = pbytes[18]; palm_blue_bits = acell->palm_blue_bits; acell->bytes = pbytes + 24; offset = 24; } } height += acell->height; } outlen = bytes_per_row * height + offset; outbytes = (unsigned char *) malloc (outlen); outptr = outbytes; *outptr++ = width >> 8; *outptr++ = width; *outptr++ = height >> 8; *outptr++ = height; *outptr++ = bytes_per_row >> 8; *outptr++ = bytes_per_row; *outptr++ = flags >> 8; *outptr++ = flags; *outptr++ = bits_per_pixel; *outptr++ = version; *outptr++ = 0; /* next_depth_offset */ *outptr++ = 0; *outptr++ = transparent_index; *outptr++ = compression_type; *outptr++ = 0; *outptr++ = 0; if (acell->bits_per_pixel == 16 && (acell->flags & PALM_DIRECT_COLOR_FLAG)) { *outptr++ = palm_red_bits; *outptr++ = palm_green_bits; *outptr++ = palm_blue_bits; *outptr++ = 0; *outptr++ = 0; *outptr++ = 0; *outptr++ = 0; *outptr++ = 0; } for (y = 0; y < rows; y++) { int i, h; acell = &CELLS (y, 0); h = acell->height; for (i = 0; i < h; i++) { for (x = 0; x < cols; x++) { acell = &CELLS (y, x); memcpy (outptr, acell->bytes, acell->bytes_per_row); acell->bytes += acell->bytes_per_row; outptr += acell->bytes_per_row; } } } status = TranscribePalmImageToJPEG (outbytes, image); free (outbytes); free (cells); return status; }