From d65264534695b39b5bee665e876e81a6f84d4136 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sat, 1 Nov 2025 19:52:33 +0800 Subject: [PATCH] [Feat] (...): Handle pixel store correctly. --- CMakeLists.txt | 2 + MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 13 -- .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 127 +++++++++---- MobileGL/MG_State/GLState/Core.cpp | 4 + MobileGL/MG_State/GLState/Core.h | 1 + .../GLState/RenderState/RenderState.cpp | 115 ++++++++++-- .../GLState/RenderState/RenderState.h | 15 +- .../GLState/TextureState/TextureObject.cpp | 12 +- .../MG_Util/Texture/PixelStoreProcessor.cpp | 177 ++++++++++++++++++ .../MG_Util/Texture/PixelStoreProcessor.h | 10 + MobileGL/MG_Util/Types.h | 3 + 11 files changed, 406 insertions(+), 73 deletions(-) create mode 100644 MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp create mode 100644 MobileGL/MG_Util/Texture/PixelStoreProcessor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ac485d65..e6a00fc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,8 @@ set(SOURCE_FILES MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp + MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp + MobileGL/MG_Impl/GLXImpl/Exporting/Definitions.cpp MobileGL/MG_Impl/GLXImpl/LookUp/LookUp.cpp diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 1be64a6f..1537c8f3 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -192,19 +192,6 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_External::GLES::glClearDepthf(MG_State::pGLContext->GetClearDepth()); } - { // Pixel store params - { - PixelStoreParam p = PixelStoreParam::UnpackAlignment; - GLint v = MG_State::pGLContext->GetPixelStoreParam(p); - MG_External::GLES::glPixelStorei(MG_Util::ConvertPixelStoreParamToGLEnum(p), v); - } - { - PixelStoreParam p = PixelStoreParam::PackAlignment; - GLint v = MG_State::pGLContext->GetPixelStoreParam(p); - MG_External::GLES::glPixelStorei(MG_Util::ConvertPixelStoreParamToGLEnum(p), v); - } - } - { // Cull face mode CullFaceMode cfm = MG_State::pGLContext->GetCullFaceMode(); MG_External::GLES::glCullFace(MG_Util::ConvertCullFaceModeToGLEnum(cfm)); diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 7c5c4348..738cb69c 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -6,10 +6,11 @@ #include #include #include +#include +#include #include #include #include -#include namespace MobileGL { namespace MG_Impl::GLImpl { @@ -39,6 +40,7 @@ namespace MobileGL { texturePixelDataType)) return; if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadingTarget, level)) return; + if (!pixels) return; // TODO: GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the // GL_PIXEL_UNPACK_BUFFER target and the buffer object's data store is currently mapped. @@ -58,44 +60,52 @@ namespace MobileGL { if (!TextureImpl::ValidateTextureSubImageOffsets(textureObject, xoffset, width, yoffset, height)) return; // ======================= Processing ================================ - // SizeT imageSize = - // MG_Util::CalculateInputTextureImageSize(textureInternalFormat, - // texturePixelDataType, - // {width, height, 1}); auto& mipmap = textureObject->GetMipmap(level); Vector& data = mipmap.data; - SizeT bytesPerPixel = MG_Util::GetInputBytesPerPixel(textureInternalFormat, texturePixelDataType); - // SizeT rowStride = mipmap.size.x() * bytesPerPixel; + SizeT imageSize = 0; + const SizeT bytesPerPixel = MG_Util::GetInputBytesPerPixel(textureInternalFormat, texturePixelDataType); - const Uint8* srcData = reinterpret_cast(pixels); - if (!srcData) { + void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack( + pixels, MG_State::pGLContext->GetPixelStoreParameters(true), bytesPerPixel, {width, height, 1}, + false /*TODO*/, imageSize); + + if (!processedPixels || imageSize == 0) { + MGLOG_E("TexSubImage2D_State: Failed to process pixel data for TexSubImage2D, width: %d, height: %d", + width, height); + if (processedPixels) free(processedPixels); return; } - if (pixels != nullptr) { - auto rowsToSkip = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackSkipRows); - auto pixelsToSkip = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackSkipPixels); + const SizeT srcRowSize = width * bytesPerPixel; + const SizeT destRowSize = mipmap.size.x() * bytesPerPixel; - srcData += (rowsToSkip * width + pixelsToSkip); + if (xoffset + width > static_cast(mipmap.size.x()) || + yoffset + height > static_cast(mipmap.size.y())) { + MGLOG_E("TexSubImage2D_State: Specified region exceeds texture dimensions, xoffset: %d, yoffset: %d, " + "width: %d, height: %d, mipmap size: (%d, %d)", + xoffset, yoffset, width, height, mipmap.size.x(), mipmap.size.y()); + free(processedPixels); + return; } - for (GLint row = 0; row < height; ++row) { - SizeT dstOffset = ((yoffset + row) * mipmap.size.x() + xoffset) * bytesPerPixel; - // Should take states from glPixelStorei/glPixelStoref into account - SizeT srcOffset = row * width * bytesPerPixel; - - if (dstOffset + width * bytesPerPixel <= data.size()) { - Memcpy(data.data() + dstOffset, srcData + srcOffset, width * bytesPerPixel); - } else { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeShared("MG_Impl/GLImpl", __func__, - "Copy failed. Dest image does not have sufficient space.")); - break; - } + const auto* srcData = static_cast(processedPixels); + Uint8* destData = data.data(); + if (!data.size()) { + SizeT totalSize = mipmap.size.x() * mipmap.size.y() * bytesPerPixel; + data.resize(totalSize); } + for (GLsizei y = 0; y < height; y++) { + const SizeT destRowOffset = (yoffset + y) * destRowSize + xoffset * bytesPerPixel; + const SizeT srcRowOffset = y * srcRowSize; + MGLOG_I("memcpy params: dst: %p, src: %p, size: %zu", destData + destRowOffset, srcData + srcRowOffset, + srcRowSize); + Memcpy(destData + destRowOffset, srcData + srcRowOffset, srcRowSize); + } + + free(processedPixels); + mipmap.dirty = true; } @@ -285,6 +295,20 @@ namespace MobileGL { void TexImage2D_State(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels) { + MGLOG_D( + "TexImage2D_State called with target: %s, level: %d, internalformat: %s, width: %d, height: %d, " + "border: %d, format: %s, type: %s, pixels: %p", + MG_Util::ConvertTextureUploadTargetToString(MG_Util::ConvertGLEnumToTextureUploadTarget(target)) + .c_str(), + level, + MG_Util::ConvertTextureInternalFormatToString( + MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat)) + .c_str(), + width, height, border, + MG_Util::ConvertTextureInputFormatToString(MG_Util::ConvertGLEnumToTextureInputFormat(format)).c_str(), + MG_Util::ConvertTexturePixelDataTypeToString(MG_Util::ConvertGLEnumToTexturePixelDataType(type)) + .c_str(), + pixels); // ======================= Converting ================================ TextureUploadTarget textureUploadingTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); TextureTarget textureTarget = MG_Util::ConvertTextureUploadTargetToTextureTarget(textureUploadingTarget); @@ -332,23 +356,50 @@ namespace MobileGL { if (!TextureImpl::ValidateTextureObject(textureObject)) return; // ======================= Processing ================================ - SizeT imageSize = isProxy ? 0 - : MG_Util::CalculateInputTextureImageSize( - textureInternalFormat, texturePixelDataType, {width, height, 1}); - Uint8* skippedPixels = (Uint8*)pixels; - if (pixels != nullptr) { - auto rowsToSkip = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackSkipRows); - auto pixelsToSkip = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackSkipPixels); + SizeT imageSize = 0; + void* processedPixels = nullptr; + const SizeT bytesPerPixel = MG_Util::GetInputBytesPerPixel(textureInternalFormat, texturePixelDataType); + const SizeT totalBytes = width * height * bytesPerPixel; - skippedPixels += (rowsToSkip * width + pixelsToSkip); + if (pixels) { + processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack( + pixels, MG_State::pGLContext->GetPixelStoreParameters(true), bytesPerPixel, {width, height, 1}, + false, imageSize); } + MG_State::GLState::MipmapLevelInput mipmap = + MG_State::GLState::MipmapLevelInput({width, height, 1}, level, false, 0, + {isProxy ? nullptr : malloc(totalBytes), isProxy ? 0 : totalBytes}); + + if (!isProxy && !mipmap.inputData.data) { + MGLOG_E("TexImage2D_State: Failed to allocate memory for mipmap level data, size: %zu", totalBytes); + if (processedPixels) free(processedPixels); + processedPixels = nullptr; + } + + if (processedPixels && imageSize > 0 && !isProxy) { + if (imageSize != totalBytes) { + MGLOG_W("TexImage2D_State: Processed pixel data size (%zu) does not match expected size (%zu). " + "This may indicate an alignment or processing issue.", + imageSize, totalBytes); + } + + const SizeT copySize = std::min(imageSize, totalBytes); + Memcpy(mipmap.inputData.data, processedPixels, copySize); + free(processedPixels); + } else if (pixels && !isProxy) { + MGLOG_E("TexImage2D_State: Failed to process pixel data, initializing with original data."); + Memcpy(mipmap.inputData.data, pixels, totalBytes); + } else { + if (mipmap.inputData.data) { + free(mipmap.inputData.data); + mipmap.inputData.data = nullptr; + } + } textureObject->SetInternalFormat(textureInternalFormat); - MG_State::GLState::MipmapLevelInput mipmap = MG_State::GLState::MipmapLevelInput( - {width, height, 1}, level, false, 0, - {const_cast(isProxy ? (void*)skippedPixels : nullptr), imageSize}); textureObject->SetMipmapLevel(mipmap); + if (mipmap.inputData.data) free(mipmap.inputData.data); } void TexImage1D_State(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index 68f3827b..2e0d089b 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -285,6 +285,10 @@ namespace MobileGL { return m_renderState.GetPixelStoreParam(param); } + PixelStoreParameters GLContext::GetPixelStoreParameters(Bool isUnpack) const { + return m_renderState.GetPixelStoreParameters(isUnpack); + } + void GLContext::SetCullFaceMode(CullFaceMode mode) { m_renderState.SetCullFaceMode(mode); } diff --git a/MobileGL/MG_State/GLState/Core.h b/MobileGL/MG_State/GLState/Core.h index 1f175c5f..bc508d73 100644 --- a/MobileGL/MG_State/GLState/Core.h +++ b/MobileGL/MG_State/GLState/Core.h @@ -90,6 +90,7 @@ namespace MobileGL { Float GetClearDepth() const; void SetPixelStoreParam(PixelStoreParam param, Int value); Int GetPixelStoreParam(PixelStoreParam param) const; + PixelStoreParameters GetPixelStoreParameters(Bool isUnpack) const; void SetCullFaceMode(CullFaceMode mode); CullFaceMode GetCullFaceMode() const; void SetScissorBox(IntVec4 box); // x, y, width, height diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp index cfbb6477..b8b0d63b 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp @@ -3,20 +3,7 @@ namespace MobileGL { namespace MG_State { namespace GLState { - RenderState::RenderState() { - for (int i = 0; i < static_cast(PixelStoreParam::PixelStoreParamCount); ++i) { - switch (static_cast(i)) { - case PixelStoreParam::PackAlignment: - case PixelStoreParam::UnpackAlignment: - m_pixelStoreParams[i] = 4; - break; - - default: - m_pixelStoreParams[i] = 0; - break; - } - } - } + RenderState::RenderState() {} // -------------------- Rasterization -------------------- void RenderState::SetViewport(IntVec4 viewport) { @@ -124,11 +111,107 @@ namespace MobileGL { // -------------------- Pixel Store -------------------- void RenderState::SetPixelStoreParam(PixelStoreParam param, Int value) { - m_pixelStoreParams[static_cast(param)] = value; + switch (param) { + case PixelStoreParam::PackAlignment: + m_packParameters.Alignment = value; + break; + case PixelStoreParam::PackRowLength: + m_packParameters.RowLength = value; + break; + case PixelStoreParam::PackImageHeight: + m_packParameters.ImageHeight = value; + break; + case PixelStoreParam::PackSkipPixels: + m_packParameters.SkipPixels = value; + break; + case PixelStoreParam::PackSkipRows: + m_packParameters.SkipRows = value; + break; + case PixelStoreParam::PackSkipImages: + m_packParameters.SkipImages = value; + break; + case PixelStoreParam::PackSwapBytes: + m_packParameters.SwapBytes = value != 0; + break; + case PixelStoreParam::PackLsbFirst: + m_packParameters.LSBFirst = value != 0; + break; + case PixelStoreParam::UnpackAlignment: + m_unpackParameters.Alignment = value; + break; + case PixelStoreParam::UnpackRowLength: + m_unpackParameters.RowLength = value; + break; + case PixelStoreParam::UnpackImageHeight: + m_unpackParameters.ImageHeight = value; + break; + case PixelStoreParam::UnpackSkipPixels: + m_unpackParameters.SkipPixels = value; + break; + case PixelStoreParam::UnpackSkipRows: + m_unpackParameters.SkipRows = value; + break; + case PixelStoreParam::UnpackSkipImages: + m_unpackParameters.SkipImages = value; + break; + case PixelStoreParam::UnpackSwapBytes: + m_unpackParameters.SwapBytes = value != 0; + break; + case PixelStoreParam::UnpackLsbFirst: + m_unpackParameters.LSBFirst = value != 0; + break; + default: + MGLOG_F("RenderState::SetPixelStoreParam: Invalid PixelStoreParam enum: %d", + static_cast(param)); + assert(false && "Invalid PixelStoreParam enum"); + return; + } } Int RenderState::GetPixelStoreParam(PixelStoreParam param) const { - return m_pixelStoreParams[static_cast(param)]; + switch (param) { + case PixelStoreParam::PackAlignment: + return m_packParameters.Alignment; + case PixelStoreParam::PackRowLength: + return m_packParameters.RowLength; + case PixelStoreParam::PackImageHeight: + return m_packParameters.ImageHeight; + case PixelStoreParam::PackSkipPixels: + return m_packParameters.SkipPixels; + case PixelStoreParam::PackSkipRows: + return m_packParameters.SkipRows; + case PixelStoreParam::PackSkipImages: + return m_packParameters.SkipImages; + case PixelStoreParam::PackSwapBytes: + return m_packParameters.SwapBytes ? 1 : 0; + case PixelStoreParam::PackLsbFirst: + return m_packParameters.LSBFirst ? 1 : 0; + case PixelStoreParam::UnpackAlignment: + return m_unpackParameters.Alignment; + case PixelStoreParam::UnpackRowLength: + return m_unpackParameters.RowLength; + case PixelStoreParam::UnpackImageHeight: + return m_unpackParameters.ImageHeight; + case PixelStoreParam::UnpackSkipPixels: + return m_unpackParameters.SkipPixels; + case PixelStoreParam::UnpackSkipRows: + return m_unpackParameters.SkipRows; + case PixelStoreParam::UnpackSkipImages: + return m_unpackParameters.SkipImages; + case PixelStoreParam::UnpackSwapBytes: + return m_unpackParameters.SwapBytes ? 1 : 0; + case PixelStoreParam::UnpackLsbFirst: + return m_unpackParameters.LSBFirst ? 1 : 0; + default: + MGLOG_F("RenderState::GetPixelStoreParam: Invalid PixelStoreParam enum: %d", + static_cast(param)); + assert(false && "Invalid PixelStoreParam enum"); + return 0; + } + } + + PixelStoreParameters RenderState::GetPixelStoreParameters(Bool isUnpack) const { + return isUnpack ? m_unpackParameters : m_packParameters; } // -------------------- Cull Face -------------------- diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.h b/MobileGL/MG_State/GLState/RenderState/RenderState.h index 7d6ae130..49998b61 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.h +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.h @@ -109,6 +109,17 @@ namespace MobileGL { Unknown = -1 }; + struct PixelStoreParameters { + Bool SwapBytes = false; + Bool LSBFirst = false; + Int RowLength = 0; + Int ImageHeight = 0; + Int SkipPixels = 0; + Int SkipRows = 0; + Int SkipImages = 0; + Int Alignment = 4; + }; + namespace MG_State { namespace GLState { class RenderState { @@ -147,6 +158,7 @@ namespace MobileGL { // Pixel Store void SetPixelStoreParam(PixelStoreParam param, Int value); Int GetPixelStoreParam(PixelStoreParam param) const; + PixelStoreParameters GetPixelStoreParameters(Bool isUnpack) const; // Cull Face void SetCullFaceMode(CullFaceMode mode); @@ -180,7 +192,8 @@ namespace MobileGL { Float m_clearDepth = 1.0f; // Pixel Store - Array(PixelStoreParam::PixelStoreParamCount)> m_pixelStoreParams; + PixelStoreParameters m_packParameters; + PixelStoreParameters m_unpackParameters; // Cull Face Bool m_cullFaceEnabled = false; diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp index c94fa135..5239dbc6 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp @@ -58,8 +58,10 @@ namespace MobileGL { } MipmapLevelInternal& TextureObjectBase::GetMipmap(Int index) { - if (index > m_mipmaps.size()) { - // fallback to the last mipmap + if (index >= m_mipmaps.size()) { + MGLOG_F("TextureObjectBase::GetMipmap: Requested mipmap level %d exceeds available levels %zu", + index, m_mipmaps.size()); + assert(false && "Requested mipmap level exceeds available levels"); index = static_cast(m_mipmaps.size() - 1); } return m_mipmaps[index]; @@ -145,7 +147,7 @@ namespace MobileGL { void TextureObject1D::SetMipmapImpl(const MipmapLevelInput& level) { if (level.size.x() > 0) { - m_mipmaps.push_back(level); + m_mipmaps.push_back(MipmapLevelInternal(level)); } } @@ -155,7 +157,7 @@ namespace MobileGL { void TextureObject2D::SetMipmapImpl(const MipmapLevelInput& level) { if (level.size.x() > 0 && level.size.y() > 0) { - m_mipmaps.push_back(level); + m_mipmaps.push_back(MipmapLevelInternal(level)); } } @@ -165,7 +167,7 @@ namespace MobileGL { void TextureObject3D::SetMipmapImpl(const MipmapLevelInput& level) { if (level.size.x() > 0 && level.size.y() > 0 && level.size.z() > 0) { - m_mipmaps.push_back(level); + m_mipmaps.push_back(MipmapLevelInternal(level)); } } diff --git a/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp b/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp new file mode 100644 index 00000000..7758cb17 --- /dev/null +++ b/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp @@ -0,0 +1,177 @@ +#include "PixelStoreProcessor.h" + +namespace MobileGL::MG_Util::PixelStoreProcessor { + static SizeT CalculateStride(Int width, SizeT pixelSize, Int alignment) { + if (width <= 0 || pixelSize == 0) return 0; + + const SizeT rowBytes = static_cast(width) * pixelSize; + const SizeT alignedRowBytes = (rowBytes + alignment - 1) & ~static_cast(alignment - 1); + return alignedRowBytes; + } + + static void SwapBytes(void* data, SizeT size, SizeT count) { + if (size <= 1) return; + + Uint8* bytes = static_cast(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(data); + + for (SizeT y = 0; y < height; ++y) { + for (SizeT i = 0; i < rowBytes; ++i) { + bytes[i] = ReverseByteBits(bytes[i]); + } + bytes += rowBytes; + } + } + + void* ProcessTexturePixelsDataUnpack(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 effectiveWidth = (params.RowLength > 0) ? params.RowLength : width; + const Int effectiveHeight = (params.ImageHeight > 0) ? params.ImageHeight : height; + const SizeT inputStride = CalculateStride(effectiveWidth, pixelSize, params.Alignment); + const SizeT outputStride = static_cast(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; + + if (copyWidth <= 0 || copyHeight <= 0 || copyDepth <= 0) { + outSize = 0; + return nullptr; + } + + outSize = static_cast(copyWidth) * copyHeight * copyDepth * pixelSize; + void* outputPixels = malloc(outSize); + if (!outputPixels) { + outSize = 0; + return nullptr; + } + + const Uint8* src = static_cast(inputPixels); + Uint8* dst = static_cast(outputPixels); + + src += static_cast(startZ) * static_cast(effectiveHeight) * inputStride; + src += static_cast(startY) * inputStride; + src += static_cast(startX) * pixelSize; + + 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(copyWidth) * pixelSize); + + if (params.SwapBytes && pixelSize > 1) { + SwapBytes(layerDst, pixelSize, static_cast(copyWidth)); + } + + if (params.LSBFirst && isBitmap) { + ProcessLSBFirst(layerDst, static_cast(copyWidth), 1); + } + + layerSrc += inputStride; + layerDst += static_cast(copyWidth) * pixelSize; + } + + src += static_cast(effectiveHeight) * inputStride; + dst += static_cast(copyHeight) * static_cast(copyWidth) * pixelSize; + } + + 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 outputStride = CalculateStride(outputWidth, pixelSize, params.Alignment); + const SizeT inputStride = static_cast(width) * pixelSize; + + const Int effectiveHeight = (params.ImageHeight > 0) ? params.ImageHeight : height; + + outSize = static_cast(outputStride) * static_cast(effectiveHeight) * static_cast(depth); + void* outputPixels = malloc(outSize); + if (!outputPixels) { + outSize = 0; + return nullptr; + } + + Memset(outputPixels, 0, outSize); + + const Uint8* src = static_cast(inputPixels); + Uint8* dst = static_cast(outputPixels); + + dst += static_cast(params.SkipImages) * static_cast(effectiveHeight) * outputStride; + dst += static_cast(params.SkipRows) * outputStride; + dst += static_cast(params.SkipPixels) * pixelSize; + + Vector tempRow(static_cast(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(width) * pixelSize); + + if (params.SwapBytes && pixelSize > 1) { + SwapBytes(tempRow.data(), pixelSize, static_cast(width)); + } + + if (params.LSBFirst && isBitmap) { + ProcessLSBFirst(tempRow.data(), static_cast(width), 1); + } + + Memcpy(layerDst, tempRow.data(), static_cast(width) * pixelSize); + + layerSrc += inputStride; + layerDst += outputStride; + } + + src += static_cast(height) * inputStride; + dst += static_cast(effectiveHeight) * outputStride; + } + + return outputPixels; + } +} // namespace MobileGL::MG_Util::PixelStoreProcessor diff --git a/MobileGL/MG_Util/Texture/PixelStoreProcessor.h b/MobileGL/MG_Util/Texture/PixelStoreProcessor.h new file mode 100644 index 00000000..84a36a5f --- /dev/null +++ b/MobileGL/MG_Util/Texture/PixelStoreProcessor.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +namespace MobileGL::MG_Util::PixelStoreProcessor { + void* ProcessTexturePixelsDataUnpack(const void* inputPixels, const PixelStoreParameters& params, SizeT pixelSize, + IntVec3 dimension, Bool isBitmap, SizeT& outSize); + void* ProcessTexturePixelsDataPack(const void* inputPixels, const PixelStoreParameters& params, SizeT pixelSize, + IntVec3 dimension, Bool isBitmap, SizeT& outSize); +} // namespace MobileGL::MG_Util::PixelStoreProcessor diff --git a/MobileGL/MG_Util/Types.h b/MobileGL/MG_Util/Types.h index f5ca0bc1..defa4b49 100644 --- a/MobileGL/MG_Util/Types.h +++ b/MobileGL/MG_Util/Types.h @@ -59,6 +59,9 @@ namespace MobileGL { inline void Memcpy(void* dest, const void* src, SizeT size) { std::memcpy(dest, src, size); } + inline void Memset(void* dest, Int value, SizeT size) { + std::memset(dest, value, size); + } template constexpr auto ToArray(Ts&&... elems) { using E = std::common_type_t;