From dd973cee8f0c8caafaede4865497ae873646caac Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@kth.se>
Date: Wed, 17 Aug 2016 22:07:51 +0200
Subject: ...
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Mattias Andrée <maandree@kth.se>
---
 src/libcoopgamma.py             |  86 ++++++++++++++++++++-
 src/libcoopgamma_native.pyx.gpp | 160 ++++++++++++++++++++++++++++++++++++----
 src/test                        |  88 +++++++++++++++++++++-
 3 files changed, 315 insertions(+), 19 deletions(-)

diff --git a/src/libcoopgamma.py b/src/libcoopgamma.py
index 3519881..c09c59e 100644
--- a/src/libcoopgamma.py
+++ b/src/libcoopgamma.py
@@ -31,6 +31,22 @@ class Support(enum.IntEnum):
     NO = 0
     MAYBE = 1
     YES = 2
+    
+    @staticmethod
+    def str(v):
+        '''
+        Get string representation of a value
+        
+        @param   v:int  The value, must be value of the enum
+        @return  :str   Human-readable string representation of the value
+        '''
+        if v == Support.NO:
+            return 'no'
+        if v == Support.YES:
+            return 'yes'
+        if v == Support.MAYBE:
+            return 'maybe'
+        raise ValueError()
 
 
 class Depth(enum.IntEnum):
@@ -53,6 +69,28 @@ class Depth(enum.IntEnum):
     UINT64 = 64
     FLOAT = -1
     DOUBLE = -2
+    
+    @staticmethod
+    def str(v):
+        '''
+        Get string representation of a value
+        
+        @param   v:int  The value, must be value of the enum
+        @return  :str   Human-readable string representation of the value
+        '''
+        if v == Depth.UINT8:
+            return '8-bits'
+        if v == Depth.UINT16:
+            return '16-bits'
+        if v == Depth.UINT32:
+            return '326-bits'
+        if v == Depth.UINT64:
+            return '64-bits'
+        if v == Depth.FLOAT:
+            return 'single-precision floating-point'
+        if v == Depth.DOUBLE:
+            return 'double-precision floating-point'
+        raise ValueError()
 
 
 class Lifespan(enum.IntEnum):
@@ -66,6 +104,22 @@ class Lifespan(enum.IntEnum):
     REMOVE = 0
     UNTIL_DEATH = 1
     UNTIL_REMOVAL = 2
+    
+    @staticmethod
+    def str(v):
+        '''
+        Get string representation of a value
+        
+        @param   v:int  The value, must be value of the enum
+        @return  :str   Human-readable string representation of the value
+        '''
+        if v == Lifespan.REMOVE:
+            return 'remove'
+        if v == Lifespan.UNTIL_DEATH:
+            return 'until death'
+        if v == Lifespan.UNTIL_REMOVAL:
+            return 'until removal'
+        raise ValueError()
 
 
 class Colourspace(enum.IntEnum):
@@ -83,6 +137,26 @@ class Colourspace(enum.IntEnum):
     RGB = 2
     NON_RGB = 3
     GREY = 4
+    
+    @staticmethod
+    def str(v):
+        '''
+        Get string representation of a value
+        
+        @param   v:int  The value, must be value of the enum
+        @return  :str   Human-readable string representation of the value
+        '''
+        if v == Colourspace.UNKNOWN:
+            return 'unknown'
+        if v == Colourspace.SRGB:
+            return 'sRGB'
+        if v == Colourspace.RGB:
+            return 'RGB'
+        if v == Colourspace.NON_RGB:
+            return 'non-RGB'
+        if v == Colourspace.GREY:
+            return 'greyscale'
+        raise ValueError()
 
 
 class Ramps:
@@ -223,6 +297,14 @@ class GamutPoint:
         '''
         params = (self.x_raw, self.y_raw)
         return 'libcoopgamma.GamutPoint(%s)' % ', '.join(repr(p) for p in params)
+    
+    def __str__(self):
+        '''
+        Get human-readable string representation of instance
+        
+        @return  :str  Human-readable string representation of instance
+        '''
+        return '(%f, %f)' % (self.x, self.y)
 
 
 class Gamut:
@@ -409,9 +491,9 @@ class QueriedFilter:
         '''
         self.priority = priority
         self.fclass = fclass
+        self.ramps = None
         if ramps is not None:
             self.ramps = Ramps(*ramps) if isinstance(ramps, tuple) else ramps
-        self.ramps = None
     
     def clone(self, shallow = False):
         '''
@@ -553,7 +635,7 @@ class ErrorReport:
         error = ErrorReport(*error)
         if not error.custom and not error.server_side:
             import os
-            return OSError(error.number, os.strerror(error))
+            return OSError(error.number, os.strerror(error.number))
         else:
             return LibcoopgammaError(error)
 
diff --git a/src/libcoopgamma_native.pyx.gpp b/src/libcoopgamma_native.pyx.gpp
index 694e8f5..5c6b796 100644
--- a/src/libcoopgamma_native.pyx.gpp
+++ b/src/libcoopgamma_native.pyx.gpp
@@ -1476,7 +1476,7 @@ def libcoopgamma_native_get_gamma_recv(address : int, async : int):
     cdef libcoopgamma_filter_table_t table
     cdef bytes bs
     cdef libcoopgamma_ramps_t* rampsp
-    cdef bytes fclass
+    cdef char* fclass
     try:
         if libcoopgamma_filter_table_initialise(&table) < 0:
             return int(errno)
@@ -1488,9 +1488,9 @@ def libcoopgamma_native_get_gamma_recv(address : int, async : int):
             ret = (False, (int(ctx.error.number), ctx.error.custom != 0, ctx.error.server_side != 0, desc))
         else:
             filters = [None] * int(table.filter_count)
-            for i in range(int(table.filter_count)):
-                rampsp = &(table.filters[i].ramps)
-                fclass = table.filters[i].fclass
+            for j in range(int(table.filter_count)):
+                rampsp = &(table.filters[j].ramps)
+                fclass = table.filters[j].fclass
                 red   = [None] * rampsp.u_red_size
                 green = [None] * rampsp.u_green_size
                 blue  = [None] * rampsp.u_blue_size
@@ -1536,8 +1536,12 @@ def libcoopgamma_native_get_gamma_recv(address : int, async : int):
                         green[i] = (<uint64_t*>(rampsp.u_green))[i]
                     for i in range(rampsp.u_blue_size):
                         blue[i] = (<uint64_t*>(rampsp.u_blue))[i]
-                ramps = (rampsp.u_red_size, rampsp.u_green_size, rampsp.u_blue_size, red, green, blue)
-                filters[i] = (int(table.filters[i].priority), fclass.decode('utf-8', 'strict'), ramps)
+                ramps = (red, green, blue)
+                pclass = None
+                if fclass is not NULL:
+                    bs = fclass
+                    pclass = bs.decode('utf-8', 'strict')
+                filters[j] = (int(table.filters[j].priority), pclass, ramps)
             ret = (True, (int(table.red_size), int(table.green_size), int(table.blue_size),
                           int(table.depth), filters))
     finally:
@@ -1565,7 +1569,7 @@ def libcoopgamma_native_get_gamma_sync(query, address : int):
     cdef libcoopgamma_filter_query_t qry
     cdef bytes bs
     cdef libcoopgamma_ramps_t* rampsp
-    cdef bytes fclass
+    cdef char* fclass
     crtc_bs = query.crtc.encode('utf-8') + bytes([0])
     qry.high_priority = <int64_t>(query.high_priority)
     qry.low_priority = <int64_t>(query.low_priority)
@@ -1589,9 +1593,9 @@ def libcoopgamma_native_get_gamma_sync(query, address : int):
                 ret = (False, (int(ctx.error.number), ctx.error.custom != 0, ctx.error.server_side != 0, desc))
             else:
                 filters = [None] * int(table.filter_count)
-                for i in range(int(table.filter_count)):
-                    rampsp = &(table.filters[i].ramps)
-                    fclass = table.filters[i].fclass
+                for j in range(int(table.filter_count)):
+                    rampsp = &(table.filters[j].ramps)
+                    fclass = table.filters[j].fclass
                     red   = [None] * rampsp.u_red_size
                     green = [None] * rampsp.u_green_size
                     blue  = [None] * rampsp.u_blue_size
@@ -1637,8 +1641,12 @@ def libcoopgamma_native_get_gamma_sync(query, address : int):
                             green[i] = (<uint64_t*>(rampsp.u_green))[i]
                         for i in range(rampsp.u_blue_size):
                             blue[i] = (<uint64_t*>(rampsp.u_blue))[i]
-                    ramps = (rampsp.u_red_size, rampsp.u_green_size, rampsp.u_blue_size, red, green, blue)
-                    filters[i] = (int(table.filters[i].priority), fclass.decode('utf-8', 'strict'), ramps)
+                    ramps = (red, green, blue)
+                    pclass = None
+                    if fclass is not NULL:
+                        bs = fclass
+                        pclass = bs.decode('utf-8', 'strict')
+                    filters[j] = (int(table.filters[j].priority), pclass, ramps)
                 ret = (True, (int(table.red_size), int(table.green_size), int(table.blue_size),
                               int(table.depth), filters))
         finally:
@@ -1648,6 +1656,116 @@ def libcoopgamma_native_get_gamma_sync(query, address : int):
     return ret
 
 
+def libcoopgamma_native_copy_ramps(intptr_t dest_address, src, libcoopgamma_depth_t depth):
+    '''
+    Copy a Python ramp-trio into C ramp-trio
+    
+    @param  dest_address:intptr_t       The address of the C ramp-trio
+    @param  src:Ramps                   The Python ramp-trio
+    @param  depth:libcoopgamma_depth_t  The data type of the ramp stops
+    '''
+    cdef libcoopgamma_ramps_t* dest = <libcoopgamma_ramps_t*><void*><intptr_t>dest_address
+    cdef uint8_t* r8
+    cdef uint8_t* g8
+    cdef uint8_t* b8
+    cdef uint16_t* r16
+    cdef uint16_t* g16
+    cdef uint16_t* b16
+    cdef uint32_t* r32
+    cdef uint32_t* g32
+    cdef uint32_t* b32
+    cdef uint64_t* r64
+    cdef uint64_t* g64
+    cdef uint64_t* b64
+    cdef float* rf
+    cdef float* gf
+    cdef float* bf
+    cdef double* rd
+    cdef double* gd
+    cdef double* bd
+    cdef size_t rn = <size_t>len(src.red)
+    cdef size_t gn = <size_t>len(src.green)
+    cdef size_t bn = <size_t>len(src.blue)
+    if depth == 8:
+        r8 = <uint8_t*>malloc((rn + gn + bn) * sizeof(uint8_t))
+        g8 = r8 + rn
+        b8 = g8 + gn
+        for i in range(int(rn)):
+            r8[i] = <uint8_t>(src.red[i])
+        for i in range(int(gn)):
+            g8[i] = <uint8_t>(src.green[i])
+        for i in range(int(bn)):
+            b8[i] = <uint8_t>(src.blue[i])
+        dest.u_red   = <void*>r8
+        dest.u_green = <void*>g8
+        dest.u_blue  = <void*>b8
+    elif depth == 16:
+        r16 = <uint16_t*>malloc((rn + gn + bn) * sizeof(uint16_t))
+        g16 = r16 + rn
+        b16 = g16 + gn
+        for i in range(int(rn)):
+            r16[i] = <uint16_t>(src.red[i])
+        for i in range(int(gn)):
+            g16[i] = <uint16_t>(src.green[i])
+        for i in range(int(bn)):
+            b16[i] = <uint16_t>(src.blue[i])
+        dest.u_red   = <void*>r16
+        dest.u_green = <void*>g16
+        dest.u_blue  = <void*>b16
+    elif depth == 32:
+        r32 = <uint32_t*>malloc((rn + gn + bn) * sizeof(uint32_t))
+        g32 = r32 + rn
+        b32 = g32 + gn
+        for i in range(int(rn)):
+            r32[i] = <uint32_t>(src.red[i])
+        for i in range(int(gn)):
+            g32[i] = <uint32_t>(src.green[i])
+        for i in range(int(bn)):
+            b32[i] = <uint32_t>(src.blue[i])
+        dest.u_red   = <void*>r32
+        dest.u_green = <void*>g32
+        dest.u_blue  = <void*>b32
+    elif depth == 64:
+        r64 = <uint64_t*>malloc((rn + gn + bn) * sizeof(uint64_t))
+        g64 = r64 + rn
+        b64 = g64 + gn
+        for i in range(int(rn)):
+            r64[i] = <uint64_t>(src.red[i])
+        for i in range(int(gn)):
+            g64[i] = <uint64_t>(src.green[i])
+        for i in range(int(bn)):
+            b64[i] = <uint64_t>(src.blue[i])
+        dest.u_red   = <void*>r64
+        dest.u_green = <void*>g64
+        dest.u_blue  = <void*>b64
+    elif depth == -1:
+        rf = <float*>malloc((rn + gn + bn) * sizeof(float))
+        gf = rf + rn
+        bf = gf + gn
+        for i in range(int(rn)):
+            rf[i] = <float>(src.red[i])
+        for i in range(int(gn)):
+            gf[i] = <float>(src.green[i])
+        for i in range(int(bn)):
+            bf[i] = <float>(src.blue[i])
+        dest.u_red   = <void*>rf
+        dest.u_green = <void*>gf
+        dest.u_blue  = <void*>bf
+    else:
+        rd = <double*>malloc((rn + gn + bn) * sizeof(double))
+        gd = rd + rn
+        bd = gd + gn
+        for i in range(int(rn)):
+            rd[i] = <double>(src.red[i])
+        for i in range(int(gn)):
+            gd[i] = <double>(src.green[i])
+        for i in range(int(bn)):
+            bd[i] = <double>(src.blue[i])
+        dest.u_red   = <void*>rd
+        dest.u_green = <void*>gd
+        dest.u_blue  = <void*>bd
+
+
 def libcoopgamma_native_set_gamma_send(filtr, address : int, async : int):
     '''
     Apply, update, or remove a gamma ramp adjustment, send request part
@@ -1670,8 +1788,14 @@ def libcoopgamma_native_set_gamma_send(filtr, address : int, async : int):
     flr.lifespan = <libcoopgamma_lifespan_t>(filtr.lifespan)
     flr.depth = <libcoopgamma_depth_t>(filtr.depth)
     flr.fclass = NULL
-    flr.crtc = <char*>malloc(len(crtc_bs) * sizeof(char))
+    flr.crtc = NULL
+    flr.ramps.u_red_size   = len(filtr.ramps.red)
+    flr.ramps.u_green_size = len(filtr.ramps.green)
+    flr.ramps.u_blue_size  = len(filtr.ramps.blue)
+    flr.ramps.u_red = NULL
     try:
+        libcoopgamma_native_copy_ramps(<intptr_t><void*>&(flr.ramps), filtr.ramps, flr.depth)
+        flr.crtc = <char*>malloc(len(crtc_bs) * sizeof(char))
         if flr.crtc is NULL:
             return int(errno)
         flr.fclass = <char*>malloc(len(clss_bs) * sizeof(char))
@@ -1686,6 +1810,7 @@ def libcoopgamma_native_set_gamma_send(filtr, address : int, async : int):
     finally:
         free(flr.crtc)
         free(flr.fclass)
+        free(flr.ramps.u_red)
     return 0
 
 
@@ -1736,8 +1861,14 @@ def libcoopgamma_native_set_gamma_sync(filtr, address : int):
     flr.lifespan = <libcoopgamma_lifespan_t>(filtr.lifespan)
     flr.depth = <libcoopgamma_depth_t>(filtr.depth)
     flr.fclass = NULL
-    flr.crtc = <char*>malloc(len(crtc_bs) * sizeof(char))
+    flr.crtc = NULL
+    flr.ramps.u_red_size   = len(filtr.ramps.red)
+    flr.ramps.u_green_size = len(filtr.ramps.green)
+    flr.ramps.u_blue_size  = len(filtr.ramps.blue)
+    flr.ramps.u_red = NULL
     try:
+        libcoopgamma_native_copy_ramps(<intptr_t><void*>&(flr.ramps), filtr.ramps, flr.depth)
+        flr.crtc = <char*>malloc(len(crtc_bs) * sizeof(char))
         if flr.crtc is NULL:
             return int(errno)
         flr.fclass = <char*>malloc(len(clss_bs) * sizeof(char))
@@ -1756,5 +1887,6 @@ def libcoopgamma_native_set_gamma_sync(filtr, address : int):
     finally:
         free(flr.crtc)
         free(flr.fclass)
+        free(flr.ramps.u_red)
     return None
 
diff --git a/src/test b/src/test
index 7f3c997..d12cfa7 100755
--- a/src/test
+++ b/src/test
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
 along with this library.  If not, see <http://www.gnu.org/licenses/>.
 '''
 
-import os, sys
+import os, sys, time
 
 os.chdir('/'.join(sys.argv[0].split('/')[:-1]))
 sys.path.append('../bin')
@@ -65,8 +65,90 @@ for crtc in g.get_crtcs_sync():
     print(crtc)
 print()
 
-print(g.get_gamma_info_sync(crtc))
-print(g.get_gamma_sync(cg.FilterQuery(crtc = crtc)))
+info = g.get_gamma_info_sync(crtc)
+print('\033[1m%s:\033[m' % 'CRTC info')
+print('Cooperative:', 'yes' if info.cooperative else 'no')
+if info.depth is not None:
+    print('Depth:', cg.Depth.str(info.depth))
+print('Supported:', cg.Support.str(info.supported))
+if info.red_size is not None:
+    print('Red stops:', info.red_size)
+if info.green_size is not None:
+    print('Green stops:', info.green_size)
+if info.blue_size is not None:
+    print('Blue stops:', info.blue_size)
+print('Colourspace:', cg.Colourspace.str(info.colourspace))
+if info.gamut is not None:
+    print('Red point:', str(info.gamut.red))
+    print('Green point:', str(info.gamut.green))
+    print('Blue point:', str(info.gamut.blue))
+    print('White point:', str(info.gamut.white))
+print()
+
+table = g.get_gamma_sync(cg.FilterQuery(crtc = crtc, coalesce = False))
+print('\033[1m%s:\033[m' % 'Filter table')
+print('Red stops:', table.red_size)
+print('Green stops:', table.green_size)
+print('Blue stops:', table.blue_size)
+print('Depth:', cg.Depth.str(table.depth))
+for i, fltr in enumerate(table.filters):
+    print('Filter %i:' % i)
+    print('  Priority:', fltr.priority)
+    print('  Class:', fltr.fclass)
+    print('  Ramps:')
+    rr, gr, br = fltr.ramps.red, fltr.ramps.green, fltr.ramps.blue
+    n = max(len(rr), len(gr), len(br))
+    fmt = '    \033[31m%s \033[32m%s \033[34m%s\033[m'
+    rr = [str(rr[i]) if i < len(rr) else '' for i in range(n)]
+    gr = [str(gr[i]) if i < len(gr) else '' for i in range(n)]
+    br = [str(br[i]) if i < len(br) else '' for i in range(n)]
+    for y in zip(rr, gr, br):
+        print(fmt % y)
+print()
+
+table = g.get_gamma_sync(cg.FilterQuery(crtc = crtc, coalesce = True))
+print('\033[1m%s:\033[m' % 'Filter table')
+print('Red stops:', table.red_size)
+print('Green stops:', table.green_size)
+print('Blue stops:', table.blue_size)
+print('Depth:', cg.Depth.str(table.depth))
+for fltr in table.filters:
+    print('Ramps:')
+    rr, gr, br = fltr.ramps.red, fltr.ramps.green, fltr.ramps.blue
+    n = max(len(rr), len(gr), len(br))
+    fmt = '  \033[31m%s \033[32m%s \033[34m%s\033[m'
+    rr = [str(rr[i]) if i < len(rr) else '' for i in range(n)]
+    gr = [str(gr[i]) if i < len(gr) else '' for i in range(n)]
+    br = [str(br[i]) if i < len(br) else '' for i in range(n)]
+    for y in zip(rr, gr, br):
+        print(fmt % y)
+print()
+
+fltr = cg.Filter(0, crtc, 'pylibcoopgamma::test::test', cg.Lifespan.UNTIL_DEATH, table.depth,
+                 cg.Ramps(table.red_size, table.green_size, table.blue_size))
+
+if table.depth < 0:
+    Y = lambda x : x
+else:
+    m = 2 ** table.depth - 1
+    Y = lambda x : int(x * m)
+
+redzero = fltr.ramps.red
+greenzero = fltr.ramps.green
+
+fltr.ramps.red = [Y(x / (table.red_size - 1)) for x in range(table.red_size)]
+g.set_gamma_sync(fltr)
+time.sleep(0.5)
+
+fltr.ramps.red = redzero
+fltr.ramps.green = [Y(x / (table.green_size - 1)) for x in range(table.green_size)]
+g.set_gamma_sync(fltr)
+time.sleep(0.5)
+
+fltr.ramps.green = greenzero
+fltr.ramps.blue = [Y(x / (table.blue_size - 1)) for x in range(table.blue_size)]
+g.set_gamma_sync(fltr)
+time.sleep(0.5)
 
 del g
 
-- 
cgit v1.2.3-70-g09d2