mirror of
https://github.com/OpenVGLab/OmniLottie.git
synced 2026-09-17 07:36:27 +00:00
199 lines
5.3 KiB
Python
199 lines
5.3 KiB
Python
import os
|
|||
|
|
import enum
|
||
|
|
import json
|
||
|
|
import inspect
|
||
|
|
|
||
|
|
from ..parsers.tgs import parse_tgs
|
||
|
|
from ..objects.base import ObjectVisitor
|
||
|
|
from ..objects.animation import Animation
|
||
|
|
from ..objects import layers
|
||
|
|
from ..objects import shapes
|
||
|
|
from ..objects import helpers
|
||
|
|
|
||
|
|
|
||
|
|
class Severity(enum.Enum):
|
||
|
|
Note = enum.auto()
|
||
|
|
Warning = enum.auto()
|
||
|
|
Error = enum.auto()
|
||
|
|
|
||
|
|
|
||
|
|
class TgsError:
|
||
|
|
def __init__(self, message, target, severity=Severity.Warning):
|
||
|
|
self.message = message
|
||
|
|
self.target = target
|
||
|
|
self.severity = severity
|
||
|
|
|
||
|
|
def target_id(self):
|
||
|
|
if isinstance(self.target, str):
|
||
|
|
return self.target
|
||
|
|
if getattr(self.target, "name", ""):
|
||
|
|
return self.target.name
|
||
|
|
return self.target.__class__.__name__
|
||
|
|
|
||
|
|
def __str__(self):
|
||
|
|
return "%s: on %s: %s" % (
|
||
|
|
self.severity.name,
|
||
|
|
self.target_id(),
|
||
|
|
self.message
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class TgsValidator(ObjectVisitor):
|
||
|
|
def __init__(self, severity=Severity.Note):
|
||
|
|
self.errors = []
|
||
|
|
self.severity = severity
|
||
|
|
|
||
|
|
def _check(self, expr, message, target, severity=Severity.Warning):
|
||
|
|
if severity.value >= self.severity.value and not expr:
|
||
|
|
self.errors.append(TgsError(message, target, severity))
|
||
|
|
|
||
|
|
def check_file_size(self, filename):
|
||
|
|
return self.check_size(os.path.getsize(filename))
|
||
|
|
|
||
|
|
def check_size(self, bytes, filename="file"):
|
||
|
|
size_k = bytes / 1024
|
||
|
|
self._check(
|
||
|
|
size_k <= 64,
|
||
|
|
"Invalid size (%.1fk), should be less than 64k" % size_k,
|
||
|
|
filename,
|
||
|
|
Severity.Error
|
||
|
|
)
|
||
|
|
|
||
|
|
def check_file(self, filename):
|
||
|
|
self.check_file_size(filename)
|
||
|
|
try:
|
||
|
|
self(parse_tgs(filename))
|
||
|
|
except json.decoder.JSONDecodeError as e:
|
||
|
|
self._check(
|
||
|
|
False,
|
||
|
|
"Invalid JSON: %s" % e,
|
||
|
|
filename,
|
||
|
|
Severity.Error
|
||
|
|
)
|
||
|
|
|
||
|
|
def visit(self, object):
|
||
|
|
for cls in inspect.getmro(object.__class__):
|
||
|
|
callback = "_visit_%s" % cls.__name__.lower()
|
||
|
|
if hasattr(self, callback):
|
||
|
|
getattr(self, callback)(object)
|
||
|
|
|
||
|
|
def _visit_animation(self, o: Animation):
|
||
|
|
self._check(
|
||
|
|
o.frame_rate in {30, 60},
|
||
|
|
"Invalid framerate %s, should be 30 or 60" % o.frame_rate,
|
||
|
|
o,
|
||
|
|
Severity.Error
|
||
|
|
)
|
||
|
|
self._check(
|
||
|
|
o.width == 512,
|
||
|
|
"Invalid width %s, should be 512" % o.width,
|
||
|
|
o,
|
||
|
|
Severity.Error
|
||
|
|
)
|
||
|
|
self._check(
|
||
|
|
o.height == 512,
|
||
|
|
"Invalid height %s, should be 512" % o.height,
|
||
|
|
o,
|
||
|
|
Severity.Error
|
||
|
|
)
|
||
|
|
self._check(
|
||
|
|
(o.out_point-o.in_point) <= 180,
|
||
|
|
"Too many frames (%s), should be less than 180" % (o.out_point-o.in_point),
|
||
|
|
o,
|
||
|
|
Severity.Error
|
||
|
|
)
|
||
|
|
|
||
|
|
def _visit_layer(self, o: layers.Layer):
|
||
|
|
self._check(
|
||
|
|
not o.has_masks and not o.masks,
|
||
|
|
"Masks are not officially supported",
|
||
|
|
o,
|
||
|
|
Severity.Note
|
||
|
|
)
|
||
|
|
self._check(
|
||
|
|
not o.effects,
|
||
|
|
"Effects are not supported",
|
||
|
|
o,
|
||
|
|
Severity.Warning
|
||
|
|
)
|
||
|
|
self._check(
|
||
|
|
not o.threedimensional,
|
||
|
|
"3D layers are not supported",
|
||
|
|
o,
|
||
|
|
Severity.Warning
|
||
|
|
)
|
||
|
|
self._check(
|
||
|
|
not isinstance(o, layers.TextLayer),
|
||
|
|
"Text layers are not supported",
|
||
|
|
o,
|
||
|
|
Severity.Warning
|
||
|
|
)
|
||
|
|
self._check(
|
||
|
|
not isinstance(o, layers.ImageLayer),
|
||
|
|
"Image layers are not supported",
|
||
|
|
o,
|
||
|
|
Severity.Warning
|
||
|
|
)
|
||
|
|
self._check(
|
||
|
|
not o.auto_orient,
|
||
|
|
"Auto-orient layers are not supported",
|
||
|
|
o,
|
||
|
|
Severity.Warning
|
||
|
|
)
|
||
|
|
self._check(
|
||
|
|
o.matte_mode in {None, layers.MatteMode.Normal},
|
||
|
|
"Mattes are not supported",
|
||
|
|
o,
|
||
|
|
Severity.Warning
|
||
|
|
)
|
||
|
|
|
||
|
|
def _visit_precomplayer(self, o: layers.PreCompLayer):
|
||
|
|
self._check(
|
||
|
|
o.time_remapping is None,
|
||
|
|
"Time remapping is not supported",
|
||
|
|
o,
|
||
|
|
Severity.Warning
|
||
|
|
)
|
||
|
|
|
||
|
|
def _visit_merge(self, o: shapes.Merge):
|
||
|
|
self._check(
|
||
|
|
False,
|
||
|
|
"Merge paths are not supported",
|
||
|
|
o,
|
||
|
|
Severity.Warning
|
||
|
|
)
|
||
|
|
|
||
|
|
def _visit_transform(self, o: helpers.Transform):
|
||
|
|
self._check(
|
||
|
|
o.skew is None or (
|
||
|
|
not o.skew.animated and o.skew.value == 0
|
||
|
|
),
|
||
|
|
"Skew transforms are not supported",
|
||
|
|
o,
|
||
|
|
Severity.Warning
|
||
|
|
)
|
||
|
|
|
||
|
|
def _visit_gradientstroke(self, o: shapes.GradientStroke):
|
||
|
|
self._check(
|
||
|
|
False,
|
||
|
|
"Gradient strokes are not officially supported",
|
||
|
|
o,
|
||
|
|
Severity.Note
|
||
|
|
)
|
||
|
|
|
||
|
|
def _visit_star(self, o: shapes.Star):
|
||
|
|
self._check(
|
||
|
|
False,
|
||
|
|
"Star Shapes are not officially supported",
|
||
|
|
o,
|
||
|
|
Severity.Note
|
||
|
|
)
|
||
|
|
|
||
|
|
def _visit_repeater(self, o: shapes.Repeater):
|
||
|
|
self._check(
|
||
|
|
False,
|
||
|
|
"Repeaters are not officially supported",
|
||
|
|
o,
|
||
|
|
Severity.Note
|
||
|
|
)
|