badge.py 6.06 KB
Newer Older
huahua committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from Quartz import *
import math

_REMOVABLE_DISK_PATH = '/System/Library/Extensions/IOStorageFamily.kext/Contents/Resources/Removable.icns'

def badge_disk_icon(badge_file, output_file):
    # Load the Removable disk icon
    url = CFURLCreateWithFileSystemPath(None, _REMOVABLE_DISK_PATH,
                                        kCFURLPOSIXPathStyle, False)
    backdrop = CGImageSourceCreateWithURL(url, None)
    backdropCount = CGImageSourceGetCount(backdrop)
    
    # Load the badge
    url = CFURLCreateWithFileSystemPath(None, badge_file,
                                        kCFURLPOSIXPathStyle, False)
    badge = CGImageSourceCreateWithURL(url, None)
    assert badge is not None, 'Unable to process image file: %s' % badge_file
    badgeCount = CGImageSourceGetCount(badge)
    
    # Set up a destination for our target
    url = CFURLCreateWithFileSystemPath(None, output_file,
                                        kCFURLPOSIXPathStyle, False)
    target = CGImageDestinationCreateWithURL(url, 'com.apple.icns',
                                             backdropCount, None)

    # Get the RGB colorspace
    rgbColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)

    # Scale
    scale = 1.0
    
    # Perspective transform
    corners = ((0.2, 0.95), (0.8, 0.95), (0.85, 0.35), (0.15, 0.35))

    # Translation
    position = (0.5, 0.5)

    for n in range(backdropCount):
        props = CGImageSourceCopyPropertiesAtIndex(backdrop, n, None)
        width = props['PixelWidth']
        height = props['PixelHeight']
        dpi = props['DPIWidth']
        depth = props['Depth']
        
        # Choose the best sized badge image
        bestWidth = None
        bestHeight = None
        bestBadge = None
        bestDepth = None
        bestDPI = None
        for m in range(badgeCount):
            badgeProps = CGImageSourceCopyPropertiesAtIndex(badge, m, None)
            badgeWidth = badgeProps['PixelWidth']
            badgeHeight = badgeProps['PixelHeight']
            badgeDPI = badgeProps['DPIWidth']
            badgeDepth = badgeProps['Depth']
            
            if bestBadge is None or (badgeWidth <= width
                                    and (bestWidth > width
                                        or badgeWidth > bestWidth
                                        or (badgeWidth == bestWidth
                                            and badgeDPI == dpi
                                            and badgeDepth <= depth
                                            and (bestDepth is None
                                                 or badgeDepth > bestDepth)))):
                bestBadge = m
                bestWidth = badgeWidth
                bestHeight = badgeHeight
                bestDPI = badgeDPI
                bestDepth = badgeDepth

        badgeImage = CGImageSourceCreateImageAtIndex(badge, bestBadge, None)
        badgeCI = CIImage.imageWithCGImage_(badgeImage)
    
        backgroundImage = CGImageSourceCreateImageAtIndex(backdrop, n, None)
        backgroundCI = CIImage.imageWithCGImage_(backgroundImage)
    
        compositor = CIFilter.filterWithName_('CISourceOverCompositing')
        lanczos = CIFilter.filterWithName_('CILanczosScaleTransform')
        perspective = CIFilter.filterWithName_('CIPerspectiveTransform')
        transform = CIFilter.filterWithName_('CIAffineTransform')
    
        lanczos.setValue_forKey_(badgeCI, kCIInputImageKey)
        lanczos.setValue_forKey_(scale * float(width)/bestWidth, kCIInputScaleKey)
        lanczos.setValue_forKey_(1.0, kCIInputAspectRatioKey)

        topLeft = (width * scale * corners[0][0],
                   width * scale * corners[0][1])
        topRight = (width * scale * corners[1][0],
                    width * scale * corners[1][1])
        bottomRight = (width * scale * corners[2][0],
                       width * scale * corners[2][1])
        bottomLeft = (width * scale * corners[3][0],
                      width * scale * corners[3][1])

        out = lanczos.valueForKey_(kCIOutputImageKey)
        if width >= 16:
            perspective.setValue_forKey_(out, kCIInputImageKey)
            perspective.setValue_forKey_(CIVector.vectorWithX_Y_(*topLeft),
                                         'inputTopLeft')
            perspective.setValue_forKey_(CIVector.vectorWithX_Y_(*topRight),
                                         'inputTopRight')
            perspective.setValue_forKey_(CIVector.vectorWithX_Y_(*bottomRight),
                                         'inputBottomRight')
            perspective.setValue_forKey_(CIVector.vectorWithX_Y_(*bottomLeft),
                                         'inputBottomLeft')
            out = perspective.valueForKey_(kCIOutputImageKey)

        tfm = NSAffineTransform.transform()
        tfm.translateXBy_yBy_(math.floor((position[0] - 0.5 * scale) * width),
                              math.floor((position[1] - 0.5 * scale) * height))

        transform.setValue_forKey_(out, kCIInputImageKey)
        transform.setValue_forKey_(tfm, 'inputTransform')
        out = transform.valueForKey_(kCIOutputImageKey)
    
        compositor.setValue_forKey_(out, kCIInputImageKey)
        compositor.setValue_forKey_(backgroundCI, kCIInputBackgroundImageKey)

        result = compositor.valueForKey_(kCIOutputImageKey)

        cgContext = CGBitmapContextCreate(None,
                                        width,
                                        height,
                                        8,
                                        0,
                                        rgbColorSpace,
                                        kCGImageAlphaPremultipliedLast)
        context = CIContext.contextWithCGContext_options_(cgContext, None)

        context.drawImage_inRect_fromRect_(result,
                                           ((0, 0), (width, height)),
                                           ((0, 0), (width, height)))

        image = CGBitmapContextCreateImage(cgContext)

        CGImageDestinationAddImage(target, image, props)

    CGImageDestinationFinalize(target)