Example #1
0
    def matrix(self) -> list:
        """
        a 3x3 or 5x5 convolution matrix

        useful for:
            convolve
        """
        ret = []
        matrix = self._getProperty('matrix',
                                   '[[0,0,0],[0,1,0],[0,0,0]]').replace(
                                       ' ', '')
        if matrix.startswith('[['):
            matrix = matrix[1:-1]
        else:
            matrix = matrix
        matrix = matrix.split('[')[1:]
        size = None
        for row in matrix:
            row = row.split(']', 1)[0].split(',')
            if (size is None and len(row) not in [5, 3]) or size != len(row):
                size = len(row)
                info = (matrix, len(row))
                raise SmartimageError(
                    self,
                    'Convolution matrix "%s" size %d not 3x3 or 5x5!' % info)
            ret.append([float(v) for v in row])
        if len(ret) != size:
            info = (matrix, len(ret))
            raise SmartimageError(
                self, 'Convolution matrix "%s" size %d not 3x3 or 5x5!' % info)
        return ret
Example #2
0
 def image(self) -> PilPlusImage:
     text = self.text
     # create a fake image, ImageDraw to use for size calculation
     img = Image.new('L', (1, 1))
     d = ImageDraw.Draw(img)
     # some sanity checking
     if not text:
         info = (self.name, self.root.filename, self.xml.sourceline)
         print('WARN: Layer "%s" has no text specified - %s line %d' % info)
         return None
     if self.w == 0:
         size = d.textsize(text, font=self.font, spacing=self.lineSpacing)
         w, h = size
     else:
         # determine the text wrapping and final size
         w = self.w
         textWrapper = textwrap.TextWrapper()
         charSize = d.textsize('l',
                               font=self.font,
                               spacing=self.lineSpacing)[0]
         textWrapper.width = int(w / len(text) * charSize)
         size = d.textsize(text, font=self.font, spacing=self.lineSpacing)
         while size[0] > w and textWrapper.width > 0:
             textWrapper.width -= 1
             text = textWrapper.fill(self.text)
             size = d.textsize(text,
                               font=self.font,
                               spacing=self.lineSpacing)
         h = max(size[1], self.h)
     # determine vertical alignment position
     x = self.x
     y = self.y
     align = self.align
     verticalAlign = self.verticalAlign
     if verticalAlign == 'top':
         pass
     elif verticalAlign == 'bottom':
         y += h - size[1]
     elif verticalAlign in ['center', 'middle']:
         y += h / 2 - size[1] / 2
     else:
         raise SmartimageError(
             self,
             'ERR: Unknown text vertical align mode "%s"' % verticalAlign)
     if align == 'left':
         pass
     elif align == 'right':
         x += w - size[0]
     elif align in ['center', 'middle']:
         x += w / 2 - size[0] / 2
     else:
         raise SmartimageError(self,
                               'Unknown text alignment mode "%s"' % align)
     # draw the stuff
     img = Image.new('RGBA', (int(w), int(h)), (128, 128, 128, 0))
     d = ImageDraw.Draw(img)
     d.multiline_text((0, 0), text, tuple(self.color), self.font,
                      self.anchor, self.lineSpacing, self.align)
     return img
Example #3
0
 def _createChild(self,xml:str)->FormElement:
     """
     create a child form element
     """
     child=None
     if xml==self.xml or xml.__class__.__name__ in ['_Comment']:
         pass
     elif xml.tag=='text':
         child=Text(self,xml)
     elif xml.tag=='points':
         child=Points(self,xml)
     elif xml.tag=='image':
         child=Image(self,xml)
     elif xml.tag=='point':
         child=Point(self,xml)
     elif xml.tag=='select':
         child=Select(self,xml)
     elif xml.tag=='color':
         child=Color(self,xml)
     elif xml.tag=='preview':
         child=Preview(self,xml)
     elif xml.tag=='numeric':
         child=Numeric(self,xml)
     else:
         raise SmartimageError(self,'Unknown element, "%s"'%xml.tag)
     return child
Example #4
0
 def _createChild(self,parent:'FormElement',xml:str)->'FormElement':
     """
     create a child form element
     """
     child=None
     if xml==self.xml or xml.__class__.__name__ in ['_Comment']:
         pass
     else:
         raise SmartimageError(self,'Unknown element, "%s"'%xml.tag)
     return child
Example #5
0
 def target(self) -> Layer:
     """
     the target to link to
     """
     if self._target is None:
         layerId = self.ref.split('.', 1)[0]
         self._target = self.getLayer(layerId)
         if self._target is None:
             raise SmartimageError(self,
                                   'ERR: broken link to layer %s' % layerId)
     return self._target
Example #6
0
 def roi(self) -> Union[PilPlusImage, None]:
     """
     "region of interest" used for smart resizing and possibly other things
     """
     ref = self._getProperty('roi')
     try:
         img = self.root.imageByRef(ref)
     except FileNotFoundError as e:
         raise SmartimageError(self,
                               'Missing roi resource "%s"' % e.filename)
     return img
Example #7
0
    def renderImage(self, layer: 'Layer') -> Union[PIL.Image.Image, None]:
        """
        render an image from the layer image and all of its children

        WARNING: Do not modify the image without doing a .copy() first!
        """
        image = None
        # loop prevention
        if layer.elementId in self.visitedLayers:
            info = (layer.elementId, layer.name)
            raise SmartimageError(layer, 'Link loop with layer %s "%s"' % info)
        self.visitedLayers.add(layer.elementId)
        # push a new variable context
        # do we need to do anything?
        opacity = layer.opacity
        if opacity <= 0.0 or not layer.visible:
            self.log('skipping', layer.name)
        else:
            self.log('creating new %s layer named "%s"' %
                     (layer.__class__.__name__, layer.name))
            image = layer.image  # NOTE: base image can be None
            for childLayer in layer.children:
                childImage = childLayer.renderImage(self)
                image = composite(childImage,
                                  image,
                                  opacity=childLayer.opacity,
                                  blendMode=childLayer.blendMode,
                                  mask=childLayer.mask,
                                  position=childLayer.location,
                                  resize=True)
                self.log('adding child layer "%s" at %s' %
                         (childLayer.name, childLayer.location))
            if layer.cropping is not None:
                image = image.crop(layer.cropping)
            if layer.rotation % 360 != 0:
                self.log('rotating', layer.name)
                bounds = Bounds(0, 0, image.width, image.height)
                bounds.rotateFit(layer.rotation)
                image = extendImageCanvas(image, bounds)
                image = image.rotate(layer.rotation)
            # logging
            if image is None:
                self.log('info', 'for "%s":' % layer.name)
                self.log('info', '   NULL IMAGE')
            else:
                self.log('info', 'for "%s":' % layer.name)
                self.log('info', '   mode=' + image.mode)
                self.log('info',
                         '   bounds=(0,0,%d,%d)' % (image.width, image.height))
        self.log('finished', layer.name)
        # pop off tracking info for this layer
        self.visitedLayers.pop()
        return image
Example #8
0
 def _createChild(self,parent:FormElement,xml:str):
     """
     the child elements are all points (duh)
     """
     child=None
     if xml==self.xml or xml.__class__.__name__ in ['_Comment']:
         pass
     elif xml.tag=='point':
         child=Point(parent,xml)
     else:
         raise SmartimageError(self,'Unknown element, "%s"'%xml.tag)
     return child
Example #9
0
 def _createChild(self,parent:FormElement,xml:str):
     """
     the only child elements allowed are option form elements
     """
     child=None
     if xml==self.xml or xml.__class__.__name__ in ['_Comment']:
         pass
     elif xml.tag=='option':
         child=Option(parent,xml)
     else:
         raise SmartimageError(self,'Unknown element, "%s"'%xml.tag)
     return child
Example #10
0
 def normalMap(self) -> Union[PilPlusImage, None]:
     """
     A 3d normal map, wherein red=X, green=Y, blue=Z (facing directly out from the screen)
     """
     ref = self._getProperty('normalMap', None)
     if ref is None:
         ref = normalMapFromImage(self.image)
     try:
         img = self.root.imageByRef(ref)
     except FileNotFoundError as e:
         raise SmartimageError(
             self, 'Missing normalMap resource "%s"' % e.filename)
     return img
Example #11
0
 def bumpMap(self) -> Union[PilPlusImage, None]:
     """
     a grayscale bump map or heightmap.
     """
     ref = self._getProperty('bumpMap', None)
     if ref is None:
         ref = heightMapFromNormalMap(self.normalMap)
     try:
         img = self.root.imageByRef(ref)
     except FileNotFoundError as e:
         raise SmartimageError(self,
                               'Missing bumpMap resource "%s"' % e.filename)
     return img
Example #12
0
 def _createChild(self, parent, xml) -> 'Layer':
     """
     create a child layer
     """
     child = None
     if xml == self.xml or xml.__class__.__name__ in ['_Comment']:
         pass
     elif xml.tag in ('form', 'color', 'select', 'preview', 'numeric'):
         # form elements do not belong to an image
         pass
     elif xml.tag == 'text':
         from . import text
         child = text.TextLayer(parent, xml)
     elif xml.tag == 'link':
         from . import link
         child = link.Link(parent, xml)
     elif xml.tag == 'image':
         from . import image
         child = image.ImageLayer(parent, xml)
     elif xml.tag == 'group':
         child = Layer(parent, xml)
     elif xml.tag == 'modifier':
         from . import modifier
         child = modifier.Modifier(parent, xml)
     elif xml.tag == 'solid':
         from . import solid
         child = solid.Solid(parent, xml)
     elif xml.tag == 'texture':
         from . import texture
         child = texture.Texture(parent, xml)
     elif xml.tag == 'pattern':
         from . import pattern
         child = pattern.Pattern(parent, xml)
     elif xml.tag == 'particles':
         from . import particles
         child = particles.Particles(parent, xml)
     elif xml.tag == 'numberspace':
         from . import numberspace
         child = numberspace.NumberSpace(parent, xml)
     elif xml.tag == 'ext':
         from . import extension
         child = extension.ExtensionLayer(parent, xml)
     else:
         raise SmartimageError(
             self, 'ERR: unexpected child element "%s"' % xml.tag)
     return child
Example #13
0
    def font(self) -> str:
        """
        change to any TrueType or OpenType font

        TODO: check inside the zipfile for embedded fonts!

        if the font is installed on your system, will attempt to install it from
            fonts.google.com

        returns PIL font object
        """
        if self.fontName is not None:
            fontName = self.fontName
        else:
            fontName = DEFAULT_FONT
        if self._font is None:
            # first look in the zipped file
            for name in self.root.componentNames:
                name = name.rsplit('.', 1)
                if name[0] == fontName:
                    if len(name) == 1 or name[1] in [
                            'ttf', 'otf', 'otc', 'ttc'
                    ]:
                        name = '.'.join(name)
                        component = self.root.getComponent(name)
                        self._font = ImageFont.truetype(
                            component, self.fontSize, self.typeFace)
                        component.close()
                        break
        if self._font is None:
            # try the os
            try:
                self._font = ImageFont.truetype(fontName, self.fontSize,
                                                self.typeFace)
            except IOError:
                self._font = None
        if self._font is None:
            fontdata = downloadFont(fontName)
            if fontdata is not None:
                self._font = ImageFont.truetype(fontdata, self.fontSize,
                                                self.typeFace)
        if self._font is None:
            raise SmartimageError(self,
                                  'Cannot find font anywhere: "%s"' % fontName)
        return self._font
Example #14
0
 def h(self) -> float:
     """
     the current height
     """
     h = self._getProperty('h', 'auto')
     if h in ('', '0', 'auto', 'None'):
         if self.children:
             h = [child.y + child.h for child in self.children]
             h = max(h) - self.y
         else:
             h = 0
     else:
         try:
             h = float(h)
         except ValueError:
             raise SmartimageError(self,
                                   'Unable to convert h="%s" to float' % h)
     return h
Example #15
0
 def w(self) -> float:
     """
     the current width
     """
     w = self._getProperty('w', 'auto')
     if w in ('', '0', 'auto', 'None'):
         if self.children:
             w = [child.x + child.w for child in self.children]
             w = max(w) - self.x
         else:
             w = 0
     else:
         try:
             w = float(w)
         except ValueError:
             raise SmartimageError(self,
                                   'Unable to convert w="%s" to float' % w)
     return w
Example #16
0
    def roi(self) -> Union[PilPlusImage, None]:
        """
        the region of interest of a graphical image

        if there is none, figure it out
        """
        ref = self._getProperty('roi')
        if ref is not None:
            try:
                img = self.root.imageByRef(ref)
            except FileNotFoundError as e:
                raise SmartimageError(self,
                                      'Missing roi resource "%s"' % e.filename)
        else:
            img = interest(self.image)
        if img.size != self.image.size:
            img.resize(self.image.size)
        return img
 def image(self):
     import time
     start = time.time()
     if self.type == 'voronoi':
         img = voronoi(self.size,
                       self.numPoints,
                       'simple',
                       self.invert,
                       seed=self.seed)
     elif self.type == 'random':
         img = smoothNoise(self.size,
                           1.0 - self.noiseSoften,
                           seed=self.seed)
     elif self.type == 'clouds':
         img = turbulence(self.size, seed=self.seed)
     elif self.type == 'waveform':
         img = waveformTexture(self.size,
                               self.waveform,
                               self.frequency,
                               self.noise,
                               self.noiseBasis,
                               self.noiseOctaves,
                               self.noiseSoften,
                               self.direction,
                               self.invert,
                               seed=self.seed)
     elif self.type == 'clock':
         img = clock2(self.size,
                      self.waveform,
                      self.frequency,
                      self.noise,
                      self.noiseBasis,
                      self.noiseOctaves,
                      self.noiseSoften,
                      self.direction,
                      self.invert,
                      seed=self.seed)
     else:
         raise SmartimageError(
             self, 'texture type "%s" not implemented' % self.type)
     img = pilImage(img)
     img.immutable = True  # mark this image so that compositor will not alter it
     end = time.time()
     return img
Example #18
0
 def x(self) -> float:
     """
     if value=="auto", then take on the child value
     """
     x = self._getProperty('x', 'auto')
     if x in ('', 'auto', 'None'):
         x = [child.x for child in self.children]
         if x:
             x = min(x)
         else:
             x = 0
     else:
         try:
             x = float(x)
         except ValueError:
             raise SmartimageError(self,
                                   'Unable to convert x="%s" to float' % x)
         if x < 0:
             x = self.w + x
     return x
Example #19
0
 def y(self) -> float:
     """
     if value=="auto", then take on the child value
     """
     y = self._getProperty('y', 'auto')
     if y in ('', 'auto', 'None'):
         y = [child.y for child in self.children]
         if y:
             y = min(y)
         else:
             y = 0
     else:
         try:
             y = float(y)
         except ValueError:
             raise SmartimageError(self,
                                   'Unable to convert y="%s" to float' % y)
         if y < 0:
             y = self.h + y
     return y
Example #20
0
 def image(self, image):
     """
     Can assign any PIL image or a filename that can be loaded as one
     """
     if image is None:
         raise SmartimageError(self, 'Attempt to assign None as an image')
     if isinstance(image, str):
         filename = image
         image = PilPlusImage(filename)
         imageFormat = image.format
     elif image.format is not None:
         filename = self.name + '.' + image.format.lower()
         imageFormat = image.format
     else:
         filename = self.name + '.png'
         imageFormat = 'PNG'
     data = io.BytesIO()
     image.save(data, imageFormat)
     data = data.getvalue()
     filename = self.root.addComponent(filename, data, overwrite=False)
     self.src = filename
Example #21
0
 def image(self) -> Union[PilPlusImage, None]:
     """
     the image for this layer
     """
     try:
         img = self.root.imageByRef(self.src)
     except FileNotFoundError as e:
         raise SmartimageError(
             self, 'Missing image src resource "%s"' % e.filename)
     if img is None:
         return img
     w = self._getProperty('w', 'auto')
     h = self._getProperty('h', 'auto')
     if (w not in ['0', 'auto']) and (h not in ['0', 'auto']):
         if w in ['0', 'auto']:
             w = img.width * (img.height / h)
         elif h in ['0', 'auto']:
             h = img.height * (img.width / w)
         img = img.resize((int(w), int(h)), img.ANTIALIAS)
     if img is not None:
         img.immutable = True  # mark this image so that compositor will not alter it
     return img
Example #22
0
    def mask(self) -> Union[PilPlusImage, None]:
        """
        grayscale image to be used as layer mask
        it can also be an image with alpha component which will be INVERTED to use as mask!

        NOTE: the mask can either be a link to another file,

        TODO: this could be made smarter with imageTools
        """
        from PIL import Image
        ref = self._getProperty('mask')
        try:
            img = self.root.imageByRef(ref)
        except FileNotFoundError as e:
            raise SmartimageError(self,
                                  'Missing mask resource "%s"' % e.filename)
        if img is not None:
            if img.mode in ['RGBA', 'LA']:
                alpha = img.split()[-1]
                img = Image.new("RGBA", img.size, (0, 0, 0, 255))
                img.paste(alpha, mask=alpha)
            img = img.convert("L")
        return img
Example #23
0
 def _transform(self, img):
     """
     For a list of more transformations available, see:
         http://pillow.readthedocs.io/en/latest/reference/ImageFilter.html
     """
     filterType = self.filterType
     if filterType == 'shadow':
         offsX = 10
         offsY = 10
         blurRadius = self.blurRadius
         shadow = img.copy()
         control = ImageEnhance.Brightness(shadow)
         shadow = control.enhance(0)
         final = Image.new(
             "RGBA", (img.width + abs(offsX), img.height + abs(offsX)))
         final.putalpha(int(self.modifierOpacity * 255))
         dest = (int(max(offsX - blurRadius,
                         0)), int(max(offsY - blurRadius, 0)))
         final.alpha_composite(shadow, dest=dest)
         if blurRadius > 0:
             final = final.filter(
                 ImageFilter.GaussianBlur(radius=blurRadius))
         final.alpha_composite(img, dest=(max(-offsX, 0), max(-offsY, 0)))
         img = final
     elif filterType == 'convolve':
         # TODO: make use of separable convoutions, fourier domain convolutions,
         #       and other speed-ups
         matrix = self.matrix
         ImageFilter.Kernel((len(matrix), len(matrix)),
                            matrix,
                            scale=self.divide,
                            offset=self.add)
     elif filterType == 'autocrop':
         # the idea is you cut off as many rows from each side that are all alpha=0
         raise NotImplementedError()
     elif filterType == 'brightness':
         control = ImageEnhance.Brightness(img)
         control.amount = self.amount
     elif filterType == 'contrast':
         control = ImageEnhance.Contrast(img)
         control.amount = self.amount
     elif filterType == 'saturation':
         control = ImageEnhance.Color(img)
         control.amount = self.amount
     elif filterType == 'blur':
         img = img.filter(ImageFilter.BLUR)  #,self.amount)
     elif filterType == 'gaussian_blur':
         blurRadius = self.blurRadius
         if blurRadius > 0:
             img = img.filter(ImageFilter.GaussianBlur(radius=blurRadius))
     elif filterType == 'box_blur':
         blurRadius = self.blurRadius
         if blurRadius > 0:
             img.filter(ImageFilter.BoxBlur(radius=blurRadius))
     elif filterType == 'unsharp_mask':
         ImageFilter.UnsharpMask(radius=self.blurRadius,
                                 percent=self.amount * 100,
                                 threshold=self.threshold)
     elif filterType == 'contour':
         img = img.filter(ImageFilter.CONTOUR, self.amount)
     elif filterType == 'detail':
         img = img.filter(ImageFilter.DETAIL, self.amount)
     elif filterType == 'edge_enhance':
         img = img.filter(ImageFilter.EDGE_ENHANCE, self.amount)
     elif filterType == 'edge_enhance_more':
         img = img.filter(ImageFilter.EDGE_ENHANCE_MORE, self.amount)
     elif filterType == 'emboss':
         img = img.filter(ImageFilter.EMBOSS, self.amount)
     elif filterType == 'edge_detect':
         img = img.filter(ImageFilter.FIND_EDGES, self.amount)
     elif filterType == 'smooth':
         img = img.filter(ImageFilter.SMOOTH, self.amount)
     elif filterType == 'smooth_more':
         img = img.filter(ImageFilter.SMOOTH_MORE, self.amount)
     elif filterType == 'sharpen':
         img = img.filter(ImageFilter.SHARPEN, self.amount)
     elif filterType == 'invert':
         img = ImageOps.invert(img)
     elif filterType == 'flip':
         img = ImageOps.flip(img)
     elif filterType == 'mirror':
         img = ImageOps.mirror(img)
     elif filterType == 'posterize':
         img = ImageOps.posterize(img)
     elif filterType == 'solarize':
         img = ImageOps.solarize(img)
     else:
         raise SmartimageError(self, 'Unknown modifier "%s"' % filterType)
     return img