Path: blob/master/modules/bcdec/image_decompress_bcdec.cpp
10277 views
/**************************************************************************/1/* image_decompress_bcdec.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "image_decompress_bcdec.h"3132#include "core/os/os.h"33#include "core/string/print_string.h"3435#define BCDEC_IMPLEMENTATION36#include "thirdparty/misc/bcdec.h"3738inline void bcdec_bc6h_half_s(const void *compressedBlock, void *decompressedBlock, int destinationPitch) {39bcdec_bc6h_half(compressedBlock, decompressedBlock, destinationPitch, true);40}4142inline void bcdec_bc6h_half_u(const void *compressedBlock, void *decompressedBlock, int destinationPitch) {43bcdec_bc6h_half(compressedBlock, decompressedBlock, destinationPitch, false);44}4546template <void (*decompress_func)(const void *, void *, int), int block_size, int pixel_size, int component_size>47static inline void _safe_decompress_mipmap(int width, int height, const uint8_t *src, uint8_t *dst) {48// A stack-allocated output buffer large enough to contain an entire uncompressed block.49uint8_t temp_buf[4 * 4 * pixel_size];5051// The amount of misaligned pixels on each axis.52const int width_diff = width - (width & ~0x03);53const int height_diff = height - (height & ~0x03);5455// The amount of uncompressed blocks on each axis.56const int width_blocks = (width & ~0x03) / 4;57const int height_blocks = (height & ~0x03) / 4;5859// The pitch of the image in bytes.60const int image_pitch = width * pixel_size;61// The pitch of a block in bytes.62const int block_pitch = 4 * pixel_size;63// The pitch of the last block in bytes.64const int odd_pitch = width_diff * pixel_size;6566size_t src_pos = 0;67size_t dst_pos = 0;6869// Decompress the blocks, starting from the top.70for (int y = 0; y < height_blocks; y += 1) {71// Decompress the blocks, starting from the left.72for (int x = 0; x < width_blocks; x += 1) {73decompress_func(&src[src_pos], &dst[dst_pos], image_pitch / component_size);74src_pos += block_size;75dst_pos += block_pitch;76}7778// Decompress the block on the right.79if (width_diff > 0) {80decompress_func(&src[src_pos], temp_buf, block_pitch / component_size);8182// Copy the data from the temporary buffer to the output.83for (int i = 0; i < 4; i++) {84memcpy(&dst[dst_pos + i * image_pitch], &temp_buf[i * block_pitch], odd_pitch);85}8687src_pos += block_size;88dst_pos += odd_pitch;89}9091// Skip to the next row of blocks, the current one has already been filled.92dst_pos += 3 * image_pitch;93}9495// Decompress the blocks at the bottom of the image.96if (height_diff > 0) {97// Decompress the blocks at the bottom.98for (int x = 0; x < width_blocks; x += 1) {99decompress_func(&src[src_pos], temp_buf, block_pitch / component_size);100101// Copy the data from the temporary buffer to the output.102for (int i = 0; i < height_diff; i++) {103memcpy(&dst[dst_pos + i * image_pitch], &temp_buf[i * block_pitch], block_pitch);104}105106src_pos += block_size;107dst_pos += block_pitch;108}109110// Decompress the block in the lower-right corner.111if (width_diff > 0) {112decompress_func(&src[src_pos], temp_buf, block_pitch / component_size);113114// Copy the data from the temporary buffer to the output.115for (int i = 0; i < height_diff; i++) {116memcpy(&dst[dst_pos + i * image_pitch], &temp_buf[i * block_pitch], odd_pitch);117}118119src_pos += block_size;120dst_pos += odd_pitch;121}122}123}124125template <void (*decompress_func)(const void *, void *, int), int block_size, int pixel_size, int component_size>126static inline void _decompress_mipmap(int width, int height, const uint8_t *src, uint8_t *dst) {127size_t src_pos = 0;128size_t dst_pos = 0;129130// The size of a single block in bytes.131const int block_pitch = 4 * pixel_size;132// The pitch of the image in bytes.133const int image_pitch = width * pixel_size;134135for (int y = 0; y < height; y += 4) {136for (int x = 0; x < width; x += 4) {137decompress_func(&src[src_pos], &dst[dst_pos], image_pitch / component_size);138src_pos += block_size;139dst_pos += block_pitch;140}141142// Skip to the next row of blocks, the current one has already been filled.143dst_pos += 3 * image_pitch;144}145}146147static void decompress_image(BCdecFormat format, const void *src, void *dst, const uint64_t width, const uint64_t height) {148const uint8_t *src_blocks = reinterpret_cast<const uint8_t *>(src);149uint8_t *dec_blocks = reinterpret_cast<uint8_t *>(dst);150151const uint64_t aligned_width = (width + 3) & ~0x03;152const uint64_t aligned_height = (height + 3) & ~0x03;153154if (width != aligned_width || height != aligned_height) {155// Decompress the mipmap in a 'safe' way, which involves starting from the top left.156// For each block row, decompress all of the 'full' blocks, then the misaligned one (on the x axis).157// Then, decompress the final misaligned block row at the bottom.158// Finally, decompress the misaligned block at the bottom right.159switch (format) {160case BCdec_BC1: {161_safe_decompress_mipmap<bcdec_bc1, BCDEC_BC1_BLOCK_SIZE, 4, 1>(width, height, src_blocks, dec_blocks);162} break;163case BCdec_BC2: {164_safe_decompress_mipmap<bcdec_bc2, BCDEC_BC2_BLOCK_SIZE, 4, 1>(width, height, src_blocks, dec_blocks);165} break;166case BCdec_BC3: {167_safe_decompress_mipmap<bcdec_bc3, BCDEC_BC3_BLOCK_SIZE, 4, 1>(width, height, src_blocks, dec_blocks);168} break;169case BCdec_BC4: {170_safe_decompress_mipmap<bcdec_bc4, BCDEC_BC4_BLOCK_SIZE, 1, 1>(width, height, src_blocks, dec_blocks);171} break;172case BCdec_BC5: {173_safe_decompress_mipmap<bcdec_bc5, BCDEC_BC5_BLOCK_SIZE, 2, 1>(width, height, src_blocks, dec_blocks);174} break;175case BCdec_BC6U: {176_safe_decompress_mipmap<bcdec_bc6h_half_u, BCDEC_BC6H_BLOCK_SIZE, 6, 2>(width, height, src_blocks, dec_blocks);177} break;178case BCdec_BC6S: {179_safe_decompress_mipmap<bcdec_bc6h_half_s, BCDEC_BC6H_BLOCK_SIZE, 6, 2>(width, height, src_blocks, dec_blocks);180} break;181case BCdec_BC7: {182_safe_decompress_mipmap<bcdec_bc7, BCDEC_BC7_BLOCK_SIZE, 4, 1>(width, height, src_blocks, dec_blocks);183} break;184}185} else {186// Just decompress as usual, as fast as possible.187switch (format) {188case BCdec_BC1: {189_decompress_mipmap<bcdec_bc1, BCDEC_BC1_BLOCK_SIZE, 4, 1>(width, height, src_blocks, dec_blocks);190} break;191case BCdec_BC2: {192_decompress_mipmap<bcdec_bc2, BCDEC_BC2_BLOCK_SIZE, 4, 1>(width, height, src_blocks, dec_blocks);193} break;194case BCdec_BC3: {195_decompress_mipmap<bcdec_bc3, BCDEC_BC3_BLOCK_SIZE, 4, 1>(width, height, src_blocks, dec_blocks);196} break;197case BCdec_BC4: {198_decompress_mipmap<bcdec_bc4, BCDEC_BC4_BLOCK_SIZE, 1, 1>(width, height, src_blocks, dec_blocks);199} break;200case BCdec_BC5: {201_decompress_mipmap<bcdec_bc5, BCDEC_BC5_BLOCK_SIZE, 2, 1>(width, height, src_blocks, dec_blocks);202} break;203case BCdec_BC6U: {204_decompress_mipmap<bcdec_bc6h_half_u, BCDEC_BC6H_BLOCK_SIZE, 6, 2>(width, height, src_blocks, dec_blocks);205} break;206case BCdec_BC6S: {207_decompress_mipmap<bcdec_bc6h_half_s, BCDEC_BC6H_BLOCK_SIZE, 6, 2>(width, height, src_blocks, dec_blocks);208} break;209case BCdec_BC7: {210_decompress_mipmap<bcdec_bc7, BCDEC_BC7_BLOCK_SIZE, 4, 1>(width, height, src_blocks, dec_blocks);211} break;212}213}214}215216void image_decompress_bcdec(Image *p_image) {217uint64_t start_time = OS::get_singleton()->get_ticks_msec();218219int width = p_image->get_width();220int height = p_image->get_height();221222Image::Format source_format = p_image->get_format();223Image::Format target_format = Image::FORMAT_MAX;224225BCdecFormat bcdec_format = BCdec_BC1;226227switch (source_format) {228case Image::FORMAT_DXT1:229bcdec_format = BCdec_BC1;230target_format = Image::FORMAT_RGBA8;231break;232233case Image::FORMAT_DXT3:234bcdec_format = BCdec_BC2;235target_format = Image::FORMAT_RGBA8;236break;237238case Image::FORMAT_DXT5:239case Image::FORMAT_DXT5_RA_AS_RG:240bcdec_format = BCdec_BC3;241target_format = Image::FORMAT_RGBA8;242break;243244case Image::FORMAT_RGTC_R:245bcdec_format = BCdec_BC4;246target_format = Image::FORMAT_R8;247break;248249case Image::FORMAT_RGTC_RG:250bcdec_format = BCdec_BC5;251target_format = Image::FORMAT_RG8;252break;253254case Image::FORMAT_BPTC_RGBFU:255bcdec_format = BCdec_BC6U;256target_format = Image::FORMAT_RGBH;257break;258259case Image::FORMAT_BPTC_RGBF:260bcdec_format = BCdec_BC6S;261target_format = Image::FORMAT_RGBH;262break;263264case Image::FORMAT_BPTC_RGBA:265bcdec_format = BCdec_BC7;266target_format = Image::FORMAT_RGBA8;267break;268269default:270ERR_FAIL_MSG("bcdec: Can't decompress unknown format: " + Image::get_format_name(source_format) + ".");271break;272}273274int mm_count = p_image->get_mipmap_count();275int64_t target_size = Image::get_image_data_size(width, height, target_format, p_image->has_mipmaps());276277// Decompressed data.278Vector<uint8_t> data;279data.resize(target_size);280uint8_t *wb = data.ptrw();281282// Source data.283const uint8_t *rb = p_image->get_data().ptr();284285// Decompress mipmaps.286for (int i = 0; i <= mm_count; i++) {287int mipmap_w = 0, mipmap_h = 0;288int64_t src_ofs = Image::get_image_mipmap_offset(width, height, source_format, i);289int64_t dst_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, mipmap_w, mipmap_h);290decompress_image(bcdec_format, rb + src_ofs, wb + dst_ofs, mipmap_w, mipmap_h);291}292293p_image->set_data(width, height, p_image->has_mipmaps(), target_format, data);294295// Swap channels if the format is using a channel swizzle.296if (source_format == Image::FORMAT_DXT5_RA_AS_RG) {297p_image->convert_ra_rgba8_to_rg();298}299300print_verbose(vformat("bcdec: Decompression of a %dx%d %s image with %d mipmaps took %d ms.",301p_image->get_width(), p_image->get_height(), Image::get_format_name(source_format), p_image->get_mipmap_count(), OS::get_singleton()->get_ticks_msec() - start_time));302}303304305