Как нарисовать цветовой круг в объективе - PullRequest
36 голосов
/ 24 февраля 2011

Я пытаюсь нарисовать цветовой круг для iPhone, но не могу заставить градиент вращаться вокруг точки.Я пытаюсь использовать градиенты, но Objective-C обеспечивает линейный градиент, который рисует градиент по прямой линии, как это: linear gradient

и радиальный градиент, который рисует градиент, начиная с точки и излучаяво всех направлениях, как это: radial gradient

Я хочу нарисовать линейный градиент, который вращается вокруг такой точки:

colorwheel

Ответы [ 6 ]

23 голосов
/ 24 февраля 2011

Следующее рисует цветовое колесо HSL в подклассе UIView.Это делается путем генерации растрового изображения путем вычисления для каждого пикселя правильного значения цвета.Это не совсем то, что вы пытаетесь сделать (похоже, это просто цвет меняется в круге с постоянной яркостью / насыщенностью), но вы должны иметь возможность адаптировать его для своих нужд.

Обратите внимание, что этоможет не иметь оптимальной производительности, но это должно помочь вам начать.Также вы можете использовать getColorWheelValue() для обработки пользовательского ввода (щелчки / касания по заданной координате).

- (void)drawRect:(CGRect)rect
{
    int dim = self.bounds.size.width; // should always be square.
    bitmapData = CFDataCreateMutable(NULL, 0);
    CFDataSetLength(bitmapData, dim * dim * 4);
    generateColorWheelBitmap(CFDataGetMutableBytePtr(bitmapData), dim, luminance);
    UIImage *image = createUIImageWithRGBAData(bitmapData, self.bounds.size.width, self.bounds.size.height);
    CFRelease(bitmapData);
    [image drawAtPoint:CGPointZero];
    [image release];
}

void generateColorWheelBitmap(UInt8 *bitmap, int widthHeight, float l)
{
    // I think maybe you can do 1/3 of the pie, then do something smart to generate the other two parts, but for now we'll brute force it.
    for (int y = 0; y < widthHeight; y++)
    {
        for (int x = 0; x < widthHeight; x++)
        {
            float h, s, r, g, b, a;
            getColorWheelValue(widthHeight, x, y, &h, &s);
            if (s < 1.0)
            {
                // Antialias the edge of the circle.
                if (s > 0.99) a = (1.0 - s) * 100;
                else a = 1.0;

                HSL2RGB(h, s, l, &r, &g, &b);
            }
            else
            {
                r = g = b = a = 0.0f;
            }

            int i = 4 * (x + y * widthHeight);
            bitmap[i] = r * 0xff;
            bitmap[i+1] = g * 0xff;
            bitmap[i+2] = b * 0xff;
            bitmap[i+3] = a * 0xff;
        }
    }
}

void getColorWheelValue(int widthHeight, int x, int y, float *outH, float *outS)
{
    int c = widthHeight / 2;
    float dx = (float)(x - c) / c;
    float dy = (float)(y - c) / c;
    float d = sqrtf((float)(dx*dx + dy*dy));
    *outS = d;
    *outH = acosf((float)dx / d) / M_PI / 2.0f;
    if (dy < 0) *outH = 1.0 - *outH;
}

UIImage *createUIImageWithRGBAData(CFDataRef data, int width, int height)
{
    CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGImageRef imageRef = CGImageCreate(width, height, 8, 32, width * 4, colorSpace, kCGImageAlphaLast, dataProvider, NULL, 0, kCGRenderingIntentDefault);
    UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
    CGDataProviderRelease(dataProvider);
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(imageRef);
    return image;
}

// Adapted from Apple sample code.  See http://en.wikipedia.org/wiki/HSV_color_space#Comparison_of_HSL_and_HSV
void HSL2RGB(float h, float s, float l, float* outR, float* outG, float* outB)
{
    float temp1, temp2;
    float temp[3];
    int i;

    // Check for saturation. If there isn't any just return the luminance value for each, which results in gray.
    if(s == 0.0)
    {
        *outR = l;
        *outG = l;
        *outB = l;
        return;
    }

    // Test for luminance and compute temporary values based on luminance and saturation 
    if(l < 0.5)
        temp2 = l * (1.0 + s);
    else
        temp2 = l + s - l * s;
    temp1 = 2.0 * l - temp2;

    // Compute intermediate values based on hue
    temp[0] = h + 1.0 / 3.0;
    temp[1] = h;
    temp[2] = h - 1.0 / 3.0;

    for(i = 0; i < 3; ++i)
    {
        // Adjust the range
        if(temp[i] < 0.0)
            temp[i] += 1.0;
        if(temp[i] > 1.0)
            temp[i] -= 1.0;


        if(6.0 * temp[i] < 1.0)
            temp[i] = temp1 + (temp2 - temp1) * 6.0 * temp[i];
        else {
            if(2.0 * temp[i] < 1.0)
                temp[i] = temp2;
            else {
                if(3.0 * temp[i] < 2.0)
                    temp[i] = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - temp[i]) * 6.0;
                else
                    temp[i] = temp1;
            }
        }
    }

    // Assign temporary values to R, G, B
    *outR = temp[0];
    *outG = temp[1];
    *outB = temp[2];
}
18 голосов
/ 24 мая 2013

Использование только методов UIKit:

//  ViewController.m; assuming ViewController is the app's root view controller
#include "ViewController.h"
@interface ViewController () 
{
    UIImage *img;
    UIImageView *iv;
}
@end

@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];

    CGSize size = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height);
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width, size.height), YES, 0.0);
    [[UIColor whiteColor] setFill];
    UIRectFill(CGRectMake(0, 0, size.width, size.height));

    int sectors = 180;
    float radius = MIN(size.width, size.height)/2;
    float angle = 2 * M_PI/sectors;
    UIBezierPath *bezierPath;
    for ( int i = 0; i < sectors; i++)
    {
        CGPoint center = CGPointMake(size.width/2, size.height/2);
        bezierPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:i * angle endAngle:(i + 1) * angle clockwise:YES];
        [bezierPath addLineToPoint:center];
        [bezierPath closePath];
        UIColor *color = [UIColor colorWithHue:((float)i)/sectors saturation:1. brightness:1. alpha:1];
        [color setFill];
        [color setStroke];
        [bezierPath fill];
        [bezierPath stroke];
    }
    img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    iv = [[UIImageView alloc] initWithImage:img];
    [self.view addSubview:iv];
}
@end

По сути, все, что делает приведенный выше код, - это обход круга, рисуя узкие сектора, заполняя их цветами постепенно увеличивающегося оттенка.

Конечно, вы можете сделать все рисование непосредственно в графическом контексте представления с помощью drawRect() и не создавать явный контекст изображения.

enter image description here

4 голосов
/ 24 февраля 2011

Я сделал что-то вроде этого, создав большую растровую карту RGBA, раскрасив каждый пиксель в зависимости от его местоположения, преобразованный в полярные координаты, затем преобразовав растровое изображение в изображение и нарисовав изображение уменьшенным.Уменьшение должно было помочь пикселизации сглаживания вблизи центра.

2 голосов
/ 24 февраля 2011

Вы должны будете сделать растровое изображение в соответствии с предложением hotpaw2. Наиболее эффективный способ рассчитать это - использовать цветовое пространство HSL . Это позволит вам создать функцию, которая принимает в качестве входных данных местоположение в пикселях и выплевывает значение RGB, используя только несколько базовых арифметических операций и небольшой триггер для его вычисления.

Если вам нужны произвольные цвета вдоль «спиц» колеса, ну, это требует больше работы. Вам необходимо рассчитать значения смешивания для каждого пикселя, и это требует больше тригонометрии и не так быстро. Когда я сделал это, мне пришлось векторизовать и распараллелить его, чтобы убрать небольшую (хотя и ощутимую) задержку обновления при пересчете растрового изображения - на MacBook Pro. На iPhone у вас нет этих опций, поэтому вам придется жить с задержкой.

Дайте мне знать, если вам нужны более подробные объяснения этих методов; Я буду счастлив сделать это.

1 голос
/ 24 февраля 2011

Я не знаю ничего, что сделает это для вас.Вы можете аппроксимировать его, нарисовав треугольники , которые идут (желтый-> красный, красный-> пурпурный, пурпурный-> синий и т. Д.), А затем поместив над ним маску круга . Посмотритев CGGradient .

Извините, я не могу помочь.

0 голосов
/ 22 февраля 2019

Теперь можно рисовать изображение цветового круга с помощью CIFilter (начиная с iOS 10). Подробнее в этом сообщении в блоге (код Swift, но может быть переведен непосредственно в Objective-C)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...