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

0% Statements 0/70
0% Branches 0/10
0% Functions 0/5
0% Lines 0/66

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                                                                                                                                                                                                                                                             
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 palettes = evt.data.palettes;
  const palettesRGB = palettes.map((colors: string[]) =>
    colors.map(hex2GBCrgb)
  );
  const dmgPalette = DMG_PALETTE.colors.map(hex2GBCrgb);
 
  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[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("");
  }
}