/**
* base64 data URL 파비콘의 밝기와 내용물 유무를 분석합니다.
*
* @param {number} [threshold=240] - 밝기 기준값 (0~255). 이 값 이상이면 색 반전 적용.
* @returns {{ analyze: (src: string) => Promise<{ isEmpty: boolean, needsInvert: boolean }> }}
*/
export function useFaviconBg(threshold = 240) {
const cache = new Map();
/**
* @param {string} src - data:image/png;base64,... 형식의 URL
* @returns {Promise<{ isEmpty: boolean, needsInvert: boolean }>}
*/
const analyze = (src) => {
if (!src) return Promise.resolve({ isEmpty: true, needsInvert: false });
if (cache.has(src)) return Promise.resolve(cache.get(src));
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
try {
const canvas = document.createElement('canvas');
canvas.width = img.width || 16;
canvas.height = img.height || 16;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);
const totalPixels = canvas.width * canvas.height;
let total = 0;
let count = 0;
for (let i = 0; i < data.length; i += 4) {
if (data[i + 3] < 10) continue; // 투명 픽셀 제외
// perceived luminance
total += 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
count++;
}
// 전체 픽셀의 2% 미만이 불투명하면 빈 이미지로 간주
const isEmpty = count < totalPixels * 0.02;
const needsInvert = !isEmpty && count > 0 && total / count >= threshold;
const result = { isEmpty, needsInvert };
cache.set(src, result);
resolve(result);
} catch {
resolve({ isEmpty: false, needsInvert: false });
}
};
img.onerror = () => resolve({ isEmpty: true, needsInvert: false });
img.src = src;
});
};
return { analyze };
}파비콘 src 입력
│
▼
┌─────────────┐ 없음
│ src 있음? │ ─────────▶ { isEmpty: true, needsInvert: false }
└─────────────┘
│ 있음
▼
┌─────────────┐ 있음
│ 캐시 있음? │ ─────────▶ 캐시된 결과 즉시 반환
└─────────────┘
│ 없음
▼
이미지 로드
Canvas에 그리기
픽셀 데이터 추출
│
▼
픽셀 순회 (4개씩: R G B A)
│
├─ Alpha < 10 → 투명 픽셀 → 건너뜀
│
└─ Alpha >= 10 → 불투명 픽셀 → 밝기 계산 후 누적
│
▼
isEmpty 판단
needsInvert 판단
│
▼
캐시 저장 후 반환analyze(src)
│
├─ 빈 이미지인가? (isEmpty)
│
└─ 흰색 이미지인가? (needsInvert)전체 픽셀 수 (width × height)
│
× 0.02 (2%)
│
▼
기준 픽셀 수
불투명 픽셀 수 < 기준 픽셀 수
│
▼
isEmpty = true → el.hidden = true (화면에서 숨김)
예시 (16×16 파비콘)
전체 픽셀: 16 × 16 = 256개
기준: 256 × 0.02 = 5.12개
불투명 픽셀 0개 → isEmpty ✓ (완전 투명 이미지)
불투명 픽셀 3개 → isEmpty ✓ (노이즈 수준)
불투명 픽셀 6개 → isEmpty ✗ (정상 이미지로 간주)밝기 = 0.299 × R + 0.587 × G + 0.114 × B
빨강 29% 초록 59% 파랑 11%모든 불투명 픽셀의 밝기 합계
÷
불투명 픽셀 수
│
▼
평균 밝기
평균 밝기 >= 240 (threshold)
│
▼
needsInvert = true → filter: invert(1) 적용
밝기 기준 예시
0 ──────────────────────────────────── 255
│ │ │
검정 240 흰색
▲
여기 이상이면
반전 적용Input code here첫 번째 행: analyze("data:image/png;base64,ABC...") → Canvas 분석 실행 → 캐시 저장
두 번째 행: analyze("data:image/png;base64,ABC...") → 캐시에서 즉시 반환 (분석 생략)
세 번째 행: analyze("data:image/png;base64,ABC...") → 캐시에서 즉시 반환 (분석 생략)