get ready for jpeg buffer
This commit is contained in:
parent
c4c1dad8a6
commit
ee17946bf9
89
TODO
89
TODO
@ -1,9 +1,8 @@
|
|||||||
- do jpegbuffer load and save
|
- fix up jpeg2vips.c
|
||||||
|
|
||||||
need a jpegsave base class, with subclasses for save to file and save to
|
fix up jpegload.c
|
||||||
buffer
|
|
||||||
|
|
||||||
same for jpegload
|
added classes for read and write jpeg buffers
|
||||||
|
|
||||||
make compat wrappers for old im_jpeg2vips() and im_vips2jpeg()
|
make compat wrappers for old im_jpeg2vips() and im_vips2jpeg()
|
||||||
|
|
||||||
@ -17,88 +16,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- vipsimage should be cached too, eg.
|
|
||||||
|
|
||||||
VipsImage *a = vips_image_new_from_file( "poop.jpg" );
|
|
||||||
|
|
||||||
can be reused
|
|
||||||
|
|
||||||
ah ha! will im_jpeg2vips() or whatever get cached? that would do the reuse
|
|
||||||
for us
|
|
||||||
|
|
||||||
move format to new-style and try it
|
|
||||||
|
|
||||||
- also VipsFormat ... could this replace vips_image_new_from_string()? or
|
|
||||||
could we call this from vips_image_new_from_string()?
|
|
||||||
|
|
||||||
at the moment, VipsFormat subclasses never get made, we just use the classes
|
|
||||||
... we'd need to start making real vipsformat objects for this to work
|
|
||||||
|
|
||||||
how would this work?
|
|
||||||
|
|
||||||
at the moment we have
|
|
||||||
|
|
||||||
image = vips_image_new_from_file( filename );
|
|
||||||
|
|
||||||
build a VipsImage with filename "r"
|
|
||||||
|
|
||||||
we also have the new CLI thing
|
|
||||||
|
|
||||||
obj = vips_object_new_from_string( class, str );
|
|
||||||
|
|
||||||
calls class->new_from_string( first-component(str) ), then sets
|
|
||||||
optional args from rest-of-str(str), then does _build()
|
|
||||||
|
|
||||||
image-class->new_from_string() just make a vipsimage "r" str
|
|
||||||
|
|
||||||
the _build() uses VipsFormat() to load via im_tiff2vips() or whatever
|
|
||||||
|
|
||||||
so ... we could change vips_image_new_from_file() to be
|
|
||||||
|
|
||||||
VIPS_IMAGE( vips_object_new_from_string( VipsImageClass, str ) )
|
|
||||||
|
|
||||||
we could also make VipsImage::new_from_string() make a real VipsFormat
|
|
||||||
object, then load options could be set from the str
|
|
||||||
|
|
||||||
how does save work? we call image-class->output_to_arg(obj, str), which in
|
|
||||||
turn calls vips_image_write(), which in turn uses VipsFormat
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
so ... make a VipsFormat class
|
|
||||||
|
|
||||||
define a VipsObject::new_from_string() method which takes a filename
|
|
||||||
|
|
||||||
gives you back a VipsFormat subclass object with methods for
|
|
||||||
load/header/flags/etc.
|
|
||||||
|
|
||||||
also has args for "load-ascii" etc. etc.
|
|
||||||
|
|
||||||
then you call _buil() to make it
|
|
||||||
|
|
||||||
... and then how does this become a VipsImage?
|
|
||||||
|
|
||||||
could be a vipsimage subclass? VipsObject.VipsImage.VipsFormat.VipsTiff
|
|
||||||
|
|
||||||
make a branch for this, seems like a big change
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
maybe keep the current structure, but just make VipsFormat & subclasses
|
|
||||||
instantiable?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- transform_g_string_array_image() can't handle quoted strings, so filenames
|
- transform_g_string_array_image() can't handle quoted strings, so filenames
|
||||||
with spaces will break
|
with spaces will break
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
noinst_LTLIBRARIES = libfile.la
|
noinst_LTLIBRARIES = libfile.la
|
||||||
|
|
||||||
libfile_la_SOURCES = \
|
libfile_la_SOURCES = \
|
||||||
jpeg.c \
|
vips2jpeg.c \
|
||||||
|
jpeg2vips.c \
|
||||||
jpeg.h \
|
jpeg.h \
|
||||||
jpegload.c \
|
jpegload.c \
|
||||||
jpegsave.c \
|
jpegsave.c \
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
/* common jpeg definitions
|
|
||||||
*
|
|
||||||
* 24/11/11
|
|
||||||
* - from im_vips2jpeg.c
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
This file is part of VIPS.
|
|
||||||
|
|
||||||
VIPS is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define DEBUG
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <config.h>
|
|
||||||
#endif /*HAVE_CONFIG_H*/
|
|
||||||
#include <vips/intl.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <setjmp.h>
|
|
||||||
|
|
||||||
#include <vips/vips.h>
|
|
||||||
|
|
||||||
/* jpeglib includes jconfig.h, which can define HAVE_STDLIB_H ... which we
|
|
||||||
* also define. Make sure it's turned off.
|
|
||||||
*/
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#undef HAVE_STDLIB_H
|
|
||||||
#endif /*HAVE_STDLIB_H*/
|
|
||||||
|
|
||||||
#include <jpeglib.h>
|
|
||||||
#include <jerror.h>
|
|
||||||
|
|
||||||
#include "jpeg.h"
|
|
||||||
|
|
||||||
/* New output message method - send to VIPS.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
vips__new_output_message( j_common_ptr cinfo )
|
|
||||||
{
|
|
||||||
char buffer[JMSG_LENGTH_MAX];
|
|
||||||
|
|
||||||
(*cinfo->err->format_message)( cinfo, buffer );
|
|
||||||
vips_error( "VipsFile*Jpeg", _( "%s" ), buffer );
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "jpeg.c: new_output_message: \"%s\"\n", buffer );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/* New error_exit handler.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
vips__new_error_exit( j_common_ptr cinfo )
|
|
||||||
{
|
|
||||||
ErrorManager *eman = (ErrorManager *) cinfo->err;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "jpeg.c: new_error_exit\n" );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
/* Close the fp if necessary.
|
|
||||||
*/
|
|
||||||
if( eman->fp ) {
|
|
||||||
(void) fclose( eman->fp );
|
|
||||||
eman->fp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send the error message to VIPS. This method is overridden above.
|
|
||||||
*/
|
|
||||||
(*cinfo->err->output_message)( cinfo );
|
|
||||||
|
|
||||||
/* Jump back.
|
|
||||||
*/
|
|
||||||
longjmp( eman->jmp, 1 );
|
|
||||||
}
|
|
||||||
|
|
@ -50,6 +50,19 @@ typedef struct {
|
|||||||
void vips__new_output_message( j_common_ptr cinfo );
|
void vips__new_output_message( j_common_ptr cinfo );
|
||||||
void vips__new_error_exit( j_common_ptr cinfo );
|
void vips__new_error_exit( j_common_ptr cinfo );
|
||||||
|
|
||||||
|
int vips__jpeg_write_file( VipsImage *in,
|
||||||
|
const char *filename, int Q, const char *profile );
|
||||||
|
int vips__jpeg_write_buffer( VipsImage *in,
|
||||||
|
void **obuf, int *olen, int Q, const char *profile );
|
||||||
|
|
||||||
|
int vips__isjpeg( const char *filename );
|
||||||
|
int vips__jpeg_read_file( const char *name, VipsImage *out,
|
||||||
|
gboolean header_only,
|
||||||
|
int shrink, gboolean fail );
|
||||||
|
int vips__jpeg_read_buffer( void *buf, size_t len, VipsImage *out,
|
||||||
|
gboolean header_only,
|
||||||
|
int shrink, int fail );
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif /*__cplusplus*/
|
#endif /*__cplusplus*/
|
||||||
|
1027
libvips/file/jpeg2vips.c
Normal file
1027
libvips/file/jpeg2vips.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,53 +1,7 @@
|
|||||||
/* save to jpeg
|
/* save to jpeg
|
||||||
*
|
*
|
||||||
* 28/11/03 JC
|
|
||||||
* - better no-overshoot on tile loop
|
|
||||||
* 12/11/04
|
|
||||||
* - better demand size choice for eval
|
|
||||||
* 30/6/05 JC
|
|
||||||
* - update im_error()/im_warn()
|
|
||||||
* - now loads and saves exif data
|
|
||||||
* 30/7/05
|
|
||||||
* - now loads ICC profiles
|
|
||||||
* - now saves ICC profiles from the VIPS header
|
|
||||||
* 24/8/05
|
|
||||||
* - jpeg load sets vips xres/yres from exif, if possible
|
|
||||||
* - jpeg save sets exif xres/yres from vips, if possible
|
|
||||||
* 29/8/05
|
|
||||||
* - cut from old vips_jpeg.c
|
|
||||||
* 20/4/06
|
|
||||||
* - auto convert to sRGB/mono for save
|
|
||||||
* 13/10/06
|
|
||||||
* - add </libexif/ prefix if required
|
|
||||||
* 19/1/07
|
|
||||||
* - oop, libexif confusion
|
|
||||||
* 2/11/07
|
|
||||||
* - use im_wbuffer() API for BG writes
|
|
||||||
* 15/2/08
|
|
||||||
* - write CMYK if Bands == 4 and Type == CMYK
|
|
||||||
* 12/5/09
|
|
||||||
* - fix signed/unsigned warning
|
|
||||||
* 13/8/09
|
|
||||||
* - allow "none" for profile, meaning don't embed one
|
|
||||||
* 4/2/10
|
|
||||||
* - gtkdoc
|
|
||||||
* 17/7/10
|
|
||||||
* - use g_assert()
|
|
||||||
* - allow space for the header in init_destination(), helps writing very
|
|
||||||
* small JPEGs (thanks Tim Elliott)
|
|
||||||
* 18/7/10
|
|
||||||
* - collect im_vips2bufjpeg() output in a list of blocks ... we no
|
|
||||||
* longer overallocate or underallocate
|
|
||||||
* 8/7/11
|
|
||||||
* - oop CMYK write was not inverting, thanks Ole
|
|
||||||
* 12/10/2011
|
|
||||||
* - write XMP data
|
|
||||||
* 18/10/2011
|
|
||||||
* - update Orientation as well
|
|
||||||
* 3/11/11
|
|
||||||
* - rebuild exif tags from coded metadata values
|
|
||||||
* 24/11/11
|
* 24/11/11
|
||||||
* - rework as a class
|
* - wrap a class around the jpeg writer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -79,7 +33,6 @@
|
|||||||
/*
|
/*
|
||||||
#define DEBUG_VERBOSE
|
#define DEBUG_VERBOSE
|
||||||
#define DEBUG
|
#define DEBUG
|
||||||
#define VIPS_DEBUG
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
@ -107,9 +60,8 @@
|
|||||||
#endif /*HAVE_EXIF*/
|
#endif /*HAVE_EXIF*/
|
||||||
|
|
||||||
#include <vips/vips.h>
|
#include <vips/vips.h>
|
||||||
#include <vips/internal.h>
|
|
||||||
#include <vips/debug.h>
|
|
||||||
#include <vips/buf.h>
|
#include <vips/buf.h>
|
||||||
|
#include <vips/internal.h>
|
||||||
|
|
||||||
/* jpeglib includes jconfig.h, which can define HAVE_STDLIB_H ... which we
|
/* jpeglib includes jconfig.h, which can define HAVE_STDLIB_H ... which we
|
||||||
* also define. Make sure it's turned off.
|
* also define. Make sure it's turned off.
|
||||||
@ -134,673 +86,25 @@ typedef struct _VipsFileSaveJpeg {
|
|||||||
*/
|
*/
|
||||||
char *profile;
|
char *profile;
|
||||||
|
|
||||||
/* Stuff we track during write.
|
|
||||||
*/
|
|
||||||
struct jpeg_compress_struct cinfo;
|
|
||||||
ErrorManager eman;
|
|
||||||
JSAMPROW *row_pointer;
|
|
||||||
char *profile_bytes;
|
|
||||||
unsigned int profile_length;
|
|
||||||
|
|
||||||
/* We sometimes need to invert the image before saving.
|
|
||||||
*/
|
|
||||||
VipsImage *inverted;
|
|
||||||
|
|
||||||
} VipsFileSaveJpeg;
|
} VipsFileSaveJpeg;
|
||||||
|
|
||||||
typedef VipsFileSaveClass VipsFileSaveJpegClass;
|
typedef VipsFileSaveClass VipsFileSaveJpegClass;
|
||||||
|
|
||||||
G_DEFINE_TYPE( VipsFileSaveJpeg, vips_file_save_jpeg, VIPS_TYPE_FILE_SAVE );
|
G_DEFINE_TYPE( VipsFileSaveJpeg, vips_file_save_jpeg, VIPS_TYPE_FILE_SAVE );
|
||||||
|
|
||||||
static void
|
|
||||||
vips_file_save_jpeg_dispose( GObject *gobject )
|
|
||||||
{
|
|
||||||
VipsFileSaveJpeg *save = (VipsFileSaveJpeg *) gobject;
|
|
||||||
|
|
||||||
VIPS_UNREF( save->inverted );
|
|
||||||
|
|
||||||
G_OBJECT_CLASS( vips_file_save_jpeg_parent_class )->dispose( gobject );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_file_save_jpeg_finalize( GObject *gobject )
|
|
||||||
{
|
|
||||||
VipsFileSaveJpeg *save = (VipsFileSaveJpeg *) gobject;
|
|
||||||
|
|
||||||
jpeg_destroy_compress( &save->cinfo );
|
|
||||||
VIPS_FREEF( fclose, save->eman.fp );
|
|
||||||
VIPS_FREE( save->row_pointer );
|
|
||||||
VIPS_FREE( save->profile_bytes );
|
|
||||||
|
|
||||||
G_OBJECT_CLASS( vips_file_save_jpeg_parent_class )->finalize( gobject );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_EXIF
|
|
||||||
static void
|
|
||||||
vips_exif_set_int( ExifData *ed,
|
|
||||||
ExifEntry *entry, unsigned long component, void *data )
|
|
||||||
{
|
|
||||||
int value = *((int *) data);
|
|
||||||
|
|
||||||
ExifByteOrder bo;
|
|
||||||
size_t sizeof_component;
|
|
||||||
size_t offset = component;
|
|
||||||
|
|
||||||
if( entry->components <= component ) {
|
|
||||||
VIPS_DEBUG_MSG( "vips_exif_set_int: too few components\n" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait until after the component check to make sure we cant get /0.
|
|
||||||
*/
|
|
||||||
bo = exif_data_get_byte_order( ed );
|
|
||||||
sizeof_component = entry->size / entry->components;
|
|
||||||
offset = component * sizeof_component;
|
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_exif_set_int: %s = %d\n",
|
|
||||||
exif_tag_get_title( entry->tag ), value );
|
|
||||||
|
|
||||||
if( entry->format == EXIF_FORMAT_SHORT )
|
|
||||||
exif_set_short( entry->data + offset, bo, value );
|
|
||||||
else if( entry->format == EXIF_FORMAT_SSHORT )
|
|
||||||
exif_set_sshort( entry->data + offset, bo, value );
|
|
||||||
else if( entry->format == EXIF_FORMAT_LONG )
|
|
||||||
exif_set_long( entry->data + offset, bo, value );
|
|
||||||
else if( entry->format == EXIF_FORMAT_SLONG )
|
|
||||||
exif_set_slong( entry->data + offset, bo, value );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_exif_set_double( ExifData *ed,
|
|
||||||
ExifEntry *entry, unsigned long component, void *data )
|
|
||||||
{
|
|
||||||
double value = *((double *) data);
|
|
||||||
|
|
||||||
ExifByteOrder bo;
|
|
||||||
size_t sizeof_component;
|
|
||||||
size_t offset;
|
|
||||||
|
|
||||||
if( entry->components <= component ) {
|
|
||||||
VIPS_DEBUG_MSG( "vips_exif_set_int: too few components\n" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait until after the component check to make sure we cant get /0.
|
|
||||||
*/
|
|
||||||
bo = exif_data_get_byte_order( ed );
|
|
||||||
sizeof_component = entry->size / entry->components;
|
|
||||||
offset = component * sizeof_component;
|
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_exif_set_double: %s = %g\n",
|
|
||||||
exif_tag_get_title( entry->tag ), value );
|
|
||||||
|
|
||||||
if( entry->format == EXIF_FORMAT_RATIONAL ) {
|
|
||||||
ExifRational rational;
|
|
||||||
unsigned int scale;
|
|
||||||
|
|
||||||
/* We scale up to fill uint32, then set that as the
|
|
||||||
* denominator. Try to avoid generating 0.
|
|
||||||
*/
|
|
||||||
scale = (int) ((UINT_MAX - 1000) / value);
|
|
||||||
scale = scale == 0 ? 1 : scale;
|
|
||||||
rational.numerator = value * scale;
|
|
||||||
rational.denominator = scale;
|
|
||||||
|
|
||||||
exif_set_rational( entry->data + offset, bo, rational );
|
|
||||||
}
|
|
||||||
else if( entry->format == EXIF_FORMAT_SRATIONAL ) {
|
|
||||||
ExifSRational rational;
|
|
||||||
int scale;
|
|
||||||
|
|
||||||
scale = (int) ((INT_MAX - 1000) / value);
|
|
||||||
scale = scale == 0 ? 1 : scale;
|
|
||||||
rational.numerator = value * scale;
|
|
||||||
rational.denominator = scale;
|
|
||||||
|
|
||||||
exif_set_srational( entry->data + offset, bo, rational );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef void (*write_fn)( ExifData *ed,
|
|
||||||
ExifEntry *entry, unsigned long component, void *data );
|
|
||||||
|
|
||||||
/* Write a component in a tag everywhere it appears.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
write_tag( ExifData *ed,
|
|
||||||
ExifTag tag, ExifFormat format, write_fn fn, void *data )
|
|
||||||
{
|
|
||||||
int found;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
found = 0;
|
|
||||||
for( i = 0; i < EXIF_IFD_COUNT; i++ ) {
|
|
||||||
ExifEntry *entry;
|
|
||||||
|
|
||||||
if( (entry = exif_content_get_entry( ed->ifd[i], tag )) &&
|
|
||||||
entry->format == format ) {
|
|
||||||
fn( ed, entry, 0, data );
|
|
||||||
found = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !found ) {
|
|
||||||
/* There was no tag we could update ... make one in ifd[0].
|
|
||||||
*/
|
|
||||||
ExifEntry *entry;
|
|
||||||
|
|
||||||
entry = exif_entry_new();
|
|
||||||
|
|
||||||
/* tag must be set before calling exif_content_add_entry.
|
|
||||||
*/
|
|
||||||
entry->tag = tag;
|
|
||||||
|
|
||||||
exif_content_add_entry( ed->ifd[0], entry );
|
|
||||||
exif_entry_initialize( entry, tag );
|
|
||||||
exif_entry_unref( entry );
|
|
||||||
|
|
||||||
fn( ed, entry, 0, data );
|
|
||||||
}
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is different, we set the xres/yres from the vips header rather than
|
|
||||||
* from the exif tags on the image metadata.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
set_exif_resolution( ExifData *ed, VipsImage *im )
|
|
||||||
{
|
|
||||||
double xres, yres;
|
|
||||||
int unit;
|
|
||||||
|
|
||||||
/* Always save as inches - more progs support it for read.
|
|
||||||
*/
|
|
||||||
xres = im->Xres * 25.4;
|
|
||||||
yres = im->Yres * 25.4;
|
|
||||||
unit = 2;
|
|
||||||
|
|
||||||
if( write_tag( ed, EXIF_TAG_X_RESOLUTION, EXIF_FORMAT_RATIONAL,
|
|
||||||
vips_exif_set_double, (void *) &xres ) ||
|
|
||||||
write_tag( ed, EXIF_TAG_Y_RESOLUTION, EXIF_FORMAT_RATIONAL,
|
|
||||||
vips_exif_set_double, (void *) &yres ) ||
|
|
||||||
write_tag( ed, EXIF_TAG_RESOLUTION_UNIT, EXIF_FORMAT_SHORT,
|
|
||||||
vips_exif_set_int, (void *) &unit ) ) {
|
|
||||||
vips_error( "VipsFileSaveJpeg",
|
|
||||||
"%s", _( "error setting JPEG resolution" ) );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See also vips_exif_to_s() ... keep in sync.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
vips_exif_from_s( ExifData *ed, ExifEntry *entry, const char *value )
|
|
||||||
{
|
|
||||||
unsigned long i;
|
|
||||||
const char *p;
|
|
||||||
|
|
||||||
if( entry->format != EXIF_FORMAT_SHORT &&
|
|
||||||
entry->format != EXIF_FORMAT_SSHORT &&
|
|
||||||
entry->format != EXIF_FORMAT_LONG &&
|
|
||||||
entry->format != EXIF_FORMAT_SLONG &&
|
|
||||||
entry->format != EXIF_FORMAT_RATIONAL &&
|
|
||||||
entry->format != EXIF_FORMAT_SRATIONAL )
|
|
||||||
return;
|
|
||||||
if( entry->components >= 10 )
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Skip any leading spaces.
|
|
||||||
*/
|
|
||||||
p = value;
|
|
||||||
while( *p == ' ' )
|
|
||||||
p += 1;
|
|
||||||
|
|
||||||
for( i = 0; i < entry->components; i++ ) {
|
|
||||||
if( entry->format == EXIF_FORMAT_SHORT ||
|
|
||||||
entry->format == EXIF_FORMAT_SSHORT ||
|
|
||||||
entry->format == EXIF_FORMAT_LONG ||
|
|
||||||
entry->format == EXIF_FORMAT_SLONG ) {
|
|
||||||
int value = atof( p );
|
|
||||||
|
|
||||||
vips_exif_set_int( ed, entry, i, &value );
|
|
||||||
}
|
|
||||||
else if( entry->format == EXIF_FORMAT_RATIONAL ||
|
|
||||||
entry->format == EXIF_FORMAT_SRATIONAL ) {
|
|
||||||
double value = g_ascii_strtod( p, NULL );
|
|
||||||
|
|
||||||
vips_exif_set_double( ed, entry, i, &value );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Skip to the next set of spaces, then to the beginning of
|
|
||||||
* the next item.
|
|
||||||
*/
|
|
||||||
while( *p && *p != ' ' )
|
|
||||||
p += 1;
|
|
||||||
while( *p == ' ' )
|
|
||||||
p += 1;
|
|
||||||
if( !*p )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _VipsExif {
|
|
||||||
VipsImage *image;
|
|
||||||
ExifData *ed;
|
|
||||||
} VipsExif;
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_exif_update_entry( ExifEntry *entry, VipsExif *ve )
|
|
||||||
{
|
|
||||||
char name[256];
|
|
||||||
char *value;
|
|
||||||
|
|
||||||
vips_snprintf( name, 256, "exif-%s", exif_tag_get_title( entry->tag ) );
|
|
||||||
if( !vips_image_get_string( ve->image, name, &value ) )
|
|
||||||
vips_exif_from_s( ve->ed, entry, value );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_exif_update_content( ExifContent *content, VipsExif *ve )
|
|
||||||
{
|
|
||||||
exif_content_foreach_entry( content,
|
|
||||||
(ExifContentForeachEntryFunc) vips_exif_update_entry, ve );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vips_exif_update( ExifData *ed, VipsImage *image )
|
|
||||||
{
|
|
||||||
VipsExif ve;
|
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_exif_update: \n" );
|
|
||||||
|
|
||||||
ve.image = image;
|
|
||||||
ve.ed = ed;
|
|
||||||
exif_data_foreach_content( ed,
|
|
||||||
(ExifDataForeachContentFunc) vips_exif_update_content, &ve );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif /*HAVE_EXIF*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
write_exif( VipsFileSaveJpeg *jpeg )
|
|
||||||
{
|
|
||||||
VipsFileSave *save = (VipsFileSave *) jpeg;
|
|
||||||
|
|
||||||
unsigned char *data;
|
|
||||||
size_t data_length;
|
|
||||||
unsigned int idl;
|
|
||||||
#ifdef HAVE_EXIF
|
|
||||||
ExifData *ed;
|
|
||||||
|
|
||||||
/* Either parse from the embedded EXIF, or if there's none, make
|
|
||||||
* some fresh EXIF we can write the resolution to.
|
|
||||||
*/
|
|
||||||
if( vips_image_get_typeof( save->in, VIPS_META_EXIF_NAME ) ) {
|
|
||||||
if( vips_image_get_blob( save->in, VIPS_META_EXIF_NAME,
|
|
||||||
(void *) &data, &data_length ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( !(ed = exif_data_new_from_data( data, data_length )) )
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ed = exif_data_new();
|
|
||||||
|
|
||||||
exif_data_set_option( ed,
|
|
||||||
EXIF_DATA_OPTION_FOLLOW_SPECIFICATION );
|
|
||||||
exif_data_set_data_type( ed, EXIF_DATA_TYPE_COMPRESSED );
|
|
||||||
exif_data_set_byte_order( ed, EXIF_BYTE_ORDER_INTEL );
|
|
||||||
|
|
||||||
/* Create the mandatory EXIF fields with default data.
|
|
||||||
*/
|
|
||||||
exif_data_fix( ed );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update EXIF tags from the image metadata.
|
|
||||||
*/
|
|
||||||
vips_exif_update( ed, save->in );
|
|
||||||
|
|
||||||
/* Update EXIF resolution from the vips image header..
|
|
||||||
*/
|
|
||||||
if( set_exif_resolution( ed, save->in ) ) {
|
|
||||||
exif_data_free( ed );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reserialise and write. exif_data_save_data() returns an int for some
|
|
||||||
* reason.
|
|
||||||
*/
|
|
||||||
exif_data_save_data( ed, &data, &idl );
|
|
||||||
if( !idl ) {
|
|
||||||
vips_error( "VipsFileSaveJpeg",
|
|
||||||
"%s", _( "error saving EXIF" ) );
|
|
||||||
exif_data_free( ed );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
data_length = idl;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "jpegsave: attaching %zd bytes of EXIF\n", data_length );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
exif_data_free( ed );
|
|
||||||
jpeg_write_marker( &jpeg->cinfo, JPEG_APP0 + 1, data, data_length );
|
|
||||||
free( data );
|
|
||||||
#else /*!HAVE_EXIF*/
|
|
||||||
/* No libexif ... just copy the embedded EXIF over.
|
|
||||||
*/
|
|
||||||
if( vips_image_get_typeof( save->in, VIPS_META_EXIF_NAME ) ) {
|
|
||||||
if( vips_image_get_blob( save->in, VIPS_META_EXIF_NAME,
|
|
||||||
(void *) &data, &data_length ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "jpegsave: attaching %zd bytes of EXIF\n",
|
|
||||||
data_length );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
jpeg_write_marker( &jpeg->cinfo, JPEG_APP0 + 1,
|
|
||||||
data, data_length );
|
|
||||||
}
|
|
||||||
#endif /*!HAVE_EXIF*/
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
write_xmp( VipsFileSaveJpeg *jpeg )
|
|
||||||
{
|
|
||||||
VipsFileSave *save = (VipsFileSave *) jpeg;
|
|
||||||
|
|
||||||
unsigned char *data;
|
|
||||||
size_t data_length;
|
|
||||||
|
|
||||||
/* No libexif ... just copy the embedded EXIF over.
|
|
||||||
*/
|
|
||||||
if( vips_image_get_typeof( save->in, VIPS_META_XMP_NAME ) ) {
|
|
||||||
if( vips_image_get_blob( save->in, VIPS_META_XMP_NAME,
|
|
||||||
(void *) &data, &data_length ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "jpegsave: attaching %zd bytes of XMP\n",
|
|
||||||
data_length );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
jpeg_write_marker( &jpeg->cinfo, JPEG_APP0 + 1,
|
|
||||||
data, data_length );
|
|
||||||
}
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ICC writer from lcms, slight tweaks.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */
|
|
||||||
#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */
|
|
||||||
#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */
|
|
||||||
#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This routine writes the given ICC profile data into a JPEG file.
|
|
||||||
* It *must* be called AFTER calling jpeg_start_compress() and BEFORE
|
|
||||||
* the first call to jpeg_write_scanlines().
|
|
||||||
* (This ordering ensures that the APP2 marker(s) will appear after the
|
|
||||||
* SOI and JFIF or Adobe markers, but before all else.)
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_profile_data (j_compress_ptr cinfo,
|
|
||||||
const JOCTET *icc_data_ptr,
|
|
||||||
unsigned int icc_data_len)
|
|
||||||
{
|
|
||||||
unsigned int num_markers; /* total number of markers we'll write */
|
|
||||||
int cur_marker = 1; /* per spec, counting starts at 1 */
|
|
||||||
unsigned int length; /* number of bytes to write in this marker */
|
|
||||||
|
|
||||||
/* rounding up will fail for length == 0 */
|
|
||||||
g_assert( icc_data_len > 0 );
|
|
||||||
|
|
||||||
/* Calculate the number of markers we'll need, rounding up of course */
|
|
||||||
num_markers = (icc_data_len + MAX_DATA_BYTES_IN_MARKER - 1) /
|
|
||||||
MAX_DATA_BYTES_IN_MARKER;
|
|
||||||
|
|
||||||
while (icc_data_len > 0) {
|
|
||||||
/* length of profile to put in this marker */
|
|
||||||
length = icc_data_len;
|
|
||||||
if (length > MAX_DATA_BYTES_IN_MARKER)
|
|
||||||
length = MAX_DATA_BYTES_IN_MARKER;
|
|
||||||
icc_data_len -= length;
|
|
||||||
|
|
||||||
/* Write the JPEG marker header (APP2 code and marker length) */
|
|
||||||
jpeg_write_m_header(cinfo, ICC_MARKER,
|
|
||||||
(unsigned int) (length + ICC_OVERHEAD_LEN));
|
|
||||||
|
|
||||||
/* Write the marker identifying string "ICC_PROFILE" (null-terminated).
|
|
||||||
* We code it in this less-than-transparent way so that the code works
|
|
||||||
* even if the local character set is not ASCII.
|
|
||||||
*/
|
|
||||||
jpeg_write_m_byte(cinfo, 0x49);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x43);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x43);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x5F);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x50);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x52);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x4F);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x46);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x49);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x4C);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x45);
|
|
||||||
jpeg_write_m_byte(cinfo, 0x0);
|
|
||||||
|
|
||||||
/* Add the sequencing info */
|
|
||||||
jpeg_write_m_byte(cinfo, cur_marker);
|
|
||||||
jpeg_write_m_byte(cinfo, (int) num_markers);
|
|
||||||
|
|
||||||
/* Add the profile data */
|
|
||||||
while (length--) {
|
|
||||||
jpeg_write_m_byte(cinfo, *icc_data_ptr);
|
|
||||||
icc_data_ptr++;
|
|
||||||
}
|
|
||||||
cur_marker++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write an ICC Profile from a file into the JPEG stream.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
write_profile_file( VipsFileSaveJpeg *jpeg, const char *profile )
|
|
||||||
{
|
|
||||||
if( !(jpeg->profile_bytes =
|
|
||||||
vips__file_read_name( profile, VIPS_ICC_DIR,
|
|
||||||
&jpeg->profile_length )) )
|
|
||||||
return( -1 );
|
|
||||||
write_profile_data( &jpeg->cinfo,
|
|
||||||
(JOCTET *) jpeg->profile_bytes, jpeg->profile_length );
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "jpegsave: attached profile \"%s\"\n", profile );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
write_profile_meta( VipsFileSaveJpeg *jpeg )
|
|
||||||
{
|
|
||||||
VipsFileSave *save = (VipsFileSave *) jpeg;
|
|
||||||
|
|
||||||
void *data;
|
|
||||||
size_t data_length;
|
|
||||||
|
|
||||||
if( vips_image_get_blob( save->in, VIPS_META_ICC_NAME,
|
|
||||||
&data, &data_length ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
write_profile_data( &jpeg->cinfo, data, data_length );
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "jpegsave: attached %zd byte profile from VIPS header\n",
|
|
||||||
data_length );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
write_jpeg_block( VipsRegion *region, VipsRect *area, void *a )
|
|
||||||
{
|
|
||||||
VipsFileSaveJpeg *jpeg = (VipsFileSaveJpeg *) a;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for( i = 0; i < area->height; i++ )
|
|
||||||
jpeg->row_pointer[i] = (JSAMPROW)
|
|
||||||
VIPS_REGION_ADDR( region, 0, area->top + i );
|
|
||||||
|
|
||||||
/* We are running in a background thread. We need to catch any
|
|
||||||
* longjmp()s from jpeg_write_scanlines() here.
|
|
||||||
*/
|
|
||||||
if( setjmp( jpeg->eman.jmp ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
jpeg_write_scanlines( &jpeg->cinfo, jpeg->row_pointer, area->height );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write a VIPS image to a JPEG compress struct.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
vips_file_save_jpeg_write( VipsFileSaveJpeg *jpeg )
|
|
||||||
{
|
|
||||||
VipsFileSave *save = (VipsFileSave *) jpeg;
|
|
||||||
|
|
||||||
VipsImage *in;
|
|
||||||
J_COLOR_SPACE space;
|
|
||||||
|
|
||||||
/* The image we'll be writing ... can change, see CMYK.
|
|
||||||
*/
|
|
||||||
in = save->ready;
|
|
||||||
|
|
||||||
/* Should have been converted for save.
|
|
||||||
*/
|
|
||||||
g_assert( in->BandFmt == VIPS_FORMAT_UCHAR );
|
|
||||||
g_assert( in->Coding == VIPS_CODING_NONE );
|
|
||||||
g_assert( in->Bands == 1 || in->Bands == 3 || in->Bands == 4 );
|
|
||||||
|
|
||||||
/* Check input image.
|
|
||||||
*/
|
|
||||||
if( vips_image_pio_input( in ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* Set compression parameters.
|
|
||||||
*/
|
|
||||||
jpeg->cinfo.image_width = in->Xsize;
|
|
||||||
jpeg->cinfo.image_height = in->Ysize;
|
|
||||||
jpeg->cinfo.input_components = in->Bands;
|
|
||||||
if( in->Bands == 4 &&
|
|
||||||
in->Type == VIPS_INTERPRETATION_CMYK ) {
|
|
||||||
space = JCS_CMYK;
|
|
||||||
/* IJG always sets an Adobe marker, so we should invert CMYK.
|
|
||||||
*/
|
|
||||||
if( vips_invert( in, &jpeg->inverted, NULL ) )
|
|
||||||
return( -1 );
|
|
||||||
in = jpeg->inverted;
|
|
||||||
}
|
|
||||||
else if( in->Bands == 3 )
|
|
||||||
space = JCS_RGB;
|
|
||||||
else if( in->Bands == 1 )
|
|
||||||
space = JCS_GRAYSCALE;
|
|
||||||
else
|
|
||||||
/* Use luminance compression for all channels.
|
|
||||||
*/
|
|
||||||
space = JCS_UNKNOWN;
|
|
||||||
jpeg->cinfo.in_color_space = space;
|
|
||||||
|
|
||||||
/* Build VIPS output stuff now we know the image we'll be writing.
|
|
||||||
*/
|
|
||||||
if( !(jpeg->row_pointer = VIPS_ARRAY( NULL, in->Ysize, JSAMPROW )) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* Rest to default.
|
|
||||||
*/
|
|
||||||
jpeg_set_defaults( &jpeg->cinfo );
|
|
||||||
jpeg_set_quality( &jpeg->cinfo, jpeg->Q, TRUE );
|
|
||||||
|
|
||||||
/* Build compress tables.
|
|
||||||
*/
|
|
||||||
jpeg_start_compress( &jpeg->cinfo, TRUE );
|
|
||||||
|
|
||||||
/* Write any APP markers we need.
|
|
||||||
*/
|
|
||||||
if( write_exif( jpeg ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( write_xmp( jpeg ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* A profile supplied as an argument overrides an embedded profile.
|
|
||||||
* "none" means don't attach a profile.
|
|
||||||
*/
|
|
||||||
if( jpeg->profile &&
|
|
||||||
strcmp( jpeg->profile, "none" ) != 0 &&
|
|
||||||
write_profile_file( jpeg, jpeg->profile ) )
|
|
||||||
return( -1 );
|
|
||||||
if( !jpeg->profile &&
|
|
||||||
vips_image_get_typeof( in, VIPS_META_ICC_NAME ) &&
|
|
||||||
write_profile_meta( jpeg ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* Write data. Note that the write function grabs the longjmp()!
|
|
||||||
*/
|
|
||||||
if( vips_sink_disc( in, write_jpeg_block, jpeg ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* We have to reinstate the setjmp() before we jpeg_finish_compress().
|
|
||||||
*/
|
|
||||||
if( setjmp( jpeg->eman.jmp ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
jpeg_finish_compress( &jpeg->cinfo );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vips_file_save_jpeg_build( VipsObject *object )
|
vips_file_save_jpeg_build( VipsObject *object )
|
||||||
{
|
{
|
||||||
VipsFile *file = (VipsFile *) object;
|
VipsFile *file = (VipsFile *) object;
|
||||||
|
VipsFileSave *save = (VipsFileSave *) object;
|
||||||
VipsFileSaveJpeg *jpeg = (VipsFileSaveJpeg *) object;
|
VipsFileSaveJpeg *jpeg = (VipsFileSaveJpeg *) object;
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_file_save_jpeg_parent_class )->
|
if( VIPS_OBJECT_CLASS( vips_file_save_jpeg_parent_class )->
|
||||||
build( object ) )
|
build( object ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
if( setjmp( jpeg->eman.jmp ) )
|
if( vips__jpeg_write_file( save->in, file->filename,
|
||||||
/* Here for longjmp() from vips__new_error_exit().
|
jpeg->Q, jpeg->profile ) )
|
||||||
*/
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* Can't do this in init, has to be after we've made the
|
|
||||||
* setjmp().
|
|
||||||
*/
|
|
||||||
jpeg_create_compress( &jpeg->cinfo );
|
|
||||||
|
|
||||||
/* Make output.
|
|
||||||
*/
|
|
||||||
if( !(jpeg->eman.fp = vips__file_open_write( file->filename, FALSE )) )
|
|
||||||
return( -1 );
|
|
||||||
jpeg_stdio_dest( &jpeg->cinfo, jpeg->eman.fp );
|
|
||||||
|
|
||||||
/* Convert!
|
|
||||||
*/
|
|
||||||
if( vips_file_save_jpeg_write( jpeg ) )
|
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
@ -825,8 +129,6 @@ vips_file_save_jpeg_class_init( VipsFileSaveJpegClass *class )
|
|||||||
VipsFileClass *file_class = (VipsFileClass *) class;
|
VipsFileClass *file_class = (VipsFileClass *) class;
|
||||||
VipsFileSaveClass *save_class = (VipsFileSaveClass *) class;
|
VipsFileSaveClass *save_class = (VipsFileSaveClass *) class;
|
||||||
|
|
||||||
gobject_class->dispose = vips_file_save_jpeg_dispose;
|
|
||||||
gobject_class->finalize = vips_file_save_jpeg_finalize;
|
|
||||||
gobject_class->set_property = vips_object_set_property;
|
gobject_class->set_property = vips_object_set_property;
|
||||||
gobject_class->get_property = vips_object_get_property;
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
@ -858,8 +160,5 @@ static void
|
|||||||
vips_file_save_jpeg_init( VipsFileSaveJpeg *jpeg )
|
vips_file_save_jpeg_init( VipsFileSaveJpeg *jpeg )
|
||||||
{
|
{
|
||||||
jpeg->Q = 75;
|
jpeg->Q = 75;
|
||||||
jpeg->cinfo.err = jpeg_std_error( &jpeg->eman.pub );
|
|
||||||
jpeg->eman.pub.error_exit = vips__new_error_exit;
|
|
||||||
jpeg->eman.pub.output_message = vips__new_output_message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1113
libvips/file/vips2jpeg.c
Normal file
1113
libvips/file/vips2jpeg.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -85,13 +85,10 @@ vips_file_load_vips_header( VipsFileLoad *load )
|
|||||||
/* Remove the @out that's there now.
|
/* Remove the @out that's there now.
|
||||||
*/
|
*/
|
||||||
g_object_get( load, "out", &out, NULL );
|
g_object_get( load, "out", &out, NULL );
|
||||||
g_object_set( load, "out", out2, NULL );
|
g_object_unref( out );
|
||||||
|
g_object_unref( out );
|
||||||
|
|
||||||
/* Unref after we install the new out to make sure load isn't
|
g_object_set( load, "out", out2, NULL );
|
||||||
* disposed.
|
|
||||||
*/
|
|
||||||
g_object_unref( out );
|
|
||||||
g_object_unref( out );
|
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ typedef struct _VipsFileLoadClass {
|
|||||||
|
|
||||||
/* Set the header fields in @out from @filename. If you can read the
|
/* Set the header fields in @out from @filename. If you can read the
|
||||||
* whole image as well with no performance cost (as with vipsload),
|
* whole image as well with no performance cost (as with vipsload),
|
||||||
* leave ->load() NULL.
|
* leave ->load() NULL and only @header will be used.
|
||||||
*/
|
*/
|
||||||
int (*header)( VipsFileLoad * );
|
int (*header)( VipsFileLoad * );
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user