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