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 <cmath>
14 #include <ctime>
15 #include <iostream>
16 #include <vector>
17 
18 #include "bitmap.h"
19 #include "graphics.h"
20 #include "save.h"
21 #include "allocate.h"
22 #include "femath.h"
23 #include "rawbit.h"
24 #include "libxbrzscale.h"
25 
26 //#define DBGMSG_V2
27 #include "dbgmsgproj.h"
28 
29 /*
30  * Blitting must be as fast as possible, even if no optimizations are used;
31  * therefore we can't use inline functions inside loops, since they may be
32  * left unexpanded. These macros will do the job efficiently, even if they
33  * are rather ugly
34  */
35 
36 #define LOAD_SRC() int SrcCol = *SrcPtr;
37 #define LOAD_DEST() int DestCol = *DestPtr;
38 #define LOAD_ALPHA() int Alpha = *AlphaPtr;
39 
40 #define STORE_COLOR() *DestPtr = Red | Green | Blue;
41 
42 #define NEW_LUMINATE_RED()\
43 int Red = (SrcCol & 0xF800) + NewRedLuminance;\
44 \
45 if(Red >= 0)\
46 {\
47   if(Red > 0xF800)\
48     Red = 0xF800;\
49 }\
50 else\
51   Red = 0;
52 
53 #define NEW_LUMINATE_GREEN()\
54 int Green = (SrcCol & 0x7E0) + NewGreenLuminance;\
55 \
56 if(Green >= 0)\
57 {\
58   if(Green > 0x7E0)\
59     Green = 0x7E0;\
60 }\
61 else\
62   Green = 0;
63 
64 #define NEW_LUMINATE_BLUE()\
65 int Blue = (SrcCol & 0x1F) + NewBlueLuminance;\
66 \
67 if(Blue >= 0)\
68 {\
69   if(Blue > 0x1F)\
70     Blue = 0x1F;\
71 }\
72 else\
73   Blue = 0;
74 
75 #define NEW_APPLY_ALPHA_RED()\
76 {\
77   int DestRed = (DestCol & 0xF800);\
78   Red = (((Red - DestRed) * Alpha >> 8) + DestRed) & 0xF800;\
79 }
80 
81 #define NEW_APPLY_ALPHA_GREEN()\
82 {\
83   int DestGreen = (DestCol & 0x7E0);\
84   Green = (((Green - DestGreen) * Alpha >> 8) + DestGreen) & 0x7E0;\
85 }
86 
87 #define NEW_APPLY_ALPHA_BLUE()\
88 {\
89   int DestBlue = (DestCol & 0x1F);\
90   Blue = ((Blue - DestBlue) * Alpha >> 8) + DestBlue;\
91 }
92 
93 #define NEW_LOAD_AND_APPLY_ALPHA_RED()\
94 int Red;\
95 {\
96   int DestRed = DestCol & 0xF800;\
97   Red = ((((SrcCol & 0xF800) - DestRed) * Alpha >> 8) + DestRed) & 0xF800;\
98 }
99 
100 #define NEW_LOAD_AND_APPLY_ALPHA_GREEN()\
101 int Green;\
102 {\
103   int DestGreen = DestCol & 0x7E0;\
104   Green = ((((SrcCol & 0x7E0) - DestGreen) * Alpha >> 8) + DestGreen)\
105           & 0x7E0;\
106 }
107 
108 #define NEW_LOAD_AND_APPLY_ALPHA_BLUE()\
109 int Blue;\
110 {\
111   int DestBlue = DestCol & 0x1F;\
112   Blue = (((SrcCol & 0x1F) - DestBlue) * Alpha >> 8) + DestBlue;\
113 }
114 
bitmap(cfestring & FileName)115 bitmap::bitmap(cfestring& FileName)
116 : FastFlag(0), AlphaMap(0), PriorityMap(0), RandMap(0)
117 {
118   rawbitmap Temp(FileName);
119   Size = Temp.Size;
120   XSizeTimesYSize = Size.X * Size.Y;
121   Alloc2D(Image, Size.Y, Size.X);
122   packcol16* Buffer = Image[0];
123   paletteindex* TempBuffer = Temp.PaletteBuffer[0];
124 
125   for(int y = 0; y < Size.Y; ++y)
126     for(int x = 0; x < Size.X; ++x)
127     {
128       int Char1 = *TempBuffer++;
129       int Char3 = Char1 + (Char1 << 1);
130       *Buffer++ = int(Temp.Palette[Char3] >> 3) << 11
131                 | int(Temp.Palette[Char3 + 1] >> 2) << 5
132                 | int(Temp.Palette[Char3 + 2] >> 3);
133     }
134 }
135 
bitmap(cbitmap * Bitmap,int Flags,truth CopyAlpha)136 bitmap::bitmap(cbitmap* Bitmap, int Flags, truth CopyAlpha)
137 : bitmap(Bitmap->Size)
138 {
139   if(CopyAlpha && Bitmap->AlphaMap)
140   {
141     Alloc2D(AlphaMap, Size.Y, Size.X);
142     Bitmap->BlitAndCopyAlpha(this, Flags);
143   }
144   else
145   {
146     if(!Flags)
147       Bitmap->FastBlit(this);
148     else
149       Bitmap->NormalBlit(this, Flags);
150   }
151 }
152 
bitmap(v2 Size)153 bitmap::bitmap(v2 Size)
154 : Size(Size), XSizeTimesYSize(Size.X * Size.Y),
155   FastFlag(0), AlphaMap(0), PriorityMap(0), RandMap(0)
156 {
157   Alloc2D(Image, Size.Y, Size.X);
158 }
159 
bitmap(v2 Size,col16 Color)160 bitmap::bitmap(v2 Size, col16 Color)
161 : bitmap(Size)
162 {
163   ClearToColor(Color);
164 }
165 
~bitmap()166 bitmap::~bitmap()
167 {
168   delete [] Image;
169   delete [] AlphaMap;
170   delete [] PriorityMap;
171   delete [] RandMap;
172 }
173 
Save(outputfile & SaveFile) const174 void bitmap::Save(outputfile& SaveFile) const
175 {
176   SaveFile.Write(reinterpret_cast<char*>(Image[0]),
177                  XSizeTimesYSize * sizeof(packcol16));
178 
179   if(AlphaMap)
180   {
181     SaveFile.Put(true);
182     SaveFile.Write(reinterpret_cast<char*>(AlphaMap[0]),
183                    XSizeTimesYSize * sizeof(packalpha));
184   }
185   else
186     SaveFile.Put(false);
187 
188   if(PriorityMap)
189   {
190     SaveFile.Put(true);
191     SaveFile.Write(reinterpret_cast<char*>(PriorityMap[0]),
192                    XSizeTimesYSize * sizeof(packpriority));
193   }
194   else
195     SaveFile.Put(false);
196 
197   SaveFile << uchar(FastFlag);
198 }
199 
Load(inputfile & SaveFile)200 void bitmap::Load(inputfile& SaveFile)
201 {
202   SaveFile.Read(reinterpret_cast<char*>(Image[0]),
203                 XSizeTimesYSize * sizeof(packcol16));
204 
205   if(SaveFile.Get())
206   {
207     Alloc2D(AlphaMap, Size.Y, Size.X);
208     SaveFile.Read(reinterpret_cast<char*>(AlphaMap[0]),
209                   XSizeTimesYSize * sizeof(packalpha));
210   }
211 
212   if(SaveFile.Get())
213   {
214     Alloc2D(PriorityMap, Size.Y, Size.X);
215     SaveFile.Read(reinterpret_cast<char*>(PriorityMap[0]),
216                   XSizeTimesYSize * sizeof(packpriority));
217   }
218 
219   FastFlag = ReadType<uchar>(SaveFile);
220 }
221 
Save(cfestring & FileName) const222 void bitmap::Save(cfestring& FileName) const
223 {
224   static char BMPHeader[] =
225   {
226     char(0x42), char(0x4D), char(0xB6), char(0x4F), char(0x12), char(0x00),
227     char(0x00), char(0x00), char(0x00), char(0x00), char(0x36), char(0x00),
228     char(0x00), char(0x00), char(0x28), char(0x00), char(0x00), char(0x00),
229     char(0x20), char(0x03), char(0x00), char(0x00), char(0xF4), char(0x01),
230     char(0x00), char(0x00), char(0x01), char(0x00), char(0x18), char(0x00),
231     char(0x00), char(0x00), char(0x00), char(0x00), char(0x80), char(0x4F),
232     char(0x12), char(0x00), char(0x33), char(0x0B), char(0x00), char(0x00),
233     char(0x33), char(0x0B), char(0x00), char(0x00), char(0x00), char(0x00),
234     char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00)
235   };
236 
237   outputfile SaveFile(FileName);
238   BMPHeader[0x12] =  Size.X       & 0xFF;
239   BMPHeader[0x13] = (Size.X >> 8) & 0xFF;
240   BMPHeader[0x16] =  Size.Y       & 0xFF;
241   BMPHeader[0x17] = (Size.Y >> 8) & 0xFF;
242   SaveFile.Write(BMPHeader, 0x36);
243 
244   for(int y = Size.Y - 1; y >= 0; --y)
245     for(int x = 0; x < Size.X; ++x)
246     {
247       col16 Pixel = GetPixel(x, y);
248       SaveFile << char(Pixel << 3)
249                << char((Pixel >> 5) << 2)
250                << char((Pixel >> 11) << 3);
251     }
252 }
253 
Fill(v2 TopLeft,int Width,int Height,col16 Color)254 void bitmap::Fill(v2 TopLeft, int Width, int Height, col16 Color)
255 { Fill(TopLeft.X, TopLeft.Y, Width, Height, Color); }
Fill(int X,int Y,v2 FillSize,col16 Color)256 void bitmap::Fill(int X, int Y, v2 FillSize, col16 Color)
257 { Fill(X, Y, FillSize.X, FillSize.Y, Color); }
Fill(v2 TopLeft,v2 FillSize,col16 Color)258 void bitmap::Fill(v2 TopLeft, v2 FillSize, col16 Color)
259 { Fill(TopLeft.X, TopLeft.Y, FillSize.X, FillSize.Y, Color); }
260 
Fill(int X,int Y,int Width,int Height,col16 Color)261 void bitmap::Fill(int X, int Y, int Width, int Height, col16 Color)
262 {
263   /* We crop the area in order to prevent buffer overflow. Take care! */
264 
265   if(X >= Size.X || Y >= Size.Y || Width <= 0 || Height <= 0 || X <= -Width || Y <= -Height)
266     return;
267 
268   if(X < 0)
269   {
270     Width += X;
271     X = 0;
272   }
273 
274   if(Y < 0)
275   {
276     Height += Y;
277     Y = 0;
278   }
279 
280   if(Width > Size.X - X)
281     Width = Size.X - X;
282 
283   if(Height > Size.Y - Y)
284     Height = Size.Y - Y;
285 
286   if(Color >> 8 == (Color & 0xFF))
287   {
288     Width <<= 1;
289 
290     for(int y = 0; y < Height; ++y)
291       memset(&Image[Y + y][X], Color, Width);
292   }
293   else
294     for(int y = 0; y < Height; ++y)
295     {
296       packcol16* Ptr = &Image[Y + y][X];
297       cpackcol16*const EndPtr = Ptr + Width;
298 
299       while(Ptr != EndPtr)
300         *Ptr++ = Color;
301     }
302 }
303 
ClearToColor(col16 Color)304 void bitmap::ClearToColor(col16 Color)
305 {
306   packcol16* Ptr = Image[0];
307 
308   if(Color >> 8 == (Color & 0xFF))
309     memset(Ptr, Color, XSizeTimesYSize * sizeof(packcol16));
310   else
311   {
312     cpackcol16*const EndPtr = Ptr + XSizeTimesYSize;
313 
314     while(Ptr != EndPtr)
315       *Ptr++ = Color;
316   }
317 }
318 
HasColor(col16 findColor)319 truth bitmap::HasColor(col16 findColor)
320 {
321   for(int iY=0;iY<Size.Y;iY++)
322     for(int iX=0;iX<Size.X;iX++)
323       if(Image[iY][iX]==findColor)
324         return true;
325 
326   return false;
327 }
328 
ReplaceColor(col16 findColor,col16 replaceWith)329 void bitmap::ReplaceColor(col16 findColor,col16 replaceWith)
330 {
331   for(int iY=0;iY<Size.Y;iY++)
332     for(int iX=0;iX<Size.X;iX++)
333       if(Image[iY][iX]==findColor)
334         Image[iY][iX]=replaceWith;
335 }
336 
CopyLineFrom(int iYDest,bitmap * bmpFrom,int iYFrom,int iSize,bool bFailSafe)337 void bitmap::CopyLineFrom(int iYDest, bitmap* bmpFrom, int iYFrom, int iSize, bool bFailSafe){
338   iSize*=sizeof(packcol16);
339   if(bFailSafe && iYDest>=Size.Y)return;
340   memcpy(&Image[iYDest][0], &bmpFrom->Image[iYFrom][0], iSize);
341 }
342 
NormalBlit(cblitdata & BlitData) const343 void bitmap::NormalBlit(cblitdata& BlitData) const
344 {
345   blitdata B = BlitData;
346 
347   if(!FastFlag)
348   {
349     if(!B.Border.X || !B.Border.Y)
350       ABORT("Zero-sized bitmap blit attempt detected!");
351 
352     if(B.Flags & ROTATE && B.Border.X != B.Border.Y)
353       ABORT("Blit error: FeLib supports only square rotating!");
354 
355     if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y,
356                      Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y))
357       return;
358   }
359 
360   packcol16** SrcImage = Image;
361   packcol16** DestImage = B.Bitmap->Image;
362 
363   switch(B.Flags & 7)
364   {
365    case NONE:
366     {
367       if(!B.Src.X && !B.Src.Y && !B.Dest.X && !B.Dest.Y
368          && B.Border.X == Size.X && B.Border.Y == Size.Y
369          && B.Border.X == B.Bitmap->Size.X && B.Border.Y == B.Bitmap->Size.Y)
370         memcpy(DestImage[0], SrcImage[0], XSizeTimesYSize * sizeof(packcol16));
371       else
372       {
373         cint Bytes = B.Border.X * sizeof(packcol16);
374 
375         for(int y = 0; y < B.Border.Y; ++y)
376           memcpy(&DestImage[B.Dest.Y + y][B.Dest.X], &SrcImage[B.Src.Y + y][B.Src.X], Bytes);
377       }
378 
379       break;
380     }
381 
382    case MIRROR:
383     {
384       B.Dest.X += B.Border.X - 1;
385 
386       for(int y = 0; y < B.Border.Y; ++y)
387       {
388         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
389         cpackcol16* EndPtr = SrcPtr + B.Border.X;
390         packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
391 
392         for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr)
393           *DestPtr = *SrcPtr;
394       }
395 
396       break;
397     }
398 
399    case FLIP:
400     {
401       B.Dest.Y += B.Border.Y - 1;
402       cint Bytes = B.Border.X * sizeof(packcol16);
403 
404       for(int y = 0; y < B.Border.Y; ++y)
405         memcpy(&DestImage[B.Dest.Y - y][B.Dest.X], &SrcImage[B.Src.Y + y][B.Src.X], Bytes);
406 
407       break;
408     }
409 
410    case (MIRROR | FLIP):
411     {
412       B.Dest.X += B.Border.X - 1;
413       B.Dest.Y += B.Border.Y - 1;
414 
415       for(int y = 0; y < B.Border.Y; ++y)
416       {
417         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
418         cpackcol16* EndPtr = SrcPtr + B.Border.X;
419         packcol16* DestPtr = &DestImage[B.Dest.Y - y][B.Dest.X];
420 
421         for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr)
422           *DestPtr = *SrcPtr;
423       }
424 
425       break;
426     }
427 
428    case ROTATE:
429     {
430       B.Dest.X += B.Border.X - 1;
431       int TrueDestXMove = B.Bitmap->Size.X;
432       packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X];
433 
434       for(int y = 0; y < B.Border.Y; ++y)
435       {
436         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
437         cpackcol16* EndPtr = SrcPtr + B.Border.X;
438         packcol16* DestPtr = DestBase - y;
439 
440         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove)
441           *DestPtr = *SrcPtr;
442       }
443 
444       break;
445     }
446 
447    case (MIRROR | ROTATE):
448     {
449       int TrueDestXMove = B.Bitmap->Size.X;
450       packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X];
451 
452       for(int y = 0; y < B.Border.Y; ++y)
453       {
454         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
455         cpackcol16* EndPtr = SrcPtr + B.Border.X;
456         packcol16* DestPtr = DestBase + y;
457 
458         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove)
459           *DestPtr = *SrcPtr;
460       }
461 
462       break;
463     }
464 
465    case (FLIP | ROTATE):
466     {
467       B.Dest.X += B.Border.X - 1;
468       B.Dest.Y += B.Border.Y - 1;
469       int TrueDestXMove = B.Bitmap->Size.X;
470       packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X];
471 
472       for(int y = 0; y < B.Border.Y; ++y)
473       {
474         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
475         cpackcol16* EndPtr = SrcPtr + B.Border.X;
476         packcol16* DestPtr = DestBase - y;
477 
478         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove)
479           *DestPtr = *SrcPtr;
480       }
481 
482       break;
483     }
484 
485    case (MIRROR | FLIP | ROTATE):
486     {
487       B.Dest.Y += B.Border.Y - 1;
488       int TrueDestXMove = B.Bitmap->Size.X;
489       packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X];
490 
491       for(int y = 0; y < B.Border.Y; ++y)
492       {
493         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
494         cpackcol16* EndPtr = SrcPtr + B.Border.X;
495         packcol16* DestPtr = DestBase + y;
496 
497         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove)
498           *DestPtr = *SrcPtr;
499       }
500 
501       break;
502     }
503   }
504 }
505 
LuminanceBlit(cblitdata & BlitData) const506 void bitmap::LuminanceBlit(cblitdata& BlitData) const
507 {
508   blitdata B = BlitData;
509 
510   if(B.Luminance == NORMAL_LUMINANCE)
511   {
512     B.Flags = 0;
513     NormalBlit(B);
514     return;
515   }
516 
517   if(!FastFlag)
518   {
519     if(!B.Border.X || !B.Border.Y)
520       ABORT("Zero-sized bitmap blit attempt detected!");
521 
522     if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y,
523                      Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y))
524       return;
525   }
526 
527   packcol16** SrcImage = Image;
528   packcol16** DestImage = B.Bitmap->Image;
529   int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000;
530   int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800;
531   int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20;
532 
533   for(int y = 0; y < B.Border.Y; ++y)
534   {
535     cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
536     cpackcol16* EndPtr = SrcPtr + B.Border.X;
537     packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
538 
539     for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr)
540     {
541       LOAD_SRC();
542       NEW_LUMINATE_RED();
543       NEW_LUMINATE_GREEN();
544       NEW_LUMINATE_BLUE();
545       STORE_COLOR();
546     }
547   }
548 }
549 
NormalMaskedBlit(cblitdata & BlitData) const550 void bitmap::NormalMaskedBlit(cblitdata& BlitData) const
551 {
552   blitdata B = BlitData;
553 
554   if(!FastFlag)
555   {
556     if(!B.Border.X || !B.Border.Y)
557       ABORT("Zero-sized bitmap masked blit attempt detected!");
558 
559     if(B.Flags & ROTATE && B.Border.X != B.Border.Y)
560       ABORT("MaskedBlit error: FeLib supports only square rotating!");
561 
562     if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y,
563                      Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y))
564       return;
565   }
566 
567   packcol16** SrcImage = Image;
568   packcol16** DestImage = B.Bitmap->Image;
569   packcol16 PackedMaskColor = B.MaskColor;
570 
571   switch(B.Flags & 7)
572   {
573    case NONE:
574     {
575       for(int y = 0; y < B.Border.Y; ++y)
576       {
577         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
578         cpackcol16* EndPtr = SrcPtr + B.Border.X;
579         packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
580 
581         for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr)
582           if(*SrcPtr != PackedMaskColor)
583             *DestPtr = *SrcPtr;
584       }
585 
586       break;
587     }
588 
589    case MIRROR:
590     {
591       B.Dest.X += B.Border.X - 1;
592 
593       for(int y = 0; y < B.Border.Y; ++y)
594       {
595         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
596         cpackcol16* EndPtr = SrcPtr + B.Border.X;
597         packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
598 
599         for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr)
600           if(*SrcPtr != PackedMaskColor)
601             *DestPtr = *SrcPtr;
602       }
603 
604       break;
605     }
606 
607    case FLIP:
608     {
609       B.Dest.Y += B.Border.Y - 1;
610 
611       for(int y = 0; y < B.Border.Y; ++y)
612       {
613         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
614         cpackcol16* EndPtr = SrcPtr + B.Border.X;
615         packcol16* DestPtr = &DestImage[B.Dest.Y - y][B.Dest.X];
616 
617         for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr)
618           if(*SrcPtr != PackedMaskColor)
619             *DestPtr = *SrcPtr;
620       }
621 
622       break;
623     }
624 
625    case (MIRROR | FLIP):
626     {
627       B.Dest.X += B.Border.X - 1;
628       B.Dest.Y += B.Border.Y - 1;
629 
630       for(int y = 0; y < B.Border.Y; ++y)
631       {
632         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
633         cpackcol16* EndPtr = SrcPtr + B.Border.X;
634         packcol16* DestPtr = &DestImage[B.Dest.Y - y][B.Dest.X];
635 
636         for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr)
637           if(*SrcPtr != PackedMaskColor)
638             *DestPtr = *SrcPtr;
639       }
640 
641       break;
642     }
643 
644    case ROTATE:
645     {
646       B.Dest.X += B.Border.X - 1;
647       int TrueDestXMove = B.Bitmap->Size.X;
648       packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X];
649 
650       for(int y = 0; y < B.Border.Y; ++y)
651       {
652         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
653         cpackcol16* EndPtr = SrcPtr + B.Border.X;
654         packcol16* DestPtr = DestBase - y;
655 
656         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove)
657           if(*SrcPtr != PackedMaskColor)
658             *DestPtr = *SrcPtr;
659       }
660 
661       break;
662     }
663 
664    case (MIRROR | ROTATE):
665     {
666       int TrueDestXMove = B.Bitmap->Size.X;
667       packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X];
668 
669       for(int y = 0; y < B.Border.Y; ++y)
670       {
671         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
672         cpackcol16* EndPtr = SrcPtr + B.Border.X;
673         packcol16* DestPtr = DestBase + y;
674 
675         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove)
676           if(*SrcPtr != PackedMaskColor)
677             *DestPtr = *SrcPtr;
678       }
679 
680       break;
681     }
682 
683    case (FLIP | ROTATE):
684     {
685       B.Dest.X += B.Border.X - 1;
686       B.Dest.Y += B.Border.Y - 1;
687       int TrueDestXMove = B.Bitmap->Size.X;
688       packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X];
689 
690       for(int y = 0; y < B.Border.Y; ++y)
691       {
692         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
693         cpackcol16* EndPtr = SrcPtr + B.Border.X;
694         packcol16* DestPtr = DestBase - y;
695 
696         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove)
697           if(*SrcPtr != PackedMaskColor)
698             *DestPtr = *SrcPtr;
699       }
700 
701       break;
702     }
703 
704    case (MIRROR | FLIP | ROTATE):
705     {
706       B.Dest.Y += B.Border.Y - 1;
707       int TrueDestXMove = B.Bitmap->Size.X;
708       packcol16* DestBase = &DestImage[B.Dest.Y][B.Dest.X];
709 
710       for(int y = 0; y < B.Border.Y; ++y)
711       {
712         cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
713         cpackcol16* EndPtr = SrcPtr + B.Border.X;
714         packcol16* DestPtr = DestBase + y;
715 
716         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove)
717           if(*SrcPtr != PackedMaskColor)
718             *DestPtr = *SrcPtr;
719       }
720 
721       break;
722     }
723   }
724 }
725 
LuminanceMaskedBlit(cblitdata & BlitData) const726 void bitmap::LuminanceMaskedBlit(cblitdata& BlitData) const
727 {
728   blitdata B = BlitData;
729 
730   if(B.Luminance == NORMAL_LUMINANCE)
731   {
732     B.Flags = 0;
733     NormalMaskedBlit(B);
734     return;
735   }
736 
737   if(!FastFlag)
738   {
739     if(!B.Border.X || !B.Border.Y)
740       ABORT("Zero-sized bitmap masked blit attempt detected!");
741 
742     if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y,
743                      Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y))
744       return;
745   }
746 
747   packcol16** SrcImage = Image;
748   packcol16** DestImage = B.Bitmap->Image;
749   int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000;
750   int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800;
751   int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20;
752 
753   for(int y = 0; y < B.Border.Y; ++y)
754   {
755     cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
756     cpackcol16* EndPtr = SrcPtr + B.Border.X;
757     packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
758 
759     for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr)
760     {
761       LOAD_SRC();
762 
763       if(SrcCol != B.MaskColor)
764       {
765         NEW_LUMINATE_RED();
766         NEW_LUMINATE_GREEN();
767         NEW_LUMINATE_BLUE();
768         STORE_COLOR();
769       }
770     }
771   }
772 }
773 
SimpleAlphaBlit(bitmap * Bitmap,alpha Alpha,col16 MaskColor) const774 void bitmap::SimpleAlphaBlit(bitmap* Bitmap, alpha Alpha, col16 MaskColor) const
775 {
776   if(Alpha == 255)
777   {
778     blitdata B = { Bitmap,
779                    { 0, 0 },
780                    { 0, 0 },
781                    { Size.X, Size.Y },
782                    { 0 },
783                    MaskColor,
784                    0 };
785 
786     NormalMaskedBlit(B);
787     return;
788   }
789 
790   if(!FastFlag && (Size.X != Bitmap->Size.X || Size.Y != Bitmap->Size.Y))
791     ABORT("Fast simple alpha blit attempt of noncongruent bitmaps detected!");
792 
793   cpackcol16* SrcPtr = Image[0];
794   cpackcol16* EndPtr = SrcPtr + XSizeTimesYSize;
795   packcol16* DestPtr = Bitmap->Image[0];
796 
797   for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr)
798   {
799     LOAD_SRC();
800 
801     if(SrcCol != MaskColor)
802     {
803       LOAD_DEST();
804       NEW_LOAD_AND_APPLY_ALPHA_RED();
805       NEW_LOAD_AND_APPLY_ALPHA_GREEN();
806       NEW_LOAD_AND_APPLY_ALPHA_BLUE();
807       STORE_COLOR();
808     }
809   }
810 }
811 
AlphaMaskedBlit(cblitdata & BlitData) const812 void bitmap::AlphaMaskedBlit(cblitdata& BlitData) const
813 {
814   blitdata B = BlitData;
815 
816   if(!AlphaMap)
817   {
818     B.Flags = 0;
819     NormalMaskedBlit(B);
820     return;
821   }
822 
823   if(!FastFlag)
824   {
825     if(!B.Border.X || !B.Border.Y)
826       ABORT("Zero-sized bitmap alpha blit attempt detected!");
827 
828     if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y,
829                      Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y))
830       return;
831   }
832 
833   packcol16** SrcImage = Image;
834   packalpha** SrcAlphaMap = AlphaMap;
835   packcol16** DestImage = B.Bitmap->Image;
836 
837   for(int y = 0; y < B.Border.Y; ++y)
838   {
839     cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
840     cpackalpha* AlphaPtr = &SrcAlphaMap[B.Src.Y + y][B.Src.X];
841     cpackcol16* EndPtr = SrcPtr + B.Border.X;
842     packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
843 
844     for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++AlphaPtr)
845     {
846       LOAD_SRC();
847 
848       if(SrcCol != B.MaskColor)
849       {
850         LOAD_DEST();
851         LOAD_ALPHA();
852         NEW_LOAD_AND_APPLY_ALPHA_RED();
853         NEW_LOAD_AND_APPLY_ALPHA_GREEN();
854         NEW_LOAD_AND_APPLY_ALPHA_BLUE();
855         STORE_COLOR();
856       }
857     }
858   }
859 }
860 
DrawLine(v2 From,int ToX,int ToY,col16 Color,truth Wide)861 void bitmap::DrawLine(v2 From, int ToX, int ToY, col16 Color, truth Wide)
862 { DrawLine(From.X, From.Y, ToX, ToY, Color, Wide); }
DrawLine(int FromX,int FromY,v2 To,col16 Color,truth Wide)863 void bitmap::DrawLine(int FromX, int FromY, v2 To, col16 Color, truth Wide)
864 { DrawLine(FromX, FromY, To.X, To.Y, Color, Wide); }
DrawLine(v2 From,v2 To,col16 Color,truth Wide)865 void bitmap::DrawLine(v2 From, v2 To, col16 Color, truth Wide)
866 { DrawLine(From.X, From.Y, To.X, To.Y, Color, Wide); }
867 
DrawLine(int OrigFromX,int OrigFromY,int OrigToX,int OrigToY,col16 Color,truth Wide)868 void bitmap::DrawLine(int OrigFromX, int OrigFromY, int OrigToX, int OrigToY, col16 Color, truth Wide)
869 {
870   if(OrigFromY == OrigToY)
871   {
872     DrawHorizontalLine(OrigFromX, OrigToX, OrigFromY, Color, Wide);
873     return;
874   }
875 
876   if(OrigFromX == OrigToX)
877   {
878     DrawVerticalLine(OrigFromX, OrigFromY, OrigToY, Color, Wide);
879     return;
880   }
881 
882   static cint PointX[] = { 0, 0, -1, 1, 0 };
883   static cint PointY[] = { 0, -1, 0, 0, 1 };
884   cint Times = Wide ? 5 : 1;
885 
886   for(int c1 = 0; c1 < Times; ++c1)
887   {
888     cint X1 = OrigFromX + PointX[c1];
889     cint Y1 = OrigFromY + PointY[c1];
890     cint X2 = OrigToX + PointX[c1];
891     cint Y2 = OrigToY + PointY[c1];
892     cint DeltaX = abs(X2 - X1);
893     cint DeltaY = abs(Y2 - Y1);
894     int x, c2;
895     int XChange, PtrXChange, PtrYChange;
896     int DoubleDeltaX, DoubleDeltaY, End;
897 
898     if(DeltaX >= DeltaY)
899     {
900       x = X1;
901       c2 = DeltaX;
902       PtrXChange = XChange = X1 < X2 ? 1 : -1;
903       PtrYChange = Y1 < Y2 ? Size.X : -Size.X;
904       DoubleDeltaX = DeltaX << 1;
905       DoubleDeltaY = DeltaY << 1;
906       End = X2;
907     }
908     else
909     {
910       x = Y1;
911       c2 = DeltaY;
912       XChange = Y1 < Y2 ? 1 : -1;
913       PtrXChange = Y1 < Y2 ? Size.X : -Size.X;
914       PtrYChange = X1 < X2 ? 1 : -1;
915       DoubleDeltaX = DeltaY << 1;
916       DoubleDeltaY = DeltaX << 1;
917       End = Y2;
918     }
919 
920     packcol16* Ptr = &Image[Y1][X1];
921     *Ptr = Color;
922 
923     while(x != End)
924     {
925       x += XChange;
926       Ptr += PtrXChange;
927       c2 += DoubleDeltaY;
928 
929       if(c2 >= DoubleDeltaX)
930       {
931         c2 -= DoubleDeltaX;
932         Ptr += PtrYChange;
933       }
934 
935       *Ptr = Color;
936     }
937   }
938 }
939 
DrawVerticalLine(int OrigX,int OrigFromY,int OrigToY,col16 Color,truth Wide)940 void bitmap::DrawVerticalLine(int OrigX, int OrigFromY, int OrigToY, col16 Color, truth Wide)
941 {
942   static cint PointX[] = { 0, -1, 1 };
943   cint Times = Wide ? 3 : 1;
944 
945   for(int c = 0; c < Times; ++c)
946   {
947     int X = OrigX + PointX[c];
948     int FromY = OrigFromY;
949     int ToY = OrigToY;
950 
951     if(FromY > ToY)
952       Swap(FromY, ToY);
953 
954     if(Wide && !c)
955     {
956       --FromY;
957       ++ToY;
958     }
959 
960     if(X < 0 || X >= Size.X || ToY < 0 || FromY >= Size.Y)
961       continue;
962 
963     FromY = Max(FromY, 0);
964     ToY = Min(ToY, Size.Y-1);
965     packcol16* Ptr = &Image[FromY][X];
966 
967     for(int y = FromY; y <= ToY; ++y, Ptr += Size.X)
968       *Ptr = Color;
969   }
970 }
971 
DrawHorizontalLine(int OrigFromX,int OrigToX,int OrigY,col16 Color,truth Wide)972 void bitmap::DrawHorizontalLine(int OrigFromX, int OrigToX, int OrigY, col16 Color, truth Wide)
973 {
974   static cint PointY[] = { 0, -1, 1 };
975   cint Times = Wide ? 3 : 1;
976 
977   for(int c = 0; c < Times; ++c)
978   {
979     int Y = OrigY + PointY[c];
980     int FromX = OrigFromX;
981     int ToX = OrigToX;
982 
983     if(FromX > ToX)
984       Swap(FromX, ToX);
985 
986     if(Wide && !c)
987     {
988       --FromX;
989       ++ToX;
990     }
991 
992     if(Y < 0 || Y >= Size.Y || ToX < 0 || FromX >= Size.X)
993       continue;
994 
995     FromX = Max(FromX, 0);
996     ToX = Min(ToX, Size.X-1);
997     packcol16* Ptr = &Image[Y][FromX];
998 
999     for(int x = FromX; x <= ToX; ++x, ++Ptr)
1000       *Ptr = Color;
1001   }
1002 }
1003 
DrawPolygon(int CenterX,int CenterY,int Radius,int NumberOfSides,col16 Color,truth DrawSides,truth DrawDiameters,double Rotation)1004 void bitmap::DrawPolygon(int CenterX, int CenterY, int Radius, int NumberOfSides,
1005                          col16 Color, truth DrawSides, truth DrawDiameters, double Rotation)
1006 {
1007   if(!DrawSides && !DrawDiameters)
1008     return;
1009 
1010   v2* Point = new v2[NumberOfSides];
1011   double AngleDelta = 2 * FPI / NumberOfSides;
1012   int c;
1013 
1014   for(c = 0; c < NumberOfSides; ++c)
1015   {
1016     Point[c].X = CenterX + int(sin(AngleDelta * c + Rotation) * Radius);
1017     Point[c].Y = CenterY + int(cos(AngleDelta * c + Rotation) * Radius);
1018   }
1019 
1020   if(DrawDiameters)
1021   {
1022     if(DrawSides)
1023     {
1024       for(c = 0; c < NumberOfSides; ++c)
1025         for(int a = 0; a < NumberOfSides; ++a)
1026           if(c != a)
1027             DrawLine(Point[c].X, Point[c].Y, Point[a].X, Point[a].Y, Color, true);
1028     }
1029     else
1030     {
1031       for(c = 0; c < NumberOfSides; ++c)
1032         for(int a = 0; a < NumberOfSides; ++a)
1033           if((c - a > 1 || a - c > 1) && (a || c != NumberOfSides - 1) && (c || a != NumberOfSides - 1))
1034             DrawLine(Point[c].X, Point[c].Y, Point[a].X, Point[a].Y, Color, true);
1035     }
1036   }
1037   else
1038   {
1039     for(c = 0; c < NumberOfSides - 1; ++c)
1040       DrawLine(Point[c].X, Point[c].Y, Point[c + 1].X, Point[c + 1].Y, Color, true);
1041 
1042     DrawLine(Point[NumberOfSides - 1].X, Point[NumberOfSides - 1].Y, Point[0].X, Point[0].Y, Color, true);
1043   }
1044 
1045   delete [] Point;
1046 }
1047 
CreateAlphaMap(alpha InitialValue)1048 void bitmap::CreateAlphaMap(alpha InitialValue)
1049 {
1050   if(AlphaMap)
1051     ABORT("Alpha leak detected!");
1052 
1053   Alloc2D(AlphaMap, Size.Y, Size.X);
1054   memset(AlphaMap[0], InitialValue, XSizeTimesYSize);
1055 }
1056 
Fade(long & AlphaSum,packalpha & AlphaAverage,int Amount)1057 truth bitmap::Fade(long& AlphaSum, packalpha& AlphaAverage, int Amount)
1058 {
1059   if(!AlphaMap)
1060     ABORT("No alpha map to fade.");
1061 
1062   truth Changes = false;
1063   long Alphas = 0;
1064   long NewAlphaSum = 0;
1065   long Size = XSizeTimesYSize;
1066 
1067   for(long c = 0; c < Size; ++c)
1068   {
1069     packalpha* AlphaPtr = &AlphaMap[0][c];
1070 
1071     if(*AlphaPtr)
1072     {
1073       if(*AlphaPtr > Amount)
1074       {
1075         *AlphaPtr -= Amount;
1076         NewAlphaSum += *AlphaPtr;
1077         ++Alphas;
1078         Changes = true;
1079       }
1080       else
1081       {
1082         *AlphaPtr = 0;
1083         Changes = true;
1084 
1085         if(RandMap)
1086           UpdateRandMap(c, false);
1087       }
1088     }
1089   }
1090 
1091   AlphaSum = NewAlphaSum;
1092   AlphaAverage = Alphas ? NewAlphaSum / Alphas : 0;
1093   return Changes;
1094 }
1095 
Outline(col16 Color,alpha Alpha,priority Priority)1096 void bitmap::Outline(col16 Color, alpha Alpha, priority Priority)
1097 {
1098   if(!AlphaMap)
1099     CreateAlphaMap(255);
1100 
1101   col16 LastColor, NextColor;
1102   int XMax = Size.X;
1103   int YMax = Size.Y - 1;
1104 
1105   for(int x = 0; x < XMax; ++x)
1106   {
1107     packcol16* Buffer = &Image[0][x];
1108     LastColor = *Buffer;
1109 
1110     for(int y = 0; y < YMax; ++y)
1111     {
1112       NextColor = *(Buffer + XMax);
1113 
1114       if((LastColor == TRANSPARENT_COLOR || !y) && NextColor != TRANSPARENT_COLOR)
1115       {
1116         *Buffer = Color;
1117         SetAlpha(x, y, Alpha);
1118         SafeSetPriority(x, y, Priority);
1119       }
1120 
1121       Buffer += XMax;
1122 
1123       if(LastColor != TRANSPARENT_COLOR && (NextColor == TRANSPARENT_COLOR || y == YMax - 1))
1124       {
1125         *Buffer = Color;
1126         SetAlpha(x, y + 1, Alpha);
1127         SafeSetPriority(x, y + 1, Priority);
1128       }
1129 
1130       LastColor = NextColor;
1131     }
1132   }
1133 
1134   --XMax;
1135   ++YMax;
1136 
1137   for(int y = 0; y < YMax; ++y)
1138   {
1139     packcol16* Buffer = Image[y];
1140     LastColor = *Buffer;
1141 
1142     for(int x = 0; x < XMax; ++x)
1143     {
1144       NextColor = *(Buffer + 1);
1145 
1146       if((LastColor == TRANSPARENT_COLOR || !x) && NextColor != TRANSPARENT_COLOR)
1147       {
1148         *Buffer = Color;
1149         SetAlpha(x, y, Alpha);
1150         SafeSetPriority(x, y, Priority);
1151       }
1152 
1153       ++Buffer;
1154 
1155       if(LastColor != TRANSPARENT_COLOR && (NextColor == TRANSPARENT_COLOR || x == XMax - 1))
1156       {
1157         *Buffer = Color;
1158         SetAlpha(x + 1, y, Alpha);
1159         SafeSetPriority(x + 1, y, Priority);
1160       }
1161 
1162       LastColor = NextColor;
1163     }
1164   }
1165 }
1166 
FadeToScreen(bitmapeditor BitmapEditor)1167 void bitmap::FadeToScreen(bitmapeditor BitmapEditor)
1168 {
1169   bitmap Backup(DOUBLE_BUFFER);
1170   Backup.ActivateFastFlag();
1171   blitdata B = { DOUBLE_BUFFER,
1172                  { 0, 0 },
1173                  { 0, 0 },
1174                  { RES.X, RES.Y },
1175                  { 0 },
1176                  0,
1177                  0 };
1178 
1179   for(int c = 0; c <= 5; ++c)
1180   {
1181     clock_t StartTime = clock();
1182     int Element = 127 - c * 25;
1183     B.Luminance = MakeRGB24(Element, Element, Element);
1184     Backup.LuminanceMaskedBlit(B);
1185 
1186     if(BitmapEditor)
1187       BitmapEditor(this, true);
1188 
1189     SimpleAlphaBlit(DOUBLE_BUFFER, c * 50, 0);
1190     graphics::BlitDBToScreen();
1191     while(clock() - StartTime < 0.05 * CLOCKS_PER_SEC);
1192   }
1193 
1194   DOUBLE_BUFFER->ClearToColor(0);
1195 
1196   if(BitmapEditor)
1197     BitmapEditor(this, true);
1198 
1199   B.Flags = 0;
1200   NormalMaskedBlit(B);
1201   graphics::BlitDBToScreen();
1202 }
1203 
1204 std::vector<SDL_Surface*> vSurfaceCache;
SurfaceCache(blitdata B,bool bUseScale)1205 SDL_Surface* SurfaceCache(blitdata B,bool bUseScale){ // good to prevent memory hungryness
1206   int iW=B.Border.X;
1207   if(bUseScale)iW*=B.Stretch;
1208 
1209   int iH=B.Border.Y;
1210   if(bUseScale)iH*=B.Stretch;
1211 
1212   for(int i=0;i<vSurfaceCache.size();i++){
1213     if(vSurfaceCache[i]->w==iW && vSurfaceCache[i]->h==iH){
1214       return vSurfaceCache[i];
1215     }
1216   }
1217 
1218   DBG4("Cache not found for surface w=",iW," h=",iH);
1219   SDL_Surface* srf = libxbrzscale::createARGBSurface(iW, iH); //src img for look zoom at least is always 16x16 tho
1220 
1221   vSurfaceCache.push_back(srf);DBG3(srf,DBGI(srf->w),DBGI(srf->h));
1222   DBGSI(vSurfaceCache.size());
1223 
1224   return srf;
1225 }
1226 
ResetBlitdataRotation(blitdata & B)1227 void bitmap::ResetBlitdataRotation(blitdata& B)
1228 {
1229   B.Flags &= ~MIRROR;
1230   B.Flags &= ~FLIP;
1231   B.Flags &= ~ROTATE;
1232 }
1233 
ConfigureBlitdataRotation(blitdata & B,int iR)1234 void bitmap::ConfigureBlitdataRotation(blitdata& B,int iR){
1235   ResetBlitdataRotation(B);
1236 
1237   // set
1238   iR %= 4;
1239   /****
1240    * 1 1
1241    * 2 2
1242    * 3 3
1243    * 4 0->4
1244    * 5 1
1245    * 6 2
1246    * 7 3
1247    * 8 0->4
1248    */
1249   if(iR==0)iR = 4 * (iR>0 ? 1 : -1);
1250   /*** the blade side is inverted, so invert the rotation
1251    * -1 -> -4
1252    * -2 -> -3
1253    * -3 -> -2
1254    * -4 -> -1
1255    */
1256   switch(iR){
1257   case -1:iR=-4;break;
1258   case -2:iR=-3;break;
1259   case -3:iR=-2;break;
1260   case -4:iR=-1;break;
1261   }
1262 
1263   switch(iR){
1264   case 1:    // 1st step always rotate once
1265     B.Flags |= ROTATE; //90 degrees
1266     break;
1267   case 2:
1268     B.Flags |= FLIP|MIRROR; //180 degrees
1269     break;
1270   case 3:
1271     B.Flags |= ROTATE|FLIP|MIRROR; //270 degrees
1272     break;
1273   case 4:
1274     // initial/default rotation
1275     break;
1276   case -1:    // 1st step always rotate once
1277     B.Flags |= MIRROR; //-90 degrees
1278     break;
1279   case -2:
1280     B.Flags |= FLIP|ROTATE; //-180 degrees
1281     break;
1282   case -3:
1283     B.Flags |= FLIP; //-270 degrees
1284     break;
1285   case -4:
1286     B.Flags |= MIRROR|ROTATE; //-0 degrees (like a mirrored image :))
1287     break;
1288   }
1289 }
1290 
1291 SDL_PixelFormat* fmt = SDL_AllocFormat(SDL_PIXELFORMAT_RGBA8888); //format based on xbrzscale code
1292 
CopyToSurface(v2 v2TopLeft,v2 v2Size,col16 MaskColor,SDL_Surface * srf) const1293 SDL_Surface* bitmap::CopyToSurface(v2 v2TopLeft, v2 v2Size, col16 MaskColor, SDL_Surface* srf) const
1294 {
1295   if(srf==NULL) srf = libxbrzscale::createARGBSurface(this->Size.X,this->Size.Y);
1296 
1297   // copy pixels to surface
1298   Uint32 color32bit;
1299   packcol16 PixelFrom;
1300   unsigned char ca=0xff;
1301   for(int x1 = 0; x1 < v2Size.X; x1++)
1302   {
1303     for(int y1 = 0; y1 < v2Size.Y; y1++)
1304     {
1305       PixelFrom = Image[v2TopLeft.Y + y1][v2TopLeft.X + x1];
1306 
1307       if(PixelFrom == MaskColor){ //0 invisible, 0xff opaque
1308         ca = 0;
1309 //        static packcol16 DarkestNotBlack = MakeRGB16(1,1,1);
1310 //        PixelFrom = DarkestNotBlack; //didnt work :(
1311         PixelFrom = WHITE; //xBRZ blends countours perfectly this way
1312       }else{
1313         ca = 0xff;
1314       }
1315 
1316       color32bit = SDL_MapRGBA(
1317         fmt,
1318         (unsigned char)GetRed16(PixelFrom),
1319         (unsigned char)GetGreen16(PixelFrom),
1320         (unsigned char)GetBlue16(PixelFrom),
1321         ca
1322       );
1323       libxbrzscale::SDL_PutPixel(srf, x1, y1, color32bit);
1324     }
1325   }
1326 
1327   return srf;
1328 }
1329 
1330 /**
1331  * stretch from 2 to 6 only!
1332  */
StretchBlitXbrz(cblitdata & BlitDataTo,bool bAllowTransparency) const1333 void bitmap::StretchBlitXbrz(cblitdata& BlitDataTo, bool bAllowTransparency) const
1334 {
1335   blitdata Bto = BlitDataTo;
1336 
1337   if(
1338     !femath::Clip( //fixes blitdata if necessary
1339       Bto.Src.X, Bto.Src.Y, Bto.Dest.X, Bto.Dest.Y,
1340       Bto.Border.X, Bto.Border.Y, Size.X, Size.Y,
1341       Bto.Bitmap->Size.X, Bto.Bitmap->Size.Y
1342     )
1343   ){
1344     return;
1345   }
1346 
1347   if( //TODO as femath::Clip() fixes the blitdata, confirm this is unnecessary now and remove this check
1348       (Bto.Src.X+Bto.Border.X) > Size.X
1349       ||
1350       (Bto.Src.Y+Bto.Border.Y) > Size.Y
1351   ){
1352     ABORT("requested copy from rectangle pos=%d,%d size=%d,%d outside of limits=%d,%d",Bto.Src.X,Bto.Src.Y,Bto.Border.X,Bto.Border.Y,Size.X,Size.Y);
1353   }
1354 
1355   if(Bto.Dest.X>=Bto.Bitmap->GetSize().X || Bto.Dest.Y>=Bto.Bitmap->GetSize().Y){
1356     ABORT("invalid stretch destination %d,%d on target bitmap size %d,%d",Bto.Dest.X,Bto.Dest.Y,Bto.Bitmap->GetSize().X,Bto.Bitmap->GetSize().Y);
1357   }
1358 
1359   static bool bXbrzLibCfgInitialized=false;
1360   if(!bXbrzLibCfgInitialized){ //TODO this config should be placed more globally?
1361     libxbrzscale::setUseCache(true);DBGLN;
1362 #ifdef DBGMSG
1363     libxbrzscale::setDebugMsg(true);DBGLN;
1364 #endif
1365     libxbrzscale::setFreeSurfaceAfterScale(false,false);DBGLN;
1366     bXbrzLibCfgInitialized=true;DBGLN;
1367   }
1368 
1369   bool bFreeImg=false;DBGLN;
1370 
1371   DBG2(Bto.Bitmap,DBGAV2(Bto.Bitmap->GetSize()));
1372   SDL_Surface* imgCopy = CopyToSurface(Bto.Src, Bto.Border, Bto.MaskColor, SurfaceCache(Bto,false));DBGLN;
1373 
1374   Uint32 color32bit;DBGLN;
1375   unsigned char cr,cg,cb,ca;DBGLN;
1376   SDL_Surface* imgStretchedCopy=NULL;DBGLN;
1377   imgStretchedCopy=libxbrzscale::scale(SurfaceCache(Bto,true), imgCopy, Bto.Stretch);DBG3(imgStretchedCopy,DBGI(imgStretchedCopy->w),DBGI(imgStretchedCopy->h));
1378 //  if( ((Bto.Dest.X+imgStretchedCopy->w) >= Bto.Bitmap->GetSize().X) ||
1379 //      ((Bto.Dest.Y+imgStretchedCopy->h) >= Bto.Bitmap->GetSize().Y)    )ABORT("blit %d,%d + %d,%d outside dest bitmap %d,%d",Bto.Dest.X,Bto.Dest.Y,imgStretchedCopy->w,imgStretchedCopy->h,Bto.Bitmap->GetSize().X,Bto.Bitmap->GetSize().Y);
1380   // copy from surface the scaled image back to where it is expected TODO comment a more precise info...
1381   for(int x1 = 0; x1 < imgStretchedCopy->w; ++x1)
1382   {
1383     for(int y1 = 0; y1 < imgStretchedCopy->h; ++y1)
1384     {
1385       color32bit = libxbrzscale::SDL_GetPixel(imgStretchedCopy,x1,y1);//DBGLN;
1386       SDL_GetRGBA(color32bit,fmt,&cr,&cg,&cb,&ca);//DBGLN;
1387       if(!bAllowTransparency || ca==0xff){ //TODO ca==0xff may work better than ca!=0 the day xBRZScale blends from opaque to transparent with a half-transparent alpha value as result!?
1388         if((Bto.Dest.X+x1)<Bto.Bitmap->GetSize().X && (Bto.Dest.Y+y1)<Bto.Bitmap->GetSize().Y){
1389           Bto.Bitmap->Image[Bto.Dest.Y+y1][Bto.Dest.X+x1] = MakeRGB16(cr,cg,cb);//DBGLN; //TODO does alpha make any sense here anyway?
1390         }
1391       }
1392     }
1393   }
1394   DBGLN;
1395 
1396 //  SDL_FreeSurface(imgCopy);
1397 //  SDL_FreeSurface(imgStretchedCopy);
1398 }
1399 
StretchBlit(cblitdata & BlitData) const1400 void bitmap::StretchBlit(cblitdata& BlitData) const
1401 {
1402   blitdata B = BlitData;
1403 
1404   if(!FastFlag)
1405   {
1406     if(!B.Border.X || !B.Border.Y)
1407       ABORT("Zero-sized bitmap stretch blit attempt detected!");
1408 
1409     if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y,
1410                      Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y))
1411       return;
1412   }
1413 
1414   if(B.Stretch > 1)
1415   {
1416     int tx = B.Dest.X;
1417 
1418     for(int x1 = B.Src.X; x1 < B.Src.X + B.Border.X; ++x1, tx += B.Stretch)
1419     {
1420       int ty = B.Dest.Y;
1421 
1422       for(int y1 = B.Src.Y; y1 < B.Src.Y + B.Border.Y; ++y1, ty += B.Stretch)
1423       {
1424         packcol16 Pixel = Image[y1][x1];
1425 
1426         if(Pixel != TRANSPARENT_COLOR)
1427           for(int x2 = tx; x2 < tx + B.Stretch; ++x2)
1428             for(int y2 = ty; y2 < ty + B.Stretch; ++y2)
1429               B.Bitmap->Image[y2][x2] = Pixel;
1430       }
1431     }
1432 
1433     return;
1434   }
1435   else if(B.Stretch < -1)
1436   {
1437     int tx = B.Dest.X;
1438 
1439     for(int x1 = B.Src.X; x1 < B.Src.X + B.Border.X; x1 -= B.Stretch, ++tx)
1440     {
1441       int ty = B.Dest.Y;
1442 
1443       for(int y1 = B.Src.Y; y1 < B.Src.Y + B.Border.Y; y1 -= B.Stretch, ++ty)
1444       {
1445         packcol16 Pixel = Image[y1][x1];
1446 
1447         if(Pixel != TRANSPARENT_COLOR)
1448           B.Bitmap->Image[ty][tx] = Pixel;
1449       }
1450     }
1451 
1452     return;
1453   }
1454   else
1455   {
1456     B.Flags = 0;
1457     NormalMaskedBlit(B);
1458     return;
1459   }
1460 }
1461 
operator <<(outputfile & SaveFile,cbitmap * Bitmap)1462 outputfile& operator<<(outputfile& SaveFile, cbitmap* Bitmap)
1463 {
1464   if(Bitmap)
1465   {
1466     SaveFile.Put(1);
1467     SaveFile << Bitmap->GetSize();
1468     Bitmap->Save(SaveFile);
1469   }
1470   else
1471     SaveFile.Put(0);
1472 
1473   return SaveFile;
1474 }
1475 
operator >>(inputfile & SaveFile,bitmap * & Bitmap)1476 inputfile& operator>>(inputfile& SaveFile, bitmap*& Bitmap)
1477 {
1478   if(SaveFile.Get())
1479   {
1480     Bitmap = new bitmap(ReadType<v2>(SaveFile));
1481     Bitmap->Load(SaveFile);
1482   }
1483   else
1484     Bitmap = 0;
1485 
1486   return SaveFile;
1487 }
1488 
DrawRectangle(v2 TopLeft,int Right,int Bottom,col16 Color,truth Wide)1489 void bitmap::DrawRectangle(v2 TopLeft, int Right, int Bottom, col16 Color, truth Wide)
1490 { DrawRectangle(TopLeft.X, TopLeft.Y, Right, Bottom, Color, Wide); }
DrawRectangle(int Left,int Top,v2 BottomRight,col16 Color,truth Wide)1491 void bitmap::DrawRectangle(int Left, int Top, v2 BottomRight, col16 Color, truth Wide)
1492 { DrawRectangle(Left, Top, BottomRight.X, BottomRight.Y, Color, Wide); }
DrawRectangle(v2 TopLeft,v2 BottomRight,col16 Color,truth Wide)1493 void bitmap::DrawRectangle(v2 TopLeft, v2 BottomRight, col16 Color, truth Wide)
1494 { DrawRectangle(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y, Color, Wide); }
1495 
DrawRectangle(int Left,int Top,int Right,int Bottom,col16 Color,truth Wide)1496 void bitmap::DrawRectangle(int Left, int Top, int Right, int Bottom, col16 Color, truth Wide)
1497 {
1498   DrawHorizontalLine(Left, Right, Top, Color, Wide);
1499   DrawHorizontalLine(Left, Right, Bottom, Color, Wide);
1500   DrawVerticalLine(Right, Top, Bottom, Color, Wide);
1501   DrawVerticalLine(Left, Top, Bottom, Color, Wide);
1502 }
1503 
AlphaLuminanceBlit(cblitdata & BlitData) const1504 void bitmap::AlphaLuminanceBlit(cblitdata& BlitData) const
1505 {
1506   if(BlitData.Luminance == NORMAL_LUMINANCE)
1507   {
1508     AlphaMaskedBlit(BlitData);
1509     return;
1510   }
1511 
1512   if(!AlphaMap)
1513   {
1514     LuminanceMaskedBlit(BlitData);
1515     return;
1516   }
1517 
1518   blitdata B = BlitData;
1519 
1520   if(!FastFlag)
1521   {
1522     if(!B.Border.X || !B.Border.Y)
1523       ABORT("Zero-sized bitmap alpha blit attempt detected!");
1524 
1525     if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y,
1526                      Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y))
1527       return;
1528   }
1529 
1530   packcol16** SrcImage = Image;
1531   packalpha** SrcAlphaMap = AlphaMap;
1532   packcol16** DestImage = B.Bitmap->Image;
1533   int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000;
1534   int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800;
1535   int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20;
1536 
1537   for(int y = 0; y < B.Border.Y; ++y)
1538   {
1539     cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
1540     cpackalpha* AlphaPtr = &SrcAlphaMap[B.Src.Y + y][B.Src.X];
1541     cpackcol16* EndPtr = SrcPtr + B.Border.X;
1542     packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
1543 
1544     for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++AlphaPtr)
1545     {
1546       LOAD_SRC();
1547 
1548       if(SrcCol != B.MaskColor)
1549       {
1550         LOAD_DEST();
1551         LOAD_ALPHA();
1552         NEW_LUMINATE_RED();
1553         NEW_APPLY_ALPHA_RED();
1554         NEW_LUMINATE_GREEN();
1555         NEW_APPLY_ALPHA_GREEN();
1556         NEW_LUMINATE_BLUE();
1557         NEW_APPLY_ALPHA_BLUE();
1558         STORE_COLOR();
1559       }
1560     }
1561   }
1562 }
1563 
1564 /* Only works for 16x16 pictures :( */
1565 
CreateFlames(rawbitmap * RawBitmap,v2 RawPos,ulong SeedNFlags,int Frame)1566 void bitmap::CreateFlames(rawbitmap* RawBitmap, v2 RawPos, ulong SeedNFlags, int Frame)
1567 {
1568   femath::SaveSeed();
1569   femath::SetSeed(SeedNFlags);
1570   int FlameTop[16], FlameBottom[16], FlamePhase[16];
1571   int x, y;
1572 
1573   for(x = 0; x < 16; ++x)
1574   {
1575     FlameBottom[x] = NO_FLAME;
1576 
1577     for(y = 0; y < 16; ++y)
1578       if(GetPixel(x, y) != TRANSPARENT_COLOR)
1579       {
1580         if(1 << RawBitmap->GetMaterialColorIndex(RawPos.X + x, RawPos.Y + y) & SeedNFlags)
1581         {
1582           FlamePhase[x] = RAND_16;
1583 
1584           if(y > 1)
1585           {
1586             FlameBottom[x] = y - 1;
1587 
1588             if(y >= 5)
1589               FlameTop[x] = (y - (RAND_32 * y >> 5)) >> 1;
1590             else
1591               FlameTop[x] = 0;
1592           }
1593           else
1594           {
1595             FlameBottom[x] = 1;
1596             FlameTop[x] = 0;
1597           }
1598         }
1599 
1600         break;
1601       }
1602   }
1603 
1604   for(x = 0; x < 16; ++x)
1605   {
1606     if(FlameBottom[x] != NO_FLAME)
1607     {
1608       int Phase = (Frame + FlamePhase[x]) & 15;
1609       int Length = FlameBottom[x] - FlameTop[x];
1610       int Top = FlameBottom[x] - Length + Phase * (15 - Phase) * Length / 56;
1611 
1612       for(y = Top; y <= FlameBottom[x]; ++y)
1613       {
1614         int Pos = y - Top;
1615         PowerPutPixel(x, y, MakeRGB16(255, 255 - (Pos << 7) / Length, 0), 127 + (Pos << 6) / Length, AVERAGE_PRIORITY);
1616       }
1617     }
1618   }
1619 
1620   femath::LoadSeed();
1621 }
1622 
CreateSparkle(v2 SparklePos,int Frame)1623 void bitmap::CreateSparkle(v2 SparklePos, int Frame)
1624 {
1625   if(Frame)
1626   {
1627     int Size = (Frame - 1) * (16 - Frame) / 10;
1628     PowerPutPixel(SparklePos.X, SparklePos.Y, WHITE, 255, SPARKLE_PRIORITY);
1629 
1630     for(int c = 1; c < Size; ++c)
1631     {
1632       int Lightness = 191 + ((Size - c) << 6) / Size;
1633       col16 RGB = MakeRGB16(Lightness, Lightness, Lightness);
1634       PowerPutPixel(SparklePos.X + c, SparklePos.Y, RGB, 255, SPARKLE_PRIORITY);
1635       PowerPutPixel(SparklePos.X - c, SparklePos.Y, RGB, 255, SPARKLE_PRIORITY);
1636       PowerPutPixel(SparklePos.X, SparklePos.Y + c, RGB, 255, SPARKLE_PRIORITY);
1637       PowerPutPixel(SparklePos.X, SparklePos.Y - c, RGB, 255, SPARKLE_PRIORITY);
1638     }
1639   }
1640 }
1641 
CreateFlies(ulong Seed,int Frame,int FlyAmount)1642 void bitmap::CreateFlies(ulong Seed, int Frame, int FlyAmount)
1643 {
1644   femath::SaveSeed();
1645   femath::SetSeed(Seed);
1646 
1647   for(int c = 0; c < FlyAmount; ++c)
1648   {
1649     double Constant = double(RAND() % 10000) / 10000 * FPI;
1650     v2 StartPos = v2(5 + RAND() % 6, 5 + RAND() % 6);
1651     double Temp = (double(16 - Frame) * FPI) / 16;
1652 
1653     if(RAND() & 1)
1654       Temp = -Temp;
1655 
1656     v2 Where;
1657     Where.X = int(StartPos.X + sin(Constant + Temp) * 3);
1658     Where.Y = int(StartPos.Y + sin(2 * (Constant + Temp)) * 3);
1659     PowerPutPixel(Where.X, Where.Y, MakeRGB16(40, 40, 60), 255, FLY_PRIORITY);
1660   }
1661 
1662   femath::LoadSeed();
1663 }
1664 
CreateLightning(ulong Seed,col16 Color)1665 void bitmap::CreateLightning(ulong Seed, col16 Color)
1666 {
1667   femath::SaveSeed();
1668   femath::SetSeed(Seed);
1669   v2 StartPos;
1670   v2 Direction(0, 0);
1671 
1672   do
1673   {
1674     do
1675     {
1676       if(RAND() & 1)
1677       {
1678         if(RAND() & 1)
1679         {
1680           StartPos.X = 0;
1681           Direction.X = 1;
1682         }
1683         else
1684         {
1685           StartPos.X = Size.X - 1;
1686           Direction.X = -1;
1687         }
1688 
1689         StartPos.Y = RAND() % Size.Y;
1690       }
1691       else
1692       {
1693         if(RAND() & 1)
1694         {
1695           StartPos.Y = 0;
1696           Direction.Y = 1;
1697         }
1698         else
1699         {
1700           StartPos.Y = Size.Y - 1;
1701           Direction.Y = -1;
1702         }
1703 
1704         StartPos.X = RAND() % Size.X;
1705       }
1706     }
1707     while(GetPixel(StartPos) != TRANSPARENT_COLOR);
1708   }
1709   while(!CreateLightning(StartPos, Direction, NO_LIMIT, Color));
1710 
1711   femath::LoadSeed();
1712 }
1713 
1714 struct pixelvectorcontroller
1715 {
Handlerpixelvectorcontroller1716   static truth Handler(int x, int y)
1717   {
1718     if(CurrentSprite->GetPixel(x, y) == TRANSPARENT_COLOR)
1719     {
1720       PixelVector.push_back(v2(x, y));
1721       return true;
1722     }
1723     else
1724       return false;
1725   }
1726   static std::vector<v2> PixelVector;
1727   static bitmap* CurrentSprite;
1728 };
1729 
1730 std::vector<v2> pixelvectorcontroller::PixelVector;
1731 bitmap* pixelvectorcontroller::CurrentSprite;
1732 
CreateLightning(v2 StartPos,v2 Direction,int MaxLength,col16 Color)1733 truth bitmap::CreateLightning(v2 StartPos, v2 Direction, int MaxLength, col16 Color)
1734 {
1735   pixelvectorcontroller::CurrentSprite = this;
1736   std::vector<v2>& PixelVector = pixelvectorcontroller::PixelVector;
1737   PixelVector.clear();
1738   v2 LastMove(0, 0);
1739   int Counter = 0;
1740 
1741   for(;;)
1742   {
1743     v2 Move(1 + (RAND() & 3), 1 + (RAND() & 3));
1744 
1745     if(Direction.X < 0 || (!Direction.X && RAND() & 1))
1746       Move.X = -Move.X;
1747 
1748     if(Direction.Y < 0 || (!Direction.Y && RAND() & 1))
1749       Move.Y = -Move.Y;
1750 
1751     LimitRef(Move.X, -StartPos.X, Size.X - StartPos.X - 1);
1752     LimitRef(Move.Y, -StartPos.Y, Size.X - StartPos.Y - 1);
1753 
1754     if(Counter < 10 && ((!Move.Y && !LastMove.Y)
1755                         || (Move.Y && LastMove.Y && (Move.X << 10) / Move.Y == (LastMove.X << 10) / LastMove.Y)))
1756     {
1757       ++Counter;
1758       continue;
1759     }
1760 
1761     Counter = 0;
1762 
1763     if(!mapmath<pixelvectorcontroller>::DoLine(StartPos.X, StartPos.Y, StartPos.X + Move.X, StartPos.Y + Move.Y)
1764        || ulong(MaxLength) <= PixelVector.size())
1765     {
1766       int Limit = Min<int>(PixelVector.size(), MaxLength);
1767 
1768       for(int c = 0; c < Limit; ++c)
1769       {
1770         PutPixel(PixelVector[c], Color);
1771         SafeSetPriority(PixelVector[c], LIGHTNING_PRIORITY);
1772       }
1773 
1774       PixelVector.clear();
1775       return true;
1776     }
1777 
1778     StartPos += Move;
1779     LastMove = Move;
1780 
1781     if((Direction.X && (!StartPos.X || StartPos.X == Size.X - 1))
1782        || (Direction.Y && (!StartPos.Y || StartPos.Y == Size.X - 1)))
1783     {
1784       PixelVector.clear();
1785       return false;
1786     }
1787   }
1788 }
1789 
BlitAndCopyAlpha(bitmap * Bitmap,int Flags) const1790 void bitmap::BlitAndCopyAlpha(bitmap* Bitmap, int Flags) const
1791 {
1792   if(!FastFlag)
1793   {
1794     if(!AlphaMap || !Bitmap->AlphaMap)
1795       ABORT("Attempt to blit and copy alpha without an alpha map detected!");
1796 
1797     if(Flags & ROTATE && Size.X != Size.Y)
1798       ABORT("Blit and copy alpha error: FeLib supports only square rotating!");
1799 
1800     if(Size.X != Bitmap->Size.X || Size.Y != Bitmap->Size.Y)
1801       ABORT("Blit and copy alpha attempt of noncongruent bitmaps detected!");
1802   }
1803 
1804   packcol16** SrcImage = Image;
1805   packalpha** SrcAlphaMap = AlphaMap;
1806   packcol16** DestImage = Bitmap->Image;
1807   packalpha** DestAlphaMap = Bitmap->AlphaMap;
1808 
1809   switch(Flags & 7)
1810   {
1811    case NONE:
1812     {
1813       memcpy(DestImage[0], SrcImage[0], XSizeTimesYSize * sizeof(packcol16));
1814       memcpy(DestAlphaMap[0], SrcAlphaMap[0], XSizeTimesYSize * sizeof(packalpha));
1815       break;
1816     }
1817 
1818    case MIRROR:
1819     {
1820       int Width = Size.X;
1821       int Height = Size.Y;
1822       int DestX = Width - 1;
1823       cpackcol16* SrcPtr = SrcImage[0];
1824       cpackalpha* SrcAlphaPtr = SrcAlphaMap[0];
1825 
1826       for(int y = 0; y < Height; ++y)
1827       {
1828         cpackcol16* EndPtr = SrcPtr + Width;
1829         packcol16* DestPtr = &DestImage[y][DestX];
1830         packalpha* DestAlphaPtr = &DestAlphaMap[y][DestX];
1831 
1832         for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr, ++SrcAlphaPtr, --DestAlphaPtr)
1833         {
1834           *DestPtr = *SrcPtr;
1835           *DestAlphaPtr = *SrcAlphaPtr;
1836         }
1837       }
1838 
1839       break;
1840     }
1841 
1842    case FLIP:
1843     {
1844       int Height = Size.Y;
1845       int Width = Size.X;
1846       int DestY = Height - 1;
1847 
1848       for(int y = 0; y < Height; ++y)
1849       {
1850         memcpy(DestImage[DestY - y], SrcImage[y], Width * sizeof(packcol16));
1851         memcpy(DestAlphaMap[DestY - y], SrcAlphaMap[y], Width * sizeof(packalpha));
1852       }
1853 
1854       break;
1855     }
1856 
1857    case (MIRROR | FLIP):
1858     {
1859       cpackcol16* SrcPtr = SrcImage[0];
1860       cpackcol16* EndPtr = SrcPtr + XSizeTimesYSize;
1861       cpackalpha* SrcAlphaPtr = SrcAlphaMap[0];
1862       packcol16* DestPtr = &DestImage[Size.Y - 1][Size.X - 1];
1863       packalpha* DestAlphaPtr = &DestAlphaMap[Size.Y - 1][Size.X - 1];
1864 
1865       for(; SrcPtr != EndPtr; ++SrcPtr, --DestPtr, ++SrcAlphaPtr, --DestAlphaPtr)
1866       {
1867         *DestPtr = *SrcPtr;
1868         *DestAlphaPtr = *SrcAlphaPtr;
1869       }
1870 
1871       break;
1872     }
1873 
1874    case ROTATE:
1875     {
1876       cint Width = Size.X;
1877       cpackcol16* SrcPtr = SrcImage[0];
1878       cpackalpha* SrcAlphaPtr = SrcAlphaMap[0];
1879       packcol16* DestBase = &DestImage[0][Width - 1];
1880       packalpha* DestAlphaBase = &DestAlphaMap[0][Width - 1];
1881 
1882       for(int y = 0; y < Width; ++y)
1883       {
1884         cpackcol16* EndPtr = SrcPtr + Width;
1885         packcol16* DestPtr = DestBase - y;
1886         packalpha* DestAlphaPtr = DestAlphaBase - y;
1887 
1888         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += Width, ++SrcAlphaPtr, DestAlphaPtr += Width)
1889         {
1890           *DestPtr = *SrcPtr;
1891           *DestAlphaPtr = *SrcAlphaPtr;
1892         }
1893       }
1894 
1895       break;
1896     }
1897 
1898    case (MIRROR | ROTATE):
1899     {
1900       cint Width = Size.X;
1901       cpackcol16* SrcPtr = SrcImage[0];
1902       cpackalpha* SrcAlphaPtr = SrcAlphaMap[0];
1903       packcol16* DestBase = DestImage[0];
1904       packalpha* DestAlphaBase = DestAlphaMap[0];
1905 
1906       for(int y = 0; y < Width; ++y)
1907       {
1908         cpackcol16* EndPtr = SrcPtr + Width;
1909         packcol16* DestPtr = DestBase + y;
1910         packalpha* DestAlphaPtr = DestAlphaBase + y;
1911 
1912         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr += Width, ++SrcAlphaPtr, DestAlphaPtr += Width)
1913         {
1914           *DestPtr = *SrcPtr;
1915           *DestAlphaPtr = *SrcAlphaPtr;
1916         }
1917       }
1918 
1919       break;
1920     }
1921 
1922    case (FLIP | ROTATE):
1923     {
1924       cint Width = Size.X;
1925       cpackcol16* SrcPtr = SrcImage[0];
1926       cpackalpha* SrcAlphaPtr = SrcAlphaMap[0];
1927       packcol16* DestBase = &DestImage[Width - 1][Width - 1];
1928       packalpha* DestAlphaBase = &DestAlphaMap[Width - 1][Width - 1];
1929 
1930       for(int y = 0; y < Width; ++y)
1931       {
1932         cpackcol16* EndPtr = SrcPtr + Width;
1933         packcol16* DestPtr = DestBase - y;
1934         packalpha* DestAlphaPtr = DestAlphaBase - y;
1935 
1936         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= Width, ++SrcAlphaPtr, DestAlphaPtr -= Width)
1937         {
1938           *DestPtr = *SrcPtr;
1939           *DestAlphaPtr = *SrcAlphaPtr;
1940         }
1941       }
1942 
1943       break;
1944     }
1945 
1946    case (MIRROR | FLIP | ROTATE):
1947     {
1948       cint Width = Size.X;
1949       cpackcol16* SrcPtr = SrcImage[0];
1950       cpackalpha* SrcAlphaPtr = SrcAlphaMap[0];
1951       packcol16* DestBase = DestImage[Width - 1];
1952       packalpha* DestAlphaBase = DestAlphaMap[Width - 1];
1953 
1954       for(int y = 0; y < Width; ++y)
1955       {
1956         cpackcol16* EndPtr = SrcPtr + Width;
1957         packcol16* DestPtr = DestBase + y;
1958         packalpha* DestAlphaPtr = DestAlphaBase + y;
1959 
1960         for(; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= Width, ++SrcAlphaPtr, DestAlphaPtr -= Width)
1961         {
1962           *DestPtr = *SrcPtr;
1963           *DestAlphaPtr = *SrcAlphaPtr;
1964         }
1965       }
1966 
1967       break;
1968     }
1969   }
1970 }
1971 
FillAlpha(alpha Alpha)1972 void bitmap::FillAlpha(alpha Alpha)
1973 {
1974   memset(AlphaMap[0], Alpha, XSizeTimesYSize);
1975 }
1976 
PowerPutPixel(int X,int Y,col16 Color,alpha Alpha,priority Priority)1977 void bitmap::PowerPutPixel(int X, int Y, col16 Color, alpha Alpha, priority Priority)
1978 {
1979   if(X >= 0 && Y >= 0 && X < Size.X && Y < Size.Y)
1980   {
1981     Image[Y][X] = Color;
1982 
1983     if(AlphaMap)
1984       AlphaMap[Y][X] = Alpha;
1985     else if(Alpha != 255)
1986     {
1987       CreateAlphaMap(255);
1988       AlphaMap[Y][X] = Alpha;
1989     }
1990 
1991     if(PriorityMap)
1992       PriorityMap[Y][X] = Priority;
1993   }
1994 }
1995 
MaskedPriorityBlit(cblitdata & BlitData) const1996 void bitmap::MaskedPriorityBlit(cblitdata& BlitData) const
1997 {
1998   if(!PriorityMap || !BlitData.Bitmap->PriorityMap)
1999   {
2000     LuminanceMaskedBlit(BlitData);
2001     return;
2002   }
2003 
2004   blitdata B = BlitData;
2005 
2006   if(!FastFlag)
2007   {
2008     if(!B.Border.X || !B.Border.Y)
2009       ABORT("Zero-sized bitmap masked priority blit attempt detected!");
2010 
2011     if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y))
2012       return;
2013   }
2014 
2015   packcol16** SrcImage = Image;
2016   packpriority** SrcPriorityMap = PriorityMap;
2017   packcol16** DestImage = B.Bitmap->Image;
2018   packpriority** DestPriorityMap = B.Bitmap->PriorityMap;
2019   int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000;
2020   int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800;
2021   int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20;
2022 
2023   for(int y = 0; y < B.Border.Y; ++y)
2024   {
2025     cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
2026     cpackpriority* SrcPriorityPtr = &SrcPriorityMap[B.Src.Y + y][B.Src.X];
2027     cpackcol16* EndPtr = SrcPtr + B.Border.X;
2028     packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
2029     packpriority* DestPriorityPtr = &DestPriorityMap[B.Dest.Y + y][B.Dest.X];
2030 
2031     for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++SrcPriorityPtr, ++DestPriorityPtr)
2032     {
2033       LOAD_SRC();
2034 
2035       if(SrcCol != B.MaskColor)
2036       {
2037         priority SrcPriority = *SrcPriorityPtr;
2038         priority DestPriority = *DestPriorityPtr;
2039 
2040         if((SrcPriority & 0xF) >= (DestPriority & 0xF) || (SrcPriority & 0xF0) >= (DestPriority & 0xF0))
2041         {
2042           NEW_LUMINATE_RED();
2043           NEW_LUMINATE_GREEN();
2044           NEW_LUMINATE_BLUE();
2045           STORE_COLOR();
2046           *DestPriorityPtr = SrcPriority;
2047         }
2048       }
2049     }
2050   }
2051 }
2052 
AlphaPriorityBlit(cblitdata & BlitData) const2053 void bitmap::AlphaPriorityBlit(cblitdata& BlitData) const
2054 {
2055   if(!AlphaMap)
2056   {
2057     MaskedPriorityBlit(BlitData);
2058     return;
2059   }
2060 
2061   if(!PriorityMap || !BlitData.Bitmap->PriorityMap)
2062   {
2063     AlphaLuminanceBlit(BlitData);
2064     return;
2065   }
2066 
2067   blitdata B = BlitData;
2068 
2069   if(!FastFlag)
2070   {
2071     if(!B.Border.X || !B.Border.Y)
2072       ABORT("Zero-sized bitmap alpha priority blit attempt detected!");
2073 
2074     if(!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y,
2075                      Size.X, Size.Y, B.Bitmap->Size.X, B.Bitmap->Size.Y))
2076       return;
2077   }
2078 
2079   packcol16** SrcImage = Image;
2080   packalpha** SrcAlphaMap = AlphaMap;
2081   packpriority** SrcPriorityMap = PriorityMap;
2082   packcol16** DestImage = B.Bitmap->Image;
2083   packpriority** DestPriorityMap = B.Bitmap->PriorityMap;
2084   int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000;
2085   int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800;
2086   int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20;
2087 
2088   for(int y = 0; y < B.Border.Y; ++y)
2089   {
2090     cpackcol16* SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
2091     cpackalpha* AlphaPtr = &SrcAlphaMap[B.Src.Y + y][B.Src.X];
2092     cpackpriority* SrcPriorityPtr = &SrcPriorityMap[B.Src.Y + y][B.Src.X];
2093     cpackcol16* EndPtr = SrcPtr + B.Border.X;
2094     packcol16* DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
2095     packpriority* DestPriorityPtr = &DestPriorityMap[B.Dest.Y + y][B.Dest.X];
2096 
2097     for(; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++AlphaPtr, ++SrcPriorityPtr, ++DestPriorityPtr)
2098     {
2099       LOAD_SRC();
2100 
2101       if(SrcCol != B.MaskColor)
2102       {
2103         priority SrcPriority = *SrcPriorityPtr;
2104         priority DestPriority = *DestPriorityPtr;
2105 
2106         if((SrcPriority & 0xF) >= (DestPriority & 0xF)
2107            || (SrcPriority & 0xF0) >= (DestPriority & 0xF0))
2108         {
2109           LOAD_DEST();
2110           LOAD_ALPHA();
2111           NEW_LUMINATE_RED();
2112           NEW_APPLY_ALPHA_RED();
2113           NEW_LUMINATE_GREEN();
2114           NEW_APPLY_ALPHA_GREEN();
2115           NEW_LUMINATE_BLUE();
2116           NEW_APPLY_ALPHA_BLUE();
2117           STORE_COLOR();
2118           *DestPriorityPtr = SrcPriority;
2119         }
2120       }
2121     }
2122   }
2123 }
2124 
InitPriorityMap(priority InitialValue)2125 void bitmap::InitPriorityMap(priority InitialValue)
2126 {
2127   if(!PriorityMap)
2128     Alloc2D(PriorityMap, Size.Y, Size.X);
2129 
2130   memset(PriorityMap[0], InitialValue, XSizeTimesYSize);
2131 }
2132 
FillPriority(priority Priority)2133 void bitmap::FillPriority(priority Priority)
2134 {
2135   memset(PriorityMap[0], Priority, XSizeTimesYSize);
2136 }
2137 
FastBlitAndCopyAlpha(bitmap * Bitmap) const2138 void bitmap::FastBlitAndCopyAlpha(bitmap* Bitmap) const
2139 {
2140   if(!FastFlag)
2141   {
2142     if(!AlphaMap || !Bitmap->AlphaMap)
2143       ABORT("Attempt to fast blit and copy alpha without an alpha map detected!");
2144 
2145     if(Size.X != Bitmap->Size.X || Size.Y != Bitmap->Size.Y)
2146       ABORT("Fast blit and copy alpha attempt of noncongruent bitmaps detected!");
2147   }
2148 
2149   memcpy(Bitmap->Image[0], Image[0], XSizeTimesYSize * sizeof(packcol16));
2150   memcpy(Bitmap->AlphaMap[0], AlphaMap[0], XSizeTimesYSize * sizeof(packalpha));
2151 }
2152 
UpdateRandMap(long Index,truth Value)2153 void bitmap::UpdateRandMap(long Index, truth Value)
2154 {
2155   long c1 = XSizeTimesYSize + Index;
2156   RandMap[c1] = Value;
2157 
2158   for(long c2 = c1 >> 1; c2; c1 = c2, c2 >>= 1)
2159   {
2160     Value |= RandMap[c1 ^ 1];
2161 
2162     if(!RandMap[c2] != !Value)
2163       RandMap[c2] = Value;
2164     else
2165       return;
2166   }
2167 }
2168 
InitRandMap()2169 void bitmap::InitRandMap()
2170 {
2171   if(!RandMap)
2172     RandMap = new truth[XSizeTimesYSize << 1];
2173 
2174   memset(RandMap, 0, (XSizeTimesYSize << 1) * sizeof(truth));
2175 }
2176 
RandomizePixel() const2177 v2 bitmap::RandomizePixel() const
2178 {
2179   if(!RandMap[1])
2180     return ERROR_V2;
2181 
2182   long Rand = RAND();
2183   ulong c, RandMask = 1;
2184   ulong MapSize = XSizeTimesYSize << 1;
2185 
2186   for(c = 2; c < MapSize; c <<= 1)
2187     if(RandMap[c + 1] && (!RandMap[c] || Rand & (RandMask <<= 1)))
2188       ++c;
2189 
2190   c = (c - MapSize) >> 1;
2191   return v2(c % Size.X, c / Size.X);
2192 }
2193 
CalculateRandMap()2194 void bitmap::CalculateRandMap()
2195 {
2196   if(!AlphaMap)
2197     ABORT("Alpha map needed to calculate random map.");
2198 
2199   ulong Size = XSizeTimesYSize;
2200 
2201   for(ulong c = 0; c < Size; ++c)
2202     UpdateRandMap(c, AlphaMap[0][c]);
2203 }
2204 
AlphaPutPixel(int x,int y,col16 SrcCol,col24 Luminance,alpha Alpha)2205 void bitmap::AlphaPutPixel(int x, int y, col16 SrcCol, col24 Luminance, alpha Alpha)
2206 {
2207   int DestCol = Image[y][x];
2208   int NewRedLuminance = (Luminance >> 7 & 0x1F800) - 0x10000;
2209   int NewGreenLuminance = (Luminance >> 4 & 0xFE0) - 0x800;
2210   int NewBlueLuminance = (Luminance >> 2 & 0x3F) - 0x20;
2211   NEW_LUMINATE_RED();
2212   NEW_APPLY_ALPHA_RED();
2213   NEW_LUMINATE_GREEN();
2214   NEW_APPLY_ALPHA_GREEN();
2215   NEW_LUMINATE_BLUE();
2216   NEW_APPLY_ALPHA_BLUE();
2217   Image[y][x] = Red|Green|Blue;
2218 
2219 }
2220 
CalculateAlphaAverage() const2221 alpha bitmap::CalculateAlphaAverage() const
2222 {
2223   if(!AlphaMap)
2224     ABORT("Alpha map needed to calculate alpha average!");
2225 
2226   long Alphas = 0;
2227   long AlphaSum = 0;
2228   ulong Size = XSizeTimesYSize;
2229 
2230   for(ulong c = 0; c < Size; ++c)
2231   {
2232     packalpha* AlphaPtr = &AlphaMap[0][c];
2233 
2234     if(*AlphaPtr)
2235     {
2236       AlphaSum += *AlphaPtr;
2237       ++Alphas;
2238     }
2239   }
2240 
2241   return Alphas ? AlphaSum / Alphas : 0;
2242 }
2243 
cachedfont(v2 Size)2244 cachedfont::cachedfont(v2 Size) : bitmap(Size)
2245 {
2246   Alloc2D(MaskMap, Size.Y, Size.X);
2247 }
2248 
cachedfont(v2 Size,col16 Color)2249 cachedfont::cachedfont(v2 Size, col16 Color) : bitmap(Size, Color)
2250 {
2251   Alloc2D(MaskMap, Size.Y, Size.X);
2252 }
2253 
PrintCharacter(cblitdata B) const2254 void cachedfont::PrintCharacter(cblitdata B) const
2255 {
2256   if(B.Dest.X < 0 || B.Dest.Y < 0 || B.Dest.X + 10 >= B.Bitmap->Size.X || B.Dest.Y + 9 >= B.Bitmap->Size.Y)
2257   {
2258     NormalMaskedBlit(B);
2259     return;
2260   }
2261 
2262   packcol16** SrcLine = &Image[B.Src.Y];
2263   packcol16** EndLine = SrcLine + 9;
2264   packcol16** SrcMaskLine = &MaskMap[B.Src.Y];
2265   packcol16** DestLine = &B.Bitmap->Image[B.Dest.Y];
2266 
2267   for(; SrcLine != EndLine; ++SrcLine, ++SrcMaskLine, ++DestLine)
2268   {
2269     culong* FontPtr = reinterpret_cast<culong*>(*SrcLine + B.Src.X);
2270         // I don't know how correct this is, but longs are 64 bit on 64 bit.
2271     culong* EndPtr = FontPtr + (20 / sizeof(ulong));
2272     culong* MaskPtr = reinterpret_cast<culong*>(*SrcMaskLine + B.Src.X);
2273     ulong* DestPtr = reinterpret_cast<ulong*>(*DestLine + B.Dest.X);
2274 
2275     for(; FontPtr != EndPtr; ++DestPtr, ++MaskPtr, ++FontPtr)
2276       *DestPtr = (*DestPtr & *MaskPtr) | *FontPtr;
2277   }
2278 }
2279 
CreateMaskMap()2280 void cachedfont::CreateMaskMap()
2281 {
2282   packcol16* SrcPtr = Image[0];
2283   packcol16* EndPtr = SrcPtr + XSizeTimesYSize;
2284   packcol16* MaskPtr = MaskMap[0];
2285 
2286   for(; SrcPtr != EndPtr; ++SrcPtr, ++MaskPtr)
2287     if(*SrcPtr == TRANSPARENT_COLOR)
2288     {
2289       *SrcPtr = 0;
2290       *MaskPtr = 0xFFFF;
2291     }
2292     else
2293       *MaskPtr = 0;
2294 }
2295 
2296 cint WaveDelta[] = { 1, 2, 2, 2, 1, 0, -1, -2, -2, -2, -1 };
2297 
Wobble(int Frame,int SpeedShift,truth Horizontally)2298 void bitmap::Wobble(int Frame, int SpeedShift, truth Horizontally)
2299 {
2300   int WavePos = (Frame << SpeedShift >> 1) - 14;
2301 
2302   if(Horizontally)
2303   {
2304     for(int c = 0; c < 11; ++c)
2305       if(WavePos + c >= 0 && WavePos + c < Size.Y)
2306         MoveLineHorizontally(WavePos + c, WaveDelta[c]);
2307   }
2308   else
2309   {
2310     for(int c = 0; c < 11; ++c)
2311       if(WavePos + c >= 0 && WavePos + c < Size.X)
2312         MoveLineVertically(WavePos + c, WaveDelta[c]);
2313   }
2314 }
2315 
MoveLineVertically(int X,int Delta)2316 void bitmap::MoveLineVertically(int X, int Delta)
2317 {
2318   int y;
2319 
2320   if(Delta < 0)
2321   {
2322     for(y = 0; y < Size.Y + Delta; ++y)
2323       PowerPutPixel(X, y, GetPixel(X, y - Delta), AlphaMap ? GetAlpha(X, y - Delta) : 255, AVERAGE_PRIORITY);
2324 
2325     for(int y = -1; y >= Delta; --y)
2326       PowerPutPixel(X, Size.Y + y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY);
2327   }
2328   else if(Delta > 0)
2329   {
2330     for(y = Size.Y - 1; y >= Delta; --y)
2331       PowerPutPixel(X, y, GetPixel(X, y - Delta), AlphaMap ? GetAlpha(X, y - Delta) : 255, AVERAGE_PRIORITY);
2332 
2333     for(y = 0; y < Delta; ++y)
2334       PowerPutPixel(X, y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY);
2335   }
2336 }
2337 
MoveLineHorizontally(int Y,int Delta)2338 void bitmap::MoveLineHorizontally(int Y, int Delta)
2339 {
2340   int x;
2341 
2342   if(Delta < 0)
2343   {
2344     for(x = 0; x < Size.X + Delta; ++x)
2345       PowerPutPixel(x, Y, GetPixel(x - Delta, Y), AlphaMap ? GetAlpha(x - Delta, Y) : 255, AVERAGE_PRIORITY);
2346 
2347     for(x = -1; x >= Delta; --x)
2348       PowerPutPixel(Size.X + x, Y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY);
2349   }
2350   else if(Delta > 0)
2351   {
2352     for(x = Size.X - 1; x >= Delta; --x)
2353       PowerPutPixel(x, Y, GetPixel(x - Delta, Y), AlphaMap ? GetAlpha(x - Delta, Y) : 255, AVERAGE_PRIORITY);
2354 
2355     for(x = 0; x < Delta; ++x)
2356       PowerPutPixel(x, Y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY);
2357   }
2358 }
2359 
InterLace()2360 void bitmap::InterLace()
2361 {
2362   for(int y = 0; y < Size.Y; ++y)
2363     if(!(y % 3))
2364       for(int x = 0; x < Size.X; ++x)
2365         if(Image[y][x] != 0)
2366           Image[y][x] = 1;
2367 }
2368