/* * Convert a SRF (Garmin vehicle) to a Portable Network Graphics file * * Written by Mike Frysinger * Released into the public domain */ #include "headers.h" static bool apng = false; static bool quiet = false; static void srf_img_png(png_structp png_ptr, png_byte *line, uint16_t line_len, struct srf_img *img, uint16_t x, uint16_t y, uint16_t w, uint16_t h) { png_byte *pp; uint16_t d, c, r; uint32_t off; for (r = y; r < y + h; ++r) { pp = line; off = r * img->header.width; for (c = x; c < x + w; ++c) { d = img->data.data[off + c]; *pp++ = SRF_IMG_R(d); *pp++ = SRF_IMG_G(d); *pp++ = SRF_IMG_B(d); /* This seems wonky, but it works ... */ d = img->alpha.data[off + c]; *pp++ = SRF_IMG_A(d); } if (line_len) memset(pp, 0, (line_len - c) * 4 * sizeof(*pp)); png_write_row(png_ptr, line); } } static int srf2png(FILE *ifp, FILE *ofp) { jmp_buf jmpbuf; png_structp png_ptr; png_infop info_ptr; png_byte *line; struct srf srf; int width, height, fwidth; uint32_t i; if (setjmp(jmpbuf)) err("setjmp returns error condition"); png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, &jmpbuf, NULL, NULL); if (!png_ptr) return 1; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) return 1; srf_read(ifp, !quiet, &srf); width = height = fwidth = 0; 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; } if (apng) width = fwidth; png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGBA, 0, 0, 0); png_init_io(png_ptr, ofp); #ifdef PNG_APNG_SUPPORTED if (apng) png_set_acTL(png_ptr, info_ptr, SRF_NUM_FRAMES, 0); #endif png_write_info(png_ptr, info_ptr); png_set_packing(png_ptr); line = malloc(sizeof(line) * width * 4); if (!line) return 1; if (apng) { #ifdef PNG_APNG_SUPPORTED uint32_t f; for (f = 0; f < SRF_NUM_FRAMES; ++f) { png_write_frame_head(png_ptr, info_ptr, &line, width, height, 0, 0, 2, SRF_NUM_FRAMES, PNG_DISPOSE_OP_NONE, PNG_BLEND_OP_SOURCE); for (i = 0; i < srf.header.img_cnt; ++i) { struct srf_img *img = &srf.imgs[i]; uint16_t w = img->header.height; srf_img_png(png_ptr, line, 0, img, f * w, 0, w, w); } png_write_frame_tail(png_ptr, info_ptr); } #endif } else { for (i = 0; i < srf.header.img_cnt; ++i) { struct srf_img *img = &srf.imgs[i]; srf_img_png(png_ptr, line, width, img, 0, 0, img->header.width, img->header.height); } } free(line); png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); srf_free(&srf); return 0; } static const char short_opts[] = "ahq"; static void usage(int status) { fprintf(status ? stderr : stdout, "Usage: srf2png [-%s] \n", short_opts); exit(status); } int main(int argc, char *argv[]) { FILE *ifp, *ofp; int c, ret; while ((c = getopt(argc, argv, short_opts)) != -1) { switch (c) { case 'a': apng = true; break; case 'h': usage(0); break; case 'q': quiet = true; break; default: usage(1); } } if (argc != optind + 2) usage(1); #ifndef PNG_APNG_SUPPORTED if (apng) err("sorry, your libpng lacks apng support"); #endif ifp = fopen(argv[optind], "r"); ofp = fopen(argv[optind + 1], "w"); ret = srf2png(ifp, ofp); fclose(ofp); fclose(ifp); return ret; }