Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gltf2_io_image.py « com « io « io_scene_gltf2 - git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: af86daeb365039dc7d6e91c32b7b32dd9abd3dd9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# Copyright 2018 The glTF-Blender-IO authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#
# Imports
#

import struct
import zlib


class Image:
    """
    Image object class to represent a 4-channel RGBA image.

    Pixel values are expected to be floating point in the range of [0.0 to 1.0]
    """

    def __init__(self, width, height, pixels):
        self.width = width
        self.height = height
        self.channels = 4
        self.pixels = pixels
        self.name = ""
        self.file_format = "PNG"

    def to_png_data(self):
        buf = bytearray([int(channel * 255.0) for channel in self.pixels])

        #
        # Taken from 'blender-thumbnailer.py' in Blender.
        #

        # reverse the vertical line order and add null bytes at the start
        width_byte_4 = self.width * 4
        raw_data = b"".join(
            b'\x00' + buf[span:span + width_byte_4] for span in range(
                (self.height - 1) * self.width * 4, -1, - width_byte_4))

        def png_pack(png_tag, data):
            chunk_head = png_tag + data
            return struct.pack("!I", len(data)) + chunk_head + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head))

        return b"".join([
            b'\x89PNG\r\n\x1a\n',
            png_pack(b'IHDR', struct.pack("!2I5B", self.width, self.height, 8, 6, 0, 0, 0)),
            png_pack(b'IDAT', zlib.compress(raw_data, 9)),
            png_pack(b'IEND', b'')])

    def to_image_data(self, mime_type):
        if mime_type == 'image/png':
            return self.to_png_data()
        raise ValueError("Unsupported image file type {}".format(mime_type))

    def save_png(self, dst_path):
        data = self.to_png_data()
        with open(dst_path, 'wb') as f:
            f.write(data)


def create_img(width, height, r=0.0, g=0.0, b=0.0, a=1.0):
    """
    Create a new image object with 4 channels and initialize it with the given default values.

    (if no arguments are given, these default to R=0, G=0, B=0, A=1.0)
    Return the created image object.
    """
    return Image(width, height, [r, g, b, a] * (width * height))


def create_img_from_pixels(width, height, pixels):
    """
    Create a new image object with 4 channels and initialize it using the given array of pixel data.

    Return the created image object.
    """
    if pixels is None or len(pixels) != width * height * 4:
        return None

    return Image(width, height, pixels)


def copy_img_channel(dst_image, dst_channel, src_image, src_channel):
    """
    Copy a single channel (identified by src_channel) from src_image to dst_image (overwriting dst_channel).

    src_image and dst_image are expected to be image objects created using create_img.
    Return True on success, False otherwise.
    """
    if dst_image is None or src_image is None:
        return False

    if dst_channel < 0 or dst_channel >= dst_image.channels or src_channel < 0 or src_channel >= src_image.channels:
        return False

    if src_image.width != dst_image.width or \
            src_image.height != dst_image.height or \
            src_image.channels != dst_image.channels:
        return False

    for i in range(0, len(dst_image.pixels), dst_image.channels):
        dst_image.pixels[i + dst_channel] = src_image.pixels[i + src_channel]

    return True


def test_save_img(image, path):
    """
    Save the given image to a PNG file (specified by path).

    Return True on success, False otherwise.
    """
    if image is None or image.channels != 4:
        return False

    width = image.width
    height = image.height

    buf = bytearray([int(channel * 255.0) for channel in image.pixels])

    #
    # Taken from 'blender-thumbnailer.py' in Blender.
    #

    # reverse the vertical line order and add null bytes at the start
    width_byte_4 = width * 4
    raw_data = b"".join(
        b'\x00' + buf[span:span + width_byte_4] for span in range((height - 1) * width * 4, -1, - width_byte_4))

    def png_pack(png_tag, data):
        chunk_head = png_tag + data
        return struct.pack("!I", len(data)) + chunk_head + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head))

    data = b"".join([
        b'\x89PNG\r\n\x1a\n',
        png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
        png_pack(b'IDAT', zlib.compress(raw_data, 9)),
        png_pack(b'IEND', b'')])

    with open(path, 'wb') as f:
        f.write(data)
        return True