| /* | |
| * Copyright 2023 Rive | |
| */ | |
| #include "rive/decoders/bitmap_decoder.hpp" | |
| #include "rive/rive_types.hpp" | |
| #include "rive/math/simd.hpp" | |
| #include <stdio.h> | |
| #include <string.h> | |
| Bitmap::Bitmap(uint32_t width, | |
| uint32_t height, | |
| size_t numBytes, | |
| PixelFormat pixelFormat, | |
| std::unique_ptr<const uint8_t[]> bytes) : | |
| m_Width(width), | |
| m_Height(height), | |
| m_NumBytes(numBytes), | |
| m_PixelFormat(pixelFormat), | |
| m_Bytes(std::move(bytes)) | |
| {} | |
| static size_t bytes_per_pixel(Bitmap::PixelFormat format) | |
| { | |
| switch (format) | |
| { | |
| case Bitmap::PixelFormat::RGB: | |
| return 3; | |
| case Bitmap::PixelFormat::RGBA: | |
| case Bitmap::PixelFormat::RGBAPremul: | |
| return 4; | |
| } | |
| RIVE_UNREACHABLE(); | |
| } | |
| void Bitmap::pixelFormat(PixelFormat format) | |
| { | |
| if (format == m_PixelFormat) | |
| { | |
| return; | |
| } | |
| size_t imageNumPixels = m_Height * m_Width; | |
| size_t fromBytesPerPixel = bytes_per_pixel(m_PixelFormat); | |
| size_t toBytesPerPixel = bytes_per_pixel(format); | |
| size_t toSizeInBytes = imageNumPixels * toBytesPerPixel; | |
| // Round our allocation up to a multiple of 8 so we can premultiply two | |
| // pixels at once if needed. | |
| size_t allocSize = (toSizeInBytes + 7) & ~7llu; | |
| assert(allocSize >= toSizeInBytes && allocSize <= toSizeInBytes + 7); | |
| auto toBytes = std::unique_ptr<uint8_t[]>(new uint8_t[allocSize]); | |
| // Copy into the new pixel buffer. | |
| int writeIndex = 0; | |
| int readIndex = 0; | |
| for (size_t i = 0; i < imageNumPixels; i++) | |
| { | |
| for (size_t j = 0; j < toBytesPerPixel; j++) | |
| { | |
| toBytes[writeIndex++] = | |
| j < fromBytesPerPixel ? m_Bytes[readIndex++] : 255; | |
| } | |
| } | |
| if (format == PixelFormat::RGBAPremul) | |
| { | |
| // Convert to premultiplied alpha. | |
| for (size_t i = 0; i < toSizeInBytes; i += 8) | |
| { | |
| // Load 2 pixels into 64 bits. We overallocated our buffer to ensure | |
| // we could always premultiply in twos. | |
| assert(i + 8 <= allocSize); | |
| rive::uint8x8 twoPx = rive::simd::load<uint8_t, 8>(&toBytes[i]); | |
| uint8_t a0 = twoPx[3]; | |
| uint8_t a1 = twoPx[7]; | |
| // Don't premultiply fully opaque pixels. | |
| if (a0 != 255 || a1 != 255) | |
| { | |
| // Cast to 16 bits to avoid overflow. | |
| rive::uint16x8 widePx = rive::simd::cast<uint16_t>(twoPx); | |
| // Multiply by alpha. | |
| rive::uint16x8 alpha = {a0, a0, a0, 255, a1, a1, a1, 255}; | |
| widePx = rive::simd::div255(widePx * alpha); | |
| // Cast back to 8 bits and store. | |
| twoPx = rive::simd::cast<uint8_t>(widePx); | |
| rive::simd::store(&toBytes[i], twoPx); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| // Unmultiplying alpha is not currently supported. | |
| assert(m_PixelFormat != PixelFormat::RGBAPremul); | |
| } | |
| m_Bytes = std::move(toBytes); | |
| m_PixelFormat = format; | |
| m_NumBytes = toSizeInBytes; | |
| } |