JavaScript Python PHP
function executar(source = 'HEX', input = '#FF0000') {
function validateChannels(channels, maxima) {
if (channels.length !== 3 || channels.some((value, index) => !Number.isFinite(value) || value < 0 || value > maxima[index])) {
throw new Error('Canais fora do intervalo permitido.');
}
}
function hslToRgb([h, s, l]) {
validateChannels([h, s, l], [360, 100, 100]);
const hue = (h % 360) / 60;
const saturation = s / 100;
const lightness = l / 100;
const chroma = (1 - Math.abs(2 * lightness - 1)) * saturation;
const x = chroma * (1 - Math.abs(hue % 2 - 1));
const offset = lightness - chroma / 2;
const sectors = [[chroma, x, 0], [x, chroma, 0], [0, chroma, x], [0, x, chroma], [x, 0, chroma], [chroma, 0, x]];
return sectors[Math.floor(hue)].map(value => Math.round((value + offset) * 255));
}
function rgbToHsl(rgb) {
validateChannels(rgb, [255, 255, 255]);
const [r, g, b] = rgb.map(value => value / 255);
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const delta = max - min;
const lightness = (max + min) / 2;
if (delta === 0)
return [0, 0, lightness * 100];
const hue = max === r ? ((g - b) / delta) % 6 : max === g ? (b - r) / delta + 2 : (r - g) / delta + 4;
return [(hue * 60 + 360) % 360, Math.min(100, delta / (1 - Math.abs(2 * lightness - 1)) * 100), lightness * 100];
}
function parseColor(source, input) {
const text = input.trim();
if (source === 'HEX') {
if (!/^#?(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test(text))
throw new Error('Informe HEX com 3 ou 6 dígitos, com ou sem #.');
let hex = text.replace(/^#/, '');
if (hex.length === 3)
hex = [...hex].map(digit => digit + digit).join('');
return [0, 2, 4].map(index => Number.parseInt(hex.slice(index, index + 2), 16));
}
if (source !== 'RGB' && source !== 'HSL')
throw new Error('Selecione HEX, RGB ou HSL.');
const name = source.toLowerCase();
const unwrapped = text.match(new RegExp(`^${name}\\((.*)\\)$`, 'i'));
const body = unwrapped ? unwrapped[1] : text;
const number = '(\\d+(?:\\.\\d+)?|\\.\\d+)';
const percent = source === 'HSL' ? '%' : '';
const match = body.match(new RegExp(`^\\s*${number}\\s*,\\s*${number}${percent}\\s*,\\s*${number}${percent}\\s*$`));
if (!match)
throw new Error(source === 'RGB' ? 'Use rgb(255, 0, 0) ou 255, 0, 0. Sem transparência.' : 'Use hsl(360, 100%, 50%) ou 360, 100%, 50%. Sem transparência.');
const channels = match.slice(1).map(Number);
validateChannels(channels, source === 'RGB' ? [255, 255, 255] : [360, 100, 100]);
return source === 'HSL' ? hslToRgb(channels) : channels.map(Math.round);
}
function convertColor(source, input) {
const rgb = parseColor(source, input);
const hsl = rgbToHsl(rgb).map(value => Math.round(value * 100) / 100);
if (hsl[0] === 360)
hsl[0] = 0;
return {
hex: '#' + rgb.map(value => value.toString(16).padStart(2, '0')).join('').toUpperCase(),
rgb: `rgb(${rgb.join(', ')})`,
hsl: `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`,
};
}
return convertColor(source, input);
}
import math
import re
def executar(source="HEX", input="#FF0000"):
text = input.strip()
def rounded(value): return math.floor(value + 0.5)
if source == "HEX":
if not re.fullmatch(r"#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})", text): raise ValueError("Informe HEX com 3 ou 6 dígitos.")
text = text.lstrip("#")
if len(text) == 3: text = "".join(c * 2 for c in text)
rgb = [int(text[i:i+2], 16) for i in (0, 2, 4)]
elif source in ("RGB", "HSL"):
wrapper = re.fullmatch(source.lower() + r"\((.*)\)", text, re.I)
body = wrapper[1] if wrapper else text
number = r"(\d+(?:\.\d+)?|\.\d+)"
percent = "%" if source == "HSL" else ""
match = re.fullmatch(r"\s*" + number + r"\s*,\s*" + number + percent + r"\s*,\s*" + number + percent + r"\s*", body, re.ASCII)
if not match: raise ValueError("Use três canais separados por vírgula; HSL exige % em saturação/luminosidade. Sem transparência.")
values = [float(value) for value in match.groups()]
maxima = (255, 255, 255) if source == "RGB" else (360, 100, 100)
if any(not math.isfinite(v) or not 0 <= v <= m for v, m in zip(values, maxima)): raise ValueError("Canais fora do intervalo.")
if source == "RGB": rgb = [rounded(v) for v in values]
else:
h, s, l = values[0] % 360 / 60, values[1] / 100, values[2] / 100
chroma = (1 - abs(2 * l - 1)) * s
x = chroma * (1 - abs(h % 2 - 1))
sectors = [(chroma,x,0),(x,chroma,0),(0,chroma,x),(0,x,chroma),(x,0,chroma),(chroma,0,x)]
rgb = [rounded((v + l - chroma / 2) * 255) for v in sectors[math.floor(h)]]
else: raise ValueError("Selecione HEX, RGB ou HSL.")
r, g, b = [v / 255 for v in rgb]
maximum, minimum = max(r, g, b), min(r, g, b)
delta, lightness = maximum - minimum, (maximum + minimum) / 2
if not delta: hsl = [0, 0, lightness * 100]
else:
hue = (g-b) / delta % 6 if maximum == r else (b-r) / delta + 2 if maximum == g else (r-g) / delta + 4
hsl = [(hue * 60 + 360) % 360, min(100, delta / (1 - abs(2 * lightness - 1)) * 100), lightness * 100]
hsl = [rounded(v * 100) / 100 for v in hsl]
if hsl[0] == 360: hsl[0] = 0
numbers = [f"{v:.2f}".rstrip("0").rstrip(".") for v in hsl]
return {"hex":"#" + "".join(f"{v:02X}" for v in rgb), "rgb":"rgb(" + ", ".join(map(str, rgb)) + ")", "hsl":f"hsl({numbers[0]}, {numbers[1]}%, {numbers[2]}%)"}
<?php
function executar($source = 'HEX', $input = '#FF0000') {
$text = trim($input);
if ($source === 'HEX') {
if (!preg_match('/^#?(?:[0-9a-f]{3}|[0-9a-f]{6})$/iD', $text)) throw new InvalidArgumentException('Use HEX com 3 ou 6 dígitos.');
$hex = ltrim($text, '#');
if (strlen($hex) === 3) $hex = implode('', array_map(fn($c) => $c.$c, str_split($hex)));
$rgb = array_map(fn($i) => hexdec(substr($hex, $i, 2)), [0,2,4]);
} elseif ($source === 'RGB' || $source === 'HSL') {
$body = preg_match('/^'.strtolower($source).'\((.*)\)$/iD', $text, $wrapper) ? $wrapper[1] : $text;
$number = '([0-9]+(?:\.[0-9]+)?|\.[0-9]+)';
$percent = $source === 'HSL' ? '%' : '';
if (!preg_match('/^\s*'.$number.'\s*,\s*'.$number.$percent.'\s*,\s*'.$number.$percent.'\s*$/D', $body, $match)) throw new InvalidArgumentException('Use três canais; HSL exige % em saturação/luminosidade.');
$values = array_map('floatval', array_slice($match, 1));
$maxima = $source === 'RGB' ? [255,255,255] : [360,100,100];
foreach ($values as $i => $value) if (!is_finite($value) || $value < 0 || $value > $maxima[$i]) throw new InvalidArgumentException('Canais fora do intervalo.');
if ($source === 'RGB') $rgb = array_map(fn($v) => (int)floor($v + 0.5), $values);
else {
[$h,$s,$l] = [fmod($values[0],360)/60, $values[1]/100, $values[2]/100];
$chroma = (1 - abs(2*$l - 1))*$s;
$x = $chroma*(1 - abs(fmod($h,2) - 1));
$sectors = [[$chroma,$x,0],[$x,$chroma,0],[0,$chroma,$x],[0,$x,$chroma],[$x,0,$chroma],[$chroma,0,$x]];
$rgb = array_map(fn($v) => (int)floor(($v + $l - $chroma/2)*255 + 0.5), $sectors[(int)floor($h)]);
}
} else throw new InvalidArgumentException('Selecione HEX, RGB ou HSL.');
[$r,$g,$b] = array_map(fn($v) => $v/255, $rgb);
$max = max($r,$g,$b); $min = min($r,$g,$b); $delta = $max-$min; $l = ($max+$min)/2;
if ($delta == 0) $hsl = [0,0,$l*100];
else {
$hue = $max === $r ? fmod(($g-$b)/$delta,6) : ($max === $g ? ($b-$r)/$delta+2 : ($r-$g)/$delta+4);
$hsl = [fmod($hue*60+360,360), min(100,$delta/(1-abs(2*$l-1))*100), $l*100];
}
$hsl = array_map(fn($v) => floor($v*100 + 0.5)/100, $hsl);
if ($hsl[0] == 360) $hsl[0] = 0;
$hsl = array_map(fn($v) => rtrim(rtrim(number_format($v,2,'.',''), '0'), '.'), $hsl);
return ['hex'=>'#'.implode('',array_map(fn($v) => sprintf('%02X',$v),$rgb)), 'rgb'=>'rgb('.implode(', ',$rgb).')', 'hsl'=>'hsl('.$hsl[0].', '.$hsl[1].'%, '.$hsl[2].'%)'];
}