• Stars
    star
    448
  • Rank 94,108 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 7 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A simple Swift wrapper for libgd

SwiftGD

This is a simple Swift wrapper for libgd, allowing for basic graphic rendering on server-side Swift where Core Graphics is not available. Although this package was originally written to accompany my book Server-Side Swift, it's likely to be of general use to anyone wishing to perform image manipulation on their server.

SwiftGD wraps GD inside classes to make it easier to use, and provides the following functionality:

  • Loading PNGs and JPEGs from disk.
  • Writing images back to disk as PNG or JPEG.
  • Creating new images at a specific width and height.
  • Resizing to a specific width or height.
  • Cropping at a location and size.
  • Flood filling a color from a coordinate.
  • Drawing lines
  • Reading and writing individual pixels.
  • Stroking and filling ellipses and rectangles.
  • Flipping images horizontally and vertically.
  • Basic effects: pixelate, blur, colorize, and desaturate.

SwiftGD manages GD resources for you, so the underlying memory is released when your images are destroyed.

Installation

Install the GD library on your computer. If you're using macOS, install Homebrew then run the command brew install gd. If you're using Linux, run apt-get libgd-dev as root.

Modify your Package.swift file to include the following dependency:

.package(url: "https://github.com/twostraws/SwiftGD.git", from: "2.0.0")

You should also include โ€œSwiftGDโ€ in your list of target dependencies.

SwiftGD itself has a single Swift dependency, which is Cgd.

Classes

SwiftGD provides four classes for basic image operations:

  • Image is responsible for loading, saving, and manipulating image data.
  • Point stores x and y coordinates as integers.
  • Size stores width and height integers.
  • Rectangle combines Point and Size into one value.
  • Color provides red, green, blue, and alpha components stored in a Double from 0 to 1, as well as some built-in colors to get you started.

These are implemented as classes rather than structs because only classes have deinitializers. These are required so that GD's memory can be cleaned up when an image is destroyed.

Reading and writing images

You can load an image from disk like this:

let location = URL(fileURLWithPath: "/path/to/image.png")
let image = Image(url: location)

That will return an optional Image object, which will be nil if the load failed for some reason. SwiftGD uses the file extension to load the correct file format, so it's important you name your files with "jpg", "jpeg", or "png".

You can also create new images from scratch by providing a width and height, like this:

let image = Image(width: 500, height: 500)

Again, that will return an optional Image if the memory was allocated correctly.

You can even create an image from Data instances:

let data: Data = ... // e.g. from networking request
let image = try Image(data: data, as: .png)

This will throw an Error if data is not actual an image data representation or does not match given raster format (.png in this case). If you omit the raster format, all supported raster formats will be evaluated and an Image will be returned if any matches (caution, this may take significantly longer).

When you want to save an image back to disk, use the write(to:) method on Image, like this:

let url = URL(fileURLWithPath: "/path/to/save.jpg")
image.write(to: url)

Again, the format is determined by your choice of file extension. write(to:) will return false and refuse to continue if the file exists already; it will return true if the file was saved successfully.

You can also export images as Data representations with certain image raster format, like so:

let image = Image(width: 500, height: 500)
image?.fill(from: .zero, color: .red)
let data = try image?.export(as: .png)

This will return the data representation of a red PNG image with 500x500px in size.

Images are also created when performing a resize or crop operation, which means your original image is untouched. You have three options for resizing:

  • resizedTo(width:height:) lets you stretch an image to any dimensions.
  • resizedTo(width:) resizes an image to a specific width, and calculates the correct height to maintain the original aspect ratio.
  • resizedTo(height:) resizes an image to a specific height, and calculates the correct width to maintain the original aspect ratio.

All three have an optional extra parameter, applySmoothing. When set to true (the default) the resize is performed using bilinear filter. When false, the resize is performed using nearest neighbor, and the result is likely to look jagged.

To crop an image, call its cropped(to:) method, passing in the Rectangle that specifies the crop origin and size.

Drawing shapes and colors

There are eight methods you can use to draw into your images:

  • fill(from:color:) performs a flood fill from a Point on your image using the Color you specify.
  • drawLine(from:to:color:) draws a line between the to and from parameters (both instances of Point) in the Color you specify.
  • set(pixel:to:) sets a pixel at a specific Point to the Color you specify.
  • get(pixel:) returns the Color value of a pixel at a specific Point.
  • strokeEllipse(center:size:color:) draws an empty ellipse at the center Point, with the Size and Color you specify.
  • func fillEllipse(center:size:color:) fills an ellipse at the center Point, with the Size and Color you specify.
  • strokeRectangle(topLeft:bottomRight:color:) draws an empty rectangle from topLeft to bottomRight (both instances of Point) using the Color you specify.
  • fillRectangle(topLeft:bottomRight:color:) fills a rectangle from topLeft to bottomRight (both instances of Point) using the Color you specify.

Manipulating images

There are several methods that apply filters to image objects:

  • pixelate(blockSize:) simplifies your image to large pixels, with the pixel size dictated by the integer you provide as blockSize.
  • blur(radius:) applies a Gaussian blur effect. Using a larger value for radius causes stronger blurs.
  • colorize(using:) applies a tint using a Color you specify.
  • desaturate() renders your image grayscale.
  • flip(_:) flips your image horizontally, vertically, or both. Pass .horizontal, ``vertical, or .both` as its parameter.

Example code

This first example creates a new 500x500 image, fills it red, draw a blue ellipse in the center, draws a green rectangle on top, runs the desaturate and colorize filters, and saves the resulting image to "output-1.png":

import Foundation
import SwiftGD

// figure out where to save our file
let currentDirectory = URL(fileURLWithPath: FileManager().currentDirectoryPath)
let destination = currentDirectory.appendingPathComponent("output-1.png")

// attempt to create a new 500x500 image
if let image = Image(width: 500, height: 500) {
    // flood from from X:250 Y:250 using red
    image.fill(from: Point(x: 250, y: 250), color: Color.red)

    // draw a filled blue ellipse in the center
    image.fillEllipse(center: Point(x: 250, y: 250), size: Size(width: 150, height: 150), color: Color.blue)
        
    // draw a filled green rectangle also in the center
    image.fillRectangle(topLeft: Point(x: 200, y: 200), bottomRight: Point(x: 300, y: 300), color: Color.green)

    // remove all the colors from the image
    image.desaturate()
        
    // now apply a dark red tint
    image.colorize(using: Color(red: 0.3, green: 0, blue: 0, alpha: 1))
        
    // save the final image to disk
    image.write(to: destination)
}

This second examples draws concentric rectangles in alternating blue and white colors, then applies a Gaussian blur to the result:

import Foundation
import SwiftGD

let currentDirectory = URL(fileURLWithPath: FileManager().currentDirectoryPath)
let destination = currentDirectory.appendingPathComponent("output-2.png")

if let image = Image(width: 500, height: 500) {
    var counter = 0
        
    for i in stride(from: 0, to: 250, by: 10) {
        let drawColor: Color
        
        if counter % 2 == 0 {
            drawColor = .blue
        } else {
            drawColor = .white
        }
        
        image.fillRectangle(topLeft: Point(x: i, y: i), bottomRight: Point(x: 500 - i, y: 500 - i), color: drawColor)
        counter += 1
    }

    image.blur(radius: 10)
    image.write(to: destination)
}

This third example creates a black, red, green, and yellow gradient by setting individual pixels in a nested loop:

import Foundation
import SwiftGD

let currentDirectory = URL(fileURLWithPath: FileManager().currentDirectoryPath)
let destination = currentDirectory.appendingPathComponent("output-3.png")

let size = 500

if let image = Image(width: size, height: size) {
    for x in 0 ... size {
        for y in 0 ... size {
            image.set(pixel: Point(x: x, y: y), to: Color(red: Double(x) / Double(size), green: Double(y) / Double(size), blue: 0, alpha: 1))
        }
    }
        
    image.write(to: destination)
}

License

This package is released under the MIT License, which is copied below.

Copyright (c) 2017 Paul Hudson

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

More Repositories

1

ControlRoom

A macOS app to control the Xcode Simulator.
Swift
5,610
star
2

HackingWithSwift

The project source code for hackingwithswift.com
Swift
5,607
star
3

Unwrap

Learn Swift interactively on your iPhone.
Swift
2,265
star
4

Inferno

Metal shaders for SwiftUI.
Metal
2,238
star
5

wwdc

WWDC Community: Learning and sharing together
1,858
star
6

Sitrep

A source code analyzer for Swift projects.
Swift
1,294
star
7

CodeScanner

A SwiftUI view that is able to scan barcodes, QR codes, and more, and send back what was found.
Swift
930
star
8

Vortex

High-performance particle effects for SwiftUI.
Swift
910
star
9

whats-new-in-swift-5-0

An Xcode playground that demonstrates the new features introduced in Swift 5.0.
Swift
731
star
10

Ignite

A static site generator for Swift developers.
Swift
707
star
11

Sourceful

A syntax highlighting source editor for iOS and macOS using UITextView and NSTextView.
Swift
683
star
12

ShaderKit

A library of fragment shaders you can use in any SpriteKit project.
GLSL
655
star
13

SwiftOnSundays

Completed projects for the Swift on Sundays livestream series
Swift
635
star
14

simple-swiftui

A collection of small SwiftUI sample projects.
Swift
634
star
15

Brisk

A proof of concept scripting library for Swift
Swift
503
star
16

VisualEffects

A semi-official SwiftUI wrapper for UIVisualEffectView
Swift
354
star
17

whats-new-in-swift-5-5

Swift
312
star
18

Subsonic

A small library that makes it easier to play audio with SwiftUI.
Swift
308
star
19

swiftui-changelog

A repository to track changes in the SwiftUI generated interface.
Swift
258
star
20

macOS

The project source code for Hacking with macOS.
Swift
239
star
21

whats-new-in-swift-4-1

An Xcode playground that demonstrates the new features introduced in Swift 4.1.
Swift
221
star
22

TapStore

Code for a YouTube video on UICollectionView.
Swift
162
star
23

NeumorphismSwiftUI

Code to accompany my article on this topic.
Swift
139
star
24

whats-new-in-swift-5-3

An Xcode playground that demonstrates the new features introduced in Swift 5.3.
Swift
127
star
25

iDine

Source code for my SwiftUI introduction tutorial.
Swift
126
star
26

whats-new-in-swift-5-1

An Xcode playground that demonstrates the new features introduced in Swift 5.1.
Swift
121
star
27

whats-new-in-swift-5-8

Swift
120
star
28

whats-new-in-swift-4-2

An Xcode playground that demonstrates the new features introduced in Swift 4.2.
Swift
116
star
29

Sharpshooter

A tiny Xcode extension for people who debug with print().
Swift
115
star
30

whats-new-in-swift-5-7

Swift
115
star
31

100

A list of solutions for the 100 Days of Swift challenge
110
star
32

HWSTranslation

A community project to translate free Swift tutorials
101
star
33

whats-new-in-swift-5-4

Swift
100
star
34

watchOS

The project source code for Hacking with watchOS.
Swift
98
star
35

vapor-clean

A Vapor 3 template with no additional cruft.
Swift
97
star
36

whats-new-in-swift-5-2

An Xcode playground that demonstrates the new features introduced in Swift 5.2.
Swift
95
star
37

AppleTime

A tiny program to use 9:41 in your iOS simulators.
Swift
92
star
38

whats-new-in-swift-5-6

Swift
91
star
39

HackingWithReact

The project source code for hackingwithreact.com
JavaScript
72
star
40

iTour

Source code for my SwiftData introduction tutorial.
Swift
67
star
41

Placeholder

Place temporary images in your iOS app showing the size of the available space.
Swift
56
star
42

Trekr

Companion code for a YouTube livestream.
Swift
54
star
43

FaceFacts

Source code for my SwiftUI + SwiftData tutorial.
Swift
51
star
44

Markdown

A small and fast Markdown parser library for Swift.
Swift
44
star
45

SwiftOverCoffee

Links to solutions for Swift over Coffee challenges
39
star
46

SwiftSlug

A simple package to convert strings to URL slugs.
Swift
38
star
47

Playmaker

Create Xcode playgrounds from Markdown.
Swift
35
star
48

IgniteStarter

A starter template for the Ignite static site generator.
Swift
33
star
49

tvOS

The project source code for Hacking with tvOS.
Swift
30
star
50

IgniteSamples

Sample code for the Ignite static site generator.
Swift
23
star
51

kitura-vs-vapor

A side-by-side comparison of two popular server-side Swift frameworks.
Swift
22
star
52

Cgd

A small Swift package exposing libgd to Swift.
Swift
20
star
53

super-powered-string-interpolation

Swift
18
star
54

SamplePackage

A test package for Swift Package Manager.
Swift
18
star
55

betterbeeb

Better Beeb
Swift
16
star
56

HowToInstrument

A deliberately broken app to help demonstrate Instruments.
Swift
14
star
57

BioBlitz

Code created during my birthday livestream 2022.
Swift
14
star
58

Paraphrase

A trivial app for storing and viewing famous quotes
Swift
12
star
59

DadJokes

The code from my try! Swift NYC 2019 talk.
Swift
10
star
60

homebrew-brew

Homebrew formulae.
Ruby
8
star
61

Paraphrase-Improved

Swift
4
star
62

switcharoo

Switcharoo
Python
4
star
63

easyoc

EasyOC
Objective-C
4
star