1 //
2 // Copyright 2022 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // storeimage_paletted.cpp: Encodes GL_PALETTE_* textures.
8
9 #include <unordered_map>
10
11 #include "image_util/storeimage.h"
12
13 #include <type_traits>
14 #include "common/mathutil.h"
15
16 #include "image_util/imageformats.h"
17
18 namespace angle
19 {
20
21 namespace
22 {
23
24 template <typename T>
OffsetDataPointer(uint8_t * data,size_t y,size_t z,size_t rowPitch,size_t depthPitch)25 inline T *OffsetDataPointer(uint8_t *data, size_t y, size_t z, size_t rowPitch, size_t depthPitch)
26 {
27 return reinterpret_cast<T *>(data + (y * rowPitch) + (z * depthPitch));
28 }
29
30 template <typename T>
OffsetDataPointer(const uint8_t * data,size_t y,size_t z,size_t rowPitch,size_t depthPitch)31 inline const T *OffsetDataPointer(const uint8_t *data,
32 size_t y,
33 size_t z,
34 size_t rowPitch,
35 size_t depthPitch)
36 {
37 return reinterpret_cast<const T *>(data + (y * rowPitch) + (z * depthPitch));
38 }
39
EncodeColor(R8G8B8A8 rgba,uint32_t redBlueBits,uint32_t greenBits,uint32_t alphaBits,void * dst)40 void EncodeColor(R8G8B8A8 rgba,
41 uint32_t redBlueBits,
42 uint32_t greenBits,
43 uint32_t alphaBits,
44 void *dst)
45 {
46 gl::ColorF color;
47 R8G8B8A8::readColor(&color, &rgba);
48
49 switch (redBlueBits)
50 {
51 case 8:
52 ASSERT(greenBits == 8);
53 switch (alphaBits)
54 {
55 case 0:
56 return R8G8B8::writeColor(reinterpret_cast<R8G8B8 *>(dst), &color);
57 case 8:
58 return R8G8B8A8::writeColor(reinterpret_cast<R8G8B8A8 *>(dst), &color);
59 default:
60 UNREACHABLE();
61 break;
62 }
63 break;
64
65 case 5:
66 switch (greenBits)
67 {
68 case 6:
69 ASSERT(alphaBits == 0);
70 return R5G6B5::writeColor(reinterpret_cast<R5G6B5 *>(dst), &color);
71 case 5:
72 ASSERT(alphaBits == 1);
73 return R5G5B5A1::writeColor(reinterpret_cast<R5G5B5A1 *>(dst), &color);
74 default:
75 UNREACHABLE();
76 break;
77 }
78 break;
79
80 case 4:
81 ASSERT(greenBits == 4 && alphaBits == 4);
82 return R4G4B4A4::writeColor(reinterpret_cast<R4G4B4A4 *>(dst), &color);
83
84 default:
85 UNREACHABLE();
86 break;
87 }
88 }
89
R8G8B8A8Key(R8G8B8A8 rgba)90 uint32_t R8G8B8A8Key(R8G8B8A8 rgba)
91 {
92 uint32_t key;
93 static_assert(sizeof(key) == sizeof(rgba));
94 memcpy(&key, &rgba, sizeof(key));
95 return key;
96 }
97
98 } // namespace
99
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)100 void StoreRGBA8ToPalettedImpl(size_t width,
101 size_t height,
102 size_t depth,
103 uint32_t indexBits,
104 uint32_t redBlueBits,
105 uint32_t greenBits,
106 uint32_t alphaBits,
107 const uint8_t *input,
108 size_t inputRowPitch,
109 size_t inputDepthPitch,
110 uint8_t *output,
111 size_t outputRowPitch,
112 size_t outputDepthPitch)
113 {
114 std::unordered_map<uint32_t, size_t> invPalette;
115
116 ASSERT((redBlueBits + greenBits + redBlueBits + alphaBits) % 8 == 0);
117
118 size_t colorBytes = (redBlueBits + greenBits + redBlueBits + alphaBits) / 8;
119 size_t paletteSize = 1 << indexBits;
120 size_t paletteBytes = paletteSize * colorBytes;
121
122 uint8_t *palette = output;
123
124 // We might not fill-out the entire palette.
125 memset(palette, 0xab, paletteBytes);
126
127 uint8_t *texels = output + paletteBytes; // + TODO(http://anglebug.com/42266155): mip levels
128
129 for (size_t z = 0; z < depth; z++)
130 {
131 for (size_t y = 0; y < height; y++)
132 {
133 const R8G8B8A8 *srcRow =
134 OffsetDataPointer<R8G8B8A8>(input, y, z, inputRowPitch, inputDepthPitch);
135 uint8_t *dstRow =
136 OffsetDataPointer<uint8_t>(texels, y, z, outputRowPitch, outputDepthPitch);
137
138 for (size_t x = 0; x < width; x++)
139 {
140 auto inversePaletteEntry = invPalette.insert(
141 std::pair<uint32_t, size_t>(R8G8B8A8Key(srcRow[x]), invPalette.size()));
142 size_t paletteIndex = inversePaletteEntry.first->second;
143 ASSERT(paletteIndex < paletteSize);
144 if (inversePaletteEntry.second)
145 {
146 EncodeColor(srcRow[x], redBlueBits, greenBits, alphaBits,
147 palette + paletteIndex * colorBytes);
148 }
149
150 switch (indexBits)
151 {
152 case 4:
153 // On even xses, initialize the location and store the high
154 // bits, on odd (which always follows even) store the low
155 // bits.
156 if (x % 2 == 0)
157 dstRow[x / 2] = static_cast<uint8_t>(paletteIndex) << 4;
158 else
159 dstRow[x / 2] |= static_cast<uint8_t>(paletteIndex);
160 break;
161
162 case 8:
163 dstRow[x] = static_cast<uint8_t>(paletteIndex);
164 break;
165
166 default:
167 UNREACHABLE();
168 }
169 }
170 }
171 }
172 }
173
174 } // namespace angle
175