ColorGenerator.swift 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. //
  2. // SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
  3. // SPDX-License-Identifier: GPL-3.0-or-later
  4. //
  5. // See https://github.com/nextcloud/nextcloud-vue/blob/56b79afae93f4701a0cb933bfeb7b4a2fbd590fb/src/functions/usernameToColor/usernameToColor.js
  6. // and https://github.com/nextcloud/nextcloud-vue/blob/56b79afae93f4701a0cb933bfeb7b4a2fbd590fb/src/utils/GenColors.js
  7. import Foundation
  8. import CryptoKit
  9. @objcMembers class ColorGenerator: NSObject {
  10. public static let shared = ColorGenerator()
  11. private let steps = 6
  12. private let finalPalette: [UIColor]
  13. // See: https://stackoverflow.com/a/22334560
  14. private static let multiplier = CGFloat(255.999999)
  15. private override init() {
  16. finalPalette = ColorGenerator.genColors(steps)
  17. super.init()
  18. }
  19. private static func stepCalc(_ steps: Int, _ ends: [UIColor]) -> [CGFloat] {
  20. var step: [CGFloat] = [0, 0, 0]
  21. var red0: CGFloat = 0
  22. var green0: CGFloat = 0
  23. var blue0: CGFloat = 0
  24. var red1: CGFloat = 0
  25. var green1: CGFloat = 0
  26. var blue1: CGFloat = 0
  27. ends[0].getRed(&red0, green: &green0, blue: &blue0, alpha: nil)
  28. ends[1].getRed(&red1, green: &green1, blue: &blue1, alpha: nil)
  29. step[0] = (red1 - red0) / CGFloat(steps)
  30. step[1] = (green1 - green0) / CGFloat(steps)
  31. step[2] = (blue1 - blue0) / CGFloat(steps)
  32. return step
  33. }
  34. private static func mixPalette(_ steps: Int, _ color1: UIColor, _ color2: UIColor) -> [UIColor] {
  35. var palette: [UIColor] = [color1]
  36. let step = stepCalc(steps, [color1, color2])
  37. for i in 1...steps - 1 {
  38. var red: CGFloat = 0
  39. var green: CGFloat = 0
  40. var blue: CGFloat = 0
  41. color1.getRed(&red, green: &green, blue: &blue, alpha: nil)
  42. let r = abs(red + step[0] * CGFloat(i))
  43. let g = abs(green + step[1] * CGFloat(i))
  44. let b = abs(blue + step[2] * CGFloat(i))
  45. palette.append(UIColor(red: r, green: g, blue: b, alpha: 1))
  46. }
  47. return palette
  48. }
  49. public static func genColors(_ steps: Int) -> [UIColor] {
  50. let red = UIColor(red: 182 / multiplier, green: 70 / multiplier, blue: 157 / multiplier, alpha: 1)
  51. let yellow = UIColor(red: 221 / multiplier, green: 203 / multiplier, blue: 85 / multiplier, alpha: 1)
  52. let blue = UIColor(red: 0, green: 130 / multiplier, blue: 201 / multiplier, alpha: 1)
  53. var palette1 = mixPalette(steps, red, yellow)
  54. let palette2 = mixPalette(steps, yellow, blue)
  55. let palette3 = mixPalette(steps, blue, red)
  56. palette1.append(contentsOf: palette2)
  57. palette1.append(contentsOf: palette3)
  58. return palette1
  59. }
  60. public func usernameToColor(_ username: String) -> UIColor {
  61. let hash = username.lowercased()
  62. var hashInt = 0
  63. if let usernameData = hash.data(using: .utf8) {
  64. let md5Hash = Insecure.MD5.hash(data: usernameData)
  65. let t = md5Hash.map { String(format: "%02hhx", $0) }.joined()
  66. hashInt = t.map { Int(String($0), radix: 16)! % 16}.reduce(0, +)
  67. }
  68. let maximum = steps * 3
  69. hashInt = hashInt % maximum
  70. return finalPalette[hashInt]
  71. }
  72. }