Spaces:
Running
on
Zero
Running
on
Zero
File size: 4,257 Bytes
d1ed09d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
"""
Binary graphical composition operators
See https://www.cairographics.org/operators/; more could easily be added from there.
"""
from __future__ import annotations
import numba as nb
import numpy as np
import os
image_operators = ('over', 'add', 'saturate', 'source')
array_operators = ('add_arr', 'max_arr', 'min_arr', 'source_arr')
__all__ = ('composite_op_lookup', 'validate_operator') + image_operators + array_operators
def validate_operator(how, is_image):
name = how if is_image else how + '_arr'
if is_image:
if name not in image_operators:
raise ValueError('Operator %r not one of the supported image operators: %s'
% (how, ', '.join(repr(el) for el in image_operators)))
elif name not in array_operators:
raise ValueError('Operator %r not one of the supported array operators: %s'
% (how, ', '.join(repr(el[:-4]) for el in array_operators)))
@nb.jit('(uint32,)', nopython=True, nogil=True, cache=True)
def extract_scaled(x):
"""Extract components as float64 values in [0.0, 1.0]"""
r = np.float64(( x & 255) / 255)
g = np.float64(((x >> 8) & 255) / 255)
b = np.float64(((x >> 16) & 255) / 255)
a = np.float64(((x >> 24) & 255) / 255)
return r, g, b, a
@nb.jit('(float64, float64, float64, float64)', nopython=True,
nogil=True, cache=True)
def combine_scaled(r, g, b, a):
"""Combine components in [0, 1] to rgba uint32"""
r2 = min(255, np.uint32(r * 255))
g2 = min(255, np.uint32(g * 255))
b2 = min(255, np.uint32(b * 255))
a2 = min(255, np.uint32(a * 255))
return np.uint32((a2 << 24) | (b2 << 16) | (g2 << 8) | r2)
jit_enabled = os.environ.get('NUMBA_DISABLE_JIT', '0') == '0'
if jit_enabled:
extract_scaled.disable_compile()
combine_scaled.disable_compile()
# Lookup table for storing compositing operators by function name
composite_op_lookup = {}
def operator(f):
"""Define and register a new image composite operator"""
if jit_enabled:
f2 = nb.vectorize(f)
f2._compile_for_argtys((nb.types.uint32, nb.types.uint32))
f2._frozen = True
else:
f2 = np.vectorize(f)
composite_op_lookup[f.__name__] = f2
return f2
@operator
def source(src, dst):
if src & 0xff000000:
return src
else:
return dst
@operator
def over(src, dst):
sr, sg, sb, sa = extract_scaled(src)
dr, dg, db, da = extract_scaled(dst)
factor = 1 - sa
a = sa + da * factor
if a == 0:
return np.uint32(0)
r = (sr * sa + dr * da * factor)/a
g = (sg * sa + dg * da * factor)/a
b = (sb * sa + db * da * factor)/a
return combine_scaled(r, g, b, a)
@operator
def add(src, dst):
sr, sg, sb, sa = extract_scaled(src)
dr, dg, db, da = extract_scaled(dst)
a = min(1, sa + da)
if a == 0:
return np.uint32(0)
r = (sr * sa + dr * da)/a
g = (sg * sa + dg * da)/a
b = (sb * sa + db * da)/a
return combine_scaled(r, g, b, a)
@operator
def saturate(src, dst):
sr, sg, sb, sa = extract_scaled(src)
dr, dg, db, da = extract_scaled(dst)
a = min(1, sa + da)
if a == 0:
return np.uint32(0)
factor = min(sa, 1 - da)
r = (factor * sr + dr * da)/a
g = (factor * sg + dg * da)/a
b = (factor * sb + db * da)/a
return combine_scaled(r, g, b, a)
def arr_operator(f):
"""Define and register a new array composite operator"""
if jit_enabled:
f2 = nb.vectorize(f)
f2._compile_for_argtys(
(nb.types.int32, nb.types.int32))
f2._compile_for_argtys(
(nb.types.int64, nb.types.int64))
f2._compile_for_argtys(
(nb.types.float32, nb.types.float32))
f2._compile_for_argtys(
(nb.types.float64, nb.types.float64))
f2._frozen = True
else:
f2 = np.vectorize(f)
composite_op_lookup[f.__name__] = f2
return f2
@arr_operator
def source_arr(src, dst):
if src:
return src
else:
return dst
@arr_operator
def add_arr(src, dst):
return src + dst
@arr_operator
def max_arr(src, dst):
return max([src, dst])
@arr_operator
def min_arr(src, dst):
return min([src, dst])
|