Files
MobileGL/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp
T

238 lines
9.2 KiB
C++

// MobileGL - MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#include "PixelStoreProcessor.h"
namespace MobileGL::MG_Util::PixelStoreProcessor {
static SizeT CalculateRowStride(Int width, SizeT pixelSize, Int alignment) {
if (width <= 0 || pixelSize == 0) return 0;
const SizeT rowBytes = static_cast<SizeT>(width) * pixelSize;
const SizeT alignedRowBytes = (rowBytes + alignment - 1) & ~static_cast<SizeT>(alignment - 1);
return alignedRowBytes;
}
static void SwapBytes(void* data, SizeT size, SizeT count) {
if (size <= 1) return;
Uint8* bytes = static_cast<Uint8*>(data);
for (SizeT i = 0; i < count; ++i) {
Uint8* pixel = bytes + i * size;
std::reverse(pixel, pixel + size);
}
}
static inline Uint8 ReverseByteBits(Uint8 b) {
Uint8 r = 0;
for (int i = 0; i < 8; ++i) {
r <<= 1;
r |= (b & 1);
b >>= 1;
}
return r;
}
static void ProcessLSBFirst(void* data, SizeT width, SizeT height) {
if (!data || width == 0 || height == 0) return;
const SizeT rowBytes = (width + 7) / 8; // packed bits per row
Uint8* bytes = static_cast<Uint8*>(data);
for (SizeT y = 0; y < height; ++y) {
for (SizeT i = 0; i < rowBytes; ++i) {
bytes[i] = ReverseByteBits(bytes[i]);
}
bytes += rowBytes;
}
}
static Uint8 GetSwizzledChannelValue(Uint8* pixel, TextureSwizzleParam param) {
switch (param) {
case TextureSwizzleParam::Red:
return pixel[0];
case TextureSwizzleParam::Green:
return pixel[1];
case TextureSwizzleParam::Blue:
return pixel[2];
case TextureSwizzleParam::Alpha:
return pixel[3];
case TextureSwizzleParam::Zero:
return 0;
case TextureSwizzleParam::One:
return 0xFF;
default:
return 0xBD;
}
}
// assume 8 bit per channel
// swizzle.size() == channel count
static void ProcessColorSwizzle(void* data, SizeT pixelCount, const Vector<TextureSwizzleParam>& swizzle) {
const auto bpp = swizzle.size();
Uint8* bytes = static_cast<Uint8*>(data);
Uint8 pixelScratch[4];
for (SizeT i = 0; i < pixelCount; ++i) {
Uint8* pixel = bytes + i * bpp;
for (SizeT ch = 0; ch < bpp; ++ch) {
pixelScratch[ch] = GetSwizzledChannelValue(pixel, swizzle[ch]);
}
Memcpy(pixel, pixelScratch, bpp);
}
}
void* ProcessTexturePixelsDataUnpack(const void* inputPixels, const PixelStoreParameters& params,
TextureInternalFormat targetInternalFormat,
TextureInputFormat textureInputFormat, TexturePixelDataType inputDataType,
IntVec3 dimension, Bool isBitmap, SizeT& outSize) {
const SizeT pixelSize = MG_Util::GetInputBytesPerPixel(textureInputFormat, inputDataType);
Int width = dimension.x();
Int height = dimension.y();
Int depth = dimension.z();
const Int effectiveWidth = (params.RowLength > 0) ? params.RowLength : width;
const Int effectiveHeight = (params.ImageHeight > 0) ? params.ImageHeight : height;
const SizeT inputRowStride = CalculateRowStride(effectiveWidth, pixelSize, params.Alignment);
const SizeT outputRowStride = static_cast<SizeT>(width) * pixelSize;
const Int startX = params.SkipPixels;
const Int startY = params.SkipRows;
const Int startZ = params.SkipImages;
const Int copyWidth = width;
const Int copyHeight = height;
const Int copyDepth = depth;
MGLOG_D("%s: start at: (%d, %d, %d), copy size: (%d, %d, %d), i/o row stride: (%d, %d)", __func__,
startX, startY, startZ, copyWidth, copyHeight, copyDepth, inputRowStride, outputRowStride);
if (copyWidth <= 0 || copyHeight <= 0 || copyDepth <= 0) {
outSize = 0;
return nullptr;
}
outSize = static_cast<SizeT>(copyWidth) * copyHeight * copyDepth * pixelSize;
void* outputPixels = malloc(outSize);
if (!outputPixels) {
outSize = 0;
return nullptr;
}
const Uint8* src = static_cast<const Uint8*>(inputPixels);
Uint8* dst = static_cast<Uint8*>(outputPixels);
src += static_cast<SizeT>(startZ) * static_cast<SizeT>(effectiveHeight) * inputRowStride;
src += static_cast<SizeT>(startY) * inputRowStride;
src += static_cast<SizeT>(startX) * pixelSize;
Bool isByteType =
(inputDataType == TexturePixelDataType::UnsignedByte || inputDataType == TexturePixelDataType::Byte);
for (Int z = 0; z < copyDepth; ++z) {
const Uint8* layerSrc = src;
Uint8* layerDst = dst;
for (Int y = 0; y < copyHeight; ++y) {
Memcpy(layerDst, layerSrc, static_cast<SizeT>(copyWidth) * pixelSize);
if (params.SwapBytes && pixelSize > 1 && !isByteType) {
MGLOG_D("%s: SwapBytes", __func__);
SwapBytes(layerDst, pixelSize, static_cast<SizeT>(copyWidth));
}
if (params.LSBFirst && isBitmap) {
MGLOG_D("%s: LSBFirst", __func__);
ProcessLSBFirst(layerDst, static_cast<SizeT>(copyWidth), 1);
}
if (textureInputFormat == TextureInputFormat::BGRA &&
targetInternalFormat == TextureInternalFormat::RGBA8) {
MGLOG_D("%s: Swizzle (BGRA)", __func__);
// MGLOG_D("%s: pixel0 before = %x", __func__, *((Uint32*)layerDst));
ProcessColorSwizzle(layerDst, static_cast<SizeT>(copyWidth),
{TextureSwizzleParam::Green, TextureSwizzleParam::Blue,
TextureSwizzleParam::Alpha, TextureSwizzleParam::Red});
// MGLOG_D("%s: pixel0 after = %x", __func__, *((Uint32*)layerDst));
}
// else
// MGLOG_D("%s: pixel0 = %x", __func__, *((Uint32*)layerDst));
layerSrc += inputRowStride;
layerDst += outputRowStride;
}
src += static_cast<SizeT>(effectiveHeight) * inputRowStride;
dst += static_cast<SizeT>(copyHeight) * outputRowStride;
}
return outputPixels;
}
void* ProcessTexturePixelsDataPack(const void* inputPixels, const PixelStoreParameters& params, SizeT pixelSize,
IntVec3 dimension, Bool isBitmap, SizeT& outSize) {
if (pixelSize == 0) {
outSize = 0;
return nullptr;
}
Int width = dimension.x();
Int height = dimension.y();
Int depth = dimension.z();
const Int outputWidth = (params.RowLength > 0) ? params.RowLength : width;
const SizeT outputRowStride = CalculateRowStride(outputWidth, pixelSize, params.Alignment);
const SizeT inputRowStride = static_cast<SizeT>(width) * pixelSize;
const Int effectiveHeight = (params.ImageHeight > 0) ? params.ImageHeight : height;
outSize = static_cast<SizeT>(outputRowStride) * static_cast<SizeT>(effectiveHeight) * static_cast<SizeT>(depth);
void* outputPixels = malloc(outSize);
if (!outputPixels) {
outSize = 0;
return nullptr;
}
Memset(outputPixels, 0, outSize);
const Uint8* src = static_cast<const Uint8*>(inputPixels);
Uint8* dst = static_cast<Uint8*>(outputPixels);
dst += static_cast<SizeT>(params.SkipImages) * static_cast<SizeT>(effectiveHeight) * outputRowStride;
dst += static_cast<SizeT>(params.SkipRows) * outputRowStride;
dst += static_cast<SizeT>(params.SkipPixels) * pixelSize;
Vector<Uint8> tempRow(static_cast<SizeT>(width) * pixelSize);
for (Int z = 0; z < depth; ++z) {
Uint8* layerDst = dst;
const Uint8* layerSrc = src;
for (Int y = 0; y < height; ++y) {
Memcpy(tempRow.data(), layerSrc, static_cast<SizeT>(width) * pixelSize);
if (params.SwapBytes && pixelSize > 1) {
SwapBytes(tempRow.data(), pixelSize, static_cast<SizeT>(width));
}
if (params.LSBFirst && isBitmap) {
ProcessLSBFirst(tempRow.data(), static_cast<SizeT>(width), 1);
}
Memcpy(layerDst, tempRow.data(), static_cast<SizeT>(width) * pixelSize);
layerSrc += inputRowStride;
layerDst += outputRowStride;
}
src += static_cast<SizeT>(height) * inputRowStride;
dst += static_cast<SizeT>(effectiveHeight) * outputRowStride;
}
return outputPixels;
}
} // namespace MobileGL::MG_Util::PixelStoreProcessor