Commit 2279d49d authored by Zéfling's avatar Zéfling 🎨

Add alpha supports

- fix float value for rgb
- fix blue additional
- Fix demo
parent 05ed8565
......@@ -2,34 +2,43 @@
// tslint:disable:no-bitwise
const pattern = {
hexa: /^^#?(([\da-f]{3})(([\da-f]{3})([\da-f]{2})?|[\da-f]{1})?)$/i,
hexa: /^#?(([\da-f]{3})(([\da-f]{3})([\da-f]{2})?|[\da-f]{1})?)$/i,
rgba: /^rgba?\(([\d]*(\.[\d]+)?)(,?\s*|\s+)([\d]*(\.[\d]+)?)(,?\s*|\s+)([\d]*(\.[\d]+)?)((,\s*)([\d]*(\.[\d]+)?))?\)$/i,
hsva: /^hsla?\(([\d]*(\.[\d]+)?)(,?\s*|\s+)([\d]*(\.[\d]+)?)\%(,?\s*|\s+)([\d]*(\.[\d]+)?)\%((,\s*)([\d]*(\.[\d]+)?))?\)$/i
};
export interface RGB {
/** Red color value [0,255] */ r: number;
/** Red color value [0,255] */ r: number;
/** Green color value [0,255] */ g: number;
/** Blue color value [0,255] */ b: number;
/** Blue color value [0,255] */ b: number;
/** Alpha color value [0,1] */ a?: number;
}
export interface HSV {
/** Hue color value [0,360] */ h: number;
/** Saluration color value [0,100] */ s: number;
/** Value/lightness color value [0,100] */ v: number;
/** Hue color value [0,360] */ h: number;
/** Saluration color value [0,100] */ s: number;
/** Value/lightness color value [0,100] */ v: number;
/** Alpha color value [0,1] */ a?: number;
}
export interface ColorData {
// RGB
/** Red color value [-256,255] */ r?: number;
/** Green color value [-256,255] */ g?: number;
/** Blue color value [-256,255] */ b?: number;
/** Red color value [-256,255] */ r?: number;
/** Green color value [-256,255] */ g?: number;
/** Blue color value [-256,255] */ b?: number;
// HSV/HSL
/** Hue value [-360,360] */ h?: number;
/** Saluration value [-100,100] */ s?: number;
/** Value/lightness value [-100,100] */ v?: number;
/** Hue value [-360,360] */ h?: number;
/** Saluration value [-100,100] */ s?: number;
/** Value/lightness value [-100,100] */ v?: number;
// Other
/** luminosity value [-1,1] */ luminosity?: number;
/** color mask */ maskColor?: string;
/** color mask opacity [0,1] */ maskOpacity?: number;
/** luminosity value [-1,1] */ luminosity?: number;
/** color mask */ maskColor?: string;
/** color mask opacity [0,1] */ maskOpacity?: number;
// alpha
/** color mask alpha [-1,1] */ alpha?: number;
}
export interface ColorNumber {
/** intcolor */ intColor: number;
/** Alpha color value [0,1] */ alpha: number;
}
export class Coloration {
......@@ -78,9 +87,9 @@ export class Coloration {
rebeccapurple: '#663399'
};
private intColor: number;
private rgb: RGB = { r: 0, g: 0, b: 0 };
private hsv: HSV = { h: 0, s: 0, v: 0 };
private calcColor: ColorNumber;
private rgb: RGB = { r: 0, g: 0, b: 0, a: 1 };
private hsv: HSV = { h: 0, s: 0, v: 0, a: 1 };
constructor(public color: string) {
this.reset();
......@@ -90,7 +99,7 @@ export class Coloration {
* reinit to base color
*/
reset() {
this.intColor = this.parseColor(this.color);
this.calcColor = this.parseColor(this.color);
this.updateColor();
}
......@@ -113,19 +122,22 @@ export class Coloration {
*/
maskColor(color: string, opacity: number = 1): Coloration {
const baseColor = this.intColor;
const baseColor = this.calcColor;
const additionalColor = this.parseColor(color);
const lum = this.minmax(opacity, 0, 1);
const R = baseColor >> 16;
const G = baseColor >> 8 & 0x00FF;
const B = baseColor & 0x0000FF;
const R = baseColor.intColor >> 16;
const G = baseColor.intColor >> 8 & 0x00FF;
const B = baseColor.intColor & 0x0000FF;
this.intColor = this.rgbToInt(
Math.round(((additionalColor >> 16) - R) * lum) + R,
Math.round(((additionalColor >> 8 & 0x00FF) - G) * lum) + G,
Math.round(((additionalColor & 0x0000FF) - B) * lum) + B
if (additionalColor.alpha) {
this.calcColor.alpha = this.minmax(baseColor.alpha + additionalColor.alpha, 0, 1);
}
this.calcColor.intColor = this.rgbToInt(
Math.round(((additionalColor.intColor >> 16) - R) * lum) + R,
Math.round(((additionalColor.intColor >> 8 & 0x00FF) - G) * lum) + G,
Math.round(((additionalColor.intColor & 0x0000FF) - B) * lum) + B
);
this.updateColor();
return this;
......@@ -147,17 +159,15 @@ export class Coloration {
if (colorData.r || colorData.g || colorData.b) {
if (colorData.r) {
console.log(this.rgb.r, colorData.r, this.rgb.r + colorData.r);
this.rgb.r = this.minmax(this.rgb.r + (+colorData.r), 0, 255);
console.log(this.rgb.r);
}
if (colorData.g) {
this.rgb.g = this.minmax(this.rgb.g + (+colorData.g), 0, 255);
}
if (colorData.b) {
this.rgb.r = this.minmax(this.rgb.b + (+colorData.b), 0, 255);
this.rgb.b = this.minmax(this.rgb.b + (+colorData.b), 0, 255);
}
this.intColor = this.rgbToInt(this.rgb.r, this.rgb.g, this.rgb.b);
this.calcColor.intColor = this.rgbToInt(this.rgb.r, this.rgb.g, this.rgb.b);
this.updateColor();
}
......@@ -171,10 +181,14 @@ export class Coloration {
if (colorData.v) {
this.hsv.v = this.minmax(this.hsv.v + (+colorData.v), 0, 100);
}
this.intColor = this.hsvToInt(this.hsv.h, this.hsv.s, this.hsv.v);
this.calcColor.intColor = this.hsvToInt(this.hsv.h, this.hsv.s, this.hsv.v);
this.updateColor();
}
if (colorData.alpha) {
this.hsv.a = this.rgb.a = this.calcColor.alpha = this.minmax(this.calcColor.alpha + (+colorData.alpha), 0, 1);
}
return this;
}
......@@ -199,7 +213,8 @@ export class Coloration {
* @returns string of #HEX
*/
toHEX(): string {
return this.value<string>(() => '#' + (0x1000000 + this.intColor).toString(16).slice(1));
return this.value<string>(() => '#' + (0x1000000 + this.calcColor.intColor).toString(16).slice(1))
+ (this.calcColor.alpha < 1 ? (0x100 + Math.round(this.calcColor.alpha * 256)).toString(16).slice(1) : '');
}
/**
......@@ -207,7 +222,9 @@ export class Coloration {
* @returns string of HVL(H, S%, V%)
*/
toHSL(): string {
return this.value<string>(() => `hsl(${this.hsv.h}, ${this.hsv.s}%, ${this.hsv.v}%)`);
return this.value<string>(
() => `hsl(${this.hsv.h}, ${this.hsv.s}%, ${this.hsv.v}%${this.hsv.a < 1 ? ', ' + this.hsv.a : ''})`
);
}
/**
......@@ -215,21 +232,25 @@ export class Coloration {
* @returns string of RGB(R, G, B)
*/
toRGB(): string {
return this.value<string>(() => `rgb(${this.rgb.r}, ${this.rgb.g}, ${this.rgb.b})`);
return this.value<string>(
() => `rgb(${this.rgb.r}, ${this.rgb.g}, ${this.rgb.b}${this.rgb.a < 1 ? ', ' + this.rgb.a : ''})`
);
}
/**
* update colors data : RGB and HSL
*/
private updateColor() {
const color = this.calcColor.intColor;
// update RGB
this.rgb.r = this.intColor >> 16;
this.rgb.g = this.intColor >> 8 & 0x00FF;
this.rgb.b = this.intColor & 0x0000FF;
this.rgb.r = color >> 16;
this.rgb.g = color >> 8 & 0x00FF;
this.rgb.b = color & 0x0000FF;
this.rgb.a = this.calcColor.alpha;
// update HSL
this.hsv = this.rgb2hsv(this.rgb.r, this.rgb.g, this.rgb.b);
this.hsv.a = this.calcColor.alpha;
}
/**
......@@ -237,7 +258,7 @@ export class Coloration {
* @param color parse color : name, #HEXA, rgba()
* @returns int color
*/
private parseColor(color: string) {
private parseColor(color: string): ColorNumber {
// si named color
if (Coloration.colorsName[color]) {
......@@ -246,13 +267,17 @@ export class Coloration {
// validate hexa string #RGB, #RGBA, #RRGGBB, #RRGGBBAA (ignore alpha)
const matchHex = String(color).match(pattern.hexa);
let intColor;
let intColor: number;
let alpha = 1;
if (matchHex) {
const hexa = matchHex[4]
const hexaColor = matchHex[4]
? matchHex[2] + matchHex[4]
: matchHex[2][0] + matchHex[2][0] + matchHex[2][1] + matchHex[2][1] + matchHex[2][2] + matchHex[2][2];
intColor = parseInt(hexa, 16);
const hexaAlpha = matchHex[4]
? matchHex[5]
: matchHex[3][1] + matchHex[3][1];
intColor = parseInt(hexaColor, 16);
alpha = hexaAlpha !== undefined ? parseInt(hexaAlpha, 16) / 256 : 1;
}
if (intColor === undefined) {
......@@ -260,6 +285,7 @@ export class Coloration {
const matchRgb = String(color).match(pattern.rgba);
if (matchRgb) {
intColor = this.rgbToInt(parseInt(matchRgb[1], 10), parseInt(matchRgb[4], 10), parseInt(matchRgb[7], 10));
alpha = matchRgb[11] !== undefined ? parseFloat(matchRgb[11]) : 1;
}
}
......@@ -268,10 +294,11 @@ export class Coloration {
const matchHsv = String(color).match(pattern.hsva);
if (matchHsv) {
intColor = this.hsvToInt(parseInt(matchHsv[1], 10), parseInt(matchHsv[4], 10), parseInt(matchHsv[7], 10));
alpha = matchHsv[11] !== undefined ? parseFloat(matchHsv[11]) : 1;
}
}
return intColor;
return { intColor, alpha };
}
......@@ -320,9 +347,9 @@ export class Coloration {
/**
* Convert RGB to HSV
* @param r [0, 255]
* @param g [0, 255]
* @param b [0, 255]
* @param r red [0, 255]
* @param g green [0, 255]
* @param b blue [0, 255]
* @returns HSV data
* @see https://stackoverflow.com/questions/39118528/rgb-to-hsl-conversion
* @see https://en.wikipedia.org/wiki/HSL_and_HSV#Formal_derivation
......@@ -379,7 +406,7 @@ export class Coloration {
* @returns int value
*/
private rgbToInt(r: number, g: number, b: number): number {
return r * 0x10000 + g * 0x100 + b;
return Math.round(r) * 0x10000 + Math.round(g) * 0x100 + Math.round(b);
}
/**
......@@ -400,7 +427,7 @@ export class Coloration {
* @returns value or null
*/
private value<L>(value: () => L): L {
return !isNaN(this.intColor) ? value() : null;
return !isNaN(this.calcColor.intColor) ? value() : null;
}
}
......@@ -18,9 +18,9 @@
<div class="additionnal">
<div class="luminosity">
<label> <span>Luminosity:</span><input type="number"
min="-1.00"
max="1.00"
step="0.01"
min="-1.000"
max="1.000"
step="0.001"
[value]="luminosity"
(change)="luminosity = $event.target.value"></label>
</div>
......@@ -83,10 +83,18 @@
<label><span>Mask opacity:</span><input type="number"
min="0"
max="1"
step="0.01"
step="0.001"
[value]="maskOpacity"
(change)="maskOpacity = $event.target.value"></label>
</div>
<div class="alpha">
<label><span>Alpha:</span><input type="number"
min="-1.000"
max="1.000"
step="0.001"
[value]="alpha"
(change)="alpha = $event.target.value"></label>
</div>
</div>
<div class="button">
<button type="button"
......@@ -97,7 +105,8 @@
<aside>
<fieldset>
<legend>Result</legend>
<div [style.background-image]="colorHex ? 'linear-gradient(to right, '+baseColor+', '+colorHex+')' : baseColor">&nbsp;</div>
<div [style.background-image]="gradient">
&nbsp;</div>
<div>
HEX : {{colorHex || '--'}}
<div [style.background-color]="colorHex">&nbsp;</div>
......
......@@ -17,10 +17,10 @@ aside {
display: grid;
width: 100%;
grid-template:
"lum . . " 35px
"color opacity . " 35px
"red green blue " 35px
"hue sat value" 35px / 1fr 1fr 1fr;
"lum . alpha " 35px
"color opacity . " 35px
"red green blue " 35px
"hue sat value " 35px / 1fr 1fr 1fr;
label {
display:flex;
......@@ -46,6 +46,7 @@ aside {
.hue {grid-area: hue;}
.saturation { grid-area: sat;}
.value{ grid-area: value;}
.alpha{ grid-area: alpha;}
.button {
text-align: center;
......
......@@ -18,17 +18,19 @@ export class AppComponent {
value = 0;
maskColor = '';
maskOpacity = 1;
alpha = 0;
colorHex: string;
colorRgb: string;
colorHsl: string;
constructor() {
}
private color: Coloration;
constructor() { }
change() {
const color = new Coloration(this.baseColor);
color.addColor({
this.color = new Coloration(this.baseColor);
this.color.addColor({
r: this.red,
g: this.green,
b: this.blue,
......@@ -37,14 +39,18 @@ export class AppComponent {
v: this.value,
luminosity: this.luminosity,
maskColor: this.maskColor,
maskOpacity: this.maskOpacity
maskOpacity: this.maskOpacity,
alpha: this.alpha
});
console.log(color);
this.colorHex = this.color.toHEX();
this.colorHsl = this.color.toHSL();
this.colorRgb = this.color.toRGB();
}
this.colorHex = color.toHEX();
this.colorHsl = color.toHSL();
this.colorRgb = color.toRGB();
get gradient() {
const hexa = this.color.toHEX();
return this.color && hexa ? 'linear-gradient(to right, ' + this.baseColor + ', ' + hexa + ')' : this.baseColor;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment