// // Swiftfin is subject to the terms of the Mozilla Public // License, v2.0. If a copy of the MPL was not distributed with this // file, you can obtain one at https://mozilla.org/MPL/2.0/. // // Copyright (c) 2022 Jellyfin & Jellyfin Contributors // import UIKit public extension BlurHash { func cgImage(size: CGSize) -> CGImage? { let width = Int(size.width) let height = Int(size.height) let bytesPerRow = width * 3 guard let data = CFDataCreateMutable(kCFAllocatorDefault, bytesPerRow * height) else { return nil } CFDataSetLength(data, bytesPerRow * height) guard let pixels = CFDataGetMutableBytePtr(data) else { return nil } for y in 0 ..< height { for x in 0 ..< width { var c: (Float, Float, Float) = (0, 0, 0) for j in 0 ..< numberOfVerticalComponents { for i in 0 ..< numberOfHorizontalComponents { let basis = cos(Float.pi * Float(x) * Float(i) / Float(width)) * cos(Float.pi * Float(y) * Float(j) / Float(height)) let component = components[j][i] c += component * basis } } let intR = UInt8(linearTosRGB(c.0)) let intG = UInt8(linearTosRGB(c.1)) let intB = UInt8(linearTosRGB(c.2)) pixels[3 * x + 0 + y * bytesPerRow] = intR pixels[3 * x + 1 + y * bytesPerRow] = intG pixels[3 * x + 2 + y * bytesPerRow] = intB } } let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue) guard let provider = CGDataProvider(data: data) else { return nil } guard let cgImage = CGImage( width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 24, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: true, intent: .defaultIntent ) else { return nil } return cgImage } func cgImage(numberOfPixels: Int = 1024, originalSize size: CGSize) -> CGImage? { let width: CGFloat let height: CGFloat if size.width > size.height { width = floor(sqrt(CGFloat(numberOfPixels) * size.width / size.height) + 0.5) height = floor(CGFloat(numberOfPixels) / width + 0.5) } else { height = floor(sqrt(CGFloat(numberOfPixels) * size.height / size.width) + 0.5) width = floor(CGFloat(numberOfPixels) / height + 0.5) } return cgImage(size: CGSize(width: width, height: height)) } func image(size: CGSize) -> UIImage? { guard let cgImage = cgImage(size: size) else { return nil } return UIImage(cgImage: cgImage) } func image(numberOfPixels: Int = 1024, originalSize size: CGSize) -> UIImage? { guard let cgImage = cgImage(numberOfPixels: numberOfPixels, originalSize: size) else { return nil } return UIImage(cgImage: cgImage) } } @objc public extension UIImage { convenience init?(blurHash string: String, size: CGSize, punch: Float = 1) { guard let blurHash = BlurHash(string: string), let cgImage = blurHash.punch(punch).cgImage(size: size) else { return nil } self.init(cgImage: cgImage) } convenience init?(blurHash string: String, numberOfPixels: Int = 1024, originalSize size: CGSize, punch: Float = 1) { guard let blurHash = BlurHash(string: string), let cgImage = blurHash.punch(punch).cgImage(numberOfPixels: numberOfPixels, originalSize: size) else { return nil } self.init(cgImage: cgImage) } }