Blender V4.3
blendthumb_png.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
12#include <cstring>
13#include <optional>
14#include <zlib.h>
15
16#include "blendthumb.hh"
17
18#include "BLI_endian_defines.h"
19#include "BLI_endian_switch.h"
20#include "BLI_vector.hh"
21
23{
24 if (ENDIAN_ORDER == L_ENDIAN) {
26 }
27 output.extend_unchecked(blender::Span((uint8_t *)&data, 4));
28}
29
31#define PNG_CHUNK_EXTRA 12
32
34 const uint32_t tag,
35 const blender::Vector<uint8_t> &data)
36{
37 uint32_t crc = crc32(0, nullptr, 0);
38 crc = crc32(crc, (uint8_t *)&tag, sizeof(tag));
39 crc = crc32(crc, (uint8_t *)data.data(), data.size());
40
41 png_extend_native_int32(output, data.size());
42 output.extend_unchecked(blender::Span((uint8_t *)&tag, sizeof(tag)));
43 output.extend_unchecked(data);
44 png_extend_native_int32(output, crc);
45}
46
48{
49 /* In the image data sent to the compression step, each scan-line is preceded by a filter type
50 * byte containing the numeric code of the filter algorithm used for that scan-line. */
51 const size_t line_size = thumb->width * 4;
53 size_t final_size = thumb->height * (line_size + 1);
54 filtered.reserve(final_size);
55 for (int i = 0; i < thumb->height; i++) {
56 filtered.append_unchecked(0x00);
57 filtered.extend_unchecked(blender::Span(&thumb->data[i * line_size], line_size));
58 }
59 BLI_assert(final_size == filtered.size());
60 return filtered;
61}
62
63static std::optional<blender::Vector<uint8_t>> zlib_compress(const blender::Vector<uint8_t> &data)
64{
65 ulong uncompressed_size = data.size();
66 uLongf compressed_size = compressBound(uncompressed_size);
67
68 blender::Vector<uint8_t> compressed(compressed_size, 0x00);
69
70 int return_value = compress2((uchar *)compressed.data(),
71 &compressed_size,
72 (uchar *)data.data(),
73 uncompressed_size,
74 Z_NO_COMPRESSION);
75 if (return_value != Z_OK) {
76 /* Something went wrong with compression of data. */
77 return std::nullopt;
78 }
79 compressed.resize(compressed_size);
80 return compressed;
81}
82
83std::optional<blender::Vector<uint8_t>> blendthumb_create_png_data_from_thumb(
84 const Thumbnail *thumb)
85{
86 if (thumb->data.is_empty()) {
87 return std::nullopt;
88 }
89
90 /* Create `IDAT` chunk data. */
91 blender::Vector<uint8_t> image_data;
92 {
93 auto image_data_opt = zlib_compress(filtered_rows_from_thumb(thumb));
94 if (image_data_opt == std::nullopt) {
95 return std::nullopt;
96 }
97 image_data = *image_data_opt;
98 }
99
100 /* Create the IHDR chunk data. */
101 blender::Vector<uint8_t> ihdr_data;
102 {
103 const size_t ihdr_data_final_size = 4 + 4 + 5;
104 ihdr_data.reserve(ihdr_data_final_size);
105 png_extend_native_int32(ihdr_data, thumb->width);
106 png_extend_native_int32(ihdr_data, thumb->height);
107 ihdr_data.extend_unchecked({
108 0x08, /* Bit Depth. */
109 0x06, /* Color Type. */
110 0x00, /* Compression method. */
111 0x00, /* Filter method. */
112 0x00, /* Interlace method. */
113 });
114 BLI_assert(size_t(ihdr_data.size()) == ihdr_data_final_size);
115 }
116
117 /* Join it all together to create a PNG image. */
119 {
120 const size_t png_buf_final_size = (
121 /* Header. */
122 8 +
123 /* `IHDR` chunk. */
124 (ihdr_data.size() + PNG_CHUNK_EXTRA) +
125 /* `IDAT` chunk. */
126 (image_data.size() + PNG_CHUNK_EXTRA) +
127 /* `IEND` chunk. */
129
130 png_buf.reserve(png_buf_final_size);
131
132 /* This is the standard PNG file header. Every PNG file starts with it. */
133 png_buf.extend_unchecked({0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A});
134
135 png_chunk_create(png_buf, MAKE_ID('I', 'H', 'D', 'R'), ihdr_data);
136 png_chunk_create(png_buf, MAKE_ID('I', 'D', 'A', 'T'), image_data);
137 png_chunk_create(png_buf, MAKE_ID('I', 'E', 'N', 'D'), {});
138
139 BLI_assert(size_t(png_buf.size()) == png_buf_final_size);
140 }
141
142 return png_buf;
143}
#define BLI_assert(a)
Definition BLI_assert.h:50
#define L_ENDIAN
#define ENDIAN_ORDER
BLI_INLINE void BLI_endian_switch_int32(int *val) ATTR_NONNULL(1)
unsigned char uchar
unsigned long ulong
#define MAKE_ID(a, b, c, d)
Definition blendthumb.hh:53
static blender::Vector< uint8_t > filtered_rows_from_thumb(const Thumbnail *thumb)
static std::optional< blender::Vector< uint8_t > > zlib_compress(const blender::Vector< uint8_t > &data)
#define PNG_CHUNK_EXTRA
static void png_extend_native_int32(blender::Vector< uint8_t > &output, int32_t data)
std::optional< blender::Vector< uint8_t > > blendthumb_create_png_data_from_thumb(const Thumbnail *thumb)
static void png_chunk_create(blender::Vector< uint8_t > &output, const uint32_t tag, const blender::Vector< uint8_t > &data)
bool is_empty() const
Definition BLI_array.hh:253
int64_t size() const
void resize(const int64_t new_size)
void extend_unchecked(Span< T > array)
void append_unchecked(const T &value)
void reserve(const int64_t min_capacity)
unsigned int uint32_t
Definition stdint.h:80
signed int int32_t
Definition stdint.h:77
unsigned char uint8_t
Definition stdint.h:78
blender::Array< uint8_t > data
Definition blendthumb.hh:23