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