我正在尝试将一个函数转换为从 Delphi 到 C# 创建 HSV Circle,但结果不正确。
我的目标是为 Windows Phone 7 做一个应用程序,我只使用 WP7.1 SDK 和WriteableBitmapEx
库。
德尔福代码:
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代码的结果:
我的 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# 结果:
我做错了什么?
提前致谢。
用解决方案编辑
借助@Aybe 的出色回答,我可以从 HSV 地狱中制作一个工作版本。
这是 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);
}
现在,新的结果:
谢谢!