Преобразование кода круга HSV из Delphi в C#

Я пытаюсь преобразовать функцию для создания круга HSV из Delphi в C #, но результат не в правильном пути.

Моя цель — сделать приложение для Windows Phone 7, и я использую только SDK WP7.1 плюс библиотеку WriteableBitmapEx.

Код Delphi:

FUNCTION CreateHueSaturationCircle(CONST size: INTEGER; CONST ValueLevel: INTEGER; CONST BackgroundColor: TColor): TBitmap;
VAR
  dSquared: INTEGER;
  H,S,V: INTEGER;
  i: INTEGER;
  j: INTEGER;
  Radius: INTEGER;
  RadiusSquared: INTEGER;
  row: pRGBTripleArray;
  X: INTEGER;
  Y: INTEGER;
BEGIN
    RESULT := TBitmap.Create;
    RESULT.PixelFormat := pf24bit;
    RESULT.Width := size;
    RESULT.Height := size;

    // Fill with background color
    RESULT.Canvas.Brush.Color := BackGroundColor;
    RESULT.Canvas.FillRect(RESULT.Canvas.ClipRect);

    Radius := size DIV 2;
    RadiusSquared := Radius * Radius;

    V := ValueLevel;
    FOR j := 0 TO RESULT.Height - 1 DO
    BEGIN
        Y := Size - 1 - j - Radius;  {Center is Radius offset}
        row := RESULT.Scanline[Size - 1 - j];
        FOR i := 0 TO RESULT.Width - 1 DO
        BEGIN
            X := i - Radius;
            dSquared := X * X + Y * Y;
            IF dSquared <= RadiusSquared THEN
            BEGIN
                S := ROUND((255 * SQRT(dSquared)) / Radius);
                H := ROUND(180 * (1 + ArcTan2(X, Y) / PI));   // 0..360 degrees
                // Shift 90 degrees so H=0 (red) occurs along "X" axis
                H := H + 90;
                IF H > 360 THEN
                    H := H - 360;
                row[i] := HSVtoRGBTriple(H,S,V)
            END
        END;
    END;
END;

FUNCTION HSVtoRGBTriple(CONST H,S,V: INTEGER): TRGBTriple;
CONST
  divisor: INTEGER = 255 * 60;
VAR
  f: INTEGER;
  hTemp: INTEGER;
  p,q,t: INTEGER;
  VS: INTEGER;
BEGIN
    IF S = 0 THEN
        RESULT := RGBtoRGBTriple(V, V, V)  // achromatic:  shades of gray
    ELSE
    BEGIN                              // chromatic color
        IF H = 360 THEN
            hTemp := 0
        ELSE
            hTemp := H;
        f := hTemp MOD 60;     // f is IN [0, 59]
        hTemp := hTemp DIV 60;     // h is now IN [0,6)
        VS := V * S;
        p := V - VS DIV 255;                 // p = v * (1 - s)
        q := V - (VS*f) DIV divisor;         // q = v * (1 - s*f)
        t := V - (VS*(60 - f)) DIV divisor;  // t = v * (1 - s * (1 - f))
        CASE hTemp OF
            0:   RESULT := RGBtoRGBTriple(V, t, p);
            1:   RESULT := RGBtoRGBTriple(q, V, p);
            2:   RESULT := RGBtoRGBTriple(p, V, t);
            3:   RESULT := RGBtoRGBTriple(p, q, V);
            4:   RESULT := RGBtoRGBTriple(t, p, V);
            5:   RESULT := RGBtoRGBTriple(V, p, q);
        ELSE
            RESULT := RGBtoRGBTriple(0,0,0)  // should never happen;
                                          // avoid compiler warning
        END
    END
END 

Результаты кода Delphi:

delphi img

Мой код C #:

    public struct HSV
    {
        public float h;
        public float s;
        public float v;
    }


    public void createHsvCircle()
    {
        int size = 300;

        wb = new WriteableBitmap(size, size);

        wb.Clear(GraphicsUtils.WhiteColor);

        int radius = size / 2;
        int radiusSquared = radius * radius;

        int x;
        int y;
        int dSquared;

        HSV hsv;
        hsv.v = 255F;

        for (int j = 0; j < size; j++)
        {
            y = size - 1 - j - radius;

            for (int i = 0; i < size; i++)
            {
                x = i - radius;
                dSquared = x * x + y * y;

                if (dSquared <= radiusSquared)
                {
                    hsv.s = (float) Math.Round((255 * Math.Sqrt(dSquared)) / radius);

                    hsv.h = (float) Math.Round(180 * (1 + Math.Atan2(y, x) / Math.PI));

                    hsv.h += 90;
                    if (hsv.h > 360)
                    {
                        hsv.h -= 360;
                    }

                    Color color = GraphicsUtils.HsvToRgb(hsv);

                    wb.SetPixel(i, j, color);
                }
            }
        }

        wb.Invalidate();

    }

    public static Color HsvToRgb(float h, float s, float v)
    {
        h = h / 360;
        if (s > 0)
        {
            if (h >= 1)
                h = 0;
            h = 6 * h;
            int hueFloor = (int)Math.Floor(h);
            byte a = (byte)Math.Round(RGB_MAX * v * (1.0 - s));
            byte b = (byte)Math.Round(RGB_MAX * v * (1.0 - (s * (h - hueFloor))));
            byte c = (byte)Math.Round(RGB_MAX * v * (1.0 - (s * (1.0 - (h - hueFloor)))));
            byte d = (byte)Math.Round(RGB_MAX * v);

            switch (hueFloor)
            {
                case 0: return Color.FromArgb(RGB_MAX, d, c, a);
                case 1: return Color.FromArgb(RGB_MAX, b, d, a);
                case 2: return Color.FromArgb(RGB_MAX, a, d, c);
                case 3: return Color.FromArgb(RGB_MAX, a, b, d);
                case 4: return Color.FromArgb(RGB_MAX, c, a, d);
                case 5: return Color.FromArgb(RGB_MAX, d, a, b);
                default: return Color.FromArgb(RGB_MAX, 0, 0, 0);
            }
        }
        else
        {
            byte d = (byte)(v * RGB_MAX);
            return Color.FromArgb(255, d, d, d);
        }
    }

    public static Color HsvToRgb(HSV hsv)
    {
        return HsvToRgb(hsv.h, hsv.s, hsv.v);
    }       

Мой результат c #:

csharp

Что я делаю неправильно?

Заранее спасибо.

ОТРЕДАКТИРОВАНО С РЕШЕНИЕМ

Благодаря отличному ответу @Aybe я мог сделать рабочую версию из HSV whell.

Это рабочий код для WP7 SDK:

    public const double PI = 3.14159265358979323846264338327950288d;

    public void createHsvCircle(double value = 1.0d)
    {
        if (value < 0.0d || value > 1.0d)
            throw new ArgumentOutOfRangeException("value");

        var size = 1024;

        wb = new WriteableBitmap(size, size);

        // fill with white.
        var white = Colors.White;
        for (int index = 0; index < wb.Pixels.Length; index++)
        {
            wb.Pixels[index] = 0xFF << 24 | white.R << 16 | white.G << 8 | white.B;
        }

        var cx = size / 2;
        var cy = size / 2;
        var radius = cx;
        var radiusSquared = radius * radius;
        for (int i = 0; i < size; i++)
        {
            for (int j = 0; j < size; j++)
            {
                var x = i - cx;
                var y = j - cy;
                var distance = (double)x * x + y * y;
                if (distance <= radiusSquared) // In circle
                {
                    var angle = 180.0d * (1 + Math.Atan2(x, y) / PI);

                    // shift 90 degrees so H=0 (red) occurs along "X" axis
                    angle += 90.0d;
                    if (angle > 360.0d)
                    {
                        angle -= 360.0d;
                    }

                    var hue = angle / 360.0d; // hue must be into 0 to 1.
                    var saturation = Math.Sqrt(distance) / radius; // saturation must be into 0 to 1.

                    var hsv = new HSV(hue, saturation, value);
                    var rgb = RGB.FromHsv(hsv.H, hsv.S, hsv.V);

                    wb.Pixels[j * size + i] = 0xFF << 24 | rgb.R << 16 | rgb.G << 8 | rgb.B;
                }
            }

        }
        wb.Invalidate();
    }

    public static RGB FromHsv(double hue, double saturation, double value)
    {
        if (hue < 0.0d || hue > 1.0d)
            throw new ArgumentOutOfRangeException("hue");
        if (saturation < 0.0d || saturation > 1.0d)
            throw new ArgumentOutOfRangeException("saturation");
        if (value < 0.0d || value > 1.0d)
            throw new ArgumentOutOfRangeException("value");

        if (saturation == 0.0d)
        {
            var b1 = (byte)(value * 255);
            return new RGB(b1, b1, b1);
        }

        double r;
        double g;
        double b;

        var h = hue * 6.0d;
        if (h == 6.0d)
        {
            h = 0.0d;
        }

        int i = (int)Math.Floor(h);

        var v1 = value * (1.0d - saturation);
        var v2 = value * (1.0d - saturation * (h - i));
        var v3 = value * (1.0d - saturation * (1.0d - (h - i)));

        switch (i)
        {
            case 0:
                r = value;
                g = v3;
                b = v1;
                break;
            case 1:
                r = v2;
                g = value;
                b = v1;
                break;
            case 2:
                r = v1;
                g = value;
                b = v3;
                break;
            case 3:
                r = v1;
                g = v2;
                b = value;
                break;
            case 4:
                r = v3;
                g = v1;
                b = value;
                break;
            default:
                r = value;
                g = v1;
                b = v2;
                break;
        }

        r = r * 255.0d;
        if (r > 255.0d)
        {
            r = 255.0d;
        }
        g = g * 255.0d;
        if (g > 255.0d)
        {
            g = 255.0d;
        }
        b = b * 255.0d;
        if (b > 255.0d)
        {
            b = 255.0d;
        }

        return new RGB((byte)r, (byte)g, (byte)b);
    }

А теперь новый результат:

okimg

Спасибо!

6
задан Rafael Cruz 25 April 2012 в 23:50
поделиться