#!/usr/bin/env swift // Draws the 1024x1024 Share app icon. Run from apps/ios: // swift Branding/make-icon.swift Share/Assets.xcassets/AppIcon.appiconset/AppIcon.png import AppKit import CoreGraphics import ImageIO let size = 1024 let output = CommandLine.arguments.count > 1 ? CommandLine.arguments[1] : "Share/Assets.xcassets/AppIcon.appiconset/AppIcon.png" func color(_ hex: UInt32, _ alpha: CGFloat = 1) -> NSColor { NSColor( calibratedRed: CGFloat((hex >> 16) & 0xFF) / 255, green: CGFloat((hex >> 8) & 0xFF) / 255, blue: CGFloat(hex & 0xFF) / 255, alpha: alpha ) } guard let rep = NSBitmapImageRep( bitmapDataPlanes: nil, pixelsWide: size, pixelsHigh: size, bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0 ) else { fatalError("Could not create bitmap") } NSGraphicsContext.saveGraphicsState() NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: rep) NSGraphicsContext.current?.imageInterpolation = .high NSGraphicsContext.current?.shouldAntialias = true let canvas = NSRect(x: 0, y: 0, width: size, height: size) let background = NSGradient(colors: [color(0x07111F), color(0x0B3B36)])! background.draw(in: canvas, angle: -55) // Soft teal glow in the upper-left and blue glow in the lower-right. let tealGlow = NSGradient(colors: [color(0x5EEAD4, 0.35), color(0x5EEAD4, 0)])! tealGlow.draw( in: NSBezierPath(ovalIn: NSRect(x: -260, y: 430, width: 1020, height: 1020)), relativeCenterPosition: NSPoint(x: 0, y: 0) ) let blueGlow = NSGradient(colors: [color(0x60A5FA, 0.28), color(0x60A5FA, 0)])! blueGlow.draw( in: NSBezierPath(ovalIn: NSRect(x: 300, y: -300, width: 980, height: 980)), relativeCenterPosition: NSPoint(x: 0, y: 0) ) // A barely-there inset panel gives the flat icon some depth. let panel = NSBezierPath( roundedRect: NSRect(x: 116, y: 116, width: 792, height: 792), xRadius: 180, yRadius: 180 ) color(0xFFFFFF, 0.035).setFill() panel.fill() color(0xFFFFFF, 0.07).setStroke() panel.lineWidth = 2 panel.stroke() func strokeArrow(shaftFrom: NSPoint, shaftTo: NSPoint, headLeft: NSPoint, headTip: NSPoint, headRight: NSPoint, tint: NSColor) { let shadow = NSShadow() shadow.shadowColor = color(0x000000, 0.3) shadow.shadowBlurRadius = 34 shadow.shadowOffset = NSSize(width: 0, height: -16) shadow.set() tint.setStroke() let shaft = NSBezierPath() shaft.move(to: shaftFrom) shaft.line(to: shaftTo) shaft.lineWidth = 74 shaft.lineCapStyle = .round shaft.stroke() let head = NSBezierPath() head.move(to: headLeft) head.line(to: headTip) head.line(to: headRight) head.lineWidth = 78 head.lineCapStyle = .round head.lineJoinStyle = .round head.stroke() } // Up arrow on the left. strokeArrow( shaftFrom: NSPoint(x: 350, y: 288), shaftTo: NSPoint(x: 350, y: 610), headLeft: NSPoint(x: 238, y: 490), headTip: NSPoint(x: 350, y: 640), headRight: NSPoint(x: 462, y: 490), tint: color(0xF4FFFC) ) // Down arrow on the right. strokeArrow( shaftFrom: NSPoint(x: 674, y: 736), shaftTo: NSPoint(x: 674, y: 414), headLeft: NSPoint(x: 562, y: 534), headTip: NSPoint(x: 674, y: 384), headRight: NSPoint(x: 786, y: 534), tint: color(0x5EEAD4) ) // Three small dots suggest a swarm: peers nearby, not a central server. for (point, alpha) in [ (NSPoint(x: 250, y: 790), CGFloat(0.55)), (NSPoint(x: 512, y: 840), CGFloat(0.35)), (NSPoint(x: 790, y: 260), CGFloat(0.45)), ] { let dot = NSBezierPath(ovalIn: NSRect(x: point.x - 7, y: point.y - 7, width: 14, height: 14)) color(0xFFFFFF, alpha).setFill() dot.fill() } NSGraphicsContext.restoreGraphicsState() guard let cgImage = rep.cgImage else { fatalError("Could not make a CGImage") } // App Store icons must not contain an alpha channel. Drawing the rendered // image into an opaque context and then encoding that image gives us an // RGB PNG without changing a single visible pixel. let width = cgImage.width let height = cgImage.height let colorSpace = CGColorSpaceCreateDeviceRGB() guard let context = CGContext( data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue ) else { fatalError("Could not create an opaque context") } context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height)) guard let opaque = context.makeImage() else { fatalError("Could not render the opaque icon") } let url = URL(fileURLWithPath: output) try FileManager.default.createDirectory( at: url.deletingLastPathComponent(), withIntermediateDirectories: true ) guard let destination = CGImageDestinationCreateWithURL( url as CFURL, "public.png" as CFString, 1, nil ) else { fatalError("Could not create a PNG destination") } CGImageDestinationAddImage(destination, opaque, nil) guard CGImageDestinationFinalize(destination) else { fatalError("Could not write the PNG") } print("Wrote \(url.path)")