Skip to content

Commit 17d5495

Browse files
committed
Improvements, formatting
1 parent 05a755b commit 17d5495

4 files changed

Lines changed: 304 additions & 293 deletions

File tree

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
using System;
2+
using System.Globalization;
3+
using Cairo;
4+
5+
namespace Pinta.Core.Extensions;
6+
7+
public static class ColorExtensions
8+
{
9+
/// <summary>
10+
/// Returns the color value as a string in hex color format.
11+
/// </summary>
12+
/// <param name="addAlpha">If false, returns in format "RRGGBB" (Alpha will not affect result).<br/>
13+
/// Otherwise, returns in format "RRGGBBAA".</param>
14+
public static String ToHex (this Color c, bool addAlpha = true)
15+
{
16+
int r = Convert.ToInt32 (c.R * 255.0);
17+
int g = Convert.ToInt32 (c.G * 255.0);
18+
int b = Convert.ToInt32 (c.B * 255.0);
19+
int a = Convert.ToInt32 (c.A * 255.0);
20+
21+
if (addAlpha)
22+
return $"{r:X2}{g:X2}{b:X2}{a:X2}";
23+
else
24+
return $"{r:X2}{g:X2}{b:X2}";
25+
}
26+
27+
/// <summary>
28+
/// Returns a color from an RGBA hex color. Accepts the following formats:<br/>
29+
/// RRGGBBAA<br/>
30+
/// RRGGBB<br/>
31+
/// RGB (Expands to RRGGBB)<br/>
32+
/// RGBA (Expands to RRGGBBAA)<br/>
33+
/// Will accept leading #.
34+
/// </summary>
35+
/// <param name="hex">Hex color as a string.</param>
36+
/// <returns>Resulting color. If null, the method could not parse it.</returns>
37+
public static Color? FromHex (String hex)
38+
{
39+
if (hex[0] == '#') {
40+
Console.WriteLine (hex);
41+
hex = hex.Remove (0, 1);
42+
Console.WriteLine (hex);
43+
}
44+
45+
// handle shorthand hex
46+
if (hex.Length == 3)
47+
hex = $"{hex[0]}{hex[0]}{hex[1]}{hex[1]}{hex[2]}{hex[2]}";
48+
if (hex.Length == 4)
49+
hex = $"{hex[0]}{hex[0]}{hex[1]}{hex[1]}{hex[2]}{hex[2]}{hex[3]}{hex[3]}";
50+
51+
if (hex.Length != 6 && hex.Length != 8)
52+
return null;
53+
try {
54+
int r = int.Parse (hex.Substring (0, 2), NumberStyles.HexNumber);
55+
int g = int.Parse (hex.Substring (2, 2), NumberStyles.HexNumber);
56+
int b = int.Parse (hex.Substring (4, 2), NumberStyles.HexNumber);
57+
int a = 255;
58+
if (hex.Length > 6)
59+
a = int.Parse (hex.Substring (5, 2), NumberStyles.HexNumber);
60+
61+
Console.WriteLine ($"{r}, {g}, {b}, {a}");
62+
63+
return new Color ().SetRgba (r / 255.0, g / 255.0, b / 255.0, a / 255.0);
64+
} catch {
65+
return null;
66+
}
67+
}
68+
69+
/// <summary>
70+
/// Hue, Saturation, Value description of a color.<br/>
71+
/// Hue varies from 0 - 360.<br/>
72+
/// Saturation and value varies from 0 - 1.
73+
/// </summary>
74+
public struct Hsv
75+
{
76+
public readonly double h;
77+
public readonly double s;
78+
public readonly double v;
79+
80+
public Hsv (double h, double s, double v)
81+
{
82+
this.h = h;
83+
this.s = s;
84+
this.v = v;
85+
}
86+
}
87+
88+
/// <summary>
89+
/// Copied from RgbColor.ToHsv<br/>
90+
/// Returns the Cairo color in HSV value.
91+
/// </summary>
92+
/// <returns>HSV struct.
93+
/// Hue varies from 0 - 360.<br/>
94+
/// Saturation and value varies from 0 - 1.
95+
/// </returns>
96+
public static Hsv GetHsv (this Color c)
97+
{
98+
// In this function, R, G, and B values must be scaled
99+
// to be between 0 and 1.
100+
// HsvColor.Hue will be a value between 0 and 360, and
101+
// HsvColor.Saturation and value are between 0 and 1.
102+
103+
double h, s, v;
104+
105+
double min = Math.Min (Math.Min (c.R, c.G), c.B);
106+
double max = Math.Max (Math.Max (c.R, c.G), c.B);
107+
108+
double delta = max - min;
109+
110+
if (max == 0 || delta == 0) {
111+
// R, G, and B must be 0, or all the same.
112+
// In this case, S is 0, and H is undefined.
113+
// Using H = 0 is as good as any...
114+
s = 0;
115+
h = 0;
116+
v = max;
117+
} else {
118+
s = delta / max;
119+
if (c.R == max) {
120+
// Between Yellow and Magenta
121+
h = (c.G - c.B) / delta;
122+
} else if (c.G == max) {
123+
// Between Cyan and Yellow
124+
h = 2 + (c.B - c.R) / delta;
125+
} else {
126+
// Between Magenta and Cyan
127+
h = 4 + (c.R - c.G) / delta;
128+
}
129+
v = max;
130+
}
131+
// Scale h to be between 0 and 360.
132+
// This may require adding 360, if the value
133+
// is negative.
134+
h *= 60;
135+
136+
if (h < 0) {
137+
h += 360;
138+
}
139+
140+
// Scale to the requirements of this
141+
// application. All values are between 0 and 255.
142+
return new Hsv (h, s, v);
143+
}
144+
145+
/// <summary>
146+
/// Returns a copy of the original color, replacing provided RGBA components.
147+
/// </summary>
148+
/// <param name="r">Red component, 0 - 1</param>
149+
/// <param name="g">Green component, 0 - 1</param>
150+
/// <param name="b">Blue component, 0 - 1</param>
151+
/// <param name="a">Alpha component, 0 - 1</param>
152+
public static Color SetRgba (this Color c, double? r = null, double? g = null, double? b = null, double? a = null)
153+
{
154+
return new Color (r ?? c.R, g ?? c.G, b ?? c.B, a ?? c.A);
155+
}
156+
157+
/// <summary>
158+
/// Returns a copy of the original color, replacing provided HSV components.
159+
/// </summary>
160+
/// <param name="hue">Hue component, 0 - 360</param>
161+
/// <param name="sat">Saturation component, 0 - 1</param>
162+
/// <param name="value">Value component, 0 - 1</param>
163+
/// <param name="alpha">Alpha component, 0 - 1</param>
164+
public static Color SetHsv (this Color c, double? hue = null, double? sat = null, double? value = null, double? alpha = null)
165+
{
166+
var hsv = c.GetHsv ();
167+
168+
double h = hue ?? hsv.h;
169+
double s = sat ?? hsv.s;
170+
double v = value ?? hsv.v;
171+
double a = alpha ?? c.A;
172+
173+
return FromHsv (h, s, v, a);
174+
}
175+
176+
/// <summary>
177+
/// Copied from HsvColor.ToRgb<br/>
178+
/// Returns a Cairo color using the given HSV values.
179+
/// </summary>
180+
/// <param name="hue">Hue component, 0 - 360</param>
181+
/// <param name="sat">Saturation component, 0 - 1</param>
182+
/// <param name="value">Value component, 0 - 1</param>
183+
/// <param name="alpha">Alpha component, 0 - 1</param>
184+
public static Color FromHsv (double hue, double sat, double value, double alpha = 1)
185+
{
186+
double h = hue;
187+
double s = sat;
188+
double v = value;
189+
190+
// Stupid hack!
191+
// If v or s is set to 0, it results in data loss for hue / sat. So we force it to be slightly above zero.
192+
if (v == 0)
193+
v = 0.0001;
194+
if (s == 0)
195+
s = 0.0001;
196+
197+
// HsvColor contains values scaled as in the color wheel.
198+
// Scale Hue to be between 0 and 360. Saturation
199+
// and value scale to be between 0 and 1.
200+
h %= 360.0;
201+
202+
double r = 0;
203+
double g = 0;
204+
double b = 0;
205+
206+
if (s == 0) {
207+
// If s is 0, all colors are the same.
208+
// This is some flavor of gray.
209+
r = v;
210+
g = v;
211+
b = v;
212+
} else {
213+
// The color wheel consists of 6 sectors.
214+
// Figure out which sector you're in.
215+
double sectorPos = h / 60;
216+
int sectorNumber = (int) (Math.Floor (sectorPos));
217+
218+
// get the fractional part of the sector.
219+
// That is, how many degrees into the sector
220+
// are you?
221+
double fractionalSector = sectorPos - sectorNumber;
222+
223+
// Calculate values for the three axes
224+
// of the color.
225+
double p = v * (1 - s);
226+
double q = v * (1 - (s * fractionalSector));
227+
double t = v * (1 - (s * (1 - fractionalSector)));
228+
229+
// Assign the fractional colors to r, g, and b
230+
// based on the sector the angle is in.
231+
switch (sectorNumber) {
232+
case 0:
233+
r = v;
234+
g = t;
235+
b = p;
236+
break;
237+
238+
case 1:
239+
r = q;
240+
g = v;
241+
b = p;
242+
break;
243+
244+
case 2:
245+
r = p;
246+
g = v;
247+
b = t;
248+
break;
249+
250+
case 3:
251+
r = p;
252+
g = q;
253+
b = v;
254+
break;
255+
256+
case 4:
257+
r = t;
258+
g = p;
259+
b = v;
260+
break;
261+
262+
case 5:
263+
r = v;
264+
g = p;
265+
b = q;
266+
break;
267+
}
268+
}
269+
// return an RgbColor structure, with values scaled
270+
// to be between 0 and 255.
271+
return new Color (r, g, b, alpha);
272+
}
273+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Gtk;
2+
3+
namespace Pinta.Core.Extensions;
4+
5+
public static class WidgetExtensions
6+
{
7+
/// <summary>
8+
/// Checks whether the mousePos (which is relative to topwidget) is within the area and returns its relative position to the area.
9+
/// </summary>
10+
/// <param name="widget">Drawing area where returns true if mouse inside.</param>
11+
/// <param name="topWidget">The top widget. This is what the mouse position is relative to.</param>
12+
/// <param name="mousePos">Position of the mouse relative to the top widget, usually obtained from Gtk.GestureClick</param>
13+
/// <param name="relPos">Position of the mouse relative to the drawing area.</param>
14+
/// <returns>Whether or not mouse position is within the drawing area.</returns>
15+
public static bool IsMouseInDrawingArea (this Widget widget, Widget topWidget, PointD mousePos, out PointD relPos)
16+
{
17+
widget.TranslateCoordinates (topWidget, 0,0, out double x, out double y);
18+
relPos = new PointD ((mousePos.X - x), (mousePos.Y - y));
19+
if (relPos.X >= 0 && relPos.X <= widget.GetWidth () && relPos.Y >= 0 && relPos.Y <= widget.GetHeight ())
20+
return true;
21+
return false;
22+
}
23+
}

0 commit comments

Comments
 (0)