{"id":19472972,"url":"https://github.com/eskils/variableblurimageview","last_synced_at":"2025-04-25T12:31:32.413Z","repository":{"id":211170776,"uuid":"728148907","full_name":"Eskils/VariableBlurImageView","owner":"Eskils","description":"Add variable blur to images in UIKit, AppKit and SwiftUI.","archived":false,"fork":false,"pushed_at":"2024-02-16T10:09:55.000Z","size":171841,"stargazers_count":36,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T03:02:34.413Z","etag":null,"topics":["appkit","blur","blurimage","blurimageview","cocoa","gaussian-blur","gradient","gradient-blur","metal","metal-shader","progressive-blur","swift-package","swiftui","uikit","variable-blur"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Eskils.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-12-06T10:32:35.000Z","updated_at":"2025-02-14T06:34:19.000Z","dependencies_parsed_at":"2023-12-14T12:52:53.031Z","dependency_job_id":null,"html_url":"https://github.com/Eskils/VariableBlurImageView","commit_stats":null,"previous_names":["eskils/variableblurimageview"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eskils%2FVariableBlurImageView","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eskils%2FVariableBlurImageView/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eskils%2FVariableBlurImageView/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eskils%2FVariableBlurImageView/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Eskils","download_url":"https://codeload.github.com/Eskils/VariableBlurImageView/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250817660,"owners_count":21492192,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appkit","blur","blurimage","blurimageview","cocoa","gaussian-blur","gradient","gradient-blur","metal","metal-shader","progressive-blur","swift-package","swiftui","uikit","variable-blur"],"created_at":"2024-11-10T19:16:35.918Z","updated_at":"2025-04-25T12:31:27.390Z","avatar_url":"https://github.com/Eskils.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"Documentation/VariableBlurImageViewIcon.png\" width=\"128\" alt=\"VariableBlurImageView logo\"\u003e\n\n# VariableBlurImageView\n\nAdd variable blur to images in UIKit, AppKit, and SwiftUI. Works on Apple platforms using Metal.\n\n![A hill with a horizontal variable blur from the leading edge to the middle](Documentation/GreenerOnTheOtherSide-HorizontalBlur-(0,20)-to-(50w,0).png)\n\n\u003e Left image has a horizontal variable blur from the leading edge to the middle. Right image has a vertical variable blur from the top edge to the middle.\n\nDemo apps for SwiftUI, UIKit and AppKit are available under [Documentation](/Documentation/).\n\n## Table of contents\n   * [Possible kinds](#possible-kinds)\n   * [Requirements](#requirements)\n   * [Installation](#installation)\n   * [Usage](#usage)\n      * [Working with UIKit and AppKit](#working-with-uikit-and-appkit)\n      * [Working with SwiftUI](#working-with-swiftui)\n      * [Working with CGImages](#working-with-cgimages)\n   * [Roadmap](#roadmap)\n   * [Project Organization](#project-organization)\n   * [Implementing new blur types](#implementing-new-blur-types)\n      * [Steps to implement a new blur type](#steps-to-implement-a-new-blur-type)\n      * [Supplying tests](#supplying-tests)\n      * [Generate images to use in the test](#generate-images-to-use-in-the-test)\n   * [Contributing to VariableBlurImageView](#contributing-to-variableblurimageview)\n\n## Possible kinds\n \n| Vertical | Horizontal | Between two points | Gradient | Multiple blurs |\n|----------|------------|--------------------|----------|----------------|\n![Vertical progressive blur](Documentation/VariableBlurTestImage-VerticalBlur.png) | ![Horizontal progressive blur](Documentation/VariableBlurTestImage-HorizontalBlur.png) | ![Progressive blur between two points](Documentation/VariableBlurTestImage-VariableBlur.png) | ![Progressive blur from the lightness in a gradient image](Documentation/VariableBlurTestImage-GradientBlur.png) | ![Multiple progressive blurs](Documentation/VariableBlurTestImage-MultipleBlurs.png)\n\n## Requirements\n- Swift 5.9\n- iOS 13.0\n- macOS 11.0\n- tvOS 13.0\n- macCatalyst 13.0\n\n## Installation\nTo use this package in a SwiftPM project, you need to set it up as a package dependency:\n\n```swift\n// swift-tools-version:5.9\nimport PackageDescription\n\nlet package = Package(\n  name: \"MyPackage\",\n  dependencies: [\n    .package(\n      url: \"https://github.com/Eskils/VariableBlurImageView\", \n      .upToNextMinor(from: \"1.1.2\") // or `.upToNextMajor\n    )\n  ],\n  targets: [\n    .target(\n      name: \"MyTarget\",\n      dependencies: [\n        .product(name: \"VariableBlurImageView\", package: \"VariableBlurImageView\")\n      ]\n    )\n  ]\n)\n```\n\n## Usage\n\nThis frameworks provides subclasses for UIImageView, and NSImageView, in addition to SwiftUI views and modifiers to apply variable blur to images.\n\nThere is also a class to apply variable blur to CGImages.\n\n### Working with UIKit and AppKit\n`VariableBlurImageView` is a subclass of `UIImageView` / `NSImageView` which asynchronously applies the wanted progressive blur.\n\nYou provide an image, start point, end point, and their respective blur radiuses.\n\n#### Example\n```swift\nlet imageView = VariableBlurImageView()\nimageView.contentMode = .scaleAspectFill\nlet backgroundImage = UIImage(resource: .onboardingBackground)\nimageView.verticalVariableBlur(\n    image: backgroundImage, \n    startPoint: 0, \n    endPoint: backgroundImage.size.height / 4, \n    startRadius: 15, \n    endRadius: 0\n)\n```\n\n\u003cimg src=\"Documentation/iOSAppDemo1.png\" width=\"300\" alt=\"Vertical progressive blur of an iOS app onboarding screen from the top to the middle\"\u003e\n\n#### Available methods\n\n```swift\n/// Adds a vertical variable blur to your image.\nVariableBlurImageView.verticalVariableBlur(image:startPoint:endPoint:startRadius:endRadius:)\n\n/// Adds a horizontal variable blur to your image.\nVariableBlurImageView.horizontalVariableBlur(image:startPoint:endPoint:startRadius:endRadius:)\n\n// Adds a variable blur between two points to your image.\nVariableBlurImageView.variableBlur(image:startPoint:endPoint:startRadius:endRadius:)\n\n/// Adds a variable blur following the lightness in the provided gradient image.\nVariableBlurImageView.gradientBlur(image:gradient:maxRadius:)\n\n/// Adds multiple variable blurs\nVariableBlurImageView.mutlipleBlurs(image:descriptions:)\n```\n\n### Working with SwiftUI\n\nViews and View Modifiers on `Image` are available to asynchronously apply the wanted progressive blur.\n\nYou provide an image, start point, end point, and their respective blur radiuses.\n\n\u003e **NOTE:** The ViewModifiers are only available from iOS 16.0 and macOS 13.0, and do not support image variations (e.g. dark mode images) \n\n#### Example\n```swift\nlet backgroundImage = UIImage(resource: .onboardingBackground)\n\nvar body: some View {\n    VStack {\n        VerticalVariableBlurImage(\n            image: backgroundImage, \n            startPoint: 0, \n            endPoint: backgroundImage.size.height / 4, \n            startRadius: 15, \n            endRadius: 0\n        )\n    }\n}\n```\n\n#### Available Views\n\n```swift\n/// Adds a vertical variable blur to your image.\nVerticalVariableBlurImage(image:startPoint:endPoint:startRadius:endRadius:)\n\n/// Adds a horizontal variable blur to your image.\nHorizontalVariableBlurImage(image:startPoint:endPoint:startRadius:endRadius:)\n\n// Adds a variable blur between two points to your image.\nVariableBlurImage(image:startPoint:endPoint:startRadius:endRadius:)\n\n/// Adds a variable blur following the lightness in the provided gradient image.\nGradientBlurImage(image:gradient:maxRadius:)\n\n/// Adds multiple variable blurs\nMultipleBlursImage(image:descriptions:)\n```\n\n#### Available Image ViewModifiers\n\n```swift\n/// Adds a vertical variable blur to your image.\nImage.verticalVariableBlur(startPoint:endPoint:startRadius:endRadius:)\n\n/// Adds a horizontal variable blur to your image.\nImage.horizontalVariableBlur(startPoint:endPoint:startRadius:endRadius:)\n\n// Adds a variable blur between two points to your image.\nImage.variableBlur(startPoint:endPoint:startRadius:endRadius:)\n\n/// Adds a variable blur following the lightness in the provided gradient image.\nImage.gradientBlur(gradient:maxRadius:)\n\n/// Adds multiple variable blurs\nImage.mutlipleBlurs(descriptions:)\n```\n\n### Working with CGImages\n\n`VariableBlurEngine` is an object used to apply progressive blur to CGImages.\n\nYou provide a CGImage, start point, end point, and their respective blur radiuses. A new CGImage is returned with the variable blur effect.\n\n#### Example\n\n```swift\nlet variableBlurEngine = VariableBlurEngine()\nlet leavesImage = UIImage(resource: .leaves)\nlet blurredImage = variableBlurEngine.applyVerticalVariableBlur(\n    toImage: leavesImage, \n    startPoint: 0, \n    endPoint: leavesImage.size.height / 4, \n    startRadius: 15, \n    endRadius: 0\n)\n```\n\n\u003cimg src=\"Documentation/Leaves-VerticalBlur-(0,20)-to-(50h,0).png\" width=\"300\" alt=\"Vertical progressive blur from the top to the middle over colorful leaves\"\u003e\n\n#### Available methods\n\n```swift\n/// Adds a vertical variable blur to your image.\nVariableBlurEngine.applyVerticalVariableBlur(image:startPoint:endPoint:startRadius:endRadius:)\n\n/// Adds a horizontal variable blur to your image.\nVariableBlurEngine.applyHorizontalVariableBlur(image:startPoint:endPoint:startRadius:endRadius:)\n\n// Adds a variable blur between two points to your image.\nVariableBlurEngine.applyVariableBlur(image:startPoint:endPoint:startRadius:endRadius:)\n\n/// Adds a variable blur following the lightness in the provided gradient image.\nVariableBlurEngine.applyGradientVariableBlur(image:gradient:maxRadius:)\n\n/// Adds multiple variable blurs\nVariableBlurEngine.applyMultipleVariableBlurs(image:descriptions:)\n```\n\n## Roadmap\n- Separable Gaussian Blur (Performance optimization)\n- Looking into applying variable blur to other UIViews\n\n## Project Organization\nThis framework is written in Swift and Metal.\n\n*VariableBlurImageView* is the primary framework.  \n*GenerateTestImages* is a small executable used to produce images to test against.\n\nThe tests for VariableBlurImageView check if the current state of the code produce the same set of images as has previously been generated by `GenerateTestImages`. \n\nWhen implementing altering the look of an existing blur type, expect the tests to fail. Running `GenerateTestImages` from Xcode will produce new images and make the tests succeed.\n\nWhen working on performance improvements, the tests should ideally not fail.\n\n## Implementing new blur types\n\nWhen implementing a new blur type, new tests and generating methods need to be provided.\n\n### Steps to implement a new blur type\n- Write a new kernel function in Metal\n- Provide a new method in *VariableBlurMetal.swift* to dispatch metal function\n  - Functions are precompiled lazily \n  - Buffers are made lazily, to be reused\n  - A kernel function has an input texture and an output texture.\n  - You may use `variableBlurGeneric(_:image:bufferConfigurationHandler:)`.\n- Write a new method in *VariableBlurEngine.swift*\n- Write a new method in *VariableBlurImageView.swift*\n  - Write a `X-variableBlurImpl` method in the extension for both iOS and macOS\n  - You may use the `transformAllVariations(ofImage:variationTransformMode:applyingTransform:)` method.\n  - Write a new method in the UIImageView subclass using `UIImage`.\n  - Write a new method in the NSImageView subclass using `NSImage`.\n    - Set the `originalImage` and `blurOperation` properties.\n- Add a new case to *VariableBlurOperation.swift*\n- Add a new View in *SwiftUI/VariableBlurImage.swift*\n  - Provide separate initializers for UIImage and NSImage.\n- Add a new Image modifier to *SwiftUI/Image+Extension.swift*\n- Write new generating methods in `GenerateTestImages`. See [Generate images to use in test](#generate-images-to-use-in-the-test)\n- Write new tests. See [Supplying tests](#supplying-tests)\n- Update docc documentation with new images, and method overview\n- Update README with new images, and method overview\n\n### Supplying tests\nGenerally, at least two tests are written for each blur type — one to check if the images produced are as expected, and one to measure performance.\n\nChecking similarity can be done with the `ìsEqual(inputImageName:expectedImageName:afterPerformingImageOperations:)` method, like so:\n\n```swift\nfunc testVerticalVariableBlur() throws {\n    XCTAssertTrue(\n        try isEqual(\n            inputImageName: inputImageName,\n            expectedImageName: \"\\(inputImageName)-VerticalBlur...\",\n            afterPerformingImageOperations: { input in\n                try variableBlurEngine.applyVerticalVariableBlur(\n                    toImage: input,\n                    startPoint: 0,\n                    endPoint: CGFloat(input.height / 2),\n                    startRadius: 20,\n                    endRadius: 0\n                )\n            }\n        )\n    )\n}\n```\n\nMeasuring performance can be done with the `provideInputImage(inputImageName:)` and `measure` methods, like so:\n\n```swift\nfunc testPerformanceOfVerticalVariableBlur() throws {\n    let inputImage = try provideInputImage(inputImageName: inputImageName)\n    measure {\n        _ = try! variableBlurEngine.applyVerticalVariableBlur(\n            toImage: inputImage,\n            startPoint: 0,\n            endPoint: CGFloat(inputImage.height / 2),\n            startRadius: 20,\n            endRadius: 0\n        )\n    }\n}\n```\n\n### Generate images to use in the test\n\nThe `GenerateImages.swift` file in *GenerateTestImages* provides the implementation to generate images.\n\nUse the `from(image:named:performingOperations:)` method on OutputImage, and add the result to the `outputImages` array. The entries in this array are written to the *ExpectedOutputs* directory under Tests.\n\n```swift\n// Vertical blur\nOutputImage\n    .from(image: inputImage, named: \"\\(name)-Vertical...\") { input in\n        try variableBlurEngine.applyVerticalVariableBlur(\n            toImage: input,\n            startPoint: 0,\n            endPoint: CGFloat(input.height / 2),\n            startRadius: 20,\n            endRadius: 0\n        )\n    }?\n    .adding(to: \u0026outputImages)\n```\n\n## Contributing to VariableBlurImageView\n\nContributions are welcome and encouraged. Feel free to check out the project, submit issues and code patches.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feskils%2Fvariableblurimageview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feskils%2Fvariableblurimageview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feskils%2Fvariableblurimageview/lists"}