Skip to content

Instantly share code, notes, and snippets.

@jcupitt
Created June 23, 2014 12:42
Show Gist options
  • Save jcupitt/bfe69f5fcac3e372e29a to your computer and use it in GitHub Desktop.
Save jcupitt/bfe69f5fcac3e372e29a to your computer and use it in GitHub Desktop.
test subclassing vips streams
/* subclass VipsStream to do read/write a memory area
*
* compile with:
*
* gcc -g -Wall subclass-stream.c `pkg-config vips --cflags --libs`
*
* test with:
*
* cat ~/pics/k2.jpg ~/pics/k4.jpg | ./a.out | cat > x
*/
#include <string.h>
#include <errno.h>
#include <vips/vips.h>
/* Subclass VipsStreamInput to read from a mem buffer.
*/
typedef struct _BufferInput {
VipsStreamInput parent_object;
/*< private >*/
/* A large area of memory not owned by us.
*/
unsigned char *buffer;
size_t length;
/* Our read position.
*/
unsigned char *next_byte;
size_t bytes_available;
} BufferInput;
typedef struct _BufferInputClass {
VipsStreamInputClass parent_class;
/* No class members, but they'd go here if there were.
*/
} BufferInputClass;
/* This macro defines a new class called BufferInput (you have to give the
* underscore version of the name as well) which subclasses
* VIPS_TYPE_STREAM_INPUT.
*/
G_DEFINE_TYPE( BufferInput, buffer_input, VIPS_TYPE_STREAM_INPUT );
/* Dispose is run during unref. The object needs to unlink from any other
* objects it is connected to. _dispose() can run many times.
*
* There's also finalize, which is run when an object is freed up. It's a
* better place to free any associated memory,
*/
static void
buffer_input_dispose( GObject *gobject )
{
BufferInput *input = (BufferInput *) gobject;
/* Do something with input.
*/
g_assert( input );
/* Chain up to our parent's _dispose().
*/
G_OBJECT_CLASS( buffer_input_parent_class )->dispose( gobject );
}
/* Our _read() method. This should behave exactly as read(2).
*/
static ssize_t
buffer_input_read( VipsStreamInput *stream,
unsigned char *buffer, size_t buffer_size )
{
BufferInput *input = (BufferInput *) stream;
size_t possible = VIPS_MIN( buffer_size, input->bytes_available );
memcpy( buffer, input->next_byte, possible );
input->next_byte += possible;
input->bytes_available -= possible;
printf( "buffer_input_read: read %zd bytes\n", possible );
return( possible );
}
static void
buffer_input_class_init( BufferInputClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsStreamInputClass *input_class = VIPS_STREAM_INPUT_CLASS( class );
gobject_class->dispose = buffer_input_dispose;
/* Override the _read() method.
*/
input_class->read = buffer_input_read;
/* Other class init goes here, eg. creating properties or signals.
*/
}
static void
buffer_input_init( BufferInput *input )
{
/* Object init goes here.
*/
}
static BufferInput *
buffer_input_new( unsigned char *buffer, size_t length )
{
BufferInput *input;
input = (BufferInput *) g_object_new( buffer_input_get_type(), NULL );
input->buffer = buffer;
input->length = length;
input->next_byte = buffer;
input->bytes_available = length;
if( vips_object_build( VIPS_OBJECT( input ) ) ) {
VIPS_UNREF( input );
return( NULL );
}
return( input );
}
/* Subclass VipsStreamOutput to write to a fixed-size mem buffer not owned by
* us.
*/
typedef struct _BufferOutput {
VipsStreamOutput parent_object;
/*< private >*/
/* A large area of memory not owned by us.
*/
unsigned char *buffer;
size_t length;
/* Our write position.
*/
unsigned char *next_byte;
size_t bytes_available;
} BufferOutput;
typedef struct _BufferOutputClass {
VipsStreamOutputClass parent_class;
/* No class members, but they'd go here if there were.
*/
} BufferOutputClass;
G_DEFINE_TYPE( BufferOutput, buffer_output, VIPS_TYPE_STREAM_OUTPUT );
/* Our _write() method. This should behave exactly as write(2).
*/
static ssize_t
buffer_output_write( VipsStreamOutput *stream,
const unsigned char *buffer, size_t buffer_size )
{
BufferOutput *output = (BufferOutput *) stream;
size_t possible = VIPS_MIN( buffer_size, output->bytes_available );
if( possible == 0 ) {
/* Memory buffer full: set overflow.
*/
errno = EFBIG;
return( -1 );
}
memcpy( output->next_byte, buffer, possible );
output->next_byte += possible;
output->bytes_available -= possible;
printf( "buffer_output_write: written %zd bytes\n", possible );
return( possible );
}
static void
buffer_output_class_init( BufferOutputClass *class )
{
VipsStreamOutputClass *output_class = VIPS_STREAM_OUTPUT_CLASS( class );
output_class->write = buffer_output_write;
}
static void
buffer_output_init( BufferOutput *output )
{
}
static BufferOutput *
buffer_output_new( unsigned char *buffer, size_t length )
{
BufferOutput *output;
output = (BufferOutput *)
g_object_new( buffer_output_get_type(), NULL );
output->buffer = buffer;
output->length = length;
output->next_byte = buffer;
output->bytes_available = length;
if( vips_object_build( VIPS_OBJECT( output ) ) ) {
VIPS_UNREF( output );
return( NULL );
}
return( output );
}
#define OUTPUT_BUFFER_SIZE (1000000)
int
main( int argc, char **argv )
{
GOptionContext *context;
GOptionGroup *main_group;
GError *error = NULL;
unsigned char *input_buffer;
unsigned int length;
unsigned char *output_buffer;
VipsStreamInput *in_stream;
VipsStreamOutput *out_stream;
if( vips_init( argv[0] ) )
vips_error_exit( NULL );
context = g_option_context_new( "subclass-stream infile outfile - "
"test streaming between memory areas" );
main_group = g_option_group_new( NULL, NULL, NULL, NULL, NULL );
g_option_context_set_main_group( context, main_group );
g_option_context_add_group( context, vips_get_option_group() );
if( !g_option_context_parse( context, &argc, &argv, &error ) ) {
if( error ) {
fprintf( stderr, "%s\n", error->message );
g_error_free( error );
}
vips_error_exit( NULL );
}
if( argc < 3 )
vips_error_exit( "usage: %s infile outfile", argv[0] );
/* Load the whole of the input file into memory and attach a stream to
* it.
*/
if( !(input_buffer = (unsigned char *)
vips__file_read_name( argv[1], NULL, &length )) )
vips_error_exit( NULL );
if( !(in_stream = (VipsStreamInput *)
buffer_input_new( input_buffer, length )) )
vips_error_exit( NULL );
/* Allocate an output area and attach a stream to that.
*/
if( !(output_buffer = vips_malloc( NULL, OUTPUT_BUFFER_SIZE )) )
vips_error_exit( NULL );
if( !(out_stream = (VipsStreamOutput *)
buffer_output_new( output_buffer, OUTPUT_BUFFER_SIZE )) )
vips_error_exit( NULL );
while( !vips_stream_input_eof( in_stream ) ) {
VipsImage *x;
VipsImage *y;
if( !(x = vips_image_new_from_stream( in_stream, "", NULL )) ||
vips_invert( x, &y, NULL ) ||
vips_image_write_to_stream( y,
".jpg", out_stream, NULL ) )
vips_error_exit( NULL );
g_object_unref( x );
g_object_unref( y );
}
g_object_unref( in_stream );
g_object_unref( out_stream );
g_free( input_buffer );
g_free( output_buffer );
return( 0 );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment