21 bool Palette::decode(
const char* filename)
23 FileStream str{filename};
27 bool Palette::decode(IStream* file)
29 if (file && file->good()) {
30 for (
size_t i = 0; i < colorCount; i++)
33 colors[i].b =
static_cast<uint8_t
>(file->getc());
34 colors[i].g =
static_cast<uint8_t
>(file->getc());
35 colors[i].r =
static_cast<uint8_t
>(file->getc());
36 colors[i]._padding = 0;
43 uint8_t Palette::GetClosestColorIndex(Palette::Color color)
45 uint32_t closestColorDistance = std::numeric_limits<uint32_t>::max();
46 size_t closestColorIndex = 0;
47 size_t currentIndex = 0;
48 for (Color palColor : colors)
50 const int diffRed = palColor.r - color.r;
51 const int diffGreen = palColor.g - color.g;
52 const int diffBlue = palColor.b - color.b;
53 const uint32_t distance =
54 uint32_t(diffRed * diffRed + diffGreen * diffGreen + diffBlue * diffBlue);
55 if (distance < closestColorDistance) {
56 closestColorDistance = distance;
57 closestColorIndex = currentIndex;
61 assert(closestColorIndex < 256);
62 return uint8_t(closestColorIndex);
75 ColorHSL ConvertRGBtoHSL(
const Palette::Color& rgb)
78 const double red = rgb.r / 255.0;
79 const double green = rgb.g / 255.0;
80 const double blue = rgb.b / 255.0;
81 const double minRGB = std::min<double>({red, green, blue});
82 const double maxRGB = std::max({red, green, blue});
83 const double minMaxSum = minRGB + maxRGB;
85 hsl.lum = minMaxSum / 2.0;
86 assert(hsl.lum >= 0.0 && hsl.lum <= 1.0);
88 if (minRGB == maxRGB) {
97 const double deltaMinMax = maxRGB - minRGB;
99 hsl.sat = deltaMinMax / (2.0 - minMaxSum);
103 hsl.sat = deltaMinMax / minMaxSum;
105 assert(hsl.sat >= 0.0 && hsl.sat <= 1.0);
108 hsl.hue = (green - blue) / deltaMinMax;
110 else if (maxRGB == green)
112 hsl.hue = (blue - red) / deltaMinMax + 2.0;
116 hsl.hue = (red - green) / deltaMinMax + 4.0;
119 if (hsl.hue < 0.0) hsl.hue += 360.0;
120 assert(hsl.hue >= 0 && hsl.hue < 360.0);
125 double hue2rgb(
double p,
double q,
double t)
127 if (t < 0.0) t += 360.0;
128 if (t > 360.0) t -= 360.0;
129 if (t < 60.0)
return (q - p) * t / 60.0 + p;
130 if (t < 180.0)
return q;
131 if (t < 240.0)
return (q - p) * (240.0 - t) / 60.0 + p;
135 Palette::Color ConvertHSLtoRGB(
const ColorHSL& hsl)
137 Palette::Color rgbColor;
138 if (hsl.sat == 0.0) {
139 rgbColor.r = rgbColor.g = rgbColor.b = uint8_t(hsl.lum * 255.0);
146 q = hsl.lum + hsl.sat - hsl.lum * hsl.sat;
148 q = (hsl.sat + 1.0) * hsl.lum;
149 const double p = 2.0 * hsl.lum - q;
151 rgbColor.r = uint8_t(hue2rgb(p, q, hsl.hue + 120.0) * 255.0);
152 rgbColor.g = uint8_t(hue2rgb(p, q, hsl.hue) * 255.0);
153 rgbColor.b = uint8_t(hue2rgb(p, q, hsl.hue - 120.0) * 255.0);
157 int GetAlphaBlendRatioFromLevel(
int alphaBlendLevel)
159 switch (alphaBlendLevel)
164 default: assert(
false);
return 0;
168 void PL2CreateLightLevelVariations(PL2& pl2)
171 for (
size_t variationIndex = 0; variationIndex < 32; variationIndex++)
173 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
175 const Palette::Color baseColor = pl2.basePalette.colors[colorIndex];
176 const Palette::Color lightColor{
177 uint8_t(((variationIndex + 1) * baseColor.r) >> 5),
178 uint8_t(((variationIndex + 1) * baseColor.g) >> 5),
179 uint8_t(((variationIndex + 1) * baseColor.b) >> 5),
181 pl2.lightLevelVariations[variationIndex].indices[colorIndex] =
182 pl2.basePalette.GetClosestColorIndex(lightColor);
187 void PL2CreateInvColorVariations(PL2& pl2)
190 for (
size_t variationIndex = 0; variationIndex < Palette::colorCount; variationIndex++)
192 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
194 const Palette::Color baseColor = pl2.basePalette.colors[colorIndex];
195 const Palette::Color lightColor{
196 uint8_t((((variationIndex + 1) * (255u - baseColor.r)) >> 4) + baseColor.r),
197 uint8_t((((variationIndex + 1) * (255u - baseColor.g)) >> 4) + baseColor.g),
198 uint8_t((((variationIndex + 1) * (255u - baseColor.b)) >> 4) + baseColor.b),
200 pl2.invColorVariations[variationIndex].indices[colorIndex] =
201 pl2.basePalette.GetClosestColorIndex(lightColor);
206 void PL2CreateSelectedUnitShift(PL2& pl2, ColorHSL hslColors[Palette::colorCount])
208 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
211 ColorHSL tmpColorHSL = hslColors[colorIndex];
212 if (tmpColorHSL.lum != 0.0) {
213 tmpColorHSL.lum = std::min(tmpColorHSL.lum + 0.2, 1.0);
216 pl2.selectedUnitShift.indices[colorIndex] =
217 pl2.basePalette.GetClosestColorIndex(ConvertHSLtoRGB(tmpColorHSL));
221 void PL2CreateAlphaBlend(PL2& pl2)
223 for (
int alphaBlendLevel = 0; alphaBlendLevel < 3; alphaBlendLevel++)
225 const int alphaBlendRatio = GetAlphaBlendRatioFromLevel(alphaBlendLevel);
226 for (
size_t dstColorIndex = 0; dstColorIndex < Palette::colorCount; ++dstColorIndex)
228 const Palette::Color dstColor = pl2.basePalette.colors[dstColorIndex];
229 for (
size_t srcColorIndex = 0; srcColorIndex < Palette::colorCount; ++srcColorIndex)
231 const Palette::Color srcColor = pl2.basePalette.colors[srcColorIndex];
232 const int invBlendRatio = 255 - alphaBlendRatio;
233 const Palette::Color blendOuput{
234 uint8_t((invBlendRatio * srcColor.r + alphaBlendRatio * dstColor.r) / 0xFF),
235 uint8_t((invBlendRatio * srcColor.g + alphaBlendRatio * dstColor.g) / 0xFF),
236 uint8_t((invBlendRatio * srcColor.b + alphaBlendRatio * dstColor.b) / 0xFF),
238 pl2.alphaBlend[alphaBlendLevel][srcColorIndex].indices[dstColorIndex] =
239 pl2.basePalette.GetClosestColorIndex(blendOuput);
245 void PL2CreateAdditiveBlend(PL2& pl2)
247 for (
size_t dstColorIndex = 0; dstColorIndex < Palette::colorCount; ++dstColorIndex)
249 const Palette::Color dstColor = pl2.basePalette.colors[dstColorIndex];
250 for (
size_t srcColorIndex = 0; srcColorIndex < Palette::colorCount; ++srcColorIndex)
252 const Palette::Color srcColor = pl2.basePalette.colors[srcColorIndex];
253 const Palette::Color blendOuput{
254 uint8_t(std::min(srcColor.r + dstColor.r, 0xFF)),
255 uint8_t(std::min(srcColor.g + dstColor.g, 0xFF)),
256 uint8_t(std::min(srcColor.b + dstColor.b, 0xFF)),
258 pl2.additiveBlend[srcColorIndex].indices[dstColorIndex] =
259 pl2.basePalette.GetClosestColorIndex(blendOuput);
264 void PL2CreateMultiplicativeBlend(PL2& pl2)
266 for (
size_t dstColorIndex = 0; dstColorIndex < Palette::colorCount; ++dstColorIndex)
268 const Palette::Color dstColor = pl2.basePalette.colors[dstColorIndex];
269 for (
size_t srcColorIndex = 0; srcColorIndex < Palette::colorCount; ++srcColorIndex)
271 const Palette::Color srcColor = pl2.basePalette.colors[srcColorIndex];
272 const Palette::Color blendOuput{
273 uint8_t((srcColor.r * dstColor.r) / 0xFF),
274 uint8_t((srcColor.g * dstColor.g) / 0xFF),
275 uint8_t((srcColor.b * dstColor.b) / 0xFF),
277 pl2.multiplicativeBlend[srcColorIndex].indices[dstColorIndex] =
278 pl2.basePalette.GetClosestColorIndex(blendOuput);
283 void PL2CreateColorshifts(PL2& pl2, ColorHSL hslColors[Palette::colorCount])
285 for (
int hueShiftIndex = 0; hueShiftIndex < 24; ++hueShiftIndex)
289 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
291 ColorHSL tmpColorHSL = hslColors[colorIndex];
292 tmpColorHSL.hue += (double)hueShiftIndex * 15.0;
293 if (tmpColorHSL.hue > 360.0)
294 tmpColorHSL.hue -= 360.0;
296 pl2.hueVariations[hueShiftIndex].indices[colorIndex] =
297 pl2.basePalette.GetClosestColorIndex(ConvertHSLtoRGB(tmpColorHSL));
300 for (
int hueShiftIndex = 0; hueShiftIndex < 24; ++hueShiftIndex)
302 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
305 ColorHSL tmpColorHSL = hslColors[colorIndex];
306 tmpColorHSL.hue += (double)hueShiftIndex * 15.0;
307 if (tmpColorHSL.hue > 360.0) tmpColorHSL.hue -= 360.0;
308 tmpColorHSL.sat = 0.5;
309 #ifdef USE_FIXED_VERSION
310 tmpColorHSL.lum -= 0.1;
312 tmpColorHSL.lum -= double(0.1f);
315 if (tmpColorHSL.lum < 0.0) tmpColorHSL.lum = 0.0;
317 pl2.hueVariations[24 + hueShiftIndex].indices[colorIndex] =
318 pl2.basePalette.GetClosestColorIndex(ConvertHSLtoRGB(tmpColorHSL));
321 for (
int hueShiftIndex = 0; hueShiftIndex < 24; ++hueShiftIndex)
323 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
326 ColorHSL tmpColorHSL = hslColors[colorIndex];
328 tmpColorHSL.hue += (double)hueShiftIndex * 15.0;
329 if (tmpColorHSL.hue > 360.0) tmpColorHSL.hue -= 360.0;
331 tmpColorHSL.sat = 0.5;
333 #ifdef USE_FIXED_VERSION
334 tmpColorHSL.lum += 0.2;
336 tmpColorHSL.lum += double(0.2f);
339 if (tmpColorHSL.lum > 1.0) {
340 tmpColorHSL.lum = 1.0;
343 pl2.hueVariations[48 + hueShiftIndex].indices[colorIndex] =
344 pl2.basePalette.GetClosestColorIndex(ConvertHSLtoRGB(tmpColorHSL));
348 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
350 ColorHSL tmpColorHSL = hslColors[colorIndex];
352 tmpColorHSL.lum = tmpColorHSL.lum / 2.0;
353 pl2.hueVariations[72].indices[colorIndex] =
354 pl2.basePalette.GetClosestColorIndex(ConvertHSLtoRGB(tmpColorHSL));
357 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
359 ColorHSL tmpColorHSL = hslColors[colorIndex];
362 #ifdef USE_FIXED_VERSION
363 tmpColorHSL.lum += 0.2;
364 tmpColorHSL.lum /= 1.2;
367 tmpColorHSL.lum += double(0.2f);
368 tmpColorHSL.lum /= double(1.2f);
370 pl2.hueVariations[73].indices[colorIndex] =
371 pl2.basePalette.GetClosestColorIndex(ConvertHSLtoRGB(tmpColorHSL));
374 for (
int hueShiftIndex = 0; hueShiftIndex < 24; ++hueShiftIndex)
376 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
378 ColorHSL tmpColorHSL = hslColors[colorIndex];
380 if (tmpColorHSL.hue > 45.0 && tmpColorHSL.hue < 315.0) {
382 tmpColorHSL.hue += (double)hueShiftIndex * 15.0;
383 if (tmpColorHSL.hue > 360.0) tmpColorHSL.hue -= 360.0;
385 pl2.hueVariations[74 + hueShiftIndex].indices[colorIndex] =
386 pl2.basePalette.GetClosestColorIndex(ConvertHSLtoRGB(tmpColorHSL));
390 #ifdef USE_FIXED_VERSION
391 pl2.hueVariations[74 + hueShiftIndex].indices[colorIndex] = colorIndex;
393 pl2.hueVariations[74 + hueShiftIndex].indices[colorIndex] =
394 pl2.hueVariations[73 + hueShiftIndex].indices[colorIndex];
400 for (
int hueShiftIndex = 0; hueShiftIndex < 12; ++hueShiftIndex)
402 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
405 ColorHSL tmpColorHSL = hslColors[colorIndex];
406 tmpColorHSL.hue = (double)hueShiftIndex * 30.0;
407 tmpColorHSL.sat = 1.0;
408 pl2.hueVariations[99 + hueShiftIndex].indices[colorIndex] =
409 pl2.basePalette.GetClosestColorIndex(ConvertHSLtoRGB(tmpColorHSL));
413 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
415 Palette::Color color = pl2.basePalette.colors[colorIndex];
416 const double colorMagnitudeDouble =
417 std::sqrt((
double)(color.r * color.r + color.g * color.g + color.b * color.b));
420 #ifdef USE_FIXED_VERSION
422 const uint8_t colorMagnitude = uint8_t(std::min(
int(colorMagnitudeDouble), 255));
425 const uint8_t colorMagnitude = uint8_t(colorMagnitudeDouble);
428 pl2.redTones.indices[colorIndex] =
429 pl2.basePalette.GetClosestColorIndex({colorMagnitude, 0, 0});
430 pl2.greenTones.indices[colorIndex] =
431 pl2.basePalette.GetClosestColorIndex({0, colorMagnitude, 0});
432 pl2.blueTones.indices[colorIndex] =
433 pl2.basePalette.GetClosestColorIndex({0, 0, colorMagnitude});
437 void PL2CreateMaxComponentBlend(PL2& pl2)
439 for (
size_t dstColorIndex = 0; dstColorIndex < Palette::colorCount; ++dstColorIndex)
441 const Palette::Color dstColor = pl2.basePalette.colors[dstColorIndex];
442 const uint8_t maxComponentDst = std::max({dstColor.r, dstColor.g, dstColor.b});
443 for (
size_t srcColorIndex = 0; srcColorIndex < Palette::colorCount; ++srcColorIndex)
445 const uint8_t invMaxComponentDst = 0xFF - maxComponentDst;
446 const Palette::Color srcColor = pl2.basePalette.colors[srcColorIndex];
447 const Palette::Color blendOuput{
448 uint8_t((invMaxComponentDst * srcColor.r + maxComponentDst * dstColor.r) / 0xFF),
449 uint8_t((invMaxComponentDst * srcColor.g + maxComponentDst * dstColor.g) / 0xFF),
450 uint8_t((invMaxComponentDst * srcColor.b + maxComponentDst * dstColor.b) / 0xFF),
452 pl2.maxComponentBlend[srcColorIndex].indices[dstColorIndex] =
453 pl2.basePalette.GetClosestColorIndex(blendOuput);
458 void PL2CreateDarkenedUnitShift(PL2& pl2)
460 for (
size_t colorIndex = 0; colorIndex < Palette::colorCount; ++colorIndex)
462 Palette::Color tmpColor = pl2.basePalette.colors[colorIndex];
463 tmpColor.r -= tmpColor.r / 3;
464 tmpColor.g -= tmpColor.g / 3;
465 tmpColor.b -= tmpColor.b / 3;
467 pl2.darkenedColorShift.indices[colorIndex] = pl2.basePalette.GetClosestColorIndex(tmpColor);
472 static const Palette::Color24Bits defaultTextColors[13] = {
489 void PL2CreateTextColorshifts(PL2& pl2)
491 static_assert(
sizeof(defaultTextColors) == 13 * 3,
"There must be 13 default text colors.");
492 memcpy(pl2.textColors, defaultTextColors,
sizeof(defaultTextColors));
495 pl2.textColorShifts[0].indices.fill(0u);
496 for (
size_t textColorIndex = 1; textColorIndex < 13; textColorIndex++)
498 const Palette::Color24Bits textColor = pl2.textColors[textColorIndex];
499 for (
size_t colorIndex = 0; colorIndex < 256; ++colorIndex)
501 const Palette::Color baseColor = pl2.basePalette.colors[colorIndex];
504 const uint8_t textColorIntensity = baseColor.r;
505 const Palette::Color newColor{
506 uint8_t((textColor.r * textColorIntensity) / 0xFF),
507 uint8_t((textColor.g * textColorIntensity) / 0xFF),
508 uint8_t((textColor.b * textColorIntensity) / 0xFF),
510 pl2.textColorShifts[textColorIndex].indices[colorIndex] =
511 pl2.basePalette.GetClosestColorIndex(newColor);
517 std::unique_ptr<PL2> PL2::CreateFromPalette(
const Palette& palette)
520 auto pl2Ptr = std::make_unique<PL2>();
523 pl2.basePalette = palette;
524 ColorHSL hslColors[Palette::colorCount];
526 for (
size_t i = 0; i < Palette::colorCount; i++)
528 hslColors[i] = ConvertRGBtoHSL(palette.colors[i]);
531 PL2CreateLightLevelVariations(pl2);
532 PL2CreateInvColorVariations(pl2);
533 PL2CreateSelectedUnitShift(pl2, hslColors);
534 PL2CreateAlphaBlend(pl2);
535 PL2CreateAdditiveBlend(pl2);
536 PL2CreateMultiplicativeBlend(pl2);
537 PL2CreateColorshifts(pl2, hslColors);
538 PL2CreateMaxComponentBlend(pl2);
539 PL2CreateDarkenedUnitShift(pl2);
540 PL2CreateTextColorshifts(pl2);
544 std::unique_ptr<PL2> PL2::ReadFromStream(IStream* stream)
546 static_assert(
sizeof(PL2) == 443175,
"PL2 struct does not match the size of the files");
547 static_assert(std::is_trivially_copyable<PL2>::value,
"PL2 is not trivially copyable");
548 if (stream && stream->good()) {
549 auto pl2 = std::make_unique<PL2>();
550 if (stream->read(pl2.get(),
sizeof(PL2)) ==
sizeof(PL2))
return pl2;