2012-01-06 23:49:06 +01:00
|
|
|
// Copyright 2010 Google Inc. All Rights Reserved.
|
2010-09-30 15:34:38 +02:00
|
|
|
//
|
|
|
|
// This code is licensed under the same terms as WebM:
|
|
|
|
// Software License Agreement: http://www.webmproject.org/license/software/
|
|
|
|
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
//
|
2011-06-20 09:45:15 +02:00
|
|
|
// Command-line tool for decoding a WebP image
|
2010-09-30 15:34:38 +02:00
|
|
|
//
|
|
|
|
// Compile with: gcc -o dwebp dwebp.c -lwebpdecode
|
|
|
|
//
|
|
|
|
// Author: Skal (pascal.massimino@gmail.com)
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2011-06-07 02:56:50 +02:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2011-02-01 07:00:33 +01:00
|
|
|
#ifdef WEBP_HAVE_PNG
|
|
|
|
#include <png.h>
|
|
|
|
#endif
|
|
|
|
|
2011-06-07 02:56:50 +02:00
|
|
|
#ifdef HAVE_WINCODEC_H
|
|
|
|
#ifdef __MINGW32__
|
|
|
|
#define INITGUID // Without this GUIDs are declared extern and fail to link
|
|
|
|
#endif
|
2011-02-17 00:01:27 +01:00
|
|
|
#define CINTERFACE
|
|
|
|
#define COBJMACROS
|
|
|
|
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
|
|
|
// code with COBJMACROS.
|
|
|
|
#include <shlwapi.h>
|
|
|
|
#include <windows.h>
|
|
|
|
#include <wincodec.h>
|
|
|
|
#endif
|
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
#include "webp/decode.h"
|
2012-05-12 01:00:57 +02:00
|
|
|
#include "./example_util.h"
|
2012-05-15 22:48:11 +02:00
|
|
|
#include "./stopwatch.h"
|
2010-09-30 15:34:38 +02:00
|
|
|
|
2011-11-04 23:20:08 +01:00
|
|
|
static int verbose = 0;
|
|
|
|
#ifndef WEBP_DLL
|
2010-09-30 15:34:38 +02:00
|
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
2011-09-02 23:30:08 +02:00
|
|
|
extern void* VP8GetCPUInfo; // opaque forward declaration.
|
2011-11-04 23:20:08 +01:00
|
|
|
|
|
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
|
|
|
} // extern "C"
|
2011-07-15 23:53:03 +02:00
|
|
|
#endif
|
2011-11-04 23:20:08 +01:00
|
|
|
#endif // WEBP_DLL
|
2011-06-11 00:10:18 +02:00
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-06-20 09:45:15 +02:00
|
|
|
|
|
|
|
// Output types
|
|
|
|
typedef enum {
|
|
|
|
PNG = 0,
|
2012-07-21 01:06:06 +02:00
|
|
|
PAM,
|
2011-06-20 09:45:15 +02:00
|
|
|
PPM,
|
|
|
|
PGM,
|
2013-01-28 23:22:14 +01:00
|
|
|
YUV,
|
2011-06-20 09:45:15 +02:00
|
|
|
ALPHA_PLANE_ONLY // this is for experimenting only
|
|
|
|
} OutputFileFormat;
|
2011-02-17 00:01:27 +01:00
|
|
|
|
2011-06-07 02:56:50 +02:00
|
|
|
#ifdef HAVE_WINCODEC_H
|
2011-02-17 00:01:27 +01:00
|
|
|
|
2012-10-09 03:33:18 +02:00
|
|
|
#define IFS(fn) \
|
|
|
|
do { \
|
2013-02-05 01:13:59 +01:00
|
|
|
if (SUCCEEDED(hr)) { \
|
|
|
|
hr = (fn); \
|
|
|
|
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
|
|
|
} \
|
2011-02-17 00:01:27 +01:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
#define MAKE_REFGUID(x) (x)
|
|
|
|
#else
|
|
|
|
#define MAKE_REFGUID(x) &(x)
|
|
|
|
#endif
|
|
|
|
|
2011-06-20 09:45:15 +02:00
|
|
|
static HRESULT CreateOutputStream(const char* out_file_name,
|
|
|
|
IStream** ppStream) {
|
2011-02-17 00:01:27 +01:00
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, ppStream));
|
2013-02-05 01:13:59 +01:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
fprintf(stderr, "Error opening output file %s (%08lx)\n",
|
|
|
|
out_file_name, hr);
|
|
|
|
}
|
2011-02-17 00:01:27 +01:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WriteUsingWIC(const char* out_file_name, REFGUID container_guid,
|
|
|
|
unsigned char* rgb, int stride,
|
2011-06-22 03:29:30 +02:00
|
|
|
uint32_t width, uint32_t height, int has_alpha) {
|
2011-02-17 00:01:27 +01:00
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IWICImagingFactory* pFactory = NULL;
|
|
|
|
IWICBitmapFrameEncode* pFrame = NULL;
|
|
|
|
IWICBitmapEncoder* pEncoder = NULL;
|
|
|
|
IStream* pStream = NULL;
|
2011-06-22 03:29:30 +02:00
|
|
|
WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
|
|
|
|
: GUID_WICPixelFormat24bppBGR;
|
2011-02-17 00:01:27 +01:00
|
|
|
|
|
|
|
IFS(CoInitialize(NULL));
|
|
|
|
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
|
|
|
CLSCTX_INPROC_SERVER, MAKE_REFGUID(IID_IWICImagingFactory),
|
|
|
|
(LPVOID*)&pFactory));
|
|
|
|
if (hr == REGDB_E_CLASSNOTREG) {
|
2012-01-24 21:46:46 +01:00
|
|
|
fprintf(stderr,
|
|
|
|
"Couldn't access Windows Imaging Component (are you running "
|
|
|
|
"Windows XP SP3 or newer?). PNG support not available. "
|
|
|
|
"Use -ppm or -pgm for available PPM and PGM formats.\n");
|
2011-02-17 00:01:27 +01:00
|
|
|
}
|
|
|
|
IFS(CreateOutputStream(out_file_name, &pStream));
|
|
|
|
IFS(IWICImagingFactory_CreateEncoder(pFactory, container_guid, NULL,
|
|
|
|
&pEncoder));
|
2011-02-18 19:26:42 +01:00
|
|
|
IFS(IWICBitmapEncoder_Initialize(pEncoder, pStream,
|
|
|
|
WICBitmapEncoderNoCache));
|
2011-02-17 00:01:27 +01:00
|
|
|
IFS(IWICBitmapEncoder_CreateNewFrame(pEncoder, &pFrame, NULL));
|
|
|
|
IFS(IWICBitmapFrameEncode_Initialize(pFrame, NULL));
|
|
|
|
IFS(IWICBitmapFrameEncode_SetSize(pFrame, width, height));
|
|
|
|
IFS(IWICBitmapFrameEncode_SetPixelFormat(pFrame, &pixel_format));
|
|
|
|
IFS(IWICBitmapFrameEncode_WritePixels(pFrame, height, stride,
|
|
|
|
height * stride, rgb));
|
|
|
|
IFS(IWICBitmapFrameEncode_Commit(pFrame));
|
|
|
|
IFS(IWICBitmapEncoder_Commit(pEncoder));
|
|
|
|
|
|
|
|
if (pFrame != NULL) IUnknown_Release(pFrame);
|
|
|
|
if (pEncoder != NULL) IUnknown_Release(pEncoder);
|
|
|
|
if (pFactory != NULL) IUnknown_Release(pFactory);
|
|
|
|
if (pStream != NULL) IUnknown_Release(pStream);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2011-06-20 09:45:15 +02:00
|
|
|
static int WritePNG(const char* out_file_name,
|
|
|
|
const WebPDecBuffer* const buffer) {
|
|
|
|
const uint32_t width = buffer->width;
|
|
|
|
const uint32_t height = buffer->height;
|
|
|
|
unsigned char* const rgb = buffer->u.RGBA.rgba;
|
|
|
|
const int stride = buffer->u.RGBA.stride;
|
2011-06-22 03:29:30 +02:00
|
|
|
const int has_alpha = (buffer->colorspace == MODE_BGRA);
|
|
|
|
|
2011-02-17 00:01:27 +01:00
|
|
|
return SUCCEEDED(WriteUsingWIC(out_file_name,
|
|
|
|
MAKE_REFGUID(GUID_ContainerFormatPng), rgb, stride, width,
|
2011-06-22 03:29:30 +02:00
|
|
|
height, has_alpha));
|
2011-02-17 00:01:27 +01:00
|
|
|
}
|
|
|
|
|
2011-06-07 02:56:50 +02:00
|
|
|
#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
|
2011-02-01 07:00:33 +01:00
|
|
|
static void PNGAPI error_function(png_structp png, png_const_charp dummy) {
|
2011-03-25 23:04:11 +01:00
|
|
|
(void)dummy; // remove variable-unused warning
|
2011-02-01 07:00:33 +01:00
|
|
|
longjmp(png_jmpbuf(png), 1);
|
|
|
|
}
|
|
|
|
|
2011-06-20 09:45:15 +02:00
|
|
|
static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
|
|
|
const uint32_t width = buffer->width;
|
|
|
|
const uint32_t height = buffer->height;
|
|
|
|
unsigned char* const rgb = buffer->u.RGBA.rgba;
|
|
|
|
const int stride = buffer->u.RGBA.stride;
|
|
|
|
const int has_alpha = (buffer->colorspace == MODE_RGBA);
|
2011-02-01 07:00:33 +01:00
|
|
|
png_structp png;
|
|
|
|
png_infop info;
|
2011-03-25 23:04:11 +01:00
|
|
|
png_uint_32 y;
|
2011-02-01 07:00:33 +01:00
|
|
|
|
|
|
|
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
|
|
|
NULL, error_function, NULL);
|
|
|
|
if (png == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
info = png_create_info_struct(png);
|
|
|
|
if (info == NULL) {
|
|
|
|
png_destroy_write_struct(&png, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (setjmp(png_jmpbuf(png))) {
|
|
|
|
png_destroy_write_struct(&png, &info);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
png_init_io(png, out_file);
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
png_set_IHDR(png, info, width, height, 8,
|
|
|
|
has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
|
2011-02-01 07:00:33 +01:00
|
|
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
|
|
|
PNG_FILTER_TYPE_DEFAULT);
|
|
|
|
png_write_info(png, info);
|
|
|
|
for (y = 0; y < height; ++y) {
|
|
|
|
png_bytep row = rgb + y * stride;
|
|
|
|
png_write_rows(png, &row, 1);
|
|
|
|
}
|
|
|
|
png_write_end(png, info);
|
|
|
|
png_destroy_write_struct(&png, &info);
|
|
|
|
return 1;
|
|
|
|
}
|
2011-06-07 02:56:50 +02:00
|
|
|
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
|
2011-06-20 09:45:15 +02:00
|
|
|
static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
2011-06-24 22:50:02 +02:00
|
|
|
(void)out_file;
|
|
|
|
(void)buffer;
|
2012-01-24 21:46:46 +01:00
|
|
|
fprintf(stderr, "PNG support not compiled. Please install the libpng "
|
|
|
|
"development package before building.\n");
|
|
|
|
fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
|
2011-02-01 07:00:33 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-07-21 01:06:06 +02:00
|
|
|
static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) {
|
2011-06-20 09:45:15 +02:00
|
|
|
const uint32_t width = buffer->width;
|
|
|
|
const uint32_t height = buffer->height;
|
|
|
|
const unsigned char* const rgb = buffer->u.RGBA.rgba;
|
|
|
|
const int stride = buffer->u.RGBA.stride;
|
2012-07-21 01:06:06 +02:00
|
|
|
const size_t bytes_per_px = alpha ? 4 : 3;
|
2011-06-20 09:45:15 +02:00
|
|
|
uint32_t y;
|
2012-07-21 01:06:06 +02:00
|
|
|
|
|
|
|
if (alpha) {
|
|
|
|
fprintf(fout, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n"
|
|
|
|
"TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
|
|
|
|
} else {
|
|
|
|
fprintf(fout, "P6\n%d %d\n255\n", width, height);
|
|
|
|
}
|
2011-06-20 09:45:15 +02:00
|
|
|
for (y = 0; y < height; ++y) {
|
2012-07-21 01:06:06 +02:00
|
|
|
if (fwrite(rgb + y * stride, width, bytes_per_px, fout) != bytes_per_px) {
|
2011-06-20 09:45:15 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
2011-02-17 00:01:27 +01:00
|
|
|
}
|
|
|
|
|
2011-06-20 09:45:15 +02:00
|
|
|
static int WriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
|
|
|
|
const uint32_t width = buffer->width;
|
|
|
|
const uint32_t height = buffer->height;
|
|
|
|
const unsigned char* const a = buffer->u.YUVA.a;
|
|
|
|
const int a_stride = buffer->u.YUVA.a_stride;
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
uint32_t y;
|
2011-06-20 09:45:15 +02:00
|
|
|
assert(a != NULL);
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
fprintf(fout, "P5\n%d %d\n255\n", width, height);
|
|
|
|
for (y = 0; y < height; ++y) {
|
2011-06-20 09:45:15 +02:00
|
|
|
if (fwrite(a + y * a_stride, width, 1, fout) != 1) {
|
|
|
|
return 0;
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-01-28 23:22:14 +01:00
|
|
|
// format=PGM: save a grayscale PGM file using the IMC4 layout
|
|
|
|
// (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for
|
|
|
|
// viewing the samples, esp. for odd dimensions.
|
|
|
|
// format=YUV: just save the Y/U/V/A planes sequentially without header.
|
|
|
|
static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer,
|
|
|
|
OutputFileFormat format) {
|
2011-06-20 09:45:15 +02:00
|
|
|
const int width = buffer->width;
|
|
|
|
const int height = buffer->height;
|
|
|
|
const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
|
2011-02-17 00:01:27 +01:00
|
|
|
int ok = 1;
|
2011-06-20 09:45:15 +02:00
|
|
|
int y;
|
2013-01-28 23:22:14 +01:00
|
|
|
const int pad = (format == YUV) ? 0 : 1;
|
2011-06-20 09:45:15 +02:00
|
|
|
const int uv_width = (width + 1) / 2;
|
|
|
|
const int uv_height = (height + 1) / 2;
|
2013-01-28 23:22:14 +01:00
|
|
|
const int out_stride = (width + pad) & ~pad;
|
2011-06-20 09:45:15 +02:00
|
|
|
const int a_height = yuv->a ? height : 0;
|
2013-01-28 23:22:14 +01:00
|
|
|
if (format == PGM) {
|
|
|
|
fprintf(fout, "P5\n%d %d\n255\n",
|
|
|
|
out_stride, height + uv_height + a_height);
|
|
|
|
}
|
2011-02-17 00:01:27 +01:00
|
|
|
for (y = 0; ok && y < height; ++y) {
|
2011-06-20 09:45:15 +02:00
|
|
|
ok &= (fwrite(yuv->y + y * yuv->y_stride, width, 1, fout) == 1);
|
2013-01-28 23:22:14 +01:00
|
|
|
if (format == PGM) {
|
|
|
|
if (width & 1) fputc(0, fout); // padding byte
|
|
|
|
}
|
2011-02-17 00:01:27 +01:00
|
|
|
}
|
2013-01-28 23:22:14 +01:00
|
|
|
if (format == PGM) { // IMC4 layout
|
|
|
|
for (y = 0; ok && y < uv_height; ++y) {
|
|
|
|
ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1);
|
|
|
|
ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (y = 0; ok && y < uv_height; ++y) {
|
|
|
|
ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1);
|
|
|
|
}
|
|
|
|
for (y = 0; ok && y < uv_height; ++y) {
|
|
|
|
ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1);
|
|
|
|
}
|
2011-06-20 09:45:15 +02:00
|
|
|
}
|
|
|
|
for (y = 0; ok && y < a_height; ++y) {
|
|
|
|
ok &= (fwrite(yuv->a + y * yuv->a_stride, width, 1, fout) == 1);
|
2013-01-28 23:22:14 +01:00
|
|
|
if (format == PGM) {
|
|
|
|
if (width & 1) fputc(0, fout); // padding byte
|
|
|
|
}
|
2011-02-17 00:01:27 +01:00
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2011-06-20 09:45:15 +02:00
|
|
|
static void SaveOutput(const WebPDecBuffer* const buffer,
|
|
|
|
OutputFileFormat format, const char* const out_file) {
|
|
|
|
FILE* fout = NULL;
|
|
|
|
int needs_open_file = 1;
|
|
|
|
int ok = 1;
|
|
|
|
Stopwatch stop_watch;
|
|
|
|
|
|
|
|
if (verbose)
|
|
|
|
StopwatchReadAndReset(&stop_watch);
|
|
|
|
|
2011-06-22 03:29:30 +02:00
|
|
|
#ifdef HAVE_WINCODEC_H
|
2011-06-20 09:45:15 +02:00
|
|
|
needs_open_file = (format != PNG);
|
|
|
|
#endif
|
|
|
|
if (needs_open_file) {
|
|
|
|
fout = fopen(out_file, "wb");
|
|
|
|
if (!fout) {
|
|
|
|
fprintf(stderr, "Error opening output file %s\n", out_file);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (format == PNG) {
|
|
|
|
#ifdef HAVE_WINCODEC_H
|
|
|
|
ok &= WritePNG(out_file, buffer);
|
|
|
|
#else
|
|
|
|
ok &= WritePNG(fout, buffer);
|
|
|
|
#endif
|
2012-07-21 01:06:06 +02:00
|
|
|
} else if (format == PAM) {
|
|
|
|
ok &= WritePPM(fout, buffer, 1);
|
2011-06-20 09:45:15 +02:00
|
|
|
} else if (format == PPM) {
|
2012-07-21 01:06:06 +02:00
|
|
|
ok &= WritePPM(fout, buffer, 0);
|
2013-01-28 23:22:14 +01:00
|
|
|
} else if (format == PGM || format == YUV) {
|
|
|
|
ok &= WritePGMOrYUV(fout, buffer, format);
|
2011-06-20 09:45:15 +02:00
|
|
|
} else if (format == ALPHA_PLANE_ONLY) {
|
|
|
|
ok &= WriteAlphaPlane(fout, buffer);
|
|
|
|
}
|
|
|
|
if (fout) {
|
|
|
|
fclose(fout);
|
|
|
|
}
|
|
|
|
if (ok) {
|
|
|
|
printf("Saved file %s\n", out_file);
|
|
|
|
if (verbose) {
|
2013-01-21 17:20:14 +01:00
|
|
|
const double write_time = StopwatchReadAndReset(&stop_watch);
|
|
|
|
printf("Time to write output: %.3fs\n", write_time);
|
2011-06-20 09:45:15 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Error writing file %s !!\n", out_file);
|
|
|
|
}
|
|
|
|
}
|
2011-02-01 07:00:33 +01:00
|
|
|
|
2011-03-25 23:04:11 +01:00
|
|
|
static void Help(void) {
|
2011-06-20 09:45:15 +02:00
|
|
|
printf("Usage: dwebp in_file [options] [-o out_file]\n\n"
|
2011-02-01 07:00:33 +01:00
|
|
|
"Decodes the WebP image file to PNG format [Default]\n"
|
|
|
|
"Use following options to convert into alternate image formats:\n"
|
2012-07-21 01:06:06 +02:00
|
|
|
" -pam ......... save the raw RGBA samples as a color PAM\n"
|
|
|
|
" -ppm ......... save the raw RGB samples as a color PPM\n"
|
2011-06-20 09:45:15 +02:00
|
|
|
" -pgm ......... save the raw YUV samples as a grayscale PGM\n"
|
|
|
|
" file with IMC4 layout.\n"
|
2013-01-28 23:22:14 +01:00
|
|
|
" -yuv ......... save the raw YUV samples in flat layout.\n"
|
|
|
|
"\n"
|
2011-06-20 09:45:15 +02:00
|
|
|
" Other options are:\n"
|
|
|
|
" -version .... print version number and exit.\n"
|
|
|
|
" -nofancy ..... don't use the fancy YUV420 upscaler.\n"
|
|
|
|
" -nofilter .... disable in-loop filtering.\n"
|
2011-07-22 22:09:10 +02:00
|
|
|
" -mt .......... use multi-threading\n"
|
2011-06-20 09:45:15 +02:00
|
|
|
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
|
|
|
|
" -scale <w> <h> .......... scale the output (*after* any cropping)\n"
|
|
|
|
" -alpha ....... only save the alpha plane.\n"
|
|
|
|
" -h ....... this help message.\n"
|
|
|
|
" -v ....... verbose (e.g. print encoding/decoding times)\n"
|
2011-07-15 23:53:03 +02:00
|
|
|
#ifndef WEBP_DLL
|
2011-06-20 09:45:15 +02:00
|
|
|
" -noasm ....... disable all assembly optimizations.\n"
|
2011-07-15 23:53:03 +02:00
|
|
|
#endif
|
2010-12-17 14:53:50 +01:00
|
|
|
);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
2011-06-20 09:45:15 +02:00
|
|
|
static const char* const kStatusMessages[] = {
|
|
|
|
"OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
|
|
|
|
"UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
|
|
|
|
};
|
|
|
|
|
2011-01-23 21:47:44 +01:00
|
|
|
int main(int argc, const char *argv[]) {
|
2010-09-30 15:34:38 +02:00
|
|
|
const char *in_file = NULL;
|
|
|
|
const char *out_file = NULL;
|
|
|
|
|
2011-06-20 09:45:15 +02:00
|
|
|
WebPDecoderConfig config;
|
|
|
|
WebPDecBuffer* const output_buffer = &config.output;
|
|
|
|
WebPBitstreamFeatures* const bitstream = &config.input;
|
2011-02-01 07:00:33 +01:00
|
|
|
OutputFileFormat format = PNG;
|
2010-09-30 15:34:38 +02:00
|
|
|
int c;
|
2011-06-20 09:45:15 +02:00
|
|
|
|
|
|
|
if (!WebPInitDecoderConfig(&config)) {
|
|
|
|
fprintf(stderr, "Library version mismatch!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
for (c = 1; c < argc; ++c) {
|
2011-02-01 07:00:33 +01:00
|
|
|
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
2011-03-25 23:04:11 +01:00
|
|
|
Help();
|
2010-09-30 15:34:38 +02:00
|
|
|
return 0;
|
|
|
|
} else if (!strcmp(argv[c], "-o") && c < argc - 1) {
|
|
|
|
out_file = argv[++c];
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
} else if (!strcmp(argv[c], "-alpha")) {
|
|
|
|
format = ALPHA_PLANE_ONLY;
|
2011-06-20 09:45:15 +02:00
|
|
|
} else if (!strcmp(argv[c], "-nofancy")) {
|
|
|
|
config.options.no_fancy_upsampling = 1;
|
|
|
|
} else if (!strcmp(argv[c], "-nofilter")) {
|
|
|
|
config.options.bypass_filtering = 1;
|
2012-07-21 01:06:06 +02:00
|
|
|
} else if (!strcmp(argv[c], "-pam")) {
|
|
|
|
format = PAM;
|
2011-02-01 07:00:33 +01:00
|
|
|
} else if (!strcmp(argv[c], "-ppm")) {
|
|
|
|
format = PPM;
|
2011-03-25 00:17:10 +01:00
|
|
|
} else if (!strcmp(argv[c], "-version")) {
|
|
|
|
const int version = WebPGetDecoderVersion();
|
|
|
|
printf("%d.%d.%d\n",
|
2012-01-24 21:46:46 +01:00
|
|
|
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
2011-03-25 00:17:10 +01:00
|
|
|
return 0;
|
2011-02-01 07:00:33 +01:00
|
|
|
} else if (!strcmp(argv[c], "-pgm")) {
|
|
|
|
format = PGM;
|
2013-01-28 23:22:14 +01:00
|
|
|
} else if (!strcmp(argv[c], "-yuv")) {
|
|
|
|
format = YUV;
|
2011-07-22 22:09:10 +02:00
|
|
|
} else if (!strcmp(argv[c], "-mt")) {
|
|
|
|
config.options.use_threads = 1;
|
2011-06-20 09:45:15 +02:00
|
|
|
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
|
|
|
|
config.options.use_cropping = 1;
|
|
|
|
config.options.crop_left = strtol(argv[++c], NULL, 0);
|
|
|
|
config.options.crop_top = strtol(argv[++c], NULL, 0);
|
|
|
|
config.options.crop_width = strtol(argv[++c], NULL, 0);
|
|
|
|
config.options.crop_height = strtol(argv[++c], NULL, 0);
|
|
|
|
} else if (!strcmp(argv[c], "-scale") && c < argc - 2) {
|
|
|
|
config.options.use_scaling = 1;
|
|
|
|
config.options.scaled_width = strtol(argv[++c], NULL, 0);
|
|
|
|
config.options.scaled_height = strtol(argv[++c], NULL, 0);
|
2011-02-17 00:01:27 +01:00
|
|
|
} else if (!strcmp(argv[c], "-v")) {
|
|
|
|
verbose = 1;
|
2011-07-15 23:53:03 +02:00
|
|
|
#ifndef WEBP_DLL
|
2011-06-11 00:10:18 +02:00
|
|
|
} else if (!strcmp(argv[c], "-noasm")) {
|
2011-09-02 23:30:08 +02:00
|
|
|
VP8GetCPUInfo = NULL;
|
2011-07-15 23:53:03 +02:00
|
|
|
#endif
|
2010-09-30 15:34:38 +02:00
|
|
|
} else if (argv[c][0] == '-') {
|
2012-01-24 21:46:46 +01:00
|
|
|
fprintf(stderr, "Unknown option '%s'\n", argv[c]);
|
2011-03-25 23:04:11 +01:00
|
|
|
Help();
|
2010-09-30 15:34:38 +02:00
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
in_file = argv[c];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (in_file == NULL) {
|
2012-01-24 21:46:46 +01:00
|
|
|
fprintf(stderr, "missing input file!!\n");
|
2011-03-25 23:04:11 +01:00
|
|
|
Help();
|
2010-09-30 15:34:38 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2011-06-20 09:45:15 +02:00
|
|
|
Stopwatch stop_watch;
|
|
|
|
VP8StatusCode status = VP8_STATUS_OK;
|
|
|
|
int ok;
|
2012-05-12 01:00:57 +02:00
|
|
|
size_t data_size = 0;
|
|
|
|
const uint8_t* data = NULL;
|
2011-06-20 09:45:15 +02:00
|
|
|
|
2012-05-12 01:00:57 +02:00
|
|
|
if (!ExUtilReadFile(in_file, &data, &data_size)) return -1;
|
2010-09-30 15:34:38 +02:00
|
|
|
|
2011-02-17 00:01:27 +01:00
|
|
|
if (verbose)
|
|
|
|
StopwatchReadAndReset(&stop_watch);
|
2011-06-20 09:45:15 +02:00
|
|
|
|
2012-05-12 01:00:57 +02:00
|
|
|
status = WebPGetFeatures(data, data_size, bitstream);
|
2011-06-20 09:45:15 +02:00
|
|
|
if (status != VP8_STATUS_OK) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2011-02-01 07:00:33 +01:00
|
|
|
switch (format) {
|
|
|
|
case PNG:
|
2011-06-22 03:29:30 +02:00
|
|
|
#ifdef HAVE_WINCODEC_H
|
|
|
|
output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
|
2011-02-17 00:01:27 +01:00
|
|
|
#else
|
2011-06-20 09:45:15 +02:00
|
|
|
output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
|
2011-02-17 00:01:27 +01:00
|
|
|
#endif
|
|
|
|
break;
|
2012-07-21 01:06:06 +02:00
|
|
|
case PAM:
|
|
|
|
output_buffer->colorspace = MODE_RGBA;
|
|
|
|
break;
|
2011-02-01 07:00:33 +01:00
|
|
|
case PPM:
|
2011-06-20 09:45:15 +02:00
|
|
|
output_buffer->colorspace = MODE_RGB; // drops alpha for PPM
|
2011-02-01 07:00:33 +01:00
|
|
|
break;
|
|
|
|
case PGM:
|
2013-01-28 23:22:14 +01:00
|
|
|
case YUV:
|
2011-06-20 09:45:15 +02:00
|
|
|
output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
|
2011-02-01 07:00:33 +01:00
|
|
|
break;
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
case ALPHA_PLANE_ONLY:
|
2011-06-20 09:45:15 +02:00
|
|
|
output_buffer->colorspace = MODE_YUVA;
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
break;
|
2011-02-01 07:00:33 +01:00
|
|
|
default:
|
2012-05-12 01:00:57 +02:00
|
|
|
free((void*)data);
|
2011-02-01 07:00:33 +01:00
|
|
|
return -1;
|
2010-10-31 08:36:33 +01:00
|
|
|
}
|
2012-05-12 01:00:57 +02:00
|
|
|
status = WebPDecode(data, data_size, &config);
|
2011-02-01 07:00:33 +01:00
|
|
|
|
2011-02-17 00:01:27 +01:00
|
|
|
if (verbose) {
|
2013-01-21 17:20:14 +01:00
|
|
|
const double decode_time = StopwatchReadAndReset(&stop_watch);
|
|
|
|
printf("Time to decode picture: %.3fs\n", decode_time);
|
2011-02-17 00:01:27 +01:00
|
|
|
}
|
2011-06-20 09:45:15 +02:00
|
|
|
end:
|
2012-05-12 01:00:57 +02:00
|
|
|
free((void*)data);
|
2011-06-20 09:45:15 +02:00
|
|
|
ok = (status == VP8_STATUS_OK);
|
|
|
|
if (!ok) {
|
|
|
|
fprintf(stderr, "Decoding of %s failed.\n", in_file);
|
|
|
|
fprintf(stderr, "Status: %d (%s)\n", status, kStatusMessages[status]);
|
|
|
|
return -1;
|
|
|
|
}
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (out_file) {
|
2011-06-20 09:45:15 +02:00
|
|
|
printf("Decoded %s. Dimensions: %d x %d%s. Now saving...\n", in_file,
|
|
|
|
output_buffer->width, output_buffer->height,
|
|
|
|
bitstream->has_alpha ? " (with alpha)" : "");
|
|
|
|
SaveOutput(output_buffer, format, out_file);
|
2011-02-17 00:01:27 +01:00
|
|
|
} else {
|
2011-06-20 09:45:15 +02:00
|
|
|
printf("File %s can be decoded (dimensions: %d x %d)%s.\n",
|
|
|
|
in_file, output_buffer->width, output_buffer->height,
|
|
|
|
bitstream->has_alpha ? " (with alpha)" : "");
|
2011-02-17 00:01:27 +01:00
|
|
|
printf("Nothing written; use -o flag to save the result as e.g. PNG.\n");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
2011-06-20 09:45:15 +02:00
|
|
|
WebPFreeDecBuffer(output_buffer);
|
2010-09-30 15:34:38 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|