forked from Green-Sky/tomato
Squashed 'external/libwebp/libwebp/' content from commit dd7364c3c
git-subtree-dir: external/libwebp/libwebp git-subtree-split: dd7364c3cefe0f5c0b3c18c3b1887d353f90fc1f
This commit is contained in:
57
imageio/Android.mk
Normal file
57
imageio/Android.mk
Normal file
@ -0,0 +1,57 @@
|
||||
# Ignore this file during non-NDK builds.
|
||||
ifdef NDK_ROOT
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
################################################################################
|
||||
# libimageio_util
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
imageio_util.c \
|
||||
|
||||
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
||||
|
||||
LOCAL_MODULE := imageio_util
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
################################################################################
|
||||
# libimagedec
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
image_dec.c \
|
||||
jpegdec.c \
|
||||
metadata.c \
|
||||
pngdec.c \
|
||||
pnmdec.c \
|
||||
tiffdec.c \
|
||||
webpdec.c \
|
||||
|
||||
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
||||
LOCAL_STATIC_LIBRARIES := imageio_util
|
||||
|
||||
LOCAL_MODULE := imagedec
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
################################################################################
|
||||
# libimageenc
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
image_enc.c \
|
||||
|
||||
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
||||
LOCAL_STATIC_LIBRARIES := imageio_util
|
||||
|
||||
LOCAL_MODULE := imageenc
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
endif # NDK_ROOT
|
32
imageio/Makefile.am
Normal file
32
imageio/Makefile.am
Normal file
@ -0,0 +1,32 @@
|
||||
AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
|
||||
noinst_LTLIBRARIES =
|
||||
noinst_LTLIBRARIES += libimageio_util.la
|
||||
if BUILD_DEMUX
|
||||
noinst_LTLIBRARIES += libimagedec.la
|
||||
endif
|
||||
noinst_LTLIBRARIES += libimageenc.la
|
||||
|
||||
noinst_HEADERS =
|
||||
noinst_HEADERS += ../src/webp/decode.h
|
||||
noinst_HEADERS += ../src/webp/types.h
|
||||
|
||||
libimageio_util_la_SOURCES =
|
||||
libimageio_util_la_SOURCES += imageio_util.c imageio_util.h
|
||||
|
||||
libimagedec_la_SOURCES =
|
||||
libimagedec_la_SOURCES += image_dec.c image_dec.h
|
||||
libimagedec_la_SOURCES += jpegdec.c jpegdec.h
|
||||
libimagedec_la_SOURCES += metadata.c metadata.h
|
||||
libimagedec_la_SOURCES += pngdec.c pngdec.h
|
||||
libimagedec_la_SOURCES += pnmdec.c pnmdec.h
|
||||
libimagedec_la_SOURCES += tiffdec.c tiffdec.h
|
||||
libimagedec_la_SOURCES += webpdec.c webpdec.h
|
||||
libimagedec_la_SOURCES += wicdec.c wicdec.h
|
||||
libimagedec_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
|
||||
libimagedec_la_CPPFLAGS += $(AM_CPPFLAGS)
|
||||
libimagedec_la_LIBADD = ../src/demux/libwebpdemux.la
|
||||
|
||||
libimageenc_la_SOURCES =
|
||||
libimageenc_la_SOURCES += image_enc.c image_enc.h
|
||||
libimageenc_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
|
||||
libimageenc_la_CPPFLAGS += $(AM_CPPFLAGS)
|
84
imageio/image_dec.c
Normal file
84
imageio/image_dec.c
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Generic image-type guessing.
|
||||
|
||||
#include "./image_dec.h"
|
||||
|
||||
const char* WebPGetEnabledInputFileFormats(void) {
|
||||
return "WebP"
|
||||
#ifdef WEBP_HAVE_JPEG
|
||||
", JPEG"
|
||||
#endif
|
||||
#ifdef WEBP_HAVE_PNG
|
||||
", PNG"
|
||||
#endif
|
||||
", PNM (PGM, PPM, PAM)"
|
||||
#ifdef WEBP_HAVE_TIFF
|
||||
", TIFF"
|
||||
#endif
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
", Windows Imaging Component (WIC)"
|
||||
#endif
|
||||
"";
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t GetBE32(const uint8_t buf[]) {
|
||||
return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
||||
}
|
||||
|
||||
WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
|
||||
size_t data_size) {
|
||||
WebPInputFileFormat format = WEBP_UNSUPPORTED_FORMAT;
|
||||
if (data != NULL && data_size >= 12) {
|
||||
const uint32_t magic1 = GetBE32(data + 0);
|
||||
const uint32_t magic2 = GetBE32(data + 8);
|
||||
if (magic1 == 0x89504E47U) {
|
||||
format = WEBP_PNG_FORMAT;
|
||||
} else if (magic1 >= 0xFFD8FF00U && magic1 <= 0xFFD8FFFFU) {
|
||||
format = WEBP_JPEG_FORMAT;
|
||||
} else if (magic1 == 0x49492A00 || magic1 == 0x4D4D002A) {
|
||||
format = WEBP_TIFF_FORMAT;
|
||||
} else if (magic1 == 0x52494646 && magic2 == 0x57454250) {
|
||||
format = WEBP_WEBP_FORMAT;
|
||||
} else if (((magic1 >> 24) & 0xff) == 'P') {
|
||||
const int type = (magic1 >> 16) & 0xff;
|
||||
// we only support 'P5 -> P7' for now.
|
||||
if (type >= '5' && type <= '7') format = WEBP_PNM_FORMAT;
|
||||
}
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
static int FailReader(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebPImageReader WebPGetImageReader(WebPInputFileFormat format) {
|
||||
switch (format) {
|
||||
case WEBP_PNG_FORMAT: return ReadPNG;
|
||||
case WEBP_JPEG_FORMAT: return ReadJPEG;
|
||||
case WEBP_TIFF_FORMAT: return ReadTIFF;
|
||||
case WEBP_WEBP_FORMAT: return ReadWebP;
|
||||
case WEBP_PNM_FORMAT: return ReadPNM;
|
||||
default: return FailReader;
|
||||
}
|
||||
}
|
||||
|
||||
WebPImageReader WebPGuessImageReader(const uint8_t* const data,
|
||||
size_t data_size) {
|
||||
return WebPGetImageReader(WebPGuessImageType(data, data_size));
|
||||
}
|
70
imageio/image_dec.h
Normal file
70
imageio/image_dec.h
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// All-in-one library to decode PNG/JPEG/WebP/TIFF/WIC input images.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||
#define WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include "./metadata.h"
|
||||
#include "./jpegdec.h"
|
||||
#include "./pngdec.h"
|
||||
#include "./pnmdec.h"
|
||||
#include "./tiffdec.h"
|
||||
#include "./webpdec.h"
|
||||
#include "./wicdec.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
WEBP_PNG_FORMAT = 0,
|
||||
WEBP_JPEG_FORMAT,
|
||||
WEBP_TIFF_FORMAT,
|
||||
WEBP_WEBP_FORMAT,
|
||||
WEBP_PNM_FORMAT,
|
||||
WEBP_UNSUPPORTED_FORMAT
|
||||
} WebPInputFileFormat;
|
||||
|
||||
// Returns a comma separated list of enabled input formats.
|
||||
const char* WebPGetEnabledInputFileFormats(void);
|
||||
|
||||
// Try to infer the image format. 'data_size' should be larger than 12.
|
||||
// Returns WEBP_UNSUPPORTED_FORMAT if format can't be guess safely.
|
||||
WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
|
||||
size_t data_size);
|
||||
|
||||
// Signature for common image-reading functions (ReadPNG, ReadJPEG, ...)
|
||||
typedef int (*WebPImageReader)(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata);
|
||||
|
||||
// Return the reader associated to a given file format.
|
||||
WebPImageReader WebPGetImageReader(WebPInputFileFormat format);
|
||||
|
||||
// This function is similar to WebPGuessImageType(), but returns a
|
||||
// suitable reader function. The returned reader is never NULL, but
|
||||
// unknown formats will return an always-failing valid reader.
|
||||
WebPImageReader WebPGuessImageReader(const uint8_t* const data,
|
||||
size_t data_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_IMAGE_DEC_H_
|
615
imageio/image_enc.c
Normal file
615
imageio/image_enc.c
Normal file
@ -0,0 +1,615 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Save image
|
||||
|
||||
#include "./image_enc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WEBP_HAVE_PNG
|
||||
#include <png.h>
|
||||
#include <setjmp.h> // note: this must be included *after* png.h
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
#ifdef __MINGW32__
|
||||
#define INITGUID // Without this GUIDs are declared extern and fail to link
|
||||
#endif
|
||||
#define CINTERFACE
|
||||
#define COBJMACROS
|
||||
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||
// code with COBJMACROS.
|
||||
#include <ole2.h> // CreateStreamOnHGlobal()
|
||||
#include <shlwapi.h>
|
||||
#include <tchar.h>
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
#endif
|
||||
|
||||
#include "./imageio_util.h"
|
||||
#include "../examples/unicode.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PNG
|
||||
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
if (SUCCEEDED(hr)) { \
|
||||
hr = (fn); \
|
||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define MAKE_REFGUID(x) (x)
|
||||
#else
|
||||
#define MAKE_REFGUID(x) &(x)
|
||||
#endif
|
||||
|
||||
static HRESULT CreateOutputStream(const char* out_file_name,
|
||||
int write_to_mem, IStream** stream) {
|
||||
HRESULT hr = S_OK;
|
||||
if (write_to_mem) {
|
||||
// Output to a memory buffer. This is freed when 'stream' is released.
|
||||
IFS(CreateStreamOnHGlobal(NULL, TRUE, stream));
|
||||
} else {
|
||||
IFS(SHCreateStreamOnFile((const LPTSTR)out_file_name,
|
||||
STGM_WRITE | STGM_CREATE, stream));
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
_ftprintf(stderr, _T("Error opening output file %s (%08lx)\n"),
|
||||
(const LPTSTR)out_file_name, hr);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
|
||||
REFGUID container_guid,
|
||||
uint8_t* rgb, int stride,
|
||||
uint32_t width, uint32_t height, int has_alpha) {
|
||||
HRESULT hr = S_OK;
|
||||
IWICImagingFactory* factory = NULL;
|
||||
IWICBitmapFrameEncode* frame = NULL;
|
||||
IWICBitmapEncoder* encoder = NULL;
|
||||
IStream* stream = NULL;
|
||||
WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
|
||||
: GUID_WICPixelFormat24bppBGR;
|
||||
|
||||
if (out_file_name == NULL || rgb == NULL) return E_INVALIDARG;
|
||||
|
||||
IFS(CoInitialize(NULL));
|
||||
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
MAKE_REFGUID(IID_IWICImagingFactory),
|
||||
(LPVOID*)&factory));
|
||||
if (hr == REGDB_E_CLASSNOTREG) {
|
||||
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");
|
||||
}
|
||||
IFS(CreateOutputStream(out_file_name, use_stdout, &stream));
|
||||
IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL,
|
||||
&encoder));
|
||||
IFS(IWICBitmapEncoder_Initialize(encoder, stream,
|
||||
WICBitmapEncoderNoCache));
|
||||
IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL));
|
||||
IFS(IWICBitmapFrameEncode_Initialize(frame, NULL));
|
||||
IFS(IWICBitmapFrameEncode_SetSize(frame, width, height));
|
||||
IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format));
|
||||
IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride,
|
||||
height * stride, rgb));
|
||||
IFS(IWICBitmapFrameEncode_Commit(frame));
|
||||
IFS(IWICBitmapEncoder_Commit(encoder));
|
||||
|
||||
if (SUCCEEDED(hr) && use_stdout) {
|
||||
HGLOBAL image;
|
||||
IFS(GetHGlobalFromStream(stream, &image));
|
||||
if (SUCCEEDED(hr)) {
|
||||
HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD mode;
|
||||
const BOOL update_mode = GetConsoleMode(std_output, &mode);
|
||||
const void* const image_mem = GlobalLock(image);
|
||||
DWORD bytes_written = 0;
|
||||
|
||||
// Clear output processing if necessary, then output the image.
|
||||
if (update_mode) SetConsoleMode(std_output, 0);
|
||||
if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image),
|
||||
&bytes_written, NULL) ||
|
||||
bytes_written != GlobalSize(image)) {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
if (update_mode) SetConsoleMode(std_output, mode);
|
||||
GlobalUnlock(image);
|
||||
}
|
||||
}
|
||||
|
||||
if (frame != NULL) IUnknown_Release(frame);
|
||||
if (encoder != NULL) IUnknown_Release(encoder);
|
||||
if (factory != NULL) IUnknown_Release(factory);
|
||||
if (stream != NULL) IUnknown_Release(stream);
|
||||
return hr;
|
||||
}
|
||||
|
||||
int WebPWritePNG(const char* out_file_name, int use_stdout,
|
||||
const WebPDecBuffer* const buffer) {
|
||||
const uint32_t width = buffer->width;
|
||||
const uint32_t height = buffer->height;
|
||||
uint8_t* const rgb = buffer->u.RGBA.rgba;
|
||||
const int stride = buffer->u.RGBA.stride;
|
||||
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||
|
||||
return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout,
|
||||
MAKE_REFGUID(GUID_ContainerFormatPng),
|
||||
rgb, stride, width, height, has_alpha));
|
||||
}
|
||||
|
||||
#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
|
||||
static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp unused) {
|
||||
(void)unused; // remove variable-unused warning
|
||||
longjmp(png_jmpbuf(png), 1);
|
||||
}
|
||||
|
||||
int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
||||
volatile png_structp png;
|
||||
volatile png_infop info;
|
||||
|
||||
if (out_file == NULL || buffer == NULL) return 0;
|
||||
|
||||
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, PNGErrorFunction, NULL);
|
||||
if (png == NULL) {
|
||||
return 0;
|
||||
}
|
||||
info = png_create_info_struct(png);
|
||||
if (info == NULL) {
|
||||
png_destroy_write_struct((png_structpp)&png, NULL);
|
||||
return 0;
|
||||
}
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
|
||||
return 0;
|
||||
}
|
||||
png_init_io(png, out_file);
|
||||
{
|
||||
const uint32_t width = buffer->width;
|
||||
const uint32_t height = buffer->height;
|
||||
png_bytep row = buffer->u.RGBA.rgba;
|
||||
const int stride = buffer->u.RGBA.stride;
|
||||
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||
uint32_t y;
|
||||
|
||||
png_set_IHDR(png, info, width, height, 8,
|
||||
has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
png_write_info(png, info);
|
||||
for (y = 0; y < height; ++y) {
|
||||
png_write_rows(png, &row, 1);
|
||||
row += stride;
|
||||
}
|
||||
}
|
||||
png_write_end(png, info);
|
||||
png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
|
||||
return 1;
|
||||
}
|
||||
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
|
||||
int WebPWritePNG(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
if (fout == NULL || buffer == NULL) return 0;
|
||||
|
||||
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");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PPM / PAM
|
||||
|
||||
static int WritePPMPAM(FILE* fout, const WebPDecBuffer* const buffer,
|
||||
int alpha) {
|
||||
if (fout == NULL || buffer == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
const uint32_t width = buffer->width;
|
||||
const uint32_t height = buffer->height;
|
||||
const uint8_t* row = buffer->u.RGBA.rgba;
|
||||
const int stride = buffer->u.RGBA.stride;
|
||||
const size_t bytes_per_px = alpha ? 4 : 3;
|
||||
uint32_t y;
|
||||
|
||||
if (row == NULL) return 0;
|
||||
|
||||
if (alpha) {
|
||||
fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
|
||||
"TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
|
||||
} else {
|
||||
fprintf(fout, "P6\n%u %u\n255\n", width, height);
|
||||
}
|
||||
for (y = 0; y < height; ++y) {
|
||||
if (fwrite(row, width, bytes_per_px, fout) != bytes_per_px) {
|
||||
return 0;
|
||||
}
|
||||
row += stride;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int WebPWritePPM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
return WritePPMPAM(fout, buffer, 0);
|
||||
}
|
||||
|
||||
int WebPWritePAM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
return WritePPMPAM(fout, buffer, 1);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Raw PGM
|
||||
|
||||
// Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose.
|
||||
int WebPWrite16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
const uint32_t width = buffer->width;
|
||||
const uint32_t height = buffer->height;
|
||||
const uint8_t* rgba = buffer->u.RGBA.rgba;
|
||||
const int stride = buffer->u.RGBA.stride;
|
||||
const uint32_t bytes_per_px = 2;
|
||||
uint32_t y;
|
||||
|
||||
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
|
||||
|
||||
fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height);
|
||||
for (y = 0; y < height; ++y) {
|
||||
if (fwrite(rgba, width, bytes_per_px, fout) != bytes_per_px) {
|
||||
return 0;
|
||||
}
|
||||
rgba += stride;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// BMP (see https://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage)
|
||||
|
||||
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
|
||||
#define BMP_HEADER_ALPHA_EXTRA_SIZE 16 // for alpha info
|
||||
int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||
const int header_size =
|
||||
BMP_HEADER_SIZE + (has_alpha ? BMP_HEADER_ALPHA_EXTRA_SIZE : 0);
|
||||
const uint32_t width = buffer->width;
|
||||
const uint32_t height = buffer->height;
|
||||
const uint8_t* rgba = buffer->u.RGBA.rgba;
|
||||
const int stride = buffer->u.RGBA.stride;
|
||||
const uint32_t bytes_per_px = has_alpha ? 4 : 3;
|
||||
uint32_t y;
|
||||
const uint32_t line_size = bytes_per_px * width;
|
||||
const uint32_t bmp_stride = (line_size + 3) & ~3; // pad to 4
|
||||
const uint32_t image_size = bmp_stride * height;
|
||||
const uint32_t total_size = image_size + header_size;
|
||||
uint8_t bmp_header[BMP_HEADER_SIZE + BMP_HEADER_ALPHA_EXTRA_SIZE] = { 0 };
|
||||
|
||||
if (fout == NULL || buffer == NULL || rgba == NULL) return 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, header_size); // offset to pixel array
|
||||
// bitmap info header
|
||||
PutLE32(bmp_header + 14, header_size - 14); // DIB header size
|
||||
PutLE32(bmp_header + 18, width); // dimensions
|
||||
PutLE32(bmp_header + 22, height); // no 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, has_alpha ? 3 : 0); // BI_BITFIELDS or BI_RGB
|
||||
PutLE32(bmp_header + 34, image_size);
|
||||
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
|
||||
if (has_alpha) { // BITMAPV3INFOHEADER complement
|
||||
PutLE32(bmp_header + 54, 0x00ff0000); // red mask
|
||||
PutLE32(bmp_header + 58, 0x0000ff00); // green mask
|
||||
PutLE32(bmp_header + 62, 0x000000ff); // blue mask
|
||||
PutLE32(bmp_header + 66, 0xff000000); // alpha mask
|
||||
}
|
||||
|
||||
// TODO(skal): color profile
|
||||
|
||||
// write header
|
||||
if (fwrite(bmp_header, header_size, 1, fout) != 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// write pixel array, bottom to top
|
||||
for (y = 0; y < height; ++y) {
|
||||
const uint8_t* const src = &rgba[(uint64_t)(height - 1 - y) * stride];
|
||||
if (fwrite(src, 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
|
||||
#undef BMP_HEADER_ALPHA_EXTRA_SIZE
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// TIFF
|
||||
|
||||
#define NUM_IFD_ENTRIES 15
|
||||
#define EXTRA_DATA_SIZE 16
|
||||
// 10b for signature/header + n * 12b entries + 4b for IFD terminator:
|
||||
#define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4)
|
||||
#define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)
|
||||
|
||||
int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||
const uint32_t width = buffer->width;
|
||||
const uint32_t height = buffer->height;
|
||||
const uint8_t* rgba = buffer->u.RGBA.rgba;
|
||||
const int stride = buffer->u.RGBA.stride;
|
||||
const uint8_t bytes_per_px = has_alpha ? 4 : 3;
|
||||
const uint8_t assoc_alpha =
|
||||
WebPIsPremultipliedMode(buffer->colorspace) ? 1 : 2;
|
||||
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
|
||||
const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
|
||||
: NUM_IFD_ENTRIES - 1;
|
||||
uint8_t tiff_header[TIFF_HEADER_SIZE] = {
|
||||
0x49, 0x49, 0x2a, 0x00, // little endian signature
|
||||
8, 0, 0, 0, // offset to the unique IFD that follows
|
||||
// IFD (offset = 8). Entries must be written in increasing tag order.
|
||||
num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
|
||||
0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
|
||||
0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
|
||||
0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
|
||||
EXTRA_DATA_OFFSET + 0, 0, 0, 0,
|
||||
0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none
|
||||
0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
|
||||
0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
|
||||
TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
|
||||
0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
|
||||
0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
|
||||
bytes_per_px, 0, 0, 0,
|
||||
0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD)
|
||||
0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
|
||||
0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
|
||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
||||
0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution
|
||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
||||
0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
|
||||
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
|
||||
0x52, 0x01, 3, 0, 1, 0, 0, 0,
|
||||
assoc_alpha, 0, 0, 0, // 178: ExtraSamples: rgbA/RGBA
|
||||
0, 0, 0, 0, // 190: IFD terminator
|
||||
// EXTRA_DATA_OFFSET:
|
||||
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
|
||||
72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
|
||||
};
|
||||
uint32_t y;
|
||||
|
||||
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
|
||||
|
||||
// Fill placeholders in IFD:
|
||||
PutLE32(tiff_header + 10 + 8, width);
|
||||
PutLE32(tiff_header + 22 + 8, height);
|
||||
PutLE32(tiff_header + 106 + 8, height);
|
||||
PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height);
|
||||
if (!has_alpha) PutLE32(tiff_header + 178, 0); // IFD terminator
|
||||
|
||||
// write header
|
||||
if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) {
|
||||
return 0;
|
||||
}
|
||||
// write pixel values
|
||||
for (y = 0; y < height; ++y) {
|
||||
if (fwrite(rgba, bytes_per_px, width, fout) != width) {
|
||||
return 0;
|
||||
}
|
||||
rgba += stride;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#undef TIFF_HEADER_SIZE
|
||||
#undef EXTRA_DATA_OFFSET
|
||||
#undef EXTRA_DATA_SIZE
|
||||
#undef NUM_IFD_ENTRIES
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Raw Alpha
|
||||
|
||||
int WebPWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
if (fout == NULL || buffer == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
const uint32_t width = buffer->width;
|
||||
const uint32_t height = buffer->height;
|
||||
const uint8_t* a = buffer->u.YUVA.a;
|
||||
const int a_stride = buffer->u.YUVA.a_stride;
|
||||
uint32_t y;
|
||||
|
||||
if (a == NULL) return 0;
|
||||
|
||||
fprintf(fout, "P5\n%u %u\n255\n", width, height);
|
||||
for (y = 0; y < height; ++y) {
|
||||
if (fwrite(a, width, 1, fout) != 1) return 0;
|
||||
a += a_stride;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PGM with IMC4 layout
|
||||
|
||||
int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
if (fout == NULL || buffer == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
const int width = buffer->width;
|
||||
const int height = buffer->height;
|
||||
const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
|
||||
const uint8_t* src_y = yuv->y;
|
||||
const uint8_t* src_u = yuv->u;
|
||||
const uint8_t* src_v = yuv->v;
|
||||
const uint8_t* src_a = yuv->a;
|
||||
const int uv_width = (width + 1) / 2;
|
||||
const int uv_height = (height + 1) / 2;
|
||||
const int a_height = (src_a != NULL) ? height : 0;
|
||||
int ok = 1;
|
||||
int y;
|
||||
|
||||
if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
|
||||
|
||||
fprintf(fout, "P5\n%d %d\n255\n",
|
||||
(width + 1) & ~1, height + uv_height + a_height);
|
||||
for (y = 0; ok && y < height; ++y) {
|
||||
ok &= (fwrite(src_y, width, 1, fout) == 1);
|
||||
if (width & 1) fputc(0, fout); // padding byte
|
||||
src_y += yuv->y_stride;
|
||||
}
|
||||
for (y = 0; ok && y < uv_height; ++y) {
|
||||
ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
|
||||
ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
|
||||
src_u += yuv->u_stride;
|
||||
src_v += yuv->v_stride;
|
||||
}
|
||||
for (y = 0; ok && y < a_height; ++y) {
|
||||
ok &= (fwrite(src_a, width, 1, fout) == 1);
|
||||
if (width & 1) fputc(0, fout); // padding byte
|
||||
src_a += yuv->a_stride;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Raw YUV(A) planes
|
||||
|
||||
int WebPWriteYUV(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
if (fout == NULL || buffer == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
const int width = buffer->width;
|
||||
const int height = buffer->height;
|
||||
const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
|
||||
const uint8_t* src_y = yuv->y;
|
||||
const uint8_t* src_u = yuv->u;
|
||||
const uint8_t* src_v = yuv->v;
|
||||
const uint8_t* src_a = yuv->a;
|
||||
const int uv_width = (width + 1) / 2;
|
||||
const int uv_height = (height + 1) / 2;
|
||||
const int a_height = (src_a != NULL) ? height : 0;
|
||||
int ok = 1;
|
||||
int y;
|
||||
|
||||
if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
|
||||
|
||||
for (y = 0; ok && y < height; ++y) {
|
||||
ok &= (fwrite(src_y, width, 1, fout) == 1);
|
||||
src_y += yuv->y_stride;
|
||||
}
|
||||
for (y = 0; ok && y < uv_height; ++y) {
|
||||
ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
|
||||
src_u += yuv->u_stride;
|
||||
}
|
||||
for (y = 0; ok && y < uv_height; ++y) {
|
||||
ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
|
||||
src_v += yuv->v_stride;
|
||||
}
|
||||
for (y = 0; ok && y < a_height; ++y) {
|
||||
ok &= (fwrite(src_a, width, 1, fout) == 1);
|
||||
src_a += yuv->a_stride;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Generic top-level call
|
||||
|
||||
int WebPSaveImage(const WebPDecBuffer* const buffer,
|
||||
WebPOutputFileFormat format,
|
||||
const char* const out_file_name) {
|
||||
FILE* fout = NULL;
|
||||
int needs_open_file = 1;
|
||||
const int use_stdout =
|
||||
(out_file_name != NULL) && !WSTRCMP(out_file_name, "-");
|
||||
int ok = 1;
|
||||
|
||||
if (buffer == NULL || out_file_name == NULL) return 0;
|
||||
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
needs_open_file = (format != PNG);
|
||||
#endif
|
||||
|
||||
if (needs_open_file) {
|
||||
fout = use_stdout ? ImgIoUtilSetBinaryMode(stdout)
|
||||
: WFOPEN(out_file_name, "wb");
|
||||
if (fout == NULL) {
|
||||
WFPRINTF(stderr, "Error opening output file %s\n",
|
||||
(const W_CHAR*)out_file_name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (format == PNG ||
|
||||
format == RGBA || format == BGRA || format == ARGB ||
|
||||
format == rgbA || format == bgrA || format == Argb) {
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
ok &= WebPWritePNG(out_file_name, use_stdout, buffer);
|
||||
#else
|
||||
ok &= WebPWritePNG(fout, buffer);
|
||||
#endif
|
||||
} else if (format == PAM) {
|
||||
ok &= WebPWritePAM(fout, buffer);
|
||||
} else if (format == PPM || format == RGB || format == BGR) {
|
||||
ok &= WebPWritePPM(fout, buffer);
|
||||
} else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) {
|
||||
ok &= WebPWrite16bAsPGM(fout, buffer);
|
||||
} else if (format == BMP) {
|
||||
ok &= WebPWriteBMP(fout, buffer);
|
||||
} else if (format == TIFF) {
|
||||
ok &= WebPWriteTIFF(fout, buffer);
|
||||
} else if (format == RAW_YUV) {
|
||||
ok &= WebPWriteYUV(fout, buffer);
|
||||
} else if (format == PGM || format == YUV || format == YUVA) {
|
||||
ok &= WebPWritePGM(fout, buffer);
|
||||
} else if (format == ALPHA_PLANE_ONLY) {
|
||||
ok &= WebPWriteAlphaPlane(fout, buffer);
|
||||
}
|
||||
if (fout != NULL && fout != stdout) {
|
||||
fclose(fout);
|
||||
}
|
||||
return ok;
|
||||
}
|
96
imageio/image_enc.h
Normal file
96
imageio/image_enc.h
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// All-in-one library to save PNG/JPEG/WebP/TIFF/WIC images.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_IMAGEIO_IMAGE_ENC_H_
|
||||
#define WEBP_IMAGEIO_IMAGE_ENC_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include "webp/types.h"
|
||||
#include "webp/decode.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Output types
|
||||
typedef enum {
|
||||
PNG = 0,
|
||||
PAM,
|
||||
PPM,
|
||||
PGM,
|
||||
BMP,
|
||||
TIFF,
|
||||
RAW_YUV,
|
||||
ALPHA_PLANE_ONLY, // this is for experimenting only
|
||||
// forced colorspace output (for testing, mostly)
|
||||
RGB, RGBA, BGR, BGRA, ARGB,
|
||||
RGBA_4444, RGB_565,
|
||||
rgbA, bgrA, Argb, rgbA_4444,
|
||||
YUV, YUVA
|
||||
} WebPOutputFileFormat;
|
||||
|
||||
// General all-purpose call.
|
||||
// Most formats expect a 'buffer' containing RGBA-like samples, except
|
||||
// RAW_YUV, YUV and YUVA formats.
|
||||
// If 'out_file_name' is "-", data is saved to stdout.
|
||||
// Returns false if an error occurred, true otherwise.
|
||||
int WebPSaveImage(const WebPDecBuffer* const buffer,
|
||||
WebPOutputFileFormat format, const char* const out_file_name);
|
||||
|
||||
// Save to PNG.
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
int WebPWritePNG(const char* out_file_name, int use_stdout,
|
||||
const struct WebPDecBuffer* const buffer);
|
||||
#else
|
||||
int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer);
|
||||
#endif
|
||||
|
||||
// Save to PPM format (RGB, no alpha)
|
||||
int WebPWritePPM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
// Save to PAM format (= PPM + alpha)
|
||||
int WebPWritePAM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
// Save 16b mode (RGBA4444, RGB565, ...) for debugging purposes.
|
||||
int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
// Save as BMP
|
||||
int WebPWriteBMP(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
// Save as TIFF
|
||||
int WebPWriteTIFF(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
// Save the ALPHA plane (only) as a PGM
|
||||
int WebPWriteAlphaPlane(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
// Save as YUV samples as PGM format (using IMC4 layout).
|
||||
// See: https://www.fourcc.org/yuv.php#IMC4.
|
||||
// (very convenient format for viewing the samples, esp. for odd dimensions).
|
||||
int WebPWritePGM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
// Save YUV(A) planes sequentially (raw dump)
|
||||
int WebPWriteYUV(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
// Save 16b mode (RGBA4444, RGB565, ...) as PGM format, for debugging purposes.
|
||||
int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_IMAGE_ENC_H_
|
162
imageio/imageio_util.c
Normal file
162
imageio/imageio_util.c
Normal file
@ -0,0 +1,162 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Utility functions used by the image decoders.
|
||||
//
|
||||
|
||||
#include "./imageio_util.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <fcntl.h> // for _O_BINARY
|
||||
#include <io.h> // for _setmode()
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../examples/unicode.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// File I/O
|
||||
|
||||
FILE* ImgIoUtilSetBinaryMode(FILE* file) {
|
||||
#if defined(_WIN32)
|
||||
if (_setmode(_fileno(file), _O_BINARY) == -1) {
|
||||
fprintf(stderr, "Failed to reopen file in O_BINARY mode.\n");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
return file;
|
||||
}
|
||||
|
||||
int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
|
||||
static const size_t kBlockSize = 16384; // default initial size
|
||||
size_t max_size = 0;
|
||||
size_t size = 0;
|
||||
uint8_t* input = NULL;
|
||||
|
||||
if (data == NULL || data_size == NULL) return 0;
|
||||
*data = NULL;
|
||||
*data_size = 0;
|
||||
|
||||
if (!ImgIoUtilSetBinaryMode(stdin)) return 0;
|
||||
|
||||
while (!feof(stdin)) {
|
||||
// We double the buffer size each time and read as much as possible.
|
||||
const size_t extra_size = (max_size == 0) ? kBlockSize : max_size;
|
||||
// we allocate one extra byte for the \0 terminator
|
||||
void* const new_data = realloc(input, max_size + extra_size + 1);
|
||||
if (new_data == NULL) goto Error;
|
||||
input = (uint8_t*)new_data;
|
||||
max_size += extra_size;
|
||||
size += fread(input + size, 1, extra_size, stdin);
|
||||
if (size < max_size) break;
|
||||
}
|
||||
if (ferror(stdin)) goto Error;
|
||||
if (input != NULL) input[size] = '\0'; // convenient 0-terminator
|
||||
*data = input;
|
||||
*data_size = size;
|
||||
return 1;
|
||||
|
||||
Error:
|
||||
free(input);
|
||||
fprintf(stderr, "Could not read from stdin\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ImgIoUtilReadFile(const char* const file_name,
|
||||
const uint8_t** data, size_t* data_size) {
|
||||
int ok;
|
||||
uint8_t* file_data;
|
||||
size_t file_size;
|
||||
FILE* in;
|
||||
const int from_stdin = (file_name == NULL) || !WSTRCMP(file_name, "-");
|
||||
|
||||
if (from_stdin) return ImgIoUtilReadFromStdin(data, data_size);
|
||||
|
||||
if (data == NULL || data_size == NULL) return 0;
|
||||
*data = NULL;
|
||||
*data_size = 0;
|
||||
|
||||
in = WFOPEN(file_name, "rb");
|
||||
if (in == NULL) {
|
||||
WFPRINTF(stderr, "cannot open input file '%s'\n", (const W_CHAR*)file_name);
|
||||
return 0;
|
||||
}
|
||||
fseek(in, 0, SEEK_END);
|
||||
file_size = ftell(in);
|
||||
fseek(in, 0, SEEK_SET);
|
||||
// we allocate one extra byte for the \0 terminator
|
||||
file_data = (uint8_t*)WebPMalloc(file_size + 1);
|
||||
if (file_data == NULL) {
|
||||
fclose(in);
|
||||
WFPRINTF(stderr, "memory allocation failure when reading file %s\n",
|
||||
(const W_CHAR*)file_name);
|
||||
return 0;
|
||||
}
|
||||
ok = (fread(file_data, file_size, 1, in) == 1);
|
||||
fclose(in);
|
||||
|
||||
if (!ok) {
|
||||
WFPRINTF(stderr, "Could not read %d bytes of data from file %s\n",
|
||||
(int)file_size, (const W_CHAR*)file_name);
|
||||
WebPFree(file_data);
|
||||
return 0;
|
||||
}
|
||||
file_data[file_size] = '\0'; // convenient 0-terminator
|
||||
*data = file_data;
|
||||
*data_size = file_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int ImgIoUtilWriteFile(const char* const file_name,
|
||||
const uint8_t* data, size_t data_size) {
|
||||
int ok;
|
||||
FILE* out;
|
||||
const int to_stdout = (file_name == NULL) || !WSTRCMP(file_name, "-");
|
||||
|
||||
if (data == NULL) {
|
||||
return 0;
|
||||
}
|
||||
out = to_stdout ? ImgIoUtilSetBinaryMode(stdout) : WFOPEN(file_name, "wb");
|
||||
if (out == NULL) {
|
||||
WFPRINTF(stderr, "Error! Cannot open output file '%s'\n",
|
||||
(const W_CHAR*)file_name);
|
||||
return 0;
|
||||
}
|
||||
ok = (fwrite(data, data_size, 1, out) == 1);
|
||||
if (out != stdout) fclose(out);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height) {
|
||||
while (height-- > 0) {
|
||||
memcpy(dst, src, width * sizeof(*dst));
|
||||
src += src_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t stride, size_t height) {
|
||||
const uint64_t total_size = stride * height;
|
||||
int ok = (total_size == (size_t)total_size);
|
||||
// check that 'stride' is representable as int:
|
||||
ok = ok && ((uint64_t)(int)stride == stride);
|
||||
#if defined(WEBP_MAX_IMAGE_SIZE)
|
||||
ok = ok && (total_size <= (uint64_t)WEBP_MAX_IMAGE_SIZE);
|
||||
#endif
|
||||
return ok;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
64
imageio/imageio_util.h
Normal file
64
imageio/imageio_util.h
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Utility functions used by the image decoders.
|
||||
//
|
||||
|
||||
#ifndef WEBP_IMAGEIO_IMAGEIO_UTIL_H_
|
||||
#define WEBP_IMAGEIO_IMAGEIO_UTIL_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// File I/O
|
||||
|
||||
// Reopen file in binary (O_BINARY) mode.
|
||||
// Returns 'file' on success, NULL otherwise.
|
||||
FILE* ImgIoUtilSetBinaryMode(FILE* file);
|
||||
|
||||
// Allocates storage for entire file 'file_name' and returns contents and size
|
||||
// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
|
||||
// be deleted using WebPFree().
|
||||
// Note: for convenience, the data will be null-terminated with an extra byte
|
||||
// (not accounted for in *data_size), in case the file is text and intended
|
||||
// to be used as a C-string.
|
||||
// If 'file_name' is NULL or equal to "-", input is read from stdin by calling
|
||||
// the function ImgIoUtilReadFromStdin().
|
||||
int ImgIoUtilReadFile(const char* const file_name,
|
||||
const uint8_t** data, size_t* data_size);
|
||||
|
||||
// Same as ImgIoUtilReadFile(), but reads until EOF from stdin instead.
|
||||
int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size);
|
||||
|
||||
// Write a data segment into a file named 'file_name'. Returns true if ok.
|
||||
// If 'file_name' is NULL or equal to "-", output is written to stdout.
|
||||
int ImgIoUtilWriteFile(const char* const file_name,
|
||||
const uint8_t* data, size_t data_size);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Copy width x height pixels from 'src' to 'dst' honoring the strides.
|
||||
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Returns 0 in case of overflow, memory over-allocation or excessive dimension.
|
||||
int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t stride, size_t height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_IMAGEIO_UTIL_H_
|
364
imageio/jpegdec.c
Normal file
364
imageio/jpegdec.c
Normal file
@ -0,0 +1,364 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// JPEG decode.
|
||||
|
||||
#include "./jpegdec.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef WEBP_HAVE_JPEG
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Metadata processing
|
||||
|
||||
#ifndef JPEG_APP1
|
||||
# define JPEG_APP1 (JPEG_APP0 + 1)
|
||||
#endif
|
||||
#ifndef JPEG_APP2
|
||||
# define JPEG_APP2 (JPEG_APP0 + 2)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
const uint8_t* data;
|
||||
size_t data_length;
|
||||
int seq; // this segment's sequence number [1, 255] for use in reassembly.
|
||||
} ICCPSegment;
|
||||
|
||||
static void SaveMetadataMarkers(j_decompress_ptr dinfo) {
|
||||
const unsigned int max_marker_length = 0xffff;
|
||||
jpeg_save_markers(dinfo, JPEG_APP1, max_marker_length); // Exif/XMP
|
||||
jpeg_save_markers(dinfo, JPEG_APP2, max_marker_length); // ICC profile
|
||||
}
|
||||
|
||||
static int CompareICCPSegments(const void* a, const void* b) {
|
||||
const ICCPSegment* s1 = (const ICCPSegment*)a;
|
||||
const ICCPSegment* s2 = (const ICCPSegment*)b;
|
||||
return s1->seq - s2->seq;
|
||||
}
|
||||
|
||||
// Extract ICC profile segments from the marker list in 'dinfo', reassembling
|
||||
// and storing them in 'iccp'.
|
||||
// Returns true on success and false for memory errors and corrupt profiles.
|
||||
static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
// ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files
|
||||
static const char kICCPSignature[] = "ICC_PROFILE";
|
||||
static const size_t kICCPSignatureLength = 12; // signature includes '\0'
|
||||
static const size_t kICCPSkipLength = 14; // signature + seq & count
|
||||
int expected_count = 0;
|
||||
int actual_count = 0;
|
||||
int seq_max = 0;
|
||||
size_t total_size = 0;
|
||||
ICCPSegment iccp_segments[255];
|
||||
jpeg_saved_marker_ptr marker;
|
||||
|
||||
memset(iccp_segments, 0, sizeof(iccp_segments));
|
||||
for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
|
||||
if (marker->marker == JPEG_APP2 &&
|
||||
marker->data_length > kICCPSkipLength &&
|
||||
!memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) {
|
||||
// ICC_PROFILE\0<seq><count>; 'seq' starts at 1.
|
||||
const int seq = marker->data[kICCPSignatureLength];
|
||||
const int count = marker->data[kICCPSignatureLength + 1];
|
||||
const size_t segment_size = marker->data_length - kICCPSkipLength;
|
||||
ICCPSegment* segment;
|
||||
|
||||
if (segment_size == 0 || count == 0 || seq == 0) {
|
||||
fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)"
|
||||
" cannot be 0!\n",
|
||||
(int)segment_size, seq, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (expected_count == 0) {
|
||||
expected_count = count;
|
||||
} else if (expected_count != count) {
|
||||
fprintf(stderr, "[ICCP] Inconsistent segment count (%d / %d)!\n",
|
||||
expected_count, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
segment = iccp_segments + seq - 1;
|
||||
if (segment->data_length != 0) {
|
||||
fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
segment->data = marker->data + kICCPSkipLength;
|
||||
segment->data_length = segment_size;
|
||||
segment->seq = seq;
|
||||
total_size += segment_size;
|
||||
if (seq > seq_max) seq_max = seq;
|
||||
++actual_count;
|
||||
}
|
||||
}
|
||||
|
||||
if (actual_count == 0) return 1;
|
||||
if (seq_max != actual_count) {
|
||||
fprintf(stderr, "[ICCP] Discontinuous segments, expected: %d actual: %d!\n",
|
||||
actual_count, seq_max);
|
||||
return 0;
|
||||
}
|
||||
if (expected_count != actual_count) {
|
||||
fprintf(stderr, "[ICCP] Segment count: %d does not match expected: %d!\n",
|
||||
actual_count, expected_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The segments may appear out of order in the file, sort them based on
|
||||
// sequence number before assembling the payload.
|
||||
qsort(iccp_segments, actual_count, sizeof(*iccp_segments),
|
||||
CompareICCPSegments);
|
||||
|
||||
iccp->bytes = (uint8_t*)malloc(total_size);
|
||||
if (iccp->bytes == NULL) return 0;
|
||||
iccp->size = total_size;
|
||||
|
||||
{
|
||||
int i;
|
||||
size_t offset = 0;
|
||||
for (i = 0; i < seq_max; ++i) {
|
||||
memcpy(iccp->bytes + offset,
|
||||
iccp_segments[i].data, iccp_segments[i].data_length);
|
||||
offset += iccp_segments[i].data_length;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Returns true on success and false for memory errors and corrupt profiles.
|
||||
// The caller must use MetadataFree() on 'metadata' in all cases.
|
||||
static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
|
||||
Metadata* const metadata) {
|
||||
static const struct {
|
||||
int marker;
|
||||
const char* signature;
|
||||
size_t signature_length;
|
||||
size_t storage_offset;
|
||||
} kJPEGMetadataMap[] = {
|
||||
// Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
|
||||
{ JPEG_APP1, "Exif\0", 6, METADATA_OFFSET(exif) },
|
||||
// XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
|
||||
// TODO(jzern) Add support for 'ExtendedXMP'
|
||||
{ JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) },
|
||||
{ 0, NULL, 0, 0 },
|
||||
};
|
||||
jpeg_saved_marker_ptr marker;
|
||||
// Treat ICC profiles separately as they may be segmented and out of order.
|
||||
if (!StoreICCP(dinfo, &metadata->iccp)) return 0;
|
||||
|
||||
for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
|
||||
int i;
|
||||
for (i = 0; kJPEGMetadataMap[i].marker != 0; ++i) {
|
||||
if (marker->marker == kJPEGMetadataMap[i].marker &&
|
||||
marker->data_length > kJPEGMetadataMap[i].signature_length &&
|
||||
!memcmp(marker->data, kJPEGMetadataMap[i].signature,
|
||||
kJPEGMetadataMap[i].signature_length)) {
|
||||
MetadataPayload* const payload =
|
||||
(MetadataPayload*)((uint8_t*)metadata +
|
||||
kJPEGMetadataMap[i].storage_offset);
|
||||
|
||||
if (payload->bytes == NULL) {
|
||||
const char* marker_data = (const char*)marker->data +
|
||||
kJPEGMetadataMap[i].signature_length;
|
||||
const size_t marker_data_length =
|
||||
marker->data_length - kJPEGMetadataMap[i].signature_length;
|
||||
if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0;
|
||||
} else {
|
||||
fprintf(stderr, "Ignoring additional '%s' marker\n",
|
||||
kJPEGMetadataMap[i].signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#undef JPEG_APP1
|
||||
#undef JPEG_APP2
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// JPEG decoding
|
||||
|
||||
struct my_error_mgr {
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
};
|
||||
|
||||
static void my_error_exit(j_common_ptr dinfo) {
|
||||
struct my_error_mgr* myerr = (struct my_error_mgr*)dinfo->err;
|
||||
fprintf(stderr, "libjpeg error: ");
|
||||
dinfo->err->output_message(dinfo);
|
||||
longjmp(myerr->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_source_mgr pub;
|
||||
const uint8_t* data;
|
||||
size_t data_size;
|
||||
} JPEGReadContext;
|
||||
|
||||
static void ContextInit(j_decompress_ptr cinfo) {
|
||||
JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
|
||||
ctx->pub.next_input_byte = ctx->data;
|
||||
ctx->pub.bytes_in_buffer = ctx->data_size;
|
||||
}
|
||||
|
||||
static boolean ContextFill(j_decompress_ptr cinfo) {
|
||||
// we shouldn't get here.
|
||||
ERREXIT(cinfo, JERR_FILE_READ);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
|
||||
JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
|
||||
size_t jump = (size_t)jump_size;
|
||||
if (jump > ctx->pub.bytes_in_buffer) { // Don't overflow the buffer.
|
||||
jump = ctx->pub.bytes_in_buffer;
|
||||
}
|
||||
ctx->pub.bytes_in_buffer -= jump;
|
||||
ctx->pub.next_input_byte += jump;
|
||||
}
|
||||
|
||||
static void ContextTerm(j_decompress_ptr cinfo) {
|
||||
(void)cinfo;
|
||||
}
|
||||
|
||||
static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
|
||||
JPEGReadContext* const ctx) {
|
||||
cinfo->src = (struct jpeg_source_mgr*)ctx;
|
||||
ctx->pub.init_source = ContextInit;
|
||||
ctx->pub.fill_input_buffer = ContextFill;
|
||||
ctx->pub.skip_input_data = ContextSkip;
|
||||
ctx->pub.resync_to_restart = jpeg_resync_to_restart;
|
||||
ctx->pub.term_source = ContextTerm;
|
||||
ctx->pub.bytes_in_buffer = 0;
|
||||
ctx->pub.next_input_byte = NULL;
|
||||
}
|
||||
|
||||
int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
Metadata* const metadata) {
|
||||
volatile int ok = 0;
|
||||
int width, height;
|
||||
int64_t stride;
|
||||
volatile struct jpeg_decompress_struct dinfo;
|
||||
struct my_error_mgr jerr;
|
||||
uint8_t* volatile rgb = NULL;
|
||||
JSAMPROW buffer[1];
|
||||
JPEGReadContext ctx;
|
||||
|
||||
if (data == NULL || data_size == 0 || pic == NULL) return 0;
|
||||
|
||||
(void)keep_alpha;
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.data = data;
|
||||
ctx.data_size = data_size;
|
||||
|
||||
memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo)); // for setjmp safety
|
||||
dinfo.err = jpeg_std_error(&jerr.pub);
|
||||
jerr.pub.error_exit = my_error_exit;
|
||||
|
||||
if (setjmp(jerr.setjmp_buffer)) {
|
||||
Error:
|
||||
MetadataFree(metadata);
|
||||
jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
|
||||
goto End;
|
||||
}
|
||||
|
||||
jpeg_create_decompress((j_decompress_ptr)&dinfo);
|
||||
ContextSetup(&dinfo, &ctx);
|
||||
if (metadata != NULL) SaveMetadataMarkers((j_decompress_ptr)&dinfo);
|
||||
jpeg_read_header((j_decompress_ptr)&dinfo, TRUE);
|
||||
|
||||
dinfo.out_color_space = JCS_RGB;
|
||||
dinfo.do_fancy_upsampling = TRUE;
|
||||
|
||||
jpeg_start_decompress((j_decompress_ptr)&dinfo);
|
||||
|
||||
if (dinfo.output_components != 3) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
width = dinfo.output_width;
|
||||
height = dinfo.output_height;
|
||||
stride = (int64_t)dinfo.output_width * dinfo.output_components * sizeof(*rgb);
|
||||
|
||||
if (stride != (int)stride ||
|
||||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
rgb = (uint8_t*)malloc((size_t)stride * height);
|
||||
if (rgb == NULL) {
|
||||
goto Error;
|
||||
}
|
||||
buffer[0] = (JSAMPLE*)rgb;
|
||||
|
||||
while (dinfo.output_scanline < dinfo.output_height) {
|
||||
if (jpeg_read_scanlines((j_decompress_ptr)&dinfo, buffer, 1) != 1) {
|
||||
goto Error;
|
||||
}
|
||||
buffer[0] += stride;
|
||||
}
|
||||
|
||||
if (metadata != NULL) {
|
||||
ok = ExtractMetadataFromJPEG((j_decompress_ptr)&dinfo, metadata);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Error extracting JPEG metadata!\n");
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
jpeg_finish_decompress((j_decompress_ptr)&dinfo);
|
||||
jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
|
||||
|
||||
// WebP conversion.
|
||||
pic->width = width;
|
||||
pic->height = height;
|
||||
ok = WebPPictureImportRGB(pic, rgb, (int)stride);
|
||||
if (!ok) {
|
||||
pic->width = 0; // WebPPictureImportRGB() barely touches 'pic' on failure.
|
||||
pic->height = 0; // Just reset dimensions but keep any 'custom_ptr' etc.
|
||||
MetadataFree(metadata); // In case the caller forgets to free it on error.
|
||||
}
|
||||
|
||||
End:
|
||||
free(rgb);
|
||||
return ok;
|
||||
}
|
||||
#else // !WEBP_HAVE_JPEG
|
||||
int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
|
||||
"development package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
#endif // WEBP_HAVE_JPEG
|
||||
|
||||
// -----------------------------------------------------------------------------
|
37
imageio/jpegdec.h
Normal file
37
imageio/jpegdec.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// JPEG decode.
|
||||
|
||||
#ifndef WEBP_IMAGEIO_JPEGDEC_H_
|
||||
#define WEBP_IMAGEIO_JPEGDEC_H_
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Metadata;
|
||||
struct WebPPicture;
|
||||
|
||||
// Reads a JPEG from 'data', returning the decoded output in 'pic'.
|
||||
// The output is RGB or YUV depending on pic->use_argb value.
|
||||
// Returns true on success.
|
||||
// 'keep_alpha' has no effect, but is kept for coherence with other signatures
|
||||
// for image readers.
|
||||
int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_JPEGDEC_H_
|
49
imageio/metadata.c
Normal file
49
imageio/metadata.c
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Metadata types and functions.
|
||||
//
|
||||
|
||||
#include "./metadata.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
void MetadataInit(Metadata* const metadata) {
|
||||
if (metadata == NULL) return;
|
||||
memset(metadata, 0, sizeof(*metadata));
|
||||
}
|
||||
|
||||
void MetadataPayloadDelete(MetadataPayload* const payload) {
|
||||
if (payload == NULL) return;
|
||||
free(payload->bytes);
|
||||
payload->bytes = NULL;
|
||||
payload->size = 0;
|
||||
}
|
||||
|
||||
void MetadataFree(Metadata* const metadata) {
|
||||
if (metadata == NULL) return;
|
||||
MetadataPayloadDelete(&metadata->exif);
|
||||
MetadataPayloadDelete(&metadata->iccp);
|
||||
MetadataPayloadDelete(&metadata->xmp);
|
||||
}
|
||||
|
||||
int MetadataCopy(const char* metadata, size_t metadata_len,
|
||||
MetadataPayload* const payload) {
|
||||
if (metadata == NULL || metadata_len == 0 || payload == NULL) return 0;
|
||||
payload->bytes = (uint8_t*)malloc(metadata_len);
|
||||
if (payload->bytes == NULL) return 0;
|
||||
payload->size = metadata_len;
|
||||
memcpy(payload->bytes, metadata, metadata_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
47
imageio/metadata.h
Normal file
47
imageio/metadata.h
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Metadata types and functions.
|
||||
//
|
||||
|
||||
#ifndef WEBP_IMAGEIO_METADATA_H_
|
||||
#define WEBP_IMAGEIO_METADATA_H_
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct MetadataPayload {
|
||||
uint8_t* bytes;
|
||||
size_t size;
|
||||
} MetadataPayload;
|
||||
|
||||
typedef struct Metadata {
|
||||
MetadataPayload exif;
|
||||
MetadataPayload iccp;
|
||||
MetadataPayload xmp;
|
||||
} Metadata;
|
||||
|
||||
#define METADATA_OFFSET(x) offsetof(Metadata, x)
|
||||
|
||||
void MetadataInit(Metadata* const metadata);
|
||||
void MetadataPayloadDelete(MetadataPayload* const payload);
|
||||
void MetadataFree(Metadata* const metadata);
|
||||
|
||||
// Stores 'metadata' to 'payload->bytes', returns false on allocation error.
|
||||
int MetadataCopy(const char* metadata, size_t metadata_len,
|
||||
MetadataPayload* const payload);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_METADATA_H_
|
374
imageio/pngdec.c
Normal file
374
imageio/pngdec.c
Normal file
@ -0,0 +1,374 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// PNG decode.
|
||||
|
||||
#include "./pngdec.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef WEBP_HAVE_PNG
|
||||
#ifndef PNG_USER_MEM_SUPPORTED
|
||||
#define PNG_USER_MEM_SUPPORTED // for png_create_read_struct_2
|
||||
#endif
|
||||
#include <png.h>
|
||||
#include <setjmp.h> // note: this must be included *after* png.h
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
|
||||
#define LOCAL_PNG_VERSION ((PNG_LIBPNG_VER_MAJOR << 8) | PNG_LIBPNG_VER_MINOR)
|
||||
#define LOCAL_PNG_PREREQ(maj, min) \
|
||||
(LOCAL_PNG_VERSION >= (((maj) << 8) | (min)))
|
||||
|
||||
static void PNGAPI error_function(png_structp png, png_const_charp error) {
|
||||
if (error != NULL) fprintf(stderr, "libpng error: %s\n", error);
|
||||
longjmp(png_jmpbuf(png), 1);
|
||||
}
|
||||
|
||||
#if LOCAL_PNG_PREREQ(1,4)
|
||||
typedef png_alloc_size_t LocalPngAllocSize;
|
||||
#else
|
||||
typedef png_size_t LocalPngAllocSize;
|
||||
#endif
|
||||
|
||||
static png_voidp MallocFunc(png_structp png_ptr, LocalPngAllocSize size) {
|
||||
(void)png_ptr;
|
||||
if (size != (size_t)size) return NULL;
|
||||
if (!ImgIoUtilCheckSizeArgumentsOverflow(size, 1)) return NULL;
|
||||
return (png_voidp)malloc((size_t)size);
|
||||
}
|
||||
|
||||
static void FreeFunc(png_structp png_ptr, png_voidp ptr) {
|
||||
(void)png_ptr;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
// Converts the NULL terminated 'hexstring' which contains 2-byte character
|
||||
// representations of hex values to raw data.
|
||||
// 'hexstring' may contain values consisting of [A-F][a-f][0-9] in pairs,
|
||||
// e.g., 7af2..., separated by any number of newlines.
|
||||
// 'expected_length' is the anticipated processed size.
|
||||
// On success the raw buffer is returned with its length equivalent to
|
||||
// 'expected_length'. NULL is returned if the processed length is less than
|
||||
// 'expected_length' or any character aside from those above is encountered.
|
||||
// The returned buffer must be freed by the caller.
|
||||
static uint8_t* HexStringToBytes(const char* hexstring,
|
||||
size_t expected_length) {
|
||||
const char* src = hexstring;
|
||||
size_t actual_length = 0;
|
||||
uint8_t* const raw_data = (uint8_t*)malloc(expected_length);
|
||||
uint8_t* dst;
|
||||
|
||||
if (raw_data == NULL) return NULL;
|
||||
|
||||
for (dst = raw_data; actual_length < expected_length && *src != '\0'; ++src) {
|
||||
char* end;
|
||||
char val[3];
|
||||
if (*src == '\n') continue;
|
||||
val[0] = *src++;
|
||||
val[1] = *src;
|
||||
val[2] = '\0';
|
||||
*dst++ = (uint8_t)strtol(val, &end, 16);
|
||||
if (end != val + 2) break;
|
||||
++actual_length;
|
||||
}
|
||||
|
||||
if (actual_length != expected_length) {
|
||||
free(raw_data);
|
||||
return NULL;
|
||||
}
|
||||
return raw_data;
|
||||
}
|
||||
|
||||
static int ProcessRawProfile(const char* profile, size_t profile_len,
|
||||
MetadataPayload* const payload) {
|
||||
const char* src = profile;
|
||||
char* end;
|
||||
int expected_length;
|
||||
|
||||
if (profile == NULL || profile_len == 0) return 0;
|
||||
|
||||
// ImageMagick formats 'raw profiles' as
|
||||
// '\n<name>\n<length>(%8lu)\n<hex payload>\n'.
|
||||
if (*src != '\n') {
|
||||
fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n",
|
||||
*src);
|
||||
return 0;
|
||||
}
|
||||
++src;
|
||||
// skip the profile name and extract the length.
|
||||
while (*src != '\0' && *src++ != '\n') {}
|
||||
expected_length = (int)strtol(src, &end, 10);
|
||||
if (*end != '\n') {
|
||||
fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n",
|
||||
*end);
|
||||
return 0;
|
||||
}
|
||||
++end;
|
||||
|
||||
// 'end' now points to the profile payload.
|
||||
payload->bytes = HexStringToBytes(end, expected_length);
|
||||
if (payload->bytes == NULL) return 0;
|
||||
payload->size = expected_length;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char* name;
|
||||
int (*process)(const char* profile, size_t profile_len,
|
||||
MetadataPayload* const payload);
|
||||
size_t storage_offset;
|
||||
} kPNGMetadataMap[] = {
|
||||
// https://exiftool.org/TagNames/PNG.html#TextualData
|
||||
// See also: ExifTool on CPAN.
|
||||
{ "Raw profile type exif", ProcessRawProfile, METADATA_OFFSET(exif) },
|
||||
{ "Raw profile type xmp", ProcessRawProfile, METADATA_OFFSET(xmp) },
|
||||
// Exiftool puts exif data in APP1 chunk, too.
|
||||
{ "Raw profile type APP1", ProcessRawProfile, METADATA_OFFSET(exif) },
|
||||
// XMP Specification Part 3, Section 3 #PNG
|
||||
{ "XML:com.adobe.xmp", MetadataCopy, METADATA_OFFSET(xmp) },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
// Looks for metadata at both the beginning and end of the PNG file, giving
|
||||
// preference to the head.
|
||||
// Returns true on success. The caller must use MetadataFree() on 'metadata' in
|
||||
// all cases.
|
||||
static int ExtractMetadataFromPNG(png_structp png,
|
||||
png_infop const head_info,
|
||||
png_infop const end_info,
|
||||
Metadata* const metadata) {
|
||||
int p;
|
||||
|
||||
for (p = 0; p < 2; ++p) {
|
||||
png_infop const info = (p == 0) ? head_info : end_info;
|
||||
png_textp text = NULL;
|
||||
const png_uint_32 num = png_get_text(png, info, &text, NULL);
|
||||
png_uint_32 i;
|
||||
// Look for EXIF / XMP metadata.
|
||||
for (i = 0; i < num; ++i, ++text) {
|
||||
int j;
|
||||
for (j = 0; kPNGMetadataMap[j].name != NULL; ++j) {
|
||||
if (!strcmp(text->key, kPNGMetadataMap[j].name)) {
|
||||
MetadataPayload* const payload =
|
||||
(MetadataPayload*)((uint8_t*)metadata +
|
||||
kPNGMetadataMap[j].storage_offset);
|
||||
png_size_t text_length;
|
||||
switch (text->compression) {
|
||||
#ifdef PNG_iTXt_SUPPORTED
|
||||
case PNG_ITXT_COMPRESSION_NONE:
|
||||
case PNG_ITXT_COMPRESSION_zTXt:
|
||||
text_length = text->itxt_length;
|
||||
break;
|
||||
#endif
|
||||
case PNG_TEXT_COMPRESSION_NONE:
|
||||
case PNG_TEXT_COMPRESSION_zTXt:
|
||||
default:
|
||||
text_length = text->text_length;
|
||||
break;
|
||||
}
|
||||
if (payload->bytes != NULL) {
|
||||
fprintf(stderr, "Ignoring additional '%s'\n", text->key);
|
||||
} else if (!kPNGMetadataMap[j].process(text->text, text_length,
|
||||
payload)) {
|
||||
fprintf(stderr, "Failed to process: '%s'\n", text->key);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Look for an ICC profile.
|
||||
{
|
||||
png_charp name;
|
||||
int comp_type;
|
||||
#if LOCAL_PNG_PREREQ(1,5)
|
||||
png_bytep profile;
|
||||
#else
|
||||
png_charp profile;
|
||||
#endif
|
||||
png_uint_32 len;
|
||||
|
||||
if (png_get_iCCP(png, info,
|
||||
&name, &comp_type, &profile, &len) == PNG_INFO_iCCP) {
|
||||
if (!MetadataCopy((const char*)profile, len, &metadata->iccp)) return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const uint8_t* data;
|
||||
size_t data_size;
|
||||
png_size_t offset;
|
||||
} PNGReadContext;
|
||||
|
||||
static void ReadFunc(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
PNGReadContext* const ctx = (PNGReadContext*)png_get_io_ptr(png_ptr);
|
||||
if (ctx->data_size - ctx->offset < length) {
|
||||
png_error(png_ptr, "ReadFunc: invalid read length (overflow)!");
|
||||
}
|
||||
memcpy(data, ctx->data + ctx->offset, length);
|
||||
ctx->offset += length;
|
||||
}
|
||||
|
||||
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
volatile png_structp png = NULL;
|
||||
volatile png_infop info = NULL;
|
||||
volatile png_infop end_info = NULL;
|
||||
PNGReadContext context = { NULL, 0, 0 };
|
||||
int color_type, bit_depth, interlaced;
|
||||
int num_channels;
|
||||
int num_passes;
|
||||
int p;
|
||||
volatile int ok = 0;
|
||||
png_uint_32 width, height, y;
|
||||
int64_t stride;
|
||||
uint8_t* volatile rgb = NULL;
|
||||
|
||||
if (data == NULL || data_size == 0 || pic == NULL) return 0;
|
||||
|
||||
context.data = data;
|
||||
context.data_size = data_size;
|
||||
|
||||
png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL,
|
||||
NULL, MallocFunc, FreeFunc);
|
||||
if (png == NULL) goto End;
|
||||
|
||||
png_set_error_fn(png, 0, error_function, NULL);
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
Error:
|
||||
MetadataFree(metadata);
|
||||
goto End;
|
||||
}
|
||||
|
||||
#if LOCAL_PNG_PREREQ(1,5) || \
|
||||
(LOCAL_PNG_PREREQ(1,4) && PNG_LIBPNG_VER_RELEASE >= 1)
|
||||
// If it looks like the bitstream is going to need more memory than libpng's
|
||||
// internal limit (default: 8M), try to (reasonably) raise it.
|
||||
if (data_size > png_get_chunk_malloc_max(png) && data_size < (1u << 24)) {
|
||||
png_set_chunk_malloc_max(png, data_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
info = png_create_info_struct(png);
|
||||
if (info == NULL) goto Error;
|
||||
end_info = png_create_info_struct(png);
|
||||
if (end_info == NULL) goto Error;
|
||||
|
||||
png_set_read_fn(png, &context, ReadFunc);
|
||||
png_read_info(png, info);
|
||||
if (!png_get_IHDR(png, info,
|
||||
&width, &height, &bit_depth, &color_type, &interlaced,
|
||||
NULL, NULL)) goto Error;
|
||||
|
||||
png_set_strip_16(png);
|
||||
png_set_packing(png);
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
png_set_palette_to_rgb(png);
|
||||
}
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
||||
if (bit_depth < 8) {
|
||||
png_set_expand_gray_1_2_4_to_8(png);
|
||||
}
|
||||
png_set_gray_to_rgb(png);
|
||||
}
|
||||
if (png_get_valid(png, info, PNG_INFO_tRNS)) {
|
||||
png_set_tRNS_to_alpha(png);
|
||||
}
|
||||
|
||||
// Apply gamma correction if needed.
|
||||
{
|
||||
double image_gamma = 1 / 2.2, screen_gamma = 2.2;
|
||||
int srgb_intent;
|
||||
if (png_get_sRGB(png, info, &srgb_intent) ||
|
||||
png_get_gAMA(png, info, &image_gamma)) {
|
||||
png_set_gamma(png, screen_gamma, image_gamma);
|
||||
}
|
||||
}
|
||||
|
||||
if (!keep_alpha) {
|
||||
png_set_strip_alpha(png);
|
||||
}
|
||||
|
||||
num_passes = png_set_interlace_handling(png);
|
||||
png_read_update_info(png, info);
|
||||
|
||||
num_channels = png_get_channels(png, info);
|
||||
if (num_channels != 3 && num_channels != 4) {
|
||||
goto Error;
|
||||
}
|
||||
stride = (int64_t)num_channels * width * sizeof(*rgb);
|
||||
if (stride != (int)stride ||
|
||||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
rgb = (uint8_t*)malloc((size_t)stride * height);
|
||||
if (rgb == NULL) goto Error;
|
||||
for (p = 0; p < num_passes; ++p) {
|
||||
png_bytep row = rgb;
|
||||
for (y = 0; y < height; ++y) {
|
||||
png_read_rows(png, &row, NULL, 1);
|
||||
row += stride;
|
||||
}
|
||||
}
|
||||
png_read_end(png, end_info);
|
||||
|
||||
if (metadata != NULL &&
|
||||
!ExtractMetadataFromPNG(png, info, end_info, metadata)) {
|
||||
fprintf(stderr, "Error extracting PNG metadata!\n");
|
||||
goto Error;
|
||||
}
|
||||
|
||||
pic->width = (int)width;
|
||||
pic->height = (int)height;
|
||||
ok = (num_channels == 4) ? WebPPictureImportRGBA(pic, rgb, (int)stride)
|
||||
: WebPPictureImportRGB(pic, rgb, (int)stride);
|
||||
|
||||
if (!ok) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
End:
|
||||
if (png != NULL) {
|
||||
png_destroy_read_struct((png_structpp)&png,
|
||||
(png_infopp)&info, (png_infopp)&end_info);
|
||||
}
|
||||
free(rgb);
|
||||
return ok;
|
||||
}
|
||||
#else // !WEBP_HAVE_PNG
|
||||
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr, "PNG support not compiled. Please install the libpng "
|
||||
"development package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
#endif // WEBP_HAVE_PNG
|
||||
|
||||
// -----------------------------------------------------------------------------
|
37
imageio/pngdec.h
Normal file
37
imageio/pngdec.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// PNG decode.
|
||||
|
||||
#ifndef WEBP_IMAGEIO_PNGDEC_H_
|
||||
#define WEBP_IMAGEIO_PNGDEC_H_
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Metadata;
|
||||
struct WebPPicture;
|
||||
|
||||
// Reads a PNG from 'data', returning the decoded output in 'pic'.
|
||||
// Output is RGBA or YUVA, depending on pic->use_argb value.
|
||||
// If 'keep_alpha' is true and the PNG has an alpha channel, the output is RGBA
|
||||
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
|
||||
// Returns true on success.
|
||||
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_PNGDEC_H_
|
301
imageio/pnmdec.c
Normal file
301
imageio/pnmdec.c
Normal file
@ -0,0 +1,301 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// (limited) PNM decoder
|
||||
|
||||
#include "./pnmdec.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "./imageio_util.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
WIDTH_FLAG = 1 << 0,
|
||||
HEIGHT_FLAG = 1 << 1,
|
||||
DEPTH_FLAG = 1 << 2,
|
||||
MAXVAL_FLAG = 1 << 3,
|
||||
TUPLE_FLAG = 1 << 4,
|
||||
ALL_NEEDED_FLAGS = WIDTH_FLAG | HEIGHT_FLAG | DEPTH_FLAG | MAXVAL_FLAG
|
||||
} PNMFlags;
|
||||
|
||||
typedef struct {
|
||||
const uint8_t* data;
|
||||
size_t data_size;
|
||||
int width, height;
|
||||
int bytes_per_px;
|
||||
int depth; // 1 (grayscale), 2 (grayscale + alpha), 3 (rgb), 4 (rgba)
|
||||
int max_value;
|
||||
int type; // 5, 6 or 7
|
||||
int seen_flags;
|
||||
} PNMInfo;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PNM decoding
|
||||
|
||||
#define MAX_LINE_SIZE 1024
|
||||
static const size_t kMinPNMHeaderSize = 3;
|
||||
|
||||
static size_t ReadLine(const uint8_t* const data, size_t off, size_t data_size,
|
||||
char out[MAX_LINE_SIZE + 1], size_t* const out_size) {
|
||||
size_t i = 0;
|
||||
*out_size = 0;
|
||||
redo:
|
||||
for (i = 0; i < MAX_LINE_SIZE && off < data_size; ++i) {
|
||||
out[i] = data[off++];
|
||||
if (out[i] == '\n') break;
|
||||
}
|
||||
if (off < data_size) {
|
||||
if (i == 0) goto redo; // empty line
|
||||
if (out[0] == '#') goto redo; // skip comment
|
||||
}
|
||||
out[i] = 0; // safety sentinel
|
||||
*out_size = i;
|
||||
return off;
|
||||
}
|
||||
|
||||
static size_t FlagError(const char flag[]) {
|
||||
fprintf(stderr, "PAM header error: flags '%s' already seen.\n", flag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// inspired from http://netpbm.sourceforge.net/doc/pam.html
|
||||
static size_t ReadPAMFields(PNMInfo* const info, size_t off) {
|
||||
char out[MAX_LINE_SIZE + 1];
|
||||
size_t out_size;
|
||||
int tmp;
|
||||
int expected_depth = -1;
|
||||
assert(info != NULL);
|
||||
while (1) {
|
||||
off = ReadLine(info->data, off, info->data_size, out, &out_size);
|
||||
if (off == 0) return 0;
|
||||
if (sscanf(out, "WIDTH %d", &tmp) == 1) {
|
||||
if (info->seen_flags & WIDTH_FLAG) return FlagError("WIDTH");
|
||||
info->seen_flags |= WIDTH_FLAG;
|
||||
info->width = tmp;
|
||||
} else if (sscanf(out, "HEIGHT %d", &tmp) == 1) {
|
||||
if (info->seen_flags & HEIGHT_FLAG) return FlagError("HEIGHT");
|
||||
info->seen_flags |= HEIGHT_FLAG;
|
||||
info->height = tmp;
|
||||
} else if (sscanf(out, "DEPTH %d", &tmp) == 1) {
|
||||
if (info->seen_flags & DEPTH_FLAG) return FlagError("DEPTH");
|
||||
info->seen_flags |= DEPTH_FLAG;
|
||||
info->depth = tmp;
|
||||
} else if (sscanf(out, "MAXVAL %d", &tmp) == 1) {
|
||||
if (info->seen_flags & MAXVAL_FLAG) return FlagError("MAXVAL");
|
||||
info->seen_flags |= MAXVAL_FLAG;
|
||||
info->max_value = tmp;
|
||||
} else if (!strcmp(out, "TUPLTYPE RGB_ALPHA")) {
|
||||
expected_depth = 4;
|
||||
info->seen_flags |= TUPLE_FLAG;
|
||||
} else if (!strcmp(out, "TUPLTYPE RGB")) {
|
||||
expected_depth = 3;
|
||||
info->seen_flags |= TUPLE_FLAG;
|
||||
} else if (!strcmp(out, "TUPLTYPE GRAYSCALE_ALPHA")) {
|
||||
expected_depth = 2;
|
||||
info->seen_flags |= TUPLE_FLAG;
|
||||
} else if (!strcmp(out, "TUPLTYPE GRAYSCALE")) {
|
||||
expected_depth = 1;
|
||||
info->seen_flags |= TUPLE_FLAG;
|
||||
} else if (!strcmp(out, "ENDHDR")) {
|
||||
break;
|
||||
} else {
|
||||
static const char kEllipsis[] = " ...";
|
||||
const size_t kLen = strlen(kEllipsis) + 1; // +1 = trailing \0
|
||||
int i;
|
||||
if (out_size > 20) snprintf(out + 20 - kLen, kLen, kEllipsis);
|
||||
for (i = 0; i < (int)strlen(out); ++i) {
|
||||
// isprint() might trigger a "char-subscripts" warning if given a char.
|
||||
if (!isprint((int)out[i])) out[i] = ' ';
|
||||
}
|
||||
fprintf(stderr, "PAM header error: unrecognized entry [%s]\n", out);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!(info->seen_flags & ALL_NEEDED_FLAGS)) {
|
||||
fprintf(stderr, "PAM header error: missing tags%s%s%s%s\n",
|
||||
(info->seen_flags & WIDTH_FLAG) ? "" : " WIDTH",
|
||||
(info->seen_flags & HEIGHT_FLAG) ? "" : " HEIGHT",
|
||||
(info->seen_flags & DEPTH_FLAG) ? "" : " DEPTH",
|
||||
(info->seen_flags & MAXVAL_FLAG) ? "" : " MAXVAL");
|
||||
return 0;
|
||||
}
|
||||
if (expected_depth != -1 && info->depth != expected_depth) {
|
||||
fprintf(stderr, "PAM header error: expected DEPTH %d but got DEPTH %d\n",
|
||||
expected_depth, info->depth);
|
||||
return 0;
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
static size_t ReadHeader(PNMInfo* const info) {
|
||||
size_t off = 0;
|
||||
char out[MAX_LINE_SIZE + 1];
|
||||
size_t out_size;
|
||||
if (info == NULL) return 0;
|
||||
if (info->data == NULL || info->data_size < kMinPNMHeaderSize) return 0;
|
||||
|
||||
info->width = info->height = 0;
|
||||
info->type = -1;
|
||||
info->seen_flags = 0;
|
||||
info->bytes_per_px = 0;
|
||||
info->depth = 0;
|
||||
info->max_value = 0;
|
||||
|
||||
off = ReadLine(info->data, off, info->data_size, out, &out_size);
|
||||
if (off == 0 || sscanf(out, "P%d", &info->type) != 1) return 0;
|
||||
if (info->type == 7) {
|
||||
off = ReadPAMFields(info, off);
|
||||
} else {
|
||||
off = ReadLine(info->data, off, info->data_size, out, &out_size);
|
||||
if (off == 0 || sscanf(out, "%d %d", &info->width, &info->height) != 2) {
|
||||
return 0;
|
||||
}
|
||||
off = ReadLine(info->data, off, info->data_size, out, &out_size);
|
||||
if (off == 0 || sscanf(out, "%d", &info->max_value) != 1) return 0;
|
||||
|
||||
// finish initializing missing fields
|
||||
info->depth = (info->type == 5) ? 1 : 3;
|
||||
}
|
||||
// perform some basic numerical validation
|
||||
if (info->width <= 0 || info->height <= 0 ||
|
||||
info->type <= 0 || info->type >= 9 ||
|
||||
info->depth <= 0 || info->depth > 4 ||
|
||||
info->max_value <= 0 || info->max_value >= 65536) {
|
||||
return 0;
|
||||
}
|
||||
info->bytes_per_px = info->depth * (info->max_value > 255 ? 2 : 1);
|
||||
return off;
|
||||
}
|
||||
|
||||
int ReadPNM(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
int ok = 0;
|
||||
int i, j;
|
||||
uint64_t stride, pixel_bytes, sample_size, depth;
|
||||
uint8_t* rgb = NULL, *tmp_rgb;
|
||||
size_t offset;
|
||||
PNMInfo info;
|
||||
|
||||
info.data = data;
|
||||
info.data_size = data_size;
|
||||
offset = ReadHeader(&info);
|
||||
if (offset == 0) {
|
||||
fprintf(stderr, "Error parsing PNM header.\n");
|
||||
goto End;
|
||||
}
|
||||
|
||||
if (info.type < 5 || info.type > 7) {
|
||||
fprintf(stderr, "Unsupported P%d PNM format.\n", info.type);
|
||||
goto End;
|
||||
}
|
||||
|
||||
// Some basic validations.
|
||||
if (pic == NULL) goto End;
|
||||
if (info.width > WEBP_MAX_DIMENSION || info.height > WEBP_MAX_DIMENSION) {
|
||||
fprintf(stderr, "Invalid %dx%d dimension for PNM\n",
|
||||
info.width, info.height);
|
||||
goto End;
|
||||
}
|
||||
|
||||
pixel_bytes = (uint64_t)info.width * info.height * info.bytes_per_px;
|
||||
if (data_size < offset + pixel_bytes) {
|
||||
fprintf(stderr, "Truncated PNM file (P%d).\n", info.type);
|
||||
goto End;
|
||||
}
|
||||
sample_size = (info.max_value > 255) ? 2 : 1;
|
||||
// final depth
|
||||
depth = (info.depth == 1 || info.depth == 3 || !keep_alpha) ? 3 : 4;
|
||||
stride = depth * info.width;
|
||||
if (stride != (size_t)stride ||
|
||||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, info.height)) {
|
||||
goto End;
|
||||
}
|
||||
|
||||
rgb = (uint8_t*)malloc((size_t)stride * info.height);
|
||||
if (rgb == NULL) goto End;
|
||||
|
||||
// Convert input.
|
||||
// We only optimize for the sample_size=1, max_value=255, depth=1 case.
|
||||
tmp_rgb = rgb;
|
||||
for (j = 0; j < info.height; ++j) {
|
||||
const uint8_t* in = data + offset;
|
||||
offset += info.bytes_per_px * info.width;
|
||||
assert(offset <= data_size);
|
||||
if (info.max_value == 255 && info.depth >= 3) {
|
||||
// RGB or RGBA
|
||||
if (info.depth == 3 || keep_alpha) {
|
||||
memcpy(tmp_rgb, in, info.depth * info.width * sizeof(*in));
|
||||
} else {
|
||||
assert(info.depth == 4 && !keep_alpha);
|
||||
for (i = 0; i < info.width; ++i) {
|
||||
tmp_rgb[3 * i + 0] = in[4 * i + 0];
|
||||
tmp_rgb[3 * i + 1] = in[4 * i + 1];
|
||||
tmp_rgb[3 * i + 2] = in[4 * i + 2];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unoptimized case, we need to handle non-trivial operations:
|
||||
// * convert 16b to 8b (if max_value > 255)
|
||||
// * rescale to [0..255] range (if max_value != 255)
|
||||
// * drop the alpha channel (if keep_alpha is false)
|
||||
const uint32_t round = info.max_value / 2;
|
||||
int k = 0;
|
||||
for (i = 0; i < info.width * info.depth; ++i) {
|
||||
uint32_t v = (sample_size == 2) ? 256u * in[2 * i + 0] + in[2 * i + 1]
|
||||
: in[i];
|
||||
if (info.max_value != 255) v = (v * 255u + round) / info.max_value;
|
||||
if (v > 255u) v = 255u;
|
||||
if (info.depth > 2) {
|
||||
if (!keep_alpha && info.depth == 4 && (i % 4) == 3) {
|
||||
// skip alpha
|
||||
} else {
|
||||
tmp_rgb[k] = v;
|
||||
k += 1;
|
||||
}
|
||||
} else if (info.depth == 1 || (i % 2) == 0) {
|
||||
tmp_rgb[k + 0] = tmp_rgb[k + 1] = tmp_rgb[k + 2] = v;
|
||||
k += 3;
|
||||
} else if (keep_alpha && info.depth == 2) {
|
||||
tmp_rgb[k] = v;
|
||||
k += 1;
|
||||
} else {
|
||||
// skip alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
tmp_rgb += stride;
|
||||
}
|
||||
|
||||
// WebP conversion.
|
||||
pic->width = info.width;
|
||||
pic->height = info.height;
|
||||
ok = (depth == 4) ? WebPPictureImportRGBA(pic, rgb, (int)stride)
|
||||
: WebPPictureImportRGB(pic, rgb, (int)stride);
|
||||
if (!ok) goto End;
|
||||
|
||||
ok = 1;
|
||||
End:
|
||||
free((void*)rgb);
|
||||
|
||||
(void)metadata;
|
||||
(void)keep_alpha;
|
||||
return ok;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
37
imageio/pnmdec.h
Normal file
37
imageio/pnmdec.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// partial PNM format decoder (ppm/pgm)
|
||||
|
||||
#ifndef WEBP_IMAGEIO_PNMDEC_H_
|
||||
#define WEBP_IMAGEIO_PNMDEC_H_
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Metadata;
|
||||
struct WebPPicture;
|
||||
|
||||
// Reads a PNM file from 'data', returning the decoded output in 'pic'.
|
||||
// The output is RGB or YUV depending on pic->use_argb value.
|
||||
// Returns true on success.
|
||||
// 'metadata' has no effect, but is kept for coherence with other signatures
|
||||
// for image readers.
|
||||
int ReadPNM(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_PNMDEC_H_
|
293
imageio/tiffdec.c
Normal file
293
imageio/tiffdec.c
Normal file
@ -0,0 +1,293 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// TIFF decode.
|
||||
|
||||
#include "./tiffdec.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WEBP_HAVE_TIFF
|
||||
#include <tiffio.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
|
||||
static const struct {
|
||||
ttag_t tag;
|
||||
size_t storage_offset;
|
||||
} kTIFFMetadataMap[] = {
|
||||
{ TIFFTAG_ICCPROFILE, METADATA_OFFSET(iccp) },
|
||||
{ TIFFTAG_XMLPACKET, METADATA_OFFSET(xmp) },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
// Returns true on success. The caller must use MetadataFree() on 'metadata' in
|
||||
// all cases.
|
||||
static int ExtractMetadataFromTIFF(TIFF* const tif, Metadata* const metadata) {
|
||||
int i;
|
||||
toff_t exif_ifd_offset;
|
||||
|
||||
for (i = 0; kTIFFMetadataMap[i].tag != 0; ++i) {
|
||||
MetadataPayload* const payload =
|
||||
(MetadataPayload*)((uint8_t*)metadata +
|
||||
kTIFFMetadataMap[i].storage_offset);
|
||||
void* tag_data;
|
||||
uint32_t tag_data_len;
|
||||
|
||||
if (TIFFGetField(tif, kTIFFMetadataMap[i].tag, &tag_data_len, &tag_data) &&
|
||||
!MetadataCopy((const char*)tag_data, tag_data_len, payload)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(jzern): To extract the raw EXIF directory some parsing of it would be
|
||||
// necessary to determine the overall size. In addition, value offsets in
|
||||
// individual directory entries may need to be updated as, depending on the
|
||||
// type, they are file based.
|
||||
// Exif 2.2 Section 4.6.2 Tag Structure
|
||||
// TIFF Revision 6.0 Part 1 Section 2 TIFF Structure #Image File Directory
|
||||
if (TIFFGetField(tif, TIFFTAG_EXIFIFD, &exif_ifd_offset)) {
|
||||
fprintf(stderr, "Warning: EXIF extraction from TIFF is unsupported.\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Ad-hoc structure to supply read-from-memory functionalities.
|
||||
typedef struct {
|
||||
const uint8_t* data;
|
||||
toff_t size;
|
||||
toff_t pos;
|
||||
} MyData;
|
||||
|
||||
static int MyClose(thandle_t opaque) {
|
||||
(void)opaque;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static toff_t MySize(thandle_t opaque) {
|
||||
const MyData* const my_data = (MyData*)opaque;
|
||||
return my_data->size;
|
||||
}
|
||||
|
||||
static toff_t MySeek(thandle_t opaque, toff_t offset, int whence) {
|
||||
MyData* const my_data = (MyData*)opaque;
|
||||
offset += (whence == SEEK_CUR) ? my_data->pos
|
||||
: (whence == SEEK_SET) ? 0
|
||||
: my_data->size;
|
||||
if (offset > my_data->size) return (toff_t)-1;
|
||||
my_data->pos = offset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int MyMapFile(thandle_t opaque, void** base, toff_t* size) {
|
||||
(void)opaque;
|
||||
(void)base;
|
||||
(void)size;
|
||||
return 0;
|
||||
}
|
||||
static void MyUnmapFile(thandle_t opaque, void* base, toff_t size) {
|
||||
(void)opaque;
|
||||
(void)base;
|
||||
(void)size;
|
||||
}
|
||||
|
||||
static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) {
|
||||
MyData* const my_data = (MyData*)opaque;
|
||||
if (my_data->pos + size > my_data->size) {
|
||||
size = (tsize_t)(my_data->size - my_data->pos);
|
||||
}
|
||||
if (size > 0) {
|
||||
memcpy(dst, my_data->data + my_data->pos, size);
|
||||
my_data->pos += size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
// Unmultiply Argb data. Taken from dsp/alpha_processing
|
||||
// (we don't want to force a dependency to a libdspdec library).
|
||||
#define MFIX 24 // 24bit fixed-point arithmetic
|
||||
#define HALF ((1u << MFIX) >> 1)
|
||||
|
||||
static uint32_t Unmult(uint8_t x, uint32_t mult) {
|
||||
const uint32_t v = (x * mult + HALF) >> MFIX;
|
||||
return (v > 255u) ? 255u : v;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t GetScale(uint32_t a) {
|
||||
return (255u << MFIX) / a;
|
||||
}
|
||||
|
||||
#undef MFIX
|
||||
#undef HALF
|
||||
|
||||
static void MultARGBRow(uint8_t* ptr, int width) {
|
||||
int x;
|
||||
for (x = 0; x < width; ++x, ptr += 4) {
|
||||
const uint32_t alpha = ptr[3];
|
||||
if (alpha < 255) {
|
||||
if (alpha == 0) { // alpha == 0
|
||||
ptr[0] = ptr[1] = ptr[2] = 0;
|
||||
} else {
|
||||
const uint32_t scale = GetScale(alpha);
|
||||
ptr[0] = Unmult(ptr[0], scale);
|
||||
ptr[1] = Unmult(ptr[1], scale);
|
||||
ptr[2] = Unmult(ptr[2], scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
Metadata* const metadata) {
|
||||
MyData my_data = { data, (toff_t)data_size, 0 };
|
||||
TIFF* tif;
|
||||
uint32_t image_width, image_height, tile_width, tile_height;
|
||||
uint64_t stride;
|
||||
uint16_t samples_per_px = 0;
|
||||
uint16_t extra_samples = 0;
|
||||
uint16_t* extra_samples_ptr = NULL;
|
||||
uint32_t* raster;
|
||||
int64_t alloc_size;
|
||||
int ok = 0;
|
||||
tdir_t dircount;
|
||||
|
||||
if (data == NULL || data_size == 0 || data_size > INT_MAX || pic == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tif = TIFFClientOpen("Memory", "r", &my_data,
|
||||
MyRead, MyRead, MySeek, MyClose,
|
||||
MySize, MyMapFile, MyUnmapFile);
|
||||
if (tif == NULL) {
|
||||
fprintf(stderr, "Error! Cannot parse TIFF file\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dircount = TIFFNumberOfDirectories(tif);
|
||||
if (dircount > 1) {
|
||||
fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n"
|
||||
"Only the first will be used, %d will be ignored.\n",
|
||||
dircount - 1);
|
||||
}
|
||||
if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) {
|
||||
fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n");
|
||||
goto End;
|
||||
}
|
||||
if (!(samples_per_px == 1 || samples_per_px == 3 || samples_per_px == 4)) {
|
||||
goto End; // not supported
|
||||
}
|
||||
|
||||
if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &image_width) &&
|
||||
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &image_height))) {
|
||||
fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
|
||||
goto End;
|
||||
}
|
||||
stride = (uint64_t)image_width * sizeof(*raster);
|
||||
if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, image_height)) {
|
||||
fprintf(stderr, "Error! TIFF image dimension (%d x %d) is too large.\n",
|
||||
image_width, image_height);
|
||||
goto End;
|
||||
}
|
||||
|
||||
// According to spec, a tile can be bigger than the image. However it should
|
||||
// be a multiple of 16 and not way too large, so check that it's not more
|
||||
// than twice the image size, for dimensions above some arbitrary minimum
|
||||
// 32. We also check that they respect WebP's dimension and memory limit.
|
||||
// Note that a tile can be 6byte/px in some cases. Here we assume
|
||||
// 4byte/px with sizeof(*raster), to be conservative.
|
||||
if (TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width) &&
|
||||
TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height)) {
|
||||
if ((tile_width > 32 && tile_width / 2 > image_width) ||
|
||||
(tile_height > 32 && tile_height / 2 > image_height) ||
|
||||
!ImgIoUtilCheckSizeArgumentsOverflow(
|
||||
(uint64_t)tile_width * sizeof(*raster), tile_height)) {
|
||||
fprintf(stderr, "Error! TIFF tile dimension (%d x %d) is too large.\n",
|
||||
tile_width, tile_height);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
|
||||
if (samples_per_px > 3 && !TIFFGetField(tif, TIFFTAG_EXTRASAMPLES,
|
||||
&extra_samples, &extra_samples_ptr)) {
|
||||
fprintf(stderr, "Error! Cannot retrieve TIFF ExtraSamples info.\n");
|
||||
goto End;
|
||||
}
|
||||
|
||||
// _Tiffmalloc uses a signed type for size.
|
||||
alloc_size = (int64_t)(stride * image_height);
|
||||
if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End;
|
||||
|
||||
raster = (uint32_t*)_TIFFmalloc((tsize_t)alloc_size);
|
||||
if (raster != NULL) {
|
||||
if (TIFFReadRGBAImageOriented(tif, image_width, image_height, raster,
|
||||
ORIENTATION_TOPLEFT, 1)) {
|
||||
pic->width = image_width;
|
||||
pic->height = image_height;
|
||||
// TIFF data is ABGR
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
TIFFSwabArrayOfLong(raster, image_width * image_height);
|
||||
#endif
|
||||
// if we have an alpha channel, we must un-multiply from rgbA to RGBA
|
||||
if (extra_samples == 1 && extra_samples_ptr != NULL &&
|
||||
extra_samples_ptr[0] == EXTRASAMPLE_ASSOCALPHA) {
|
||||
uint32_t y;
|
||||
uint8_t* tmp = (uint8_t*)raster;
|
||||
for (y = 0; y < image_height; ++y) {
|
||||
MultARGBRow(tmp, image_width);
|
||||
tmp += stride;
|
||||
}
|
||||
}
|
||||
ok = keep_alpha
|
||||
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, (int)stride)
|
||||
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, (int)stride);
|
||||
}
|
||||
_TIFFfree(raster);
|
||||
} else {
|
||||
fprintf(stderr, "Error allocating TIFF RGBA memory!\n");
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
if (metadata != NULL) {
|
||||
ok = ExtractMetadataFromTIFF(tif, metadata);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Error extracting TIFF metadata!\n");
|
||||
MetadataFree(metadata);
|
||||
WebPPictureFree(pic);
|
||||
}
|
||||
}
|
||||
}
|
||||
End:
|
||||
TIFFClose(tif);
|
||||
return ok;
|
||||
}
|
||||
#else // !WEBP_HAVE_TIFF
|
||||
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr, "TIFF support not compiled. Please install the libtiff "
|
||||
"development package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
#endif // WEBP_HAVE_TIFF
|
||||
|
||||
// -----------------------------------------------------------------------------
|
37
imageio/tiffdec.h
Normal file
37
imageio/tiffdec.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// TIFF decode.
|
||||
|
||||
#ifndef WEBP_IMAGEIO_TIFFDEC_H_
|
||||
#define WEBP_IMAGEIO_TIFFDEC_H_
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Metadata;
|
||||
struct WebPPicture;
|
||||
|
||||
// Reads a TIFF from 'data', returning the decoded output in 'pic'.
|
||||
// Output is RGBA or YUVA, depending on pic->use_argb value.
|
||||
// If 'keep_alpha' is true and the TIFF has an alpha channel, the output is RGBA
|
||||
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
|
||||
// Returns true on success.
|
||||
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_TIFFDEC_H_
|
244
imageio/webpdec.c
Normal file
244
imageio/webpdec.c
Normal file
@ -0,0 +1,244 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// WebP decode.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include "./webpdec.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
#include "webp/encode.h"
|
||||
#include "../examples/unicode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebP decoding
|
||||
|
||||
static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = {
|
||||
"OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
|
||||
"UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
|
||||
};
|
||||
|
||||
static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
|
||||
if (config->input.has_animation) {
|
||||
fprintf(stderr,
|
||||
"Error! Decoding of an animated WebP file is not supported.\n"
|
||||
" Use webpmux to extract the individual frames or\n"
|
||||
" vwebp to view this image.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void PrintWebPError(const char* const in_file, int status) {
|
||||
WFPRINTF(stderr, "Decoding of %s failed.\n", (const W_CHAR*)in_file);
|
||||
fprintf(stderr, "Status: %d", status);
|
||||
if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
fprintf(stderr, "(%s)", kStatusMessages[status]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
int LoadWebP(const char* const in_file,
|
||||
const uint8_t** data, size_t* data_size,
|
||||
WebPBitstreamFeatures* bitstream) {
|
||||
VP8StatusCode status;
|
||||
WebPBitstreamFeatures local_features;
|
||||
if (!ImgIoUtilReadFile(in_file, data, data_size)) return 0;
|
||||
|
||||
if (bitstream == NULL) {
|
||||
bitstream = &local_features;
|
||||
}
|
||||
|
||||
status = WebPGetFeatures(*data, *data_size, bitstream);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
WebPFree((void*)*data);
|
||||
*data = NULL;
|
||||
*data_size = 0;
|
||||
PrintWebPError(in_file, status);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config) {
|
||||
if (config == NULL) return VP8_STATUS_INVALID_PARAM;
|
||||
PrintAnimationWarning(config);
|
||||
return WebPDecode(data, data_size, config);
|
||||
}
|
||||
|
||||
VP8StatusCode DecodeWebPIncremental(
|
||||
const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config) {
|
||||
VP8StatusCode status = VP8_STATUS_OK;
|
||||
if (config == NULL) return VP8_STATUS_INVALID_PARAM;
|
||||
|
||||
PrintAnimationWarning(config);
|
||||
|
||||
// Decoding call.
|
||||
{
|
||||
WebPIDecoder* const idec = WebPIDecode(data, data_size, config);
|
||||
if (idec == NULL) {
|
||||
fprintf(stderr, "Failed during WebPIDecode().\n");
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
} else {
|
||||
status = WebPIUpdate(idec, data, data_size);
|
||||
WebPIDelete(idec);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Metadata
|
||||
|
||||
static int ExtractMetadata(const uint8_t* const data, size_t data_size,
|
||||
Metadata* const metadata) {
|
||||
WebPData webp_data = { data, data_size };
|
||||
WebPDemuxer* const demux = WebPDemux(&webp_data);
|
||||
WebPChunkIterator chunk_iter;
|
||||
uint32_t flags;
|
||||
|
||||
if (demux == NULL) return 0;
|
||||
assert(metadata != NULL);
|
||||
|
||||
flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
|
||||
|
||||
if ((flags & ICCP_FLAG) && WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter)) {
|
||||
MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
|
||||
&metadata->iccp);
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
}
|
||||
if ((flags & EXIF_FLAG) && WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter)) {
|
||||
MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
|
||||
&metadata->exif);
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
}
|
||||
if ((flags & XMP_FLAG) && WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter)) {
|
||||
MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
|
||||
&metadata->xmp);
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
}
|
||||
WebPDemuxDelete(demux);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic,
|
||||
int keep_alpha, Metadata* const metadata) {
|
||||
int ok = 0;
|
||||
VP8StatusCode status = VP8_STATUS_OK;
|
||||
WebPDecoderConfig config;
|
||||
WebPDecBuffer* const output_buffer = &config.output;
|
||||
WebPBitstreamFeatures* const bitstream = &config.input;
|
||||
|
||||
if (data == NULL || data_size == 0 || pic == NULL) return 0;
|
||||
|
||||
if (!WebPInitDecoderConfig(&config)) {
|
||||
fprintf(stderr, "Library version mismatch!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = WebPGetFeatures(data, data_size, bitstream);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
PrintWebPError("input data", status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
const int has_alpha = keep_alpha && bitstream->has_alpha;
|
||||
uint64_t stride;
|
||||
pic->width = bitstream->width;
|
||||
pic->height = bitstream->height;
|
||||
if (pic->use_argb) {
|
||||
stride = (uint64_t)bitstream->width * 4;
|
||||
} else {
|
||||
stride = (uint64_t)bitstream->width * (has_alpha ? 5 : 3) / 2;
|
||||
pic->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420;
|
||||
}
|
||||
|
||||
if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, bitstream->height)) {
|
||||
status = VP8_STATUS_OUT_OF_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
ok = WebPPictureAlloc(pic);
|
||||
if (!ok) {
|
||||
status = VP8_STATUS_OUT_OF_MEMORY;
|
||||
break;
|
||||
}
|
||||
if (pic->use_argb) {
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
output_buffer->colorspace = MODE_ARGB;
|
||||
#else
|
||||
output_buffer->colorspace = MODE_BGRA;
|
||||
#endif
|
||||
output_buffer->u.RGBA.rgba = (uint8_t*)pic->argb;
|
||||
output_buffer->u.RGBA.stride = pic->argb_stride * sizeof(uint32_t);
|
||||
output_buffer->u.RGBA.size = output_buffer->u.RGBA.stride * pic->height;
|
||||
} else {
|
||||
output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV;
|
||||
output_buffer->u.YUVA.y = pic->y;
|
||||
output_buffer->u.YUVA.u = pic->u;
|
||||
output_buffer->u.YUVA.v = pic->v;
|
||||
output_buffer->u.YUVA.a = has_alpha ? pic->a : NULL;
|
||||
output_buffer->u.YUVA.y_stride = pic->y_stride;
|
||||
output_buffer->u.YUVA.u_stride = pic->uv_stride;
|
||||
output_buffer->u.YUVA.v_stride = pic->uv_stride;
|
||||
output_buffer->u.YUVA.a_stride = has_alpha ? pic->a_stride : 0;
|
||||
output_buffer->u.YUVA.y_size = pic->height * pic->y_stride;
|
||||
output_buffer->u.YUVA.u_size = (pic->height + 1) / 2 * pic->uv_stride;
|
||||
output_buffer->u.YUVA.v_size = (pic->height + 1) / 2 * pic->uv_stride;
|
||||
output_buffer->u.YUVA.a_size = pic->height * pic->a_stride;
|
||||
}
|
||||
output_buffer->is_external_memory = 1;
|
||||
|
||||
status = DecodeWebP(data, data_size, &config);
|
||||
ok = (status == VP8_STATUS_OK);
|
||||
if (ok && !keep_alpha && pic->use_argb) {
|
||||
// Need to wipe out the alpha value, as requested.
|
||||
int x, y;
|
||||
uint32_t* argb = pic->argb;
|
||||
for (y = 0; y < pic->height; ++y) {
|
||||
for (x = 0; x < pic->width; ++x) argb[x] |= 0xff000000u;
|
||||
argb += pic->argb_stride;
|
||||
}
|
||||
}
|
||||
} while (0); // <- so we can 'break' out of the loop
|
||||
|
||||
if (status != VP8_STATUS_OK) {
|
||||
PrintWebPError("input data", status);
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
WebPFreeDecBuffer(output_buffer);
|
||||
|
||||
if (ok && metadata != NULL) {
|
||||
ok = ExtractMetadata(data, data_size, metadata);
|
||||
if (!ok) {
|
||||
PrintWebPError("metadata", VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
}
|
||||
if (!ok) WebPPictureFree(pic);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
67
imageio/webpdec.h
Normal file
67
imageio/webpdec.h
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// WebP decode.
|
||||
|
||||
#ifndef WEBP_IMAGEIO_WEBPDEC_H_
|
||||
#define WEBP_IMAGEIO_WEBPDEC_H_
|
||||
|
||||
#include "webp/decode.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Metadata;
|
||||
struct WebPPicture;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebP decoding
|
||||
|
||||
// Prints an informative error message regarding decode failure of 'in_file'.
|
||||
// 'status' is treated as a VP8StatusCode and if valid will be printed as a
|
||||
// text string.
|
||||
void PrintWebPError(const char* const in_file, int status);
|
||||
|
||||
// Reads a WebP from 'in_file', returning the contents and size in 'data' and
|
||||
// 'data_size'. If not NULL, 'bitstream' is populated using WebPGetFeatures().
|
||||
// Returns true on success.
|
||||
int LoadWebP(const char* const in_file,
|
||||
const uint8_t** data, size_t* data_size,
|
||||
WebPBitstreamFeatures* bitstream);
|
||||
|
||||
// Decodes the WebP contained in 'data'.
|
||||
// 'config' is a structure previously initialized by WebPInitDecoderConfig().
|
||||
// 'config->output' should have the desired colorspace selected.
|
||||
// Returns the decoder status. On success 'config->output' will contain the
|
||||
// decoded picture.
|
||||
VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config);
|
||||
|
||||
// Same as DecodeWebP(), but using the incremental decoder.
|
||||
VP8StatusCode DecodeWebPIncremental(
|
||||
const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Decodes a WebP contained in 'data', returning the decoded output in 'pic'.
|
||||
// Output is RGBA or YUVA, depending on pic->use_argb value.
|
||||
// If 'keep_alpha' is true and the WebP has an alpha channel, the output is RGBA
|
||||
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
|
||||
// Returns true on success.
|
||||
int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_WEBPDEC_H_
|
413
imageio/wicdec.c
Normal file
413
imageio/wicdec.c
Normal file
@ -0,0 +1,413 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Windows Imaging Component (WIC) decode.
|
||||
|
||||
#include "./wicdec.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
#ifdef __MINGW32__
|
||||
#define INITGUID // Without this GUIDs are declared extern and fail to link
|
||||
#endif
|
||||
#define CINTERFACE
|
||||
#define COBJMACROS
|
||||
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||
// code with COBJMACROS.
|
||||
#include <ole2.h> // CreateStreamOnHGlobal()
|
||||
#include <shlwapi.h>
|
||||
#include <tchar.h>
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
|
||||
#include "../examples/unicode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
#include "webp/encode.h"
|
||||
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
if (SUCCEEDED(hr)) { \
|
||||
hr = (fn); \
|
||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// modified version of DEFINE_GUID from guiddef.h.
|
||||
#define WEBP_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
static const GUID name = \
|
||||
{ l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define MAKE_REFGUID(x) (x)
|
||||
#else
|
||||
#define MAKE_REFGUID(x) &(x)
|
||||
#endif
|
||||
|
||||
typedef struct WICFormatImporter {
|
||||
const GUID* pixel_format;
|
||||
int bytes_per_pixel;
|
||||
int (*import)(WebPPicture* const, const uint8_t* const, int);
|
||||
} WICFormatImporter;
|
||||
|
||||
// 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);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_,
|
||||
0x1562ff7c, 0xd352, 0x46f9,
|
||||
0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_,
|
||||
0x6fddc324, 0x4e03, 0x4bfe,
|
||||
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16);
|
||||
|
||||
static HRESULT OpenInputStream(const char* filename, IStream** stream) {
|
||||
HRESULT hr = S_OK;
|
||||
if (!WSTRCMP(filename, "-")) {
|
||||
const uint8_t* data = NULL;
|
||||
size_t data_size = 0;
|
||||
const int ok = ImgIoUtilReadFile(filename, &data, &data_size);
|
||||
if (ok) {
|
||||
HGLOBAL image = GlobalAlloc(GMEM_MOVEABLE, data_size);
|
||||
if (image != NULL) {
|
||||
void* const image_mem = GlobalLock(image);
|
||||
if (image_mem != NULL) {
|
||||
memcpy(image_mem, data, data_size);
|
||||
GlobalUnlock(image);
|
||||
IFS(CreateStreamOnHGlobal(image, TRUE, stream));
|
||||
} else {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
} else {
|
||||
hr = E_OUTOFMEMORY;
|
||||
}
|
||||
free((void*)data);
|
||||
} else {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
} else {
|
||||
IFS(SHCreateStreamOnFile((const LPTSTR)filename, STGM_READ, stream));
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
_ftprintf(stderr, _T("Error opening input file %s (%08lx)\n"),
|
||||
(const LPTSTR)filename, hr);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Metadata processing
|
||||
|
||||
// Stores the first non-zero sized color profile from 'frame' to 'iccp'.
|
||||
// Returns an HRESULT to indicate success or failure. The caller is responsible
|
||||
// for freeing 'iccp->bytes' in either case.
|
||||
static HRESULT ExtractICCP(IWICImagingFactory* const factory,
|
||||
IWICBitmapFrameDecode* const frame,
|
||||
MetadataPayload* const iccp) {
|
||||
HRESULT hr = S_OK;
|
||||
UINT i, count;
|
||||
IWICColorContext** color_contexts;
|
||||
|
||||
IFS(IWICBitmapFrameDecode_GetColorContexts(frame, 0, NULL, &count));
|
||||
if (FAILED(hr) || count == 0) {
|
||||
// Treat unsupported operation as a non-fatal error. See crbug.com/webp/506.
|
||||
return (hr == WINCODEC_ERR_UNSUPPORTEDOPERATION) ? S_OK : hr;
|
||||
}
|
||||
|
||||
color_contexts = (IWICColorContext**)calloc(count, sizeof(*color_contexts));
|
||||
if (color_contexts == NULL) return E_OUTOFMEMORY;
|
||||
for (i = 0; SUCCEEDED(hr) && i < count; ++i) {
|
||||
IFS(IWICImagingFactory_CreateColorContext(factory, &color_contexts[i]));
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
UINT num_color_contexts;
|
||||
IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
|
||||
count, color_contexts,
|
||||
&num_color_contexts));
|
||||
assert(FAILED(hr) || num_color_contexts <= count);
|
||||
for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
|
||||
WICColorContextType type;
|
||||
IFS(IWICColorContext_GetType(color_contexts[i], &type));
|
||||
if (SUCCEEDED(hr) && type == WICColorContextProfile) {
|
||||
UINT size;
|
||||
IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
|
||||
0, NULL, &size));
|
||||
if (SUCCEEDED(hr) && size > 0) {
|
||||
iccp->bytes = (uint8_t*)malloc(size);
|
||||
if (iccp->bytes == NULL) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
break;
|
||||
}
|
||||
iccp->size = size;
|
||||
IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
|
||||
(UINT)iccp->size, iccp->bytes,
|
||||
&size));
|
||||
if (SUCCEEDED(hr) && size != iccp->size) {
|
||||
fprintf(stderr, "Warning! ICC profile size (%u) != expected (%u)\n",
|
||||
size, (uint32_t)iccp->size);
|
||||
iccp->size = size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (color_contexts[i] != NULL) IUnknown_Release(color_contexts[i]);
|
||||
}
|
||||
free(color_contexts);
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT ExtractMetadata(IWICImagingFactory* const factory,
|
||||
IWICBitmapFrameDecode* const frame,
|
||||
Metadata* const metadata) {
|
||||
// TODO(jzern): add XMP/EXIF extraction.
|
||||
const HRESULT hr = ExtractICCP(factory, frame, &metadata->iccp);
|
||||
if (FAILED(hr)) MetadataFree(metadata);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static int HasPalette(GUID pixel_format) {
|
||||
return (IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||
MAKE_REFGUID(GUID_WICPixelFormat1bppIndexed)) ||
|
||||
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||
MAKE_REFGUID(GUID_WICPixelFormat2bppIndexed)) ||
|
||||
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||
MAKE_REFGUID(GUID_WICPixelFormat4bppIndexed)) ||
|
||||
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||
MAKE_REFGUID(GUID_WICPixelFormat8bppIndexed)));
|
||||
}
|
||||
|
||||
static int HasAlpha(IWICImagingFactory* const factory,
|
||||
IWICBitmapDecoder* const decoder,
|
||||
IWICBitmapFrameDecode* const frame,
|
||||
GUID pixel_format) {
|
||||
int has_alpha;
|
||||
if (HasPalette(pixel_format)) {
|
||||
IWICPalette* frame_palette = NULL;
|
||||
IWICPalette* global_palette = NULL;
|
||||
BOOL frame_palette_has_alpha = FALSE;
|
||||
BOOL global_palette_has_alpha = FALSE;
|
||||
|
||||
// A palette may exist at the frame or container level,
|
||||
// check IWICPalette::HasAlpha() for both if present.
|
||||
if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &frame_palette)) &&
|
||||
SUCCEEDED(IWICBitmapFrameDecode_CopyPalette(frame, frame_palette))) {
|
||||
IWICPalette_HasAlpha(frame_palette, &frame_palette_has_alpha);
|
||||
}
|
||||
if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &global_palette)) &&
|
||||
SUCCEEDED(IWICBitmapDecoder_CopyPalette(decoder, global_palette))) {
|
||||
IWICPalette_HasAlpha(global_palette, &global_palette_has_alpha);
|
||||
}
|
||||
has_alpha = frame_palette_has_alpha || global_palette_has_alpha;
|
||||
|
||||
if (frame_palette != NULL) IUnknown_Release(frame_palette);
|
||||
if (global_palette != NULL) IUnknown_Release(global_palette);
|
||||
} else {
|
||||
has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||
MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
|
||||
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||
MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_)) ||
|
||||
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||
MAKE_REFGUID(GUID_WICPixelFormat64bppRGBA_)) ||
|
||||
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||
MAKE_REFGUID(GUID_WICPixelFormat64bppBGRA_));
|
||||
}
|
||||
return has_alpha;
|
||||
}
|
||||
|
||||
int ReadPictureWithWIC(const char* const filename,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
Metadata* const metadata) {
|
||||
// From Microsoft SDK 6.0a -- ks.h
|
||||
// Define a local copy to avoid link errors under mingw.
|
||||
WEBP_DEFINE_GUID(GUID_NULL_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
static const WICFormatImporter kAlphaFormatImporters[] = {
|
||||
{ &GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA },
|
||||
{ &GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA },
|
||||
{ NULL, 0, NULL },
|
||||
};
|
||||
static const WICFormatImporter kNonAlphaFormatImporters[] = {
|
||||
{ &GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR },
|
||||
{ &GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB },
|
||||
{ NULL, 0, NULL },
|
||||
};
|
||||
HRESULT hr = S_OK;
|
||||
IWICBitmapFrameDecode* frame = NULL;
|
||||
IWICFormatConverter* converter = NULL;
|
||||
IWICImagingFactory* factory = NULL;
|
||||
IWICBitmapDecoder* decoder = NULL;
|
||||
IStream* stream = NULL;
|
||||
UINT frame_count = 0;
|
||||
UINT width = 0, height = 0;
|
||||
BYTE* rgb = NULL;
|
||||
WICPixelFormatGUID src_pixel_format = GUID_WICPixelFormatUndefined;
|
||||
const WICFormatImporter* importer = NULL;
|
||||
GUID src_container_format = GUID_NULL_;
|
||||
// From Windows Kits\10\Include\10.0.19041.0\um\wincodec.h
|
||||
WEBP_DEFINE_GUID(GUID_ContainerFormatWebp_,
|
||||
0xe094b0e2, 0x67f2, 0x45b3,
|
||||
0xb0, 0xea, 0x11, 0x53, 0x37, 0xca, 0x7c, 0xf3);
|
||||
static const GUID* kAlphaContainers[] = {
|
||||
&GUID_ContainerFormatBmp,
|
||||
&GUID_ContainerFormatPng,
|
||||
&GUID_ContainerFormatTiff,
|
||||
&GUID_ContainerFormatWebp_,
|
||||
NULL
|
||||
};
|
||||
int has_alpha = 0;
|
||||
int64_t stride;
|
||||
|
||||
if (filename == NULL || pic == NULL) return 0;
|
||||
|
||||
IFS(CoInitialize(NULL));
|
||||
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
MAKE_REFGUID(IID_IWICImagingFactory),
|
||||
(LPVOID*)&factory));
|
||||
if (hr == REGDB_E_CLASSNOTREG) {
|
||||
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");
|
||||
}
|
||||
// Prepare for image decoding.
|
||||
IFS(OpenInputStream(filename, &stream));
|
||||
IFS(IWICImagingFactory_CreateDecoderFromStream(
|
||||
factory, stream, NULL,
|
||||
WICDecodeMetadataCacheOnDemand, &decoder));
|
||||
IFS(IWICBitmapDecoder_GetFrameCount(decoder, &frame_count));
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (frame_count == 0) {
|
||||
fprintf(stderr, "No frame found in input file.\n");
|
||||
hr = E_FAIL;
|
||||
} else if (frame_count > 1) {
|
||||
// WIC will be tried before native WebP decoding so avoid duplicating the
|
||||
// error message.
|
||||
hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
IFS(IWICBitmapDecoder_GetFrame(decoder, 0, &frame));
|
||||
IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format));
|
||||
IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format));
|
||||
|
||||
if (SUCCEEDED(hr) && keep_alpha) {
|
||||
const GUID** guid;
|
||||
for (guid = kAlphaContainers; *guid != NULL; ++guid) {
|
||||
if (IsEqualGUID(MAKE_REFGUID(src_container_format),
|
||||
MAKE_REFGUID(**guid))) {
|
||||
has_alpha = HasAlpha(factory, decoder, frame, src_pixel_format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for pixel format conversion (if necessary).
|
||||
IFS(IWICImagingFactory_CreateFormatConverter(factory, &converter));
|
||||
|
||||
for (importer = has_alpha ? kAlphaFormatImporters : kNonAlphaFormatImporters;
|
||||
hr == S_OK && importer->import != NULL; ++importer) {
|
||||
BOOL can_convert;
|
||||
const HRESULT cchr = IWICFormatConverter_CanConvert(
|
||||
converter,
|
||||
MAKE_REFGUID(src_pixel_format),
|
||||
MAKE_REFGUID(*importer->pixel_format),
|
||||
&can_convert);
|
||||
if (SUCCEEDED(cchr) && can_convert) break;
|
||||
}
|
||||
if (importer->import == NULL) hr = E_FAIL;
|
||||
|
||||
IFS(IWICFormatConverter_Initialize(converter, (IWICBitmapSource*)frame,
|
||||
importer->pixel_format,
|
||||
WICBitmapDitherTypeNone,
|
||||
NULL, 0.0, WICBitmapPaletteTypeCustom));
|
||||
|
||||
// Decode.
|
||||
IFS(IWICFormatConverter_GetSize(converter, &width, &height));
|
||||
stride = (int64_t)importer->bytes_per_pixel * width * sizeof(*rgb);
|
||||
if (stride != (int)stride ||
|
||||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
rgb = (BYTE*)malloc((size_t)stride * height);
|
||||
if (rgb == NULL)
|
||||
hr = E_OUTOFMEMORY;
|
||||
}
|
||||
IFS(IWICFormatConverter_CopyPixels(converter, NULL,
|
||||
(UINT)stride, (UINT)stride * height, rgb));
|
||||
|
||||
// WebP conversion.
|
||||
if (SUCCEEDED(hr)) {
|
||||
int ok;
|
||||
pic->width = width;
|
||||
pic->height = height;
|
||||
pic->use_argb = 1; // For WIC, we always force to argb
|
||||
ok = importer->import(pic, rgb, (int)stride);
|
||||
if (!ok) hr = E_FAIL;
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (metadata != NULL) {
|
||||
hr = ExtractMetadata(factory, frame, metadata);
|
||||
if (FAILED(hr)) {
|
||||
fprintf(stderr, "Error extracting image metadata using WIC!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
if (converter != NULL) IUnknown_Release(converter);
|
||||
if (frame != NULL) IUnknown_Release(frame);
|
||||
if (decoder != NULL) IUnknown_Release(decoder);
|
||||
if (factory != NULL) IUnknown_Release(factory);
|
||||
if (stream != NULL) IUnknown_Release(stream);
|
||||
free(rgb);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
#else // !HAVE_WINCODEC_H
|
||||
int ReadPictureWithWIC(const char* const filename,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
(void)filename;
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr, "Windows Imaging Component (WIC) support not compiled. "
|
||||
"Visual Studio and mingw-w64 builds support WIC. Make sure "
|
||||
"wincodec.h detection is working correctly if using autoconf "
|
||||
"and HAVE_WINCODEC_H is defined before building.\n");
|
||||
return 0;
|
||||
}
|
||||
#endif // HAVE_WINCODEC_H
|
||||
|
||||
// -----------------------------------------------------------------------------
|
34
imageio/wicdec.h
Normal file
34
imageio/wicdec.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Windows Imaging Component (WIC) decode.
|
||||
|
||||
#ifndef WEBP_IMAGEIO_WICDEC_H_
|
||||
#define WEBP_IMAGEIO_WICDEC_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Metadata;
|
||||
struct WebPPicture;
|
||||
|
||||
// Reads an image from 'filename', returning the decoded output in 'pic'.
|
||||
// If 'keep_alpha' is true and the image has an alpha channel, the output is
|
||||
// RGBA otherwise it will be RGB. pic->use_argb is always forced to true.
|
||||
// Returns true on success.
|
||||
int ReadPictureWithWIC(const char* const filename,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_WICDEC_H_
|
Reference in New Issue
Block a user