# The MIT License (MIT) # # Copyright (c) 2016 WUSTL ZPLAB # Copyright (c) 2016-2021 Celiagg Contributors # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # # Authors: John Wiggins cpdef enum GradientSpread: SpreadPad = _enums.k_GradientSpreadPad SpreadReflect = _enums.k_GradientSpreadReflect SpreadRepeat = _enums.k_GradientSpreadRepeat cpdef enum GradientUnits: UserSpace = _enums.k_GradientUnitsUserSpace ObjectBoundingBox = _enums.k_GradientUnitsObjectBoundingBox cpdef enum PatternStyle: StyleRepeat = _enums.k_PatternStyleRepeat StyleReflect = _enums.k_PatternStyleReflect cdef _get_gradient_points(points, length): cdef double[::1] arr = numpy.asarray(points, dtype=numpy.float64, order='c') if arr.shape[0] != length: msg = 'points argument must be an iterable of length {}'.format(length) raise ValueError(msg) return arr cdef _get_gradient_stops(stops): cdef double[:,::1] arr = numpy.asarray(stops, dtype=numpy.float64, order='c') if arr.shape[1] != 5: msg = ('stops argument must be an iterable of (off, r, g, b, a) ' 'tuples.') raise ValueError(msg) return arr @cython.internal cdef class Paint: cdef _paint.Paint* _this def __dealloc__(self): del self._this property transform: def __get__(self): cdef Transform trans = Transform() trans._assign_obj(self._this.transform()) return trans def __set__(self, trans): if not isinstance(trans, Transform): raise TypeError("transform must be a Transform instance") cdef Transform _trans = trans self._this.transform(dereference(_trans._this)) cdef class LinearGradientPaint(Paint): """LinearGradientPaint(x1, y1, x2, y2, stops, spread, units) A ``Paint`` for drawing linear gradients :param x1: Gradient start point X :param y1: Gradient start point Y :param x2: Gradient end point X :param y2: Gradient end point Y :param stops: An iterable of gradient stop tuples: (offset, r, g, b, a) :param spread: The ``GradientSpread`` type for this gradient :param units: The ``GradientUnits`` type for this gradient """ cdef object _points cdef object _stops def __cinit__(self, double x1, double y1, double x2, double y2, stops, GradientSpread spread, GradientUnits units): cdef double[::1] _points = _get_gradient_points((x1, y1, x2, y2), 4) cdef double[:,::1] _stops = _get_gradient_stops(stops) self._this = new _paint.Paint(_enums.k_PaintTypeLinearGradient, &_points[0], _points.shape[0], &_stops[0][0], _stops.shape[0], spread, units) # Hold on to the allocated arrays for safety self._points = _points self._stops = _stops def copy(self): points = self._points # Not a deep copy! stops = self._stops # Not a deep copy! cpy = LinearGradientPaint( points[0], points[1], points[2], points[3], stops, self._this.spread(), self._this.units() ) cpy.transform = self.transform return cpy cdef class RadialGradientPaint(Paint): """RadialGradientPaint(cx, cy, r, fx, fy, stops, spread, units) A ``Paint`` for drawing radial gradients :param cx: Gradient center point X :param cy: Gradient center point Y :param r: Gradient radius :param fx: Gradient focus point X :param fy: Gradient focus point Y :param stops: An iterable of gradient stop tuples: (offset, r, g, b, a) :param spread: The ``GradientSpread`` type for this gradient :param units: The ``GradientUnits`` type for this gradient """ cdef object _points cdef object _stops def __cinit__(self, double cx, double cy, double r, double fx, double fy, stops, GradientSpread spread, GradientUnits units): cdef double[::1] _points = _get_gradient_points([cx, cy, r, fx, fy], 5) cdef double[:,::1] _stops = _get_gradient_stops(stops) self._this = new _paint.Paint(_enums.k_PaintTypeRadialGradient, &_points[0], _points.shape[0], &_stops[0][0], _stops.shape[0], spread, units) # Hold on to the allocated arrays for safety self._points = _points self._stops = _stops def copy(self): points = self._points # Not a deep copy! stops = self._stops # Not a deep copy! cpy = RadialGradientPaint( points[0], points[1], points[2], points[3], points[4], stops, self._this.spread(), self._this.units() ) cpy.transform = self.transform return cpy cdef class PatternPaint(Paint): """PatternPaint(style, image) A ``Paint`` for repeating patterns. :param style: A ``PatternStyle`` :param image: An ``Image`` object """ cdef PatternStyle style cdef Image img_obj def __cinit__(self, PatternStyle style, image): if not isinstance(image, Image): raise TypeError("image must be an Image instance") cdef Image img = image self._this = new _paint.Paint(style, img._this) # Hold a reference to the pattern image self.img_obj = img self.style = style def _with_format(self, PixelFormat fmt): if self.img_obj.format == fmt: return self fmt_image = convert_image(self.img_obj, fmt, bottom_up=self.img_obj.bottom_up) fmt_pattern = PatternPaint(self.style, fmt_image) fmt_pattern.transform = self.transform return fmt_pattern def copy(self): img = self.img_obj.copy() cpy = PatternPaint(self.style, img) cpy.transform = self.transform return cpy cdef class SolidPaint(Paint): """SolidPaint(r, g, b, a) A ``Paint`` for solid colors. :param r: Red value in [0, 1] :param g: Green value in [0, 1] :param b: Blue value in [0, 1] :param a: Alpha value in [0, 1] (defaults to 1.0) """ def __cinit__(self, double r, double g, double b, double a=1.0): self._this = new _paint.Paint(r, g, b, a) def __repr__(self): name = type(self).__name__ return "{}({}, {}, {}, {})".format( name, self._this.r(), self._this.g(), self._this.b(), self._this.a() ) def __richcmp__(SolidPaint self, SolidPaint other, int op): if op == 2: # == return (self._this.a() == other._this.a() and self._this.r() == other._this.r() and self._this.g() == other._this.g() and self._this.b() == other._this.b()) else: msg = "That type of comparison is not implemented for SolidPaint" raise NotImplementedError(msg) def copy(self): cpy = SolidPaint(self._this.r(), self._this.g(), self._this.b(), self._this.a()) cpy.transform = self.transform return cpy property a: def __get__(self): return self._this.a() def __set__(self, double value): self._this.a(value) property r: def __get__(self): return self._this.r() def __set__(self, double value): self._this.r(value) property g: def __get__(self): return self._this.g() def __set__(self, double value): self._this.g(value) property b: def __get__(self): return self._this.b() def __set__(self, double value): self._this.b(value)