Color Processing

Color in Fallout

The paper describes the color algorithms (Palette => RGB, RGB => Index in the palette, animated color) used in Fallout.
Information about them was obtained in the course of reviewing relevant functions in the file Mapper2.exe and Fallout2.exe.

## 1. Transforming 'palette => RGB'

Transforming 'palette => RGB' presented to the following pattern.

```
                                    +----------------------------------+
                                    +----------------------------------+
+--------------+                                     V
+-----+--------+                     +----+-----------------------+         +-----+-------+                 +-----+-------+
+-----+--------+                     +----+-----------------------+         +-----+-------+   Red   << 2    +-----+-------+
+-----+--------+               |                                            +-----+-------+                 +-----+-------+
+-----+--------+                                                            +-----+-------+                 +-----+-------+
+-----+--------+               |                                            +-----+-------+                 +-----+-------+
+---------------------------+

```

1. While the loading the PAL file, if the value of a component of a color is not in the range 0 .. 63, the value of all components of that color piled are set to 0.
2. If the computed value is less 0, the value of the corresponding table element is set to 0. If the computed value is greater 63, the value of the appropriate element of the table is set to 63. ('pow' is the function for raising a number to a power)
3. See 3. Animated color
4. In Fallout the currentGamma variable is the type of double and varies from 1.000000 to 1.179993.

It should be noted that changes brightness algorithm used is a zero-defect and the first element of the color conversion table will never change.

## Transforming 'RGB => Index in the palette'

Transforming 'RGB => index of the palette' presented to the following pattern.

```
      Red                Green                 Blue
+---------|-----+    +---------|-----+    +---------|-----+
+---------|-----+    +---------|-----+    +---------|-----+
    +-----+              |              +-----+
          V              V              V
+-|--------------|--------------|--------------+
+-|--------------|--------------|--------------+

```

The resulting number is used as an index in the PAL file conversion table 'RGB => index of the palette'. Perhaps the tables on the relevant index and the index is desired in the palette.

## Animated color

In Fallout 'Animated color' has been used to reduce the size of image files, as do allow one frame izmenyayuschimesya with flowers instead of a few static frames with flowers.
The user can control the 'Animated color' with two parameters in files fallout2.cfg and mapper2.cfg

```
[system]
color_cycling = 1
cycle_speed_factor = 1

```

The value color\_cycling allowed (1) / prohibited (0) animation colors.
Cycle\_speed\_factor parameter governs the speed of animation, the higher the parameter so less frequently change color.
There are six groups Animated in primary colors with different parameters (see Table 3.1).

Table 3.1 - Initial parameters teams' colors Animirovannyh '

Note:
Periods of changes have been introduced to cycle\_speed\_factor = 1

Laws change colors appear below in the form of function.

```
// palette
BYTE g_Palette [768]

// Start Value color
BYTE g_nSlime [] = (0, 108, 0, 11, 115, 7, 27, 123, 15, 43, 131, 27);                            // Mucus
BYTE g_nMonitors [] = (107, 107, 111, 99, 103, 127, 87, 107, 143, 0, 147, 163, 107, 187, 255);   // Monitors
BYTE g_nFireSlow [] = (255, 0, 0, 215, 0, 0, 147, 43, 11, 255, 119, 0, 255, 59, 0);              // Medlennogoryaschy fire
BYTE g_nFireFast [] = (71, 0, 0, 123, 0, 0, 179, 0 , 0, 123, 0, 0, 71, 0, 0);                    // Bystrogoryaschy fire
BYTE g_nShoreline [] = (83, 63, 43, 75, 59, 43, 67, 55, 39, 63, 51, 39, 55, 47, 35, 51, 43, 35); // Seaside
BYTE g_nBlinkingRed = 252;                                                                       // Signaling

// Current value of the parameter cycle
DWORD g_dwSlimeCurrent = 0;
DWORD g_dwMonitorsCurrent = 0;
DWORD g_dwFireSlowCurrent = 0;
DWORD g_dwFireFastCurrent = 0;
DWORD g_dwShorelineCurrent = 0;
BYTE g_nBlinkingRedCurrent = 0;

// Time previous change in color
DWORD g_dwLastCycleSlow = 0;
DWORD g_dwLastCycleMedium = 0;
DWORD g_dwLastCycleFast = 0;
DWORD g_dwLastCycleVeryFast = 0;

// Current value koeffitsieta speed animation
DWORD g_dwCycleSpeedFactor = 1;

void AnimatePalette()
{
	BOOL bPaletteChanged = FALSE;
	DWORD dwCurrentTime = GetTickCount();

	if (dwCurrentTime - g_dwLastCycleSlow >= 200 * g_dwCycleSpeedFactor) {
		// Slime
		DWORD dwSlimeCurrentWork = g_dwSlimeCurrent;

		for (int i = 3; i >= 0, i--) {
			g_Palette[687 + i * 3] = g_nSlime[dwSlimeCurrentWork * 3] >> 2;             // Red
			g_Palette[687 + i * 3 + 1] = g_nSlime[dwSlimeCurrentWork * 3 + 1] >> 2;     // Green
			g_Palette[687 + i * 3 + 2] = g_nSlime[dwSlimeCurrentWork * 3 + 2] >> 2;     // Blue

			if (dwSlimeCurrentWork == 3)
				dwSlimeCurrentWork = 0;
			else
				dwSlimeCurrentWork++;
		}

		if (g_dwSlimeCurrent == 3)
			g_dwSlimeCurrent = 0;
		else
			g_dwSlimeCurrent++;

		// Shoreline
		DWORD dwShorelineCurrentWork = g_dwShorelineCurrent;

		for (int i = 5; i >= 0, i--) {
			g_Palette[744 + i * 3] = g_nShoreline[dwShorelineCurrentWork * 3] >> 2;            // Red
			g_Palette[744 + i * 3 + 1] = g_nShoreline[dwShorelineCurrentWork * 3 + 1] >> 2;    // Green
			g_Palette[744 + i * 3 + 2] = g_nShoreline[dwShorelineCurrentWork * 3 + 2] >> 2;    // Blue

			if (dwShorelineCurrentWork == 5)
				dwShorelineCurrentWork = 0;
			else
				dwShorelineCurrentWork++;
		}

		if (g_dwShorelineCurrent == 5)
			g_dwShorelineCurrent = 0;
		else
			g_dwShorelineCurrent++;

		// Fire_slow
		DWORD dwFireSlowCurrentWork = g_dwFireSlowCurrent;

		for (int i = 4; i >= 0, i--) {
			g_Palette[714 + i * 3] = g_nFireSlow[dwFireSlowCurrentWork * 3] >> 2;         // Red
			g_Palette[714 + i * 3 + 1] = g_nFireSlow[dwFireSlowCurrentWork * 3 + 1] >> 2; // Green
			g_Palette[714 + i * 3 + 2] = g_nFireSlow[dwFireSlowCurrentWork * 3 + 2] >> 2; // Blue

			if (dwFireSlowCurrentWork == 4)
				dwFireSlowCurrentWork = 0;
			else
				dwFireSlowCurrentWork++;
		}

		if (g_dwFireSlowCurrent == 4)
			g_dwFireSlowCurrent = 0;
		else
			g_dwFireSlowCurrent++;

		g_dwLastCycleSlow = dwCurrentTime;
		bPaletteChanged = TRUE;
	}

	dwCurrentTime = GetTickCount()

	if (dwCurrentTime - g_dwLastCycleMedium >= 142 * g_dwCycleSpeedFactor) {
		// Fire_fast
		DWORD dwFireFastCurrentWork = g_dwFireFastCurrent;
		for (int i = 4; i >= 0, i--) {
			g_Palette[729 + i * 3] = g_nFireFast[dwFireFastCurrentWork * 3] >> 2;         // Red
			g_Palette[729 + i * 3 + 1] = g_nFireFast[dwFireFastCurrentWork * 3 + 1] >> 2; // Green
			g_Palette[729 + i * 3 + 2] = g_nFireFast[dwFireFastCurrentWork * 3 + 2] >> 2; // Blue

		if (dwFireFastCurrentWork == 4)
			dwFireFastCurrentWork = 0;
		else
			dwFireFastCurrentWork++;
	}

	if (g_dwFireFastCurrent == 4)
		g_dwFireFastCurrent = 0;
	else
		g_dwFireFastCurrent++;

		g_dwLastCycleMedium = dwCurrentTime;
		bPaletteChanged = TRUE;
	}

	dwCurrentTime = GetTickCount();

	if (dwCurrentTime - g_dwLastCycleFast >= 100 * g_dwCycleSpeedFactor) {
		// Monitors
		DWORD dwMonitorsCurrentWork = g_dwMonitorsCurrent;

		for (int i = 4; i >= 0, i--) {
			g_Palette[699 + i * 3] = g_nMonitors[dwMonitorsCurrentWork * 3] >> 2; // Red
			g_Palette[699 + i * 3 + 1] = g_nMonitors[dwMonitorsCurrentWork * 3 + 1] >> 2; // Green
			g_Palette[699 + i * 3 + 2] = g_nMonitors[dwMonitorsCurrentWork * 3 + 2] >> 2; // Blue

			if (dwMonitorsCurrentWork == 4)
				dwMonitorsCurrentWork = 0;
			else
				dwMonitorsCurrentWork++;
		}

		if (g_dwMonitorsCurrent == 4)
			g_dwMonitorsCurrent = 0;
		else
			g_dwMonitorsCurrent++;

		g_dwLastCycleFast = dwCurrentTime;
		bPaletteChanged = TRUE;
	}

	dwCurrentTime = GetTickCount();

	if (dwCurrentTime - g_dwLastCycleVeryFast >= 33 * g_dwCycleSpeedFactor) {
		// Blinking red
		if ((g_nBlinkingRedCurrent == 0) || (g_nBlinkingRedCurrent == 60))
			g_nBlinkingRed = BYTE(-g_nBlinkingRed);

		g_Palette [762] = g_nBlinkingRed + g_nBlinkingRedCurrent; // Red
		g_Palette [763] = 0;                                      // Green
		g_Palette [764] = 0;                                      // Blue

		g_nBlinkingRedCurrent = g_nBlinkingRed + g_nBlinkingRedCurrent;

		g_dwLastCycleVeryFast = dwCurrentTime;
		bPaletteChanged = TRUE;
	}

	if (bPaletteChanged)
		UpdatePalette ();
}

```