Published
Edited
Feb 19, 2022
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
canvas = {
const context = DOM.context2d(image.naturalWidth * 2 + 10, image.naturalHeight, 1)
context.drawImage(image, 0, 0)
const image_data = context.getImageData(0, 0, image.naturalWidth, image.naturalHeight)
const rgba_data = image_data.data

context.translate(image.width + 10, 0)
renderDownsampledImage(context, rgba_data, image.width, image.height)
return context.canvas
}
Insert cell
function renderDownsampledImage(context, rgba_data, w, h) {
for (let row = 0; row < h / tile_size; row++) {
for (let col = 0; col < w / tile_size; col++) {
context.save()
context.translate(col * tile_size, row * tile_size)
const index = (row * tile_size * w + col * tile_size) * 4
const r = average(rgba_data, row, col, 0)
const g = average(rgba_data, row, col, 1)
const b = average(rgba_data, row, col, 2)

if (monochrome[0] === "Gray") {
const gray = Math.floor((0.299 * r / 255 + 0.587 * g / 255 + 0.114 * b / 255) * 255)
drawTile(context, gray, gray, gray)
}
else {
drawTile(context, r, g, b)
}

context.restore()
}
}
}
Insert cell
function average(data, row, col, stride) {
const index = (row * tile_size * image.width * 4) + (col * tile_size * 4)
let total = 0
let count = 0
for (let i = 0; i < tile_size; i++) {

if ((col + i) * tile_size > image.width) continue;

for (let j = 0; j < tile_size; j++) {

if ((row + j) * tile_size > image.height) continue;

// ** to convert rgba to rgb you need to blend the background, like so
// Target.R = ((1 - Source.A) * BGColor.R) + (Source.A * Source.R)
// Target.G = ((1 - Source.A) * BGColor.G) + (Source.A * Source.G)
// Target.B = ((1 - Source.A) * BGColor.B) + (Source.A * Source.B)

// assume background = white (255, 255, 255)

const color = data[index + (j * image.width + i) * 4 + stride]
const alpha = data[index + (j * image.width + i) * 4 + 3] / 255

total += (1 - alpha) * 255 + (alpha * color)
count++
}
}
return +(total / count).toFixed(0)
}
Insert cell
function drawTile(context, r, g, b, a) {
context.save()
const s = Math.trunc(tile_size)
context.fillStyle = rgbToHex(r, g, b)
context.fillRect(0, 0, s, s)
context.restore()
}
Insert cell
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
Insert cell
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b)
}
Insert cell
function rgbaToHex(r, g, b, a) {
return rgbToHex(r, g, b) + componentToHex(a)
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more