diff --git a/kivy_ios/tools/external/xcassets.py b/kivy_ios/tools/external/xcassets.py index 11833dbe..d2289e52 100644 --- a/kivy_ios/tools/external/xcassets.py +++ b/kivy_ios/tools/external/xcassets.py @@ -4,6 +4,7 @@ .. author:: Mathieu Virbel """ + # flake8: noqa (E121 mainly) __all__ = ["launchimage"] @@ -353,344 +354,323 @@ } } - - launchimage_json = { - "images": [ - { - "extent": "full-screen", - "idiom": "iphone", - "subtype": "736h", - "filename": "Default1242x2208.png", - "minimum-system-version": "8.0", - "orientation": "portrait", - "scale": "3x" - }, - { - "extent": "full-screen", - "idiom": "iphone", - "subtype": "736h", - "filename": "Default2208x1242.png", - "minimum-system-version": "8.0", - "orientation": "landscape", - "scale": "3x" - }, - { - "extent": "full-screen", - "idiom": "iphone", - "subtype": "667h", - "filename": "Default750x1334.png", - "minimum-system-version": "8.0", - "orientation": "portrait", - "scale": "2x" - }, - { - "orientation": "portrait", - "idiom": "iphone", - "extent": "full-screen", - "minimum-system-version": "7.0", - "filename": "Default640x960.png", - "scale": "2x" - }, - { - "extent": "full-screen", - "idiom": "iphone", - "subtype": "retina4", - "filename": "Default640x1136.png", - "minimum-system-version": "7.0", - "orientation": "portrait", - "scale": "2x" - }, - { - "orientation": "portrait", - "idiom": "ipad", - "extent": "full-screen", - "minimum-system-version": "7.0", - "filename": "Default768x1024.png", - "scale": "1x" - }, - { - "orientation": "landscape", - "idiom": "ipad", - "extent": "full-screen", - "minimum-system-version": "7.0", - "filename": "Default1024x768.png", - "scale": "1x" - }, - { - "orientation": "portrait", - "idiom": "ipad", - "extent": "full-screen", - "minimum-system-version": "7.0", - "filename": "Default1536x2048.png", - "scale": "2x" + "images": [ + { + "extent": "full-screen", + "idiom": "iphone", + "subtype": "736h", + "filename": "Default1242x2208.png", + "minimum-system-version": "8.0", + "orientation": "portrait", + "scale": "3x", + }, + { + "extent": "full-screen", + "idiom": "iphone", + "subtype": "736h", + "filename": "Default2208x1242.png", + "minimum-system-version": "8.0", + "orientation": "landscape", + "scale": "3x", + }, + { + "extent": "full-screen", + "idiom": "iphone", + "subtype": "667h", + "filename": "Default750x1334.png", + "minimum-system-version": "8.0", + "orientation": "portrait", + "scale": "2x", + }, + { + "orientation": "portrait", + "idiom": "iphone", + "extent": "full-screen", + "minimum-system-version": "7.0", + "filename": "Default640x960.png", + "scale": "2x", + }, + { + "extent": "full-screen", + "idiom": "iphone", + "subtype": "retina4", + "filename": "Default640x1136.png", + "minimum-system-version": "7.0", + "orientation": "portrait", + "scale": "2x", + }, + { + "orientation": "portrait", + "idiom": "ipad", + "extent": "full-screen", + "minimum-system-version": "7.0", + "filename": "Default768x1024.png", + "scale": "1x", + }, + { + "orientation": "landscape", + "idiom": "ipad", + "extent": "full-screen", + "minimum-system-version": "7.0", + "filename": "Default1024x768.png", + "scale": "1x", + }, + { + "orientation": "portrait", + "idiom": "ipad", + "extent": "full-screen", + "minimum-system-version": "7.0", + "filename": "Default1536x2048.png", + "scale": "2x", + }, + { + "orientation": "landscape", + "idiom": "ipad", + "extent": "full-screen", + "minimum-system-version": "7.0", + "filename": "Default2048x1536.png", + "scale": "2x", + }, + { + "orientation": "portrait", + "idiom": "iphone", + "extent": "full-screen", + "filename": "Default320x480.png", + "scale": "1x", + }, + { + "orientation": "portrait", + "idiom": "iphone", + "extent": "full-screen", + "filename": "Default640x960.png", + "scale": "2x", + }, + { + "orientation": "portrait", + "idiom": "iphone", + "extent": "full-screen", + "filename": "Default640x1136.png", + "subtype": "retina4", + "scale": "2x", + }, + { + "orientation": "portrait", + "idiom": "ipad", + "extent": "full-screen", + "filename": "Default768x1024.png", + "scale": "1x", + }, + { + "orientation": "landscape", + "idiom": "ipad", + "extent": "full-screen", + "filename": "Default1024x768.png", + "scale": "1x", + }, + { + "orientation": "portrait", + "idiom": "ipad", + "extent": "full-screen", + "filename": "Default1536x2048.png", + "scale": "2x", + }, + { + "orientation": "landscape", + "idiom": "ipad", + "extent": "full-screen", + "filename": "Default2048x1536.png", + "scale": "2x", + }, + ], + "info": { + "version": 1, + "author": "xcode" }, - { - "orientation": "landscape", - "idiom": "ipad", - "extent": "full-screen", - "minimum-system-version": "7.0", - "filename": "Default2048x1536.png", - "scale": "2x" - }, - { - "orientation": "portrait", - "idiom": "iphone", - "extent": "full-screen", - "filename": "Default320x480.png", - "scale": "1x" - }, - { - "orientation": "portrait", - "idiom": "iphone", - "extent": "full-screen", - "filename": "Default640x960.png", - "scale": "2x" - }, - { - "orientation": "portrait", - "idiom": "iphone", - "extent": "full-screen", - "filename": "Default640x1136.png", - "subtype": "retina4", - "scale": "2x" - }, - { - "orientation": "portrait", - "idiom": "ipad", - "extent": "full-screen", - "filename": "Default768x1024.png", - "scale": "1x" - }, - { - "orientation": "landscape", - "idiom": "ipad", - "extent": "full-screen", - "filename": "Default1024x768.png", - "scale": "1x" - }, - { - "orientation": "portrait", - "idiom": "ipad", - "extent": "full-screen", - "filename": "Default1536x2048.png", - "scale": "2x" - }, - { - "orientation": "landscape", - "idiom": "ipad", - "extent": "full-screen", - "filename": "Default2048x1536.png", - "scale": "2x" - }, - ], - "info": { - "version": 1, - "author": "xcode" - } } def icon(image_xcassets, image_fn): - """Generate all the possible Icon from a single image_fn - """ + """Generate all the possible Icon from a single image_fn""" appicon_dir = join(image_xcassets, "AppIcon.appiconset") if not exists(appicon_dir): makedirs(appicon_dir) - with open(join(appicon_dir, "Contents.json"), "w") as fd: - json.dump(appicon_json, fd) - - options = ( - # iPhone - # Spotlight - iOS 5,6 - # Settings - iOS 5-8 - # 29pt - 1x,2x,3x - ("87", None, "Icon87.png"), - ("58", None, "Icon58.png"), - ("29", "Icon58.png", "Icon29.png"), - - # iPhone notification - # 20pt - 2x,3x - # ("40", None, "Icon40.png"), - ("60", None, "Icon60.png"), - - # iPhone - # Spotlight - iOS 7-8 - # 40pt 2x,3x - ("120", None, "Icon120.png"), - ("80", None, "Icon80.png"), - - # iPhone - # App - iOS 5,6 - # 57pt 1x,2x - ("114", None, "Icon114.png"), - ("57", "Icon114.png", "Icon57.png"), - - # iPhone - # App - iOS 7,8 - # 60pt 2x,3x - ("180", None, "Icon180.png"), - # ("120", None, "Icon120.png # duplicate"), - - # iPad - # Notifications - # 20pt 1x,2x - ("20", "Icon80.png", "Icon20.png"), - ("40", "Icon80.png", "Icon40.png"), - - # iPad - # Settings iOS 5-8 - # ("58", None, "Icon58.png # duplicate"), - # ("29", "Icon58.png", "Icon29.png # duplicate"), - - # iPad - # Spotlight iOS 7,8 - # 40pt 1x,2x - # ("80", None, "Icon80.png # duplicate"), - ("40", "Icon80.png", "Icon40.png"), - - # iPad - # Spotlight iOS 5,6 - # 50pt 1x,2x - ("100", None, "Icon100.png"), - ("50", "Icon100.png", "Icon50.png"), - - # iPad - # App iOS 5,6 - # 72pt 1x,2x - ("144", None, "Icon144.png"), - ("72", "Icon144.png", "Icon72.png"), - - # iPad - # App iOS 7,8 - # 76pt 1x,2x - ("152", None, "Icon152.png"), - ("76", "Icon152.png", "Icon76.png"), - - # iPad - # App iOS 9 - # 83.5pt 2x - ("167", None, "Icon167.png"), - - - # CarPlay - # App iOS 8 - # 120pt 1x - # ("120", None, "Icon120.png # duplicate"), - - - # Apple Watch - # Notification Center - # 38mm, 42mm - ("48", None, "Icon48.png"), - ("55", None, "Icon55.png"), - - # Apple Watch - # Companion Settings - # 29pt 2x,3x - # ("58", None, "Icon58.png # duplicate"), - # ("87", None, "Icon87.png # duplicate"), - - # Apple Watch - # Home Screen (All) - # Long Look (38mm) - # ("80", None, "Icon80.png # duplicate"), - - # Apple Watch - # Long Look (42mm) - ("88", None, "Icon88.png"), - - # Apple Watch - # Short Look - # 38mm, 42mm, 44mm - ("172", None, "Icon172.png"), - ("196", None, "Icon196.png"), - ("216", None, "Icon216.png"), - - - # OS X - # 512pt 1x,2x - ("1024", None, "Icon1024.png"), - ("512", "Icon1024.png", "Icon512.png"), - - # OS X - # 256pt 1x,2x - # ("512", "Icon1024.png", "Icon512.png # duplicate"), - ("256", "Icon512.png", "Icon256.png"), - - # OS X - # 128pt 1x,2x - # ("256", "Icon512.png", "Icon256.png # duplicate"), - ("128", "Icon256.png", "Icon128.png"), - - # OS X - # 32pt 1x,2x - ("64", "Icon128.png", "Icon64.png"), - ("32", "Icon64.png", "Icon32.png"), - - # OS X - # 16pt 1x,2x - # ("32", "Icon64.png", "Icon32.png # duplicate"), - ("16", "Icon32.png", "Icon16.png")) - - _generate("AppIcon.appiconset", image_xcassets, image_fn, options, icon=True) + with open(join(appicon_dir, "Contents.json"), "w") as fd: + json.dump(appicon_json, fd) + + options = ( + # iPhone + # Spotlight - iOS 5,6 + # Settings - iOS 5-8 + # 29pt - 1x,2x,3x + ("87 87", None, "Icon87.png"), + ("58 58", None, "Icon58.png"), + ("29 29", "Icon58.png", "Icon29.png"), + # iPhone notification + # 20pt - 2x,3x + # ("40", None, "Icon40.png"), + ("60 60", None, "Icon60.png"), + # iPhone + # Spotlight - iOS 7-8 + # 40pt 2x,3x + ("120 120", None, "Icon120.png"), + ("80 80", None, "Icon80.png"), + # iPhone + # App - iOS 5,6 + # 57pt 1x,2x + ("114 114", None, "Icon114.png"), + ("57 57", "Icon114.png", "Icon57.png"), + # iPhone + # App - iOS 7,8 + # 60pt 2x,3x + ("180 180", None, "Icon180.png"), + # ("120", None, "Icon120.png # duplicate"), + # iPad + # Notifications + # 20pt 1x,2x + ("20 20", "Icon80.png", "Icon20.png"), + ("40 40", "Icon80.png", "Icon40.png"), + # iPad + # Settings iOS 5-8 + # ("58", None, "Icon58.png # duplicate"), + # ("29", "Icon58.png", "Icon29.png # duplicate"), + # iPad + # Spotlight iOS 7,8 + # 40pt 1x,2x + # ("80", None, "Icon80.png # duplicate"), + # ("40", "Icon80.png", "Icon40.png" # duplicate), + # iPad + # Spotlight iOS 5,6 + # 50pt 1x,2x + ("100 100", None, "Icon100.png"), + ("50 50", "Icon100.png", "Icon50.png"), + # iPad + # App iOS 5,6 + # 72pt 1x,2x + ("144 144", None, "Icon144.png"), + ("72 72", "Icon144.png", "Icon72.png"), + # iPad + # App iOS 7,8 + # 76pt 1x,2x + ("152 152", None, "Icon152.png"), + ("76 76", "Icon152.png", "Icon76.png"), + # iPad + # App iOS 9 + # 83.5pt 2x + ("167 167", None, "Icon167.png"), + # CarPlay + # App iOS 8 + # 120pt 1x + # ("120", None, "Icon120.png # duplicate"), + # Apple Watch + # Notification Center + # 38mm, 42mm + ("48 48", None, "Icon48.png"), + ("55 55", None, "Icon55.png"), + # Apple Watch + # Companion Settings + # 29pt 2x,3x + # ("58", None, "Icon58.png # duplicate"), + # ("87", None, "Icon87.png # duplicate"), + # Apple Watch + # Home Screen (All) + # Long Look (38mm) + # ("80", None, "Icon80.png # duplicate"), + # Apple Watch + # Long Look (42mm) + ("88 88", None, "Icon88.png"), + # Apple Watch + # Short Look + # 38mm, 42mm, 44mm + ("172 172", None, "Icon172.png"), + ("196 196", None, "Icon196.png"), + ("216 216", None, "Icon216.png"), + # OS X + # 512pt 1x,2x + ("1024 1024", None, "Icon1024.png"), + ("512 512", "Icon1024.png", "Icon512.png"), + # OS X + # 256pt 1x,2x + # ("512", "Icon1024.png", "Icon512.png # duplicate"), + ("256 256", "Icon512.png", "Icon256.png"), + # OS X + # 128pt 1x,2x + # ("256", "Icon512.png", "Icon256.png # duplicate"), + ("128 128", "Icon256.png", "Icon128.png"), + # OS X + # 32pt 1x,2x + ("64 64", "Icon128.png", "Icon64.png"), + ("32 32", "Icon64.png", "Icon32.png"), + # OS X + # 16pt 1x,2x + # ("32", "Icon64.png", "Icon32.png # duplicate"), + ("16 16", "Icon32.png", "Icon16.png"), + ) + _generate("AppIcon.appiconset", image_xcassets, image_fn, options, icon=True) def launchimage(image_xcassets, image_fn): - """Generate all the possible Launch Images from a single image_fn - """ + """Generate all the possible Launch Images from a single image_fn""" launchimage_dir = join(image_xcassets, "LaunchImage.launchimage") if not exists(launchimage_dir): makedirs(launchimage_dir) - with open(join(launchimage_dir, "Contents.json"), "w") as fd: - json.dump(launchimage_json, fd) - - options = ( - # size, input, output - # iPhone 3.5" @2x - ("640 960", None, "Default640x960.png"), - # iPhone 3.5" @1x - ("320 480", None, "Default320x480.png"), - # iPhone 4.0" @2x - ("640 1136", None, "Default640x1136.png"), - # iPhone 5.5" @3x - landscape - ("2208 1242", None, "Default2208x1242.png"), - # iPhone 5.5" @3x - portrait - ("1242 2208", None, "Default1242x2208.png"), - # iPhone 4.7" @2x - ("750 1334", None, "Default750x1334.png"), - # iPad @2x - landscape - ("2048 1536", None, "Default2048x1536.png"), - # iPad @2x - portrait - ("1536 2048", None, "Default1536x2048.png"), - # iPad @1x - landscape - ("1024 768", None, "Default1024x768.png"), - # iPad @1x - portrait - ("768 1024", None, "Default768x1024.png"), - ) - - _generate("LaunchImage.launchimage", image_xcassets, image_fn, options) + with open(join(launchimage_dir, "Contents.json"), "w") as fd: + json.dump(launchimage_json, fd) + + options = ( + # size, input, output + # iPhone 3.5" @2x + ("640 960", None, "Default640x960.png"), + # iPhone 3.5" @1x + ("320 480", None, "Default320x480.png"), + # iPhone 4.0" @2x + ("640 1136", None, "Default640x1136.png"), + # iPhone 5.5" @3x - landscape + ("2208 1242", None, "Default2208x1242.png"), + # iPhone 5.5" @3x - portrait + ("1242 2208", None, "Default1242x2208.png"), + # iPhone 4.7" @2x + ("750 1334", None, "Default750x1334.png"), + # iPad @2x - landscape + ("2048 1536", None, "Default2048x1536.png"), + # iPad @2x - portrait + ("1536 2048", None, "Default1536x2048.png"), + # iPad @1x - landscape + ("1024 768", None, "Default1024x768.png"), + # iPad @1x - portrait + ("768 1024", None, "Default768x1024.png"), + ) + + _generate("LaunchImage.launchimage", image_xcassets, image_fn, options) def _buildimage(in_fn, out_fn, size, padcolor=None): + im = Image.open(in_fn) - # read the first left/bottom pixel + # Read the first left/bottom pixel bgcolor = im.getpixel((0, 0)) - # ensure the image fit in the destination size - if im.size[0] > size[0] or im.size[1] > size[1]: - f = max(im.size[0] / size[0], im.size[1] / size[1]) - newsize = int(im.size[0] / f), int(im.size[1] / f) - im = im.resize(newsize) + # Calculate the scaling factor to fit the image within the destination size + scaling_factor = min(size[0] / im.size[0], size[1] / im.size[1]) + + # Compute the new size while maintaining the aspect ratio + newsize = (int(im.size[0] * scaling_factor), int(im.size[1] * scaling_factor)) - # create final image + # Resize the image + im = im.resize(newsize, Image.LANCZOS) + + # Create the final image with the background color outim = Image.new("RGB", size, bgcolor[:3]) - x = (size[0] - im.size[0]) // 2 - y = (size[1] - im.size[1]) // 2 + + # Compute the top-left coordinates to paste the resized image + x = (size[0] - newsize[0]) // 2 + y = (size[1] - newsize[1]) // 2 + + # Paste the resized image onto the background outim.paste(im, (x, y)) - # save the image + # Save the final image outim.save(out_fn) @@ -704,12 +684,8 @@ def _generate(d, image_xcassets, image_fn, options, icon=False): if icon: args += [filename, "-Z", c] - args += [ - "--out", - join(image_xcassets, d, out_fn) - ] + args += ["--out", join(image_xcassets, d, out_fn)] print("sips", " ".join(args)) sh.sips(*args) - else: - size = [int(x) for x in c.split()] - _buildimage(filename, join(image_xcassets, d, out_fn), size) + size = [int(x) for x in c.split()] + _buildimage(filename, join(image_xcassets, d, out_fn), size)