sovereignx/src/palette.c

796 lines
20 KiB
C

#include "global.h"
#include "palette.h"
#include "util.h"
#include "decompress.h"
#include "gpu_regs.h"
#include "task.h"
#include "constants/rgb.h"
enum
{
NORMAL_FADE,
FAST_FADE,
HARDWARE_FADE,
};
static u32 UpdateNormalPaletteFade(void);
static void BeginFastPaletteFadeInternal(u32);
static u32 UpdateFastPaletteFade(void);
static u32 UpdateHardwarePaletteFade(void);
static void UpdateBlendRegisters(void);
static bool32 IsSoftwarePaletteFadeFinishing(void);
static void Task_BlendPalettesGradually(u8 taskId);
// palette buffers require alignment with agbcc because
// unaligned word reads are issued in BlendPalette otherwise
ALIGNED(4) EWRAM_DATA u16 gPlttBufferUnfaded[PLTT_BUFFER_SIZE] = {0};
ALIGNED(4) EWRAM_DATA u16 gPlttBufferFaded[PLTT_BUFFER_SIZE] = {0};
EWRAM_DATA struct PaletteFadeControl gPaletteFade = {0};
static EWRAM_DATA u32 sPlttBufferTransferPending = 0;
static const u8 sRoundedDownGrayscaleMap[] = {
0, 0, 0, 0, 0,
5, 5, 5, 5, 5,
11, 11, 11, 11, 11,
16, 16, 16, 16, 16,
21, 21, 21, 21, 21,
27, 27, 27, 27, 27,
31, 31
};
void LoadCompressedPalette(const u32 *src, u32 offset, u32 size)
{
LZDecompressWram(src, gDecompressionBuffer);
CpuCopy16(gDecompressionBuffer, &gPlttBufferUnfaded[offset], size);
CpuCopy16(gDecompressionBuffer, &gPlttBufferFaded[offset], size);
}
void LoadPalette(const void *src, u32 offset, u32 size)
{
CpuCopy16(src, &gPlttBufferUnfaded[offset], size);
CpuCopy16(src, &gPlttBufferFaded[offset], size);
}
void FillPalette(u32 value, u32 offset, u32 size)
{
CpuFill16(value, &gPlttBufferUnfaded[offset], size);
CpuFill16(value, &gPlttBufferFaded[offset], size);
}
void TransferPlttBuffer(void)
{
if (!gPaletteFade.bufferTransferDisabled)
{
void *src = gPlttBufferFaded;
void *dest = (void *)PLTT;
DmaCopy16(3, src, dest, PLTT_SIZE);
sPlttBufferTransferPending = FALSE;
if (gPaletteFade.mode == HARDWARE_FADE && gPaletteFade.active)
UpdateBlendRegisters();
}
}
u32 UpdatePaletteFade(void)
{
u32 result;
if (sPlttBufferTransferPending)
return PALETTE_FADE_STATUS_LOADING;
if (gPaletteFade.mode == NORMAL_FADE)
result = UpdateNormalPaletteFade();
else if (gPaletteFade.mode == FAST_FADE)
result = UpdateFastPaletteFade();
else
result = UpdateHardwarePaletteFade();
sPlttBufferTransferPending = gPaletteFade.multipurpose1;
return result;
}
void ResetPaletteFade(void)
{
ResetPaletteFadeControl();
}
bool32 BeginNormalPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, u32 blendColor)
{
u8 temp;
if (gPaletteFade.active)
{
return FALSE;
}
else
{
gPaletteFade.deltaY = 2;
if (delay < 0)
{
gPaletteFade.deltaY += (delay * -1);
delay = 0;
}
gPaletteFade_selectedPalettes = selectedPalettes;
gPaletteFade.delayCounter = delay;
gPaletteFade_delay = delay;
gPaletteFade.y = startY;
gPaletteFade.targetY = targetY;
gPaletteFade.blendColor = blendColor;
gPaletteFade.active = TRUE;
gPaletteFade.mode = NORMAL_FADE;
if (startY < targetY)
gPaletteFade.yDec = 0;
else
gPaletteFade.yDec = 1;
UpdatePaletteFade();
temp = gPaletteFade.bufferTransferDisabled;
gPaletteFade.bufferTransferDisabled = FALSE;
CpuCopy32(gPlttBufferFaded, (void *)PLTT, PLTT_SIZE);
sPlttBufferTransferPending = FALSE;
if (gPaletteFade.mode == HARDWARE_FADE && gPaletteFade.active)
UpdateBlendRegisters();
gPaletteFade.bufferTransferDisabled = temp;
return TRUE;
}
}
void ResetPaletteFadeControl(void)
{
gPaletteFade.multipurpose1 = 0;
gPaletteFade.multipurpose2 = 0;
gPaletteFade.delayCounter = 0;
gPaletteFade.y = 0;
gPaletteFade.targetY = 0;
gPaletteFade.blendColor = 0;
gPaletteFade.active = FALSE;
gPaletteFade.multipurpose2 = 0; // assign same value twice
gPaletteFade.yDec = 0;
gPaletteFade.bufferTransferDisabled = FALSE;
gPaletteFade.shouldResetBlendRegisters = FALSE;
gPaletteFade.hardwareFadeFinishing = FALSE;
gPaletteFade.softwareFadeFinishing = FALSE;
gPaletteFade.softwareFadeFinishingCounter = 0;
gPaletteFade.objPaletteToggle = 0;
gPaletteFade.deltaY = 2;
}
static u32 UpdateNormalPaletteFade(void)
{
u16 paletteOffset;
u16 selectedPalettes;
if (!gPaletteFade.active)
return PALETTE_FADE_STATUS_DONE;
if (IsSoftwarePaletteFadeFinishing())
{
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
}
else
{
if (!gPaletteFade.objPaletteToggle)
{
if (gPaletteFade.delayCounter < gPaletteFade_delay)
{
gPaletteFade.delayCounter++;
return 2;
}
gPaletteFade.delayCounter = 0;
}
paletteOffset = 0;
if (!gPaletteFade.objPaletteToggle)
{
selectedPalettes = gPaletteFade_selectedPalettes;
}
else
{
selectedPalettes = gPaletteFade_selectedPalettes >> 16;
paletteOffset = OBJ_PLTT_OFFSET;
}
while (selectedPalettes)
{
if (selectedPalettes & 1)
BlendPalette(
paletteOffset,
16,
gPaletteFade.y,
gPaletteFade.blendColor);
selectedPalettes >>= 1;
paletteOffset += 16;
}
gPaletteFade.objPaletteToggle ^= 1;
if (!gPaletteFade.objPaletteToggle)
{
if (gPaletteFade.y == gPaletteFade.targetY)
{
gPaletteFade_selectedPalettes = 0;
gPaletteFade.softwareFadeFinishing = TRUE;
}
else
{
s8 val;
if (!gPaletteFade.yDec)
{
val = gPaletteFade.y;
val += gPaletteFade.deltaY;
if (val > gPaletteFade.targetY)
val = gPaletteFade.targetY;
gPaletteFade.y = val;
}
else
{
val = gPaletteFade.y;
val -= gPaletteFade.deltaY;
if (val < gPaletteFade.targetY)
val = gPaletteFade.targetY;
gPaletteFade.y = val;
}
}
}
// gPaletteFade.active cannot change since the last time it was checked. So this
// is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
}
}
void InvertPlttBuffer(u32 selectedPalettes)
{
u16 paletteOffset = 0;
while (selectedPalettes)
{
if (selectedPalettes & 1)
{
u32 i;
for (i = 0; i < 16; i++)
gPlttBufferFaded[paletteOffset + i] = ~gPlttBufferFaded[paletteOffset + i];
}
selectedPalettes >>= 1;
paletteOffset += 16;
}
}
void TintPlttBuffer(u32 selectedPalettes, s8 r, s8 g, s8 b)
{
u16 paletteOffset = 0;
while (selectedPalettes)
{
if (selectedPalettes & 1)
{
u32 i;
for (i = 0; i < 16; i++)
{
struct PlttData *data = (struct PlttData *)&gPlttBufferFaded[paletteOffset + i];
data->r += r;
data->g += g;
data->b += b;
}
}
selectedPalettes >>= 1;
paletteOffset += 16;
}
}
void UnfadePlttBuffer(u32 selectedPalettes)
{
u16 paletteOffset = 0;
while (selectedPalettes)
{
if (selectedPalettes & 1)
{
u8 i;
for (i = 0; i < 16; i++)
gPlttBufferFaded[paletteOffset + i] = gPlttBufferUnfaded[paletteOffset + i];
}
selectedPalettes >>= 1;
paletteOffset += 16;
}
}
void BeginFastPaletteFade(u32 submode)
{
gPaletteFade.deltaY = 2;
BeginFastPaletteFadeInternal(submode);
}
static void BeginFastPaletteFadeInternal(u32 submode)
{
gPaletteFade.y = 31;
gPaletteFade_submode = submode & 0x3F;
gPaletteFade.active = TRUE;
gPaletteFade.mode = FAST_FADE;
if (submode == FAST_FADE_IN_FROM_BLACK)
CpuFill16(RGB_BLACK, gPlttBufferFaded, PLTT_SIZE);
if (submode == FAST_FADE_IN_FROM_WHITE)
CpuFill16(RGB_WHITE, gPlttBufferFaded, PLTT_SIZE);
UpdatePaletteFade();
}
static u32 UpdateFastPaletteFade(void)
{
u32 i;
u16 paletteOffsetStart;
u16 paletteOffsetEnd;
s8 r0;
s8 g0;
s8 b0;
s8 r;
s8 g;
s8 b;
if (!gPaletteFade.active)
return PALETTE_FADE_STATUS_DONE;
if (IsSoftwarePaletteFadeFinishing())
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
if (gPaletteFade.objPaletteToggle)
{
paletteOffsetStart = OBJ_PLTT_OFFSET;
paletteOffsetEnd = PLTT_BUFFER_SIZE;
}
else
{
paletteOffsetStart = 0;
paletteOffsetEnd = OBJ_PLTT_OFFSET;
}
switch (gPaletteFade_submode)
{
case FAST_FADE_IN_FROM_WHITE:
for (i = paletteOffsetStart; i < paletteOffsetEnd; i++)
{
struct PlttData *unfaded;
struct PlttData *faded;
unfaded = (struct PlttData *)&gPlttBufferUnfaded[i];
r0 = unfaded->r;
g0 = unfaded->g;
b0 = unfaded->b;
faded = (struct PlttData *)&gPlttBufferFaded[i];
r = faded->r - 2;
g = faded->g - 2;
b = faded->b - 2;
if (r < r0)
r = r0;
if (g < g0)
g = g0;
if (b < b0)
b = b0;
gPlttBufferFaded[i] = RGB(r, g, b);
}
break;
case FAST_FADE_OUT_TO_WHITE:
for (i = paletteOffsetStart; i < paletteOffsetEnd; i++)
{
struct PlttData *data = (struct PlttData *)&gPlttBufferFaded[i];
r = data->r + 2;
g = data->g + 2;
b = data->b + 2;
if (r > 31)
r = 31;
if (g > 31)
g = 31;
if (b > 31)
b = 31;
gPlttBufferFaded[i] = RGB(r, g, b);
}
break;
case FAST_FADE_IN_FROM_BLACK:
for (i = paletteOffsetStart; i < paletteOffsetEnd; i++)
{
struct PlttData *unfaded;
struct PlttData *faded;
unfaded = (struct PlttData *)&gPlttBufferUnfaded[i];
r0 = unfaded->r;
g0 = unfaded->g;
b0 = unfaded->b;
faded = (struct PlttData *)&gPlttBufferFaded[i];
r = faded->r + 2;
g = faded->g + 2;
b = faded->b + 2;
if (r > r0)
r = r0;
if (g > g0)
g = g0;
if (b > b0)
b = b0;
gPlttBufferFaded[i] = RGB(r, g, b);
}
break;
case FAST_FADE_OUT_TO_BLACK:
for (i = paletteOffsetStart; i < paletteOffsetEnd; i++)
{
struct PlttData *data = (struct PlttData *)&gPlttBufferFaded[i];
r = data->r - 2;
g = data->g - 2;
b = data->b - 2;
if (r < 0)
r = 0;
if (g < 0)
g = 0;
if (b < 0)
b = 0;
gPlttBufferFaded[i] = RGB(r, g, b);
}
}
gPaletteFade.objPaletteToggle ^= 1;
if (gPaletteFade.objPaletteToggle)
// gPaletteFade.active cannot change since the last time it was checked. So this
// is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
if (gPaletteFade.y - gPaletteFade.deltaY < 0)
gPaletteFade.y = 0;
else
gPaletteFade.y -= gPaletteFade.deltaY;
if (gPaletteFade.y == 0)
{
switch (gPaletteFade_submode)
{
case FAST_FADE_IN_FROM_WHITE:
case FAST_FADE_IN_FROM_BLACK:
CpuCopy32(gPlttBufferUnfaded, gPlttBufferFaded, PLTT_SIZE);
break;
case FAST_FADE_OUT_TO_WHITE:
CpuFill32(0xFFFFFFFF, gPlttBufferFaded, PLTT_SIZE);
break;
case FAST_FADE_OUT_TO_BLACK:
CpuFill32(0x00000000, gPlttBufferFaded, PLTT_SIZE);
break;
}
gPaletteFade.mode = NORMAL_FADE;
gPaletteFade.softwareFadeFinishing = TRUE;
}
// gPaletteFade.active cannot change since the last time it was checked. So this
// is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
}
void BeginHardwarePaletteFade(u32 blendCnt, u32 delay, u32 y, u32 targetY, u32 shouldResetBlendRegisters)
{
gPaletteFade_blendCnt = blendCnt;
gPaletteFade.delayCounter = delay;
gPaletteFade_delay = delay;
gPaletteFade.y = y;
gPaletteFade.targetY = targetY;
gPaletteFade.active = TRUE;
gPaletteFade.mode = HARDWARE_FADE;
gPaletteFade.shouldResetBlendRegisters = shouldResetBlendRegisters & 1;
gPaletteFade.hardwareFadeFinishing = FALSE;
if (y < targetY)
gPaletteFade.yDec = 0;
else
gPaletteFade.yDec = 1;
}
static u32 UpdateHardwarePaletteFade(void)
{
if (!gPaletteFade.active)
return PALETTE_FADE_STATUS_DONE;
if (gPaletteFade.delayCounter < gPaletteFade_delay)
{
gPaletteFade.delayCounter++;
return PALETTE_FADE_STATUS_DELAY;
}
gPaletteFade.delayCounter = 0;
if (!gPaletteFade.yDec)
{
gPaletteFade.y++;
if (gPaletteFade.y > gPaletteFade.targetY)
{
gPaletteFade.hardwareFadeFinishing++;
gPaletteFade.y--;
}
}
else
{
s32 y = gPaletteFade.y--;
if (y - 1 < gPaletteFade.targetY)
{
gPaletteFade.hardwareFadeFinishing++;
gPaletteFade.y++;
}
}
if (gPaletteFade.hardwareFadeFinishing)
{
if (gPaletteFade.shouldResetBlendRegisters)
{
gPaletteFade_blendCnt = 0;
gPaletteFade.y = 0;
}
gPaletteFade.shouldResetBlendRegisters = FALSE;
}
// gPaletteFade.active cannot change since the last time it was checked. So this
// is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
}
static void UpdateBlendRegisters(void)
{
SetGpuReg(REG_OFFSET_BLDCNT, (u16)gPaletteFade_blendCnt);
SetGpuReg(REG_OFFSET_BLDY, gPaletteFade.y);
if (gPaletteFade.hardwareFadeFinishing)
{
gPaletteFade.hardwareFadeFinishing = FALSE;
gPaletteFade.mode = 0;
gPaletteFade_blendCnt = 0;
gPaletteFade.y = 0;
gPaletteFade.active = FALSE;
}
}
static bool32 IsSoftwarePaletteFadeFinishing(void)
{
if (gPaletteFade.softwareFadeFinishing)
{
if (gPaletteFade.softwareFadeFinishingCounter == 4)
{
gPaletteFade.active = FALSE;
gPaletteFade.softwareFadeFinishing = FALSE;
gPaletteFade.softwareFadeFinishingCounter = 0;
}
else
{
gPaletteFade.softwareFadeFinishingCounter++;
}
return TRUE;
}
else
{
return FALSE;
}
}
void BlendPalettes(u32 selectedPalettes, u8 coeff, u32 color)
{
u16 paletteOffset;
for (paletteOffset = 0; selectedPalettes; paletteOffset += 16)
{
if (selectedPalettes & 1)
BlendPalette(paletteOffset, 16, coeff, color);
selectedPalettes >>= 1;
}
}
void BlendPalettesUnfaded(u32 selectedPalettes, u8 coeff, u32 color)
{
void *src = gPlttBufferUnfaded;
void *dest = gPlttBufferFaded;
DmaCopy32(3, src, dest, PLTT_SIZE);
BlendPalettes(selectedPalettes, coeff, color);
}
void TintPalette_GrayScale(u16 *palette, u32 count)
{
s32 r, g, b;
u32 i, gray;
for (i = 0; i < count; i++)
{
r = GET_R(*palette);
g = GET_G(*palette);
b = GET_B(*palette);
gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
*palette++ = RGB2(gray, gray, gray);
}
}
void TintPalette_GrayScale2(u16 *palette, u32 count)
{
s32 r, g, b;
u32 i, gray;
for (i = 0; i < count; i++)
{
r = GET_R(*palette);
g = GET_G(*palette);
b = GET_B(*palette);
gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
if (gray > 31)
gray = 31;
gray = sRoundedDownGrayscaleMap[gray];
*palette++ = RGB2(gray, gray, gray);
}
}
void TintPalette_SepiaTone(u16 *palette, u32 count)
{
s32 r, g, b;
u32 i, gray;
for (i = 0; i < count; i++)
{
r = GET_R(*palette);
g = GET_G(*palette);
b = GET_B(*palette);
gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
r = (u16)((Q_8_8(1.2) * gray)) >> 8;
g = (u16)((Q_8_8(1.0) * gray)) >> 8;
b = (u16)((Q_8_8(0.94) * gray)) >> 8;
if (r > 31)
r = 31;
*palette++ = RGB2(r, g, b);
}
}
void TintPalette_CustomTone(u16 *palette, u32 count, u16 rTone, u16 gTone, u16 bTone)
{
s32 r, g, b;
u32 i, gray;
for (i = 0; i < count; i++)
{
r = GET_R(*palette);
g = GET_G(*palette);
b = GET_B(*palette);
gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
r = (u16)((rTone * gray)) >> 8;
g = (u16)((gTone * gray)) >> 8;
b = (u16)((bTone * gray)) >> 8;
if (r > 31)
r = 31;
if (g > 31)
g = 31;
if (b > 31)
b = 31;
*palette++ = RGB2(r, g, b);
}
}
#define tCoeff data[0]
#define tCoeffTarget data[1]
#define tCoeffDelta data[2]
#define tDelay data[3]
#define tDelayTimer data[4]
#define tPalettes 5 // data[5] and data[6], set/get via Set/GetWordTaskArg
#define tColor data[7]
#define tId data[8]
// Blend the selected palettes in a series of steps toward or away from the color.
// Only used by the Groudon/Kyogre fight scene to flash the screen for lightning.
// One call is used to fade the bg from white, while another fades the duo from black
void BlendPalettesGradually(u32 selectedPalettes, s8 delay, u8 coeff, u8 coeffTarget, u16 color, u8 priority, u8 id)
{
u8 taskId;
taskId = CreateTask((void *)Task_BlendPalettesGradually, priority);
gTasks[taskId].tCoeff = coeff;
gTasks[taskId].tCoeffTarget = coeffTarget;
if (delay >= 0)
{
gTasks[taskId].tDelay = delay;
gTasks[taskId].tCoeffDelta = 1;
}
else
{
gTasks[taskId].tDelay = 0;
gTasks[taskId].tCoeffDelta = -delay + 1;
}
if (coeffTarget < coeff)
gTasks[taskId].tCoeffDelta *= -1;
SetWordTaskArg(taskId, tPalettes, selectedPalettes);
gTasks[taskId].tColor = color;
gTasks[taskId].tId = id;
gTasks[taskId].func(taskId);
}
static bool32 UNUSED IsBlendPalettesGraduallyTaskActive(u8 id)
{
int i;
for (i = 0; i < NUM_TASKS; i++)
if ((gTasks[i].isActive == TRUE)
&& (gTasks[i].func == Task_BlendPalettesGradually)
&& (gTasks[i].tId == id))
return TRUE;
return FALSE;
}
static void UNUSED DestroyBlendPalettesGraduallyTask(void)
{
u8 taskId;
while (1)
{
taskId = FindTaskIdByFunc(Task_BlendPalettesGradually);
if (taskId == TASK_NONE)
break;
DestroyTask(taskId);
}
}
static void Task_BlendPalettesGradually(u8 taskId)
{
u32 palettes;
s16 *data;
s16 target;
data = gTasks[taskId].data;
palettes = GetWordTaskArg(taskId, tPalettes);
if (++tDelayTimer > tDelay)
{
tDelayTimer = 0;
BlendPalettes(palettes, tCoeff, tColor);
target = tCoeffTarget;
if (tCoeff == target)
{
DestroyTask(taskId);
}
else
{
tCoeff += tCoeffDelta;
if (tCoeffDelta >= 0)
{
if (tCoeff < target)
return;
}
else if (tCoeff > target)
{
return;
}
tCoeff = target;
}
}
}