From 4c8f6f696882cb4a924c483dfe5a734c7b41de0f Mon Sep 17 00:00:00 2001 From: Robin Watts Date: Sat, 11 Mar 2017 17:15:50 -0600 Subject: Recast TGA output as a band writer. Annoyingly TGA requires lines to be written from bottom to top, so require callers to flip the image. Also fix TGA to cope with alpha or not. Update mutool draw to use band writer interface for TGA. --- include/mupdf/fitz/output-tga.h | 24 +++++++++ source/fitz/output-tga.c | 105 +++++++++++++++++++++++++++++----------- source/tools/mudraw.c | 10 +++- 3 files changed, 108 insertions(+), 31 deletions(-) diff --git a/include/mupdf/fitz/output-tga.h b/include/mupdf/fitz/output-tga.h index e9af03b3..ad98c1c1 100644 --- a/include/mupdf/fitz/output-tga.h +++ b/include/mupdf/fitz/output-tga.h @@ -5,7 +5,31 @@ #include "mupdf/fitz/context.h" #include "mupdf/fitz/pixmap.h" +/* + fz_save_pixmap_as_tga: Save a pixmap as a TGA image file. + Can accept RGB, BGR or Grayscale pixmaps, with or without + alpha. +*/ void fz_save_pixmap_as_tga(fz_context *ctx, fz_pixmap *pixmap, const char *filename); + +/* + Write a pixmap to an output stream in TGA format. + Can accept RGB, BGR or Grayscale pixmaps, with or without + alpha. +*/ void fz_write_pixmap_as_tga(fz_context *ctx, fz_output *out, fz_pixmap *pixmap); +/* + fz_new_tga_band_writer: Generate a new band writer for TGA + format images. Note that image must be generated vertically + flipped for use with this writer! + + Can accept RGB, BGR or Grayscale pixmaps, with or without + alpha. + + is_bgr: True, if the image is generated in bgr format. +*/ +fz_band_writer *fz_new_tga_band_writer(fz_context *ctx, fz_output *out, int is_bgr); + + #endif diff --git a/source/fitz/output-tga.c b/source/fitz/output-tga.c index 8a595265..0e3cf3c5 100644 --- a/source/fitz/output-tga.c +++ b/source/fitz/output-tga.c @@ -4,7 +4,12 @@ * Write pixmap to TGA file (with or without alpha channel) */ -static inline void tga_put_pixel(fz_context *ctx, fz_output *out, unsigned char *data, int n, int is_bgr) +typedef struct { + fz_band_writer super; + int is_bgr; +} tga_band_writer; + +static inline void tga_put_pixel(fz_context *ctx, fz_output *out, const unsigned char *data, int n, int is_bgr) { switch(n) { @@ -30,7 +35,6 @@ static inline void tga_put_pixel(fz_context *ctx, fz_output *out, unsigned char fz_putc(ctx, out, data[1]); fz_putc(ctx, out, data[2]); } - fz_putc(ctx, out, 255); break; case 2: /* GA */ fz_putc(ctx, out, data[0]); @@ -38,11 +42,8 @@ static inline void tga_put_pixel(fz_context *ctx, fz_output *out, unsigned char fz_putc(ctx, out, data[0]); fz_putc(ctx, out, data[1]); break; - case 1: /* GA */ - fz_putc(ctx, out, data[0]); + case 1: /* G */ fz_putc(ctx, out, data[0]); - fz_putc(ctx, out, data[0]); - fz_putc(ctx, out, 255); break; } } @@ -62,50 +63,96 @@ fz_save_pixmap_as_tga(fz_context *ctx, fz_pixmap *pixmap, const char *filename) void fz_write_pixmap_as_tga(fz_context *ctx, fz_output *out, fz_pixmap *pixmap) { - unsigned char head[18]; - int n = pixmap->n; - int d = pixmap->alpha || n == 1 ? n : n - 1; - int is_bgr = pixmap->colorspace == fz_device_bgr(ctx); - int k; + fz_band_writer *writer = fz_new_tga_band_writer(ctx, out, pixmap->colorspace == fz_device_bgr(ctx)); - if (pixmap->colorspace && pixmap->colorspace != fz_device_gray(ctx) && - pixmap->colorspace != fz_device_rgb(ctx) && pixmap->colorspace != fz_device_bgr(ctx)) + fz_try(ctx) { - fz_throw(ctx, FZ_ERROR_GENERIC, "pixmap must be grayscale or rgb to write as tga"); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0); + fz_write_band(ctx, writer, -pixmap->stride, pixmap->h, pixmap->samples + pixmap->stride * (pixmap->h-1)); } + fz_always(ctx) + fz_drop_band_writer(ctx, writer); + fz_catch(ctx) + fz_rethrow(ctx); +} +static void +tga_write_header(fz_context *ctx, fz_band_writer *writer_) +{ + tga_band_writer *writer = (tga_band_writer *)writer_; + fz_output *out = writer->super.out; + int w = writer->super.w; + int h = writer->super.h; + int n = writer->super.n; + int alpha = writer->super.alpha; + unsigned char head[18]; + int d = (alpha && n > 1) ? 4 : (n == 1 ? 1 : 3); + + if (n-alpha > 1 && n != 3+alpha) + fz_throw(ctx, FZ_ERROR_GENERIC, "pixmap must be grayscale/rgb/rgba (with or without alpha) to write as tga"); memset(head, 0, sizeof(head)); - head[2] = n == 4 ? 10 : 11; - head[12] = pixmap->w & 0xFF; head[13] = (pixmap->w >> 8) & 0xFF; - head[14] = pixmap->h & 0xFF; head[15] = (pixmap->h >> 8) & 0xFF; - head[16] = d * 8; - head[17] = pixmap->alpha && n > 1 ? 8 : 0; - if (pixmap->alpha && d == 2) - head[16] = 32; + head[2] = n > 1 ? 10 /* RGB or RGBA or GA */ : 11 /* G */; + head[12] = w & 0xFF; head[13] = (w >> 8) & 0xFF; + head[14] = h & 0xFF; head[15] = (h >> 8) & 0xFF; + head[16] = d * 8; /* BPP */ + head[17] = alpha && n > 1 ? 8 : 0; /* Alpha bpp */ fz_write(ctx, out, head, sizeof(head)); - for (k = 1; k <= pixmap->h; k++) +} + +static void +tga_write_band(fz_context *ctx, fz_band_writer *writer_, int stride, int band_start, int band_height, const unsigned char *samples) +{ + tga_band_writer *writer = (tga_band_writer *)writer_; + fz_output *out = writer->super.out; + int w = writer->super.w; + int h = writer->super.h; + int n = writer->super.n; + int d = (writer->super.alpha && n > 1) ? 4 : (n == 1 ? 1 : 3); + int is_bgr = writer->is_bgr; + int k; + + for (k = 0; k < h; k++) { int i, j; - unsigned char *line = pixmap->samples + pixmap->w * n * (pixmap->h - k); - for (i = 0, j = 1; i < pixmap->w; i += j, j = 1) + const unsigned char *line = samples + stride * k; + for (i = 0, j = 1; i < w; i += j, j = 1) { - for (; i + j < pixmap->w && j < 128 && !memcmp(line + i * n, line + (i + j) * n, d); j++); + for (; i + j < w && j < 128 && !memcmp(line + i * n, line + (i + j) * n, d); j++); if (j > 1) { fz_putc(ctx, out, j - 1 + 128); - tga_put_pixel(ctx, out, line + i * n, d, is_bgr); + tga_put_pixel(ctx, out, line + i * n, n, is_bgr); } else { - for (; i + j < pixmap->w && j <= 128 && memcmp(line + (i + j - 1) * n, line + (i + j) * n, d) != 0; j++); - if (i + j < pixmap->w || j > 128) + for (; i + j < w && j <= 128 && memcmp(line + (i + j - 1) * n, line + (i + j) * n, d) != 0; j++); + if (i + j < w || j > 128) j--; fz_putc(ctx, out, j - 1); for (; j > 0; j--, i++) - tga_put_pixel(ctx, out, line + i * n, d, is_bgr); + tga_put_pixel(ctx, out, line + i * n, n, is_bgr); } } } +} + +static void +tga_write_trailer(fz_context *ctx, fz_band_writer *writer) +{ + fz_output *out = writer->out; + fz_write(ctx, out, "\0\0\0\0\0\0\0\0TRUEVISION-XFILE.\0", 26); } + +fz_band_writer *fz_new_tga_band_writer(fz_context *ctx, fz_output *out, int is_bgr) +{ + tga_band_writer *writer = fz_new_band_writer(ctx, tga_band_writer, out); + + writer->super.header = tga_write_header; + writer->super.band = tga_write_band; + writer->super.trailer = tga_write_trailer; + writer->is_bgr = is_bgr; + + return &writer->super; +} diff --git a/source/tools/mudraw.c b/source/tools/mudraw.c index 74d7c3e2..46609542 100644 --- a/source/tools/mudraw.c +++ b/source/tools/mudraw.c @@ -636,6 +636,12 @@ static void dodrawpage(fz_context *ctx, fz_page *page, fz_display_list *list, in fz_bound_page(ctx, page, &bounds); zoom = resolution / 72; fz_pre_scale(fz_rotate(&ctm, rotation), zoom, zoom); + + if (output_format == OUT_TGA) + { + fz_pre_scale(fz_pre_translate(&ctm, 0, -height), 1, -1); + } + tbounds = bounds; fz_round_rect(&ibounds, fz_transform_rect(&tbounds, &ctm)); @@ -749,6 +755,8 @@ static void dodrawpage(fz_context *ctx, fz_page *page, fz_display_list *list, in bander = fz_new_pkm_band_writer(ctx, out); else if (output_format == OUT_PS) bander = fz_new_ps_band_writer(ctx, out); + else if (output_format == OUT_TGA) + bander = fz_new_tga_band_writer(ctx, out, colorspace == fz_device_bgr(ctx)); else if (output_format == OUT_PCL) { if (out_cs == CS_MONO) @@ -781,8 +789,6 @@ static void dodrawpage(fz_context *ctx, fz_page *page, fz_display_list *list, in fz_write_band(ctx, bander, bit ? bit->stride : pix->stride, drawheight, bit ? bit->samples : pix->samples); else if (output_format == OUT_PWG) fz_write_pixmap_as_pwg(ctx, out, pix, NULL); - else if (output_format == OUT_TGA) - fz_write_pixmap_as_tga(ctx, out, pix); fz_drop_bitmap(ctx, bit); bit = NULL; } -- cgit v1.2.3