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