1 /*
2  *
3  *  Iter Vehemens ad Necem (IVAN)
4  *  Copyright (C) Timo Kiviluoto
5  *  Released under the GNU General
6  *  Public License
7  *
8  *  See LICENSING which should be included
9  *  along with this file for more details
10  *
11  */
12 
13 #include <cstdarg>
14 #include <memory>
15 
16 #include "png.h"
17 
18 #include "allocate.h"
19 #include "rawbit.h"
20 #include "bitmap.h"
21 #include "save.h"
22 #include "femath.h"
23 
MaskedBlit(bitmap * Bitmap,packcol16 * Color) const24 void rawbitmap::MaskedBlit(bitmap* Bitmap, packcol16* Color) const { MaskedBlit(Bitmap, ZERO_V2, ZERO_V2, Size, Color); }
25 
rawbitmap(cfestring & FileName)26 rawbitmap::rawbitmap(cfestring& FileName)
27 {
28   std::shared_ptr<FILE> File(fopen(FileName.CStr(), "rb"), fclose);
29 
30   if(!File)
31     ABORT("Bitmap %s not found!", FileName.CStr());
32 
33   png_structp PNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
34 
35   if(!PNGStruct)
36     ABORT("Couldn't read PNG file %s!", FileName.CStr());
37 
38   png_infop PNGInfo = png_create_info_struct(PNGStruct);
39 
40   if(!PNGInfo)
41     abort();
42 
43   if(setjmp(png_jmpbuf(PNGStruct)) != 0)
44     abort();
45 
46   png_init_io(PNGStruct, File.get());
47   png_read_info(PNGStruct, PNGInfo);
48 
49   Size.X = png_get_image_width(PNGStruct, PNGInfo);
50   Size.Y = png_get_image_height(PNGStruct, PNGInfo);
51 
52   if(png_get_bit_depth(PNGStruct, PNGInfo) != 8)
53     ABORT("%s has wrong bit depth %d, should be 8!", FileName.CStr(),
54           int(png_get_bit_depth(PNGStruct, PNGInfo)));
55 
56   png_colorp PNGPalette;
57   int PaletteSize;
58 
59   if(!png_get_PLTE(PNGStruct, PNGInfo, &PNGPalette, &PaletteSize))
60     ABORT("%s is not in indexed color mode!", FileName.CStr());
61 
62   if(PaletteSize != 256)
63     ABORT("%s has wrong palette size %d, should be 256!", FileName.CStr(), PaletteSize);
64 
65   Palette = new uchar[768];
66 
67   for(int i = 0; i < PaletteSize; ++i)
68   {
69     Palette[i * 3 + 0] = PNGPalette[255 - i].red;
70     Palette[i * 3 + 1] = PNGPalette[255 - i].green;
71     Palette[i * 3 + 2] = PNGPalette[255 - i].blue;
72   }
73 
74   Alloc2D(PaletteBuffer, Size.Y, Size.X);
75   png_read_image(PNGStruct, PaletteBuffer);
76   paletteindex* Buffer = PaletteBuffer[0];
77   paletteindex* End = &PaletteBuffer[Size.Y - 1][Size.X];
78 
79   for(; Buffer != End; ++Buffer)
80     *Buffer = 255 - *Buffer;
81 
82   png_destroy_read_struct(&PNGStruct, &PNGInfo, nullptr);
83 }
84 
rawbitmap(v2 Size)85 rawbitmap::rawbitmap(v2 Size) : Size(Size)
86 {
87   Palette = new uchar[768];
88   Alloc2D(PaletteBuffer, Size.Y, Size.X);
89 }
90 
~rawbitmap()91 rawbitmap::~rawbitmap()
92 {
93   delete [] Palette;
94   delete [] PaletteBuffer;
95 
96   for(fontcache::value_type& p : FontCache)
97   {
98     delete p.second.first;
99     delete p.second.second;
100   }
101 }
102 
Save(cfestring & FileName)103 void rawbitmap::Save(cfestring& FileName)
104 {
105   std::shared_ptr<FILE> File(fopen(FileName.CStr(), "wb"), fclose);
106   png_structp PNGStruct = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
107   png_infop PNGInfo = png_create_info_struct(PNGStruct);
108   png_init_io(PNGStruct, File.get());
109 
110   png_color PNGPalette[256];
111 
112   for(int i = 0; i < 256; ++i)
113   {
114     PNGPalette[255 - i].red = Palette[i * 3 + 0];
115     PNGPalette[255 - i].green = Palette[i * 3 + 1];
116     PNGPalette[255 - i].blue = Palette[i * 3 + 2];
117   }
118 
119   png_set_IHDR(PNGStruct, PNGInfo, Size.X, Size.Y, 8, PNG_COLOR_TYPE_PALETTE,
120                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
121   png_set_PLTE(PNGStruct, PNGInfo, PNGPalette, 256);
122   png_write_info(PNGStruct, PNGInfo);
123 
124   paletteindex* Buffer = PaletteBuffer[0];
125   paletteindex* End = &PaletteBuffer[Size.Y - 1][Size.X];
126 
127   for(; Buffer != End; ++Buffer)
128     *Buffer = 255 - *Buffer;
129 
130   png_write_image(PNGStruct, PaletteBuffer);
131   png_write_end(PNGStruct, PNGInfo);
132   png_destroy_write_struct(&PNGStruct, &PNGInfo);
133 
134   Buffer = PaletteBuffer[0];
135 
136   for(; Buffer != End; ++Buffer)
137     *Buffer = 255 - *Buffer;
138 }
139 
MaskedBlit(bitmap * Bitmap,v2 Src,v2 Dest,v2 Border,packcol16 * Color) const140 void rawbitmap::MaskedBlit(bitmap* Bitmap, v2 Src, v2 Dest, v2 Border, packcol16* Color) const
141 {
142   if(!femath::Clip(Src.X, Src.Y, Dest.X, Dest.Y, Border.X, Border.Y,
143                    Size.X, Size.Y, Bitmap->GetSize().X, Bitmap->GetSize().Y))
144     return;
145 
146   paletteindex* Buffer = &PaletteBuffer[Src.Y][Src.X];
147   packcol16* DestBuffer = &Bitmap->GetImage()[Dest.Y][Dest.X];
148   int BitmapXSize = Bitmap->GetSize().X;
149   uchar* Palette = this->Palette; // eliminate the efficiency cost of dereferencing
150 
151   for(int y = 0; y < Border.Y; ++y)
152   {
153     for(int x = 0; x < Border.X; ++x)
154     {
155       int PaletteElement = Buffer[x];
156 
157       if(PaletteElement >= 192)
158       {
159         int ThisColor = Color[(PaletteElement - 192) >> 4];
160         int Index = PaletteElement & 15;
161         int Red = (ThisColor >> 8 & 0xF8) * Index;
162         int Green = (ThisColor >> 3 & 0xFC) * Index;
163         int Blue = (ThisColor << 3 & 0xF8) * Index;
164 
165         if(Red > 0x7FF)
166           Red = 0x7FF;
167 
168         if(Green > 0x7FF)
169           Green = 0x7FF;
170 
171         if(Blue > 0x7FF)
172           Blue = 0x7FF;
173 
174         DestBuffer[x] = (Red << 5 & 0xF800) | (Green & 0x7E0) | (Blue >> 6 & 0x1F);
175       }
176       else
177       {
178         int PaletteIndex = PaletteElement + (PaletteElement << 1);
179         int ThisColor = ((Palette[PaletteIndex] & 0xFFF8) << 8)
180                         | ((Palette[PaletteIndex + 1] & 0xFFFC) << 3)
181                         |  (Palette[PaletteIndex + 2] >> 3);
182 
183         if(ThisColor != TRANSPARENT_COLOR)
184           DestBuffer[x] = ThisColor;
185       }
186     }
187 
188     DestBuffer += BitmapXSize;
189     Buffer += Size.X;
190   }
191 }
192 
Colorize(cpackcol16 * Color,alpha BaseAlpha,cpackalpha * Alpha) const193 cachedfont* rawbitmap::Colorize(cpackcol16* Color, alpha BaseAlpha, cpackalpha* Alpha) const
194 {
195   cachedfont* Bitmap = new cachedfont(Size);
196   paletteindex* Buffer = PaletteBuffer[0];
197   packcol16* DestBuffer = Bitmap->GetImage()[0];
198   uchar* Palette = this->Palette; // eliminate the efficiency cost of dereferencing
199   packalpha* AlphaMap;
200   truth UseAlpha;
201 
202   if(BaseAlpha != 255 || (Alpha && (Alpha[0] != 255 || Alpha[1] != 255 || Alpha[2] != 255 || Alpha[3] != 255)))
203   {
204     Bitmap->CreateAlphaMap(BaseAlpha);
205     AlphaMap = Bitmap->GetAlphaMap()[0];
206     UseAlpha = true;
207   }
208   else
209   {
210     AlphaMap = 0;
211     UseAlpha = false;
212   }
213 
214   int BitmapXSize = Bitmap->GetSize().X;
215 
216   for(int y = 0; y < Size.Y; ++y)
217   {
218     for(int x = 0; x < Size.X; ++x)
219     {
220       int PaletteElement = Buffer[x];
221 
222       if(PaletteElement >= 192)
223       {
224         int ColorIndex = (PaletteElement - 192) >> 4;
225         int ThisColor = Color[ColorIndex];
226 
227         if(ThisColor != TRANSPARENT_COLOR)
228         {
229           int Index = PaletteElement & 15;
230           int Red = (ThisColor >> 8 & 0xF8) * Index;
231           int Green = (ThisColor >> 3 & 0xFC) * Index;
232           int Blue = (ThisColor << 3 & 0xF8) * Index;
233 
234           if(Red > 0x7FF)
235             Red = 0x7FF;
236 
237           if(Green > 0x7FF)
238             Green = 0x7FF;
239 
240           if(Blue > 0x7FF)
241             Blue = 0x7FF;
242 
243           DestBuffer[x] = (Red << 5 & 0xF800)
244                           | (Green & 0x7E0)
245                           | (Blue >> 6 & 0x1F);
246 
247           if(UseAlpha)
248             AlphaMap[x] = Alpha[ColorIndex];
249         }
250         else
251           DestBuffer[x] = TRANSPARENT_COLOR;
252       }
253       else
254       {
255         int PaletteIndex = PaletteElement + (PaletteElement << 1);
256         DestBuffer[x] = ((Palette[PaletteIndex] & 0xFFF8) << 8)
257                         | ((Palette[PaletteIndex + 1] & 0xFFFC) << 3)
258                         |  (Palette[PaletteIndex + 2] >> 3);
259       }
260     }
261 
262     DestBuffer += BitmapXSize;
263     AlphaMap += BitmapXSize;
264     Buffer += Size.X;
265   }
266 
267   return Bitmap;
268 }
269 
Colorize(v2 Pos,v2 Border,v2 Move,cpackcol16 * Color,alpha BaseAlpha,cpackalpha * Alpha,cuchar * RustData,cuchar * BurnData,truth AllowReguralColors) const270 bitmap* rawbitmap::Colorize(v2 Pos, v2 Border, v2 Move, cpackcol16* Color, alpha BaseAlpha, cpackalpha* Alpha,
271                             cuchar* RustData, cuchar* BurnData, truth AllowReguralColors) const
272 {
273   bitmap* Bitmap = new bitmap(Border);
274   v2 TargetPos(0, 0);
275 
276   if(Move.X || Move.Y)
277   {
278     Bitmap->ClearToColor(TRANSPARENT_COLOR);
279 
280     if(Move.X < 0)
281     {
282       Pos.X -= Move.X;
283       Border.X += Move.X;
284     }
285     else if(Move.X > 0)
286     {
287       TargetPos.X = Move.X;
288       Border.X -= Move.X;
289     }
290 
291     if(Move.Y < 0)
292     {
293       Pos.Y -= Move.Y;
294       Border.Y += Move.Y;
295     }
296     else if(Move.Y > 0)
297     {
298       TargetPos.Y = Move.Y;
299       Border.Y -= Move.Y;
300     }
301   }
302 
303   paletteindex* Buffer = &PaletteBuffer[Pos.Y][Pos.X];
304   packcol16* DestBuffer = &Bitmap->GetImage()[TargetPos.Y][TargetPos.X];
305   int BitmapXSize = Bitmap->GetSize().X;
306   uchar* Palette = this->Palette; // eliminate the efficiency cost of dereferencing
307   packalpha* AlphaMap;
308   truth UseAlpha;
309 
310   if(BaseAlpha != 255 || (Alpha && (Alpha[0] != 255 || Alpha[1] != 255 || Alpha[2] != 255 || Alpha[3] != 255)))
311   {
312     Bitmap->CreateAlphaMap(BaseAlpha);
313     AlphaMap = &Bitmap->GetAlphaMap()[TargetPos.Y][TargetPos.X];
314     UseAlpha = true;
315   }
316   else
317   {
318     AlphaMap = 0;
319     UseAlpha = false;
320   }
321 
322   truth Rusted = RustData && (RustData[0] || RustData[1] || RustData[2] || RustData[3]);
323   ulong RustSeed[4];
324 
325   if(Rusted)
326   {
327     RustSeed[0] = (RustData[0] & 0xFC) >> 2;
328     RustSeed[1] = (RustData[1] & 0xFC) >> 2;
329     RustSeed[2] = (RustData[2] & 0xFC) >> 2;
330     RustSeed[3] = (RustData[3] & 0xFC) >> 2;
331   }
332 
333   truth Burnt = BurnData && (BurnData[0] || BurnData[1] || BurnData[2] || BurnData[3]);
334   ulong BurnSeed[4];
335 
336   if(Burnt)
337   {
338     BurnSeed[0] = (BurnData[0] & 0xFC) >> 2;
339     BurnSeed[1] = (BurnData[1] & 0xFC) >> 2;
340     BurnSeed[2] = (BurnData[2] & 0xFC) >> 2;
341     BurnSeed[3] = (BurnData[3] & 0xFC) >> 2;
342   }
343 
344   for(int y = 0; y < Border.Y; ++y)
345   {
346     for(int x = 0; x < Border.X; ++x)
347     {
348       int PaletteElement = Buffer[x];
349 
350       if(PaletteElement >= 192)
351       {
352         int ColorIndex = (PaletteElement - 192) >> 4;
353         int ThisColor = Color[ColorIndex];
354 
355         if(ThisColor != TRANSPARENT_COLOR)
356         {
357           int Index = PaletteElement & 15;
358           int Red = (ThisColor >> 8 & 0xF8) * Index;
359           int Green = (ThisColor >> 3 & 0xFC) * Index;
360           int Blue = (ThisColor << 3 & 0xF8) * Index;
361           int Max = (Red > Green ? Red : Green);
362           Max = (Max > Blue ? Max : Blue);
363 
364           if(Rusted && RustData[ColorIndex]
365              && (RustData[ColorIndex] & 3UL)
366              > (RustSeed[ColorIndex] = RustSeed[ColorIndex] * 1103515245 + 12345) >> 30)
367           {
368             Green = ((Green << 1) + Green) >> 2;
369             Blue >>= 1;
370           }
371 
372           if(Burnt && BurnData[ColorIndex]
373              && (BurnData[ColorIndex] & 3UL)
374              > (BurnSeed[ColorIndex] = BurnSeed[ColorIndex] * 1103515245 + 12345) >> 30)
375           {
376             Max >>= 2;
377             Red = Max + (Red >> 3);
378             Green = Max + (Green >> 3);
379             Blue = Max + (Blue >> 3);
380           }
381 
382           if(Red > 0x7FF)
383             Red = 0x7FF;
384 
385           if(Green > 0x7FF)
386             Green = 0x7FF;
387 
388           if(Blue > 0x7FF)
389             Blue = 0x7FF;
390 
391           DestBuffer[x] = (Red << 5 & 0xF800)
392                           | (Green & 0x7E0)
393                           | (Blue >> 6 & 0x1F);
394 
395           if(UseAlpha)
396             AlphaMap[x] = Alpha[ColorIndex];
397         }
398         else
399           DestBuffer[x] = TRANSPARENT_COLOR;
400       }
401       else if(AllowReguralColors)
402       {
403         int PaletteIndex = PaletteElement + (PaletteElement << 1);
404         DestBuffer[x] = ((Palette[PaletteIndex] & 0xFFF8) << 8)
405                         | ((Palette[PaletteIndex + 1] & 0xFFFC) << 3)
406                         |  (Palette[PaletteIndex + 2] >> 3);
407       }
408       else
409         DestBuffer[x] = TRANSPARENT_COLOR;
410     }
411 
412     DestBuffer += BitmapXSize;
413     AlphaMap += BitmapXSize;
414     Buffer += Size.X;
415   }
416 
417   return Bitmap;
418 }
419 
Printf(bitmap * Bitmap,v2 Pos,packcol16 Color,cchar * Format,...) const420 void rawbitmap::Printf(bitmap* Bitmap, v2 Pos, packcol16 Color, cchar* Format, ...) const
421 {
422   char Buffer[256];
423 
424   va_list AP;
425   va_start(AP, Format);
426   vsprintf(Buffer, Format, AP);
427   va_end(AP);
428 
429   fontcache::const_iterator Iterator = FontCache.find(Color);
430 
431   if(Iterator == FontCache.end())
432   {
433     packcol16 ShadeCol = MakeShadeColor(Color);
434 
435     for(int c = 0; Buffer[c]; ++c)
436     {
437       v2 F(((Buffer[c] - 0x20) & 0xF) << 4, (Buffer[c] - 0x20) & 0xF0);
438           //printf("X=%4d -- Y=%d\n", F.X, F.Y);
439       MaskedBlit(Bitmap, F, v2(Pos.X + (c << 3) + 1, Pos.Y + 1), v2(8, 8), &ShadeCol);
440       MaskedBlit(Bitmap, F, v2(Pos.X + (c << 3), Pos.Y), v2(8, 8), &Color);
441     }
442   }
443   else
444   {
445     const cachedfont* Font = Iterator->second.first;
446     blitdata B = { Bitmap,
447                    { 0, 0 },
448                    { Pos.X, Pos.Y },
449                    { 9, 9 },
450                    { 0 },
451                    TRANSPARENT_COLOR,
452                    0 };
453 
454     for(int c = 0; Buffer[c]; ++c, B.Dest.X += 8)
455     {
456       B.Src.X = ((Buffer[c] - 0x20) & 0xF) << 4;
457       B.Src.Y = (Buffer[c] - 0x20) & 0xF0;
458           //printf("'%c' -> X=%5d -- Y=%5d\n", Buffer[c], B.Src.X, B.Src.Y);
459       Font->PrintCharacter(B);
460     }
461   }
462 }
463 
PrintfUnshaded(bitmap * Bitmap,v2 Pos,packcol16 Color,cchar * Format,...) const464 void rawbitmap::PrintfUnshaded(bitmap* Bitmap, v2 Pos, packcol16 Color, cchar* Format, ...) const
465 {
466   char Buffer[256];
467 
468   va_list AP;
469   va_start(AP, Format);
470   vsprintf(Buffer, Format, AP);
471   va_end(AP);
472 
473   fontcache::const_iterator Iterator = FontCache.find(Color);
474 
475   if(Iterator == FontCache.end())
476   {
477     for(int c = 0; Buffer[c]; ++c)
478     {
479       v2 F(((Buffer[c] - 0x20) & 0xF) << 4, (Buffer[c] - 0x20) & 0xF0);
480       MaskedBlit(Bitmap, F, v2(Pos.X + (c << 3), Pos.Y), v2(8, 8), &Color);
481     }
482   }
483   else
484   {
485     const cachedfont* Font = Iterator->second.second;
486     blitdata B = { Bitmap,
487                    { 0, 0 },
488                    { Pos.X, Pos.Y },
489                    { 9, 9 },
490                    { 0 },
491                    TRANSPARENT_COLOR,
492                    0 };
493 
494     for(int c = 0; Buffer[c]; ++c, B.Dest.X += 8)
495     {
496       B.Src.X = ((Buffer[c] - 0x20) & 0xF) << 4;
497       B.Src.Y = (Buffer[c] - 0x20) & 0xF0;
498       Font->PrintCharacter(B);
499     }
500   }
501 }
502 
AlterGradient(v2 Pos,v2 Border,int MColor,int Amount,truth Clip)503 void rawbitmap::AlterGradient(v2 Pos, v2 Border, int MColor, int Amount, truth Clip)
504 {
505   int ColorMin = 192 + (MColor << 4);
506   int ColorMax = 207 + (MColor << 4);
507 
508   if(Clip)
509   {
510     for(int x = Pos.X; x < Pos.X + Border.X; ++x)
511       for(int y = Pos.Y; y < Pos.Y + Border.Y; ++y)
512       {
513         int Pixel = PaletteBuffer[y][x];
514 
515         if(Pixel >= ColorMin && Pixel <= ColorMax)
516         {
517           int NewPixel = Pixel + Amount;
518 
519           if(NewPixel < ColorMin)
520             NewPixel = ColorMin;
521 
522           if(NewPixel > ColorMax)
523             NewPixel = ColorMax;
524 
525           PaletteBuffer[y][x] = NewPixel;
526         }
527       }
528   }
529   else
530   {
531     int x;
532 
533     for(x = Pos.X; x < Pos.X + Border.X; ++x)
534       for(int y = Pos.Y; y < Pos.Y + Border.Y; ++y)
535       {
536         int Pixel = PaletteBuffer[y][x];
537 
538         if(Pixel >= ColorMin && Pixel <= ColorMax)
539         {
540           int NewPixel = Pixel + Amount;
541 
542           if(NewPixel < ColorMin)
543             return;
544 
545           if(NewPixel > ColorMax)
546             return;
547         }
548       }
549 
550     for(x = Pos.X; x < Pos.X + Border.X; ++x)
551       for(int y = Pos.Y; y < Pos.Y + Border.Y; ++y)
552       {
553         int Pixel = PaletteBuffer[y][x];
554 
555         if(Pixel >= ColorMin && Pixel <= ColorMax)
556           PaletteBuffer[y][x] = Pixel + Amount;
557       }
558   }
559 }
560 
SwapColors(v2 Pos,v2 Border,int Color1,int Color2)561 void rawbitmap::SwapColors(v2 Pos, v2 Border, int Color1, int Color2)
562 {
563   if(Color1 > 3 || Color2 > 3)
564     ABORT("Illegal col swap!");
565 
566   for(int x = Pos.X; x < Pos.X + Border.X; ++x)
567     for(int y = Pos.Y; y < Pos.Y + Border.Y; ++y)
568     {
569       paletteindex& Pixel = PaletteBuffer[y][x];
570 
571       if(Pixel >= 192 + (Color1 << 4) && Pixel <= 207 + (Color1 << 4))
572         Pixel += (Color2 - Color1) << 4;
573       else if(Pixel >= 192 + (Color2 << 4) && Pixel <= 207 + (Color2 << 4))
574         Pixel += (Color1 - Color2) << 4;
575     }
576 }
577 
578 /* TempBuffer must be an array of Border.X * Border.Y paletteindices */
579 
Roll(v2 Pos,v2 Border,v2 Move,paletteindex * TempBuffer)580 void rawbitmap::Roll(v2 Pos, v2 Border, v2 Move, paletteindex* TempBuffer)
581 {
582   int x, y;
583 
584   for(x = Pos.X; x < Pos.X + Border.X; ++x)
585     for(y = Pos.Y; y < Pos.Y + Border.Y; ++y)
586     {
587       int XPos = x + Move.X, YPos = y + Move.Y;
588 
589       if(XPos < Pos.X)
590         XPos += Border.X;
591 
592       if(YPos < Pos.Y)
593         YPos += Border.Y;
594 
595       if(XPos >= Pos.X + Border.X)
596         XPos -= Border.X;
597 
598       if(YPos >= Pos.Y + Border.Y)
599         YPos -= Border.Y;
600 
601       TempBuffer[(YPos - Pos.Y) * Border.X + XPos - Pos.X] = PaletteBuffer[y][x];
602     }
603 
604   for(x = Pos.X; x < Pos.X + Border.X; ++x)
605     for(y = Pos.Y; y < Pos.Y + Border.Y; ++y)
606       PaletteBuffer[y][x] = TempBuffer[(y - Pos.Y) * Border.X + x - Pos.X];
607 }
608 
CreateFontCache(packcol16 Color)609 void rawbitmap::CreateFontCache(packcol16 Color)
610 {
611   if(FontCache.find(Color) != FontCache.end())
612     return;
613 
614   packcol16 ShadeColor = MakeShadeColor(Color);
615   cachedfont* Font = new cachedfont(Size, TRANSPARENT_COLOR);
616   MaskedBlit(Font, ZERO_V2, v2(1, 1), v2(Size.X - 1, Size.Y - 1), &ShadeColor);
617   cachedfont* UnshadedFont = Colorize(&Color);
618 
619   blitdata B = { Font,
620                  { 0, 0 },
621                  { 0, 0 },
622                  { Size.X, Size.Y },
623                  { 0 },
624                  TRANSPARENT_COLOR,
625                  0 };
626 
627   UnshadedFont->NormalMaskedBlit(B);
628   Font->CreateMaskMap();
629   UnshadedFont->CreateMaskMap();
630   FontCache[Color] = std::make_pair(Font, UnshadedFont);
631 }
632 
633 /* returns ERROR_V2 if fails find Pos else returns pos */
634 
RandomizeSparklePos(cv2 * ValidityArray,v2 * PossibleBuffer,v2 Pos,v2 Border,int ValidityArraySize,int SparkleFlags) const635 v2 rawbitmap::RandomizeSparklePos(cv2* ValidityArray, v2* PossibleBuffer, v2 Pos, v2 Border,
636                                   int ValidityArraySize, int SparkleFlags) const
637 {
638   if(!SparkleFlags)
639     return ERROR_V2;
640 
641   /* Don't use { } to initialize, or GCC optimizations will produce code that crashes! */
642 
643   v2* BadPossible[4];
644   BadPossible[0] = PossibleBuffer;
645   BadPossible[1] = BadPossible[0] + ((Border.X + Border.Y) << 1) -  4;
646   BadPossible[2] = BadPossible[1] + ((Border.X + Border.Y) << 1) - 12;
647   BadPossible[3] = BadPossible[2] + ((Border.X + Border.Y) << 1) - 20;
648   v2* PreferredPossible = BadPossible[3] + ((Border.X + Border.Y) << 1) - 28;
649   int Preferred = 0;
650   int Bad[4] = { 0, 0, 0, 0 };
651   int XMax = Pos.X + Border.X;
652   int YMax = Pos.Y + Border.Y;
653 
654   for(int c = 0; c < ValidityArraySize; ++c)
655   {
656     v2 V = ValidityArray[c] + Pos;
657     int Entry = PaletteBuffer[V.Y][V.X];
658 
659     if(IsMaterialColor(Entry) && 1 << GetMaterialColorIndex(Entry) & SparkleFlags)
660     {
661       int MinDist = 0x7FFF;
662 
663       if(V.X < Pos.X + 4)
664         MinDist = Min(V.X - Pos.X, MinDist);
665 
666       if(V.X >= XMax - 4)
667         MinDist = Min(XMax - V.X - 1, MinDist);
668 
669       if(V.Y < Pos.Y + 4)
670         MinDist = Min(V.Y - Pos.Y, MinDist);
671 
672       if(V.Y >= YMax - 4)
673         MinDist = Min(YMax - V.Y - 1, MinDist);
674 
675       if(MinDist >= 4)
676         PreferredPossible[Preferred++] = V;
677       else
678         BadPossible[MinDist][Bad[MinDist]++] = V;
679     }
680   }
681 
682   v2 Return;
683 
684   if(Preferred)
685     Return = PreferredPossible[RAND() % Preferred] - Pos;
686   else if(Bad[3])
687     Return = BadPossible[3][RAND() % Bad[3]] - Pos;
688   else if(Bad[2])
689     Return = BadPossible[2][RAND() % Bad[2]] - Pos;
690   else if(Bad[1])
691     Return = BadPossible[1][RAND() % Bad[1]] - Pos;
692   else if(Bad[0])
693     Return = BadPossible[0][RAND() % Bad[0]] - Pos;
694   else
695     Return = ERROR_V2;
696 
697   return Return;
698 }
699 
IsTransparent(v2 Pos) const700 truth rawbitmap::IsTransparent(v2 Pos) const
701 {
702   return PaletteBuffer[Pos.Y][Pos.X] == TRANSPARENT_PALETTE_INDEX;
703 }
704 
IsMaterialColor1(v2 Pos) const705 truth rawbitmap::IsMaterialColor1(v2 Pos) const
706 {
707   int P = PaletteBuffer[Pos.Y][Pos.X];
708   return P >= 192 && P < 208;
709 }
710 
CopyPaletteFrom(rawbitmap * Bitmap)711 void rawbitmap::CopyPaletteFrom(rawbitmap* Bitmap)
712 {
713   memcpy(Palette, Bitmap->Palette, 768);
714 }
715 
Clear()716 void rawbitmap::Clear()
717 {
718   memset(PaletteBuffer[0], TRANSPARENT_PALETTE_INDEX, Size.X * Size.Y * sizeof(paletteindex));
719 }
720 
NormalBlit(rawbitmap * Bitmap,v2 Src,v2 Dest,v2 Border,int Flags) const721 void rawbitmap::NormalBlit(rawbitmap* Bitmap, v2 Src, v2 Dest, v2 Border, int Flags) const
722 {
723   paletteindex** SrcBuffer = PaletteBuffer;
724   paletteindex** DestBuffer = Bitmap->PaletteBuffer;
725 
726   switch(Flags & 7)
727   {
728    case NONE:
729     {
730       if(Size.X == Bitmap->Size.X && Size.Y == Bitmap->Size.Y)
731         memcpy(DestBuffer[0], SrcBuffer[0], Size.X * Size.Y * sizeof(paletteindex));
732       else
733       {
734         cint Bytes = Border.X * sizeof(paletteindex);
735 
736         for(int y = 0; y < Border.Y; ++y)
737           memcpy(&DestBuffer[Dest.Y + y][Dest.X], &SrcBuffer[Src.Y + y][Src.X], Bytes);
738       }
739 
740       break;
741     }
742 
743    case MIRROR:
744     {
745       Dest.X += Border.X - 1;
746 
747       for(int y = 0; y < Border.Y; ++y)
748       {
749         cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X];
750         cpaletteindex* EndPtr = SrcPtr + Border.X;
751         paletteindex* DestPtr = &DestBuffer[Dest.Y + y][Dest.X];
752 
753         for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr)
754           *DestPtr = *SrcPtr;
755       }
756 
757       break;
758     }
759 
760    case FLIP:
761     {
762       Dest.Y += Border.Y - 1;
763       cint Bytes = Border.X * sizeof(paletteindex);
764 
765       for(int y = 0; y < Border.Y; ++y)
766         memcpy(&DestBuffer[Dest.Y - y][Dest.X], &SrcBuffer[Src.Y + y][Src.X], Bytes);
767 
768       break;
769     }
770 
771    case (MIRROR | FLIP):
772     {
773       Dest.X += Border.X - 1;
774       Dest.Y += Border.Y - 1;
775 
776       for(int y = 0; y < Border.Y; ++y)
777       {
778         cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X];
779         cpaletteindex* EndPtr = SrcPtr + Border.X;
780         paletteindex* DestPtr = &DestBuffer[Dest.Y - y][Dest.X];
781 
782         for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr)
783           *DestPtr = *SrcPtr;
784       }
785 
786       break;
787     }
788 
789    case ROTATE:
790     {
791       Dest.X += Border.X - 1;
792       int TrueDestXMove = Bitmap->Size.X;
793       paletteindex* DestBase = &DestBuffer[Dest.Y][Dest.X];
794 
795       for(int y = 0; y < Border.Y; ++y)
796       {
797         cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X];
798         cpaletteindex* EndPtr = SrcPtr + Border.X;
799         paletteindex* DestPtr = DestBase - y;
800 
801         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove)
802           *DestPtr = *SrcPtr;
803       }
804 
805       break;
806     }
807 
808    case (MIRROR | ROTATE):
809     {
810       int TrueDestXMove = Bitmap->Size.X;
811       paletteindex* DestBase = &DestBuffer[Dest.Y][Dest.X];
812 
813       for(int y = 0; y < Border.Y; ++y)
814       {
815         cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X];
816         cpaletteindex* EndPtr = SrcPtr + Border.X;
817         paletteindex* DestPtr = DestBase + y;
818 
819         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove)
820           *DestPtr = *SrcPtr;
821       }
822 
823       break;
824     }
825 
826    case (FLIP | ROTATE):
827     {
828       Dest.X += Border.X - 1;
829       Dest.Y += Border.Y - 1;
830       int TrueDestXMove = Bitmap->Size.X;
831       paletteindex* DestBase = &DestBuffer[Dest.Y][Dest.X];
832 
833       for(int y = 0; y < Border.Y; ++y)
834       {
835         cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X];
836         cpaletteindex* EndPtr = SrcPtr + Border.X;
837         paletteindex* DestPtr = DestBase - y;
838 
839         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove)
840           *DestPtr = *SrcPtr;
841       }
842 
843       break;
844     }
845 
846    case (MIRROR | FLIP | ROTATE):
847     {
848       Dest.Y += Border.Y - 1;
849       int TrueDestXMove = Bitmap->Size.X;
850       paletteindex* DestBase = &DestBuffer[Dest.Y][Dest.X];
851 
852       for(int y = 0; y < Border.Y; ++y)
853       {
854         cpaletteindex* SrcPtr = &SrcBuffer[Src.Y + y][Src.X];
855         cpaletteindex* EndPtr = SrcPtr + Border.X;
856         paletteindex* DestPtr = DestBase + y;
857 
858         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove)
859           *DestPtr = *SrcPtr;
860       }
861 
862       break;
863     }
864   }
865 }
866