about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2011-05-01 21:48:30 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2011-05-01 21:48:30 +0000
commit2cd3594509f1b2efd14eab9106b33924db5900b4 (patch)
tree6ca6b97c08379dade9026496aecef2d5f0f359ff
parent6e2bce5dc0c6f2ba658061096353d9d3b83aca08 (diff)
downloadnetpbm-mirror-2cd3594509f1b2efd14eab9106b33924db5900b4.tar.gz
netpbm-mirror-2cd3594509f1b2efd14eab9106b33924db5900b4.tar.xz
netpbm-mirror-2cd3594509f1b2efd14eab9106b33924db5900b4.zip
Add pamtosrf, srftopam
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1483 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--converter/other/Makefile9
-rw-r--r--converter/other/pamtosrf.c231
-rw-r--r--converter/other/srf.c646
-rw-r--r--converter/other/srf.h180
-rw-r--r--converter/other/srftopam.c222
-rw-r--r--doc/HISTORY3
6 files changed, 1289 insertions, 2 deletions
diff --git a/converter/other/Makefile b/converter/other/Makefile
index 3bad1996..96196b8a 100644
--- a/converter/other/Makefile
+++ b/converter/other/Makefile
@@ -93,7 +93,7 @@ ifneq ($(DONT_HAVE_PROCESS_MGMT),Y)
   PORTBINARIES += pstopnm
 endif
 
-BINARIES = $(PORTBINARIES) pnmtorast rasttopnm pamtopdbimg pdbimgtopam
+BINARIES = $(PORTBINARIES) pnmtorast rasttopnm pamtopdbimg pdbimgtopam srftopam pamtosrf
 
 ifeq ($(HAVE_PNGLIB),Y)
   BINARIES += pnmtopng pngtopam pamrgbatopng
@@ -117,7 +117,7 @@ endif
 
 MERGEBINARIES = $(BINARIES)
 
-EXTRA_OBJECTS = exif.o rast.o bmepsoe.o ipdb.o
+EXTRA_OBJECTS = exif.o rast.o bmepsoe.o ipdb.o srf.o
 ifeq ($(HAVE_PNGLIB),Y)
   EXTRA_OBJECTS += pngtxt.o
   EXTRA_OBJECTS += pngx.o
@@ -184,6 +184,11 @@ pnmtojpeg: %: %.o $(NETPBMLIB) $(LIBOPT)
 	  $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIB)) \
 	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
 
+srftopam pamtosrf: %: %.o srf.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) -o $@ $@.o srf.o \
+	  $(shell $(LIBOPT) $(NETPBMLIB)) \
+	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+
 svgtopam: %: %.o $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $@.o \
 	  $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR)) \
diff --git a/converter/other/pamtosrf.c b/converter/other/pamtosrf.c
new file mode 100644
index 00000000..a65fe39e
--- /dev/null
+++ b/converter/other/pamtosrf.c
@@ -0,0 +1,231 @@
+/*
+ * Convert a Netpbm image to SRF (Garmin vehicle)
+ *
+ * Copyright (C) 2011 by Mike Frysinger <vapier@gentoo.org>
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "pam.h"
+#include "srf.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *  inputFileName;  /* '-' if stdin */
+    unsigned int  verbose;
+};
+
+static bool verbose;
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+  optEntry *option_def;
+      /* Instructions to pm_optParseOptions3 on how to parse our options.
+       */
+  optStruct3 opt;
+
+  unsigned int option_def_index;
+
+  MALLOCARRAY_NOFAIL(option_def, 100);
+
+  option_def_index = 0;   /* incremented by OPTENT3 */
+  OPTENT3(0, "verbose",          OPT_FLAG,      NULL,
+          &cmdlineP->verbose,    0);
+
+  opt.opt_table = option_def;
+  opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+  opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+  pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+      /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+  if (argc-1 < 1)
+    cmdlineP->inputFileName = "-";
+  else if (argc-1 == 1)
+    cmdlineP->inputFileName = argv[1];
+  else
+    pm_error("Program takes at most one argument:  input file name");
+}
+
+
+
+static uint8_t
+srfScale(sample             const unscaled,
+         const struct pam * const pamP) {
+
+    return pnm_scalesample(unscaled, pamP->maxval, 255);
+}
+
+
+
+static uint16_t
+srfColorFromTuple(tuple              const t,
+                  const struct pam * const pamP) {
+
+    unsigned int redPlane, grnPlane, bluPlane;
+
+    if (pamP->depth >= 3) {
+        redPlane = PAM_RED_PLANE;
+        grnPlane = PAM_GRN_PLANE;
+        bluPlane = PAM_BLU_PLANE;
+    } else {
+        redPlane = 0;
+        grnPlane = 0;
+        bluPlane = 0;
+    }
+    return
+        (((srfScale(t[redPlane], pamP) >> 3) & 0x1f) << 11) |
+        (((srfScale(t[grnPlane], pamP) >> 3) & 0x1f) <<  6) |
+        (((srfScale(t[bluPlane], pamP) >> 3) & 0x1f) <<  0);
+}
+
+
+
+static uint8_t
+srfAlphaFromTuple(tuple              const t,
+                  const struct pam * const pamP) {
+
+    uint8_t retval;
+    bool haveOpacity;
+    unsigned int opacityPlane;
+
+    pnm_getopacity(pamP, &haveOpacity, &opacityPlane);
+
+    if (haveOpacity) {
+        uint8_t const scaled = srfScale(t[opacityPlane], pamP);
+
+        retval = scaled == 0xff ? SRF_ALPHA_OPAQUE :  128 - (scaled >> 1);
+    } else
+        retval = SRF_ALPHA_OPAQUE;
+
+    return retval;
+}
+
+
+
+static void
+producepam(struct cmdlineInfo const cmdline,
+           struct pam *       const pamP,
+           FILE *             const ofP) {
+/*----------------------------------------------------------------------------
+   Design note:  It's is really a modularity violation that we have
+   all the command line parameters as an argument.  We do it because we're
+   lazy -- it takes a great deal of work to carry all that information as
+   separate arguments -- and it's only a very small violation.
+-----------------------------------------------------------------------------*/
+    uint16_t width3d, height3d;
+    uint16_t widthOv, heightOv;
+    unsigned int row;
+    unsigned int imgCt;
+    unsigned int i;
+    struct srf srf;
+    tuple * tuplerow;
+
+    if (verbose)
+        pm_message("reading %ux%u image", pamP->width, pamP->height);
+
+    /* Figure out the dimensions.  The frame series should come in pairs,
+       each series should contain 36 frames, the first set should never
+       be smaller than the 2nd set, the sets should have the same dimension
+       combos as other sets, and each frame is square.
+
+       So if we have two frame series with the first being 80px tall and
+       the second is 60px tall, we can figure out things from there.
+    */
+    height3d = pamP->width / SRF_NUM_FRAMES;
+    for (row = 1; row <= pamP->height / height3d; ++row) {
+        heightOv = (pamP->height - (height3d * row)) / row;
+        if (heightOv <= height3d) {
+            if ((heightOv + height3d) * row == pamP->height)
+                break;
+        }
+    }
+    imgCt = row * 2;
+    width3d = height3d * SRF_NUM_FRAMES;
+    widthOv = heightOv * SRF_NUM_FRAMES;
+
+    if (verbose)
+        pm_message("detected %u sets of 16-bit RGB images (%ux%u and %ux%u)",
+                   imgCt, width3d, height3d, widthOv, heightOv);
+
+    srf_init(&srf, imgCt, width3d, height3d, widthOv, heightOv);
+
+    /* Scan out each frame series */
+    tuplerow = pnm_allocpamrow(pamP);
+    for (i = 0; i < srf.header.img_cnt; ++i) {
+        struct srf_img * const imgP = &srf.imgs[i];
+
+        unsigned int row;
+
+        for (row = 0; row < imgP->header.height; ++row) {
+            uint32_t        const off   = row * imgP->header.width;
+            uint16_t *      const data  = &imgP->data.data[off];
+            unsigned char * const alpha = &imgP->alpha.data[off];
+
+            unsigned int col;
+
+            pnm_readpamrow(pamP, tuplerow);
+            for (col = 0; col < imgP->header.width; ++col) {
+                alpha[col] = srfAlphaFromTuple(tuplerow[col], pamP);
+                data[col]  = srfColorFromTuple(tuplerow[col], pamP);
+            }
+        }
+    }
+    pnm_freepamrow(tuplerow);
+
+    srf_write(ofP, &srf);
+
+    srf_term(&srf);
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+  struct cmdlineInfo cmdline;
+  FILE *             ifP;
+  struct pam         inPam;
+
+  pm_proginit(&argc, argv);
+
+  parseCommandLine(argc, argv, &cmdline);
+
+  verbose = cmdline.verbose;
+
+  ifP = pm_openr(cmdline.inputFileName);
+
+  pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type));
+
+  producepam(cmdline, &inPam, stdout);
+
+  pm_closer(ifP);
+
+  return 0;
+}
diff --git a/converter/other/srf.c b/converter/other/srf.c
new file mode 100644
index 00000000..ce9ae604
--- /dev/null
+++ b/converter/other/srf.c
@@ -0,0 +1,646 @@
+/*
+ * Funcs for working with SRF (Garmin vehicle) files
+ *
+ * Written by Mike Frysinger <vapier@gentoo.org>
+ * Released into the public domain
+ */
+
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "srf.h"
+
+
+static unsigned char
+csumRaw(void * const p,
+       size_t const len) {
+
+    unsigned char retval;
+
+    unsigned int i;
+    unsigned char * c;
+
+    for (i = 0, retval = 0, c = p; i < len; ++i)
+        retval += *c++;
+    
+    return retval;
+}
+
+
+
+static unsigned char
+csumPstring(struct srf_pstring * const pstringP) {
+
+  return
+      csumRaw(&pstringP->len, 4) + csumRaw(pstringP->val, pstringP->len);
+}
+
+
+
+static bool
+readPstring(FILE *               const ifP,
+            struct srf_pstring * const pstringP) {
+
+    bool retval;
+    size_t bytesRead;
+
+    pm_readlittlelong2u(ifP, &pstringP->len);
+
+    MALLOCARRAY_NOFAIL(pstringP->val, pstringP->len + 1);
+
+    bytesRead = fread(pstringP->val, 1, pstringP->len, ifP);
+    if (bytesRead != pstringP->len)
+        retval = false;
+    else {
+        pstringP->val[pstringP->len] = '\0';
+        retval = true;
+    }        
+    return retval;
+}
+
+
+
+static bool
+writePstring(FILE *               const ofP,
+             struct srf_pstring * const pstringP) {
+
+    bool retval;
+    size_t bytesWritten;
+
+    pm_writelittlelongu(ofP, pstringP->len);
+
+    bytesWritten = fwrite(pstringP->val, 1, pstringP->len, ofP);
+
+    if (bytesWritten == pstringP->len)
+        retval = true;
+    else
+        retval = false;
+
+    return retval;
+}
+
+
+
+static size_t
+lenHeader(struct srf_header * const headerP) {
+
+    return 16 + (4 * 4) + 4 + headerP->s578.len
+        + 4 + 4 + headerP->ver.len
+        + 4 + 4 + headerP->prod.len;
+}
+
+
+
+static unsigned char
+csumHeader(struct srf_header * const headerP) {
+
+    return
+        csumRaw(headerP->magic, 16) +
+        csumRaw(&headerP->_int4, 2 * 4) +
+        csumRaw(&headerP->img_cnt, 4) +
+        csumRaw(&headerP->_int5, 4) +
+        csumPstring(&headerP->s578) +
+        csumRaw(&headerP->_int6, 4) +
+        csumPstring(&headerP->ver) +
+        csumRaw(&headerP->_int7, 4) +
+        csumPstring(&headerP->prod);
+}
+
+
+
+static bool
+readHeader(FILE *              const ifP,
+           struct srf_header * const headerP) {
+
+    bool const retval =
+        fread(headerP->magic, 1, 16, ifP) == 16 &&
+        pm_readlittlelong2u(ifP, &headerP->_int4[0]) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->_int4[1]) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->img_cnt) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->_int5) == 0 &&
+        readPstring(ifP, &headerP->s578) &&
+        pm_readlittlelong2u(ifP, &headerP->_int6) == 0 &&
+        readPstring(ifP, &headerP->ver) &&
+        pm_readlittlelong2u(ifP, &headerP->_int7) == 0 &&
+        readPstring(ifP, &headerP->prod);
+
+    headerP->magic[16] = '\0';
+
+    return retval;
+}
+
+
+
+static bool
+writeHeader(FILE *              const ofP,
+            struct srf_header * const headerP) {
+
+    return
+        fwrite(headerP->magic, 1, 16, ofP) == 16 &&
+        pm_writelittlelongu(ofP, headerP->_int4[0]) == 0 &&
+        pm_writelittlelongu(ofP, headerP->_int4[1]) == 0 &&
+        pm_writelittlelongu(ofP, headerP->img_cnt) == 0 &&
+        pm_writelittlelongu(ofP, headerP->_int5) == 0 &&
+        writePstring(ofP, &headerP->s578) &&
+        pm_writelittlelongu(ofP, headerP->_int6) == 0 &&
+        writePstring(ofP, &headerP->ver) &&
+        pm_writelittlelongu(ofP, headerP->_int7) == 0 &&
+        writePstring(ofP, &headerP->prod);
+}
+
+
+
+static bool
+checkHeader(struct srf_header * const headerP) {
+
+    return
+        streq(headerP->magic, SRF_MAGIC) &&
+        headerP->_int4[0] == 4 &&
+        headerP->_int4[1] == 4 &&
+        /* Should we require img_cnt to be multiple of 2 ? */
+        headerP->img_cnt > 0 &&
+        headerP->_int5 == 5 &&
+        headerP->s578.len == 3 &&
+        strcmp(headerP->s578.val, "578") == 0 &&
+        headerP->_int6 == 6 &&
+        headerP->ver.len == 4 &&
+        /* Allow any headerP->ver value */
+        headerP->_int7 == 7 &&
+        headerP->prod.len == 12;
+    /* Allow any headerP->prod value */
+}
+
+
+
+static size_t
+lenImg(struct srf_img * const imgP) {
+
+    return
+        (4 * 3) + (2 * 2) + (1 * 2) + 2 + 4 +
+        4 + 4 + imgP->alpha.data_len +
+        4 + 4 + imgP->data.data_len;
+}
+
+
+
+static unsigned char
+csumImg(struct srf_img * const imgP) {
+
+    struct srf_img_header * const headerP = &imgP->header;
+    struct srf_img_alpha *  const alphaP  = &imgP->alpha;
+    struct srf_img_data *   const dataP   = &imgP->data;
+
+    return
+        csumRaw(&headerP->_ints, 4 * 3) +
+        csumRaw(&headerP->height, 2) +
+        csumRaw(&headerP->width, 2) +
+        csumRaw(headerP->_bytes, 2) +
+        csumRaw(&headerP->line_len, 2) +
+        csumRaw(&headerP->zeros, 4) +
+        csumRaw(&alphaP->type, 4) +
+        csumRaw(&alphaP->data_len, 4) +
+        csumRaw(alphaP->data, alphaP->data_len) +
+        csumRaw(&dataP->type, 4) +
+        csumRaw(&dataP->data_len, 4) +
+        csumRaw(dataP->data, dataP->data_len);
+}
+
+
+
+static bool
+readImgHeader(FILE *                  const ifP,
+              struct srf_img_header * const headerP) {
+    return
+        pm_readlittlelong2u(ifP, &headerP->_ints[0]) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->_ints[1]) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->_ints[2]) == 0 &&
+        pm_readlittleshortu(ifP, &headerP->height) == 0 &&
+        pm_readlittleshortu(ifP, &headerP->width) == 0 &&
+        fread(&headerP->_bytes[0], 1, 1, ifP) == 1 &&
+        fread(&headerP->_bytes[1], 1, 1, ifP) == 1 &&
+        pm_readlittleshortu(ifP, &headerP->line_len) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->zeros) == 0;
+}
+
+
+
+static bool
+writeImgHeader(FILE *                  const ofP,
+               struct srf_img_header * const headerP) {
+    return
+        pm_writelittlelongu(ofP, headerP->_ints[0]) == 0 &&
+        pm_writelittlelongu(ofP, headerP->_ints[1]) == 0 &&
+        pm_writelittlelongu(ofP, headerP->_ints[2]) == 0 &&
+        pm_writelittleshortu(ofP, headerP->height) == 0 &&
+        pm_writelittleshortu(ofP, headerP->width) == 0 &&
+        fwrite(&headerP->_bytes[0], 1, 1, ofP) == 1 &&
+        fwrite(&headerP->_bytes[1], 1, 1, ofP) == 1 &&
+        pm_writelittleshortu(ofP, headerP->line_len) == 0 &&
+        pm_writelittlelongu(ofP, headerP->zeros) == 0;
+}
+
+
+
+static bool
+checkImgHeader(struct srf_img_header * const headerP) {
+
+    return
+        headerP->_ints[0] == 0 &&
+        headerP->_ints[1] == 16 &&
+        headerP->_ints[2] == 0 &&
+        headerP->_bytes[0] == 16 &&
+        headerP->_bytes[1] == 8 &&
+        headerP->line_len == headerP->width * 2 &&
+        headerP->zeros == 0;
+}
+
+
+
+static bool
+readImgAlpha(FILE *                 const ifP,
+                   struct srf_img_alpha * const alphaP) {
+
+    bool retval;
+
+    pm_readlittlelong2u(ifP, &alphaP->type);
+    pm_readlittlelong2u(ifP, &alphaP->data_len);
+
+    MALLOCARRAY(alphaP->data, alphaP->data_len);
+
+    if (!alphaP->data)
+        retval = false;
+    else {
+        size_t bytesRead;
+
+        bytesRead = fread(alphaP->data, 1, alphaP->data_len, ifP);
+        retval = (bytesRead ==alphaP->data_len);
+    }
+    return retval;
+}
+
+
+
+static bool
+writeImageAlpha(FILE *                 const ofP,
+                    struct srf_img_alpha * const alphaP) {
+
+    return
+        pm_writelittlelongu(ofP, alphaP->type) == 0 &&
+        pm_writelittlelongu(ofP, alphaP->data_len) == 0 &&
+        fwrite(alphaP->data, 1, alphaP->data_len, ofP) == alphaP->data_len;
+}
+
+
+
+static bool
+checkImgAlpha(struct srf_img_alpha * const alphaP) {
+
+    return (alphaP->type == 11);
+}
+
+
+
+static bool
+readImgData(FILE *                const ifP,
+                  struct srf_img_data * const dataP) {
+
+    bool retval;
+
+    pm_readlittlelong2u(ifP, &dataP->type);
+    pm_readlittlelong2u(ifP, &dataP->data_len);
+
+    MALLOCARRAY(dataP->data, dataP->data_len / 2);
+
+    if (!dataP->data)
+        retval = false;
+    else {
+        size_t bytesRead;
+        bytesRead = fread(dataP->data, 2, dataP->data_len / 2, ifP);
+
+        retval = (bytesRead == dataP->data_len / 2);
+    }
+    return retval;
+}
+
+
+
+static bool
+writeImgData(FILE *                const ofP,
+                   struct srf_img_data * const dataP) {
+
+    return
+        pm_writelittlelongu(ofP, dataP->type) == 0 &&
+        pm_writelittlelongu(ofP, dataP->data_len) == 0 &&
+        fwrite(dataP->data, 2, dataP->data_len / 2, ofP)
+        == dataP->data_len / 2;
+}
+
+
+
+static bool
+checkImgData(struct srf_img_data * const dataP) {
+  return dataP->type == 1;
+}
+
+
+
+static bool
+readImg(FILE *           const ifP,
+             bool             const verbose,
+             uint32_t         const i,
+             struct srf_img * const imgP) {
+
+    if (!readImgHeader(ifP, &imgP->header))
+        pm_error("short srf image %u header", i);
+    if (!checkImgHeader(&imgP->header))
+        pm_error("invalid srf image %u header", i);
+
+    if (verbose)
+        pm_message("reading srf 16-bit RGB %ux%u image %u",
+                   imgP->header.width, imgP->header.height, i);
+
+    if (!readImgAlpha(ifP, &imgP->alpha))
+        pm_error("short srf image %u alpha mask", i);
+    if (!checkImgAlpha(&imgP->alpha))
+        pm_error("invalid srf image %u alpha mask", i);
+
+    if (!readImgData(ifP, &imgP->data))
+        pm_error("short srf image %u data", i);
+    if (!checkImgData(&imgP->data))
+        pm_error("invalid srf image %u data", i);
+
+    return true;
+}
+
+
+
+static bool
+writeImg(FILE *           const ofP,
+              uint32_t         const i,
+              struct srf_img * const imgP) {
+
+    if (!checkImgHeader(&imgP->header))
+        pm_error("invalid srf image %u header", i);
+    if (!writeImgHeader(ofP, &imgP->header))
+        pm_error("short srf image %u header", i);
+
+    if (!checkImgAlpha(&imgP->alpha))
+        pm_error("invalid srf image %u alpha mask", i);
+    if (!writeImageAlpha(ofP, &imgP->alpha))
+        pm_error("short srf image %u alpha mask", i);
+
+    if (!checkImgData(&imgP->data))
+        pm_error("invalid srf image %u data", i);
+    if (!writeImgData(ofP, &imgP->data))
+        pm_error("short srf image %u data", i);
+
+    return true;
+}
+
+
+
+static uint8_t
+csum(struct srf * const srfP,
+     size_t       const padLen) {
+/*----------------------------------------------------------------------------
+   The sum of everything in the SRF image except the checksum byte.  The
+   checksum byte is supposed to be the arithmetic opposite of this so that the
+   sum of everything is zero.
+-----------------------------------------------------------------------------*/
+    unsigned char retval;
+    unsigned int i;
+
+    retval = csumHeader(&srfP->header);
+
+    for (i = 0; i < srfP->header.img_cnt; ++i)
+        retval += csumImg(&srfP->imgs[i]);
+    
+    for (i = 0; i < padLen; ++i)
+        retval += 0xff;
+    
+    return retval;
+}
+
+
+
+void
+srf_read(FILE *       const ifP,
+         bool         const verbose,
+         struct srf * const srfP) {
+
+    uint8_t       trialCsum;
+    size_t        padLen;
+    unsigned char pad[256];
+    unsigned int  i;
+
+    if (!readHeader(ifP, &srfP->header))
+        pm_error("short srf header");
+    if (!checkHeader(&srfP->header))
+        pm_error("invalid srf header");
+
+    if (verbose)
+        pm_message("reading srf ver %s with prod code %s and %u images",
+                   srfP->header.ver.val, srfP->header.prod.val,
+                   srfP->header.img_cnt);
+
+    MALLOCARRAY(srfP->imgs, srfP->header.img_cnt);
+
+    if (!srfP->imgs)
+        pm_error("Could not allocate memory for %u images",
+                 srfP->header.img_cnt);
+
+    for (i = 0; i < srfP->header.img_cnt; ++i)
+        if (!readImg(ifP, verbose, i, &srfP->imgs[i]))
+            pm_error("invalid srf image %u", i);
+
+    padLen = fread(pad, 1, sizeof(pad), ifP);
+    if (!feof(ifP)) {
+        pm_errormsg("excess data at end of file");
+        return;
+    }
+
+    trialCsum = csum(srfP, 0);  /* initial value */
+    for (i = 0; i < padLen; ++i)
+        trialCsum += pad[i];
+    if (trialCsum != 0)
+        pm_errormsg("checksum does not match");
+}
+
+
+
+void
+srf_write(FILE *       const ofP,
+          struct srf * const srfP) {
+
+    uint8_t      srfCsum;    /* checksum value in SRF image */
+    size_t       padLen;
+    unsigned int i;
+    size_t       bytesWritten;
+
+    padLen = 1;  /* initial value */
+
+    if (!checkHeader(&srfP->header))
+        pm_error("invalid srf header");
+    if (!writeHeader(ofP, &srfP->header))
+        pm_error("write srf header");
+    padLen += lenHeader(&srfP->header);
+
+    for (i = 0; i < srfP->header.img_cnt; ++i) {
+        if (!writeImg(ofP, i, &srfP->imgs[i]))
+            pm_error("invalid srf image %u", i);
+        padLen += lenImg(&srfP->imgs[i]);
+    }
+
+    /* Pad to 256 bytes */
+    padLen = 256 - (padLen % 256);
+    if (padLen) {
+        char * d;
+        size_t bytesWritten;
+
+        MALLOCARRAY(d, padLen);
+
+        if (!d)
+            pm_error("Could not allocate memory for %u bytes of padding",
+                     (unsigned)padLen);
+
+        memset(d, 0xff, padLen);
+
+        bytesWritten = fwrite(d, 1, padLen, ofP);
+
+        if (bytesWritten != padLen)
+            pm_error("unable to 0xff pad file");
+
+        free(d);
+    }
+
+    /* Write out checksum byte */
+    srfCsum = 0xff - csum(srfP, padLen) + 1;
+    bytesWritten = fwrite(&srfCsum, 1, 1, ofP);
+    if (bytesWritten != 1)
+        pm_error("unable to write checksum");
+}
+
+
+
+static void
+freeImg(struct srf_img * const imgP) {
+
+    free(imgP->alpha.data);
+    free(imgP->data.data);
+}
+
+
+
+void
+srf_term(struct srf * const srfP) {
+
+    unsigned int i;
+    
+    free(srfP->header.s578.val);
+    free(srfP->header.ver.val);
+    free(srfP->header.prod.val);
+
+    for (i = 0; i < srfP->header.img_cnt; ++i)
+        freeImg(&srfP->imgs[i]);
+
+    free(srfP->imgs);
+}
+
+
+
+static void
+srf_img_init(struct srf_img * const imgP,
+             uint16_t         const width,
+             uint16_t         const height) {
+
+    struct srf_img_header * const headerP = &imgP->header;
+    struct srf_img_alpha *  const alphaP  = &imgP->alpha;
+    struct srf_img_data *   const dataP   = &imgP->data;
+
+    headerP->_ints[0]   = 0;
+    headerP->_ints[1]   = 16;
+    headerP->_ints[2]   = 0;
+    headerP->height     = height;
+    headerP->width      = width;
+    headerP->_bytes[0]  = 16;
+    headerP->_bytes[1]  = 8;
+    headerP->line_len   = width * 2;
+    headerP->zeros      = 0;
+
+    alphaP->type     = 11;
+    alphaP->data_len = height * width;
+    MALLOCARRAY(alphaP->data, alphaP->data_len);
+
+    if (!alphaP->data)
+        pm_error("Could not allocate buffer for %u bytes of alpha",
+                 alphaP->data_len);
+
+    dataP->type     = 1;
+    dataP->data_len = height * width * 2;
+    MALLOCARRAY(dataP->data, dataP->data_len / 2);
+
+    if (!dataP->data)
+        pm_error("Could not allocation buffer for %u units of data",
+                 dataP->data_len);
+}
+
+
+
+static void
+initPstring(struct srf_pstring * const pstringP,
+                 const char *         const s) {
+
+    pstringP->len = strlen(s);
+
+    MALLOCARRAY(pstringP->val, pstringP->len);
+
+    if (!pstringP->val)
+        pm_error("Could not allocate memory for string of length %u",
+                 pstringP->len);
+
+
+    memcpy(pstringP->val, s, pstringP->len + 1);
+}
+
+
+
+void
+srf_init(struct srf * const srfP,
+         uint32_t     const imgCnt,
+         uint16_t     const width3d,
+         uint16_t     const height3d,
+         uint16_t     const widthOv,
+         uint16_t     const heightOv) {
+
+    struct srf_header * const headerP = &srfP->header;
+    unsigned int i;
+
+    if (imgCnt == 0 || imgCnt % 2 != 0)
+        pm_error("invalid image count");
+
+    strcpy(headerP->magic, SRF_MAGIC);
+    headerP->_int4[0] = 4;
+    headerP->_int4[1] = 4;
+    headerP->img_cnt = imgCnt;
+    headerP->_int5 = 5;
+    initPstring(&headerP->s578, "578");
+    headerP->_int6 = 6;
+    initPstring(&headerP->ver, "1.00");
+    headerP->_int7 = 7;
+    initPstring(&headerP->prod, "006-D0578-XX");
+
+    MALLOCARRAY(srfP->imgs, headerP->img_cnt);
+
+    if (!srfP->imgs)
+        pm_error("Could not allocate memory for %u images", headerP->img_cnt);
+
+    for (i = 0; i < headerP->img_cnt; i += 2) {
+        srf_img_init(&srfP->imgs[i], width3d, height3d);
+        srf_img_init(&srfP->imgs[i + 1], widthOv, heightOv);
+    }
+}
+
+
+
diff --git a/converter/other/srf.h b/converter/other/srf.h
new file mode 100644
index 00000000..9214bd16
--- /dev/null
+++ b/converter/other/srf.h
@@ -0,0 +1,180 @@
+#ifndef SRF_H_INCLUDED
+#define SRF_H_INCLUDED
+
+/*
+ * Structures for working with SRF (Garmin vehicle) files
+ * http://www.techmods.net/nuvi/
+ *
+ * Written by Mike Frysinger <vapier@gentoo.org>
+ * Released into the public domain
+ */
+
+#include "pm_config.h"
+#include "pam.h"
+
+struct srf_pstring {
+    uint32_t len;
+    char *   val;
+};
+
+#define SRF_NUM_FRAMES 36
+
+/*
+  File Header
+      16 bytes - string - "GARMIN BITMAP 01"
+      32 bytes - two 32-bit ints, [4, 4] -- purpose unknown
+      4 bytes - 32-bit int -- number of images (usually just 2)
+      4 bytes - 32-bit int, [5] -- purpose unknown
+      7 bytes - PString - "578"
+      4 bytes - 32-bit int, [6] -- purpose unknown
+      8 bytes - PString - version number ("1.00", "2.00", "2.10", or "2.20")
+      4 bytes - 32-bit int, [7] -- purpose unknown
+      16 bytes - PString - "006-D0578-XX" (where "XX" changes) --
+                           I assume this is Garmin's product code?
+*/
+#define SRF_MAGIC "GARMIN BITMAP 01"
+
+struct srf_header {
+    char magic[16 + 1];
+
+    uint32_t           _int4[2];
+
+    uint32_t           img_cnt;
+
+    uint32_t           _int5;
+
+    struct srf_pstring s578;
+
+    uint32_t           _int6;
+
+    struct srf_pstring ver;
+
+    uint32_t           _int7;
+
+    struct srf_pstring prod;
+};
+
+/*
+  Image Header
+     12 bytes - three 32-bit ints, [0,16,0] -- purpose unknown
+     2 bytes - 16-bit int -- height of image (just the 3D section, so it's 80)
+     2 bytes - 16-bit int -- width of image (just the 3D section, 2880 or 2881)
+     2 bytes - [16, 8] -- purpose unknown
+     2 bytes - 16-bit int -- byte length of each line of image RGB data
+                             (16-bit RGB), so "width * 2"
+     4 bytes - all zeroes -- purpose unknown
+*/
+struct srf_img_header {
+    uint32_t _ints[3];
+
+    uint16_t height, width;
+
+    char     _bytes[2];
+
+    uint16_t line_len;
+
+    uint32_t zeros;
+};
+
+/*
+  Image Alpha Mask
+
+      4 bytes - 32-bit int, [11] -- Might specify the type of data that
+                follows?
+
+      4 bytes - 32-bit int, length of following data (width*height of 3D
+                section)
+
+      width*height bytes - alpha mask data, 0 = opaque, 128 = transparent
+                           (across, then down)
+
+  Notes: The Garmin format has 129 values: [0..128] [opaque..transparent]
+         The PNG format has 256 values:    [0..255] [transparent..opaque]
+         So we have to do a little edge case tweaking to keep things lossless.
+*/
+
+#define SRF_ALPHA_OPAQUE 0
+#define SRF_ALPHA_TRANS  128
+
+#define SRF_TOPAM_A(d) srf_alpha_srftopam((uint8_t)(d))
+
+#define SRF_FROMPAM_A(d) srf_alpha_pamtosrf((uint8_t)(d))
+
+struct srf_img_alpha {
+    uint32_t        type;
+
+    uint32_t        data_len;
+    unsigned char * data;
+};
+
+/*
+  Image RGB Data
+      4 bytes - 32-bit int, [1] -- Might specify the type of data that follows?
+      4 bytes - 32-bit int, length of following data (width*height*2 of 3D
+                            section, as the RGB data is 16-bit)
+      width*height*2 bytes - RBG values as "rrrrrggggg0bbbbb" bits
+                             (across, then down)
+*/
+#define SRF_TOPAM_R(d) ((((d) >> 11) & 0x1f) << 3)
+#define SRF_TOPAM_G(d) ((((d) >>  6) & 0x1f) << 3)
+#define SRF_TOPAM_B(d) ((((d) >>  0) & 0x1f) << 3)
+
+
+
+
+struct srf_img_data {
+    uint32_t   type;
+
+    uint32_t   data_len;
+    uint16_t * data;
+};
+
+struct srf_img {
+    struct srf_img_header header;
+    struct srf_img_alpha  alpha;
+    struct srf_img_data   data;
+};
+
+/*
+  Footer
+      arbitrary number of bytes - all 0xFF -- these are used (as well as the
+                                              checksum byte) to pad the file
+                                              size to a multiple of 256.
+
+      1 byte - checksum byte -- use this byte to adjust so that the ascii sum
+                                of all bytes in the file is a multiple of 256.
+ */
+
+struct srf {
+    struct srf_header header;
+    struct srf_img *  imgs;
+};
+
+uint8_t
+srf_alpha_srftopam(uint8_t const d);
+
+uint8_t
+srf_alpha_pamtosrf(uint8_t const d);
+
+void
+srf_read(FILE *       const ifP,
+         bool         const verbose,
+         struct srf * const srfP);
+
+void
+srf_write(FILE *       const ofP,
+          struct srf * const srfP);
+
+void
+srf_term(struct srf * const srfP);
+
+void
+srf_init(struct srf * const srfP,
+         uint32_t     const imgCnt,
+         uint16_t     const width3d,
+         uint16_t     const height3d,
+         uint16_t     const widthOv,
+         uint16_t     const heightOv);
+
+#endif
+
diff --git a/converter/other/srftopam.c b/converter/other/srftopam.c
new file mode 100644
index 00000000..cd7de02a
--- /dev/null
+++ b/converter/other/srftopam.c
@@ -0,0 +1,222 @@
+/*
+ * Convert a SRF (Garmin vehicle) to a PAM image
+ *
+ * Copyright (C) 2011 by Mike Frysinger <vapier@gentoo.org>
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "pam.h"
+#include "srf.h"
+
+struct cmdlineInfo {
+  /* All the information the user supplied in the command line,
+     in a form easy for the program to use.
+  */
+  const char *  inputFileName;  /* '-' if stdin */
+  unsigned int  verbose;
+};
+
+static bool verbose;
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+    /* Instructions to pm_optParseOptions3 on how to parse our options.
+     */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "verbose",          OPT_FLAG,      NULL,
+            &cmdlineP->verbose,    0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("Program takes at most one argument:  input file name");
+}
+
+
+
+static unsigned int
+srfRed(uint16_t const pixel) {
+    return ((pixel >> 11) & 0x1f) << 3;
+}
+
+
+static unsigned int
+srfGrn(uint16_t const pixel) {
+    return ((pixel >>  6) & 0x1f) << 3;
+}
+
+
+static unsigned int
+srfBlu(uint16_t const pixel) {
+    return ((pixel >>  0) & 0x1f) << 3;
+}
+
+
+static uint8_t
+srfAlpha(uint8_t const d) {
+
+    uint8_t retval;
+
+    if (d == SRF_ALPHA_OPAQUE)
+        retval = 0xff;
+    else
+        retval = (128 - d) << 1;
+
+    return retval;
+}
+
+
+
+static void
+producePam(struct pam *     const pamP,
+           uint16_t         const lineLen,
+           struct srf_img * const imgP) {
+
+    tuple *  tuplerow;
+    uint16_t r;
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    for (r = 0; r < imgP->header.height; ++r) {
+        unsigned int const off = r * imgP->header.width;
+
+        unsigned int col;
+
+        for (col = 0; col < imgP->header.width; ++col) {
+            uint16_t const data  = imgP->data.data[off + col];
+            uint16_t const alpha = imgP->alpha.data[off + col];
+
+            
+            tuplerow[col][PAM_RED_PLANE] = srfRed(data);
+            tuplerow[col][PAM_GRN_PLANE] = srfGrn(data);
+            tuplerow[col][PAM_BLU_PLANE] = srfBlu(data);
+            tuplerow[col][PAM_TRN_PLANE] = srfAlpha(alpha);
+        }
+
+        for (; col < lineLen; ++col) {
+            tuplerow[col][PAM_RED_PLANE] = 0;
+            tuplerow[col][PAM_GRN_PLANE] = 0;
+            tuplerow[col][PAM_BLU_PLANE] = 0;
+            tuplerow[col][PAM_TRN_PLANE] = 0;
+        }            
+
+        pnm_writepamrow(pamP, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+convertsrf(struct cmdlineInfo const cmdline,
+           FILE *             const ifP,
+           FILE *             const ofP) {
+/*----------------------------------------------------------------------------
+  Design note:  It's is really a modularity violation that we have
+  all the command line parameters as an argument.  We do it because we're
+  lazy -- it takes a great deal of work to carry all that information as
+  separate arguments -- and it's only a very small violation.
+  -----------------------------------------------------------------------------*/
+    const char * comment = "Produced by srftopam";  /* constant */
+    long         width, height;
+    long         fwidth;
+    unsigned int i;
+    struct srf   srf;
+    struct pam   outPam;
+
+    srf_read(ifP, verbose, &srf);
+
+    width = height = 0;  /* initial value */
+    for (i = 0; i < srf.header.img_cnt; ++i) {
+        if (width < srf.imgs[i].header.width) {
+            width  = srf.imgs[i].header.width;
+            fwidth = srf.imgs[i].header.height;
+        }
+        height += srf.imgs[i].header.height;
+    }
+
+    outPam.size             = sizeof(struct pam);
+    outPam.len              = PAM_STRUCT_SIZE(comment_p);
+    outPam.file             = ofP;
+    outPam.format           = PAM_FORMAT;
+    outPam.plainformat      = 0;
+    outPam.width            = width;
+    outPam.height           = height;
+    outPam.depth            = 4;
+    outPam.maxval           = 255;
+    outPam.bytes_per_sample = 1;
+    sprintf(outPam.tuple_type, "RGB_ALPHA");
+    outPam.allocation_depth = 4;
+    outPam.comment_p        = &comment;
+
+    pnm_writepaminit(&outPam);
+
+    for (i = 0; i < srf.header.img_cnt; ++i)
+        producePam(&outPam, width, &srf.imgs[i]);
+
+    srf_term(&srf);
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE *             ifP;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    convertsrf(cmdline, ifP, stdout);
+
+    pm_closer(ifP);
+
+    return 0;
+}
diff --git a/doc/HISTORY b/doc/HISTORY
index 4258f533..6d3406d5 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -6,6 +6,9 @@ CHANGE HISTORY
 
 not yet  BJH  Release 10.55.00
 
+              Add pamtosrf, srftopam.  Thanks Mike Frysinger
+              (vapier@gentoo.org).
+
               pamrubber: fix crash.  Introduced in 10.54.
 
               libnetpbm: pm_system(): fix bug - program always takes