diff --git a/ChangeLog b/ChangeLog index 1a282ac1..024a81f3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,10 @@ - support 16-bit palette TIFFs, plus palette TIFFs can have an alpha - libgsf is now required - dzsave can directly write a ZIP file +- add ".vips" as an alternative suffix for vips files +- added vips_tiffload_buffer() +- added vips_foreign_load_buffer(), vips_foreign_save_buffer() +- added vips_object_set_from_string() 6/3/14 started 7.38.6 - grey ramp minimum was wrong diff --git a/TODO b/TODO index b3a4177b..e908969c 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,30 @@ +- use vips_object_set_from_string() for vips_foreign_load_options() and + friends + + + +- use this for dzsave_buffer + + + +- clean up foreign.c, there seems to be some cruft + +- vips_filename_suffix_match() is used by + vips_foreign_load_new_from_foreign_sub(), but it splits on ':' ... argh! + + deprecate this thing and stop ':' split + + vips_foreign_find_save() should split on [], there's something to find the + start of the rightmost [] pair, use that + + see also vips_foreign_find_save_buffer() + + + + +- support 1/2/4 bit palette tiff with alpha + - can we use postbuild elsewhere? look at use of "preclose" / "written", etc. diff --git a/compile b/compile new file mode 100755 index 00000000..531136b0 --- /dev/null +++ b/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/configure.ac b/configure.ac index be910efd..8ae7d2c1 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,8 @@ AC_PREREQ(2.62) # gobject-introspection recommends -Wno-portability # foreign stops complaints about a missing README (we use README.md instead) # and missing INSTALL (the standard Gnu INSTALL is not very useful) -AM_INIT_AUTOMAKE([-Wno-portability foreign]) +# subdir-objects lets us have dumy.cc in a subdir +AM_INIT_AUTOMAKE([-Wno-portability foreign subdir-objects]) AC_CONFIG_HEADERS(config.h) AC_CONFIG_MACRO_DIR([m4]) diff --git a/libvips/convolution/gaussblur.c b/libvips/convolution/gaussblur.c index 8b1c253c..c41ea12b 100644 --- a/libvips/convolution/gaussblur.c +++ b/libvips/convolution/gaussblur.c @@ -73,7 +73,7 @@ vips_gaussblur_build( VipsObject *object ) /* Stop at 20% of max ... bit mean, but means mask radius is roughly * right. */ - if( vips_gaussmat( &t[0], gaussblur->radius / 2, 0.2, + if( vips_gaussmat( &t[0], gaussblur->radius / 2.0, 0.2, "separable", TRUE, "integer", gaussblur->precision != VIPS_PRECISION_FLOAT, NULL ) ) @@ -162,8 +162,8 @@ vips_gaussblur_init( VipsGaussblur *gaussblur ) * This operator runs vips_gaussmat() and vips_convsep() for you on an image. * * @radius is not used directly. Instead the standard deviation of - * vips_gaussmat() is set to @radius / 2 and the minimum amplitude set to 20%. - * This gives a mask radius of approximately @radius pixels. + * vips_gaussmat() is set to @radius / 2.0 and the minimum amplitude set to + * 20%. This gives a mask radius of approximately @radius pixels. * * See also: vips_gaussmat(), vips_conv(). * diff --git a/libvips/deprecated/format.c b/libvips/deprecated/format.c index be3849ec..36bbe1c0 100644 --- a/libvips/deprecated/format.c +++ b/libvips/deprecated/format.c @@ -346,7 +346,7 @@ vips_format_get_flags( VipsFormatClass *format, const char *filename ) /* VIPS format class. */ -static const char *vips_suffs[] = { ".v", NULL }; +static const char *vips_suffs[] = { ".v", ".vips", NULL }; int im_isvips( const char *filename ) diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index eb91a9b8..6d3b03b6 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -87,10 +87,12 @@ * the file name extension) with JPEG compression. * * |[ - * vips_foreign_save_options (my_image, "frank.tiff[compression=jpeg]"); + * vips_foreign_save_options (my_image, "frank.tiff[compression=jpeg]", NULL); * ]| * - * Is the same thing, but with the option in the filename. + * Is the same thing, but with the option in the filename. You can put + * name - value pairs after the filename as well: these will override any + * options set in the filename. * * You can also invoke the operations directly, for example: * @@ -108,9 +110,8 @@ * transparently supported by vips_image_new_from_file() and friends. * * VIPS comes with VipsForeign for TIFF, JPEG, PNG, Analyze, PPM, OpenEXR, CSV, - * Matlab, Radiance, RAW, FITS and VIPS. It also - * includes import filters which can - * load with libMagick and with OpenSlide. + * Matlab, Radiance, RAW, FITS, WebP and VIPS. It also includes import filters + * which can load with libMagick and with OpenSlide. */ /** @@ -159,7 +160,7 @@ * Add a new loader to VIPS by subclassing #VipsForeignLoad. Subclasses need to * implement at least @header(). * - * As a complete example, here's the code for the PNG loader, minus the actual + * As a complete example, here's code for a PNG loader, minus the actual * calls to libpng. * * |[ @@ -505,7 +506,7 @@ vips_foreign_load_new_from_foreign_sub( VipsForeignLoadClass *load_class, * * Searches for an operation you could use to load @filename. * - * See also: vips_foreign_read(). + * See also: vips_foreign_load(). * * Returns: the name of an operation on success, %NULL on error */ @@ -532,6 +533,46 @@ vips_foreign_find_load( const char *filename ) return( G_OBJECT_CLASS_NAME( load_class ) ); } +/* Can this VipsForeign open this buffer? + */ +static void * +vips_foreign_find_load_buffer_sub( VipsForeignLoadClass *load_class, + void **buf, size_t *len ) +{ + if( load_class->is_a_buffer && + load_class->is_a_buffer( *buf, *len ) ) + return( load_class ); + + return( NULL ); +} + +/** + * vips_foreign_find_load: + * @filename: file to find a loader for + * + * Searches for an operation you could use to load @filename. + * + * See also: vips_foreign_load_buffer(). + * + * Returns: the name of an operation on success, %NULL on error + */ +const char * +vips_foreign_find_load_buffer( void *buf, size_t len ) +{ + VipsForeignLoadClass *load_class; + + if( !(load_class = (VipsForeignLoadClass *) vips_foreign_map( + "VipsForeignLoad", + (VipsSListMap2Fn) vips_foreign_find_load_buffer_sub, + &buf, &len )) ) { + vips_error( "VipsForeignLoad", + "%s", _( "buffer is not in a known format" ) ); + return( NULL ); + } + + return( G_OBJECT_CLASS_NAME( load_class ) ); +} + /** * vips_foreign_find_load_options: * @filename: file to find a loader for @@ -1022,7 +1063,7 @@ vips_foreign_find_save_sub( VipsForeignSaveClass *save_class, * @filename may not contain embedded options. See * vips_foreign_find_save_options() if your filename may have options in. * - * See also: vips_foreign_write(). + * See also: vips_foreign_save(). * * Returns: the name of an operation on success, %NULL on error */ @@ -1044,6 +1085,55 @@ vips_foreign_find_save( const char *filename ) return( G_OBJECT_CLASS_NAME( save_class ) ); } +/* Can we write this buffer with this file type? + */ +static void * +vips_foreign_find_save_buffer_sub( VipsForeignSaveClass *save_class, + const char *suffix ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( save_class ); + VipsForeignClass *class = VIPS_FOREIGN_CLASS( save_class ); + + if( class->suffs && + vips_ispostfix( object_class->nickname, "_buffer" ) && + vips_filename_suffix_match( suffix, class->suffs ) ) + return( save_class ); + + return( NULL ); +} + +/** + * vips_foreign_find_save_buffer: + * @suffix: name to find a saver for + * + * Searches for an operation you could use to write to a buffer in @suffix + * format. + * + * @filename may not contain embedded options. See + * vips_foreign_find_save_options() if your filename may have options in. + * + * See also: vips_foreign_save_buffer(). + * + * Returns: the name of an operation on success, %NULL on error + */ +const char * +vips_foreign_find_save_buffer( const char *suffix ) +{ + VipsForeignSaveClass *save_class; + + if( !(save_class = (VipsForeignSaveClass *) vips_foreign_map( + "VipsForeignSave", + (VipsSListMap2Fn) vips_foreign_find_save_buffer_sub, + (void *) suffix, NULL )) ) { + vips_error( "VipsForeignSave", + _( "\"%s\" is not a known file format" ), suffix ); + + return( NULL ); + } + + return( G_OBJECT_CLASS_NAME( save_class ) ); +} + /** * vips_foreign_find_save_options: * @filename: name to find a saver for @@ -1051,7 +1141,7 @@ vips_foreign_find_save( const char *filename ) * Searches for an operation you could use to write to @filename. * * @filename may contain embedded options. See - * vips_foreign_find_save() if your filename does not options in. + * vips_foreign_find_save() if your filename does not have options in. * * See also: vips_foreign_write(). * @@ -1473,6 +1563,44 @@ vips_foreign_load( const char *filename, VipsImage **out, ... ) return( result ); } +/** + * vips_foreign_load_buffer: + * @buf: start of memory buffer + * @len: length of memory buffer + * @out: output image + * @...: %NULL-terminated list of optional named arguments + * + * Loads @buf, @len into @out using the loader recommended by + * vips_foreign_find_load_buffer(). + * + * See also: vips_foreign_save(), vips_foreign_load_options(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_foreign_load_buffer( void *buf, size_t len, VipsImage **out, ... ) +{ + const char *operation; + VipsArea *area; + va_list ap; + int result; + + if( !(operation = vips_foreign_find_load_buffer( buf, len )) ) + return( -1 ); + + /* We don't take a copy of the data or free it. + */ + area = vips_area_new_blob( NULL, buf, len ); + + va_start( ap, out ); + result = vips_call_split( operation, ap, area, out ); + va_end( ap ); + + vips_area_unref( area ); + + return( result ); +} + /** * vips_foreign_save: * @in: image to write @@ -1504,6 +1632,99 @@ vips_foreign_save( VipsImage *in, const char *filename, ... ) return( result ); } +/** + * vips_foreign_save_buffer: + * @in: image to write + * @suffix: format to write + * @buf: return buffer start here + * @len: return buffer length here + * @...: %NULL-terminated list of optional named arguments + * + * Saves @in to a memory buffer selected from @suffix. @suffix may also set + * save options, for example it could be ".jpg[Q=80]". + * Save options may also be given + * as a NULL-terminated list of name-value pairs. + * + * See also: vips_foreign_load_buffer(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_foreign_save_buffer( VipsImage *in, + const char *suffix, void **buf, size_t *len, + ... ) +{ + char str[VIPS_PATH_MAX]; + char *p; + const char *operation_name; + VipsOperation *operation; + VipsArea *area; + va_list ap; + int result; + + /* Take any [options] off the suffix. + */ + vips_strncpy( str, suffix, VIPS_PATH_MAX ); + if( (p = (char *) vips__find_rightmost_brackets( str )) ) + *p = '\0'; + + if( !(operation_name = vips_foreign_find_save_buffer( str )) ) + return( -1 ); + + if( !(operation = vips_operation_new( operation_name )) ) + return( -1 ); + + g_object_set( operation, "in", in, NULL ); + + /* Now set any operation args from the options list. + */ + if( (p = (char *) vips__find_rightmost_brackets( suffix )) && + vips_object_set_from_string( VIPS_OBJECT( operation ), p ) ) { + vips_object_unref_outputs( VIPS_OBJECT( operation ) ); + g_object_unref( operation ); + return( -1 ); + } + + /* Set any from varargs. + */ + va_start( ap, len ); + result = vips_object_set_valist( VIPS_OBJECT( operation ), ap ); + va_end( ap ); + + if( result ) { + vips_object_unref_outputs( VIPS_OBJECT( operation ) ); + g_object_unref( operation ); + return( -1 ); + } + + if( vips_cache_operation_buildp( &operation ) ) { + vips_object_unref_outputs( VIPS_OBJECT( operation ) ); + g_object_unref( operation ); + return( -1 ); + } + + g_object_get( operation, "buffer", &area, NULL ); + + /* Getting @buffer will have upped its count so it'll be safe. + * We can junk all other outputs, + */ + vips_object_unref_outputs( VIPS_OBJECT( operation ) ); + g_object_unref( operation ); + + if( area ) { + if( buf ) { + *buf = area->data; + area->free_fn = NULL; + } + if( len ) + *len = area->length; + + vips_area_unref( area ); + } + + return( 0 ); +} + /** * vips_foreign_load_options: * @filename: file to load @@ -1651,7 +1872,8 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_jpeg_file_get_type( void ); extern GType vips_foreign_save_jpeg_buffer_get_type( void ); extern GType vips_foreign_save_jpeg_mime_get_type( void ); - extern GType vips_foreign_load_tiff_get_type( void ); + extern GType vips_foreign_load_tiff_file_get_type( void ); + extern GType vips_foreign_load_tiff_buffer_get_type( void ); extern GType vips_foreign_save_tiff_get_type( void ); extern GType vips_foreign_load_vips_get_type( void ); extern GType vips_foreign_save_vips_get_type( void ); @@ -1709,7 +1931,8 @@ vips_foreign_operation_init( void ) #endif /*HAVE_LIBWEBP*/ #ifdef HAVE_TIFF - vips_foreign_load_tiff_get_type(); + vips_foreign_load_tiff_file_get_type(); + vips_foreign_load_tiff_buffer_get_type(); vips_foreign_save_tiff_get_type(); #endif /*HAVE_TIFF*/ @@ -1807,6 +2030,44 @@ vips_tiffload( const char *filename, VipsImage **out, ... ) return( result ); } +/** + * vips_tiffload_buffer: + * @buf: memory area to load + * @len: size of memory area + * @out: image to write + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @page: load this page + * + * Read a TIFF-formatted memory block into a VIPS image. Exactly as + * vips_tiffload(), but read from a memory source. + * + * See also: vips_tiffload(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_tiffload_buffer( void *buf, size_t len, VipsImage **out, ... ) +{ + va_list ap; + VipsArea *area; + int result; + + /* We don't take a copy of the data or free it. + */ + area = vips_area_new_blob( NULL, buf, len ); + + va_start( ap, out ); + result = vips_call_split( "tiffload_buffer", ap, area, out ); + va_end( ap ); + + vips_area_unref( area ); + + return( result ); +} + /** * vips_tiffsave: * @in: image to save @@ -1899,46 +2160,6 @@ vips_tiffsave( VipsImage *in, const char *filename, ... ) return( result ); } -/** - * vips_jpegload_buffer: - * @buf: memory area to load - * @len: size of memory area - * @out: image to write - * @...: %NULL-terminated list of optional named arguments - * - * Read a JPEG-formatted memory block into a VIPS image. It can read most - * 8-bit JPEG images, including CMYK and YCbCr. - * - * This function is handy for processing JPEG image thumbnails. - * - * Caution: on return only the header will have been read, the pixel data is - * not decompressed until the first pixel is read. Therefore you must not free - * @buf until you have read pixel data from @out. - * - * See also: vips_jpegload(). - * - * Returns: 0 on success, -1 on error. - */ -int -vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... ) -{ - va_list ap; - VipsArea *area; - int result; - - /* We don't take a copy of the data or free it. - */ - area = vips_area_new_blob( NULL, buf, len ); - - va_start( ap, out ); - result = vips_call_split( "jpegload_buffer", ap, area, out ); - va_end( ap ); - - vips_area_unref( area ); - - return( result ); -} - /** * vips_jpegload: * @filename: file to load @@ -2008,6 +2229,45 @@ vips_jpegload( const char *filename, VipsImage **out, ... ) return( result ); } +/** + * vips_jpegload_buffer: + * @buf: memory area to load + * @len: size of memory area + * @out: image to write + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @shrink: shrink by this much on load + * @fail: fail on warnings + * + * Read a JPEG-formatted memory block into a VIPS image. Exactly as + * vips_jpegload(), but read from a memory buffer. + * + * See also: vips_jpegload(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... ) +{ + va_list ap; + VipsArea *area; + int result; + + /* We don't take a copy of the data or free it. + */ + area = vips_area_new_blob( NULL, buf, len ); + + va_start( ap, out ); + result = vips_call_split( "jpegload_buffer", ap, area, out ); + va_end( ap ); + + vips_area_unref( area ); + + return( result ); +} + /** * vips_jpegsave: * @in: image to save @@ -2121,7 +2381,7 @@ vips_jpegsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) *buf = area->data; area->free_fn = NULL; } - if( buf ) + if( len ) *len = area->length; vips_area_unref( area ); @@ -2282,7 +2542,7 @@ vips_webpsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) *buf = area->data; area->free_fn = NULL; } - if( buf ) + if( len ) *len = area->length; vips_area_unref( area ); @@ -2611,7 +2871,7 @@ vips_pngsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) *buf = area->data; area->free_fn = NULL; } - if( buf ) + if( len ) *len = area->length; vips_area_unref( area ); diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 0eabd0fa..2483de3c 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -1247,14 +1247,27 @@ vips__jpeg_read_buffer( void *buf, size_t len, VipsImage *out, return( result ); } +int +vips__isjpeg_buffer( void *buf, size_t len ) +{ + guchar *str = (guchar *) buf; + + if( len >= 2 && + str[0] == 0xff && + str[1] == 0xd8 ) + return( 1 ); + + return( 0 ); +} + int vips__isjpeg( const char *filename ) { unsigned char buf[2]; - if( vips__get_bytes( filename, buf, 2 ) ) - if( (int) buf[0] == 0xff && (int) buf[1] == 0xd8 ) - return( 1 ); + if( vips__get_bytes( filename, buf, 2 ) && + vips__isjpeg_buffer( buf, 2 ) ) + return( 1 ); return( 0 ); } diff --git a/libvips/foreign/jpegload.c b/libvips/foreign/jpegload.c index c24dd690..7c9c1609 100644 --- a/libvips/foreign/jpegload.c +++ b/libvips/foreign/jpegload.c @@ -287,6 +287,12 @@ vips_foreign_load_jpeg_buffer_load( VipsForeignLoad *load ) return( 0 ); } +static gboolean +vips_foreign_load_jpeg_buffer_is_a( void *buf, size_t len ) +{ + return( vips__isjpeg_buffer( buf, len ) ); +} + static void vips_foreign_load_jpeg_buffer_class_init( VipsForeignLoadJpegBufferClass *class ) @@ -301,6 +307,7 @@ vips_foreign_load_jpeg_buffer_class_init( object_class->nickname = "jpegload_buffer"; object_class->description = _( "load jpeg from buffer" ); + load_class->is_a_buffer = vips_foreign_load_jpeg_buffer_is_a; load_class->header = vips_foreign_load_jpeg_buffer_header; load_class->load = vips_foreign_load_jpeg_buffer_load; diff --git a/libvips/foreign/jpegsave.c b/libvips/foreign/jpegsave.c index 5ced2b36..5c54e537 100644 --- a/libvips/foreign/jpegsave.c +++ b/libvips/foreign/jpegsave.c @@ -112,6 +112,7 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; gobject_class->set_property = vips_object_set_property; @@ -120,6 +121,8 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class ) object_class->nickname = "jpegsave_base"; object_class->description = _( "save jpeg" ); + foreign_class->suffs = vips__jpeg_suffs; + save_class->saveable = VIPS_SAVEABLE_RGB_CMYK; save_class->format_table = bandfmt_jpeg; @@ -204,7 +207,6 @@ vips_foreign_save_jpeg_file_class_init( VipsForeignSaveJpegFileClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; - VipsForeignClass *foreign_class = (VipsForeignClass *) class; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -213,8 +215,6 @@ vips_foreign_save_jpeg_file_class_init( VipsForeignSaveJpegFileClass *class ) object_class->description = _( "save image to jpeg file" ); object_class->build = vips_foreign_save_jpeg_file_build; - foreign_class->suffs = vips__jpeg_suffs; - VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "Filename to save to" ), diff --git a/libvips/foreign/pngload.c b/libvips/foreign/pngload.c index a4954f66..2d1ba065 100644 --- a/libvips/foreign/pngload.c +++ b/libvips/foreign/pngload.c @@ -198,6 +198,7 @@ vips_foreign_load_png_buffer_class_init( VipsForeignLoadPngBufferClass *class ) object_class->nickname = "pngload_buffer"; object_class->description = _( "load png from buffer" ); + load_class->is_a_buffer = vips__png_ispng_buffer; load_class->header = vips_foreign_load_png_buffer_header; load_class->load = vips_foreign_load_png_buffer_load; diff --git a/libvips/foreign/tiff.h b/libvips/foreign/tiff.h index 5090df53..4cb489d1 100644 --- a/libvips/foreign/tiff.h +++ b/libvips/foreign/tiff.h @@ -50,12 +50,18 @@ int vips__tiff_write( VipsImage *in, const char *filename, gboolean bigtiff, gboolean rgbjpeg ); -int vips__tiff_read( const char *filename, VipsImage *out, int page, - gboolean readbehind ); int vips__tiff_read_header( const char *filename, VipsImage *out, int page ); +int vips__tiff_read( const char *filename, VipsImage *out, + int page, gboolean readbehind ); gboolean vips__istifftiled( const char *filename ); +gboolean vips__istiff_buffer( void *buf, size_t len ); gboolean vips__istiff( const char *filename ); +int vips__tiff_read_header_buffer( void *buf, size_t len, VipsImage *out, + int page ); +int vips__tiff_read_buffer( void *buf, size_t len, VipsImage *out, + int page, gboolean readbehind ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index 8ffcc79b..19ec69e0 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -144,6 +144,8 @@ * 11/4/14 * - support 16 bits per sample palette images * - palette images can have an alpha + * 22/4/14 + * - add read from buffer */ /* @@ -208,6 +210,8 @@ typedef struct _ReadTiff { /* Parameters. */ char *filename; + void *buf; + size_t len; VipsImage *out; int page; gboolean readbehind; @@ -225,6 +229,10 @@ typedef struct _ReadTiff { */ gboolean memcpy; + /* The current 'file pointer' for memory buffers. + */ + size_t pos; + /* Geometry. */ uint32 twidth, theight; /* Tile size */ @@ -1635,15 +1643,16 @@ readtiff_destroy( VipsObject *object, ReadTiff *rtiff ) } static ReadTiff * -readtiff_new( const char *filename, VipsImage *out, int page, - gboolean readbehind ) +readtiff_new( VipsImage *out, int page, gboolean readbehind ) { ReadTiff *rtiff; if( !(rtiff = VIPS_NEW( out, ReadTiff )) ) return( NULL ); - rtiff->filename = vips_strdup( VIPS_OBJECT( out ), filename ); + rtiff->filename = NULL; + rtiff->buf = NULL; + rtiff->len = 0; rtiff->out = out; rtiff->page = page; rtiff->readbehind = readbehind; @@ -1651,6 +1660,7 @@ readtiff_new( const char *filename, VipsImage *out, int page, rtiff->sfn = NULL; rtiff->client = NULL; rtiff->memcpy = FALSE; + rtiff->pos = 0; rtiff->twidth = 0; rtiff->theight = 0; rtiff->separate = FALSE; @@ -1669,33 +1679,138 @@ readtiff_new( const char *filename, VipsImage *out, int page, return( rtiff ); } -/* Pull out the nth directory from a TIFF file. - */ -static TIFF * -get_directory( const char *filename, int page ) +static ReadTiff * +readtiff_new_filename( const char *filename, VipsImage *out, int page, + gboolean readbehind ) { - TIFF *tif; + ReadTiff *rtiff; int i; + if( !(rtiff = readtiff_new( out, page, readbehind )) ) + return( NULL ); + + rtiff->filename = vips_strdup( VIPS_OBJECT( out ), filename ); + /* No mmap --- no performance advantage with libtiff, and it burns up * our VM if the tiff file is large. */ - if( !(tif = TIFFOpen( filename, "rm" )) ) { - vips_error( "tiff2vips", - _( "unable to open \"%s\" for input" ), + if( !(rtiff->tiff = TIFFOpen( filename, "rm" )) ) { + vips_error( "tiff2vips", _( "unable to open \"%s\" for input" ), filename ); return( NULL ); } for( i = 0; i < page; i++ ) - if( !TIFFReadDirectory( tif ) ) { - /* Run out of directories. - */ - TIFFClose( tif ); + if( !TIFFReadDirectory( rtiff->tiff ) ) { + vips_error( "tiff2vips", + _( "TIFF does not contain page %d" ), + rtiff->page ); return( NULL ); } - return( tif ); + return( rtiff ); +} + +static tsize_t +my_tiff_read( thandle_t st, tdata_t buffer, tsize_t size ) +{ + ReadTiff *rtiff = (ReadTiff *) st; + + size_t available = rtiff->len - rtiff->pos; + size_t copy = VIPS_MIN( size, available ); + + memcpy( buffer, rtiff->buf + rtiff->pos, copy ); + rtiff->pos += copy; + + return( copy ); +} + +static tsize_t +my_tiff_write( thandle_t st, tdata_t buffer, tsize_t size ) +{ + g_assert( 0 ); + + return( 0 ); +} + +static int +my_tiff_close( thandle_t st ) +{ + return 0; +} + +static toff_t +my_tiff_seek( thandle_t st, toff_t pos, int whence ) +{ + ReadTiff *rtiff = (ReadTiff *) st; + + if( whence == SEEK_SET ) + rtiff->pos = pos; + else if( whence == SEEK_CUR ) + rtiff->pos += pos; + else if( whence == SEEK_END ) + rtiff->pos = rtiff->len + pos; + else + g_assert( 0 ); + + return( rtiff->pos ); +} + +static toff_t +my_tiff_size( thandle_t st ) +{ + ReadTiff *rtiff = (ReadTiff *) st; + + return( rtiff->len ); +} + +static int +my_tiff_map( thandle_t st, tdata_t *start, toff_t *len ) +{ + g_assert( 0 ); + + return 0; +} + +static void +my_tiff_unmap( thandle_t st, tdata_t start, toff_t len ) +{ + g_assert( 0 ); + + return; +} + +static ReadTiff * +readtiff_new_buffer( void *buf, size_t len, VipsImage *out, int page, + gboolean readbehind ) +{ + ReadTiff *rtiff; + int i; + + if( !(rtiff = readtiff_new( out, page, readbehind )) ) + return( NULL ); + + rtiff->buf = buf; + rtiff->len = len; + + if( !(rtiff->tiff = TIFFClientOpen( "memory buffer", "rm", + (thandle_t) rtiff, + my_tiff_read, my_tiff_write, my_tiff_seek, my_tiff_close, + my_tiff_size, my_tiff_map, my_tiff_unmap )) ) { + vips_error( "tiff2vips", "%s", + _( "unable to open memory buffer for input" ) ); + return( NULL ); + } + + for( i = 0; i < page; i++ ) + if( !TIFFReadDirectory( rtiff->tiff ) ) { + vips_error( "tiff2vips", + _( "TIFF does not contain page %d" ), + rtiff->page ); + return( NULL ); + } + + return( rtiff ); } /* @@ -1732,15 +1847,10 @@ vips__tiff_read( const char *filename, VipsImage *out, int page, vips__tiff_init(); - if( !(rtiff = readtiff_new( filename, out, page, readbehind )) ) + if( !(rtiff = readtiff_new_filename( filename, + out, page, readbehind )) ) return( -1 ); - if( !(rtiff->tiff = get_directory( rtiff->filename, rtiff->page )) ) { - vips_error( "tiff2vips", _( "TIFF file does not " - "contain page %d" ), rtiff->page ); - return( -1 ); - } - if( TIFFIsTiled( rtiff->tiff ) ) { if( read_tilewise( rtiff, out ) ) return( -1 ); @@ -1760,16 +1870,9 @@ vips__tiff_read_header( const char *filename, VipsImage *out, int page ) vips__tiff_init(); - if( !(rtiff = readtiff_new( filename, out, page, FALSE )) ) + if( !(rtiff = readtiff_new_filename( filename, out, page, FALSE )) ) return( -1 ); - if( !(rtiff->tiff = get_directory( rtiff->filename, rtiff->page )) ) { - vips_error( "tiff2vips", - _( "TIFF file does not contain page %d" ), - rtiff->page ); - return( -1 ); - } - if( parse_header( rtiff, out ) ) return( -1 ); @@ -1795,16 +1898,72 @@ vips__istifftiled( const char *filename ) } gboolean -vips__istiff( const char *filename ) +vips__istiff_buffer( void *buf, size_t len ) { - unsigned char buf[2]; + char *str = (char *) buf; - if( vips__get_bytes( filename, buf, 2 ) ) - if( (buf[0] == 'M' && buf[1] == 'M') || - (buf[0] == 'I' && buf[1] == 'I') ) - return( TRUE ); + if( len >= 2 && + ((str[0] == 'M' && str[1] == 'M') || + (str[0] == 'I' && str[1] == 'I')) ) + return( TRUE ); return( FALSE ); } +gboolean +vips__istiff( const char *filename ) +{ + unsigned char buf[2]; + + if( vips__get_bytes( filename, buf, 2 ) && + vips__istiff_buffer( buf, 2 ) ) + return( TRUE ); + + return( FALSE ); +} + +int +vips__tiff_read_header_buffer( void *buf, size_t len, VipsImage *out, int page ) +{ + ReadTiff *rtiff; + + vips__tiff_init(); + + if( !(rtiff = readtiff_new_buffer( buf, len, out, page, FALSE )) ) + return( -1 ); + + if( parse_header( rtiff, out ) ) + return( -1 ); + + return( 0 ); +} + +int +vips__tiff_read_buffer( void *buf, size_t len, VipsImage *out, + int page, gboolean readbehind ) +{ + ReadTiff *rtiff; + +#ifdef DEBUG + printf( "tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() ); + printf( "tiff2vips: libtiff starting for %s\n", filename ); +#endif /*DEBUG*/ + + vips__tiff_init(); + + if( !(rtiff = readtiff_new_buffer( buf, len, out, page, readbehind )) ) + return( -1 ); + + if( TIFFIsTiled( rtiff->tiff ) ) { + if( read_tilewise( rtiff, out ) ) + return( -1 ); + } + else { + if( read_stripwise( rtiff, out ) ) + return( -1 ); + } + + return( 0 ); +} + #endif /*HAVE_TIFF*/ diff --git a/libvips/foreign/tiffload.c b/libvips/foreign/tiffload.c index cbcff533..a5c19b95 100644 --- a/libvips/foreign/tiffload.c +++ b/libvips/foreign/tiffload.c @@ -55,10 +55,6 @@ typedef struct _VipsForeignLoadTiff { VipsForeignLoad parent_object; - /* Filename for load. - */ - char *filename; - /* Load this page. */ int page; @@ -67,11 +63,58 @@ typedef struct _VipsForeignLoadTiff { typedef VipsForeignLoadClass VipsForeignLoadTiffClass; -G_DEFINE_TYPE( VipsForeignLoadTiff, vips_foreign_load_tiff, +G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadTiff, vips_foreign_load_tiff, VIPS_TYPE_FOREIGN_LOAD ); +static void +vips_foreign_load_tiff_class_init( VipsForeignLoadTiffClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + /* Other libraries may be using libtiff, we want to capture tiff + * warning and error as soon as we can. + * + * This class init will be triggered during startup. + */ + vips__tiff_init(); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "tiffload_base"; + object_class->description = _( "load tiff" ); + + VIPS_ARG_INT( class, "page", 10, + _( "Page" ), + _( "Load this page from the image" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadTiff, page ), + 0, 100000, 0 ); +} + +static void +vips_foreign_load_tiff_init( VipsForeignLoadTiff *tiff ) +{ + tiff->page = 0; +} + +typedef struct _VipsForeignLoadTiffFile { + VipsForeignLoadTiff parent_object; + + /* Filename for load. + */ + char *filename; + +} VipsForeignLoadTiffFile; + +typedef VipsForeignLoadTiffClass VipsForeignLoadTiffFileClass; + +G_DEFINE_TYPE( VipsForeignLoadTiffFile, vips_foreign_load_tiff_file, + vips_foreign_load_tiff_get_type() ); + static VipsForeignFlags -vips_foreign_load_tiff_get_flags_filename( const char *filename ) +vips_foreign_load_tiff_file_get_flags_filename( const char *filename ) { VipsForeignFlags flags; @@ -85,30 +128,33 @@ vips_foreign_load_tiff_get_flags_filename( const char *filename ) } static VipsForeignFlags -vips_foreign_load_tiff_get_flags( VipsForeignLoad *load ) +vips_foreign_load_tiff_file_get_flags( VipsForeignLoad *load ) { - VipsForeignLoadTiff *tiff = (VipsForeignLoadTiff *) load; + VipsForeignLoadTiffFile *file = (VipsForeignLoadTiffFile *) load; - return( vips_foreign_load_tiff_get_flags_filename( tiff->filename ) ); + return( vips_foreign_load_tiff_file_get_flags_filename( + file->filename ) ); } static int -vips_foreign_load_tiff_header( VipsForeignLoad *load ) +vips_foreign_load_tiff_file_header( VipsForeignLoad *load ) { VipsForeignLoadTiff *tiff = (VipsForeignLoadTiff *) load; + VipsForeignLoadTiffFile *file = (VipsForeignLoadTiffFile *) load; - if( vips__tiff_read_header( tiff->filename, load->out, tiff->page ) ) + if( vips__tiff_read_header( file->filename, load->out, tiff->page ) ) return( -1 ); return( 0 ); } static int -vips_foreign_load_tiff_load( VipsForeignLoad *load ) +vips_foreign_load_tiff_file_load( VipsForeignLoad *load ) { VipsForeignLoadTiff *tiff = (VipsForeignLoadTiff *) load; + VipsForeignLoadTiffFile *file = (VipsForeignLoadTiffFile *) load; - if( vips__tiff_read( tiff->filename, load->real, tiff->page, + if( vips__tiff_read( file->filename, load->real, tiff->page, load->access == VIPS_ACCESS_SEQUENTIAL ) ) return( -1 ); @@ -118,20 +164,13 @@ vips_foreign_load_tiff_load( VipsForeignLoad *load ) const char *vips__foreign_tiff_suffs[] = { ".tif", ".tiff", NULL }; static void -vips_foreign_load_tiff_class_init( VipsForeignLoadTiffClass *class ) +vips_foreign_load_tiff_file_class_init( VipsForeignLoadTiffFileClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; VipsForeignClass *foreign_class = (VipsForeignClass *) class; VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; - /* Other libraries may be using libtiff, we want to capture tiff - * warning and error as soon as we can. - * - * This class init will be triggered during startup. - */ - vips__tiff_init(); - gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -142,28 +181,95 @@ vips_foreign_load_tiff_class_init( VipsForeignLoadTiffClass *class ) load_class->is_a = vips__istiff; load_class->get_flags_filename = - vips_foreign_load_tiff_get_flags_filename; - load_class->get_flags = vips_foreign_load_tiff_get_flags; - load_class->header = vips_foreign_load_tiff_header; - load_class->load = vips_foreign_load_tiff_load; + vips_foreign_load_tiff_file_get_flags_filename; + load_class->get_flags = vips_foreign_load_tiff_file_get_flags; + load_class->header = vips_foreign_load_tiff_file_header; + load_class->load = vips_foreign_load_tiff_file_load; VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "Filename to load from" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadTiff, filename ), + G_STRUCT_OFFSET( VipsForeignLoadTiffFile, filename ), NULL ); - - VIPS_ARG_INT( class, "page", 10, - _( "Page" ), - _( "Load this page from the file" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadTiff, page ), - 0, 100000, 0 ); } static void -vips_foreign_load_tiff_init( VipsForeignLoadTiff *tiff ) +vips_foreign_load_tiff_file_init( VipsForeignLoadTiffFile *file ) +{ +} + +typedef struct _VipsForeignLoadTiffBuffer { + VipsForeignLoadTiff parent_object; + + /* Load from a buffer. + */ + VipsArea *buf; + +} VipsForeignLoadTiffBuffer; + +typedef VipsForeignLoadTiffClass VipsForeignLoadTiffBufferClass; + +G_DEFINE_TYPE( VipsForeignLoadTiffBuffer, vips_foreign_load_tiff_buffer, + vips_foreign_load_tiff_get_type() ); + +static int +vips_foreign_load_tiff_buffer_header( VipsForeignLoad *load ) +{ + VipsForeignLoadTiff *tiff = (VipsForeignLoadTiff *) load; + VipsForeignLoadTiffBuffer *buffer = (VipsForeignLoadTiffBuffer *) load; + + if( vips__tiff_read_header_buffer( + buffer->buf->data, buffer->buf->length, load->out, + tiff->page ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_foreign_load_tiff_buffer_load( VipsForeignLoad *load ) +{ + VipsForeignLoadTiff *tiff = (VipsForeignLoadTiff *) load; + VipsForeignLoadTiffBuffer *buffer = (VipsForeignLoadTiffBuffer *) load; + + if( vips__tiff_read_buffer( + buffer->buf->data, buffer->buf->length, load->real, + tiff->page, + load->access == VIPS_ACCESS_SEQUENTIAL ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_load_tiff_buffer_class_init( + VipsForeignLoadTiffBufferClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "tiffload_buffer"; + object_class->description = _( "load tiff from buffer" ); + + load_class->is_a_buffer = vips__istiff_buffer; + load_class->header = vips_foreign_load_tiff_buffer_header; + load_class->load = vips_foreign_load_tiff_buffer_load; + + VIPS_ARG_BOXED( class, "buffer", 1, + _( "Buffer" ), + _( "Buffer to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadTiffBuffer, buf ), + VIPS_TYPE_BLOB ); +} + +static void +vips_foreign_load_tiff_buffer_init( VipsForeignLoadTiffBuffer *buffer ) { } diff --git a/libvips/foreign/vipsjpeg.h b/libvips/foreign/vipsjpeg.h index 3e2872c2..c4271add 100644 --- a/libvips/foreign/vipsjpeg.h +++ b/libvips/foreign/vipsjpeg.h @@ -46,6 +46,7 @@ int vips__jpeg_write_buffer( VipsImage *in, gboolean optimize_coding, gboolean progressive, gboolean strip, gboolean no_subsample ); +int vips__isjpeg_buffer( void *buf, size_t len ); int vips__isjpeg( const char *filename ); int vips__jpeg_read_file( const char *name, VipsImage *out, gboolean header_only, int shrink, gboolean fail, gboolean readbehind ); diff --git a/libvips/foreign/vipsload.c b/libvips/foreign/vipsload.c index 54d7b324..dd8a445f 100644 --- a/libvips/foreign/vipsload.c +++ b/libvips/foreign/vipsload.c @@ -105,7 +105,7 @@ vips_foreign_load_vips_header( VipsForeignLoad *load ) return( 0 ); } -static const char *vips_suffs[] = { ".v", NULL }; +const char *vips__suffs[] = { ".v", ".vips", NULL }; static void vips_foreign_load_vips_class_init( VipsForeignLoadVipsClass *class ) @@ -121,7 +121,7 @@ vips_foreign_load_vips_class_init( VipsForeignLoadVipsClass *class ) object_class->nickname = "vipsload"; object_class->description = _( "load vips from file" ); - foreign_class->suffs = vips_suffs; + foreign_class->suffs = vips__suffs; load_class->is_a = vips_foreign_load_vips_is_a; load_class->get_flags = vips_foreign_load_vips_get_flags; diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index 5062741a..31ae0895 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -561,13 +561,23 @@ vips__png_read( const char *filename, VipsImage *out, gboolean readbehind ) return( 0 ); } +int +vips__png_ispng_buffer( void *buf, size_t len ) +{ + if( len >= 8 && + !png_sig_cmp( buf, 0, 8 ) ) + return( TRUE ); + + return( FALSE ); +} + int vips__png_ispng( const char *filename ) { unsigned char buf[8]; return( vips__get_bytes( filename, buf, 8 ) && - !png_sig_cmp( buf, 0, 8 ) ); + vips__png_ispng_buffer( buf, 8 ) ); } static void diff --git a/libvips/foreign/vipspng.h b/libvips/foreign/vipspng.h index 051ac55b..0bcd6467 100644 --- a/libvips/foreign/vipspng.h +++ b/libvips/foreign/vipspng.h @@ -37,6 +37,7 @@ extern "C" { int vips__png_header( const char *name, VipsImage *out ); int vips__png_read( const char *name, VipsImage *out, gboolean readbehind ); +int vips__png_ispng_buffer( void *buf, size_t len ); int vips__png_ispng( const char *filename ); gboolean vips__png_isinterlaced( const char *filename ); extern const char *vips__png_suffs[]; diff --git a/libvips/foreign/vipssave.c b/libvips/foreign/vipssave.c index 47af18fc..74929a80 100644 --- a/libvips/foreign/vipssave.c +++ b/libvips/foreign/vipssave.c @@ -75,7 +75,9 @@ vips_foreign_save_vips_build( VipsObject *object ) return( 0 ); } -static const char *vips_suffs[] = { ".v", NULL }; +/* From vipsload.c. + */ +extern const char *vips__suffs[]; static void vips_foreign_save_vips_class_init( VipsForeignSaveVipsClass *class ) @@ -94,7 +96,7 @@ vips_foreign_save_vips_class_init( VipsForeignSaveVipsClass *class ) object_class->description = _( "save image to vips file" ); object_class->build = vips_foreign_save_vips_build; - foreign_class->suffs = vips_suffs; + foreign_class->suffs = vips__suffs; save_class->saveable = VIPS_SAVEABLE_ANY; for( i = 0; i < VIPS_CODING_LAST; i++ ) diff --git a/libvips/foreign/webp.h b/libvips/foreign/webp.h index 7441b893..28bf0906 100644 --- a/libvips/foreign/webp.h +++ b/libvips/foreign/webp.h @@ -37,6 +37,7 @@ extern "C" { extern const char *vips__webp_suffs[]; +int vips__iswebp_buffer( void *buf, size_t len ); int vips__iswebp( const char *filename ); int vips__webp_read_file_header( const char *name, VipsImage *out ); diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index 2611d4ff..2bd93736 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -87,13 +87,23 @@ typedef struct { WebPIDecoder *idec; } Read; +int +vips__iswebp_buffer( void *buf, size_t len ) +{ + if( len >= MINIMAL_HEADER && + WebPGetInfo( buf, MINIMAL_HEADER, NULL, NULL ) ) + return( 1 ); + + return( 0 ); +} + int vips__iswebp( const char *filename ) { unsigned char header[MINIMAL_HEADER]; if( vips__get_bytes( filename, header, MINIMAL_HEADER ) && - WebPGetInfo( header, MINIMAL_HEADER, NULL, NULL ) ) + vips__iswebp_buffer( header, MINIMAL_HEADER ) ) return( 1 ); return( 0 ); diff --git a/libvips/foreign/webpload.c b/libvips/foreign/webpload.c index 2099e8bf..9dd79249 100644 --- a/libvips/foreign/webpload.c +++ b/libvips/foreign/webpload.c @@ -233,6 +233,7 @@ vips_foreign_load_webp_buffer_class_init( object_class->nickname = "webpload_buffer"; object_class->description = _( "load webp from buffer" ); + load_class->is_a_buffer = vips__iswebp_buffer; load_class->header = vips_foreign_load_webp_buffer_header; load_class->load = vips_foreign_load_webp_buffer_load; diff --git a/libvips/foreign/webpsave.c b/libvips/foreign/webpsave.c index 432e6e96..d1342ba7 100644 --- a/libvips/foreign/webpsave.c +++ b/libvips/foreign/webpsave.c @@ -81,6 +81,7 @@ vips_foreign_save_webp_class_init( VipsForeignSaveWebpClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; gobject_class->set_property = vips_object_set_property; @@ -89,6 +90,8 @@ vips_foreign_save_webp_class_init( VipsForeignSaveWebpClass *class ) object_class->nickname = "webpsave_base"; object_class->description = _( "save webp" ); + foreign_class->suffs = vips__webp_suffs; + save_class->saveable = VIPS_SAVEABLE_RGBA; save_class->format_table = bandfmt_webp; @@ -151,7 +154,6 @@ vips_foreign_save_webp_file_class_init( VipsForeignSaveWebpFileClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; - VipsForeignClass *foreign_class = (VipsForeignClass *) class; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -160,8 +162,6 @@ vips_foreign_save_webp_file_class_init( VipsForeignSaveWebpFileClass *class ) object_class->description = _( "save image to webp file" ); object_class->build = vips_foreign_save_webp_file_build; - foreign_class->suffs = vips__webp_suffs; - VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "Filename to save to" ), diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index a2ab314f..d539618e 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -161,6 +161,13 @@ typedef struct _VipsForeignLoadClass { */ gboolean (*is_a)( const char * ); + /* Is a buffer in this format. + * + * This function should return %TRUE if the buffer contains an image of + * this type. + */ + gboolean (*is_a_buffer)( void *, size_t ); + /* Get the flags from a filename. * * This function should examine the file and return a set @@ -207,6 +214,7 @@ typedef struct _VipsForeignLoadClass { GType vips_foreign_load_get_type( void ); const char *vips_foreign_find_load( const char *filename ); +const char *vips_foreign_find_load_buffer( void *buf, size_t len ); const char *vips_foreign_find_load_options( const char *filename ); VipsForeignFlags vips_foreign_flags( const char *loader, const char *filename ); @@ -300,6 +308,7 @@ typedef struct _VipsForeignSaveClass { GType vips_foreign_save_get_type( void ); const char *vips_foreign_find_save( const char *filename ); +const char * vips_foreign_find_save_buffer( const char *suffix ); const char *vips_foreign_find_save_options( const char *filename ); /* Read/write an image convenience functions. @@ -314,6 +323,12 @@ int vips_foreign_load_options( const char *filename, VipsImage **out, ... ) int vips_foreign_save_options( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); +int vips_foreign_load_buffer( void *buf, size_t len, VipsImage **out, ... ) + __attribute__((sentinel)); +int vips_foreign_save_buffer( VipsImage *in, + const char *suffix, void **buf, size_t *len, ... ) + __attribute__((sentinel)); + int vips_openslideload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); @@ -397,6 +412,8 @@ typedef enum { int vips_tiffload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_tiffload_buffer( void *buf, size_t len, VipsImage **out, ... ) + __attribute__((sentinel)); int vips_tiffsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index a23af7ec..2489c439 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -587,6 +587,7 @@ VipsObject *vips_object_new( GType type, int vips_object_set_valist( VipsObject *object, va_list ap ); int vips_object_set( VipsObject *object, ... ) __attribute__((sentinel)); +int vips_object_set_from_string( VipsObject *object, const char *string ); VipsObject *vips_object_new_from_string( VipsObjectClass *object_class, const char *p ); diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index dd07472c..70cf40eb 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -2054,7 +2054,7 @@ vips_object_set_valist( VipsObject *object, va_list ap ) * Input arguments are given in-line, output arguments are given as pointers * to where the output value should be written. * - * See also: vips_object_set_valist(). + * See also: vips_object_set_valist(), vips_object_set_from_string(). * * Returns: 0 on success, -1 on error */ @@ -2072,7 +2072,7 @@ vips_object_set( VipsObject *object, ... ) } /* Set object args from a string. @p should be the initial left bracket and - * there should be no tokens after the matching right bracket. + * there should be no tokens after the matching right bracket. @p is modified. */ static int vips_object_set_args( VipsObject *object, const char *p ) @@ -2154,6 +2154,44 @@ vips_object_set_args( VipsObject *object, const char *p ) return( 0 ); } +/** + * vips_object_set_from_string: + * @object: object to set arguments on + * @string: arguments as a string + * + * Set object arguments from a string. The string can be something like + * "a=12", or "a = 12, b = 13", or "fred". The string can optionally be + * enclosed in brackets. + * + * You'd typically use this between creating the object and building it. + * + * See also: vips_object_set(), vips_object_build(), + * vips_cache_operation_buildp(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_object_set_from_string( VipsObject *object, const char *string ) +{ + const char *q; + VipsToken token; + char buffer[VIPS_PATH_MAX]; + char str[VIPS_PATH_MAX]; + + vips_strncpy( buffer, string, VIPS_PATH_MAX ); + + /* Does string start with a bracket? If it doesn't, enclose the whole + * thing in []. + */ + if( !(q = vips__token_get( buffer, &token, str, VIPS_PATH_MAX )) || + token != VIPS_TOKEN_LEFT ) + vips_snprintf( buffer, VIPS_PATH_MAX, "[%s]", string ); + else + vips_strncpy( buffer, string, VIPS_PATH_MAX ); + + return( vips_object_set_args( object, buffer ) ); +} + VipsObject * vips_object_new_from_string( VipsObjectClass *object_class, const char *p ) { @@ -2177,7 +2215,7 @@ vips_object_new_from_string( VipsObjectClass *object_class, const char *p ) /* More tokens there? Set any other args. */ if( q && - vips_object_set_args( object, q ) ) { + vips_object_set_from_string( object, q ) ) { g_object_unref( object ); return( NULL ); } diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c index 2a816b90..59ebc04d 100644 --- a/libvips/iofuncs/util.c +++ b/libvips/iofuncs/util.c @@ -765,16 +765,13 @@ vips__file_open_write( const char *filename, gboolean text_mode ) char * vips__file_read( FILE *fp, const char *filename, unsigned int *length_out ) { - long len; + gint64 len; size_t read; char *str; - /* Find length. - */ - fseek( fp, 0L, 2 ); - len = ftell( fp ); - if( len > 20 * 1024 * 1024 ) { - /* Seems crazy! + len = vips_file_length( fileno( fp ) ); + if( len > 1024 * 1024 * 1024 ) { + /* Over a gb? Seems crazy! */ vips_error( "vips__file_read", _( "\"%s\" too long" ), filename );