I got some reference from these links-
我从这些链接中得到了一些参考 -
-
What is the Algorithm behind a color wheel?
色轮背后的算法是什么?
-
色轮背后的数学
-
基本配色方案
-
How to fill a bezier path with gradient color
如何使用渐变颜色填充贝塞尔曲线路径
I have gone through the concept of "HSV colour space". But I want to draw a color wheel using RGB with the help of CAGradientLayer
.
我已经经历了“HSV色彩空间”的概念。但我想在CAGradientLayer的帮助下使用RGB绘制色轮。
Here is the code snippet of making a color wheel by using simple RGB color array and UIBezierPath
-
这是使用简单的RGB颜色数组和UIBezierPath制作色轮的代码片段 -
func drawColorWheel()
{
context?.saveGState()
range = CGFloat(100.00 / CGFloat(colorArray.count))
for k in 0 ..< colorArray.count
{
drawSlice(startPercent: CGFloat(k) * range, endPercent: CGFloat(CGFloat(k + 1) * range), color: colorArray.object(at: k) as! UIColor)
}
context?.restoreGState()
}
func drawSlice(startPercent: CGFloat, endPercent: CGFloat, color: UIColor)
{
let startAngle = getAngleAt(percentage: startPercent)
let endAngle = getAngleAt(percentage: endPercent)
let path = getArcPath(startAngle: startAngle, endAngle: endAngle)
color.setFill()
path.fill()
}
Where getAngleAt()
and getArcPath()
are the private functions to draw the path with an angle.
其中getAngleAt()和getArcPath()是用角度绘制路径的私有函数。
Here is the final output of my code -
这是我的代码的最终输出 -
Now, my question is the how to give these colors a gradient effect so that each colors mix up with gradient color layer?
现在,我的问题是如何为这些颜色提供渐变效果,以便每种颜色与渐变颜色层混合?
1 个解决方案
#1
6
One approach is to build an image and manipulate the pixel buffer manually:
一种方法是构建图像并手动操作像素缓冲区:
- Create
CGContext
of certain size and certain type; - Access its data buffer via
data
property; - Rebind that to something that makes it easy to manipulate that buffer (I use a
Pixel
, astruct
for the 32-bit representation of a pixel); - Loop through the pixels, one by one, converting that to an
angle
andradius
for the circle within this image; and - Create a pixel of the appropriate color if it's inside the circle; make it a zero-alpha pixel if not.
创建一定大小和特定类型的CGContext;
通过数据属性访问其数据缓冲区;
将其重新绑定到可以轻松操作缓冲区的东西(我使用Pixel,一个用于像素的32位表示的结构);
逐个循环通过像素,将其转换为此图像中圆圈的角度和半径;和
如果它在圆圈内,则创建一个适当颜色的像素;如果不是,则将其设为零alpha像素。
So in Swift 3:
所以在Swift 3中:
func buildHueCircle(in rect: CGRect, radius: CGFloat, scale: CGFloat = UIScreen.main.scale) -> UIImage? {
let width = Int(rect.size.width * scale)
let height = Int(rect.size.height * scale)
let center = CGPoint(x: width / 2, y: height / 2)
let space = CGColorSpaceCreateDeviceRGB()
let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width * 4, space: space, bitmapInfo: Pixel.bitmapInfo)!
let buffer = context.data!
let pixels = buffer.bindMemory(to: Pixel.self, capacity: width * height)
var pixel: Pixel
for y in 0 ..< height {
for x in 0 ..< width {
let angle = fmod(atan2(CGFloat(x) - center.x, CGFloat(y) - center.y) + 2 * .pi, 2 * .pi)
let distance = hypot(CGFloat(x) - center.x, CGFloat(y) - center.y)
let value = UIColor(hue: angle / 2 / .pi, saturation: 1, brightness: 1, alpha: 1)
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
value.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
if distance <= (radius * scale) {
pixel = Pixel(red: UInt8(red * 255),
green: UInt8(green * 255),
blue: UInt8(blue * 255),
alpha: UInt8(alpha * 255))
} else {
pixel = Pixel(red: 255, green: 255, blue: 255, alpha: 0)
}
pixels[y * width + x] = pixel
}
}
let cgImage = context.makeImage()!
return UIImage(cgImage: cgImage, scale: scale, orientation: .up)
}
Where
struct Pixel: Equatable {
private var rgba: UInt32
var red: UInt8 {
return UInt8((rgba >> 24) & 255)
}
var green: UInt8 {
return UInt8((rgba >> 16) & 255)
}
var blue: UInt8 {
return UInt8((rgba >> 8) & 255)
}
var alpha: UInt8 {
return UInt8((rgba >> 0) & 255)
}
init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
rgba = (UInt32(red) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0)
}
static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
static func ==(lhs: Pixel, rhs: Pixel) -> Bool {
return lhs.rgba == rhs.rgba
}
}
That yields:
Or you can tweak the brightness or saturation based upon the radius:
或者您可以根据半径调整亮度或饱和度:
#1
6
One approach is to build an image and manipulate the pixel buffer manually:
一种方法是构建图像并手动操作像素缓冲区:
- Create
CGContext
of certain size and certain type; - Access its data buffer via
data
property; - Rebind that to something that makes it easy to manipulate that buffer (I use a
Pixel
, astruct
for the 32-bit representation of a pixel); - Loop through the pixels, one by one, converting that to an
angle
andradius
for the circle within this image; and - Create a pixel of the appropriate color if it's inside the circle; make it a zero-alpha pixel if not.
创建一定大小和特定类型的CGContext;
通过数据属性访问其数据缓冲区;
将其重新绑定到可以轻松操作缓冲区的东西(我使用Pixel,一个用于像素的32位表示的结构);
逐个循环通过像素,将其转换为此图像中圆圈的角度和半径;和
如果它在圆圈内,则创建一个适当颜色的像素;如果不是,则将其设为零alpha像素。
So in Swift 3:
所以在Swift 3中:
func buildHueCircle(in rect: CGRect, radius: CGFloat, scale: CGFloat = UIScreen.main.scale) -> UIImage? {
let width = Int(rect.size.width * scale)
let height = Int(rect.size.height * scale)
let center = CGPoint(x: width / 2, y: height / 2)
let space = CGColorSpaceCreateDeviceRGB()
let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width * 4, space: space, bitmapInfo: Pixel.bitmapInfo)!
let buffer = context.data!
let pixels = buffer.bindMemory(to: Pixel.self, capacity: width * height)
var pixel: Pixel
for y in 0 ..< height {
for x in 0 ..< width {
let angle = fmod(atan2(CGFloat(x) - center.x, CGFloat(y) - center.y) + 2 * .pi, 2 * .pi)
let distance = hypot(CGFloat(x) - center.x, CGFloat(y) - center.y)
let value = UIColor(hue: angle / 2 / .pi, saturation: 1, brightness: 1, alpha: 1)
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
value.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
if distance <= (radius * scale) {
pixel = Pixel(red: UInt8(red * 255),
green: UInt8(green * 255),
blue: UInt8(blue * 255),
alpha: UInt8(alpha * 255))
} else {
pixel = Pixel(red: 255, green: 255, blue: 255, alpha: 0)
}
pixels[y * width + x] = pixel
}
}
let cgImage = context.makeImage()!
return UIImage(cgImage: cgImage, scale: scale, orientation: .up)
}
Where
struct Pixel: Equatable {
private var rgba: UInt32
var red: UInt8 {
return UInt8((rgba >> 24) & 255)
}
var green: UInt8 {
return UInt8((rgba >> 16) & 255)
}
var blue: UInt8 {
return UInt8((rgba >> 8) & 255)
}
var alpha: UInt8 {
return UInt8((rgba >> 0) & 255)
}
init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
rgba = (UInt32(red) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0)
}
static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
static func ==(lhs: Pixel, rhs: Pixel) -> Bool {
return lhs.rgba == rhs.rgba
}
}
That yields:
Or you can tweak the brightness or saturation based upon the radius:
或者您可以根据半径调整亮度或饱和度: