Added Color class

- Added Color class to make color representation and convertions more uniform
- Replaced standard convertions with Color.convertion
- Removed _algorithms.js, _deleteColor.js, ajax.js and the other convertion files
- Fixed bug in ColorModule, moved replaceAllOfColor to ColorModule as well as deleteColor
This commit is contained in:
unsettledgames 2021-07-11 12:40:48 +02:00
parent dbffc0b9da
commit f5807417ec
17 changed files with 367 additions and 643 deletions

169
js/Color.js Normal file
View File

@ -0,0 +1,169 @@
// OPTIMIZABLE: add a normalize function that returns the normalized colour in the current
// format.
class Color {
constructor(fmt, v1, v2, v3, v4) {
this.fmt = fmt;
switch (fmt) {
case 'hsv':
this.hsv = {h: v1, s: v2, v: v3};
this.rgb = Color.hsvToRgb(this.hsv);
this.hsl = Color.rgbToHsl(this.rgb);
this.hex = Color.rgbToHex(this.rgb);
break;
case 'hsl':
this.hsl = {h: v1, s: v2, l: v3};
this.rgb = Color.hslToRgb(this.hsl);
this.hsv = Color.rgbToHsv(this.rgb);
this.hex = Color.rgbToHex(this.rgb);
break;
case 'rgb':
this.rgb = {r: v1, g: v2, b: v3};
this.hsl = Color.rgbToHsl(this.rgb);
this.hsv = Color.rgbToHsv(this.rgb);
this.hex = Color.rgbToHex(this.rgb);
break;
case 'hex':
this.hex = v1;
this.rgb = Color.hexToRgb(this.hex);
this.hsl = Color.rgbToHsl(this.rgb);
this.hsv = Color.rgbToHsv(this.rgb);
break;
default:
console.error("Unsupported color mode " + fmt);
break;
}
}
static hexToRgb(hex, divisor) {
//if divisor isn't set, set it to one (so it has no effect)
divisor = divisor || 1;
//split given hex code into array of 3 values
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex.trim());
return result ? {
r: parseInt(result[1], 16)/divisor,
g: parseInt(result[2], 16)/divisor,
b: parseInt(result[3], 16)/divisor
} : null;
}
static rgbToHex(rgb) {
//convert a decimal number to 2-digit hex
function componentToHex (c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
return componentToHex(rgb.r) + componentToHex(rgb.g) + componentToHex(rgb.b);
}
static hslToRgb(hsl) {
var r, g, b;
var h = hsl.h, s = hsl.s, l = hsl.l;
h /= 255;
s /= 255;
l /= 255;
if(s == 0){
r = g = b = l; // achromatic
}else{
var hue2rgb = function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return {
r:Math.round(r * 255),
g:Math.round(g * 255),
b:Math.round(b * 255)
};
}
static rgbToHsl(rgb) {
var r, g, b;
r = rgb.r; g = rgb.g; b = rgb.b;
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var hue, saturation, luminosity = (max + min) / 2;
if(max == min){
hue = saturation = 0; // achromatic
}else{
var d = max - min;
saturation = luminosity > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: hue = (g - b) / d + (g < b ? 6 : 0); break;
case g: hue = (b - r) / d + 2; break;
case b: hue = (r - g) / d + 4; break;
}
hue /= 6;
}
return {h:hue, s:saturation, l:luminosity};
}
static hsvToRgb(hsv) {
var r, g, b, h, s, v;
h = hsv.h; s = hsv.s; v = hsv.v;
h /= 360;
s /= 100;
v /= 100;
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return {r: r * 255, g: g * 255, b: b * 255 };
}
static rgbToHsv(rgb) {
let r = rgb.r, g = rgb.g, b = rgb.b;
r /= 255, g /= 255, b /= 255;
let max = Math.max(r, g, b), min = Math.min(r, g, b);
let myH, myS, myV = max;
let d = max - min;
myS = max == 0 ? 0 : d / max;
if (max == min) {
myH = 0; // achromatic
}
else {
switch (max) {
case r: myH = (g - b) / d + (g < b ? 6 : 0); break;
case g: myH = (b - r) / d + 2; break;
case b: myH = (r - g) / d + 4; break;
}
myH /= 6;
}
return {h: myH * 360, s: myS * 100, v: myV * 100};
}
}

View File

@ -6,7 +6,6 @@
const ColorModule = (() => {
const currentPalette = [];
const coloursList = document.getElementById("palette-list");
console.info("Initialized Color Module..");
document.getElementById('jscolor-hex-input').addEventListener('change',colorChanged, false);
@ -22,10 +21,12 @@ const ColorModule = (() => {
// Changes all of one color to another after being changed from color picker
function colorChanged(colorHexElement) {
console.log("Clicked:");
console.log(colorHexElement);
// Get old and new colors from the element
const hexElement = colorHexElement.target;
const hexElementValue = hexElement.value;
const newColor = hexToRgb(hexElementValue);
const newColor = Color.hexToRgb(hexElementValue);
const oldColor = hexElement.oldColor;
//if the color is not a valid hex color, exit this function and do nothing
@ -36,7 +37,7 @@ const ColorModule = (() => {
newColor.a = 255;
//save undo state
new HistoryStateEditColor(hexElementValue.toLowerCase(), rgbToHex(oldColor));
new HistoryStateEditColor(hexElementValue.toLowerCase(), Color.rgbToHex(oldColor));
//get the currently selected color
const currentlyEditedColor = document.getElementsByClassName('jscolor-active')[0];
@ -73,7 +74,7 @@ const ColorModule = (() => {
currentlyEditedColor.firstChild.jscolor.fromString(newColorHex);
replaceAllOfColor(oldColor, newColor);
ColorModule.replaceAllOfColor(oldColor, newColor);
//set new old color to changed color
hexElement.oldColor = newColor;
@ -82,7 +83,7 @@ const ColorModule = (() => {
//if this is the current color, update the drawing color
if (hexElement.colorElement.parentElement.classList.contains('selected')) {
for (let i=1; i<layers.length - nAppLayers; i++) {
layers[i].context.fillStyle = '#'+ rgbToHex(newColor.r,newColor.g,newColor.b);
layers[i].context.fillStyle = '#'+ Color.rgbToHex(newColor);
}
currentGlobalColor = newColor;
}
@ -90,11 +91,7 @@ const ColorModule = (() => {
function addColorButtonEvent() {
//generate random color
const hue = Math.floor(Math.random()*255);
const sat = 130+Math.floor(Math.random()*100);
const lit = 70+Math.floor(Math.random()*100);
const newColorRgb = hslToRgb(hue,sat,lit);
const newColor = rgbToHex(newColorRgb.r,newColorRgb.g,newColorRgb.b);
const newColor = new Color("hsl", Math.floor(Math.random()*255), 130+Math.floor(Math.random()*100), 70+Math.floor(Math.random()*100)).hex;
//remove current color selection
document.querySelector('#colors-menu li.selected').classList.remove('selected');
@ -183,13 +180,13 @@ const ColorModule = (() => {
newEditButton = editButtonTemplate.cloneNode(true);
listItem.appendChild(newEditButton);
newEditButton.addEventListener('click', (e,button) => {
newEditButton.addEventListener('click', () => {
//hide edit button
button.parentElement.lastChild.classList.add('hidden');
newEditButton.parentElement.lastChild.classList.add('hidden');
//show jscolor picker, if basic mode is enabled
if (pixelEditorMode == 'Basic')
button.parentElement.firstChild.jscolor.show();
newEditButton.parentElement.firstChild.jscolor.show();
else
showDialogue("palette-block", false);
});
@ -197,9 +194,100 @@ const ColorModule = (() => {
return listItem;
}
function deleteColor (color) {
const logStyle = 'background: #913939; color: white; padding: 5px;';
//if color is a string, then find the corresponding button
if (typeof color === 'string') {
//console.log('trying to find ',color);
//get all colors in palette
colors = document.getElementsByClassName('color-button');
//loop through colors
for (var i = 0; i < colors.length; i++) {
//console.log(color,'=',colors[i].jscolor.toString());
if (color == colors[i].jscolor.toString()) {
//set color to the color button
color = colors[i];
break;
}
}
//if the color wasn't found
if (typeof color === 'string') {
//exit function
return;
}
}
//hide color picker
color.jscolor.hide();
//find lightest color in palette
var colors = document.getElementsByClassName('color-button');
var lightestColor = [0,null];
for (var i = 0; i < colors.length; i++) {
//get colors lightness
var lightness = Color.rgbToHsl(colors[i].jscolor.toRgb()).l;
//if not the color we're deleting
if (colors[i] != color) {
//if lighter than the current lightest, set as the new lightest
if (lightness > lightestColor[0]) {
lightestColor[0] = lightness;
lightestColor[1] = colors[i];
}
}
}
//replace deleted color with lightest color
ColorModule.replaceAllOfColor(color.jscolor.toString(),lightestColor[1].jscolor.toString());
//if the color you are deleting is the currently selected color
if (color.parentElement.classList.contains('selected')) {
//set current color TO LIGHTEST COLOR
lightestColor[1].parentElement.classList.add('selected');
currentLayer.context.fillStyle = '#'+lightestColor[1].jscolor.toString();
}
//delete the element
colorsMenu.removeChild(color.parentElement);
}
//replaces all of a single color on the canvas with a different color
//input two rgb color objects {r:0,g:0,b:0}
function replaceAllOfColor (oldColor, newColor) {
//convert strings to objects if nessesary
if (typeof oldColor === 'string') oldColor = Color.hexToRgb(oldColor);
if (typeof newColor === 'string') newColor = Color.hexToRgb(newColor);
//create temporary image from canvas to search through
var tempImage = currentLayer.context.getImageData(0, 0, canvasSize[0], canvasSize[1]);
//loop through all pixels
for (var i=0;i<tempImage.data.length;i+=4) {
//check if pixel matches old color
if(tempImage.data[i]==oldColor.r && tempImage.data[i+1]==oldColor.g && tempImage.data[i+2]==oldColor.b){
//change to new color
tempImage.data[i]=newColor.r;
tempImage.data[i+1]=newColor.g;
tempImage.data[i+2]=newColor.b;
}
}
//put temp image back onto canvas
currentLayer.context.putImageData(tempImage,0,0);
}
return {
currentPalette,
addColor,
deleteColor,
replaceAllOfColor,
AddToSimplePalette
}
})();

View File

@ -1,5 +1,11 @@
// Acts as a public static class
class Util {
/** Returns elementOrElementId if the argument is already an element, otherwise it finds
* the element by its ID (given by the argument) and returns it
*
* @param {*} elementOrElementId The element to return, or the ID of the element to return
* @returns The desired element
*/
static getElement(elementOrElementId) {
if (typeof(elementOrElementId) == "object") {
return elementOrElementId;
@ -11,25 +17,29 @@ class Util {
console.log("Type not supported: " + typeof(elementOrElementId));
}
}
// Returns the text content of the element with ID elementId
static getText(elementId) {
return Util.getElement(elementId).textContent;
}
// Sets the text content of the element with ID elementId
static setText(elementId, text) {
Util.getElement(elementId).textContent = text;
}
// Gets the value of the element with ID elementId
static getValue(elementId) {
return Util.getElement(elementId).value;
}
// Sets the value of the element with ID elementId
static setValue(elementId, value) {
Util.getElement(elementId).value = value;
}
//add class .selected to specified element
static select(elementId) {
Util.getElement(elementId).classList.add('selected');
}
//remove .selected class from specified element
static deselect(elementId) {
Util.getElement(elementId).classList.remove('selected');

View File

@ -1,185 +0,0 @@
// CONSTS
// Degrees to radiants
let degreesToRad = Math.PI / 180;
// I'm pretty sure that precision is necessary
let referenceWhite = {x: 95.05, y: 100, z: 108.89999999999999};
/**********************SECTION: COLOUR CONVERSIONS****************************** */
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param {number} h The hue
* @param {number} s The saturation
* @param {number} l The lightness
* @return {Array} The RGB representation
*/
function cpHslToRgb(h, s, l){
var r, g, b;
h /= 360;
s /= 100;
l /= 100;
if(s == 0){
r = g = b = l; // achromatic
}else{
var hue2rgb = function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
function hsvToRgb(h, s, v) {
var r, g, b;
h /= 360;
s /= 100;
v /= 100;
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return [ r * 255, g * 255, b * 255 ];
}
function hslToHex(h, s, l) {
h /= 360;
s /= 100;
l /= 100;
let r, g, b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
const toHex = x => {
const hex = Math.round(x * 255).toString(16);
return hex.length === 1 ? '0' + hex : hex;
};
return `${toHex(r)}${toHex(g)}${toHex(b)}`;
}
function rgbToHsl(col) {
let r = col.r;
let g = col.g;
let b = col.b;
r /= 255, g /= 255, b /= 255;
let max = Math.max(r, g, b), min = Math.min(r, g, b);
let myH, myS, myL = (max + min) / 2;
if (max == min) {
myH = myS = 0; // achromatic
}
else {
let d = max - min;
myS = myL > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: myH = (g - b) / d + (g < b ? 6 : 0); break;
case g: myH = (b - r) / d + 2; break;
case b: myH = (r - g) / d + 4; break;
}
myH /= 6;
}
return {h: myH, s: myS, l: myL };
}
function rgbToHsv(col) {
let r = col.r;
let g = col.g;
let b = col.b;
r /= 255, g /= 255, b /= 255;
let max = Math.max(r, g, b), min = Math.min(r, g, b);
let myH, myS, myV = max;
let d = max - min;
myS = max == 0 ? 0 : d / max;
if (max == min) {
myH = 0; // achromatic
}
else {
switch (max) {
case r: myH = (g - b) / d + (g < b ? 6 : 0); break;
case g: myH = (b - r) / d + 2; break;
case b: myH = (r - g) / d + 4; break;
}
myH /= 6;
}
return {h: myH, s: myS, v: myV};
}
function RGBtoCIELAB(rgbColour) {
// Convert to XYZ first via matrix transformation
let x = 0.412453 * rgbColour.r + 0.357580 * rgbColour.g + 0.180423 * rgbColour.b;
let y = 0.212671 * rgbColour.r + 0.715160 * rgbColour.g + 0.072169 * rgbColour.b;
let z = 0.019334 * rgbColour.r + 0.119193 * rgbColour.g + 0.950227 * rgbColour.b;
let xFunc = CIELABconvF(x / referenceWhite.x);
let yFunc = CIELABconvF(y / referenceWhite.y);
let zFunc = CIELABconvF(z / referenceWhite.z);
let myL = 116 * yFunc - 16;
let myA = 500 * (xFunc - yFunc);
let myB = 200 * (yFunc - zFunc);
return {l: myL, a: myA, b: myB};
}
function CIELABconvF(value) {
if (value > Math.pow(6/29, 3)) {
return Math.cbrt(value);
}
return 1/3 * Math.pow(6/29, 2) * value + 4/29;
}

View File

@ -71,26 +71,12 @@ function updateSliderValue (sliderIndex, updateMini = true) {
// Update preview colour
// get slider values
sliderValues = getSlidersValues();
// Generate preview colour
switch (currentPickerMode) {
case 'rgb':
hexColour = rgbToHex(sliderValues[0], sliderValues[1], sliderValues[2]);
break;
case 'hsv':
let tmpRgb = hsvToRgb(sliderValues[0], sliderValues[1], sliderValues[2]);
hexColour = rgbToHex(parseInt(tmpRgb[0]), parseInt(tmpRgb[1]), parseInt(tmpRgb[2]));
break;
case 'hsl':
hexColour = hslToHex(sliderValues[0], sliderValues[1], sliderValues[2]);
break;
default:
console.log("wtf select a decent picker mode");
return;
}
hexColour = new Color(currentPickerMode, sliderValues[0], sliderValues[1], sliderValues[2]);
// Update preview colour div
colourPreview.style.backgroundColor = '#' + hexColour;
colourValue.value = '#' + hexColour;
colourPreview.style.backgroundColor = '#' + hexColour.hex;
colourValue.value = '#' + hexColour.hex;
// Update sliders background
// there's no other way than creating a custom css file, appending it to the head and
@ -270,6 +256,7 @@ function changePickerMode(target, newMode) {
let colArray;
let rgbTmp;
let hexColour = colourValue.value.replace('#', '');
let currColor = new Color("hex", hexColour);
currentPickerMode = newMode;
document.getElementsByClassName("cp-selected-mode")[0].classList.remove("cp-selected-mode");
@ -309,30 +296,16 @@ function changePickerMode(target, newMode) {
// Putting the current colour in the new slider
switch(currentPickerMode) {
case 'rgb':
colArray = hexToRgb(hexColour);
colArray = currColor.rgb;
colArray = [colArray.r, colArray.g, colArray.b];
break;
case 'hsv':
rgbTmp = hexToRgb(hexColour);
colArray = rgbToHsv(rgbTmp);
colArray.h *= 360;
colArray.s *= 100;
colArray.v *= 100;
colArray = currColor.hsv;
colArray = [colArray.h, colArray.s, colArray.v];
break;
case 'hsl':
rgbTmp = hexToRgb(hexColour);
colArray = rgbToHsl(rgbTmp);
colArray.h *= 360;
colArray.s *= 100;
colArray.l *= 100;
colArray = currColor.hsl;
colArray = [colArray.h, colArray.s, colArray.l];
break;
default:
break;
@ -385,14 +358,14 @@ function movePickerIcon(event) {
// Updates the main sliders given a hex value computed with the minipicker
function updateSlidersByHex(hex, updateMini = true) {
let colour;
let colour = new Color("hex", hex);
let mySliders = [sliders[0].getElementsByTagName("input")[0],
sliders[1].getElementsByTagName("input")[0],
sliders[2].getElementsByTagName("input")[0]];
switch (currentPickerMode) {
case 'rgb':
colour = hexToRgb(hex);
colour = colour.rgb;
mySliders[0].value = colour.r;
mySliders[1].value = colour.g;
@ -400,7 +373,7 @@ function updateSlidersByHex(hex, updateMini = true) {
break;
case 'hsv':
colour = rgbToHsv(hexToRgb(hex));
colour = colour.hsv;
mySliders[0].value = colour.h * 360;
mySliders[1].value = colour.s * 100;
@ -408,7 +381,7 @@ function updateSlidersByHex(hex, updateMini = true) {
break;
case 'hsl':
colour = rgbToHsl(hexToRgb(hex));
colour = colour.hsl;
mySliders[0].value = colour.h * 360;
mySliders[1].value = colour.s * 100;
@ -445,7 +418,7 @@ function getCursorPosMinipicker(e) {
// Updates the minipicker given a hex computed by the main sliders
// Moves the cursor
function updatePickerByHex(hex) {
let hsv = rgbToHsv(hexToRgb(hex));
let hsv = new Color("hex", hex).hsv;
let xPos = miniPickerCanvas.width * hsv.h - 8;
let yPos = miniPickerCanvas.height * hsv.s + 8;
@ -471,15 +444,15 @@ function updatePickerByHex(hex) {
// Fired when the value of the minislider changes: updates the spectrum gradient and the hex colour
function miniSliderInput(event) {
let newHex;
let newHsv = rgbToHsv(hexToRgb(getMiniPickerColour()));
let currColor = new Color("hex", getMiniPickerColour());
let newHex = currColor.hex;
let newHsv = currColor.hsv;
let rgb;
// Adding slider value to value
newHsv.v = parseInt(event.target.value);
// Updating hex
rgb = hsvToRgb(newHsv.h * 360, newHsv.s * 100, newHsv.v);
newHex = rgbToHex(Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]));
newHex = Color.rgbToHex(Color.hsvToRgb(newHsv));
colourValue.value = newHex;
@ -504,20 +477,17 @@ function updateMiniPickerColour() {
// Returns the current colour of the minipicker
function getMiniPickerColour() {
let hex;
let pickedColour;
pickedColour = miniPickerCanvas.getContext('2d').getImageData(currPickerIconPos[0][0] + 8,
currPickerIconPos[0][1] + 8, 1, 1).data;
hex = rgbToHex(pickedColour[0], pickedColour[1], pickedColour[2]);
return hex;
return new Color("rgb", pickedColour[0], pickedColour[1], pickedColour[2]).hex;
}
// Update the background gradient of the slider in the minipicker
function updateMiniSlider(hex) {
let rgb = hexToRgb(hex);
let rgb = Color.hexToRgb(hex);
styles[1] = "input[type=range]#cp-minipicker-slider::-webkit-slider-runnable-track { background: rgb(2,0,36);";
styles[1] += "background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(0,0,0,1) 0%, " +
@ -530,11 +500,9 @@ function updateMiniSlider(hex) {
// Updates the gradient of the spectrum canvas in the minipicker
function updateMiniPickerSpectrum() {
let ctx = miniPickerCanvas.getContext('2d');
let hsv = rgbToHsv(hexToRgb(colourValue.value));
let hsv = new Color("hex", colourValue.value).hsv;
let tmp;
let white = {h:hsv.h * 360, s:0, v: parseInt(miniPickerSlider.value)};
white = hsvToRgb(white.h, white.s, white.v);
let white = new Color("hsv", hsv.h, 0, parseInt(miniPickerSlider.value)).rgb;
ctx.clearRect(0, 0, miniPickerCanvas.width, miniPickerCanvas.height);
@ -542,20 +510,16 @@ function updateMiniPickerSpectrum() {
var hGrad = ctx.createLinearGradient(0, 0, miniPickerCanvas.width, 0);
for (let i=0; i<7; i++) {
tmp = hsvToRgb(60 * i, 100, hsv.v * 100);
hGrad.addColorStop(i / 6, '#' + rgbToHex(Math.round(tmp[0]), Math.round(tmp[1]), Math.round(tmp[2])));
let stopHex = new Color("hsv", 60*i, 100, hsv.v);
hGrad.addColorStop(i / 6, '#' + stopHex.hex);
}
ctx.fillStyle = hGrad;
ctx.fillRect(0, 0, miniPickerCanvas.width, miniPickerCanvas.height);
// Drawing sat / lum
var vGrad = ctx.createLinearGradient(0, 0, 0, miniPickerCanvas.height);
vGrad.addColorStop(0, 'rgba(' + white[0] +',' + white[1] + ',' + white[2] + ',0)');
/*
vGrad.addColorStop(0.1, 'rgba(255,255,255,0)');
vGrad.addColorStop(0.9, 'rgba(255,255,255,1)');
*/
vGrad.addColorStop(1, 'rgba(' + white[0] +',' + white[1] + ',' + white[2] + ',1)');
vGrad.addColorStop(0, 'rgba(' + white.r +',' + white.g + ',' + white.b + ',0)');
vGrad.addColorStop(1, 'rgba(' + white.r +',' + white.g + ',' + white.b + ',1)');
ctx.fillStyle = vGrad;
ctx.fillRect(0, 0, miniPickerCanvas.width, miniPickerCanvas.height);
@ -649,89 +613,77 @@ function changePickingMode(event, newMode) {
}
function updateOtherIcons() {
let currentColorHex = colourValue.value;
let currentColourHsv = rgbToHsv(hexToRgb(currentColorHex));
let newColourHsv = {h:currentColourHsv.h, s:currentColourHsv.s, v:currentColourHsv.v};
let currentColorHex = new Color("hex", colourValue.value).hex;
let currentColourHsv = new Color("hex", currentColorHex).hsv;
let newColourHsv;
let newColourHexes = ['', '', ''];
let tmpRgb;
// Salvo tutti i
switch (currentPickingMode)
{
case 'mono':
break;
case 'analog':
// First colour
newColourHsv.h = (((currentColourHsv.h*360 + 40) % 360) / 360);
newColourHsv = new Color("hsv", ((currentColourHsv.h + 40) % 360), currentColourHsv.s, currentColourHsv.v);
currPickerIconPos[1][0] = miniPickerCanvas.width * newColourHsv.h - 8;
currPickerIconPos[1][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.s + 8);
currPickerIconPos[1][0] = miniPickerCanvas.width * newColourHsv.hsv.h/360 - 8;
currPickerIconPos[1][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.hsv.s/100 + 8);
tmpRgb = hsvToRgb(newColourHsv.h*360, newColourHsv.s*100, newColourHsv.v*100);
newColourHexes[0] = rgbToHex(Math.round(tmpRgb[0]), Math.round(tmpRgb[1]), Math.round(tmpRgb[2]));
newColourHexes[0] = newColourHsv.hex;
// Second colour
newColourHsv.h = (((currentColourHsv.h*360 - 40) % 360) / 360);
if (newColourHsv.h < 0) {
newColourHsv.h += 1;
}
newColourHsv.h = new Color("hsv", ((currentColourHsv.h + 320) % 360), currentColourHsv.s, currentColourHsv.v);
currPickerIconPos[2][0] = miniPickerCanvas.width * newColourHsv.h - 8;
currPickerIconPos[2][0] = miniPickerCanvas.width * newColourHsv.hsv.h/360 - 8;
currPickerIconPos[2][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.s + 8);
tmpRgb = hsvToRgb(newColourHsv.h*360, newColourHsv.s*100, newColourHsv.v*100);
newColourHexes[1] = rgbToHex(Math.round(tmpRgb[0]), Math.round(tmpRgb[1]), Math.round(tmpRgb[2]));
newColourHexes[1] = newColourHsv.hex;
break;
case 'cmpt':
newColourHsv.h = (((currentColourHsv.h*360 + 180) % 360) / 360);
newColourHsv = new Color("hsv", ((currentColourHsv.h + 180) % 360), currentColourHsv.s, currentColourHsv.v);
currPickerIconPos[1][0] = miniPickerCanvas.width * newColourHsv.h - 8;
currPickerIconPos[1][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.s + 8);
tmpRgb = hsvToRgb(newColourHsv.h*360, newColourHsv.s*100, newColourHsv.v*100);
newColourHexes[0] = rgbToHex(Math.round(tmpRgb[0]), Math.round(tmpRgb[1]), Math.round(tmpRgb[2]));
currPickerIconPos[1][0] = miniPickerCanvas.width * newColourHsv.hsv.h/360 - 8;
currPickerIconPos[1][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.hsv.s/100 + 8);
newColourHexes[0] = newColourHsv.hex;
break;
case 'tri':
for (let i=1; i< 3; i++) {
newColourHsv.h = (((currentColourHsv.h*360 + 120*i) % 360) / 360);
newColourHsv = new Color("hsv", (currentColourHsv.h + 120*i) % 360, currentColourHsv.s, currentColourHsv.v);
currPickerIconPos[i][0] = miniPickerCanvas.width * newColourHsv.h - 8;
currPickerIconPos[i][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.s + 8);
tmpRgb = hsvToRgb(newColourHsv.h*360, newColourHsv.s*100, newColourHsv.v*100);
newColourHexes[i - 1] = rgbToHex(Math.round(tmpRgb[0]), Math.round(tmpRgb[1]), Math.round(tmpRgb[2]));
currPickerIconPos[i][0] = miniPickerCanvas.width * newColourHsv.hsv.h/360 - 8;
currPickerIconPos[i][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.hsv.s/100 + 8);
newColourHexes[i - 1] = newColourHsv.hex;
}
break
case 'scmpt':
// First colour
newColourHsv.h = (((currentColourHsv.h*360 + 210) % 360) / 360);
newColourHsv = new Color("hsv", (currentColourHsv.h + 210) % 360, currentColourHsv.s, currentColourHsv.v);
currPickerIconPos[1][0] = miniPickerCanvas.width * newColourHsv.h - 8;
currPickerIconPos[1][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.s + 8);
tmpRgb = hsvToRgb(newColourHsv.h*360, newColourHsv.s*100, newColourHsv.v*100);
newColourHexes[0] = rgbToHex(Math.round(tmpRgb[0]), Math.round(tmpRgb[1]), Math.round(tmpRgb[2]));
currPickerIconPos[1][0] = miniPickerCanvas.width * newColourHsv.hsv.h/360 - 8;
currPickerIconPos[1][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.hsv.s/100 + 8);
newColourHexes[0] = newColourHsv.hex;
// Second colour
newColourHsv.h = (((currentColourHsv.h*360 + 150) % 360) / 360);
newColourHsv = new Color("hsv", (currentColourHsv.h + 150) % 360, currentColourHsv.s, currentColourHsv.v);
currPickerIconPos[2][0] = miniPickerCanvas.width * newColourHsv.h - 8;
currPickerIconPos[2][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.s + 8);
tmpRgb = hsvToRgb(newColourHsv.h*360, newColourHsv.s*100, newColourHsv.v*100);
newColourHexes[1] = rgbToHex(Math.round(tmpRgb[0]), Math.round(tmpRgb[1]), Math.round(tmpRgb[2]));
currPickerIconPos[2][0] = miniPickerCanvas.width * newColourHsv.hsv.h/360 - 8;
currPickerIconPos[2][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.hsv.s/100 + 8);
newColourHexes[1] = newColourHsv.hex;
break;
case 'tetra':
for (let i=1; i< 4; i++) {
newColourHsv.h = (((currentColourHsv.h*360 + 90*i) % 360) / 360);
newColourHsv = new Color("hsv", (currentColourHsv.h + 90*i) % 360, currentColourHsv.s, currentColourHsv.v);
currPickerIconPos[i][0] = miniPickerCanvas.width * newColourHsv.h - 8;
currPickerIconPos[i][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.s + 8);
tmpRgb = hsvToRgb(newColourHsv.h*360, newColourHsv.s*100, newColourHsv.v*100);
newColourHexes[i - 1] = rgbToHex(Math.round(tmpRgb[0]), Math.round(tmpRgb[1]), Math.round(tmpRgb[2]));
currPickerIconPos[i][0] = miniPickerCanvas.width * newColourHsv.hsv.h/360 - 8;
currPickerIconPos[i][1] = miniPickerCanvas.height - (miniPickerCanvas.height * newColourHsv.hsv.s/100 + 8);
newColourHexes[i - 1] = newColourHsv.hex;
}
break;
default:

View File

@ -14,22 +14,22 @@ function createColorPalette(paletteColors, deletePreviousPalette = true) {
}
}
var lightestColor = '#000000';
var darkestColor = '#ffffff';
var lightestColor = new Color("hex", '#000000');
var darkestColor = new Color("hex", '#ffffff');
// Adding all the colours in the array
for (var i = 0; i < paletteColors.length; i++) {
var newColor = paletteColors[i];
var newColorElement = ColorModule.addColor(newColor);
var newColor = new Color("hex", paletteColors[i]);
var newColorElement = ColorModule.addColor(newColor.hex);
var newColorHex = hexToRgb(newColor);
var newColRgb = newColor.rgb;
var lightestColorHex = hexToRgb(lightestColor);
if (newColorHex.r + newColorHex.g + newColorHex.b > lightestColorHex.r + lightestColorHex.g + lightestColorHex.b)
var lightestColorRgb = lightestColor.rgb;
if (newColRgb.r + newColRgb.g + newColRgb.b > lightestColorRgb.r + lightestColorRgb.g + lightestColorRgb.b)
lightestColor = newColor;
var darkestColorHex = hexToRgb(darkestColor);
if (newColorHex.r + newColorHex.g + newColorHex.b < darkestColorHex.r + darkestColorHex.g + darkestColorHex.b) {
var darkestColorRgb = darkestColor.rgb;
if (newColRgb.r + newColRgb.g + newColRgb.b < darkestColorRgb.r + darkestColorRgb.g + darkestColorRgb.b) {
//remove current color selection
var selectedColor = document.querySelector('#colors-menu li.selected');
@ -37,16 +37,15 @@ function createColorPalette(paletteColors, deletePreviousPalette = true) {
//set as current color
newColorElement.classList.add('selected');
darkestColor = newColor;
}
}
//prepend # if not present
if (!darkestColor.includes('#')) darkestColor = '#' + darkestColor;
if (!darkestColor.hex.includes('#')) darkestColor.hex = '#' + darkestColor.hex;
//set as current color
currentLayer.context.fillStyle = darkestColor;
currentLayer.context.fillStyle = darkestColor.hex;
}
/** Creates the palette with the colours used in all the layers
@ -65,7 +64,7 @@ function createPaletteFromLayers() {
let color = imageData[j]+','+imageData[j + 1]+','+imageData[j + 2];
if (!colors[color]) {
colors[color] = {r:imageData[j],g:imageData[j + 1],b:imageData[j + 2]};
colors[color] = new Color("rgb", imageData[j], imageData[j + 1], imageData[j + 2]).rgb;
//don't allow more than 256 colors to be added
if (Object.keys(colors).length >= settings.maxColorsOnImportedImage) {
@ -82,7 +81,7 @@ function createPaletteFromLayers() {
let colorPaletteArray = [];
for (let color in colors) {
if (colors.hasOwnProperty(color)) {
colorPaletteArray.push('#'+rgbToHex(colors[color]));
colorPaletteArray.push('#'+Color.rgbToHex(colors[color]));
}
}

View File

@ -1,84 +0,0 @@
//called when the delete button is pressed on color picker
//input color button or hex string
function deleteColor (color) {
const logStyle = 'background: #913939; color: white; padding: 5px;';
//console.log('%c'+'deleting color', logStyle);
//if color is a string, then find the corresponding button
if (typeof color === 'string') {
//console.log('trying to find ',color);
//get all colors in palette
colors = document.getElementsByClassName('color-button');
//loop through colors
for (var i = 0; i < colors.length; i++) {
//console.log(color,'=',colors[i].jscolor.toString());
if (color == colors[i].jscolor.toString()) {
//console.log('match');
//set color to the color button
color = colors[i];
//console.log('found color', color);
//exit loop
break;
}
}
//if the color wasn't found
if (typeof color === 'string') {
//console.log('color not found');
//exit function
return;
}
}
//hide color picker
color.jscolor.hide();
//find lightest color in palette
var colors = document.getElementsByClassName('color-button');
var lightestColor = [0,null];
for (var i = 0; i < colors.length; i++) {
//get colors lightness
var lightness = rgbToHsl(colors[i].jscolor.toRgb()).l;
//console.log('%c'+lightness, logStyle)
//if not the color we're deleting
if (colors[i] != color) {
//if lighter than the current lightest, set as the new lightest
if (lightness > lightestColor[0]) {
lightestColor[0] = lightness;
lightestColor[1] = colors[i];
}
}
}
//console.log('%c'+'replacing with lightest color: '+lightestColor[1].jscolor.toString(), logStyle)
//replace deleted color with lightest color
replaceAllOfColor(color.jscolor.toString(),lightestColor[1].jscolor.toString());
//if the color you are deleting is the currently selected color
if (color.parentElement.classList.contains('selected')) {
//console.log('%c'+'deleted color is currently selected', logStyle);
//set current color TO LIGHTEST COLOR
lightestColor[1].parentElement.classList.add('selected');
currentLayer.context.fillStyle = '#'+lightestColor[1].jscolor.toString();
}
//delete the element
colorsMenu.removeChild(color.parentElement);
}

View File

@ -379,7 +379,7 @@ function HistoryStateAddColor (colorValue) {
this.undo = function () {
redoStates.push(this);
deleteColor(this.colorValue);
ColorModule.deleteColor(this.colorValue);
};
this.redo = function () {
@ -410,7 +410,7 @@ function HistoryStateDeleteColor (colorValue) {
var currentCanvas = currentLayer.context.getImageData(0, 0, canvasSize[0], canvasSize[1]);
currentLayer.context.putImageData(this.canvas, 0, 0);
deleteColor(this.colorValue);
ColorModule.deleteColor(this.colorValue);
this.canvas = currentCanvas;
undoStates.push(this);

View File

@ -553,7 +553,7 @@ if (!window.jscolor) { window.jscolor = (function () {
//saveHistoryState({type: 'deletecolor', colorValue: jsc.picker.owner.toString(), canvas: canvas.context.getImageData(0, 0, canvasSize[0], canvasSize[1])});
new HistoryStateDeleteColor(jsc.picker.owner.toString());
deleteColor(jsc.picker.owner.styleElement);
ColorModule.deleteColor(jsc.picker.owner.styleElement);
}
else if (e.target.className == 'jscolor-picker-bottom') {
//console.log('clicked color picker bottom')
@ -1071,12 +1071,9 @@ if (!window.jscolor) { window.jscolor = (function () {
this.hide = function () {
///console.log(this.styleElement)
if (isPickerOwner()) {
//console.log('color picker hidden')
//set the color to old color, in case the color is a duplicate that hasn't been resolved yet [lospec]
var hexInput = document.getElementById('jscolor-hex-input');
var oldColor = '#'+rgbToHex(hexInput.oldColor);
var oldColor = '#'+Color.rgbToHex(hexInput.oldColor);
this.fromString(oldColor);
document.getElementById('duplicate-color-warning').style.visibility = 'hidden';
@ -1105,7 +1102,7 @@ if (!window.jscolor) { window.jscolor = (function () {
this.exportColor();
//set old color for updating colors on canvas
hexInput.oldColor = hexToRgb(hexInput.value);
hexInput.oldColor = Color.hexToRgb(hexInput.value);
//set the color element to the clicked button
hexInput.colorElement = this.styleElement;

View File

@ -117,12 +117,12 @@ function newPixel (width, height, editorMode, fileContent = null) {
history.pushState(null, null, '/pixel-editor');
//generate default colors
var fg = hslToRgb(Math.floor(Math.random()*255), 230,70);
var bg = hslToRgb(Math.floor(Math.random()*255), 230,170);
var fg = new Color("hsl", Math.floor(Math.random()*255), 230,70).rgb;
var bg = new Color("hsl", Math.floor(Math.random()*255), 230,170).rgb;
//convert colors to hex
var defaultForegroundColor = rgbToHex(fg.r,fg.g,fg.b);
var defaultBackgroundColor = rgbToHex(bg.r,bg.g,bg.b);
var defaultForegroundColor = Color.rgbToHex(fg);
var defaultBackgroundColor = Color.rgbToHex(bg);
//add colors to palette
ColorModule.addColor(defaultForegroundColor).classList.add('selected');

View File

@ -1,25 +0,0 @@
//replaces all of a single color on the canvas with a different color
//input two rgb color objects {r:0,g:0,b:0}
function replaceAllOfColor (oldColor, newColor) {
//convert strings to objects if nessesary
if (typeof oldColor === 'string') oldColor = hexToRgb(oldColor);
if (typeof newColor === 'string') newColor = hexToRgb(newColor);
//create temporary image from canvas to search through
var tempImage = currentLayer.context.getImageData(0, 0, canvasSize[0], canvasSize[1]);
//loop through all pixels
for (var i=0;i<tempImage.data.length;i+=4) {
//check if pixel matches old color
if(tempImage.data[i]==oldColor.r && tempImage.data[i+1]==oldColor.g && tempImage.data[i+2]==oldColor.b){
//change to new color
tempImage.data[i]=newColor.r;
tempImage.data[i+1]=newColor.g;
tempImage.data[i+2]=newColor.b;
}
}
//put temp image back onto canvas
currentLayer.context.putImageData(tempImage,0,0);
}

View File

@ -3,15 +3,11 @@
//=include util/onChildren.js
//=include util/onClick.js
//=include util/onClickChildren.js
//=include util/hexToRgb.js
//=include util/rgbToHex.js
//=include util/rgbToHsl.js
//=include util/hslToRgb.js
//=include lib/cookies.js
//=include _pixelEditorUtility.js
//=include lib/sortable.js
//=include _algorithms.js
//=include Util.js
//=include Color.js
/**init**/
//=include _consts.js
@ -37,8 +33,6 @@
//=include _fill.js
//=include _line.js
//=include _history.js
//=include _deleteColor.js
//=include _replaceAllOfColor.js
//=include _checkerboard.js
//=include _pixelGrid.js
//=include _layer.js

View File

@ -1,79 +0,0 @@
//GET: ajax(String url, Function success [,timeout])
//POST: ajax(String url, Object postData, Function success [,timeout])
Util.ajax = function (url, arg2, arg3, arg4) {
if (typeof arg2 == 'function') {
var success = arg2;
var timeout = arg3 || 10000;
}
else {
var postData = arg2;
var success = arg3;
var timeout = arg4 || 10000;
}
var start = new Date();
console.log('AJAX - STARTING REQUEST', url, '(' + timeout + ')');
//start new request
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var result;
//try to parse as json
try {
result = JSON.parse(this.response);
console.log('AJAX - COMPLETE ('+(new Date()-start)+'ms) - json:', result);
}
catch (e) {
result = this.response;
console.log('AJAX - COMPLETE ('+(new Date()-start)+'ms) - string:', this.response, e);
}
//return result
success(result);
xhr = null;
}
else if (this.readyState == 4) {
console.log('ajax failed', this.readyState, this.status);
success({ error: 'failure' });
}
};
xhr.ontimeout = function(e) {
console.log('ajax request timed out')
success({ error: 'timeout' });
};
if (postData) {
//post request
console.log('post data: ', postData instanceof FormData, postData);
//the the input isn't already formdata, convert it to form data
if (!(postData instanceof FormData)) {
console.log('converting to form data');
var formData = new FormData();
for (var key in postData) {
formData.append(key, postData[key]);
}
postData = formData;
}
//send to server
xhr.open("POST", url, true);
xhr.timeout = timeout;
xhr.send(postData);
}
else {
//get request
xhr.open("GET", url, true);
xhr.timeout = timeout;
xhr.send();
}
return xhr;
}

View File

@ -1,21 +0,0 @@
//put in a hex color code (f464b2 or #f464b2) string
//and get an rgb color object {r:0,g:0,b:0}
//divisor is an optional argument, which makes it so you can get values other than 0-255
function hexToRgb(hex, divisor) {
//if divisor isn't set, set it to one (so it has no effect)
divisor = divisor || 1;
//split given hex code into array of 3 values
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex.trim());
//console.log('hex: '+hex)
//console.log([parseInt(result[1], 16)/divisor, parseInt(result[2], 16)/divisor, parseInt(result[3], 16)/divisor])
//console.log(result)
return result ? {
r: parseInt(result[1], 16)/divisor,
g: parseInt(result[2], 16)/divisor,
b: parseInt(result[3], 16)/divisor
} : null;
}

View File

@ -1,32 +0,0 @@
function hslToRgb(h, s, l){
h /= 255;
s /= 255;
l /= 255;
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
var hue2rgb = function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return {
r:Math.round(r * 255),
g:Math.round(g * 255),
b:Math.round(b * 255)
};
}

View File

@ -1,23 +0,0 @@
//convert rgb values to a hex string for html
function rgbToHex (argument0,g,b) {
var r;
//if the first argument is an object
if (typeof argument0 === 'object'){
r = argument0.r;
g = argument0.g;
b = argument0.b;
}
else
r = argument0;
//console.log('converting rgb('+r+','+g+','+b+') to hex');
//convert a decimal number to 2-digit hex
function componentToHex (c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
return componentToHex(r) + componentToHex(g) + componentToHex(b);
}

View File

@ -1,36 +0,0 @@
//put in red green blue values and get out hue saturation luminosity values
function rgbToHsl(argument0, g, b){
var r;
//if the first argument is an object
if (typeof argument0 === 'object'){
r = argument0.r;
g = argument0.g;
b = argument0.b;
}
else
r = argument0;
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var hue, saturation, luminosity = (max + min) / 2;
if(max == min){
hue = saturation = 0; // achromatic
}else{
var d = max - min;
saturation = luminosity > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: hue = (g - b) / d + (g < b ? 6 : 0); break;
case g: hue = (b - r) / d + 2; break;
case b: hue = (r - g) / d + 4; break;
}
hue /= 6;
}
return {h:hue, s:saturation, l:luminosity};
}