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