2012-01-06 23:49:06 +01:00
|
|
|
// Copyright 2011 Google Inc. All Rights Reserved.
|
2011-02-19 08:33:46 +01: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/
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// simple command line calling the WebPEncode function.
|
|
|
|
// Encodes a raw .YUV into WebP bitstream
|
|
|
|
//
|
|
|
|
// Author: Skal (pascal.massimino@gmail.com)
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2011-03-26 17:27:45 +01:00
|
|
|
#include <stdlib.h>
|
2011-02-19 08:33:46 +01:00
|
|
|
#include <string.h>
|
|
|
|
|
2011-06-07 02:56:50 +02:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_WINCODEC_H
|
|
|
|
#ifdef __MINGW32__
|
|
|
|
#define INITGUID // Without this GUIDs are declared extern and fail to link
|
|
|
|
#endif
|
2011-02-19 08:33:46 +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>
|
2011-06-07 02:56:50 +02:00
|
|
|
#endif /* HAVE_WINCODEC_H */
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
#include "webp/encode.h"
|
2012-12-11 11:02:39 +01:00
|
|
|
|
2012-12-04 03:20:00 +01:00
|
|
|
#include "./metadata.h"
|
2012-05-15 22:48:11 +02:00
|
|
|
#include "./stopwatch.h"
|
2012-12-11 11:02:39 +01:00
|
|
|
|
|
|
|
#include "./jpegdec.h"
|
|
|
|
#include "./pngdec.h"
|
2012-12-06 23:04:36 +01:00
|
|
|
#include "./tiffdec.h"
|
2012-12-11 11:02:39 +01:00
|
|
|
|
2011-07-15 23:53:03 +02:00
|
|
|
#ifndef WEBP_DLL
|
2011-11-04 23:20:08 +01: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-04-22 21:14:45 +02:00
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
static int verbose = 0;
|
|
|
|
|
|
|
|
static int ReadYUV(FILE* in_file, WebPPicture* const pic) {
|
2012-07-18 23:58:53 +02:00
|
|
|
const int use_argb = pic->use_argb;
|
2011-02-19 08:33:46 +01:00
|
|
|
const int uv_width = (pic->width + 1) / 2;
|
|
|
|
const int uv_height = (pic->height + 1) / 2;
|
|
|
|
int y;
|
|
|
|
int ok = 0;
|
|
|
|
|
2012-07-18 23:58:53 +02:00
|
|
|
pic->use_argb = 0;
|
2011-02-19 08:33:46 +01:00
|
|
|
if (!WebPPictureAlloc(pic)) return ok;
|
|
|
|
|
|
|
|
for (y = 0; y < pic->height; ++y) {
|
|
|
|
if (fread(pic->y + y * pic->y_stride, pic->width, 1, in_file) != 1) {
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (y = 0; y < uv_height; ++y) {
|
|
|
|
if (fread(pic->u + y * pic->uv_stride, uv_width, 1, in_file) != 1)
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
for (y = 0; y < uv_height; ++y) {
|
|
|
|
if (fread(pic->v + y * pic->uv_stride, uv_width, 1, in_file) != 1)
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
ok = 1;
|
2012-07-18 23:58:53 +02:00
|
|
|
if (use_argb) ok = WebPPictureYUVAToARGB(pic);
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
End:
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2011-06-07 02:56:50 +02:00
|
|
|
#ifdef HAVE_WINCODEC_H
|
2011-02-19 08:33:46 +01:00
|
|
|
|
2012-10-09 03:33:18 +02:00
|
|
|
#define IFS(fn) \
|
|
|
|
do { \
|
|
|
|
if (SUCCEEDED(hr)) { \
|
|
|
|
hr = (fn); \
|
|
|
|
if (FAILED(hr)) fprintf(stderr, #fn " failed %08x\n", hr); \
|
|
|
|
} \
|
2011-02-19 08:33:46 +01:00
|
|
|
} while (0)
|
|
|
|
|
2012-05-09 02:49:39 +02:00
|
|
|
// modified version of DEFINE_GUID from guiddef.h.
|
|
|
|
#define WEBP_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
|
|
const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
|
|
|
|
2011-02-19 08:33:46 +01:00
|
|
|
#ifdef __cplusplus
|
|
|
|
#define MAKE_REFGUID(x) (x)
|
|
|
|
#else
|
|
|
|
#define MAKE_REFGUID(x) &(x)
|
|
|
|
#endif
|
|
|
|
|
2012-10-20 03:34:06 +02:00
|
|
|
typedef struct WICFormatImporter {
|
|
|
|
const GUID* pixel_format;
|
|
|
|
int bytes_per_pixel;
|
|
|
|
int (*import)(WebPPicture* const, const uint8_t* const, int);
|
|
|
|
} WICFormatImporter;
|
|
|
|
|
2011-02-19 08:33:46 +01:00
|
|
|
static HRESULT OpenInputStream(const char* filename, IStream** ppStream) {
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IFS(SHCreateStreamOnFileA(filename, STGM_READ, ppStream));
|
|
|
|
if (FAILED(hr))
|
2012-01-24 21:46:46 +01:00
|
|
|
fprintf(stderr, "Error opening input file %s (%08x)\n", filename, hr);
|
2011-02-19 08:33:46 +01:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT ReadPictureWithWIC(const char* filename,
|
2011-06-22 03:29:30 +02:00
|
|
|
WebPPicture* const pic, int keep_alpha) {
|
2012-10-20 03:34:06 +02:00
|
|
|
// From Microsoft SDK 7.0a -- wincodec.h
|
|
|
|
// Create local copies for compatibility when building against earlier
|
|
|
|
// versions of the SDK.
|
|
|
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppBGR_,
|
|
|
|
0x6fddc324, 0x4e03, 0x4bfe,
|
|
|
|
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c);
|
|
|
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_,
|
|
|
|
0x6fddc324, 0x4e03, 0x4bfe,
|
|
|
|
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
|
|
|
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_,
|
|
|
|
0x6fddc324, 0x4e03, 0x4bfe,
|
|
|
|
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f);
|
|
|
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
|
|
|
|
0xf5c7ad2d, 0x6a8d, 0x43dd,
|
|
|
|
0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
|
|
|
|
const WICFormatImporter alphaFormatImporters[] = {
|
|
|
|
{ &GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA },
|
|
|
|
{ &GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA },
|
|
|
|
{ NULL, 0, NULL },
|
|
|
|
};
|
|
|
|
const WICFormatImporter nonAlphaFormatImporters[] = {
|
|
|
|
{ &GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR },
|
|
|
|
{ &GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB },
|
|
|
|
{ NULL, 0, NULL },
|
|
|
|
};
|
2011-02-19 08:33:46 +01:00
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IWICBitmapFrameDecode* pFrame = NULL;
|
|
|
|
IWICFormatConverter* pConverter = NULL;
|
|
|
|
IWICImagingFactory* pFactory = NULL;
|
|
|
|
IWICBitmapDecoder* pDecoder = NULL;
|
|
|
|
IStream* pStream = NULL;
|
|
|
|
UINT frameCount = 0;
|
2012-08-02 23:03:30 +02:00
|
|
|
UINT width = 0, height = 0;
|
2011-02-19 08:33:46 +01:00
|
|
|
BYTE* rgb = NULL;
|
2011-06-22 03:29:30 +02:00
|
|
|
WICPixelFormatGUID srcPixelFormat = { 0 };
|
2012-10-20 03:34:06 +02:00
|
|
|
const WICFormatImporter* importer = NULL;
|
2011-06-22 03:29:30 +02:00
|
|
|
GUID srcContainerFormat = { 0 };
|
|
|
|
const GUID* alphaContainers[] = {
|
|
|
|
&GUID_ContainerFormatBmp,
|
|
|
|
&GUID_ContainerFormatPng,
|
|
|
|
&GUID_ContainerFormatTiff
|
|
|
|
};
|
|
|
|
int has_alpha = 0;
|
|
|
|
int i, stride;
|
2011-02-19 08:33:46 +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?). Most formats not available. "
|
|
|
|
"Use -s for the available YUV input.\n");
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
// Prepare for image decoding.
|
|
|
|
IFS(OpenInputStream(filename, &pStream));
|
|
|
|
IFS(IWICImagingFactory_CreateDecoderFromStream(pFactory, pStream, NULL,
|
|
|
|
WICDecodeMetadataCacheOnDemand, &pDecoder));
|
|
|
|
IFS(IWICBitmapDecoder_GetFrameCount(pDecoder, &frameCount));
|
|
|
|
if (SUCCEEDED(hr) && frameCount == 0) {
|
2012-01-24 21:46:46 +01:00
|
|
|
fprintf(stderr, "No frame found in input file.\n");
|
2011-02-19 08:33:46 +01:00
|
|
|
hr = E_FAIL;
|
|
|
|
}
|
|
|
|
IFS(IWICBitmapDecoder_GetFrame(pDecoder, 0, &pFrame));
|
2011-06-22 03:29:30 +02:00
|
|
|
IFS(IWICBitmapFrameDecode_GetPixelFormat(pFrame, &srcPixelFormat));
|
|
|
|
IFS(IWICBitmapDecoder_GetContainerFormat(pDecoder, &srcContainerFormat));
|
|
|
|
|
2012-10-09 03:15:30 +02:00
|
|
|
if (keep_alpha) {
|
|
|
|
for (i = 0;
|
|
|
|
i < sizeof(alphaContainers) / sizeof(alphaContainers[0]);
|
|
|
|
++i) {
|
|
|
|
if (IsEqualGUID(MAKE_REFGUID(srcContainerFormat),
|
|
|
|
MAKE_REFGUID(*alphaContainers[i]))) {
|
|
|
|
has_alpha =
|
|
|
|
IsEqualGUID(MAKE_REFGUID(srcPixelFormat),
|
|
|
|
MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
|
|
|
|
IsEqualGUID(MAKE_REFGUID(srcPixelFormat),
|
|
|
|
MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_));
|
|
|
|
break;
|
|
|
|
}
|
2011-06-22 03:29:30 +02:00
|
|
|
}
|
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
// Prepare for pixel format conversion (if necessary).
|
|
|
|
IFS(IWICImagingFactory_CreateFormatConverter(pFactory, &pConverter));
|
2012-10-20 03:34:06 +02:00
|
|
|
|
|
|
|
for (importer = has_alpha ? alphaFormatImporters : nonAlphaFormatImporters;
|
|
|
|
hr == S_OK && importer->import != NULL; ++importer) {
|
|
|
|
BOOL canConvert;
|
|
|
|
const HRESULT cchr = IWICFormatConverter_CanConvert(
|
|
|
|
pConverter,
|
|
|
|
MAKE_REFGUID(srcPixelFormat),
|
|
|
|
MAKE_REFGUID(*importer->pixel_format),
|
|
|
|
&canConvert);
|
|
|
|
if (SUCCEEDED(cchr) && canConvert) break;
|
|
|
|
}
|
|
|
|
if (importer->import == NULL) hr = E_FAIL;
|
|
|
|
|
2011-02-19 08:33:46 +01:00
|
|
|
IFS(IWICFormatConverter_Initialize(pConverter, (IWICBitmapSource*)pFrame,
|
2012-10-20 03:34:06 +02:00
|
|
|
importer->pixel_format,
|
2011-06-22 03:29:30 +02:00
|
|
|
WICBitmapDitherTypeNone,
|
2011-02-19 08:33:46 +01:00
|
|
|
NULL, 0.0, WICBitmapPaletteTypeCustom));
|
|
|
|
|
|
|
|
// Decode.
|
|
|
|
IFS(IWICFormatConverter_GetSize(pConverter, &width, &height));
|
2012-10-20 03:34:06 +02:00
|
|
|
stride = importer->bytes_per_pixel * width * sizeof(*rgb);
|
2011-02-19 08:33:46 +01:00
|
|
|
if (SUCCEEDED(hr)) {
|
2011-06-22 03:29:30 +02:00
|
|
|
rgb = (BYTE*)malloc(stride * height);
|
2011-02-19 08:33:46 +01:00
|
|
|
if (rgb == NULL)
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
}
|
2011-06-22 03:29:30 +02:00
|
|
|
IFS(IWICFormatConverter_CopyPixels(pConverter, NULL, stride,
|
|
|
|
stride * height, rgb));
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
// WebP conversion.
|
|
|
|
if (SUCCEEDED(hr)) {
|
2011-06-22 03:29:30 +02:00
|
|
|
int ok;
|
2011-02-19 08:33:46 +01:00
|
|
|
pic->width = width;
|
|
|
|
pic->height = height;
|
2012-10-20 03:34:06 +02:00
|
|
|
ok = importer->import(pic, rgb, stride);
|
2011-06-22 03:29:30 +02:00
|
|
|
if (!ok)
|
2011-02-19 08:33:46 +01:00
|
|
|
hr = E_FAIL;
|
|
|
|
}
|
2012-01-17 09:18:22 +01:00
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
if (has_alpha && keep_alpha == 2) {
|
|
|
|
WebPCleanupTransparentArea(pic);
|
|
|
|
}
|
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
// Cleanup.
|
|
|
|
if (pConverter != NULL) IUnknown_Release(pConverter);
|
|
|
|
if (pFrame != NULL) IUnknown_Release(pFrame);
|
|
|
|
if (pDecoder != NULL) IUnknown_Release(pDecoder);
|
|
|
|
if (pFactory != NULL) IUnknown_Release(pFactory);
|
|
|
|
if (pStream != NULL) IUnknown_Release(pStream);
|
|
|
|
free(rgb);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
static int ReadPicture(const char* const filename, WebPPicture* const pic,
|
2012-12-04 03:20:00 +01:00
|
|
|
int keep_alpha, Metadata* const metadata) {
|
2011-02-19 08:33:46 +01:00
|
|
|
int ok;
|
2012-12-04 03:20:00 +01:00
|
|
|
(void)metadata; // TODO(jzern): add metadata extraction using WIC
|
2011-02-19 08:33:46 +01:00
|
|
|
if (pic->width != 0 && pic->height != 0) {
|
|
|
|
// If image size is specified, infer it as YUV format.
|
|
|
|
FILE* in_file = fopen(filename, "rb");
|
|
|
|
if (in_file == NULL) {
|
|
|
|
fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ok = ReadYUV(in_file, pic);
|
|
|
|
fclose(in_file);
|
|
|
|
} else {
|
|
|
|
// If no size specified, try to decode it using WIC.
|
2011-06-22 03:29:30 +02:00
|
|
|
ok = SUCCEEDED(ReadPictureWithWIC(filename, pic, keep_alpha));
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
if (!ok) {
|
|
|
|
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2011-06-07 02:56:50 +02:00
|
|
|
#else // !HAVE_WINCODEC_H
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
typedef enum {
|
2012-07-02 20:24:23 +02:00
|
|
|
PNG_ = 0,
|
|
|
|
JPEG_,
|
2012-07-02 02:55:21 +02:00
|
|
|
TIFF_, // 'TIFF' clashes with libtiff
|
2012-01-28 02:39:47 +01:00
|
|
|
UNSUPPORTED
|
2011-02-19 08:33:46 +01:00
|
|
|
} InputFileFormat;
|
|
|
|
|
|
|
|
static InputFileFormat GetImageType(FILE* in_file) {
|
|
|
|
InputFileFormat format = UNSUPPORTED;
|
|
|
|
unsigned int magic;
|
|
|
|
unsigned char buf[4];
|
|
|
|
|
|
|
|
if ((fread(&buf[0], 4, 1, in_file) != 1) ||
|
|
|
|
(fseek(in_file, 0, SEEK_SET) != 0)) {
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
|
|
|
magic = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
|
|
|
if (magic == 0x89504E47U) {
|
2012-07-02 20:24:23 +02:00
|
|
|
format = PNG_;
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (magic >= 0xFFD8FF00U && magic <= 0xFFD8FFFFU) {
|
2012-07-02 20:24:23 +02:00
|
|
|
format = JPEG_;
|
2012-07-02 02:55:21 +02:00
|
|
|
} else if (magic == 0x49492A00 || magic == 0x4D4D002A) {
|
|
|
|
format = TIFF_;
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
static int ReadPicture(const char* const filename, WebPPicture* const pic,
|
2012-12-04 03:20:00 +01:00
|
|
|
int keep_alpha, Metadata* const metadata) {
|
2011-02-19 08:33:46 +01:00
|
|
|
int ok = 0;
|
|
|
|
FILE* in_file = fopen(filename, "rb");
|
2012-12-04 03:20:00 +01:00
|
|
|
(void)metadata; // TODO(jzern): add metadata extraction to the formats below.
|
2011-02-19 08:33:46 +01:00
|
|
|
if (in_file == NULL) {
|
|
|
|
fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pic->width == 0 || pic->height == 0) {
|
|
|
|
// If no size specified, try to decode it as PNG/JPEG (as appropriate).
|
|
|
|
const InputFileFormat format = GetImageType(in_file);
|
2012-07-02 20:24:23 +02:00
|
|
|
if (format == PNG_) {
|
2012-12-05 04:00:30 +01:00
|
|
|
ok = ReadPNG(in_file, pic, keep_alpha, metadata);
|
2012-07-02 20:24:23 +02:00
|
|
|
} else if (format == JPEG_) {
|
2012-12-17 04:13:56 +01:00
|
|
|
ok = ReadJPEG(in_file, pic, metadata);
|
2012-07-02 02:55:21 +02:00
|
|
|
} else if (format == TIFF_) {
|
|
|
|
ok = ReadTIFF(filename, pic, keep_alpha);
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If image size is specified, infer it as YUV format.
|
|
|
|
ok = ReadYUV(in_file, pic);
|
|
|
|
}
|
|
|
|
if (!ok) {
|
|
|
|
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(in_file);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2011-06-07 02:56:50 +02:00
|
|
|
#endif // !HAVE_WINCODEC_H
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
static void AllocExtraInfo(WebPPicture* const pic) {
|
|
|
|
const int mb_w = (pic->width + 15) / 16;
|
|
|
|
const int mb_h = (pic->height + 15) / 16;
|
|
|
|
pic->extra_info = (uint8_t*)malloc(mb_w * mb_h * sizeof(*pic->extra_info));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PrintByteCount(const int bytes[4], int total_size,
|
|
|
|
int* const totals) {
|
|
|
|
int s;
|
|
|
|
int total = 0;
|
|
|
|
for (s = 0; s < 4; ++s) {
|
|
|
|
fprintf(stderr, "| %7d ", bytes[s]);
|
|
|
|
total += bytes[s];
|
|
|
|
if (totals) totals[s] += bytes[s];
|
|
|
|
}
|
2011-11-04 23:20:08 +01:00
|
|
|
fprintf(stderr, "| %7d (%.1f%%)\n", total, 100.f * total / total_size);
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void PrintPercents(const int counts[4], int total) {
|
|
|
|
int s;
|
|
|
|
for (s = 0; s < 4; ++s) {
|
|
|
|
fprintf(stderr, "| %2d%%", 100 * counts[s] / total);
|
|
|
|
}
|
2011-11-04 23:20:08 +01:00
|
|
|
fprintf(stderr, "| %7d\n", total);
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void PrintValues(const int values[4]) {
|
|
|
|
int s;
|
|
|
|
for (s = 0; s < 4; ++s) {
|
|
|
|
fprintf(stderr, "| %7d ", values[s]);
|
|
|
|
}
|
2011-11-04 23:20:08 +01:00
|
|
|
fprintf(stderr, "|\n");
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
2012-07-25 01:15:36 +02:00
|
|
|
static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
|
|
|
|
const char* const description) {
|
|
|
|
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
|
|
|
|
description, stats->lossless_size);
|
|
|
|
if (stats->lossless_features) {
|
|
|
|
fprintf(stderr, " * Lossless features used:");
|
|
|
|
if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
|
|
|
|
if (stats->lossless_features & 2) fprintf(stderr, " CROSS-COLOR-TRANSFORM");
|
|
|
|
if (stats->lossless_features & 4) fprintf(stderr, " SUBTRACT-GREEN");
|
|
|
|
if (stats->lossless_features & 8) fprintf(stderr, " PALETTE");
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
fprintf(stderr, " * Precision Bits: histogram=%d transform=%d cache=%d\n",
|
|
|
|
stats->histogram_bits, stats->transform_bits, stats->cache_bits);
|
|
|
|
if (stats->palette_size > 0) {
|
|
|
|
fprintf(stderr, " * Palette size: %d\n", stats->palette_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-09 07:57:46 +02:00
|
|
|
static void PrintExtraInfoLossless(const WebPPicture* const pic,
|
|
|
|
int short_output,
|
|
|
|
const char* const file_name) {
|
|
|
|
const WebPAuxStats* const stats = pic->stats;
|
|
|
|
if (short_output) {
|
|
|
|
fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "File: %s\n", file_name);
|
|
|
|
fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
|
|
|
|
fprintf(stderr, "Output: %d bytes\n", stats->coded_size);
|
2012-07-25 01:15:36 +02:00
|
|
|
PrintFullLosslessInfo(stats, "ARGB");
|
2012-05-09 07:57:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
|
|
|
const char* const file_name) {
|
2011-02-19 08:33:46 +01:00
|
|
|
const WebPAuxStats* const stats = pic->stats;
|
|
|
|
if (short_output) {
|
|
|
|
fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
|
2011-11-04 23:20:08 +01:00
|
|
|
} else {
|
2011-02-19 08:33:46 +01:00
|
|
|
const int num_i4 = stats->block_count[0];
|
|
|
|
const int num_i16 = stats->block_count[1];
|
|
|
|
const int num_skip = stats->block_count[2];
|
|
|
|
const int total = num_i4 + num_i16;
|
2012-01-20 16:20:56 +01:00
|
|
|
fprintf(stderr, "File: %s\n", file_name);
|
|
|
|
fprintf(stderr, "Dimension: %d x %d%s\n",
|
2012-06-08 02:32:29 +02:00
|
|
|
pic->width, pic->height,
|
|
|
|
stats->alpha_data_size ? " (with alpha)" : "");
|
2012-01-20 16:20:56 +01:00
|
|
|
fprintf(stderr, "Output: "
|
|
|
|
"%d bytes Y-U-V-All-PSNR %2.2f %2.2f %2.2f %2.2f dB\n",
|
2011-02-19 08:33:46 +01:00
|
|
|
stats->coded_size,
|
|
|
|
stats->PSNR[0], stats->PSNR[1], stats->PSNR[2], stats->PSNR[3]);
|
|
|
|
if (total > 0) {
|
|
|
|
int totals[4] = { 0, 0, 0, 0 };
|
|
|
|
fprintf(stderr, "block count: intra4: %d\n"
|
|
|
|
" intra16: %d (-> %.2f%%)\n",
|
|
|
|
num_i4, num_i16, 100.f * num_i16 / total);
|
|
|
|
fprintf(stderr, " skipped block: %d (%.2f%%)\n",
|
|
|
|
num_skip, 100.f * num_skip / total);
|
|
|
|
fprintf(stderr, "bytes used: header: %6d (%.1f%%)\n"
|
|
|
|
" mode-partition: %6d (%.1f%%)\n",
|
|
|
|
stats->header_bytes[0],
|
|
|
|
100.f * stats->header_bytes[0] / stats->coded_size,
|
|
|
|
stats->header_bytes[1],
|
|
|
|
100.f * stats->header_bytes[1] / stats->coded_size);
|
2012-07-25 01:15:36 +02:00
|
|
|
if (stats->alpha_data_size > 0) {
|
|
|
|
fprintf(stderr, " transparency: %6d (%.1f dB)\n",
|
|
|
|
stats->alpha_data_size, stats->PSNR[4]);
|
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
|
|
|
}
|
2011-05-03 02:19:00 +02:00
|
|
|
if (stats->layer_data_size) {
|
|
|
|
fprintf(stderr, " enhancement: %6d\n",
|
|
|
|
stats->layer_data_size);
|
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
fprintf(stderr, " Residuals bytes "
|
|
|
|
"|segment 1|segment 2|segment 3"
|
|
|
|
"|segment 4| total\n");
|
|
|
|
fprintf(stderr, " intra4-coeffs: ");
|
|
|
|
PrintByteCount(stats->residual_bytes[0], stats->coded_size, totals);
|
|
|
|
fprintf(stderr, " intra16-coeffs: ");
|
|
|
|
PrintByteCount(stats->residual_bytes[1], stats->coded_size, totals);
|
|
|
|
fprintf(stderr, " chroma coeffs: ");
|
|
|
|
PrintByteCount(stats->residual_bytes[2], stats->coded_size, totals);
|
|
|
|
fprintf(stderr, " macroblocks: ");
|
|
|
|
PrintPercents(stats->segment_size, total);
|
|
|
|
fprintf(stderr, " quantizer: ");
|
|
|
|
PrintValues(stats->segment_quant);
|
|
|
|
fprintf(stderr, " filter level: ");
|
|
|
|
PrintValues(stats->segment_level);
|
|
|
|
fprintf(stderr, "------------------+---------");
|
|
|
|
fprintf(stderr, "+---------+---------+---------+-----------------\n");
|
|
|
|
fprintf(stderr, " segments total: ");
|
|
|
|
PrintByteCount(totals, stats->coded_size, NULL);
|
|
|
|
}
|
2012-07-25 01:15:36 +02:00
|
|
|
if (stats->lossless_size > 0) {
|
|
|
|
PrintFullLosslessInfo(stats, "alpha");
|
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
2012-07-25 01:15:36 +02:00
|
|
|
if (pic->extra_info != NULL) {
|
2011-02-19 08:33:46 +01:00
|
|
|
const int mb_w = (pic->width + 15) / 16;
|
|
|
|
const int mb_h = (pic->height + 15) / 16;
|
|
|
|
const int type = pic->extra_info_type;
|
|
|
|
int x, y;
|
|
|
|
for (y = 0; y < mb_h; ++y) {
|
|
|
|
for (x = 0; x < mb_w; ++x) {
|
|
|
|
const int c = pic->extra_info[x + y * mb_w];
|
|
|
|
if (type == 1) { // intra4/intra16
|
|
|
|
printf("%c", "+."[c]);
|
|
|
|
} else if (type == 2) { // segments
|
|
|
|
printf("%c", ".-*X"[c]);
|
|
|
|
} else if (type == 3) { // quantizers
|
|
|
|
printf("%.2d ", c);
|
|
|
|
} else if (type == 6 || type == 7) {
|
|
|
|
printf("%3d ", c);
|
|
|
|
} else {
|
|
|
|
printf("0x%.2x ", c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
static int MyWriter(const uint8_t* data, size_t data_size,
|
|
|
|
const WebPPicture* const pic) {
|
|
|
|
FILE* const out = (FILE*)pic->custom_ptr;
|
|
|
|
return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dumps a picture as a PGM file using the IMC4 layout.
|
|
|
|
static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
|
|
|
|
int y;
|
|
|
|
const int uv_width = (picture->width + 1) / 2;
|
|
|
|
const int uv_height = (picture->height + 1) / 2;
|
|
|
|
const int stride = (picture->width + 1) & ~1;
|
2012-06-05 00:50:05 +02:00
|
|
|
const int alpha_height =
|
|
|
|
WebPPictureHasTransparency(picture) ? picture->height : 0;
|
2011-07-08 01:08:15 +02:00
|
|
|
const int height = picture->height + uv_height + alpha_height;
|
2011-02-19 08:33:46 +01:00
|
|
|
FILE* const f = fopen(PGM_name, "wb");
|
2012-01-20 16:20:56 +01:00
|
|
|
if (f == NULL) return 0;
|
2011-02-19 08:33:46 +01:00
|
|
|
fprintf(f, "P5\n%d %d\n255\n", stride, height);
|
|
|
|
for (y = 0; y < picture->height; ++y) {
|
|
|
|
if (fwrite(picture->y + y * picture->y_stride, picture->width, 1, f) != 1)
|
|
|
|
return 0;
|
|
|
|
if (picture->width & 1) fputc(0, f); // pad
|
|
|
|
}
|
|
|
|
for (y = 0; y < uv_height; ++y) {
|
|
|
|
if (fwrite(picture->u + y * picture->uv_stride, uv_width, 1, f) != 1)
|
|
|
|
return 0;
|
|
|
|
if (fwrite(picture->v + y * picture->uv_stride, uv_width, 1, f) != 1)
|
|
|
|
return 0;
|
|
|
|
}
|
2011-07-08 01:08:15 +02:00
|
|
|
for (y = 0; y < alpha_height; ++y) {
|
|
|
|
if (fwrite(picture->a + y * picture->a_stride, picture->width, 1, f) != 1)
|
|
|
|
return 0;
|
|
|
|
if (picture->width & 1) fputc(0, f); // pad
|
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
fclose(f);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-02-19 08:33:46 +01:00
|
|
|
|
2011-12-01 11:24:50 +01:00
|
|
|
static int ProgressReport(int percent, const WebPPicture* const picture) {
|
|
|
|
printf("[%s]: %3d %% \r",
|
2012-07-28 04:53:16 +02:00
|
|
|
(char*)picture->user_data, percent);
|
2011-12-01 11:24:50 +01:00
|
|
|
fflush(stdout);
|
|
|
|
return 1; // all ok
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
2011-03-25 23:04:11 +01:00
|
|
|
static void HelpShort(void) {
|
2011-02-19 08:33:46 +01:00
|
|
|
printf("Usage:\n\n");
|
|
|
|
printf(" cwebp [options] -q quality input.png -o output.webp\n\n");
|
|
|
|
printf("where quality is between 0 (poor) to 100 (very good).\n");
|
|
|
|
printf("Typical value is around 80.\n\n");
|
|
|
|
printf("Try -longhelp for an exhaustive list of advanced options.\n");
|
|
|
|
}
|
|
|
|
|
2011-03-25 23:04:11 +01:00
|
|
|
static void HelpLong(void) {
|
2011-02-19 08:33:46 +01:00
|
|
|
printf("Usage:\n");
|
|
|
|
printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n");
|
|
|
|
printf("If input size (-s) for an image is not specified, "
|
2012-07-13 23:55:39 +02:00
|
|
|
"it is assumed to be a PNG, JPEG or TIFF file.\n");
|
2011-06-07 02:56:50 +02:00
|
|
|
#ifdef HAVE_WINCODEC_H
|
2011-02-19 08:33:46 +01:00
|
|
|
printf("Windows builds can take as input any of the files handled by WIC\n");
|
|
|
|
#endif
|
|
|
|
printf("options:\n");
|
|
|
|
printf(" -h / -help ............ short help\n");
|
|
|
|
printf(" -H / -longhelp ........ long help\n");
|
|
|
|
printf(" -q <float> ............. quality factor (0:small..100:big)\n");
|
2011-12-01 10:41:34 +01:00
|
|
|
printf(" -alpha_q <int> ......... Transparency-compression quality "
|
|
|
|
"(0..100).\n");
|
2011-02-19 08:33:46 +01:00
|
|
|
printf(" -preset <string> ....... Preset setting, one of:\n");
|
|
|
|
printf(" default, photo, picture,\n");
|
|
|
|
printf(" drawing, icon, text\n");
|
|
|
|
printf(" -preset must come first, as it overwrites other parameters.");
|
|
|
|
printf("\n");
|
|
|
|
printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n");
|
|
|
|
printf(" -segments <int> ........ number of segments to use (1..4)\n");
|
2011-05-05 07:59:51 +02:00
|
|
|
printf(" -size <int> ............ Target size (in bytes)\n");
|
|
|
|
printf(" -psnr <float> .......... Target PSNR (in dB. typically: 42)\n");
|
2011-02-19 08:33:46 +01:00
|
|
|
printf("\n");
|
|
|
|
printf(" -s <int> <int> ......... Input size (width x height) for YUV\n");
|
|
|
|
printf(" -sns <int> ............. Spatial Noise Shaping (0:off, 100:max)\n");
|
|
|
|
printf(" -f <int> ............... filter strength (0=off..100)\n");
|
|
|
|
printf(" -sharpness <int> ....... "
|
|
|
|
"filter sharpness (0:most .. 7:least sharp)\n");
|
|
|
|
printf(" -strong ................ use strong filter instead of simple.\n");
|
2011-08-24 00:58:22 +02:00
|
|
|
printf(" -partition_limit <int> . limit quality to fit the 512k limit on\n");
|
|
|
|
printf(" "
|
|
|
|
"the first partition (0=no degradation ... 100=full)\n");
|
2011-02-19 08:33:46 +01:00
|
|
|
printf(" -pass <int> ............ analysis pass number (1..10)\n");
|
|
|
|
printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
|
2011-05-03 02:19:00 +02:00
|
|
|
printf(" -resize <w> <h> ........ resize picture (after any cropping)\n");
|
|
|
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
|
|
printf(" -444 / -422 / -gray ..... Change colorspace\n");
|
|
|
|
#endif
|
2011-02-19 08:33:46 +01:00
|
|
|
printf(" -map <int> ............. print map of extra info.\n");
|
2012-01-20 16:20:56 +01:00
|
|
|
printf(" -print_psnr ............ prints averaged PSNR distortion.\n");
|
2012-10-18 17:26:40 +02:00
|
|
|
printf(" -print_ssim ............ prints averaged SSIM distortion.\n");
|
|
|
|
printf(" -print_lsim ............ prints local-similarity distortion.\n");
|
2011-02-19 08:33:46 +01:00
|
|
|
printf(" -d <file.pgm> .......... dump the compressed output (PGM file).\n");
|
2011-12-01 10:41:34 +01:00
|
|
|
printf(" -alpha_method <int> .... Transparency-compression method (0..1)\n");
|
2012-01-09 04:27:21 +01:00
|
|
|
printf(" -alpha_filter <string> . predictive filtering for alpha plane.\n");
|
|
|
|
printf(" One of: none, fast (default) or best.\n");
|
2012-01-17 09:18:22 +01:00
|
|
|
printf(" -alpha_cleanup ......... Clean RGB values in transparent area.\n");
|
2011-12-01 10:41:34 +01:00
|
|
|
printf(" -noalpha ............... discard any transparency information.\n");
|
2012-06-22 08:44:48 +02:00
|
|
|
printf(" -lossless .............. Encode image losslessly.\n");
|
|
|
|
printf(" -hint <string> ......... Specify image characteristics hint.\n");
|
2012-08-01 08:07:52 +02:00
|
|
|
printf(" One of: photo, picture or graph\n");
|
2011-05-03 02:19:00 +02:00
|
|
|
|
2011-02-19 08:33:46 +01:00
|
|
|
printf("\n");
|
|
|
|
printf(" -short ................. condense printed message\n");
|
|
|
|
printf(" -quiet ................. don't print anything.\n");
|
2011-03-25 00:17:10 +01:00
|
|
|
printf(" -version ............... print version number and exit.\n");
|
2011-07-15 23:53:03 +02:00
|
|
|
#ifndef WEBP_DLL
|
2011-04-22 21:14:45 +02:00
|
|
|
printf(" -noasm ................. disable all assembly optimizations.\n");
|
2011-07-15 23:53:03 +02:00
|
|
|
#endif
|
2011-02-19 08:33:46 +01:00
|
|
|
printf(" -v ..................... verbose, e.g. print encoding/decoding "
|
|
|
|
"times\n");
|
2011-12-01 11:24:50 +01:00
|
|
|
printf(" -progress .............. report encoding progress\n");
|
2011-02-19 08:33:46 +01:00
|
|
|
printf("\n");
|
|
|
|
printf("Experimental Options:\n");
|
|
|
|
printf(" -af .................... auto-adjust filter strength.\n");
|
|
|
|
printf(" -pre <int> ............. pre-processing filter\n");
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-06-02 15:55:03 +02:00
|
|
|
// Error messages
|
|
|
|
|
|
|
|
static const char* const kErrorMessages[] = {
|
|
|
|
"OK",
|
|
|
|
"OUT_OF_MEMORY: Out of memory allocating objects",
|
|
|
|
"BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
|
|
|
|
"NULL_PARAMETER: NULL parameter passed to function",
|
|
|
|
"INVALID_CONFIGURATION: configuration is invalid",
|
2011-08-24 00:58:22 +02:00
|
|
|
"BAD_DIMENSION: Bad picture dimension. Maximum width and height "
|
|
|
|
"allowed is 16383 pixels.",
|
|
|
|
"PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
|
|
|
|
"To reduce the size of this partition, try using less segments "
|
|
|
|
"with the -segments option, and eventually reduce the number of "
|
|
|
|
"header bits using -partition_limit. More details are available "
|
|
|
|
"in the manual (`man cwebp`)",
|
|
|
|
"PARTITION_OVERFLOW: Partition is too big to fit 16M",
|
2011-12-01 12:34:22 +01:00
|
|
|
"BAD_WRITE: Picture writer returned an I/O error",
|
2011-12-01 11:24:50 +01:00
|
|
|
"FILE_TOO_BIG: File would be too big to fit in 4G",
|
|
|
|
"USER_ABORT: encoding abort requested by user"
|
2011-06-02 15:55:03 +02:00
|
|
|
};
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
int main(int argc, const char *argv[]) {
|
2012-05-09 09:32:20 +02:00
|
|
|
int return_value = -1;
|
2011-02-19 08:33:46 +01:00
|
|
|
const char *in_file = NULL, *out_file = NULL, *dump_file = NULL;
|
|
|
|
FILE *out = NULL;
|
|
|
|
int c;
|
|
|
|
int short_output = 0;
|
|
|
|
int quiet = 0;
|
2011-12-01 10:41:34 +01:00
|
|
|
int keep_alpha = 1;
|
2011-02-19 08:33:46 +01:00
|
|
|
int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
|
2011-05-03 02:19:00 +02:00
|
|
|
int resize_w = 0, resize_h = 0;
|
2011-12-01 11:24:50 +01:00
|
|
|
int show_progress = 0;
|
2011-02-19 08:33:46 +01:00
|
|
|
WebPPicture picture;
|
2012-10-18 17:26:40 +02:00
|
|
|
int print_distortion = -1; // -1=off, 0=PSNR, 1=SSIM, 2=LSIM
|
2012-01-20 16:20:56 +01:00
|
|
|
WebPPicture original_picture; // when PSNR or SSIM is requested
|
2011-02-19 08:33:46 +01:00
|
|
|
WebPConfig config;
|
|
|
|
WebPAuxStats stats;
|
2012-12-04 03:20:00 +01:00
|
|
|
Metadata metadata;
|
2011-02-19 08:33:46 +01:00
|
|
|
Stopwatch stop_watch;
|
2011-05-03 02:19:00 +02:00
|
|
|
|
2012-12-04 03:20:00 +01:00
|
|
|
MetadataInit(&metadata);
|
2012-01-20 16:20:56 +01:00
|
|
|
if (!WebPPictureInit(&picture) ||
|
|
|
|
!WebPPictureInit(&original_picture) ||
|
|
|
|
!WebPConfigInit(&config)) {
|
2011-02-19 08:33:46 +01:00
|
|
|
fprintf(stderr, "Error! Version mismatch!\n");
|
2012-07-28 03:56:55 +02:00
|
|
|
return -1;
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (argc == 1) {
|
|
|
|
HelpShort();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (c = 1; c < argc; ++c) {
|
|
|
|
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
|
|
|
HelpShort();
|
|
|
|
return 0;
|
|
|
|
} else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
|
|
|
|
HelpLong();
|
|
|
|
return 0;
|
|
|
|
} else if (!strcmp(argv[c], "-o") && c < argc - 1) {
|
|
|
|
out_file = argv[++c];
|
|
|
|
} else if (!strcmp(argv[c], "-d") && c < argc - 1) {
|
|
|
|
dump_file = argv[++c];
|
|
|
|
config.show_compressed = 1;
|
2012-01-20 16:20:56 +01:00
|
|
|
} else if (!strcmp(argv[c], "-print_psnr")) {
|
2012-10-18 17:26:40 +02:00
|
|
|
config.show_compressed = 1;
|
|
|
|
print_distortion = 0;
|
|
|
|
} else if (!strcmp(argv[c], "-print_ssim")) {
|
2012-01-20 16:20:56 +01:00
|
|
|
config.show_compressed = 1;
|
|
|
|
print_distortion = 1;
|
2012-10-18 17:26:40 +02:00
|
|
|
} else if (!strcmp(argv[c], "-print_lsim")) {
|
|
|
|
config.show_compressed = 1;
|
|
|
|
print_distortion = 2;
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-short")) {
|
|
|
|
short_output++;
|
|
|
|
} else if (!strcmp(argv[c], "-s") && c < argc - 2) {
|
2011-03-26 17:27:45 +01:00
|
|
|
picture.width = strtol(argv[++c], NULL, 0);
|
|
|
|
picture.height = strtol(argv[++c], NULL, 0);
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-m") && c < argc - 1) {
|
2011-03-26 17:27:45 +01:00
|
|
|
config.method = strtol(argv[++c], NULL, 0);
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-q") && c < argc - 1) {
|
2011-06-28 16:02:56 +02:00
|
|
|
config.quality = (float)strtod(argv[++c], NULL);
|
2011-12-01 10:41:34 +01:00
|
|
|
} else if (!strcmp(argv[c], "-alpha_q") && c < argc - 1) {
|
|
|
|
config.alpha_quality = strtol(argv[++c], NULL, 0);
|
|
|
|
} else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
|
|
|
|
config.alpha_compression = strtol(argv[++c], NULL, 0);
|
2012-01-17 09:18:22 +01:00
|
|
|
} else if (!strcmp(argv[c], "-alpha_cleanup")) {
|
|
|
|
keep_alpha = keep_alpha ? 2 : 0;
|
2012-01-05 08:34:30 +01:00
|
|
|
} else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
|
2012-01-09 04:27:21 +01:00
|
|
|
++c;
|
|
|
|
if (!strcmp(argv[c], "none")) {
|
|
|
|
config.alpha_filtering = 0;
|
|
|
|
} else if (!strcmp(argv[c], "fast")) {
|
|
|
|
config.alpha_filtering = 1;
|
|
|
|
} else if (!strcmp(argv[c], "best")) {
|
|
|
|
config.alpha_filtering = 2;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Error! Unrecognized alpha filter: %s\n", argv[c]);
|
|
|
|
goto Error;
|
|
|
|
}
|
2011-12-01 10:41:34 +01:00
|
|
|
} else if (!strcmp(argv[c], "-noalpha")) {
|
|
|
|
keep_alpha = 0;
|
2012-04-24 12:59:08 +02:00
|
|
|
} else if (!strcmp(argv[c], "-lossless")) {
|
|
|
|
config.lossless = 1;
|
2012-07-18 23:58:53 +02:00
|
|
|
picture.use_argb = 1;
|
2012-06-22 08:44:48 +02:00
|
|
|
} else if (!strcmp(argv[c], "-hint") && c < argc - 1) {
|
|
|
|
++c;
|
|
|
|
if (!strcmp(argv[c], "photo")) {
|
|
|
|
config.image_hint = WEBP_HINT_PHOTO;
|
|
|
|
} else if (!strcmp(argv[c], "picture")) {
|
|
|
|
config.image_hint = WEBP_HINT_PICTURE;
|
2012-08-01 08:07:52 +02:00
|
|
|
} else if (!strcmp(argv[c], "graph")) {
|
|
|
|
config.image_hint = WEBP_HINT_GRAPH;
|
2012-06-22 08:44:48 +02:00
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Error! Unrecognized image hint: %s\n", argv[c]);
|
|
|
|
goto Error;
|
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-size") && c < argc - 1) {
|
2011-03-26 17:27:45 +01:00
|
|
|
config.target_size = strtol(argv[++c], NULL, 0);
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-psnr") && c < argc - 1) {
|
2011-06-28 16:02:56 +02:00
|
|
|
config.target_PSNR = (float)strtod(argv[++c], NULL);
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-sns") && c < argc - 1) {
|
2011-03-26 17:27:45 +01:00
|
|
|
config.sns_strength = strtol(argv[++c], NULL, 0);
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-f") && c < argc - 1) {
|
2011-03-26 17:27:45 +01:00
|
|
|
config.filter_strength = strtol(argv[++c], NULL, 0);
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-af")) {
|
|
|
|
config.autofilter = 1;
|
2011-05-06 03:10:08 +02:00
|
|
|
} else if (!strcmp(argv[c], "-strong")) {
|
2011-02-19 08:33:46 +01:00
|
|
|
config.filter_type = 1;
|
|
|
|
} else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) {
|
2011-03-26 17:27:45 +01:00
|
|
|
config.filter_sharpness = strtol(argv[++c], NULL, 0);
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-pass") && c < argc - 1) {
|
2011-03-26 17:27:45 +01:00
|
|
|
config.pass = strtol(argv[++c], NULL, 0);
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-pre") && c < argc - 1) {
|
2011-03-26 17:27:45 +01:00
|
|
|
config.preprocessing = strtol(argv[++c], NULL, 0);
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-segments") && c < argc - 1) {
|
2011-03-26 17:27:45 +01:00
|
|
|
config.segments = strtol(argv[++c], NULL, 0);
|
2011-08-24 00:58:22 +02:00
|
|
|
} else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) {
|
|
|
|
config.partition_limit = strtol(argv[++c], NULL, 0);
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-map") && c < argc - 1) {
|
2011-03-26 17:27:45 +01:00
|
|
|
picture.extra_info_type = strtol(argv[++c], NULL, 0);
|
2011-05-03 02:19:00 +02:00
|
|
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
|
|
} else if (!strcmp(argv[c], "-444")) {
|
|
|
|
picture.colorspace = WEBP_YUV444;
|
|
|
|
} else if (!strcmp(argv[c], "-422")) {
|
|
|
|
picture.colorspace = WEBP_YUV422;
|
|
|
|
} else if (!strcmp(argv[c], "-gray")) {
|
|
|
|
picture.colorspace = WEBP_YUV400;
|
|
|
|
#endif
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
|
|
|
|
crop = 1;
|
2011-03-26 17:27:45 +01:00
|
|
|
crop_x = strtol(argv[++c], NULL, 0);
|
|
|
|
crop_y = strtol(argv[++c], NULL, 0);
|
|
|
|
crop_w = strtol(argv[++c], NULL, 0);
|
|
|
|
crop_h = strtol(argv[++c], NULL, 0);
|
2011-05-03 02:19:00 +02:00
|
|
|
} else if (!strcmp(argv[c], "-resize") && c < argc - 2) {
|
|
|
|
resize_w = strtol(argv[++c], NULL, 0);
|
|
|
|
resize_h = strtol(argv[++c], NULL, 0);
|
2011-07-15 23:53:03 +02:00
|
|
|
#ifndef WEBP_DLL
|
2011-04-22 21:14:45 +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
|
2011-03-25 00:17:10 +01:00
|
|
|
} else if (!strcmp(argv[c], "-version")) {
|
|
|
|
const int version = WebPGetEncoderVersion();
|
|
|
|
printf("%d.%d.%d\n",
|
|
|
|
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
|
|
|
return 0;
|
2011-12-01 11:24:50 +01:00
|
|
|
} else if (!strcmp(argv[c], "-progress")) {
|
|
|
|
show_progress = 1;
|
2011-02-19 08:33:46 +01:00
|
|
|
} else if (!strcmp(argv[c], "-quiet")) {
|
|
|
|
quiet = 1;
|
|
|
|
} else if (!strcmp(argv[c], "-preset") && c < argc - 1) {
|
|
|
|
WebPPreset preset;
|
|
|
|
++c;
|
|
|
|
if (!strcmp(argv[c], "default")) {
|
|
|
|
preset = WEBP_PRESET_DEFAULT;
|
|
|
|
} else if (!strcmp(argv[c], "photo")) {
|
|
|
|
preset = WEBP_PRESET_PHOTO;
|
|
|
|
} else if (!strcmp(argv[c], "picture")) {
|
|
|
|
preset = WEBP_PRESET_PICTURE;
|
|
|
|
} else if (!strcmp(argv[c], "drawing")) {
|
|
|
|
preset = WEBP_PRESET_DRAWING;
|
|
|
|
} else if (!strcmp(argv[c], "icon")) {
|
|
|
|
preset = WEBP_PRESET_ICON;
|
|
|
|
} else if (!strcmp(argv[c], "text")) {
|
|
|
|
preset = WEBP_PRESET_TEXT;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Error! Unrecognized preset: %s\n", argv[c]);
|
|
|
|
goto Error;
|
|
|
|
}
|
|
|
|
if (!WebPConfigPreset(&config, preset, config.quality)) {
|
2011-03-17 22:49:19 +01:00
|
|
|
fprintf(stderr, "Error! Could initialize configuration with preset.\n");
|
2011-02-19 08:33:46 +01:00
|
|
|
goto Error;
|
|
|
|
}
|
|
|
|
} else if (!strcmp(argv[c], "-v")) {
|
|
|
|
verbose = 1;
|
|
|
|
} else if (argv[c][0] == '-') {
|
|
|
|
fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
|
|
|
|
HelpLong();
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
in_file = argv[c];
|
|
|
|
}
|
|
|
|
}
|
2012-05-09 09:32:20 +02:00
|
|
|
if (in_file == NULL) {
|
|
|
|
fprintf(stderr, "No input file specified!\n");
|
|
|
|
HelpShort();
|
|
|
|
goto Error;
|
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
|
2012-06-20 05:32:25 +02:00
|
|
|
// Check for unsupported command line options for lossless mode and log
|
|
|
|
// warning for such options.
|
2012-06-20 11:20:34 +02:00
|
|
|
if (!quiet && config.lossless == 1) {
|
2012-06-20 05:32:25 +02:00
|
|
|
if (config.target_size > 0 || config.target_PSNR > 0) {
|
|
|
|
fprintf(stderr, "Encoding for specified size or PSNR is not supported"
|
|
|
|
" for lossless encoding. Ignoring such option(s)!\n");
|
|
|
|
}
|
|
|
|
if (config.partition_limit > 0) {
|
|
|
|
fprintf(stderr, "Partition limit option is not required for lossless"
|
|
|
|
" encoding. Ignoring this option!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-19 08:33:46 +01:00
|
|
|
if (!WebPValidateConfig(&config)) {
|
|
|
|
fprintf(stderr, "Error! Invalid configuration.\n");
|
|
|
|
goto Error;
|
|
|
|
}
|
|
|
|
|
2011-02-25 21:03:27 +01:00
|
|
|
// Read the input
|
2011-05-05 07:59:51 +02:00
|
|
|
if (verbose) {
|
2011-02-25 21:03:27 +01:00
|
|
|
StopwatchReadAndReset(&stop_watch);
|
2011-05-05 07:59:51 +02:00
|
|
|
}
|
2012-12-04 03:20:00 +01:00
|
|
|
if (!ReadPicture(in_file, &picture, keep_alpha, &metadata)) {
|
2012-01-20 16:20:56 +01:00
|
|
|
fprintf(stderr, "Error! Cannot read input picture file '%s'\n", in_file);
|
2011-03-17 22:49:19 +01:00
|
|
|
goto Error;
|
|
|
|
}
|
2011-12-01 11:24:50 +01:00
|
|
|
picture.progress_hook = (show_progress && !quiet) ? ProgressReport : NULL;
|
|
|
|
|
2011-02-25 21:03:27 +01:00
|
|
|
if (verbose) {
|
|
|
|
const double time = StopwatchReadAndReset(&stop_watch);
|
|
|
|
fprintf(stderr, "Time to read input: %.3fs\n", time);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open the output
|
2011-02-19 08:33:46 +01:00
|
|
|
if (out_file) {
|
|
|
|
out = fopen(out_file, "wb");
|
2012-01-20 16:20:56 +01:00
|
|
|
if (out == NULL) {
|
2011-02-19 08:33:46 +01:00
|
|
|
fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
|
|
|
|
goto Error;
|
|
|
|
} else {
|
|
|
|
if (!short_output && !quiet) {
|
|
|
|
fprintf(stderr, "Saving file '%s'\n", out_file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
picture.writer = MyWriter;
|
|
|
|
picture.custom_ptr = (void*)out;
|
|
|
|
} else {
|
|
|
|
out = NULL;
|
|
|
|
if (!quiet && !short_output) {
|
|
|
|
fprintf(stderr, "No output file specified (no -o flag). Encoding will\n");
|
|
|
|
fprintf(stderr, "be performed, but its results discarded.\n\n");
|
|
|
|
}
|
|
|
|
}
|
2012-07-25 01:15:36 +02:00
|
|
|
if (!quiet) {
|
|
|
|
picture.stats = &stats;
|
2012-07-28 04:53:16 +02:00
|
|
|
picture.user_data = (void*)in_file;
|
2012-07-25 01:15:36 +02:00
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
|
2011-02-25 21:03:27 +01:00
|
|
|
// Compress
|
2011-05-05 07:59:51 +02:00
|
|
|
if (verbose) {
|
2011-02-19 08:33:46 +01:00
|
|
|
StopwatchReadAndReset(&stop_watch);
|
2011-05-05 07:59:51 +02:00
|
|
|
}
|
2012-06-21 09:30:43 +02:00
|
|
|
if (crop != 0) {
|
|
|
|
// We use self-cropping using a view.
|
|
|
|
if (!WebPPictureView(&picture, crop_x, crop_y, crop_w, crop_h, &picture)) {
|
|
|
|
fprintf(stderr, "Error! Cannot crop picture\n");
|
|
|
|
goto Error;
|
|
|
|
}
|
2011-03-17 22:49:19 +01:00
|
|
|
}
|
2011-05-03 02:19:00 +02:00
|
|
|
if ((resize_w | resize_h) > 0) {
|
|
|
|
if (!WebPPictureRescale(&picture, resize_w, resize_h)) {
|
|
|
|
fprintf(stderr, "Error! Cannot resize picture\n");
|
|
|
|
goto Error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (picture.extra_info_type > 0) {
|
|
|
|
AllocExtraInfo(&picture);
|
|
|
|
}
|
2012-10-18 17:26:40 +02:00
|
|
|
if (print_distortion >= 0) { // Save original picture for later comparison
|
2012-01-20 16:20:56 +01:00
|
|
|
WebPPictureCopy(&picture, &original_picture);
|
|
|
|
}
|
2011-03-17 22:49:19 +01:00
|
|
|
if (!WebPEncode(&config, &picture)) {
|
|
|
|
fprintf(stderr, "Error! Cannot encode picture as WebP\n");
|
2011-06-02 15:55:03 +02:00
|
|
|
fprintf(stderr, "Error code: %d (%s)\n",
|
|
|
|
picture.error_code, kErrorMessages[picture.error_code]);
|
2011-03-17 22:49:19 +01:00
|
|
|
goto Error;
|
|
|
|
}
|
2011-02-19 08:33:46 +01:00
|
|
|
if (verbose) {
|
|
|
|
const double time = StopwatchReadAndReset(&stop_watch);
|
|
|
|
fprintf(stderr, "Time to encode picture: %.3fs\n", time);
|
|
|
|
}
|
2011-02-25 21:03:27 +01:00
|
|
|
|
|
|
|
// Write info
|
2011-05-05 07:59:51 +02:00
|
|
|
if (dump_file) {
|
2012-07-18 23:58:53 +02:00
|
|
|
if (picture.use_argb) {
|
2012-06-05 00:50:05 +02:00
|
|
|
fprintf(stderr, "Warning: can't dump file (-d option) in lossless mode.");
|
|
|
|
} else if (!DumpPicture(&picture, dump_file)) {
|
|
|
|
fprintf(stderr, "Warning, couldn't dump picture %s\n", dump_file);
|
|
|
|
}
|
2011-05-05 07:59:51 +02:00
|
|
|
}
|
2012-01-20 16:20:56 +01:00
|
|
|
|
2011-05-05 07:59:51 +02:00
|
|
|
if (!quiet) {
|
2012-05-09 07:57:46 +02:00
|
|
|
if (config.lossless) {
|
|
|
|
PrintExtraInfoLossless(&picture, short_output, in_file);
|
|
|
|
} else {
|
|
|
|
PrintExtraInfoLossy(&picture, short_output, in_file);
|
|
|
|
}
|
2012-01-20 16:20:56 +01:00
|
|
|
}
|
2012-10-18 17:26:40 +02:00
|
|
|
if (!quiet && !short_output && print_distortion >= 0) { // print distortion
|
|
|
|
static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
|
2012-01-20 16:20:56 +01:00
|
|
|
float values[5];
|
|
|
|
WebPPictureDistortion(&picture, &original_picture,
|
2012-10-18 17:26:40 +02:00
|
|
|
print_distortion, values);
|
2012-01-20 16:20:56 +01:00
|
|
|
fprintf(stderr, "%s: Y:%.2f U:%.2f V:%.2f A:%.2f Total:%.2f\n",
|
2012-10-18 17:26:40 +02:00
|
|
|
distortion_names[print_distortion],
|
2012-01-20 16:20:56 +01:00
|
|
|
values[0], values[1], values[2], values[3], values[4]);
|
2011-05-05 07:59:51 +02:00
|
|
|
}
|
2012-05-09 09:32:20 +02:00
|
|
|
return_value = 0;
|
2011-02-19 08:33:46 +01:00
|
|
|
|
|
|
|
Error:
|
|
|
|
free(picture.extra_info);
|
2012-12-04 03:20:00 +01:00
|
|
|
MetadataFree(&metadata);
|
2011-02-19 08:33:46 +01:00
|
|
|
WebPPictureFree(&picture);
|
2012-01-20 16:20:56 +01:00
|
|
|
WebPPictureFree(&original_picture);
|
2011-02-19 08:33:46 +01:00
|
|
|
if (out != NULL) {
|
|
|
|
fclose(out);
|
|
|
|
}
|
|
|
|
|
2012-05-09 09:32:20 +02:00
|
|
|
return return_value;
|
2011-02-19 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|