blob: 6bed8f99c0aa7fccf2d1d147b9a5ffc6dc59316e [file] [edit]
//
// Copyright 2022 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// storeimage_paletted.cpp: Encodes GL_PALETTE_* textures.
#include <unordered_map>
#include "common/unsafe_buffers.h"
#include "image_util/storeimage.h"
#include <type_traits>
#include "common/mathutil.h"
#include "image_util/imageformats.h"
namespace angle
{
namespace
{
template <typename T>
inline T *OffsetDataPointer(uint8_t *data, size_t y, size_t z, size_t rowPitch, size_t depthPitch)
{
return reinterpret_cast<T *>(ANGLE_UNSAFE_TODO(data + (y * rowPitch) + (z * depthPitch)));
}
template <typename T>
inline const T *OffsetDataPointer(const uint8_t *data,
size_t y,
size_t z,
size_t rowPitch,
size_t depthPitch)
{
return reinterpret_cast<const T *>(ANGLE_UNSAFE_TODO(data + (y * rowPitch) + (z * depthPitch)));
}
void EncodeColor(R8G8B8A8 rgba,
uint32_t redBlueBits,
uint32_t greenBits,
uint32_t alphaBits,
void *dst)
{
gl::ColorF color;
R8G8B8A8::readColor(&color, &rgba);
switch (redBlueBits)
{
case 8:
ASSERT(greenBits == 8);
switch (alphaBits)
{
case 0:
return R8G8B8::writeColor(reinterpret_cast<R8G8B8 *>(dst), &color);
case 8:
return R8G8B8A8::writeColor(reinterpret_cast<R8G8B8A8 *>(dst), &color);
default:
UNREACHABLE();
break;
}
break;
case 5:
switch (greenBits)
{
case 6:
ASSERT(alphaBits == 0);
return R5G6B5::writeColor(reinterpret_cast<R5G6B5 *>(dst), &color);
case 5:
ASSERT(alphaBits == 1);
return R5G5B5A1::writeColor(reinterpret_cast<R5G5B5A1 *>(dst), &color);
default:
UNREACHABLE();
break;
}
break;
case 4:
ASSERT(greenBits == 4 && alphaBits == 4);
return R4G4B4A4::writeColor(reinterpret_cast<R4G4B4A4 *>(dst), &color);
default:
UNREACHABLE();
break;
}
}
uint32_t R8G8B8A8Key(R8G8B8A8 rgba)
{
uint32_t key;
static_assert(sizeof(key) == sizeof(rgba));
ANGLE_UNSAFE_TODO(memcpy(&key, &rgba, sizeof(key)));
return key;
}
} // namespace
void StoreRGBA8ToPalettedImpl(size_t width,
size_t height,
size_t depth,
uint32_t indexBits,
uint32_t redBlueBits,
uint32_t greenBits,
uint32_t alphaBits,
const uint8_t *input,
size_t inputRowPitch,
size_t inputDepthPitch,
uint8_t *output,
size_t outputRowPitch,
size_t outputDepthPitch)
{
std::unordered_map<uint32_t, size_t> invPalette;
ASSERT((redBlueBits + greenBits + redBlueBits + alphaBits) % 8 == 0);
size_t colorBytes = (redBlueBits + greenBits + redBlueBits + alphaBits) / 8;
size_t paletteSize = 1 << indexBits;
size_t paletteBytes = paletteSize * colorBytes;
uint8_t *palette = output;
// We might not fill-out the entire palette.
ANGLE_UNSAFE_TODO(memset(palette, 0xab, paletteBytes));
uint8_t *texels = ANGLE_UNSAFE_TODO(
output + paletteBytes); // + TODO(http://anglebug.com/42266155): mip levels
for (size_t z = 0; z < depth; z++)
{
for (size_t y = 0; y < height; y++)
{
const R8G8B8A8 *srcRow =
OffsetDataPointer<R8G8B8A8>(input, y, z, inputRowPitch, inputDepthPitch);
uint8_t *dstRow =
OffsetDataPointer<uint8_t>(texels, y, z, outputRowPitch, outputDepthPitch);
for (size_t x = 0; x < width; x++)
{
auto inversePaletteEntry = invPalette.insert(std::pair<uint32_t, size_t>(
R8G8B8A8Key(ANGLE_UNSAFE_TODO(srcRow[x])), invPalette.size()));
size_t paletteIndex = inversePaletteEntry.first->second;
ASSERT(paletteIndex < paletteSize);
if (inversePaletteEntry.second)
{
ANGLE_UNSAFE_TODO({
EncodeColor(srcRow[x], redBlueBits, greenBits, alphaBits,
palette + paletteIndex * colorBytes);
})
}
switch (indexBits)
{
case 4:
// On even xses, initialize the location and store the high
// bits, on odd (which always follows even) store the low
// bits.
if (x % 2 == 0)
ANGLE_UNSAFE_TODO(dstRow[x / 2]) = static_cast<uint8_t>(paletteIndex)
<< 4;
else
ANGLE_UNSAFE_TODO(dstRow[x / 2]) |= static_cast<uint8_t>(paletteIndex);
break;
case 8:
ANGLE_UNSAFE_TODO(dstRow[x]) = static_cast<uint8_t>(paletteIndex);
break;
default:
UNREACHABLE();
}
}
}
}
}
} // namespace angle