| Index: packagerChrome.py | 
| =================================================================== | 
| --- a/packagerChrome.py | 
| +++ b/packagerChrome.py | 
| @@ -18,6 +18,9 @@ | 
| import sys, os, re, json, struct | 
| from StringIO import StringIO | 
|  | 
| +import PIL.Image | 
| +import PIL.ImageMath | 
| + | 
| import packager | 
| from packager import readMetadata, getMetadataPath, getDefaultFileName, getBuildVersion, getTemplate, Files | 
|  | 
| @@ -279,10 +282,11 @@ | 
| files[operaFile] = files[chromeFile] | 
| del files[chromeFile] | 
|  | 
| -    # Hack: Replace "Chrome" by "Opera" in the locales | 
| +  if params['type'] in ('opera', 'safari'): | 
| +    # Hack: Replace "Chrome" by "Opera" or "Safari" in the locales | 
| for path, data in files.iteritems(): | 
| if path.startswith("_locales/") and path.endswith("/messages.json"): | 
| -        files[path] = re.sub(r"\bChrome\b", "Opera", data) | 
| +        files[path] = re.sub(r"\bChrome\b", params['type'].capitalize(), data) | 
|  | 
| def signBinary(zipdata, keyFile): | 
| import M2Crypto | 
| @@ -308,6 +312,126 @@ | 
| file.write(signature) | 
| file.write(zipdata) | 
|  | 
| +class ImageConverter(object): | 
| +  def convert(self, params, files): | 
| +    for filename, chain in params['metadata'].items('convert_img'): | 
| +      steps = re.split(r'\s*->\s*', chain) | 
| +      image = PIL.Image.open(steps.pop(0)) | 
| + | 
| +      for step in steps: | 
| +        filter, args = re.match(r'([^(]+)(?:\((.*)\))?', step).groups() | 
| +        args = tuple(re.split(r'\s*,\s*', args)) if args else () | 
| +        image = getattr(self, 'filter_' + filter)(image, *args) | 
| + | 
| +      f = StringIO() | 
| +      f.name = filename | 
| +      image.save(f) | 
| +      files[filename] = f.getvalue() | 
| + | 
| +  def filter_blend(self, image, *args): | 
| +    if len(args) == 2:    # args = (filename, opacity) | 
| +      overlay = PIL.Image.open(args[0]) | 
| + | 
| +      if image.mode != overlay.mode or image.mode == 'P': | 
| +        overlay = overlay.convert('RGBA') | 
| +        image   =   image.convert('RGBA') | 
| +    elif len(args) == 4:  # args = (red, green, blue, opacity) | 
| +      overlay = PIL.Image.new('RGB', image.size, tuple(map(int, args[:3]))) | 
| + | 
| +      if image.mode == 'P': | 
| +        image = image.convert('RGBA') | 
| + | 
| +      if image.mode in ('RGBA', 'LA'): | 
| +        overlay = PIL.Image.merge('RGBA', overlay.split() + image.split()[-1:]) | 
| + | 
| +      if image.mode != overlay.mode: | 
| +        image = image.convert(overlay.mode) | 
| +    else: | 
| +      raise TypeError | 
| + | 
| +    return PIL.Image.blend(image, overlay, float(args[-1])) | 
| + | 
| +  def filter_grayscale(self, image): | 
| +    if image.mode == 'P': | 
| +      image = image.convert('RGBA') | 
| + | 
| +    bands = list(image.split()) | 
| +    alpha = bands.pop(-1) if image.mode in ('RGBA', 'LA') else None | 
| + | 
| +    if len(bands) == 1: | 
| +      return image | 
| + | 
| +    new_image = PIL.ImageMath.eval( | 
| +      "convert((%s) / %d, 'L')" % ( | 
| +        ' + '.join('image%d' % i for i in xrange(len(bands))), | 
| +        len(bands) | 
| +      ), | 
| +      **dict(('image%d' % i, band) for i, band in enumerate(bands)) | 
| +    ) | 
| + | 
| +    if alpha: | 
| +      new_image = PIL.Image.merge('LA', [new_image, alpha]) | 
| + | 
| +    return new_image | 
| + | 
| +  def filter_colorToAlpha(self, image, *color): | 
| +    bands = [band.convert('F') for band in image.convert('RGBA').split()] | 
| +    color = map(float, color) | 
| + | 
| +    # Find the maximum difference rate between source and color. I had to use two | 
| +    # difference functions because ImageMath.eval only evaluates the expression | 
| +    # once. | 
| +    alpha = PIL.ImageMath.eval('''\ | 
| +      float( | 
| +        max( | 
| +          max( | 
| +            max( | 
| +              difference1(red_band, cred_band), | 
| +              difference1(green_band, cgreen_band) | 
| +            ), | 
| +            difference1(blue_band, cblue_band) | 
| +          ), | 
| +          max( | 
| +            max( | 
| +              difference2(red_band, cred_band), | 
| +              difference2(green_band, cgreen_band) | 
| +            ), | 
| +            difference2(blue_band, cblue_band) | 
| +          ) | 
| +        ) | 
| +      )''', | 
| +      difference1=lambda source, col: (source - col) / (255.0 - col), | 
| +      difference2=lambda source, col: (col - source) / col, | 
| + | 
| +      red_band  = bands[0], | 
| +      green_band= bands[1], | 
| +      blue_band = bands[2], | 
| + | 
| +      cred_band  = color[0], | 
| +      cgreen_band= color[1], | 
| +      cblue_band = color[2], | 
| +    ) | 
| + | 
| +    # Calculate the new image colors after the removal of the selected color | 
| +    new_bands = [ | 
| +      PIL.ImageMath.eval( | 
| +        "convert((image - color) / alpha + color, 'L')", | 
| +        image=band, | 
| +        color=col, | 
| +        alpha=alpha | 
| +      ) | 
| +      for band, col in zip(bands, color) | 
| +    ] | 
| + | 
| +    # Add the new alpha band | 
| +    new_bands.append(PIL.ImageMath.eval( | 
| +      "convert(alpha_band * alpha, 'L')", | 
| +      alpha = alpha, | 
| +      alpha_band = bands[3] | 
| +    )) | 
| + | 
| +    return PIL.Image.merge('RGBA', new_bands) | 
| + | 
| def createBuild(baseDir, type='chrome', outFile=None, buildNum=None, releaseBuild=False, keyFile=None, experimentalAPI=False, devenv=False): | 
| metadata = readMetadata(baseDir, type) | 
| version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum) | 
| @@ -335,6 +459,9 @@ | 
| if metadata.has_section('convert_js'): | 
| convertJS(params, files) | 
|  | 
| +  if metadata.has_section('convert_img'): | 
| +    ImageConverter().convert(params, files) | 
| + | 
| if metadata.has_section('import_locales'): | 
| importGeckoLocales(params, files) | 
|  | 
|  |