summaryrefslogtreecommitdiffhomepage
path: root/StarsEx/Color.h
blob: e451e91802351637fe16388f76aef67ba89c004b (plain)
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/*  Starshatter: The Open Source Project
    Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors
    Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors
    Copyright (c) 1997-2006, Destroyer Studios LLC.

    AUTHOR:       John DiCamillo


    OVERVIEW
    ========
    Universal Color Format class
*/

#ifndef Color_h
#define Color_h

#include "Types.h"

// +--------------------------------------------------------------------+

class Video;

// +--------------------------------------------------------------------+

struct ColorFormat
{
    ColorFormat(int palsize)
    : pal(palsize), bpp(8),
    rdown(0), rshift(0), rmask(0),
    gdown(0), gshift(0), gmask(0),
    bdown(0), bshift(0), bmask(0),
    adown(0), ashift(0), amask(0) { }

    ColorFormat(int size, BYTE r, BYTE rs, BYTE g, BYTE gs, BYTE b, BYTE bs, BYTE a=0, BYTE as=0)
    : pal(0), bpp(size),
    rdown(8-r), rshift(rs), rmask(((1<<r)-1)<<rs),
    gdown(8-g), gshift(gs), gmask(((1<<g)-1)<<gs),
    bdown(8-b), bshift(bs), bmask(((1<<b)-1)<<bs),
    adown(8-a), ashift(as), amask(((1<<a)-1)<<as) { }

    int   pal;
    int   bpp;
    BYTE  rdown, rshift;
    BYTE  gdown, gshift;
    BYTE  bdown, bshift;
    BYTE  adown, ashift;

    DWORD rmask;
    DWORD gmask;
    DWORD bmask;
    DWORD amask;
};

// +--------------------------------------------------------------------+

class Color
{
    friend class ColorIndex;
    friend class ColorValue;

public:
    static const char* TYPENAME() { return "Color"; }

    enum Misc  { SHADE_LEVELS = 64 }; // max 128!
    enum Mask  { RMask   = 0x00ff0000,
        GMask   = 0x0000ff00,
        BMask   = 0x000000ff,
        AMask   = 0xff000000,
        RGBMask = 0x00ffffff };
    enum Shift { RShift  = 16,
        GShift  =  8,
        BShift  =  0,
        AShift  = 24 };

    Color()               : rgba(0)      { }
    Color(const Color& c) : rgba(c.rgba) { }
    Color(BYTE r, BYTE g, BYTE b, BYTE a=255) {
        rgba = (r<<RShift)|(g<<GShift)|(b<<BShift)|(a<<AShift); }

    Color(BYTE index);

    Color&   operator= (const Color& c)       { rgba = c.rgba; return *this; }
    int      operator==(const Color& c) const { return rgba == c.rgba; }
    int      operator!=(const Color& c) const { return rgba != c.rgba; }
    Color&   operator+=(const Color& c);       // simple summation

    Color    operator+(DWORD d) const;

    Color    operator+(const Color& c) const;  // color alpha blending
    Color    operator*(const Color& c) const;  // color modulation
    Color    operator*(double scale)   const;
    Color    dim(double scale)         const;

    void     Set(DWORD value) { rgba = value; }
    void     Set(BYTE r, BYTE g, BYTE b, BYTE a=255) {
        rgba = (r<<RShift)|(g<<GShift)|(b<<BShift)|(a<<AShift); }

    DWORD    Value()  const { return rgba;  }

    void     SetRed(BYTE r)    { rgba = (rgba & ~RMask) | (r << RShift); }
    void     SetGreen(BYTE g)  { rgba = (rgba & ~GMask) | (g << GShift); }
    void     SetBlue(BYTE b)   { rgba = (rgba & ~BMask) | (b << BShift); }
    void     SetAlpha(BYTE a)  { rgba = (rgba & ~AMask) | (a << AShift); }

    DWORD    Red()    const { return (rgba & RMask) >> RShift; }
    DWORD    Green()  const { return (rgba & GMask) >> GShift; }
    DWORD    Blue()   const { return (rgba & BMask) >> BShift; }
    DWORD    Alpha()  const { return (rgba & AMask) >> AShift; }

    float    fRed()   const { return (float)(Red()  /255.0); }
    float    fGreen() const { return (float)(Green()/255.0); }
    float    fBlue()  const { return (float)(Blue() /255.0); }
    float    fAlpha() const { return (float)(Alpha()/255.0); }

    BYTE     Index()  const { return table[((rgba&RMask)>>(RShift+3)<<10)|
        ((rgba&GMask)>>(GShift+3)<< 5)|
        ((rgba&BMask)>>(BShift+3))]; }

    inline BYTE  IndexedBlend(BYTE dst) const;
    static DWORD FormattedBlend(DWORD c1, DWORD c2);

    DWORD    TextureFormat(int keep_alpha=0)  const;
    DWORD    Formatted()                      const;
    DWORD    Unfaded()                        const;
    Color    ShadeColor(int shade)            const;
    DWORD    Shaded(int shade)                const;
    Color    Faded()                          const;

    // some useful colors
    static   Color          White;
    static   Color          Black;
    static   Color          Gray;
    static   Color          LightGray;
    static   Color          DarkGray;
    static   Color          BrightRed;
    static   Color          BrightBlue;
    static   Color          BrightGreen;
    static   Color          DarkRed;
    static   Color          DarkBlue;
    static   Color          DarkGreen;
    static   Color          Yellow;
    static   Color          Cyan;
    static   Color          Magenta;
    static   Color          Tan;
    static   Color          Brown;
    static   Color          Violet;
    static   Color          Orange;

    static void UseVideo(Video* v);
    static void UseFormat(const ColorFormat& cf);
    static void UseTextureFormat(const ColorFormat& cf, int alpha_level=0);
    static void WithTextureFormat(int alpha_level=0);
    static void SetPalette(PALETTEENTRY* pal, int palsize, BYTE* invpal=0);
    static void SavePalette(const char* basename);
    static void SetFade(double f, Color c=Black, int build_shade=false);

    static const ColorFormat& GetTextureFormat(int alpha_level=0) { return texture_format[alpha_level]; }
    static const ColorFormat& GetScreenFormat()                   { return format; }

    // indexed color initialization:
    static void BuildShadeTable();
    static void SaveShadeTable(const char* basename);
    static void BuildBlendTable();

    static double GetFade()       { return fade;       }
    static Color  GetFadeColor()  { return fade_color; }

    static Color Scale(const Color& c1, const Color& c2, double scale);
    static Color Unformat(DWORD formatted_color);

private:
    DWORD    rgba;

    static   bool           standard_format;
    static   PALETTEENTRY   palette[256];
    static   BYTE           table[32768];
    static   ColorFormat    format;
    static   int            texture_alpha_level;
    static   ColorFormat    texture_format[4];
    static   double         fade;
    static   Color          fade_color;
    static   Video*         video;
};

// +--------------------------------------------------------------------+

class ColorValue
{
    friend class Color;

public:
    static const char* TYPENAME() { return "ColorValue"; }

    ColorValue()                     : r(0),   g(0),   b(0),   a(0)      { }
    ColorValue(const ColorValue& c)  : r(c.r), g(c.g), b(c.b), a(c.a)    { }
    ColorValue(const Color& c)       : r( c.fRed()   ),
    g( c.fGreen() ),
    b( c.fBlue()  ),
    a( c.fAlpha() )                   { }
    ColorValue(float ar,
    float ag,
    float ab,
    float aa=1.0f)        : r(ar),  g(ag),  b(ab),  a(aa)     { }

    int         operator==(const ColorValue& c) const { return r==c.r && g==c.g && b==c.b && a==c.a; }
    int         operator!=(const ColorValue& c) const { return r!=c.r || g!=c.g || b!=c.b || a!=c.a; }
    ColorValue& operator+=(const ColorValue& c);       // simple summation

    ColorValue  operator+(const ColorValue& c)   const;  // color alpha blending
    ColorValue  operator*(const ColorValue& c)   const;  // color modulation
    ColorValue  operator*(double scale)          const;
    ColorValue  dim(double scale)                const;

    void     Set(float ar, float ag, float ab, float aa=1.0f) { r=ar; g=ag; b=ab; a=aa; }

    void     SetRed(float ar)    { r=ar; }
    void     SetGreen(float ag)  { g=ag; }
    void     SetBlue(float ab)   { b=ab; }
    void     SetAlpha(float aa)  { a=aa; }

    float    Red()    const { return r; }
    float    Green()  const { return g; }
    float    Blue()   const { return b; }
    float    Alpha()  const { return a; }

    Color    ToColor()                        const;
    DWORD    TextureFormat(int keep_alpha=0)  const { return ToColor().TextureFormat(keep_alpha);  }
    DWORD    Formatted()                      const { return ToColor().Formatted();                }
    DWORD    Unfaded()                        const { return ToColor().Unfaded();                  }
    Color    ShadeColor(int shade)            const { return ToColor().ShadeColor(shade);          }
    DWORD    Shaded(int shade)                const { return ToColor().Shaded(shade);              }
    Color    Faded()                          const { return ToColor().Faded();                    }

private:
    float    r, g, b, a;
};

// +--------------------------------------------------------------------+

class ColorIndex
{
    friend class Color;

public:
    static const char* TYPENAME() { return "ColorIndex"; }

    ColorIndex()                        : index(0)        { }
    ColorIndex(const ColorIndex& c)     : index(c.index)  { }
    ColorIndex(BYTE r, BYTE g, BYTE b)                    { index = Color(r,g,b).Index(); }
    ColorIndex(BYTE i)                  : index(i)        { }

    ColorIndex& operator= (const ColorIndex& c)       { index = c.index; return *this; }
    int         operator==(const ColorIndex& c) const { return index == c.index; }
    int         operator!=(const ColorIndex& c) const { return index != c.index; }

    BYTE     Index()  const { return index; }

    DWORD    Red()    const { return Color::palette[index].peRed;   }
    DWORD    Green()  const { return Color::palette[index].peGreen; }
    DWORD    Blue()   const { return Color::palette[index].peBlue;  }

    float    fRed()   const { return (float)(Red()  /255.0); }
    float    fGreen() const { return (float)(Green()/255.0); }
    float    fBlue()  const { return (float)(Blue() /255.0); }

    DWORD    TextureFormat()   const { return texture_palette[index];    }
    DWORD    Unfaded()         const { return unfaded_palette[index];    }
    DWORD    Formatted()       const { return formatted_palette[index];  }
    DWORD    Shaded(int shade) const { return shade_table[shade*256+index]; }
    ColorIndex Faded()         const { return ColorIndex(index);         }

    // note: this will only work in 8-bit color mode...
    ColorIndex ShadeColor(int s) const { return ColorIndex((BYTE)(shade_table[s*256+index])); }

    // for poly shading optimization
    static DWORD*  ShadeTable()      { return shade_table; }

    BYTE     IndexedBlend(BYTE dst) const { return blend_table[dst*256+index]; }

private:
    BYTE     index;

    static   DWORD*   texture_palette;
    static   DWORD    texture_palettes[4][256];
    static   DWORD    unfaded_palette[256];
    static   DWORD    formatted_palette[256];
    static   DWORD    shade_table[256*256];
    static   BYTE     blend_table[256*256];
};

inline BYTE Color::IndexedBlend(BYTE dst) const { return ColorIndex::blend_table[dst*256+Index()]; }

#endif  // Color_h