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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blendthumb/src/blendthumb_png.cc')
-rw-r--r--source/blender/blendthumb/src/blendthumb_png.cc158
1 files changed, 158 insertions, 0 deletions
diff --git a/source/blender/blendthumb/src/blendthumb_png.cc b/source/blender/blendthumb/src/blendthumb_png.cc
new file mode 100644
index 00000000000..d8156150078
--- /dev/null
+++ b/source/blender/blendthumb/src/blendthumb_png.cc
@@ -0,0 +1,158 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup blendthumb
+ *
+ * Expose #blendthumb_create_png_data_from_thumb that creates the PNG data
+ * but does not write it to a file.
+ */
+
+#include <cstring>
+#include <optional>
+#include <zlib.h>
+
+#include "blendthumb.hh"
+
+#include "BLI_endian_defines.h"
+#include "BLI_endian_switch.h"
+#include "BLI_vector.hh"
+
+static void png_extend_native_int32(blender::Vector<uint8_t> &output, int32_t data)
+{
+ if (ENDIAN_ORDER == L_ENDIAN) {
+ BLI_endian_switch_int32(&data);
+ }
+ output.extend_unchecked(blender::Span((uint8_t *)&data, 4));
+}
+
+/** The number of bytes each chunk uses on top of the data that's written. */
+#define PNG_CHUNK_EXTRA 12
+
+static void png_chunk_create(blender::Vector<uint8_t> &output,
+ const uint32_t tag,
+ const blender::Vector<uint8_t> &data)
+{
+ uint32_t crc = crc32(0, nullptr, 0);
+ crc = crc32(crc, (uint8_t *)&tag, sizeof(tag));
+ crc = crc32(crc, (uint8_t *)data.data(), data.size());
+
+ png_extend_native_int32(output, data.size());
+ output.extend_unchecked(blender::Span((uint8_t *)&tag, sizeof(tag)));
+ output.extend_unchecked(data);
+ png_extend_native_int32(output, crc);
+}
+
+static blender::Vector<uint8_t> filtered_rows_from_thumb(const Thumbnail *thumb)
+{
+ /* In the image data sent to the compression step, each scan-line is preceded by a filter type
+ * byte containing the numeric code of the filter algorithm used for that scan-line. */
+ const size_t line_size = thumb->width * 4;
+ blender::Vector<uint8_t> filtered{};
+ size_t final_size = thumb->height * (line_size + 1);
+ filtered.reserve(final_size);
+ for (int i = 0; i < thumb->height; i++) {
+ filtered.append_unchecked(0x00);
+ filtered.extend_unchecked(blender::Span(&thumb->data[i * line_size], line_size));
+ }
+ BLI_assert(final_size == filtered.size());
+ return filtered;
+}
+
+static std::optional<blender::Vector<uint8_t>> zlib_compress(const blender::Vector<uint8_t> &data)
+{
+ unsigned long uncompressed_size = data.size();
+ uLongf compressed_size = compressBound(uncompressed_size);
+
+ blender::Vector<uint8_t> compressed(compressed_size, 0x00);
+
+ int return_value = compress2((uchar *)compressed.data(),
+ &compressed_size,
+ (uchar *)data.data(),
+ uncompressed_size,
+ Z_NO_COMPRESSION);
+ if (return_value != Z_OK) {
+ /* Something went wrong with compression of data. */
+ return std::nullopt;
+ }
+ compressed.resize(compressed_size);
+ return compressed;
+}
+
+std::optional<blender::Vector<uint8_t>> blendthumb_create_png_data_from_thumb(
+ const Thumbnail *thumb)
+{
+ if (thumb->data.is_empty()) {
+ return std::nullopt;
+ }
+
+ /* Create `IDAT` chunk data. */
+ blender::Vector<uint8_t> image_data{};
+ {
+ auto image_data_opt = zlib_compress(filtered_rows_from_thumb(thumb));
+ if (image_data_opt == std::nullopt) {
+ return std::nullopt;
+ }
+ image_data = *image_data_opt;
+ }
+
+ /* Create the IHDR chunk data. */
+ blender::Vector<uint8_t> ihdr_data{};
+ {
+ const size_t ihdr_data_final_size = 4 + 4 + 5;
+ ihdr_data.reserve(ihdr_data_final_size);
+ png_extend_native_int32(ihdr_data, thumb->width);
+ png_extend_native_int32(ihdr_data, thumb->height);
+ ihdr_data.extend_unchecked({
+ 0x08, /* Bit Depth. */
+ 0x06, /* Color Type. */
+ 0x00, /* Compression method. */
+ 0x00, /* Filter method. */
+ 0x00, /* Interlace method. */
+ });
+ BLI_assert((size_t)ihdr_data.size() == ihdr_data_final_size);
+ }
+
+ /* Join it all together to create a PNG image. */
+ blender::Vector<uint8_t> png_buf{};
+ {
+ const size_t png_buf_final_size = (
+ /* Header. */
+ 8 +
+ /* `IHDR` chunk. */
+ (ihdr_data.size() + PNG_CHUNK_EXTRA) +
+ /* `IDAT` chunk. */
+ (image_data.size() + PNG_CHUNK_EXTRA) +
+ /* `IEND` chunk. */
+ PNG_CHUNK_EXTRA);
+
+ png_buf.reserve(png_buf_final_size);
+
+ /* This is the standard PNG file header. Every PNG file starts with it. */
+ png_buf.extend_unchecked({0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A});
+
+ png_chunk_create(png_buf, MAKE_ID('I', 'H', 'D', 'R'), ihdr_data);
+ png_chunk_create(png_buf, MAKE_ID('I', 'D', 'A', 'T'), image_data);
+ png_chunk_create(png_buf, MAKE_ID('I', 'E', 'N', 'D'), {});
+
+ BLI_assert((size_t)png_buf.size() == png_buf_final_size);
+ }
+
+ return png_buf;
+}