Last active
December 30, 2022 12:38
-
-
Save KillyMXI/936450451f76e29e5c1f04604c453c1a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// #region DIGRAMS =============================================================================== | |
function computeDigramColors( | |
/** @type { Uint8Array } */ uint8Array, | |
paletteKind = 'p5_lerp', | |
blendingMiddlePoint = 0.5 | |
) { | |
const digramStats = Array.from({ length: 65536 }, () => ({ | |
count: 0, // digram occurrences | |
offsetAcc: 0.0 // accumulator to compute average position | |
})); | |
const pairsNumber = uint8Array.length - 1; | |
let maxDigramCount = 0; | |
// loop over input once | |
for (let i = 0; i < pairsNumber; i++) { | |
const digram = uint8Array[i] * 256 + uint8Array[i+1]; | |
const stat = digramStats[digram]; | |
stat.count++; | |
stat.offsetAcc += i; | |
if (stat.count > maxDigramCount) { | |
maxDigramCount = stat.count; | |
} | |
} | |
const colors = []; | |
const palette = palettes[paletteKind]; | |
// loop over 256*256 digram variants once | |
for (let i = 0; i < 65536; i++) { | |
const stat = digramStats[i]; | |
const avgOffset = stat.offsetAcc / (pairsNumber || 1) / (stat.count || 1); | |
const pixelColor = (paletteKind === 'p5_lerp') | |
? lerpColor(startColor, endColor, avgOffset) | |
: color(sliceByLength(palette, 4 * Math.trunc(avgOffset * 255), 3)); | |
const amplitude = Math.log10(stat.count) / Math.log10(maxDigramCount); | |
// approximate HSL lightness by blending with black or white color depending on the amplitude | |
const blendedColor = (amplitude < blendingMiddlePoint) | |
? lerpColor(blackColor, pixelColor, amplitude / blendingMiddlePoint) | |
: lerpColor(pixelColor, whiteColor, (amplitude - blendingMiddlePoint) / (1 - blendingMiddlePoint)) | |
colors.push(red(blendedColor), green(blendedColor), blue(blendedColor), 0x00); | |
} | |
return colors; | |
} | |
// #endregion | |
// #region LAYERED DISTRIBUTION ================================================================== | |
function computeLayeredDistributionColors( | |
/** @type { Uint8Array } */ uint8Array, | |
paletteKind = 'p5_lerp', | |
blendingMiddlePoint = 0.5 | |
) { | |
const layerStats = Array.from({ length: 256 }, () => ({ | |
maxCount: 0, // max byte occurrence number in the layer | |
byteCounts: Array.from({ length: 256 }, () => 0) // each byte occurrence numbers in the layer | |
})); | |
const layerSize = Math.ceil(uint8Array.length / 256); | |
// loop over input once | |
for (let i = 0; i < uint8Array.length; i++) { | |
const byte = uint8Array[i]; | |
const layerIndex = Math.floor(i / layerSize); | |
const stat = layerStats[layerIndex]; | |
const newCount = ++stat.byteCounts[byte]; | |
if (newCount > stat.maxCount) { | |
stat.maxCount = newCount; | |
} | |
} | |
let maxCount = 0; // max byte occurrence number across all layers | |
// loop over 256 layers once | |
for (let layerIndex = 0; layerIndex < 256; layerIndex++) { | |
const stat = layerStats[layerIndex]; | |
if (stat.maxCount > maxCount) { | |
maxCount = stat.maxCount; | |
} | |
} | |
const colors = []; | |
const palette = palettes[paletteKind]; | |
// loop over 256*256 layer/byte pairs once | |
for (let layerIndex = 0; layerIndex < 256; layerIndex++) { | |
const stat = layerStats[layerIndex]; | |
const pixelColor = (paletteKind === 'p5_lerp') | |
? lerpColor(startColor, endColor, layerIndex / 255) | |
: color(sliceByLength(palette, 4 * layerIndex, 3)); | |
for (let byte = 0; byte < 256; byte++) { | |
const byteCount = stat.byteCounts[byte]; | |
const amplitude = Math.log10(byteCount) / Math.log10(maxCount); | |
// approximate HSL lightness by blending with black or white color depending on the amplitude | |
const blendedColor = (amplitude < blendingMiddlePoint) | |
? lerpColor(blackColor, pixelColor, amplitude / blendingMiddlePoint) | |
: lerpColor(pixelColor, whiteColor, (amplitude - blendingMiddlePoint) / (1 - blendingMiddlePoint)) | |
colors.push(red(blendedColor), green(blendedColor), blue(blendedColor), 0x00); | |
} | |
} | |
return colors; | |
} | |
// #endregion | |
// #region UTILS ================================================================================= | |
function sliceByLength (array, start, length) { | |
return array.slice(start, start + length); | |
} | |
// #endregion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment