<canvas id="canvas"></canvas>
<h4 id="fill-area"></h4>
canvas {
background-color: lightgrey;
}
// 이미지 로드 함수
const loadImage = (src) => {
return fetch(src)
.then((r) => {
if (!r.ok) throw new Error("Network response was not ok");
return r.blob();
})
.then((blob) => createImageBitmap(blob));
};
// 이미지 데이터 압축 함수
const getCompressedImageData = (imageData, scale) => {
const compressSize = Math.floor(1 / scale);
const compressWidth = Math.ceil(imageData.width / compressSize);
const compressHeight = Math.ceil(imageData.height / compressSize);
const tempData = new Uint8ClampedArray(
compressWidth * compressHeight * 4
);
for (let y = 0; y < compressHeight; y++) {
for (let x = 0; x < compressWidth; x++) {
const index = y * compressWidth * 4 + x * 4;
const rgba = imageData.data.slice(
(y * compressSize * imageData.width + x * compressSize) * 4,
(y * compressSize * imageData.width + x * compressSize) * 4 + 4
);
tempData.set(rgba, index);
}
}
return new ImageData(tempData, compressWidth, compressHeight);
};
// 영역 비율 계산함수
const calculateFillArea = (baseImageData, colorImageData) => {
let total = 0;
let progress = 0;
for (let y = 0; y < baseImageData.height; y++) {
for (let x = 0; x < baseImageData.width; x++) {
const index = (y * baseImageData.width + x) * 4;
const baseRgba = baseImageData.data.slice(index, index + 4);
const colorRgba = colorImageData.data.slice(index, index + 4);
if (baseRgba[3] === 255) total += 1;
if (baseRgba[3] === 255 && colorRgba[3] === 255) progress += 1;
}
}
return ((progress / total) * 100).toFixed(2) + "%";
};
// 이미지 합성 함수
const compositeImageData = (baseImageData, colorImageData) => {
const tempData = new Uint8ClampedArray(
baseImageData.width * baseImageData.height * 4
);
for (let y = 0; y < baseImageData.height; y++) {
for (let x = 0; x < baseImageData.width; x++) {
const index = (y * baseImageData.width + x) * 4;
const baseRgba = baseImageData.data.slice(index, index + 4);
const colorRgba = colorImageData.data.slice(index, index + 4);
if (baseRgba[3] === 255 && colorRgba[3] === 255) {
tempData.set(colorRgba, index);
} else {
tempData.set(baseRgba, index);
}
}
}
return new ImageData(
tempData,
baseImageData.width,
baseImageData.height
);
};
const task = async () => {
const canvas = document.querySelector("#canvas");
const fillArea = document.querySelector("#fill-area");
const baseImageSource = `https://w-log.dev/content/images/2024/05/baseImage-1.png`;
const colorImageSource = `https://w-log.dev/content/images/2024/05/colorImage-1.png`;
const offCanvas = new OffscreenCanvas(640, 360);
const ctx = offCanvas.getContext("2d");
const [baseImageData, colorImageData] = await Promise.all(
[baseImageSource, colorImageSource].map(async (src) => {
const bitmap = await loadImage(src);
offCanvas.width = bitmap.width;
offCanvas.height = bitmap.height;
ctx.clearRect(0, 0, 9999, 9999);
ctx.drawImage(bitmap, 0, 0);
return getCompressedImageData(
ctx.getImageData(0, 0, bitmap.width, bitmap.height),
0.25 // 이미지 데이터를 1/4로 압축
);
})
);
const fillPercentage = calculateFillArea(baseImageData, colorImageData);
const compositedImageData = compositeImageData(baseImageData, colorImageData);
fillArea.textContent = fillPercentage;
canvas.width = compositedImageData.width;
canvas.height = compositedImageData.height;
canvas.getContext("2d").putImageData(compositedImageData, 0, 0);
};
task();
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.