slightly better path and light tracing (try to trace line from both points before...
[k8-i-v-a-n.git] / src / felib / bitmap.cpp
blob856998d3ae2298e4e1a0786c9d91caa6cc97dc7d
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
12 #include <stdint.h>
13 #include <cmath>
14 #include <ctime>
16 #if defined(HAVE_IMLIB2)
17 # include <Imlib2.h>
18 #elif defined(HAVE_LIBPNG)
19 # include <png.h>
20 #endif
22 #ifdef USE_ZLIB
23 # include <zlib.h>
24 #endif
26 #include "bitmap.h"
27 #include "graphics.h"
28 #include "fesave.h"
29 #include "allocate.h"
30 #include "femath.h"
31 #include "rawbit.h"
34 #ifdef USE_ZLIB
35 static gzFile xcreate (const char *fname) {
36 return gzopen(fname, "wb1");
39 static gzFile xopenro (const char *fname) {
40 return gzopen(fname, "rb");
43 static void xclose (gzFile fl) {
44 if (fl) gzclose(fl);
47 static bool xread (gzFile fi, void *buf, size_t bytes) {
48 if (!fi) return false;
49 if (bytes == 0) return true;
50 return (gzread(fi, buf, (int)bytes) == (int)bytes);
53 static bool xwrite (gzFile fo, const void *buf, size_t bytes) {
54 if (!fo || bytes == 0) return false;
55 return (gzwrite(fo, buf, (int)bytes) == (int)bytes);
58 #else
60 static FILE *xcreate (const char *fname) {
61 return fopen(fname, "wb");
64 static FILE *xopenro (const char *fname) {
65 return fopen(fname, "rb");
68 static void xclose (FILE *fl) {
69 if (fl) fclose(fl);
72 static bool inline xread (FILE *fi, void *buf, size_t bytes) {
73 if (!fi) return false;
74 if (bytes == 0) return true;
75 return (fread(buf, bytes, 1, fi) == (size_t)bytes);
78 static bool xwrite (FILE *fo, const void *buf, size_t bytes) {
79 if (!fo || bytes == 0) return false;
80 return (fwrite(fo, buf, 1, bytes) == bytes);
82 #endif
86 * Blitting must be as fast as possible, even if no optimizations are used;
87 * therefore we can't use inline functions inside loops, since they may be
88 * left unexpanded. These macros will do the job efficiently, even if they
89 * are rather ugly
91 #define LOAD_SRC() int SrcCol = *SrcPtr;
92 #define LOAD_DEST() int DestCol = *DestPtr;
93 #define LOAD_ALPHA() int Alpha = *AlphaPtr;
94 #define STORE_COLOR() *DestPtr = Red | Green | Blue;
96 #define NEW_LUMINATE_RED() \
97 int Red = (SrcCol & 0xF800)+NewRedLuminance;\
98 if (Red >= 0) { if(Red > 0xF800) Red = 0xF800; } else Red = 0;
100 #define NEW_LUMINATE_GREEN() \
101 int Green = (SrcCol & 0x7E0)+NewGreenLuminance;\
102 if (Green >= 0) { if(Green > 0x7E0) Green = 0x7E0; } else Green = 0;
104 #define NEW_LUMINATE_BLUE() \
105 int Blue = (SrcCol & 0x1F)+NewBlueLuminance;\
106 if (Blue >= 0) { if(Blue > 0x1F) Blue = 0x1F; } else Blue = 0;
108 #define NEW_APPLY_ALPHA_RED() {\
109 int DestRed = (DestCol & 0xF800);\
110 Red = (((Red - DestRed) * Alpha >> 8) + DestRed) & 0xF800;\
113 #define NEW_APPLY_ALPHA_GREEN() {\
114 int DestGreen = (DestCol & 0x7E0);\
115 Green = (((Green - DestGreen) * Alpha >> 8) + DestGreen) & 0x7E0;\
118 #define NEW_APPLY_ALPHA_BLUE() {\
119 int DestBlue = (DestCol & 0x1F);\
120 Blue = ((Blue - DestBlue) * Alpha >> 8) + DestBlue;\
123 #define NEW_LOAD_AND_APPLY_ALPHA_RED()\
124 int Red;\
126 int DestRed = DestCol & 0xF800;\
127 Red = ((((SrcCol & 0xF800) - DestRed) * Alpha >> 8) + DestRed) & 0xF800;\
130 #define NEW_LOAD_AND_APPLY_ALPHA_GREEN()\
131 int Green;\
133 int DestGreen = DestCol & 0x7E0;\
134 Green = ((((SrcCol & 0x7E0) - DestGreen) * Alpha >> 8) + DestGreen) & 0x7E0;\
137 #define NEW_LOAD_AND_APPLY_ALPHA_BLUE()\
138 int Blue;\
140 int DestBlue = DestCol & 0x1F;\
141 Blue = (((SrcCol & 0x1F) - DestBlue) * Alpha >> 8) + DestBlue;\
145 bitmap::bitmap (cfestring &FileName) : FastFlag(0), AlphaMap(0), PriorityMap(0), RandMap(0) {
146 auto rawpic = rawbitmap(FileName);
147 mSize = rawpic.GetSize();
148 const uChar *Palette = rawpic.getPalette();
149 XSizeTimesYSize = mSize.X*mSize.Y;
150 Alloc2D(Image, mSize.Y, mSize.X);
151 packcol16 *Buffer = Image[0];
152 const uChar *buf = rawpic.getBuffer();
153 for (int y = 0; y < mSize.Y; ++y) {
154 for (int x = 0; x < mSize.X; ++x) {
155 int Char1 = *buf++;
156 int Char3 = Char1 + (Char1 << 1);
157 *Buffer++ = int(Palette[Char3] >> 3) << 11 | int(Palette[Char3 + 1] >> 2) << 5 | int(Palette[Char3 + 2] >> 3);
163 bitmap::bitmap (cbitmap *Bitmap, int Flags, truth CopyAlpha) :
164 mSize(Bitmap->mSize),
165 XSizeTimesYSize(Bitmap->XSizeTimesYSize),
166 FastFlag(0),
167 PriorityMap(0),
168 RandMap(0)
170 Alloc2D(Image, mSize.Y, mSize.X);
171 if (CopyAlpha && Bitmap->AlphaMap) {
172 Alloc2D(AlphaMap, mSize.Y, mSize.X);
173 Bitmap->BlitAndCopyAlpha(this, Flags);
174 } else {
175 AlphaMap = 0;
176 if (!Flags) Bitmap->FastBlit(this); else Bitmap->NormalBlit(this, Flags);
181 bitmap::bitmap (v2 aSize) :
182 mSize(aSize),
183 XSizeTimesYSize(mSize.X * mSize.Y),
184 FastFlag(0),
185 AlphaMap(0),
186 PriorityMap(0),
187 RandMap(0)
189 Alloc2D(Image, mSize.Y, mSize.X);
193 bitmap::bitmap (v2 aSize, col16 Color) :
194 mSize(aSize),
195 XSizeTimesYSize(mSize.X*mSize.Y),
196 FastFlag(0),
197 AlphaMap(0),
198 PriorityMap(0),
199 RandMap(0)
201 Alloc2D(Image, mSize.Y, mSize.X);
202 ClearToColor(Color);
206 bitmap::~bitmap () {
207 delete [] Image;
208 delete [] AlphaMap;
209 delete [] PriorityMap;
210 delete [] RandMap;
214 void bitmap::Save (outputfile &SaveFile) const {
215 SaveFile.Write(reinterpret_cast<char *>(Image[0]), XSizeTimesYSize*sizeof(packcol16));
216 if (AlphaMap) {
217 SaveFile.Put(true);
218 SaveFile.Write(reinterpret_cast<char *>(AlphaMap[0]), XSizeTimesYSize*sizeof(packalpha));
219 } else {
220 SaveFile.Put(false);
222 if (PriorityMap) {
223 SaveFile.Put(true);
224 SaveFile.Write(reinterpret_cast<char *>(PriorityMap[0]), XSizeTimesYSize*sizeof(packpriority));
225 } else {
226 SaveFile.Put(false);
228 SaveFile << uChar(FastFlag);
232 void bitmap::Load (inputfile &SaveFile) {
233 SaveFile.Read(reinterpret_cast<char *>(Image[0]), XSizeTimesYSize*sizeof(packcol16));
234 if (SaveFile.Get()) {
235 Alloc2D(AlphaMap, mSize.Y, mSize.X);
236 SaveFile.Read(reinterpret_cast<char *>(AlphaMap[0]), XSizeTimesYSize*sizeof(packalpha));
238 if (SaveFile.Get()) {
239 Alloc2D(PriorityMap, mSize.Y, mSize.X);
240 SaveFile.Read(reinterpret_cast<char *>(PriorityMap[0]), XSizeTimesYSize*sizeof(packpriority));
242 FastFlag = ReadType(uChar, SaveFile);
246 #if defined(HAVE_LIBPNG) && !defined(HAVE_IMLIB2)
247 static void pngWrite (png_structp png_ptr, png_bytep data, png_size_t length) {
248 FILE *fp = (FILE *)png_get_io_ptr(png_ptr);
249 fwrite(data, length, 1, fp);
251 #endif
254 #if defined(HAVE_IMLIB2) || defined(HAVE_LIBPNG)
255 void bitmap::SavePNG (cfestring &FileName) const {
256 #if defined(HAVE_IMLIB2)
257 if (mSize.X < 1 || mSize.Y < 1) return;
258 Imlib_Image img = imlib_create_image(mSize.X, mSize.Y);
259 imlib_context_set_image(img);
260 DATA32 *raw = imlib_image_get_data(); //brga
261 uChar *pp = (uChar *)raw;
262 for (int y = 0; y < mSize.Y; y++) {
263 for (int x = 0; x < mSize.X; x++) {
264 col16 Pixel = GetPixel(x, y);
265 uChar b = (Pixel << 3)&0xff;
266 uChar g = ((Pixel >> 5) << 2)&0xff;
267 uChar r = ((Pixel >> 11) << 3)&0xff;
268 *pp++ = b;
269 *pp++ = g;
270 *pp++ = r;
271 *pp++ = 255; //0?
274 imlib_image_set_format("png");
275 imlib_save_image(FileName.CStr());
276 imlib_free_image();
277 #elif defined(HAVE_LIBPNG)
278 png_structp png_ptr;
279 png_infop info_ptr;
280 //int ret;
281 //png_colorp palette;
282 png_byte **row_pointers = NULL;
283 png_ptr = NULL;
284 info_ptr = NULL;
285 //palette = NULL;
286 //ret = -1;
287 FILE *fp = fopen(FileName.CStr(), "wb");
288 if (fp == 0) return;
289 row_pointers = (png_byte **)malloc(mSize.Y*sizeof(png_byte *));
290 //if (!row_pointers);
291 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
292 //if (!png_ptr);
293 info_ptr = png_create_info_struct(png_ptr);
294 //if (!info_ptr);
295 // setup custom writer function
296 png_set_write_fn(png_ptr, (voidp)fp, pngWrite, NULL);
297 if (setjmp(png_jmpbuf(png_ptr))) {
298 ABORT("err");
301 if (compression > Z_BEST_COMPRESSION) compression = Z_BEST_COMPRESSION;
302 if (compression == Z_NO_COMPRESSION) {
303 // No compression
304 png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
305 png_set_compression_level(png_ptr, Z_NO_COMPRESSION);
306 } else if (compression < 0) {
307 png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
308 } else {
309 png_set_compression_level(png_ptr, compression);
312 png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
313 png_set_compression_level(png_ptr, /*Z_BEST_COMPRESSION*/Z_DEFAULT_COMPRESSION);
314 //png_set_compression_level(png_ptr, Z_NO_COMPRESSION);
315 png_set_IHDR(png_ptr, info_ptr, mSize.X, mSize.Y, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
316 png_write_info(png_ptr, info_ptr);
317 uChar *tcImg = (uChar *)malloc((mSize.X*3)*mSize.Y), *tc = tcImg;
318 for (int y = 0; y < mSize.Y; y++) {
319 row_pointers[y] = (png_bytep)tc;
320 for (int x = 0; x < mSize.X; x++) {
321 col16 Pixel = GetPixel(x, y);
322 uChar b = (Pixel << 3)&0xff;
323 uChar g = ((Pixel >> 5) << 2)&0xff;
324 uChar r = ((Pixel >> 11) << 3)&0xff;
325 *tc++ = r;
326 *tc++ = g;
327 *tc++ = b;
328 //*tc++ = 255; //0?
331 png_write_image(png_ptr, row_pointers);
332 png_write_end(png_ptr, NULL);
333 fclose(fp);
334 free(tcImg);
335 //savedone: /* clean up and return */
336 png_destroy_write_struct(&png_ptr, &info_ptr);
337 if (row_pointers) free(row_pointers);
338 #else
339 BUG!
340 #endif
342 #endif
345 void bitmap::SaveBMP (cfestring &FileName) const {
346 static char BMPHeader[] = {
347 char(0x42), char(0x4D), char(0xB6), char(0x4F), char(0x12), char(0x00),
348 char(0x00), char(0x00), char(0x00), char(0x00), char(0x36), char(0x00),
349 char(0x00), char(0x00), char(0x28), char(0x00), char(0x00), char(0x00),
350 char(0x20), char(0x03), char(0x00), char(0x00), char(0xF4), char(0x01),
351 char(0x00), char(0x00), char(0x01), char(0x00), char(0x18), char(0x00),
352 char(0x00), char(0x00), char(0x00), char(0x00), char(0x80), char(0x4F),
353 char(0x12), char(0x00), char(0x33), char(0x0B), char(0x00), char(0x00),
354 char(0x33), char(0x0B), char(0x00), char(0x00), char(0x00), char(0x00),
355 char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00)
357 FILE *fo = fopen(FileName.CStr(), "wb");
358 BMPHeader[0x12] = mSize.X & 0xFF;
359 BMPHeader[0x13] = (mSize.X >> 8) & 0xFF;
360 BMPHeader[0x16] = mSize.Y & 0xFF;
361 BMPHeader[0x17] = (mSize.Y >> 8) & 0xFF;
362 fwrite(BMPHeader, 0x36, 1, fo);
363 for (int y = mSize.Y - 1; y >= 0; --y) {
364 for (int x = 0; x < mSize.X; ++x) {
365 col16 Pixel = GetPixel(x, y);
366 char b0 = Pixel << 3, b1 = (Pixel >> 5) << 2, b2 = (Pixel >> 11) << 3;
367 //SaveFile << char(Pixel << 3) << char((Pixel >> 5) << 2) << char((Pixel >> 11) << 3);
368 fwrite(&b0, 1, 1, fo);
369 fwrite(&b1, 1, 1, fo);
370 fwrite(&b2, 1, 1, fo);
373 fclose(fo);
377 uChar *bitmap::createScaled (double scale, v2* size) const {
378 int newX = (int)((double)mSize.X*scale);
379 int newY = (int)((double)mSize.Y*scale);
380 if (newX < 1 || newY < 1) return 0;
381 uChar *unp = new uChar[mSize.X*mSize.Y*3];
382 // unpack image
383 uChar *pp = unp;
384 for (int y = 0; y < mSize.Y; ++y) {
385 for (int x = 0; x < mSize.X; ++x) {
386 col16 Pixel = GetPixel(x, y);
387 uChar b = (Pixel<<3)&0xff;
388 uChar g = ((Pixel>>5)<<2)&0xff;
389 uChar r = ((Pixel>>11)<<3)&0xff;
390 *pp++ = r;
391 *pp++ = g;
392 *pp++ = b;
395 // now scale
396 uChar *nbx = new uChar[newX*mSize.Y*3];
397 uChar *nb = new uChar[newX*newY*3];
398 double sx = (double)mSize.X/(double)newX;
399 double sy = (double)mSize.Y/(double)newY;
400 #define GETRGB(x, y, r, g, b) \
401 r = unp[((y)*mSize.X+(x))*3+0]; \
402 g = unp[((y)*mSize.X+(x))*3+1]; \
403 b = unp[((y)*mSize.X+(x))*3+2];
404 // first X
406 double cx;
407 uChar *dst = nbx;
408 for (int y = 0; y < mSize.Y; y++) {
409 cx = 0;
410 for (int x = 0; x < newX; x++, cx += sx) {
411 double ix, rx;
412 int px, r0 = 0, g0 = 0, b0 = 0, r1, g1, b1;
413 rx = modf(cx, &ix);
414 px = (int)ix;
415 if (px >= 0 && px < mSize.X) { GETRGB(px, y, r0, g0, b0) }
416 if (px >= 0 && px < mSize.X-1) {
417 GETRGB(px+1, y, r1, g1, b1)
418 r0 = (int)((double)r0*(1.0-rx)+(double)r1*rx);
419 g0 = (int)((double)g0*(1.0-rx)+(double)g1*rx);
420 b0 = (int)((double)b0*(1.0-rx)+(double)b1*rx);
422 if (r0 < 0) r0 = 0; else if (r0 > 255) r0 = 255;
423 if (g0 < 0) g0 = 0; else if (g0 > 255) g0 = 255;
424 if (b0 < 0) b0 = 0; else if (b0 > 255) b0 = 255;
425 *dst++ = r0&0xff;
426 *dst++ = g0&0xff;
427 *dst++ = b0&0xff;
431 #undef GETRGB
432 #define GETRGB1(x, y, r, g, b) \
433 r = nbx[((y)*newX+(x))*3+0]; \
434 g = nbx[((y)*newX+(x))*3+1]; \
435 b = nbx[((y)*newX+(x))*3+2];
436 // now Y
438 double cx, cy = 0.0;
439 uChar *dst = nb;
440 for (int y = 0; y < newY; y++, cy += sy) {
441 cx = 0;
442 for (int x = 0; x < newX; x++, cx += sx) {
443 double iy, ry;
444 int py, r0 = 0, g0 = 0, b0 = 0, r1, g1, b1;
445 ry = modf(cy, &iy);
446 py = (int)iy;
447 if (py >= 0 && py < mSize.Y) { GETRGB1(x, py, r0, g0, b0) }
448 if (py >= 0 && py < mSize.Y-1) {
449 GETRGB1(x, py+1, r1, g1, b1)
450 r0 = (int)((double)r0*(1.0-ry)+(double)r1*ry);
451 g0 = (int)((double)g0*(1.0-ry)+(double)g1*ry);
452 b0 = (int)((double)b0*(1.0-ry)+(double)b1*ry);
454 if (r0 < 0) r0 = 0; else if (r0 > 255) r0 = 255;
455 if (g0 < 0) g0 = 0; else if (g0 > 255) g0 = 255;
456 if (b0 < 0) b0 = 0; else if (b0 > 255) b0 = 255;
457 *dst++ = r0&0xff;
458 *dst++ = g0&0xff;
459 *dst++ = b0&0xff;
463 #undef GETRGB1
464 delete [] nbx;
465 delete [] unp;
466 // done
467 if (size) *size = v2(newX, newY);
468 return nb;
472 #if defined(HAVE_IMLIB2) || defined(HAVE_LIBPNG)
473 void bitmap::SaveScaledPNG (cfestring &fileName, double scale) const {
474 #if defined(HAVE_IMLIB2)
475 Imlib_Image img = imlib_create_image(mSize.X, mSize.Y);
476 imlib_context_set_image(img);
477 DATA32 *raw = imlib_image_get_data(); //bgra
478 uChar *pp = (uChar *)raw;
479 for (int y = 0; y < mSize.Y; y++) {
480 for (int x = 0; x < mSize.X; x++) {
481 col16 Pixel = GetPixel(x, y);
482 uChar b = (Pixel << 3)&0xff;
483 uChar g = ((Pixel >> 5) << 2)&0xff;
484 uChar r = ((Pixel >> 11) << 3)&0xff;
485 *pp++ = b;
486 *pp++ = g;
487 *pp++ = r;
488 *pp++ = 255;
491 int newW = (int)(scale*mSize.X);
492 int newH = (int)(scale*mSize.Y);
493 Imlib_Image i2 = imlib_create_cropped_scaled_image(0, 0, mSize.X, mSize.Y, newW, newH);
494 imlib_free_image();
495 imlib_context_set_image(i2);
496 imlib_image_set_format("png");
497 imlib_save_image(fileName.CStr());
498 imlib_free_image();
499 #elif defined(HAVE_LIBPNG)
500 v2 size;
501 auto rgb = createScaled(scale, &size);
502 png_structp png_ptr;
503 png_infop info_ptr;
504 png_byte **row_pointers = NULL;
505 png_ptr = NULL;
506 info_ptr = NULL;
507 FILE *fp = fopen(fileName.CStr(), "wb");
508 if (!fp) return;
509 row_pointers = (png_byte **)malloc(size.Y*sizeof(png_byte*));
510 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
511 info_ptr = png_create_info_struct(png_ptr);
512 // setup custom writer function
513 png_set_write_fn(png_ptr, (voidp)fp, pngWrite, NULL);
514 if (setjmp(png_jmpbuf(png_ptr))) {
515 fclose(fp);
516 return;
518 png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
519 png_set_compression_level(png_ptr, /*Z_BEST_COMPRESSION*/Z_DEFAULT_COMPRESSION);
520 //png_set_compression_level(png_ptr, Z_NO_COMPRESSION);
521 png_set_IHDR(png_ptr, info_ptr, size.X, size.Y, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
522 png_write_info(png_ptr, info_ptr);
523 for (int y = 0; y < size.Y; ++y) row_pointers[y] = (png_bytep)(rgb+y*(size.X*3));
524 png_write_image(png_ptr, row_pointers);
525 png_write_end(png_ptr, NULL);
526 fclose(fp);
527 // clean up and return
528 png_destroy_write_struct(&png_ptr, &info_ptr);
529 if (row_pointers) free(row_pointers);
530 #else
531 BUG!
532 #endif
536 truth bitmap::LoadPNG (cfestring &fileName) {
537 #if defined(HAVE_IMLIB2)
538 Imlib_Image img = imlib_load_image(fileName.CStr());
539 if (!img) return false;
540 delete [] Image;
541 delete [] AlphaMap;
542 delete [] PriorityMap;
543 delete [] RandMap;
544 PriorityMap = 0;
545 RandMap = 0;
546 AlphaMap = 0;
547 imlib_context_set_image(img);
548 mSize.X = imlib_image_get_width();
549 mSize.Y = imlib_image_get_height();
550 Alloc2D(Image, mSize.Y, mSize.X);
551 //Alloc2D(AlphaMap, mSize.Y, mSize.X);
552 const DATA32 *raw = imlib_image_get_data_for_reading_only(); //bgra
553 const uChar *pp = (const uChar *)raw;
554 for (int y = 0; y < mSize.Y; y++) {
555 for (int x = 0; x < mSize.X; x++) {
556 uChar b = *pp++;
557 uChar g = *pp++;
558 uChar r = *pp++;
559 //uChar a = *pp++;
560 pp++; // alpha
561 PutPixel(x, y, MakeRGB16(r, g, b));
564 imlib_free_image();
565 return true;
566 #elif defined(HAVE_LIBPNG)
567 FILE *fl = fopen(fileName.CStr(), "rb");
568 if (!fl) return false;
570 uChar *rgb = 0;
571 png_bytep *rowptrs = 0;
573 png_structp PNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
574 if (!PNGStruct) {
575 fclose(fl);
576 delete [] rowptrs;
577 delete [] rgb;
578 return false;
581 png_infop PNGInfo = png_create_info_struct(PNGStruct);
582 if (!PNGInfo) {
583 fclose(fl);
584 png_destroy_read_struct(&PNGStruct, (png_infopp)nullptr, (png_infopp)nullptr);
585 delete [] rowptrs;
586 delete [] rgb;
587 return false;
590 if (setjmp(png_jmpbuf(PNGStruct)) != 0) {
591 if (fl) fclose(fl);
592 png_destroy_read_struct(&PNGStruct, (png_infopp)nullptr, (png_infopp)nullptr);
593 delete [] rowptrs;
594 delete [] rgb;
595 return false;
598 png_init_io(PNGStruct, fl);
599 png_read_info(PNGStruct, PNGInfo);
601 int sizeX = png_get_image_width(PNGStruct, PNGInfo);
602 int sizeY = png_get_image_height(PNGStruct, PNGInfo);
604 if (sizeX < 1 || sizeX > 8192 || sizeY < 1 || sizeY > 8192) {
605 if (fl) fclose(fl);
606 png_destroy_read_struct(&PNGStruct, (png_infopp)nullptr, (png_infopp)nullptr);
607 delete [] rowptrs;
608 delete [] rgb;
609 return false;
612 //fprintf(stderr, "size: %dx%d; depth=%d\n", sizeX, sizeY, (int)png_get_bit_depth(PNGStruct, PNGInfo));
614 if (png_get_bit_depth(PNGStruct, PNGInfo) != 8 || png_get_color_type(PNGStruct, PNGInfo) != PNG_COLOR_TYPE_RGB) {
615 if (fl) fclose(fl);
616 png_destroy_read_struct(&PNGStruct, (png_infopp)nullptr, (png_infopp)nullptr);
617 delete [] rowptrs;
618 delete [] rgb;
619 return false;
622 rgb = new uChar[sizeY*(sizeX*3)];
623 rowptrs = new png_bytep[sizeY];
625 for (int y = 0; y < sizeY; ++y) rowptrs[y] = (png_bytep)(rgb+y*(sizeX*3));
627 png_read_image(PNGStruct, rowptrs);
629 png_destroy_read_struct(&PNGStruct, &PNGInfo, nullptr);
631 delete [] rowptrs;
633 fclose(fl);
635 delete [] Image;
636 delete [] AlphaMap;
637 delete [] PriorityMap;
638 delete [] RandMap;
639 PriorityMap = 0;
640 RandMap = 0;
641 AlphaMap = 0;
642 mSize.X = sizeX;
643 mSize.Y = sizeY;
644 Alloc2D(Image, mSize.Y, mSize.X);
645 //Alloc2D(AlphaMap, mSize.Y, mSize.X);
646 const uChar *pp = rgb;
647 for (int y = 0; y < sizeY; ++y) {
648 for (int x = 0; x < sizeX; ++x) {
649 uChar r = *pp++;
650 uChar g = *pp++;
651 uChar b = *pp++;
652 PutPixel(x, y, MakeRGB16(r, g, b));
656 delete [] rgb;
657 return true;
658 #endif
660 #endif
663 void bitmap::SaveScaledIPU (cfestring &fileName, double scale) const {
664 v2 size;
665 uChar *rgb = createScaled(scale, &size);
666 if (!rgb) return;
667 // and save it
668 auto fo = xcreate(fileName.CStr());
669 if (fo) {
670 uint16_t ii;
671 ii = 0x29a; xwrite(fo, &ii, sizeof(ii));
672 ii = size.X; xwrite(fo, &ii, sizeof(ii));
673 ii = size.Y; xwrite(fo, &ii, sizeof(ii));
674 xwrite(fo, rgb, size.X*size.Y*3);
675 xclose(fo);
677 delete [] rgb;
681 truth bitmap::LoadIPU (cfestring &fileName) {
682 uint16_t ii = 0;
683 int wdt = 0, hgt = 0;
684 auto fi = xopenro(fileName.CStr());
685 if (!fi) return false;
686 if (!xread(fi, &ii, sizeof(ii))) { xclose(fi); return false; }
687 if (ii != 0x29a) { xclose(fi); return false; }
688 if (!xread(fi, &ii, sizeof(ii))) { xclose(fi); return false; }
689 if (ii < 1 || ii > 4096) { xclose(fi); return false; }
690 wdt = ii;
691 if (!xread(fi, &ii, sizeof(ii))) { xclose(fi); return false; }
692 if (ii < 1 || ii > 4096) { xclose(fi); return false; }
693 hgt = ii;
694 uChar *nb = new uChar[wdt*hgt*3];
695 if (!xread(fi, nb, wdt*hgt*3)) { xclose(fi); delete [] nb; xclose(fi); return false; }
696 xclose(fi);
698 delete [] Image;
699 delete [] AlphaMap;
700 delete [] PriorityMap;
701 delete [] RandMap;
703 PriorityMap = 0;
704 RandMap = 0;
705 AlphaMap = 0;
706 mSize.X = wdt;
707 mSize.Y = hgt;
708 Alloc2D(Image, mSize.Y, mSize.X);
709 //Alloc2D(AlphaMap, mSize.Y, mSize.X);
710 uChar *pp = nb;
711 for (int y = 0; y < mSize.Y; y++) {
712 for (int x = 0; x < mSize.X; x++) {
713 uChar r = *pp++;
714 uChar g = *pp++;
715 uChar b = *pp++;
716 PutPixel(x, y, MakeRGB16(r, g, b));
719 delete [] nb;
720 return true;
724 void bitmap::Fill (v2 TopLeft, int Width, int Height, col16 Color) { Fill(TopLeft.X, TopLeft.Y, Width, Height, Color); }
725 void bitmap::Fill (int X, int Y, v2 FillSize, col16 Color) { Fill(X, Y, FillSize.X, FillSize.Y, Color); }
726 void bitmap::Fill (v2 TopLeft, v2 FillSize, col16 Color) { Fill(TopLeft.X, TopLeft.Y, FillSize.X, FillSize.Y, Color); }
728 void bitmap::Fill (int X, int Y, int Width, int Height, col16 Color) {
729 if (X >= mSize.X || Y >= mSize.Y) return;
730 if (X + Width > mSize.X) Width = mSize.X - X;
731 if (Y + Height > mSize.Y) Height = mSize.Y - Y;
732 if (Color >> 8 == (Color & 0xFF)) {
733 Width <<= 1;
734 for (int y = 0; y < Height; ++y) memset(&Image[Y + y][X], Color, Width);
735 } else {
736 for (int y = 0; y < Height; ++y) {
737 packcol16 *Ptr = &Image[Y + y][X];
738 cpackcol16 *const EndPtr = Ptr + Width;
739 while (Ptr != EndPtr) *Ptr++ = Color;
745 void bitmap::ClearToColor (col16 Color) {
746 packcol16 *Ptr = Image[0];
747 if (Color >> 8 == (Color & 0xFF)) {
748 memset(Ptr, Color, XSizeTimesYSize*sizeof(packcol16));
749 } else {
750 cpackcol16 *const EndPtr = Ptr + XSizeTimesYSize;
751 while (Ptr != EndPtr) *Ptr++ = Color;
756 void bitmap::NormalBlit (cblitdata &BlitData) const {
757 blitdata B = BlitData;
758 if (!FastFlag) {
759 if (!B.Border.X || !B.Border.Y) ABORT("Zero-sized bitmap blit attempt detected!");
760 if ((B.Flags&ROTATE) && B.Border.X != B.Border.Y) ABORT("Blit error: FeLib supports only square rotating!");
761 if (!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, mSize.X, mSize.Y, B.Bitmap->mSize.X, B.Bitmap->mSize.Y)) return;
763 packcol16 **SrcImage = Image;
764 packcol16 **DestImage = B.Bitmap->Image;
765 switch (B.Flags&7) {
766 case NONE: {
767 if (!B.Src.X && !B.Src.Y && !B.Dest.X && !B.Dest.Y && B.Border.X == mSize.X && B.Border.Y == mSize.Y &&
768 B.Border.X == B.Bitmap->mSize.X && B.Border.Y == B.Bitmap->mSize.Y) {
769 memmove(DestImage[0], SrcImage[0], XSizeTimesYSize * sizeof(packcol16));
770 } else {
771 cint Bytes = B.Border.X * sizeof(packcol16);
772 for (int y = 0; y < B.Border.Y; ++y) memmove(&DestImage[B.Dest.Y + y][B.Dest.X], &SrcImage[B.Src.Y + y][B.Src.X], Bytes);
774 break; }
775 case MIRROR: {
776 B.Dest.X += B.Border.X - 1;
777 for (int y = 0; y < B.Border.Y; ++y) {
778 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
779 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
780 packcol16 *DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
781 for (; SrcPtr != EndPtr; ++SrcPtr, --DestPtr) *DestPtr = *SrcPtr;
783 break; }
784 case FLIP: {
785 B.Dest.Y += B.Border.Y - 1;
786 cint Bytes = B.Border.X * sizeof(packcol16);
787 for (int y = 0; y < B.Border.Y; ++y) memmove(&DestImage[B.Dest.Y - y][B.Dest.X], &SrcImage[B.Src.Y + y][B.Src.X], Bytes);
788 break; }
789 case (MIRROR | FLIP): {
790 B.Dest.X += B.Border.X - 1;
791 B.Dest.Y += B.Border.Y - 1;
792 for (int y = 0; y < B.Border.Y; ++y) {
793 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
794 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
795 packcol16 *DestPtr = &DestImage[B.Dest.Y - y][B.Dest.X];
796 for (; SrcPtr != EndPtr; ++SrcPtr, --DestPtr) *DestPtr = *SrcPtr;
798 break; }
799 case ROTATE: {
800 B.Dest.X += B.Border.X - 1;
801 int TrueDestXMove = B.Bitmap->mSize.X;
802 packcol16 *DestBase = &DestImage[B.Dest.Y][B.Dest.X];
803 for (int y = 0; y < B.Border.Y; ++y) {
804 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
805 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
806 packcol16 *DestPtr = DestBase - y;
807 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove) *DestPtr = *SrcPtr;
809 break; }
810 case (MIRROR | ROTATE): {
811 int TrueDestXMove = B.Bitmap->mSize.X;
812 packcol16 *DestBase = &DestImage[B.Dest.Y][B.Dest.X];
813 for (int y = 0; y < B.Border.Y; ++y) {
814 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
815 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
816 packcol16 *DestPtr = DestBase + y;
817 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove) *DestPtr = *SrcPtr;
819 break; }
820 case (FLIP | ROTATE): {
821 B.Dest.X += B.Border.X - 1;
822 B.Dest.Y += B.Border.Y - 1;
823 int TrueDestXMove = B.Bitmap->mSize.X;
824 packcol16 *DestBase = &DestImage[B.Dest.Y][B.Dest.X];
825 for (int y = 0; y < B.Border.Y; ++y) {
826 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
827 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
828 packcol16 *DestPtr = DestBase - y;
829 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove) *DestPtr = *SrcPtr;
831 break; }
832 case (MIRROR | FLIP | ROTATE): {
833 B.Dest.Y += B.Border.Y - 1;
834 int TrueDestXMove = B.Bitmap->mSize.X;
835 packcol16 *DestBase = &DestImage[B.Dest.Y][B.Dest.X];
836 for (int y = 0; y < B.Border.Y; ++y) {
837 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
838 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
839 packcol16 *DestPtr = DestBase + y;
840 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove) *DestPtr = *SrcPtr;
842 break; }
847 void bitmap::LuminanceBlit (cblitdata &BlitData) const {
848 blitdata B = BlitData;
849 if (B.Luminance == NORMAL_LUMINANCE) {
850 B.Flags = 0;
851 NormalBlit(B);
852 return;
854 if (!FastFlag) {
855 if (!B.Border.X || !B.Border.Y) ABORT("Zero-sized bitmap blit attempt detected!");
856 if (!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, mSize.X, mSize.Y, B.Bitmap->mSize.X, B.Bitmap->mSize.Y)) return;
858 packcol16 **SrcImage = Image;
859 packcol16 **DestImage = B.Bitmap->Image;
860 int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000;
861 int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800;
862 int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20;
863 for (int y = 0; y < B.Border.Y; ++y) {
864 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
865 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
866 packcol16 *DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
867 for (; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr) {
868 LOAD_SRC();
869 NEW_LUMINATE_RED();
870 NEW_LUMINATE_GREEN();
871 NEW_LUMINATE_BLUE();
872 STORE_COLOR();
878 void bitmap::NormalMaskedBlit (cblitdata &BlitData) const {
879 blitdata B = BlitData;
880 if (!FastFlag) {
881 if (!B.Border.X || !B.Border.Y) ABORT("Zero-sized bitmap masked blit attempt detected!");
882 if ((B.Flags&ROTATE) && B.Border.X != B.Border.Y) ABORT("MaskedBlit error: FeLib supports only square rotating!");
883 if (!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, mSize.X, mSize.Y, B.Bitmap->mSize.X, B.Bitmap->mSize.Y)) return;
885 packcol16 **SrcImage = Image;
886 packcol16 **DestImage = B.Bitmap->Image;
887 packcol16 PackedMaskColor = B.MaskColor;
888 switch (B.Flags&7) {
889 case NONE: {
890 for (int y = 0; y < B.Border.Y; ++y) {
891 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
892 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
893 packcol16 *DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
894 for (; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr) if (*SrcPtr != PackedMaskColor) *DestPtr = *SrcPtr;
896 break; }
897 case MIRROR: {
898 B.Dest.X += B.Border.X - 1;
899 for (int y = 0; y < B.Border.Y; ++y) {
900 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
901 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
902 packcol16 *DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
903 for (; SrcPtr != EndPtr; ++SrcPtr, --DestPtr) if (*SrcPtr != PackedMaskColor) *DestPtr = *SrcPtr;
905 break; }
906 case FLIP: {
907 B.Dest.Y += B.Border.Y - 1;
908 for (int y = 0; y < B.Border.Y; ++y) {
909 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
910 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
911 packcol16 *DestPtr = &DestImage[B.Dest.Y - y][B.Dest.X];
912 for (; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr) if (*SrcPtr != PackedMaskColor) *DestPtr = *SrcPtr;
914 break; }
915 case (MIRROR | FLIP): {
916 B.Dest.X += B.Border.X - 1;
917 B.Dest.Y += B.Border.Y - 1;
918 for (int y = 0; y < B.Border.Y; ++y) {
919 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
920 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
921 packcol16 *DestPtr = &DestImage[B.Dest.Y - y][B.Dest.X];
922 for (; SrcPtr != EndPtr; ++SrcPtr, --DestPtr) if (*SrcPtr != PackedMaskColor) *DestPtr = *SrcPtr;
924 break; }
925 case ROTATE: {
926 B.Dest.X += B.Border.X - 1;
927 int TrueDestXMove = B.Bitmap->mSize.X;
928 packcol16 *DestBase = &DestImage[B.Dest.Y][B.Dest.X];
929 for (int y = 0; y < B.Border.Y; ++y) {
930 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
931 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
932 packcol16 *DestPtr = DestBase - y;
933 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove) if (*SrcPtr != PackedMaskColor) *DestPtr = *SrcPtr;
935 break; }
936 case (MIRROR | ROTATE): {
937 int TrueDestXMove = B.Bitmap->mSize.X;
938 packcol16 *DestBase = &DestImage[B.Dest.Y][B.Dest.X];
939 for (int y = 0; y < B.Border.Y; ++y) {
940 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
941 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
942 packcol16 *DestPtr = DestBase + y;
943 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr += TrueDestXMove) if (*SrcPtr != PackedMaskColor) *DestPtr = *SrcPtr;
945 break; }
946 case (FLIP | ROTATE): {
947 B.Dest.X += B.Border.X - 1;
948 B.Dest.Y += B.Border.Y - 1;
949 int TrueDestXMove = B.Bitmap->mSize.X;
950 packcol16 *DestBase = &DestImage[B.Dest.Y][B.Dest.X];
951 for (int y = 0; y < B.Border.Y; ++y) {
952 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
953 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
954 packcol16 *DestPtr = DestBase - y;
955 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove) if (*SrcPtr != PackedMaskColor) *DestPtr = *SrcPtr;
957 break; }
958 case (MIRROR | FLIP | ROTATE): {
959 B.Dest.Y += B.Border.Y - 1;
960 int TrueDestXMove = B.Bitmap->mSize.X;
961 packcol16 *DestBase = &DestImage[B.Dest.Y][B.Dest.X];
962 for (int y = 0; y < B.Border.Y; ++y) {
963 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
964 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
965 packcol16 *DestPtr = DestBase + y;
966 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= TrueDestXMove) if (*SrcPtr != PackedMaskColor) *DestPtr = *SrcPtr;
968 break; }
973 void bitmap::LuminanceMaskedBlit (cblitdata &BlitData) const {
974 blitdata B = BlitData;
975 if (B.Luminance == NORMAL_LUMINANCE) {
976 B.Flags = 0;
977 NormalMaskedBlit(B);
978 return;
980 if (!FastFlag) {
981 if (!B.Border.X || !B.Border.Y) ABORT("Zero-sized bitmap masked blit attempt detected!");
982 if (!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, mSize.X, mSize.Y, B.Bitmap->mSize.X, B.Bitmap->mSize.Y)) return;
984 packcol16 **SrcImage = Image;
985 packcol16 **DestImage = B.Bitmap->Image;
986 int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000;
987 int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800;
988 int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20;
989 for (int y = 0; y < B.Border.Y; ++y) {
990 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
991 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
992 packcol16 *DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
993 for (; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr) {
994 LOAD_SRC();
995 if (SrcCol != B.MaskColor) {
996 NEW_LUMINATE_RED();
997 NEW_LUMINATE_GREEN();
998 NEW_LUMINATE_BLUE();
999 STORE_COLOR();
1006 void bitmap::SimpleAlphaBlit (bitmap *Bitmap, alpha Alpha, col16 MaskColor) const {
1007 if (Alpha == 255) {
1008 blitdata B = {
1009 Bitmap,
1010 { 0, 0 },
1011 { 0, 0 },
1012 { mSize.X, mSize.Y },
1013 { 0 },
1014 MaskColor,
1017 NormalMaskedBlit(B);
1018 return;
1020 if (!FastFlag && (mSize.X != Bitmap->mSize.X || mSize.Y != Bitmap->mSize.Y)) ABORT("Fast simple alpha blit attempt of noncongruent bitmaps detected!");
1021 cpackcol16 *SrcPtr = Image[0];
1022 cpackcol16 *EndPtr = SrcPtr + XSizeTimesYSize;
1023 packcol16 *DestPtr = Bitmap->Image[0];
1024 for (; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr) {
1025 LOAD_SRC();
1026 if (SrcCol != MaskColor) {
1027 LOAD_DEST();
1028 NEW_LOAD_AND_APPLY_ALPHA_RED();
1029 NEW_LOAD_AND_APPLY_ALPHA_GREEN();
1030 NEW_LOAD_AND_APPLY_ALPHA_BLUE();
1031 STORE_COLOR();
1037 void bitmap::AlphaMaskedBlit (cblitdata &BlitData) const {
1038 blitdata B = BlitData;
1039 if (!AlphaMap) {
1040 B.Flags = 0;
1041 NormalMaskedBlit(B);
1042 return;
1044 if (!FastFlag) {
1045 if (!B.Border.X || !B.Border.Y) ABORT("Zero-sized bitmap alpha blit attempt detected!");
1046 if (!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, mSize.X, mSize.Y, B.Bitmap->mSize.X, B.Bitmap->mSize.Y)) return;
1049 packcol16 **SrcImage = Image;
1050 packalpha **SrcAlphaMap = AlphaMap;
1051 packcol16 **DestImage = B.Bitmap->Image;
1053 for (int y = 0; y < B.Border.Y; ++y) {
1054 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
1055 cpackalpha *AlphaPtr = &SrcAlphaMap[B.Src.Y + y][B.Src.X];
1056 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
1057 packcol16 *DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
1058 for (; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++AlphaPtr) {
1059 LOAD_SRC();
1060 if (SrcCol != B.MaskColor) {
1061 LOAD_DEST();
1062 LOAD_ALPHA();
1063 NEW_LOAD_AND_APPLY_ALPHA_RED();
1064 NEW_LOAD_AND_APPLY_ALPHA_GREEN();
1065 NEW_LOAD_AND_APPLY_ALPHA_BLUE();
1066 STORE_COLOR();
1073 void bitmap::DrawLine (v2 From, int ToX, int ToY, col16 Color, truth Wide) { DrawLine(From.X, From.Y, ToX, ToY, Color, Wide); }
1074 void bitmap::DrawLine (int FromX, int FromY, v2 To, col16 Color, truth Wide) { DrawLine(FromX, FromY, To.X, To.Y, Color, Wide); }
1075 void bitmap::DrawLine (v2 From, v2 To, col16 Color, truth Wide) { DrawLine(From.X, From.Y, To.X, To.Y, Color, Wide); }
1077 void bitmap::DrawLine (int OrigFromX, int OrigFromY, int OrigToX, int OrigToY, col16 Color, truth Wide) {
1078 if (OrigFromY == OrigToY) {
1079 DrawHorizontalLine(OrigFromX, OrigToX, OrigFromY, Color, Wide);
1080 return;
1082 if (OrigFromX == OrigToX) {
1083 DrawVerticalLine(OrigFromX, OrigFromY, OrigToY, Color, Wide);
1084 return;
1087 static cint PointX[] = { 0, 0, -1, 1, 0 };
1088 static cint PointY[] = { 0, -1, 0, 0, 1 };
1089 cint Times = Wide ? 5 : 1;
1091 for (int c1 = 0; c1 < Times; ++c1) {
1092 cint X1 = OrigFromX + PointX[c1];
1093 cint Y1 = OrigFromY + PointY[c1];
1094 cint X2 = OrigToX + PointX[c1];
1095 cint Y2 = OrigToY + PointY[c1];
1096 cint DeltaX = abs(X2 - X1);
1097 cint DeltaY = abs(Y2 - Y1);
1098 int x, c2;
1099 int XChange, PtrXChange, PtrYChange;
1100 int DoubleDeltaX, DoubleDeltaY, End;
1101 if (DeltaX >= DeltaY) {
1102 x = X1;
1103 c2 = DeltaX;
1104 PtrXChange = XChange = X1 < X2 ? 1 : -1;
1105 PtrYChange = Y1 < Y2 ? mSize.X : -mSize.X;
1106 DoubleDeltaX = DeltaX << 1;
1107 DoubleDeltaY = DeltaY << 1;
1108 End = X2;
1109 } else {
1110 x = Y1;
1111 c2 = DeltaY;
1112 XChange = Y1 < Y2 ? 1 : -1;
1113 PtrXChange = Y1 < Y2 ? mSize.X : -mSize.X;
1114 PtrYChange = X1 < X2 ? 1 : -1;
1115 DoubleDeltaX = DeltaY << 1;
1116 DoubleDeltaY = DeltaX << 1;
1117 End = Y2;
1119 packcol16 *Ptr = &Image[Y1][X1];
1120 *Ptr = Color;
1121 while (x != End) {
1122 x += XChange;
1123 Ptr += PtrXChange;
1124 c2 += DoubleDeltaY;
1125 if (c2 >= DoubleDeltaX) {
1126 c2 -= DoubleDeltaX;
1127 Ptr += PtrYChange;
1129 *Ptr = Color;
1135 void bitmap::DrawVerticalLine (int OrigX, int OrigFromY, int OrigToY, col16 Color, truth Wide) {
1136 static cint PointX[] = { 0, -1, 1 };
1137 cint Times = Wide ? 3 : 1;
1138 for (int c = 0; c < Times; ++c) {
1139 int X = OrigX + PointX[c];
1140 int FromY = OrigFromY;
1141 int ToY = OrigToY;
1142 if (FromY > ToY) Swap(FromY, ToY);
1143 if (Wide && !c) {
1144 --FromY;
1145 ++ToY;
1147 if (X < 0 || X >= mSize.X || ToY < 0 || FromY >= mSize.Y) continue;
1148 FromY = Max(FromY, 0);
1149 ToY = Min(ToY, mSize.Y-1);
1150 packcol16 *Ptr = &Image[FromY][X];
1151 for (int y = FromY; y <= ToY; ++y, Ptr += mSize.X) *Ptr = Color;
1156 void bitmap::DrawHorizontalLine(int OrigFromX, int OrigToX, int OrigY, col16 Color, truth Wide) {
1157 static cint PointY[] = { 0, -1, 1 };
1158 cint Times = Wide ? 3 : 1;
1159 for (int c = 0; c < Times; ++c) {
1160 int Y = OrigY + PointY[c];
1161 int FromX = OrigFromX;
1162 int ToX = OrigToX;
1163 if (FromX > ToX) Swap(FromX, ToX);
1164 if (Wide && !c) {
1165 --FromX;
1166 ++ToX;
1168 if (Y < 0 || Y >= mSize.Y || ToX < 0 || FromX >= mSize.X) continue;
1169 FromX = Max(FromX, 0);
1170 ToX = Min(ToX, mSize.X-1);
1171 packcol16 *Ptr = &Image[Y][FromX];
1172 for (int x = FromX; x <= ToX; ++x, ++Ptr) *Ptr = Color;
1177 void bitmap::DrawPolygon (int CenterX, int CenterY, int Radius, int NumberOfSides, col16 Color, truth DrawSides, truth DrawDiameters, double Rotation) {
1178 if (!DrawSides && !DrawDiameters) return;
1179 v2 *Point = new v2[NumberOfSides];
1180 double AngleDelta = 2 * FPI / NumberOfSides;
1181 int c;
1182 for (c = 0; c < NumberOfSides; ++c) {
1183 Point[c].X = CenterX + int(sin(AngleDelta * c + Rotation) * Radius);
1184 Point[c].Y = CenterY + int(cos(AngleDelta * c + Rotation) * Radius);
1186 if (DrawDiameters) {
1187 if (DrawSides) {
1188 for (c = 0; c < NumberOfSides; ++c)
1189 for (int a = 0; a < NumberOfSides; ++a)
1190 if (c != a) DrawLine(Point[c].X, Point[c].Y, Point[a].X, Point[a].Y, Color, true);
1191 } else {
1192 for (c = 0; c < NumberOfSides; ++c)
1193 for (int a = 0; a < NumberOfSides; ++a)
1194 if ((int(c - a) > 1 || int(a - c) > 1) && (a || c != NumberOfSides - 1) && (c || a != NumberOfSides - 1))
1195 DrawLine(Point[c].X, Point[c].Y, Point[a].X, Point[a].Y, Color, true);
1197 } else {
1198 for (c = 0; c < NumberOfSides - 1; ++c)
1199 DrawLine(Point[c].X, Point[c].Y, Point[c + 1].X, Point[c + 1].Y, Color, true);
1200 DrawLine(Point[NumberOfSides - 1].X, Point[NumberOfSides - 1].Y, Point[0].X, Point[0].Y, Color, true);
1202 delete [] Point;
1206 void bitmap::CreateAlphaMap (alpha InitialValue) {
1207 if (AlphaMap) ABORT("Alpha leak detected!");
1208 Alloc2D(AlphaMap, mSize.Y, mSize.X);
1209 memset(AlphaMap[0], InitialValue, XSizeTimesYSize);
1213 truth bitmap::Fade (sLong &AlphaSum, packalpha& AlphaAverage, int Amount) {
1214 if (!AlphaMap) ABORT("No alpha map to fade.");
1215 truth Changes = false;
1216 sLong Alphas = 0;
1217 sLong NewAlphaSum = 0;
1218 sLong Size = XSizeTimesYSize;
1219 for (sLong c = 0; c < Size; ++c) {
1220 packalpha *AlphaPtr = &AlphaMap[0][c];
1221 if (*AlphaPtr) {
1222 if (*AlphaPtr > Amount) {
1223 *AlphaPtr -= Amount;
1224 NewAlphaSum += *AlphaPtr;
1225 ++Alphas;
1226 Changes = true;
1227 } else {
1228 *AlphaPtr = 0;
1229 Changes = true;
1230 if (RandMap) UpdateRandMap(c, false);
1234 AlphaSum = NewAlphaSum;
1235 AlphaAverage = Alphas ? NewAlphaSum / Alphas : 0;
1236 return Changes;
1240 void bitmap::Outline (col16 Color, alpha Alpha, priority Priority) {
1241 if (!AlphaMap) CreateAlphaMap(255);
1242 col16 LastColor, NextColor;
1243 int XMax = mSize.X;
1244 int YMax = mSize.Y - 1;
1245 for (int x = 0; x < XMax; ++x) {
1246 packcol16 *Buffer = &Image[0][x];
1247 LastColor = *Buffer;
1248 for (int y = 0; y < YMax; ++y) {
1249 NextColor = *(Buffer + XMax);
1250 if ((LastColor == TRANSPARENT_COLOR || !y) && NextColor != TRANSPARENT_COLOR) {
1251 *Buffer = Color;
1252 SetAlpha(x, y, Alpha);
1253 SafeSetPriority(x, y, Priority);
1255 Buffer += XMax;
1256 if (LastColor != TRANSPARENT_COLOR && (NextColor == TRANSPARENT_COLOR || y == YMax - 1)) {
1257 *Buffer = Color;
1258 SetAlpha(x, y + 1, Alpha);
1259 SafeSetPriority(x, y + 1, Priority);
1261 LastColor = NextColor;
1264 --XMax;
1265 ++YMax;
1266 for (int y = 0; y < YMax; ++y) {
1267 packcol16 *Buffer = Image[y];
1268 LastColor = *Buffer;
1269 for (int x = 0; x < XMax; ++x) {
1270 NextColor = *(Buffer + 1);
1271 if ((LastColor == TRANSPARENT_COLOR || !x) && NextColor != TRANSPARENT_COLOR) {
1272 *Buffer = Color;
1273 SetAlpha(x, y, Alpha);
1274 SafeSetPriority(x, y, Priority);
1276 ++Buffer;
1277 if (LastColor != TRANSPARENT_COLOR && (NextColor == TRANSPARENT_COLOR || x == XMax - 1)) {
1278 *Buffer = Color;
1279 SetAlpha(x + 1, y, Alpha);
1280 SafeSetPriority(x + 1, y, Priority);
1282 LastColor = NextColor;
1288 void bitmap::FadeToScreen (bitmapeditor BitmapEditor) {
1289 bitmap Backup(DOUBLE_BUFFER);
1290 Backup.ActivateFastFlag();
1291 blitdata B = {
1292 DOUBLE_BUFFER,
1293 { 0, 0 },
1294 { 0, 0 },
1295 { RES.X, RES.Y },
1296 { 0 },
1300 for (int c = 0; c <= 5; ++c) {
1301 clock_t StartTime = clock();
1302 int Element = 127 - c * 25;
1303 B.Luminance = MakeRGB24(Element, Element, Element);
1304 Backup.LuminanceMaskedBlit(B);
1305 if (BitmapEditor) BitmapEditor(this, true);
1306 SimpleAlphaBlit(DOUBLE_BUFFER, c * 50, 0);
1307 graphics::BlitDBToScreen();
1308 while(clock() - StartTime < 0.05 * CLOCKS_PER_SEC);
1310 DOUBLE_BUFFER->ClearToColor(0);
1311 if (BitmapEditor) BitmapEditor(this, true);
1312 B.Flags = 0;
1313 NormalMaskedBlit(B);
1314 graphics::BlitDBToScreen();
1318 void bitmap::StretchBlit (cblitdata &BlitData) const {
1319 blitdata B = BlitData;
1320 if (!FastFlag) {
1321 if (!B.Border.X || !B.Border.Y) ABORT("Zero-sized bitmap stretch blit attempt detected!");
1322 if (!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, mSize.X, mSize.Y, B.Bitmap->mSize.X, B.Bitmap->mSize.Y)) return;
1324 if (B.Stretch > 1) {
1325 int tx = B.Dest.X;
1326 for (int x1 = B.Src.X; x1 < B.Src.X + B.Border.X; ++x1, tx += B.Stretch) {
1327 int ty = B.Dest.Y;
1328 for (int y1 = B.Src.Y; y1 < B.Src.Y + B.Border.Y; ++y1, ty += B.Stretch) {
1329 packcol16 Pixel = Image[y1][x1];
1330 if (Pixel != TRANSPARENT_COLOR)
1331 for (int x2 = tx; x2 < tx + B.Stretch; ++x2)
1332 for (int y2 = ty; y2 < ty + B.Stretch; ++y2)
1333 B.Bitmap->Image[y2][x2] = Pixel;
1336 return;
1337 } else if (B.Stretch < -1) {
1338 int tx = B.Dest.X;
1339 for (int x1 = B.Src.X; x1 < B.Src.X + B.Border.X; x1 -= B.Stretch, ++tx) {
1340 int ty = B.Dest.Y;
1341 for (int y1 = B.Src.Y; y1 < B.Src.Y + B.Border.Y; y1 -= B.Stretch, ++ty) {
1342 packcol16 Pixel = Image[y1][x1];
1343 if (Pixel != TRANSPARENT_COLOR) B.Bitmap->Image[ty][tx] = Pixel;
1346 return;
1347 } else {
1348 B.Flags = 0;
1349 NormalMaskedBlit(B);
1350 return;
1355 outputfile &operator << (outputfile &SaveFile, cbitmap *Bitmap) {
1356 if (Bitmap) {
1357 SaveFile.Put(1);
1358 SaveFile << Bitmap->GetSize();
1359 Bitmap->Save(SaveFile);
1360 } else {
1361 SaveFile.Put(0);
1363 return SaveFile;
1367 inputfile &operator >> (inputfile &SaveFile, bitmap *&Bitmap) {
1368 if (SaveFile.Get()) {
1369 Bitmap = new bitmap(ReadType(v2, SaveFile));
1370 Bitmap->Load(SaveFile);
1371 } else {
1372 Bitmap = 0;
1374 return SaveFile;
1378 void bitmap::DrawRectangle (v2 TopLeft, int Right, int Bottom, col16 Color, truth Wide) { DrawRectangle(TopLeft.X, TopLeft.Y, Right, Bottom, Color, Wide); }
1379 void bitmap::DrawRectangle (int Left, int Top, v2 BottomRight, col16 Color, truth Wide) { DrawRectangle(Left, Top, BottomRight.X, BottomRight.Y, Color, Wide); }
1380 void bitmap::DrawRectangle (v2 TopLeft, v2 BottomRight, col16 Color, truth Wide) { DrawRectangle(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y, Color, Wide); }
1382 void bitmap::DrawRectangle (int Left, int Top, int Right, int Bottom, col16 Color, truth Wide) {
1383 DrawHorizontalLine(Left, Right, Top, Color, Wide);
1384 DrawHorizontalLine(Left, Right, Bottom, Color, Wide);
1385 DrawVerticalLine(Right, Top, Bottom, Color, Wide);
1386 DrawVerticalLine(Left, Top, Bottom, Color, Wide);
1390 void bitmap::AlphaLuminanceBlit (cblitdata &BlitData) const {
1391 if (BlitData.Luminance == NORMAL_LUMINANCE) {
1392 AlphaMaskedBlit(BlitData);
1393 return;
1395 if (!AlphaMap) {
1396 LuminanceMaskedBlit(BlitData);
1397 return;
1399 blitdata B = BlitData;
1400 if (!FastFlag) {
1401 if (!B.Border.X || !B.Border.Y) ABORT("Zero-sized bitmap alpha blit attempt detected!");
1402 if (!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, mSize.X, mSize.Y, B.Bitmap->mSize.X, B.Bitmap->mSize.Y)) return;
1404 packcol16 **SrcImage = Image;
1405 packalpha **SrcAlphaMap = AlphaMap;
1406 packcol16 **DestImage = B.Bitmap->Image;
1407 int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000;
1408 int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800;
1409 int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20;
1410 for (int y = 0; y < B.Border.Y; ++y) {
1411 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
1412 cpackalpha *AlphaPtr = &SrcAlphaMap[B.Src.Y + y][B.Src.X];
1413 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
1414 packcol16 *DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
1415 for (; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++AlphaPtr) {
1416 LOAD_SRC();
1417 if (SrcCol != B.MaskColor) {
1418 LOAD_DEST();
1419 LOAD_ALPHA();
1420 NEW_LUMINATE_RED();
1421 NEW_APPLY_ALPHA_RED();
1422 NEW_LUMINATE_GREEN();
1423 NEW_APPLY_ALPHA_GREEN();
1424 NEW_LUMINATE_BLUE();
1425 NEW_APPLY_ALPHA_BLUE();
1426 STORE_COLOR();
1433 /* Only works for 16x16 pictures :( */
1434 void bitmap::CreateFlames (rawbitmap *RawBitmap, v2 RawPos, feuLong SeedNFlags, int Frame) {
1435 femath::SaveSeed();
1436 femath::SetSeed(SeedNFlags);
1437 int FlameTop[16], FlameBottom[16], FlamePhase[16];
1438 int x, y;
1439 for (x = 0; x < 16; ++x) {
1440 FlameBottom[x] = NO_FLAME;
1441 for (y = 0; y < 16; ++y) {
1442 if (GetPixel(x, y) != TRANSPARENT_COLOR) {
1443 if (1 << RawBitmap->GetMaterialColorIndex(RawPos.X + x, RawPos.Y + y) & SeedNFlags) {
1444 FlamePhase[x] = RAND_16;
1445 if (y > 1) {
1446 FlameBottom[x] = y - 1;
1447 if (y >= 5) FlameTop[x] = (y - (RAND_32 * y >> 5)) >> 1; else FlameTop[x] = 0;
1448 } else {
1449 FlameBottom[x] = 1;
1450 FlameTop[x] = 0;
1453 break;
1457 for (x = 0; x < 16; ++x) {
1458 if (FlameBottom[x] != NO_FLAME) {
1459 int Phase = (Frame + FlamePhase[x]) & 15;
1460 int Length = FlameBottom[x] - FlameTop[x];
1461 int Top = FlameBottom[x] - Length + Phase * (15 - Phase) * Length / 56;
1462 for (y = Top; y <= FlameBottom[x]; ++y) {
1463 int Pos = y - Top;
1464 PowerPutPixel(x, y, MakeRGB16(255, 255 - (Pos << 7) / Length, 0), 127 + (Pos << 6) / Length, AVERAGE_PRIORITY);
1468 femath::LoadSeed();
1472 void bitmap::CreateSparkle (v2 SparklePos, int Frame) {
1473 if (Frame) {
1474 int Size = (Frame - 1) * (16 - Frame) / 10;
1475 PowerPutPixel(SparklePos.X, SparklePos.Y, WHITE, 255, SPARKLE_PRIORITY);
1476 for (int c = 1; c < Size; ++c) {
1477 int Lightness = 191 + ((Size - c) << 6) / Size;
1478 col16 RGB = MakeRGB16(Lightness, Lightness, Lightness);
1479 PowerPutPixel(SparklePos.X + c, SparklePos.Y, RGB, 255, SPARKLE_PRIORITY);
1480 PowerPutPixel(SparklePos.X - c, SparklePos.Y, RGB, 255, SPARKLE_PRIORITY);
1481 PowerPutPixel(SparklePos.X, SparklePos.Y + c, RGB, 255, SPARKLE_PRIORITY);
1482 PowerPutPixel(SparklePos.X, SparklePos.Y - c, RGB, 255, SPARKLE_PRIORITY);
1488 void bitmap::CreateFlies (feuLong Seed, int Frame, int FlyAmount) {
1489 femath::SaveSeed();
1490 femath::SetSeed(Seed);
1491 for (int c = 0; c < FlyAmount; ++c) {
1492 double Constant = double(RAND() % 10000) / 10000 * FPI;
1493 v2 StartPos = v2(5 + RAND() % 6, 5 + RAND() % 6);
1494 double Temp = (double(16 - Frame) * FPI) / 16;
1495 if (RAND() & 1) Temp = -Temp;
1496 v2 Where;
1497 Where.X = int(StartPos.X + sin(Constant + Temp) * 3);
1498 Where.Y = int(StartPos.Y + sin(2*(Constant + Temp)) * 3);
1499 PowerPutPixel(Where.X, Where.Y, MakeRGB16(40, 40, 60), 255, FLY_PRIORITY);
1501 femath::LoadSeed();
1505 void bitmap::CreateLightning (feuLong Seed, col16 Color) {
1506 femath::SaveSeed();
1507 femath::SetSeed(Seed);
1508 v2 StartPos;
1509 v2 Direction(0, 0);
1510 do {
1511 do {
1512 if (RAND() & 1) {
1513 if (RAND() & 1) {
1514 StartPos.X = 0;
1515 Direction.X = 1;
1516 } else {
1517 StartPos.X = mSize.X - 1;
1518 Direction.X = -1;
1520 StartPos.Y = RAND() % mSize.Y;
1521 } else {
1522 if (RAND() & 1) {
1523 StartPos.Y = 0;
1524 Direction.Y = 1;
1525 } else {
1526 StartPos.Y = mSize.Y - 1;
1527 Direction.Y = -1;
1529 StartPos.X = RAND() % mSize.X;
1531 } while (GetPixel(StartPos) != TRANSPARENT_COLOR);
1532 } while (!CreateLightning(StartPos, Direction, NO_LIMIT, Color));
1533 femath::LoadSeed();
1537 struct pixelvectorcontroller {
1538 static truth Handler (int x, int y) {
1539 if (CurrentSprite->GetPixel(x, y) == TRANSPARENT_COLOR) {
1540 PixelVector.push_back(v2(x, y));
1541 return true;
1543 return false;
1545 static std::vector<v2> PixelVector;
1546 static bitmap *CurrentSprite;
1550 std::vector<v2> pixelvectorcontroller::PixelVector;
1551 bitmap *pixelvectorcontroller::CurrentSprite;
1554 truth bitmap::CreateLightning (v2 StartPos, v2 Direction, int MaxLength, col16 Color) {
1555 pixelvectorcontroller::CurrentSprite = this;
1556 std::vector<v2> &PixelVector = pixelvectorcontroller::PixelVector;
1557 PixelVector.clear();
1558 v2 LastMove(0, 0);
1559 int Counter = 0;
1560 for (;;) {
1561 v2 Move(1 + (RAND() & 3), 1 + (RAND() & 3));
1562 if (Direction.X < 0 || (!Direction.X && RAND() & 1)) Move.X = -Move.X;
1563 if (Direction.Y < 0 || (!Direction.Y && RAND() & 1)) Move.Y = -Move.Y;
1564 LimitRef(Move.X, -StartPos.X, mSize.X - StartPos.X - 1);
1565 LimitRef(Move.Y, -StartPos.Y, mSize.X - StartPos.Y - 1);
1566 if (Counter < 10 && ((!Move.Y && !LastMove.Y) || (Move.Y && LastMove.Y && (Move.X << 10) / Move.Y == (LastMove.X << 10) / LastMove.Y))) {
1567 ++Counter;
1568 continue;
1570 Counter = 0;
1571 if (!mapmath<pixelvectorcontroller>::DoLine(StartPos.X, StartPos.Y, StartPos.X + Move.X, StartPos.Y + Move.Y, LINE_BOTH_DIRS) || feuLong(MaxLength) <= PixelVector.size()) {
1572 int Limit = Min<int>(PixelVector.size(), MaxLength);
1573 for (int c = 0; c < Limit; ++c) {
1574 PutPixel(PixelVector[c], Color);
1575 SafeSetPriority(PixelVector[c], LIGHTNING_PRIORITY);
1577 PixelVector.clear();
1578 return true;
1580 StartPos += Move;
1581 LastMove = Move;
1582 if ((Direction.X && (!StartPos.X || StartPos.X == mSize.X - 1)) || (Direction.Y && (!StartPos.Y || StartPos.Y == mSize.X - 1))) {
1583 PixelVector.clear();
1584 return false;
1590 void bitmap::BlitAndCopyAlpha (bitmap *Bitmap, int Flags) const {
1591 if (!FastFlag) {
1592 if (!AlphaMap || !Bitmap->AlphaMap) ABORT("Attempt to blit and copy alpha without an alpha map detected!");
1593 if ((Flags&ROTATE) && mSize.X != mSize.Y) ABORT("Blit and copy alpha error: FeLib supports only square rotating!");
1594 if (mSize.X != Bitmap->mSize.X || mSize.Y != Bitmap->mSize.Y) ABORT("Blit and copy alpha attempt of noncongruent bitmaps detected!");
1596 packcol16 **SrcImage = Image;
1597 packalpha **SrcAlphaMap = AlphaMap;
1598 packcol16 **DestImage = Bitmap->Image;
1599 packalpha **DestAlphaMap = Bitmap->AlphaMap;
1600 switch (Flags&7) {
1601 case NONE: {
1602 memmove(DestImage[0], SrcImage[0], XSizeTimesYSize * sizeof(packcol16));
1603 memmove(DestAlphaMap[0], SrcAlphaMap[0], XSizeTimesYSize * sizeof(packalpha));
1604 break; }
1605 case MIRROR: {
1606 int Width = mSize.X;
1607 int Height = mSize.Y;
1608 int DestX = Width - 1;
1609 cpackcol16 *SrcPtr = SrcImage[0];
1610 cpackalpha *SrcAlphaPtr = SrcAlphaMap[0];
1611 for (int y = 0; y < Height; ++y) {
1612 cpackcol16 *EndPtr = SrcPtr + Width;
1613 packcol16 *DestPtr = &DestImage[y][DestX];
1614 packalpha *DestAlphaPtr = &DestAlphaMap[y][DestX];
1615 for (; SrcPtr != EndPtr; ++SrcPtr, --DestPtr, ++SrcAlphaPtr, --DestAlphaPtr) {
1616 *DestPtr = *SrcPtr;
1617 *DestAlphaPtr = *SrcAlphaPtr;
1620 break; }
1621 case FLIP: {
1622 int Height = mSize.Y;
1623 int Width = mSize.X;
1624 int DestY = Height - 1;
1625 for (int y = 0; y < Height; ++y) {
1626 memmove(DestImage[DestY - y], SrcImage[y], Width * sizeof(packcol16));
1627 memmove(DestAlphaMap[DestY - y], SrcAlphaMap[y], Width * sizeof(packalpha));
1629 break; }
1630 case (MIRROR | FLIP): {
1631 cpackcol16 *SrcPtr = SrcImage[0];
1632 cpackcol16 *EndPtr = SrcPtr + XSizeTimesYSize;
1633 cpackalpha *SrcAlphaPtr = SrcAlphaMap[0];
1634 packcol16 *DestPtr = &DestImage[mSize.Y - 1][mSize.X - 1];
1635 packalpha *DestAlphaPtr = &DestAlphaMap[mSize.Y - 1][mSize.X - 1];
1636 for (; SrcPtr != EndPtr; ++SrcPtr, --DestPtr, ++SrcAlphaPtr, --DestAlphaPtr) {
1637 *DestPtr = *SrcPtr;
1638 *DestAlphaPtr = *SrcAlphaPtr;
1640 break; }
1641 case ROTATE: {
1642 cint Width = mSize.X;
1643 cpackcol16 *SrcPtr = SrcImage[0];
1644 cpackalpha *SrcAlphaPtr = SrcAlphaMap[0];
1645 packcol16 *DestBase = &DestImage[0][Width - 1];
1646 packalpha *DestAlphaBase = &DestAlphaMap[0][Width - 1];
1647 for (int y = 0; y < Width; ++y) {
1648 cpackcol16 *EndPtr = SrcPtr + Width;
1649 packcol16 *DestPtr = DestBase - y;
1650 packalpha *DestAlphaPtr = DestAlphaBase - y;
1651 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr += Width, ++SrcAlphaPtr, DestAlphaPtr += Width) {
1652 *DestPtr = *SrcPtr;
1653 *DestAlphaPtr = *SrcAlphaPtr;
1656 break; }
1657 case (MIRROR | ROTATE): {
1658 cint Width = mSize.X;
1659 cpackcol16 *SrcPtr = SrcImage[0];
1660 cpackalpha *SrcAlphaPtr = SrcAlphaMap[0];
1661 packcol16 *DestBase = DestImage[0];
1662 packalpha *DestAlphaBase = DestAlphaMap[0];
1663 for (int y = 0; y < Width; ++y) {
1664 cpackcol16 *EndPtr = SrcPtr + Width;
1665 packcol16 *DestPtr = DestBase + y;
1666 packalpha *DestAlphaPtr = DestAlphaBase + y;
1667 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr += Width, ++SrcAlphaPtr, DestAlphaPtr += Width) {
1668 *DestPtr = *SrcPtr;
1669 *DestAlphaPtr = *SrcAlphaPtr;
1672 break; }
1673 case (FLIP | ROTATE): {
1674 cint Width = mSize.X;
1675 cpackcol16 *SrcPtr = SrcImage[0];
1676 cpackalpha *SrcAlphaPtr = SrcAlphaMap[0];
1677 packcol16 *DestBase = &DestImage[Width - 1][Width - 1];
1678 packalpha *DestAlphaBase = &DestAlphaMap[Width - 1][Width - 1];
1679 for (int y = 0; y < Width; ++y) {
1680 cpackcol16 *EndPtr = SrcPtr + Width;
1681 packcol16 *DestPtr = DestBase - y;
1682 packalpha *DestAlphaPtr = DestAlphaBase - y;
1683 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= Width, ++SrcAlphaPtr, DestAlphaPtr -= Width) {
1684 *DestPtr = *SrcPtr;
1685 *DestAlphaPtr = *SrcAlphaPtr;
1688 break; }
1689 case (MIRROR | FLIP | ROTATE): {
1690 cint Width = mSize.X;
1691 cpackcol16 *SrcPtr = SrcImage[0];
1692 cpackalpha *SrcAlphaPtr = SrcAlphaMap[0];
1693 packcol16 *DestBase = DestImage[Width - 1];
1694 packalpha *DestAlphaBase = DestAlphaMap[Width - 1];
1695 for (int y = 0; y < Width; ++y) {
1696 cpackcol16 *EndPtr = SrcPtr + Width;
1697 packcol16 *DestPtr = DestBase + y;
1698 packalpha *DestAlphaPtr = DestAlphaBase + y;
1699 for (; SrcPtr != EndPtr; ++SrcPtr, DestPtr -= Width, ++SrcAlphaPtr, DestAlphaPtr -= Width) {
1700 *DestPtr = *SrcPtr;
1701 *DestAlphaPtr = *SrcAlphaPtr;
1704 break; }
1709 void bitmap::FillAlpha (alpha Alpha) {
1710 memset(AlphaMap[0], Alpha, XSizeTimesYSize);
1714 void bitmap::PowerPutPixel (int X, int Y, col16 Color, alpha Alpha, priority Priority) {
1715 if (X >= 0 && Y >= 0 && X < mSize.X && Y < mSize.Y) {
1716 Image[Y][X] = Color;
1717 if (AlphaMap) {
1718 AlphaMap[Y][X] = Alpha;
1719 } else if (Alpha != 255) {
1720 CreateAlphaMap(255);
1721 AlphaMap[Y][X] = Alpha;
1723 if (PriorityMap) PriorityMap[Y][X] = Priority;
1728 void bitmap::MaskedPriorityBlit (cblitdata &BlitData) const {
1729 if (!PriorityMap || !BlitData.Bitmap->PriorityMap) {
1730 LuminanceMaskedBlit(BlitData);
1731 return;
1733 blitdata B = BlitData;
1734 if (!FastFlag) {
1735 if (!B.Border.X || !B.Border.Y) ABORT("Zero-sized bitmap masked priority blit attempt detected!");
1736 if (!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, mSize.X, mSize.Y, B.Bitmap->mSize.X, B.Bitmap->mSize.Y)) return;
1738 packcol16 **SrcImage = Image;
1739 packpriority **SrcPriorityMap = PriorityMap;
1740 packcol16 **DestImage = B.Bitmap->Image;
1741 packpriority **DestPriorityMap = B.Bitmap->PriorityMap;
1742 int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000;
1743 int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800;
1744 int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20;
1746 if (B.Dest.X < 0) B.Dest.X = 0; //FIXME: k8: ???
1748 for (int y = 0; y < B.Border.Y; ++y) {
1749 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
1750 cpackpriority *SrcPriorityPtr = &SrcPriorityMap[B.Src.Y + y][B.Src.X];
1751 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
1752 packcol16 *DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
1753 packpriority *DestPriorityPtr = &DestPriorityMap[B.Dest.Y + y][B.Dest.X];
1754 for (; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++SrcPriorityPtr, ++DestPriorityPtr) {
1755 LOAD_SRC();
1756 if (SrcCol != B.MaskColor) {
1757 priority SrcPriority = *SrcPriorityPtr;
1758 priority DestPriority = *DestPriorityPtr;
1759 if ((SrcPriority & 0xF) >= (DestPriority & 0xF) || (SrcPriority & 0xF0) >= (DestPriority & 0xF0)) {
1760 NEW_LUMINATE_RED();
1761 NEW_LUMINATE_GREEN();
1762 NEW_LUMINATE_BLUE();
1763 STORE_COLOR();
1764 *DestPriorityPtr = SrcPriority;
1772 void bitmap::AlphaPriorityBlit (cblitdata &BlitData) const {
1773 if (!AlphaMap) {
1774 MaskedPriorityBlit(BlitData);
1775 return;
1777 if (!PriorityMap || !BlitData.Bitmap->PriorityMap) {
1778 AlphaLuminanceBlit(BlitData);
1779 return;
1781 blitdata B = BlitData;
1782 if (!FastFlag) {
1783 if (!B.Border.X || !B.Border.Y) ABORT("Zero-sized bitmap alpha priority blit attempt detected!");
1784 if (!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, mSize.X, mSize.Y, B.Bitmap->mSize.X, B.Bitmap->mSize.Y)) return;
1785 } else {
1786 // but who cares?
1787 if (!femath::Clip(B.Src.X, B.Src.Y, B.Dest.X, B.Dest.Y, B.Border.X, B.Border.Y, mSize.X, mSize.Y, B.Bitmap->mSize.X, B.Bitmap->mSize.Y)) return;
1789 packcol16 **SrcImage = Image;
1790 packalpha **SrcAlphaMap = AlphaMap;
1791 packpriority **SrcPriorityMap = PriorityMap;
1792 packcol16 **DestImage = B.Bitmap->Image;
1793 packpriority **DestPriorityMap = B.Bitmap->PriorityMap;
1794 int NewRedLuminance = (B.Luminance >> 7 & 0x1F800) - 0x10000;
1795 int NewGreenLuminance = (B.Luminance >> 4 & 0xFE0) - 0x800;
1796 int NewBlueLuminance = (B.Luminance >> 2 & 0x3F) - 0x20;
1797 for (int y = 0; y < B.Border.Y; ++y) {
1798 cpackcol16 *SrcPtr = &SrcImage[B.Src.Y + y][B.Src.X];
1799 cpackalpha *AlphaPtr = &SrcAlphaMap[B.Src.Y + y][B.Src.X];
1800 cpackpriority *SrcPriorityPtr = &SrcPriorityMap[B.Src.Y + y][B.Src.X];
1801 cpackcol16 *EndPtr = SrcPtr + B.Border.X;
1802 packcol16 *DestPtr = &DestImage[B.Dest.Y + y][B.Dest.X];
1803 packpriority *DestPriorityPtr = &DestPriorityMap[B.Dest.Y + y][B.Dest.X];
1804 for (; SrcPtr != EndPtr; ++SrcPtr, ++DestPtr, ++AlphaPtr, ++SrcPriorityPtr, ++DestPriorityPtr) {
1805 LOAD_SRC();
1806 if (SrcCol != B.MaskColor) {
1807 priority SrcPriority = *SrcPriorityPtr;
1808 priority DestPriority = *DestPriorityPtr;
1809 if ((SrcPriority & 0xF) >= (DestPriority & 0xF) || (SrcPriority & 0xF0) >= (DestPriority & 0xF0)) {
1810 LOAD_DEST();
1811 LOAD_ALPHA();
1812 NEW_LUMINATE_RED();
1813 NEW_APPLY_ALPHA_RED();
1814 NEW_LUMINATE_GREEN();
1815 NEW_APPLY_ALPHA_GREEN();
1816 NEW_LUMINATE_BLUE();
1817 NEW_APPLY_ALPHA_BLUE();
1818 STORE_COLOR();
1819 *DestPriorityPtr = SrcPriority;
1827 void bitmap::InitPriorityMap (priority InitialValue) {
1828 if (!PriorityMap) Alloc2D(PriorityMap, mSize.Y, mSize.X);
1829 memset(PriorityMap[0], InitialValue, XSizeTimesYSize);
1833 void bitmap::FillPriority (priority Priority) {
1834 memset(PriorityMap[0], Priority, XSizeTimesYSize);
1838 void bitmap::FastBlitAndCopyAlpha (bitmap *Bitmap) const {
1839 if (!FastFlag) {
1840 if (!AlphaMap || !Bitmap->AlphaMap) ABORT("Attempt to fast blit and copy alpha without an alpha map detected!");
1841 if (mSize.X != Bitmap->mSize.X || mSize.Y != Bitmap->mSize.Y) ABORT("Fast blit and copy alpha attempt of noncongruent bitmaps detected!");
1843 memmove(Bitmap->Image[0], Image[0], XSizeTimesYSize * sizeof(packcol16));
1844 memmove(Bitmap->AlphaMap[0], AlphaMap[0], XSizeTimesYSize * sizeof(packalpha));
1848 void bitmap::UpdateRandMap (sLong Index, truth Value) {
1849 sLong c1 = XSizeTimesYSize + Index;
1850 RandMap[c1] = Value;
1851 for (sLong c2 = c1 >> 1; c2; c1 = c2, c2 >>= 1) {
1852 Value |= RandMap[c1 ^ 1];
1853 if (!RandMap[c2] != !Value) RandMap[c2] = Value; else return;
1858 void bitmap::InitRandMap () {
1859 if (!RandMap) RandMap = new truth[XSizeTimesYSize << 1];
1860 memset(RandMap, 0, (XSizeTimesYSize << 1) * sizeof(truth));
1864 v2 bitmap::RandomizePixel () const {
1865 if (!RandMap[1]) return ERROR_V2;
1866 sLong Rand = RAND();
1867 feuLong c, RandMask = 1;
1868 feuLong MapSize = XSizeTimesYSize << 1;
1869 for (c = 2; c < MapSize; c <<= 1) if (RandMap[c + 1] && (!RandMap[c] || Rand & (RandMask <<= 1))) ++c;
1870 c = (c - MapSize) >> 1;
1871 return v2(c % mSize.X, c / mSize.X);
1875 void bitmap::CalculateRandMap () {
1876 if (!AlphaMap) ABORT("Alpha map needed to calculate random map.");
1877 feuLong Size = XSizeTimesYSize;
1878 for (feuLong c = 0; c < Size; ++c) UpdateRandMap(c, AlphaMap[0][c]);
1882 void bitmap::AlphaPutPixel (int x, int y, col16 SrcCol, col24 Luminance, alpha Alpha) {
1883 if (x < 0 || y < 0 || x >= mSize.X || y >= mSize.Y) return;
1884 int DestCol = Image[y][x];
1885 int NewRedLuminance = (Luminance >> 7 & 0x1F800) - 0x10000;
1886 int NewGreenLuminance = (Luminance >> 4 & 0xFE0) - 0x800;
1887 int NewBlueLuminance = (Luminance >> 2 & 0x3F) - 0x20;
1888 NEW_LUMINATE_RED();
1889 NEW_APPLY_ALPHA_RED();
1890 NEW_LUMINATE_GREEN();
1891 NEW_APPLY_ALPHA_GREEN();
1892 NEW_LUMINATE_BLUE();
1893 NEW_APPLY_ALPHA_BLUE();
1894 Image[y][x] = Red|Green|Blue;
1898 alpha bitmap::CalculateAlphaAverage () const {
1899 if (!AlphaMap) ABORT("Alpha map needed to calculate alpha average!");
1900 sLong Alphas = 0;
1901 sLong AlphaSum = 0;
1902 feuLong Size = XSizeTimesYSize;
1903 for (feuLong c = 0; c < Size; ++c) {
1904 packalpha *AlphaPtr = &AlphaMap[0][c];
1905 if (*AlphaPtr) {
1906 AlphaSum += *AlphaPtr;
1907 ++Alphas;
1910 return Alphas ? AlphaSum / Alphas : 0;
1914 cachedfont::cachedfont (v2 aSize) : bitmap(aSize) {
1915 Alloc2D(MaskMap, mSize.Y, mSize.X);
1919 cachedfont::cachedfont (v2 aSize, col16 Color) : bitmap(aSize, Color) {
1920 Alloc2D(MaskMap, mSize.Y, mSize.X);
1924 void cachedfont::PrintCharacter (cblitdata B) const {
1925 if (B.Dest.X < 0 || B.Dest.Y < 0 || B.Dest.X+10 >= B.Bitmap->mSize.X || B.Dest.Y+9 >= B.Bitmap->mSize.Y) {
1926 NormalMaskedBlit(B);
1927 return;
1929 packcol16 **SrcLine = &Image[B.Src.Y];
1930 packcol16 **EndLine = SrcLine+9;
1931 packcol16 **SrcMaskLine = &MaskMap[B.Src.Y];
1932 packcol16 **DestLine = &B.Bitmap->Image[B.Dest.Y];
1933 for (; SrcLine != EndLine; ++SrcLine, ++SrcMaskLine, ++DestLine) {
1934 culong *FontPtr = reinterpret_cast<culong *>(*SrcLine + B.Src.X);
1935 culong *EndPtr = FontPtr+5;
1936 culong *MaskPtr = reinterpret_cast<culong *>(*SrcMaskLine + B.Src.X);
1937 feuLong *DestPtr = reinterpret_cast<feuLong *>(*DestLine + B.Dest.X);
1938 for (; FontPtr != EndPtr; ++DestPtr, ++MaskPtr, ++FontPtr) *DestPtr = (*DestPtr & *MaskPtr) | *FontPtr;
1943 void cachedfont::CreateMaskMap () {
1944 packcol16 *SrcPtr = Image[0];
1945 packcol16 *EndPtr = SrcPtr+XSizeTimesYSize;
1946 packcol16 *MaskPtr = MaskMap[0];
1947 for (; SrcPtr != EndPtr; ++SrcPtr, ++MaskPtr) {
1948 if (*SrcPtr == TRANSPARENT_COLOR) {
1949 *SrcPtr = 0;
1950 *MaskPtr = 0xFFFF;
1951 } else {
1952 *MaskPtr = 0;
1958 static const cint WaveDelta[] = { 1, 2, 2, 2, 1, 0, -1, -2, -2, -2, -1 };
1960 void bitmap::Wobble (int Frame, int SpeedShift, truth Horizontally) {
1961 int WavePos = (Frame << SpeedShift >> 1) - 14;
1962 if (Horizontally) {
1963 for (int c = 0; c < 11; ++c) if (WavePos + c >= 0 && WavePos + c < mSize.Y) MoveLineHorizontally(WavePos + c, WaveDelta[c]);
1964 } else {
1965 for (int c = 0; c < 11; ++c) if (WavePos + c >= 0 && WavePos + c < mSize.X) MoveLineVertically(WavePos + c, WaveDelta[c]);
1970 void bitmap::MoveLineVertically (int X, int Delta) {
1971 int y;
1972 if (Delta < 0) {
1973 for (y = 0; y < mSize.Y + Delta; ++y)
1974 PowerPutPixel(X, y, GetPixel(X, y - Delta), AlphaMap ? GetAlpha(X, y - Delta) : 255, AVERAGE_PRIORITY);
1975 for (int y = -1; y >= Delta; --y)
1976 PowerPutPixel(X, mSize.Y + y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY);
1977 } else if (Delta > 0) {
1978 for (y = mSize.Y - 1; y >= Delta; --y)
1979 PowerPutPixel(X, y, GetPixel(X, y - Delta), AlphaMap ? GetAlpha(X, y - Delta) : 255, AVERAGE_PRIORITY);
1980 for (y = 0; y < Delta; ++y)
1981 PowerPutPixel(X, y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY);
1986 void bitmap::MoveLineHorizontally (int Y, int Delta) {
1987 int x;
1988 if (Delta < 0) {
1989 for (x = 0; x < mSize.X + Delta; ++x)
1990 PowerPutPixel(x, Y, GetPixel(x - Delta, Y), AlphaMap ? GetAlpha(x - Delta, Y) : 255, AVERAGE_PRIORITY);
1991 for (x = -1; x >= Delta; --x)
1992 PowerPutPixel(mSize.X + x, Y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY);
1993 } else if (Delta > 0) {
1994 for (x = mSize.X - 1; x >= Delta; --x)
1995 PowerPutPixel(x, Y, GetPixel(x - Delta, Y), AlphaMap ? GetAlpha(x - Delta, Y) : 255, AVERAGE_PRIORITY);
1996 for (x = 0; x < Delta; ++x)
1997 PowerPutPixel(x, Y, TRANSPARENT_COLOR, 255, AVERAGE_PRIORITY);
2002 void bitmap::InterLace () {
2003 for (int y = 0; y < mSize.Y; ++y) {
2004 if (!(y % 3)) for (int x = 0; x < mSize.X; ++x) if (Image[y][x] != 0) Image[y][x] = 1;