From b66caee410bdc0cad5b1de2719098058a64658fe Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Tue, 9 Apr 2013 18:51:59 -0700 Subject: [PATCH] dwebp: add support for BMP output Supports alpha output too. Change-Id: I42ea0bf7b6bbb420f0e537d372a3b13fe00528a9 --- README | 5 +-- examples/dwebp.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++-- man/dwebp.1 | 5 ++- 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/README b/README index aa6bca45..b03ca785 100644 --- a/README +++ b/README @@ -247,9 +247,10 @@ Decodes the WebP image file to PNG format [Default] Use following options to convert into alternate image formats: -pam ......... save the raw RGBA samples as a color PAM -ppm ......... save the raw RGB samples as a color PPM + -bmp ......... save as uncompressed BMP format -pgm ......... save the raw YUV samples as a grayscale PGM - file with IMC4 layout. - -yuv ......... save the raw YUV samples in flat layout. + file with IMC4 layout + -yuv ......... save the raw YUV samples in flat layout Other options are: -version .... print version number and exit. diff --git a/examples/dwebp.c b/examples/dwebp.c index a3dc615e..9da44df6 100644 --- a/examples/dwebp.c +++ b/examples/dwebp.c @@ -62,6 +62,7 @@ typedef enum { PAM, PPM, PGM, + BMP, YUV, ALPHA_PLANE_ONLY // this is for experimenting only } OutputFileFormat; @@ -225,6 +226,72 @@ static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) { return 1; } +static void PutLE16(uint8_t* const dst, uint32_t value) { + dst[0] = (value >> 0) & 0xff; + dst[1] = (value >> 8) & 0xff; +} + +static void PutLE32(uint8_t* const dst, uint32_t value) { + PutLE16(dst + 0, (value >> 0) & 0xffff); + PutLE16(dst + 2, (value >> 16) & 0xffff); +} + +#define BMP_HEADER_SIZE 54 +static int WriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { + const int has_alpha = (buffer->colorspace != MODE_BGR); + const uint32_t width = buffer->width; + const uint32_t height = buffer->height; + const uint8_t* const rgba = buffer->u.RGBA.rgba; + const int stride = buffer->u.RGBA.stride; + const size_t bytes_per_px = has_alpha ? 4 : 3; + uint32_t y; + const int line_size = bytes_per_px * width; + const int bmp_stride = (line_size + 3) & ~3; // pad to 4 + const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE; + uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 }; + + // bitmap file header + PutLE16(bmp_header + 0, 0x4d42); // signature 'BM' + PutLE32(bmp_header + 2, total_size); // size including header + PutLE32(bmp_header + 6, 0); // reserved + PutLE32(bmp_header + 10, BMP_HEADER_SIZE); // offset to pixel array + // bitmap info header + PutLE32(bmp_header + 14, 40); // DIB header size + PutLE32(bmp_header + 18, width); // dimensions + PutLE32(bmp_header + 22, -(int)height); // vertical flip! + PutLE16(bmp_header + 26, 1); // number of planes + PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel + PutLE32(bmp_header + 30, 0); // no compression (BI_RGB) + PutLE32(bmp_header + 34, 0); // image size (dummy) + PutLE32(bmp_header + 38, 2400); // x pixels/meter + PutLE32(bmp_header + 42, 2400); // y pixels/meter + PutLE32(bmp_header + 46, 0); // number of palette colors + PutLE32(bmp_header + 50, 0); // important color count + + // TODO(skal): color profile + + // write header + if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) { + return 0; + } + + // write pixel array + for (y = 0; y < height; ++y) { + if (fwrite(rgba + y * stride, line_size, 1, fout) != 1) { + return 0; + } + // write padding zeroes + if (bmp_stride != line_size) { + const uint8_t zeroes[3] = { 0 }; + if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) { + return 0; + } + } + } + return 1; +} +#undef BMP_HEADER_SIZE + static int WriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) { const uint32_t width = buffer->width; const uint32_t height = buffer->height; @@ -320,6 +387,8 @@ static void SaveOutput(const WebPDecBuffer* const buffer, ok &= WritePPM(fout, buffer, 1); } else if (format == PPM) { ok &= WritePPM(fout, buffer, 0); + } else if (format == BMP) { + ok &= WriteBMP(fout, buffer); } else if (format == PGM || format == YUV) { ok &= WritePGMOrYUV(fout, buffer, format); } else if (format == ALPHA_PLANE_ONLY) { @@ -345,9 +414,10 @@ static void Help(void) { "Use following options to convert into alternate image formats:\n" " -pam ......... save the raw RGBA samples as a color PAM\n" " -ppm ......... save the raw RGB samples as a color PPM\n" + " -bmp ......... save as uncompressed BMP format\n" " -pgm ......... save the raw YUV samples as a grayscale PGM\n" - " file with IMC4 layout.\n" - " -yuv ......... save the raw YUV samples in flat layout.\n" + " file with IMC4 layout\n" + " -yuv ......... save the raw YUV samples in flat layout\n" "\n" " Other options are:\n" " -version .... print version number and exit.\n" @@ -401,6 +471,8 @@ int main(int argc, const char *argv[]) { format = PAM; } else if (!strcmp(argv[c], "-ppm")) { format = PPM; + } else if (!strcmp(argv[c], "-bmp")) { + format = BMP; } else if (!strcmp(argv[c], "-version")) { const int version = WebPGetDecoderVersion(); printf("%d.%d.%d\n", @@ -481,6 +553,9 @@ int main(int argc, const char *argv[]) { case PPM: output_buffer->colorspace = MODE_RGB; // drops alpha for PPM break; + case BMP: + output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR; + break; case PGM: case YUV: output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; diff --git a/man/dwebp.1 b/man/dwebp.1 index d665eb1c..7fcdb62b 100644 --- a/man/dwebp.1 +++ b/man/dwebp.1 @@ -1,5 +1,5 @@ .\" Hey, EMACS: -*- nroff -*- -.TH DWEBP 1 "February 01, 2013" +.TH DWEBP 1 "April 09, 2013" .SH NAME dwebp \- decompress a WebP file to an image file .SH SYNOPSIS @@ -24,6 +24,9 @@ Print the version number (as major.minor.revision) and exit. .BI \-o " string Specify the name of the output file (as PNG format by default). .TP +.B \-bmp +Change the output format to uncompressed BMP. +.TP .B \-pam Change the output format to PAM (retains alpha). .TP