iOS znajdź kolor w punkcie między dwoma kolorami

Mam problem: muszę być w stanie wziąć dwa kolory i zrobić z nich "wirtualny gradient". Następnie muszę być w stanie znaleźć kolor w dowolnym momencie na tej linii. Moje obecne podejście jest takie:

if (fahrenheit < kBottomThreshold)
{
    return [UIColor colorWithRed:kBottomR/255.0f green:kBottomG/255.0f blue:kBottomB/255.0f alpha:1];
}
if (fahrenheit > kTopThreshold)
{
    return [UIColor colorWithRed:kTopR/255.0f green:kTopG/255.0f blue:kTopB/255.0f alpha:1];
}

double rDiff = kTopR - kBottomR;
double gDiff = kTopG - kBottomG;
double bDiff = kTopB - kBottomB;

double tempDiff = kTopThreshold - kBottomThreshold;

double rValue;
double gValue;
double bValue;

rValue = kBottomR + ((rDiff/tempDiff) * fahrenheit);
gValue = kBottomG + ((gDiff/tempDiff) * fahrenheit);
bValue = kBottomB + ((bDiff/tempDiff) * fahrenheit);

return [UIColor colorWithRed:rValue/255.0f green:gValue/255.0f blue:bValue/255.0f alpha:1];

Zmienne:

  • fahrenheit jest zmienną przekazywaną do mojej funkcji, która jest liczbą w tej wirtualnej linii, dla której chcę znaleźć kolor.
  • kTopR, kTopB, i kTopG są wartościami RGB dla jednego końca gradientu. To samo dla ich kBottom odpowiedniki.
  • kBottomThreshold i kTopThreshold są punktami końcowymi mojego gradientu.

Oto mój problem: gdy fahrenheit przechodzi przez oba końce gradientu, gradient wydaje się "przeskakiwać" do innej wartości.

Dodałem przykładowy projekt, hostowany na moim serwerze S3, tutaj .

naprawdę musisz pobrać projekt i wypróbować go na symulatorze / urządzeniu, aby zobaczyć, co mam na myśli (chyba, że jesteś szalony mądry i może powiedzieć po prostu patrząc na kod)

Author: Undo, 2013-02-22

5 answers

Problem polega na tym, że nie odejmujesz kBottomThreshold od farenheit.

Ale uprośćmy.

Najpierw chcemy zmapować temperaturę wejściową do parametru t w zakresie [0 ... 1]. Następnie chcemy odwzorować t na wyjście z zakresu [kBottomR... kTopR], a także na wyjście z zakresu [kBottomG... kTopG], a także na wyjście z zakresu [kBottomB ... kTopB].

UIColor *colorForDegreesFahrenheit(double fahrenheit) {
    double t = (fahrenheit - kBottomThreshold) / (kTopThreshold - kBottomThreshold);

    // Clamp t to the range [0 ... 1].
    t = MAX(0.0, MIN(t, 1.0));

    double r = kBottomR + t * (kTopR - kBottomR);
    double g = kBottomG + t * (kTopG - kBottomG);
    double b = kBottomB + t * (kTopB - kBottomB);

    return [UIColor colorWithRed:r/255 green:g/255 blue:b/255 alpha:1];
}
 8
Author: rob mayoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-13 13:29:40

Swift - 3.0 && 4.0

extension UIColor {
    func toColor(_ color: UIColor, percentage: CGFloat) -> UIColor {
        let percentage = max(min(percentage, 100), 0) / 100
        switch percentage {
        case 0: return self
        case 1: return color
        default:
            var (r1, g1, b1, a1): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
            var (r2, g2, b2, a2): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
            guard self.getRed(&r1, green: &g1, blue: &b1, alpha: &a1) else { return self }
            guard color.getRed(&r2, green: &g2, blue: &b2, alpha: &a2) else { return self }

            return UIColor(red: CGFloat(r1 + (r2 - r1) * percentage),
                           green: CGFloat(g1 + (g2 - g1) * percentage),
                           blue: CGFloat(b1 + (b2 - b1) * percentage),
                           alpha: CGFloat(a1 + (a2 - a1) * percentage))
        }
    }
}

użycie:-

let colorRed = UIColor.red
let colorBlue = UIColor.blue

let colorOutput = colorRed.toColor(colorBlue, percentage: 50)

Wynik

Tutaj wpisz opis obrazka

 31
Author: ramchandra n,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-13 12:22:56

W przypadku, gdy gradient jest bardziej złożony niż gradient 2 kolorów, możesz rozważyć narysowanie CGGradientRef do tymczasowego CGImageRef i bezpośrednio odczytać wartości RGBA z bufora obrazu.

Oto coś, co miałem do czynienia z 5 stopami gradientu i kolorami:

    CGFloat tmpImagewidth = 1000.0f; // Make this bigger or smaller if you need more or less resolution (number of different colors).
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    // create a gradient
    CGFloat locations[] = { 0.0,
        0.35,
        0.55,
        0.8,
        1.0 };
    NSArray *colors = @[(__bridge id) [UIColor redColor].CGColor,
                        (__bridge id) [UIColor greenColor].CGColor,
                        (__bridge id) [UIColor blueColor].CGColor,
                        (__bridge id) [UIColor yellowColor].CGColor,
                        (__bridge id) [UIColor redColor].CGColor,
                        ];
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
    CGPoint startPoint = CGPointMake(0, 0);
    CGPoint endPoint = CGPointMake(tmpImagewidth, 0);

    // create a bitmap context to draw the gradient to, 1 pixel high.
    CGContextRef context = CGBitmapContextCreate(NULL, tmpImagewidth, 1, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);

    // draw the gradient into it
    CGContextAddRect(context, CGRectMake(0, 0, tmpImagewidth, 1));
    CGContextClip(context);
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);

    // Get our RGB bytes into a buffer with a couple of intermediate steps...
    //      CGImageRef -> CFDataRef -> byte array
    CGImageRef cgImage = CGBitmapContextCreateImage(context);
    CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
    CFDataRef pixelData = CGDataProviderCopyData(provider);

    // cleanup:
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(cgImage);
    CGContextRelease(context);

    const UInt8* data = CFDataGetBytePtr(pixelData);

    // we got all the data we need.
    // bytes in the data buffer are a succession of R G B A bytes

    // For instance, the color of the point 27% in our gradient is:
    CGFloat x = tmpImagewidth * .27;
    int pixelIndex = (int)x * 4; // 4 bytes per color
    UIColor *color = [UIColor colorWithRed:data[pixelIndex + 0]/255.0f
                                     green:data[pixelIndex + 1]/255.0f
                                      blue:data[pixelIndex + 2]/255.0f
                                     alpha:data[pixelIndex + 3]/255.0f];

    // done fetching color data, finally release the buffer
    CGDataProviderRelease(provider);

Nie mówię, że jest to lepsze niż "sposób matematyczny" w odpowiedzi powyżej, na pewno istnieje podatek od pamięci i procesora, który idzie do produkcji tymczasowego obrazu. Zaletą tego jest jednak to, że kod złożoność pozostaje taka sama bez względu na to, ile stopni gradientu potrzebujesz...

 3
Author: Sebastien Windal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-21 14:06:15

Chciałbym rozszerzyć / zmodyfikować odpowiedź @ Sebastien Windals, ponieważ jego odpowiedź działa świetnie w moim przypadku użycia, ale może być jeszcze bardziej ulepszona poprzez pobranie pixelData bezpośrednio z kontekstu(zobacz Get Pixel data as array from UIImage / CGImage in swift ).

Zaimplementowałem jego odpowiedź w języku swift, dlatego zmiany są również napisane w języku swift.

Wystarczy przekazać tablicę Int do kontekstu przed narysowaniem gradientu. Tablica zostanie następnie wypełniona danymi pikselowymi podczas rysowania kontekst.

let dataSize = tmpImagewidth * 1 * 4
var pixelData = [UInt8](repeating: 0, count: Int(dataSize))


let context = CGContext(data: &pixelData, width: Int(tmpImagewidth), height: 1, bitsPerComponent: 8, bytesPerRow: 4 * Int(tmpImagewidth), space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)

Następnie możemy pominąć tworzenie obrazu najpierw odczytać pixelData stamtąd.

 1
Author: LaleMuc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-03 10:07:26

Dzięki @ ramchandra-N zaimplementowałem rozszerzenie, aby uzyskać pośredni kolor z tablicy kolorów w procentach

extension Array where Element: UIColor {
    func intermediate(percentage: CGFloat) -> UIColor {
        let percentage = Swift.max(Swift.min(percentage, 100), 0) / 100
        switch percentage {
        case 0: return first ?? .clear
        case 1: return last ?? .clear
        default:
            let approxIndex = percentage / (1 / CGFloat(count - 1))
            let firstIndex = Int(approxIndex.rounded(.down))
            let secondIndex = Int(approxIndex.rounded(.up))
            let fallbackIndex = Int(approxIndex.rounded())

            let firstColor = self[firstIndex]
            let secondColor = self[secondIndex]
            let fallbackColor = self[fallbackIndex]

            var (r1, g1, b1, a1): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
            var (r2, g2, b2, a2): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
            guard firstColor.getRed(&r1, green: &g1, blue: &b1, alpha: &a1) else { return fallbackColor }
            guard secondColor.getRed(&r2, green: &g2, blue: &b2, alpha: &a2) else { return fallbackColor }

            let intermediatePercentage = approxIndex - CGFloat(firstIndex)
            return UIColor(red: CGFloat(r1 + (r2 - r1) * intermediatePercentage),
                           green: CGFloat(g1 + (g2 - g1) * intermediatePercentage),
                           blue: CGFloat(b1 + (b2 - b1) * intermediatePercentage),
                           alpha: CGFloat(a1 + (a2 - a1) * intermediatePercentage))
        }
    }
}

Możesz go użyć, aby uzyskać kolor pośredni między dwoma lub więcej kolorami:

let color = [.green, .yellow, .red].intermediate(percentage: 70)
 1
Author: Nikaaner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-30 23:28:56