Joe G
Joe G

Reputation: 109

SFML Color Picker Issue

I am working on a color picker for my image generation program, and im trying to make a color picker box like this:

Goal

Here is the code I have so far:

RenderWindow colorPicker(VideoMode(255, 255), "Color Picker");
vector<RectangleShape> pixels(255*255);

int j, red, gb = 0;
for (int i = 0; i < pixels.size(); i++) {
    pixels[i] = RectangleShape(Vector2f(1, 1));
    if (i != 0) {
        j = i / 255;
        red = 255 - j;
        gb = i - (255 * j) - j;

        if (gb > red) { gb -= j; } else if (gb < 0) { gb = 0; }

        pixels[i].setFillColor(Color(red, gb, gb));
        pixels[i].setPosition(i - (255 * j), j);
    }
    else {
        pixels[i].setFillColor(Color::Red);
        pixels[i].setPosition(0, 0);
    }
}

And this is what it returns:

Color Picker

The part I am confused about is why am I getting that line through the center? all the values seem right for the most part, but the line through the center makes no sense to me.

Upvotes: 0

Views: 1906

Answers (2)

Hazel
Hazel

Reputation: 1

As i can't comment on Mario's post, here is a reply to the topic:

When the window is stretched, the gradient gets filled with extra lines going in a perpendicular direction. This is probably unintentional but worth mentioning.

Upvotes: 0

Mario
Mario

Reputation: 36567

As mentioned in the comments, your problem is most likely related due to unintentional rounding (since you're using integers in your calculations, which won't work for something like this unless you scale everything before doing the math or round carefully.

Overall, I'd suggest you use a sf::VertexArray or a shader to actually render and display your colors, because directly modifying a texture is very expensive (since you'll have to send the whole texture every time).

Here's a quick example I came up with to do the display using a sf::VertexArray, since the overall implementation is very similar to what you've done so far. Also it's important to know that what you're trying to do is actually implementing a color picker based on the HSV color model. While your current implementation works for red, it might be tricky for other colors.

The x and y coordinates inside your "texture" basically represent the saturation and value of your colors, while the hue is selected outside (in your case locked to 0°/red).

My example implementation is based on the German Wikipedia article for HSV, which differs a bit from the English version (since there are multiple ways to get there). Overall I found it easier to read/implement.

#include <SFML/Graphics.hpp>

void modulate(sf::VertexArray &points, double hue) {
    // First, Let's "sanitize" inputs a bit.
    // Don't accept negative numbers.
    if (hue < 0)
        hue = 0;
    // Lazy overflow by subtracting the integer portion of the number.
    else if (hue > 1)
        hue -= static_cast<int>(hue);

    // Now iterate over all "pixels" and upate their colors.
    for (unsigned int y = 0; y <= 255; ++y) {
        for (unsigned int x = 0; x <= 255; ++x) {
            // "Calculate" our missing HSV components with ranges from 0 to 1.
            const double s = x / 255.; // x is our saturation
            const double v = y / 255.; // y is our value

            // Pick the correct case based on our position on the color wheel.
            const int cs = hue * 6;

            // Calculate some helper values used in our cases below.
            const double f = hue * 6 - cs;
            const double p = v * (1 - s);
            const double q = v * (1 - s * f);
            const double t = v * (1 - s * (1 - f));

            switch (cs) {
                case 0:
                case 6:
                    points[y * 256 + x].color = sf::Color(v * 255, t * 255, p * 255);
                    break;
                case 1:
                    points[y * 256 + x].color = sf::Color(q * 255, v * 255, p * 255);
                    break;
                case 2:
                    points[y * 256 + x].color = sf::Color(p * 255, v * 255, t * 255);
                    break;
                case 3:
                    points[y * 256 + x].color = sf::Color(p * 255, q * 255, v * 255);
                    break;
                case 4:
                    points[y * 256 + x].color = sf::Color(t * 255, p * 255, v * 255);
                    break;
                case 5:
                    points[y * 256 + x].color = sf::Color(v * 255, p * 255, q * 255);
                    break;
            }
        }
    }
}

int main(int argc, char **argv) {
    // Setup a render window
    sf::RenderWindow window(sf::VideoMode(256, 256), "Color Picker");

    // We're using a clock to change the hue dynamically.
    sf::Clock timer;

    // This vertex array is going to be used for drawing.
    // It includes one vertex/point/pixel per color.
    sf::VertexArray colors(sf::Points, 256 * 256);
    for (unsigned int y = 0; y <= 255; ++y) {
        for (unsigned int x = 0; x <= 255; ++x) {
            sf::Vertex &vertex(colors[y * 256 + x]);

            // Note that I "flip" the displayed texture here, by subtracting
            // x/y from 255 rather than just using x/y, but that's really just
            // to get the same orientation that you have.
            vertex.position.x = 255 - x;
            vertex.position.y = 255 - y;
        }
    }

    while (window.isOpen()) {
        sf::Event event;
        // Your typical event loop
        while (window.pollEvent(event)) {
            switch (event.type) {
                case sf::Event::Closed:
                    window.close();
                    break;
            }
        }

        // Update our colors based on the time passed.
        // I want to cycle all hues in 5 seconds, so dividing the timer.
        modulate(colors, timer.getElapsedTime().asSeconds() / 5);

        // Draw and display our colors
        window.clear();
        window.draw(colors);
        window.display();
    }

    return 0;
}

When running this example you'll get the colors displayed in a small window, cycling once every 5 seconds (presented in all its mangled GIF beauty):

Example Rendering

Upvotes: 1

Related Questions