1 /*
2  *  Abuse - dark 2D side-scrolling platform game
3  *  Copyright (c) 1995 Crack dot Com
4  *  Copyright (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
5  *
6  *  This software was released into the Public Domain. As with most public
7  *  domain software, no warranty is made or implied by Crack dot Com, by
8  *  Jonathan Clark, or by Sam Hocevar.
9  */
10 
11 #if defined HAVE_CONFIG_H
12 #   include "config.h"
13 #endif
14 
15 #include "common.h"
16 
17 #include "image.h"
18 #include "filter.h"
19 
Filter(int colors)20 Filter::Filter(int colors)
21 {
22     CONDITION(colors >= 0 && colors <= 256, "bad colors value");
23     m_size = colors;
24     m_table = (uint8_t *)malloc(m_size);
25     memset(m_table, 0, m_size * sizeof(*m_table));
26 }
27 
28 // Creates a conversion filter from one palette to another
Filter(palette * from,palette * to)29 Filter::Filter(palette *from, palette *to)
30 {
31     m_size = Max(from->pal_size(), to->pal_size());
32     m_table = (uint8_t *)malloc(m_size);
33 
34     uint8_t *dst = m_table;
35     uint8_t *src = (uint8_t *)from->addr();
36     int dk = to->darkest(1);
37 
38     for (int i = 0; i < m_size; i++)
39     {
40        int r = *src++;
41        int g = *src++;
42        int b = *src++;
43        int color = to->find_closest(r, g, b);
44 
45        // Make sure non-blacks don't get remapped to the transparency
46        if ((r || g || b) && to->red(color) == 0
47             && to->green(color) == 0 && to->blue(color) == 0)
48            color = dk;
49 
50        *dst++ = color;
51     }
52 }
53 
~Filter()54 Filter::~Filter()
55 {
56     free(m_table);
57 }
58 
Set(int color_num,int change_to)59 void Filter::Set(int color_num, int change_to)
60 {
61     CONDITION(color_num >= 0 && color_num < m_size, "Bad colors_num");
62     m_table[color_num] = change_to;
63 }
64 
Apply(image * im)65 void Filter::Apply(image *im)
66 {
67     im->Lock();
68     uint8_t *dst = im->scan_line(0);
69     int npixels = im->Size().x * im->Size().y;
70     while (npixels--)
71     {
72         CONDITION(*dst < m_size, "not enough filter colors");
73         *dst = m_table[*dst];
74         dst++;
75     }
76     im->Unlock();
77 }
78 
79 /* This is only ever used in the editor, when showing the toolbar. It
80  * does not look like it's very useful. */
PutImage(image * screen,image * im,vec2i pos)81 void Filter::PutImage(image *screen, image *im, vec2i pos)
82 {
83     int cx1, cy1, cx2, cy2, x1 = 0, y1 = 0,
84         x2 = im->Size().x, y2 = im->Size().y;
85     screen->GetClip(cx1, cy1, cx2, cy2);
86 
87     // See if the image gets clipped off the screen
88     if(pos.x >= cx2 || pos.y >= cy2 ||
89        pos.x + (x2 - x1) <= cx1 || pos.y + (y2 - y1) <= cy1)
90         return;
91 
92     x1 += Max(cx1 - pos.x, 0);
93     y1 += Max(cy1 - pos.y, 0);
94     pos.x = Max(pos.x, cx1);
95     pos.y = Max(pos.y, cy1);
96     x2 = Min(x2, cx2 - pos.x + x1);
97     y2 = Min(y2, cy2 - pos.y + y1);
98 
99     if(x1 >= x2 || y1 >= y2)
100         return;
101 
102     int xl = x2 - x1;
103     int yl = y2 - y1;
104 
105     screen->AddDirty(pos.x, pos.y, pos.x + xl, pos.y + yl);
106 
107     screen->Lock();
108     im->Lock();
109 
110     for(int j = 0; j < yl; j++)
111     {
112         uint8_t *source = im->scan_line(y1 + j) + x1;
113         uint8_t *dest = screen->scan_line(pos.y + j) + pos.x;
114 
115         for(int i = 0; i < xl; i++, source++, dest++)
116             if (*source)
117                 *dest = m_table[*source];
118     }
119 
120     im->Unlock();
121     screen->Unlock();
122 }
123 
ColorFilter(palette * pal,int color_bits)124 ColorFilter::ColorFilter(palette *pal, int color_bits)
125 {
126     int max = pal->pal_size();
127     int mul = 1 << (8 - color_bits);
128     m_size = 1 << color_bits;
129     m_table = (uint8_t *)malloc(m_size * m_size * m_size);
130 
131     /* For each colour in the RGB cube, find the nearest palette element. */
132     for (int r = 0; r < m_size; r++)
133     for (int g = 0; g < m_size; g++)
134     for (int b = 0; b < m_size; b++)
135     {
136         int best = 256 * 256 * 3;
137         int color = 0;
138         uint8_t *pp = (uint8_t *)pal->addr();
139 
140         for (int i = 0; i < max; i++)
141         {
142             int rd = *pp++ - r * mul,
143                 gd = *pp++ - g * mul,
144                 bd = *pp++ - b * mul;
145 
146             int dist = rd * rd + bd * bd + gd * gd;
147             if (dist < best)
148             {
149                 best = dist;
150                 color = i;
151             }
152         }
153         m_table[(r * m_size + g) * m_size + b] = color;
154     }
155 }
156 
ColorFilter(spec_entry * e,bFILE * fp)157 ColorFilter::ColorFilter(spec_entry *e, bFILE *fp)
158 {
159     fp->seek(e->offset, 0);
160     m_size = fp->read_uint16();
161     m_table = (uint8_t *)malloc(m_size * m_size * m_size);
162     fp->read(m_table, m_size * m_size * m_size);
163 }
164 
~ColorFilter()165 ColorFilter::~ColorFilter()
166 {
167     free(m_table);
168 }
169 
DiskUsage()170 size_t ColorFilter::DiskUsage()
171 {
172     return sizeof(uint16_t) + m_size * m_size * m_size;
173 }
174 
Write(bFILE * fp)175 int ColorFilter::Write(bFILE *fp)
176 {
177     fp->write_uint16(m_size);
178     int bytes = m_size * m_size * m_size;
179     return fp->write(m_table, bytes) == bytes;
180 }
181 
182