1 
2 /*
3  *  picture-object...
4  *  Copyright (c) 2002-2006 by Mattias Hultgren <mattias_hultgren@tele2.se>
5  *
6  *  See picture.h
7  */
8 
9 /*
10 v16  2006-07-28
11 ---
12 
13 	Replaced all throw_... function calls (as they no longer exists) with THROW_ERROR
14 
15 v15  2006-04-22
16 ---
17 
18 	Picture's destructor is now virtual
19 
20 v14  2005-09-15 - 2005-10-23
21 ---
22 
23 	Removed the support for delayed picture copying
24 	Added function draw_lines (which draws multiple lines with or without antialiasing)
25 	Rewrote filled_circle to use Bresenham's algorithm
26 	Added support for antialiasing in circle, filled_circle & ellipse
27 	Added operator==()
28 	Removed PictureType_YCrCb, PictureType_PALETTE as they wasn't supported anyway
29 
30 v1.0.12  2005-09-09 - 2005-09-11
31 -------
32 
33 	Removed class textpicture, as it wasn't used and I will build a vector-based replacer
34 	Rewrote circle to use Bresenham's algorithm
35 
36 v1.0.11  2005-08-08 - 2005-08-18
37 -------
38 
39 	Replaced all asin(1.0) with M_PI similarities
40 	Added picture::line_antialiasing()
41 
42 v1.0.10  2005-02-03 - 2005-02-04
43 -------
44 
45 	Added support for background pictures (ie pictures without solidness-buffer)
46 	Added support for delayed picture copying
47 
48 v1.0.9  2004-10-03 & 2004-12-19
49 ------
50 
51 	Removed unused variables
52 	Small fixes in circle, filed_circle & ellipse
53 	save_ppm now uses bool for save_as_ascii-argument
54 	Fixed some compiler warnings
55 
56 v1.0.8  2004-07-14 - 2004-09-06
57 ------
58 
59 	Removed all appearance of Thrower
60 	Added copy constructor to picture
61 	Translation support completed
62 	Added picture::circle, picture::filled_circle, picture::ellipse
63 	Renamed a lot of functions
64 	Clear now also sets the solidness to 255 for the whole picture
65 
66 v1.0.7  2004-07-03 - 2004-07-13
67 ------
68 
69 	Added some missed error checking
70 	Replaced some throw-lines with Throw_functions, to enable easy translation
71 	Fixed a complete check and recalculation of points outside picture in line-function
72 	Merged file picture.line.inc.cpp with picture.cpp
73 	Added color::ORANGE
74 
75 v1.0.6  2004-06-17
76 ------
77 
78 	Replaced some const-groups with enum's, this changed the 'const'-names...
79 
80 v1.0.5  2004-04-13 - 2004-04-17
81 ------
82 
83 	Added code in line-function so that lines that are totally outside the picture is ignored
84 
85 v1.0.4  2003-12-18 - 2003-12-29 & 2004-03-20
86 ------
87 
88 	Small fix (2004-03-20)
89 	Pictures can be saved as PPM-files
90 	Added setpixel_grayscale (this function does nothing if the picture isn't grayscale)
91 
92 v1.0.3  2003-02-22
93 ------
94 
95 	Added mirror, upsidedown, rotate90, rotate180 and rotate270
96 	The picture type grayscale is supported
97 
98 v1.0.2  2003-02-04 - 2003-02-18
99 ------
100 
101 	textpicture now supports TEXT_RIGHT and TEXT_CENTER aligned text
102 	savepcx added to picture
103 	Added getwidth(),getheight() to textpicture
104 
105 v1.0.1  2003-01-27
106 ------
107 
108 	Greatly reduced time usage for drawing pictures partly or totaly outside dest
109 	A speedup for RGB to RGB-picture-drawing
110 	A small speedup for drawshadow-function
111 
112 v1.0.0  2002-12-14 - 2003-01-09
113 ------
114 
115 	Basic functions for the picture object
116 	PCX-files can be loaded
117 	Textpicture-object works
118 	The picture type RGB is supported
119 	The picture types YCrCb/grayscale/palette is not supported (but planned)
120 */
121 
122 #include "picture.h"
123 #include "keyfile.h"
124 #include <stdio.h>
125 #include <stdlib.h>
126 #include <memory.h>
127 #include <math.h>
128 
129 namespace picture_h
130 {
131 
132 picture *dest = 0;
133 
134 
operator ==(const colorrgb & color) const135 bool colorrgb::operator==(const colorrgb &color) const
136 {
137 	if( red == color.red  &&  green == color.green  &&  blue == color.blue )
138 		return true;
139 	return false;
140 }
141 
142 namespace color
143 {
144 	const colorrgb RED        = { 255,   0,   0 };
145 	const colorrgb GREEN      = {   0, 255,   0 };
146 	const colorrgb BLUE       = {   0,   0, 255 };
147 	const colorrgb DARK_RED   = { 170,   0,   0 };
148 	const colorrgb DARK_GREEN = {   0, 170,   0 };
149 	const colorrgb DARK_BLUE  = {   0,   0, 170 };
150 	const colorrgb YELLOW     = { 255, 255,   0 };
151 	const colorrgb WHITE      = { 255, 255, 255 };
152 	const colorrgb LIGHT_GRAY = { 170, 170, 170 };
153 	const colorrgb DARK_GRAY  = {  85,  85,  85 };
154 	const colorrgb BLACK      = {   0,   0,   0 };
155 	const colorrgb PURPLE     = { 165,   0, 255 };
156 	const colorrgb PINK       = { 255,   0, 229 };
157 	const colorrgb ORANGE     = { 255, 147,   0 };
158 }
159 
picture()160 picture::picture()
161 {
162 	width = 0;
163 	height = 0;
164 	type = PictureType_RGB;
165 
166 	isBackground = false;
167 
168 	data[0].buffer = 0;
169 	data[0].buffersize = 0;
170 	data[1].buffer = 0;
171 	data[1].buffersize = 0;
172 	data[2].buffer = 0;
173 	data[2].buffersize = 0;
174 	solidness.buffer = 0;
175 	solidness.buffersize = 0;
176 }
picture(const picture & src)177 picture::picture(const picture &src) throw(error_obj)
178 {
179 	width = 0;
180 	height = 0;
181 	type = PictureType_RGB;
182 
183 	isBackground = false;
184 
185 	data[0].buffer = 0;
186 	data[0].buffersize = 0;
187 	data[1].buffer = 0;
188 	data[1].buffersize = 0;
189 	data[2].buffer = 0;
190 	data[2].buffersize = 0;
191 	solidness.buffer = 0;
192 	solidness.buffersize = 0;
193 
194 	*this = src;
195 }
196 
~picture()197 picture::~picture()
198 {
199 	erase();
200 }
201 
set_isBackground(bool value)202 void picture::set_isBackground(bool value) throw(error_obj)
203 {
204 	if( value )
205 	{
206 		delete [] solidness.buffer;
207 		solidness.buffer = 0;
208 		solidness.buffersize = 0;
209 		isBackground = value;
210 	}
211 	else
212 	{
213 		if( solidness.buffersize < (width*height) )
214 		{
215 			delete [] solidness.buffer;
216 			solidness.buffer = 0;
217 			solidness.buffersize = 0;
218 			try
219 			{
220 				solidness.buffer = new uint8 [ width*height ];
221 				solidness.buffersize = width*height;
222 			}
223 			catch(...)
224 			{
225 				isBackground = true;
226 				THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );
227 			}
228 		}
229 		isBackground = value;
230 	}
231 }
232 
233 
234 //
235 //  erase - freeing all the memory used by picture-object
236 //
erase(void)237 void picture::erase(void)
238 {
239 	delete [] data[0].buffer;
240 	delete [] data[1].buffer;
241 	delete [] data[2].buffer;
242 	delete [] solidness.buffer;
243 
244 	width = 0;
245 	height = 0;
246 
247 	data[0].buffer = 0;
248 	data[0].buffersize = 0;
249 	data[1].buffer = 0;
250 	data[1].buffersize = 0;
251 	data[2].buffer = 0;
252 	data[2].buffersize = 0;
253 	solidness.buffer = 0;
254 	solidness.buffersize = 0;
255 }
256 
257 
258 
259 //
260 //  setsize - changes the size of the image
261 //    Note: this is NOT a resizer, the imagedata will be destroyed
262 //
set_size(int32 newwidth,int32 newheight)263 void picture::set_size(int32 newwidth,int32 newheight) throw(error_obj)
264 {
265 	int32 size,i;
266 
267 	if( newwidth < 0  ||  newheight < 0 )
268 	{
269 		THROW_ERROR( ErrorType_General, _("Invalid width and/or height.") );
270 	}
271 	else if( newwidth == 0  ||  newheight == 0 )
272 	{
273 		erase();
274 		return;
275 	}
276 	else if( newwidth == width  &&  newheight == height )
277 		return;
278 
279 	try
280 	{
281 		size = newwidth * newheight;
282 
283 		if( !isBackground )
284 		{
285 			if(solidness.buffersize < size)
286 			{
287 				delete [] solidness.buffer;
288 				solidness.buffer = 0;
289 				solidness.buffersize = 0;
290 
291 				solidness.buffer = new uint8 [size];
292 
293 				solidness.buffersize = size;
294 			}
295 		}
296 
297 		for(i=0;i<3;i++)
298 		{
299 			if( i != 0  &&  type == PictureType_GRAYSCALE )
300 				break;
301 
302 			if(data[i].buffersize < size)
303 			{
304 				delete [] data[i].buffer;
305 				data[i].buffer = 0;
306 				data[i].buffersize = 0;
307 
308 				data[i].buffer = new uint8 [size];
309 
310 				data[i].buffersize = size;
311 			}
312 		}
313 		width = newwidth;
314 		height = newheight;
315 	}
316 	catch(...)
317 	{
318 		erase();
319 		THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );
320 	}
321 }
322 
circle(int32 x,int32 y,uint32 r,const colorrgb & color,bool antialiased)323 void picture::circle(int32 x, int32 y, uint32 r, const colorrgb &color, bool antialiased )
324                 throw(error_obj)
325 {
326 	if( antialiased )
327 	{
328 		uint32 nroflines,i, *lines=0;
329 		double angle,dr,step,dx,dy,dcx,dcy;
330 		float *vertex=0;
331 
332 		dr = double(r);
333 		step = M_PI * dr / 2.0;
334 
335 		if(step > 100.0)
336 			nroflines = 100;
337 		else
338 			nroflines = uint32(step + 0.5);
339 		if( nroflines < 6 )
340 			nroflines = 6;
341 
342 		step = 2.0*M_PI / double(nroflines);
343 		angle = step;
344 
345 		try
346 		{
347 			dcx = double(x);
348 			dcy = double(y);
349 
350 			vertex = new float [nroflines*2+2];
351 			lines = new uint32 [nroflines*2];
352 
353 			vertex[0] = float(dcx+dr);
354 			vertex[1] = float(dcy);
355 
356 			for(i=0;i<nroflines;i++,angle+=step)
357 			{
358 				dx = cos(angle) * dr;
359 				dy = sin(angle) * dr;
360 
361 				vertex[i*2+2] = float(dx+dcx);
362 				vertex[i*2+3] = float(dy+dcy);
363 				lines[i*2] = i*2;
364 				lines[i*2+1] = i*2+2;
365 			}
366 			draw_lines( vertex, lines, nroflines, color, true );
367 		}
368 		catch(...)
369 		{
370 			delete [] vertex;
371 			delete [] lines;
372 			THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );
373 		}
374 		delete [] vertex;
375 		delete [] lines;
376 	}
377 	else
378 	{
379 // Using Bresenham's algorithm
380 		if( r != 0 )
381 		{
382 			int32 d, tx, ty;
383 
384 			tx = 0;
385 			ty = int32(r);
386 			d = 3 - 2*ty;
387 
388 			while(true)
389 			{
390 				set_pixel( x + tx, y + ty, color );
391 				set_pixel( x + tx, y - ty, color );
392 				set_pixel( x - tx, y + ty, color );
393 				set_pixel( x - tx, y - ty, color );
394 				set_pixel( x + ty, y + tx, color );
395 				set_pixel( x + ty, y - tx, color );
396 				set_pixel( x - ty, y + tx, color );
397 				set_pixel( x - ty, y - tx, color );
398 
399 				if( tx >= ty )
400 					break;
401 
402 				if( d < 0 )
403 					d += 4*tx + 6;
404 				else
405 				{
406 					d += 4*(tx - ty) + 10;
407 					ty--;
408 				}
409 				tx++;
410 			}
411 		}
412 	}
413 }
414 
ellipse(int32 x,int32 y,uint32 rx,uint32 ry,const colorrgb & color,bool antialiased)415 void picture::ellipse( int32 x, int32 y, uint32 rx, uint32 ry, const colorrgb &color,
416                        bool antialiased ) throw(error_obj)
417 {
418 	uint32 nroflines,i, *lines=0;
419 	int32 nx,ny,prevx,prevy;
420 	double angle,drx,dry,step,dx,dy,dcx,dcy;
421 	float *vertex=0;
422 
423 	drx = double(rx);
424 	dry = double(ry);
425 	step = M_PI * ( (drx > dry) ? drx : dry ) / 2.0;
426 
427 	if(step > 100.0)
428 		nroflines = 100;
429 	else
430 		nroflines = uint32(step + 0.5);
431 	if( nroflines < 6 )
432 		nroflines = 6;
433 
434 	step = 2.0*M_PI / double(nroflines);
435 	angle = step;
436 
437 	try
438 	{
439 		if( antialiased )
440 		{
441 			dcx = double(x);
442 			dcy = double(y);
443 
444 			vertex = new float [nroflines*2+2];
445 			lines = new uint32 [nroflines*2];
446 
447 			vertex[0] = float(dcx+drx);
448 			vertex[1] = float(dcy);
449 		}
450 		else
451 		{
452 			prevx = x + rx;
453 			prevy = y;
454 		}
455 
456 		for(i=0;i<nroflines;i++,angle+=step)
457 		{
458 			dx = cos(angle) * drx;
459 			dy = sin(angle) * dry;
460 
461 			if( antialiased )
462 			{
463 				vertex[i*2+2] = float(dx+dcx);
464 				vertex[i*2+3] = float(dy+dcy);
465 				lines[i*2] = i*2;
466 				lines[i*2+1] = i*2+2;
467 			}
468 			else
469 			{
470 				nx = x + int32(dx + 0.5);
471 				ny = y + int32(dy + 0.5);
472 				line(prevx,prevy,nx,ny,color);
473 
474 				prevx = nx;
475 				prevy = ny;
476 			}
477 		}
478 		if( antialiased )
479 			draw_lines( vertex, lines, nroflines, color, true );
480 	}
481 	catch(...)
482 	{
483 		delete [] vertex;
484 		delete [] lines;
485 		THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );
486 	}
487 	delete [] vertex;
488 	delete [] lines;
489 }
490 
filled_circle(int32 x,int32 y,uint32 r,const colorrgb & color,bool antialiased)491 void picture::filled_circle(int32 x, int32 y, uint32 r, const colorrgb &color, bool antialiased)
492                 throw(error_obj)
493 {
494 // Using Bresenham's algorithm
495 	if( r != 0 )
496 	{
497 		if( antialiased )
498 		{
499 			float cx, cy, fr = float(r), row, fx, fr2, t;
500 			cx = float(x) + 0.5f;
501 			cy = float(y) + 0.5f;
502 			fr2 = fr*fr;
503 			fx = fr;
504 
505 			for( row=0.0f; row<fx; row++ )
506 			{
507 				fx = sqrt( fr2 - row*row );
508 
509 				if( fx != trunc(fx) )
510 				{
511 					t = fx - trunc(fx);
512 					fx = trunc(fx);
513 
514 					set_pixel( int32(cx-fx)-1, y+int32(row), color, uint8( 255.0*t) );
515 					set_pixel( int32(cx-fx)-1, y-int32(row), color, uint8( 255.0*t) );
516 					set_pixel( y+int32(row), int32(cx-fx)-1, color, uint8( 255.0*t) );
517 					set_pixel( y-int32(row), int32(cx-fx)-1, color, uint8( 255.0*t) );
518 					if( row != 0.0f )
519 					{
520 						set_pixel( int32(cx+fx)+1, y+int32(row), color, uint8( 255.0*t) );
521 						set_pixel( int32(cx+fx)+1, y-int32(row), color, uint8( 255.0*t) );
522 						set_pixel( y+int32(row), int32(cx+fx)+1, color, uint8( 255.0*t) );
523 						set_pixel( y-int32(row), int32(cx+fx)+1, color, uint8( 255.0*t) );
524 					}
525 				}
526 
527 				line( int32(cx-fx), y+int32(row), int32(cx+fx), y+int32(row), color );
528 				line( int32(cx-fx), y-int32(row), int32(cx+fx), y-int32(row), color );
529 				line( y+int32(row), int32(cx-fx), y+int32(row), int32(cx+fx), color );
530 				line( y-int32(row), int32(cx-fx), y-int32(row), int32(cx+fx), color );
531 			}
532 		}
533 		else
534 		{
535 			int32 d, tx, ty, oldy;
536 
537 			tx = 0;
538 			ty = int32(r);
539 			d = 3 - 2*ty;
540 
541 			oldy = ty;
542 			while(true)
543 			{
544 				if( oldy != ty )
545 				{
546 					line( x - (tx-1), y + oldy, x + tx-1, y + oldy, color );
547 					line( x - (tx-1), y - oldy, x + tx-1, y - oldy, color );
548 					oldy = ty;
549 				}
550 				line( x - ty, y + tx, x + ty, y + tx, color );
551 				line( x - ty, y - tx, x + ty, y - tx, color );
552 
553 				if( tx >= ty )
554 					break;
555 
556 				if( d < 0 )
557 					d += 4*tx + 6;
558 				else
559 				{
560 					d += 4*(tx - ty) + 10;
561 					ty--;
562 				}
563 				tx++;
564 			}
565 		}
566 	}
567 }
568 
569 //
570 //  line - draws a line on the picture
571 //
line_antialiasing(float x1,float y1,float x2,float y2,const colorrgb & color,uint8 solid)572 void picture::line_antialiasing(float x1,float y1,float x2,float y2,const colorrgb &color,
573                                 uint8 solid) throw(error_obj)
574 {
575 	float dx, dy, gradient, xend, yend, gap, tmp, fheight, fwidth;
576 	int32 xpxl1, xpxl2, ypxl1, ypxl2;
577 
578 	if(y1 > y2) // makes sure that y1 is lower than y2
579 	{
580 		tmp = x1;
581 		x1 = x2;
582 		x2 = tmp;
583 		tmp = y1;
584 		y1 = y2;
585 		y2 = tmp;
586 	}
587 	fheight = float(height);
588 	fwidth = float(width);
589 
590 // picture_line_calc_p1
591 	if(y1 >= fheight)
592 		return; // whole line is below the picture
593 	if(y1 < 0.0f)
594 	{
595 		if(y2 < 0.0f)
596 			return; // whole line is over the picture
597 
598 		x1 = x1 - ( y1 * (x2 - x1) ) / (y2 - y1);
599 		y1 = 0.0f;
600 	}
601 	if(x1 < 0.0f)
602 	{
603 		if(x2 < 0.0f)
604 			return; // whole line is to the left of the picture
605 
606 		y1 = y1 - ( x1 * (y2 - y1) ) / (x2 - x1);
607 		x1 = 0.0f;
608 		if(y1 >= fheight)
609 			return; // the line is to the left and below of the picture
610 	}
611 	else if(x1 >= fwidth)
612 	{
613 		if(x2 >= fwidth)
614 			return; // the line is to the right...
615 
616 		y1 = y1 + ( (fwidth - 1.0f - x1) * (y2 - y1) ) / (x2 - x1);
617 		x1 = fwidth - 1.0f;
618 		if(y1 >= fheight)
619 			return; // the line is to the right and below...
620 	}
621 
622 
623 // picture_line_calc_p2
624 	if(y2 >= fheight)
625 	{
626 		x2 = x1 + ( (fheight - 1.0f - y1) * (x2 - x1) ) / (y2 - y1);
627 		y2 = fheight - 1.0f;
628 	}
629 	if(x2 < 0.0f)
630 	{
631 		y2 = y1 - ( x1 * (y2 - y1) ) / (x2 - x1);
632 		x2 = 0.0f;
633 	}
634 	else if(x2 >= fwidth)
635 	{
636 		y2 = y1 + ( (fwidth - 1.0f - x1) * (y2 - y1) ) / (x2 - x1);
637 		x2 = fwidth - 1.0f;
638 	}
639 
640 	dx = x2 - x1;
641 	dy = y2 - y1;
642 
643 	if( fabs(dx) > fabs(dy) )
644 	{
645 		if( x2 < x1 )
646 		{
647 			tmp = x1;
648 			x1 = x2;
649 			x2 = tmp;
650 			tmp = y1;
651 			y1 = y2;
652 			y2 = tmp;
653 		}
654 		gradient = dy / dx;
655 
656 // handle first endpoint
657 		xend = round(x1);
658 		yend = y1 + gradient * (xend - x1);
659 		gap = 1.0 - (x1+0.5 - trunc(x1 + 0.5));
660 		xpxl1 = int32(xend);  // this will be used in the main loop
661 		ypxl1 = int32( trunc(yend) );
662 		set_pixel( xpxl1, ypxl1, color, uint8( float(solid)* (1.0 - (yend - trunc(yend)) * gap) ) );
663 		set_pixel( xpxl1, ypxl1 + 1, color, uint8( float(solid)* (yend - trunc(yend)) * gap ) );
664 		tmp = yend + gradient; // first y-intersection for the main loop
665 
666 // handle second endpoint
667 		xend = round(x2);
668 		yend = y2 + gradient * (xend - x2);
669 		gap = 1.0 - (x2-0.5 - trunc(x2 - 0.5));
670 		xpxl2 = int32(xend);  // this will be used in the main loop
671 		ypxl2 = int32( trunc(yend) );
672 		set_pixel( xpxl2, ypxl2, color, uint8( float(solid)* (1.0 - (yend - trunc(yend)) *gap) ) );
673 		set_pixel( xpxl2, ypxl2 + 1, color, uint8( float(solid)* (yend - trunc(yend)) *gap ) );
674 
675 // main loop
676 		for( xpxl1++; xpxl1< xpxl2; xpxl1++ )
677 		{
678 			set_pixel( xpxl1, int32(trunc(tmp)), color, uint8( float(solid)* (1.0 - (tmp - trunc(tmp))) ) );
679 			set_pixel( xpxl1, int32(trunc(tmp)) + 1, color, uint8( float(solid)* (tmp - trunc(tmp)) ) );
680 			tmp += gradient;
681 		}
682 	}
683 	else
684 	{
685 		if( y2 < y1 )
686 		{
687 			tmp = x1;
688 			x1 = x2;
689 			x2 = tmp;
690 			tmp = y1;
691 			y1 = y2;
692 			y2 = tmp;
693 		}
694 		gradient = dx / dy;
695 
696 // handle first endpoint
697 		yend = round(y1);
698 		xend = x1 + gradient * (yend - y1);
699 		gap = 1.0 - (y1+0.5 - trunc(y1 + 0.5));
700 		ypxl1 = int32(yend);  // this will be used in the main loop
701 		xpxl1 = int32( trunc(xend) );
702 		set_pixel( xpxl1, ypxl1, color, uint8( float(solid)* (1.0 - (xend - trunc(xend)) * gap) ) );
703 		set_pixel( xpxl1 + 1, ypxl1, color, uint8( float(solid)* (xend - trunc(xend)) * gap ) );
704 		tmp = xend + gradient; // first y-intersection for the main loop
705 
706 // handle second endpoint
707 		yend = round(y2);
708 		xend = x2 + gradient * (yend - y2);
709 		gap = 1.0 - (y2-0.5 - trunc(y2 - 0.5));
710 		ypxl2 = int32(yend);  // this will be used in the main loop
711 		xpxl2 = int32( trunc(xend) );
712 		set_pixel( xpxl2, ypxl2, color, uint8( float(solid)* (1.0 - (xend - trunc(xend)) *gap) ) );
713 		set_pixel( xpxl2 + 1, ypxl2, color, uint8( float(solid)* (xend - trunc(xend)) *gap ) );
714 
715 // main loop
716 		for( ypxl1++; ypxl1< ypxl2; ypxl1++ )
717 		{
718 			set_pixel( int32(trunc(tmp)), ypxl1, color, uint8( float(solid)* (1.0 - (tmp - trunc(tmp))) ) );
719 			set_pixel( int32(trunc(tmp)) + 1, ypxl1, color, uint8( float(solid)* (tmp - trunc(tmp)) ) );
720 			tmp += gradient;
721 		}
722 	}
723 }
724 
725 
726 
727 //
728 //  line - draws a line on the picture
729 //
line(int32 x1,int32 y1,int32 x2,int32 y2,const colorrgb & color,uint8 solid)730 void picture::line(int32 x1,int32 y1,int32 x2,int32 y2,const colorrgb &color,
731                    uint8 solid) throw(error_obj)
732 {
733 	int32 dx,dy,incr1,incr2,d,x,y,xend,yend,yinc,xinc;
734 
735 	float fx1,fx2,fy1,fy2,fheight,fwidth; // the calculations are done using float's to avoid
736 // overflows on very large values
737 
738 	if(y1 == y2)
739 		goto picture_line_draw_h_line;
740 
741 	if(y1 > y2) // makes sure that y1 is lower than y2
742 	{
743 		xend = x1;
744 		x1 = x2;
745 		x2 = xend;
746 		xend = y1;
747 		y1 = y2;
748 		y2 = xend;
749 	}
750 	fx1 = float(x1);
751 	fx2 = float(x2);
752 	fy1 = float(y1);
753 	fy2 = float(y2);
754 	fheight = float(height);
755 	fwidth = float(width);
756 
757 // picture_line_calc_p1
758 	if(y1 >= height)
759 		return; // whole line is below the picture
760 	if(y1 < 0)
761 	{
762 		if(y2 < 0)
763 			return; // whole line is over the picture
764 
765 		fx1 = fx1 - ( fy1 * (fx2 - fx1) ) / (fy2 - fy1);
766 		fy1 = 0.0f;
767 	}
768 	if(fx1 < 0.0f)
769 	{
770 		if(fx2 < 0.0f)
771 			return; // whole line is to the left of the picture
772 
773 		fy1 = fy1 - ( fx1 * (fy2 - fy1) ) / (fx2 - fx1);
774 		fx1 = 0.0f;
775 		if(fy1 >= fheight)
776 			return; // the line is to the left and below of the picture
777 	}
778 	else if(fx1 >= fwidth)
779 	{
780 		if(fx2 >= fwidth)
781 			return; // the line is to the right...
782 
783 		fy1 = fy1 + ( (fwidth - 1.0f - fx1) * (fy2 - fy1) ) / (fx2 - fx1);
784 		fx1 = fwidth - 1.0f;
785 		if(fy1 >= fheight)
786 			return; // the line is to the right and below...
787 	}
788 
789 
790 // picture_line_calc_p2
791 	if( trunc(fy1) != trunc(fy2) )
792 	{
793 		if( fy2 >= fheight )
794 		{
795 			fx2 = fx1 + ( (fheight - 1.0f - fy1) * (fx2 - fx1) ) / (fy2 - fy1);
796 			fy2 = fheight - 1.0f;
797 		}
798 		if( fx2 < 0.0f )
799 		{
800 			fy2 = fy1 - ( fx1 * (fy2 - fy1) ) / (fx2 - fx1);
801 			fx2 = 0.0f;
802 		}
803 		else if( fx2 >= fwidth )
804 		{
805 			fy2 = fy1 + ( (fwidth - 1.0f - fx1) * (fy2 - fy1) ) / (fx2 - fx1);
806 			fx2 = fwidth - 1.0f;
807 		}
808 
809 		x1 = int32(trunc(fx1));
810 		x2 = int32(trunc(fx2));
811 		y1 = int32(trunc(fy1));
812 		y2 = int32(trunc(fy2));
813 	}
814 	else
815 	{
816 		x1 = int32(trunc(fx1));
817 		x2 = int32(trunc(fx2));
818 		y1 = int32(trunc(fy1));
819 
820 		picture_line_draw_h_line:
821 		if( y1 >= height  ||  y1 < 0 )
822 			return; // the whole line is over/below the picture
823 		if( x1 > x2 )
824 		{
825 			xend=x1;
826 			x1=x2;
827 			x2=xend;
828 		}
829 
830 		if( x1 >= width  ||  x2 < 0 )
831 			return; // the whole line is to the right/left of the picture
832 
833 		if( x1 < 0 )
834 			x1 = 0;
835 		if( x2 >= width )
836 			x2 = width-1;
837 
838 		if( x1 > x2 )
839 			return; // the whole line is to the right/left of the picture
840 
841 		if( solid == 255  &&  type == PictureType_RGB )
842 		{
843 			xend = x1 + y1*width; // offset
844 			yend = x2 - x1 + 1;   // length
845 
846 			memset( &data[0].buffer[xend], color.red, yend);
847 			memset( &data[1].buffer[xend], color.green, yend);
848 			memset( &data[2].buffer[xend], color.blue, yend);
849 
850 			if( !isBackground )
851 				memset( &solidness.buffer[xend], 255, yend );
852 		}
853 		else
854 		{
855 			for(;x1 <= x2;x1++)
856 				set_pixel(x1,y1,color,solid);
857 		}
858 		return;
859 	}
860 
861 	set_pixel(x1,y1,color,solid);
862 	set_pixel(x2,y2,color,solid);
863 
864 	dx=abs(x2-x1);
865 	dy=abs(y2-y1);
866 	if(dx>=dy)
867 	{
868 		if(x1>x2)
869 		{
870 			x=x2;
871 			y=y2;
872 			xend=x1;
873 			if(dy==0)
874 				yinc=0;
875 			else
876 			{
877 				if(y2>y1)
878 					yinc=-1;
879 				else
880 					yinc=1;
881 			}
882 		}
883 		else
884 		{
885 			x=x1;
886 			y=y1;
887 			xend=x2;
888 			if(dy==0)
889 				yinc=0;
890 			else
891 			{
892 				if(y2>y1)
893 					yinc=1;
894 				else
895 					yinc=-1;
896 			}
897 		}
898 		incr1=dy+dy;
899 		d=incr1-dx;
900 		incr2=(dy-dx)<<1;
901 		while(x<xend)
902 		{
903 			x++;
904 			if(d<0)
905 				d+=incr1;
906 			else
907 			{
908 				y+=yinc;
909 				d+=incr2;
910 			}
911 			set_pixel(x,y,color,solid);
912 		}
913 	}
914 	else
915 	{
916 		if(y1>y2)
917 		{
918 			x=x2;
919 			y=y2;
920 			yend=y1;
921 			if(dx==0)
922 				xinc=0;
923 			else
924 			{
925 				if(x2>x1)
926 					xinc=-1;
927 				else
928 					xinc=1;
929 			}
930 		}
931 		else
932 		{
933 			x=x1;
934 			y=y1;
935 			yend=y2;
936 			if( dx == 0 )
937 				xinc=0;
938 			else
939 			{
940 				if(x2>x1)
941 					xinc=1;
942 				else
943 					xinc=-1;
944 			}
945 		}
946 		incr1=dx+dx;
947 		d=incr1-dy;
948 		incr2=(dx-dy)<<1;
949 		while( y < yend )
950 		{
951 			y++;
952 			if( d < 0 )
953 				d+=incr1;
954 			else
955 			{
956 				x+=xinc;
957 				d+=incr2;
958 			}
959 			set_pixel(x,y,color,solid);
960 		}
961 	}
962 }
963 
964 
965 //
966 //  setsolidness - sets the solidness of a specific pixel
967 //
set_solidness(int32 x,int32 y,uint8 newsolidness)968 void picture::set_solidness(int32 x,int32 y,uint8 newsolidness) throw(error_obj)
969 {
970 	int32 offset = x + y*width;
971 
972 	if( !isBackground )
973 	{
974 		if((x < width) & (y < height) & (x >= 0) & (y >= 0))
975 			solidness.buffer[offset] = newsolidness;
976 	}
977 }
978 
979 
980 //
981 //  setsolidnesscolor - sets the solidness to one specific color
982 //
set_solidness_color(const colorrgb & color,uint8 newsolidness)983 void picture::set_solidness_color(const colorrgb &color,uint8 newsolidness) throw(error_obj)
984 {
985 	int32 x,y;
986 
987 	if( !isBackground )
988 	{
989 		for(y=0;y<height;y++)
990 		{
991 			for(x=0;x<width;x++)
992 			{
993 				if(color == get_pixel(x,y))
994 					set_solidness(x,y,newsolidness);
995 			}
996 		}
997 	}
998 }
999 
1000 
1001 //
1002 //  setsolidnessall - sets the solidness of the whole picture
1003 //
set_solidness_all(uint8 newsolidness)1004 void picture::set_solidness_all(uint8 newsolidness) throw(error_obj)
1005 {
1006 	if( !isBackground )
1007 		memset(solidness.buffer,newsolidness, height * width);
1008 }
1009 
1010 
1011 //
1012 //  clear - set all pixels to the specified color
1013 //
clear(const colorrgb & color)1014 void picture::clear(const colorrgb &color) throw(error_obj)
1015 {
1016 	int32 size = height*width;
1017 
1018 	if( !isBackground )
1019 		memset(solidness.buffer, 255, size );
1020 	if( type == PictureType_RGB )
1021 	{
1022 		memset(data[0].buffer, color.red,   size);
1023 		memset(data[1].buffer, color.green, size);
1024 		memset(data[2].buffer, color.blue,  size);
1025 	}
1026 	else //if( type == PictureType_GRAYSCALE )
1027 	{
1028 		uint8 grey = uint8(
1029 		                   float(color.red)*0.299f +
1030 		                   float(color.green)*0.587f +
1031 		                   float(color.blue)*0.114f
1032 		                  );
1033 		memset(data[0].buffer, grey, size);
1034 	}
1035 }
1036 
1037 
1038 //
1039 //  convto_rgb - converts the picture into a rgb-picture
1040 //
convto_rgb(void)1041 void picture::convto_rgb(void) throw(error_obj)
1042 {
1043 	if( type == PictureType_GRAYSCALE )
1044 	{
1045 		set_size(width,height); // fixing space for the green and blue channel
1046 		type = PictureType_RGB;
1047 		memcpy(data[1].buffer,data[0].buffer,width*height);
1048 		memcpy(data[2].buffer,data[0].buffer,width*height);
1049 	}
1050 //	else if( type == PictureType_RGB ) // this is already a rgb-picture
1051 //	{ }
1052 }
1053 
1054 //
1055 //  convto_grayscale - converts the picture into a grayscaled-picture
1056 //
convto_grayscale(void)1057 void picture::convto_grayscale(void) throw(error_obj)
1058 {
1059 //Y = 0.299*R + 0.587*G + 0.114*B
1060 	int32 offset = width * height -1;
1061 
1062 	if( type == PictureType_RGB )
1063 	{
1064 		for(;offset >= 0;offset--)
1065 			data[0].buffer[offset] = uint8(
1066 			                               float(data[0].buffer[offset])*0.299f +
1067 			                               float(data[1].buffer[offset])*0.587f +
1068 			                               float(data[2].buffer[offset])*0.114f
1069 			                              );
1070 		type = PictureType_GRAYSCALE;
1071 	}
1072 //	else if( type == PictureType_GRAYSCALE ) // this is already a grayscale-picture
1073 //	{ }
1074 }
1075 
1076 
set_pixel_grayscale(int32 x,int32 y,uint8 grey,uint8 solid)1077 void picture::set_pixel_grayscale(int32 x,int32 y,uint8 grey,uint8 solid) throw(error_obj)
1078 {
1079 	if((x < width) & (y < height) & (x > 0) & (y > 0) & (type == PictureType_GRAYSCALE))
1080 	{
1081 		if(solid == 255)
1082 			data[0].buffer[x+y*width] = grey;
1083 		else
1084 			data[0].buffer[x+y*width] = uint8( (uint16(grey) * uint16(solid) + uint16(data[0].buffer[x+y*width]) * uint16(255 - solid)) / 255);
1085 
1086 		if( solid != 0  &&  !isBackground )
1087 			solidness.buffer[x+y*width] = 255;
1088 	}
1089 }
1090 
1091 //
1092 //  setpixel - sets a pixel to a specific color
1093 //
1094 //  the color will be converted to grayscale/YCrCb/palette if the picture is in those modes
1095 //
1096 //  if the point (x,y) is outside the picture nothing will happen
1097 //
1098 //  the picture-pixel's solidness will be 255
1099 //
set_pixel(int32 x,int32 y,const colorrgb & color,uint8 solid)1100 void picture::set_pixel(int32 x,int32 y,const colorrgb &color,uint8 solid) throw(error_obj)
1101 {
1102 	int32 offset = x + y*width;
1103 
1104 	if((x < width) & (y < height) & (x >= 0) & (y >= 0))
1105 	{
1106 		if( type == PictureType_RGB )
1107 		{
1108 			if(solid == 255)
1109 			{
1110 				data[0].buffer[offset] = color.red;
1111 				data[1].buffer[offset] = color.green;
1112 				data[2].buffer[offset] = color.blue;
1113 			}
1114 			else
1115 			{
1116 				data[0].buffer[offset] = uint8( (uint16(color.red) * uint16(solid) + uint16(data[0].buffer[offset]) * uint16(255 - solid)) / 255);
1117 				data[1].buffer[offset] = uint8( (uint16(color.green) * uint16(solid) + uint16(data[1].buffer[offset]) * uint16(255 - solid)) / 255);
1118 				data[2].buffer[offset] = uint8( (uint16(color.blue) * uint16(solid) + uint16(data[2].buffer[offset]) * uint16(255 - solid)) / 255);
1119 			}
1120 			if( solid != 0  &&  !isBackground)
1121 				solidness.buffer[offset] = 255;
1122 		}
1123 		else //if( type == PictureType_GRAYSCALE )
1124 		{
1125 			uint8 gray;
1126 
1127 			if((color.red == color.green) & (color.red == color.green))
1128 				gray = color.red;
1129 			else
1130 			{
1131 				gray = uint8(
1132 							 float(color.red)*0.299f +
1133 							 float(color.green)*0.587f +
1134 							 float(color.blue)*0.114f
1135 							);
1136 			}
1137 			if(solid == 255)
1138 				data[0].buffer[offset] = gray;
1139 			else
1140 				data[0].buffer[offset] = uint8( (uint16(gray) * uint16(solid) + uint16(data[0].buffer[offset]) * uint16(255 - solid)) / 255);
1141 			if( solid != 0  &&  !isBackground )
1142 				solidness.buffer[offset] = 255;
1143 
1144 		}
1145 	}
1146 }
1147 
1148 
box(int32 x,int32 y,int32 width,int32 height,const colorrgb & color,uint8 solid)1149 void picture::box(int32 x,int32 y,int32 width,int32 height,const colorrgb &color,
1150                   uint8 solid) throw(error_obj)
1151 {
1152 	if( width <= 0  ||  height <= 0 )
1153 		return;
1154 	width--;
1155 	height--;
1156 	line(x,y,x+width,y,color,solid);
1157 	line(x,y+height,x+width,y+height,color,solid);
1158 	line(x,y,x,y+height,color,solid);
1159 	line(x+width,y,x+width,y+height,color,solid);
1160 }
1161 
filled_box(int32 x,int32 y,int32 width,int32 height,const colorrgb & color,uint8 solid)1162 void picture::filled_box(int32 x,int32 y,int32 width,int32 height,const colorrgb &color,
1163                          uint8 solid) throw(error_obj)
1164 {
1165 	int32 ystop,xstop;
1166 
1167 	if( width <= 0  ||  height <= 0 )
1168 		return;
1169 
1170 	xstop = x+width-1;
1171 	ystop = y+height-1;
1172 
1173 	for(;y<=ystop;y++)
1174 		line(x,y,xstop,y,color,solid);
1175 }
1176 
1177 
1178 
1179 //
1180 //  draw - draws this picture on *dest
1181 //
draw(int32 x,int32 y) const1182 void picture::draw(int32 x,int32 y) const throw(error_obj)
1183 {
1184 	int32 xt,yt,xstart,ystart,xstop,ystop;
1185 	colorrgb color;
1186 	uint8 solid;
1187 
1188 	if(dest == 0)
1189 		return;
1190 
1191 	if(x < 0)
1192 		xstart = -x;
1193 	else
1194 		xstart = 0;
1195 
1196 	if(y < 0)
1197 		ystart = -y;
1198 	else
1199 		ystart = 0;
1200 
1201 	if(width > (dest->width - x))
1202 		xstop = dest->width - x;
1203 	else
1204 		xstop = width;
1205 
1206 	if(height > (dest->height - y))
1207 		ystop = dest->height - y;
1208 	else
1209 		ystop = height;
1210 
1211 	if( type == PictureType_RGB  &&  dest->type == PictureType_RGB )
1212 	{
1213 		int32 offset = xstart + ystart*width;
1214 		int32 lineskip = xstart + width - xstop;
1215 
1216 		for(yt=ystart;yt<ystop;yt++,offset += lineskip)
1217 		{
1218 			for(xt=xstart;xt<xstop;xt++,offset++)
1219 			{
1220 				dest->set_pixel(xt+x,yt+y,(colorrgb) {data[0].buffer[offset],
1221 				data[1].buffer[offset],
1222 				data[2].buffer[offset]},
1223 				solidness.buffer[offset]);
1224 			}
1225 		}
1226 	}
1227 	else
1228 	{
1229 		for(yt=ystart;yt<ystop;yt++)
1230 		{
1231 			for(xt=xstart;xt<xstop;xt++)
1232 			{
1233 				color = get_pixel(xt,yt);
1234 				solid = get_solidness(xt,yt);
1235 				dest->set_pixel(xt+x,yt+y,color,solid);
1236 			}
1237 		}
1238 	}
1239 }
1240 
1241 
1242 //
1243 //  draw_shadow - draws a shadow of the picture in the specified color
1244 //
draw_shadow(int32 x,int32 y,const colorrgb & color) const1245 void picture::draw_shadow(int32 x,int32 y,const colorrgb &color) const throw(error_obj)
1246 {
1247 	int32 xt,yt,xstart,ystart,xstop,ystop;
1248 
1249 	if(dest == 0)
1250 		return;
1251 
1252 	if(x < 0)
1253 		xstart = -x;
1254 	else
1255 		xstart = 0;
1256 
1257 	if(y < 0)
1258 		ystart = -y;
1259 	else
1260 		ystart = 0;
1261 
1262 	if(width > (dest->width - x))
1263 		xstop = dest->width - x;
1264 	else
1265 		xstop = width;
1266 
1267 	if(height > (dest->height - y))
1268 		ystop = dest->height - y;
1269 	else
1270 		ystop = height;
1271 
1272 	int32 offset = xstart + ystart*width;
1273 	int32 lineskip = xstart + width - xstop;
1274 
1275 	for(yt=ystart;yt<ystop;yt++, offset += lineskip)
1276 	{
1277 		for(xt=xstart;xt<xstop;xt++, offset++)
1278 			dest->set_pixel(xt+x,yt+y,color,solidness.buffer[offset]);
1279 	}
1280 }
1281 
1282 //
1283 //  get_pixel - returns a colorrgb-struct containing the rgb-values for the pixel
1284 //              if picture isn't a RGB-picture the rgb-value will be calculated
1285 //
get_pixel(int32 x,int32 y) const1286 colorrgb picture::get_pixel(int32 x,int32 y) const
1287 {
1288 	if( x < 0  ||  y < 0  ||  x >= width  ||  y >= height )
1289 		return color::BLACK;
1290 
1291 	int32 offset = x+y*width;
1292 	if( type == PictureType_RGB )
1293 		return (colorrgb) { data[0].buffer[offset], data[1].buffer[offset], data[2].buffer[offset] };
1294 	else //if( type == PictureType_GRAYSCALE )
1295 		return (colorrgb) { data[0].buffer[offset], data[0].buffer[offset], data[0].buffer[offset] };
1296 }
1297 
1298 
1299 
get_solidness(int32 x,int32 y) const1300 uint8 picture::get_solidness(int32 x,int32 y) const
1301 {
1302 	if( x < 0  ||  y < 0  ||  x >= width  ||  y >= height )
1303 		return 0;
1304 
1305 	if( isBackground )
1306 		return 255;
1307 	else
1308 		return solidness.buffer[x+y*width];
1309 }
1310 
1311 
save_ppm(const char * filename,bool save_as_ascii) const1312 void picture::save_ppm(const char *filename, bool save_as_ascii) const throw(error_obj)
1313 {
1314 	FILE *fp;
1315 	colorrgb farg;
1316 	int32 x,y;
1317 
1318 	fp = fopen(filename,"wb");
1319 	if(fp == NULL)
1320 	{
1321 		char tmp_err[ERROR_OBJ_MSG_LEN];
1322 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/write to file (%s)"), filename );
1323 		THROW_ERROR( ErrorType_File_IO, tmp_err );
1324 	}
1325 
1326 	if(save_as_ascii)
1327 		fprintf(fp,"P3\n");
1328 	else
1329 		fprintf(fp,"P6\n");
1330 
1331 	fprintf(fp,"%ld\n%ld\n255%c",width,height,10);
1332 
1333 	for(y=0;y<height;y++)
1334 	{
1335 		for(x=0;x<width;x++)
1336 		{
1337 			farg = get_pixel(x,y);
1338 
1339 			if(save_as_ascii)
1340 				fprintf(fp,"%lu %lu %lu\n",uint32(farg.red),uint32(farg.green),uint32(farg.blue));
1341 			else
1342 			{
1343 				fputc(farg.red,fp);
1344 				fputc(farg.green,fp);
1345 				fputc(farg.blue,fp);
1346 			}
1347 		}
1348 	}
1349 
1350 	if(fclose(fp) != 0)
1351 	{
1352 		char tmp_err[ERROR_OBJ_MSG_LEN];
1353 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't close file (%s)"), filename );
1354 		THROW_ERROR( ErrorType_File_IO, tmp_err );
1355 	}
1356 }
1357 
1358 
save_pcx(const char * filename) const1359 void picture::save_pcx(const char *filename) const throw(error_obj)
1360 {
1361 	FILE *fp;
1362 	uint8 header[128],tmp,lastbyte;
1363 	int32 x,y,i,antal;
1364 	colorrgb farg;
1365 
1366 	fp = fopen(filename,"wb");
1367 	if(fp == NULL)
1368 	{
1369 		char tmp_err[ERROR_OBJ_MSG_LEN];
1370 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/write to file (%s)"), filename );
1371 		THROW_ERROR( ErrorType_File_IO, tmp_err );
1372 	}
1373 
1374 	memset(header,0,128);
1375 
1376 	header[0] = 10; //ZSoft .PCX
1377 	header[1] = 5;  //version 3.0
1378 	header[2] = 1;  //RLE encoding
1379 	header[3] = 8;  //bit depth
1380 	header[65] = 3; //number of planes
1381 
1382 	header[4] = 0;  //minX
1383 	header[5] = 0;
1384 
1385 	header[6] = 0;  //minY
1386 	header[7] = 0;
1387 
1388 	header[8] = uint8((width - 1) % 256); //maxX
1389 	header[9] = uint8((width - 1) / 256);
1390 
1391 	header[10] = uint8((height - 1) % 256);//maxY
1392 	header[11] = uint8((height - 1) / 256);
1393 
1394 	header[66] = uint8(width % 256); //bytes per line per plane
1395 	header[67] = uint8(width / 256);
1396 
1397 	for(i=0;i<128;i++)
1398 		fputc(header[i],fp);
1399 
1400 // saves picture
1401 	for(y=0;y<height;y++)
1402 	{
1403 		for(i=0;i<3;i++)
1404 		{
1405 			antal = 1;
1406 			farg = get_pixel(0,y);
1407 			if(i == 0)
1408 				lastbyte = farg.red;
1409 			else if(i == 1)
1410 				lastbyte = farg.green;
1411 			else
1412 				lastbyte = farg.blue;
1413 
1414 
1415 			for(x=1;x<width;x++)
1416 			{
1417 				farg = get_pixel(x,y);
1418 				if(i == 0)
1419 					tmp = farg.red;
1420 				else if(i == 1)
1421 					tmp = farg.green;
1422 				else
1423 					tmp = farg.blue;
1424 
1425 				if(tmp == lastbyte)
1426 					antal++;
1427 				else
1428 				{
1429 saveit_pcx_picture:
1430 					if((antal != 1) | (lastbyte >= uint8(0xC0)))
1431 					{
1432 						tmp = uint8(antal);
1433 						if(antal > int32(0x3F))
1434 							tmp = uint8(0x3F);
1435 						antal -= int32(tmp);
1436 
1437 						tmp += uint8(0xC0);
1438 						fputc(tmp,fp);
1439 						fputc(lastbyte,fp);
1440 						if(antal > 0)
1441 							goto saveit_pcx_picture;
1442 					}
1443 					else
1444 						fputc(lastbyte,fp);
1445 
1446 					antal = 1;
1447 					farg = get_pixel(x,y);
1448 					if(i == 0)
1449 						lastbyte = farg.red;
1450 					else if(i == 1)
1451 						lastbyte = farg.green;
1452 					else
1453 						lastbyte = farg.blue;
1454 				}
1455 				if(x == (width - 1))
1456 				{
1457 					x = width;
1458 					goto saveit_pcx_picture;
1459 				}
1460 			}
1461 		}
1462 	}
1463 	if(fclose(fp) != 0)
1464 	{
1465 		char tmp_err[ERROR_OBJ_MSG_LEN];
1466 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't close file (%s)"), filename );
1467 		THROW_ERROR( ErrorType_File_IO, tmp_err );
1468 	}
1469 }
1470 
1471 
mirror(void)1472 void picture::mirror(void) throw(error_obj)
1473 {
1474 	colorrgb color;
1475 	uint8 solid;
1476 	int32 x,y;
1477 
1478 	for(y=0;y<height;y++)
1479 	{
1480 		for(x=0;x<(width/2);x++)
1481 		{
1482 			color = get_pixel(x,y);
1483 			solid = get_solidness(x,y);
1484 			set_pixel(x,y,get_pixel(width-1-x,y));
1485 			set_solidness(x,y,get_solidness(width-1-x,y));
1486 			set_pixel(width-1-x,y,color);
1487 			set_solidness(width-1-x,y,solid);
1488 		}
1489 	}
1490 }
upside_down(void)1491 void picture::upside_down(void) throw(error_obj)
1492 {
1493 	colorrgb color;
1494 	uint8 solid;
1495 	int32 x,y;
1496 
1497 	for(y=0;y<(height/2);y++)
1498 	{
1499 		for(x=0;x<width;x++)
1500 		{
1501 			color = get_pixel(x,y);
1502 			solid = get_solidness(x,y);
1503 			set_pixel(x,y,get_pixel(x,height-1-y));
1504 			set_solidness(x,y,get_solidness(x,height-1-y));
1505 			set_pixel(x,height-1-y,color);
1506 			set_solidness(x,height-1-y,solid);
1507 		}
1508 	}
1509 }
rotate90(void)1510 void picture::rotate90(void) throw(error_obj)
1511 {
1512 	picture tmp;
1513 	int32 x,y;
1514 
1515 	tmp = *this;
1516 
1517 	height = tmp.width;
1518 	width = tmp.height;
1519 
1520 	for(y=0;y<height;y++)
1521 	{
1522 		for(x=0;x<width;x++)
1523 		{
1524 			set_pixel(x,y,tmp.get_pixel(height-1-y,x));
1525 			set_solidness(x,y,tmp.get_solidness(height-1-y,x));
1526 		}
1527 	}
1528 }
rotate180(void)1529 void picture::rotate180(void) throw(error_obj)
1530 {
1531 	mirror();
1532 	upside_down();
1533 }
rotate270(void)1534 void picture::rotate270(void) throw(error_obj)
1535 {
1536 	rotate90();
1537 	rotate180();
1538 }
1539 
1540 //
1541 //  load_pcx - loads a PCX-picture
1542 //  note that the PCX-file must be a 24bit RLE v3.0
1543 //
load_pcx(const char * filename)1544 void picture::load_pcx(const char *filename) throw(error_obj)
1545 {
1546 	uint8 header[128],byte,color;
1547 	FILE *fp;
1548 	int32 i,tmp,x,y;
1549 
1550 	erase();
1551 	type = PictureType_RGB;
1552 
1553 	fp = fopen(filename,"rb"); // open pcx-file
1554 	if(fp == NULL)
1555 	{
1556 		char tmp_err[ERROR_OBJ_MSG_LEN];
1557 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/read from file (%s)"), filename );
1558 		THROW_ERROR( ErrorType_File_IO, tmp_err );
1559 	}
1560 
1561 	for(i=0;i<128;i++)
1562 	{
1563 		header[i] = fgetc(fp);  // load pcx-header
1564 		if(feof(fp) != 0)
1565 		{
1566 			fclose(fp);
1567 			char tmp_err[ERROR_OBJ_MSG_LEN];
1568 			snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/read from file (%s)"), filename );
1569 			THROW_ERROR( ErrorType_File_IO, tmp_err );
1570 		}
1571 	}
1572 
1573 	if( header[0] != 10 ||  // 10 = ZSoft .PCX
1574 	    header[1] != 5 ||   // version should be 5 = v3.0
1575 	    header[2] != 1 ||   // encoding 1 = .PCX Run Length Encoding (RLE)
1576 	    header[65] != 3 ||  // number of planes should be 3 (r,g,b)
1577 	    header[3] != 8 )    // bits per pixel per plane should be 8 (8 bit)
1578 	{
1579 		fclose(fp);
1580 
1581 		char tmp_err[ERROR_OBJ_MSG_LEN];
1582 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("%s is not a valid PCX file"), filename );
1583 		THROW_ERROR( ErrorType_File_IO, tmp_err );
1584 	}
1585 
1586 	tmp = int32(header[4]) + (int32(header[5])<<8); //getting the width
1587 	x = int32(header[8]) + (int32(header[9])<<8);
1588 	x -= (tmp - 1);
1589 
1590 
1591 	tmp = int32(header[6]) + (int32(header[7])<<8); //getting the height
1592 	y = int32(header[10]) + (int32(header[11])<<8);
1593 	y -= (tmp - 1);
1594 
1595 
1596 	try{ set_size(x,y); }
1597 	catch(error_obj error)
1598 	{
1599 		fclose(fp);
1600 		throw error;
1601 	}
1602 	if( !isBackground )
1603 		memset(solidness.buffer,255,width*height);
1604 
1605 	tmp = 0;
1606 	for(y=0;y<height;y++)
1607 	{
1608 		for(i=0;i<3;i++)
1609 		{
1610 			x = 0;
1611 			do{
1612 				byte = fgetc(fp);
1613 				if(feof(fp) != 0)
1614 				{
1615 					fclose(fp);
1616 
1617 					char tmp_err[ERROR_OBJ_MSG_LEN];
1618 					snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/read from file (%s)"), filename );
1619 					THROW_ERROR( ErrorType_File_IO, tmp_err );
1620 				}
1621 				if(byte >= uint8(0xc0))
1622 				{
1623 					color = fgetc(fp);
1624 					if(feof(fp) != 0)
1625 					{
1626 						fclose(fp);
1627 
1628 						char tmp_err[ERROR_OBJ_MSG_LEN];
1629 						snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/read from file (%s)"), filename );
1630 						THROW_ERROR( ErrorType_File_IO, tmp_err );
1631 					}
1632 
1633 					for(byte -= uint8(0xc0);byte!=0;byte--)
1634 					{
1635 						if(x == width)
1636 						{
1637 							tmp = 1;
1638 							break;
1639 						}
1640 
1641 						data[i].buffer[x + y*width] = color;
1642 
1643 						x++;
1644 					}
1645 				}
1646 				else
1647 				{
1648 					data[i].buffer[x + y*width] = byte;
1649 
1650 					x++;
1651 				}
1652 			}while(x < width);
1653 		}
1654 	}
1655 
1656 	if(fclose(fp) != 0)
1657 	{
1658 		char tmp_err[ERROR_OBJ_MSG_LEN];
1659 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't close file (%s)"), filename );
1660 		THROW_ERROR( ErrorType_File_IO, tmp_err );
1661 	}
1662 }
1663 
1664 
operator =(const picture & source)1665 void picture::operator=(const picture & source) throw(error_obj)
1666 {
1667 	int32 size = source.width*source.height;
1668 
1669 	type = source.type;
1670 	set_size(source.width,source.height);
1671 
1672 	if( !isBackground )
1673 		memcpy(solidness.buffer,source.solidness.buffer,size);
1674 	memcpy(data[0].buffer,source.data[0].buffer,size);
1675 
1676 	if( type == PictureType_RGB )
1677 	{
1678 		memcpy(data[1].buffer,source.data[1].buffer,size);
1679 		memcpy(data[2].buffer,source.data[2].buffer,size);
1680 	}
1681 }
1682 
operator ==(const picture & pic) const1683 bool picture::operator==(const picture &pic) const
1684 {
1685 	if( width != pic.width  ||  height != pic.height )
1686 		return false;
1687 	if( type != pic.type  ||  isBackground != pic.isBackground )
1688 		return false;
1689 
1690 	int32 tot = width*height;
1691 	int32 i;
1692 
1693 	if( !isBackground )
1694 	{
1695 		for( i=0; i<tot; i++ )
1696 		{
1697 			if( solidness.buffer[i] != pic.solidness.buffer[i] )
1698 				return false;
1699 		}
1700 	}
1701 	for( i=0; i<tot; i++ )
1702 	{
1703 		if( data[0].buffer[i] != pic.data[0].buffer[i] )
1704 			return false;
1705 	}
1706 
1707 	if( type == PictureType_RGB )
1708 	{
1709 		for( i=0; i<tot; i++ )
1710 		{
1711 			if( data[1].buffer[i] != pic.data[1].buffer[i] )
1712 				return false;
1713 			if( data[2].buffer[i] != pic.data[2].buffer[i] )
1714 				return false;
1715 		}
1716 	}
1717 	return true;
1718 }
1719 
1720 //
1721 //  capture - extracts a part of a picture
1722 //
capture(const picture & source,int32 x,int32 y,int32 width,int32 height)1723 void picture::capture(const picture &source,int32 x,int32 y,int32 width,int32 height)
1724                 throw(error_obj)
1725 {
1726 	int32 xi,yi,tx,x2,y2;
1727 
1728 	if( width <= 0  ||  height <= 0 )
1729 		return;
1730 
1731 	x2 = x+width-1;
1732 	y2 = y+height-1;
1733 
1734 	type = PictureType_RGB;
1735 	set_isBackground( source.isBackground );
1736 	set_size( width, height );
1737 
1738 	tx = x;
1739 	for(yi=0;y<=y2;y++,yi++)
1740 	{
1741 		for(x=tx,xi=0;x<=x2;x++,xi++)
1742 		{
1743 			set_pixel(xi,yi,source.get_pixel(x,y));
1744 			if( !isBackground )
1745 				set_solidness(xi,yi,source.get_solidness(x,y));
1746 		}
1747 	}
1748 }
1749 
1750 
draw_lines(const float * vertex,const uint32 * lines,uint32 nroflines,const colorrgb & color,bool antialiased,uint8 solid)1751 void picture::draw_lines( const float *vertex, const uint32 *lines, uint32 nroflines,
1752                           const colorrgb &color, bool antialiased, uint8 solid ) throw(error_obj)
1753 {
1754 	if( nroflines == 0 )
1755 		return;
1756 
1757 	if( antialiased )
1758 	{
1759 		float x, y, min_dist, xmax, xmin, ymax, ymin;
1760 		float x1, x2, y1, y2, tmp;
1761 		uint32 i, i2;
1762 
1763 		nroflines *= 2;
1764 
1765 		for( i=0; i<nroflines; i+=2 )
1766 		{
1767 			xmin = vertex[lines[i]];
1768 			ymin = vertex[lines[i]+1];
1769 			x2 = vertex[lines[i+1]];
1770 			y2 = vertex[lines[i+1]+1];
1771 
1772 			if( xmin < -1.0f  &&  x2 < -1.0f ) // line to the left of picture
1773 				continue;
1774 			if( ymin < -1.0f  &&  y2 < -1.0f ) // line above picture
1775 				continue;
1776 			if( xmin > float(width+1)  &&  x2 > float(width+1) ) // line to the right of picture
1777 				continue;
1778 			if( ymin > float(height+1)  &&  y2 > float(height+1) ) // line below picture
1779 				continue;
1780 
1781 			xmax = xmin;
1782 			ymax = ymin;
1783 			xmin = (xmin<x2) ? xmin : x2;
1784 			ymin = (ymin<y2) ? ymin : y2;
1785 			xmax = (xmax>x2) ? xmax : x2;
1786 			ymax = (ymax>y2) ? ymax : y2;
1787 
1788 			xmin = trunc(xmin);
1789 			ymin = trunc(ymin);
1790 			xmax = trunc(xmax+1.0f);
1791 			ymax = trunc(ymax+1.0f);
1792 
1793 			xmin = (xmin<0.0f) ? 0.0f : xmin;
1794 			ymin = (ymin<0.0f) ? 0.0f : ymin;
1795 			xmax = (xmax>float(width-1)) ? float(width-1) : xmax;
1796 			ymax = (ymax>float(height-1)) ? float(height-1) : ymax;
1797 
1798 			for( y=ymin; y<=ymax; y++ )
1799 			{
1800 				for( x=xmin; x<=xmax; x++ )
1801 				{
1802 					for( i2=i-2; i2<nroflines; i2-=2 )
1803 					{
1804 						x1 = vertex[lines[i2]];
1805 						y1 = vertex[lines[i2]+1];
1806 						x2 = vertex[lines[i2+1]];
1807 						y2 = vertex[lines[i2+1]+1];
1808 
1809 						if( x1 > x2 )
1810 						{
1811 							tmp = x1;
1812 							x1 = x2;
1813 							x2 = tmp;
1814 						}
1815 						if( y1  > y2 )
1816 						{
1817 							tmp = y1;
1818 							y1 = y2;
1819 							y2 = tmp;
1820 						}
1821 						x1 = trunc(x1);
1822 						y1 = trunc(y1);
1823 						x2 = trunc(x2+1.0f);
1824 						y2 = trunc(y2+1.0f);
1825 						if( x >= x1  &&  x <= x2  &&  y >= y1  &&  y <= y2 )
1826 							break;
1827 					}
1828 					if( i2 < nroflines )
1829 						continue;
1830 
1831 					for( i2=i, min_dist=1.0f; i2<nroflines; i2+=2 )
1832 					{
1833 						x1 = vertex[lines[i2]];
1834 						y1 = vertex[lines[i2]+1];
1835 						x2 = vertex[lines[i2+1]];
1836 						y2 = vertex[lines[i2+1]+1];
1837 
1838 						if( x < (( (x1<x2)?x1:x2 )-1.0f) )
1839 							continue;
1840 						if( x > (( (x1>x2)?x1:x2 )+1.0f) )
1841 							continue;
1842 						if( y < (( (y1<y2)?y1:y2)-1.0f) )
1843 							continue;
1844 						if( y > (( (y1>y2)?y1:y2 )+1.0f) )
1845 							continue;
1846 
1847 						if( x1 == x2 )
1848 						{
1849 							if( y1 == y2 )  // A point
1850 								tmp = -1.0f;
1851 							else  //  A vertical line
1852 								tmp = (y-y1) / (y2-y1);
1853 						}
1854 						else
1855 						{  //  An ordinary line
1856 							tmp = (y1-y2)/(x2-x1);
1857 							tmp = ( x1 - x - tmp*(y1-y) ) / ( tmp*(y2-y1) - x2 + x1 );
1858 						}
1859 						if( tmp < 0.0f )
1860 						{  //  Closest point lies below the line
1861 							tmp = (x-x1)*(x-x1) + (y-y1)*(y-y1);
1862 							if( tmp < min_dist )
1863 								min_dist = tmp;
1864 						}
1865 						else if( tmp > 1.0f )
1866 						{  //  Closest point lies above the line
1867 							tmp = (x-x2)*(x-x2) + (y-y2)*(y-y2);
1868 							if( tmp < min_dist )
1869 								min_dist = tmp;
1870 						}
1871 						else
1872 						{  //  Closest point lies on the line
1873 							x1 += tmp*(x2-x1); // Calculating the point which lies closest to (x,y)
1874 							y1 += tmp*(y2-y1);
1875 
1876 							tmp = (x-x1)*(x-x1) + (y-y1)*(y-y1);
1877 							if( tmp < min_dist )
1878 								min_dist = tmp;
1879 						}
1880 					}
1881 					if( min_dist < 1.0f )
1882 						set_pixel( int32(x), int32(y), color,
1883 						           uint8( float(solid)* (1.0f-sqrt(min_dist)) ) );
1884 				}
1885 			}
1886 		}
1887 	}
1888 	else
1889 	{
1890 		nroflines *= 2;
1891 		for( uint32 i=0; i<nroflines; i+=2 )
1892 			line( int32(vertex[lines[i]]), int32(vertex[lines[i]+1]),
1893 			      int32(vertex[lines[i+1]]), int32(vertex[lines[i+1]+1]), color, solid );
1894 	}
1895 }
1896 
1897 } // end of namespace picture_h
1898