1515#
1616from __future__ import annotations
1717
18+ import math
19+
1820from . import Image , ImageFile
1921from ._binary import i16be as i16
2022from ._binary import o8
3537 b"P6" : "RGB" ,
3638 # extensions
3739 b"P0CMYK" : "CMYK" ,
40+ b"Pf" : "F" ,
3841 # PIL extensions (for test purposes only)
3942 b"PyP" : "P" ,
4043 b"PyRGBA" : "RGBA" ,
4346
4447
4548def _accept (prefix ):
46- return prefix [0 :1 ] == b"P" and prefix [1 ] in b"0123456y "
49+ return prefix [0 :1 ] == b"P" and prefix [1 ] in b"0123456fy "
4750
4851
4952##
@@ -110,6 +113,14 @@ def _open(self):
110113 if magic_number in (b"P1" , b"P2" , b"P3" ):
111114 decoder_name = "ppm_plain"
112115 for ix in range (3 ):
116+ if mode == "F" and ix == 2 :
117+ scale = float (self ._read_token ())
118+ if scale == 0.0 or not math .isfinite (scale ):
119+ msg = "scale must be finite and non-zero"
120+ raise ValueError (msg )
121+ rawmode = "F;32F" if scale < 0 else "F;32BF"
122+ self .info ["scale" ] = abs (scale )
123+ continue
113124 token = int (self ._read_token ())
114125 if ix == 0 : # token is the x size
115126 xsize = token
@@ -136,7 +147,8 @@ def _open(self):
136147 elif maxval != 255 :
137148 decoder_name = "ppm"
138149
139- args = (rawmode , 0 , 1 ) if decoder_name == "raw" else (rawmode , maxval )
150+ row_order = - 1 if mode == "F" else 1
151+ args = (rawmode , 0 , row_order ) if decoder_name == "raw" else (rawmode , maxval )
140152 self ._size = xsize , ysize
141153 self .tile = [(decoder_name , (0 , 0 , xsize , ysize ), self .fp .tell (), args )]
142154
@@ -307,6 +319,7 @@ def decode(self, buffer):
307319
308320
309321def _save (im , fp , filename ):
322+ row_order = 1
310323 if im .mode == "1" :
311324 rawmode , head = "1;I" , b"P4"
312325 elif im .mode == "L" :
@@ -315,6 +328,9 @@ def _save(im, fp, filename):
315328 rawmode , head = "I;16B" , b"P5"
316329 elif im .mode in ("RGB" , "RGBA" ):
317330 rawmode , head = "RGB" , b"P6"
331+ elif im .mode == "F" :
332+ rawmode , head = "F;32F" , b"Pf"
333+ row_order = - 1
318334 else :
319335 msg = f"cannot write mode { im .mode } as PPM"
320336 raise OSError (msg )
@@ -326,7 +342,9 @@ def _save(im, fp, filename):
326342 fp .write (b"255\n " )
327343 else :
328344 fp .write (b"65535\n " )
329- ImageFile ._save (im , fp , [("raw" , (0 , 0 ) + im .size , 0 , (rawmode , 0 , 1 ))])
345+ elif head == b"Pf" :
346+ fp .write (b"-1.0\n " )
347+ ImageFile ._save (im , fp , [("raw" , (0 , 0 ) + im .size , 0 , (rawmode , 0 , row_order ))])
330348
331349
332350#
@@ -339,6 +357,6 @@ def _save(im, fp, filename):
339357Image .register_decoder ("ppm" , PpmDecoder )
340358Image .register_decoder ("ppm_plain" , PpmPlainDecoder )
341359
342- Image .register_extensions (PpmImageFile .format , [".pbm" , ".pgm" , ".ppm" , ".pnm" ])
360+ Image .register_extensions (PpmImageFile .format , [".pbm" , ".pgm" , ".ppm" , ".pnm" , ".pfm" ])
343361
344362Image .register_mime (PpmImageFile .format , "image/x-portable-anymap" )
0 commit comments