1 //---------------------------------------------------------------------------
2 #include "stdafx.h"
3
4 #include <cassert>
5 #include <cmath> // n.b., needed on Linux at least
6
7 //#define TIMING
8
9 #ifdef TIMING
10 #include <ctime> // for performance testing
11 #endif
12
13 #include <algorithm>
14 using std::min;
15 using std::max;
16
17 #include "image.h"
18 #include "utils.h"
19 //---------------------------------------------------------------------------
20
CreateMask(Uint32 & rmask,Uint32 & gmask,Uint32 & bmask,Uint32 & amask)21 inline void CreateMask( Uint32& rmask, Uint32& gmask, Uint32& bmask, Uint32& amask) {
22 #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
23 rmask = 0xff000000;
24 gmask = 0x00ff0000;
25 bmask = 0x0000ff00;
26 amask = 0x000000ff;
27 #elif defined(__APPLE__) && defined(__MACH__)
28 rmask = 0x00ff0000;
29 gmask = 0x0000ff00;
30 bmask = 0x000000ff;
31 amask = 0xff000000;
32 #else
33 rmask = 0x000000ff;
34 gmask = 0x0000ff00;
35 bmask = 0x00ff0000;
36 amask = 0xff000000;
37 // TEST:
38 /*rmask = 0x00ff0000;
39 gmask = 0x0000ff00;
40 bmask = 0x000000ff;
41 amask = 0xff000000;*/
42 #endif
43 }
44
45 extern const bool DEBUG;
46 extern const int DEBUGLEVEL;
47
48 using namespace Gigalomania;
49
50 #if SDL_MAJOR_VERSION == 1
51 SDL_Surface *Image::dest_surf = NULL;
52 #else
53 SDL_Renderer *Image::sdlRenderer = NULL;
54 #endif
55
Image()56 Image::Image() {
57 this->data = NULL;
58 this->need_to_free_data = false;
59 this->surface = NULL;
60 #if SDL_MAJOR_VERSION == 1
61 #else
62 this->texture = NULL;
63 #endif
64 this->scale_x = 1;
65 this->scale_y = 1;
66 this->offset_x = 0;
67 this->offset_y = 0;
68 }
69
~Image()70 Image::~Image() {
71 free();
72 }
73
free()74 void Image::free() {
75 if( this->surface != NULL ) {
76 SDL_FreeSurface(this->surface);
77 this->surface = NULL;
78 }
79 #if SDL_MAJOR_VERSION == 1
80 #else
81 if( this->texture != NULL ) {
82 SDL_DestroyTexture(this->texture);
83 this->texture = NULL;
84 }
85 #endif
86 if( need_to_free_data && this->data != NULL ) {
87 delete [] this->data;
88 this->data = NULL;
89 }
90 }
91
draw(int x,int y) const92 void Image::draw(int x, int y) const {
93 x += offset_x;
94 y += offset_y;
95 x = (int)(x * scale_x);
96 y = (int)(y * scale_y);
97 #if SDL_MAJOR_VERSION == 1
98 SDL_Rect srcrect;
99 srcrect.x = 0;
100 srcrect.y = 0;
101 srcrect.w = (short)this->getWidth();
102 srcrect.h = (short)this->getHeight();
103 SDL_Rect dstrect;
104 dstrect.x = (short)x;
105 dstrect.y = (short)y;
106 dstrect.w = 0;
107 dstrect.h = 0;
108 SDL_BlitSurface(surface, &srcrect, dest_surf, &dstrect);
109 #else
110 SDL_Rect dstrect;
111 dstrect.x = (short)x;
112 dstrect.y = (short)y;
113 dstrect.w = (short)this->getWidth();
114 dstrect.h = (short)this->getHeight();
115 SDL_RenderCopy(sdlRenderer, texture, NULL, &dstrect);
116 #endif
117 }
118
draw(int x,int y,int sw,int sh) const119 void Image::draw(int x, int y, int sw, int sh) const {
120 x += offset_x;
121 y += offset_y;
122 x = (int)(x * scale_x);
123 y = (int)(y * scale_y);
124 sw = (int)(sw * scale_x);
125 sh = (int)(sh * scale_y);
126 #if SDL_MAJOR_VERSION == 1
127 SDL_Rect srcrect;
128 srcrect.x = 0;
129 srcrect.y = 0;
130 srcrect.w = sw;
131 srcrect.h = sh;
132 SDL_Rect dstrect;
133 dstrect.x = (short)x;
134 dstrect.y = (short)y;
135 dstrect.w = 0;
136 dstrect.h = 0;
137 SDL_BlitSurface(surface, &srcrect, dest_surf, &dstrect);
138 #else
139 SDL_Rect srcrect;
140 srcrect.x = 0;
141 srcrect.y = 0;
142 srcrect.w = sw;
143 srcrect.h = sh;
144 SDL_Rect dstrect;
145 dstrect.x = (short)x;
146 dstrect.y = (short)y;
147 dstrect.w = sw;
148 dstrect.h = sh;
149 SDL_RenderCopy(sdlRenderer, texture, &srcrect, &dstrect);
150 #endif
151 }
152
draw(int x,int y,float scale_w,float scale_h) const153 void Image::draw(int x, int y, float scale_w, float scale_h) const {
154 x = (int)(x * scale_x);
155 y = (int)(y * scale_y);
156 #if SDL_MAJOR_VERSION == 1
157 // scaling only supported for SDL 2
158 if( scale_w == 1.0f && scale_h == 1.0f ) {
159 SDL_Rect srcrect;
160 srcrect.x = 0;
161 srcrect.y = 0;
162 srcrect.w = (short)this->getWidth();
163 srcrect.h = (short)this->getHeight();
164 SDL_Rect dstrect;
165 dstrect.x = (short)x;
166 dstrect.y = (short)y;
167 dstrect.w = 0;
168 dstrect.h = 0;
169 SDL_BlitSurface(surface, &srcrect, dest_surf, &dstrect);
170 }
171 #else
172 SDL_Rect dstrect;
173 dstrect.x = (short)x;
174 dstrect.y = (short)y;
175 dstrect.w = (short)(this->getWidth()*scale_w);
176 dstrect.h = (short)(this->getHeight()*scale_h);
177 SDL_RenderCopy(sdlRenderer, texture, NULL, &dstrect);
178 #endif
179 }
180
drawWithAlpha(int x,int y,unsigned char alpha) const181 void Image::drawWithAlpha(int x, int y, unsigned char alpha) const {
182 // n.b., only works if the image doesn't have per-pixel alpha channel
183 #if SDL_MAJOR_VERSION == 1
184 SDL_SetAlpha(this->surface, SDL_SRCALPHA|SDL_RLEACCEL, alpha);
185 #else
186 SDL_SetTextureAlphaMod(texture, alpha);
187 #endif
188 this->draw(x, y);
189 }
190
getWidth() const191 int Image::getWidth() const {
192 return this->surface->w;
193 }
194
getHeight() const195 int Image::getHeight() const {
196 return this->surface->h;
197 }
198
setScale(float scale_x,float scale_y)199 void Image::setScale(float scale_x,float scale_y) {
200 this->scale_x = scale_x;
201 this->scale_y = scale_y;
202 }
203
isPaletted() const204 bool Image::isPaletted() const {
205 return this->surface->format->palette != NULL;
206 }
207
getNColors() const208 int Image::getNColors() const {
209 return this->surface->format->palette->ncolors;
210 }
211
getPixelIndex(int x,int y) const212 unsigned char Image::getPixelIndex(int x,int y) const {
213 if( !isPaletted() )
214 return 0;
215
216 SDL_LockSurface(this->surface);
217 unsigned char c = ((unsigned char *)this->surface->pixels)[ y * this->surface->pitch + x ];
218 SDL_UnlockSurface(this->surface);
219 return c;
220 }
221
setPixelIndex(int x,int y,unsigned char c)222 bool Image::setPixelIndex(int x,int y,unsigned char c) {
223 if( !isPaletted() )
224 return false;
225
226 SDL_LockSurface(this->surface);
227 ((unsigned char *)this->surface->pixels)[ y * this->surface->pitch + x ] = c;
228 SDL_UnlockSurface(this->surface);
229 return true;
230 }
231
setColor(int index,unsigned char r,unsigned char g,unsigned char b)232 bool Image::setColor(int index,unsigned char r,unsigned char g,unsigned char b) {
233 if( !isPaletted() )
234 return false;
235
236 SDL_LockSurface(this->surface);
237 SDL_Color *col = &this->surface->format->palette->colors[index];
238 col->r = r;
239 col->g = g;
240 col->b = b;
241 SDL_UnlockSurface(this->surface);
242 return true;
243 }
244
245 /*
246 * Return the pixel value at (x, y)
247 * NOTE: The surface must be locked before calling this!
248 */
getpixel(SDL_Surface * surface,int x,int y)249 Uint32 getpixel(SDL_Surface *surface, int x, int y) {
250 int bpp = surface->format->BytesPerPixel;
251 /* Here p is the address to the pixel we want to retrieve */
252 Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
253
254 switch(bpp) {
255 case 1:
256 return *p;
257
258 case 2:
259 return *(Uint16 *)p;
260
261 case 3:
262 if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
263 return p[0] << 16 | p[1] << 8 | p[2];
264 else
265 return p[0] | p[1] << 8 | p[2] << 16;
266
267 case 4:
268 return *(Uint32 *)p;
269
270 default:
271 return 0; /* shouldn't happen, but avoids warnings */
272 }
273 }
274
275 /*
276 * Set the pixel at (x, y) to the given value
277 * NOTE: The surface must be locked before calling this!
278 */
putpixel(SDL_Surface * surface,int x,int y,Uint32 pixel)279 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel) {
280 int bpp = surface->format->BytesPerPixel;
281 /* Here p is the address to the pixel we want to set */
282 Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
283
284 switch(bpp) {
285 case 1:
286 *p = pixel;
287 break;
288
289 case 2:
290 *(Uint16 *)p = pixel;
291 break;
292
293 case 3:
294 if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
295 p[0] = (pixel >> 16) & 0xff;
296 p[1] = (pixel >> 8) & 0xff;
297 p[2] = pixel & 0xff;
298 } else {
299 p[0] = pixel & 0xff;
300 p[1] = (pixel >> 8) & 0xff;
301 p[2] = (pixel >> 16) & 0xff;
302 }
303 break;
304
305 case 4:
306 *(Uint32 *)p = pixel;
307 break;
308 }
309 }
310
311 // Creates an alpha from the mask; also adds in shadow effect based on supplied ar/ag/ab colour
createAlphaForColor(bool mask,unsigned char mr,unsigned char mg,unsigned char mb,unsigned char ar,unsigned char ag,unsigned char ab,unsigned char alpha)312 bool Image::createAlphaForColor(bool mask, unsigned char mr, unsigned char mg, unsigned char mb, unsigned char ar, unsigned char ag, unsigned char ab, unsigned char alpha) {
313 int w = this->getWidth();
314 int h = this->getHeight();
315
316 this->convertToHiColor(true);
317
318 #ifdef TIMING
319 int time_s = clock();
320 #endif
321
322 SDL_LockSurface(this->surface);
323 // faster to read in x direction! (caching?)
324 for(int cy=0;cy<h;cy++) {
325 for(int cx=0;cx<w;cx++) {
326 Uint32 pixel = getpixel(this->surface, cx, cy);
327 Uint8 r = 0, g = 0, b = 0, a = 0;
328 SDL_GetRGB(pixel, this->surface->format, &r, &g, &b);
329 if( r == ar && g == ag && b == ab ) {
330 r = 0;
331 g = 0;
332 b = 0;
333 a = alpha;
334 pixel = SDL_MapRGBA(this->surface->format, r, g, b, a);
335 putpixel(this->surface, cx, cy, pixel);
336 }
337 else if( mask && r == mr && g == mg && b == mb ) {
338 r = 0;
339 g = 0;
340 b = 0;
341 a = 0;
342 pixel = SDL_MapRGBA(this->surface->format, r, g, b, a);
343 putpixel(this->surface, cx, cy, pixel);
344 }
345 }
346 }
347
348 SDL_UnlockSurface(this->surface);
349 #ifdef TIMING
350 int time_taken = clock() - time_s;
351 LOG(" image createAlphaForColor time %d\n", time_taken);
352 static int total = 0;
353 total += time_taken;
354 LOG(" image createAlphaForColor total %d\n", total);
355 #endif
356 return true;
357 }
358
scaleAlpha(float scale)359 void Image::scaleAlpha(float scale) {
360 int bpp = this->surface->format->BitsPerPixel;
361 if( bpp != 32 )
362 return;
363
364 #ifdef TIMING
365 int time_s = clock();
366 #endif
367
368 int w = this->getWidth();
369 int h = this->getHeight();
370 SDL_LockSurface(this->surface);
371 // faster to read in x direction! (caching?)
372 for(int cy=0;cy<h;cy++) {
373 for(int cx=0;cx<w;cx++) {
374 Uint32 pixel = getpixel(this->surface, cx, cy);
375 Uint8 r = 0, g = 0, b = 0, a = 0;
376 SDL_GetRGBA(pixel, this->surface->format, &r, &g, &b, &a);
377 //a *= scale;
378 a = (Uint8)(a*scale);
379 pixel = SDL_MapRGBA(this->surface->format, r, g, b, a);
380 putpixel(this->surface, cx, cy, pixel);
381 }
382 }
383 SDL_UnlockSurface(this->surface);
384 #ifdef TIMING
385 int time_taken = clock() - time_s;
386 LOG(" image scaleAlpha time %d\n", time_taken);
387 static int total = 0;
388 total += time_taken;
389 LOG(" image scaleAlpha total %d\n", total);
390 #endif
391 }
392
convertToHiColor(bool alpha)393 bool Image::convertToHiColor(bool alpha) {
394 #ifdef TIMING
395 int time_s = clock();
396 #endif
397 // todo: repeated conversions don't seem to work? seems to be due to repeated blitting not working
398 int bpp = this->surface->format->BitsPerPixel;
399 if( bpp == 32 )
400 return false;
401 Uint32 rmask, gmask, bmask, amask;
402 CreateMask(rmask, gmask, bmask, amask);
403
404 int depth = alpha ? 32 : 24;
405 SDL_Surface *new_surf = SDL_CreateRGBSurface(0, this->getWidth(), this->getHeight(), depth, rmask, gmask, bmask, amask);
406 SDL_BlitSurface(this->surface, NULL, new_surf, NULL);
407 free();
408 this->surface = new_surf;
409 this->need_to_free_data = false;
410
411 #ifdef TIMING
412 int time_taken = clock() - time_s;
413 LOG(" image convert time %d\n", time_taken);
414 static int total = 0;
415 total += time_taken;
416 LOG(" image convert total %d\n", total);
417 #endif
418 return true;
419 }
420
convertToDisplayFormat()421 bool Image::convertToDisplayFormat() {
422 #if SDL_MAJOR_VERSION == 1
423 SDL_Surface *new_surf = NULL;
424 int bpp = this->surface->format->BitsPerPixel;
425 if( bpp == 32 && this->surface->format->Amask != 0 )
426 new_surf = SDL_DisplayFormatAlpha(this->surface);
427 else
428 new_surf = SDL_DisplayFormat(this->surface);
429 SDL_FreeSurface(this->surface);
430 this->surface = new_surf;
431 #else
432 texture = SDL_CreateTextureFromSurface(sdlRenderer, surface);
433 if( texture == NULL ) {
434 LOG("SDL_CreateTextureFromSurface failed\n");
435 return false;
436 }
437 /*{
438 SDL_BlendMode blendMode = SDL_BLENDMODE_NONE;
439 SDL_GetTextureBlendMode(texture, &blendMode);
440 LOG("texture blend mode: %d\n", blendMode);
441 }*/
442 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); // seems to be default, but just in case
443 #endif
444 return true;
445 }
446
copyPalette(const Image * image)447 bool Image::copyPalette(const Image *image) {
448 if( this->surface->format->palette == NULL || image->surface->format->palette == NULL )
449 return false;
450
451 /*if( this->surface->format->palette->ncolors != image->surface->format->palette->ncolors )
452 return false;*/
453
454 #if SDL_MAJOR_VERSION == 1
455 SDL_SetColors(this->surface, image->surface->format->palette->colors, 0, this->surface->format->palette->ncolors);
456 #else
457 SDL_SetPaletteColors(this->surface->format->palette, image->surface->format->palette->colors, 0, this->surface->format->palette->ncolors);
458 #endif
459 return true;
460 }
461
462 // side-effect: also converts images with < 256 colours to have 256 colours, unless scaling is 1.0
scale(float sx,float sy)463 void Image::scale(float sx,float sy) {
464 if( sx == 1.0f && sy == 1.0f ) {
465 //LOG("no need to scale\n");
466 return;
467 }
468 //LOG("having to scale %f x %f\n", sx, sy);
469 // only supported for either reducing or englarging the size - this is all we need, and is easier to optimise for performance
470 bool enlarging = false;
471 if( sx > 1.0f || sy > 1.0f ) {
472 // making larger
473 ASSERT( sx > 1.0f );
474 ASSERT( sy > 1.0f );
475 enlarging = true;
476 }
477 #ifdef TIMING
478 int time_s = clock();
479 #endif
480 int w = this->getWidth();
481 int h = this->getHeight();
482 SDL_LockSurface(this->surface);
483 unsigned char *src_data = (unsigned char *)this->surface->pixels;
484 int bytesperpixel = this->surface->format->BytesPerPixel;
485 int new_width = (int)(w * sx);
486 int new_height = (int)(h * sy);
487 int new_size = (int)(new_width * new_height * bytesperpixel);
488 bool is_paletted = this->isPaletted();
489 unsigned char *new_data = NULL;
490 int *new_data_nonpaletted = NULL;
491 int *count = NULL;
492 if( is_paletted || enlarging ) {
493 new_data = new unsigned char[new_size];
494 }
495 else {
496 new_data_nonpaletted = new int[new_size];
497 count = new int[new_size];
498 for(int i=0;i<new_size;i++) {
499 new_data_nonpaletted[i] = 0;
500 count[i] = 0;
501 }
502 }
503 // faster to read in x direction! (caching?)
504 if( enlarging ) {
505 for(int cy=0;cy<h;cy++) {
506 int src_indx = cy * this->surface->pitch;
507 for(int cx=0;cx<w;cx++) {
508 for(int i=0;i<bytesperpixel;i++, src_indx++) {
509 T_ASSERT(src_indx >= 0 && src_indx < this->surface->pitch*h );
510 unsigned char pt = src_data[ src_indx ];
511 for(int y=0;y<sy;y++) {
512 for(int x=0;x<sx;x++) {
513 int dx = (int)(cx * sx + x);
514 int dy = (int)(cy * sy + y);
515 T_ASSERT( dx >= 0 && dx < new_width );
516 T_ASSERT( dy >= 0 && dy < new_height );
517 int dst_indx = (int)(dy * new_width * bytesperpixel + dx * bytesperpixel + i);
518 T_ASSERT(dst_indx >= 0 && dst_indx < new_width*new_height*bytesperpixel );
519 new_data[ dst_indx ] = pt;
520 }
521 }
522 }
523 }
524 }
525 }
526 else {
527 for(int cy=0;cy<h;cy++) {
528 int src_indx = cy * this->surface->pitch;
529 for(int cx=0;cx<w;cx++) {
530 for(int i=0;i<bytesperpixel;i++, src_indx++) {
531 T_ASSERT(src_indx >= 0 && src_indx < this->surface->pitch*h );
532 unsigned char pt = src_data[ src_indx ];
533
534 int dx = (int)(cx * sx);
535 int dy = (int)(cy * sy);
536 if( dx >= new_width || dy >= new_height ) {
537 // ignore leftover part
538 continue;
539 }
540 T_ASSERT( dx >= 0 && dx < new_width );
541 T_ASSERT( dy >= 0 && dy < new_height );
542 int dst_indx = (int)(dy * new_width * bytesperpixel + dx * bytesperpixel + i);
543 T_ASSERT(dst_indx >= 0 && dst_indx < new_width*new_height*bytesperpixel );
544 if( is_paletted )
545 new_data[ dst_indx ] = pt;
546 else {
547 new_data_nonpaletted[ dst_indx ] += pt;
548 count[ dst_indx ]++;
549 }
550 }
551 }
552 }
553 }
554 SDL_UnlockSurface(this->surface);
555
556 {
557 w = (int)(w * sx);
558 h = (int)(h * sy);
559 int bpp = this->surface->format->BitsPerPixel;
560 int pitch = this->surface->format->BytesPerPixel * w;
561
562 Uint32 rmask = this->surface->format->Rmask;
563 Uint32 gmask = this->surface->format->Gmask;
564 Uint32 bmask = this->surface->format->Bmask;
565 Uint32 amask = this->surface->format->Amask;
566 if( !(is_paletted || enlarging) ) {
567 new_data = new unsigned char[new_size];
568 for(int i=0;i<new_size;i++) {
569 new_data[i] = (unsigned char)(new_data_nonpaletted[i] / count[i]);
570 }
571 delete [] new_data_nonpaletted;
572 new_data_nonpaletted = NULL;
573 delete [] count;
574 count = NULL;
575 }
576 SDL_Surface *new_surf = SDL_CreateRGBSurfaceFrom(new_data, w, h, bpp, pitch, rmask, gmask, bmask, amask);
577 if( this->surface->format->palette != NULL ) {
578 #if SDL_MAJOR_VERSION == 1
579 SDL_SetColors(new_surf, this->surface->format->palette->colors, 0, this->surface->format->palette->ncolors);
580 #else
581 SDL_SetPaletteColors(new_surf->format->palette, this->surface->format->palette->colors, 0, this->surface->format->palette->ncolors);
582 #endif
583 }
584 free();
585 this->surface = new_surf;
586 this->data = new_data;
587 this->need_to_free_data = true;
588 }
589
590 #ifdef TIMING
591 int time_taken = clock() - time_s;
592 LOG(" image scale time %d\n", time_taken);
593 static int total = 0;
594 total += time_taken;
595 LOG(" image scale total %d\n", total);
596 #endif
597 }
598
scaleTo(int n_w)599 bool Image::scaleTo(int n_w) {
600 float scale_factor = ((float)n_w) / (float)this->getWidth();
601 this->scale(scale_factor, scale_factor); // nb, still scale if scale_factor==1, as this is a way of converting to 8bit
602 return true;
603 }
604
remap(unsigned char sr,unsigned char sg,unsigned char sb,unsigned char rr,unsigned char rg,unsigned char rb)605 void Image::remap(unsigned char sr,unsigned char sg,unsigned char sb,unsigned char rr,unsigned char rg,unsigned char rb) {
606 if( this->surface->format->BitsPerPixel != 24 && this->surface->format->BitsPerPixel != 32 ) {
607 return;
608 }
609 if( rr == sr && rg == sg && rb == sb ) {
610 return;
611 }
612 #ifdef TIMING
613 int time_s = clock();
614 #endif
615 SDL_LockSurface(this->surface);
616 int w = getWidth();
617 int h = getHeight();
618 /*int isr = (int)sr;
619 int isg = (int)sg;
620 int isb = (int)sb;
621 int mag_s = (int)sqrt( (float)(isr*isr + isg*isg + isb*isb) ); // *255
622 const int threshold = 0.9f * mag_s; // *255
623 int irr = (int)rr;
624 int irg = (int)rg;
625 int irb = (int)rb;
626 int mag_r = (int)sqrt( (float)(irr*irr + irg*irg + irb*irb) ); // *255*/
627 // faster to read in x direction! (caching?)
628 for(int y=0;y<h;y++) {
629 for(int x=0;x<w;x++) {
630 Uint32 pixel = getpixel(this->surface, x, y);
631 Uint8 r = 0, g = 0, b = 0, a = 0;
632 SDL_GetRGBA(pixel, this->surface->format, &r, &g, &b, &a);
633 if( r == sr && g == sg && b == sb ) {
634 pixel = SDL_MapRGBA(surface->format, rr, rg, rb, a);
635 putpixel(this->surface, x, y, pixel);
636 }
637 /*if( r == 0 && g == 0 && b == 0 ) {
638 continue;
639 }
640 int ir = (int)r;
641 int ig = (int)g;
642 int ib = (int)b;
643 int mag = (int)sqrt( (float)(ir*ir + ig*ig + ib*ib) ); // *255
644 int dot = ( sr*ir + sg*ig + sb*ib ); // *255*255
645 if( dot >= threshold*mag ) {
646 float cos_angle = ((float)dot) / (float)( mag_s * mag ); // *1
647 int proj_mag = mag * cos_angle; // *255
648 ir = proj_mag * rr / mag_r;
649 ig = proj_mag * rg / mag_r;
650 ib = proj_mag * rb / mag_r;
651 pixel = SDL_MapRGBA(surface->format, ir, ig, ib, a);
652 putpixel(this->surface, x, y, pixel);
653 }*/
654 }
655 }
656
657 SDL_UnlockSurface(this->surface);
658
659 #ifdef TIMING
660 int time_taken = clock() - time_s;
661 LOG(" image remap time %d\n", time_taken);
662 static int total = 0;
663 total += time_taken;
664 LOG(" image remap total %d\n", total);
665 #endif
666 }
667
reshadeRGB(int from,bool to_r,bool to_g,bool to_b)668 void Image::reshadeRGB(int from, bool to_r, bool to_g, bool to_b) {
669 ASSERT(from >= 0 && from < 3);
670 if( this->surface->format->BitsPerPixel != 24 && this->surface->format->BitsPerPixel != 32 ) {
671 return;
672 }
673 bool to[3] = {to_r, to_g, to_b};
674 SDL_LockSurface(this->surface);
675 int w = getWidth();
676 int h = getHeight();
677 // faster to read in x direction! (caching?)
678 for(int y=0;y<h;y++) {
679 for(int x=0;x<w;x++) {
680 Uint32 pixel = getpixel(this->surface, x, y);
681 Uint8 rgba[] = {0, 0, 0, 0};
682 SDL_GetRGBA(pixel, this->surface->format, &rgba[0], &rgba[1], &rgba[2], &rgba[3]);
683 int val = rgba[from];
684 int t_diff = 0;
685 int n = 0;
686 for(int j=0;j<3;j++) {
687 if( to[j] && j != from ) {
688 int val2 = rgba[j];
689 int diff = val2 - val;
690 t_diff += diff;
691 n++;
692 rgba[j] = val;
693 }
694 }
695 if( n > 0 && !to[from] ) {
696 t_diff /= n;
697 val += t_diff;
698 ASSERT(val >=0 && val < 256);
699 rgba[from] = val;
700 }
701 pixel = SDL_MapRGBA(surface->format, rgba[0], rgba[1], rgba[2], rgba[3]);
702 putpixel(this->surface, x, y, pixel);
703 }
704 }
705
706 SDL_UnlockSurface(this->surface);
707 }
708
brighten(float sr,float sg,float sb)709 void Image::brighten(float sr, float sg, float sb) {
710 if( this->surface->format->BitsPerPixel != 24 && this->surface->format->BitsPerPixel != 32 ) {
711 return;
712 }
713 #ifdef TIMING
714 int time_s = clock();
715 #endif
716 float scale[3] = {sr, sg, sb};
717 SDL_LockSurface(this->surface);
718 int w = getWidth();
719 int h = getHeight();
720 // faster to read in x direction! (caching?)
721 for(int y=0;y<h;y++) {
722 for(int x=0;x<w;x++) {
723 Uint32 pixel = getpixel(this->surface, x, y);
724 Uint8 rgba[] = {0, 0, 0, 0};
725 SDL_GetRGBA(pixel, this->surface->format, &rgba[0], &rgba[1], &rgba[2], &rgba[3]);
726 for(int j=0;j<3;j++) {
727 float col = (float)rgba[j];
728 col *= scale[j];
729 if( col < 0 )
730 col = 0;
731 else if( col > 255 )
732 col = 255;
733 rgba[j] = (unsigned char)col;
734 }
735 pixel = SDL_MapRGBA(surface->format, rgba[0], rgba[1], rgba[2], rgba[3]);
736 putpixel(this->surface, x, y, pixel);
737 }
738 }
739 SDL_UnlockSurface(this->surface);
740 #ifdef TIMING
741 int time_taken = clock() - time_s;
742 LOG(" image brighten time %d\n", time_taken);
743 static int total = 0;
744 total += time_taken;
745 LOG(" image brighten total %d\n", total);
746 #endif
747 }
748
fadeAlpha(bool x_dir,bool fwd)749 void Image::fadeAlpha(bool x_dir, bool fwd) {
750 if( this->surface->format->BitsPerPixel != 24 && this->surface->format->BitsPerPixel != 32 ) {
751 return;
752 }
753 #ifdef TIMING
754 int time_s = clock();
755 #endif
756 SDL_LockSurface(this->surface);
757 int w = getWidth();
758 int h = getHeight();
759 // faster to read in x direction! (caching?)
760 for(int y=0;y<h;y++) {
761 for(int x=0;x<w;x++) {
762 Uint32 pixel = getpixel(this->surface, x, y);
763 Uint8 rgba[] = {0, 0, 0, 0};
764 SDL_GetRGBA(pixel, this->surface->format, &rgba[0], &rgba[1], &rgba[2], &rgba[3]);
765 float frac = 0.0f;
766 //float perp_frac = 0.0f;
767 if( x_dir ) {
768 frac = ((float)x)/(float)(w-1.0f);
769 //perp_frac = ((float)y)/(float)(h-1.0f);
770 }
771 else {
772 frac = ((float)y)/(float)(h-1.0f);
773 //perp_frac = ((float)x)/(float)(w-1.0f);
774 }
775 if( !fwd ) {
776 frac = 1.0f - frac;
777 }
778 float cutoff = 0.75f;
779 float alpha = 0.0f;
780 if( frac >= cutoff ) {
781 alpha = (frac-cutoff) / (1.0f-cutoff);
782 }
783 rgba[3] = (Uint8)(rgba[3] * alpha);
784 pixel = SDL_MapRGBA(surface->format, rgba[0], rgba[1], rgba[2], rgba[3]);
785 putpixel(this->surface, x, y, pixel);
786 }
787 }
788 SDL_UnlockSurface(this->surface);
789 #ifdef TIMING
790 int time_taken = clock() - time_s;
791 LOG(" image linearFade time %d\n", time_taken);
792 static int total = 0;
793 total += time_taken;
794 LOG(" image linearFade total %d\n", total);
795 #endif
796 }
797
798 #if SDL_MAJOR_VERSION == 1
799 // If using SDL2, should just blit the rectangle directly with Screen::fillRect() or Screen::fillRectWithAlpha().
800 // We need this for SDL1.2, as there Screen::fillRectWithAlpha() isn't supported (due to SDL_fillRect not supporting alpha blending in SDL 1.2)
fillRect(int x,int y,int w,int h,unsigned char r,unsigned char g,unsigned char b)801 void Image::fillRect(int x, int y, int w, int h, unsigned char r, unsigned char g, unsigned char b) {
802 int col = SDL_MapRGB(this->surface->format, r, g, b);
803 SDL_Rect rect;
804 rect.x = x;
805 rect.y = y;
806 rect.w = w;
807 rect.h = h;
808 SDL_FillRect(this->surface, &rect, col);
809 }
810 #endif
811
copy(int x,int y,int w,int h) const812 Image *Image::copy(int x, int y, int w, int h) const {
813 //LOG("Image::copy(%d,%d,%d,%d)\n",x,y,w,h);
814 #ifdef TIMING
815 int time_s = clock();
816 #endif
817
818 x = (int)(x * scale_x);
819 y = (int)(y * scale_y);
820 w = (int)(w * scale_x);
821 h = (int)(h * scale_y);
822
823 Image *copy_image = NULL;
824 {
825 SDL_Surface *surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, this->surface->format->BitsPerPixel, this->surface->format->Rmask, this->surface->format->Gmask, this->surface->format->Bmask, this->surface->format->Amask);
826 copy_image = new Image();
827 copy_image->surface = surface;
828 copy_image->data = (unsigned char *)copy_image->surface->pixels;
829 copy_image->need_to_free_data = false;
830 }
831
832 copy_image->copyPalette(this); // must be called before SDL_BlitSurface
833
834 SDL_Rect src_rect;
835 src_rect.x = x;
836 src_rect.y = y;
837 src_rect.w = w;
838 src_rect.h = h;
839 #if SDL_MAJOR_VERSION == 1
840 SDL_SetAlpha(this->surface, 0, SDL_ALPHA_OPAQUE);
841 #else
842 SDL_SetSurfaceBlendMode(this->surface, SDL_BLENDMODE_NONE);
843 #endif
844 if( SDL_BlitSurface(this->surface, &src_rect, copy_image->surface, NULL) < 0 ) {
845 LOG("SDL_BlitSurface failed: %s\n", SDL_GetError());
846 }
847 /*unsigned char *src_data = (unsigned char *)this->surface->pixels;
848 unsigned char *dst_data = (unsigned char *)copy_image->surface->pixels;
849 SDL_LockSurface(this->surface);
850 int bytesperpixel = this->surface->format->BytesPerPixel;
851 // faster to read in x direction! (caching?)
852 for(int cy=0;cy<h;cy++) {
853 for(int cx=0;cx<w;cx++) {
854 for(int i=0;i<bytesperpixel;i++) {
855 int src_indx = ( y + cy ) * this->surface->pitch + ( x + cx ) * bytesperpixel + i;
856 int dst_indx = cy * copy_image->surface->pitch + cx * bytesperpixel + i;
857 ASSERT( src_indx >= 0 && src_indx < this->surface->pitch * this->surface->h * bytesperpixel );
858 ASSERT( dst_indx >= 0 && dst_indx < copy_image->surface->pitch * copy_image->surface->h * copy_image->surface->format->BytesPerPixel );
859 //if( dst_data[ dst_indx ] != src_data[ src_indx ] ) {
860 // LOG("not equal: %d, %d, %d: %d vs %d\n", cx, cy, i, src_data[ src_indx ], dst_data[ dst_indx ]);
861 //}
862 dst_data[ dst_indx ] = src_data[ src_indx ];
863 }
864 }
865 }
866 SDL_UnlockSurface(this->surface);*/
867
868 copy_image->scale_x = scale_x;
869 copy_image->scale_y = scale_y;
870
871 #ifdef TIMING
872 int time_taken = clock() - time_s;
873 LOG(" image copy time %d\n", time_taken);
874 static int total = 0;
875 total += time_taken;
876 LOG(" image copy total %d\n", total);
877 #endif
878
879 return copy_image;
880 }
881
882 // The following code is taken from IMG_lbm.c from SDL_image 2,
883 // under the following licence.
884
885 /*
886 SDL_image: An example image loading library for use with SDL
887 Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
888
889 This software is provided 'as-is', without any express or implied
890 warranty. In no event will the authors be held liable for any damages
891 arising from the use of this software.
892
893 Permission is granted to anyone to use this software for any purpose,
894 including commercial applications, and to alter it and redistribute it
895 freely, subject to the following restrictions:
896
897 1. The origin of this software must not be misrepresented; you must not
898 claim that you wrote the original software. If you use this software
899 in a product, an acknowledgment in the product documentation would be
900 appreciated but is not required.
901 2. Altered source versions must be plainly marked as such, and must not be
902 misrepresented as being the original software.
903 3. This notice may not be removed or altered from any source distribution.
904 */
905
906
907 #define MAXCOLORS 256
908
909 /* Structure for an IFF picture ( BMHD = Bitmap Header ) */
910
911 typedef struct
912 {
913 Uint16 w, h; /* width & height of the bitmap in pixels */
914 Sint16 x, y; /* screen coordinates of the bitmap */
915 Uint8 planes; /* number of planes of the bitmap */
916 Uint8 mask; /* mask type ( 0 => no mask ) */
917 Uint8 tcomp; /* compression type */
918 Uint8 pad1; /* dummy value, for padding */
919 Uint16 tcolor; /* transparent color */
920 Uint8 xAspect, /* pixel aspect ratio */
921 yAspect;
922 Sint16 Lpage; /* width of the screen in pixels */
923 Sint16 Hpage; /* height of the screen in pixels */
924 } BMHD;
925
my_IMG_LoadLBM_RW(SDL_RWops * src)926 SDL_Surface *my_IMG_LoadLBM_RW( SDL_RWops *src )
927 {
928 Sint64 start;
929 SDL_Surface *Image;
930 Uint8 id[4], pbm, colormap[MAXCOLORS*3], *MiniBuf, *ptr, count, color, msk;
931 Uint32 size, bytesloaded, nbcolors;
932 Uint32 i, j, bytesperline, nbplanes, stencil, plane, h;
933 Uint32 remainingbytes;
934 Uint32 width;
935 BMHD bmhd;
936 char const *error;
937 Uint8 flagHAM,flagEHB;
938
939 Image = NULL;
940 error = NULL;
941 MiniBuf = NULL;
942
943 if ( !src ) {
944 /* The error message has been set in SDL_RWFromFile */
945 return NULL;
946 }
947 start = SDL_RWtell(src);
948
949 if ( !SDL_RWread( src, id, 4, 1 ) )
950 {
951 error="error reading IFF chunk";
952 goto done;
953 }
954
955 /* Should be the size of the file minus 4+4 ( 'FORM'+size ) */
956 if ( !SDL_RWread( src, &size, 4, 1 ) )
957 {
958 error="error reading IFF chunk size";
959 goto done;
960 }
961
962 /* As size is not used here, no need to swap it */
963
964 if ( SDL_memcmp( id, "FORM", 4 ) != 0 )
965 {
966 error="not a IFF file";
967 goto done;
968 }
969
970 if ( !SDL_RWread( src, id, 4, 1 ) )
971 {
972 error="error reading IFF chunk";
973 goto done;
974 }
975
976 pbm = 0;
977
978 /* File format : PBM=Packed Bitmap, ILBM=Interleaved Bitmap */
979 if ( !SDL_memcmp( id, "PBM ", 4 ) ) pbm = 1;
980 else if ( SDL_memcmp( id, "ILBM", 4 ) )
981 {
982 error="not a IFF picture";
983 goto done;
984 }
985
986 nbcolors = 0;
987
988 SDL_memset( &bmhd, 0, sizeof( BMHD ) );
989 flagHAM = 0;
990 flagEHB = 0;
991
992 while ( SDL_memcmp( id, "BODY", 4 ) != 0 )
993 {
994 if ( !SDL_RWread( src, id, 4, 1 ) )
995 {
996 error="error reading IFF chunk";
997 goto done;
998 }
999
1000 if ( !SDL_RWread( src, &size, 4, 1 ) )
1001 {
1002 error="error reading IFF chunk size";
1003 goto done;
1004 }
1005
1006 bytesloaded = 0;
1007
1008 size = SDL_SwapBE32( size );
1009
1010 if ( !SDL_memcmp( id, "BMHD", 4 ) ) /* Bitmap header */
1011 {
1012 if ( !SDL_RWread( src, &bmhd, sizeof( BMHD ), 1 ) )
1013 {
1014 error="error reading BMHD chunk";
1015 goto done;
1016 }
1017
1018 bytesloaded = sizeof( BMHD );
1019
1020 bmhd.w = SDL_SwapBE16( bmhd.w );
1021 bmhd.h = SDL_SwapBE16( bmhd.h );
1022 bmhd.x = SDL_SwapBE16( bmhd.x );
1023 bmhd.y = SDL_SwapBE16( bmhd.y );
1024 bmhd.tcolor = SDL_SwapBE16( bmhd.tcolor );
1025 bmhd.Lpage = SDL_SwapBE16( bmhd.Lpage );
1026 bmhd.Hpage = SDL_SwapBE16( bmhd.Hpage );
1027 }
1028
1029 if ( !SDL_memcmp( id, "CMAP", 4 ) ) /* palette ( Color Map ) */
1030 {
1031 if ( !SDL_RWread( src, &colormap, size, 1 ) )
1032 {
1033 error="error reading CMAP chunk";
1034 goto done;
1035 }
1036
1037 bytesloaded = size;
1038 nbcolors = size / 3;
1039 }
1040
1041 if ( !SDL_memcmp( id, "CAMG", 4 ) ) /* Amiga ViewMode */
1042 {
1043 Uint32 viewmodes;
1044 if ( !SDL_RWread( src, &viewmodes, sizeof(viewmodes), 1 ) )
1045 {
1046 error="error reading CAMG chunk";
1047 goto done;
1048 }
1049
1050 bytesloaded = size;
1051 viewmodes = SDL_SwapBE32( viewmodes );
1052 if ( viewmodes & 0x0800 )
1053 flagHAM = 1;
1054 if ( viewmodes & 0x0080 )
1055 flagEHB = 1;
1056 }
1057
1058 if ( SDL_memcmp( id, "BODY", 4 ) )
1059 {
1060 if ( size & 1 ) ++size; /* padding ! */
1061 size -= bytesloaded;
1062 /* skip the remaining bytes of this chunk */
1063 if ( size ) SDL_RWseek( src, size, RW_SEEK_CUR );
1064 }
1065 }
1066
1067 /* compute some usefull values, based on the bitmap header */
1068
1069 width = ( bmhd.w + 15 ) & 0xFFFFFFF0; /* Width in pixels modulo 16 */
1070
1071 bytesperline = ( ( bmhd.w + 15 ) / 16 ) * 2;
1072
1073 nbplanes = bmhd.planes;
1074
1075 if ( pbm ) /* File format : 'Packed Bitmap' */
1076 {
1077 bytesperline *= 8;
1078 nbplanes = 1;
1079 }
1080
1081 stencil = (bmhd.mask & 1); /* There is a mask ( 'stencil' ) */
1082
1083 /* Allocate memory for a temporary buffer ( used for
1084 decompression/deinterleaving ) */
1085
1086 MiniBuf = (Uint8 *)SDL_malloc( bytesperline * (nbplanes + stencil) );
1087 if ( MiniBuf == NULL )
1088 {
1089 error="no enough memory for temporary buffer";
1090 goto done;
1091 }
1092
1093 if ( ( Image = SDL_CreateRGBSurface( SDL_SWSURFACE, width, bmhd.h, (bmhd.planes==24 || flagHAM==1)?24:8, 0, 0, 0, 0 ) ) == NULL )
1094 goto done;
1095
1096 if ( bmhd.mask & 2 ) /* There is a transparent color */
1097 SDL_SetColorKey( Image, SDL_TRUE, bmhd.tcolor );
1098
1099 /* Update palette informations */
1100
1101 /* There is no palette in 24 bits ILBM file */
1102 if ( nbcolors>0 && flagHAM==0 )
1103 {
1104 /* FIXME: Should this include the stencil? See comment below */
1105 int nbrcolorsfinal = 1 << (nbplanes + stencil);
1106 ptr = &colormap[0];
1107
1108 for ( i=0; i<nbcolors; i++ )
1109 {
1110 Image->format->palette->colors[i].r = *ptr++;
1111 Image->format->palette->colors[i].g = *ptr++;
1112 Image->format->palette->colors[i].b = *ptr++;
1113 }
1114
1115 /* Amiga EHB mode (Extra-Half-Bright) */
1116 /* 6 bitplanes mode with a 32 colors palette */
1117 /* The 32 last colors are the same but divided by 2 */
1118 /* Some Amiga pictures save 64 colors with 32 last wrong colors, */
1119 /* they shouldn't !, and here we overwrite these 32 bad colors. */
1120 if ( (nbcolors==32 || flagEHB ) && (1<<bmhd.planes)==64 )
1121 {
1122 nbcolors = 64;
1123 ptr = &colormap[0];
1124 for ( i=32; i<64; i++ )
1125 {
1126 Image->format->palette->colors[i].r = (*ptr++)/2;
1127 Image->format->palette->colors[i].g = (*ptr++)/2;
1128 Image->format->palette->colors[i].b = (*ptr++)/2;
1129 }
1130 }
1131
1132 /* If nbcolors < 2^nbplanes, repeat the colormap */
1133 /* This happens when pictures have a stencil mask */
1134 if ( nbrcolorsfinal > (1<<bmhd.planes) ) {
1135 nbrcolorsfinal = (1<<bmhd.planes);
1136 }
1137 for ( i=nbcolors; i < (Uint32)nbrcolorsfinal; i++ )
1138 {
1139 Image->format->palette->colors[i].r = Image->format->palette->colors[i%nbcolors].r;
1140 Image->format->palette->colors[i].g = Image->format->palette->colors[i%nbcolors].g;
1141 Image->format->palette->colors[i].b = Image->format->palette->colors[i%nbcolors].b;
1142 }
1143 if ( !pbm )
1144 Image->format->palette->ncolors = nbrcolorsfinal;
1145 }
1146
1147 /* Get the bitmap */
1148
1149 for ( h=0; h < bmhd.h; h++ )
1150 {
1151 /* uncompress the datas of each planes */
1152
1153 for ( plane=0; plane < (nbplanes+stencil); plane++ )
1154 {
1155 ptr = MiniBuf + ( plane * bytesperline );
1156
1157 remainingbytes = bytesperline;
1158
1159 if ( bmhd.tcomp == 1 ) /* Datas are compressed */
1160 {
1161 do
1162 {
1163 if ( !SDL_RWread( src, &count, 1, 1 ) )
1164 {
1165 error="error reading BODY chunk";
1166 goto done;
1167 }
1168
1169 if ( count & 0x80 )
1170 {
1171 count ^= 0xFF;
1172 count += 2; /* now it */
1173
1174 if ( ( count > remainingbytes ) || !SDL_RWread( src, &color, 1, 1 ) )
1175 {
1176 error="error reading BODY chunk";
1177 goto done;
1178 }
1179 //ptrdiff_t diff = ptr - MiniBuf;
1180 //SDL_memset( ptr, color, count );
1181 memset( ptr, color, count );
1182 }
1183 else
1184 {
1185 ++count;
1186
1187 if ( ( count > remainingbytes ) || !SDL_RWread( src, ptr, count, 1 ) )
1188 {
1189 error="error reading BODY chunk";
1190 goto done;
1191 }
1192 }
1193
1194 ptr += count;
1195 remainingbytes -= count;
1196
1197 } while ( remainingbytes > 0 );
1198 }
1199 else
1200 {
1201 if ( !SDL_RWread( src, ptr, bytesperline, 1 ) )
1202 {
1203 error="error reading BODY chunk";
1204 goto done;
1205 }
1206 }
1207 }
1208
1209 /* One line has been read, store it ! */
1210
1211 ptr = (Uint8 *)Image->pixels;
1212 if ( nbplanes==24 || flagHAM==1 )
1213 ptr += h * width * 3;
1214 else
1215 ptr += h * width;
1216
1217 if ( pbm ) /* File format : 'Packed Bitmap' */
1218 {
1219 SDL_memcpy( ptr, MiniBuf, width );
1220 }
1221 else /* We have to un-interlace the bits ! */
1222 {
1223 if ( nbplanes!=24 && flagHAM==0 )
1224 {
1225 size = ( width + 7 ) / 8;
1226
1227 for ( i=0; i < size; i++ )
1228 {
1229 memset( ptr, 0, 8 );
1230
1231 for ( plane=0; plane < (nbplanes + stencil); plane++ )
1232 {
1233 color = *( MiniBuf + i + ( plane * bytesperline ) );
1234 msk = 0x80;
1235
1236 for ( j=0; j<8; j++ )
1237 {
1238 if ( ( plane + j ) <= 7 ) ptr[j] |= (Uint8)( color & msk ) >> ( 7 - plane - j );
1239 else ptr[j] |= (Uint8)( color & msk ) << ( plane + j - 7 );
1240
1241 msk >>= 1;
1242 }
1243 }
1244 ptr += 8;
1245 }
1246 }
1247 else
1248 {
1249 Uint32 finalcolor = 0;
1250 size = ( width + 7 ) / 8;
1251 /* 24 bitplanes ILBM : R0...R7,G0...G7,B0...B7 */
1252 /* or HAM (6 bitplanes) or HAM8 (8 bitplanes) modes */
1253 for ( i=0; i<width; i=i+8 )
1254 {
1255 Uint8 maskBit = 0x80;
1256 for ( j=0; j<8; j++ )
1257 {
1258 Uint32 pixelcolor = 0;
1259 Uint32 maskColor = 1;
1260 Uint8 dataBody;
1261 for ( plane=0; plane < nbplanes; plane++ )
1262 {
1263 dataBody = MiniBuf[ plane*size+i/8 ];
1264 if ( dataBody&maskBit )
1265 pixelcolor = pixelcolor | maskColor;
1266 maskColor = maskColor<<1;
1267 }
1268 /* HAM : 12 bits RGB image (4 bits per color component) */
1269 /* HAM8 : 18 bits RGB image (6 bits per color component) */
1270 if ( flagHAM )
1271 {
1272 switch( pixelcolor>>(nbplanes-2) )
1273 {
1274 case 0: /* take direct color from palette */
1275 finalcolor = colormap[ pixelcolor*3 ] + (colormap[ pixelcolor*3+1 ]<<8) + (colormap[ pixelcolor*3+2 ]<<16);
1276 break;
1277 case 1: /* modify only blue component */
1278 finalcolor = finalcolor&0x00FFFF;
1279 finalcolor = finalcolor | (pixelcolor<<(16+(10-nbplanes)));
1280 break;
1281 case 2: /* modify only red component */
1282 finalcolor = finalcolor&0xFFFF00;
1283 finalcolor = finalcolor | pixelcolor<<(10-nbplanes);
1284 break;
1285 case 3: /* modify only green component */
1286 finalcolor = finalcolor&0xFF00FF;
1287 finalcolor = finalcolor | (pixelcolor<<(8+(10-nbplanes)));
1288 break;
1289 }
1290 }
1291 else
1292 {
1293 finalcolor = pixelcolor;
1294 }
1295 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1296 *ptr++ = (Uint8)(finalcolor>>16);
1297 *ptr++ = (Uint8)(finalcolor>>8);
1298 *ptr++ = (Uint8)(finalcolor);
1299 #else
1300 *ptr++ = (Uint8)(finalcolor);
1301 *ptr++ = (Uint8)(finalcolor>>8);
1302 *ptr++ = (Uint8)(finalcolor>>16);
1303 #endif
1304 maskBit = maskBit>>1;
1305 }
1306 }
1307 }
1308 }
1309 }
1310
1311 done:
1312
1313 if ( MiniBuf ) SDL_free( MiniBuf );
1314
1315 if ( error )
1316 {
1317 SDL_RWseek(src, start, RW_SEEK_SET);
1318 if ( Image ) {
1319 SDL_FreeSurface( Image );
1320 Image = NULL;
1321 }
1322 IMG_SetError( error );
1323 }
1324
1325 return( Image );
1326 }
1327
1328 // end of code taken from IMG_lbm.c from SDL_image 2
1329
loadImage(const char * filename)1330 Image *Image::loadImage(const char *filename) {
1331 #ifdef TIMING
1332 int time_s = clock();
1333 #endif
1334 //LOG("Image::loadImage(\"%s\")\n",filename); // disabled logging to improve performance on startup
1335 SDL_RWops *src = SDL_RWFromFile(filename, "rb");
1336 if( src == NULL ) {
1337 LOG("SDL_RWFromFile failed: %s\n", SDL_GetError());
1338 return NULL;
1339 }
1340 Image *image = new Image();
1341 #if SDL_MAJOR_VERSION == 1
1342 #else
1343 // workaround IMG_Load_RW crashes on SDL_memset when loading IFF files?!
1344 if( strstr(filename, ".") == NULL ) {
1345 LOG("load as IFF, using local function\n");
1346 image->surface = my_IMG_LoadLBM_RW(src);
1347 }
1348 #endif
1349 if( image->surface == NULL ) {
1350 image->surface = IMG_Load_RW(src, 1);
1351 }
1352 if( image->surface == NULL ) {
1353 LOG("IMG_Load_RW failed: %s\n", IMG_GetError());
1354 delete image;
1355 image = NULL;
1356 }
1357 #ifdef TIMING
1358 int time_taken = clock() - time_s;
1359 LOG(" image load time %d\n", time_taken);
1360 static int total = 0;
1361 total += time_taken;
1362 LOG(" image load total %d\n", total);
1363 #endif
1364
1365 // Workaround: every image is loaded as 32 bit, but on <32 bit images, the alpha mask is not set
1366 // so we have to set it manually for transparency to work:
1367 #if defined(__APPLE__) && defined(__MACH__)
1368 if (image->surface->format->BitsPerPixel == 32 && image->surface->format->Amask == 0)
1369 {
1370 Uint32 rmask, gmask, bmask, amask;
1371 CreateMask(rmask, gmask, bmask, amask);
1372 image->surface->format->Amask = amask;
1373 }
1374 #endif
1375
1376 return image;
1377 }
1378
createBlankImage(int width,int height,int bpp)1379 Image *Image::createBlankImage(int width,int height, int bpp) {
1380 Uint32 rmask, gmask, bmask, amask;
1381 CreateMask(rmask, gmask, bmask, amask);
1382
1383 SDL_Surface *surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, bpp, rmask, gmask, bmask, amask);
1384
1385 Image *image = new Image();
1386 image->surface = surface;
1387 image->data = (unsigned char *)image->surface->pixels;
1388 image->need_to_free_data = false;
1389
1390 return image;
1391 }
1392
createNoise(int w,int h,float scale_u,float scale_v,const unsigned char filter_max[3],const unsigned char filter_min[3],NOISEMODE_t noisemode,int n_iterations)1393 Image *Image::createNoise(int w,int h,float scale_u,float scale_v,const unsigned char filter_max[3],const unsigned char filter_min[3],NOISEMODE_t noisemode,int n_iterations) {
1394 Image *image = Image::createBlankImage(w, h, 32);
1395 SDL_LockSurface(image->surface);
1396 float fvec[2] = {0.0f, 0.0f};
1397 for(int y=0;y<h;y++) {
1398 fvec[0] = scale_v * ((float)y) / ((float)h - 1.0f);
1399 for(int x=0;x<w;x++) {
1400 fvec[1] = scale_u * ((float)x) / ((float)w - 1.0f);
1401 float h = 0.0f;
1402 float max_val = 0.0f;
1403 float mult = 1.0f;
1404 for(int j=0;j<n_iterations;j++,mult*=2.0f) {
1405 float this_fvec[2];
1406 this_fvec[0] = fvec[0] * mult;
1407 this_fvec[1] = fvec[1] * mult;
1408 float this_h = perlin_noise2(this_fvec) / mult;
1409 if( noisemode == NOISEMODE_PATCHY || noisemode == NOISEMODE_MARBLE )
1410 this_h = abs(this_h);
1411 h += this_h;
1412 max_val += 1.0f / mult;
1413 }
1414 if( noisemode == NOISEMODE_PATCHY ) {
1415 h /= max_val;
1416 }
1417 else if( noisemode == NOISEMODE_MARBLE ) {
1418 h = sin(scale_u * ((float)x) / ((float)w - 1.0f) + h);
1419 h = 0.5f + 0.5f * h;
1420 }
1421 else {
1422 h /= max_val;
1423 h = 0.5f + 0.5f * h;
1424 }
1425
1426 if( noisemode == NOISEMODE_CLOUDS ) {
1427 //const float offset = 0.4f;
1428 //const float offset = 0.3f;
1429 const float offset = 0.2f;
1430 h = offset - h * h;
1431 h = max(h, 0.0f);
1432 h /= offset;
1433 }
1434 // h is now in range [0, 1]
1435 if( h < 0.0 || h > 1.0 ) {
1436 LOG("h value is out of bounds\n");
1437 ASSERT(false);
1438 }
1439 if( noisemode == NOISEMODE_WOOD ) {
1440 h = 20 * h;
1441 h = h - floor(h);
1442 }
1443 Uint8 r = (Uint8)((filter_max[0] - filter_min[0]) * h + filter_min[0]);
1444 Uint8 g = (Uint8)((filter_max[1] - filter_min[1]) * h + filter_min[1]);
1445 Uint8 b = (Uint8)((filter_max[2] - filter_min[2]) * h + filter_min[2]);
1446 Uint8 a = 255;
1447 Uint32 pixel = SDL_MapRGBA(image->surface->format, r, g, b, a);
1448 putpixel(image->surface, x, y, pixel);
1449 }
1450 }
1451 SDL_UnlockSurface(image->surface);
1452
1453 return image;
1454 }
1455
createRadial(int w,int h,float alpha_scale)1456 Image * Image::createRadial(int w,int h,float alpha_scale) {
1457 return createRadial(w, h, alpha_scale, 255, 255, 255);
1458 }
1459
createRadial(int w,int h,float alpha_scale,Uint8 r,Uint8 g,Uint8 b)1460 Image * Image::createRadial(int w,int h,float alpha_scale, Uint8 r, Uint8 g, Uint8 b) {
1461 Image *image = Image::createBlankImage(w, h, 32);
1462 SDL_LockSurface(image->surface);
1463 int radius = min(w/2, h/2);
1464 for(int y=0;y<h;y++) {
1465 int dy = abs(y - h/2);
1466 for(int x=0;x<w;x++) {
1467 int dx = abs(x - w/2);
1468 float dist = sqrt((float)(dx*dx + dy*dy));
1469 dist /= (float)radius;
1470 if( dist >= 1.0f )
1471 dist = 1.0f;
1472 dist = 1.0f - dist;
1473 dist *= alpha_scale;
1474 unsigned char v = (int)(255.0f * dist);
1475 Uint8 a = v;
1476 Uint32 pixel = SDL_MapRGBA(image->surface->format, r, g, b, a);
1477 putpixel(image->surface, x, y, pixel);
1478 }
1479 }
1480
1481 SDL_UnlockSurface(image->surface);
1482 return image;
1483 }
1484
1485 #if SDL_MAJOR_VERSION == 1
setGraphicsOutput(SDL_Surface * dest_surf)1486 void Image::setGraphicsOutput(SDL_Surface *dest_surf) {
1487 Image::dest_surf = dest_surf;
1488 }
1489 #else
setGraphicsOutput(SDL_Renderer * sdlRenderer)1490 void Image::setGraphicsOutput(SDL_Renderer *sdlRenderer) {
1491 Image::sdlRenderer = sdlRenderer;
1492 }
1493 #endif
1494
writeNumbers(int x,int y,Image * images[10],int number,Justify justify)1495 void Image::writeNumbers(int x,int y,Image *images[10],int number,Justify justify) {
1496 char buffer[16] = "";
1497 sprintf(buffer,"%d",number);
1498 int len = strlen(buffer);
1499 int w = images[0]->getScaledWidth();
1500 int sx = 0;
1501 if( justify == JUSTIFY_LEFT )
1502 sx = x;
1503 else if( justify == JUSTIFY_CENTRE )
1504 sx = x - ( w * len ) / 2;
1505 else if( justify == JUSTIFY_RIGHT )
1506 sx = x - w * len;
1507
1508 for(int i=0;i<len;i++) {
1509 images[ buffer[i] - '0' ]->draw(sx, y);
1510 sx += w;
1511 }
1512 }
1513
write(int x,int y,Image * images[n_font_chars_c],const char * text,Justify justify)1514 void Image::write(int x,int y,Image *images[n_font_chars_c],const char *text,Justify justify) {
1515 writeMixedCase(x, y, images, images, NULL, text, justify);
1516 }
1517
writeMixedCase(int x,int y,Image * large[n_font_chars_c],Image * little[n_font_chars_c],Image * numbers[10],const char * text,Justify justify)1518 void Image::writeMixedCase(int x,int y,Image *large[n_font_chars_c],Image *little[n_font_chars_c],Image *numbers[10],const char *text,Justify justify) {
1519 int len = strlen(text);
1520 int n_lines = 0;
1521 int s_w = little[0]->getScaledWidth();
1522 int l_w = large[0]->getScaledWidth();
1523 int max_wid = 0;
1524 textLines(&n_lines, &max_wid, text, s_w, l_w);
1525 int n_h = 0;
1526 if( numbers != NULL )
1527 n_h = numbers[0]->getScaledHeight();
1528 int s_h = little[0]->getScaledHeight();
1529 int l_h = large[0]->getScaledHeight();
1530 int sx = 0;
1531 if( justify == JUSTIFY_LEFT )
1532 sx = x;
1533 else if( justify == JUSTIFY_CENTRE )
1534 sx = x - max_wid / 2;
1535 else if( justify == JUSTIFY_RIGHT )
1536 sx = x - max_wid;
1537 int cx = sx;
1538
1539 for(int i=0;i<len;i++) {
1540 char ch = text[i];
1541 bool was_large = false;
1542 if( numbers == NULL && ch == '0' ) {
1543 ch = 'O'; // hack for 0 (we don't spell it O, due to alphabetical ordering)
1544 }
1545 if( ch == '\n' ) {
1546 // newline
1547 cx = sx;
1548 y += l_h + 2;
1549 continue; // don't increase cx
1550 }
1551 else if( isspace( ch ) )
1552 ; // do nothing
1553 else if( ch >= '0' && ch <= '9' ) {
1554 ASSERT( numbers != NULL );
1555 int indx = ch - '0';
1556 numbers[indx]->draw(cx, y + l_h - n_h);
1557 }
1558 else if( isupper( ch ) ) {
1559 int indx = ch - 'A';
1560 large[indx]->draw(cx, y);
1561 was_large = true;
1562 }
1563 else if( islower( ch ) ) {
1564 little[ ch - 'a' ]->draw(cx, y + l_h - s_h);
1565 }
1566 else if( ch == '.' ) {
1567 if( little[font_index_period_c] != NULL )
1568 little[font_index_period_c]->draw(cx, y + l_h - s_h);
1569 else if( large[font_index_period_c] != NULL )
1570 large[font_index_period_c]->draw(cx, y);
1571 }
1572 else if( ch == ',' ) {
1573 if( little[font_index_comma_c] != NULL )
1574 little[font_index_comma_c]->draw(cx, y + l_h - s_h);
1575 else if( large[font_index_comma_c] != NULL )
1576 large[font_index_comma_c]->draw(cx, y);
1577 }
1578 else if( ch == '\'' ) {
1579 if( little[font_index_apostrophe_c] != NULL )
1580 little[font_index_apostrophe_c]->draw(cx, y + l_h - s_h);
1581 else if( large[font_index_apostrophe_c] != NULL )
1582 large[font_index_apostrophe_c]->draw(cx, y);
1583 }
1584 else if( ch == '!' ) {
1585 if( little[font_index_exclamation_c] != NULL )
1586 little[font_index_exclamation_c]->draw(cx, y + l_h - s_h);
1587 else if( large[font_index_exclamation_c] != NULL )
1588 large[font_index_exclamation_c]->draw(cx, y);
1589 }
1590 else if( ch == '?' ) {
1591 if( little[font_index_question_c] != NULL )
1592 little[font_index_question_c]->draw(cx, y + l_h - s_h);
1593 else if( large[font_index_question_c] != NULL )
1594 large[font_index_question_c]->draw(cx, y);
1595 }
1596 else if( ch == '-' ) {
1597 if( little[font_index_dash_c] != NULL )
1598 little[font_index_dash_c]->draw(cx, y + l_h - s_h);
1599 else if( large[font_index_dash_c] != NULL )
1600 large[font_index_dash_c]->draw(cx, y);
1601 }
1602 else {
1603 continue; // don't increase cx
1604 }
1605 cx += was_large ? l_w : s_w;
1606 }
1607 }
1608
smooth()1609 void Image::smooth() {
1610 if( this->surface->format->BitsPerPixel != 24 && this->surface->format->BitsPerPixel != 32 ) {
1611 return;
1612 }
1613 int w = getWidth();
1614 int h = getHeight();
1615 unsigned char *src_data = (unsigned char *)this->surface->pixels;
1616 int bytesperpixel = this->surface->format->BytesPerPixel;
1617 int pitch = this->surface->pitch;
1618 unsigned char *new_data = new unsigned char[w * h * bytesperpixel];
1619
1620 SDL_LockSurface(this->surface);
1621 for(int y=0;y<h;y++) {
1622 for(int x=0;x<w;x++) {
1623 for(int i=0;i<bytesperpixel;i++) {
1624 Uint32 col = 0;
1625 if( x > 0 && x < w-1 && y > 0 && y < h-1 ) {
1626 Uint32 sq[9];
1627 int indx = 0;
1628 for(int sx=x-1;sx<=x+1;sx++) {
1629 for(int sy=y-1;sy<=y+1;sy++) {
1630 sq[indx++] = src_data[ sy * pitch + sx * bytesperpixel + i ];
1631 }
1632 }
1633 col = ( sq[0] + 2 * sq[1] + sq[2] + 2 * sq[3] + 4 * sq[4] + 2 * sq[5] + sq[6] + 2 * sq[7] + sq[8] ) / 16;
1634 //col = ( sq[1] + sq[3] + sq[5] + sq[7] + 12 * sq[4] ) / 16;
1635 }
1636 else
1637 col = src_data[ y * pitch + x * bytesperpixel + i ];
1638 new_data[ y * w * bytesperpixel + x * bytesperpixel + i] = (unsigned char)col;
1639 }
1640 }
1641 }
1642 for(int y=0;y<h;y++) {
1643 for(int x=0;x<w;x++) {
1644 for(int i=0;i<bytesperpixel;i++) {
1645 src_data[ y * pitch + x * bytesperpixel + i ] = new_data[ y * w * bytesperpixel + x * bytesperpixel + i];
1646 }
1647 }
1648 }
1649 delete [] new_data;
1650 SDL_UnlockSurface(this->surface);
1651 }
1652