| /* |
| * 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)); |
| } |