glTF exporter: various fixes & enhancement
* Fix some Yup conversions * reading material from glTF node group material if exists * Fix normal export * Round transforms near 0 and 1 * Fix exporting from Edit mode * Various image format management
This commit is contained in:
parent
9aa6c8058b
commit
bf867f5022
|
@ -36,7 +36,7 @@ from bpy.props import (CollectionProperty,
|
|||
|
||||
bl_info = {
|
||||
'name': 'glTF 2.0 format',
|
||||
'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann & Moritz Becher',
|
||||
'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann Moritz Becher, Benjamin Schmithüsen',
|
||||
"version": (0, 0, 1),
|
||||
'blender': (2, 80, 0),
|
||||
'location': 'File > Import-Export',
|
||||
|
|
|
@ -29,7 +29,11 @@ def list_to_mathutils(values: typing.List[float], data_path: str) -> typing.Unio
|
|||
"""Transform a list to blender py object."""
|
||||
target = get_target_property_name(data_path)
|
||||
|
||||
if target == 'location':
|
||||
if target == 'delta_location':
|
||||
return Vector(values) # TODO Should be Vector(values) - Vector(something)?
|
||||
elif target == 'delta_rotation_euler':
|
||||
return Euler(values).to_quaternion() # TODO Should be multiply(Euler(values).to_quaternion(), something)?
|
||||
elif target == 'location':
|
||||
return Vector(values)
|
||||
elif target == 'rotation_axis_angle':
|
||||
angle = values[0]
|
||||
|
@ -75,6 +79,8 @@ def swizzle_yup(v: typing.Union[Vector, Quaternion], data_path: str) -> typing.U
|
|||
"""Manage Yup."""
|
||||
target = get_target_property_name(data_path)
|
||||
swizzle_func = {
|
||||
"delta_location": swizzle_yup_location,
|
||||
"delta_rotation_euler": swizzle_yup_rotation,
|
||||
"location": swizzle_yup_location,
|
||||
"rotation_axis_angle": swizzle_yup_rotation,
|
||||
"rotation_euler": swizzle_yup_rotation,
|
||||
|
@ -114,6 +120,8 @@ def transform(v: typing.Union[Vector, Quaternion], data_path: str, transform: Ma
|
|||
"""Manage transformations."""
|
||||
target = get_target_property_name(data_path)
|
||||
transform_func = {
|
||||
"delta_location": transform_location,
|
||||
"delta_rotation_euler": transform_rotation,
|
||||
"location": transform_location,
|
||||
"rotation_axis_angle": transform_rotation,
|
||||
"rotation_euler": transform_rotation,
|
||||
|
@ -157,3 +165,8 @@ def transform_value(value: Vector, _: Matrix = Matrix.Identity(4)) -> Vector:
|
|||
"""Transform value."""
|
||||
return value
|
||||
|
||||
|
||||
def round_if_near(value: float, target: float) -> float:
|
||||
"""If value is very close to target, round to target."""
|
||||
return value if abs(value - target) > 2.0e-6 else target
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import bpy
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
|
@ -25,6 +26,9 @@ from io_scene_gltf2.io.exp import gltf2_io_export
|
|||
|
||||
def save(context, export_settings):
|
||||
"""Start the glTF 2.0 export and saves to content either to a .gltf or .glb file."""
|
||||
if bpy.context.active_object is not None:
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
__notify_start(context)
|
||||
json, buffer = __export(export_settings)
|
||||
__write_file(json, buffer, export_settings)
|
||||
|
|
|
@ -648,7 +648,7 @@ def extract_primitives(glTF, blender_mesh, blender_vertex_groups, modifiers, exp
|
|||
|
||||
bone_count = 0
|
||||
|
||||
if vertex.groups is not None and len(vertex.groups) > 0 and export_settings[gltf2_blender_export_keys.SKINS]:
|
||||
if blender_vertex_groups is not None and vertex.groups is not None and len(vertex.groups) > 0 and export_settings[gltf2_blender_export_keys.SKINS]:
|
||||
joint = []
|
||||
weight = []
|
||||
for group_element in vertex.groups:
|
||||
|
@ -668,13 +668,15 @@ def extract_primitives(glTF, blender_mesh, blender_vertex_groups, modifiers, exp
|
|||
#
|
||||
|
||||
joint_index = 0
|
||||
modifiers_dict = {m.type: m for m in modifiers}
|
||||
if "ARMATURE" in modifiers_dict:
|
||||
armature = modifiers_dict["ARMATURE"].object
|
||||
skin = gltf2_blender_gather_skins.gather_skin(armature, export_settings)
|
||||
for index, j in enumerate(skin.joints):
|
||||
if j.name == vertex_group_name:
|
||||
joint_index = index
|
||||
|
||||
if modifiers is not None:
|
||||
modifiers_dict = {m.type: m for m in modifiers}
|
||||
if "ARMATURE" in modifiers_dict:
|
||||
armature = modifiers_dict["ARMATURE"].object
|
||||
skin = gltf2_blender_gather_skins.gather_skin(armature, export_settings)
|
||||
for index, j in enumerate(skin.joints):
|
||||
if j.name == vertex_group_name:
|
||||
joint_index = index
|
||||
|
||||
joint_weight = group_element.weight
|
||||
|
||||
|
|
|
@ -67,6 +67,8 @@ def __gather_path(channels: typing.Tuple[bpy.types.FCurve],
|
|||
) -> str:
|
||||
target = channels[0].data_path.split('.')[-1]
|
||||
path = {
|
||||
"delta_location": "translation",
|
||||
"delta_rotation_euler": "rotation",
|
||||
"location": "translation",
|
||||
"rotation_axis_angle": "rotation",
|
||||
"rotation_euler": "rotation",
|
||||
|
|
|
@ -35,6 +35,8 @@ class Keyframe:
|
|||
|
||||
def __get_target_len(self):
|
||||
length = {
|
||||
"delta_location": 3,
|
||||
"delta_rotation_euler": 3,
|
||||
"location": 3,
|
||||
"rotation_axis_angle": 4,
|
||||
"rotation_euler": 3,
|
||||
|
|
|
@ -107,37 +107,40 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve],
|
|||
|
||||
target_datapath = channels[0].data_path
|
||||
|
||||
transform = Matrix.Identity(4)
|
||||
transform = blender_object.matrix_parent_inverse
|
||||
|
||||
isYup = export_settings[gltf2_blender_export_keys.YUP]
|
||||
|
||||
if blender_object.type == "ARMATURE":
|
||||
bone = blender_object.path_resolve(get_target_object_path(target_datapath))
|
||||
if isinstance(bone, bpy.types.PoseBone):
|
||||
transform = bone.bone.matrix_local
|
||||
if bone.parent is not None:
|
||||
parent_transform = bone.parent.bone.matrix_local
|
||||
transform = gltf2_blender_math.multiply(parent_transform.inverted(), transform)
|
||||
# if not export_settings[gltf2_blender_export_keys.YUP]:
|
||||
# transform = gltf2_blender_math.multiply(gltf2_blender_math.to_zup(), transform)
|
||||
transform = gltf2_blender_math.multiply(transform, parent_transform.inverted())
|
||||
# if not isYup:
|
||||
# transform = gltf2_blender_math.multiply(transform, gltf2_blender_math.to_zup())
|
||||
else:
|
||||
# only apply the y-up conversion to root bones, as child bones already are in the y-up space
|
||||
if export_settings[gltf2_blender_export_keys.YUP]:
|
||||
transform = gltf2_blender_math.multiply(gltf2_blender_math.to_yup(), transform)
|
||||
if isYup:
|
||||
transform = gltf2_blender_math.multiply(transform, gltf2_blender_math.to_yup())
|
||||
local_transform = bone.bone.matrix_local
|
||||
transform = gltf2_blender_math.multiply(transform, local_transform)
|
||||
|
||||
values = []
|
||||
for keyframe in keyframes:
|
||||
# Transform the data and extract
|
||||
value = gltf2_blender_math.transform(keyframe.value, target_datapath, transform)
|
||||
if export_settings[gltf2_blender_export_keys.YUP] and not blender_object.type == "ARMATURE":
|
||||
if isYup and not blender_object.type == "ARMATURE":
|
||||
value = gltf2_blender_math.swizzle_yup(value, target_datapath)
|
||||
keyframe_value = gltf2_blender_math.mathutils_to_gltf(value)
|
||||
if keyframe.in_tangent is not None:
|
||||
in_tangent = gltf2_blender_math.transform(keyframe.in_tangent, target_datapath, transform)
|
||||
if export_settings[gltf2_blender_export_keys.YUP] and not blender_object.type == "ARMATURE":
|
||||
if isYup and not blender_object.type == "ARMATURE":
|
||||
in_tangent = gltf2_blender_math.swizzle_yup(in_tangent, target_datapath)
|
||||
keyframe_value = gltf2_blender_math.mathutils_to_gltf(in_tangent) + keyframe_value
|
||||
if keyframe.out_tangent is not None:
|
||||
out_tangent = gltf2_blender_math.transform(keyframe.out_tangent, target_datapath, transform)
|
||||
if export_settings[gltf2_blender_export_keys.YUP] and not blender_object.type == "ARMATURE":
|
||||
if isYup and not blender_object.type == "ARMATURE":
|
||||
out_tangent = gltf2_blender_math.swizzle_yup(out_tangent, target_datapath)
|
||||
keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(out_tangent)
|
||||
values += keyframe_value
|
||||
|
|
|
@ -156,7 +156,7 @@ def __get_blender_actions(blender_object: bpy.types.Object
|
|||
for strip in track.strips:
|
||||
blender_actions.append(strip.action)
|
||||
|
||||
if blender_object.type == "MESH"\
|
||||
if blender_object.type == "MESH" \
|
||||
and blender_object.data is not None \
|
||||
and blender_object.data.shape_keys is not None \
|
||||
and blender_object.data.shape_keys.animation_data is not None:
|
||||
|
|
|
@ -41,7 +41,7 @@ def gather_material(blender_material, export_settings):
|
|||
alpha_cutoff=__gather_alpha_cutoff(blender_material, export_settings),
|
||||
alpha_mode=__gather_alpha_mode(blender_material, export_settings),
|
||||
double_sided=__gather_double_sided(blender_material, export_settings),
|
||||
emissive_factor=__gather_emmissive_factor(blender_material, export_settings),
|
||||
emissive_factor=__gather_emissive_factor(blender_material, export_settings),
|
||||
emissive_texture=__gather_emissive_texture(blender_material, export_settings),
|
||||
extensions=__gather_extensions(blender_material, export_settings),
|
||||
extras=__gather_extras(blender_material, export_settings),
|
||||
|
@ -94,8 +94,10 @@ def __gather_double_sided(blender_material, export_settings):
|
|||
return None
|
||||
|
||||
|
||||
def __gather_emmissive_factor(blender_material, export_settings):
|
||||
def __gather_emissive_factor(blender_material, export_settings):
|
||||
emissive_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Emissive")
|
||||
if emissive_socket is None:
|
||||
emissive_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "EmissiveFactor")
|
||||
if isinstance(emissive_socket, bpy.types.NodeSocket) and not emissive_socket.is_linked:
|
||||
return list(emissive_socket.default_value)[0:3]
|
||||
return None
|
||||
|
@ -103,6 +105,8 @@ def __gather_emmissive_factor(blender_material, export_settings):
|
|||
|
||||
def __gather_emissive_texture(blender_material, export_settings):
|
||||
emissive = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Emissive")
|
||||
if emissive is None:
|
||||
emissive = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "Emissive")
|
||||
return gltf2_blender_gather_texture_info.gather_texture_info((emissive,), export_settings)
|
||||
|
||||
|
||||
|
@ -127,15 +131,19 @@ def __gather_name(blender_material, export_settings):
|
|||
|
||||
def __gather_normal_texture(blender_material, export_settings):
|
||||
normal = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Normal")
|
||||
if normal is None:
|
||||
normal = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "Normal")
|
||||
return gltf2_blender_gather_material_normal_texture_info_class.gather_material_normal_texture_info_class(
|
||||
(normal,),
|
||||
export_settings)
|
||||
|
||||
|
||||
def __gather_occlusion_texture(blender_material, export_settings):
|
||||
emissive = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Occlusion")
|
||||
occlusion = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Occlusion")
|
||||
if occlusion is None:
|
||||
occlusion = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "Occlusion")
|
||||
return gltf2_blender_gather_material_occlusion_texture_info_class.gather_material_occlusion_texture_info_class(
|
||||
(emissive,),
|
||||
(occlusion,),
|
||||
export_settings)
|
||||
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ def __gather_base_color_factor(blender_material, export_settings):
|
|||
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Base Color")
|
||||
if base_color_socket is None:
|
||||
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "BaseColor")
|
||||
if base_color_socket is None:
|
||||
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "BaseColorFactor")
|
||||
if isinstance(base_color_socket, bpy.types.NodeSocket) and not base_color_socket.is_linked:
|
||||
return list(base_color_socket.default_value)
|
||||
return None
|
||||
|
@ -54,6 +56,8 @@ def __gather_base_color_texture(blender_material, export_settings):
|
|||
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Base Color")
|
||||
if base_color_socket is None:
|
||||
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "BaseColor")
|
||||
if base_color_socket is None:
|
||||
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "BaseColor")
|
||||
return gltf2_blender_gather_texture_info.gather_texture_info((base_color_socket,), export_settings)
|
||||
|
||||
|
||||
|
@ -67,6 +71,8 @@ def __gather_extras(blender_material, export_settings):
|
|||
|
||||
def __gather_metallic_factor(blender_material, export_settings):
|
||||
metallic_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Metallic")
|
||||
if metallic_socket is None:
|
||||
metallic_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "MetallicFactor")
|
||||
if isinstance(metallic_socket, bpy.types.NodeSocket) and not metallic_socket.is_linked:
|
||||
return metallic_socket.default_value
|
||||
return None
|
||||
|
@ -78,6 +84,8 @@ def __gather_metallic_roughness_texture(blender_material, export_settings):
|
|||
|
||||
if metallic_socket is None and roughness_socket is None:
|
||||
metallic_roughness = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "MetallicRoughness")
|
||||
if metallic_roughness is None:
|
||||
metallic_roughness = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "MetallicRoughness")
|
||||
texture_input = (metallic_roughness,)
|
||||
else:
|
||||
texture_input = (metallic_socket, roughness_socket)
|
||||
|
@ -87,6 +95,8 @@ def __gather_metallic_roughness_texture(blender_material, export_settings):
|
|||
|
||||
def __gather_roughness_factor(blender_material, export_settings):
|
||||
roughness_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Roughness")
|
||||
if roughness_socket is None:
|
||||
roughness_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "RoughnessFactor")
|
||||
if isinstance(roughness_socket, bpy.types.NodeSocket) and not roughness_socket.is_linked:
|
||||
return roughness_socket.default_value
|
||||
return None
|
||||
|
|
|
@ -17,6 +17,7 @@ import bpy
|
|||
from mathutils import Quaternion
|
||||
|
||||
from . import gltf2_blender_export_keys
|
||||
from io_scene_gltf2.blender.com import gltf2_blender_math
|
||||
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_gather_skins
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_gather_cameras
|
||||
|
@ -50,16 +51,17 @@ def gather_node(blender_object, export_settings):
|
|||
)
|
||||
node.translation, node.rotation, node.scale = __gather_trans_rot_scale(blender_object, export_settings)
|
||||
|
||||
if blender_object.type == 'LIGHT' and export_settings[gltf2_blender_export_keys.LIGHTS]:
|
||||
correction_node = __get_correction_node(blender_object, export_settings)
|
||||
correction_node.extensions = {"KHR_lights_punctual": node.extensions["KHR_lights_punctual"]}
|
||||
del node.extensions["KHR_lights_punctual"]
|
||||
node.children.append(correction_node)
|
||||
if blender_object.type == 'CAMERA' and export_settings[gltf2_blender_export_keys.CAMERAS]:
|
||||
correction_node = __get_correction_node(blender_object, export_settings)
|
||||
correction_node.camera = node.camera
|
||||
node.children.append(correction_node)
|
||||
node.camera = None
|
||||
if export_settings[gltf2_blender_export_keys.YUP]:
|
||||
if blender_object.type == 'LIGHT' and export_settings[gltf2_blender_export_keys.LIGHTS]:
|
||||
correction_node = __get_correction_node(blender_object, export_settings)
|
||||
correction_node.extensions = {"KHR_lights_punctual": node.extensions["KHR_lights_punctual"]}
|
||||
del node.extensions["KHR_lights_punctual"]
|
||||
node.children.append(correction_node)
|
||||
if blender_object.type == 'CAMERA' and export_settings[gltf2_blender_export_keys.CAMERAS]:
|
||||
correction_node = __get_correction_node(blender_object, export_settings)
|
||||
correction_node.camera = node.camera
|
||||
node.children.append(correction_node)
|
||||
node.camera = None
|
||||
|
||||
return node
|
||||
|
||||
|
@ -153,8 +155,18 @@ def __gather_mesh(blender_object, export_settings):
|
|||
modifiers = None
|
||||
|
||||
if export_settings[gltf2_blender_export_keys.APPLY]:
|
||||
auto_smooth = blender_object.data.use_auto_smooth
|
||||
if auto_smooth:
|
||||
blender_object = blender_object.copy()
|
||||
edge_split = blender_object.modifiers.new('Temporary_Auto_Smooth', 'EDGE_SPLIT')
|
||||
edge_split.split_angle = blender_object.data.auto_smooth_angle
|
||||
edge_split.use_edge_angle = not blender_object.data.has_custom_normals
|
||||
|
||||
blender_mesh = blender_object.to_mesh(bpy.context.depsgraph, True)
|
||||
skip_filter = True
|
||||
|
||||
if auto_smooth:
|
||||
bpy.data.objects.remove(blender_object)
|
||||
else:
|
||||
blender_mesh = blender_object.data
|
||||
skip_filter = False
|
||||
|
@ -179,6 +191,12 @@ def __gather_trans_rot_scale(blender_object, export_settings):
|
|||
trans = -gltf2_blender_extract.convert_swizzle_location(
|
||||
blender_object.instance_collection.instance_offset, export_settings)
|
||||
translation, rotation, scale = (None, None, None)
|
||||
trans[0], trans[1], trans[2] = gltf2_blender_math.round_if_near(trans[0], 0.0), gltf2_blender_math.round_if_near(trans[1], 0.0), \
|
||||
gltf2_blender_math.round_if_near(trans[2], 0.0)
|
||||
rot[0], rot[1], rot[2], rot[3] = gltf2_blender_math.round_if_near(rot[0], 0.0), gltf2_blender_math.round_if_near(rot[1], 0.0), \
|
||||
gltf2_blender_math.round_if_near(rot[2], 0.0), gltf2_blender_math.round_if_near(rot[3], 1.0)
|
||||
sca[0], sca[1], sca[2] = gltf2_blender_math.round_if_near(sca[0], 1.0), gltf2_blender_math.round_if_near(sca[1], 1.0), \
|
||||
gltf2_blender_math.round_if_near(sca[2], 1.0)
|
||||
if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0:
|
||||
translation = [trans[0], trans[1], trans[2]]
|
||||
if rot[0] != 0.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 1.0:
|
||||
|
|
|
@ -43,24 +43,42 @@ def get_socket_or_texture_slot(blender_material: bpy.types.Material, name: str):
|
|||
:return: either a blender NodeSocket, if the material is a node tree or a blender Texture otherwise
|
||||
"""
|
||||
if blender_material.node_tree and blender_material.use_nodes:
|
||||
i = [input for input in blender_material.node_tree.inputs]
|
||||
o = [output for output in blender_material.node_tree.outputs]
|
||||
nodes = [node for node in blender_material.node_tree.nodes]
|
||||
#i = [input for input in blender_material.node_tree.inputs]
|
||||
#o = [output for output in blender_material.node_tree.outputs]
|
||||
if name == "Emissive":
|
||||
nodes = filter(lambda n: isinstance(n, bpy.types.ShaderNodeEmission), nodes)
|
||||
type = bpy.types.ShaderNodeEmission
|
||||
name = "Color"
|
||||
else:
|
||||
nodes = filter(lambda n: isinstance(n, bpy.types.ShaderNodeBsdfPrincipled), nodes)
|
||||
type = bpy.types.ShaderNodeBsdfPrincipled
|
||||
nodes = [n for n in blender_material.node_tree.nodes if isinstance(n, type)]
|
||||
inputs = sum([[input for input in node.inputs if input.name == name] for node in nodes], [])
|
||||
if not inputs:
|
||||
return None
|
||||
return inputs[0]
|
||||
if inputs:
|
||||
return inputs[0]
|
||||
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_socket_or_texture_slot_old(blender_material: bpy.types.Material, name: str):
|
||||
"""
|
||||
For a given material input name, retrieve the corresponding node tree socket in the special glTF Metallic Roughness nodes (which might be deprecated?).
|
||||
|
||||
:param blender_material: a blender material for which to get the socket/slot
|
||||
:param name: the name of the socket/slot
|
||||
:return: either a blender NodeSocket, if the material is a node tree or a blender Texture otherwise
|
||||
"""
|
||||
if blender_material.node_tree and blender_material.use_nodes:
|
||||
nodes = [n for n in blender_material.node_tree.nodes if \
|
||||
isinstance(n, bpy.types.ShaderNodeGroup) and \
|
||||
n.node_tree.name.startswith('glTF Metallic Roughness')]
|
||||
inputs = sum([[input for input in node.inputs if input.name == name] for node in nodes], [])
|
||||
if inputs:
|
||||
return inputs[0]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def find_shader_image_from_shader_socket(shader_socket, max_hops=10):
|
||||
"""Find any ShaderNodeTexImage in the path from the socket."""
|
||||
if shader_socket is None:
|
||||
|
|
|
@ -144,8 +144,7 @@ class GlTF2Exporter:
|
|||
:return:
|
||||
"""
|
||||
for image in self.__images:
|
||||
dst_path = output_path + image.name + ".png"
|
||||
|
||||
dst_path = output_path + image.name + image.get_extension()
|
||||
src_path = bpy.path.abspath(image.filepath)
|
||||
if os.path.isfile(src_path):
|
||||
# Source file exists.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import typing
|
||||
import struct
|
||||
import re
|
||||
import zlib
|
||||
import numpy as np
|
||||
|
||||
|
@ -76,6 +77,14 @@ class ImageData:
|
|||
return None
|
||||
return self.channels[3]
|
||||
|
||||
def get_extension(self):
|
||||
allowed_extensions = ['.png', '.jpg', '.jpeg']
|
||||
fallback_extension = allowed_extensions[0]
|
||||
|
||||
matches = re.findall(r'\.\w+$', self.filepath)
|
||||
extension = matches[0] if len(matches) > 0 else fallback_extension
|
||||
return extension if extension.lower() in allowed_extensions else fallback_extension
|
||||
|
||||
def to_image_data(self, mime_type: str) -> bytes:
|
||||
if mime_type == 'image/png':
|
||||
return self.to_png_data()
|
||||
|
|
Loading…
Reference in New Issue