# -*- coding: utf-8 -*-
"""
Manage blending ranges.
"""
from __future__ import unicode_literals, absolute_import
import numpy as np
from . import docs
from . import util
from typing import BinaryIO, List, Optional, TYPE_CHECKING # NOQA
if TYPE_CHECKING:
from . import core # NOQA
[docs]class BlendingRange(object):
"""
Blending range data.
Comprises 2 black values and 2 white values.
"""
def __init__(self,
black0=0, # type: int
black1=0, # type: int
white0=0, # type: int
white1=0, # type: int
_values=None # type: Optional[np.ndarray]
): # type: (...) -> None
if _values is not None:
self._values = _values
else:
self._values = np.array(
[black0, black1, white0, white1],
np.uint8)
@property
def black0(self): # type: (...) -> int
return self._values[0]
@black0.setter
def black0(self, val): # type: (int) -> None
self._values[0] = val
@property
def black1(self): # type: (...) -> int
return self._values[1]
@black1.setter
def black1(self, val): # type: (int) -> None
self._values[1] = val
@property
def white0(self): # type: (...) -> int
return self._values[2]
@white0.setter
def white0(self, val): # type: (int) -> None
self._values[2] = val
@property
def white1(self): # type: (...) -> int
return self._values[3]
@white1.setter
def white1(self, val): # type: (int) -> None
self._values[3] = val
[docs] @classmethod
@util.trace_read
def read(cls, fd):
# type: (BinaryIO) -> BlendingRange
buf = fd.read(4)
values = np.frombuffer(buf, np.uint8)
util.log(
"values: {}",
values)
return cls(_values=values)
read.__func__.__doc__ = docs.read
[docs] @util.trace_write
def write(self, fd, header):
# type: (BinaryIO, core.Header) -> None
fd.write(self._values.tobytes())
write.__doc__ = docs.write
[docs]class BlendingRangePair(object):
"""
Blending range pair.
The combination of a source and destination blending range.
"""
def __init__(self,
src=None, # type: Optional[BlendingRange]
dst=None # type: Optional[BlendingRange]
): # type: (...) -> None
if src is None:
src = BlendingRange()
if dst is None:
dst = BlendingRange()
self._src = src
self._dst = dst
@property
def src(self): # type: (...) -> BlendingRange
return self._src
@src.setter
def src(self, val): # type: (BlendingRange) -> None
if not isinstance(val, BlendingRange):
raise TypeError("src must be BlendingRange instance")
self._src = val
@property
def dst(self): # type: (...) -> BlendingRange
return self._dst
@dst.setter
def dst(self, val): # type: (BlendingRange) -> None
if not isinstance(val, BlendingRange):
raise TypeError("dst must be BlendingRange instance")
self._dst = val
[docs] def length(self, header): # type: (core.Header) -> int
return 8
length.__doc__ = docs.length # type: ignore
[docs] def total_length(self, header): # type: (core.Header) -> int
return self.length(header)
total_length.__doc__ = docs.total_length # type: ignore
[docs] @classmethod
@util.trace_read
def read(cls, fd):
# type: (BinaryIO) -> BlendingRangePair
src = BlendingRange.read(fd)
dst = BlendingRange.read(fd)
return cls(src=src, dst=dst)
read.__func__.__doc__ = docs.read
[docs] @util.trace_write
def write(self, fd, header):
# type: (BinaryIO, core.Header) -> None
self.src.write(fd, header)
self.dst.write(fd, header)
write.__doc__ = docs.write
[docs]class BlendingRanges(object):
"""
All of the layer blending range data.
Consists of a composite gray blend pair followed by N additional
pairs.
"""
def __init__(
self,
composite_gray_blend=None, # type: Optional[BlendingRangePair]
channels=None # type: Optional[List[BlendingRangePair]]
): # type: (...) -> None
self.composite_gray_blend = composite_gray_blend
if channels is None:
channels = []
self.channels = channels
@property
def composite_gray_blend(self): # type: (...) -> BlendingRangePair
"""Composite gray `BlendingRangePair`."""
return self._composite_gray_blend
@composite_gray_blend.setter
def composite_gray_blend(self, value):
# type: (BlendingRangePair) -> None
if (value is not None and
not isinstance(value, BlendingRangePair)):
raise TypeError(
"composite_gray_blend must be None or BlendingRangePair "
"instance."
)
self._composite_gray_blend = value
@property
def channels(self): # type: (...) -> List[BlendingRangePair]
"""List of additional `BlendingRangePair` instances."""
return self._channels
@channels.setter
def channels(self, value):
# type: (List[BlendingRangePair]) -> None
util.assert_is_list_of(value, BlendingRangePair)
self._channels = value
[docs] def length(self, header): # type: (core.Header) -> int
if (self.composite_gray_blend is not None or
len(self.channels)):
if self.composite_gray_blend is None:
composite_gray_blend = BlendingRangePair()
else:
composite_gray_blend = self.composite_gray_blend
return (
composite_gray_blend.total_length(header) +
sum(x.total_length(header) for x in self.channels))
return 0
length.__doc__ = docs.length # type: ignore
[docs] def total_length(self, header): # type: (core.Header) -> int
return 4 + self.length(header)
total_length.__doc__ = docs.total_length # type: ignore
[docs] @classmethod
@util.trace_read
def read(cls, fd, num_channels):
# type: (BinaryIO, int) -> BlendingRanges
length = util.read_value(fd, 'I')
end = fd.tell() + length
util.log("length: {}, end: {}", length, end)
if length == 0:
return cls()
composite_gray_blend = BlendingRangePair.read(fd)
channels = []
while fd.tell() < end:
channels.append(BlendingRangePair.read(fd))
fd.seek(end)
return cls(
composite_gray_blend=composite_gray_blend,
channels=channels)
read.__func__.__doc__ = docs.read
[docs] @util.trace_write
def write(self, fd, header):
# type: (BinaryIO, core.Header) -> None
util.write_value(fd, 'I', self.length(header))
if (self.composite_gray_blend is not None or
len(self.channels)):
if self.composite_gray_blend is None:
composite_gray_blend = BlendingRangePair()
else:
composite_gray_blend = self.composite_gray_blend
composite_gray_blend.write(fd, header)
for channel in self.channels:
channel.write(fd, header)
write.__doc__ = docs.write