Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Unified Diff: tools/resources/ico_tools.py

Issue 1272763003: optimize-ico-files: Automatically convert large BMPs to PNG format. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@optimize-ico-files
Patch Set: Added TODO to compress all the images (no longer need XP). Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/app/theme/README ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/resources/ico_tools.py
diff --git a/tools/resources/ico_tools.py b/tools/resources/ico_tools.py
index 259f7f197612b6dcc92e924e413729d78d280a19..9c3128b56a7e0d422a0ab6565b38f0cc8dfad6d0 100644
--- a/tools/resources/ico_tools.py
+++ b/tools/resources/ico_tools.py
@@ -11,6 +11,7 @@ import sys
import tempfile
OPTIMIZE_PNG_FILES = 'tools/resources/optimize-png-files.sh'
+IMAGEMAGICK_CONVERT = 'convert'
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
@@ -69,6 +70,79 @@ def OptimizePng(png_data, optimization_level=None):
os.unlink(png_filename)
os.rmdir(temp_dir)
+def ExportSingleEntry(icon_dir_entry, icon_data, outfile):
+ """Export a single icon dir entry to its own ICO file.
+
+ Args:
+ icon_dir_entry: Struct containing the fields of an ICONDIRENTRY.
+ icon_data: Raw pixel data of the icon.
+ outfile: File object to write to.
+ """
+ # Write the ICONDIR header.
+ logging.debug('len(icon_data) = %d', len(icon_data))
+ outfile.write(struct.pack('<HHH', 0, 1, 1))
+
+ # Write the ICONDIRENTRY header.
+ width, height, num_colors, r1, r2, r3, size, _ = icon_dir_entry
+ offset = 22;
+ icon_dir_entry = width, height, num_colors, r1, r2, r3, size, offset
+ outfile.write(struct.pack('<BBBBHHLL', *icon_dir_entry))
+
+ # Write the image data.
+ outfile.write(icon_data)
+
+def ConvertIcoToPng(ico_filename, png_filename):
+ """Convert a single-entry ICO file to a PNG image.
+
+ Requires that the user has `convert` (ImageMagick) installed.
+
+ Raises:
+ OSError: If ImageMagick was not found.
+ subprocess.CalledProcessError: If convert failed.
+ """
+ logging.debug('Converting BMP image to PNG...')
+ args = [IMAGEMAGICK_CONVERT, ico_filename, png_filename]
+ result = subprocess.check_call(args, stdout=sys.stderr)
+ logging.info('Converted BMP image to PNG format')
+
+def OptimizeBmp(icon_dir_entry, icon_data):
+ """Convert a BMP file to PNG and optimize it.
+
+ Args:
+ icon_dir_entry: Struct containing the fields of an ICONDIRENTRY.
+ icon_data: Raw pixel data of the icon.
+
+ Returns:
+ The raw bytes of a PNG file, an optimized version of the input.
+ """
+ temp_dir = tempfile.mkdtemp()
+ try:
+ logging.debug('temp_dir = %s', temp_dir)
+ ico_filename = os.path.join(temp_dir, 'image.ico')
+ png_filename = os.path.join(temp_dir, 'image.png')
+ with open(ico_filename, 'wb') as ico_file:
+ logging.debug('writing %s', ico_filename)
+ ExportSingleEntry(icon_dir_entry, icon_data, ico_file)
+
+ try:
+ ConvertIcoToPng(ico_filename, png_filename)
+ except Exception as e:
+ logging.warning('Could not convert BMP to PNG format: %s', e)
+ if isinstance(e, OSError):
+ logging.info('This is because ImageMagick (`convert`) was not found. '
+ 'Please install it, or manually convert large BMP images '
+ 'into PNG before running this utility.')
+ return icon_data
+
+ return OptimizePngFile(temp_dir, png_filename)
+
+ finally:
+ if os.path.exists(ico_filename):
+ os.unlink(ico_filename)
+ if os.path.exists(png_filename):
+ os.unlink(png_filename)
+ os.rmdir(temp_dir)
+
def ComputeANDMaskFromAlpha(image_data, width, height):
"""Compute an AND mask from 32-bit BGRA image data."""
and_bytes = []
@@ -175,19 +249,21 @@ def OptimizeIcoFile(infile, outfile, optimization_level=None):
height, size, 'PNG' if entry_is_png else 'BMP')
if entry_is_png:
+ # It is a PNG. Crush it.
icon_data = OptimizePng(icon_data, optimization_level=optimization_level)
+ elif width >= 256 or height >= 256:
+ # It is a large BMP. Reformat as a PNG, then crush it.
+ # Note: Smaller images are kept uncompressed, for compatibility with
+ # Windows XP.
+ # TODO(mgiuca): Now that we no longer support XP, we can probably compress
+ # all of the images. https://crbug.com/663136
+ icon_data = OptimizeBmp(icon_dir_entries[i], icon_data)
else:
new_icon_data = RebuildANDMask(icon_data)
if new_icon_data != icon_data:
logging.info(' * Rebuilt AND mask for this image from alpha channel.')
icon_data = new_icon_data
- if width >= 256 or height >= 256:
- # TODO(mgiuca): Automatically convert large BMP images to PNGs.
- logging.warning('Entry #%d is a large image in uncompressed BMP '
- 'format. Please manually convert to PNG format before '
- 'running this utility.', i + 1)
-
new_size = len(icon_data)
current_offset += new_size
icon_dir_entries[i] = (width % 256, height % 256, num_colors, r1, r2, r3,
« no previous file with comments | « chrome/app/theme/README ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698