1818
1919from . import Image , ImageFile , ImagePalette
2020from ._binary import i32le as i32
21+ from ._binary import o8
2122from ._binary import o32le as o32
2223
2324# Magic ("DDS ")
@@ -341,6 +342,7 @@ def _open(self):
341342
342343 flags , height , width = struct .unpack ("<3I" , header .read (12 ))
343344 self ._size = (width , height )
345+ extents = (0 , 0 ) + self .size
344346
345347 pitch , depth , mipmaps = struct .unpack ("<3I" , header .read (12 ))
346348 struct .unpack ("<11I" , header .read (44 )) # reserved
@@ -351,22 +353,16 @@ def _open(self):
351353 rawmode = None
352354 if pfflags & DDPF .RGB :
353355 # Texture contains uncompressed RGB data
354- masks = struct .unpack ("<4I" , header .read (16 ))
355- masks = {mask : ["R" , "G" , "B" , "A" ][i ] for i , mask in enumerate (masks )}
356- if bitcount == 24 :
357- self ._mode = "RGB"
358- rawmode = masks [0x000000FF ] + masks [0x0000FF00 ] + masks [0x00FF0000 ]
359- elif bitcount == 32 and pfflags & DDPF .ALPHAPIXELS :
356+ if pfflags & DDPF .ALPHAPIXELS :
360357 self ._mode = "RGBA"
361- rawmode = (
362- masks [0x000000FF ]
363- + masks [0x0000FF00 ]
364- + masks [0x00FF0000 ]
365- + masks [0xFF000000 ]
366- )
358+ mask_count = 4
367359 else :
368- msg = f"Unsupported bitcount { bitcount } for { pfflags } "
369- raise OSError (msg )
360+ self ._mode = "RGB"
361+ mask_count = 3
362+
363+ masks = struct .unpack (f"<{ mask_count } I" , header .read (mask_count * 4 ))
364+ self .tile = [("dds_rgb" , extents , 0 , (bitcount , masks ))]
365+ return
370366 elif pfflags & DDPF .LUMINANCE :
371367 if bitcount == 8 :
372368 self ._mode = "L"
@@ -464,7 +460,6 @@ def _open(self):
464460 msg = f"Unknown pixel format flags { pfflags } "
465461 raise NotImplementedError (msg )
466462
467- extents = (0 , 0 ) + self .size
468463 if n :
469464 self .tile = [
470465 ImageFile ._Tile ("bcn" , extents , offset , (n , self .pixel_format ))
@@ -476,6 +471,39 @@ def load_seek(self, pos):
476471 pass
477472
478473
474+ class DdsRgbDecoder (ImageFile .PyDecoder ):
475+ _pulls_fd = True
476+
477+ def decode (self , buffer ):
478+ bitcount , masks = self .args
479+
480+ # Some masks will be padded with zeros, e.g. R 0b11 G 0b1100
481+ # Calculate how many zeros each mask is padded with
482+ mask_offsets = []
483+ # And the maximum value of each channel without the padding
484+ mask_totals = []
485+ for mask in masks :
486+ offset = 0
487+ if mask != 0 :
488+ while mask >> (offset + 1 ) << (offset + 1 ) == mask :
489+ offset += 1
490+ mask_offsets .append (offset )
491+ mask_totals .append (mask >> offset )
492+
493+ data = bytearray ()
494+ bytecount = bitcount // 8
495+ while len (data ) < self .state .xsize * self .state .ysize * len (masks ):
496+ value = int .from_bytes (self .fd .read (bytecount ), "little" )
497+ for i , mask in enumerate (masks ):
498+ masked_value = value & mask
499+ # Remove the zero padding, and scale it to 8 bits
500+ data += o8 (
501+ int (((masked_value >> mask_offsets [i ]) / mask_totals [i ]) * 255 )
502+ )
503+ self .set_as_raw (bytes (data ))
504+ return - 1 , 0
505+
506+
479507def _save (im , fp , filename ):
480508 if im .mode not in ("RGB" , "RGBA" , "L" , "LA" ):
481509 msg = f"cannot write mode { im .mode } as DDS"
@@ -533,5 +561,6 @@ def _accept(prefix):
533561
534562
535563Image .register_open (DdsImageFile .format , DdsImageFile , _accept )
564+ Image .register_decoder ("dds_rgb" , DdsRgbDecoder )
536565Image .register_save (DdsImageFile .format , _save )
537566Image .register_extension (DdsImageFile .format , ".dds" )
0 commit comments