~ubuntu-branches/ubuntu/wily/python-imaging/wily

« back to all changes in this revision

Viewing changes to .pc/git-updates.diff/PIL/PngImagePlugin.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2013-01-31 20:49:20 UTC
  • mfrom: (27.1.1 raring-proposed)
  • Revision ID: package-import@ubuntu.com-20130131204920-b5zshy6vgfvdionl
Tags: 1.1.7+1.7.8-1ubuntu1
Rewrite build dependencies to allow cross builds.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# The Python Imaging Library.
 
3
# $Id$
 
4
#
 
5
# PNG support code
 
6
#
 
7
# See "PNG (Portable Network Graphics) Specification, version 1.0;
 
8
# W3C Recommendation", 1996-10-01, Thomas Boutell (ed.).
 
9
#
 
10
# history:
 
11
# 1996-05-06 fl   Created (couldn't resist it)
 
12
# 1996-12-14 fl   Upgraded, added read and verify support (0.2)
 
13
# 1996-12-15 fl   Separate PNG stream parser
 
14
# 1996-12-29 fl   Added write support, added getchunks
 
15
# 1996-12-30 fl   Eliminated circular references in decoder (0.3)
 
16
# 1998-07-12 fl   Read/write 16-bit images as mode I (0.4)
 
17
# 2001-02-08 fl   Added transparency support (from Zircon) (0.5)
 
18
# 2001-04-16 fl   Don't close data source in "open" method (0.6)
 
19
# 2004-02-24 fl   Don't even pretend to support interlaced files (0.7)
 
20
# 2004-08-31 fl   Do basic sanity check on chunk identifiers (0.8)
 
21
# 2004-09-20 fl   Added PngInfo chunk container
 
22
# 2004-12-18 fl   Added DPI read support (based on code by Niki Spahiev)
 
23
# 2008-08-13 fl   Added tRNS support for RGB images
 
24
# 2009-03-06 fl   Support for preserving ICC profiles (by Florian Hoech)
 
25
# 2009-03-08 fl   Added zTXT support (from Lowell Alleman)
 
26
# 2009-03-29 fl   Read interlaced PNG files (from Conrado Porto Lopes Gouvua)
 
27
#
 
28
# Copyright (c) 1997-2009 by Secret Labs AB
 
29
# Copyright (c) 1996 by Fredrik Lundh
 
30
#
 
31
# See the README file for information on usage and redistribution.
 
32
#
 
33
 
 
34
__version__ = "0.9"
 
35
 
 
36
import re, string
 
37
 
 
38
import Image, ImageFile, ImagePalette, zlib
 
39
 
 
40
 
 
41
def i16(c):
 
42
    return ord(c[1]) + (ord(c[0])<<8)
 
43
def i32(c):
 
44
    return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
 
45
 
 
46
is_cid = re.compile("\w\w\w\w").match
 
47
 
 
48
 
 
49
_MAGIC = "\211PNG\r\n\032\n"
 
50
 
 
51
 
 
52
_MODES = {
 
53
    # supported bits/color combinations, and corresponding modes/rawmodes
 
54
    (1, 0): ("1", "1"),
 
55
    (2, 0): ("L", "L;2"),
 
56
    (4, 0): ("L", "L;4"),
 
57
    (8, 0): ("L", "L"),
 
58
    (16,0): ("I", "I;16B"),
 
59
    (8, 2): ("RGB", "RGB"),
 
60
    (16,2): ("RGB", "RGB;16B"),
 
61
    (1, 3): ("P", "P;1"),
 
62
    (2, 3): ("P", "P;2"),
 
63
    (4, 3): ("P", "P;4"),
 
64
    (8, 3): ("P", "P"),
 
65
    (8, 4): ("LA", "LA"),
 
66
    (16,4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
 
67
    (8, 6): ("RGBA", "RGBA"),
 
68
    (16,6): ("RGBA", "RGBA;16B"),
 
69
}
 
70
 
 
71
 
 
72
# --------------------------------------------------------------------
 
73
# Support classes.  Suitable for PNG and related formats like MNG etc.
 
74
 
 
75
class ChunkStream:
 
76
 
 
77
    def __init__(self, fp):
 
78
 
 
79
        self.fp = fp
 
80
        self.queue = []
 
81
 
 
82
        if not hasattr(Image.core, "crc32"):
 
83
            self.crc = self.crc_skip
 
84
 
 
85
    def read(self):
 
86
        "Fetch a new chunk. Returns header information."
 
87
 
 
88
        if self.queue:
 
89
            cid, pos, len = self.queue[-1]
 
90
            del self.queue[-1]
 
91
            self.fp.seek(pos)
 
92
        else:
 
93
            s = self.fp.read(8)
 
94
            cid = s[4:]
 
95
            pos = self.fp.tell()
 
96
            len = i32(s)
 
97
 
 
98
        if not is_cid(cid):
 
99
            raise SyntaxError, "broken PNG file (chunk %s)" % repr(cid)
 
100
 
 
101
        return cid, pos, len
 
102
 
 
103
    def close(self):
 
104
        self.queue = self.crc = self.fp = None
 
105
 
 
106
    def push(self, cid, pos, len):
 
107
 
 
108
        self.queue.append((cid, pos, len))
 
109
 
 
110
    def call(self, cid, pos, len):
 
111
        "Call the appropriate chunk handler"
 
112
 
 
113
        if Image.DEBUG:
 
114
            print "STREAM", cid, pos, len
 
115
        return getattr(self, "chunk_" + cid)(pos, len)
 
116
 
 
117
    def crc(self, cid, data):
 
118
        "Read and verify checksum"
 
119
 
 
120
        crc1 = Image.core.crc32(data, Image.core.crc32(cid))
 
121
        crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
 
122
        if crc1 != crc2:
 
123
            raise SyntaxError, "broken PNG file"\
 
124
                "(bad header checksum in %s)" % cid
 
125
 
 
126
    def crc_skip(self, cid, data):
 
127
        "Read checksum.  Used if the C module is not present"
 
128
 
 
129
        self.fp.read(4)
 
130
 
 
131
    def verify(self, endchunk = "IEND"):
 
132
 
 
133
        # Simple approach; just calculate checksum for all remaining
 
134
        # blocks.  Must be called directly after open.
 
135
 
 
136
        cids = []
 
137
 
 
138
        while 1:
 
139
            cid, pos, len = self.read()
 
140
            if cid == endchunk:
 
141
                break
 
142
            self.crc(cid, ImageFile._safe_read(self.fp, len))
 
143
            cids.append(cid)
 
144
 
 
145
        return cids
 
146
 
 
147
 
 
148
# --------------------------------------------------------------------
 
149
# PNG chunk container (for use with save(pnginfo=))
 
150
 
 
151
class PngInfo:
 
152
 
 
153
    def __init__(self):
 
154
        self.chunks = []
 
155
 
 
156
    def add(self, cid, data):
 
157
        self.chunks.append((cid, data))
 
158
 
 
159
    def add_text(self, key, value, zip=0):
 
160
        if zip:
 
161
            import zlib
 
162
            self.add("zTXt", key + "\0\0" + zlib.compress(value))
 
163
        else:
 
164
            self.add("tEXt", key + "\0" + value)
 
165
 
 
166
# --------------------------------------------------------------------
 
167
# PNG image stream (IHDR/IEND)
 
168
 
 
169
class PngStream(ChunkStream):
 
170
 
 
171
    def __init__(self, fp):
 
172
 
 
173
        ChunkStream.__init__(self, fp)
 
174
 
 
175
        # local copies of Image attributes
 
176
        self.im_info = {}
 
177
        self.im_text = {}
 
178
        self.im_size = (0,0)
 
179
        self.im_mode = None
 
180
        self.im_tile = None
 
181
        self.im_palette = None
 
182
 
 
183
    def chunk_iCCP(self, pos, len):
 
184
 
 
185
        # ICC profile
 
186
        s = ImageFile._safe_read(self.fp, len)
 
187
        # according to PNG spec, the iCCP chunk contains:
 
188
        # Profile name  1-79 bytes (character string)
 
189
        # Null separator        1 byte (null character)
 
190
        # Compression method    1 byte (0)
 
191
        # Compressed profile    n bytes (zlib with deflate compression)
 
192
        i = string.find(s, chr(0))
 
193
        if Image.DEBUG:
 
194
            print "iCCP profile name", s[:i]
 
195
            print "Compression method", ord(s[i])
 
196
        comp_method = ord(s[i])
 
197
        if comp_method != 0:
 
198
            raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method)
 
199
        try:
 
200
            icc_profile = zlib.decompress(s[i+2:])
 
201
        except zlib.error:
 
202
            icc_profile = None # FIXME
 
203
        self.im_info["icc_profile"] = icc_profile
 
204
        return s
 
205
 
 
206
    def chunk_IHDR(self, pos, len):
 
207
 
 
208
        # image header
 
209
        s = ImageFile._safe_read(self.fp, len)
 
210
        self.im_size = i32(s), i32(s[4:])
 
211
        try:
 
212
            self.im_mode, self.im_rawmode = _MODES[(ord(s[8]), ord(s[9]))]
 
213
        except:
 
214
            pass
 
215
        if ord(s[12]):
 
216
            self.im_info["interlace"] = 1
 
217
        if ord(s[11]):
 
218
            raise SyntaxError, "unknown filter category"
 
219
        return s
 
220
 
 
221
    def chunk_IDAT(self, pos, len):
 
222
 
 
223
        # image data
 
224
        self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)]
 
225
        self.im_idat = len
 
226
        raise EOFError
 
227
 
 
228
    def chunk_IEND(self, pos, len):
 
229
 
 
230
        # end of PNG image
 
231
        raise EOFError
 
232
 
 
233
    def chunk_PLTE(self, pos, len):
 
234
 
 
235
        # palette
 
236
        s = ImageFile._safe_read(self.fp, len)
 
237
        if self.im_mode == "P":
 
238
            self.im_palette = "RGB", s
 
239
        return s
 
240
 
 
241
    def chunk_tRNS(self, pos, len):
 
242
 
 
243
        # transparency
 
244
        s = ImageFile._safe_read(self.fp, len)
 
245
        if self.im_mode == "P":
 
246
            i = string.find(s, chr(0))
 
247
            if i >= 0:
 
248
                self.im_info["transparency"] = i
 
249
        elif self.im_mode == "L":
 
250
            self.im_info["transparency"] = i16(s)
 
251
        elif self.im_mode == "RGB":
 
252
            self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:])
 
253
        return s
 
254
 
 
255
    def chunk_gAMA(self, pos, len):
 
256
 
 
257
        # gamma setting
 
258
        s = ImageFile._safe_read(self.fp, len)
 
259
        self.im_info["gamma"] = i32(s) / 100000.0
 
260
        return s
 
261
 
 
262
    def chunk_pHYs(self, pos, len):
 
263
 
 
264
        # pixels per unit
 
265
        s = ImageFile._safe_read(self.fp, len)
 
266
        px, py = i32(s), i32(s[4:])
 
267
        unit = ord(s[8])
 
268
        if unit == 1: # meter
 
269
            dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5)
 
270
            self.im_info["dpi"] = dpi
 
271
        elif unit == 0:
 
272
            self.im_info["aspect"] = px, py
 
273
        return s
 
274
 
 
275
    def chunk_tEXt(self, pos, len):
 
276
 
 
277
        # text
 
278
        s = ImageFile._safe_read(self.fp, len)
 
279
        try:
 
280
            k, v = string.split(s, "\0", 1)
 
281
        except ValueError:
 
282
            k = s; v = "" # fallback for broken tEXt tags
 
283
        if k:
 
284
            self.im_info[k] = self.im_text[k] = v
 
285
        return s
 
286
 
 
287
    def chunk_zTXt(self, pos, len):
 
288
 
 
289
        # compressed text
 
290
        s = ImageFile._safe_read(self.fp, len)
 
291
        k, v = string.split(s, "\0", 1)
 
292
        comp_method = ord(v[0])
 
293
        if comp_method != 0:
 
294
            raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method)
 
295
        import zlib
 
296
        self.im_info[k] = self.im_text[k] = zlib.decompress(v[1:])
 
297
        return s
 
298
 
 
299
# --------------------------------------------------------------------
 
300
# PNG reader
 
301
 
 
302
def _accept(prefix):
 
303
    return prefix[:8] == _MAGIC
 
304
 
 
305
##
 
306
# Image plugin for PNG images.
 
307
 
 
308
class PngImageFile(ImageFile.ImageFile):
 
309
 
 
310
    format = "PNG"
 
311
    format_description = "Portable network graphics"
 
312
 
 
313
    def _open(self):
 
314
 
 
315
        if self.fp.read(8) != _MAGIC:
 
316
            raise SyntaxError, "not a PNG file"
 
317
 
 
318
        #
 
319
        # Parse headers up to the first IDAT chunk
 
320
 
 
321
        self.png = PngStream(self.fp)
 
322
 
 
323
        while 1:
 
324
 
 
325
            #
 
326
            # get next chunk
 
327
 
 
328
            cid, pos, len = self.png.read()
 
329
 
 
330
            try:
 
331
                s = self.png.call(cid, pos, len)
 
332
            except EOFError:
 
333
                break
 
334
            except AttributeError:
 
335
                if Image.DEBUG:
 
336
                    print cid, pos, len, "(unknown)"
 
337
                s = ImageFile._safe_read(self.fp, len)
 
338
 
 
339
            self.png.crc(cid, s)
 
340
 
 
341
        #
 
342
        # Copy relevant attributes from the PngStream.  An alternative
 
343
        # would be to let the PngStream class modify these attributes
 
344
        # directly, but that introduces circular references which are
 
345
        # difficult to break if things go wrong in the decoder...
 
346
        # (believe me, I've tried ;-)
 
347
 
 
348
        self.mode = self.png.im_mode
 
349
        self.size = self.png.im_size
 
350
        self.info = self.png.im_info
 
351
        self.text = self.png.im_text # experimental
 
352
        self.tile = self.png.im_tile
 
353
 
 
354
        if self.png.im_palette:
 
355
            rawmode, data = self.png.im_palette
 
356
            self.palette = ImagePalette.raw(rawmode, data)
 
357
 
 
358
        self.__idat = len # used by load_read()
 
359
 
 
360
 
 
361
    def verify(self):
 
362
        "Verify PNG file"
 
363
 
 
364
        if self.fp is None:
 
365
            raise RuntimeError("verify must be called directly after open")
 
366
 
 
367
        # back up to beginning of IDAT block
 
368
        self.fp.seek(self.tile[0][2] - 8)
 
369
 
 
370
        self.png.verify()
 
371
        self.png.close()
 
372
 
 
373
        self.fp = None
 
374
 
 
375
    def load_prepare(self):
 
376
        "internal: prepare to read PNG file"
 
377
 
 
378
        if self.info.get("interlace"):
 
379
            self.decoderconfig = self.decoderconfig + (1,)
 
380
 
 
381
        ImageFile.ImageFile.load_prepare(self)
 
382
 
 
383
    def load_read(self, bytes):
 
384
        "internal: read more image data"
 
385
 
 
386
        while self.__idat == 0:
 
387
            # end of chunk, skip forward to next one
 
388
 
 
389
            self.fp.read(4) # CRC
 
390
 
 
391
            cid, pos, len = self.png.read()
 
392
 
 
393
            if cid not in ["IDAT", "DDAT"]:
 
394
                self.png.push(cid, pos, len)
 
395
                return ""
 
396
 
 
397
            self.__idat = len # empty chunks are allowed
 
398
 
 
399
        # read more data from this chunk
 
400
        if bytes <= 0:
 
401
            bytes = self.__idat
 
402
        else:
 
403
            bytes = min(bytes, self.__idat)
 
404
 
 
405
        self.__idat = self.__idat - bytes
 
406
 
 
407
        return self.fp.read(bytes)
 
408
 
 
409
 
 
410
    def load_end(self):
 
411
        "internal: finished reading image data"
 
412
 
 
413
        self.png.close()
 
414
        self.png = None
 
415
 
 
416
 
 
417
# --------------------------------------------------------------------
 
418
# PNG writer
 
419
 
 
420
def o16(i):
 
421
    return chr(i>>8&255) + chr(i&255)
 
422
 
 
423
def o32(i):
 
424
    return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255)
 
425
 
 
426
_OUTMODES = {
 
427
    # supported PIL modes, and corresponding rawmodes/bits/color combinations
 
428
    "1":   ("1", chr(1)+chr(0)),
 
429
    "L;1": ("L;1", chr(1)+chr(0)),
 
430
    "L;2": ("L;2", chr(2)+chr(0)),
 
431
    "L;4": ("L;4", chr(4)+chr(0)),
 
432
    "L":   ("L", chr(8)+chr(0)),
 
433
    "LA":  ("LA", chr(8)+chr(4)),
 
434
    "I":   ("I;16B", chr(16)+chr(0)),
 
435
    "P;1": ("P;1", chr(1)+chr(3)),
 
436
    "P;2": ("P;2", chr(2)+chr(3)),
 
437
    "P;4": ("P;4", chr(4)+chr(3)),
 
438
    "P":   ("P", chr(8)+chr(3)),
 
439
    "RGB": ("RGB", chr(8)+chr(2)),
 
440
    "RGBA":("RGBA", chr(8)+chr(6)),
 
441
}
 
442
 
 
443
def putchunk(fp, cid, *data):
 
444
    "Write a PNG chunk (including CRC field)"
 
445
 
 
446
    data = string.join(data, "")
 
447
 
 
448
    fp.write(o32(len(data)) + cid)
 
449
    fp.write(data)
 
450
    hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
 
451
    fp.write(o16(hi) + o16(lo))
 
452
 
 
453
class _idat:
 
454
    # wrap output from the encoder in IDAT chunks
 
455
 
 
456
    def __init__(self, fp, chunk):
 
457
        self.fp = fp
 
458
        self.chunk = chunk
 
459
    def write(self, data):
 
460
        self.chunk(self.fp, "IDAT", data)
 
461
 
 
462
def _save(im, fp, filename, chunk=putchunk, check=0):
 
463
    # save an image to disk (called by the save method)
 
464
 
 
465
    mode = im.mode
 
466
 
 
467
    if mode == "P":
 
468
 
 
469
        #
 
470
        # attempt to minimize storage requirements for palette images
 
471
 
 
472
        if im.encoderinfo.has_key("bits"):
 
473
 
 
474
            # number of bits specified by user
 
475
            n = 1 << im.encoderinfo["bits"]
 
476
 
 
477
        else:
 
478
 
 
479
            # check palette contents
 
480
            n = 256 # FIXME
 
481
 
 
482
        if n <= 2:
 
483
            bits = 1
 
484
        elif n <= 4:
 
485
            bits = 2
 
486
        elif n <= 16:
 
487
            bits = 4
 
488
        else:
 
489
            bits = 8
 
490
 
 
491
        if bits != 8:
 
492
            mode = "%s;%d" % (mode, bits)
 
493
 
 
494
    # encoder options
 
495
    if im.encoderinfo.has_key("dictionary"):
 
496
        dictionary = im.encoderinfo["dictionary"]
 
497
    else:
 
498
        dictionary = ""
 
499
 
 
500
    im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary)
 
501
 
 
502
    # get the corresponding PNG mode
 
503
    try:
 
504
        rawmode, mode = _OUTMODES[mode]
 
505
    except KeyError:
 
506
        raise IOError, "cannot write mode %s as PNG" % mode
 
507
 
 
508
    if check:
 
509
        return check
 
510
 
 
511
    #
 
512
    # write minimal PNG file
 
513
 
 
514
    fp.write(_MAGIC)
 
515
 
 
516
    chunk(fp, "IHDR",
 
517
          o32(im.size[0]), o32(im.size[1]),     #  0: size
 
518
          mode,                                 #  8: depth/type
 
519
          chr(0),                               # 10: compression
 
520
          chr(0),                               # 11: filter category
 
521
          chr(0))                               # 12: interlace flag
 
522
 
 
523
    if im.mode == "P":
 
524
        chunk(fp, "PLTE", im.im.getpalette("RGB"))
 
525
 
 
526
    if im.encoderinfo.has_key("transparency"):
 
527
        if im.mode == "P":
 
528
            transparency = max(0, min(255, im.encoderinfo["transparency"]))
 
529
            chunk(fp, "tRNS", chr(255) * transparency + chr(0))
 
530
        elif im.mode == "L":
 
531
            transparency = max(0, min(65535, im.encoderinfo["transparency"]))
 
532
            chunk(fp, "tRNS", o16(transparency))
 
533
        elif im.mode == "RGB":
 
534
            red, green, blue = im.encoderinfo["transparency"]
 
535
            chunk(fp, "tRNS", o16(red) + o16(green) + o16(blue))
 
536
        else:
 
537
            raise IOError("cannot use transparency for this mode")
 
538
 
 
539
    if 0:
 
540
        # FIXME: to be supported some day
 
541
        chunk(fp, "gAMA", o32(int(gamma * 100000.0)))
 
542
 
 
543
    dpi = im.encoderinfo.get("dpi")
 
544
    if dpi:
 
545
        chunk(fp, "pHYs",
 
546
              o32(int(dpi[0] / 0.0254 + 0.5)),
 
547
              o32(int(dpi[1] / 0.0254 + 0.5)),
 
548
              chr(1))
 
549
 
 
550
    info = im.encoderinfo.get("pnginfo")
 
551
    if info:
 
552
        for cid, data in info.chunks:
 
553
            chunk(fp, cid, data)
 
554
 
 
555
    # ICC profile writing support -- 2008-06-06 Florian Hoech
 
556
    if im.info.has_key("icc_profile"):
 
557
        # ICC profile
 
558
        # according to PNG spec, the iCCP chunk contains:
 
559
        # Profile name  1-79 bytes (character string)
 
560
        # Null separator        1 byte (null character)
 
561
        # Compression method    1 byte (0)
 
562
        # Compressed profile    n bytes (zlib with deflate compression)
 
563
        try:
 
564
            import ICCProfile
 
565
            p = ICCProfile.ICCProfile(im.info["icc_profile"])
 
566
            name = p.tags.desc.get("ASCII", p.tags.desc.get("Unicode", p.tags.desc.get("Macintosh", p.tags.desc.get("en", {}).get("US", "ICC Profile")))).encode("latin1", "replace")[:79]
 
567
        except ImportError:
 
568
            name = "ICC Profile"
 
569
        data = name + "\0\0" + zlib.compress(im.info["icc_profile"])
 
570
        chunk(fp, "iCCP", data)
 
571
 
 
572
    ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)])
 
573
 
 
574
    chunk(fp, "IEND", "")
 
575
 
 
576
    try:
 
577
        fp.flush()
 
578
    except:
 
579
        pass
 
580
 
 
581
 
 
582
# --------------------------------------------------------------------
 
583
# PNG chunk converter
 
584
 
 
585
def getchunks(im, **params):
 
586
    """Return a list of PNG chunks representing this image."""
 
587
 
 
588
    class collector:
 
589
        data = []
 
590
        def write(self, data):
 
591
            pass
 
592
        def append(self, chunk):
 
593
            self.data.append(chunk)
 
594
 
 
595
    def append(fp, cid, *data):
 
596
        data = string.join(data, "")
 
597
        hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
 
598
        crc = o16(hi) + o16(lo)
 
599
        fp.append((cid, data, crc))
 
600
 
 
601
    fp = collector()
 
602
 
 
603
    try:
 
604
        im.encoderinfo = params
 
605
        _save(im, fp, None, append)
 
606
    finally:
 
607
        del im.encoderinfo
 
608
 
 
609
    return fp.data
 
610
 
 
611
 
 
612
# --------------------------------------------------------------------
 
613
# Registry
 
614
 
 
615
Image.register_open("PNG", PngImageFile, _accept)
 
616
Image.register_save("PNG", _save)
 
617
 
 
618
Image.register_extension("PNG", ".png")
 
619
 
 
620
Image.register_mime("PNG", "image/png")