mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Feat] (...): Handle pixel store correctly.
This commit is contained in:
@@ -60,6 +60,8 @@ set(SOURCE_FILES
|
|||||||
|
|
||||||
MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp
|
MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp
|
||||||
|
|
||||||
|
MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp
|
||||||
|
|
||||||
MobileGL/MG_Impl/GLXImpl/Exporting/Definitions.cpp
|
MobileGL/MG_Impl/GLXImpl/Exporting/Definitions.cpp
|
||||||
MobileGL/MG_Impl/GLXImpl/LookUp/LookUp.cpp
|
MobileGL/MG_Impl/GLXImpl/LookUp/LookUp.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -192,19 +192,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
MG_External::GLES::glClearDepthf(MG_State::pGLContext->GetClearDepth());
|
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
|
{ // Cull face mode
|
||||||
CullFaceMode cfm = MG_State::pGLContext->GetCullFaceMode();
|
CullFaceMode cfm = MG_State::pGLContext->GetCullFaceMode();
|
||||||
MG_External::GLES::glCullFace(MG_Util::ConvertCullFaceModeToGLEnum(cfm));
|
MG_External::GLES::glCullFace(MG_Util::ConvertCullFaceModeToGLEnum(cfm));
|
||||||
|
|||||||
@@ -6,10 +6,11 @@
|
|||||||
#include <MG_State/GLState/Core.h>
|
#include <MG_State/GLState/Core.h>
|
||||||
#include <MG_Util/Metrics/TextureMetrics.h>
|
#include <MG_Util/Metrics/TextureMetrics.h>
|
||||||
#include <MG_State/GLState/ErrorState/Error.h>
|
#include <MG_State/GLState/ErrorState/Error.h>
|
||||||
|
#include <MG_Util/Texture/PixelStoreProcessor.h>
|
||||||
|
#include <MG_Util/Converters/MGToMG/TextureEnumConverter.h>
|
||||||
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
|
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
|
||||||
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
|
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
|
||||||
#include <MG_Util/Converters/MGToStr/TextureEnumConverter.h>
|
#include <MG_Util/Converters/MGToStr/TextureEnumConverter.h>
|
||||||
#include <MG_Util/Converters/MGToMG/TextureEnumConverter.h>
|
|
||||||
|
|
||||||
namespace MobileGL {
|
namespace MobileGL {
|
||||||
namespace MG_Impl::GLImpl {
|
namespace MG_Impl::GLImpl {
|
||||||
@@ -39,6 +40,7 @@ namespace MobileGL {
|
|||||||
texturePixelDataType))
|
texturePixelDataType))
|
||||||
return;
|
return;
|
||||||
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadingTarget, level)) 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
|
// 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.
|
// 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;
|
if (!TextureImpl::ValidateTextureSubImageOffsets(textureObject, xoffset, width, yoffset, height)) return;
|
||||||
|
|
||||||
// ======================= Processing ================================
|
// ======================= Processing ================================
|
||||||
// SizeT imageSize =
|
|
||||||
// MG_Util::CalculateInputTextureImageSize(textureInternalFormat,
|
|
||||||
// texturePixelDataType,
|
|
||||||
// {width, height, 1});
|
|
||||||
auto& mipmap = textureObject->GetMipmap(level);
|
auto& mipmap = textureObject->GetMipmap(level);
|
||||||
Vector<Uint8>& data = mipmap.data;
|
Vector<Uint8>& data = mipmap.data;
|
||||||
|
|
||||||
SizeT bytesPerPixel = MG_Util::GetInputBytesPerPixel(textureInternalFormat, texturePixelDataType);
|
SizeT imageSize = 0;
|
||||||
// SizeT rowStride = mipmap.size.x() * bytesPerPixel;
|
const SizeT bytesPerPixel = MG_Util::GetInputBytesPerPixel(textureInternalFormat, texturePixelDataType);
|
||||||
|
|
||||||
const Uint8* srcData = reinterpret_cast<const Uint8*>(pixels);
|
void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack(
|
||||||
if (!srcData) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pixels != nullptr) {
|
const SizeT srcRowSize = width * bytesPerPixel;
|
||||||
auto rowsToSkip = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackSkipRows);
|
const SizeT destRowSize = mipmap.size.x() * bytesPerPixel;
|
||||||
auto pixelsToSkip = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackSkipPixels);
|
|
||||||
|
|
||||||
srcData += (rowsToSkip * width + pixelsToSkip);
|
if (xoffset + width > static_cast<GLsizei>(mipmap.size.x()) ||
|
||||||
|
yoffset + height > static_cast<GLsizei>(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) {
|
const auto* srcData = static_cast<const Uint8*>(processedPixels);
|
||||||
SizeT dstOffset = ((yoffset + row) * mipmap.size.x() + xoffset) * bytesPerPixel;
|
Uint8* destData = data.data();
|
||||||
// Should take states from glPixelStorei/glPixelStoref into account
|
if (!data.size()) {
|
||||||
SizeT srcOffset = row * width * bytesPerPixel;
|
SizeT totalSize = mipmap.size.x() * mipmap.size.y() * bytesPerPixel;
|
||||||
|
data.resize(totalSize);
|
||||||
if (dstOffset + width * bytesPerPixel <= data.size()) {
|
|
||||||
Memcpy(data.data() + dstOffset, srcData + srcOffset, width * bytesPerPixel);
|
|
||||||
} else {
|
|
||||||
MG_State::pGLContext->RecordError(
|
|
||||||
ErrorCode::InvalidOperation,
|
|
||||||
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
||||||
"Copy failed. Dest image does not have sufficient space."));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
mipmap.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +295,20 @@ namespace MobileGL {
|
|||||||
|
|
||||||
void TexImage2D_State(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height,
|
void TexImage2D_State(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height,
|
||||||
GLint border, GLenum format, GLenum type, const void* pixels) {
|
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 ================================
|
// ======================= Converting ================================
|
||||||
TextureUploadTarget textureUploadingTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
TextureUploadTarget textureUploadingTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||||
TextureTarget textureTarget = MG_Util::ConvertTextureUploadTargetToTextureTarget(textureUploadingTarget);
|
TextureTarget textureTarget = MG_Util::ConvertTextureUploadTargetToTextureTarget(textureUploadingTarget);
|
||||||
@@ -332,23 +356,50 @@ namespace MobileGL {
|
|||||||
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
||||||
|
|
||||||
// ======================= Processing ================================
|
// ======================= Processing ================================
|
||||||
SizeT imageSize = isProxy ? 0
|
|
||||||
: MG_Util::CalculateInputTextureImageSize(
|
|
||||||
textureInternalFormat, texturePixelDataType, {width, height, 1});
|
|
||||||
|
|
||||||
Uint8* skippedPixels = (Uint8*)pixels;
|
SizeT imageSize = 0;
|
||||||
if (pixels != nullptr) {
|
void* processedPixels = nullptr;
|
||||||
auto rowsToSkip = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackSkipRows);
|
const SizeT bytesPerPixel = MG_Util::GetInputBytesPerPixel(textureInternalFormat, texturePixelDataType);
|
||||||
auto pixelsToSkip = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackSkipPixels);
|
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);
|
textureObject->SetInternalFormat(textureInternalFormat);
|
||||||
MG_State::GLState::MipmapLevelInput mipmap = MG_State::GLState::MipmapLevelInput(
|
|
||||||
{width, height, 1}, level, false, 0,
|
|
||||||
{const_cast<void*>(isProxy ? (void*)skippedPixels : nullptr), imageSize});
|
|
||||||
textureObject->SetMipmapLevel(mipmap);
|
textureObject->SetMipmapLevel(mipmap);
|
||||||
|
if (mipmap.inputData.data) free(mipmap.inputData.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TexImage1D_State(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border,
|
void TexImage1D_State(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border,
|
||||||
|
|||||||
@@ -285,6 +285,10 @@ namespace MobileGL {
|
|||||||
return m_renderState.GetPixelStoreParam(param);
|
return m_renderState.GetPixelStoreParam(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PixelStoreParameters GLContext::GetPixelStoreParameters(Bool isUnpack) const {
|
||||||
|
return m_renderState.GetPixelStoreParameters(isUnpack);
|
||||||
|
}
|
||||||
|
|
||||||
void GLContext::SetCullFaceMode(CullFaceMode mode) {
|
void GLContext::SetCullFaceMode(CullFaceMode mode) {
|
||||||
m_renderState.SetCullFaceMode(mode);
|
m_renderState.SetCullFaceMode(mode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ namespace MobileGL {
|
|||||||
Float GetClearDepth() const;
|
Float GetClearDepth() const;
|
||||||
void SetPixelStoreParam(PixelStoreParam param, Int value);
|
void SetPixelStoreParam(PixelStoreParam param, Int value);
|
||||||
Int GetPixelStoreParam(PixelStoreParam param) const;
|
Int GetPixelStoreParam(PixelStoreParam param) const;
|
||||||
|
PixelStoreParameters GetPixelStoreParameters(Bool isUnpack) const;
|
||||||
void SetCullFaceMode(CullFaceMode mode);
|
void SetCullFaceMode(CullFaceMode mode);
|
||||||
CullFaceMode GetCullFaceMode() const;
|
CullFaceMode GetCullFaceMode() const;
|
||||||
void SetScissorBox(IntVec4 box); // x, y, width, height
|
void SetScissorBox(IntVec4 box); // x, y, width, height
|
||||||
|
|||||||
@@ -3,20 +3,7 @@
|
|||||||
namespace MobileGL {
|
namespace MobileGL {
|
||||||
namespace MG_State {
|
namespace MG_State {
|
||||||
namespace GLState {
|
namespace GLState {
|
||||||
RenderState::RenderState() {
|
RenderState::RenderState() {}
|
||||||
for (int i = 0; i < static_cast<int>(PixelStoreParam::PixelStoreParamCount); ++i) {
|
|
||||||
switch (static_cast<PixelStoreParam>(i)) {
|
|
||||||
case PixelStoreParam::PackAlignment:
|
|
||||||
case PixelStoreParam::UnpackAlignment:
|
|
||||||
m_pixelStoreParams[i] = 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
m_pixelStoreParams[i] = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------- Rasterization --------------------
|
// -------------------- Rasterization --------------------
|
||||||
void RenderState::SetViewport(IntVec4 viewport) {
|
void RenderState::SetViewport(IntVec4 viewport) {
|
||||||
@@ -124,11 +111,107 @@ namespace MobileGL {
|
|||||||
|
|
||||||
// -------------------- Pixel Store --------------------
|
// -------------------- Pixel Store --------------------
|
||||||
void RenderState::SetPixelStoreParam(PixelStoreParam param, Int value) {
|
void RenderState::SetPixelStoreParam(PixelStoreParam param, Int value) {
|
||||||
m_pixelStoreParams[static_cast<SizeT>(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<int>(param));
|
||||||
|
assert(false && "Invalid PixelStoreParam enum");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Int RenderState::GetPixelStoreParam(PixelStoreParam param) const {
|
Int RenderState::GetPixelStoreParam(PixelStoreParam param) const {
|
||||||
return m_pixelStoreParams[static_cast<SizeT>(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<int>(param));
|
||||||
|
assert(false && "Invalid PixelStoreParam enum");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PixelStoreParameters RenderState::GetPixelStoreParameters(Bool isUnpack) const {
|
||||||
|
return isUnpack ? m_unpackParameters : m_packParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------- Cull Face --------------------
|
// -------------------- Cull Face --------------------
|
||||||
|
|||||||
@@ -109,6 +109,17 @@ namespace MobileGL {
|
|||||||
Unknown = -1
|
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 MG_State {
|
||||||
namespace GLState {
|
namespace GLState {
|
||||||
class RenderState {
|
class RenderState {
|
||||||
@@ -147,6 +158,7 @@ namespace MobileGL {
|
|||||||
// Pixel Store
|
// Pixel Store
|
||||||
void SetPixelStoreParam(PixelStoreParam param, Int value);
|
void SetPixelStoreParam(PixelStoreParam param, Int value);
|
||||||
Int GetPixelStoreParam(PixelStoreParam param) const;
|
Int GetPixelStoreParam(PixelStoreParam param) const;
|
||||||
|
PixelStoreParameters GetPixelStoreParameters(Bool isUnpack) const;
|
||||||
|
|
||||||
// Cull Face
|
// Cull Face
|
||||||
void SetCullFaceMode(CullFaceMode mode);
|
void SetCullFaceMode(CullFaceMode mode);
|
||||||
@@ -180,7 +192,8 @@ namespace MobileGL {
|
|||||||
Float m_clearDepth = 1.0f;
|
Float m_clearDepth = 1.0f;
|
||||||
|
|
||||||
// Pixel Store
|
// Pixel Store
|
||||||
Array<Int, static_cast<SizeT>(PixelStoreParam::PixelStoreParamCount)> m_pixelStoreParams;
|
PixelStoreParameters m_packParameters;
|
||||||
|
PixelStoreParameters m_unpackParameters;
|
||||||
|
|
||||||
// Cull Face
|
// Cull Face
|
||||||
Bool m_cullFaceEnabled = false;
|
Bool m_cullFaceEnabled = false;
|
||||||
|
|||||||
@@ -58,8 +58,10 @@ namespace MobileGL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MipmapLevelInternal& TextureObjectBase::GetMipmap(Int index) {
|
MipmapLevelInternal& TextureObjectBase::GetMipmap(Int index) {
|
||||||
if (index > m_mipmaps.size()) {
|
if (index >= m_mipmaps.size()) {
|
||||||
// fallback to the last mipmap
|
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<Int>(m_mipmaps.size() - 1);
|
index = static_cast<Int>(m_mipmaps.size() - 1);
|
||||||
}
|
}
|
||||||
return m_mipmaps[index];
|
return m_mipmaps[index];
|
||||||
@@ -145,7 +147,7 @@ namespace MobileGL {
|
|||||||
|
|
||||||
void TextureObject1D::SetMipmapImpl(const MipmapLevelInput& level) {
|
void TextureObject1D::SetMipmapImpl(const MipmapLevelInput& level) {
|
||||||
if (level.size.x() > 0) {
|
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) {
|
void TextureObject2D::SetMipmapImpl(const MipmapLevelInput& level) {
|
||||||
if (level.size.x() > 0 && level.size.y() > 0) {
|
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) {
|
void TextureObject3D::SetMipmapImpl(const MipmapLevelInput& level) {
|
||||||
if (level.size.x() > 0 && level.size.y() > 0 && level.size.z() > 0) {
|
if (level.size.x() > 0 && level.size.y() > 0 && level.size.z() > 0) {
|
||||||
m_mipmaps.push_back(level);
|
m_mipmaps.push_back(MipmapLevelInternal(level));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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<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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<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;
|
||||||
|
|
||||||
|
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) * inputStride;
|
||||||
|
src += static_cast<SizeT>(startY) * inputStride;
|
||||||
|
src += static_cast<SizeT>(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<SizeT>(copyWidth) * pixelSize);
|
||||||
|
|
||||||
|
if (params.SwapBytes && pixelSize > 1) {
|
||||||
|
SwapBytes(layerDst, pixelSize, static_cast<SizeT>(copyWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.LSBFirst && isBitmap) {
|
||||||
|
ProcessLSBFirst(layerDst, static_cast<SizeT>(copyWidth), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
layerSrc += inputStride;
|
||||||
|
layerDst += static_cast<SizeT>(copyWidth) * pixelSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
src += static_cast<SizeT>(effectiveHeight) * inputStride;
|
||||||
|
dst += static_cast<SizeT>(copyHeight) * static_cast<SizeT>(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<SizeT>(width) * pixelSize;
|
||||||
|
|
||||||
|
const Int effectiveHeight = (params.ImageHeight > 0) ? params.ImageHeight : height;
|
||||||
|
|
||||||
|
outSize = static_cast<SizeT>(outputStride) * 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) * outputStride;
|
||||||
|
dst += static_cast<SizeT>(params.SkipRows) * outputStride;
|
||||||
|
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 += inputStride;
|
||||||
|
layerDst += outputStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
src += static_cast<SizeT>(height) * inputStride;
|
||||||
|
dst += static_cast<SizeT>(effectiveHeight) * outputStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputPixels;
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Util::PixelStoreProcessor
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
#include <MG_State/GLState/RenderState/RenderState.h>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -59,6 +59,9 @@ namespace MobileGL {
|
|||||||
inline void Memcpy(void* dest, const void* src, SizeT size) {
|
inline void Memcpy(void* dest, const void* src, SizeT size) {
|
||||||
std::memcpy(dest, src, size);
|
std::memcpy(dest, src, size);
|
||||||
}
|
}
|
||||||
|
inline void Memset(void* dest, Int value, SizeT size) {
|
||||||
|
std::memset(dest, value, size);
|
||||||
|
}
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
constexpr auto ToArray(Ts&&... elems) {
|
constexpr auto ToArray(Ts&&... elems) {
|
||||||
using E = std::common_type_t<Ts...>;
|
using E = std::common_type_t<Ts...>;
|
||||||
|
|||||||
Reference in New Issue
Block a user