blob: 4ba38a427fed625ce8b3d51cbe1a6dd4fe909dce [file] [edit]
/*
* Copyright 2026 Rive
*/
#include "rive/decoders/bitmap_decoder.hpp"
#include "rive/gpu_texture_format.hpp"
// bc7decomp provides BC7 software decompression.
// rgbcx provides BC1/BC2/BC3 software decompression.
#include "bc7decomp.h"
#include "rgbcx.h"
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <memory>
std::unique_ptr<Bitmap> decode_bc_texture(const uint8_t* blocks,
size_t /*byteCount*/,
uint32_t width,
uint32_t height,
rive::GPUTextureFormat format)
{
// All BCn formats use 4x4 pixel blocks.
const uint32_t blocksX = (width + 3) / 4;
const uint32_t blocksY = (height + 3) / 4;
const size_t pixelCount = static_cast<size_t>(width) * height;
auto pixels = std::make_unique<uint8_t[]>(pixelCount * 4);
memset(pixels.get(), 0, pixelCount * 4);
const uint8_t* src = blocks;
for (uint32_t by = 0; by < blocksY; by++)
{
for (uint32_t bx = 0; bx < blocksX; bx++)
{
// Decode one 4x4 block into a temporary 16-pixel RGBA buffer.
// uint32_t storage gives the bc7decomp/rgbcx union casts proper
// alignment (required on ARM) and lets the copy below move one
// pixel per assignment.
uint32_t blockPixels[16] = {};
switch (format)
{
case rive::GPUTextureFormat::bc7:
bc7decomp::unpack_bc7(
src,
reinterpret_cast<bc7decomp::color_rgba*>(blockPixels));
src += 16;
break;
case rive::GPUTextureFormat::bc1:
rgbcx::unpack_bc1(
src,
reinterpret_cast<rgbcx::color32*>(blockPixels),
true);
src += 8;
break;
case rive::GPUTextureFormat::bc3:
rgbcx::unpack_bc3(
src,
reinterpret_cast<rgbcx::color32*>(blockPixels));
src += 16;
break;
default:
fprintf(stderr,
"DecodeBcTexture - unsupported BC format %u\n",
static_cast<unsigned>(format));
return nullptr;
}
// Copy decoded pixels into the output image. The last block
// row/column may extend past the image edge — clamp via a
// precomputed pixel count so each loop has a single exit.
uint32_t* dst32 = reinterpret_cast<uint32_t*>(pixels.get());
const uint32_t copyW = std::min<uint32_t>(4u, width - bx * 4);
const uint32_t copyH = std::min<uint32_t>(4u, height - by * 4);
for (uint32_t py = 0; py < copyH; py++)
{
const uint32_t dstY = by * 4 + py;
for (uint32_t px = 0; px < copyW; px++)
{
const uint32_t dstX = bx * 4 + px;
dst32[dstY * width + dstX] = blockPixels[py * 4 + px];
}
}
}
}
const size_t numBytes = static_cast<size_t>(width) * height * 4;
return std::make_unique<Bitmap>(width,
height,
numBytes,
Bitmap::PixelFormat::RGBA,
std::move(pixels));
}