116 lines
4.0 KiB
Swift
Executable File
116 lines
4.0 KiB
Swift
Executable File
//
|
|
// 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 {
|
|
init(blendingTop top: BlurHash, bottom: BlurHash) {
|
|
guard top.components.count == 1, bottom.components.count == 1 else {
|
|
fatalError("Blended BlurHashses must have only one vertical component")
|
|
}
|
|
|
|
let average = zip(top.components[0], bottom.components[0]).map { ($0 + $1) / 2 }
|
|
let difference = zip(top.components[0], bottom.components[0]).map { ($0 - $1) / 2 }
|
|
self.init(components: [average, difference])
|
|
}
|
|
|
|
init(blendingLeft left: BlurHash, right: BlurHash) {
|
|
self = BlurHash(blendingTop: left.transposed, bottom: right.transposed).transposed
|
|
}
|
|
}
|
|
|
|
public extension BlurHash {
|
|
init(colour: UIColor) {
|
|
self.init(components: [[colour.linear]])
|
|
}
|
|
|
|
init(blendingTop topColour: UIColor, bottom bottomColour: UIColor) {
|
|
self = BlurHash(blendingTop: .init(colour: topColour), bottom: .init(colour: bottomColour))
|
|
}
|
|
|
|
init(blendingLeft leftColour: UIColor, right rightColour: UIColor) {
|
|
self = BlurHash(blendingLeft: .init(colour: leftColour), right: .init(colour: rightColour))
|
|
}
|
|
|
|
init(
|
|
blendingTopLeft topLeftColour: UIColor,
|
|
topRight topRightColour: UIColor,
|
|
bottomLeft bottomLeftColour: UIColor,
|
|
bottomRight bottomRightColour: UIColor
|
|
) {
|
|
self = BlurHash(
|
|
blendingTop: BlurHash(blendingTop: topLeftColour, bottom: topRightColour).transposed,
|
|
bottom: BlurHash(blendingTop: bottomLeftColour, bottom: bottomRightColour).transposed
|
|
)
|
|
}
|
|
}
|
|
|
|
public extension BlurHash {
|
|
init(horizontalColours colours: [(Float, Float, Float)], numberOfComponents: Int) {
|
|
guard numberOfComponents >= 1, numberOfComponents <= 9 else {
|
|
fatalError("Number of components bust be between 1 and 9 inclusive")
|
|
}
|
|
|
|
self.init(components: [(0 ..< numberOfComponents).map { i in
|
|
let normalisation: Float = i == 0 ? 1 : 2
|
|
var sum: (Float, Float, Float) = (0, 0, 0)
|
|
for x in 0 ..< colours.count {
|
|
let basis = normalisation * cos(Float.pi * Float(i) * Float(x) / Float(colours.count - 1))
|
|
sum += basis * colours[x]
|
|
}
|
|
|
|
return sum / Float(colours.count)
|
|
}])
|
|
}
|
|
}
|
|
|
|
public extension BlurHash {
|
|
var mirroredHorizontally: BlurHash {
|
|
.init(components: (0 ..< numberOfVerticalComponents).map { j -> [(Float, Float, Float)] in
|
|
(0 ..< numberOfHorizontalComponents).map { i -> (Float, Float, Float) in
|
|
components[j][i] * (i % 2 == 0 ? 1 : -1)
|
|
}
|
|
})
|
|
}
|
|
|
|
var mirroredVertically: BlurHash {
|
|
.init(components: (0 ..< numberOfVerticalComponents).map { j -> [(Float, Float, Float)] in
|
|
(0 ..< numberOfHorizontalComponents).map { i -> (Float, Float, Float) in
|
|
components[j][i] * (j % 2 == 0 ? 1 : -1)
|
|
}
|
|
})
|
|
}
|
|
|
|
var transposed: BlurHash {
|
|
.init(components: (0 ..< numberOfHorizontalComponents).map { i in
|
|
(0 ..< numberOfVerticalComponents).map { j in
|
|
components[j][i]
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
extension UIColor {
|
|
var linear: (Float, Float, Float) {
|
|
guard let c = cgColor.converted(to: CGColorSpace(name: CGColorSpace.sRGB)!, intent: .defaultIntent, options: nil)?.components
|
|
else { return (0, 0, 0) }
|
|
|
|
switch c.count {
|
|
case 1, 2: return (sRGBToLinear(c[0]), sRGBToLinear(c[0]), sRGBToLinear(c[0]))
|
|
case 3, 4: return (sRGBToLinear(c[0]), sRGBToLinear(c[1]), sRGBToLinear(c[2]))
|
|
default: return (0, 0, 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
func sRGBToLinear(_ value: CGFloat) -> Float {
|
|
let v = Float(value)
|
|
if v <= 0.04045 { return v / 12.92 }
|
|
else { return pow((v + 0.055) / 1.055, 2.4) }
|
|
}
|