1 /*--License:
2 	Kyra Sprite Engine
3 	Copyright Lee Thomason (Grinning Lizard Software) 2001-2005
4 	www.grinninglizard.com/kyra
5 	www.sourceforge.net/projects/kyra
6 
7 	Kyra is provided under the LGPL.
8 
9 	I kindly request you display a splash screen (provided in the HTML documentation)
10 	to promote Kyra and acknowledge the software and everyone who has contributed to it,
11 	but it is not required by the license.
12 
13 --- LGPL License --
14 
15     This library is free software; you can redistribute it and/or
16     modify it under the terms of the GNU Lesser General Public
17     License as published by the Free Software Foundation; either
18     version 2.1 of the License, or (at your option) any later version.
19 
20     This library is distributed in the hope that it will be useful,
21     but WITHOUT ANY WARRANTY; without even the implied warranty of
22     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23     Lesser General Public License for more details.
24 
25     You should have received a copy of the GNU Lesser General Public
26     License along with this library; if not, write to the Free Software
27     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 
29 	The full text of the license can be found in lgpl.txt
30 */
31 #include "pixelblock.h"
32 #include "SDL.h"
33 #include "SDL_rwops.h"
34 #include "SDL_endian.h"
35 #include "../../grinliz/glmemorypool.h"
36 #include "engine.h"
37 #include "ogltexture.h"
38 #include "../../grinliz/glgeometry.h"
39 
40 using namespace grinliz;
41 
42 #ifdef DEBUG
43 	U32 KrPixelBlock::numRGBA = 0;
44 #endif
45 
46 
KrPixelBlock()47 KrPixelBlock::KrPixelBlock()
48 {
49 	flags = 0;
50 	block = NULL;
51 	size.x = size.y = 0;
52 	texture = 0;
53 }
54 
55 
~KrPixelBlock()56 KrPixelBlock::~KrPixelBlock()
57 {
58 	if ( ! ( flags & MEMORYPOOL ) )
59 		delete [] block;
60 	delete texture;
61 }
62 
63 
Create(KrPaintInfo * surface,int x,int y,int width,int height)64 bool KrPixelBlock::Create(	KrPaintInfo* surface,
65 							int x, int y, int width, int height )
66 {
67 	int i, j;
68 
69 	size.x = width;
70 	size.y = height;
71 
72 	KrPainter painter( surface );
73 	GLASSERT( block == 0 );
74 	block = new KrRGBA[ width * height ];
75 	flags = 0;
76 
77 	for( i=0; i<width; i++ )
78 	{
79 		for( j=0; j<height; j++ )
80 		{
81 			KrRGBA rgba;
82 			painter.BreakPixel( x+i, y+j, &rgba );
83 
84 			// Check for alpha:
85 			if ( rgba.c.alpha != KrRGBA::KR_OPAQUE )
86 			{
87 				flags |= ALPHA;
88 			}
89 			block[ j*width + i ] = rgba;
90 		}
91 	}
92 	return true;
93 }
94 
95 
Create(int width,int height,bool alphaSupport)96 bool KrPixelBlock::Create( int width, int height, bool alphaSupport )
97 {
98 	flags = 0;
99 	if ( alphaSupport) flags |= ALPHA;
100 	size.x = width;
101 	size.y = height;
102 	GLASSERT( block == 0 );
103 
104 	block = new KrRGBA[ width * height ];
105 	//memset( block, 0, width * height * sizeof( KrRGBA ) );
106 
107 	// Need to get alpha channel correct for openGL
108 	KrRGBA color;
109 	color.Set( 0, 0, 0, alphaSupport ? 0 : 255 );	// if alpha not supported, surface is black, else transparent
110 	U32 v = color.all;
111 	U32 count = width * height;
112 	U32* block32 = (U32*) block;
113 
114 	for( U32 i=0; i<count; ++i )
115 		block32[i] = v;
116 
117 	return true;
118 }
119 
120 
Write(SDL_RWops * fp)121 bool KrPixelBlock::Write( SDL_RWops* fp )
122 {
123 	#ifdef DEBUG
124 		GLOUTPUT(( "Writing tile size=(%d,%d) (", size.x, size.y ));
125 		if ( flags & ALPHA ) GLOUTPUT(( "ALPHA " ));
126 		GLOUTPUT(( ")\n" ));
127 	#endif
128 	SDL_WriteLE32( fp, flags );
129 	SDL_WriteLE32( fp, size.x );
130 	SDL_WriteLE32( fp, size.y );
131 
132 	for ( int i=0; i<( size.x * size.y ); i++ )
133 	{
134 		SDL_RWwrite( fp, &block[i].c.red,   1, 1);
135 		SDL_RWwrite( fp, &block[i].c.green, 1, 1);
136 		SDL_RWwrite( fp, &block[i].c.blue,  1, 1);
137 		if ( flags & ALPHA )
138 			SDL_RWwrite( fp, &block[i].c.alpha, 1, 1);
139 	}
140 	return true;
141 }
142 
143 
Read(SDL_RWops * data)144 bool KrPixelBlock::Read( SDL_RWops* data )
145 {
146 	flags = SDL_ReadLE32( data );
147 	size.x = SDL_ReadLE32( data );
148 	size.y = SDL_ReadLE32( data );
149 	GLASSERT( block == 0 );
150 
151 	block = 0;
152 	if ( KrRle::memoryPoolRGBA )
153 	{
154 		block = (KrRGBA*) KrRle::memoryPoolRGBA->Alloc( size.x * size.y * sizeof( KrRGBA ) );
155 		flags |= MEMORYPOOL;
156 	}
157 	if ( !block )
158 	{
159 		block = new KrRGBA[ size.x * size.y ];
160 	}
161 	#ifdef DEBUG
162 		numRGBA += size.x * size.y;
163 	#endif
164 
165 // 	block = new KrRGBA[ size.x * size.y ];
166 
167 	for ( int i=0; i<size.x*size.y; i++ )
168 	{
169 		SDL_RWread( data, &block[i].c.red,   1, 1);
170 		SDL_RWread( data, &block[i].c.green, 1, 1);
171 		SDL_RWread( data, &block[i].c.blue,  1, 1);
172 
173 		if ( flags & ALPHA )
174 			SDL_RWread( data, &block[i].c.alpha, 1, 1);
175 		else
176 			block[i].c.alpha = 255;
177 	}
178 	return true;
179 }
180 
181 
CalculateBounds(const KrMatrix2 & xForm,Rectangle2I * bounds) const182 void KrPixelBlock::CalculateBounds( const KrMatrix2& xForm, Rectangle2I* bounds ) const
183 {
184 	bounds->min.x = xForm.x.ToIntRound();
185 	bounds->min.y = xForm.y.ToIntRound();
186 	bounds->max.x = ( xForm.xScale * size.x + xForm.x ).ToIntRound() - 1;
187 	bounds->max.y = ( xForm.yScale * size.y + xForm.y ).ToIntRound() - 1;
188 
189 	#ifdef DEBUG
190 		if ( xForm.xScale == 1 && xForm.yScale == 1 )
191 		{
192 			GLASSERT( bounds->Height() == size.y );
193 			GLASSERT( bounds->Width()  == size.x );
194 		}
195 		else
196 		{
197 			GLASSERT( bounds->Width() <= ( xForm.xScale * size.x ).ToIntRoundUp() );
198 			GLASSERT( bounds->Width() >= ( xForm.xScale * size.x ).ToInt() );
199 			GLASSERT( bounds->Height()<= ( xForm.yScale * size.y ).ToIntRoundUp() );
200 			GLASSERT( bounds->Height()>= ( xForm.yScale * size.y ).ToInt() );
201 		}
202 	#endif
203 }
204 
205 
DrawScaledDown(KrPaintInfo * info,const KrMatrix2 & xForm,const KrColorTransform & cForm,const Rectangle2I & clipping)206 void KrPixelBlock::DrawScaledDown(	KrPaintInfo* info,
207 									const KrMatrix2& xForm,
208 									const KrColorTransform& cForm,
209 									const Rectangle2I& clipping )
210 {
211 	GLASSERT( !info->OpenGL() );
212 
213 	Rectangle2I tBounds;
214 	CalculateBounds( xForm, &tBounds );
215 
216 	Rectangle2I isect = tBounds;
217 	isect.DoIntersection( clipping );
218 
219 	if ( isect.IsValid() )
220 	{
221 		int txOffset = isect.min.x - tBounds.min.x;
222 		int tyOffset = isect.min.y - tBounds.min.y;
223 
224 		// A square blit won't introduce an alpha:
225 		KrPaintFunc blitter = info->GetBlitter( ( flags & ALPHA ), cForm );
226 
227 		// Every pixel in the target 'increments' the source
228 		// by a certain amount. This is the integer scaling factor
229 		// between source and target.
230 		U32 xInc = 0x10000 * Width()  / tBounds.Width();
231 		U32 yInc = 0x10000 * Height() / tBounds.Height();
232 
233 		// The 'error' terms. Every time the exceed x10000,
234 		// its time to move a pixel in source. The distance
235 		// traveled on the target is *always* one pixel.
236 		U32 xError = ( txOffset * xInc );
237 		U32 yError = ( tyOffset * yInc );
238 
239 		// Every new row will have the same x-error:
240 		U32 xErrorPerRow = xError & 0xffff;
241 
242 		// The low part of error contains the current error;
243 		// the high part the # of pixels to move in source.
244 		KrRGBA* rowSource = block
245 							+ ( ( xError & 0xffff0000 ) >> 16 )
246 							+ ( ( yError & 0xffff0000 ) >> 16 ) * size.x;
247 		xError &= 0xffff;
248 		yError &= 0xffff;
249 		KrRGBA* source;
250 
251 		// Now set up the target:
252 		U8* rowTarget = (U8*) info->pixels
253 						+ isect.min.y * info->pitch
254 						+ isect.min.x * info->bytesPerPixel;
255 		U8* target;
256 
257 
258 		int iWidth  = isect.Width();
259 		int iHeight = isect.Height();
260 
261 		for ( int j=0; j<iHeight; ++j )
262 		{
263 			target = rowTarget;
264 			source = rowSource;
265 
266 			for( int i=0; i<iWidth; ++i )
267 			{
268 				// This one is a little different. It is always a
269 				// downsample, and sums all the colors over the downsample
270 				// rectangle.
271 				U32 color[4] = { 0, 0, 0, 0 };
272 
273 				GLASSERT( xInc >= 0x1000 );
274 				GLASSERT( yInc >= 0x1000 );
275 				U32 dx = ( xInc + xError ) >> 16;
276 				U32 dy = ( yInc + yError ) >> 16;
277 				int subSize = 0;	// For anti-artifacting, we don't necessary use every pixel.
278 
279 				KrRGBA* row = source;
280 				KrRGBA* scan;
281 
282 				for( unsigned jj=0; jj<dy; ++jj )
283 				{
284 					scan = row;
285 					for( unsigned ii=0; ii<dx; ++ii )
286 					{
287 						++scan;
288 						if ( scan->c.alpha != 0 )
289 						{
290 							++subSize;
291 							for( unsigned k=0; k<4; ++k )
292 							{
293 								color[k] += scan->array[k];
294 							}
295 						}
296 					}
297 					row += size.x;	// the scanline for a canvas
298 				}
299 				if ( subSize != 0 )
300 				{
301 					for( int k=0; k<4; ++k )
302 					{
303 						color[k] /= subSize;
304 					}
305 				}
306 				else
307 				{
308 					color[0] = 0;
309 					color[1] = 0;
310 					color[2] = 0;
311 					color[3] = 0;
312 				}
313 				GLASSERT( color[0] <= 255 );
314 				GLASSERT( color[1] <= 255 );
315 				GLASSERT( color[2] <= 255 );
316 				GLASSERT( color[3] <= 255 );
317 				#ifdef DEBUG
318 					if ( !(flags & ALPHA) ) GLASSERT( color[ KrRGBA::ALPHA ] == 255 );
319 				#endif
320 
321 				KrRGBA buffer;
322 				buffer.Set( color[ KrRGBA::RED ],
323 							color[ KrRGBA::GREEN ],
324 							color[ KrRGBA::BLUE ],
325 							color[ KrRGBA::ALPHA ] );
326 
327 				blitter( info, target, &buffer, 1, cForm );
328 
329 				target += info->bytesPerPixel;
330 				xError += xInc;
331 				while ( xError & 0xffff0000 )
332 				{
333 					xError -= 0x10000;
334 					++source;
335 				}
336 			}
337 
338 			rowTarget += info->pitch;
339 
340 			yError += yInc;
341 			while ( yError & 0xffff0000 )
342 			{
343 				yError -= 0x10000;
344 				rowSource += size.x;	// also pitch, for canvases
345 			}
346 
347 			xError = xErrorPerRow;
348 		}
349 	}
350 }
351 
352 
DrawScaledLinear(KrPaintInfo * info,const KrMatrix2 & xForm,const KrColorTransform & cForm,const Rectangle2I & clipping)353 void KrPixelBlock::DrawScaledLinear(	KrPaintInfo* info,
354 										const KrMatrix2& xForm,
355 										const KrColorTransform& cForm,
356 										const Rectangle2I& clipping )
357 {
358 	GLASSERT( !info->OpenGL() );
359 
360 	Rectangle2I tBounds;
361 	CalculateBounds( xForm, &tBounds );
362 
363 	Rectangle2I isect = tBounds;
364 	isect.DoIntersection( clipping );
365 
366 	if ( isect.IsValid() )
367 	{
368 		int txOffset = isect.min.x - tBounds.min.x;
369 		int tyOffset = isect.min.y - tBounds.min.y;
370 
371 		// A square blit won't introduce an alpha:
372 		KrPaintFunc blitter = info->GetBlitter( ( flags & ALPHA ), cForm );
373 
374 		// Every pixel in the target 'increments' the source
375 		// by a certain amount. This is the integer scaling factor
376 		// between source and target. Note however, that we need
377 		// one less source, since the interpolation will be between
378 		// the source pixel and the one to the right and down.
379 		U32 xInc = 0x10000 * ( Width() - 1 )  / tBounds.Width();
380 		U32 yInc = 0x10000 * ( Height() - 1 ) / tBounds.Height();
381 
382 		// The 'error' terms. Every time the exceed x10000,
383 		// its time to move a pixel in source. The distance
384 		// traveled on the target is *always* one pixel.
385 		U32 xError = ( txOffset * xInc );
386 		U32 yError = ( tyOffset * yInc );
387 
388 		// Every new row will have the same x-error:
389 		U32 xErrorPerRow = xError & 0xffff;
390 
391 		// The low part of error contains the current error;
392 		// the high part the # of pixels to move in source.
393 		KrRGBA* rowSource = block
394 							+ ( ( xError & 0xffff0000 ) >> 16 )
395 							+ ( ( yError & 0xffff0000 ) >> 16 ) * size.x;
396 		xError &= 0xffff;
397 		yError &= 0xffff;
398 		KrRGBA* source0;
399 		KrRGBA* source1;
400 
401 		// Now set up the target:
402 		U8* rowTarget = (U8*) info->pixels
403 						+ isect.min.y * info->pitch
404 						+ isect.min.x * info->bytesPerPixel;
405 		U8* target;
406 
407 
408 		int iWidth  = isect.Width();
409 		int iHeight = isect.Height();
410 
411 		for ( int j=0; j<iHeight; ++j )
412 		{
413 			target = rowTarget;
414 			source0 = rowSource;
415 			source1 = rowSource + size.x;
416 
417 			KrRGBA color, c00, c01, c10, c11;
418 
419 			for( int i=0; i<iWidth; ++i )
420 			{
421 				// We now copy and process, for anti-artifacting
422 				c00 = *source0;
423 				c10 = *(source0+1);
424 				c01 = *source1;
425 				c11 = *(source1+1);
426 				int k;
427 
428 // 				for( k=KrRGBA::START; k<KrRGBA::END; ++k )
429 // 				{
430 // 					U32 g0, g1;
431 //
432 // 					// interpelate on scanline, top
433 // 					g0 = (   (( c00.array[k] * ( 0x10000 - xError ) ) >> 16 ) * ( c00.c.alpha + 1 )
434 // 						   + (( c10.array[k] * ( xError ) ) >> 16 ) * ( c10.c.alpha + 1 ) ) / ( ( c00.c.alpha + c10.c.alpha ) / 2 + 1);
435 // 					// interpolate on scanline, bottom
436 // 					g1 = (   (( c01.array[k] * ( 0x10000 - xError ) ) >> 16 ) * ( c01.c.alpha + 1 )
437 // 						   + (( c11.array[k] * ( xError ) ) >> 16 ) * ( c11.c.alpha + 1 ) ) / ( ( c01.c.alpha + c11.c.alpha ) / 2 + 1);
438 // 					// interpolate between scanlines.
439 // 					color.array[k]  = (   (( g0 * ( 0x10000 - yError ) ) >> 16 )
440 // 										+ (( g1 * ( yError ) ) >> 16 ) );
441 // 				}
442 //
443 // 				k = KrRGBA::ALPHA;
444 				for( k=0; k<4; ++k )
445 				{
446 					U32 g0, g1;
447 
448 					// interpelate on scanline, top
449 					g0 = (   (( c00.array[k] * ( 0x10000 - xError ) ) >> 16 )
450 						   + (( c10.array[k] * ( xError ) ) >> 16 ) );
451 					// interpolate on scanline, bottom
452 					g1 = (   (( c01.array[k] * ( 0x10000 - xError ) ) >> 16 )
453 						   + (( c11.array[k] * ( xError ) ) >> 16 ) );
454 					// interpolate between scanlines.
455 					color.array[k]  = (   (( g0 * ( 0x10000 - yError ) ) >> 16 )
456 										+ (( g1 * ( yError ) ) >> 16 ) );
457 				}
458 
459 				blitter( info, target, &color, 1, cForm );
460 				target += info->bytesPerPixel;
461 
462 				xError += xInc;
463 				while ( xError & 0xffff0000 )
464 				{
465 					xError -= 0x10000;
466 					++source0;
467 					++source1;
468 					GLASSERT( source0 < rowSource + size.x );
469 					GLASSERT( source1 < rowSource + size.x * size.y );
470 				}
471 			}
472 
473 			rowTarget += info->pitch;
474 
475 			yError += yInc;
476 			while ( yError & 0xffff0000 )
477 			{
478 				yError -= 0x10000;
479 				rowSource += size.x;	// also pitch, for canvases
480 			}
481 
482 			xError = xErrorPerRow;
483 		}
484 	}
485 }
486 
487 
DrawScaledFast(KrPaintInfo * info,const KrMatrix2 & xForm,const KrColorTransform & cForm,const Rectangle2I & clipping,bool invert)488 void KrPixelBlock::DrawScaledFast(	KrPaintInfo* info,
489 									const KrMatrix2& xForm,
490 									const KrColorTransform& cForm,
491 									const Rectangle2I& clipping,
492 									bool invert )
493 {
494 	GLASSERT( !info->OpenGL() );
495 
496 	Rectangle2I tBounds;
497 	CalculateBounds( xForm, &tBounds );
498 
499 	Rectangle2I isect = tBounds;
500 	isect.DoIntersection( clipping );
501 
502 	if ( isect.IsValid() )
503 	{
504 		int txOffset = isect.min.x - tBounds.min.x;
505 		int tyOffset = isect.min.y - tBounds.min.y;
506 
507 		// A square blit won't introduce an alpha:
508 		KrPaintFunc blitter = info->GetBlitter( ( flags & ALPHA ), cForm );
509 
510 		// Every pixel in the target 'increments' the source
511 		// by a certain amount. This is the integer scaling factor
512 		// between source and target.
513 		U32 xInc = 0x10000 * Width()  / tBounds.Width();
514 		U32 yInc = 0x10000 * Height() / tBounds.Height();
515 
516 		// The 'error' terms. Every time the exceed x10000,
517 		// its time to move a pixel in source. The distance
518 		// traveled on the target is *always* one pixel.
519 		U32 xError = ( txOffset * xInc );
520 		U32 yError = ( tyOffset * yInc );
521 
522 		// Every new row will have the same x-error:
523 		U32 xErrorPerRow = xError & 0xffff;
524 
525 		int yBias = 1;
526 		int yStartBias = 0;
527 		if ( invert )
528 		{
529 			yBias = -1;
530 			yStartBias = size.y - 1;
531 		}
532 
533 		// The low part of error contains the current error;
534 		// the high part the # of pixels to move in source.
535 		KrRGBA* rowSource = block
536 							+ ( ( xError & 0xffff0000 ) >> 16 )
537 							+ ( yBias * ( ( yError & 0xffff0000 ) >> 16 ) + yStartBias ) * size.x;
538 		GLASSERT( rowSource >= block );
539 		GLASSERT( rowSource <  block + size.y * size.x );
540 
541 		xError &= 0xffff;
542 		yError &= 0xffff;
543 		KrRGBA* source;
544 
545 		// Now set up the target:
546 		U8* rowTarget = (U8*) info->pixels
547 						+ isect.min.y * info->pitch
548 						+ isect.min.x * info->bytesPerPixel;
549 		U8* target;
550 
551 
552 		int iWidth  = isect.Width();
553 		int iHeight = isect.Height();
554 
555 		for ( int j=0; j<iHeight; ++j )
556 		{
557 			target = rowTarget;
558 			source = rowSource;
559 
560 			for( int i=0; i<iWidth; ++i )
561 			{
562 				GLASSERT( source >= block );
563 				GLASSERT( source <  block + size.y * size.x );
564 				GLASSERT( target >= (U8*) info->pixels );
565 				GLASSERT( target <  (U8*) info->pixels + info->pitch * info->height );
566 
567 				blitter( info, target, source, 1, cForm );
568 				target += info->bytesPerPixel;
569 
570 				xError += xInc;
571 				while ( xError & 0xffff0000 )
572 				{
573 					xError -= 0x10000;
574 					++source;
575 				}
576 			}
577 
578 			rowTarget += info->pitch;
579 
580 			yError += yInc;
581 			while ( yError & 0xffff0000 )
582 			{
583 				yError -= 0x10000;
584 				rowSource += yBias * size.x;	// also pitch, for canvases
585 			}
586 
587 			xError = xErrorPerRow;
588 		}
589 	}
590 }
591 
592 
DrawScaled(KrPaintInfo * info,const KrMatrix2 & xForm,const KrColorTransform & cForm,const Rectangle2I & clipping,int quality,bool invert)593 void KrPixelBlock::DrawScaled(	KrPaintInfo* info,
594 								const KrMatrix2& xForm,
595 								const KrColorTransform& cForm,
596 								const Rectangle2I& clipping,
597 								int quality,
598 								bool invert )
599 {
600 	GLASSERT( !info->OpenGL() );
601 
602 	if ( invert || quality == KrQualityFast )
603 	{
604 		DrawScaledFast( info, xForm, cForm, clipping, invert );
605 	}
606 	else if ( quality == KrQualityLinear )
607 	{
608 		DrawScaledLinear( info, xForm, cForm, clipping );
609 	}
610 	else
611 	{
612 		GLASSERT( quality == KrQualityAdaptive );
613 		if (    xForm.xScale.v <= GlFixed_1 / 2
614 		     && xForm.yScale.v <= GlFixed_1 / 2 )
615 		{
616 			DrawScaledDown( info, xForm, cForm, clipping );
617 		}
618 		else
619 		{
620 			DrawScaledLinear( info, xForm, cForm, clipping );
621 		}
622 	}
623 }
624 
625 
LoadNewTexture()626 void KrPixelBlock::LoadNewTexture()
627 {
628 	if ( texture )
629 	{
630 		texture->SetTexture( block, size.x, size.y );
631 	}
632 }
633 
634 
DrawOpenGL(KrPaintInfo * paintInfo,const KrMatrix2 & xForm,const KrColorTransform & cForm,const Rectangle2I & clipping,int rotation)635 void KrPixelBlock::DrawOpenGL(		KrPaintInfo* paintInfo,
636 									const KrMatrix2& xForm,
637 									const KrColorTransform& cForm,
638 									const Rectangle2I& clipping,
639 									int rotation )
640 {
641 	#ifdef KYRA_SUPPORT_OPENGL
642 	GLASSERT( paintInfo->OpenGL() );
643 
644 	if ( !texture )
645 	{
646 		KrTextureManager* manager = KrTextureManager::Instance();
647 		if ( manager )
648 		{
649 			// Create a canvas resource with the data we need.
650 			texture = manager->CreateTexture( block, size.x, size.y );
651 		}
652 	}
653 
654 	GLASSERT( texture );
655 	if ( !texture ) return;
656 
657 	glBindTexture( GL_TEXTURE_2D, texture->Id() );
658 
659 	paintInfo->SetOpenGLTextureMode( Alpha(), cForm, xForm.IsScaled(), texture );
660 
661 	Rectangle2I bounds;
662 	CalculateBounds( xForm, &bounds );
663 
664 	Vector2F texCoord[ 4 ];
665 
666 	texCoord[ (rotation+0) % 4 ].x = texture->Bounds().min.x;
667 	texCoord[ (rotation+0) % 4 ].y = texture->Bounds().min.y;
668 
669 	texCoord[ (rotation+1) % 4 ].x = texture->Bounds().max.x;
670 	texCoord[ (rotation+1) % 4 ].y = texture->Bounds().min.y;
671 
672 	texCoord[ (rotation+2) % 4 ].x = texture->Bounds().max.x;
673 	texCoord[ (rotation+2) % 4 ].y = texture->Bounds().max.y;
674 
675 	texCoord[ (rotation+3) % 4 ].x = texture->Bounds().min.x;
676 	texCoord[ (rotation+3) % 4 ].y = texture->Bounds().max.y;
677 
678 	if ( rotation > 3 )
679 	{
680 		Swap( &texCoord[0], &texCoord[3] );
681 		Swap( &texCoord[1], &texCoord[2] );
682 	}
683 
684 	glBegin( GL_QUADS );
685 	{
686 		glTexCoord2f( texCoord[0].x,	texCoord[0].y );
687 		glVertex3i( bounds.min.x,				bounds.min.y,					0 );
688 
689 		glTexCoord2f( texCoord[1].x,	texCoord[1].y );
690 		glVertex3i( bounds.min.x+bounds.Width(), bounds.min.y,					0 );
691 
692 		glTexCoord2f( texCoord[2].x,	texCoord[2].y );
693 		glVertex3i( bounds.min.x+bounds.Width(), bounds.min.y+bounds.Height(),	0 );
694 
695 		glTexCoord2f( texCoord[3].x,	texCoord[3].y );
696 		glVertex3i( bounds.min.x,				bounds.min.y+bounds.Height(),	0 );
697 	}
698 	glEnd();
699 	GLASSERT( glGetError() == GL_NO_ERROR );
700 	#endif
701 }
702 
703 
Draw(KrPaintInfo * paintInfo,const KrMatrix2 & xForm,bool invert,const KrColorTransform & cForm,const Rectangle2I & clipping,int quality)704 void KrPixelBlock::Draw(	KrPaintInfo* paintInfo,
705 							const KrMatrix2& xForm,
706 							bool invert,
707 							const KrColorTransform& cForm,
708 							const Rectangle2I& clipping,
709 							int quality )
710 {
711 	if ( paintInfo->OpenGL() )
712 	{
713 		#ifdef KYRA_SUPPORT_OPENGL
714 			GLASSERT( invert == false );	// This path should only come from a canvas, which does no invert.
715 			DrawOpenGL( paintInfo, xForm, cForm, clipping, 0 );
716 		#else
717 			GLASSERT( 0 );
718 		#endif
719 		return;
720 	}
721 
722 	if ( xForm.IsScaled() )
723 	{
724 		//GLASSERT( invert == false );	// we don't scale tiles.
725 		DrawScaled( paintInfo, xForm, cForm, clipping, quality, invert );
726 		return;
727 	}
728 
729 	Rectangle2I bounds;
730 	CalculateBounds( xForm, &bounds );
731 
732 	Rectangle2I isect = bounds;
733 	isect.DoIntersection( clipping );
734 
735 	int j;
736 	int yOffset		= isect.min.y - bounds.min.y;
737 	int xOffset		= isect.min.x - bounds.min.x;
738 	int width		= isect.Width();
739 	int height		= isect.Height();
740 	int sourcePitch	= size.x;
741 
742 	U8* target =   (U8*) paintInfo->pixels
743 				   + isect.min.y * paintInfo->pitch
744 				   + isect.min.x * paintInfo->bytesPerPixel;
745 
746 	// for rotations 4-7, we run the source in reverse.
747 	KrRGBA* source;
748 	if ( !invert )
749 	{
750 		source =   block
751 				 + yOffset * size.x
752 				 + xOffset;
753 	}
754 	else
755 	{
756 		source =   block
757 				 + ( size.y - yOffset - 1) * size.x
758 				 + xOffset;
759 		sourcePitch = -sourcePitch;
760 	}
761 
762 	if ( width > 0 && height > 0 )
763 	{
764 		KrPaintFunc blitter = paintInfo->GetBlitter( ( flags & ALPHA ), cForm );
765 		if ( blitter )
766 		{
767 			for ( j=0;
768 				  j<height;
769 				  j++, source += sourcePitch, target += paintInfo->pitch )
770 			{
771 				blitter( paintInfo, target, source, width, cForm );
772 			}
773 		}
774 	}
775 }
776 
777 
CountComponents(U32 * numRGBA)778 void KrPixelBlock::CountComponents( U32* numRGBA )
779 {
780 	*numRGBA = size.x * size.y;
781 }
782