libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

lcms2.py (3942B)


      1 #!/usr/bin/env python3
      2 # Copyright (c) the JPEG XL Project Authors. All rights reserved.
      3 #
      4 # Use of this source code is governed by a BSD-style
      5 # license that can be found in the LICENSE file.
      6 
      7 import ctypes
      8 from numpy.ctypeslib import ndpointer
      9 import numpy
     10 import os
     11 import platform
     12 
     13 IS_OSX = (platform.system() == "Darwin")
     14 
     15 default_libcms2_lib_path = ["liblcms2.so.2", "liblcms2.2.dylib"][IS_OSX]
     16 lcms2_lib_path = os.getenv("LCMS2_LIB_PATH", default_libcms2_lib_path)
     17 lcms2_lib = ctypes.cdll.LoadLibrary(lcms2_lib_path)
     18 
     19 native_open_profile = lcms2_lib.cmsOpenProfileFromMem
     20 native_open_profile.restype = ctypes.c_void_p
     21 native_open_profile.argtypes = [
     22     ctypes.c_char_p,  # MemPtr
     23     ctypes.c_size_t  # dwSize
     24 ]
     25 
     26 native_close_profile = lcms2_lib.cmsCloseProfile
     27 native_close_profile.restype = ctypes.c_int
     28 native_close_profile.argtypes = [
     29     ctypes.c_void_p  # hProfile
     30 ]
     31 
     32 native_create_transform = lcms2_lib.cmsCreateTransform
     33 native_create_transform.restype = ctypes.c_void_p
     34 native_create_transform.argtypes = [
     35     ctypes.c_void_p,  # Input
     36     ctypes.c_uint32,  # InputFormat
     37     ctypes.c_void_p,  # Output
     38     ctypes.c_uint32,  # OutputFormat
     39     ctypes.c_uint32,  # Intent
     40     ctypes.c_uint32  # dwFlags
     41 ]
     42 
     43 native_delete_transform = lcms2_lib.cmsDeleteTransform
     44 native_delete_transform.restype = None
     45 native_delete_transform.argtypes = [
     46     ctypes.c_void_p  # hTransform
     47 ]
     48 
     49 native_do_transform = lcms2_lib.cmsDoTransform
     50 native_do_transform.restype = None
     51 native_do_transform.argtypes = [
     52     ctypes.c_void_p,  # Transform
     53     ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"),  # InputBuffer
     54     ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"),  # OutputBuffer
     55     ctypes.c_uint32  # Size
     56 ]
     57 
     58 
     59 def make_format(
     60     bytes_per_sample=4,  # float32
     61     num_channels=3,  # RGB or XYZ
     62     extra_channels=0,
     63     swap_channels=0,
     64     swap_endiannes=0,
     65     planar=0,
     66     flavor=0,
     67     swap_first=0,
     68     unused=0,
     69     pixel_type=4,  # RGB
     70     optimized=0,
     71     floating_point=1):
     72     values = [bytes_per_sample, num_channels, extra_channels, swap_channels,
     73         swap_endiannes, planar, flavor, swap_first, unused, pixel_type,
     74         optimized, floating_point]
     75     bit_width = [3, 4, 3, 1, 1, 1, 1, 1, 1, 5, 1, 1]
     76     result = 0
     77     shift = 0
     78     for i in range(len(bit_width)):
     79         result += values[i] << shift
     80         shift += bit_width[i]
     81     return result
     82 
     83 
     84 def convert_pixels(from_icc, to_icc, from_pixels):
     85     from_icc = bytearray(from_icc)
     86     to_icc = bytearray(to_icc)
     87 
     88     if len(from_pixels.shape) != 3 or from_pixels.shape[2] != 3:
     89         raise ValueError("Only WxHx3 shapes are supported")
     90     from_pixels_plain = from_pixels.ravel().astype(numpy.float64)
     91     num_pixels = len(from_pixels_plain) // 3
     92     to_pixels_plain = numpy.empty(num_pixels * 3, dtype=numpy.float64)
     93 
     94     from_icc = (ctypes.c_char * len(from_icc)).from_buffer(from_icc)
     95     from_profile = native_open_profile(
     96         ctypes.cast(ctypes.pointer(from_icc), ctypes.c_char_p), len(from_icc))
     97 
     98     to_icc = (ctypes.c_char * len(to_icc)).from_buffer(to_icc)
     99     to_profile = native_open_profile(
    100         ctypes.cast(ctypes.pointer(to_icc), ctypes.c_char_p), len(to_icc))
    101 
    102     # bytes_per_sample=0 actually means 8 bytes (but there are just 3 bits to
    103     # encode the length of sample)
    104     format_rgb_f64 = make_format(bytes_per_sample=0)
    105     intent = 0  # INTENT_PERCEPTUAL
    106     flags = 0  # default; no "no-optimization"
    107     transform = native_create_transform(
    108         from_profile, format_rgb_f64, to_profile, format_rgb_f64, intent, flags)
    109 
    110     native_do_transform(
    111         transform, from_pixels_plain, to_pixels_plain, num_pixels)
    112 
    113     native_delete_transform(transform)
    114     native_close_profile(to_profile)
    115     native_close_profile(from_profile)
    116 
    117     # Return same shape and size as input
    118     return to_pixels_plain.reshape(from_pixels.shape).astype(from_pixels.dtype)
    119 
    120 if __name__ == '__main__':
    121     raise Exception("Not an executable")