Created
June 23, 2014 12:42
-
-
Save jcupitt/bfe69f5fcac3e372e29a to your computer and use it in GitHub Desktop.
test subclassing vips streams
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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