Skip to content

Commit

Permalink
BMPDecoder の全ビット深度対応と、それにあたっての修正 (#1207)
Browse files Browse the repository at this point in the history
  • Loading branch information
Raclamusi authored Feb 24, 2024
1 parent 66cd64b commit a5a6d7b
Showing 1 changed file with 191 additions and 46 deletions.
237 changes: 191 additions & 46 deletions Siv3D/src/Siv3D/ImageFormat/BMP/BMPDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ namespace s3d
}

const Size size(header.biWidth, std::abs(header.biHeight));

ImagePixelFormat pixelFormat = ImagePixelFormat::R8G8B8;

if (header.biBitCount == 32)
Expand All @@ -77,7 +77,7 @@ namespace s3d
LOG_SCOPED_TRACE(U"BMPDecoder::decode()");

BMPHeader header;

if (not reader.read(header))
{
LOG_FAIL(U"❌ BMPDecoder::decode(): Failed to read header");
Expand Down Expand Up @@ -108,91 +108,236 @@ namespace s3d
return{};
}

const uint32 paletteSize = ((header.biBitCount > 8) ? 0 : (header.biClrUsed == 0) ? (1 << header.biBitCount) : header.biClrUsed);
Array<uint8> paletteOwner(paletteSize * 4);
const auto palette = paletteOwner.data();

if (paletteSize)
{
reader.read(palette, paletteOwner.size());
}

if (header.bfOffBits > (sizeof(header) + paletteOwner.size()))
{
reader.setPos(header.bfOffBits);
}

Image image(width, height);

LOG_VERBOSE(U"BMPHeader::biBitCount: {}"_fmt(header.biBitCount));

switch (const int32 depth = header.biBitCount)
{
case 8:
case 1:
{
uint8 palette[1024];
reader.read(palette);

const uint32 rowSize = width + (width % 4 ? 4 - width % 4 : 0);
const uint32 rowSize = ((width + 31) / 32 * 4);
const int32 lineStep = reverse ? -width : width;
Color* pDstLine = image[reverse ? height - 1 : 0];

if (uint8* const buffer = static_cast<uint8*>(std::malloc(rowSize * 4)))

Array<uint8> bufferOwner(rowSize * 4);
const auto buffer = bufferOwner.data();

for (int32 y = 0; y < height; ++y)
{
for (int32 y = 0; y < height; ++y)
if (height - y < 4)
{
if (height - y < 4)
{
reader.read(buffer, rowSize * (height - y));
}
else if (y % 4 == 0)
{
reader.read(buffer, rowSize * 4);
}
reader.read(buffer, rowSize * (height - y));
}
else if (y % 4 == 0)
{
reader.read(buffer, rowSize * 4);
}

uint8* tmp = &buffer[rowSize * (y % 4)];
const Color* const pDstEnd = pDstLine + width;
uint8* tmp = &buffer[rowSize * (y % 4)];
Color* pDst = pDstLine;

for (Color* pDst = pDstLine; pDst != pDstEnd; ++pDst)
for (int32 x = 0; x < width; x += 8)
{
const int32 n = Min(8, (width - x));

for (int32 i = 0; i < n; ++i)
{
const uint8* src = palette + (static_cast<size_t>(*tmp++) << 2);
const size_t index = ((*tmp >> (7 - i)) & 1);
const uint8* src = (palette + (index << 2));

pDst->set(src[2], src[1], src[0]);
pDst++->set(src[2], src[1], src[0]);
}

pDstLine += lineStep;
++tmp;
}

std::free(buffer);
pDstLine += lineStep;
}

break;
}
case 4:
{
const uint32 rowSize = ((width + 7) / 8 * 4);
const int32 lineStep = reverse ? -width : width;
Color* pDstLine = image[reverse ? height - 1 : 0];

Array<uint8> bufferOwner(rowSize * 4);
const auto buffer = bufferOwner.data();

for (int32 y = 0; y < height; ++y)
{
if (height - y < 4)
{
reader.read(buffer, rowSize * (height - y));
}
else if (y % 4 == 0)
{
reader.read(buffer, rowSize * 4);
}

uint8* tmp = &buffer[rowSize * (y % 4)];
Color* pDst = pDstLine;
const int32 w = (width - 1);

for (int32 x = 0; x < w; x += 2)
{
const size_t index1 = ((*tmp >> 4) & 0x0f);
const size_t index2 = (*tmp & 0x0f);
const uint8* src1 = (palette + (index1 << 2));
const uint8* src2 = (palette + (index2 << 2));

pDst++->set(src1[2], src1[1], src1[0]);
pDst++->set(src2[2], src2[1], src2[0]);

++tmp;
}

if (width & 1)
{
const size_t index = ((*tmp >> 4) & 0x0f);
const uint8* src = (palette + (index << 2));

pDst++->set(src[2], src[1], src[0]);
}

pDstLine += lineStep;
}

break;
}
case 8:
{
const uint32 rowSize = width + (width % 4 ? 4 - width % 4 : 0);
const int32 lineStep = reverse ? -width : width;
Color* pDstLine = image[reverse ? height - 1 : 0];

Array<uint8> bufferOwner(rowSize * 4);
const auto buffer = bufferOwner.data();

for (int32 y = 0; y < height; ++y)
{
if (height - y < 4)
{
reader.read(buffer, rowSize * (height - y));
}
else if (y % 4 == 0)
{
reader.read(buffer, rowSize * 4);
}

uint8* tmp = &buffer[rowSize * (y % 4)];
const Color* const pDstEnd = pDstLine + width;

for (Color* pDst = pDstLine; pDst != pDstEnd; ++pDst)
{
const uint8* src = palette + (static_cast<size_t>(*tmp++) << 2);

pDst->set(src[2], src[1], src[0]);
}

pDstLine += lineStep;
}

break;
}
case 16:
{
const size_t rowSize16 = ((width + 1) / 2 * 2);
const size_t rowSize = (rowSize16 * 2);
const int32 lineStep = reverse ? -width : width;
Color* pDstLine = image[reverse ? height - 1 : 0];

Array<uint16> bufferOwner(rowSize16 * 4);
const auto buffer = bufferOwner.data();

for (int32 y = 0; y < height; ++y)
{
if (height - y < 4)
{
reader.read(buffer, rowSize * (height - y));
}
else if (y % 4 == 0)
{
reader.read(buffer, rowSize * 4);
}

const Color* const pDstEnd = pDstLine + width;
uint16* pSrc = &buffer[rowSize16 * (y % 4)];

for (Color* pDst = pDstLine; pDst != pDstEnd; ++pDst)
{
uint32 b = ((*pSrc & 0x001f) << 3);
uint32 g = ((*pSrc & 0x07e0) >> 2);
uint32 r = ((*pSrc & 0xf800) >> 7);

pDst->set(r, g, b);

++pSrc;
}

pDstLine += lineStep;
}

break;
}
case 24:
case 32:
{
const size_t rowSize = depth == 24 ? width * 3 + width % 4 : width * 4;
const size_t rowSize = depth == 24 ? width * 3 + width % 4 : width * 4;
const int32 depthBytes = depth / 8;
const int32 lineStep = reverse ? -width : width;
Color* pDstLine = image[reverse ? height - 1 : 0];

if (uint8 * const buffer = static_cast<uint8*>(std::malloc(rowSize * 4)))
Array<uint8> bufferOwner(rowSize * 4);
const auto buffer = bufferOwner.data();

for (int32 y = 0; y < height; ++y)
{
for (int32 y = 0; y < height; ++y)
if (height - y < 4)
{
if (height - y < 4)
{
reader.read(buffer, rowSize * (height - y));
}
else if (y % 4 == 0)
{
reader.read(buffer, rowSize * 4);
}

const Color* const pDstEnd = pDstLine + width;
uint8* pSrc = &buffer[rowSize * (y % 4)];
reader.read(buffer, rowSize * (height - y));
}
else if (y % 4 == 0)
{
reader.read(buffer, rowSize * 4);
}

for (Color* pDst = pDstLine; pDst != pDstEnd; ++pDst)
{
pDst->set(pSrc[2], pSrc[1], pSrc[0]);
const Color* const pDstEnd = pDstLine + width;
uint8* pSrc = &buffer[rowSize * (y % 4)];

pSrc += depthBytes;
}
for (Color* pDst = pDstLine; pDst != pDstEnd; ++pDst)
{
pDst->set(pSrc[2], pSrc[1], pSrc[0]);

pDstLine += lineStep;
pSrc += depthBytes;
}

std::free(buffer);
pDstLine += lineStep;
}

break;
}
default:
{
LOG_FAIL(U"❌ BMPDecoder::decode(): BMPHeader::biBitCount is invalid");
return{};
}
}

LOG_VERBOSE(U"Image ({}x{}) decoded"_fmt(
Expand Down

0 comments on commit a5a6d7b

Please sign in to comment.