Improved light color blending logic.

Added auxiliary .pla files to gbafx, to support setting color high bits.
Updated light palettes to new system.
This commit is contained in:
Ariel A 2023-06-08 20:49:18 -04:00
parent 996f43120d
commit 91794286b5
51 changed files with 380 additions and 251 deletions

View File

@ -35,6 +35,23 @@ Additional branches to mention:
To set up the repository, see [INSTALL.md](INSTALL.md).
## FAQ
### Q: How do I mark certain colors in a palette as light-blended?
A: Create a `.pla` file in the same folder as the `.pal` with the same name.
In this file you can enter color indices [0,15]
on separate lines to mark those colors as being light-blended, i.e:
`06.pla:`
```
# A comment
0 # if color 0 is listed, uses it to blend with instead of the default!
1
9
10
```
You might have to `make mostlyclean` or change the `.pal` file to pick up the changes.
## See also
Other disassembly and/or decompilation projects:

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
232 0 128
0 0 0
216 224 232
222 230 238
184 200 224

View File

@ -0,0 +1,6 @@
# Color indices to set high bit
1
3
4
5
15

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 197 0
0 0 0
246 197 123
238 156 90
205 115 41

View File

@ -0,0 +1,3 @@
# Color indices to set high bit
9
10

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 192 0
0 0 0
222 230 164
180 197 115
148 156 82

View File

@ -0,0 +1,3 @@
# Color indices to set high bit
9
10

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 96 8
176 184 200
255 255 255
246 238 189
230 213 172

View File

@ -0,0 +1,6 @@
# Color indices to set high bit
0
8
9
11
15

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 192 24
224 40 0
64 72 104
192 200 0
224 232 232

View File

@ -0,0 +1,7 @@
# Color indices to set high bit
0
9
10
11
12
15

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
24 200 0
0 0 0
168 184 240
112 144 208
115 148 213

View File

@ -0,0 +1,6 @@
# Color indices to set high bit
1
2
6
9
10

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
8 0 128
123 180 189
255 255 180
238 238 90
213 213 65

View File

@ -0,0 +1,3 @@
# Color indices to set high bit
1
15

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
8 192 0
123 180 189
255 255 255
148 222 205
123 180 172

View File

@ -0,0 +1,4 @@
# Color indices to set high bit
1
9
10

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 48 0
123 180 189
255 255 255
222 230 238
164 180 197

View File

@ -0,0 +1,3 @@
# Color indices to set high bit
7
8

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 192 0
0 0 0
255 255 255
205 205 197
180 180 164

View File

@ -0,0 +1,3 @@
# Color indices to set high bit
9
10

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 0 128
0 0 0
222 230 164
180 189 123
156 164 98

View File

@ -0,0 +1,2 @@
# Color indices to set high bit
15

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 192 0
0 0 0
255 255 255
230 222 222
213 213 197

View File

@ -0,0 +1,3 @@
# Color indices to set high bit
9
10

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 192 0
0 0 0
213 222 172
0 0 0
189 189 131

View File

@ -0,0 +1,3 @@
# Color indices to set high bit
9
10

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 192 0
0 0 0
232 224 200
208 200 144
200 176 120

View File

@ -0,0 +1,3 @@
# Color indices to set high bit
9
10

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
240 192 0
115 197 164
255 255 255
216 224 232
184 200 224

View File

@ -0,0 +1,7 @@
# Colors in palette to set high (alpha) bit
2
3
4
5
9
10

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 32 0
0 0 0
255 255 255
230 230 238
205 205 222

View File

@ -0,0 +1,2 @@
# Color indices to set high bit
8

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
96 160 72
0 0 0
222 205 148
184 168 80
168 224 200

View File

@ -0,0 +1,6 @@
# Color indices to set high bit
3
4
8
10
11

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 192 0
248 240 176
255 255 255
238 238 213
222 222 180

View File

@ -0,0 +1,5 @@
# Color indices to set high bit
0
9
10
15

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 230 25
246 74 65
139 115 106
115 90 82
90 65 65

View File

@ -0,0 +1,8 @@
# Color indices to set high bit
0
8
9
10
11
12
15

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 8 248
197 65 0
255 255 255
255 238 74
255 197 16
@ -16,4 +16,4 @@ JASC-PAL
197 65 0
139 32 0
90 0 0
197 65 0
255 0 255

View File

@ -0,0 +1,7 @@
# Color indices to set high bit
0
6
11
12
13
14

View File

@ -1,7 +1,7 @@
JASC-PAL
0100
16
0 8 248
65 106 205
255 255 255
246 180 213
213 148 180
@ -16,4 +16,4 @@ JASC-PAL
65 106 205
32 65 156
49 74 139
65 106 205
255 0 255

View File

@ -0,0 +1,7 @@
# Color indices to set high bit
0
6
11
12
13
14

View File

@ -2,6 +2,13 @@
// Whether a palette has a night version, located at ((x + 9) % 16).pal
#define SWAP_PAL(x) ((x) < NUM_PALS_IN_PRIMARY ? 1 << (x) : 1 << ((x) - NUM_PALS_IN_PRIMARY))
// NOTE: Instead of using LIGHT_PAL,
// consider taking a look at the .pla files
// to mark colors as lights, instead.
// The old method *should* still work, however.
// See the README for details.
// Whether a palette has lights
// the color indices to blend are stored in the palette's color 0
#define LIGHT_PAL(x) ((x) < NUM_PALS_IN_PRIMARY ? 1 << (x) : 1 << ((x) - NUM_PALS_IN_PRIMARY))
@ -11,7 +18,6 @@ const struct Tileset gTileset_General =
.isCompressed = TRUE,
.swapPalettes = SWAP_PAL(4),
.isSecondary = FALSE,
.lightPalettes = LIGHT_PAL(4),
.tiles = gTilesetTiles_General,
.palettes = gTilesetPalettes_General,
.metatiles = gMetatiles_General,
@ -24,7 +30,6 @@ const struct Tileset gTileset_Petalburg =
.isCompressed = TRUE,
.swapPalettes = SWAP_PAL(9),
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(6) | LIGHT_PAL(7) | LIGHT_PAL(9),
.tiles = gTilesetTiles_Petalburg,
.palettes = gTilesetPalettes_Petalburg,
.metatiles = gMetatiles_Petalburg,
@ -36,7 +41,6 @@ const struct Tileset gTileset_Rustboro =
{
.isCompressed = TRUE,
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(6),
.tiles = gTilesetTiles_Rustboro,
.palettes = gTilesetPalettes_Rustboro,
.metatiles = gMetatiles_Rustboro,
@ -60,7 +64,6 @@ const struct Tileset gTileset_Slateport =
.isCompressed = TRUE,
.swapPalettes = SWAP_PAL(6),
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(6) | LIGHT_PAL(12),
.tiles = gTilesetTiles_Slateport,
.palettes = gTilesetPalettes_Slateport,
.metatiles = gMetatiles_Slateport,
@ -72,7 +75,6 @@ const struct Tileset gTileset_Mauville =
{
.isCompressed = TRUE,
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(7) | LIGHT_PAL(10) | LIGHT_PAL(11),
.tiles = gTilesetTiles_Mauville,
.palettes = gTilesetPalettes_Mauville,
.metatiles = gMetatiles_Mauville,
@ -84,8 +86,6 @@ const struct Tileset gTileset_Lavaridge =
{
.isCompressed = TRUE,
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(6),
.customLightColor = LIGHT_PAL(6),
.tiles = gTilesetTiles_Lavaridge,
.palettes = gTilesetPalettes_Lavaridge,
.metatiles = gMetatiles_Lavaridge,
@ -98,7 +98,6 @@ const struct Tileset gTileset_Fallarbor =
.isCompressed = TRUE,
.swapPalettes = SWAP_PAL(7),
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(7),
.tiles = gTilesetTiles_Fallarbor,
.palettes = gTilesetPalettes_Fallarbor,
.metatiles = gMetatiles_Fallarbor,
@ -110,8 +109,6 @@ const struct Tileset gTileset_Fortree =
{
.isCompressed = TRUE,
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(8),
.customLightColor = LIGHT_PAL(8),
.tiles = gTilesetTiles_Fortree,
.palettes = gTilesetPalettes_Fortree,
.metatiles = gMetatiles_Fortree,
@ -124,7 +121,6 @@ const struct Tileset gTileset_Lilycove =
.isCompressed = TRUE,
.swapPalettes = SWAP_PAL(6),
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(6),
.tiles = gTilesetTiles_Lilycove,
.palettes = gTilesetPalettes_Lilycove,
.metatiles = gMetatiles_Lilycove,
@ -137,7 +133,6 @@ const struct Tileset gTileset_Mossdeep =
.isCompressed = TRUE,
.swapPalettes = SWAP_PAL(8),
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(8),
.tiles = gTilesetTiles_Mossdeep,
.palettes = gTilesetPalettes_Mossdeep,
.metatiles = gMetatiles_Mossdeep,
@ -154,7 +149,6 @@ const struct Tileset gTileset_EverGrande =
.metatiles = gMetatiles_EverGrande,
.metatileAttributes = gMetatileAttributes_EverGrande,
.callback = InitTilesetAnim_EverGrande,
.lightPalettes = LIGHT_PAL(8),
.swapPalettes = SWAP_PAL(8),
};
@ -163,7 +157,6 @@ const struct Tileset gTileset_Pacifidlog =
.isCompressed = TRUE,
.swapPalettes = SWAP_PAL(9),
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(9),
.tiles = gTilesetTiles_Pacifidlog,
.palettes = gTilesetPalettes_Pacifidlog,
.metatiles = gMetatiles_Pacifidlog,
@ -176,8 +169,6 @@ const struct Tileset gTileset_Sootopolis =
.isCompressed = TRUE,
.swapPalettes = SWAP_PAL(6),
.isSecondary = TRUE,
.lightPalettes = LIGHT_PAL(6) | LIGHT_PAL(10),
.customLightColor = LIGHT_PAL(6) | LIGHT_PAL(10),
.tiles = gTilesetTiles_Sootopolis,
.palettes = gTilesetPalettes_Sootopolis,
.metatiles = gMetatiles_Sootopolis,

View File

@ -1765,16 +1765,11 @@ static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool8 shiny) {
// so that palette tags do not overlap
const struct CompressedSpritePalette *spritePalette = &(shiny ? gMonShinyPaletteTable : gMonPaletteTable)[species];
if ((paletteNum = IndexOfSpritePaletteTag(spritePalette->tag)) == 0xFF) { // Load compressed palette
LoadCompressedSpritePalette(spritePalette);
paletteNum = IndexOfSpritePaletteTag(spritePalette->tag); // Tag is always present
if (species == SPECIES_AMPHAROS) { // palette should be light-blended TODO: Add more glowing pokemon
LoadCompressedSpritePalette(spritePalette);
paletteNum = IndexOfSpritePaletteTag(spritePalette->tag); // Tag is always present
// TODO: Add more glowing pokemon besides Ampharos
// CHARIZARD LINE ? CHINCHOU LANTERN FLAAFY MAREEP UMBREON VOLBEAT ?
u16 * palette = &gPlttBufferUnfaded[(paletteNum+16)*16];
palette[0] |= 0x8000;
if (palette[0] & 0x4000) // If color 15 is blended, use it as the alternate color
palette[15] |= 0x8000;
}
UpdateSpritePaletteWithWeather(paletteNum, FALSE);
UpdateSpritePaletteWithWeather(paletteNum, FALSE);
}
return paletteNum;
}

View File

@ -904,14 +904,17 @@ void LoadTilesetPalette(struct Tileset const *tileset, u16 destOffset, u16 size,
LoadCompressedPalette((const u32 *)tileset->palettes, destOffset, size);
ApplyGlobalTintToPaletteEntries(destOffset, size >> 1);
}
if (tileset->isSecondary == FALSE || tileset->isSecondary == TRUE) {
u32 i;
// convert legacy light palette system to current
if (tileset->lightPalettes) {
u32 i, j, color;
for (i = low; i < high; i++) {
if (tileset->lightPalettes & (1 << (i - low))) { // Mark as light palette
u32 index = i * 16;
gPlttBufferFaded[index] = gPlttBufferUnfaded[index] |= 0x8000;
if (tileset->customLightColor & (1 << (i - low))) // Mark as custom light color
gPlttBufferFaded[index+15] = gPlttBufferUnfaded[index+15] |= 0x8000;
if (tileset->lightPalettes & (1 << (i - low))) { // Mark light colors
for (j = 1, color = gPlttBufferUnfaded[PLTT_ID(i)]; j < 16 && color; j++, color >>= 1) {
if (color & 1)
gPlttBufferFaded[PLTT_ID(i)+j] = gPlttBufferUnfaded[PLTT_ID(i)+j] |= RGB_ALPHA;
}
if (tileset->customLightColor & (1 << (i - low))) // Copy old custom light color to index 0
gPlttBufferFaded[PLTT_ID(i)] = gPlttBufferUnfaded[PLTT_ID(i)] = gPlttBufferUnfaded[PLTT_ID(i)+15] | RGB_ALPHA;
}
}
}

View File

@ -1003,40 +1003,40 @@ static bool8 IsSoftwarePaletteFadeFinishing(void)
// optimized based on lucktyphlosion's BlendPalettesFine
void BlendPalettesFine(u32 palettes, u16 *src, u16 *dst, u32 coeff, u32 color) {
s32 newR, newG, newB;
s32 newR, newG, newB;
if (!palettes)
return;
if (!palettes)
return;
coeff *= 2;
newR = (color << 27) >> 27;
newG = (color << 22) >> 27;
newB = (color << 17) >> 27;
coeff *= 2;
newR = (color << 27) >> 27;
newG = (color << 22) >> 27;
newB = (color << 17) >> 27;
do {
if (palettes & 1) {
u16 *srcEnd = src + 16;
while (src != srcEnd) { // Transparency is blended because it can matter for tile palettes
u32 srcColor = *src;
s32 r = (srcColor << 27) >> 27;
s32 g = (srcColor << 22) >> 27;
s32 b = (srcColor << 17) >> 27;
do {
if (palettes & 1) {
u16 *srcEnd = src + 16;
while (src != srcEnd) { // Transparency is blended (for backdrop reasons)
u32 srcColor = *src;
s32 r = (srcColor << 27) >> 27;
s32 g = (srcColor << 22) >> 27;
s32 b = (srcColor << 16) >> 26;
*dst++ = ((r + (((newR - r) * coeff) >> 5)) << 0)
| ((g + (((newG - g) * coeff) >> 5)) << 5)
| ((b + (((newB - b) * coeff) >> 5)) << 10);
src++;
}
} else {
src += 16;
dst += 16;
}
palettes >>= 1;
} while (palettes);
*dst++ = ((r + (((newR - r) * (s32)coeff) >> 5)) << 0)
| ((g + (((newG - g) * (s32)coeff) >> 5)) << 5)
| ((b + (((newB - (b & 31)) * (s32)coeff) >> 5)) << 10);
src++;
}
} else {
src += 16;
dst += 16;
}
palettes >>= 1;
} while (palettes);
}
void BlendPalettes(u32 palettes, u8 coeff, u16 color) {
BlendPalettesFine(palettes, gPlttBufferUnfaded, gPlttBufferFaded, coeff, color);
BlendPalettesFine(palettes, gPlttBufferUnfaded, gPlttBufferFaded, coeff, color);
}
#define DEFAULT_LIGHT_COLOR 0x3f9f
@ -1044,186 +1044,172 @@ void BlendPalettes(u32 palettes, u8 coeff, u16 color) {
// Like BlendPalette, but ignores blendColor if the transparency high bit is set
// Optimization help by lucktyphlosion
void TimeBlendPalette(u16 palOffset, u32 coeff, u32 blendColor) {
s32 newR, newG, newB, defR, defG, defB;
u16 * src = gPlttBufferUnfaded + palOffset;
u16 * dst = gPlttBufferFaded + palOffset;
u32 defaultBlendColor = DEFAULT_LIGHT_COLOR;
u16 *srcEnd = src + 16;
u16 altBlendIndices = *dst++ = *src++; // color 0 is copied through unchanged
u32 altBlendColor;
s32 newR, newG, newB, defR, defG, defB;
u16 * src = gPlttBufferUnfaded + palOffset;
u16 * dst = gPlttBufferFaded + palOffset;
u32 defaultBlendColor = DEFAULT_LIGHT_COLOR;
u16 *srcEnd = src + 16;
u32 altBlendColor = *dst++ = *src++; // color 0 is copied through unchanged
coeff *= 2;
newR = (blendColor << 27) >> 27;
newG = (blendColor << 22) >> 27;
newB = (blendColor << 17) >> 27;
coeff *= 2;
newR = (blendColor << 27) >> 27;
newG = (blendColor << 22) >> 27;
newB = (blendColor << 17) >> 27;
if (altBlendIndices >> 15) { // High bit set; bitmask of which colors to alt-blend
// Note that bit 0 of altBlendIndices specifies color 1
altBlendColor = src[14]; // color 15
if (altBlendColor >> 15) { // Set alternate blend color
defR = (altBlendColor << 27) >> 27;
defG = (altBlendColor << 22) >> 27;
defB = (altBlendColor << 17) >> 27;
} else { // Set default blend color
defR = (defaultBlendColor << 27) >> 27;
defG = (defaultBlendColor << 22) >> 27;
defB = (defaultBlendColor << 17) >> 27;
if (altBlendColor >> 15) { // Transparency high bit set; alt blend color
defR = (altBlendColor << 27) >> 27;
defG = (altBlendColor << 22) >> 27;
defB = (altBlendColor << 17) >> 27;
} else {
defR = (defaultBlendColor << 27) >> 27;
defG = (defaultBlendColor << 22) >> 27;
defB = (defaultBlendColor << 17) >> 27;
altBlendColor = 0;
}
} else {
altBlendIndices = 0;
}
while (src != srcEnd) {
u32 srcColor = *src;
s32 r = (srcColor << 27) >> 27;
s32 g = (srcColor << 22) >> 27;
s32 b = (srcColor << 17) >> 27;
while (src != srcEnd) {
u32 srcColor = *src;
s32 r = (srcColor << 27) >> 27;
s32 g = (srcColor << 22) >> 27;
s32 b = (srcColor << 16) >> 26;
if (altBlendIndices & 1) {
*dst = ((r + (((defR - r) * coeff) >> 5)) << 0)
| ((g + (((defG - g) * coeff) >> 5)) << 5)
| ((b + (((defB - b) * coeff) >> 5)) << 10);
} else { // Use provided blend color
*dst = ((r + (((newR - r) * coeff) >> 5)) << 0)
| ((g + (((newG - g) * coeff) >> 5)) << 5)
| ((b + (((newB - b) * coeff) >> 5)) << 10);
if (srcColor >> 15) {
*dst = ((r + (((defR - r) * (s32)coeff) >> 5)) << 0)
| ((g + (((defG - g) * (s32)coeff) >> 5)) << 5)
| ((b + (((defB - (b & 31)) * (s32)coeff) >> 5)) << 10);
} else { // Use provided blend color
*dst = ((r + (((newR - r) * (s32)coeff) >> 5)) << 0)
| ((g + (((newG - g) * (s32)coeff) >> 5)) << 5)
| ((b + (((newB - (b & 31)) * (s32)coeff) >> 5)) << 10);
}
src++;
dst++;
}
src++;
dst++;
altBlendIndices >>= 1;
}
}
// Blends a weighted average of two blend parameters
// Parameters can be either blended (as in BlendPalettes) or tinted (as in TintPaletteRGB_Copy)
void TimeMixPalettes(u32 palettes, u16 *src, u16 *dst, struct BlendSettings *blend0, struct BlendSettings *blend1, u16 weight0) {
s32 r0, g0, b0, r1, g1, b1, defR, defG, defB, altR, altG, altB;
u32 color0, coeff0, color1, coeff1;
bool8 tint0, tint1;
u16 weight1;
u32 defaultColor = DEFAULT_LIGHT_COLOR;
s32 r0, g0, b0, r1, g1, b1, defR, defG, defB, altR, altG, altB;
u32 color0, coeff0, color1, coeff1;
bool8 tint0, tint1;
u32 defaultColor = DEFAULT_LIGHT_COLOR;
if (!palettes)
if (!palettes)
return;
color0 = blend0->blendColor;
tint0 = blend0->isTint;
coeff0 = tint0 ? 8*2 : blend0->coeff*2;
color1 = blend1->blendColor;
tint1 = blend1->isTint;
coeff1 = tint1 ? 8*2 : blend1->coeff*2;
color0 = blend0->blendColor;
tint0 = blend0->isTint;
coeff0 = tint0 ? 8*2 : blend0->coeff*2;
color1 = blend1->blendColor;
tint1 = blend1->isTint;
coeff1 = tint1 ? 8*2 : blend1->coeff*2;
if (tint0) {
r0 = (color0 << 24) >> 24;
g0 = (color0 << 16) >> 24;
b0 = (color0 << 8) >> 24;
} else {
r0 = (color0 << 27) >> 27;
g0 = (color0 << 22) >> 27;
b0 = (color0 << 17) >> 27;
}
if (tint1) {
r1 = (color1 << 24) >> 24;
g1 = (color1 << 16) >> 24;
b1 = (color1 << 8) >> 24;
} else {
r1 = (color1 << 27) >> 27;
g1 = (color1 << 22) >> 27;
b1 = (color1 << 17) >> 27;
}
defR = (defaultColor << 27) >> 27;
defG = (defaultColor << 22) >> 27;
defB = (defaultColor << 17) >> 27;
weight1 = 256 - weight0;
do {
if (palettes & 1) {
u16 *srcEnd = src + 16;
u16 altBlendIndices = *dst++ = *src++; // color 0 is copied through
u32 altBlendColor;
if (altBlendIndices >> 15) { // High bit set; bitmask of which colors to alt-blend
// Note that bit 0 of altBlendIndices specifies color 1
altBlendColor = src[14]; // color 15
if (altBlendColor >> 15) { // Set alternate blend color
altR = (altBlendColor << 27) >> 27;
altG = (altBlendColor << 22) >> 27;
altB = (altBlendColor << 17) >> 27;
} else {
altBlendColor = 0;
}
} else {
altBlendIndices = 0;
}
while (src != srcEnd) {
u32 srcColor = *src;
s32 r = (srcColor << 27) >> 27;
s32 g = (srcColor << 22) >> 27;
s32 b = (srcColor << 17) >> 27;
if (altBlendIndices & 1) {
if (altBlendColor) { // Use alternate blend color
r = (weight0*(r + (((altR - r) * coeff0) >> 5)) + weight1*(r + (((altR - r) * coeff1) >> 5))) >> 8;
g = (weight0*(g + (((altG - g) * coeff0) >> 5)) + weight1*(g + (((altG - g) * coeff1) >> 5))) >> 8;
b = (weight0*(b + (((altB - b) * coeff0) >> 5)) + weight1*(b + (((altB - b) * coeff1) >> 5))) >> 8;
*dst = RGB2(r, g, b);
} else { // Use default blend color
r = (weight0*(r + (((defR - r) * coeff0) >> 5)) + weight1*(r + (((defR - r) * coeff1) >> 5))) >> 8;
g = (weight0*(g + (((defG - g) * coeff0) >> 5)) + weight1*(g + (((defG - g) * coeff1) >> 5))) >> 8;
b = (weight0*(b + (((defB - b) * coeff0) >> 5)) + weight1*(b + (((defB - b) * coeff1) >> 5))) >> 8;
*dst = RGB2(r, g, b);
}
} else { // Use provided blend colors
s32 r2, g2, b2, r3, g3, b3;
if (!tint0) { // blend-based
r2 = (r + (((r0 - r) * coeff0) >> 5));
g2 = (g + (((g0 - g) * coeff0) >> 5));
b2 = (b + (((b0 - b) * coeff0) >> 5));
} else { // tint-based
r2 = (u16)((r0 * r)) >> 8;
g2 = (u16)((g0 * g)) >> 8;
b2 = (u16)((b0 * b)) >> 8;
if (r2 > 31)
r2 = 31;
if (g2 > 31)
g2 = 31;
if (b2 > 31)
b2 = 31;
}
if (!tint1) { // blend-based
r3 = (r + (((r1 - r) * coeff1) >> 5));
g3 = (g + (((g1 - g) * coeff1) >> 5));
b3 = (b + (((b1 - b) * coeff1) >> 5));
} else { // tint-based
r3 = (u16)((r1 * r)) >> 8;
g3 = (u16)((g1 * g)) >> 8;
b3 = (u16)((b1 * b)) >> 8;
if (r3 > 31)
r3 = 31;
if (g3 > 31)
g3 = 31;
if (b3 > 31)
b3 = 31;
}
r = (weight0*r2 + weight1*r3) >> 8;
g = (weight0*g2 + weight1*g3) >> 8;
b = (weight0*b2 + weight1*b3) >> 8;
*dst = RGB2(r, g, b);
}
src++;
dst++;
altBlendIndices >>= 1;
}
if (tint0) {
r0 = (color0 << 24) >> 24;
g0 = (color0 << 16) >> 24;
b0 = (color0 << 8) >> 24;
} else {
src += 16;
dst += 16;
r0 = (color0 << 27) >> 27;
g0 = (color0 << 22) >> 27;
b0 = (color0 << 17) >> 27;
}
palettes >>= 1;
} while (palettes);
if (tint1) {
r1 = (color1 << 24) >> 24;
g1 = (color1 << 16) >> 24;
b1 = (color1 << 8) >> 24;
} else {
r1 = (color1 << 27) >> 27;
g1 = (color1 << 22) >> 27;
b1 = (color1 << 17) >> 27;
}
defR = (defaultColor << 27) >> 27;
defG = (defaultColor << 22) >> 27;
defB = (defaultColor << 17) >> 27;
do {
if (palettes & 1) {
u16 *srcEnd = src + 16;
u32 altBlendColor = *dst++ = *src++; // color 0 is copied through
if (altBlendColor >> 15) { // Transparency high bit set; alt blend color
altR = (altBlendColor << 27) >> 27;
altG = (altBlendColor << 22) >> 27;
altB = (altBlendColor << 17) >> 27;
} else {
altBlendColor = 0;
}
while (src != srcEnd) {
u32 srcColor = *src;
s32 r = (srcColor << 27) >> 27;
s32 g = (srcColor << 22) >> 27;
s32 b = (srcColor << 17) >> 27;
s32 r2, g2, b2;
if (srcColor >> 15) {
if (altBlendColor) { // Use alternate blend color
r2 = r + (((altR - r) * (s32)coeff1) >> 5);
g2 = g + (((altG - g) * (s32)coeff1) >> 5);
b2 = b + (((altB - b) * (s32)coeff1) >> 5);
r = r + (((altR - r) * (s32)coeff0) >> 5);
g = g + (((altG - g) * (s32)coeff0) >> 5);
b = b + (((altB - b) * (s32)coeff0) >> 5);
} else { // Use default blend color
r2 = r + (((defR - r) * (s32)coeff1) >> 5);
g2 = g + (((defG - g) * (s32)coeff1) >> 5);
b2 = b + (((defB - b) * (s32)coeff1) >> 5);
r = r + (((defR - r) * (s32)coeff0) >> 5);
g = g + (((defG - g) * (s32)coeff0) >> 5);
b = b + (((defB - b) * (s32)coeff0) >> 5);
}
} else { // Use provided blend colors
if (!tint1) { // blend-based
r2 = (r + (((r1 - r) * (s32)coeff1) >> 5));
g2 = (g + (((g1 - g) * (s32)coeff1) >> 5));
b2 = (b + (((b1 - b) * (s32)coeff1) >> 5));
} else { // tint-based
r2 = (u16)((r1 * r)) >> 8;
g2 = (u16)((g1 * g)) >> 8;
b2 = (u16)((b1 * b)) >> 8;
if (r2 > 31)
r2 = 31;
if (g2 > 31)
g2 = 31;
if (b2 > 31)
b2 = 31;
}
if (!tint0) { // blend-based
r = (r + (((r0 - r) * (s32)coeff0) >> 5));
g = (g + (((g0 - g) * (s32)coeff0) >> 5));
b = (b + (((b0 - b) * (s32)coeff0) >> 5));
} else { // tint-based
r = (u16)((r0 * r)) >> 8;
g = (u16)((g0 * g)) >> 8;
b = (u16)((b0 * b)) >> 8;
if (r > 31)
r = 31;
if (g > 31)
g = 31;
if (b > 31)
b = 31;
}
}
r = r2 + (((r - r2) * (s32)weight0) >> 8);
g = g2 + (((g - g2) * (s32)weight0) >> 8);
b = b2 + (((b - b2) * (s32)weight0) >> 8);
*dst++ = RGB2(r, g, b);
// *dst++ = RGB2(r, g, b) | (srcColor >> 15) << 15;
src++;
}
} else {
src += 16;
dst += 16;
}
palettes >>= 1;
} while (palettes);
}
// Apply weighted average to palettes, preserving high bits of dst throughout
void AvgPaletteWeighted(u16 *src0, u16 *src1, u16 *dst, u16 weight0) {
u16 *srcEnd = src0 + 16;
u16 weight1 = 256 - weight0;
src0++;
src1++;
dst++; // leave dst transparency unchanged
@ -1238,10 +1224,10 @@ void AvgPaletteWeighted(u16 *src0, u16 *src1, u16 *dst, u16 weight0) {
s32 b1 = (src1Color << 17) >> 27;
// Average and bitwise-OR
r0 = (weight0*r0 + weight1*r1) >> 8;
g0 = (weight0*g0 + weight1*g1) >> 8;
b0 = (weight0*b0 + weight1*b1) >> 8;
*dst++ = (*dst & 0x8000) | RGB2(r0, g0, b0); // preserve high bit of dst
r0 = r1 + (((r0 - r1) * weight0) >> 8);
g0 = g1 + (((g0 - g1) * weight0) >> 8);
b0 = b1 + (((b0 - b1) * weight0) >> 8);
*dst++ = (*dst & RGB_ALPHA) | RGB2(r0, g0, b0); // preserve high bit of dst
}
}

View File

@ -14,6 +14,7 @@
#define GET_GBA_PAL_BLUE(x) (((x) >> 10) & 0x1F)
#define SET_GBA_PAL(r, g, b) (((b) << 10) | ((g) << 5) | (r))
#define SET_GBA_PAL_RGBA(r, g, b, a) (((a) << 15) | ((b) << 10) | ((g) << 5) | (r))
#define UPCONVERT_BIT_DEPTH(x) (((x) * 255) / 31)
@ -516,8 +517,9 @@ void WriteGbaPalette(char *path, struct Palette *palette)
unsigned char red = DOWNCONVERT_BIT_DEPTH(palette->colors[i].red);
unsigned char green = DOWNCONVERT_BIT_DEPTH(palette->colors[i].green);
unsigned char blue = DOWNCONVERT_BIT_DEPTH(palette->colors[i].blue);
bool alpha = palette->colors[i].alpha;
uint16_t paletteEntry = SET_GBA_PAL(red, green, blue);
uint16_t paletteEntry = SET_GBA_PAL_RGBA(red, green, blue, alpha);
fputc(paletteEntry & 0xFF, fp);
fputc(paletteEntry >> 8, fp);

View File

@ -10,6 +10,7 @@ struct Color {
unsigned char red;
unsigned char green;
unsigned char blue;
bool alpha;
};
struct Palette {

View File

@ -22,7 +22,7 @@
// Blue - "0 0 255\r\n"
// Brown - "150 75 0\r\n"
#define MAX_LINE_LENGTH 11
#define MAX_LINE_LENGTH 64
void ReadJascPaletteLine(FILE *fp, char *line)
{
@ -152,6 +152,43 @@ void ReadJascPalette(char *path, struct Palette *palette)
FATAL_ERROR("Garbage after color data.\n");
fclose(fp);
// Try to parse alpha/high bit info for colors from auxiliary .pla file
char *dot = strrchr(path, '.');
if (strcmp(dot, ".pal") != 0)
return;
strcpy(dot, ".pla"); // replace .pal with .pla
fp = fopen(path, "rb");
if (fp == NULL)
return;
// fprintf(stderr, "Opened auxiliary .pla file for reading: %s\n", path);
int i = 0;
// Keep reading lines until number of colors is reached or we run out
while (i < palette->numColors && fgets(line, MAX_LINE_LENGTH, fp) != NULL)
{
if (line[0] == '#') // comment line; ignore
continue;
char *s = line;
char *end;
int colorIndex;
if (!ParseNumber(s, &end, 10, &colorIndex))
FATAL_ERROR("Failed to parse aux color index.\n");
s = end;
if (colorIndex >= palette->numColors)
FATAL_ERROR("Aux color index %d out of bounds.\n", colorIndex);
palette->colors[colorIndex].alpha = 1; // set alpha color
// fprintf(stderr, "Color index: %d\n", colorIndex);
i++;
}
fclose(fp);
}
void WriteJascPalette(char *path, struct Palette *palette)

View File

@ -247,7 +247,7 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a
void HandlePngToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Palette palette = {};
struct Palette palette = {0};
ReadPngPalette(inputPath, &palette);
WriteJascPalette(outputPath, &palette);
@ -255,7 +255,7 @@ void HandlePngToJascPaletteCommand(char *inputPath, char *outputPath, int argc U
void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Palette palette = {};
struct Palette palette = {0};
ReadPngPalette(inputPath, &palette);
WriteGbaPalette(outputPath, &palette);
@ -263,7 +263,7 @@ void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UN
void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Palette palette = {};
struct Palette palette = {0};
ReadGbaPalette(inputPath, &palette);
WriteJascPalette(outputPath, &palette);
@ -296,7 +296,7 @@ void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc,
}
}
struct Palette palette = {};
struct Palette palette = {0};
ReadJascPalette(inputPath, &palette);