///|
/// An owning handle to a Cairo drawing destination or source.
///
/// Every backend uses this same opaque wrapper. It holds one internal
/// `cairo_surface_t` owner, releases it through the raw object's finalizer, and
/// may retain a parent surface or caller pixel buffer when Cairo depends on it.
/// Identity equality and hashing compare the underlying Cairo pointer.
struct Surface(@surface_impl.RawSurface)
///|
fn Surface::from_raw(raw : @surface_impl.RawSurface) -> Surface {
Surface(raw)
}
///|
fn Surface::to_raw(self : Surface) -> @surface_impl.RawSurface {
self.0
}
///|
fn surface_status_from_raw(raw : Int) -> Status {
status_from_raw(raw) catch {
_ => InvalidStatus
}
}
///|
fn check_surface_status_raw(raw : Int) -> Unit raise CairoError {
check_status(status_from_raw(raw))
}
///|
fn surface_type_from_raw(raw : Int) -> SurfaceType raise CairoError {
match raw {
0 => SurfaceTypeImage
1 => SurfaceTypePdf
2 => SurfaceTypePs
3 => SurfaceTypeXlib
4 => SurfaceTypeXcb
5 => SurfaceTypeGlitz
6 => SurfaceTypeQuartz
7 => SurfaceTypeWin32
8 => SurfaceTypeBeos
9 => SurfaceTypeDirectfb
10 => SurfaceTypeSvg
11 => SurfaceTypeOs2
12 => SurfaceTypeWin32Printing
13 => SurfaceTypeQuartzImage
14 => SurfaceTypeScript
15 => SurfaceTypeQt
16 => SurfaceTypeRecording
17 => SurfaceTypeVg
18 => SurfaceTypeGl
19 => SurfaceTypeDrm
20 => SurfaceTypeTee
21 => SurfaceTypeXml
22 => SurfaceTypeSkia
23 => SurfaceTypeSubsurface
24 => SurfaceTypeCogl
_ =>
raise CairoInvalidArgument(
InvalidStatus,
"unknown cairo surface type: \{raw}",
)
}
}
///|
fn checked_c_string_bytes(value : String) -> Bytes raise CairoError {
let bytes = @utf8.encode(value)
check_no_embedded_nul(bytes)
bytes
}
///|
fn checked_path_bytes(path : String) -> Bytes raise CairoError {
checked_c_string_bytes(path)
}
///|
/// Create a surface compatible with this surface's backend.
///
/// `content` selects color and alpha channels; dimensions are backend device
/// units and must be non-negative. The returned surface is an independent
/// owner, although Cairo may choose a different backend internally. Invalid
/// sizes and source-surface failures raise the corresponding `CairoError`.
pub fn Surface::create_similar(
self : Surface,
content : Content,
width : Int,
height : Int,
) -> Surface raise CairoError {
let status = Ref(0)
let raw = @surface_impl.create_similar_raw(
self.to_raw(),
content.to_raw(),
width,
height,
status,
)
check_surface_status_raw(status.val)
check_surface_status_raw(@surface_impl.status_raw(raw))
Surface::from_raw(raw)
}
///|
/// Create a compatible surface from a pycairo-style raw `cairo_content_t`.
///
/// Accepted values are `0x1000`, `0x2000`, and `0x3000`, matching Cairo's
/// color, alpha, and color-alpha constants. Unknown integers raise
/// `CairoInvalidArgument(InvalidContent, _)`; dimensions and source errors use
/// the same checked behavior as `create_similar()`.
pub fn Surface::create_similar_raw(
self : Surface,
content : Int,
width : Int,
height : Int,
) -> Surface raise CairoError {
let status = Ref(0)
let raw = @surface_impl.create_similar_raw(
self.to_raw(),
checked_content_raw(content),
width,
height,
status,
)
check_surface_status_raw(status.val)
check_surface_status_raw(@surface_impl.status_raw(raw))
Surface::from_raw(raw)
}
///|
/// Create a compatible image surface with an explicit pixel format.
///
/// Unlike `create_similar()`, a successful result is always an image surface
/// and owns storage independent of `self`. `width` and `height` are pixels and
/// must be non-negative. Invalid formats, sizes, or source state raise their
/// checked `CairoError` status.
pub fn Surface::create_similar_image(
self : Surface,
format : Format,
width : Int,
height : Int,
) -> Surface raise CairoError {
let status = Ref(0)
let raw = @surface_impl.create_similar_image_raw(
self.to_raw(),
format.to_raw(),
width,
height,
status,
)
check_surface_status_raw(status.val)
check_surface_status_raw(@surface_impl.status_raw(raw))
Surface::from_raw(raw)
}
///|
/// Create a compatible image surface from a raw `cairo_format_t` integer.
///
/// This entry point preserves pycairo C-int compatibility, including formats
/// supported by the linked Cairo version. The result is still checked and
/// owned exactly like `create_similar_image()`; unknown values raise
/// `CairoInvalidArgument(InvalidFormat, _)`.
pub fn Surface::create_similar_image_raw(
self : Surface,
format : Int,
width : Int,
height : Int,
) -> Surface raise CairoError {
let status = Ref(0)
let raw = @surface_impl.create_similar_image_raw(
self.to_raw(),
format,
width,
height,
status,
)
check_surface_status_raw(status.val)
check_surface_status_raw(@surface_impl.status_raw(raw))
Surface::from_raw(raw)
}
///|
/// Create a rectangular subsurface that redirects drawing into `self`.
///
/// The rectangle is expressed in this surface's device-space units. Drawing to
/// the child is translated and clipped to that rectangle; drawing outside it
/// is discarded. Cairoon retains the parent wrapper for the child's lifetime.
/// Negative dimensions or an unusable parent raise checked `CairoError`.
pub fn Surface::create_for_rectangle(
self : Surface,
x : Double,
y : Double,
width : Double,
height : Double,
) -> Surface raise CairoError {
let status = Ref(0)
let raw = @surface_impl.create_for_rectangle_raw(
self.to_raw(),
x,
y,
width,
height,
status,
)
check_surface_status_raw(status.val)
check_surface_status_raw(@surface_impl.status_raw(raw))
Surface::from_raw(raw)
}
///|
/// Return this surface's current Cairo status without raising.
///
/// This diagnostic also reports `SurfaceFinished` after cairoon has explicitly
/// finished a successful surface. Safe operations check and raise statuses
/// themselves, so callers do not need to poll this method after each call.
pub fn Surface::status(self : Surface) -> Status {
surface_status_from_raw(@surface_impl.status_raw(self.to_raw()))
}
///|
/// Return whether two wrappers refer to the same `cairo_surface_t`.
///
/// This is pointer identity, not pixel or document-content equality. Borrowed
/// surfaces returned by Context or Pattern bridges compare equal to the owner
/// they reference.
pub fn Surface::equal(self : Surface, other : Surface) -> Bool {
@surface_impl.equal_raw(self.to_raw(), other.to_raw())
}
///|
/// Return a process-local hash of the underlying Cairo surface pointer.
///
/// Equal live surfaces have equal hashes. The value is meaningful only for
/// identity-based collections in the current process and is not stable across
/// runs.
pub fn Surface::hash(self : Surface) -> UInt64 {
@surface_impl.hash_raw(self.to_raw())
}
///|
pub impl Eq for Surface with fn equal(self, other) {
self.equal(other)
}
///|
pub impl Hash for Surface with fn hash(self) {
self.hash().hash()
}
///|
pub impl Hash for Surface with fn hash_combine(self, hasher) {
hasher.combine_uint64(self.hash())
}
///|
fn content_from_raw(raw : Int) -> Content raise CairoError {
match raw {
0x1000 => ContentColor
0x2000 => ContentAlpha
0x3000 => ContentColorAlpha
_ =>
raise CairoInvalidArgument(InvalidStatus, "unknown cairo content: \{raw}")
}
}
///|
fn Content::to_raw(self : Content) -> Int {
match self {
ContentColor => 0x1000
ContentAlpha => 0x2000
ContentColorAlpha => 0x3000
}
}
///|
fn checked_content_raw(raw : Int) -> Int raise CairoError {
match raw {
0x1000 | 0x2000 | 0x3000 => raw
_ => raise CairoInvalidArgument(InvalidContent, InvalidContent.message())
}
}
///|
/// Return the typed color/alpha content carried by this surface.
///
/// The value describes channels, not a concrete pixel layout. Finished or
/// otherwise failed surfaces raise their checked `CairoError` status.
pub fn Surface::get_content(self : Surface) -> Content raise CairoError {
content_from_raw(self.get_content_raw())
}
///|
/// Return the raw `cairo_content_t` integer for pycairo-compatible code.
///
/// Successful values are the exact Cairo ABI constants `0x1000`, `0x2000`, or
/// `0x3000`. Surface failures are checked before the integer is returned.
pub fn Surface::get_content_raw(self : Surface) -> Int raise CairoError {
check_surface_status_raw(@surface_impl.status_raw(self.to_raw()))
@surface_impl.get_content_raw(self.to_raw())
}
///|
/// Return the Cairo backend type of this surface.
///
/// This identifies image, PDF, PS, SVG, recording, tee, subsurface, and other
/// Cairo backends; it does not transfer ownership. A failed or finished surface
/// raises its current `CairoError` status.
pub fn Surface::get_type(self : Surface) -> SurfaceType raise CairoError {
check_surface_status_raw(@surface_impl.status_raw(self.to_raw()))
surface_type_from_raw(@surface_impl.get_type_raw(self.to_raw()))
}
///|
/// Return this surface's backend device, when it has one.
///
/// Cairo returns a borrowed device; cairoon takes a native reference and wraps
/// it as an independently owned `Device`, so the result remains valid after the
/// surface wrapper leaves scope. Image and other device-less surfaces return
/// `None`. Surface or device errors raise checked `CairoError`.
pub fn Surface::get_device(self : Surface) -> Device? raise CairoError {
let has_device = Ref(0)
let status = Ref(0)
let device = @device_impl.surface_get_device_raw(
self.to_raw(),
has_device,
status,
)
check_device_status_raw(status.val)
if has_device.val == 0 {
None
} else {
check_device_status_raw(@device_impl.status_raw(device))
Some(Device::from_raw(device))
}
}
///|
/// Finish the surface and release backend resources deterministically.
///
/// Finishing is idempotent for a successful surface and leaves the wrapper
/// available for status, identity, and final destruction only. Buffer-backed
/// image storage retained by cairoon is released here. Even with a pre-existing
/// sticky Cairo error, native cleanup still runs and that original error is
/// then raised.
pub fn Surface::finish(self : Surface) -> Unit raise CairoError {
check_surface_status_raw(@surface_impl.finish_raw(self.to_raw()))
}
///|
/// Run `f`, then finish this surface on both success and error paths.
///
/// On success, a finish failure is raised and otherwise the closure value is
/// returned. If `f` raises, cairoon performs best-effort raw cleanup and
/// re-raises the original closure error even when finishing also reports a
/// sticky status. This is the MoonBit counterpart to pycairo's surface context
/// manager.
pub fn[T] Surface::with_finished(
self : Surface,
f : () -> T raise CairoError,
) -> T raise CairoError {
try f() catch {
err => {
let _ = @surface_impl.finish_raw(self.to_raw())
raise err
}
} noraise {
value => {
self.finish()
value
}
}
}