Alchemy
Loading...
Searching...
No Matches

color.c

Go to the documentation of this file.
1/// @file
2/// The colour buffer: palettes moved toward target colours.
3/// Part of object.c, the field object, compiled only through it.
4
6
7// Palette work stores 224 BG colours and 224 OBJ colours, each as B, G, R.
8#define HEAP_SLOT_PALBUF 32 ///< the colour buffer (gPaletteBufferWork)
9#define COLOR_MAX 448
10#define COLOR_HALF 224
11#define COLOR_CHANNEL_COUNT (COLOR_MAX * 3)
12
13/// Sets each of the 1344 colour channels of step to the amount to add each
14/// frame for source to reach target in frames frames; with frames 0 or less
15/// step is left as it was. Called by BattleFx_StartBufferInterpolation and by
16/// the weather effects (psynergy_effects/weather.c).
17void BattleFx_InterpolateBuffers(s16 *source, s16 *target, s16 *step,
18 s32 frames) {
19 s32 remainingChannels;
20 s32 sourceChannel;
21 s32 targetChannel;
22
23 if (frames > 0) {
24 remainingChannels = COLOR_CHANNEL_COUNT - 1;
25 do {
26 sourceChannel = *source;
27 targetChannel = *target;
28 *step = Iwram_SignedDivide(targetChannel - sourceChannel, frames);
29 remainingChannels--;
30 source++;
31 target++;
32 step++;
33 } while (remainingChannels >= 0);
34 }
35}
36
37/// Each frame (unless flag 0x152 is set): moves the colour buffer one step
38/// toward its target, packs it into RGB555 and queues it for the BG and OBJ
39/// palettes. The packed colours are double-buffered: work->page names the
40/// page the queued DMA reads, and the other is written. Registered by
41/// BattleEffect_InitializeBuffers as a scheduler callback; the menus clear
42/// and set flag 0x152 around it (menu/common.c).
45 u16 *channelDelta = work->delta;
46 s32 i;
47
48 if (GameFlag_TestFar(0x152) != 0) {
49 return;
50 }
51 if (work->duration == 0) {
52 return;
53 }
54 // Accumulate modular 16-bit channel steps; on the last frame copy the
55 // target itself, so rounding in the steps never leaves the colours short.
56 if (++work->step < work->duration) {
57 u16 *currentChannel = work->current;
58 for (i = 0; i < COLOR_CHANNEL_COUNT; i++) {
59 *currentChannel++ += *channelDelta++;
60 }
61 } else {
62 Iwram_CopyWords(work->current, work->target, sizeof(work->current));
63 work->duration = 0;
64 }
65
66 {
67 // Pack B, G, R fixed-point channels into the hidden RGB555 page: each
68 // channel's top 5 bits (bits 14..10) become its RGB555 field.
69 u16 *packed = work->packed[1 ^ work->page];
70 s32 blueMask = 0x7c00;
71 u16 *current;
72
73 i = COLOR_MAX;
74 current = work->current;
75 for (; i != 0; i--) {
76 *packed++ = (current[0] & blueMask) | (((s16)current[1] >> 5) & 0x3e0) |
77 (((s16)current[2] >> 10) & 0x1f);
78 current += 3;
79 }
80 }
81
82 // Show the page just written: queue its BG half and OBJ half (224 colours,
83 // 112 words each) for the palettes.
84 work->page ^= 1;
85 {
86 u16 *packed = work->packed[work->page];
87
89 (DMA_ENABLE | DMA_32BIT) << 16 | (COLOR_HALF / 2));
91 (DMA_ENABLE | DMA_32BIT) << 16 | (COLOR_HALF / 2));
92 }
93}
94
95// Curves mapping a 5-bit channel to the buffer's channel shifted up 10 bits.
99
100/// Builds colours for the colour buffer from the palette src into dst, which
101/// holds each colour as B, G and R channels shifted up 10 bits. mode below
102/// 0x8000 is one colour for all; PALETTE_TRANSFORM_FROM_SOURCE the backdrop
103/// colour; the other PALETTE_TRANSFORM_* values transform each colour; any
104/// other value is the address of a ready buffer to copy. half: 0 for all 448
105/// colours, 1 for the BG's 224, 2 for the OBJ's 224. Called by the
106/// ApplyColorTo functions below, BattleEffect_InitializeBuffers and the
107/// weather effects (psynergy_effects/weather.c).
108void BattleFx_BuildBuffer(u32 mode, u16 *src, u16 *dst, s32 half) {
109 u32 i;
110 u32 colorCount = COLOR_MAX;
111 s32 red;
112 s32 blue;
113 u32 color;
114 s32 green;
115
117 mode = *(u16 *)BG_PLTT;
118 if (half == 1) {
119 colorCount = COLOR_HALF;
120 } else if (half == 2) {
121 dst += COLOR_HALF * 3;
122 src += COLOR_HALF;
123 colorCount = COLOR_HALF;
124 }
125
126 // One colour: write it once, then let DMA copy it over the rest. The copy
127 // reads three halfwords behind where it writes, so the colour repeats.
129 *dst++ = mode & 0x7c00;
130 *dst++ = (mode & 0x03e0) << 5;
131 *dst++ = (mode & 0x001f) << 10;
132 Dma_Set(dst - 3, dst, (((colorCount - 1) * 6) >> 1) | (DMA_ENABLE << 16),
133 REG_DMA3);
134 } else if (mode < PALETTE_TRANSFORM_FLAGS) {
135 switch (mode) {
136 case PALETTE_TRANSFORM_GREY: // grey of brightness (2R + 4G + B) / 7
137 for (i = 0; i < colorCount; i++) {
138 color = *src++;
139 red = ((color << 11) & 0xf800);
140 green = ((color << 7) & 0x1f000);
141 blue = (color & 0x7c00);
142 color = Iwram_SignedDivide(red + green + blue, 7);
143 *dst++ = color;
144 *dst++ = color;
145 *dst++ = color;
146 }
147 break;
148 case PALETTE_TRANSFORM_SEPIA: // the brightness, red stronger, within 8 to
149 // 28
150 for (i = 0; i < colorCount; i++) {
151 color = *src++;
152 red = color & 31;
153 color = Iwram_SignedDivide(
154 red + ((color >> 5) & 31) + ((color >> 10) & 31), 10);
155 red = color * 4 + 5;
156 blue = color * 3 + 5;
157 green = blue;
158 if (red < 8)
159 red = 8;
160 if (green < 8)
161 green = 8;
162 if (blue < 8)
163 blue = 8;
164 if (red > 28)
165 red = 28;
166 if (green > 28)
167 green = 28;
168 if (blue > 28)
169 blue = 28;
171 *dst++ = gBattlePalette_ChannelCurveLight[green];
173 }
174 break;
175 case PALETTE_TRANSFORM_WARM: // half R + 10, two thirds G + 8, B - 7 (black
176 // stays black)
177 for (i = 0; i < colorCount; i++) {
178 color = *src++;
179 red = color & 31;
180 green = (color >> 5) & 31;
181 blue = (color >> 10) & 31;
182 if ((red | green | blue) != 0) {
183 red -= (u32)red >> 1;
184 green -= green / 3;
185 red += 10;
186 green += 8;
187 blue -= 7;
189 green = BattleFx_ClampRgb555Channel(green);
190 blue = BattleFx_ClampRgb555Channel(blue);
191 }
193 *dst++ = gBattlePalette_ChannelCurveLight[green];
194 *dst++ = gBattlePalette_ChannelCurve[red];
195 }
196 break;
197 case PALETTE_TRANSFORM_MUTED: // narrow the range, G and B + 2
198 for (i = 0; i < colorCount; i++) {
199 color = *src++;
200 red = color & 31;
201 green = (color >> 5) & 31;
202 blue = (color >> 10) & 31;
203 if (red < 10)
204 red = 10;
205 if (green < 16)
206 green = 16;
207 if (blue < 16)
208 blue = 16;
209 if (red > 28)
210 red = 28;
211 if (green > 24)
212 green = 24;
213 if (blue > 26)
214 blue = 26;
215 green += 2;
216 blue += 2;
218 green = BattleFx_ClampRgb555Channel(green);
219 blue = BattleFx_ClampRgb555Channel(blue);
220 *dst++ = gBattlePalette_ChannelCurve[blue];
221 *dst++ = gBattlePalette_ChannelCurve[green];
222 *dst++ = gBattlePalette_ChannelCurve[red];
223 }
224 break;
225 case PALETTE_TRANSFORM_PALE: // half of each, plus the mean
226 for (i = 0; i < colorCount; i++) {
227 s32 mean;
228
229 color = *src++;
230 red = color & 31;
231 green = (color >> 5) & 31;
232 blue = (color >> 10) & 31;
233 mean = BattleFx_ClampRgb555Channel((red + green + blue) / 3);
234 red = (red >> 1) + mean;
235 green = (green >> 1) + mean;
236 blue = (blue >> 1) + mean;
238 green = BattleFx_ClampRgb555Channel(green);
239 blue = BattleFx_ClampRgb555Channel(blue);
243 dst += 3;
244 }
245 break;
246 case PALETTE_TRANSFORM_RED: // an eighth of G and B added to R; G and B two
247 // thirds
248 for (i = 0; i < colorCount; i++) {
249 color = *src++;
250 red = color & 31;
251 green = (color >> 5) & 31;
252 blue = (color >> 10) & 31;
253 red += ((green >> 3) + (blue >> 3));
255 green -= green / 3;
256 blue -= blue / 3;
257 dst[0] = gBattlePalette_ChannelCurve[blue];
258 dst[1] = gBattlePalette_ChannelCurve[green];
260 dst += 3;
261 }
262 break;
263 case PALETTE_TRANSFORM_WARM2: // half R + 6, two thirds G + 4, B - 6
264 for (i = 0; i < colorCount; i++) {
265 color = *src++;
266 red = color & 31;
267 green = (color >> 5) & 31;
268 blue = (color >> 10) & 31;
269 red -= (u32)red >> 1;
270 green -= green / 3;
271 red += 6;
272 green += 4;
273 blue -= 6;
275 green = BattleFx_ClampRgb555Channel(green);
276 blue = BattleFx_ClampRgb555Channel(blue);
278 dst[1] = gBattlePalette_ChannelCurveLight[green];
279 dst[2] = gBattlePalette_ChannelCurve[red];
280 dst += 3;
281 }
282 break;
283 default: // unchanged
284 for (i = 0; i < colorCount; i++) {
285 color = *src++;
286 dst[i * 3 + 0] = color & 0x7c00;
287 dst[i * 3 + 1] = (color & 0x03e0) << 5;
288 dst[i * 3 + 2] = (color & 0x001f) << 10;
289 }
290 break;
291 }
292 } else if (mode & PALETTE_TRANSFORM_TINT) {
293 // The brightness (2R + 4G + B) times the colour in mode.
294 u32 tintRed;
295 u32 tintGreen;
296 u32 tintBlue;
297 s32 red;
298 s32 green;
299
300 tintRed = mode & 31;
301 tintGreen = (mode >> 5) & 31;
302 tintBlue = (mode >> 10) & 31;
303 for (i = 0; i < colorCount; i++) {
304 color = *src++;
305 red = ((color << 11) & 0xf800);
306 green = ((color << 7) & 0x1f000);
307 blue = (color & 0x7c00);
308 color = Iwram_SignedDivide(red + green + blue, 96);
309 red = color * tintRed;
310 green = color * tintGreen;
311 blue = color * tintBlue;
313 green = BattleFx_ClampRgb555Component(green);
315 dst[0] = blue;
316 dst[1] = green;
317 dst[2] = red;
318 dst += 3;
319 }
320 } else if (mode & PALETTE_TRANSFORM_BLEND) {
321 // The brightness R + G + B shared out in the proportions of mode's colour.
322 u32 tintRed;
323 u32 tintGreen;
324 u32 tintBlue;
325 s32 blendedRed;
326 s32 blendedGreen;
327 s32 blendedBlue;
328
329 tintRed = mode & 31;
330 tintGreen = (mode >> 5) & 31;
331 tintBlue = (mode >> 10) & 31;
332 for (i = 0; i < colorCount; i++) {
333 color = *src++;
334 red = color & 31;
335 color = Iwram_SignedDivide(
336 (red + ((color >> 5) & 31) + ((color >> 10) & 31)) << 4,
337 tintRed + tintGreen + tintBlue);
338 blendedRed = Iwram_MulQ16(((color * tintRed) >> 4) << 16,
339 (s32)(tintRed << 16) >> 4);
340 blendedGreen = Iwram_MulQ16(((color * tintGreen) >> 4) << 16,
341 (s32)(tintGreen << 16) >> 4);
342 blendedBlue = Iwram_MulQ16(((color * tintBlue) >> 4) << 16,
343 (s32)(tintBlue << 16) >> 4);
344 blendedRed = BattleFx_ClampRgb555Channel((u32)blendedRed >> 16);
345 blendedGreen = BattleFx_ClampRgb555Channel((u32)blendedGreen >> 16);
346 blendedBlue = BattleFx_ClampRgb555Channel((u32)blendedBlue >> 16);
347 *dst++ = gBattlePalette_ChannelCurve[blendedBlue];
348 *dst++ = gBattlePalette_ChannelCurve[blendedGreen];
349 *dst++ = gBattlePalette_ChannelCurve[blendedRed];
350 }
351 } else if (mode & PALETTE_TRANSFORM_SPLIT) {
352 // The colours unchanged, as the default transform writes them.
353 for (i = 0; i < colorCount; i++) {
354 color = *src++;
355 dst[i * 3 + 0] = color & 0x7c00;
356 dst[i * 3 + 1] = (color & 0x03e0) << 5;
357 dst[i * 3 + 2] = (color & 0x001f) << 10;
358 }
359 } else {
360 // Copy a ready buffer: mode is its address, and the OBJ half starts 224
361 // colours of three halfwords in.
362 if (half == 2)
363 mode += COLOR_HALF * 3 * sizeof(u16);
364 Dma_Set((const void *)mode, dst,
365 ((colorCount * 6) >> 2) | (DMA_ENABLE | DMA_32BIT) << 16, REG_DMA3);
366 }
367}
368
369/// Sets up the colour buffer (heap block 32) from the BG and OBJ palettes and
370/// starts updating the palettes from it each frame. Called by Field_RunScene
371/// on scene load; allocates heap slot 32 (gPaletteBufferWork) and registers
372/// BattlePalette_UpdateBlend at priority 0xc8f.
374 struct BattleEffectBuffers *work;
375
376 work = Runtime_AllocateBlock(HEAP_SLOT_PALBUF, sizeof(*work));
377 Dma_Fill32(0, work, sizeof(*work));
378 Dma_Set((void *)BG_PLTT, &work->palette[0],
379 (DMA_ENABLE | DMA_32BIT) << 16 | (COLOR_HALF / 2), REG_DMA3);
380 Dma_Set((void *)OBJ_PLTT, &work->palette[COLOR_HALF],
381 (DMA_ENABLE | DMA_32BIT) << 16 | (COLOR_HALF / 2), REG_DMA3);
384}
385
386/// Stops the colour buffer and frees it: removes BattlePalette_UpdateBlend and
387/// releases heap slot 32. Reached only through a bare far-call veneer; no ☀️
388/// code names it.
392}
393
394/// Sets the colours the buffer moves toward (see BattleFx_BuildBuffer); does
395/// nothing without a colour buffer. Called by the field Psynergy effects,
396/// the scene transitions, the shop cursor, and scene overlays as
397/// Engine_ColorBufferApplyTarget.
400
401 if (work != NULL) {
402 BattleFx_BuildBuffer(mode, work->palette, work->target, half);
403 }
404}
405
406/// Sets the colours the buffer shows now (see BattleFx_BuildBuffer). Called by
407/// Reveal, the leader's steps (step.c), the transitions, and scene overlays
408/// as Engine_ColorBufferApplySource.
411
412 if (work != NULL) {
413 BattleFx_BuildBuffer(mode, work->palette, work->current, half);
414 }
415}
416
417/// Sets the buffer's source backdrop colour (BG colour 0 of its palette
418/// copy). Called by the transitions (transition.c).
421
422 if (work != NULL) {
423 work->palette[0] = color;
424 }
425}
426
427/// Starts moving the shown colours to the target colours over frames frames:
428/// sets work->duration, clears work->step and fills work->delta. Called after
429/// BattleFx_ApplyColorToTargetBuffer by the same callers, and by scene
430/// overlays as Engine_ColorBufferInterpolate.
433
434 if (work != NULL) {
435 work->duration = frames;
436 work->step = 0;
438 frames);
439 }
440}
441
442/// Clamps a 5-bit colour channel to 0 to 31.
444 if (value > 31)
445 return 31;
446 if (value < 0)
447 value = 0;
448 return value;
449}
450
451/// Clamps a colour channel shifted up 10 bits to at most 31 << 10.
453 // 31744 is 31 << 10.
454 if (value > 31744)
455 value = 31744;
456 return value;
457}
458
459#undef HEAP_SLOT_PALBUF
460#undef COLOR_MAX
461#undef COLOR_HALF
462#undef COLOR_CHANNEL_COUNT
struct BattleEffectBuffers * gPaletteBufferWork
void BattlePalette_StopBlendAndReleaseBuffer(void)
Stops the colour buffer and frees it: removes BattlePalette_UpdateBlend and releases heap slot 32.
Definition color.c:389
#define COLOR_MAX
Definition color.c:9
void BattleFx_InterpolateBuffers(s16 *source, s16 *target, s16 *step, s32 frames)
Sets each of the 1344 colour channels of step to the amount to add each frame for source to reach tar...
Definition color.c:17
void BattleFx_ApplyColorToTargetBuffer(s32 mode, s32 half)
Sets the colours the buffer moves toward (see BattleFx_BuildBuffer); does nothing without a colour bu...
Definition color.c:398
void BattlePalette_UpdateBlend(void)
Each frame (unless flag 0x152 is set): moves the colour buffer one step toward its target,...
Definition color.c:43
s32 BattleFx_ClampRgb555Component(s32 value)
Clamps a colour channel shifted up 10 bits to at most 31 << 10.
Definition color.c:452
void BattleEffect_InitializeBuffers(void)
Sets up the colour buffer (heap block 32) from the BG and OBJ palettes and starts updating the palett...
Definition color.c:373
void BattleFx_SetPrimaryBufferValue(u32 color)
Sets the buffer's source backdrop colour (BG colour 0 of its palette copy).
Definition color.c:419
void BattleFx_BuildBuffer(u32 mode, u16 *src, u16 *dst, s32 half)
Builds colours for the colour buffer from the palette src into dst, which holds each colour as B,...
Definition color.c:108
#define COLOR_CHANNEL_COUNT
Definition color.c:11
#define COLOR_HALF
Definition color.c:10
s32 BattleFx_ClampRgb555Channel(s32 value)
Clamps a 5-bit colour channel to 0 to 31.
Definition color.c:443
#define HEAP_SLOT_PALBUF
the colour buffer (gPaletteBufferWork)
Definition color.c:8
void BattleFx_StartBufferInterpolation(s32 frames)
Starts moving the shown colours to the target colours over frames frames: sets work->duration,...
Definition color.c:431
void BattleFx_ApplyColorToSourceBuffer(s32 mode, s32 half)
Sets the colours the buffer shows now (see BattleFx_BuildBuffer).
Definition color.c:409
static __inline__ void Dma_Set(const volatile void *source, volatile void *destination, u32 control, volatile u32 *channel)
Starts a DMA transfer: stores the source, destination and control words into channel's registers with...
Definition dma.h:62
#define Dma_Fill32(value, destination, size)
Fills size bytes at destination with the word value by DMA3.
Definition dma.h:30
s32 GameFlag_TestFar(s32 flag)
const u16 gBattlePalette_ChannelCurveContrast[32]
Definition fxdata.c:154
const u16 gBattlePalette_ChannelCurveLight[32]
Definition fxdata.c:147
const u16 gBattlePalette_ChannelCurve[32]
Colour channel curves (0 to 31, to channels up to 0x7c00).
Definition fxdata.c:140
void * Runtime_AllocateBlock(s32 kind, s32 size)
As Runtime_AllocateHeapBlock, but tries EWRAM first, for blocks that need room more than speed: the b...
Definition heap.c:80
#define OBJ_PLTT
Definition io_reg.h:159
#define DMA_32BIT
Definition io_reg.h:210
#define REG_DMA3
Definition io_reg.h:153
#define BG_PLTT
Definition io_reg.h:157
#define DMA_ENABLE
Definition io_reg.h:217
#define IO_WRITE_QUEUE_PUSH(address, value, control)
Disables interrupts to add one register write or DMA to the queue; a write to a full queue is dropped...
static s32 Iwram_CopyWords(volatile void *destination, const volatile void *source, s32 size)
Definition iwram_call.h:85
static s32 Iwram_SignedDivide(s32 dividend, s32 divisor)
Definition iwram_call.h:73
#define Iwram_MulQ16(left, right)
Definition iwram_call.h:127
void Runtime_ReleaseHeapBlock(s32 slot)
What every part of object.c shares: its includes and the macros and types more than one part uses.
@ PALETTE_TRANSFORM_GREY
grey of brightness (2R + 4G + B) / 7
Definition palbuf.h:73
@ PALETTE_TRANSFORM_MUTED
Definition palbuf.h:76
@ PALETTE_TRANSFORM_BLEND
The brightness R + G + B shared out in the proportions of the colour carried.
Definition palbuf.h:88
@ PALETTE_TRANSFORM_PLAIN
each colour unchanged
Definition palbuf.h:72
@ PALETTE_TRANSFORM_PALE
Definition palbuf.h:77
@ PALETTE_TRANSFORM_FROM_SOURCE
The backdrop colour (palette RAM's first colour) for all.
Definition palbuf.h:71
@ PALETTE_TRANSFORM_WARM
Definition palbuf.h:75
@ PALETTE_TRANSFORM_SEPIA
Definition palbuf.h:74
@ PALETTE_TRANSFORM_SPLIT
each colour unchanged
Definition palbuf.h:89
@ PALETTE_TRANSFORM_WARM2
half R + 6, two thirds G + 4, B - 6
Definition palbuf.h:79
@ PALETTE_TRANSFORM_FLAGS
From here the TINT, BLEND and SPLIT bits are tested, TINT and BLEND carrying an RGB555 colour in the ...
Definition palbuf.h:83
@ PALETTE_TRANSFORM_RED
Definition palbuf.h:78
@ PALETTE_TRANSFORM_TINT
The brightness (2R + 4G + B) times the colour carried.
Definition palbuf.h:85
s32 Scheduler_AddOrUpdateCallback(SchedulerCallback callback, s32 order)
Schedules callback with order, or gives a task already running it the new order, and re-sorts the tab...
Definition scheduler.c:102
s32 Scheduler_RemoveCallback(SchedulerCallback callback)
Unschedules the task running callback: clears its routine and gives it order 0x7fff,...
Definition scheduler.c:146
#define NULL
Definition bilibin.h:6
The colour buffer (heap block 32) that fades the BG and OBJ palettes: the source palette,...
Definition palbuf.h:40
u16 current[0x540]
The shown colours, as B, G and R channels shifted up 10 bits, three a colour.
Definition palbuf.h:46
u16 target[0x540]
the colours current moves toward, as current
Definition palbuf.h:47
u16 packed[2][0x1c0]
Two pages of RGB555 colours: one is built while the other is queued.
Definition palbuf.h:50
s8 step
frames
Definition palbuf.h:53
u8 page
the packed page queued this frame
Definition palbuf.h:51
s8 duration
frames; 0 when the colours are not moving
Definition palbuf.h:52
u16 palette[0x1c0]
The source palette, RGB555: 224 BG colours, then 224 OBJ colours; palette[0] is the backdrop.
Definition palbuf.h:43
u16 delta[0x540]
added to each current channel each frame
Definition palbuf.h:48
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed short s16
Definition types.h:12
unsigned short u16
Definition types.h:13