형식화된 배열을 사용하는 JS에서 가장 빠른 색상 변환 및 블렌딩

안녕,

https://pixa.pics/인 픽셀 아트 편집기를 만들고 있었고 이 UI에서 최상의 성능을 얻기 위해 많은 연구를 했습니다... 그리고 최대 512x512 이미지를 조작하기 위해 캔버스의 2D 컨텍스트를 선택했습니다. , 색상 등을 비교하여 이미지의 일부를 채우십시오 ...

100달러짜리 Samsung J3에서도 훌륭하게 작동합니다. ;)

하지만 레이어링 시스템의 경우 16진수(r, g, b, a)에 저장된 색상을 "#e2e2e2ff"와 같은 문자열로 불투명도와 가중치를 다르게 혼합해야 했습니다...

먼저 "#fff"(#rgb) 또는 "#fffe"(#rgba)가 "#ffffffff"(#rrggbbaa)를 가져오도록 색상 형식을 지정해야 했습니다.

function format_color(color) { // Supports #fff (short rgb), #fff0 (short rgba), #e2e2e2 (full rgb) and #e2e2e2ff (full rgba)

        const hex = color || "#00000000";
        const hex_length = hex.length;

        if(hex_length === 9) {

            return hex;

        } else if (hex_length === 7) {

            return hex.concat("ff");
        } else if (hex_length === 5) {

            const a = hex.charAt(1), b = hex.charAt(2), c = hex.charAt(3), d = hex.charAt(4);
            return "#".concat(a, a, b, b, c, c, d, d);
        } else if (hex_length === 4) {

            const a = hex.charAt(1), b = hex.charAt(2), c = hex.charAt(3);
            return "#".concat(a, a, b, b, c, c, "ff");
        }
    }



그런 다음 32바이트의 unsigned int를 사용합니다(10진수에서 0-255 범위의 8바이트 unsigned int 배열로 변환하기 위한 배열).

function get_rgba_from_hex(hex) {

        return new Uint8ClampedArray(Uint32Array.of(parseInt(hex.slice(1), 16)).buffer).reverse();
}


따라서 수행하는 작업은 16진수 문자열을 가져온 다음 첫 번째 문자를 제거하여 16진수(기본 16) 값의 8 길이 문자열로 줄입니다. 이 문자열은 일반적으로 숫자로 표시될 수 있는 정수로 변환됩니다. 기본 10, 그것은 Uint32에 완벽하게 맞고 이전에 말한 것을 생성하는 하나의 요소로 배열을 형성합니다. 즉, "이진"값에서 생성하는 Uint8 숫자(0-255)의 배열입니다. 하나의 큰 수의 32바이트 집합(이것이 우리가 배열의 버퍼를 호출하는 이유입니다) 그리고 [a, r, g, b]를 얻을 때 마지막으로 역방향 메서드를 호출합니다.

첫 번째 문자만 슬라이스하여 올바른 순서로 반환하도록 정렬한 0에서 255 사이로 고정된 4개의 숫자로 변환된 숫자로 변환하기 때문에 빠릅니다.

그런 다음 역연산을 수행해야 합니다. 음.. 역연산은 역순으로 "수동"으로 넣기 때문에 역방향 메서드가 필요하지 않습니다. 해시태그가 없으면 문자열의 구걸 시 패딩해야 하는 일부 0이 부족하고 padstart 함수는 성능이 낮기 때문에 8개의 0을 추가하고 문자열의 끝에서 8자를 가져옵니다.

function get_hex_color_from_rgba_values (r, g, b, a) {

        return "#".concat("00000000".concat(new Uint32Array(Uint8ClampedArray.of(a, b, g, r).buffer)[0].toString(16)).slice(-8));
}


그래서! 그런 다음 HSL 색상 변환도 얻었지만 혁신적인 생각이 부족합니다. 이것은 매우 일반적인 기능입니다.

function rgb_to_hsl (r, g, b) {

        r /= 255, g /= 255, b /= 255;
        const max = Math.max(r, g, b), min = Math.min(r, g, b);
        let h, s, l = (max + min) / 2;

        if(max === min){
            h = s = 0; // achromatic
        }else {
            const d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
            switch(max){
                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                case g: h = (b - r) / d + 2; break;
                case b: h = (r - g) / d + 4; break;
            }
            h /= 6;
        }

        return Array.of(parseInt(h * 360), parseInt(s * 100), parseInt(l * 100));
}


function hsl_to_rgb(h, s, l) {

        h /= 360, s /= 100, l /= 100;

        let r, g, b;
        if (s === 0) {
            r = g = b = l;
        } else {
            const hue_to_rgb = function(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 = hue_to_rgb(p, q, h + 1 / 3);
            g = hue_to_rgb(p, q, h);
            b = hue_to_rgb(p, q, h - 1 / 3);
        }

        return Uint8ClampedArray.of(r * 255, g * 255, b * 255);
}


조금 복잡한 기능은 설명하지 않겠습니다. 하지만 모든 것이 잘 작동합니다!

하지만 블렌딩을 살펴봅시다 ;)

blend_colors (color_a, color_b, amount = 1, should_return_transparent = false, alpha_addition = false) => {

        color_a = format_color(color_a);
        // If we blend the first color with the second with 0 "force", return transparent
        if(amount === 0 && color_b !== "hover" && should_return_transparent) {

            return "#00000000";
        }

        // Make sure we have a color based on the 4*2 hex char format

        if(color_b === "hover") {

            const rgba = get_rgba_from_hex(color_a);
            const hsl = rgb_to_hsl(rgba[0], rgba[1], rgba[2], rgba[3]);
            const rgb = hsl_to_rgb(hsl[0], hsl[1], parseInt(hsl[2] >= 50 ? hsl[2]/2: hsl[2]*2));
            color_b = get_hex_color_from_rgba_values(rgb[0], rgb[1], rgb[2], 255);
        }else {

            color_b = format_color(color_b);
        }
        // If the second color is transparent, return transparent (useful when we need to use transparent to "erase"
        if(should_return_transparent && color_b === "#00000000" && amount === 1) { return "#00000000"; }

        // Extract RGBA from both colors
        const base = get_rgba_from_hex(color_a);
        const added = get_rgba_from_hex(color_b);

        // If the opacity of color B is full and that we blend with an amount of 1, we directly return the color B 
        if(added[3] === 255 && amount === 1) { return color_b; }

        const ba3 = base[3] / 255;
        const ad3 = (added[3] / 255) * amount;

        let mix = new Uint8ClampedArray(4);
        let mi3 = 0;

        if (ba3 > 0 && ad3 > 0) {

            if(alpha_addition) { // Sometimes we add

                mi3 = ad3 + ba3;
            }else { // Sometimes we blend

                mi3 = 1 - ((1 - ad3) * (1 - ba3));
            }

            const ao = ad3 / mi3;
            const bo = ba3 * (1 - ad3) / mi3;

            mix[0] = parseInt(added[0] * ao + base[0] * bo); // red
            mix[1] = parseInt(added[1] * ao + base[1] * bo); // green
            mix[2] = parseInt(added[2] * ao + base[2] * bo); // blue
        }else if(ad3 > 0) {

            mi3 = added[3] / 255;

            mix[0] = added[0];
            mix[1] = added[1];
            mix[2] = added[2];
        }else {

            mi3 = base[3] / 255;

            mix[0] = base[0];
            mix[1] = base[1];
            mix[2] = base[2];
        }

        if(alpha_addition) {
            mi3 /= 2;
        }

        mix[3] = parseInt(mi3 * 255);

        return get_hex_color_from_rgba_values(mix[0], mix[1], mix[2], mix[3]);
}


코드: https://gist.github.com/vipertechofficial/c5353b4df4910aebffed4f0a07fd725e

좋은 한 주 보내세요, ;)
즐기다!

좋은 웹페이지 즐겨찾기