aboutsummaryrefslogtreecommitdiffstats
path: root/liblss16_optimise.c
blob: f532a943c60a3aafdf8c7ca7725be13a55a9a256 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/* See LICENSE file for copyright and license details. */
#include "liblss16.h"
#include <string.h>

#if defined(__clang__)
# pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#endif


void
liblss16_optimise(struct liblss16_header *header, uint8_t *pixels)
{
	uint8_t remap[16], unmap[16], ncolours = 0, colour, preferred, dummy = 0;
	unsigned x, y, count, width;
	size_t p, n;
	int32_t zero[16], nonzero[16];
	int32_t zero_penalty, least;

	for (x = 0; x < 16U; x++) {
		remap[x] = (uint8_t)x;
		unmap[x] = 0;
	}

	memset(zero, 0, sizeof(zero));
	memset(nonzero, 0, sizeof(nonzero));

	for (x = 0; x < 15U; x++) {
		if (unmap[x])
			continue;
		header->colour_map[ncolours++] = header->colour_map[x];
		for (y = x + 1U; y < 16U; y++) {
			if (header->colour_map[y].r != header->colour_map[x].r ||
			    header->colour_map[y].g != header->colour_map[x].g ||
			    header->colour_map[y].b != header->colour_map[x].b)
				continue;
			unmap[y] = 1;
			remap[y] = ncolours;
		}
	}

	width = (unsigned)header->width;
	width = width < 17U ? width : 17U;
	for (y = 0, p = 0; y < (unsigned)header->height; y++) {
#ifdef HARDEN
		for (x = 0; x < (unsigned)header->width; x++)
			pixels[p + x] &= 15U;
#endif
		colour = remap[pixels[p]];
		count = 1U;
		for (x = 1U; x < width; x++, p++) {
			if (remap[pixels[p]] != colour)
				break;
			count += 1U;
		}
		p += (size_t)header->width - (size_t)x;
		zero[colour] += count < 16U ? 2 : 3;
		nonzero[colour] += count == 1 ? 1 : count < 17 ? 3 : 4;
	}

	least = zero[0] - nonzero[0];
	preferred = 0U;
	if (ncolours < 16U)
		dummy = ncolours++;
	for (x = 1U; x < (unsigned)ncolours; x++) {
		zero_penalty = zero[x] - nonzero[x];
		if (zero_penalty < least) {
			least = zero_penalty;
			preferred = (uint8_t)x;
		}
	}

	if (preferred) {
		struct liblss16_colour tc;
		uint8_t t8;
		tc = header->colour_map[preferred];
		header->colour_map[preferred] = header->colour_map[0];
		header->colour_map[0] = tc;
		t8 = remap[preferred];
		remap[preferred] = remap[0];
		remap[0] = t8;
		if (preferred == dummy) {
			header->colour_map[0].r = header->colour_map[1].r;
			header->colour_map[0].g = header->colour_map[1].r;
			header->colour_map[0].b = header->colour_map[1].r;
		}
	}

	n = p;
	for (n = p, p = 0; p < n; p++)
		pixels[p] = remap[pixels[p]];

	memset(&header->colour_map[ncolours],
	       header->colour_map[ncolours - 1U].b,
	       (16U - ncolours) * sizeof(*header->colour_map));
}