python-FloatImg/floatimg/image.py

263 lines
9.3 KiB
Python

import ctypes as ct
from floatimg.settings import LIB
############################################################################################################
# type constants
GRAY = 1
RGB = 3
RGBA = 4
RGBZ = 99
############################################################################################################
# Type mapping
class C_FloatImg(ct.Structure):
"""mapping to the C structure of FloatImg"""
pass
C_FloatImg._fields_ = [
("magic", ct.c_ulong),
("width", ct.c_int),
("height", ct.c_int),
("type", ct.c_int),
("fval", ct.c_float),
("count", ct.c_int),
("R", ct.POINTER(ct.c_float)),
("G", ct.POINTER(ct.c_float)),
("B", ct.POINTER(ct.c_float)),
("A", ct.POINTER(ct.c_float)),
("reserved", ct.c_int),
]
############################################################################################################
# declaration of input / output types
c_fimgcreate = LIB.fimg_create
c_fimgcreate.argtypes = (ct.POINTER(C_FloatImg), ct.c_int, ct.c_int, ct.c_int)
c_fimgcreate.restype = ct.c_int
c_fimg_destroy = LIB.fimg_destroy
c_fimg_destroy.argtypes = (ct.POINTER(C_FloatImg),)
c_fimg_destroy.restype = ct.c_int
c_fimg_clone = LIB.fimg_clone
c_fimg_clone.argtypes = (ct.POINTER(C_FloatImg), ct.POINTER(C_FloatImg))
c_fimg_clone.restype = ct.c_int
c_fimg_copy_data = LIB.fimg_copy_data
c_fimg_copy_data.argtypes = (ct.POINTER(C_FloatImg), ct.POINTER(C_FloatImg))
c_fimg_copy_data.restype = ct.c_int
c_fimg_type_is_valid = LIB.fimg_type_is_valid
c_fimg_type_is_valid.argtypes = (ct.c_int,)
c_fimg_copy_data.restype = ct.c_int
c_fimg_str_type = LIB.fimg_str_type
c_fimg_str_type.argtypes = (ct.c_int,)
c_fimg_str_type.restype = ct.c_char_p
c_fimg_clear = LIB.fimg_clear
c_fimg_clear.argtypes = (ct.POINTER(C_FloatImg),)
c_fimg_clear.restype = ct.c_int
c_fimg_plot_rgb = LIB.fimg_plot_rgb
c_fimg_plot_rgb.argtypes = (
ct.POINTER(C_FloatImg),
ct.c_int,
ct.c_int,
ct.c_float,
ct.c_float,
ct.c_float,
)
c_fimg_plot_rgb.restype = ct.c_int
# int fimg_get_rgb(FloatImg *head, int x, int y, float *rgb);
c_fimg_get_rgb = LIB.fimg_get_rgb
c_fimg_get_rgb.argtypes = (
ct.POINTER(C_FloatImg),
ct.c_int,
ct.c_int,
ct.POINTER(ct.c_float * 3),
)
c_fimg_get_rgb.restype = ct.c_int
# int fimg_put_rgb(FloatImg *head, int x, int y, float *rgb);
c_fimg_put_rgb = LIB.fimg_put_rgb
c_fimg_put_rgb.argtypes = (
ct.POINTER(C_FloatImg),
ct.c_int,
ct.c_int,
ct.POINTER(ct.c_float * 3),
)
c_fimg_put_rgb.restype = ct.c_int
c_fimg_rgb_constant = LIB.fimg_rgb_constant
c_fimg_rgb_constant.argtypes = (
ct.POINTER(C_FloatImg),
ct.c_float,
ct.c_float,
ct.c_float,
)
c_fimg_rgb_constant.restype = ct.c_int
c_fimg_dump_to_file = LIB.fimg_dump_to_file
c_fimg_dump_to_file.argtypes = (ct.POINTER(C_FloatImg), ct.c_char_p, ct.c_int)
c_fimg_dump_to_file.restype = ct.c_int
c_fimg_load_from_dump = LIB.fimg_load_from_dump
c_fimg_load_from_dump.argtypes = (ct.c_char_p, ct.POINTER(C_FloatImg))
c_fimg_load_from_dump.restype = ct.c_int
c_fimg_fileinfos = LIB.fimg_fileinfos
c_fimg_fileinfos.argtypes = (ct.c_char_p, ct.POINTER(ct.c_int * 3))
c_fimg_fileinfos.restype = ct.c_int
############################################################################################################
class FloatImg:
"""
Pythonic Object-Oriented encapsulation of floatimg library
:attr c_img: an instance of C_FloatImg structure
:attr c_img: an pointer to the c_img structure
"""
# proxy attributes to the C_FloatImg structure
magic = property(lambda self: self.c_img.magic)
width = property(lambda self: self.c_img.width)
height = property(lambda self: self.c_img.height)
type_id = property(lambda self: self.c_img.type)
fval = property(lambda self: self.c_img.fval)
count = property(lambda self: self.c_img.count)
# oh yeah, really really sluggish access to pixels img.R[0].contents
# however, pixel data are not designed to be accessed this way
R = property(
lambda self: pointer(self.c_img.contents.R)[: self.width * self.height]
)
#######################################################################################################
def __init__(self, c_img):
self.c_img = c_img
self.c_img_p = ct.pointer(c_img)
#######################################################################################################
def __str__(self):
return f"<{self.__class__.__name__} instance {id(self)} width={self.width} height={self.height} type={self.str_type}>"
#######################################################################################################
def destroy(self):
"""destroy the underlying structure. automattically called at instance destruction"""
assert c_fimg_destroy(self.c_img) == 0
#######################################################################################################
def __del__(self):
self.destroy()
#######################################################################################################
def clear(self):
"""clear data"""
return c_fimg_clear(self.c_img_p)
#######################################################################################################
def clone(self, flags=0):
"""return a clone of the current instance"""
new_pic = C_FloatImg()
assert c_fimg_clone(self.c_img_p, ct.pointer(new_pic), flags) == 0
return FloatImg(new_pic)
#######################################################################################################
def copy_data(self, to_img):
assert c_fimg_copy_data(self.c_img_p, to_img.c_img_p) == 0
#######################################################################################################
@property
def str_type(self):
"""return the type of the image as a string"""
return c_fimg_str_type(self.c_img.type).decode("utf-8")
#######################################################################################################
def rgb_constant(self, r, v, b):
assert c_fimg_rgb_constant(self.c_img_p, r, v, b) == 0
#######################################################################################################
def get_rgb(self, x, y):
"""get r,g,b triplet from a pixel"""
rgb = (ct.c_float * 3)()
assert c_fimg_get_rgb(self.c_img_p, x, y, ct.pointer(rgb)) == 0
return rgb[:3]
#######################################################################################################
def put_rgb(self, x, y, rgb):
"""put r,g,b triplet to a pixel"""
# TODO may be a better way to create the array rather than iterating
c_rgb = (ct.c_float * 3)()
for i, c in enumerate(rgb):
c_rgb[i] = c
assert c_fimg_put_rgb(self.c_img_p, x, y, ct.pointer(c_rgb)) == 0
#######################################################################################################
def dump_to_file(self, fname):
"""save data to a dump file"""
# TODO use system encoding instead of utf-8
assert (
c_fimg_dump_to_file(
self.c_img_p, ct.c_char_p(bytes(fname, encoding="utf8")), 0
)
== 0
)
#######################################################################################################
def load_from_dump(self, fname):
"""load data from a dump. size and type have to be compatible"""
assert (
c_fimg_load_from_dump(
ct.c_char_p(bytes(fname, encoding="utf8")), self.c_img_p
)
== 0
)
###########################################################################################################
def fileinfos(fname):
"""return witdh, height, img_type triplet read from a dump file"""
datas = (ct.c_int * 3)()
assert (
c_fimg_fileinfos(ct.c_char_p(bytes(fname, encoding="utf8")), ct.pointer(datas))
== 0
)
return datas[:3]
###########################################################################################################
def create(witdh, height, type_id):
"""create an new FloatImg instance"""
assert type_is_valid(type_id)
img = C_FloatImg()
assert c_fimgcreate(ct.pointer(img), witdh, height, type_id) == 0
return FloatImg(img)
###########################################################################################################
def create_rgb(witdh, height):
"""create a new rgb instance """
return create(witdh, height, RGB)
###########################################################################################################
def create_from_dump(fname):
"""Create a new instance from a dump file"""
witdh, height, img_type = fileinfos(fname)
img = create(witdh, height, img_type)
img.load_from_dump(fname)
return img
###########################################################################################################
def type_is_valid(type_id):
"""return True if type_id is a valid one"""
return c_fimg_type_is_valid(type_id) == 1