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 # TODO: now more about them magic = property(lambda self: self.c_img.magic) 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.width = c_img.width self.height = c_img.height self.type_id = c_img.type # convenient pointer for later calls to C library 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""" assert c_fimg_clear(self.c_img_p) == 0 ####################################################################################################### def clone(self, copy_data=False): """return a clone of the current instance""" if copy_data: flags = 1 else: flags = 0 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 fill(self, color): assert c_fimg_rgb_constant(self.c_img_p, *color) == 0 ####################################################################################################### def get(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(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(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(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 ) # TODO # fimg_plot_rgb # fimg_add_rgb # export(fname, flags) : depending file extension, save to the correct format ########################################################################################################### 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(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