add webp metadata write
untested, but it does compile
This commit is contained in:
parent
47222bc1f5
commit
b56b888bf0
@ -6,7 +6,7 @@
|
|||||||
- better vipsheader behaviour with complex field types
|
- better vipsheader behaviour with complex field types
|
||||||
- added vips_image_hasalpha()
|
- added vips_image_hasalpha()
|
||||||
- added vips_thumbnail() / vips_thumbnail_buffer()
|
- added vips_thumbnail() / vips_thumbnail_buffer()
|
||||||
- webpload reads icc, xmp, exif metadata
|
- webpload/webpsave read and write icc, xmp, exif metadata
|
||||||
|
|
||||||
18/10/16 started 8.4.3
|
18/10/16 started 8.4.3
|
||||||
- fix error detection in gif_close, thanks aaron42net
|
- fix error detection in gif_close, thanks aaron42net
|
||||||
|
12
TODO
12
TODO
@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
test with ubuntu ... seems to have libwebpmux2
|
test with ubuntu ... seems to have libwebpmux2
|
||||||
|
|
||||||
|
test webp metadata read and write
|
||||||
|
|
||||||
|
webpsave should support strip
|
||||||
|
|
||||||
|
update docs
|
||||||
|
|
||||||
|
need to check for >4gb overflow ... use guint64 for all size calculations
|
||||||
|
|
||||||
|
webpsave needs hooking up to exif.c
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- not sure about utf8 error messages on win
|
- not sure about utf8 error messages on win
|
||||||
|
|
||||||
- strange:
|
- strange:
|
||||||
|
@ -196,9 +196,14 @@ int vips__png_write_buf( VipsImage *in,
|
|||||||
typedef struct _VipsWebPNames {
|
typedef struct _VipsWebPNames {
|
||||||
const char *vips;
|
const char *vips;
|
||||||
const char *webp;
|
const char *webp;
|
||||||
|
|
||||||
|
/* The webp flag bit for this chunk of metadata
|
||||||
|
*/
|
||||||
|
int flag;
|
||||||
} VipsWebPNames;
|
} VipsWebPNames;
|
||||||
|
|
||||||
extern const VipsWebPNames vips__webp_names[];
|
extern const VipsWebPNames vips__webp_names[];
|
||||||
|
extern const int vips__n_webp_names;
|
||||||
extern const char *vips__webp_suffs[];
|
extern const char *vips__webp_suffs[];
|
||||||
|
|
||||||
int vips__iswebp_buffer( const void *buf, size_t len );
|
int vips__iswebp_buffer( const void *buf, size_t len );
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
* - buffer write ignored lossless, thanks aaron42net
|
* - buffer write ignored lossless, thanks aaron42net
|
||||||
* 2/5/16 Felix Bünemann
|
* 2/5/16 Felix Bünemann
|
||||||
* - used advanced encoding API, expose controls
|
* - used advanced encoding API, expose controls
|
||||||
|
* 8/11/16
|
||||||
|
* - add metadata write
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -89,26 +91,22 @@ typedef struct {
|
|||||||
uint8_t *mem;
|
uint8_t *mem;
|
||||||
size_t size;
|
size_t size;
|
||||||
size_t max_size;
|
size_t max_size;
|
||||||
} VipsWebPMemoryWriter;
|
} VipsWebPWriter;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
init_memory_writer( VipsWebPMemoryWriter *writer ) {
|
vips_webp_writer_init( VipsWebPWriter *writer )
|
||||||
|
{
|
||||||
writer->mem = NULL;
|
writer->mem = NULL;
|
||||||
writer->size = 0;
|
writer->size = 0;
|
||||||
writer->max_size = 0;
|
writer->max_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
memory_write( const uint8_t *data, size_t data_size,
|
vips_webp_writer_append( VipsWebPWriter *writer,
|
||||||
const WebPPicture *picture ) {
|
const uint8_t *data, size_t data_size )
|
||||||
VipsWebPMemoryWriter * const writer =
|
{
|
||||||
(VipsWebPMemoryWriter*) picture->custom_ptr;
|
|
||||||
|
|
||||||
size_t next_size;
|
size_t next_size;
|
||||||
|
|
||||||
if( !writer )
|
|
||||||
return( 0 );
|
|
||||||
|
|
||||||
next_size = writer->size + data_size;
|
next_size = writer->size + data_size;
|
||||||
|
|
||||||
if( next_size > writer->max_size ) {
|
if( next_size > writer->max_size ) {
|
||||||
@ -117,13 +115,10 @@ memory_write( const uint8_t *data, size_t data_size,
|
|||||||
VIPS_MAX( 8192, VIPS_MAX( next_size,
|
VIPS_MAX( 8192, VIPS_MAX( next_size,
|
||||||
writer->max_size * 2 ) );
|
writer->max_size * 2 ) );
|
||||||
|
|
||||||
if( !(new_mem = (uint8_t*) g_try_malloc( next_max_size )) )
|
if( !(new_mem = (uint8_t *)
|
||||||
|
g_try_realloc( writer->mem, next_max_size )) )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
if( writer->size > 0 )
|
|
||||||
memcpy( new_mem, writer->mem, writer->size );
|
|
||||||
|
|
||||||
g_free( writer->mem );
|
|
||||||
writer->mem = new_mem;
|
writer->mem = new_mem;
|
||||||
writer->max_size = next_max_size;
|
writer->max_size = next_max_size;
|
||||||
}
|
}
|
||||||
@ -136,6 +131,78 @@ memory_write( const uint8_t *data, size_t data_size,
|
|||||||
return( 1 );
|
return( 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_webp_writer_appendle( VipsWebPWriter *writer, uint32_t val, int n )
|
||||||
|
{
|
||||||
|
unsigned char buf[4];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
g_assert( n < 4 );
|
||||||
|
|
||||||
|
for( i = 0; i < n; i++ ) {
|
||||||
|
buf[i] = (unsigned char) (val & 0xff);
|
||||||
|
val >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return( vips_webp_writer_append( writer, buf, n ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_webp_writer_appendle32( VipsWebPWriter *writer, uint32_t val )
|
||||||
|
{
|
||||||
|
return( vips_webp_writer_appendle( writer, val, 4 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_webp_writer_appendle24( VipsWebPWriter *writer, uint32_t val )
|
||||||
|
{
|
||||||
|
return( vips_webp_writer_appendle( writer, val, 3 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_webp_writer_appendcc( VipsWebPWriter *writer, const char buf[4] )
|
||||||
|
{
|
||||||
|
return( vips_webp_writer_append( writer, (const uint8_t *) buf, 4 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
vips_webp_writer_appendc( VipsWebPWriter *writer,
|
||||||
|
const char fourcc[4], const uint8_t *data, size_t data_size )
|
||||||
|
{
|
||||||
|
const int zero = 0;
|
||||||
|
gboolean need_padding = (data_size & 1) != 0;
|
||||||
|
|
||||||
|
if( !vips_webp_writer_appendcc( writer, fourcc ) ||
|
||||||
|
!vips_webp_writer_appendle32( writer, data_size ) ||
|
||||||
|
!vips_webp_writer_append( writer, data, data_size ) )
|
||||||
|
return( 0 );
|
||||||
|
|
||||||
|
if( need_padding &&
|
||||||
|
!vips_webp_writer_append( writer, (const uint8_t *) &zero, 1 ) )
|
||||||
|
return( 0 );
|
||||||
|
|
||||||
|
return( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_webp_writer_unset( VipsWebPWriter *writer )
|
||||||
|
{
|
||||||
|
VIPS_FREE( writer->mem );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
memory_write( const uint8_t *data, size_t data_size,
|
||||||
|
const WebPPicture *picture )
|
||||||
|
{
|
||||||
|
VipsWebPWriter * const writer =
|
||||||
|
(VipsWebPWriter *) picture->custom_ptr;
|
||||||
|
|
||||||
|
if( !writer )
|
||||||
|
return( 0 );
|
||||||
|
|
||||||
|
return( vips_webp_writer_append( writer, data, data_size ) );
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
write_webp( WebPPicture *pic, VipsImage *in,
|
write_webp( WebPPicture *pic, VipsImage *in,
|
||||||
int Q, gboolean lossless, VipsForeignWebpPreset preset,
|
int Q, gboolean lossless, VipsForeignWebpPreset preset,
|
||||||
@ -147,25 +214,23 @@ write_webp( WebPPicture *pic, VipsImage *in,
|
|||||||
webp_import import;
|
webp_import import;
|
||||||
|
|
||||||
if ( !WebPConfigPreset( &config, get_preset( preset ), Q ) ) {
|
if ( !WebPConfigPreset( &config, get_preset( preset ), Q ) ) {
|
||||||
vips_error( "vips2webp",
|
vips_error( "vips2webp", "%s", _( "config version error" ) );
|
||||||
"%s", _( "config version error" ) );
|
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
|
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
|
||||||
config.lossless = lossless || near_lossless;
|
config.lossless = lossless || near_lossless;
|
||||||
config.alpha_quality = alpha_q;
|
config.alpha_quality = alpha_q;
|
||||||
/* Smart subsampling requires use_argb because
|
/* Smart subsampling needs use_argb because
|
||||||
* it is applied during RGB to YUV conversion.
|
* it is applied during RGB to YUV conversion.
|
||||||
*/
|
*/
|
||||||
pic->use_argb = lossless || near_lossless || smart_subsample;
|
pic->use_argb = lossless || near_lossless || smart_subsample;
|
||||||
#else
|
#else
|
||||||
if( lossless || near_lossless )
|
if( lossless ||
|
||||||
vips_warn( "vips2webp",
|
near_lossless )
|
||||||
"%s", _( "lossless unsupported" ) );
|
vips_warn( "vips2webp", "%s", _( "lossless unsupported" ) );
|
||||||
if( alpha_q != 100 )
|
if( alpha_q != 100 )
|
||||||
vips_warn( "vips2webp",
|
vips_warn( "vips2webp", "%s", _( "alpha_q unsupported" ) );
|
||||||
"%s", _( "alpha_q unsupported" ) );
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
|
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
|
||||||
@ -175,16 +240,14 @@ write_webp( WebPPicture *pic, VipsImage *in,
|
|||||||
config.preprocessing |= 4;
|
config.preprocessing |= 4;
|
||||||
#else
|
#else
|
||||||
if( near_lossless )
|
if( near_lossless )
|
||||||
vips_warn( "vips2webp",
|
vips_warn( "vips2webp", "%s", _( "near_lossless unsupported" ) );
|
||||||
"%s", _( "near_lossless unsupported" ) );
|
|
||||||
if( smart_subsample )
|
if( smart_subsample )
|
||||||
vips_warn( "vips2webp",
|
vips_warn( "vips2webp",
|
||||||
"%s", _( "smart_subsample unsupported" ) );
|
"%s", _( "smart_subsample unsupported" ) );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if( !WebPValidateConfig( &config ) ) {
|
if( !WebPValidateConfig( &config ) ) {
|
||||||
vips_error( "vips2webp",
|
vips_error( "vips2webp", "%s", _( "invalid configuration" ) );
|
||||||
"%s", _( "invalid configuration" ) );
|
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,15 +265,13 @@ write_webp( WebPPicture *pic, VipsImage *in,
|
|||||||
if( !import( pic, VIPS_IMAGE_ADDR( memory, 0, 0 ),
|
if( !import( pic, VIPS_IMAGE_ADDR( memory, 0, 0 ),
|
||||||
VIPS_IMAGE_SIZEOF_LINE( memory ) ) ) {
|
VIPS_IMAGE_SIZEOF_LINE( memory ) ) ) {
|
||||||
VIPS_UNREF( memory );
|
VIPS_UNREF( memory );
|
||||||
vips_error( "vips2webp",
|
vips_error( "vips2webp", "%s", _( "picture memory error" ) );
|
||||||
"%s", _( "picture memory error" ) );
|
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !WebPEncode( &config, pic ) ) {
|
if( !WebPEncode( &config, pic ) ) {
|
||||||
VIPS_UNREF( memory );
|
VIPS_UNREF( memory );
|
||||||
vips_error( "vips2webp",
|
vips_error( "vips2webp", "%s", _( "unable to encode" ) );
|
||||||
"%s", _( "unable to encode" ) );
|
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,6 +280,196 @@ write_webp( WebPPicture *pic, VipsImage *in,
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_webp_add_chunk( VipsWebPWriter *writer, VipsImage *image,
|
||||||
|
const char *vips, const char webp[4] )
|
||||||
|
{
|
||||||
|
if( vips_image_get_typeof( image, vips) ) {
|
||||||
|
void *data;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
/* We've done this before, it can't fail now.
|
||||||
|
*/
|
||||||
|
(void) vips_image_get_blob( image, vips, &data, &length );
|
||||||
|
|
||||||
|
if( !vips_webp_writer_appendc( writer, webp, data, length ) )
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn @writer into a VP8X image with metadata from @image.
|
||||||
|
*
|
||||||
|
* Based (partly) on cwep.c
|
||||||
|
*
|
||||||
|
* We need to know some detail about the webp file format. The encoder will
|
||||||
|
* make something like this:
|
||||||
|
*
|
||||||
|
* 0 - 3 RIFF
|
||||||
|
* 4 - 7 size of this data chunk (byte 8 to end of file)
|
||||||
|
* 8 - 11 WEBP
|
||||||
|
* 12 - 15 VP8L (L for lossless, space for lossy, possibly 8 for alpha)
|
||||||
|
* 16 - 19 size of chunk (10 for VP8X, something else otherwise)
|
||||||
|
* 20 - 23 flags (VP8L has alpha in bit 29 of this; byte 20 has flags for VP8X)
|
||||||
|
* 24 -
|
||||||
|
*
|
||||||
|
* If there is metadata to add, we make a VP8X image, which looks like this:
|
||||||
|
*
|
||||||
|
* 0 - 3 RIFF
|
||||||
|
* 4 - 7 size of this data chunk (byte 8 to end of file)
|
||||||
|
* 8 - 11 WEBP
|
||||||
|
* 12 - 15 VP8X
|
||||||
|
* 16 - 19 10 (size of vp8x chunk)
|
||||||
|
* 20 - 23 flags
|
||||||
|
* 24 - 26 width - 1 (note: only 3 bytes)
|
||||||
|
* 27 - 29 height - 1 (note: only 3 bytes)
|
||||||
|
* 30 -
|
||||||
|
*
|
||||||
|
* Followed by ICCP, ANIM, image, EXIF and XMP chunks, in that order.
|
||||||
|
*
|
||||||
|
* See:
|
||||||
|
* https://developers.google.com/speed/webp/docs/riff_container
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
vips_webp_add_metadata( VipsWebPWriter *writer, VipsImage *image )
|
||||||
|
{
|
||||||
|
/* The image in @writer may be VP8X already.
|
||||||
|
*/
|
||||||
|
gboolean is_vp8x = !memcmp( writer->mem + 12, "VP8X", 4 );
|
||||||
|
gboolean is_lossless = !memcmp( writer->mem + 12, "VP8L", 4 );
|
||||||
|
|
||||||
|
guint64 metadata_size;
|
||||||
|
uint32_t flags;
|
||||||
|
int i;
|
||||||
|
guint64 new_size;
|
||||||
|
VipsWebPWriter new;
|
||||||
|
|
||||||
|
/* We have to find the size of the block we will write before we can
|
||||||
|
* start to write it.
|
||||||
|
*/
|
||||||
|
metadata_size = 0;
|
||||||
|
|
||||||
|
/* If there are any flags there already, we add to them.
|
||||||
|
*/
|
||||||
|
flags = is_vp8x ? writer->mem[20] : 0;
|
||||||
|
|
||||||
|
for( i = 0; i < vips__n_webp_names; i++ ) {
|
||||||
|
const char *vips = vips__webp_names[i].vips;
|
||||||
|
uint32_t flag = vips__webp_names[i].flag;
|
||||||
|
|
||||||
|
if( vips_image_get_typeof( image, vips ) ) {
|
||||||
|
void *data;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
if( vips_image_get_blob( image, vips, &data, &length ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* +8 since we have to prepend each chunk with a type
|
||||||
|
* char[4] and a length guint32.
|
||||||
|
*
|
||||||
|
* Chunks are always rounded up to an even size.
|
||||||
|
*/
|
||||||
|
metadata_size += length + 8 + (length & 1);
|
||||||
|
|
||||||
|
flags |= flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !metadata_size )
|
||||||
|
/* No metadata to write, so we can just leave the image alone.
|
||||||
|
*/
|
||||||
|
return( 0 );
|
||||||
|
|
||||||
|
/* If it's not already vp8x, we'll need to add a vp8x header, and
|
||||||
|
* that'll add 18 bytes. -8 since size includes the RIFF header.
|
||||||
|
*/
|
||||||
|
new_size = writer->size - 8 + (is_vp8x ? 0 : 18) + metadata_size;
|
||||||
|
|
||||||
|
vips_webp_writer_init( &new );
|
||||||
|
|
||||||
|
if( !vips_webp_writer_appendcc( &new, "RIFF" ) ||
|
||||||
|
!vips_webp_writer_appendle32( &new, new_size ) ||
|
||||||
|
!vips_webp_writer_appendcc( &new, "WEBP" ) ||
|
||||||
|
!vips_webp_writer_appendcc( &new, "VP8X" ) ||
|
||||||
|
!vips_webp_writer_appendle32( &new, 10 ) ) {
|
||||||
|
vips_webp_writer_unset( &new );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( is_vp8x ) {
|
||||||
|
/* Copy the existing VP8X body and update the flag bits.
|
||||||
|
*/
|
||||||
|
if( vips_webp_writer_append( &new, writer->mem + 20, 10 ) ) {
|
||||||
|
vips_webp_writer_unset( &new );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
new.mem[20] = flags;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* We have to make a new vp8x header.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Presence of alpha is stored in the 29th bit of VP8L
|
||||||
|
* data.
|
||||||
|
*
|
||||||
|
* +12 gets us to the VP8L cc, 4 to skip that,
|
||||||
|
* another 4 to skip the length, then bit 8 - 3 == 5
|
||||||
|
*/
|
||||||
|
if( is_lossless &&
|
||||||
|
(writer->mem[12 + 8 + 3] & (1 << 5)) )
|
||||||
|
flags |= 0x010;
|
||||||
|
|
||||||
|
/* 10 is the length of the VPX8X header chunk.
|
||||||
|
*/
|
||||||
|
if( !vips_webp_writer_appendle32( &new, flags ) ||
|
||||||
|
!vips_webp_writer_appendle24( &new, image->Xsize - 1 ) ||
|
||||||
|
!vips_webp_writer_appendle24( &new, image->Ysize- 1 ) ) {
|
||||||
|
vips_webp_writer_unset( &new );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extra chunks have to be in this order.
|
||||||
|
*/
|
||||||
|
if( vips_webp_add_chunk( &new, image, VIPS_META_ICC_NAME, "ICCP" ) ) {
|
||||||
|
vips_webp_writer_unset( &new );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The image chunk must come here.
|
||||||
|
*/
|
||||||
|
if( is_vp8x ) {
|
||||||
|
if( !vips_webp_writer_append( &new,
|
||||||
|
writer->mem + 30, writer->size - 30 ) ) {
|
||||||
|
vips_webp_writer_unset( &new );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if( !vips_webp_writer_append( &new,
|
||||||
|
writer->mem + 12, writer->size - 12 ) ) {
|
||||||
|
vips_webp_writer_unset( &new );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( vips_webp_add_chunk( &new, image, VIPS_META_EXIF_NAME, "EXIF" ) ) {
|
||||||
|
vips_webp_writer_unset( &new );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( vips_webp_add_chunk( &new, image, VIPS_META_XMP_NAME, "XMP " ) ) {
|
||||||
|
vips_webp_writer_unset( &new );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
vips_webp_writer_unset( writer );
|
||||||
|
*writer = new;
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vips__webp_write_file( VipsImage *in, const char *filename,
|
vips__webp_write_file( VipsImage *in, const char *filename,
|
||||||
int Q, gboolean lossless, VipsForeignWebpPreset preset,
|
int Q, gboolean lossless, VipsForeignWebpPreset preset,
|
||||||
@ -226,7 +477,7 @@ vips__webp_write_file( VipsImage *in, const char *filename,
|
|||||||
int alpha_q )
|
int alpha_q )
|
||||||
{
|
{
|
||||||
WebPPicture pic;
|
WebPPicture pic;
|
||||||
VipsWebPMemoryWriter writer;
|
VipsWebPWriter writer;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
if( !WebPPictureInit( &pic ) ) {
|
if( !WebPPictureInit( &pic ) ) {
|
||||||
@ -235,32 +486,37 @@ vips__webp_write_file( VipsImage *in, const char *filename,
|
|||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
init_memory_writer( &writer );
|
vips_webp_writer_init( &writer );
|
||||||
pic.writer = memory_write;
|
pic.writer = memory_write;
|
||||||
pic.custom_ptr = &writer;
|
pic.custom_ptr = &writer;
|
||||||
|
|
||||||
if( write_webp( &pic, in, Q, lossless, preset, smart_subsample,
|
if( write_webp( &pic, in, Q, lossless, preset, smart_subsample,
|
||||||
near_lossless, alpha_q ) ) {
|
near_lossless, alpha_q ) ) {
|
||||||
WebPPictureFree( &pic );
|
WebPPictureFree( &pic );
|
||||||
g_free( writer.mem );
|
vips_webp_writer_unset( &writer );
|
||||||
return -1;
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPPictureFree( &pic );
|
WebPPictureFree( &pic );
|
||||||
|
|
||||||
|
if( vips_webp_add_metadata( &writer, in ) ) {
|
||||||
|
vips_webp_writer_unset( &writer );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
if( !(fp = vips__file_open_write( filename, FALSE )) ) {
|
if( !(fp = vips__file_open_write( filename, FALSE )) ) {
|
||||||
g_free( writer.mem );
|
vips_webp_writer_unset( &writer );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( vips__file_write( writer.mem, writer.size, 1, fp ) ) {
|
if( vips__file_write( writer.mem, writer.size, 1, fp ) ) {
|
||||||
fclose( fp );
|
fclose( fp );
|
||||||
g_free( writer.mem );
|
vips_webp_writer_unset( &writer );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose( fp );
|
fclose( fp );
|
||||||
g_free( writer.mem );
|
vips_webp_writer_unset( &writer );
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
@ -272,7 +528,7 @@ vips__webp_write_buffer( VipsImage *in, void **obuf, size_t *olen,
|
|||||||
int alpha_q )
|
int alpha_q )
|
||||||
{
|
{
|
||||||
WebPPicture pic;
|
WebPPicture pic;
|
||||||
VipsWebPMemoryWriter writer;
|
VipsWebPWriter writer;
|
||||||
|
|
||||||
if( !WebPPictureInit( &pic ) ) {
|
if( !WebPPictureInit( &pic ) ) {
|
||||||
vips_error( "vips2webp",
|
vips_error( "vips2webp",
|
||||||
@ -280,19 +536,24 @@ vips__webp_write_buffer( VipsImage *in, void **obuf, size_t *olen,
|
|||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
init_memory_writer( &writer );
|
vips_webp_writer_init( &writer );
|
||||||
pic.writer = memory_write;
|
pic.writer = memory_write;
|
||||||
pic.custom_ptr = &writer;
|
pic.custom_ptr = &writer;
|
||||||
|
|
||||||
if( write_webp( &pic, in, Q, lossless, preset, smart_subsample,
|
if( write_webp( &pic, in, Q, lossless, preset, smart_subsample,
|
||||||
near_lossless, alpha_q ) ) {
|
near_lossless, alpha_q ) ) {
|
||||||
WebPPictureFree( &pic );
|
WebPPictureFree( &pic );
|
||||||
g_free( writer.mem );
|
vips_webp_writer_unset( &writer );
|
||||||
return -1;
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPPictureFree( &pic );
|
WebPPictureFree( &pic );
|
||||||
|
|
||||||
|
if( vips_webp_add_metadata( &writer, in ) ) {
|
||||||
|
vips_webp_writer_unset( &writer );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
*obuf = writer.mem;
|
*obuf = writer.mem;
|
||||||
*olen = writer.size;
|
*olen = writer.size;
|
||||||
|
|
||||||
|
@ -212,10 +212,11 @@ read_new( const char *filename, const void *data, size_t length, int shrink )
|
|||||||
/* Map vips metadata names to webp names.
|
/* Map vips metadata names to webp names.
|
||||||
*/
|
*/
|
||||||
const VipsWebPNames vips__webp_names[] = {
|
const VipsWebPNames vips__webp_names[] = {
|
||||||
{ VIPS_META_ICC_NAME, "ICCP" },
|
{ VIPS_META_ICC_NAME, "ICCP", 0x20 },
|
||||||
{ VIPS_META_XMP_NAME, "XMP " },
|
{ VIPS_META_XMP_NAME, "XMP ", 0x04 },
|
||||||
{ VIPS_META_EXIF_NAME, "EXIF" }
|
{ VIPS_META_EXIF_NAME, "EXIF", 0x08 }
|
||||||
};
|
};
|
||||||
|
const int vips__n_webp_names = VIPS_NUMBER( vips__webp_names );
|
||||||
|
|
||||||
static int
|
static int
|
||||||
read_header( Read *read, VipsImage *out )
|
read_header( Read *read, VipsImage *out )
|
||||||
@ -245,10 +246,12 @@ read_header( Read *read, VipsImage *out )
|
|||||||
}
|
}
|
||||||
|
|
||||||
for( i = 0; i < VIPS_NUMBER( vips__webp_names ); i++ ) {
|
for( i = 0; i < VIPS_NUMBER( vips__webp_names ); i++ ) {
|
||||||
|
const char *vips = vips__webp_names[i].vips;
|
||||||
|
const char *webp = vips__webp_names[i].webp;
|
||||||
|
|
||||||
WebPData data;
|
WebPData data;
|
||||||
|
|
||||||
if( WebPMuxGetChunk( mux, vips__webp_names[i].webp, &data ) ==
|
if( WebPMuxGetChunk( mux, webp, &data ) == WEBP_MUX_OK ) {
|
||||||
WEBP_MUX_OK ) {
|
|
||||||
void *blob;
|
void *blob;
|
||||||
|
|
||||||
if( !(blob = vips_malloc( NULL, data.size )) ) {
|
if( !(blob = vips_malloc( NULL, data.size )) ) {
|
||||||
@ -257,7 +260,7 @@ read_header( Read *read, VipsImage *out )
|
|||||||
}
|
}
|
||||||
|
|
||||||
memcpy( blob, data.bytes, data.size );
|
memcpy( blob, data.bytes, data.size );
|
||||||
vips_image_set_blob( out, vips__webp_names[i].vips,
|
vips_image_set_blob( out, vips,
|
||||||
(VipsCallbackFn) vips_free, blob, data.size );
|
(VipsCallbackFn) vips_free, blob, data.size );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user