Skip to content
This repository was archived by the owner on Jun 8, 2021. It is now read-only.

cairo_surface_create*() can return errors #141

Closed
federicomenaquintero opened this issue Jul 7, 2017 · 10 comments
Closed

cairo_surface_create*() can return errors #141

federicomenaquintero opened this issue Jul 7, 2017 · 10 comments

Comments

@federicomenaquintero
Copy link
Contributor

Cairo's surface creation functions can return errors in the form of a valid cairo_surface_t* but whose cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS.

For example, cairo_image_surface_create() can return CAIRO_STATUS_INVALID_SIZE if the passed size is greater than what Pixman supports, or it can return CAIRO_STATUS_NO_MEMORY if allocation fails.

Cairo-rs similarly returns a valid Surface (or ImageSurface) whose status must be checked before proceeding.

Should we return a Result<Surface, Status> (or the corresponding Result<ImageSurface, Status>) instead?

@GuillaumeGomez
Copy link
Member

Seems like so. Good suggestion!

@federicomenaquintero
Copy link
Contributor Author

Are API-breaking PRs accepted? :)

@GuillaumeGomez
Copy link
Member

Yup, we already have a few. The next release will be a "middle" one.

@federicomenaquintero
Copy link
Contributor Author

I have a question about possible APIs.

Let's say you do

let raw_surface = ffi::cairo_image_surface_create(Format::ARgb32, 50000, 50000);

Cairo will return a surface with a status of CAIRO_STATUS_INVALID_SIZE, as pixman only supports sizes up to 32767. For a valid size, it could return CAIRO_STATUS_NO_MEMORY. Also, cairo_image_surface_create_for_data() could return CAIRO_STATUS_INVALID_STRIDE, etc.

At first I was thinking that the cairo-rs functions that create surfaces could simply return Result<ImageSurface, Status>. However, the functions that convert raw pointers into Rust structures should simply return an ImageSurface which possibly have a status != Success. This includes ImageSurface::from_raw_full() and the corresponding impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for ImageSurface. The rationale is that we can keep a Rust-like API for the creation functions, but for data that comes from elsewhere, we just pass it through the wrapper and let the user check for statuses. That is, it would look like

impl ImageSurface {
    pub fn create(format: Format, width: i32, height: i32) -> Result<ImageSurface, Status> { ... }
    pub fn create_for_data<F>(data: Box<[u8]>, free: F, format: Format, width: i32, height: i32, 
                              stride: i32) -> Result<ImageSurface, Status> { ... }

    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> ImageSurface { ... }
}

But then I thought, that's kind of asymmetrical. What if we instead had

impl ImageSurface {
    pub fn create(format: Format, width: i32, height: i32) -> Result<ImageSurface, ImageSurface> { ... }
    pub fn create_for_data<F>(data: Box<[u8]>, free: F, format: Format, width: i32, height: i32, 
                              stride: i32) -> Result<ImageSurface, ImageSurface> { ... }

    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> ImageSurface { ... }
}

Note that both Ok() and Err() have an ImageSurface inside them. The guarantee then would be that if Ok(surf), then the surface's status is Success, and if Err(surf), then it has an error status and the surface is essentially unusable (you can call Cairo methods on it, but they won't do anything).

As before, ImageSurface::from_raw_full() would simply wrap the pointer and give you an ImageSurface which may have an error status.

Opinions are appreciated :)

@federicomenaquintero
Copy link
Contributor Author

The API is looking like this: federicomenaquintero@7f79123

@EPashkin
Copy link
Member

EPashkin commented Jul 8, 2017

API look good, but not sure if it works good with all surfaces.
Minimal alternative is adding check or to_result function to SurfaceExt

@federicomenaquintero
Copy link
Contributor Author

After talking with @GuillaumeGomez on IRC, I pushed an updated version to my repository. This one returns Result<ImageSurface, Status>. The code is at https://github.com/federicomenaquintero/cairo/tree/image-surface-error

@EPashkin
Copy link
Member

EPashkin commented Aug 8, 2017

LGFM.
@federicomenaquintero Thanks

@federicomenaquintero
Copy link
Contributor Author

Pull request for cairo - #146

Pull request for lgpl-docs - gtk-rs/lgpl-docs#30

@federicomenaquintero
Copy link
Contributor Author

Closing now that this is in place.

federicomenaquintero added a commit to federicomenaquintero/cairo that referenced this issue Oct 9, 2019
…esult

Cairo's surface creation functions never return NULL; instead they
always return a surface, but it may be in an error state.  In gtk-rs#141 we
started making the binding functions return Result for this; some
returned the plain FooSurface type, some others Option<FooSurface>.

This makes the following functions return Result<FooSurface, Status>

  Surface::create_similar()
  Surface::create_similar_image()
  RecordingSurface::create()
  Device.surface_create()
  Device.surface_create_for_target()

The foundation for all of this is that Surface::from_raw_full() now
also returns Result<FooSurface, Status>.  This is to make things
consistent with ImageSurface::from_raw_full().  Analogously, we now
have RecordingSurface::from_raw_full() that also returns Result.

Fixes gtk-rs#251
federicomenaquintero added a commit to federicomenaquintero/cairo that referenced this issue Oct 10, 2019
…esult

Cairo's surface creation functions never return NULL; instead they
always return a surface, but it may be in an error state.  In gtk-rs#141 we
started making the binding functions return Result for this; some
returned the plain FooSurface type, some others Option<FooSurface>.

This makes the following functions return Result<FooSurface, Status>

  Surface::create_similar()
  Surface::create_similar_image()

  Device.surface_create()
  Device.surface_create_for_target()
  PdfSurface::new()
  RecordingSurface::create()
  RecordingSurface::from_raw_full()
  SvgSurface::new()
  XCBSurface::create()
  XCBSurface::create_for_bitmap()
  XCBSurface::create_with_xrender_format()

  From macro for_stream_constructors!:
  *::for_stream()
  *::for_raw_stream()

The foundation for all of this is that Surface::from_raw_full() now
also returns Result<FooSurface, Status>.  This is to make things
consistent with ImageSurface::from_raw_full().  Analogously, we now
have RecordingSurface::from_raw_full() that also returns Result.

Fixes gtk-rs#251
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants