CGImage+.swift 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. //
  2. // CGImage+.swift
  3. // EFQRCode
  4. //
  5. // Created by EyreFree on 2017/4/9.
  6. //
  7. // Copyright (c) 2017 EyreFree <eyrefree@eyrefree.org>
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. import CoreGraphics
  27. #if os(iOS) || os(tvOS) || os(macOS)
  28. import CoreImage
  29. #endif
  30. public extension CGImage {
  31. #if os(iOS) || os(tvOS) || os(macOS)
  32. /// Convert UIImage to CIImage
  33. /// http://wiki.hawkguide.com/wiki/Swift:_Convert_between_CGImage,_CIImage_and_UIImage
  34. func toCIImage() -> CIImage {
  35. return CIImage(cgImage: self)
  36. }
  37. #endif
  38. /// Get pixels from CIImage
  39. func pixels() -> [[EFUIntPixel]]? {
  40. let dataSize = width * height * 4
  41. var pixelData = [UInt8](repeating: 0, count: Int(dataSize))
  42. let colorSpace = CGColorSpaceCreateDeviceRGB()
  43. guard let context = CGContext(
  44. data: &pixelData,
  45. width: width,
  46. height: height,
  47. bitsPerComponent: 8,
  48. bytesPerRow: 4 * width,
  49. space: colorSpace,
  50. bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
  51. ) else {
  52. return nil
  53. }
  54. context.draw(self, in: CGRect(x: 0, y: 0, width: width, height: height))
  55. let pixels: [[EFUIntPixel]] = ( 0 ..< height ).map { y in
  56. ( 0 ..< width ).map { x in
  57. let offset = 4 * (x + y * width)
  58. return EFUIntPixel(
  59. red: pixelData[offset + 0],
  60. green: pixelData[offset + 1],
  61. blue: pixelData[offset + 2],
  62. alpha: pixelData[offset + 3]
  63. )
  64. }
  65. }
  66. return pixels
  67. }
  68. /// Get avarage color
  69. func avarageColor() -> CGColor? {
  70. let rgba = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: 4)
  71. guard let context = CGContext(
  72. data: rgba,
  73. width: 1,
  74. height: 1,
  75. bitsPerComponent: 8,
  76. bytesPerRow: 4,
  77. space: CGColorSpaceCreateDeviceRGB(),
  78. bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
  79. ) else {
  80. return nil
  81. }
  82. context.draw(self, in: CGRect(x: 0, y: 0, width: 1, height: 1))
  83. return CGColor.fromRGB(
  84. red: CGFloat(rgba[0]) / 255.0,
  85. green: CGFloat(rgba[1]) / 255.0,
  86. blue: CGFloat(rgba[2]) / 255.0,
  87. alpha: CGFloat(rgba[3]) / 255.0
  88. )
  89. }
  90. /// Grayscale
  91. /// http://stackoverflow.com/questions/1311014/convert-to-grayscale-too-slow
  92. func grayscale() -> CGImage? {
  93. guard let context = CGContext(
  94. data: nil, width: width, height: height,
  95. bitsPerComponent: 8, bytesPerRow: 4 * width,
  96. space: CGColorSpaceCreateDeviceGray(),
  97. bitmapInfo: CGImageAlphaInfo.none.rawValue
  98. ) else {
  99. return nil
  100. }
  101. context.draw(self, in: CGRect(origin: .zero, size: CGSize(width: width, height: height)))
  102. return context.makeImage()
  103. }
  104. /// Binarization
  105. /// http://blog.sina.com.cn/s/blog_6b7ba99d0101js23.html
  106. func binarization(
  107. value: CGFloat = 0.5,
  108. foregroundColor: CGColor = .EFWhite(),
  109. backgroundColor: CGColor = .EFBlack()
  110. ) -> CGImage? {
  111. let dataSize = width * height * 4
  112. var pixelData = [UInt8](repeating: 0, count: Int(dataSize))
  113. let colorSpace = CGColorSpaceCreateDeviceRGB()
  114. guard let backgroundPixel = EFUIntPixel(color: backgroundColor),
  115. let foregroundPixel = EFUIntPixel(color: foregroundColor),
  116. let context = CGContext(
  117. data: &pixelData,
  118. width: width,
  119. height: height,
  120. bitsPerComponent: 8,
  121. bytesPerRow: 4 * width,
  122. space: colorSpace,
  123. bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
  124. ) else {
  125. return nil
  126. }
  127. context.draw(self, in: CGRect(x: 0, y: 0, width: width, height: height))
  128. for x in 0 ..< width {
  129. for y in 0 ..< height {
  130. let offset = 4 * (x + y * width)
  131. // RGBA
  132. let alpha = CGFloat(pixelData[offset + 3]) / 255.0
  133. let intensity = (
  134. CGFloat(pixelData[offset + 0]) + CGFloat(pixelData[offset + 1]) + CGFloat(pixelData[offset + 2])
  135. ) / 3.0 / 255.0 * alpha + (1.0 - alpha)
  136. let finalPixel = intensity > value ? backgroundPixel : foregroundPixel
  137. pixelData[offset + 0] = finalPixel.red
  138. pixelData[offset + 1] = finalPixel.green
  139. pixelData[offset + 2] = finalPixel.blue
  140. pixelData[offset + 3] = finalPixel.alpha
  141. }
  142. }
  143. return context.makeImage()
  144. }
  145. }