1 //-------------------------------------------------------------------------------------
2 // DirectXTexImage.cpp
3 //
4 // DirectX Texture Library - Image container
5 //
6 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
7 // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
8 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
9 // PARTICULAR PURPOSE.
10 //
11 // Copyright (c) Microsoft Corporation. All rights reserved.
12 //
13 // http://go.microsoft.com/fwlink/?LinkId=248926
14 //-------------------------------------------------------------------------------------
15 
16 #include "DirectXTexP.h"
17 
18 namespace DirectX
19 {
20 
21 extern bool _CalculateMipLevels( _In_ size_t width, _In_ size_t height, _Inout_ size_t& mipLevels );
22 extern bool _CalculateMipLevels3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth, _Inout_ size_t& mipLevels );
23 extern bool _IsAlphaAllOpaqueBC( _In_ const Image& cImage );
24 
25 //-------------------------------------------------------------------------------------
26 // Determines number of image array entries and pixel size
27 //-------------------------------------------------------------------------------------
28 _Use_decl_annotations_
_DetermineImageArray(const TexMetadata & metadata,DWORD cpFlags,size_t & nImages,size_t & pixelSize)29 void _DetermineImageArray( const TexMetadata& metadata, DWORD cpFlags,
30                            size_t& nImages, size_t& pixelSize )
31 {
32     assert( metadata.width > 0 && metadata.height > 0 && metadata.depth > 0 );
33     assert( metadata.arraySize > 0 );
34     assert( metadata.mipLevels > 0 );
35 
36     size_t _pixelSize = 0;
37     size_t _nimages = 0;
38 
39     switch( metadata.dimension )
40     {
41     case TEX_DIMENSION_TEXTURE1D:
42     case TEX_DIMENSION_TEXTURE2D:
43         for( size_t item = 0; item < metadata.arraySize; ++item )
44         {
45             size_t w = metadata.width;
46             size_t h = metadata.height;
47 
48             for( size_t level=0; level < metadata.mipLevels; ++level )
49             {
50                 size_t rowPitch, slicePitch;
51                 ComputePitch( metadata.format, w, h, rowPitch, slicePitch, cpFlags );
52 
53                 _pixelSize += slicePitch;
54                 ++_nimages;
55 
56                 if ( h > 1 )
57                     h >>= 1;
58 
59                 if ( w > 1 )
60                     w >>= 1;
61             }
62         }
63         break;
64 
65     case TEX_DIMENSION_TEXTURE3D:
66         {
67             size_t w = metadata.width;
68             size_t h = metadata.height;
69             size_t d = metadata.depth;
70 
71             for( size_t level=0; level < metadata.mipLevels; ++level )
72             {
73                 size_t rowPitch, slicePitch;
74                 ComputePitch( metadata.format, w, h, rowPitch, slicePitch, cpFlags );
75 
76                 for( size_t slice=0; slice < d; ++slice )
77                 {
78                     _pixelSize += slicePitch;
79                     ++_nimages;
80                 }
81 
82                 if ( h > 1 )
83                     h >>= 1;
84 
85                 if ( w > 1 )
86                     w >>= 1;
87 
88                 if ( d > 1 )
89                     d >>= 1;
90             }
91         }
92         break;
93 
94     default:
95         assert( false );
96         break;
97     }
98 
99     nImages = _nimages;
100     pixelSize = _pixelSize;
101 }
102 
103 
104 //-------------------------------------------------------------------------------------
105 // Fills in the image array entries
106 //-------------------------------------------------------------------------------------
107 _Use_decl_annotations_
_SetupImageArray(uint8_t * pMemory,size_t pixelSize,const TexMetadata & metadata,DWORD cpFlags,Image * images,size_t nImages)108 bool _SetupImageArray( uint8_t *pMemory, size_t pixelSize,
109                        const TexMetadata& metadata, DWORD cpFlags,
110                        Image* images, size_t nImages )
111 {
112     assert( pMemory );
113     assert( pixelSize > 0 );
114     assert( nImages > 0 );
115 
116     if ( !images )
117         return false;
118 
119     size_t index = 0;
120     uint8_t* pixels = pMemory;
121     const uint8_t* pEndBits = pMemory + pixelSize;
122 
123     switch( metadata.dimension )
124     {
125     case TEX_DIMENSION_TEXTURE1D:
126     case TEX_DIMENSION_TEXTURE2D:
127         if (metadata.arraySize == 0 || metadata.mipLevels == 0)
128         {
129             return false;
130         }
131 
132         for( size_t item = 0; item < metadata.arraySize; ++item )
133         {
134             size_t w = metadata.width;
135             size_t h = metadata.height;
136 
137             for( size_t level=0; level < metadata.mipLevels; ++level )
138             {
139                 if ( index >= nImages )
140                 {
141                     return false;
142                 }
143 
144                 size_t rowPitch, slicePitch;
145                 ComputePitch( metadata.format, w, h, rowPitch, slicePitch, cpFlags );
146 
147                 images[index].width = w;
148                 images[index].height = h;
149                 images[index].format = metadata.format;
150                 images[index].rowPitch = rowPitch;
151                 images[index].slicePitch = slicePitch;
152                 images[index].pixels = pixels;
153                 ++index;
154 
155                 pixels += slicePitch;
156                 if ( pixels > pEndBits )
157                 {
158                     return false;
159                 }
160 
161                 if ( h > 1 )
162                     h >>= 1;
163 
164                 if ( w > 1 )
165                     w >>= 1;
166             }
167         }
168         return true;
169 
170     case TEX_DIMENSION_TEXTURE3D:
171         {
172             if (metadata.mipLevels == 0 || metadata.depth == 0)
173             {
174                 return false;
175             }
176 
177             size_t w = metadata.width;
178             size_t h = metadata.height;
179             size_t d = metadata.depth;
180 
181             for( size_t level=0; level < metadata.mipLevels; ++level )
182             {
183                 size_t rowPitch, slicePitch;
184                 ComputePitch( metadata.format, w, h, rowPitch, slicePitch, cpFlags );
185 
186                 for( size_t slice=0; slice < d; ++slice )
187                 {
188                     if ( index >= nImages )
189                     {
190                         return false;
191                     }
192 
193                     // We use the same memory organization that Direct3D 11 needs for D3D11_SUBRESOURCE_DATA
194                     // with all slices of a given miplevel being continuous in memory
195                     images[index].width = w;
196                     images[index].height = h;
197                     images[index].format = metadata.format;
198                     images[index].rowPitch = rowPitch;
199                     images[index].slicePitch = slicePitch;
200                     images[index].pixels = pixels;
201                     ++index;
202 
203                     pixels += slicePitch;
204                     if ( pixels > pEndBits )
205                     {
206                         return false;
207                     }
208                 }
209 
210                 if ( h > 1 )
211                     h >>= 1;
212 
213                 if ( w > 1 )
214                     w >>= 1;
215 
216                 if ( d > 1 )
217                     d >>= 1;
218             }
219         }
220         return true;
221 
222     default:
223         return false;
224     }
225 }
226 
227 
228 //=====================================================================================
229 // ScratchImage - Bitmap image container
230 //=====================================================================================
231 
operator =(ScratchImage && moveFrom)232 ScratchImage& ScratchImage::operator= (ScratchImage&& moveFrom)
233 {
234     if ( this != &moveFrom )
235     {
236         Release();
237 
238         _nimages = moveFrom._nimages;
239         _size = moveFrom._size;
240         _metadata = moveFrom._metadata;
241         _image = moveFrom._image;
242         _memory = moveFrom._memory;
243 
244         moveFrom._nimages = 0;
245         moveFrom._size = 0;
246         moveFrom._image = nullptr;
247         moveFrom._memory = nullptr;
248     }
249     return *this;
250 }
251 
252 
253 //-------------------------------------------------------------------------------------
254 // Methods
255 //-------------------------------------------------------------------------------------
256 _Use_decl_annotations_
Initialize(const TexMetadata & mdata,DWORD flags)257 HRESULT ScratchImage::Initialize( const TexMetadata& mdata, DWORD flags )
258 {
259     if ( !IsValid(mdata.format) )
260         return E_INVALIDARG;
261 
262     if ( IsPalettized(mdata.format) )
263         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
264 
265     size_t mipLevels = mdata.mipLevels;
266 
267     switch( mdata.dimension )
268     {
269     case TEX_DIMENSION_TEXTURE1D:
270         if ( !mdata.width || mdata.height != 1 || mdata.depth != 1 || !mdata.arraySize )
271             return E_INVALIDARG;
272 
273         if ( !_CalculateMipLevels(mdata.width,1,mipLevels) )
274             return E_INVALIDARG;
275         break;
276 
277     case TEX_DIMENSION_TEXTURE2D:
278         if ( !mdata.width || !mdata.height || mdata.depth != 1 || !mdata.arraySize )
279             return E_INVALIDARG;
280 
281         if ( mdata.IsCubemap() )
282         {
283             if ( (mdata.arraySize % 6) != 0 )
284                 return E_INVALIDARG;
285         }
286 
287         if ( !_CalculateMipLevels(mdata.width,mdata.height,mipLevels) )
288             return E_INVALIDARG;
289         break;
290 
291     case TEX_DIMENSION_TEXTURE3D:
292         if ( !mdata.width || !mdata.height || !mdata.depth || mdata.arraySize != 1 )
293             return E_INVALIDARG;
294 
295         if ( !_CalculateMipLevels3D(mdata.width,mdata.height,mdata.depth,mipLevels) )
296             return E_INVALIDARG;
297         break;
298 
299     default:
300         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
301     }
302 
303     Release();
304 
305     _metadata.width = mdata.width;
306     _metadata.height = mdata.height;
307     _metadata.depth = mdata.depth;
308     _metadata.arraySize = mdata.arraySize;
309     _metadata.mipLevels = mipLevels;
310     _metadata.miscFlags = mdata.miscFlags;
311     _metadata.miscFlags2 = mdata.miscFlags2;
312     _metadata.format = mdata.format;
313     _metadata.dimension = mdata.dimension;
314 
315     size_t pixelSize, nimages;
316     _DetermineImageArray( _metadata, flags, nimages, pixelSize );
317 
318     _image = new (std::nothrow) Image[ nimages ];
319     if ( !_image )
320         return E_OUTOFMEMORY;
321 
322     _nimages = nimages;
323     memset( _image, 0, sizeof(Image) * nimages );
324 
325     _memory = reinterpret_cast<uint8_t*>( _aligned_malloc( pixelSize, 16 ) );
326     if ( !_memory )
327     {
328         Release();
329         return E_OUTOFMEMORY;
330     }
331     _size = pixelSize;
332     if ( !_SetupImageArray( _memory, pixelSize, _metadata, flags, _image, nimages ) )
333     {
334         Release();
335         return E_FAIL;
336     }
337 
338     return S_OK;
339 }
340 
341 _Use_decl_annotations_
Initialize1D(DXGI_FORMAT fmt,size_t length,size_t arraySize,size_t mipLevels,DWORD flags)342 HRESULT ScratchImage::Initialize1D( DXGI_FORMAT fmt, size_t length, size_t arraySize, size_t mipLevels, DWORD flags )
343 {
344     if ( !length || !arraySize )
345         return E_INVALIDARG;
346 
347     // 1D is a special case of the 2D case
348     HRESULT hr = Initialize2D( fmt, length, 1, arraySize, mipLevels, flags );
349     if ( FAILED(hr) )
350         return hr;
351 
352     _metadata.dimension = TEX_DIMENSION_TEXTURE1D;
353 
354     return S_OK;
355 }
356 
357 _Use_decl_annotations_
Initialize2D(DXGI_FORMAT fmt,size_t width,size_t height,size_t arraySize,size_t mipLevels,DWORD flags)358 HRESULT ScratchImage::Initialize2D( DXGI_FORMAT fmt, size_t width, size_t height, size_t arraySize, size_t mipLevels, DWORD flags )
359 {
360     if ( !IsValid(fmt) || !width || !height || !arraySize )
361         return E_INVALIDARG;
362 
363     if ( IsPalettized(fmt) )
364         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
365 
366     if ( !_CalculateMipLevels(width,height,mipLevels) )
367         return E_INVALIDARG;
368 
369     Release();
370 
371     _metadata.width = width;
372     _metadata.height = height;
373     _metadata.depth = 1;
374     _metadata.arraySize = arraySize;
375     _metadata.mipLevels = mipLevels;
376     _metadata.miscFlags = 0;
377     _metadata.miscFlags2 = 0;
378     _metadata.format = fmt;
379     _metadata.dimension = TEX_DIMENSION_TEXTURE2D;
380 
381     size_t pixelSize, nimages;
382     _DetermineImageArray( _metadata, flags, nimages, pixelSize );
383 
384     _image = new (std::nothrow) Image[ nimages ];
385     if ( !_image )
386         return E_OUTOFMEMORY;
387 
388     _nimages = nimages;
389     memset( _image, 0, sizeof(Image) * nimages );
390 
391     _memory = reinterpret_cast<uint8_t*>( _aligned_malloc( pixelSize, 16 ) );
392     if ( !_memory )
393     {
394         Release();
395         return E_OUTOFMEMORY;
396     }
397     _size = pixelSize;
398     if ( !_SetupImageArray( _memory, pixelSize, _metadata, flags, _image, nimages ) )
399     {
400         Release();
401         return E_FAIL;
402     }
403 
404     return S_OK;
405 }
406 
407 _Use_decl_annotations_
Initialize3D(DXGI_FORMAT fmt,size_t width,size_t height,size_t depth,size_t mipLevels,DWORD flags)408 HRESULT ScratchImage::Initialize3D( DXGI_FORMAT fmt, size_t width, size_t height, size_t depth, size_t mipLevels, DWORD flags )
409 {
410     if ( !IsValid(fmt) || !width || !height || !depth )
411         return E_INVALIDARG;
412 
413     if ( IsPalettized(fmt) )
414         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
415 
416     if ( !_CalculateMipLevels3D(width,height,depth,mipLevels) )
417         return E_INVALIDARG;
418 
419     Release();
420 
421     _metadata.width = width;
422     _metadata.height = height;
423     _metadata.depth = depth;
424     _metadata.arraySize = 1;    // Direct3D 10.x/11 does not support arrays of 3D textures
425     _metadata.mipLevels = mipLevels;
426     _metadata.miscFlags = 0;
427     _metadata.miscFlags2 = 0;
428     _metadata.format = fmt;
429     _metadata.dimension = TEX_DIMENSION_TEXTURE3D;
430 
431     size_t pixelSize, nimages;
432     _DetermineImageArray( _metadata, flags, nimages, pixelSize );
433 
434     _image = new (std::nothrow) Image[ nimages ];
435     if ( !_image )
436     {
437         Release();
438         return E_OUTOFMEMORY;
439     }
440     _nimages = nimages;
441     memset( _image, 0, sizeof(Image) * nimages );
442 
443     _memory = reinterpret_cast<uint8_t*>( _aligned_malloc( pixelSize, 16 ) );
444     if ( !_memory )
445     {
446         Release();
447         return E_OUTOFMEMORY;
448     }
449     _size = pixelSize;
450 
451     if ( !_SetupImageArray( _memory, pixelSize, _metadata, flags, _image, nimages ) )
452     {
453         Release();
454         return E_FAIL;
455     }
456 
457     return S_OK;
458 }
459 
460 _Use_decl_annotations_
InitializeCube(DXGI_FORMAT fmt,size_t width,size_t height,size_t nCubes,size_t mipLevels,DWORD flags)461 HRESULT ScratchImage::InitializeCube( DXGI_FORMAT fmt, size_t width, size_t height, size_t nCubes, size_t mipLevels, DWORD flags )
462 {
463     if ( !width || !height || !nCubes )
464         return E_INVALIDARG;
465 
466     // A DirectX11 cubemap is just a 2D texture array that is a multiple of 6 for each cube
467     HRESULT hr = Initialize2D( fmt, width, height, nCubes * 6, mipLevels, flags );
468     if ( FAILED(hr) )
469         return hr;
470 
471     _metadata.miscFlags |= TEX_MISC_TEXTURECUBE;
472 
473     return S_OK;
474 }
475 
476 _Use_decl_annotations_
InitializeFromImage(const Image & srcImage,bool allow1D,DWORD flags)477 HRESULT ScratchImage::InitializeFromImage( const Image& srcImage, bool allow1D, DWORD flags )
478 {
479     HRESULT hr = ( srcImage.height > 1 || !allow1D )
480                  ? Initialize2D( srcImage.format, srcImage.width, srcImage.height, 1, 1, flags )
481                  : Initialize1D( srcImage.format, srcImage.width, 1, 1, flags );
482 
483     if ( FAILED(hr) )
484         return hr;
485 
486     size_t rowCount = ComputeScanlines( srcImage.format, srcImage.height );
487     if ( !rowCount )
488         return E_UNEXPECTED;
489 
490     const uint8_t* sptr = reinterpret_cast<const uint8_t*>( srcImage.pixels );
491     if ( !sptr )
492         return E_POINTER;
493 
494     auto dptr = reinterpret_cast<uint8_t*>( _image[0].pixels );
495     if ( !dptr )
496         return E_POINTER;
497 
498     size_t spitch = srcImage.rowPitch;
499     size_t dpitch = _image[0].rowPitch;
500 
501     size_t size = std::min<size_t>( dpitch, spitch );
502 
503     for( size_t y = 0; y < rowCount; ++y )
504     {
505         memcpy_s( dptr, dpitch, sptr, size );
506         sptr += spitch;
507         dptr += dpitch;
508     }
509 
510     return S_OK;
511 }
512 
513 _Use_decl_annotations_
InitializeArrayFromImages(const Image * images,size_t nImages,bool allow1D,DWORD flags)514 HRESULT ScratchImage::InitializeArrayFromImages( const Image* images, size_t nImages, bool allow1D, DWORD flags )
515 {
516     if ( !images || !nImages )
517         return E_INVALIDARG;
518 
519     DXGI_FORMAT format = images[0].format;
520     size_t width = images[0].width;
521     size_t height = images[0].height;
522 
523     for( size_t index=0; index < nImages; ++index )
524     {
525         if ( !images[index].pixels )
526             return E_POINTER;
527 
528         if ( images[index].format != format || images[index].width != width || images[index].height != height )
529         {
530             // All images must be the same format, width, and height
531             return E_FAIL;
532         }
533     }
534 
535     HRESULT hr = ( height > 1 || !allow1D )
536                  ? Initialize2D( format, width, height, nImages, 1, flags )
537                  : Initialize1D( format, width, nImages, 1, flags );
538 
539     if ( FAILED(hr) )
540         return hr;
541 
542     size_t rowCount = ComputeScanlines( format, height );
543     if ( !rowCount )
544         return E_UNEXPECTED;
545 
546     for( size_t index=0; index < nImages; ++index )
547     {
548         auto sptr = reinterpret_cast<const uint8_t*>( images[index].pixels );
549         if ( !sptr )
550             return E_POINTER;
551 
552         assert( index < _nimages );
553         auto dptr = reinterpret_cast<uint8_t*>( _image[index].pixels );
554         if ( !dptr )
555             return E_POINTER;
556 
557         size_t spitch = images[index].rowPitch;
558         size_t dpitch = _image[index].rowPitch;
559 
560         size_t size = std::min<size_t>( dpitch, spitch );
561 
562         for( size_t y = 0; y < rowCount; ++y )
563         {
564             memcpy_s( dptr, dpitch, sptr, size );
565             sptr += spitch;
566             dptr += dpitch;
567         }
568     }
569 
570     return S_OK;
571 }
572 
573 _Use_decl_annotations_
InitializeCubeFromImages(const Image * images,size_t nImages,DWORD flags)574 HRESULT ScratchImage::InitializeCubeFromImages( const Image* images, size_t nImages, DWORD flags )
575 {
576     if ( !images || !nImages )
577         return E_INVALIDARG;
578 
579     // A DirectX11 cubemap is just a 2D texture array that is a multiple of 6 for each cube
580     if ( ( nImages % 6 ) != 0 )
581         return E_INVALIDARG;
582 
583     HRESULT hr = InitializeArrayFromImages( images, nImages, false, flags );
584     if ( FAILED(hr) )
585         return hr;
586 
587     _metadata.miscFlags |= TEX_MISC_TEXTURECUBE;
588 
589     return S_OK;
590 }
591 
592 _Use_decl_annotations_
Initialize3DFromImages(const Image * images,size_t depth,DWORD flags)593 HRESULT ScratchImage::Initialize3DFromImages( const Image* images, size_t depth, DWORD flags )
594 {
595     if ( !images || !depth )
596         return E_INVALIDARG;
597 
598     DXGI_FORMAT format = images[0].format;
599     size_t width = images[0].width;
600     size_t height = images[0].height;
601 
602     for( size_t slice=0; slice < depth; ++slice )
603     {
604         if ( !images[slice].pixels )
605             return E_POINTER;
606 
607         if ( images[slice].format != format || images[slice].width != width || images[slice].height != height )
608         {
609             // All images must be the same format, width, and height
610             return E_FAIL;
611         }
612     }
613 
614     HRESULT hr = Initialize3D( format, width, height, depth, 1, flags );
615     if ( FAILED(hr) )
616         return hr;
617 
618     size_t rowCount = ComputeScanlines( format, height );
619     if ( !rowCount )
620         return E_UNEXPECTED;
621 
622     for( size_t slice=0; slice < depth; ++slice )
623     {
624         auto sptr = reinterpret_cast<const uint8_t*>( images[slice].pixels );
625         if ( !sptr )
626             return E_POINTER;
627 
628         assert( slice < _nimages );
629         auto dptr = reinterpret_cast<uint8_t*>( _image[slice].pixels );
630         if ( !dptr )
631             return E_POINTER;
632 
633         size_t spitch = images[slice].rowPitch;
634         size_t dpitch = _image[slice].rowPitch;
635 
636         size_t size = std::min<size_t>( dpitch, spitch );
637 
638         for( size_t y = 0; y < rowCount; ++y )
639         {
640             memcpy_s( dptr, dpitch, sptr, size );
641             sptr += spitch;
642             dptr += dpitch;
643         }
644     }
645 
646     return S_OK;
647 }
648 
Release()649 void ScratchImage::Release()
650 {
651     _nimages = 0;
652     _size = 0;
653 
654     if ( _image )
655     {
656         delete [] _image;
657         _image = 0;
658     }
659 
660     if ( _memory )
661     {
662         _aligned_free( _memory );
663         _memory = 0;
664     }
665 
666     memset(&_metadata, 0, sizeof(_metadata));
667 }
668 
669 _Use_decl_annotations_
OverrideFormat(DXGI_FORMAT f)670 bool ScratchImage::OverrideFormat( DXGI_FORMAT f )
671 {
672     if ( !_image )
673         return false;
674 
675     if ( !IsValid( f ) || IsPlanar( f ) || IsPalettized( f ) )
676         return false;
677 
678     for( size_t index = 0; index < _nimages; ++index )
679     {
680         _image[ index ].format = f;
681     }
682 
683     _metadata.format = f;
684 
685     return true;
686 }
687 
688 _Use_decl_annotations_
GetImage(size_t mip,size_t item,size_t slice) const689 const Image* ScratchImage::GetImage(size_t mip, size_t item, size_t slice) const
690 {
691     if ( mip >= _metadata.mipLevels )
692         return nullptr;
693 
694     size_t index = 0;
695 
696     switch( _metadata.dimension )
697     {
698     case TEX_DIMENSION_TEXTURE1D:
699     case TEX_DIMENSION_TEXTURE2D:
700         if ( slice > 0 )
701             return nullptr;
702 
703         if ( item >= _metadata.arraySize )
704             return nullptr;
705 
706         index = item*( _metadata.mipLevels ) + mip;
707         break;
708 
709     case TEX_DIMENSION_TEXTURE3D:
710         if ( item > 0 )
711         {
712             // No support for arrays of volumes
713             return nullptr;
714         }
715         else
716         {
717             size_t d = _metadata.depth;
718 
719             for( size_t level = 0; level < mip; ++level )
720             {
721                 index += d;
722                 if ( d > 1 )
723                     d >>= 1;
724             }
725 
726             if ( slice >= d )
727                 return nullptr;
728 
729             index += slice;
730         }
731         break;
732 
733     default:
734         return nullptr;
735     }
736 
737     return &_image[index];
738 }
739 
IsAlphaAllOpaque() const740 bool ScratchImage::IsAlphaAllOpaque() const
741 {
742     if ( !_image )
743         return false;
744 
745     if ( !HasAlpha( _metadata.format ) )
746         return true;
747 
748     if ( IsCompressed( _metadata.format ) )
749     {
750         for( size_t index = 0; index < _nimages; ++index )
751         {
752             if ( !_IsAlphaAllOpaqueBC( _image[ index ] ) )
753                 return false;
754         }
755     }
756     else
757     {
758         ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*_metadata.width), 16 ) ) );
759         if ( !scanline )
760             return false;
761 
762         static const XMVECTORF32 threshold = { 0.99f, 0.99f, 0.99f, 0.99f };
763 
764         for( size_t index = 0; index < _nimages; ++index )
765         {
766 #pragma warning( suppress : 6011 )
767             const Image& img = _image[ index ];
768 
769             const uint8_t *pPixels = img.pixels;
770             assert( pPixels );
771 
772             for( size_t h = 0; h < img.height; ++h )
773             {
774                 if ( !_LoadScanline( scanline.get(), img.width, pPixels, img.rowPitch, img.format ) )
775                     return false;
776 
777                 XMVECTOR* ptr = scanline.get();
778                 for( size_t w = 0; w < img.width; ++w )
779                 {
780                     XMVECTOR alpha = XMVectorSplatW( *ptr );
781                     if ( XMVector4Less( alpha, threshold ) )
782                         return false;
783                     ++ptr;
784                 }
785 
786                 pPixels += img.rowPitch;
787             }
788         }
789     }
790 
791     return true;
792 }
793 
794 }; // namespace
795