Skip to content

API Reference

photoff.core.types

CudaImage

Represents an image stored in GPU memory with optional dimension constraints.

The image has an underlying GPU buffer and stores its logical and allocated dimensions. Use .width and .height to manage the actual used size, while the allocation size is fixed on creation. Memory is managed via create_buffer and free_buffer.

Attributes:

Name Type Description
width int

Logical width (can be set lower than allocated width).

height int

Logical height (can be set lower than allocated height).

buffer CudaBuffer

Pointer to the underlying CUDA buffer.

Methods:

Name Description
init_image

Allocates the GPU buffer if not already allocated.

free

Frees the associated GPU buffer.

Example

img = CudaImage(512, 512) img.width = 256 # Use only part of the allocation img.free()

Source code in photoff\core\types.py
class CudaImage:
    """
    Represents an image stored in GPU memory with optional dimension constraints.

    The image has an underlying GPU buffer and stores its logical and allocated dimensions.
    Use `.width` and `.height` to manage the actual used size, while the allocation
    size is fixed on creation. Memory is managed via `create_buffer` and `free_buffer`.

    Attributes:
        width (int): Logical width (can be set lower than allocated width).
        height (int): Logical height (can be set lower than allocated height).
        buffer (CudaBuffer): Pointer to the underlying CUDA buffer.

    Methods:
        init_image(): Allocates the GPU buffer if not already allocated.
        free(): Frees the associated GPU buffer.

    Example:
        >>> img = CudaImage(512, 512)
        >>> img.width = 256  # Use only part of the allocation
        >>> img.free()
    """

    def __init__(self, width: int, height: int, auto_init: bool = True):
        """
        Initializes a new CudaImage with specified dimensions.

        Args:
            width (int): Allocation and initial logical width in pixels.
            height (int): Allocation and initial logical height in pixels.
            auto_init (bool, optional): Whether to automatically allocate the buffer. Defaults to True.
        """

        self._alloc_width  = width
        self._alloc_height = height

        self._width  = width
        self._height = height

        self.buffer = None
        if auto_init:
            self.init_image()

    @property
    def width(self) -> int:
        return self._width

    @width.setter
    def width(self, value: int):
        if value > self._alloc_width:
            raise ValueError(f"width {value} > alloc_width {self._alloc_width}")
        self._width = value

    @property
    def height(self) -> int:
        return self._height

    @height.setter
    def height(self, value: int):
        if value > self._alloc_height:
            raise ValueError(f"height {value} > alloc_height {self._alloc_height}")
        self._height = value

    def init_image(self):
        if self.buffer is None:
            self.buffer = create_buffer(self._alloc_width, self._alloc_height)

    def free(self):
        if self.buffer is not None:
            free_buffer(self.buffer)
            self.buffer = None

__init__(width, height, auto_init=True)

Initializes a new CudaImage with specified dimensions.

Parameters:

Name Type Description Default
width int

Allocation and initial logical width in pixels.

required
height int

Allocation and initial logical height in pixels.

required
auto_init bool

Whether to automatically allocate the buffer. Defaults to True.

True
Source code in photoff\core\types.py
def __init__(self, width: int, height: int, auto_init: bool = True):
    """
    Initializes a new CudaImage with specified dimensions.

    Args:
        width (int): Allocation and initial logical width in pixels.
        height (int): Allocation and initial logical height in pixels.
        auto_init (bool, optional): Whether to automatically allocate the buffer. Defaults to True.
    """

    self._alloc_width  = width
    self._alloc_height = height

    self._width  = width
    self._height = height

    self.buffer = None
    if auto_init:
        self.init_image()

RGBA dataclass

Represents an RGBA color with 8-bit channels.

Attributes:

Name Type Description
r int

Red channel (0 – 255).

g int

Green channel (0 – 255).

b int

Blue channel (0 – 255).

a int

Alpha channel (0 – 255), defaults to 255 (opaque).

Example

color = RGBA(255, 0, 0) # Opaque red transparent_black = RGBA(0, 0, 0, 0)

Source code in photoff\core\types.py
@_dataclass
class RGBA:
    """
    Represents an RGBA color with 8-bit channels.

    Attributes:
        r (int): Red channel (0 – 255).
        g (int): Green channel (0 – 255).
        b (int): Blue channel (0 – 255).
        a (int): Alpha channel (0 – 255), defaults to 255 (opaque).

    Example:
        >>> color = RGBA(255, 0, 0)  # Opaque red
        >>> transparent_black = RGBA(0, 0, 0, 0)
    """
    r: int
    g: int
    b: int
    a: int = 255

photoff.io

image_to_pil(image)

Converts a CudaImage to a PIL.Image in RGBA format.

The image is copied from GPU memory to host memory and returned as a Pillow image object.

Parameters:

Name Type Description Default
image CudaImage

The image in GPU memory.

required

Returns:

Type Description
Image

PIL.Image: A new PIL Image with RGBA channels.

Example

pil_img = image_to_pil(cuda_img) pil_img.show()

Source code in photoff\io\__init__.py
def image_to_pil(image: CudaImage) -> Image:
    """
    Converts a CudaImage to a PIL.Image in RGBA format.

    The image is copied from GPU memory to host memory and returned
    as a Pillow image object.

    Args:
        image (CudaImage): The image in GPU memory.

    Returns:
        PIL.Image: A new PIL Image with RGBA channels.

    Example:
        >>> pil_img = image_to_pil(cuda_img)
        >>> pil_img.show()
    """

    img_data = bytearray(image.width * image.height * 4)
    data_ptr = ffi.from_buffer(img_data)
    copy_to_host(ffi.cast("uchar4*", data_ptr), image.buffer, image.width,
                 image.height)
    return Image.frombytes("RGBA", (image.width, image.height),
                           bytes(img_data))

load_image(filename, container=None)

Loads an image from disk and transfers it to a CudaImage.

The image is loaded using Pillow and converted to RGBA format. It is then copied to GPU memory. Optionally, a pre-allocated CudaImage container can be used to avoid allocation.

Parameters:

Name Type Description Default
filename str

Path to the image file to load.

required
container CudaImage

Pre-allocated image buffer. Must be large enough to hold the image.

None

Returns:

Name Type Description
CudaImage CudaImage

A new or reused image object with the loaded data.

Raises:

Type Description
ValueError

If the input image is larger than the provided container.

Example

cuda_img = load_image("texture.png")

Source code in photoff\io\__init__.py
def load_image(filename: str, container: CudaImage | None = None) -> CudaImage:
    """
    Loads an image from disk and transfers it to a CudaImage.

    The image is loaded using Pillow and converted to RGBA format. It is then copied
    to GPU memory. Optionally, a pre-allocated CudaImage container can be used to avoid allocation.

    Args:
        filename (str): Path to the image file to load.
        container (CudaImage, optional): Pre-allocated image buffer. Must be large enough to hold the image.

    Returns:
        CudaImage: A new or reused image object with the loaded data.

    Raises:
        ValueError: If the input image is larger than the provided container.

    Example:
        >>> cuda_img = load_image("texture.png")
    """

    img = Image.open(filename).convert("RGBA")
    width, height = img.size

    if container is None:
        container = CudaImage(width, height)
    if width > container.width or height > container.height:
        raise ValueError("Image dimensions exceed container dimensions")

    host_buf = bytearray(img.tobytes())

    src_ptr = ffi.cast("uchar4*", ffi.from_buffer(host_buf))
    copy_to_device(container.buffer, src_ptr, width, height)

    container.width = width
    container.height = height
    return container

save_image(image, filename)

Saves a CudaImage to disk as a standard image file.

This function converts the image from GPU memory to a Pillow image and saves it using the given filename. The format is inferred from the file extension.

Parameters:

Name Type Description Default
image CudaImage

The image to save.

required
filename str

Destination path, including extension (e.g., 'output.png').

required

Returns:

Type Description
None

None

Example

save_image(cuda_img, "output.png")

Source code in photoff\io\__init__.py
def save_image(image: CudaImage, filename: str) -> None:
    """
    Saves a CudaImage to disk as a standard image file.

    This function converts the image from GPU memory to a Pillow image and saves it
    using the given filename. The format is inferred from the file extension.

    Args:
        image (CudaImage): The image to save.
        filename (str): Destination path, including extension (e.g., 'output.png').

    Returns:
        None

    Example:
        >>> save_image(cuda_img, "output.png")
    """

    img = image_to_pil(image)
    img.save(filename)
    img.close()

photoff.operations.blend

blend(background, over, x, y)

Blends an image (over) on top of another (background) at a specified position.

The blending respects the alpha channel of the overlaid image. No clipping is performed if the over image exceeds the bounds of the background; behavior in such cases depends on the underlying CUDA implementation.

Parameters:

Name Type Description Default
background CudaImage

The base image to draw onto.

required
over CudaImage

The image to blend on top.

required
x int

Horizontal position in the background where the top-left corner of over is placed.

required
y int

Vertical position in the background where the top-left corner of over is placed.

required

Returns:

Type Description
None

None

Example

blend(bg_img, icon_img, x=100, y=50)

Source code in photoff\operations\blend.py
def blend(background: CudaImage, over: CudaImage, x: int, y: int) -> None:
    """
    Blends an image (`over`) on top of another (`background`) at a specified position.

    The blending respects the alpha channel of the overlaid image. No clipping is performed
    if the `over` image exceeds the bounds of the `background`; behavior in such cases depends on the underlying CUDA implementation.

    Args:
        background (CudaImage): The base image to draw onto.
        over (CudaImage): The image to blend on top.
        x (int): Horizontal position in the background where the top-left corner of `over` is placed.
        y (int): Vertical position in the background where the top-left corner of `over` is placed.

    Returns:
        None

    Example:
        >>> blend(bg_img, icon_img, x=100, y=50)
    """
    _lib.blend_buffers(background.buffer,
                       over.buffer,
                       background.width,
                       background.height,
                       over.width,
                       over.height,
                       x,
                       y,
                       )

photoff.operations.fill

fill_color(image, color)

Fills the entire image with a solid color.

This operation overwrites all pixels in the image with the specified RGBA color.

Parameters:

Name Type Description Default
image CudaImage

The image to fill.

required
color RGBA

The fill color to apply.

required

Returns:

Type Description
None

None

Example

fill_color(img, RGBA(255, 255, 255, 255)) # Fill with solid white

Source code in photoff\operations\fill.py
def fill_color(image: CudaImage, color: RGBA) -> None:
    """
    Fills the entire image with a solid color.

    This operation overwrites all pixels in the image with the specified RGBA color.

    Args:
        image (CudaImage): The image to fill.
        color (RGBA): The fill color to apply.

    Returns:
        None

    Example:
        >>> fill_color(img, RGBA(255, 255, 255, 255))  # Fill with solid white
    """

    _lib.fill_color(image.buffer,
                    image.width,
                    image.height,
                    color.r,
                    color.g,
                    color.b,
                    color.a)

fill_gradient(image, color1, color2, direction=0, seamless=False)

Fills the image with a linear gradient between two colors.

The gradient direction and style (seamless or linear) can be customized.

Parameters:

Name Type Description Default
image CudaImage

The target image to fill.

required
color1 RGBA

The starting color of the gradient.

required
color2 RGBA

The ending color of the gradient.

required
direction int

Gradient direction: - 0 = vertical (top to bottom) - 1 = horizontal (left to right) - 2 = diagonal (top-left to bottom-right) - 3 = diagonal (bottom-left to top-right) Defaults to 0.

0
seamless bool

Whether the gradient should repeat seamlessly. Defaults to False.

False

Returns:

Type Description
None

None

Raises:

Type Description
ValueError

If an invalid direction value is provided.

Example

fill_gradient(img, RGBA(0, 0, 0, 255), RGBA(255, 255, 255, 255), direction=1)

Source code in photoff\operations\fill.py
def fill_gradient(image: CudaImage,
                  color1: RGBA,
                  color2: RGBA,
                  direction: int = 0,
                  seamless: bool = False) -> None:
    """
    Fills the image with a linear gradient between two colors.

    The gradient direction and style (seamless or linear) can be customized.

    Args:
        image (CudaImage): The target image to fill.
        color1 (RGBA): The starting color of the gradient.
        color2 (RGBA): The ending color of the gradient.
        direction (int, optional): Gradient direction:
            - 0 = vertical (top to bottom)
            - 1 = horizontal (left to right)
            - 2 = diagonal (top-left to bottom-right)
            - 3 = diagonal (bottom-left to top-right)
            Defaults to 0.
        seamless (bool, optional): Whether the gradient should repeat seamlessly. Defaults to False.

    Returns:
        None

    Raises:
        ValueError: If an invalid direction value is provided.

    Example:
        >>> fill_gradient(img, RGBA(0, 0, 0, 255), RGBA(255, 255, 255, 255), direction=1)
    """
    if direction not in (0, 1, 2, 3):
        raise ValueError(f"Invalid gradient direction: {direction}. Must be 0, 1, 2 or 3.")

    _lib.fill_gradient(image.buffer,
                       image.width,
                       image.height,
                       color1.r,
                       color1.g,
                       color1.b,
                       color1.a,
                       color2.r,
                       color2.g,
                       color2.b,
                       color2.a,
                       direction,
                       seamless,
                       )

photoff.operations.filters

apply_chroma_key(image, key_image, channel='A', threshold=128, invert=False, zero_all_channels=False)

Applies a chroma key mask based on a channel of another image.

Parameters:

Name Type Description Default
image CudaImage

The target image to apply transparency.

required
key_image CudaImage

Image whose channel values are used as a mask.

required
channel str

Channel to use from the key image ('R', 'G', 'B', 'A'). Defaults to 'A'.

'A'
threshold int

Threshold (0–255) to apply masking. Defaults to 128.

128
invert bool

Invert the mask logic. Defaults to False.

False
zero_all_channels bool

If True, sets RGB to zero where mask applies. Defaults to False.

False

Raises:

Type Description
ValueError

If the provided channel is invalid.

Returns:

Type Description
None

None

Source code in photoff\operations\filters.py
def apply_chroma_key(image: CudaImage,
                     key_image: CudaImage,
                     channel: str = "A",
                     threshold: int = 128,
                     invert: bool = False,
                     zero_all_channels: bool = False) -> None:
    """
    Applies a chroma key mask based on a channel of another image.

    Args:
        image (CudaImage): The target image to apply transparency.
        key_image (CudaImage): Image whose channel values are used as a mask.
        channel (str, optional): Channel to use from the key image ('R', 'G', 'B', 'A'). Defaults to 'A'.
        threshold (int, optional): Threshold (0–255) to apply masking. Defaults to 128.
        invert (bool, optional): Invert the mask logic. Defaults to False.
        zero_all_channels (bool, optional): If True, sets RGB to zero where mask applies. Defaults to False.

    Raises:
        ValueError: If the provided channel is invalid.

    Returns:
        None
    """

    channel_upper = (channel.upper() if isinstance(channel, str) else str(channel).upper())

    if channel_upper == "R":
        channel_idx = 0
    elif channel_upper == "G":
        channel_idx = 1
    elif channel_upper == "B":
        channel_idx = 2
    elif channel_upper == "A":
        channel_idx = 3
    else:
        raise ValueError(f"Invalid channel: {channel}, must be one of 'R', 'G', 'B', 'A'")

    _lib.apply_chroma_key(image.buffer,
                          key_image.buffer,
                          image.width,
                          image.height,
                          key_image.width,
                          key_image.height,
                          channel_idx,
                          threshold,
                          invert,
                          zero_all_channels,
                          )

apply_corner_radius(image, size)

Applies a rounded corner mask to an image in-place.

Parameters:

Name Type Description Default
image CudaImage

Image to be modified.

required
size int

Radius of the corner in pixels.

required

Returns:

Type Description
None

None

Source code in photoff\operations\filters.py
def apply_corner_radius(image: CudaImage, size: int) -> None:
    """
    Applies a rounded corner mask to an image in-place.

    Args:
        image (CudaImage): Image to be modified.
        size (int): Radius of the corner in pixels.

    Returns:
        None
    """

    _lib.apply_corner_radius(image.buffer, image.width, image.height, size)

apply_flip(image, flip_horizontal=False, flip_vertical=False)

Flips an image horizontally or vertically in-place.

Parameters:

Name Type Description Default
image CudaImage

Image to flip.

required
flip_horizontal bool

Flip the image horizontally. Defaults to False.

False
flip_vertical bool

Flip the image vertically. Defaults to False.

False

Raises:

Type Description
ValueError

If both flip_horizontal and flip_vertical are True.

Returns:

Type Description
None

None

Source code in photoff\operations\filters.py
def apply_flip(image: CudaImage,
               flip_horizontal: bool = False,
               flip_vertical: bool = False) -> None:
    """
    Flips an image horizontally or vertically in-place.

    Args:
        image (CudaImage): Image to flip.
        flip_horizontal (bool, optional): Flip the image horizontally. Defaults to False.
        flip_vertical (bool, optional): Flip the image vertically. Defaults to False.

    Raises:
        ValueError: If both `flip_horizontal` and `flip_vertical` are True.

    Returns:
        None
    """

    if flip_horizontal and flip_vertical:
        raise ValueError("Cannot flip both horizontal and vertical at the same time")

    _lib.apply_flip(image.buffer, image.width, image.height, flip_horizontal, flip_vertical)

apply_gaussian_blur(image, radius, image_copy_cache=None)

Applies a Gaussian blur effect to an image in-place.

Parameters:

Name Type Description Default
image CudaImage

Image to blur.

required
radius float

Radius of the blur in pixels.

required
image_copy_cache CudaImage

Optional buffer. Must match image size.

None

Raises:

Type Description
ValueError

If the cache does not match the image dimensions.

Returns:

Type Description
None

None

Source code in photoff\operations\filters.py
def apply_gaussian_blur(image: CudaImage,
                        radius: float,
                        image_copy_cache: CudaImage = None) -> None:
    """
    Applies a Gaussian blur effect to an image in-place.

    Args:
        image (CudaImage): Image to blur.
        radius (float): Radius of the blur in pixels.
        image_copy_cache (CudaImage, optional): Optional buffer. Must match image size.

    Raises:
        ValueError: If the cache does not match the image dimensions.

    Returns:
        None
    """

    need_free = False
    if image_copy_cache is None:
        image_copy_cache = CudaImage(image.width, image.height)
        copy_buffers_same_size(image_copy_cache.buffer, image.buffer, image.width, image.height)
        need_free = True
    else:
        if (image_copy_cache.width != image.width or image_copy_cache.height != image.height):
            raise ValueError(f"El buffer auxiliar debe coincidir con las dimensiones de la imagen original: {image.width}x{image.height}, recibido {image_copy_cache.width}x{image_copy_cache.height}")

    _lib.apply_gaussian_blur(image.buffer, image_copy_cache.buffer, image.width, image.height, radius)

    if need_free:
        image_copy_cache.free()

apply_grayscale(image)

Converts an image to grayscale in-place using luminosity method.

Parameters:

Name Type Description Default
image CudaImage

Image to convert.

required

Returns:

Type Description
None

None

Source code in photoff\operations\filters.py
def apply_grayscale(image: CudaImage) -> None:
    """
    Converts an image to grayscale in-place using luminosity method.

    Args:
        image (CudaImage): Image to convert.

    Returns:
        None
    """

    _lib.apply_grayscale(image.buffer, image.width, image.height)

apply_opacity(image, opacity)

Modifies the alpha channel of an image to apply global opacity.

Parameters:

Name Type Description Default
image CudaImage

Image to modify.

required
opacity float

Opacity value between 0.0 (transparent) and 1.0 (opaque).

required

Returns:

Type Description
None

None

Source code in photoff\operations\filters.py
def apply_opacity(image: CudaImage, opacity: float) -> None:
    """
    Modifies the alpha channel of an image to apply global opacity.

    Args:
        image (CudaImage): Image to modify.
        opacity (float): Opacity value between 0.0 (transparent) and 1.0 (opaque).

    Returns:
        None
    """

    _lib.apply_opacity(image.buffer, image.width, image.height, opacity)

apply_shadow(image, radius, intensity, shadow_color, image_copy_cache=None, inner=False)

Applies a shadow effect around the opaque regions of an image.

Parameters:

Name Type Description Default
image CudaImage

Image to apply the shadow to.

required
radius float

Blur radius of the shadow.

required
intensity float

Intensity multiplier of the shadow.

required
shadow_color RGBA

Color of the shadow.

required
image_copy_cache CudaImage

Optional image copy buffer. Must match original image size.

None
inner bool

Whether to draw the shadow inside the shape. Defaults to False.

False

Raises:

Type Description
ValueError

If the cache does not match the image dimensions.

Returns:

Type Description
None

None

Source code in photoff\operations\filters.py
def apply_shadow(image: CudaImage,
                 radius: float,
                 intensity: float,
                 shadow_color: RGBA,
                 image_copy_cache: CudaImage = None,
                 inner: bool = False) -> None:
    """
    Applies a shadow effect around the opaque regions of an image.

    Args:
        image (CudaImage): Image to apply the shadow to.
        radius (float): Blur radius of the shadow.
        intensity (float): Intensity multiplier of the shadow.
        shadow_color (RGBA): Color of the shadow.
        image_copy_cache (CudaImage, optional): Optional image copy buffer. Must match original image size.
        inner (bool, optional): Whether to draw the shadow inside the shape. Defaults to False.

    Raises:
        ValueError: If the cache does not match the image dimensions.

    Returns:
        None
    """

    need_free = False
    if image_copy_cache is None:
        image_copy_cache = CudaImage(image.width, image.height)
        copy_buffers_same_size(image_copy_cache.buffer, image.buffer, image.width, image.height)
        need_free = True
    else:
        if (image_copy_cache.width != image.width or image_copy_cache.height != image.height):
            raise ValueError(f"Destination image dimensions must match original image dimensions: {image.width}x{image.height}, got {image_copy_cache.width}x{image_copy_cache.height}")

    _lib.apply_shadow(image.buffer,
                      image_copy_cache.buffer,
                      image.width,
                      image.height,
                      radius,
                      intensity,
                      shadow_color.r,
                      shadow_color.g,
                      shadow_color.b,
                      shadow_color.a,
                      int(inner),
                      )

    if need_free:
        image_copy_cache.free()

apply_stroke(image, stroke_width, stroke_color, image_copy_cache=None, inner=True)

Draws a stroke (outline) around the non-transparent areas of an image.

Parameters:

Name Type Description Default
image CudaImage

Image to which the stroke will be applied.

required
stroke_width int

Width of the stroke in pixels.

required
stroke_color RGBA

Color of the stroke.

required
image_copy_cache CudaImage

Optional cache of the original image. Must match dimensions.

None
inner bool

If True, stroke is drawn inside the shape; otherwise outside. Defaults to True.

True

Raises:

Type Description
ValueError

If the provided cache does not match image dimensions.

Returns:

Type Description
None

None

Source code in photoff\operations\filters.py
def apply_stroke(image: CudaImage,
                 stroke_width: int,
                 stroke_color: RGBA,
                 image_copy_cache: CudaImage = None,
                 inner: bool = True) -> None:
    """
    Draws a stroke (outline) around the non-transparent areas of an image.

    Args:
        image (CudaImage): Image to which the stroke will be applied.
        stroke_width (int): Width of the stroke in pixels.
        stroke_color (RGBA): Color of the stroke.
        image_copy_cache (CudaImage, optional): Optional cache of the original image. Must match dimensions.
        inner (bool, optional): If True, stroke is drawn inside the shape; otherwise outside. Defaults to True.

    Raises:
        ValueError: If the provided cache does not match image dimensions.

    Returns:
        None
    """

    need_free = False
    if image_copy_cache is None:
        image_copy_cache = CudaImage(image.width, image.height)
        copy_buffers_same_size(image_copy_cache.buffer, image.buffer, image.width, image.height)
        need_free = True
    else:
        if (image_copy_cache.width != image.width or image_copy_cache.height != image.height):
            raise ValueError(f"Destination image dimensions must match original image dimensions: {image.width}x{image.height}, got {image_copy_cache.width}x{image_copy_cache.height}")

    _lib.apply_stroke(image.buffer,
                      image_copy_cache.buffer,
                      image.width,
                      image.height,
                      stroke_width,
                      stroke_color.r,
                      stroke_color.g,
                      stroke_color.b,
                      stroke_color.a,
                      int(inner),
                      )

    if need_free:
        image_copy_cache.free()

photoff.operations.resize

ResizeMethod

Bases: Enum

Enum representing supported image resizing algorithms.

Attributes:

Name Type Description
BILINEAR

Bilinear interpolation (smooth, reasonably fast).

NEAREST

Nearest neighbor interpolation (fastest, lowest quality).

BICUBIC

Bicubic interpolation (higher quality, slower).

Usage

method = ResizeMethod.BICUBIC

Source code in photoff\operations\resize.py
class ResizeMethod(Enum):
    """
    Enum representing supported image resizing algorithms.

    Attributes:
        BILINEAR: Bilinear interpolation (smooth, reasonably fast).
        NEAREST: Nearest neighbor interpolation (fastest, lowest quality).
        BICUBIC: Bicubic interpolation (higher quality, slower).

    Usage:
        method = ResizeMethod.BICUBIC
    """
    BILINEAR = "bilinear"
    NEAREST = "nearest"
    BICUBIC = "bicubic"

crop_margins(image, left=0, top=0, right=0, bottom=0, crop_image_cache=None)

Crops margins from the edges of a CudaImage.

Margins are specified in pixels. The resulting image will be smaller and positioned relative to the top-left corner of the cropped area.

Parameters:

Name Type Description Default
image CudaImage

The input image to crop.

required
left int

Pixels to remove from the left edge. Defaults to 0.

0
top int

Pixels to remove from the top edge. Defaults to 0.

0
right int

Pixels to remove from the right edge. Defaults to 0.

0
bottom int

Pixels to remove from the bottom edge. Defaults to 0.

0
crop_image_cache CudaImage

Pre-allocated buffer for the cropped image. Must match the expected result dimensions.

None

Returns:

Name Type Description
CudaImage CudaImage

The cropped image.

Raises:

Type Description
ValueError

If any margin is negative.

ValueError

If margins exceed the image's size.

ValueError

If the cache image does not match the result size.

Example

cropped = crop_margins(img, left=10, top=10, right=10, bottom=10)

Source code in photoff\operations\resize.py
def crop_margins(image: CudaImage,
                 left: int = 0,
                 top: int = 0,
                 right: int = 0,
                 bottom: int = 0,
                 crop_image_cache: CudaImage = None,
                 ) -> CudaImage:
    """
    Crops margins from the edges of a CudaImage.

    Margins are specified in pixels. The resulting image will be smaller and positioned
    relative to the top-left corner of the cropped area.

    Args:
        image (CudaImage): The input image to crop.
        left (int, optional): Pixels to remove from the left edge. Defaults to 0.
        top (int, optional): Pixels to remove from the top edge. Defaults to 0.
        right (int, optional): Pixels to remove from the right edge. Defaults to 0.
        bottom (int, optional): Pixels to remove from the bottom edge. Defaults to 0.
        crop_image_cache (CudaImage, optional): Pre-allocated buffer for the cropped image.
            Must match the expected result dimensions.

    Returns:
        CudaImage: The cropped image.

    Raises:
        ValueError: If any margin is negative.
        ValueError: If margins exceed the image's size.
        ValueError: If the cache image does not match the result size.

    Example:
        >>> cropped = crop_margins(img, left=10, top=10, right=10, bottom=10)
    """

    if left < 0 or top < 0 or right < 0 or bottom < 0:
        raise ValueError("Margins cannot be negative")

    if left + right >= image.width or top + bottom >= image.height:
        raise ValueError("Total margins exceed image dimensions")

    new_width = image.width - left - right
    new_height = image.height - top - bottom

    if crop_image_cache is None:
        result = CudaImage(new_width, new_height)
    else:
        if crop_image_cache.width != new_width or crop_image_cache.height != new_height:
            raise ValueError(f"Destination image cache dimensions must match crop result: {new_width}x{new_height}")
        result = crop_image_cache

    _lib.crop_image(result.buffer,
                    image.buffer,
                    image.width,
                    image.height,
                    new_width,
                    new_height,
                    left,
                    top,
                    )

    return result

resize(image, width, height, method=ResizeMethod.BICUBIC, resize_image_cache=None)

Resizes a CudaImage to the specified dimensions using the chosen interpolation method.

Supports bilinear, nearest-neighbor, and bicubic resampling. A cache image can be reused for performance to avoid memory allocation.

Parameters:

Name Type Description Default
image CudaImage

The input image to resize.

required
width int

Target width.

required
height int

Target height.

required
method ResizeMethod

Resampling method. Defaults to BICUBIC.

BICUBIC
resize_image_cache CudaImage

Pre-allocated image for the resized result. Must match target dimensions if provided.

None

Returns:

Name Type Description
CudaImage CudaImage

A new (or reused) image resized to the given dimensions.

Raises:

Type Description
ValueError

If the cache image dimensions do not match the target size.

ValueError

If the interpolation method is not supported.

Example

resized = resize(img, 256, 256, method=ResizeMethod.BILINEAR)

Source code in photoff\operations\resize.py
def resize(image: CudaImage,
           width: int,
           height: int,
           method: ResizeMethod = ResizeMethod.BICUBIC,
           resize_image_cache: CudaImage = None,
           ) -> CudaImage:
    """
    Resizes a CudaImage to the specified dimensions using the chosen interpolation method.

    Supports bilinear, nearest-neighbor, and bicubic resampling. A cache image can be reused
    for performance to avoid memory allocation.

    Args:
        image (CudaImage): The input image to resize.
        width (int): Target width.
        height (int): Target height.
        method (ResizeMethod, optional): Resampling method. Defaults to BICUBIC.
        resize_image_cache (CudaImage, optional): Pre-allocated image for the resized result.
            Must match target dimensions if provided.

    Returns:
        CudaImage: A new (or reused) image resized to the given dimensions.

    Raises:
        ValueError: If the cache image dimensions do not match the target size.
        ValueError: If the interpolation method is not supported.

    Example:
        >>> resized = resize(img, 256, 256, method=ResizeMethod.BILINEAR)
    """

    if resize_image_cache is None:
        result = CudaImage(width, height)
    else:
        if resize_image_cache.width != width or resize_image_cache.height != height:
            raise ValueError(f"Destination image dimensions must match resize dimensions: {width}x{height}, got {resize_image_cache.width}x{resize_image_cache.height}")
        result = resize_image_cache

    if method == ResizeMethod.BILINEAR:
        _lib.resize_bilinear(result.buffer, image.buffer, width, height, image.width, image.height)
    elif method == ResizeMethod.NEAREST:
        _lib.resize_nearest(result.buffer, image.buffer, width, height, image.width, image.height)
    elif method == ResizeMethod.BICUBIC:
        _lib.resize_bicubic(result.buffer, image.buffer, width, height, image.width, image.height)
    else:
        raise ValueError(f"Unsupported resize method: {method}")

    return result

photoff.operations.text

render_text(text, font_path, font_size=24, color=RGBA(0, 0, 0, 255))

Renders a string of text into a CudaImage using a specified TrueType font.

The function uses Pillow (PIL) to rasterize the text and transfers the resulting RGBA image into GPU memory as a CudaImage. Font metrics are computed to fit the rendered text exactly, avoiding unnecessary padding.

Parameters:

Name Type Description Default
text str

The string to render.

required
font_path str

Path to a TrueType (.ttf) or OpenType (.otf) font file.

required
font_size int

Font size in points. Defaults to 24.

24
color RGBA

Text color. Defaults to opaque black (0, 0, 0, 255).

RGBA(0, 0, 0, 255)

Returns:

Name Type Description
CudaImage CudaImage

GPU image containing the rendered text.

Raises:

Type Description
ValueError

If the font file cannot be loaded.

Example

img = render_text("Hello GPU!", "/fonts/Roboto-Regular.ttf", 32, RGBA(255, 255, 255, 255))

Source code in photoff\operations\text.py
def render_text(text: str,
                font_path: str,
                font_size: int = 24,
                color: RGBA = RGBA(0, 0, 0, 255)
                ) -> CudaImage:
    """
    Renders a string of text into a CudaImage using a specified TrueType font.

    The function uses Pillow (PIL) to rasterize the text and transfers the resulting
    RGBA image into GPU memory as a `CudaImage`. Font metrics are computed to fit
    the rendered text exactly, avoiding unnecessary padding.

    Args:
        text (str): The string to render.
        font_path (str): Path to a TrueType (.ttf) or OpenType (.otf) font file.
        font_size (int, optional): Font size in points. Defaults to 24.
        color (RGBA, optional): Text color. Defaults to opaque black (0, 0, 0, 255).

    Returns:
        CudaImage: GPU image containing the rendered text.

    Raises:
        ValueError: If the font file cannot be loaded.

    Example:
        >>> img = render_text("Hello GPU!", "/fonts/Roboto-Regular.ttf", 32, RGBA(255, 255, 255, 255))
    """

    try:
        font = ImageFont.truetype(font_path, font_size)
    except Exception as e:
        raise ValueError(f"Error loading font '{font_path}': {e}")

    tmp_img = Image.new("RGBA", (1, 1))
    tmp_draw = ImageDraw.Draw(tmp_img)
    try:
        left, top, right, bottom = font.getbbox(text)
    except AttributeError:
        left, top, right, bottom = tmp_draw.textbbox((0, 0), text, font=font)

    width, height = right - left, bottom - top

    pil_img = Image.new("RGBA", (width, height), (0, 0, 0, 0))
    draw = ImageDraw.Draw(pil_img)
    draw.text((-left, -top), text, fill=(color.r, color.g, color.b, color.a), font=font)

    cuda_img = CudaImage(width, height)

    host_buf = bytearray(pil_img.tobytes())
    src_ptr = ffi.cast("uchar4*", ffi.from_buffer(host_buf))
    copy_to_device(cuda_img.buffer, src_ptr, width, height)

    return cuda_img

photoff.operations.utils

blend_aligned(background, image, align='center', offset_x=0, offset_y=0)

Blends an image onto a background at a specified alignment and optional offset.

The image is positioned relative to the background based on the align parameter, which supports standard alignment strings (e.g., "center", "top-left", etc.). Offsets can be applied to adjust the final position.

Parameters:

Name Type Description Default
background CudaImage

The background image where the input image will be blended.

required
image CudaImage

The image to blend.

required
align str

Alignment method. Supported values: - "center" / "middle" - "top", "bottom", "left", "right" - "top-left", "top-right", "bottom-left", "bottom-right" Defaults to "center".

'center'
offset_x int

Horizontal pixel offset to apply after alignment. Defaults to 0.

0
offset_y int

Vertical pixel offset to apply after alignment. Defaults to 0.

0

Returns:

Type Description
None

None

Example

blend_aligned(bg, img, align="bottom-right", offset_x=-10, offset_y=-10)

Source code in photoff\operations\utils.py
def blend_aligned(background: CudaImage,
                  image: CudaImage,
                  align: str = "center",
                  offset_x: int = 0,
                  offset_y: int = 0,
                  ) -> None:
    """
    Blends an image onto a background at a specified alignment and optional offset.

    The image is positioned relative to the background based on the `align` parameter,
    which supports standard alignment strings (e.g., "center", "top-left", etc.).
    Offsets can be applied to adjust the final position.

    Args:
        background (CudaImage): The background image where the input image will be blended.
        image (CudaImage): The image to blend.
        align (str, optional): Alignment method. Supported values:
            - "center" / "middle"
            - "top", "bottom", "left", "right"
            - "top-left", "top-right", "bottom-left", "bottom-right"
            Defaults to "center".
        offset_x (int, optional): Horizontal pixel offset to apply after alignment. Defaults to 0.
        offset_y (int, optional): Vertical pixel offset to apply after alignment. Defaults to 0.

    Returns:
        None

    Example:
        >>> blend_aligned(bg, img, align="bottom-right", offset_x=-10, offset_y=-10)
    """

    if align == "center" or align == "middle":
        x = (background.width - image.width) // 2
        y = (background.height - image.height) // 2

    elif align == "top":
        x = (background.width - image.width) // 2
        y = 0

    elif align == "bottom":
        x = (background.width - image.width) // 2
        y = background.height - image.height

    elif align == "left":
        x = 0
        y = (background.height - image.height) // 2

    elif align == "right":
        x = background.width - image.width
        y = (background.height - image.height) // 2

    elif align == "top-left":
        x = 0
        y = 0

    elif align == "top-right":
        x = background.width - image.width
        y = 0

    elif align == "bottom-left":
        x = 0
        y = background.height - image.height

    elif align == "bottom-right":
        x = background.width - image.width
        y = background.height - image.height

    else:
        x = (background.width - image.width) // 2
        y = (background.height - image.height) // 2

    x += offset_x
    y += offset_y

    blend(background, image, x, y)

cover_image_in_container(image, container_width, container_height, offset_x=0, offset_y=0, background_color=RGBA(0, 0, 0, 0), container_image_cache=None, resize_image_cache=None, resize_mode=ResizeMethod.BICUBIC)

Resizes an image to fully cover a container while maintaining aspect ratio, and blends it centered (or offset) within the container.

The image is scaled up proportionally so that it completely fills the container dimensions (container_width x container_height). The resized image may overflow on one axis, similar to CSS's background-size: cover. Optional caching and resizing method are supported for performance and quality tuning.

Parameters:

Name Type Description Default
image CudaImage

The input image to be resized and placed.

required
container_width int

Width of the container image.

required
container_height int

Height of the container image.

required
offset_x int

Horizontal offset to apply after centering. Defaults to 0.

0
offset_y int

Vertical offset to apply after centering. Defaults to 0.

0
background_color RGBA

Background color to fill the container. Defaults to transparent black.

RGBA(0, 0, 0, 0)
container_image_cache CudaImage

Pre-allocated container image buffer. Must match the container dimensions.

None
resize_image_cache CudaImage

Pre-allocated image for resized content. Must match computed resize dimensions.

None
resize_mode ResizeMethod

Resize algorithm to use (e.g., BICUBIC, NEAREST). Defaults to BICUBIC.

BICUBIC

Returns:

Name Type Description
CudaImage CudaImage

A new image with the resized input blended over the background.

Raises:

Type Description
ValueError

If the number of images exceeds the grid capacity.

ValueError

If provided resize_image_cache or container_image_cache have incorrect dimensions.

Source code in photoff\operations\utils.py
def cover_image_in_container(image: CudaImage,
                             container_width: int,
                             container_height: int,
                             offset_x: int = 0,
                             offset_y: int = 0,
                             background_color: RGBA = RGBA(0, 0, 0, 0),
                             container_image_cache: CudaImage = None,
                             resize_image_cache: CudaImage = None,
                             resize_mode: ResizeMethod = ResizeMethod.BICUBIC,
                             ) -> CudaImage:
    """
    Resizes an image to fully cover a container while maintaining aspect ratio,
    and blends it centered (or offset) within the container.

    The image is scaled up proportionally so that it completely fills the container
    dimensions (`container_width` x `container_height`). The resized image may overflow
    on one axis, similar to CSS's `background-size: cover`. Optional caching and
    resizing method are supported for performance and quality tuning.

    Args:
        image (CudaImage): The input image to be resized and placed.
        container_width (int): Width of the container image.
        container_height (int): Height of the container image.
        offset_x (int, optional): Horizontal offset to apply after centering. Defaults to 0.
        offset_y (int, optional): Vertical offset to apply after centering. Defaults to 0.
        background_color (RGBA, optional): Background color to fill the container. Defaults to transparent black.
        container_image_cache (CudaImage, optional): Pre-allocated container image buffer. Must match the container dimensions.
        resize_image_cache (CudaImage, optional): Pre-allocated image for resized content. Must match computed resize dimensions.
        resize_mode (ResizeMethod, optional): Resize algorithm to use (e.g., BICUBIC, NEAREST). Defaults to BICUBIC.

    Returns:
        CudaImage: A new image with the resized input blended over the background.

    Raises:
        ValueError: If the number of images exceeds the grid capacity.
        ValueError: If provided `resize_image_cache` or `container_image_cache` have incorrect dimensions.
    """
    scale = max(container_width / image.width, container_height / image.height)
    new_width = int(image.width * scale)
    new_height = int(image.height * scale)

    need_free_resized = False
    if resize_image_cache is None:
        resized_image = CudaImage(new_width, new_height)
        need_free_resized = True
    else:
        if (resize_image_cache.width != new_width or resize_image_cache.height != new_height):
            raise ValueError(f"Resize cache dimensions must match: {new_width}x{new_height}, got {resize_image_cache.width}x{resize_image_cache.height}")
        resized_image = resize_image_cache

    resize(image,
           new_width,
           new_height,
           method=resize_mode,
           resize_image_cache=resized_image,
           )

    if container_image_cache is None:
        container = CudaImage(container_width, container_height)
    else:
        if (container_image_cache.width != container_width or container_image_cache.height != container_height):
            raise ValueError(f"Container cache dimensions must match: {container_width}x{container_height}, got {container_image_cache.width}x{container_image_cache.height}")
        container = container_image_cache

    fill_color(container, background_color)

    x = (container_width - new_width) // 2 + offset_x
    y = (container_height - new_height) // 2 + offset_y

    blend(container, resized_image, x, y)

    if need_free_resized:
        resized_image.free()

    return container

create_image_collage(images, grid_width, grid_height, spacing=0, background_color=RGBA(0, 0, 0, 0), collage_image_cache=None)

Creates a collage from a list of CUDA images arranged in a grid.

All images must have identical dimensions. The function supports optional spacing between images and a custom background color. It also allows reuse of a pre-allocated image buffer to avoid reallocations.

Parameters:

Name Type Description Default
images list[CudaImage]

List of images to include in the collage.

required
grid_width int

Number of columns in the collage grid.

required
grid_height int

Number of rows in the collage grid.

required
spacing int

Space in pixels between images in the grid. Defaults to 0.

0
background_color RGBA

Background color to fill the collage. Defaults to transparent black (0, 0, 0, 0).

RGBA(0, 0, 0, 0)
collage_image_cache CudaImage

Optional pre-allocated image to use as the collage buffer. Must match the final dimensions. If not provided, a new image will be created.

None

Returns:

Name Type Description
CudaImage CudaImage

A new image containing the collage.

Raises:

Type Description
ValueError

If the number of images exceeds the grid capacity.

ValueError

If input images do not have the same dimensions.

ValueError

If the provided collage_image_cache does not match the required dimensions.

Source code in photoff\operations\utils.py
def create_image_collage(images: list[CudaImage],
                         grid_width: int,
                         grid_height: int,
                         spacing: int = 0,
                         background_color: RGBA = RGBA(0, 0, 0, 0),
                         collage_image_cache: CudaImage = None,
                         ) -> CudaImage:
    """
    Creates a collage from a list of CUDA images arranged in a grid.

    All images must have identical dimensions. The function supports optional
    spacing between images and a custom background color. It also allows
    reuse of a pre-allocated image buffer to avoid reallocations.

    Args:
        images (list[CudaImage]): List of images to include in the collage.
        grid_width (int): Number of columns in the collage grid.
        grid_height (int): Number of rows in the collage grid.
        spacing (int, optional): Space in pixels between images in the grid. Defaults to 0.
        background_color (RGBA, optional): Background color to fill the collage. Defaults to transparent black (0, 0, 0, 0).
        collage_image_cache (CudaImage, optional): Optional pre-allocated image to use as the collage buffer.
            Must match the final dimensions. If not provided, a new image will be created.

    Returns:
        CudaImage: A new image containing the collage.

    Raises:
        ValueError: If the number of images exceeds the grid capacity.
        ValueError: If input images do not have the same dimensions.
        ValueError: If the provided collage_image_cache does not match the required dimensions.
    """

    total_cells = grid_width * grid_height
    num_images = len(images)
    if num_images > total_cells:
        raise ValueError(f"Number of images ({num_images}) exceeds grid capacity ({total_cells})")

    first = images[0]
    img_w, img_h = first.width, first.height
    for img in images:
        if img.width != img_w or img.height != img_h:
            raise ValueError("All images must have the same dimensions to form a uniform collage")

    width = (img_w * grid_width) + (spacing * (grid_width - 1))
    height = (img_h * grid_height) + (spacing * (grid_height - 1))

    if collage_image_cache is None:
        result = CudaImage(width, height)
    else:
        if (collage_image_cache.width != width or collage_image_cache.height != height):
            raise ValueError(f"Collage cache dimensions must match: {width}x{height}, got {collage_image_cache.width}x{collage_image_cache.height}")
        result = collage_image_cache

    fill_color(result, background_color)

    for idx, img in enumerate(images):
        col = idx % grid_width
        row = idx // grid_width
        x_pos = col * (img_w + spacing)
        y_pos = row * (img_h + spacing)
        blend(result, img, x_pos, y_pos)

    return result

create_image_grid(image, grid_width, grid_height, num_images, spacing=0, background_color=RGBA(0, 0, 0, 0), grid_image_cache=None)

Creates a grid layout by duplicating a single image multiple times.

The image is repeated num_images times and arranged in a grid with the specified width and height. Optional spacing and background color can be configured. A cached output image can be reused for efficiency.

Parameters:

Name Type Description Default
image CudaImage

The image to be repeated across the grid.

required
grid_width int

Number of columns in the grid.

required
grid_height int

Number of rows in the grid.

required
num_images int

Total number of image copies to include in the grid. Must not exceed grid capacity.

required
spacing int

Number of pixels between images in both directions. Defaults to 0.

0
background_color RGBA

Background color to fill empty areas. Defaults to transparent black.

RGBA(0, 0, 0, 0)
grid_image_cache CudaImage

Pre-allocated image buffer to use. Must match final grid size if provided.

None

Returns:

Name Type Description
CudaImage CudaImage

A new image containing the grid of repeated images.

Raises:

Type Description
ValueError

If num_images exceeds the grid's capacity.

ValueError

If grid_image_cache is provided but has incorrect dimensions.

Source code in photoff\operations\utils.py
def create_image_grid(image: CudaImage,
                      grid_width: int,
                      grid_height: int,
                      num_images: int,
                      spacing: int = 0,
                      background_color: RGBA = RGBA(0, 0, 0, 0),
                      grid_image_cache: CudaImage = None,
                      ) -> CudaImage:
    """
    Creates a grid layout by duplicating a single image multiple times.

    The image is repeated `num_images` times and arranged in a grid with the specified
    width and height. Optional spacing and background color can be configured. A cached
    output image can be reused for efficiency.

    Args:
        image (CudaImage): The image to be repeated across the grid.
        grid_width (int): Number of columns in the grid.
        grid_height (int): Number of rows in the grid.
        num_images (int): Total number of image copies to include in the grid. Must not exceed grid capacity.
        spacing (int, optional): Number of pixels between images in both directions. Defaults to 0.
        background_color (RGBA, optional): Background color to fill empty areas. Defaults to transparent black.
        grid_image_cache (CudaImage, optional): Pre-allocated image buffer to use. Must match final grid size if provided.

    Returns:
        CudaImage: A new image containing the grid of repeated images.

    Raises:
        ValueError: If `num_images` exceeds the grid's capacity.
        ValueError: If `grid_image_cache` is provided but has incorrect dimensions.
    """

    total_cells = grid_width * grid_height
    if num_images > total_cells:
        raise ValueError(f"Number of images ({num_images}) exceeds grid capacity ({total_cells})")

    width = (image.width * grid_width) + (spacing * (grid_width - 1))
    height = (image.height * grid_height) + (spacing * (grid_height - 1))

    if grid_image_cache is None:
        result = CudaImage(width, height)
    else:
        if grid_image_cache.width != width or grid_image_cache.height != height:
            raise ValueError(f"Grid cache dimensions must match: {width}x{height}, got {grid_image_cache.width}x{grid_image_cache.height}")
        result = grid_image_cache

    fill_color(result, background_color)

    count = 0
    for y in range(grid_height):
        for x in range(grid_width):
            if count >= num_images:
                break

            pos_x = x * (image.width + spacing)
            pos_y = y * (image.height + spacing)

            blend(result, image, pos_x, pos_y)

            count += 1

        if count >= num_images:
            break

    return result

get_cover_resize_dimensions(image, container_width, container_height)

Calculates the new dimensions to resize an image so that it fully covers a container while preserving its aspect ratio.

The resulting dimensions ensure that the image completely fills the target container (container_width x container_height), potentially exceeding one of the container's dimensions. This mimics the behavior of background-size: cover in CSS.

Parameters:

Name Type Description Default
image CudaImage

The original image to be resized.

required
container_width int

Target container width.

required
container_height int

Target container height.

required

Returns:

Type Description
tuple[int, int]

tuple[int, int]: The new width and height that the image should be resized to.

Example

get_cover_resize_dimensions(img, 1920, 1080) (1920, 1280)

Source code in photoff\operations\utils.py
def get_cover_resize_dimensions(image: CudaImage,
                                container_width: int,
                                container_height: int) -> tuple[int, int]:
    """
    Calculates the new dimensions to resize an image so that it fully covers
    a container while preserving its aspect ratio.

    The resulting dimensions ensure that the image completely fills the target
    container (`container_width` x `container_height`), potentially exceeding
    one of the container's dimensions. This mimics the behavior of `background-size: cover` in CSS.

    Args:
        image (CudaImage): The original image to be resized.
        container_width (int): Target container width.
        container_height (int): Target container height.

    Returns:
        tuple[int, int]: The new width and height that the image should be resized to.

    Example:
        >>> get_cover_resize_dimensions(img, 1920, 1080)
        (1920, 1280)
    """

    scale = max(container_width / image.width, container_height / image.height)
    new_width = int(image.width * scale)
    new_height = int(image.height * scale)

    return new_width, new_height

get_no_padding_size(image, padding)

Calculates the size of an image after removing equal padding from all sides.

Parameters:

Name Type Description Default
image CudaImage

The padded image.

required
padding int

Padding in pixels to remove from each side.

required

Returns:

Type Description
tuple[int, int]

tuple[int, int]: The resulting width and height without padding.

Example

get_no_padding_size(img, 10) (image.width - 20, image.height - 20)

Source code in photoff\operations\utils.py
def get_no_padding_size(image: CudaImage, padding: int) -> tuple[int, int]:
    """
    Calculates the size of an image after removing equal padding from all sides.

    Args:
        image (CudaImage): The padded image.
        padding (int): Padding in pixels to remove from each side.

    Returns:
        tuple[int, int]: The resulting width and height without padding.

    Example:
        >>> get_no_padding_size(img, 10)
        (image.width - 20, image.height - 20)
    """
    return image.width - (padding * 2), image.height - (padding * 2)

get_padding_size(image, padding)

Calculates the size of an image after adding equal padding on all sides.

Parameters:

Name Type Description Default
image CudaImage

The original image.

required
padding int

Padding in pixels to add on each side.

required

Returns:

Type Description
tuple[int, int]

tuple[int, int]: The new width and height including padding.

Example

get_padding_size(img, 10) (image.width + 20, image.height + 20)

Source code in photoff\operations\utils.py
def get_padding_size(image: CudaImage, padding: int) -> tuple[int, int]:
    """
    Calculates the size of an image after adding equal padding on all sides.

    Args:
        image (CudaImage): The original image.
        padding (int): Padding in pixels to add on each side.

    Returns:
        tuple[int, int]: The new width and height including padding.

    Example:
        >>> get_padding_size(img, 10)
        (image.width + 20, image.height + 20)
    """
    return image.width + (padding * 2), image.height + (padding * 2)

photoff.core.buffer

copy_buffers_same_size(dst, src, width, height)

Copies data between two CUDA buffers of the same size.

This is useful for in-GPU memory operations like duplicating an image or preparing a temporary working buffer.

Parameters:

Name Type Description Default
dst CudaBuffer

Destination buffer on the device.

required
src CudaBuffer

Source buffer on the device.

required
width int

Width of the image.

required
height int

Height of the image.

required

Returns:

Type Description
None

None

Example

copy_buffers_same_size(tmp_buf, original_buf, 512, 512)

Source code in photoff\core\buffer.py
def copy_buffers_same_size(dst: "CudaBuffer", src: "CudaBuffer", width: int, height: int) -> None:
    """
    Copies data between two CUDA buffers of the same size.

    This is useful for in-GPU memory operations like duplicating an image or
    preparing a temporary working buffer.

    Args:
        dst (CudaBuffer): Destination buffer on the device.
        src (CudaBuffer): Source buffer on the device.
        width (int): Width of the image.
        height (int): Height of the image.

    Returns:
        None

    Example:
        >>> copy_buffers_same_size(tmp_buf, original_buf, 512, 512)
    """

    _lib.copy_buffers_same_size(dst, src, width, height)

copy_to_device(d_dst, h_src, width, height)

Copies image data from host (CPU) to device (GPU) memory.

Parameters:

Name Type Description Default
d_dst CudaBuffer

Destination buffer in device memory.

required
h_src CudaBuffer

Source buffer in host memory.

required
width int

Width of the image.

required
height int

Height of the image.

required

Returns:

Type Description
None

None

Example

copy_to_device(gpu_buf, cpu_buf, 256, 256)

Source code in photoff\core\buffer.py
def copy_to_device(d_dst: "CudaBuffer", h_src: "CudaBuffer", width: int, height: int) -> None:
    """
    Copies image data from host (CPU) to device (GPU) memory.

    Args:
        d_dst (CudaBuffer): Destination buffer in device memory.
        h_src (CudaBuffer): Source buffer in host memory.
        width (int): Width of the image.
        height (int): Height of the image.

    Returns:
        None

    Example:
        >>> copy_to_device(gpu_buf, cpu_buf, 256, 256)
    """

    _lib.copy_to_device(d_dst, h_src, width, height)

copy_to_host(h_dst, d_src, width, height)

Copies image data from device (GPU) to host (CPU) memory.

Parameters:

Name Type Description Default
h_dst CudaBuffer

Destination buffer in host memory.

required
d_src CudaBuffer

Source buffer in device memory.

required
width int

Width of the image.

required
height int

Height of the image.

required

Returns:

Type Description
None

None

Example

copy_to_host(cpu_buf, gpu_buf, 256, 256)

Source code in photoff\core\buffer.py
def copy_to_host(h_dst: "CudaBuffer", d_src: "CudaBuffer", width: int, height: int) -> None:
    """
    Copies image data from device (GPU) to host (CPU) memory.

    Args:
        h_dst (CudaBuffer): Destination buffer in host memory.
        d_src (CudaBuffer): Source buffer in device memory.
        width (int): Width of the image.
        height (int): Height of the image.

    Returns:
        None

    Example:
        >>> copy_to_host(cpu_buf, gpu_buf, 256, 256)
    """

    _lib.copy_to_host(h_dst, d_src, width, height)

create_buffer(width, height)

Allocates a new CUDA buffer for an image of given dimensions.

Parameters:

Name Type Description Default
width int

Width of the buffer in pixels.

required
height int

Height of the buffer in pixels.

required

Returns:

Name Type Description
CudaBuffer CudaBuffer

A pointer to the allocated device memory buffer.

Example

buffer = create_buffer(512, 512)

Source code in photoff\core\buffer.py
def create_buffer(width: int, height: int) -> "CudaBuffer":
    """
    Allocates a new CUDA buffer for an image of given dimensions.

    Args:
        width (int): Width of the buffer in pixels.
        height (int): Height of the buffer in pixels.

    Returns:
        CudaBuffer: A pointer to the allocated device memory buffer.

    Example:
        >>> buffer = create_buffer(512, 512)
    """

    return _lib.create_buffer(width, height)

free_buffer(buffer)

Frees a CUDA buffer previously allocated on the device.

Parameters:

Name Type Description Default
buffer CudaBuffer

The buffer to free.

required

Returns:

Type Description
None

None

Example

free_buffer(buffer)

Source code in photoff\core\buffer.py
def free_buffer(buffer: "CudaBuffer") -> None:
    """
    Frees a CUDA buffer previously allocated on the device.

    Args:
        buffer (CudaBuffer): The buffer to free.

    Returns:
        None

    Example:
        >>> free_buffer(buffer)
    """

    _lib.free_buffer(buffer)

photoff.core.cuda_interface