1 #include <stdint.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "base.h"
6 #include "Exception.h"
7 #include "Log.h"
8 #include "MathBase.h"
9 #include "Image.h"
10
11 namespace DisplayOutput
12 {
13
14 /*
15 CImage().
16
17 */
CImage()18 CImage::CImage() : m_Format( eImage_None ), m_Width(0), m_Height(0), m_nMipMaps(0), m_bRef( false )
19 {
20 }
21
22 /*
23 CImage().
24
25 */
Copy(const CImage & _image,const uint32 _mipLevel)26 void CImage::Copy( const CImage &_image, const uint32 _mipLevel )
27 {
28 g_Log->Info( "CImage( const CImage & )..." );
29
30 if( _mipLevel == 0 )
31 {
32 m_Width = _image.m_Width;
33 m_Height = _image.m_Width;
34 m_nMipMaps = _image.m_nMipMaps;
35 m_Format = _image.m_Format;
36 m_bRef = _image.isReference();
37
38 if( !m_bRef )
39 {
40 uint32 size = getMipMappedSize( 0, m_nMipMaps );
41 m_spData = new Base::CAlignedBuffer( size );
42 if (m_spData->IsValid())
43 memcpy( m_spData->GetBufferPtr(), _image.GetData( 0 ), size );
44 }
45 }
46 else
47 {
48 m_Width = _image.GetWidth( _mipLevel );
49 m_Height = _image.GetHeight( _mipLevel );
50 m_nMipMaps = 1;//getNumberOfMipMapsFromDimesions();
51 m_Format = _image.m_Format;
52 m_bRef = false;
53
54 if( !m_bRef )
55 {
56 uint32 size = getMipMappedSize( 0, m_nMipMaps );
57 m_spData = new Base::CAlignedBuffer( size );
58 if ( m_spData->IsValid() )
59 memcpy( m_spData->GetBufferPtr(), _image.GetData( _mipLevel ), size );
60 }
61 }
62 }
63
64 /*
65 CImage().
66
67 */
Create(const uint32 _w,const uint32 _h,const eImageFormat _format,const bool _bMipmaps,const bool _bRef)68 void CImage::Create( const uint32 _w, const uint32 _h, const eImageFormat _format, const bool _bMipmaps, const bool _bRef )
69 {
70 // g_Log->Info( "Create( %d, %d, 0x%x )", _w, _h, _format );
71
72 m_Width = _w;
73 m_Height = _h;
74 m_nMipMaps = _bMipmaps ? getNumberOfMipMapsFromDimesions() : 1;
75 m_Format = _format;
76 m_bRef = _bRef ;
77
78 if( !m_bRef )
79 {
80 uint32 size = getMipMappedSize( 0, m_nMipMaps );
81 m_spData = new Base::CAlignedBuffer( size );
82 memset( m_spData->GetBufferPtr(), 0, size );
83 }
84 }
85
86 /*
87 ~CImage().
88
89 */
~CImage()90 CImage::~CImage()
91 {
92 // g_Log->Info( "~CImage()..." );
93 }
94
95 /*
96 GetData().
97
98 */
GetData(const uint32 _mipLevel) const99 uint8 *CImage::GetData( const uint32 _mipLevel ) const
100 {
101 if( m_bRef && m_spData.IsNull() )
102 return( NULL );
103
104 const Base::CAlignedBuffer *ab = m_spData.GetRepPtr()->getRealPointer();
105
106 if( _mipLevel == 0 )
107 return( ab->GetBufferPtr() );
108
109 return( _mipLevel < m_nMipMaps) ? ab->GetBufferPtr() + getMipMappedSize( 0, _mipLevel ) : NULL;
110 }
111
112 /*
113 SetData().
114
115 */
SetData(uint8 *)116 void CImage::SetData( uint8* /*_pData*/ )
117 {
118 //if( !m_bRef /*|| m_nMipMaps > 1*/ )
119 //return;
120
121 //m_pData = _pData;
122 }
123
124
125 /*
126 GetPitch().
127
128 */
GetPitch(const uint32 _level) const129 uint32 CImage::GetPitch( const uint32 _level ) const
130 {
131 return( GetWidth( _level ) * m_Format.getBPPixel() );
132 }
133
134
135 /*
136 */
getMipMappedSize(const uint32 _firstMipMapLevel,const uint32 _nMipMapLevels) const137 uint32 CImage::getMipMappedSize( const uint32 _firstMipMapLevel, const uint32 _nMipMapLevels ) const
138 {
139 return( getMipMappedSize( _firstMipMapLevel, _nMipMapLevels, m_Format ) );
140 }
141
142 /*int Image::getPixelCount(const int firstMipMapLevel, int nMipMapLevels) const {
143 int w = getWidth (firstMipMapLevel);
144 int h = getHeight(firstMipMapLevel);
145 int d = getDepth (firstMipMapLevel);
146 int size = 0;
147 while (nMipMapLevels){
148 size += w * h * d;
149 w >>= 1;
150 h >>= 1;
151 d >>= 1;
152 if (w + h + d == 0) break;
153 if (w == 0) w = 1;
154 if (h == 0) h = 1;
155 if (d == 0) d = 1;
156
157 nMipMapLevels--;
158 }
159
160 return (depth == 0)? 6 * size : size;
161 }*/
162
163 /*
164 getMipMappedSize().
165
166 */
getMipMappedSize(const uint32 _firstMipMapLevel,const uint32 _nMipMapLevels,const CImageFormat & _format) const167 uint32 CImage::getMipMappedSize( const uint32 _firstMipMapLevel, const uint32 _nMipMapLevels, const CImageFormat &_format ) const
168 {
169 uint32 w = GetWidth( _firstMipMapLevel ) << 1;
170 uint32 h = GetHeight( _firstMipMapLevel ) << 1;
171
172 uint32 level = 0;
173 uint32 size = 0;
174
175 while( level < _nMipMapLevels && (w != 1 || h != 1) )
176 {
177 if( w > 1 ) w >>= 1;
178 if( h > 1 ) h >>= 1;
179
180 if( _format.isCompressed() )
181 size += ((w + 3) >> 2) * ((h + 3) >> 2);
182 else
183 size += w * h;
184
185 level++;
186 }
187
188 if( _format.isCompressed() )
189 size *= _format.getBPBlock();
190 else
191 size *= _format.getBPPixel();
192
193 return( size );
194 }
195
196
197 /*
198 getNumberOfMipMapsFromDimesions().
199
200 */
getNumberOfMipMapsFromDimesions(void) const201 uint32 CImage::getNumberOfMipMapsFromDimesions( void ) const
202 {
203 uint32 m = (m_Width > m_Height)? m_Width : m_Height;
204 uint32 i = 0;
205
206 while( m > 0 )
207 {
208 m >>= 1;
209 i++;
210 }
211
212 return( i );
213 }
214
215 /*
216 Load().
217
218 */
Load(const std::string & _fileName,const bool _calcMipmaps)219 bool CImage::Load( const std::string &_fileName, const bool _calcMipmaps )
220 {
221 std::string ext = "";
222
223 size_t offs = _fileName.find_last_of( '.', _fileName.size() );
224 if( offs != _fileName.size() )
225 ext = _fileName.substr( offs+1, _fileName.size()-1 );
226
227 if( ext == "" )
228 {
229 g_Log->Warning( "CImage::Load() No extension found for %s", _fileName.c_str() );
230 return( false );
231 }
232
233 bool foundExt = false;
234
235 // DDS?
236 if( ext == "dds" )
237 {
238 foundExt = true;
239 if( !LoadDDS( _fileName, _calcMipmaps ) )
240 return( false );
241 }
242
243 // TGA?
244 /*if( ext == "tga" )
245 {
246 foundExt = true;
247 if( !LoadTGA( _fileName, _calcMipmaps ) )
248 return( false );
249 }*/
250
251 // JPG?
252 /*if( ext == "jpg" )
253 {
254 foundExt = true;
255 if( !LoadJPG( _fileName, _calcMipmaps ) )
256 return( false );
257 }*/
258
259 // PNG?
260 if( ext == "png" )
261 {
262 foundExt = true;
263 if( !LoadPNG( _fileName, _calcMipmaps ) )
264 return( false );
265 }
266
267 // Unknown extension?
268 if( foundExt == false )
269 {
270 g_Log->Warning( "CImage::Load() Unknown extension for %s", _fileName.c_str() );
271 return( false );
272 }
273
274 if( _calcMipmaps && !m_Format.isCompressed() )
275 {
276 if( !GenerateMipmaps() )
277 return( false );
278 }
279
280 if( m_nMipMaps == 0 )
281 m_nMipMaps = 1;
282
283 // Done!
284 g_Log->Info( "CImage::Load( %s ): (%d x %d, %d MipMaps)", (const char *)_fileName.c_str(), m_Width, m_Height, m_nMipMaps );
285 return( true );
286 }
287
288 /*
289 GenerateMipmaps().
290
291 */
GenerateMipmaps(void)292 bool CImage::GenerateMipmaps( void )
293 {
294 // Check if the image is power of two.
295 uint32 w = Base::Math::ClosestPowerOfTwo( m_Width );
296 uint32 h = Base::Math::ClosestPowerOfTwo( m_Height );
297
298 if( w != m_Width || h != m_Height)
299 {
300 // TODO: resize?
301 g_Log->Error( "CImage::GenerateMipmaps(): Image is not power of two!" );
302 return( false );
303 }
304
305 m_nMipMaps = getNumberOfMipMapsFromDimesions();
306 return( createMipMaps() );
307 }
308
309 /*
310 Load().
311
312 */
Save(const std::string & _fileName)313 bool CImage::Save( const std::string &_fileName )
314 {
315 std::string ext = "";
316
317 size_t offs = _fileName.find_last_of( '.', _fileName.size() );
318 if( offs != _fileName.size() )
319 ext = _fileName.substr( offs+1, _fileName.size()-1 );
320
321 if( ext == "" )
322 {
323 g_Log->Warning( "CImage::Save() No extension found for %s", _fileName.c_str() );
324 return( false );
325 }
326
327 // DDS?
328 if( ext == "dds" )
329 {
330 if( !SaveDDS( _fileName ) )
331 return( false );
332 }
333
334 g_Log->Info( "CImage::Save( %s ): Complete!", (const char *)_fileName.c_str(), m_Width, m_Height, m_nMipMaps );
335 return( true );
336 }
337
338 /*
339 SaveDDS().
340
341 */
342
343 #define DDPF_ALPHAPIXELS 0x00000001
344 #define DDPF_FOURCC 0x00000004
345 #define DDPF_RGB 0x00000040
346
347 #define DDSD_CAPS 0x00000001
348 #define DDSD_HEIGHT 0x00000002
349 #define DDSD_WIDTH 0x00000004
350 #define DDSD_PITCH 0x00000008
351 #define DDSD_PIXELFORMAT 0x00001000
352 #define DDSD_MIPMAPCOUNT 0x00020000
353 #define DDSD_LINEARSIZE 0x00080000
354 #define DDSD_DEPTH 0x00800000
355 #define DDSCAPS_COMPLEX 0x00000008
356 #define DDSCAPS_TEXTURE 0x00001000
357 #define DDSCAPS_MIPMAP 0x00400000
358
359 //
360 #pragma pack (push, 1)
361
362 struct DDSHeader {
363 unsigned int ddsIdentifier;
364 unsigned int size;
365 unsigned int flags;
366 unsigned int height;
367 unsigned int width;
368 unsigned int pitchOrLinearSize;
369 unsigned int depth;
370 unsigned int nMipMaps;
371 unsigned int reserved[11];
372 unsigned int size2;
373 unsigned int flags2;
374 unsigned int fourCC;
375 unsigned int bpp;
376
377 unsigned int rBitMask;
378 unsigned int gBitMask;
379 unsigned int bBitMask;
380 unsigned int aBitMask;
381
382 unsigned int caps1;
383 unsigned int caps2;
384 unsigned int reserved2[3];
385 };
386 #pragma pack (pop)
387
SaveDDS(const std::string & _fileName)388 bool CImage::SaveDDS( const std::string &_fileName )
389 {
390 eImageFormat fmt = m_Format.getFormatEnum();
391
392 if( (fmt < eImage_I8 || fmt > eImage_RGBA8 ) && fmt != eImage_RGB565 && !m_Format.isCompressed() )
393 return( false );
394
395 uint32 nChannels = m_Format.GetChannels();
396
397 uint32 fourCC[] = { MCHAR4('D','X','T','1'),
398 MCHAR4('D','X','T','3'),
399 MCHAR4('D','X','T','5'),
400 MCHAR4('A','T','I','2') };
401
402 DDSHeader header = {
403 MCHAR4('D','D','S',' '),
404 124,
405 DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | static_cast<unsigned int>(m_nMipMaps > 1? DDSD_MIPMAPCOUNT : 0),
406 m_Height,
407 m_Width,
408 0,
409 0,
410 (m_nMipMaps > 1)? m_nMipMaps : 0,
411 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
412 32,
413 m_Format.isCompressed() ? DDPF_FOURCC : static_cast<unsigned int>((nChannels < 3)? 0x00020000 : DDPF_RGB) | static_cast<unsigned int>((nChannels & 1)? 0 : DDPF_ALPHAPIXELS),
414 m_Format.isCompressed() ? fourCC[ fmt - eImage_DXT1 ] : 0,
415 8 * nChannels,
416 static_cast<unsigned int>((nChannels >= 3)? 0x00ff0000 : 0xFF),
417 static_cast<unsigned int>((nChannels >= 3)? 0x0000ff00 : 0),
418 static_cast<unsigned int>((nChannels >= 3)? 0x000000ff : 0),
419 (nChannels >= 3)? 0xff000000 : (nChannels == 2)? 0xFF00 : 0,
420 DDSCAPS_TEXTURE | static_cast<unsigned int>(m_nMipMaps > 1? DDSCAPS_MIPMAP | DDSCAPS_COMPLEX : 0),
421 0,
422 { 0, 0, 0 },
423 };
424
425 FILE *pFile = fopen( _fileName.c_str(), "wb" );
426 if( !pFile )
427 return( false );
428
429 fwrite( &header, sizeof(header), 1, pFile );
430
431 uint32 size = getMipMappedSize( 0, m_nMipMaps );
432
433 bool bAlpha = false;
434
435 if( bAlpha && (fmt == eImage_RGB8 || fmt == eImage_RGBA8) )
436 flipChannels( m_spData->GetBufferPtr(), size / nChannels, nChannels );
437
438 fwrite( GetData( 0 ), size, 1, pFile );
439
440 // Flip back... =)
441 if( bAlpha && (fmt == eImage_RGB8 || fmt == eImage_RGBA8) )
442 flipChannels( m_spData->GetBufferPtr(), size / nChannels, nChannels );
443
444 fclose( pFile );
445
446 return( true );
447 }
448
449 /*
450 buildMipMap8().
451
452 */
buildMipMap8(uint8 * dest,uint8 * src,uint32 width,uint32 height,uint32 channels)453 void buildMipMap8( uint8 *dest, uint8 *src, uint32 width, uint32 height, uint32 channels )
454 {
455 uint32 xOff = (width < 2)? 0 : channels;
456 uint32 yOff = (height < 2)? 0 : width * channels;
457
458 for( uint32 y=0; y<height; y += 2 )
459 {
460 for( uint32 x=0; x<width; x += 2 )
461 {
462 for( uint32 i=0; i<channels; i++ )
463 {
464 *dest++ = ((src[0] + src[xOff] + src[yOff] + src[yOff + xOff]) + 2) >> 2;
465 src++;
466 }
467 src += xOff;
468 }
469 src += yOff;
470 }
471 }
472
473 /*
474 buildMipMap32f().
475
476 */
buildMipMap32f(fp4 * dest,fp4 * src,uint32 width,uint32 height,uint32 channels)477 void buildMipMap32f( fp4 *dest, fp4 *src, uint32 width, uint32 height, uint32 channels )
478 {
479 uint32 xOff = (width < 2)? 0 : channels;
480 uint32 yOff = (height < 2)? 0 : width * channels;
481
482 for( uint32 y=0; y<height; y += 2 )
483 {
484 for( uint32 x=0; x<width; x += 2 )
485 {
486 for( uint32 i=0; i<channels; i++ )
487 {
488 *dest++ = (src[0] + src[xOff] + src[yOff] + src[yOff + xOff]) * 0.25f;
489 src++;
490 }
491
492 src += xOff;
493 }
494
495 src += yOff;
496 }
497 }
498
499 /*
500 buildMipMapRGB565().
501
502 */
buildMipMapRGB565(uint16 * dest,uint16 * src,uint32 width,uint32 height)503 void buildMipMapRGB565( uint16 *dest, uint16 *src, uint32 width, uint32 height )
504 {
505 uint32 x,y,diff;
506 uint32 xOff = (width < 2)? 0 : 1;
507 uint32 yOff = (height < 2)? 0 : 1;
508
509 uint32 r,g,b;
510
511 diff = yOff * width;
512
513 for( y=0; y<height; y += 2 )
514 {
515 for( x=0; x<width; x += 2 )
516 {
517 r = (((src[0] >> 8) & 0xF8) + ((src[xOff] >> 8) & 0xF8) + ((src[diff] >> 8) & 0xF8) + ((src[diff + xOff] >> 8) & 0xF8));
518 g = (((src[0] >> 3) & 0xFC) + ((src[xOff] >> 3) & 0xFC) + ((src[diff] >> 3) & 0xFC) + ((src[diff + xOff] >> 3) & 0xFC));
519 b = (((src[0] << 3) & 0xF8) + ((src[xOff] << 3) & 0xF8) + ((src[diff] << 3) & 0xF8) + ((src[diff + xOff] << 3) & 0xF8));
520
521 *dest++ = (uint16)(((r << 6) & 0xF800) | ((g << 1) & 0x07E0) | ((b >> 5) & 0x1F));
522 src += 2;
523 }
524
525 src += width;
526 }
527 }
528
529 /*
530 createMipMaps().
531
532 */
createMipMaps(void)533 bool CImage::createMipMaps( void )
534 {
535 if( m_Format.isCompressed() )
536 return( false );
537
538 if( m_bRef )
539 return( false );
540
541 uint32 w = m_Width;
542 uint32 h = m_Height;
543
544 union {
545 uint8 *src;
546 uint16 *src16;
547 fp4 *src32f;
548 };
549
550 union {
551 uint8 *dest;
552 uint16 *dest16;
553 fp4 *dest32f;
554 };
555
556 if( m_nMipMaps <= 1 )
557 {
558 m_spData->Reallocate( getMipMappedSize() );
559 m_nMipMaps = getNumberOfMipMapsFromDimesions();
560 }
561
562 dest = m_spData->GetBufferPtr();
563
564 uint32 nChannels = m_Format.GetChannels();
565
566 while( w > 1 || h > 1 )
567 {
568 src = dest;
569
570 if( m_Format.isPlain() )
571 {
572 if( m_Format.isFloat() )
573 {
574 dest32f += w * h * nChannels;
575 buildMipMap32f(dest32f, src32f, w, h, nChannels);
576 }
577 else
578 {
579 dest += w * h * nChannels;
580 buildMipMap8(dest, src, w, h, nChannels);
581 }
582 }
583 else
584 {
585 dest16 += w * h;
586 buildMipMapRGB565(dest16, src16, w, h);
587 }
588
589 if( w > 1) w >>= 1;
590 if( h > 1) h >>= 1;
591 }
592
593 return( true );
594 }
595
596 /*
597 */
getNumPixels(const uint32 _firstMipMapLevel,const uint32 _nMipMapLevels) const598 uint32 CImage::getNumPixels( const uint32 _firstMipMapLevel, const uint32 _nMipMapLevels ) const
599 {
600 uint32 w = GetWidth( _firstMipMapLevel ) << 1;
601 uint32 h = GetHeight( _firstMipMapLevel ) << 1;
602 uint32 level = 0;
603 uint32 size = 0;
604
605 while( level < _nMipMapLevels && (w != 1 || h != 1) )
606 {
607 if( w > 1 ) w >>= 1;
608 if( h > 1 ) h >>= 1;
609
610 size += w * h;
611 level++;
612 }
613
614 return( size );
615 }
616
617 /*
618 Convert().
619
620 */
Convert(const eImageFormat _newFormatType)621 bool CImage::Convert( const eImageFormat _newFormatType )
622 {
623 if( _newFormatType == m_Format.m_Format )
624 return( false );
625
626 if( !m_Format.isPlain() )
627 return( false );
628
629 if( m_bRef )
630 return( false );
631
632 //
633 CImageFormat newFormat = CImageFormat( _newFormatType );
634 if( !newFormat.isPlain() )
635 return( false );
636
637 uint8 *src = m_spData->GetBufferPtr();
638 Base::spCAlignedBuffer newPixels = new Base::CAlignedBuffer( getMipMappedSize( 0, m_nMipMaps, newFormat ) );
639 uint8 *dest = newPixels->GetBufferPtr();
640
641 uint32 nPixels = getNumPixels( 0, m_nMipMaps );
642
643 if( m_Format.is( eImage_RGB8 ) && newFormat.is( eImage_RGBA8 ) )
644 {
645 // Fast path for RGB->RGBA8
646 do
647 {
648 dest[0] = src[0];
649 dest[1] = src[1];
650 dest[2] = src[2];
651 dest[3] = 255;
652 dest += 4;
653 src += 3;
654 } while ( --nPixels );
655 }
656 else
657 {
658 uint32 srcSize = m_Format.getBPPixel();
659 uint32 nSrcChannels = m_Format.GetChannels();
660
661 uint32 destSize = newFormat.getBPPixel();
662 uint32 nDestChannels = newFormat.GetChannels();
663
664 do
665 {
666 fp4 rgba[4];
667
668 rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0.0f;
669
670 if( m_Format.isFloat() )
671 {
672 for( uint32 i=0; i<nSrcChannels; i++ )
673 rgba[i] = ((fp4 *)src)[i];
674 }
675 else if( m_Format.m_Format >= eImage_I16 && m_Format.m_Format <= eImage_RGBA16 )
676 {
677 for( uint32 i=0; i<nSrcChannels; i++ )
678 rgba[i] = ((uint16 *)src)[i] * (1.0f / 65535.0f);
679 }
680 else
681 {
682 for( uint32 i=0; i<nSrcChannels; i++ )
683 rgba[i] = src[i] * (1.0f / 255.0f);
684 }
685
686 if( nSrcChannels < 4 ) rgba[3] = 1.0f;
687 if( nSrcChannels == 1 ) rgba[2] = rgba[1] = rgba[0];
688
689 if( nDestChannels == 1 )
690 rgba[0] = 0.30f * rgba[0] + 0.59f * rgba[1] + 0.11f * rgba[2];
691
692 if( newFormat.isFloat() )
693 {
694 for( uint32 i=0; i<nDestChannels; i++ )
695 ((fp4 *)dest)[i] = rgba[i];
696 }
697 else if( newFormat.m_Format >= eImage_I16 && newFormat.m_Format <= eImage_RGBA16 )
698 {
699 for( uint32 i=0; i<nDestChannels; i++ )
700 ((uint16 *)dest)[i] = (uint16)(65535 * Base::Math::Clamped( rgba[i], 0.0f, 1.1f ) + 0.5f );
701 }
702 /*else if( newFormat == FORMAT_RGB10A2)
703 {
704 *(uint *) dest =
705 (uint(1023.0f * saturate(rgba[0]) + 0.5f) << 22) |
706 (uint(1023.0f * saturate(rgba[1]) + 0.5f) << 12) |
707 (uint(1023.0f * saturate(rgba[2]) + 0.5f) << 2) |
708 (uint( 3.0f * saturate(rgba[3]) + 0.5f));
709 }*/
710 else
711 {
712 for( uint32 i=0; i<nDestChannels; i++ )
713 dest[i] = (uint8)( 255 * Base::Math::Clamped( rgba[i], 0.0f, 1.0f ) + 0.5f );
714 }
715
716 src += srcSize;
717 dest += destSize;
718
719 } while( --nPixels );
720 }
721
722 m_spData = newPixels;
723 m_Format = newFormat;
724
725 return( true );
726 }
727
icerp(int32 _a,int32 _b,int32 _c,int32 _d,int32 _x)728 static int32 icerp( int32 _a, int32 _b, int32 _c, int32 _d, int32 _x )
729 {
730 int32 p = (_d - _c) - (_a - _b);
731 int32 q = (_a - _b) - p;
732 int32 r = _c - _a;
733 return( (_x * (_x * (_x * p + (q << 7)) + (r << 14)) + (_b << 21)) >> 21 );
734 }
735
736
737 /*
738 Scale().
739
740 */
Scale(const uint32 _newWidth,const uint32 _newHeight,const eScaleFilters _eFilter)741 bool CImage::Scale( const uint32 _newWidth, const uint32 _newHeight, const eScaleFilters _eFilter )
742 {
743 if( !m_Format.isPlain() || m_Format.isFloat() )
744 {
745 if( m_Format.isCompressed() )
746 g_Log->Warning( "CImage::Scale(): No deal, image is compressed." );
747
748 if( m_Format.isFloat() )
749 g_Log->Warning( "CImage::Scale(): No deal, image is float." );
750
751 if( m_Format.isDepth() )
752 g_Log->Warning( "CImage::Scale(): No deal, image is depth." );
753
754 return( false );
755 }
756
757 if ( _newHeight < 2 || _newWidth < 2 )
758 {
759 g_Log->Warning( "CImage::Scale(): No deal, new size too small." );
760 return ( false );
761 }
762
763 uint32 nChannels = m_Format.GetChannels();
764
765 Base::spCAlignedBuffer newPixels = new Base::CAlignedBuffer( _newWidth * _newHeight * nChannels );
766
767 uint8 *pData = GetData( 0 );
768
769 uint32 x,y,k,sampleX, sampleY, wX, wY;
770 uint8 *src, *dest = newPixels->GetBufferPtr();
771
772 switch( _eFilter )
773 {
774 case eImage_Nearest:
775 for( y=0; y<_newHeight; y++ )
776 {
777 sampleY = (m_Height - 1) * y / (_newHeight - 1);
778 for( x=0; x<_newWidth; x++ )
779 {
780 sampleX = (m_Width - 1) * x / (_newWidth - 1);
781 for( k=0; k<nChannels; k++ )
782 {
783 *dest++ = pData[ (sampleY * m_Width + sampleX) * nChannels + k ];
784 }
785 }
786 }
787 break;
788
789 case eImage_Bilinear:
790 for( y=0; y<_newHeight; y++ )
791 {
792 sampleY = (((m_Height - 1) * y) << 8) / (_newHeight - 1);
793
794 if (y == _newHeight - 1)
795 sampleY--;
796
797 wY = sampleY & 0xFF;
798 sampleY >>= 8;
799
800 for( x=0; x<_newWidth; x++ )
801 {
802 sampleX = (((m_Width - 1) * x) << 8) / (_newWidth - 1);
803 wX = sampleX & 0xFF;
804 sampleX >>= 8;
805
806 src = pData + (sampleY * m_Width + sampleX) * nChannels;
807
808 for( k=0; k<nChannels; k++ )
809 {
810 *dest++ = (( (256 - wX) * (256 - wY) * static_cast<uint32>(src[ 0 ]) +
811 (256 - wX) * ( wY) * static_cast<uint32>(src[ m_Width * nChannels ]) +
812 ( wX) * (256 - wY) * static_cast<uint32>(src[ nChannels ]) +
813 ( wX) * ( wY) * static_cast<uint32>(src[ (m_Width + 1) * nChannels ]) ) >> 16) & 0xFF;
814 src++;
815 }
816 }
817 }
818 break;
819 case eImage_Bicubic:
820 int a,b,c,d;
821 int res;
822
823 for( y=0; y<_newHeight; y++ )
824 {
825 sampleY = (((m_Height - 1) * y) << 7) / (_newHeight - 1);
826 wY = sampleY & 0x7F;
827 sampleY >>= 7;
828
829 for( x=0; x<_newWidth; x++ )
830 {
831 sampleX = (((m_Width - 1) * x) << 7) / (_newWidth - 1);
832 wX = sampleX & 0x7F;
833 sampleX >>= 7;
834
835 src = pData + ((sampleY - 1) * m_Width + (sampleX - 1)) * nChannels;
836
837 for( k=0; k<nChannels; k++ )
838 {
839 b = icerp( src[ m_Width * nChannels], src[( m_Width + 1) * nChannels], src[( m_Width + 2) * nChannels], src[( m_Width + 3) * nChannels], static_cast<int32>(wX)) & 0xFF;
840 if( sampleY > 0 )
841 a = icerp(src[0], src[nChannels], src[2 * nChannels], src[3 * nChannels], static_cast<int32>(wX)) & 0xFF;
842 else
843 a = b;
844
845 c = icerp(src[2 * m_Width * nChannels], src[(2 * m_Width + 1) * nChannels], src[(2 * m_Width + 2) * nChannels], src[(2 * m_Width + 3) * nChannels], static_cast<int32>(wX)) & 0xFF;
846 if( sampleY < _newHeight - 1 )
847 d = icerp(src[3 * m_Width * nChannels], src[(3 * m_Width + 1) * nChannels], src[(3 * m_Width + 2) * nChannels], src[(3 * m_Width + 3) * nChannels], static_cast<int32>(wX)) & 0xFF;
848 else
849 d = c;
850
851 res = icerp( a, b, c, d, static_cast<int32>(wY) ) & 0xFF;
852 *dest++ = (res < 0)? 0 : (res > 255)? 255 : (res & 0xFF);
853 src++;
854 }
855 }
856 }
857 break;
858 }
859
860 m_spData = newPixels;
861 m_Width = _newWidth;
862 m_Height = _newHeight;
863 m_nMipMaps = 1;
864
865 return( true );
866 }
867
868 /*
869 */
PutPixel(const int32 _x,const int32 _y,const fp4 _r,const fp4 _g,const fp4 _b,const fp4 _a)870 void CImage::PutPixel( const int32 _x, const int32 _y, const fp4 _r, const fp4 _g, const fp4 _b, const fp4 _a )
871 {
872 // Complicated formats are no go.
873 if( !m_Format.isPlain() )
874 return;
875
876 if( _x < 0 || _x >= (int)m_Width )
877 return;
878
879 if( _y < 0 || _y >= (int)m_Height )
880 return;
881
882 fp4 rgba[4] = { _r, _g, _b, _a };
883
884 uint32 nDestChannels = m_Format.GetChannels();
885 uint8 *pData = (GetData(0) + (static_cast<uint32>(_y) * GetPitch())) + (static_cast<uint32>(_x) * m_Format.getBPPixel() );
886
887 if (pData == NULL)
888 return;
889
890 if( m_Format.isFloat() )
891 {
892 for( uint8 i=0; i<nDestChannels; i++ )
893 ((fp4 *)pData)[i] = rgba[i];
894 }
895 else if( m_Format.m_Format >= eImage_I16 && m_Format.m_Format <= eImage_RGBA16 )
896 {
897 for( uint8 i=0; i<nDestChannels; i++ )
898 ((uint16 *)pData)[i] = (uint16)(65535 * Base::Math::Clamped( rgba[i], 0.0f, 1.1f ) + 0.5f );
899 }
900 else
901 {
902 for( uint8 i=0; i<nDestChannels; i++ )
903 pData[i] = (uint8)( 255 * Base::Math::Clamped( rgba[i], 0.0f, 1.0f ) + 0.5f );
904 }
905 }
906
907 /*
908 */
GetPixel(const int32 _x,const int32 _y,fp4 & _r,fp4 & _g,fp4 & _b,fp4 & _a)909 void CImage::GetPixel( const int32 _x, const int32 _y, fp4 &_r, fp4 &_g, fp4 &_b, fp4 &_a )
910 {
911 // Complicated formats are no go.
912 if( !m_Format.isPlain() )
913 return;
914
915 if( _x < 0 || _x > (int)m_Width )
916 return;
917
918 if( _y < 0 || _y > (int)m_Height )
919 return;
920
921 uint32 nSrcChannels = m_Format.GetChannels();
922 uint8 *pData = (GetData(0) + (static_cast<uint32>(_y) * GetPitch())) + (static_cast<uint32>(_x) * m_Format.getBPPixel() );
923 fp4 rgba[4];
924
925 _r = _g = _b = _a = 0;
926 rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0.0f;
927
928 if (pData == NULL)
929 return;
930
931 if( m_Format.isFloat() )
932 {
933 for( uint32 i=0; i<nSrcChannels; i++ )
934 rgba[i] = ((fp4 *)pData)[i];
935 }
936 else if( m_Format.m_Format >= eImage_I16 && m_Format.m_Format <= eImage_RGBA16 )
937 {
938 for( uint32 i=0; i<nSrcChannels; i++ )
939 rgba[i] = ((uint16 *)pData)[i] * (1.0f / 65535.0f);
940 }
941 else
942 {
943 for( uint32 i=0; i<nSrcChannels; i++ )
944 rgba[i] = pData[i] * (1.0f / 255.0f);
945 }
946
947 if( nSrcChannels < 4 ) rgba[3] = 1.0f;
948 if( nSrcChannels == 1 ) rgba[2] = rgba[1] = rgba[0];
949
950 _r = rgba[0];
951 _g = rgba[1];
952 _b = rgba[2];
953 _a = rgba[3];
954 }
955
956
957
958 };
959
960