[cairo] API Shakeup: cairo_output_stream_t and cairo_surface_finish()

Kristian Høgsberg krh at bitplanet.net
Tue Feb 15 20:42:14 PST 2005


Hi,

Here is a proposal for an output stream abstraction to be used for
cairo's output stream needs.  We add a new object, 
cairo_output_stream_t, to the user API:

   typedef cairo_status_t (*cairo_output_stream_write_func_t)
	(const void *data,
	 size_t length,
	 void *closure);


   cairo_output_stream_t *
   cairo_output_stream_create
	(cairo_output_stream_write_func_t    write_func,
	 cairo_destroy_func_t                destroy_func,
	 void                                *closure);

   void
   cairo_output_stream_destroy
	(cairo_output_stream_t *stream);

A cairo_output_stream_t object is passed to the 
cairo_pdf_surface_create() constructor:

   s = cairo_pdf_surface_create (stream, width_inches, height_inches);

and the image surface helper function to write PNG files will take a stream:

   cairo_image_surface_write_png (surface, stream);

We still provide stdio convenience functions:

   FILE *fp;

   fp = fopen (...);
   cairo_pdf_surface_create_for_file (fp, width_inches, height_inches);

This just wraps the stdio functions in a cairo_output_stream_object_t 
and calls cairo_pdf_surface_create ().  Owen suggests we use filenames 
with the convenience functions instead, but that has encoding problems 
on Windows and using stdio makes it easy to use in a web graphics 
application.

Finally, there is cairo_surface_finish(), which we discussed Monday and 
which has been discussed on the list earlier.  The proposal at this 
point is:

   cairo_status_t
   cairo_surface_finish (cairo_surface_t *surface);

This function will write out the file trailer, or in general, complete 
the surface and drop all references to external resources.  For the PDF 
backend it means that the FILE will not be used anymore and can be 
closed, for the Xlib backend it means the drawable can be freed. 
Further drawing the the surface will not affect the surface but set the 
surface status to an error.  When cairo_surface_finish() is called, 
cairo will destroy the output stream at which point the output stream 
destroy_func will be called to release resources associated with the 
output stream.  After this, cairo is guaranteed to no longer call the 
write_func.

When the reference count for a surface reaches zero, cairo will call
cairo_surface_finish() if it hasn't been called already, before
destroying the surface.


For internal backend use the following functions will be available:

   cairo_private cairo_status_t
   cairo_output_stream_write (cairo_output_stream_t *stream,
                              const void *data, size_t length);

   cairo_private cairo_status_t
   cairo_output_stream_printf (cairo_output_stream_t *stream,
                               const char *fmt, ...);

   cairo_private cairo_status_t
   cairo_output_stream_status (cairo_output_stream_t *stream);

The reason for having cairo_output_stream_status() is that in case of 
failure the output functions become inert, so you can do:

   cairo_output_stream_write (stream, data1, data1_size);
   cairo_output_stream_write (stream, data2, data2_size);

   return cairo_output_stream_status (stream);

and get the right behavior even if the first write fails.

Issues:

- cairo takes ownership of the cairo_output_stream_t when you create a 
surface for the output stream, and frees it after calling the 
destroy_func.  Maybe the solution is to just not do this, rename 
destroy_func to finish_func, and let the user call 
cairo_stream_destroy() either after calling cairo_surface_finish() or in 
a user data destroy callback.

- the write_func is a user provided callback that will be called from 
deep inside cairo internals... keithp, is this something you object to?

- should we keep the cairo_output_stream_t object out of the public API
entirely and make the user pass in write_func, destroy_func, and closure 
as extra arguments to the surface create functions?

cheers,
Kristian




More information about the cairo mailing list