Convert HSV circle code from Delphi to C #

I am trying to convert a function to create an HSV circle from Delphi to C #, but the result does not match the correct path.

My goal is to make an application for Windows Phone 7, and I use only the WP7.1 SDK, plus WriteableBitmapEx .

Delphi Code:

 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 code results:

delphi img

My C # code:

  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); } 

My C # result:

csharp

What am I doing wrong?

Thanks in advance.

EDIT DECISION

With a great answer from @Aybe, I could make a working version from HSV whell.

This is the working code for the 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); } 

And now a new result:

okimg

Thanks!

+6
source share
2 answers

After spending an hour or so, I learned a few things in this process ...

Now the code: (works for any size)

This is HSL, but I gave you a url where you have other algos.

 using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; namespace ColorWheel { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void BuildWheel() { var width = 1024; var height = width; var cx = width/2; var cy = height/2; var colors = new int[width*height]; var gray = Colors.Gray.ToBgr32(); for (int index = 0; index < colors.Length; index++) colors[index] = gray; var radius = cx; var radiusSquared = radius*radius; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { var x = j - cx; var y = i - cy; var distanceSquared = (double) x*x + y*y; if (distanceSquared <= radiusSquared) // In circle { var h = Math.Atan2(x, y).ToDegrees() + 180.0d; // Angle var s = 1.0d; var l = (1.0d - ((1.0d/radiusSquared)*distanceSquared)); // 1 - (distance normalized) var hsl = new HSL((float) h, (float) s, (float) l); var rgb = RGB.FromHsl(hsl.H, hsl.S, hsl.L); colors[i*width + j] = rgb.R << 16 | rgb.G << 8 | rgb.B; } } } var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr32, null); bitmap.WritePixels(new Int32Rect(0, 0, width, height), colors, width*4, 0); image.Source = bitmap; } private void Window_Loaded(object sender, RoutedEventArgs e) { BuildWheel(); } } public static class Helpers { public static double ToDegrees(this double radians) { return radians*57.2957795130823; // radians * (180.0d / Math.PI) } public static double ToRadians(this double degrees) { return degrees*0.0174532925199433; // degrees * (Math.PI / 180.0d) } } public static class ColorExtensions { public static Color FromBgr32(this Int32 color) { return Color.FromRgb((byte) ((color & 0xFF0000) >> 16), (byte) ((color & 0xFF00) >> 8), (byte) (color & 0xFF)); } public static int ToBgr32(this Color color) { return color.R << 16 | color.G << 8 | color.B; } } /// <summary> /// Represents a color in an HSL space. /// </summary> [StructLayout(LayoutKind.Sequential)] public struct HSL { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double _h; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double _s; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double _l; /// <summary> /// Returns the fully qualified type name of this instance. /// </summary> /// <returns> A <see cref="T:System.String" /> containing a fully qualified type name. </returns> /// <filterpriority>2</filterpriority> public override string ToString() { return string.Format("H: {0}, S: {1}, L: {2}", _h, _s, _l); } /// <summary> /// Create a new instance of <see cref="HSL" /> . /// </summary> /// <param name="h"> Value of <see cref="H" /> component. </param> /// <param name="s"> Value of <see cref="S" /> component. </param> /// <param name="l"> Value of <see cref="L" /> component. </param> public HSL(double h, double s, double l) { _h = h; _s = s; _l = l; } /// <summary> /// Gets the value of the hue component. /// </summary> public double H { get { return _h; } } /// <summary> /// Gets the value of the saturation component. /// </summary> public double S { get { return _s; } } /// <summary> /// Gets the value of the lightness component. /// </summary> public double L { get { return _l; } } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (obj.GetType() != typeof (HSL)) return false; return Equals((HSL) obj); } public bool Equals(HSL other) { return other._h.Equals(_h) && other._l.Equals(_l) && other._s.Equals(_s); } /// <summary> /// Create a new instance of <see cref="HSL" /> , from RGB values. /// </summary> /// <param name="red"> Value of the red component. </param> /// <param name="green"> Value of the green component. </param> /// <param name="blue"> Value of the blue component. </param> /// <returns> <see cref="HSL" /> instance created. </returns> public static HSL FromRGB(byte red, byte green, byte blue) { var r1 = red/255.0d; var g1 = green/255.0d; var b1 = blue/255.0d; var min = Math.Min(r1, Math.Min(g1, b1)); var max = Math.Max(r1, Math.Max(g1, b1)); var l = (max + min)/2.0d; var s = 0.0d; var h = 0.0d; if (min == max) { h = 0.0d; s = 0.0d; } else { if (l < 0.5d) { s = (max - min)/(max + min); } else if (l >= 0.5d) { s = (max - min)/(2.0d - max - min); } if (r1 == max) { h = (g1 - b1)/(max - min); } else if (g1 == max) { h = 2.0d + (b1 - r1)/(max - min); } else if (b1 == max) { h = 4.0d + (r1 - g1)/(max - min); } } h *= 60.0d; if (h < 0.0d) h += 360.0d; return new HSL(h, s, l); } /// <summary> /// Returns the hash code for this instance. /// </summary> /// <returns> A 32-bit signed integer that is the hash code for this instance. </returns> /// <filterpriority>2</filterpriority> public override int GetHashCode() { unchecked { var result = _h.GetHashCode(); result = (result*397) ^ _l.GetHashCode(); result = (result*397) ^ _s.GetHashCode(); return result; } } public static BitmapSource GetHslPalette(int width = 360, int height = 100) { // Creates an HSL palette image like in Photoshop, etc ... var pixels = new int[width*height]; const double saturation = 1.0d; for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var hue = (1.0d/width)*x*360.0d; var lightness = 1.0d - ((1.0f/height)*y); var rgb = RGB.FromHsl(hue, saturation, lightness); pixels[y*width + x] = 0xFF << 24 | rgb.R << 16 | rgb.G << 8 | rgb.B; } } return BitmapSource.Create(width, height, 96, 96, PixelFormats.Pbgra32, null, pixels, width*4); } public static bool operator ==(HSL left, HSL right) { return left.Equals(right); } public static bool operator !=(HSL left, HSL right) { return !left.Equals(right); } } /// <summary> /// Represents a color in an RGB space. /// </summary> [StructLayout(LayoutKind.Sequential)] public struct RGB { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte _r; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte _g; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte _b; /// <summary> /// Create a new instance of <see cref="RGB" /> . /// </summary> /// <param name="r"> Value of red component. </param> /// <param name="g"> Value of green component. </param> /// <param name="b"> Value of blue component. </param> public RGB(byte r, byte g, byte b) { _r = r; _g = g; _b = b; } /// <summary> /// Returns the fully qualified type name of this instance. /// </summary> /// <returns> A <see cref="T:System.String" /> containing a fully qualified type name. </returns> /// <filterpriority>2</filterpriority> public override string ToString() { return string.Format("R: {0}, G: {1}, B: {2}", _r, _g, _b); } /// <summary> /// Gets the value of the red component. /// </summary> public byte R { get { return _r; } } /// <summary> /// Gets the value of the green component. /// </summary> public byte G { get { return _g; } } /// <summary> /// Gets the value of the blue component. /// </summary> public byte B { get { return _b; } } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (obj.GetType() != typeof (RGB)) return false; return Equals((RGB) obj); } public bool Equals(RGB other) { return other._b == _b && other._g == _g && other._r == _r; } /// <summary> /// Create a new instance of <see cref="RGB" /> , from HSL values. /// </summary> /// <param name="hue"> Hue, from 0.0 to 360.0. </param> /// <param name="saturation"> Saturation, from 0.0 to 1.0. </param> /// <param name="lightness"> Lightness, from 0.0 to 1.0. </param> /// <returns> <see cref="RGB" /> instance created. </returns> public static RGB FromHsl(double hue, double saturation, double lightness) { if (hue < 0.0d || hue > 360.0d) throw new ArgumentOutOfRangeException("hue"); if (saturation < 0.0d || saturation > 1.0d) throw new ArgumentOutOfRangeException("saturation"); if (lightness < 0.0d || lightness > 1.0d) throw new ArgumentOutOfRangeException("lightness"); if (saturation == 0.0d) { var b1 = (byte) (lightness*255); return new RGB(b1, b1, b1); } var t2 = 0.0d; if (lightness < 0.5d) t2 = lightness*(1.0d + saturation); else if (lightness >= 0.5d) t2 = lightness + saturation - lightness*saturation; var t1 = 2.0d*lightness - t2; var h = hue/360.0d; var tr = h + 1.0d/3.0d; var tg = h; var tb = h - 1.0d/3.0d; tr = tr < 0.0d ? tr + 1.0d : tr > 1.0d ? tr - 1.0d : tr; tg = tg < 0.0d ? tg + 1.0d : tg > 1.0d ? tg - 1.0d : tg; tb = tb < 0.0d ? tb + 1.0d : tb > 1.0d ? tb - 1.0d : tb; double r; if (6.0d*tr < 1.0d) r = t1 + (t2 - t1)*6.0d*tr; else if (2.0d*tr < 1.0d) r = t2; else if (3.0d*tr < 2.0d) r = t1 + (t2 - t1)*((2.0d/3.0d) - tr)*6.0d; else r = t1; double g; if (6.0d*tg < 1.0d) g = t1 + (t2 - t1)*6.0d*tg; else if (2.0d*tg < 1.0d) g = t2; else if (3.0d*tg < 2.0d) g = t1 + (t2 - t1)*((2.0d/3.0d) - tg)*6.0d; else g = t1; double b; if (6.0d*tb < 1.0d) b = t1 + (t2 - t1)*6.0d*tb; else if (2.0d*tb < 1.0d) b = t2; else if (3.0d*tb < 2.0d) b = t1 + (t2 - t1)*((2.0d/3.0d) - tb)*6.0d; else b = t1; return new RGB((byte) (r*255), (byte) (g*255), (byte) (b*255)); } /// <summary> /// Returns the hash code for this instance. /// </summary> /// <returns> A 32-bit signed integer that is the hash code for this instance. </returns> /// <filterpriority>2</filterpriority> public override int GetHashCode() { unchecked { var result = _b.GetHashCode(); result = (result*397) ^ _g.GetHashCode(); result = (result*397) ^ _r.GetHashCode(); return result; } } public static bool operator ==(RGB left, RGB right) { return left.Equals(right); } public static bool operator !=(RGB left, RGB right) { return !left.Equals(right); } } } 
+4
source

Not default bit depth on 16 bit WP7 devices? On my Omnia 7 it is. There is a registry parameter that controls this parameter, but may not be available on all phones, instead you should smooth your colors from 24-bit to 16-bit, then it will be available on all phones. I suggest you take a look at Microsoft.Xna.Framework.Graphics.PackedVector , there are constructors like BGR565 that accept float as input. And for all your color conversion needs: http://www.easyrgb.com/ , almost all algorithms exist for converting color spaces.

0
source

Source: https://habr.com/ru/post/913991/


All Articles