@@ -259,29 +259,44 @@ class BlpImageFile(ImageFile.ImageFile):
259
259
260
260
def _open (self ) -> None :
261
261
self .magic = self .fp .read (4 )
262
+ if not _accept (self .magic ):
263
+ msg = f"Bad BLP magic { repr (self .magic )} "
264
+ raise BLPFormatError (msg )
262
265
263
- self .fp .seek (5 , os .SEEK_CUR )
264
- (self ._blp_alpha_depth ,) = struct .unpack ("<b" , self .fp .read (1 ))
266
+ compression = struct .unpack ("<i" , self .fp .read (4 ))[0 ]
267
+ if self .magic == b"BLP1" :
268
+ alpha = struct .unpack ("<I" , self .fp .read (4 ))[0 ] != 0
269
+ else :
270
+ encoding = struct .unpack ("<b" , self .fp .read (1 ))[0 ]
271
+ alpha = struct .unpack ("<b" , self .fp .read (1 ))[0 ] != 0
272
+ alpha_encoding = struct .unpack ("<b" , self .fp .read (1 ))[0 ]
273
+ self .fp .seek (1 , os .SEEK_CUR ) # mips
265
274
266
- self .fp .seek (2 , os .SEEK_CUR )
267
275
self ._size = struct .unpack ("<II" , self .fp .read (8 ))
268
276
269
- if self .magic in (b"BLP1" , b"BLP2" ):
270
- decoder = self .magic .decode ()
277
+ args : tuple [int , int , bool ] | tuple [int , int , bool , int ]
278
+ if self .magic == b"BLP1" :
279
+ encoding = struct .unpack ("<i" , self .fp .read (4 ))[0 ]
280
+ self .fp .seek (4 , os .SEEK_CUR ) # subtype
281
+
282
+ args = (compression , encoding , alpha )
283
+ offset = 28
271
284
else :
272
- msg = f"Bad BLP magic { repr ( self . magic ) } "
273
- raise BLPFormatError ( msg )
285
+ args = ( compression , encoding , alpha , alpha_encoding )
286
+ offset = 20
274
287
275
- self ._mode = "RGBA" if self ._blp_alpha_depth else "RGB"
276
- self .tile = [ImageFile ._Tile (decoder , (0 , 0 ) + self .size , 0 , self .mode )]
288
+ decoder = self .magic .decode ()
289
+
290
+ self ._mode = "RGBA" if alpha else "RGB"
291
+ self .tile = [ImageFile ._Tile (decoder , (0 , 0 ) + self .size , offset , args )]
277
292
278
293
279
294
class _BLPBaseDecoder (ImageFile .PyDecoder ):
280
295
_pulls_fd = True
281
296
282
297
def decode (self , buffer : bytes | Image .SupportsArrayInterface ) -> tuple [int , int ]:
283
298
try :
284
- self ._read_blp_header ()
299
+ self ._read_header ()
285
300
self ._load ()
286
301
except struct .error as e :
287
302
msg = "Truncated BLP file"
@@ -292,25 +307,9 @@ def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int
292
307
def _load (self ) -> None :
293
308
pass
294
309
295
- def _read_blp_header (self ) -> None :
296
- assert self .fd is not None
297
- self .fd .seek (4 )
298
- (self ._blp_compression ,) = struct .unpack ("<i" , self ._safe_read (4 ))
299
-
300
- (self ._blp_encoding ,) = struct .unpack ("<b" , self ._safe_read (1 ))
301
- (self ._blp_alpha_depth ,) = struct .unpack ("<b" , self ._safe_read (1 ))
302
- (self ._blp_alpha_encoding ,) = struct .unpack ("<b" , self ._safe_read (1 ))
303
- self .fd .seek (1 , os .SEEK_CUR ) # mips
304
-
305
- self .size = struct .unpack ("<II" , self ._safe_read (8 ))
306
-
307
- if isinstance (self , BLP1Decoder ):
308
- # Only present for BLP1
309
- (self ._blp_encoding ,) = struct .unpack ("<i" , self ._safe_read (4 ))
310
- self .fd .seek (4 , os .SEEK_CUR ) # subtype
311
-
312
- self ._blp_offsets = struct .unpack ("<16I" , self ._safe_read (16 * 4 ))
313
- self ._blp_lengths = struct .unpack ("<16I" , self ._safe_read (16 * 4 ))
310
+ def _read_header (self ) -> None :
311
+ self ._offsets = struct .unpack ("<16I" , self ._safe_read (16 * 4 ))
312
+ self ._lengths = struct .unpack ("<16I" , self ._safe_read (16 * 4 ))
314
313
315
314
def _safe_read (self , length : int ) -> bytes :
316
315
assert self .fd is not None
@@ -326,37 +325,41 @@ def _read_palette(self) -> list[tuple[int, int, int, int]]:
326
325
ret .append ((b , g , r , a ))
327
326
return ret
328
327
329
- def _read_bgra (self , palette : list [tuple [int , int , int , int ]]) -> bytearray :
328
+ def _read_bgra (
329
+ self , palette : list [tuple [int , int , int , int ]], alpha : bool
330
+ ) -> bytearray :
330
331
data = bytearray ()
331
- _data = BytesIO (self ._safe_read (self ._blp_lengths [0 ]))
332
+ _data = BytesIO (self ._safe_read (self ._lengths [0 ]))
332
333
while True :
333
334
try :
334
335
(offset ,) = struct .unpack ("<B" , _data .read (1 ))
335
336
except struct .error :
336
337
break
337
338
b , g , r , a = palette [offset ]
338
339
d : tuple [int , ...] = (r , g , b )
339
- if self . _blp_alpha_depth :
340
+ if alpha :
340
341
d += (a ,)
341
342
data .extend (d )
342
343
return data
343
344
344
345
345
346
class BLP1Decoder (_BLPBaseDecoder ):
346
347
def _load (self ) -> None :
347
- if self ._blp_compression == Format .JPEG :
348
+ self ._compression , self ._encoding , alpha = self .args
349
+
350
+ if self ._compression == Format .JPEG :
348
351
self ._decode_jpeg_stream ()
349
352
350
- elif self ._blp_compression == 1 :
351
- if self ._blp_encoding in (4 , 5 ):
353
+ elif self ._compression == 1 :
354
+ if self ._encoding in (4 , 5 ):
352
355
palette = self ._read_palette ()
353
- data = self ._read_bgra (palette )
356
+ data = self ._read_bgra (palette , alpha )
354
357
self .set_as_raw (data )
355
358
else :
356
- msg = f"Unsupported BLP encoding { repr (self ._blp_encoding )} "
359
+ msg = f"Unsupported BLP encoding { repr (self ._encoding )} "
357
360
raise BLPFormatError (msg )
358
361
else :
359
- msg = f"Unsupported BLP compression { repr (self ._blp_encoding )} "
362
+ msg = f"Unsupported BLP compression { repr (self ._encoding )} "
360
363
raise BLPFormatError (msg )
361
364
362
365
def _decode_jpeg_stream (self ) -> None :
@@ -365,8 +368,8 @@ def _decode_jpeg_stream(self) -> None:
365
368
(jpeg_header_size ,) = struct .unpack ("<I" , self ._safe_read (4 ))
366
369
jpeg_header = self ._safe_read (jpeg_header_size )
367
370
assert self .fd is not None
368
- self ._safe_read (self ._blp_offsets [0 ] - self .fd .tell ()) # What IS this?
369
- data = self ._safe_read (self ._blp_lengths [0 ])
371
+ self ._safe_read (self ._offsets [0 ] - self .fd .tell ()) # What IS this?
372
+ data = self ._safe_read (self ._lengths [0 ])
370
373
data = jpeg_header + data
371
374
image = JpegImageFile (BytesIO (data ))
372
375
Image ._decompression_bomb_check (image .size )
@@ -383,47 +386,47 @@ def _decode_jpeg_stream(self) -> None:
383
386
384
387
class BLP2Decoder (_BLPBaseDecoder ):
385
388
def _load (self ) -> None :
389
+ self ._compression , self ._encoding , alpha , self ._alpha_encoding = self .args
390
+
386
391
palette = self ._read_palette ()
387
392
388
393
assert self .fd is not None
389
- self .fd .seek (self ._blp_offsets [0 ])
394
+ self .fd .seek (self ._offsets [0 ])
390
395
391
- if self ._blp_compression == 1 :
396
+ if self ._compression == 1 :
392
397
# Uncompressed or DirectX compression
393
398
394
- if self ._blp_encoding == Encoding .UNCOMPRESSED :
395
- data = self ._read_bgra (palette )
399
+ if self ._encoding == Encoding .UNCOMPRESSED :
400
+ data = self ._read_bgra (palette , alpha )
396
401
397
- elif self ._blp_encoding == Encoding .DXT :
402
+ elif self ._encoding == Encoding .DXT :
398
403
data = bytearray ()
399
- if self ._blp_alpha_encoding == AlphaEncoding .DXT1 :
400
- linesize = (self .size [0 ] + 3 ) // 4 * 8
401
- for yb in range ((self .size [1 ] + 3 ) // 4 ):
402
- for d in decode_dxt1 (
403
- self ._safe_read (linesize ), alpha = bool (self ._blp_alpha_depth )
404
- ):
404
+ if self ._alpha_encoding == AlphaEncoding .DXT1 :
405
+ linesize = (self .state .xsize + 3 ) // 4 * 8
406
+ for yb in range ((self .state .ysize + 3 ) // 4 ):
407
+ for d in decode_dxt1 (self ._safe_read (linesize ), alpha ):
405
408
data += d
406
409
407
- elif self ._blp_alpha_encoding == AlphaEncoding .DXT3 :
408
- linesize = (self .size [ 0 ] + 3 ) // 4 * 16
409
- for yb in range ((self .size [ 1 ] + 3 ) // 4 ):
410
+ elif self ._alpha_encoding == AlphaEncoding .DXT3 :
411
+ linesize = (self .state . xsize + 3 ) // 4 * 16
412
+ for yb in range ((self .state . ysize + 3 ) // 4 ):
410
413
for d in decode_dxt3 (self ._safe_read (linesize )):
411
414
data += d
412
415
413
- elif self ._blp_alpha_encoding == AlphaEncoding .DXT5 :
414
- linesize = (self .size [ 0 ] + 3 ) // 4 * 16
415
- for yb in range ((self .size [ 1 ] + 3 ) // 4 ):
416
+ elif self ._alpha_encoding == AlphaEncoding .DXT5 :
417
+ linesize = (self .state . xsize + 3 ) // 4 * 16
418
+ for yb in range ((self .state . ysize + 3 ) // 4 ):
416
419
for d in decode_dxt5 (self ._safe_read (linesize )):
417
420
data += d
418
421
else :
419
- msg = f"Unsupported alpha encoding { repr (self ._blp_alpha_encoding )} "
422
+ msg = f"Unsupported alpha encoding { repr (self ._alpha_encoding )} "
420
423
raise BLPFormatError (msg )
421
424
else :
422
- msg = f"Unknown BLP encoding { repr (self ._blp_encoding )} "
425
+ msg = f"Unknown BLP encoding { repr (self ._encoding )} "
423
426
raise BLPFormatError (msg )
424
427
425
428
else :
426
- msg = f"Unknown BLP compression { repr (self ._blp_compression )} "
429
+ msg = f"Unknown BLP compression { repr (self ._compression )} "
427
430
raise BLPFormatError (msg )
428
431
429
432
self .set_as_raw (data )
@@ -472,10 +475,15 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
472
475
473
476
assert im .palette is not None
474
477
fp .write (struct .pack ("<i" , 1 )) # Uncompressed or DirectX compression
475
- fp .write (struct .pack ("<b" , Encoding .UNCOMPRESSED ))
476
- fp .write (struct .pack ("<b" , 1 if im .palette .mode == "RGBA" else 0 ))
477
- fp .write (struct .pack ("<b" , 0 )) # alpha encoding
478
- fp .write (struct .pack ("<b" , 0 )) # mips
478
+
479
+ alpha_depth = 1 if im .palette .mode == "RGBA" else 0
480
+ if magic == b"BLP1" :
481
+ fp .write (struct .pack ("<L" , alpha_depth ))
482
+ else :
483
+ fp .write (struct .pack ("<b" , Encoding .UNCOMPRESSED ))
484
+ fp .write (struct .pack ("<b" , alpha_depth ))
485
+ fp .write (struct .pack ("<b" , 0 )) # alpha encoding
486
+ fp .write (struct .pack ("<b" , 0 )) # mips
479
487
fp .write (struct .pack ("<II" , * im .size ))
480
488
if magic == b"BLP1" :
481
489
fp .write (struct .pack ("<i" , 5 ))
0 commit comments