All files / src/components/world ColorizedImage.worker.ts

0% Statements 0/72
0% Branches 0/14
0% Functions 0/5
0% Lines 0/68

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129                                                                                                                                                                                                                                                                 
import { DMG_PALETTE } from "consts";
import { hex2GBCrgb } from "shared/lib/helpers/color";
 
// eslint-disable-next-line no-restricted-globals
const workerCtx: Worker = self as unknown as Worker;
 
const indexColour = (g: number) => {
  Iif (g < 65) {
    return 3;
  }
  Iif (g < 130) {
    return 2;
  }
  Iif (g < 205) {
    return 1;
  }
  return 0;
};
 
interface CacheRecord {
  canvas: OffscreenCanvas;
  ctx: OffscreenCanvasRenderingContext2D;
  img: ImageBitmap;
}
 
export interface ColorizedImageResult {
  id: number;
  width: number;
  height: number;
  canvasImage: ImageBitmap;
}
 
const cache: Record<string, CacheRecord> = {};
const TILE_COLOR_PALETTE = 0x7;
 
workerCtx.onmessage = async (evt) => {
  const id = evt.data.id;
  const src = evt.data.src;
  const tiles = evt.data.tiles;
  const previewAsMono = evt.data.previewAsMono;
  const monoBGP = evt.data.monoBGP ?? [0, 1, 2, 3];
  const palettes = evt.data.palettes;
  const colorCorrectionFn = hex2GBCrgb(evt.data.colorCorrection);
  const palettesRGB = palettes.map((colors: string[]) =>
    colors.map(colorCorrectionFn),
  );
  const dmgPalette = DMG_PALETTE.colors.map(colorCorrectionFn);
 
  let canvas: OffscreenCanvas;
  let ctx: OffscreenCanvasRenderingContext2D;
  let img: ImageBitmap;
 
  if (cache[src]) {
    // Using Cached Data
    canvas = cache[src].canvas;
    ctx = cache[src].ctx;
    img = cache[src].img;
  } else {
    // Fetch New Data
    const imgblob = await fetch(src).then((r) => r.blob());
    img = await createImageBitmap(imgblob);
 
    canvas = new OffscreenCanvas(img.width, img.height);
 
    const tmpCtx = canvas.getContext("2d", { willReadFrequently: true });
    Iif (!tmpCtx) {
      return;
    }
    ctx = tmpCtx;
 
    cache[src] = {
      canvas,
      ctx,
      img,
    };
  }
 
  const width = img.width;
  const height = img.height;
  const tileWidth = Math.floor(width / 8);
  const tileHeight = Math.floor(height / 8);
  const tilesLength = tileWidth * tileHeight;
 
  canvas.width = width;
  canvas.height = height;
 
  ctx.drawImage(img, 0, 0, width, height);
 
  const imageData = ctx.getImageData(0, 0, width, height);
  const data = imageData.data;
 
  for (let t = 0; t < tilesLength; t++) {
    const tX = t % tileWidth;
    const tY = Math.floor(t / tileWidth);
    const palette =
      palettesRGB[tiles[t] & TILE_COLOR_PALETTE] || palettesRGB[0];
    const p1X = tX * 8;
    const p2X = p1X + 8;
    const p1Y = tY * 8;
    const p2Y = p1Y + 8;
    for (let pX = p1X; pX < p2X; pX++) {
      for (let pY = p1Y; pY < p2Y; pY++) {
        const index = (pX + pY * width) * 4;
        const colorIndex = indexColour(data[index + 1]);
        const color = previewAsMono
          ? dmgPalette[monoBGP[colorIndex]]
          : palette[colorIndex];
        data[index] = color.r;
        data[index + 1] = color.g;
        data[index + 2] = color.b;
        data[index + 3] = 255;
      }
    }
  }
 
  ctx.putImageData(imageData, 0, 0);
 
  const canvasImage = canvas.transferToImageBitmap();
  workerCtx.postMessage({ id, width, height, canvasImage }, [canvasImage]);
};
 
// -----------------------------------------------------------------
 
export default class W extends Worker {
  constructor() {
    super("");
  }
}