diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
commit | 1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch) | |
tree | 64c8c96cf54d8718847339a403e5e67b922e8c3f /converter/other/pnmtopalm | |
download | netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip |
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/other/pnmtopalm')
-rw-r--r-- | converter/other/pnmtopalm/LICENSE | 16 | ||||
-rw-r--r-- | converter/other/pnmtopalm/Makefile | 35 | ||||
-rw-r--r-- | converter/other/pnmtopalm/README | 57 | ||||
-rw-r--r-- | converter/other/pnmtopalm/gen_palm_colormap.c | 24 | ||||
-rw-r--r-- | converter/other/pnmtopalm/palm.h | 66 | ||||
-rw-r--r-- | converter/other/pnmtopalm/palmcolor8.map | 235 | ||||
-rw-r--r-- | converter/other/pnmtopalm/palmcolormap.c | 277 | ||||
-rw-r--r-- | converter/other/pnmtopalm/palmgray1.map | 4 | ||||
-rw-r--r-- | converter/other/pnmtopalm/palmgray2.map | 6 | ||||
-rw-r--r-- | converter/other/pnmtopalm/palmgray4.map | 8 | ||||
-rw-r--r-- | converter/other/pnmtopalm/palmtopnm.c | 1131 | ||||
-rw-r--r-- | converter/other/pnmtopalm/pnmtopalm.c | 1235 |
12 files changed, 3094 insertions, 0 deletions
diff --git a/converter/other/pnmtopalm/LICENSE b/converter/other/pnmtopalm/LICENSE new file mode 100644 index 00000000..740b6080 --- /dev/null +++ b/converter/other/pnmtopalm/LICENSE @@ -0,0 +1,16 @@ +LICENSE FOR PNMTOPALM SUBPACKAGE OF NETPBM. THIS LICENSE APPLIES TO ALL +THE MATERIALS IN THE PNMTOPALM DIRECTORY OF THE NETPBM SOURCE PACKAGE. + +Copyright 1995-2001 by Ian Goldberg, George Caswell, and Bill Janssen. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. This software is provided "as is" without express or +implied warranty. + + +(Bryan Henderson extracted this license memorandum from the comments in the +original man page nroff source supplied to him with the rest of the Pnmtopalm +package in January 2001 by Bill Janssen). diff --git a/converter/other/pnmtopalm/Makefile b/converter/other/pnmtopalm/Makefile new file mode 100644 index 00000000..2a76297e --- /dev/null +++ b/converter/other/pnmtopalm/Makefile @@ -0,0 +1,35 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/../../.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = converter/other/pnmtopalm +VPATH=.:$(SRCDIR)/$(SUBDIR) + +include $(BUILDDIR)/Makefile.config + +BINARIES = palmtopnm pnmtopalm +SCRIPTS = +OBJECTS = $(BINARIES:%=%.o) palmcolormap.o +MERGE_OBJECTS = $(BINARIES:%=%.o2) palmcolormap.o +MERGEBINARIES = $(BINARIES) +DATAFILES = palmcolor8.map palmgray1.map palmgray2.map palmgray4.map + +all: $(BINARIES) + +include $(SRCDIR)/Makefile.common + +LIBOPTS = $(shell $(LIBOPT) $(NETPBMLIB)) + +$(BINARIES): %: %.o palmcolormap.o $(NETPBMLIB) $(LIBOPT) + $(LD) $(LDFLAGS) -o $@ $< palmcolormap.o $(LIBOPTS) \ + $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD) + +gen_palm_colormap : $(SUBDIR)/gen_palm_colormap.c palmcolormap.o + $(CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -o $@ $< palmcolormap.o \ + $(LIBOPTS) $(MATHLIB) $(LDLIBS) $(LADD) + + +clean: cleanspecial +.PHONY: cleanspecial +cleanspecial: + rm -f gen_palm_colormap diff --git a/converter/other/pnmtopalm/README b/converter/other/pnmtopalm/README new file mode 100644 index 00000000..ebae9492 --- /dev/null +++ b/converter/other/pnmtopalm/README @@ -0,0 +1,57 @@ +This is version 1.0 of pnmpalm, netpbm converters for Palm pixmaps. + +It's derived from version 1.0 of ppmtoTbmp. The original 'ppmtoTbmp' +program has been renamed 'pnmtopalm', and has been rewritten to handle +the various bitmaps that exist as of PalmOS 3.5, and to handle +colormaps, a transparent color, and the various compression formats +recognized by Palm OS. The 'Tbmptopnm' program is now 'palmtopnm', +and has been updated to handle the various newer bitmap formats as +well. Man pages for 'pnmtopalm' and 'palmtopnm' have been added. See +those man pages for more information. Note that command-line switches +have changed. + +As with Tbmptoppm, ppm images must be quantified properly before +being passed to pnmtopalm. Several colormap files are provided to +use for this: + - palmgray1.map -- 1-bit grayscale (i.e. monochrome) + - palmgray2.map -- 2-bit grayscale + - palmgray4.map -- 4-bit grayscale + - palmcolor8.map -- 8-bit color, using the default color palette + +-- Bill Janssen <bill@janssen.org> + + +One source of information on Palm image formats is +http://www.kawt.de/doc/palmimages.html. + + + +The original ppmtoTbmp README read as follows: + +This is version 1.0 of ppmtoTbmp, a ppm to Pilot bitmap converter. + +To compile it, you'll need the netpbm package. Last I checked, it was +available at "ftp://ftp.x.org/contrib/utilities/netpbm-1mar1994.p1.tar.gz". +Change the Makefile to point to the directory that contains the netpbm +header files, and make. + +Usage: ppmtoTbmp [-2bit] [file.ppm] + +If the ppm file is not specified, one is read from stdin. The -2bit option +produces a 2-bit bitmap instead of the normal 1-bit bitmap. The ppm must +have at most 4 colors in it (for 2-bit) or at most 2 colors (for 1-bit). +Common invokations might be: + +xbmtopbm icon.xbm | ppmquant -fs -map q2.map | ppmtoTbmp > tAIB03e8.bin +giftopnm image.gif | ppmquant -fs -map q4.map | ppmtoTbmp -2bit > Tbmp0bb8.bin + +(q2.map and q4.map are trivial 2 and 4 color maps, respectively, and are +included in this distribution.) + +To include the resulting bitmap in a .prc file, just name the output +tAIB03e8.bin (for an application icon) or Tbmpxxxx.bin (for a form bitmap, +where xxxx is replaced by four hex digits giving the bitmap ID). + +I'll probably make Tbmptopnm later, and put it in the 1.1 release. + + - Ian Goldberg <iang@cs.berkeley.edu> diff --git a/converter/other/pnmtopalm/gen_palm_colormap.c b/converter/other/pnmtopalm/gen_palm_colormap.c new file mode 100644 index 00000000..4b65e631 --- /dev/null +++ b/converter/other/pnmtopalm/gen_palm_colormap.c @@ -0,0 +1,24 @@ +/* gen_palm_colormap.c - generate a ppm file containing the default Palm colormap + * + * Bill Janssen <bill@janssen.org> + */ + +#include "pnm.h" + +#include "palm.h" + +int main( int argc, char **argv ) { + + int i; + Color_s current; + Colormap default_map = palmcolor_build_default_8bit_colormap (); + + printf("P3\n%d 1\n255\n", default_map->ncolors); + for (i = 0; i < default_map->ncolors; i++) { + current = default_map->color_entries[i]; + printf ("%d %d %d\n", (current & 0xFF0000) >> 16, (current & 0xFF00) >> 8, (current & 0xFF)); + /* printf ("%x: %d %d %d\n", (current & 0xFF000000) >> 24, (current & 0xFF0000) >> 16, (current & 0xFF00) >> 8, (current & 0xFF)); */ + }; + return 0; +} + diff --git a/converter/other/pnmtopalm/palm.h b/converter/other/pnmtopalm/palm.h new file mode 100644 index 00000000..170c8cec --- /dev/null +++ b/converter/other/pnmtopalm/palm.h @@ -0,0 +1,66 @@ +#ifndef PALM_H_INCLUDED +#define PALM_H_INCLUDED + +#define PALM_IS_COMPRESSED_FLAG 0x8000 +#define PALM_HAS_COLORMAP_FLAG 0x4000 +#define PALM_HAS_TRANSPARENCY_FLAG 0x2000 +#define PALM_INDIRECT_BITMAP 0x1000 /* Palm says internal use only */ +#define PALM_FOR_SCREEN 0x0800 /* Palm says internal use only */ +#define PALM_DIRECT_COLOR_FLAG 0x0400 +#define PALM_INDIRECT_COLORMAP 0x0200 /* Palm says internal use only */ +#define PALM_NO_DITHER_FLAG 0x0100 /* rather mysterious */ + +#define PALM_COMPRESSION_SCANLINE 0x00 +#define PALM_COMPRESSION_RLE 0x01 +#define PALM_COMPRESSION_PACKBITS 0x02 +#define PALM_COMPRESSION_END 0x03 /* Palm says internal use only */ +#define PALM_COMPRESSION_BEST 0x64 /* Palm says internal use only */ +#define PALM_COMPRESSION_NONE 0xFF /* Palm says internal use only */ + +#define PALM_DENSITY_LOW 72 +#define PALM_DENSITY_ONEANDAHALF 108 +#define PALM_DENSITY_DOUBLE 144 +#define PALM_DENSITY_TRIPLE 216 +#define PALM_DENSITY_QUADRUPLE 288 + +#define PALM_FORMAT_INDEXED 0x00 +#define PALM_FORMAT_565 0x01 +#define PALM_FORMAT_565LE 0x02 /* Palm says internal use only */ +#define PALM_FORMAT_INDEXEDLE 0x03 /* Palm says internal use only */ + +typedef unsigned long Color_s; + +typedef Color_s * Color; + +typedef struct { + unsigned int nentries; + /* number of allocated entries in 'color_entries' */ + unsigned int ncolors; + /* number of colors actually in 'color_entries' -- entries are + filled from 0 consecutively, one color per entry. + */ + Color_s * color_entries; /* Array of colors */ +} Colormap_s; + +typedef Colormap_s * Colormap; + +int +palmcolor_compare_indices(const void * const p1, + const void * const p2); + +int +palmcolor_compare_colors(const void * const p1, + const void * const p2); + +Colormap +palmcolor_build_custom_8bit_colormap(unsigned int const rows, + unsigned int const cols, + pixel ** const pixels); + +Colormap +palmcolor_build_default_8bit_colormap(void); + +Colormap +palmcolor_read_colormap (FILE * const ifP); + +#endif diff --git a/converter/other/pnmtopalm/palmcolor8.map b/converter/other/pnmtopalm/palmcolor8.map new file mode 100644 index 00000000..2e054616 --- /dev/null +++ b/converter/other/pnmtopalm/palmcolor8.map @@ -0,0 +1,235 @@ +P3 +232 1 +255 +0 0 0 +0 0 0 +0 0 51 +0 0 102 +0 0 153 +0 0 204 +0 0 255 +0 51 0 +0 51 51 +0 51 102 +0 51 153 +0 51 204 +0 51 255 +0 102 0 +0 102 51 +0 102 102 +0 102 153 +0 102 204 +0 102 255 +0 128 0 +0 128 128 +0 153 0 +0 153 51 +0 153 102 +0 153 153 +0 153 204 +0 153 255 +0 204 0 +0 204 51 +0 204 102 +0 204 153 +0 204 204 +0 204 255 +0 255 0 +0 255 51 +0 255 102 +0 255 153 +0 255 204 +0 255 255 +17 17 17 +34 34 34 +51 0 0 +51 0 51 +51 0 102 +51 0 153 +51 0 204 +51 0 255 +51 51 0 +51 51 51 +51 51 102 +51 51 153 +51 51 204 +51 51 255 +51 102 0 +51 102 51 +51 102 102 +51 102 153 +51 102 204 +51 102 255 +51 153 0 +51 153 51 +51 153 102 +51 153 153 +51 153 204 +51 153 255 +51 204 0 +51 204 51 +51 204 102 +51 204 153 +51 204 204 +51 204 255 +51 255 0 +51 255 51 +51 255 102 +51 255 153 +51 255 204 +51 255 255 +68 68 68 +85 85 85 +102 0 0 +102 0 51 +102 0 102 +102 0 153 +102 0 204 +102 0 255 +102 51 0 +102 51 51 +102 51 102 +102 51 153 +102 51 204 +102 51 255 +102 102 0 +102 102 51 +102 102 102 +102 102 153 +102 102 204 +102 102 255 +102 153 0 +102 153 51 +102 153 102 +102 153 153 +102 153 204 +102 153 255 +102 204 0 +102 204 51 +102 204 102 +102 204 153 +102 204 204 +102 204 255 +102 255 0 +102 255 51 +102 255 102 +102 255 153 +102 255 204 +102 255 255 +119 119 119 +128 0 0 +128 0 128 +136 136 136 +153 0 0 +153 0 51 +153 0 102 +153 0 153 +153 0 204 +153 0 255 +153 51 0 +153 51 51 +153 51 102 +153 51 153 +153 51 204 +153 51 255 +153 102 0 +153 102 51 +153 102 102 +153 102 153 +153 102 204 +153 102 255 +153 153 0 +153 153 51 +153 153 102 +153 153 153 +153 153 204 +153 153 255 +153 204 0 +153 204 51 +153 204 102 +153 204 153 +153 204 204 +153 204 255 +153 255 0 +153 255 51 +153 255 102 +153 255 153 +153 255 204 +153 255 255 +170 170 170 +187 187 187 +192 192 192 +204 0 0 +204 0 51 +204 0 102 +204 0 153 +204 0 204 +204 0 255 +204 51 0 +204 51 51 +204 51 102 +204 51 153 +204 51 204 +204 51 255 +204 102 0 +204 102 51 +204 102 102 +204 102 153 +204 102 204 +204 102 255 +204 153 0 +204 153 51 +204 153 102 +204 153 153 +204 153 204 +204 153 255 +204 204 0 +204 204 51 +204 204 102 +204 204 153 +204 204 204 +204 204 255 +204 255 0 +204 255 51 +204 255 102 +204 255 153 +204 255 204 +204 255 255 +221 221 221 +238 238 238 +255 0 0 +255 0 51 +255 0 102 +255 0 153 +255 0 204 +255 0 255 +255 51 0 +255 51 51 +255 51 102 +255 51 153 +255 51 204 +255 51 255 +255 102 0 +255 102 51 +255 102 102 +255 102 153 +255 102 204 +255 102 255 +255 153 0 +255 153 51 +255 153 102 +255 153 153 +255 153 204 +255 153 255 +255 204 0 +255 204 51 +255 204 102 +255 204 153 +255 204 204 +255 204 255 +255 255 0 +255 255 51 +255 255 102 +255 255 153 +255 255 204 +255 255 255 diff --git a/converter/other/pnmtopalm/palmcolormap.c b/converter/other/pnmtopalm/palmcolormap.c new file mode 100644 index 00000000..a1a1cec1 --- /dev/null +++ b/converter/other/pnmtopalm/palmcolormap.c @@ -0,0 +1,277 @@ +/* See LICENSE file for licensing information. +*/ + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "pnm.h" + +#include "palm.h" + +int +palmcolor_compare_indices(const void * const p1, + const void * const p2) { + + if ((*((Color) p1) & 0xFF000000) < (*((Color) p2) & 0xFF000000)) + return -1; + else if ((*((Color) p1) & 0xFF000000) > (*((Color) p2) & 0xFF000000)) + return 1; + else + return 0; +} + + + +int +palmcolor_compare_colors(const void * const p1, + const void * const p2) { + + unsigned long const val1 = *((const unsigned long *) p1) & 0xFFFFFF; + unsigned long const val2 = *((const unsigned long *) p2) & 0xFFFFFF; + + if (val1 < val2) + return -1; + else if (val1 > val2) + return 1; + else + return 0; +} + +/*********************************************************************** + *********************************************************************** + *********************************************************************** + ******* colortables from pilrc-2.6/bitmap.c *************************** + *********************************************************************** + *********************************************************************** + ***********************************************************************/ + +#if 0 + +/* + * The 1bit-2 color system palette for Palm Computing Devices. + */ +static int PalmPalette1bpp[2][3] = +{ + { 255, 255, 255}, { 0, 0, 0 } +}; + +/* + * The 2bit-4 color system palette for Palm Computing Devices. + */ +static int PalmPalette2bpp[4][3] = +{ + { 255, 255, 255}, { 192, 192, 192}, { 128, 128, 128 }, { 0, 0, 0 } +}; + +/* + * The 4bit-16 color system palette for Palm Computing Devices. + */ +static int PalmPalette4bpp[16][3] = +{ + { 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 } +}; + +/* + * The 4bit-16 color system palette for Palm Computing Devices. + */ +static int PalmPalette4bppColor[16][3] = +{ + { 255, 255, 255}, { 128, 128, 128 }, { 128, 0, 0 }, { 128, 128, 0 }, + { 0, 128, 0}, { 0, 128, 128 }, { 0, 0, 128 }, { 128, 0, 128 }, + { 255, 0, 255}, { 192, 192, 192 }, { 255, 0, 0 }, { 255, 255, 0 }, + { 0, 255, 0}, { 0, 255, 255 }, { 0, 0, 255 }, { 0, 0, 0 } +}; + +#endif /* 0 */ + +/* + * The 8bit-256 color system palette for Palm Computing Devices. + * + * NOTE: only the first 231, plus the last one, are valid. + */ +static int PalmPalette8bpp[256][3] = +{ + { 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 } +}; + + + +Colormap +palmcolor_build_default_8bit_colormap(void) { + + unsigned int i; + + Colormap cm; + + MALLOCVAR_NOFAIL(cm); + cm->nentries = 232; + MALLOCARRAY_NOFAIL(cm->color_entries, cm->nentries); + + /* Fill in the colors */ + for (i = 0; i < 231; ++i) { + cm->color_entries[i] = ((i << 24) | + (PalmPalette8bpp[i][0] << 16) | + (PalmPalette8bpp[i][1] << 8) | + (PalmPalette8bpp[i][2])); + } + cm->color_entries[231] = 0xFF000000; + cm->ncolors = 232; + + /* now sort the table */ + qsort (cm->color_entries, cm->ncolors, sizeof(Color_s), + palmcolor_compare_colors); + return cm; +} + + + +Colormap +palmcolor_build_custom_8bit_colormap(unsigned int const rows, + unsigned int const cols, + pixel ** const pixels) { + unsigned int row; + Colormap colormap; + + MALLOCVAR_NOFAIL(colormap); + colormap->nentries = 256; + MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries); + colormap->ncolors = 0; /* initial value */ + + for (row = 0; row < rows; ++row) { + unsigned int col; + for (col = 0; col < cols; ++col) { + Color found; + Color_s temp; + + temp = ((PPM_GETR(pixels[row][col]) << 16) | + (PPM_GETG(pixels[row][col]) << 8) | + PPM_GETB(pixels[row][col])); + found = (bsearch (&temp, + colormap->color_entries, colormap->ncolors, + sizeof(Color_s), palmcolor_compare_colors)); + if (!found) { + if (colormap->ncolors >= colormap->nentries) + pm_error("Too many colors for custom colormap " + "(max 256). " + "Try using pnmquant to reduce the number " + "of colors."); + else { + /* add the new color, and re-sort */ + temp |= ((colormap->ncolors) << 24); + colormap->color_entries[colormap->ncolors] = temp; + colormap->ncolors += 1; + qsort(colormap->color_entries, colormap->ncolors, + sizeof(Color_s), palmcolor_compare_colors); + } + } + } + } + return colormap; +} + + + +Colormap +palmcolor_read_colormap (FILE * const ifP) { + + unsigned short ncolors; + Colormap retval; + int rc; + + rc = pm_readbigshort(ifP, (short *) &ncolors); + if (rc != 0) + retval = NULL; + else { + long colorentry; + Colormap colormap; + unsigned int i; + bool error; + + MALLOCVAR_NOFAIL(colormap); + colormap->nentries = ncolors; + MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries); + + for (i = 0, error = FALSE; i < ncolors && !error; ++i) { + int rc; + rc = pm_readbiglong(ifP, &colorentry); + if (rc != 0) + error = TRUE; + else + colormap->color_entries[i] = (colorentry & 0xFFFFFFFF); + } + if (error) { + free (colormap->color_entries); + free (colormap); + retval = NULL; + } else { + colormap->ncolors = ncolors; + retval = colormap; + } + } + return retval; +} diff --git a/converter/other/pnmtopalm/palmgray1.map b/converter/other/pnmtopalm/palmgray1.map new file mode 100644 index 00000000..96e20400 --- /dev/null +++ b/converter/other/pnmtopalm/palmgray1.map @@ -0,0 +1,4 @@ +P1 +# Black and white palette +1 2 +0 1 diff --git a/converter/other/pnmtopalm/palmgray2.map b/converter/other/pnmtopalm/palmgray2.map new file mode 100644 index 00000000..e8c95b01 --- /dev/null +++ b/converter/other/pnmtopalm/palmgray2.map @@ -0,0 +1,6 @@ +P2 +# 4 level grayscale palette +2 2 +3 +0 1 +2 3 diff --git a/converter/other/pnmtopalm/palmgray4.map b/converter/other/pnmtopalm/palmgray4.map new file mode 100644 index 00000000..bba2e6b8 --- /dev/null +++ b/converter/other/pnmtopalm/palmgray4.map @@ -0,0 +1,8 @@ +P2 +# 16 level grayscale palette +4 4 +15 + 0 1 2 3 + 4 5 6 7 + 8 9 10 11 +12 13 14 15 diff --git a/converter/other/pnmtopalm/palmtopnm.c b/converter/other/pnmtopalm/palmtopnm.c new file mode 100644 index 00000000..9cd695e3 --- /dev/null +++ b/converter/other/pnmtopalm/palmtopnm.c @@ -0,0 +1,1131 @@ +/****************************************************************************** + palmtopnm +******************************************************************************* + By Bryan Henderson, San Jose, California, June 2004. + + Inspired by and using methods from Tbmptopnm by Ian Goldberg + <iang@cs.berkeley.edu>, and Bill Janssen <bill@janssen.org>. + + Major fixes and new capability added by Paul Bolle <pebolle@tiscali.nl> + in late 2004 / early 2005. + + Bryan's work is contributed to the public domain by its author. +******************************************************************************/ + +#include <string.h> +#include <assert.h> + +#include "pnm.h" +#include "shhopt.h" +#include "mallocvar.h" + +#include "palm.h" + + + +enum palmCompressionType { + COMPRESSION_NONE, + COMPRESSION_RLE, + COMPRESSION_SCANLINE, + COMPRESSION_PACKBITS +}; + +struct palmHeader { + unsigned short cols; + unsigned short rows; + unsigned short bytesPerRow; + unsigned short flags; + bool directColor; + /* The header indicates a direct color raster, either by flag + (the old way) or by pixel format (the new way) + */ + bool hasColormap; + bool hasTransparency; + unsigned char pixelSizeCode; + unsigned int pixelSize; + unsigned char version; + unsigned int transparentIndex; + enum palmCompressionType compressionType; + /* version 3 encoding specific */ + unsigned char size; + unsigned char pixelFormat; + unsigned short density; + unsigned long transparentValue; +}; + + + +struct directPixelFormat { + unsigned int redbits; + unsigned int greenbits; + unsigned int bluebits; +}; + + + +struct directColorInfo { + struct directPixelFormat pixelFormat; + Color_s transparentColor; +}; + + + + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFilespec; + unsigned int verbose; + unsigned int rendition; + unsigned int showhist; + unsigned int transparent; +}; + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc( 100*sizeof( optEntry ) ); + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int renditionSpec; + + unsigned int option_def_index; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + OPTENT3(0, "showhist", OPT_FLAG, NULL, + &cmdlineP->showhist, 0); + OPTENT3(0, "transparent", OPT_FLAG, NULL, + &cmdlineP->transparent, 0); + OPTENT3(0, "rendition", OPT_UINT, &cmdlineP->rendition, + &renditionSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + + if (renditionSpec) { + if (cmdlineP->rendition < 1) + pm_error("The -rendition value must be at least 1"); + } else + cmdlineP->rendition = 1; + + if (cmdlineP->transparent && cmdlineP->showhist) + pm_error("You can't specify -showhist with -transparent"); + + if (argc-1 < 1) + cmdlineP->inputFilespec = "-"; + else { + cmdlineP->inputFilespec = argv[1]; + if (argc-1 > 1) + pm_error("Too many arguments (%d). The only non-option " + "argument is the file name", argc-1); + } +} + + + +static xelval * +createGraymap(unsigned int const ncolors, + xelval const maxval) { + int i; + xelval *map; + + MALLOCARRAY_NOFAIL(map, ncolors); + for (i = 0; i < ncolors; ++i) { + map[i] = maxval - (i * maxval) / (ncolors - 1); + } + return map; +} + + + +static void +skipbytes(FILE * const ifP, + unsigned int const nbytes) { + + unsigned char buf[256]; + unsigned int n; + size_t bytesRead; + + n = nbytes; /* initial value */ + + while (n > 0) { + if (n > sizeof(buf)) { + bytesRead = fread(buf, sizeof(char), sizeof(buf), ifP); + if (bytesRead != sizeof(buf)) + pm_error("Error reading Palm file. Short read."); + n -= sizeof(buf); + } else { + bytesRead = fread(buf, sizeof(char), n, ifP); + if (bytesRead != n) + pm_error("Error reading Palm file. Short read."); + n = 0; + } + } +} + + + +static void +interpretCompression(unsigned char const compressionValue, + enum palmCompressionType * const compressionTypeP) { + + switch (compressionValue) { + case PALM_COMPRESSION_RLE: + *compressionTypeP = COMPRESSION_RLE; + break; + case PALM_COMPRESSION_SCANLINE: + *compressionTypeP = COMPRESSION_SCANLINE; + break; + case PALM_COMPRESSION_PACKBITS: + *compressionTypeP = COMPRESSION_PACKBITS; + break; + case PALM_COMPRESSION_NONE: + /* according to the spec this is not possible */ + *compressionTypeP = COMPRESSION_NONE; + break; + default: + pm_error("The Palm image header has an unrecognized value for " + "compression type: 0x%02x", (unsigned)compressionValue); + } +} + + + +static void +readRestOfHeaderVersion3(FILE * const ifP, + unsigned int const pixelSize, + unsigned char * const sizeP, + unsigned char * const pixelFormatP, + unsigned char * const compressionTypeP, + short * const densityP, + unsigned int * const transparentIndexP, + long * const transparentValueP, + long * const nextBitmapOffsetP, + short * const nextDepthOffsetP) { + + unsigned char unused; + + pm_readcharu(ifP, sizeP); + /* should be 0x18, but I can't see why we should really care */ + if (*sizeP != 0x18) + pm_message("Strange value for Palm bitmap header size: %hu", *sizeP); + + pm_readcharu(ifP, pixelFormatP); + if (*pixelFormatP != PALM_FORMAT_INDEXED && + *pixelFormatP != PALM_FORMAT_565) + pm_error("Unrecognized pixelformat type: %u", *pixelFormatP); + + pm_readcharu(ifP, &unused); + + + pm_readcharu(ifP, compressionTypeP); + + pm_readbigshort(ifP, densityP); + /* the specs imply that 0x00 is not valid */ + if (*densityP != PALM_DENSITY_LOW && + *densityP != PALM_DENSITY_ONEANDAHALF && + *densityP != PALM_DENSITY_DOUBLE && + *densityP != PALM_DENSITY_TRIPLE && + *densityP != PALM_DENSITY_QUADRUPLE) + pm_error("Invalid value for -density: %d.", *densityP); + + pm_readbiglong(ifP, transparentValueP); + if (pixelSize < 16) + *transparentIndexP = *transparentValueP; + else + *transparentIndexP = 0; + + pm_readbiglong(ifP, nextBitmapOffsetP); + + /* version < 3 specific */ + *nextDepthOffsetP = 0; +} + + + +static void +readRestOfHeaderOld(FILE * const ifP, + unsigned char * const sizeP, + unsigned char * const pixelFormatP, + unsigned char * const compressionTypeP, + short * const densityP, + unsigned int * const transparentIndexP, + long * const transparentValueP, + long * const nextBitmapOffsetP, + short * const nextDepthOffsetP) { + + short pad; + unsigned char transparentIndex; + + pm_readbigshort(ifP, nextDepthOffsetP); + pm_readcharu(ifP, &transparentIndex); + *transparentIndexP = transparentIndex; + + pm_readcharu(ifP,compressionTypeP); + + pm_readbigshort(ifP, &pad); /* reserved by Palm as of 8/9/00 */ + + /* version 3 specific */ + *sizeP = 0; + *pixelFormatP = 0; + *densityP = 0; + *transparentValueP = 0; + *nextBitmapOffsetP = 0; +} + + + +static void +interpretHeader(struct palmHeader * const palmHeaderP, + short const cols, + short const rows, + short const bytesPerRow, + short const flags, + unsigned char const pixelSizeCode, + unsigned int const pixelSize, + unsigned char const version, + unsigned char const size, + unsigned char const pixelFormat, + short const density, + long const transparentValue, + unsigned int const transparentIndex, + unsigned char const compressionType) { + + palmHeaderP->cols = cols; + palmHeaderP->rows = rows; + palmHeaderP->bytesPerRow = bytesPerRow; + palmHeaderP->flags = flags; /* Just for diagnostics */ + palmHeaderP->hasColormap = !!(flags & PALM_HAS_COLORMAP_FLAG); + palmHeaderP->hasTransparency = !!(flags & PALM_HAS_TRANSPARENCY_FLAG); + palmHeaderP->pixelSizeCode = pixelSizeCode; + palmHeaderP->pixelSize = pixelSize; + palmHeaderP->version = version; + palmHeaderP->size = size; + palmHeaderP->pixelFormat = pixelFormat; + palmHeaderP->density = density; + palmHeaderP->transparentValue = transparentValue; + palmHeaderP->transparentIndex = transparentIndex; + + if (palmHeaderP->version == 3 && (flags & PALM_DIRECT_COLOR_FLAG)) + /* There's no directColorInfoType section in a version 3 Palm Bitmap */ + pm_error("PALM_DIRECT_COLOR_FLAG is not valid for version 3 " + "encoding type."); + + palmHeaderP->directColor = ((flags & PALM_DIRECT_COLOR_FLAG) || + palmHeaderP->pixelFormat == PALM_FORMAT_565); + + if (flags & PALM_IS_COMPRESSED_FLAG) + interpretCompression(compressionType, + &palmHeaderP->compressionType); + else + palmHeaderP->compressionType = COMPRESSION_NONE; +} + + + +static void +readHeader(FILE * const ifP, + unsigned int const requestedRendition, + struct palmHeader * const palmHeaderP) { +/*---------------------------------------------------------------------------- + Read the Palm Bitmap header from the file 'ifP'. Read past all + renditions up to 'requestedRendition' and read the header of that + rendition. Return the information contained in the header as *palmHeaderP. +-----------------------------------------------------------------------------*/ + bool gotHeader; + unsigned int currentRendition; + + gotHeader = FALSE; + currentRendition = 1; + while (!gotHeader) { + short cols, rows, bytesPerRow, flags, nextDepthOffset, density; + unsigned char pixelSizeCode, version, compressionType, + size, pixelFormat; + long transparentValue, nextBitmapOffset; + unsigned int pixelSize, transparentIndex; + + pm_readbigshort(ifP, &cols); + pm_readbigshort(ifP, &rows); + pm_readbigshort(ifP, &bytesPerRow); + pm_readbigshort(ifP, &flags); + + pm_readcharu(ifP, &pixelSizeCode); + pixelSize = pixelSizeCode == 0 ? 1 : pixelSizeCode; + if (pixelSizeCode != 0x00 && + pixelSizeCode != 0x01 && + pixelSizeCode != 0x02 && + pixelSizeCode != 0x04 && + pixelSizeCode != 0x08 && + pixelSizeCode != 0x10 && + pixelSizeCode != 0xFF) + pm_error("Invalid value for bits per pixel: %u.", pixelSizeCode); + + if ((bytesPerRow * 8) < (cols * pixelSize)) + pm_error("%u bytes per row is not valid with %u columns and %u " + "bits per pixel.", bytesPerRow, cols, pixelSize); + + pm_readcharu(ifP, &version); + if (version > 3) + pm_error("Unknown encoding version type: %d", version); + else if (version == 3) + readRestOfHeaderVersion3(ifP, pixelSize, + &size, &pixelFormat, &compressionType, + &density, &transparentIndex, + &transparentValue, &nextBitmapOffset, + &nextDepthOffset); + else + readRestOfHeaderOld(ifP, + &size, &pixelFormat, &compressionType, + &density, &transparentIndex, + &transparentValue, &nextBitmapOffset, + &nextDepthOffset); + + if (currentRendition < requestedRendition) { + if (version < 3 && nextDepthOffset == 0 && pixelSizeCode != 0xFF) + pm_error("Not enough renditions in the input Palm Bitmap " + "to extract the %dth", requestedRendition); + if (version == 3 && nextBitmapOffset == 0) + pm_error("Not enough renditions in the input Palm Bitmap " + "to extract the %dth", requestedRendition); + /* nextDepthOffset is calculated in 4 byte words + from the beginning of this bitmap (so it equals its size) + */ + if (version < 3 && pixelSizeCode != 0xFF ) + skipbytes(ifP, (nextDepthOffset*4)-16); + else if (version == 3) + /* FIXME rewrite skipbytes to accept longs? */ + skipbytes(ifP, (short) nextBitmapOffset-24); + if (pixelSizeCode != 0xFF) + ++currentRendition; + } else if (pixelSizeCode != 0xFF) { + gotHeader = TRUE; + + interpretHeader(palmHeaderP, + cols, rows, bytesPerRow, flags, pixelSizeCode, + pixelSize, version, size, pixelFormat, density, + transparentValue, transparentIndex, + compressionType); + } + } +} + + + +static const char * +yesno(bool const arg) { + + if (arg) + return "YES"; + else + return "NO"; +} + + +static void +reportPalmHeader(struct palmHeader const palmHeader, + struct directColorInfo const directColorInfo) { + + const char *ctype; + + switch (palmHeader.compressionType) { + case COMPRESSION_RLE: + ctype = "rle (Palm OS 3.5)"; + break; + case COMPRESSION_SCANLINE: + ctype = "scanline (Palm OS 2.0)"; + break; + case COMPRESSION_PACKBITS: + ctype = "packbits (Palm OS 4.0)"; + break; + case COMPRESSION_NONE: + ctype = "none"; + break; + } + pm_message("Dimensions: %hu columns x %hu rows", + palmHeader.cols, palmHeader.rows); + pm_message("Row layout: %hu bytes per row, %hu bits per pixel", + palmHeader.bytesPerRow, palmHeader.pixelSize); + pm_message("Pixel Size code: %hu", palmHeader.pixelSizeCode); + pm_message("Flags: 0x%04hx", palmHeader.flags); + pm_message(" Direct Color: %s", yesno(palmHeader.directColor)); + pm_message(" Colormap: %s", yesno(palmHeader.hasColormap)); + pm_message(" Transparency: %s", yesno(palmHeader.hasTransparency)); + pm_message("Version %d", palmHeader.version); + if (palmHeader.hasTransparency) { + if (palmHeader.directColor) { + /* Copied from doTransparent(...) */ + Color_s const color = directColorInfo.transparentColor; + pm_message("Transparent value: #%02x%02x%02x", + (unsigned int)((color >> 16) & 0xFF), + (unsigned int)((color >> 8) & 0xFF), + (unsigned int)((color >> 0) & 0xFF)); + } else + pm_message("Transparent index: %u", palmHeader.transparentIndex); + } + pm_message("Compression type: %s", ctype); + if (palmHeader.version == 3) + pm_message("Density: %d", palmHeader.density); +} + + + +static void +determineOutputFormat(struct palmHeader const palmHeader, + int * const formatP, + xelval * const maxvalP) { + + if (palmHeader.directColor) { + *formatP = PPM_TYPE; + *maxvalP = 255; + } else if (palmHeader.hasColormap) { + *formatP = PPM_TYPE; + *maxvalP = 255; + } else if (palmHeader.pixelSize == 1) { + *formatP = PBM_TYPE; + *maxvalP = 1; + } else if (palmHeader.pixelSize >= 8) { + *formatP = PPM_TYPE; + *maxvalP = pm_bitstomaxval(palmHeader.pixelSize); + } else { + *formatP = PGM_TYPE; + *maxvalP = pm_bitstomaxval(palmHeader.pixelSize); + } +} + + + +static void +readRgbFormat(FILE * const ifP, + struct directPixelFormat * const pixelFormatP) { + + unsigned char r, g, b; + + pm_readcharu(ifP, &r); + pm_readcharu(ifP, &g); + pm_readcharu(ifP, &b); + + if (r != 5 || g != 6 || b != 5) + pm_error("This image has a direct color pixel format of " + "%u red, %u green, %u blue bits. This program " + "can handle only 5, 6, 5.", r, g, b); + else { + pixelFormatP->redbits = r; + pixelFormatP->greenbits = g; + pixelFormatP->bluebits = b; + } +} + + + +static void +readDirectTransparentColor(FILE * const ifP, + Color_s * const colorP) { + + unsigned char r, g, b; + + pm_readcharu(ifP, &r); + pm_readcharu(ifP, &g); + pm_readcharu(ifP, &b); + + *colorP = (r << 16) | (g << 8) | (b << 0); +} + + + +static void +readDirectInfoType(FILE * const ifP, + struct palmHeader const palmHeader, + struct directColorInfo * const directInfoTypeP) { +/*---------------------------------------------------------------------------- + Read the Palm Bitmap Direct Info Type section, if any. + + The Direct Info Type section is a section of a pre-Version 3 direct + color Palm Bitmap that tells how to interpret the direct color + raster. + + Return an undefined value as *directInfoTypeP if there is no such + section in this Palm Bitmap. +-----------------------------------------------------------------------------*/ + if ((palmHeader.directColor) && palmHeader.pixelSize != 16) + pm_error("The image is of the direct color type, but has %u " + "bits per pixel. The only kind of direct color images " + "this program understands are 16 bit ones.", + palmHeader.pixelSize); + + if (palmHeader.version == 3) { + /* All direct color info is in the header, because it'sversion + 3 encoding. No Direct Info Type section. + */ + } else { + if (palmHeader.directColor) { + unsigned char padding; + + readRgbFormat(ifP, &directInfoTypeP->pixelFormat); + + pm_readcharu(ifP, &padding); + pm_readcharu(ifP, &padding); + + readDirectTransparentColor(ifP, + &directInfoTypeP->transparentColor); + } else { + /* Not a direct color image; no Direct Info Type section. */ + } + } +} + + + +static void +readColormap(FILE * const ifP, + struct palmHeader const palmHeader, + Colormap * const colormapP) { +/*---------------------------------------------------------------------------- + Read the colormap, if any from the Palm Bitmap. + + If the image described by 'palmHeader' doesn't have a colormap, + return an undefined value as *colormapP. +-----------------------------------------------------------------------------*/ + if (palmHeader.hasColormap) + *colormapP = palmcolor_read_colormap(ifP); +} + + + +static void +getColorInfo(struct palmHeader const palmHeader, + struct directColorInfo const directInfoType, + Colormap const colormapFromImage, + Colormap * const colormapP, + unsigned int * const ncolorsP, + struct directColorInfo * const directColorInfoP) { +/*---------------------------------------------------------------------------- + Gather color encoding information from the various sources. + + Note that 'directInfoType' and 'colormapFromImage' are meaningful only + with certain values of 'palmHeader'. + + If it's a version 3 direct color, the pixel format must be "565". +-----------------------------------------------------------------------------*/ + if (palmHeader.version == 3 && palmHeader.directColor) { + *colormapP = NULL; + + assert(palmHeader.pixelFormat == PALM_FORMAT_565); + + directColorInfoP->pixelFormat.redbits = 5; + directColorInfoP->pixelFormat.greenbits = 6; + directColorInfoP->pixelFormat.bluebits = 5; + /* FIXME Just guessing here ... */ + directColorInfoP->transparentColor = + (((palmHeader.transparentValue >> 11) & 0x1F) << 16) | + (((palmHeader.transparentValue >> 5) & 0x3F) << 8) | + (((palmHeader.transparentValue >> 0) & 0x1F) << 0); + } else if (palmHeader.directColor) { + *colormapP = NULL; + *directColorInfoP = directInfoType; + } else if (palmHeader.hasColormap) + *colormapP = colormapFromImage; + else if (palmHeader.pixelSize >= 8) { + Colormap colormap; + colormap = palmcolor_build_default_8bit_colormap(); + qsort(colormap->color_entries, colormap->ncolors, + sizeof(Color_s), palmcolor_compare_indices); + *colormapP = colormap; + } else + *colormapP = NULL; + + *ncolorsP = 1 << palmHeader.pixelSize; +} + + + +static void +doTransparent(FILE * const ofP, + bool const hasTransparency, + bool const directColor, + unsigned char const transparentIndex, + unsigned char const pixelSize, + Colormap const colormap, + struct directColorInfo const directColorInfo) { +/*---------------------------------------------------------------------------- + Generate a PNM comment on *ofP telling what color in the raster is + supposed to be transparent. + + Note that PNM itself doesn't have any way to represent transparency. + (But this program could be converted to a PAM program and use the + RGB_ALPHA and GRAYSCALE_ALPHA tuple types). +-----------------------------------------------------------------------------*/ + if (hasTransparency) { + if (colormap) { + Color_s const color = transparentIndex << 24; + Color const actualColor = (bsearch(&color, + colormap->color_entries, + colormap->ncolors, + sizeof(color), + palmcolor_compare_indices)); + fprintf(ofP, "#%02x%02x%02x\n", + (unsigned int) ((*actualColor >> 16) & 0xFF), + (unsigned int) ((*actualColor >> 8) & 0xFF), + (unsigned int) ((*actualColor >> 0) & 0xFF)); + } else if (directColor) { + Color_s const color = directColorInfo.transparentColor; + fprintf(ofP, "#%02x%02x%02x\n", + (unsigned int)((color >> 16) & 0xFF), + (unsigned int)((color >> 8) & 0xFF), + (unsigned int)((color >> 0) & 0xFF)); + } else { + unsigned int const maxval = pm_bitstomaxval(pixelSize); + unsigned int const grayval = + ((maxval - transparentIndex) * 256) / maxval; + fprintf(ofP, "#%02x%02x%02x\n", grayval, grayval, grayval); + } + } +} + + + +static void +createHistogram(unsigned int const ncolors, + unsigned int ** const seenP) { + + unsigned int * seen; + + MALLOCARRAY(seen, ncolors); + if (!seen) + pm_error("Can't allocate array for keeping track of " + "how many pixels of each of %u colors are in the image.", + ncolors); + + { + /* Initialize the counter for each color to zero */ + unsigned int i; + for (i = 0; i < ncolors; ++i) + seen[i] = 0; + } + *seenP = seen; +} + + + +static void +readScanlineRow(FILE * const ifP, + unsigned char * const palmrow, + unsigned char * const lastrow, + unsigned int const bytesPerRow, + bool const firstRow) { + + unsigned int j; + + for (j = 0; j < bytesPerRow; j += 8) { + unsigned char diffmask; + /* A mask telling whether each of the 8 raster bytes indexed + j through j+7 is the same as in the previous row ('lastrow') + or is to be read from the file. Bit 0 of the mask refers + to byte j, Bit 1 to byte j + 1, etc. + */ + unsigned int byteCount; + /* How many bytes are covered by 'diffmask'. Normally 8, but + at the end of the row, could be less. + */ + unsigned int k; + + pm_readcharu(ifP, &diffmask); + byteCount = MIN(bytesPerRow - j, 8); + + for (k = 0; k < byteCount; ++k) { + /* the first row cannot be compressed */ + if (firstRow || ((diffmask & (1 << (7 - k))) != 0)) { + unsigned char inval; + pm_readcharu(ifP, &inval); + palmrow[j + k] = inval; + } else + palmrow[j + k] = lastrow[j + k]; + } + } + memcpy(lastrow, palmrow, bytesPerRow); +} + + + +static void +readRleRow(FILE * const ifP, + unsigned char * const palmrow, + unsigned int const bytesPerRow) { + + unsigned int j; + + for (j = 0; j < bytesPerRow; ) { + unsigned char incount; + unsigned char inval; + + pm_readcharu(ifP, &incount); + if (incount == 0) + pm_error("Invalid (zero) count in RLE compression."); + if (j + incount > bytesPerRow) + pm_error("Bytes in RLE compressed row exceed bytes per row. " + "Bytes per row is %u. A run length of %u bytes " + "pushes the bytes in this row up to %u bytes (and then" + "we gave up.", bytesPerRow, incount, j + incount); + pm_readcharu(ifP, &inval); + memset(palmrow + j, inval, incount); + j += incount; + } +} + + + +static void +readPackBitsRow(FILE * const ifP, + unsigned char * const palmrow, + unsigned int const bytesPerRow) { + + unsigned int j; + + for (j = 0; j < bytesPerRow; ) { + char incount; + pm_readchar(ifP, &incount); + if (incount < 0) { + /* How do we handle incount == -128 ? */ + unsigned int const runlength = -incount + 1; + unsigned char inval; + pm_readcharu(ifP, &inval); + memset(palmrow + j, inval, runlength); + j += runlength; + } else { + unsigned int const nonrunlength = incount + 1; + unsigned int k; + for (k = 0; k < nonrunlength; ++k) { + unsigned char inval; + pm_readcharu(ifP, &inval); + palmrow[j + k] = inval; + } + j += nonrunlength; + } + if (j > bytesPerRow) + pm_error("Bytes in PackBits compressed row exceed bytes per row."); + } +} + + + +static void +readUncompressedRow(FILE * const ifP, + unsigned char * const palmrow, + unsigned int const bytesPerRow) { + + int bytesRead; + + bytesRead = fread(palmrow, 1, bytesPerRow, ifP); + if (bytesRead != bytesPerRow) + pm_error("Error reading Palm file. Short read."); +} + + + +static void +readDecompressedRow(FILE * const ifP, + unsigned char * const palmrow, + unsigned char * const lastrow, + enum palmCompressionType const compressionType, + unsigned int const bytesPerRow, + bool const firstRow) { +/*---------------------------------------------------------------------------- + Read a row from Palm file 'ifP', in uncompressed form (i.e. decompress if + necessary). Assume the row contains 'bytesPerRow' uncompressed bytes, + compressed according to 'compressionType'. Return the data at 'palmrow'. + + 'firstRow' means decompress it as if it is the first row of the image + (some compression schemes transform the first row differently from the + rest, because each row depends on the row before it). + + If 'compressionType' is COMPRESSION_SCANLINE, (which means + transformation of a row depends on the contents of the row before + it), then 'lastRow' is as input the uncompressed contents of the + previous row (undefined if 'firstRow' is true). In that case, we + modify 'lastrow' to contain a copy of 'palmrow' (so Caller can + conveniently use it to read the next row). + + If 'compressionType' is not COMPRESSION_SCANLINE, 'lastrow' is + undefined both as input and output. +-----------------------------------------------------------------------------*/ + switch (compressionType) { + case COMPRESSION_RLE: + readRleRow(ifP, palmrow, bytesPerRow); + break; + case COMPRESSION_SCANLINE: + readScanlineRow(ifP, palmrow, lastrow, bytesPerRow, firstRow); + break; + case COMPRESSION_PACKBITS: + readPackBitsRow(ifP, palmrow, bytesPerRow); + break; + case COMPRESSION_NONE: + readUncompressedRow(ifP, palmrow, bytesPerRow); + break; + } +} + + + +static void +convertRowToPnmDirect(const unsigned char * const palmrow, + xel * const xelrow, + unsigned int const cols, + xelval const maxval, + unsigned int * const seen) { + + /* There's a problem with this. Take the Palm 16-bit + direct color. That's 5 bits for the red, 6 for the + green, and 5 for the blue. So what should the MAXVAL + be? I decided to use 255 (8 bits) for everything, + since that's the theoretical max of the number of bits + in any one color, according to Palm. So the Palm color + 0xFFFF (white) would be red=0x1F, green=0x3F, and + blue=0x1F. How do we promote those colors? Simple + shift would give us R=248,G=252,B=248; which is + slightly green. Hardly seems right. + + So I've perverted the math a bit. Each color value is + multiplied by 255, then divided by either 31 (red or + blue) or 63 (green). That's the right way to do it + anyway. + */ + + const unsigned char *inbyte; + unsigned int j; + + for (inbyte = palmrow, j = 0; j < cols; ++j) { + unsigned int inval; + inval = *inbyte++ << 8; + inval |= *inbyte++; + + if (seen) + ++seen[inval]; + + PPM_ASSIGN(xelrow[j], + (((inval >> 11) & 0x1F) * maxval) / 0x1F, + (((inval >> 5) & 0x3F) * maxval) / 0x3F, + (((inval >> 0) & 0x1F) * maxval) / 0x1F + ); + } +} + + + +static void +convertRowToPnmNotDirect(const unsigned char * const palmrow, + xel * const xelrow, + unsigned int const cols, + Colormap const colormap, + xelval * const graymap, + unsigned int * const seen, + unsigned int const pixelSize) { + + unsigned int const mask = (1 << pixelSize) - 1; + + const unsigned char *inbyte; + unsigned int inbit; + unsigned int j; + + inbit = 8 - pixelSize; + inbyte = palmrow; + for (j = 0; j < cols; ++j) { + short const color = ((*inbyte) & (mask << inbit)) >> inbit; + if (seen) + ++seen[color]; + + if (colormap) { + Color_s const color2 = color << 24; + Color const actualColor = (bsearch (&color2, + colormap->color_entries, + colormap->ncolors, + sizeof(color2), + palmcolor_compare_indices)); + PPM_ASSIGN(xelrow[j], + (*actualColor >> 16) & 0xFF, + (*actualColor >> 8) & 0xFF, + (*actualColor >> 0) & 0xFF); + } else + PNM_ASSIGN1(xelrow[j], graymap[color]); + + if (!inbit) { + ++inbyte; + inbit = 8 - pixelSize; + } else + inbit -= pixelSize; + } +} + + + +static void +writePnm(FILE * const ofP, + struct palmHeader const palmHeader, + FILE * const ifP, + Colormap const colormap, + xelval * const graymap, + unsigned int const nColors, + int const format, + xelval const maxval, + unsigned int ** const seenP) { + + int const cols = palmHeader.cols; + int const rows = palmHeader.rows; + + unsigned char * palmrow; + unsigned char * lastrow; + xel * xelrow; + unsigned int * seen; + unsigned int row; + + pnm_writepnminit(ofP, cols, rows, maxval, format, 0); + xelrow = pnm_allocrow(cols); + + /* Read the picture data, one row at a time */ + MALLOCARRAY_NOFAIL(palmrow, palmHeader.bytesPerRow); + MALLOCARRAY_NOFAIL(lastrow, palmHeader.bytesPerRow); + + if (seenP) { + createHistogram(nColors, &seen); + *seenP = seen; + } else + seen = NULL; + + /* We should actually use compressedDataSizeNN for checking the sanity + of the data we're reading ... + */ + if (palmHeader.compressionType != COMPRESSION_NONE) { + if (palmHeader.version < 3) { + short compressedDataSize16; + pm_readbigshort(ifP, &compressedDataSize16); + } else { + long compressedDataSize32; + pm_readbiglong(ifP, &compressedDataSize32); + } + } + + for (row = 0; row < rows; ++row) { + readDecompressedRow(ifP, palmrow, lastrow, + palmHeader.compressionType, + palmHeader.bytesPerRow, + row == 0); + + if (palmHeader.directColor) { + assert(palmHeader.pixelSize == 16); + convertRowToPnmDirect(palmrow, xelrow, cols, maxval, seen); + } else + convertRowToPnmNotDirect(palmrow, xelrow, cols, colormap, graymap, + seen, palmHeader.pixelSize); + + pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0); + } + free(lastrow); + free(palmrow); + pnm_freerow(xelrow); +} + + + +static void +showHistogram(unsigned int * const seen, + Colormap const colormap, + const xelval * const graymap, + unsigned int const ncolors) { + + unsigned int colorIndex; + + for (colorIndex = 0; colorIndex < ncolors; ++colorIndex) { + if (!colormap) + pm_message("%.3d -> %.3d: %d", + colorIndex, graymap[colorIndex], seen[colorIndex]); + else { + Color_s const color = colorIndex << 24; + Color const actualColor = (bsearch(&color, + colormap->color_entries, + colormap->ncolors, + sizeof(color), + palmcolor_compare_indices)); + if (actualColor) + pm_message("%.3d -> %ld,%ld,%ld: %d", colorIndex, + (*actualColor >> 16) & 0xFF, + (*actualColor >> 8) & 0xFF, + (*actualColor & 0xFF), seen[colorIndex]); + } + } +} + + + +int +main(int argc, char **argv) { + + struct cmdlineInfo cmdline; + + FILE* ifP; + struct palmHeader palmHeader; + struct directColorInfo directInfoType; + Colormap colormapFromImage; + Colormap colormap; + struct directColorInfo directColorInfo; + int format; + xelval maxval; + unsigned int nColors; + + /* Parse default params */ + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + + readHeader(ifP, cmdline.rendition, &palmHeader); + + readDirectInfoType(ifP, palmHeader, &directInfoType); + + readColormap(ifP, palmHeader, &colormapFromImage); + + determineOutputFormat(palmHeader, &format, &maxval); + + getColorInfo(palmHeader, directInfoType, colormapFromImage, + &colormap, &nColors, &directColorInfo); + + if (cmdline.verbose) + reportPalmHeader(palmHeader, directColorInfo); + + if (cmdline.transparent) + doTransparent(stdout, + palmHeader.hasTransparency, palmHeader.directColor, + palmHeader.transparentIndex, + palmHeader.pixelSize, colormap, directColorInfo); + else { + unsigned int * seen; + xelval * graymap; + + graymap = createGraymap(nColors, maxval); + + writePnm(stdout, + palmHeader, ifP, colormap, graymap, nColors, format, maxval, + cmdline.showhist ? &seen : NULL); + + if (cmdline.showhist) + showHistogram(seen, colormap, graymap, nColors); + + free(graymap); + } + pm_close(ifP); + + return 0; +} diff --git a/converter/other/pnmtopalm/pnmtopalm.c b/converter/other/pnmtopalm/pnmtopalm.c new file mode 100644 index 00000000..3b9eec8f --- /dev/null +++ b/converter/other/pnmtopalm/pnmtopalm.c @@ -0,0 +1,1235 @@ +/* pnmtopalm.c - read a PNM image and write a Palm Bitmap file + * + * Inspired by and using methods from ppmtoTbmp.c by Ian Goldberg + * <iang@cs.berkeley.edu>, which was based on ppmtopuzz.c by Jef + * Poskanzer, from the netpbm-1mar1994 package. + * + * Mods for multiple bits per pixel were added to ppmtoTbmp.c by + * George Caswell <tetsujin@sourceforge.net> and Bill Janssen + * <bill@janssen.org>. + * + * Major fixes and new capability added by Paul Bolle <pebolle@tiscali.nl> + * in late 2004 / early 2005. + * + * See LICENSE file for licensing information. + * + * + */ + +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits.h> + +#include "pnm.h" +#include "palm.h" +#include "shhopt.h" +#include "mallocvar.h" + +enum compressionType {COMP_NONE, COMP_SCANLINE, COMP_RLE, COMP_PACKBITS}; + +struct cmdline_info { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilespec; /* Filespecs of input files */ + char *transparent; /* -transparent value. Null if unspec */ + unsigned int depth; /* -depth value. 0 if unspec */ + unsigned int maxdepth; /* -maxdepth value. 0 if unspec */ + enum compressionType compression; + unsigned int verbose; + unsigned int colormap; + unsigned int offset; /* -offset specified */ + unsigned int density; /* screen density */ + unsigned int withdummy; +}; + + + +static void +parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optStruct3 opt; /* set by OPTENT3 */ + optEntry *option_def; + unsigned int option_def_index; + + unsigned int transSpec, depthSpec, maxdepthSpec, densitySpec; + unsigned int scanline_compression, rle_compression, packbits_compression; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "transparent", OPT_STRING, + &cmdlineP->transparent, &transSpec, 0); + OPTENT3(0, "depth", OPT_UINT, + &cmdlineP->depth, &depthSpec, 0); + OPTENT3(0, "maxdepth", OPT_UINT, + &cmdlineP->maxdepth, &maxdepthSpec, 0); + OPTENT3(0, "scanline_compression", OPT_FLAG, + NULL, &scanline_compression, 0); + OPTENT3(0, "rle_compression", OPT_FLAG, + NULL, &rle_compression, 0); + OPTENT3(0, "packbits_compression", OPT_FLAG, + NULL, &packbits_compression, 0); + OPTENT3(0, "verbose", OPT_FLAG, + NULL, &cmdlineP->verbose, 0); + OPTENT3(0, "colormap", OPT_FLAG, + NULL, &cmdlineP->colormap, 0); + OPTENT3(0, "offset", OPT_FLAG, + NULL, &cmdlineP->offset, 0); + OPTENT3(0, "density", OPT_UINT, + &cmdlineP->density, &densitySpec, 0); + OPTENT3(0, "withdummy", OPT_FLAG, + NULL, &cmdlineP->withdummy, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have some short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdline_p and others. */ + + if (depthSpec) { + if (cmdlineP->depth != 1 && cmdlineP->depth != 2 + && cmdlineP->depth != 4 && cmdlineP->depth != 8 + && cmdlineP->depth != 16) + pm_error("invalid value for -depth: %u. Valid values are " + "1, 2, 4, 8, and 16", cmdlineP->depth); + } else + cmdlineP->depth = 0; + + if (maxdepthSpec) { + if (cmdlineP->maxdepth != 1 && cmdlineP->maxdepth != 2 + && cmdlineP->maxdepth != 4 && cmdlineP->maxdepth != 8 + && cmdlineP->maxdepth != 16) + pm_error("invalid value for -maxdepth: %u. Valid values are " + "1, 2, 4, 8, and 16", cmdlineP->maxdepth); + } else + cmdlineP->maxdepth = 0; + + if (depthSpec && maxdepthSpec && + cmdlineP->depth > cmdlineP->maxdepth) + pm_error("-depth value (%u) is greater than -maxdepth (%u) value.", + cmdlineP->depth, cmdlineP->maxdepth); + + if (!transSpec) + cmdlineP->transparent = NULL; + + if (densitySpec) { + if (cmdlineP->density != PALM_DENSITY_LOW && + cmdlineP->density != PALM_DENSITY_ONEANDAHALF && + cmdlineP->density != PALM_DENSITY_DOUBLE && + cmdlineP->density != PALM_DENSITY_TRIPLE && + cmdlineP->density != PALM_DENSITY_QUADRUPLE) + pm_error("Invalid value for -density: %d. Valid values are " + "%d, %d, %d, %d and %d.", cmdlineP->density, + PALM_DENSITY_LOW, PALM_DENSITY_ONEANDAHALF, + PALM_DENSITY_DOUBLE, PALM_DENSITY_TRIPLE, + PALM_DENSITY_QUADRUPLE); + } else + cmdlineP->density = PALM_DENSITY_LOW; + + if (cmdlineP->density != PALM_DENSITY_LOW && cmdlineP->withdummy) + pm_error("You can't specify -withdummy with -density value %u. " + "It is valid only with low density (%u)", + cmdlineP->density, PALM_DENSITY_LOW); + + if (cmdlineP->withdummy && !cmdlineP->offset) + pm_error("-withdummy does not make sense without -offset"); + + if (scanline_compression + rle_compression + packbits_compression > 1) + pm_error("You may specify only one of -scanline_compression, " + "-rle_compression, and -packbits_compression"); + else { + if (scanline_compression) + cmdlineP->compression = COMP_SCANLINE; + else if (rle_compression) + cmdlineP->compression = COMP_RLE; + else if (packbits_compression) + cmdlineP->compression = COMP_PACKBITS; + else + cmdlineP->compression = COMP_NONE; + } + + if (argc-1 > 1) + pm_error("This program takes at most 1 argument: the file name. " + "You specified %d", argc-1); + else if (argc-1 > 0) + cmdlineP->inputFilespec = argv[1]; + else + cmdlineP->inputFilespec = "-"; +} + + + +static void +determinePalmFormat(unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + xel ** const xels, + unsigned int const specified_bpp, + unsigned int const max_bpp, + bool const custom_colormap, + bool const verbose, + unsigned int * const bppP, + bool * const directColorP, + Colormap * const colormapP) { + + if (PNM_FORMAT_TYPE(format) == PBM_TYPE) { + if (custom_colormap) + pm_error("You specified -colormap with a black and white input " + "image. -colormap is valid only with color."); + if (specified_bpp) + *bppP = specified_bpp; + else + *bppP = 1; /* no point in wasting bits */ + *directColorP = FALSE; + *colormapP = NULL; + if (verbose) + pm_message("output is black and white"); + } else if (PNM_FORMAT_TYPE(format) == PGM_TYPE) { + /* we can usually handle this one, but may not have enough + pixels. So check... */ + if (custom_colormap) + pm_error("You specified -colormap with a black and white input" + "image. -colormap is valid only with color."); + if (specified_bpp) + *bppP = specified_bpp; + else if (max_bpp && (maxval >= (1 << max_bpp))) + *bppP = max_bpp; + else if (maxval > 16) + *bppP = 4; + else { + /* scale to minimum number of bpp needed */ + for (*bppP = 1; (1 << *bppP) < maxval; *bppP *= 2) + ; + } + if (*bppP > 4) + *bppP = 4; + if (verbose) + pm_message("output is grayscale %d bits-per-pixel", *bppP); + *directColorP = FALSE; + *colormapP = NULL; + } else if (PNM_FORMAT_TYPE(format) == PPM_TYPE) { + + /* We assume that we only get a PPM if the image cannot be + represented as PBM or PGM. There are two options here: either + 8-bit with a colormap, either the standard one or a custom one, + or 16-bit direct color. In the 8-bit case, if "custom_colormap" + is specified (not recommended by Palm) we will put in our own + colormap; otherwise we will assume that the colors have been + mapped to the default Palm colormap by appropriate use of + pnmquant. We try for 8-bit color first, since it works on + more PalmOS devices. + */ + if ((specified_bpp == 16) || + (specified_bpp == 0 && max_bpp == 16)) { + /* we do the 16-bit direct color */ + *directColorP = TRUE; + *colormapP = NULL; + *bppP = 16; + } else if (!custom_colormap) { + /* standard indexed 8-bit color */ + *colormapP = palmcolor_build_default_8bit_colormap(); + *bppP = 8; + if (((specified_bpp != 0) && (specified_bpp != 8)) || + ((max_bpp != 0) && (max_bpp < 8))) + pm_error("Must use depth of 8 for color Palm Bitmap without " + "custom color table."); + *directColorP = FALSE; + if (verbose) + pm_message("Output is color with default colormap at 8 bpp"); + } else { + /* indexed 8-bit color with a custom colormap */ + *colormapP = + palmcolor_build_custom_8bit_colormap(rows, cols, xels); + for (*bppP = 1; (1 << *bppP) < (*colormapP)->ncolors; *bppP *= 2); + if (specified_bpp != 0) { + if (specified_bpp >= *bppP) + *bppP = specified_bpp; + else + pm_error("Too many colors for specified depth. " + "Use pnmquant to reduce."); + } else if ((max_bpp != 0) && (max_bpp < *bppP)) { + pm_error("Too many colors for specified max depth. " + "Use pnmquant to reduce."); + } + *directColorP = FALSE; + if (verbose) + pm_message("Output is color with custom colormap " + "with %d colors at %d bpp", + (*colormapP)->ncolors, *bppP); + } + } else { + pm_error("unknown format 0x%x on input file", (unsigned) format); + } +} + + + +static const char * +formatName(int const format) { + + const char * retval; + + switch(PNM_FORMAT_TYPE(format)) { + case PBM_TYPE: retval = "black and white"; break; + case PGM_TYPE: retval = "grayscale"; break; + case PPM_TYPE: retval = "color"; break; + default: retval = "???"; break; + } + return retval; +} + + + +static void +findTransparentColor(char * const colorSpec, + pixval const newMaxval, + bool const directColor, + pixval const maxval, + Colormap const colormap, + xel * const transcolorP, + unsigned int * const transindexP) { + + *transcolorP = ppm_parsecolor(colorSpec, maxval); + if (!directColor) { + Color_s const temp_color = + ((((PPM_GETR(*transcolorP)*newMaxval) / maxval) << 16) + | (((PPM_GETG(*transcolorP)*newMaxval) / maxval) << 8) + | ((PPM_GETB(*transcolorP)*newMaxval) / maxval)); + Color const found = + (bsearch(&temp_color, + colormap->color_entries, colormap->ncolors, + sizeof(Color_s), palmcolor_compare_colors)); + if (!found) { + pm_error("Specified transparent color %s not found " + "in colormap.", colorSpec); + } else + *transindexP = (*found >> 24) & 0xFF; + } +} + + + +static unsigned int +bitmapVersion(unsigned int const bpp, + bool const colormap, + bool const transparent, + enum compressionType const compression, + unsigned int const density) { +/*---------------------------------------------------------------------------- + Return the version number of the oldest version that can represent + the specified attributes. +-----------------------------------------------------------------------------*/ + unsigned int version; + /* we need Version 1 if we use more than 1 bpp, + Version 2 if we use compression or transparency, + Version 3 if density is 108 or higher + */ + if (density > PALM_DENSITY_LOW) + version = 3; + else if (transparent || compression != COMP_NONE) + version = 2; + else if (bpp > 1 || colormap) + version = 1; + else + version = 0; + + return version; +} + + + +static void +writeCommonHeader(unsigned int const cols, + unsigned int const rows, + unsigned int const rowbytes, + enum compressionType const compression, + bool const colormap, + bool const transparent, + bool const directColor, + unsigned int const bpp, + unsigned int const version) { +/*---------------------------------------------------------------------------- + Write the first 10 bytes of the Palm Bitmap header. + These are common to all encodings (versions 0, 1, 2 and 3). +-----------------------------------------------------------------------------*/ + unsigned short flags; + + if (cols > USHRT_MAX) + pm_error("Too many columns for Palm Bitmap: %u", cols); + pm_writebigshort(stdout, cols); /* width */ + if (rows > USHRT_MAX) + pm_error("Too many columns for Palm Bitmap: %u", rows); + pm_writebigshort(stdout, rows); /* height */ + if (rowbytes > USHRT_MAX) + pm_error("Too many bytes per row for Palm Bitmap: %u", rowbytes); + pm_writebigshort(stdout, rowbytes); + + flags = 0; /* initial value */ + if (compression != COMP_NONE) + flags |= PALM_IS_COMPRESSED_FLAG; + if (colormap) + flags |= PALM_HAS_COLORMAP_FLAG; + if (transparent) + flags |= PALM_HAS_TRANSPARENCY_FLAG; + if (directColor) + flags |= PALM_DIRECT_COLOR_FLAG; + pm_writebigshort(stdout, flags); + assert(bpp <= UCHAR_MAX); + fputc(bpp, stdout); + + fputc(version, stdout); +} + + + +static unsigned char +compressionFieldValue(enum compressionType const compression) { + + unsigned char retval; + + switch (compression) { + case COMP_SCANLINE: + retval = PALM_COMPRESSION_SCANLINE; + break; + case COMP_RLE: + retval = PALM_COMPRESSION_RLE; + break; + case COMP_PACKBITS: + retval = PALM_COMPRESSION_PACKBITS; + break; + case COMP_NONE: + retval = 0x00; /* empty */ + break; + } + return retval; +} + + + +static void +writeRemainingHeaderLow(unsigned int const nextDepthOffset, + unsigned int const transindex, + enum compressionType const compression, + unsigned int const bpp) { +/*---------------------------------------------------------------------------- + Write last 6 bytes of a low density Palm Bitmap header. +-----------------------------------------------------------------------------*/ + if (nextDepthOffset > USHRT_MAX) + pm_error("Image too large for Palm Bitmap"); + + pm_writebigshort(stdout, nextDepthOffset); + + if (bpp != 16) { + assert(transindex <= UCHAR_MAX); + fputc(transindex, stdout); /* transparent index */ + } else + fputc(0, stdout); /* the DirectInfoType will hold this info */ + + fputc(compressionFieldValue(compression), stdout); + + pm_writebigshort(stdout, 0); /* reserved by Palm */ +} + + + +static void +writeRemainingHeaderHigh(unsigned int const bpp, + enum compressionType const compression, + unsigned int const density, + xelval const maxval, + bool const transparent, + xel const transcolor, + unsigned int const transindex, + unsigned int const nextBitmapOffset) { +/*---------------------------------------------------------------------------- + Write last 16 bytes of a high density Palm Bitmap header. +-----------------------------------------------------------------------------*/ + if ((nextBitmapOffset >> 31) > 1) + pm_error("Image too large for Palm Bitmap. nextBitmapOffset " + "value doesn't fit in 4 bytes"); + + fputc(0x18, stdout); /* size of this high density header */ + + if (bpp != 16) + fputc(PALM_FORMAT_INDEXED, stdout); + else + fputc(PALM_FORMAT_565, stdout); + + fputc(0x00, stdout); /* unused */ + + fputc(compressionFieldValue(compression), stdout); + + pm_writebigshort(stdout, density); + + if (transparent) { + if (bpp == 16) { + /* Blind guess here */ + fputc(0, stdout); + fputc((PPM_GETR(transcolor) * 255) / maxval, stdout); + fputc((PPM_GETG(transcolor) * 255) / maxval, stdout); + fputc((PPM_GETB(transcolor) * 255) / maxval, stdout); + } else { + assert(transindex <= UCHAR_MAX); + fputc(0, stdout); + fputc(0, stdout); + fputc(0, stdout); + fputc(transindex, stdout); /* transparent index */ + } + } else + pm_writebiglong(stdout, 0); + + pm_writebiglong(stdout, nextBitmapOffset); +} + + + +static void +writeDummy() { +/*---------------------------------------------------------------------------- + Write a dummy Palm Bitmap header. This is a 16 byte header, of + type version 1 and with (only) pixelSize set to 0xFF. + + An old viewer will see this as invalid due to the pixelSize, and stop + reading the stream. A new viewer will recognize this for what it is + (a dummy header designed to stop old viewers from reading further in + the stream) and continue reading the stream. Presumably, what follows + in the stream is understandable by a new viewer, but would confuse an + old one. +-----------------------------------------------------------------------------*/ + pm_writebiglong(stdout, 0x00); + pm_writebiglong(stdout, 0x00); + fputc(0xFF, stdout); /* pixelSize */ + fputc(0x01, stdout); /* version */ + pm_writebigshort(stdout, 0x00); + pm_writebiglong(stdout, 0x00); +} + + + +static void +writeColormap(bool const explicitColormap, + Colormap const colormap, + bool const directColor, + unsigned int const bpp, + bool const transparent, + xel const transcolor, + xelval const maxval, + unsigned int const version) { + + /* if there's a colormap, write it out */ + if (explicitColormap) { + unsigned int row; + if (!colormap) + pm_error("Internal error: user specified -colormap, but we did " + "not generate a colormap."); + qsort (colormap->color_entries, colormap->ncolors, + sizeof(Color_s), palmcolor_compare_indices); + pm_writebigshort( stdout, colormap->ncolors ); + for (row = 0; row < colormap->ncolors; ++row) + pm_writebiglong (stdout, colormap->color_entries[row]); + } + + if (directColor && (version < 3)) { + /* write the DirectInfoType (8 bytes) */ + if (bpp == 16) { + fputc(5, stdout); /* # of bits of red */ + fputc(6, stdout); /* # of bits of green */ + fputc(5, stdout); /* # of bits of blue */ + fputc(0, stdout); /* reserved by Palm */ + } else + pm_error("Don't know how to create %d bit DirectColor bitmaps.", + bpp); + if (transparent) { + fputc(0, stdout); + fputc((PPM_GETR(transcolor) * 255) / maxval, stdout); + fputc((PPM_GETG(transcolor) * 255) / maxval, stdout); + fputc((PPM_GETB(transcolor) * 255) / maxval, stdout); + } else + pm_writebiglong(stdout, 0); /* no transparent color */ + } +} + + + +static void +computeRawRowDirectColor(const xel * const xelrow, + unsigned int const cols, + xelval const maxval, + unsigned char * const rowdata) { + + unsigned int col; + unsigned char *outptr; + + for (col = 0, outptr = rowdata; col < cols; ++col) { + unsigned int const color = + ((((PPM_GETR(xelrow[col])*31)/maxval) << 11) | + (((PPM_GETG(xelrow[col])*63)/maxval) << 5) | + ((PPM_GETB(xelrow[col])*31)/maxval)); + *outptr++ = (color >> 8) & 0xFF; + *outptr++ = color & 0xFF; + } +} + + + +static void +computeRawRowNonDirect(const xel * const xelrow, + unsigned int const cols, + xelval const maxval, + unsigned int const bpp, + Colormap const colormap, + unsigned int const newMaxval, + unsigned char * const rowdata) { + + unsigned int col; + unsigned char *outptr; + unsigned char outbyte; + /* Accumulated bits to be output */ + unsigned char outbit; + /* The lowest bit number we want to access for this pixel */ + + outbyte = 0x00; + outptr = rowdata; + + for (outbit = 8 - bpp, col = 0; col < cols; ++col) { + unsigned int color; + if (!colormap) { + /* we assume grayscale, and use simple scaling */ + color = (PNM_GET1(xelrow[col]) * newMaxval)/maxval; + if (color > newMaxval) + pm_error("oops. Bug in color re-calculation code. " + "color of %u.", color); + color = newMaxval - color; /* note grayscale maps are inverted */ + } else { + Color_s const temp_color = + ((((PPM_GETR(xelrow[col])*newMaxval)/maxval)<<16) + | (((PPM_GETG(xelrow[col])*newMaxval)/maxval)<<8) + | (((PPM_GETB(xelrow[col])*newMaxval)/maxval))); + Color const found = (bsearch (&temp_color, + colormap->color_entries, + colormap->ncolors, + sizeof(Color_s), + palmcolor_compare_colors)); + if (!found) { + pm_error("Color %d:%d:%d not found in colormap. " + "Try using pnmquant to reduce the " + "number of colors.", + PPM_GETR(xelrow[col]), + PPM_GETG(xelrow[col]), + PPM_GETB(xelrow[col])); + } + color = (*found >> 24) & 0xFF; + } + + if (color > newMaxval) + pm_error("oops. Bug in color re-calculation code. " + "color of %u.", color); + outbyte |= (color << outbit); + if (outbit == 0) { + /* Bit buffer is full. Flush to to rowdata. */ + *outptr++ = outbyte; + outbyte = 0x00; + outbit = 8 - bpp; + } else + outbit -= bpp; + } + if ((cols % (8 / bpp)) != 0) { + /* Flush bits remaining in the bit buffer to rowdata */ + *outptr++ = outbyte; + } +} + + +struct seqBuffer { +/*---------------------------------------------------------------------------- + A buffer to which one can write bytes sequentially. +-----------------------------------------------------------------------------*/ + char * buffer; + unsigned int allocatedSize; + unsigned int occupiedSize; +}; + + +static void +createBuffer(struct seqBuffer ** const bufferPP) { + + struct seqBuffer * bufferP; + + MALLOCVAR_NOFAIL(bufferP); + + bufferP->allocatedSize = 4096; + MALLOCARRAY(bufferP->buffer, bufferP->allocatedSize); + if (bufferP == NULL) + pm_error("Unable to allocate %u bytes of buffer", + bufferP->allocatedSize); + bufferP->occupiedSize = 0; + + *bufferPP = bufferP; +} + + + +static void +destroyBuffer(struct seqBuffer * const bufferP) { + + free(bufferP->buffer); + free(bufferP); +} + + + +static void +addByteToBuffer(struct seqBuffer * const bufferP, + unsigned char const newByte) { + + assert(bufferP->allocatedSize >= bufferP->occupiedSize); + + if (bufferP->allocatedSize == bufferP->occupiedSize) { + bufferP->allocatedSize *= 2; + REALLOCARRAY(bufferP->buffer, bufferP->allocatedSize); + if (bufferP->buffer == NULL) + pm_error("Couldn't (re)allocate %u bytes of memory " + "for buffer.", bufferP->allocatedSize); + } + bufferP->buffer[bufferP->occupiedSize++] = newByte; +} + + + +static unsigned int +bufferLength(struct seqBuffer * const bufferP) { + return bufferP->occupiedSize; +} + + + +static void +writeOutBuffer(struct seqBuffer * const bufferP, + FILE * const fileP) { + + size_t bytesWritten; + + bytesWritten = fwrite(bufferP->buffer, sizeof(char), + bufferP->occupiedSize, fileP); + + if (bytesWritten != bufferP->occupiedSize) + pm_error("fwrite() failed to write out the buffer."); +} + + + +static void +copyRowToBuffer(const unsigned char * const rowdata, + unsigned int const rowbytes, + struct seqBuffer * const rasterBufferP) { + + unsigned int pos; + for (pos = 0; pos < rowbytes; ++pos) + addByteToBuffer(rasterBufferP, rowdata[pos]); +} + + + +static void +scanlineCompressAndBufferRow(const unsigned char * const rowdata, + unsigned int const rowbytes, + struct seqBuffer * const rasterBufferP, + const unsigned char * const lastrow) { +/*---------------------------------------------------------------------------- + Take the raw Palm Bitmap row 'rowdata', which is 'rowbytes' + columns, and add the scanline-compressed representation of it to + the buffer with handle 'rasterBufferP'. + + 'lastrow' is the raw contents of the row immediately before the one + we're compressing -- i.e. we compress with respect to that row. This + function does not work on the first row of an image. +-----------------------------------------------------------------------------*/ + unsigned int pos; + + for (pos = 0; pos < rowbytes; pos += 8) { + unsigned int const limit = MIN(rowbytes - pos, 8); + + unsigned char map; + /* mask indicating which of the next 8 pixels are + different from the previous row, and therefore present + in the file immediately following the map byte. + */ + unsigned char differentPixels[8]; + unsigned char *outptr; + unsigned char outbit; + + for (outbit = 0, map = 0x00, outptr = differentPixels; + outbit < limit; + ++outbit) { + if (!lastrow + || (lastrow[pos + outbit] != rowdata[pos + outbit])) { + map |= (1 << (7 - outbit)); + *outptr++ = rowdata[pos + outbit]; + } + } + + addByteToBuffer(rasterBufferP, map); + { + unsigned int j; + for (j = 0; j < (outptr - differentPixels); ++j) + addByteToBuffer(rasterBufferP, differentPixels[j]); + } + } +} + + + +static void +rleCompressAndBufferRow(const unsigned char * const rowdata, + unsigned int const rowbytes, + struct seqBuffer * const rasterBufferP) { +/*---------------------------------------------------------------------------- + Take the raw Palm Bitmap row 'rowdata', which is 'rowbytes' bytes, + and add the rle-compressed representation of it to the buffer with + handle 'rasterBufferP'. +-----------------------------------------------------------------------------*/ + unsigned int pos; + + /* we output a count of the number of bytes a value is + repeated, followed by that byte value + */ + pos = 0; + while (pos < rowbytes) { + unsigned int repeatcount; + for (repeatcount = 1; + repeatcount < (rowbytes - pos) && repeatcount < 255; + ++repeatcount) + if (rowdata[pos + repeatcount] != rowdata[pos]) + break; + + addByteToBuffer(rasterBufferP, repeatcount); + addByteToBuffer(rasterBufferP, rowdata[pos]); + pos += repeatcount; + } +} + + + +static void +computeNextPackbitsRun(const unsigned char * const rowdata, + unsigned int const rowbytes, + unsigned int const startPos, + unsigned int * const nextPosP, + unsigned char * const output, + int * const countP) { + + unsigned int pos; + int count; + + pos = startPos; + count = 0; + + if (rowdata[pos] == rowdata[pos + 1]) { + ++pos; + --count; + while ((count > -127) && (pos < (rowbytes - 1)) && + (rowdata[pos] == rowdata[pos + 1])) { + ++pos; + --count; + } + ++pos; /* push pos past end of this run */ + } else { + output[count] = rowdata[pos]; + ++pos; + while ((count < 127) && (pos < (rowbytes - 1)) && + (rowdata[pos] != rowdata[pos + 1])) { + ++count; + output[count] = rowdata[pos]; + ++pos; + } + /* trailing literal */ + if ((count < 127) && (pos == (rowbytes - 1)) && + (rowdata[pos - 1] != rowdata[pos])) { + ++count; + output[count] = rowdata[pos]; + ++pos; + } + } + *nextPosP = pos; + *countP = count; +} + + + +static void +addPackbitsRunToBuffer(const unsigned char * const rowdata, + unsigned int const rowbytes, + unsigned int const pos, + unsigned char * const output, + int const count, + struct seqBuffer * const rasterBufferP) { + + addByteToBuffer(rasterBufferP, (unsigned char)(signed char)count); + if (count < 0) { + addByteToBuffer(rasterBufferP, rowdata[pos - 1]); + } else { + unsigned int j; + for (j = 0; j <= count; j++) + addByteToBuffer(rasterBufferP, output[j]); + } + + if (pos == (rowbytes - 1) && (rowdata[pos - 1] != rowdata[pos])) { + /* orphaned byte, treat as literal */ + addByteToBuffer(rasterBufferP, 0); + addByteToBuffer(rasterBufferP, rowdata[pos]); + } +} + + + +static void +packbitsCompressAndBufferRow(const unsigned char * const rowdata, + unsigned int const rowbytes, + struct seqBuffer * const rasterBufferP) { +/*---------------------------------------------------------------------------- + Take the raw Palm Bitmap row 'rowdata', which is 'rowbytess' bytes, and + add the packbits-compressed representation of it to the buffer + with handle 'rasterBufferP'. +-----------------------------------------------------------------------------*/ + unsigned int position; + /* byte position within the row */ + + position = 0; /* Start at beginning of row */ + + while (position < rowbytes - 1) { + unsigned char output[128]; + int count; + + computeNextPackbitsRun(rowdata, rowbytes, position, + &position, output, &count); + + addPackbitsRunToBuffer(rowdata, rowbytes, position, output, count, + rasterBufferP); + } +} + + + +static void +bufferRowFromRawRowdata(const unsigned char * const rowdata, + unsigned int const rowbytes, + enum compressionType const compression, + const unsigned char * const lastrow, + struct seqBuffer * const rasterBufferP) { +/*---------------------------------------------------------------------------- + Starting with a raw (uncompressed) Palm raster line, do the + compression identified by 'compression' and add the compressed row + to the buffer with handle 'rasterBufferP'. + + If 'compression' indicates scanline compression, 'lastrow' is the + row immediately preceding this one in the image (and this function + doesn't work on the first row of an image). Otherwise, 'lastrow' + is meaningless. +-----------------------------------------------------------------------------*/ + switch (compression) { + case COMP_NONE: + copyRowToBuffer(rowdata, rowbytes, rasterBufferP); + break; + case COMP_SCANLINE: + scanlineCompressAndBufferRow(rowdata, rowbytes, rasterBufferP, + lastrow); + break; + case COMP_RLE: + rleCompressAndBufferRow(rowdata, rowbytes, rasterBufferP); + break; + case COMP_PACKBITS: + packbitsCompressAndBufferRow(rowdata, rowbytes, rasterBufferP); + break; + } +} + + + +static void +bufferRow(const xel * const xelrow, + unsigned int const cols, + xelval const maxval, + unsigned int const rowbytes, + unsigned int const bpp, + unsigned int const newMaxval, + enum compressionType const compression, + bool const directColor, + Colormap const colormap, + unsigned char * const rowdata, + unsigned char * const lastrow, + struct seqBuffer * const rasterBufferP) { +/*---------------------------------------------------------------------------- + Add a row of the Palm Bitmap raster to buffer 'rasterBufferP'. + + 'xelrow' is the image contents of row. It is 'cols' columns wide. + + If 'compression' indicates scanline compression, 'lastrow' is the + row immediately preceding this one in the image (and this function + doesn't work on the first row of an image). Otherwise, 'lastrow' + is meaningless. + + 'rowdata' is a work buffer 'rowbytes' in size. +-----------------------------------------------------------------------------*/ + if (directColor) + computeRawRowDirectColor(xelrow, cols, maxval, rowdata); + else + computeRawRowNonDirect(xelrow, cols, maxval, bpp, colormap, newMaxval, + rowdata); + + bufferRowFromRawRowdata(rowdata, rowbytes, compression, + lastrow, rasterBufferP); +} + + + +static void +bufferRaster(xel ** const xels, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + unsigned int const rowbytes, + unsigned int const bpp, + unsigned int const newMaxval, + enum compressionType const compression, + bool const directColor, + Colormap const colormap, + struct seqBuffer ** const rasterBufferPP) { + + unsigned char * rowdata; + unsigned char * lastrow; + unsigned int row; + + createBuffer(rasterBufferPP); + + MALLOCARRAY_NOFAIL(rowdata, rowbytes); + MALLOCARRAY_NOFAIL(lastrow, rowbytes); + + /* And write out the data. */ + for (row = 0; row < rows; ++row) { + bufferRow(xels[row], cols, maxval, rowbytes, bpp, newMaxval, + compression, + directColor, colormap, rowdata, row > 0 ? lastrow : NULL, + *rasterBufferPP); + + if (compression == COMP_SCANLINE) + memcpy(lastrow, rowdata, rowbytes); + } + free(lastrow); + free(rowdata); +} + + + +static void +computeOffsetStuff(bool const offsetWanted, + unsigned int const version, + bool const directColor, + enum compressionType const compression, + bool const colormap, + unsigned int const colormapColorCount, + unsigned int const sizePlusRasterSize, + unsigned int * const nextDepthOffsetP, + unsigned int * const nextBitmapOffsetP, + unsigned int * const padBytesRequiredP) { + + if (offsetWanted) { + /* Offset is measured in 4-byte words (double words in + Intel/Microsoft terminology). Account for header, + colormap, and raster size and round up + */ + unsigned int const headerSize = ((version < 3) ? 16 : 24); + unsigned int const colormapSize = + (colormap ? (2 + colormapColorCount * 4) : 0); + if (version < 3) { + unsigned int const directSize = + (directColor && version < 3) ? 8 : 0; + if (compression != COMP_NONE && sizePlusRasterSize > USHRT_MAX) + pm_error("Oversized compressed bitmap: %u bytes", + sizePlusRasterSize); + *padBytesRequiredP = 4 - (sizePlusRasterSize + headerSize + + directSize + colormapSize) % 4; + *nextDepthOffsetP = + (sizePlusRasterSize + headerSize + + directSize + colormapSize + *padBytesRequiredP) / 4; + } else { + if (compression != COMP_NONE && (sizePlusRasterSize >> 31) > 1) + pm_error("Oversized compressed bitmap: %u bytes", + sizePlusRasterSize); + /* Does version 3 need padding? Probably won't hurt */ + *padBytesRequiredP = 4 - (sizePlusRasterSize + headerSize + + colormapSize) % 4; + *nextBitmapOffsetP = sizePlusRasterSize + headerSize + + colormapSize + *padBytesRequiredP; + } + } else { + *padBytesRequiredP = 0; + *nextDepthOffsetP = 0; + *nextBitmapOffsetP = 0; + } +} + + + +static void +writeRasterSize(unsigned int const sizePlusRasterSize, + unsigned int const version, + FILE * const fileP) { +/*---------------------------------------------------------------------------- + Write to file 'fileP' a raster size field for a Palm Bitmap version + 'version' header, indicating 'sizePlusRasterSize' bytes. +-----------------------------------------------------------------------------*/ + if (version < 3) + pm_writebigshort(fileP, sizePlusRasterSize); + else + pm_writebiglong(fileP, sizePlusRasterSize); +} + + + +static void +writeBitmap(xel ** const xels, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + unsigned int const rowbytes, + unsigned int const bpp, + unsigned int const newMaxval, + enum compressionType const compression, + bool const transparent, + bool const directColor, + bool const offsetWanted, + bool const hasColormap, + Colormap const colormap, + unsigned int const transindex, + xel const transcolor, + unsigned int const version, + unsigned int const density, + bool const withdummy) { + + unsigned int sizePlusRasterSize; + unsigned int nextDepthOffset; + unsigned int nextBitmapOffset; + /* Offset from the beginning of the image we write to the beginning + of the next one, assuming user writes another one following this + one. + nextDepthOffset is used in encodings 1, 2 and is in 4 byte words + nextBitmapOffset is used in encoding 3, is in 4 bytes + */ + unsigned int padBytesRequired; + /* Number of bytes of padding we need to put after the image in + order to align properly for User to add the next image to the + stream. + */ + struct seqBuffer * rasterBufferP; + + writeCommonHeader(cols, rows, rowbytes, compression, hasColormap, + transparent, directColor, bpp, version); + + bufferRaster(xels, cols, rows, maxval, rowbytes, bpp, newMaxval, + compression, directColor, colormap, &rasterBufferP); + + /* rasterSize itself takes 2 or 4 bytes */ + if (version < 3) + sizePlusRasterSize = 2 + bufferLength(rasterBufferP); + else + sizePlusRasterSize = 4 + bufferLength(rasterBufferP); + + computeOffsetStuff(offsetWanted, version, directColor, compression, + hasColormap, hasColormap ? colormap->ncolors : 0, + sizePlusRasterSize, + &nextDepthOffset, &nextBitmapOffset, + &padBytesRequired); + + if (version < 3) + writeRemainingHeaderLow(nextDepthOffset, transindex, compression, bpp); + else + writeRemainingHeaderHigh(bpp, compression, density, + maxval, transparent, transcolor, + transindex, nextBitmapOffset); + + writeColormap(hasColormap, colormap, directColor, bpp, + transparent, transcolor, maxval, version); + + if (compression != COMP_NONE) + writeRasterSize(sizePlusRasterSize, version, stdout); + + writeOutBuffer(rasterBufferP, stdout); + + destroyBuffer(rasterBufferP); + + { + unsigned int i; + for (i = 0; i < padBytesRequired; ++i) + fputc(0x00, stdout); + } + + if (withdummy) + writeDummy(); +} + + + +int +main( int argc, char **argv ) { + struct cmdline_info cmdline; + unsigned int version; + FILE* ifP; + xel** xels; + xel transcolor; + unsigned int transindex; + int rows, cols; + unsigned int rowbytes; + xelval maxval; + int format; + unsigned int bpp; + bool directColor; + unsigned int newMaxval; + Colormap colormap; + + /* Parse default params */ + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + + xels = pnm_readpnm(ifP, &cols, &rows, &maxval, &format); + pm_close(ifP); + + if (cmdline.verbose) + pm_message("Input is %dx%d %s, maxval %d", + cols, rows, formatName(format), maxval); + + determinePalmFormat(cols, rows, maxval, format, xels, cmdline.depth, + cmdline.maxdepth, cmdline.colormap, cmdline.verbose, + &bpp, &directColor, &colormap); + + newMaxval = (1 << bpp) - 1; + + if (cmdline.transparent) + findTransparentColor(cmdline.transparent, newMaxval, directColor, + maxval, colormap, &transcolor, &transindex); + else + transindex = 0; + + rowbytes = ((cols + (16 / bpp -1)) / (16 / bpp)) * 2; + /* bytes per row - always a word boundary */ + + version = bitmapVersion(bpp, cmdline.colormap, !!cmdline.transparent, + cmdline.compression, cmdline.density); + + writeBitmap(xels, cols, rows, maxval, + rowbytes, bpp, newMaxval, cmdline.compression, + !!cmdline.transparent, directColor, cmdline.offset, + cmdline.colormap, colormap, transindex, transcolor, + version, cmdline.density, cmdline.withdummy); + + return 0; +} |