Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: imageCompression.py

Issue 4663693082099712: Issue 1897 - Compress auto-generated images with pngout (Closed)
Patch Set: Created Jan. 28, 2015, 9:40 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | imageConversion.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # coding: utf-8
2
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 import os
8 import subprocess
9 import threading
10 import errno
11 import logging
12 from StringIO import StringIO
13
14 try:
15 from PIL import Image
16 except ImportError:
17 import Image
18
19 use_pngout = True
20
21 class Pngout:
22 def __init__(self, image):
23 args = ['pngout', '-', '-', '-q']
24
25 # Preserve mode for grayscale images. pngout tends to convert
26 # everyting to palette. However, the toolbar icons for Safari
27 # require the grayscale+alpha mode. Moreover, pngout seems to
28 # generate smaller files when forced to preserve grayscale mode.
29 if image.mode == 'LA' and any(px < 0xff for px in image.split()[1].getdata() ):
30 args.append('-c4') # grayscale+alpha
31 elif Image.getmodebase(image.mode) == 'L':
32 args.append('-c0') # grayscale
33
34 self._pngout = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subproce ss.PIPE)
35
36 # Writing will block when the buffer is full until we read more data
37 # from the output. Reading the output will block when the input isn't
38 # complete yet. So we have to use threads to do both at the same time.
39 self._thread = threading.Thread(target=self._run_thread, args=(image,))
40 self._thread.daemon = True
41 self._thread.start()
42
43 # This is supposed to be a file-like object, reading the compressed PNG file.
44 # So proxy methods like read() to the stdout of the underlying subprocess.
45 def __getattr__(self, name):
46 return getattr(self._pngout.stdout, name)
47
48 def _run_thread(self, image):
49 image.save(self._pngout.stdin, 'PNG')
50 self._pngout.stdin.close()
51
52 def close(self):
53 self._thread.join()
54 self._pngout.stdout.close()
55 self._pngout.wait()
56
57 class ImageCompressor:
58 use_pngout = True
59
60 def make_uncompressed_file(self, image, filename):
61 file = StringIO()
62 file.name = filename # Set the 'name' attribute, that PIL can determine
kzar 2015/02/10 09:29:54 Nitpick: Comment should read "...so that PIL can..
Sebastian Noack 2015/02/10 09:57:38 Done.
63 # the correct image type based on the file extension
64 image.save(file)
65 file.seek(0)
66
67 return file
68
69 def make_file(self, image, filename):
70 if self.use_pngout and os.path.splitext(filename)[1].lower() == '.png':
71 try:
72 return Pngout(image)
73 except OSError, e:
74 if e.errno != errno.ENOENT:
75 raise
76
77 logging.warning("Couldn't find 'pngout', can't compress images")
78 self.use_pngout = False
79
80 return self.make_uncompressed_file(image, filename)
81
82 image_to_file = ImageCompressor().make_file
OLDNEW
« no previous file with comments | « no previous file | imageConversion.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld