Task: Color wheel


C++: 
#include "colorwheelwidget.h"
#include <QPainter>
#include <QPaintEvent>
#include <cmath>

namespace {

QColor hsvToRgb(int h, double s, double v) {
    double hp = h/60.0;
    double c = s * v;
    double x = c * (1 - std::abs(std::fmod(hp, 2) - 1));
    double m = v - c;
    double r = 0, g = 0, b = 0;
    if (hp <= 1) {
        r = c;
        g = x;
    } else if (hp <= 2) {
        r = x;
        g = c;
    } else if (hp <= 3) {
        g = c;
        b = x;
    } else if (hp <= 4) {
        g = x;
        b = c;
    } else if (hp <= 5) {
        r = x;
        b = c;
    } else {
        r = c;
        b = x;
    }
    r += m;
    g += m;
    b += m;
    return QColor(r * 255, g * 255, b * 255);
}

}

ColorWheelWidget::ColorWheelWidget(QWidget *parent)
    : QWidget(parent) {
    setWindowTitle(tr("Color Wheel"));
    resize(400, 400);
}

void ColorWheelWidget::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    const QColor backgroundColor(0, 0, 0);
    const QColor white(255, 255, 255);
    painter.fillRect(event->rect(), backgroundColor);
    const int margin = 10;
    const double diameter = std::min(width(), height()) - 2*margin;
    QPointF center(width()/2.0, height()/2.0);
    QRectF rect(center.x() - diameter/2.0, center.y() - diameter/2.0,
                diameter, diameter);
    for (int angle = 0; angle < 360; ++angle) {
        QColor color(hsvToRgb(angle, 1.0, 1.0));
        QRadialGradient gradient(center, diameter/2.0);
        gradient.setColorAt(0, white);
        gradient.setColorAt(1, color);
        QBrush brush(gradient);
        QPen pen(brush, 1.0);
        painter.setPen(pen);
        painter.setBrush(brush);
        painter.drawPie(rect, angle * 16, 16);
    }
}


C#: 

public MainWindow()
{
    InitializeComponent();
    RenderOptions.SetBitmapScalingMode(imgMain, BitmapScalingMode.HighQuality);
    imgMain.Source = new WriteableBitmap(480, 480, 96, 96, PixelFormats.Bgr32, null);
    
    DrawHue(100);
}

void DrawHue(int saturation)
{
    var bmp = (WriteableBitmap)imgMain.Source;

    int centerX = (int)bmp.Width / 2;
    int centerY = (int)bmp.Height / 2;
    int radius = Math.Min(centerX, centerY);
    int radius2 = radius - 40;

    bmp.Lock();
    unsafe{
        var buf = bmp.BackBuffer;
        IntPtr pixLineStart;
        for(int y=0; y < bmp.Height; y++){
            pixLineStart = buf + bmp.BackBufferStride * y;
            double dy = (y - centerY);
            for(int x=0; x < bmp.Width; x++){
                double dx = (x - centerX);
                double dist = Math.Sqrt(dx * dx + dy * dy);
                if (radius2 <= dist && dist <= radius) {
                    double theta = Math.Atan2(dy, dx);
                    double hue = (theta + Math.PI) / (2.0 * Math.PI);
                    *((int*)(pixLineStart + x * 4)) = HSB_to_RGB((int)(hue * 360), saturation, 100);
                }
            }
        }
    }
    bmp.AddDirtyRect(new Int32Rect(0, 0, 480, 480));
    bmp.Unlock();
}

static int HSB_to_RGB(int h, int s, int v)
{
    var rgb = new int[3];

    var baseColor = (h + 60) % 360 / 120;
    var shift = (h + 60) % 360 - (120 * baseColor + 60 );
    var secondaryColor = (baseColor + (shift >= 0 ? 1 : -1) + 3) % 3;

    
    rgb[baseColor] = 255;
    rgb[secondaryColor] = (int) ((Math.Abs(shift) / 60.0f) * 255.0f);

    
    for (var i = 0; i < 3; i++)
        rgb[i] += (int) ((255 - rgb[i]) * ((100 - s) / 100.0f));

    
    for (var i = 0; i < 3; i++)
        rgb[i] -= (int) (rgb[i] * (100-v) / 100.0f);

    return RGB2int(rgb[0], rgb[1], rgb[2]);
}

public static int RGB2int(int r, int g, int b) => r << 16 | g << 8 | b;