A simple Swift wrapper for libgd

Related tags

Image SwiftGD
Overview

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.

Comments
  • Expose stringFT

    Expose stringFT

    This PR:

    • exposes gdImageStringFT to render text on images via SwiftGD
    • fixes a couple of typos that I've spotted in the codebase
    • removes a few unused imports

    Thank you for this great library!

    opened by zntfdr 6
  • Build fails on Ubuntu

    Build fails on Ubuntu

    /Packages/SwiftGD-1.1.0/Sources/SwiftGD.swift:206:19: error: use of unresolved identifier 'gdImageCopyGaussianBlurred' if let result = gdImageCopyGaussianBlurred(internalImage, Int32(radius), -1) { ^~~~~~~~~~~~~~~~~~~~~~~~~~ Cgdlinux.gdImageGaussianBlur:1:13: note: did you mean 'gdImageGaussianBlur'? public func gdImageGaussianBlur(_ im: gdImagePtr!) -> Int32 ^

    Any idea?

    opened by maximedegreve 6
  • FIX: Image.renderText() in Xcode 12 beta 5

    FIX: Image.renderText() in Xcode 12 beta 5

    Adding Image.renderText() is great work. But the renderText method should pass pointers instead of values to gdImageStringFT. In fact, this causes compiler Errors in Xcode 12 Beta 5 (Swift 5.5).

    This PR fixes these errors.

    Additional info: (swiftlang-1300.0.20.104 clang-1300.0.21.1)

    opened by mcritz 4
  • Improved macos/linux compatibility.

    Improved macos/linux compatibility.

    opened by fappelman 2
  • Loading external images

    Loading external images

    let url = "https://i.ytimg.com/vi/t34kH5eOVhM/maxresdefault.jpg"
            guard let image = Image(url: URL(fileURLWithPath: url)) else {
                print(url + " could not be loaded")
                return
            }
            print("OK")
    

    Will result in "could not be loaded"

    Internal images work just fine, is there a way to load external? Or load them via bytes?

    opened by Casperhr 2
  • 'width' is inaccessible due to 'internal' protection level

    'width' is inaccessible due to 'internal' protection level

    Hi!

    When I use let size = Size(width: 500, height: 348) the properties width and height are inaccessible. Please make them public to:

    public struct Size {
        public var width: Int
        public var height: Int
    
    	public init(width: Int, height: Int) {
    		self.width = width
    		self.height = height
    	}
    }
    
    opened by JohnKnust 2
  • Added quality setting to WebPFormatter

    Added quality setting to WebPFormatter

    Currently, the WEBPFormatter class uses gdImageWebpPtr which does not allow quality to be set. This defaults quality to -1, which produces rough-looking results with certain input images.

    By changing gdImageWebpPtr to gdImageWebpPtrEx and supplying a quality parameter, similar to JPGFormatter, an optional quality value can be passed which allows higher output image quality.

    See documentation for https://libgd.github.io/manuals/2.2.5/files/gd_webp-c.html#gdImageWebpEx indicating that a quality setting can be supplied that overrides the default -1 value.

    opened by jameseunson 1
  • SwiftGd is not doing anything on mac

    SwiftGd is not doing anything on mac

    I have the image uploaded to disk

                let url = URL(fileURLWithPath: path)
                let image = Image(url: url)
                if let im = image {
                    if let mg = im.resizedTo(width: 250, height: 250){
                        mg.write(to: url)
                    }
                }
    

    I tried to resize the image and save it with no hope?

    opened by HosMercury 1
  • renderText improvements

    renderText improvements

    As discussed in #29, this PR:

    • uses positive angles to rotate the rendered text clockwise
    • changes the order of the bounding points returned by the method

    Both changes aim to make the API more familiar to Apple platform developers

    opened by zntfdr 1
  • SwiftGD working great on Mac but not working on Ubuntu.

    SwiftGD working great on Mac but not working on Ubuntu.

    Hi,

    I have a problem with SwiftGD working on Ubuntu 18.04.

    I've installed it like "apt-get install libgd-dev" but on vapor if I try to upload an image I get some errors:

    in console: [ ERROR ] Error Domain=NSCocoaErrorDomain Code=4 "The file doesn’t exist." (ErrorMiddleware.swift:26) [ DEBUG ] Conform NSError to Debuggable for better debug info. (ErrorMiddleware.swift:26)

    in Safari: {"reason":"Something went wrong.","error":true}

    I'd like to notice everything is working great on my Mac. Those errors are only on Ubuntu and image is not being uploaded. I'm using exactly the same Swift code on both platforms:

    func addUserProfileImagePostHandler(_ req: Request) throws -> Future { // create an array of the file typer we're willing to accept let acceptableTypes = [MediaType.png, MediaType.jpeg] // look for image from post data return try flatMap(to: Response.self, req.parameters.next(User.self), req.content.decode(UploadFileData.self)) { user, data in // getting directories for properties images if !self.fm.fileExists(atPath: "(self.rootDirectory)/Public/real_estate_website/uploads/user_images/(user.id!)") { try self.fm.createDirectory(atPath: "(self.rootDirectory)/Public/real_estate_website/uploads/user_images/(user.id!)", withIntermediateDirectories: false) } let uploadDirectory = URL(fileURLWithPath: "(self.rootDirectory)/Public/real_estate_website/uploads") let propertyImagesDirectory = uploadDirectory.appendingPathComponent("user_images/(user.id!)") // ensure this image is one of the valid types if let mimeType = data.upload.contentType { if acceptableTypes.contains(mimeType) { let filename = "profileImage.jpg" let newURL = propertyImagesDirectory.appendingPathComponent(filename) let image = try Image(data: data.upload.data) // resizing the image and saving it if let resized = image.resizedTo(width: 362) { resized.write(to: newURL, allowOverwrite: true) user.image = filename } } } let redirect = req.redirect(to: "/real_estate/admin/users/(user.id!)/profile") if user.image == "profileImage.jpg" { return user.save(on: req).transform(to: redirect) } else { return req.future().transform(to: redirect) } } }

    Please help.

    opened by hacik81 1
  • Free memory of image pointer after export

    Free memory of image pointer after export

    When exporting we were copying the image bytes over to a Data instance but never actually freed the original data.

    This PR does not copy the bytes into a Data instance but instead creates a Data instance from the given exported bytes and adds a custom Data.Deallocator that calls gdFree to free the memory after usage.

    Fixes #17.

    Alternatively, we could also copy the bytes as before and just add a call to gdFree in the end:

    defer {
      gdFree(bytesPtr)
    }
    

    But this PR avoids the additional copy.

    opened by t089 1
  • Add cloned, rotated, flipped

    Add cloned, rotated, flipped

    • Add cloned() -> Image? function to clone an image
    • Add rotated(_ angle: Angle) -> Image? function to rotate an image by angle degrees
    • Add flipped(_ mode: FlipMode) -> Image? function to flip an image
    opened by ratranqu 0
  • Apple Silicon M1 compatibility

    Apple Silicon M1 compatibility

    Hello! I'm having trouble compiling this project on a mac with M1 processor. I successfully installed gd with brew install gd.

    When compiling I get the error 'gd.h' file not found; it looks like SPM is not picking up headers correctly

    $ pkg-config --cflags gdlib                                   
    -I/opt/homebrew/Cellar/gd/2.3.2/include
    
    $ ls /usr/local/include | grep gd 
    gd.h
    gd_color_map.h
    gd_errors.h
    gd_io.h
    gdcache.h
    gdfontg.h
    gdfontl.h
    gdfontmb.h
    gdfonts.h
    gdfontt.h
    gdfx.h
    gdpp.h
    

    Is there any workaround available or am I doing something wrong?

    opened by n3d1117 2
  • Memory Leaks (Instruments)

    Memory Leaks (Instruments)

    So i was observing SwiftGD's behaviour since I'm using it for scaling images. Instruments detects memory leaks, at least when using it in Vapor. I've confirmed that they only occur when using SwiftGD. Instruments shows that the Responsible Frame is "gdReallocDynamic".

    opened by Akazm 0
  • gdImagePtr not found in 2.5.0, but found in 2.4.0

    gdImagePtr not found in 2.5.0, but found in 2.4.0

    I have a strange regression bug. Version 2.5.0 gives tons of `use of undeclared type 'gdImagePtr' errors, but v2.4.0 does not.

    steps taken:

    • SwiftGD 2.5.0 could not find gdImagePtr
    • brew reinstall gd
    • remove .build
    • clean project etc.
    • Double-check double setting of /usr/local/lib
    • downgraded to SwiftGD 2.4.0
    • In the end removing the xcodeproj was the only thing which worked

    However, when I upgraded to SwiftGD 2.5.0 again, removing the xcodeproj and deep cleaning the build folder, and adding the library search paths did not work anymore.

    • Saved both build logs
    • downgraded to SwiftGD 2.4.0

    The only discrepancy I see, is that the Format.swift file is in different positions: 2.4.0: checkouts/SwiftGD/Sources/Format.swift 2.5.0: checkouts/SwiftGD/Sources/SwiftGD/Format.swift

    Also, it does link with the gd library, as you can see from the compile statement.

    Build project5-Package_SwiftGD2.5.0.txt.zip Build project5-Package_SwiftGD2.4.0.txt.zip

    opened by axello 7
  • No such module 'SwiftGD'

    No such module 'SwiftGD'

    I've installed gd using "brew install gd". I'm running a Kitura REST web service that does image manipulation and returns to the client! Mac OS Mojave latest version!

    Can this library run in both Linux and MacOS? Also the issue. Can I draw image as eclipse or rectangle?

    Then below is the package declaration but getting no such module "SwiftGD"

    // swift-tools-version:5.0 // The swift-tools-version declares the minimum version of Swift required to build this package.

    import PackageDescription

    let package = Package( name: "Project", dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), .package(url: "https://github.com/IBM-Swift/Kitura.git", from: "2.5.0"), .package(url: "https://github.com/IBM-Swift/HeliumLogger.git", from: "1.7.1"), .package(url: "https://github.com/IBM-Swift/Kitura-CredentialsFacebook.git", from: "2.2.0"), .package(url: "https://github.com/IBM-Swift/Kitura-CredentialsGoogle.git", from: "2.2.0"), .package(url: "https://github.com/IBM-Swift/Kitura-CredentialsHTTP.git", from: "2.1.0"), // .package(url: "https://github.com/mongodb/mongo-swift-driver", .branch("master")) .package(url: "https://github.com/mongodb/mongo-swift-driver", from: "0.1.3"), .package(url: "https://github.com/twostraws/SwiftGD.git", from: "2.0.0") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "Project", dependencies: ["MongoSwift", "Kitura" , "HeliumLogger", "CredentialsFacebook", "CredentialsGoogle", "CredentialsHTTP", "SwiftGD"]), .testTarget( name: "ProjectTests", dependencies: ["Project"]), ] )

    opened by ghost 3
Releases(2.0.0)
  • 2.0.0(Sep 13, 2017)

    This introduces more intelligent image downsampling (thanks, @enricode!), but modifies the way the size property is returned for images: it now uses the Size struct, which has width and height properties instead of just returning a tuple.

    Source code(tar.gz)
    Source code(zip)
Owner
Paul Hudson
Creator of Hacking with Swift, author of books about iOS, macOS, watchOS, and tvOS, public speaker, Rubik's cube enthusiast, and herder of my kids.
Paul Hudson
XIV-on-Mac - Wine Wrapper, Setup tool and launcher for FFXIV on mac

XIV on Mac Wine Wrapper, Setup tool and alternative launcher for FFXIV on MacOS.

null 210 Dec 26, 2022
Fast and simple OCR library written in Swift

⛔️ This Project is deprecated and no longer gets maintained! Please use Apple's Vision framework instead of SwiftOCR. It is very fast, accurate and mu

Nicolas Camenisch 4.5k Dec 29, 2022
A simple, declarative, functional drawing framework, in Swift!

DePict - A simple, declarative, functional drawing framework. To produce a drawing, call the Draw function (just type Draw and let autocomplete do the

David Cairns 35 Sep 16, 2021
A simple macOS app to read code from images, written purely in Swift using Vision Framework.

CodeReader A simple macOS app to read code from images, written purely in Swift using Vision Framework. Usage Drag an image Click the convert button R

Md Ibrahim Hassan 44 Nov 20, 2022
Simple PhotoBrowser/Viewer inspired by facebook, twitter photo browsers written by swift

SKPhotoBrowser [![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-) Simple PhotoBrowser

keishi suzuki 2.4k Jan 6, 2023
A simple UIImageView extension for using initials as a profile image, written in swift

InitialsImageView An easy, helpful UIImageView extension that generates letter initials as a placeholder for user profile images, with a randomized ba

Tom Bachant 215 Dec 17, 2022
Fast and simple OCR library written in Swift

⛔️ This Project is deprecated and no longer gets maintained! Please use Apple's Vision framework instead of SwiftOCR. It is very fast, accurate and mu

Nicolas Camenisch 4.5k Dec 29, 2022
A simple mesh viewer for MacOS based on Swift and Metal and using Assimp for loading meshes

Metal Mesh Viewer A simple triangle mesh viewer for MacOS This application is a simple (triangle) mesh viewer that should be capable of rendering even

J. Andreas Bærentzen 0 Dec 13, 2021
PhotoApp - A Simple Photo App using Swift and MVC pattern

PhotoApp A Simple Photo App using Swift and MVC pattern After App launch, you wi

null 2 Aug 14, 2022
FlickrSearchPhotos - Simple search photos application which uses Flickr REST API made in Swift

FlickrSearchPhotos - Simple search photos application which uses Flickr REST API made in Swift

 Mihai Erős 1 Jun 6, 2022
FMPhotoPicker is a modern, simple and zero-dependency photo picker with an elegant and customizable image editor

FMPhotoPicker is a modern, simple and zero-dependency photo picker with an elegant and customizable image editor Quick demo Batch select/deselect Smoo

Cong Nguyen 648 Dec 27, 2022
A simple, performant, and lightweight SVG parser

Key Features Parsing performance that meets or beats other popular SVG Frameworks A simple architecture, optimized for extension, flexibility and deve

Michael Choe 1.8k Dec 29, 2022
Simple CLI utility to save off an image from every webcam hooked into a mac

macOSCameraCapture Simple CLI utility to save off an image from every webcam connected to the macOS machine. This utility is meant for research and te

Cody Thomas 12 Jan 26, 2022
Simple camera application for iOS that uploads pictures to WebDAV server or Dropbox quickly. Available on the AppStore.

Upupu Simple camera application for iOS that uploads pictures to WebDAV server or Dropbox quickly. Also available on the AppStore. Features Easy and f

Xcoo 65 Nov 15, 2022
This simple cordova plugin will download picture from an URL and save to IOS Photo Gallery.

Photo Viewer This plugin is intended to download a picture from an URL into IOS Photo library.. How to Install Cordova: cordova plugin add https://git

Alwin jose 1 Oct 23, 2021
A simple iOS photo and video browser with grid view, captions and selections.

MWPhotoBrowser A simple iOS photo and video browser with optional grid view, captions and selections. MWPhotoBrowser can display one or more images or

Michael Waterfall 8.8k Dec 27, 2022
A simple Image full screen pop up

CLImageViewPopup Description A simple UIImageView for easy fullscreen image pop up. No matter where your UIImageView may be. Image pops up from where

Vineeth Vijayan 36 Apr 29, 2021
A simple and flexible way to add source of overlapping circular pictures, currently supports horizontal overlapping or distant pictures with great layout flexibility.

THIS PROJECT IS NO LONGER MAINTAINED. STILL ONE ONLY BEST UI SOLUTION FOR UIKIT DEVELOPERS. SOON WILL COME UP WITH SWIFTUI STILL CONTRIBUTORS ARE WELC

Kiran Jasvanee 673 Dec 19, 2022
A simple auto clicker for your Mac.

MaClicker MaClicker is a simple auto clicker for your Mac. It was built with swift and requires macOS 10.12 Sierra or higher. It is possible to achiev

WorldOfBasti 3 Jun 29, 2022