mirror of
https://github.com/OpenVGLab/OmniLottie.git
synced 2026-09-17 07:36:27 +00:00
Initial Commit
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
import os
|
||||
import re
|
||||
import base64
|
||||
import mimetypes
|
||||
from io import BytesIO
|
||||
from .base import LottieObject, LottieProp, PseudoBool, Index
|
||||
from .layers import Layer
|
||||
from .shapes import ShapeElement
|
||||
from .composition import Composition
|
||||
|
||||
|
||||
## @ingroup Lottie
|
||||
class Asset(LottieObject):
|
||||
@classmethod
|
||||
def _load_get_class(cls, lottiedict):
|
||||
if "p" in lottiedict or "u" in lottiedict:
|
||||
return Image
|
||||
if "layers" in lottiedict:
|
||||
return Precomp
|
||||
|
||||
|
||||
## @ingroup Lottie
|
||||
class Image(Asset):
|
||||
"""!
|
||||
External image
|
||||
|
||||
@see http://docs.aenhancers.com/sources/filesource/
|
||||
"""
|
||||
_props = [
|
||||
LottieProp("height", "h", float, False),
|
||||
LottieProp("width", "w", float, False),
|
||||
LottieProp("id", "id", str, False),
|
||||
LottieProp("image", "p", str, False),
|
||||
LottieProp("image_path", "u", str, False),
|
||||
LottieProp("is_embedded", "e", PseudoBool, False),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def guess_mime(file):
|
||||
if isinstance(file, str):
|
||||
filename = file
|
||||
elif hasattr(file, "name"):
|
||||
filename = file.name
|
||||
else:
|
||||
return "application/octet-stream"
|
||||
return mimetypes.guess_type(filename)
|
||||
|
||||
def __init__(self, id=""):
|
||||
## Image Height
|
||||
self.height = 0
|
||||
## Image Width
|
||||
self.width = 0
|
||||
## Image ID
|
||||
self.id = id
|
||||
## Image name
|
||||
self.image = ""
|
||||
## Image path
|
||||
self.image_path = ""
|
||||
## Image data is stored as a data: url
|
||||
self.is_embedded = False
|
||||
|
||||
def load(self, file, format=None):
|
||||
"""!
|
||||
@param file Filename, file object, or PIL.Image.Image to load
|
||||
@param format Format to store the image data as
|
||||
"""
|
||||
from PIL import Image
|
||||
|
||||
if not isinstance(file, Image.Image):
|
||||
image = Image.open(file)
|
||||
else:
|
||||
image = file
|
||||
|
||||
self._id_from_file(file)
|
||||
|
||||
self.image_path = ""
|
||||
if format is None:
|
||||
format = (image.format or "png").lower()
|
||||
self.width, self.height = image.size
|
||||
output = BytesIO()
|
||||
image.save(output, format=format)
|
||||
self.image = "data:image/%s;base64,%s" % (
|
||||
format,
|
||||
base64.b64encode(output.getvalue()).decode("ascii")
|
||||
)
|
||||
self.is_embedded = True
|
||||
return self
|
||||
|
||||
def _id_from_file(self, file):
|
||||
if not self.id:
|
||||
if isinstance(file, str):
|
||||
self.id = os.path.basename(file)
|
||||
elif hasattr(file, "name"):
|
||||
self.id = os.path.basename(file.name)
|
||||
elif hasattr(file, "filename"):
|
||||
self.id = os.path.basename(file.filename)
|
||||
else:
|
||||
self.id = "image_%s" % id(self)
|
||||
|
||||
@classmethod
|
||||
def embedded(cls, image, format=None):
|
||||
"""!
|
||||
Create an object from an image file
|
||||
"""
|
||||
lottie_image = cls()
|
||||
return lottie_image.load(image, format)
|
||||
|
||||
@classmethod
|
||||
def linked(cls, filename):
|
||||
from PIL import Image
|
||||
image = Image.open(filename)
|
||||
lottie_image = cls()
|
||||
lottie_image._id_from_file(filename)
|
||||
lottie_image.image_path, lottie_image.image = os.path.split(filename)
|
||||
lottie_image.image_path += "/"
|
||||
lottie_image.width = image.width
|
||||
lottie_image.height = image.height
|
||||
return lottie_image
|
||||
|
||||
def image_data(self):
|
||||
"""
|
||||
Returns a tuple (format, data) with the contents of the image
|
||||
|
||||
`format` is a string like "png", and `data` is just raw binary data.
|
||||
|
||||
If it's impossible to fetch this info, returns (None, None)
|
||||
"""
|
||||
if self.is_embedded:
|
||||
m = re.match("data:[^/]+/([^;,]+);base64,(.*)", self.image)
|
||||
if m:
|
||||
return m.group(1), base64.b64decode(m.group(2))
|
||||
return None, None
|
||||
path = self.image_path + self.image
|
||||
if os.path.isfile(path):
|
||||
with open(path, "rb") as imgfile:
|
||||
return os.path.splitext(path)[1][1:], imgfile.read()
|
||||
return None, None
|
||||
|
||||
|
||||
## @ingroup Lottie
|
||||
class CharacterData(LottieObject):
|
||||
"""!
|
||||
Character shapes
|
||||
"""
|
||||
_props = [
|
||||
LottieProp("shapes", "shapes", ShapeElement, True),
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self.shapes = []
|
||||
|
||||
|
||||
## @ingroup Lottie
|
||||
class Chars(LottieObject):
|
||||
"""!
|
||||
Defines character shapes to avoid loading system fonts
|
||||
"""
|
||||
_props = [
|
||||
LottieProp("character", "ch", str, False),
|
||||
LottieProp("font_family", "fFamily", str, False),
|
||||
LottieProp("font_size", "size", float, False),
|
||||
LottieProp("font_style", "style", str, False),
|
||||
LottieProp("width", "w", float, False),
|
||||
LottieProp("data", "data", CharacterData, False),
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
## Character Value
|
||||
self.character = ""
|
||||
## Character Font Family
|
||||
self.font_family = ""
|
||||
## Character Font Size
|
||||
self.font_size = 0
|
||||
## Character Font Style
|
||||
self.font_style = "" # Regular
|
||||
## Character Width
|
||||
self.width = 0
|
||||
## Character Data
|
||||
self.data = CharacterData()
|
||||
|
||||
@property
|
||||
def shapes(self):
|
||||
return self.data.shapes
|
||||
|
||||
|
||||
## @ingroup Lottie
|
||||
class Precomp(Asset, Composition):
|
||||
_props = [
|
||||
LottieProp("id", "id", str, False),
|
||||
]
|
||||
|
||||
def __init__(self, id="", animation=None):
|
||||
super().__init__()
|
||||
## Precomp ID
|
||||
self.id = id
|
||||
self.animation = animation
|
||||
if animation:
|
||||
self.animation.assets.append(self)
|
||||
|
||||
def _on_prepare_layer(self, layer):
|
||||
if self.animation:
|
||||
self.animation.prepare_layer(layer)
|
||||
|
||||
def set_timing(self, outpoint, inpoint=0, override=True):
|
||||
for layer in self.layers:
|
||||
if override or layer.in_point is None:
|
||||
layer.in_point = inpoint
|
||||
if override or layer.out_point is None:
|
||||
layer.out_point = outpoint
|
||||
Reference in New Issue
Block a user