diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2011-05-01 21:48:30 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2011-05-01 21:48:30 +0000 |
commit | 2cd3594509f1b2efd14eab9106b33924db5900b4 (patch) | |
tree | 6ca6b97c08379dade9026496aecef2d5f0f359ff | |
parent | 6e2bce5dc0c6f2ba658061096353d9d3b83aca08 (diff) | |
download | netpbm-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/Makefile | 9 | ||||
-rw-r--r-- | converter/other/pamtosrf.c | 231 | ||||
-rw-r--r-- | converter/other/srf.c | 646 | ||||
-rw-r--r-- | converter/other/srf.h | 180 | ||||
-rw-r--r-- | converter/other/srftopam.c | 222 | ||||
-rw-r--r-- | doc/HISTORY | 3 |
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 |