All files / src/shared/lib/tiles autoFlip.ts

100% Statements 38/38
100% Branches 2/2
100% Functions 3/3
100% Lines 34/34

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 1084x         4x             4x               4x                 5x 5x   5x 5x 5x 3x 3x     5x 90x 2880x 2880x   2880x     2880x               2880x 2880x   2880x 2880x 2880x   2880x 2880x 2880x   2880x                                       3525x   2880x   2675x 2675x     205x 205x 205x         5x            
import {
  TILE_SIZE,
  TILE_COLOR_PROP_FLIP_VERTICAL,
  TILE_COLOR_PROP_FLIP_HORIZONTAL,
} from "consts";
import {
  IndexedImage,
  sliceIndexedImage,
  indexedImageTo2bppTileData,
  flipIndexedImageX,
  flipIndexedImageY,
} from "shared/lib/tiles/indexedImage";
import { TileLookup, hashTileData } from "shared/lib/tiles/tileData";
 
interface AutoFlipResult {
  tileData: Uint8Array[];
  tileAttrs: number[];
  tilesetData: Uint8Array[];
}
 
export const autoFlipTiles = ({
  indexedImage,
  tileColors,
  commonTileData,
}: {
  indexedImage: IndexedImage;
  tileColors: readonly number[];
  commonTileData: Uint8Array[];
}): AutoFlipResult => {
  const xTiles = Math.floor(indexedImage.width / TILE_SIZE);
  const yTiles = Math.floor(indexedImage.height / TILE_SIZE);
 
  const newTileData: Uint8Array[] = [];
  const newTileColors = [...tileColors];
  const tileLookup: TileLookup = commonTileData.reduce((memo, tileData) => {
    memo[hashTileData(tileData)] = tileData;
    return memo;
  }, {} as TileLookup);
 
  for (let tyi = 0; tyi < yTiles; tyi++) {
    for (let txi = 0; txi < xTiles; txi++) {
      const index = tyi * xTiles + txi;
      const attr = tileColors[index];
      const clearedAttr =
        attr &
        ~(TILE_COLOR_PROP_FLIP_VERTICAL | TILE_COLOR_PROP_FLIP_HORIZONTAL);
 
      const slicedTile = sliceIndexedImage(
        indexedImage,
        txi * TILE_SIZE,
        tyi * TILE_SIZE,
        TILE_SIZE,
        TILE_SIZE,
      );
 
      const origData = indexedImageTo2bppTileData(slicedTile);
      const origHash = hashTileData(origData);
 
      const slicedTileFlipX = flipIndexedImageX(slicedTile);
      const slicedTileFlipY = flipIndexedImageY(slicedTile);
      const slicedTileFlipXY = flipIndexedImageX(slicedTileFlipY);
 
      const flipX = indexedImageTo2bppTileData(slicedTileFlipX);
      const flipY = indexedImageTo2bppTileData(slicedTileFlipY);
      const flipXY = indexedImageTo2bppTileData(slicedTileFlipXY);
 
      const variants = [
        { data: origData, hash: origHash, mask: 0 },
        {
          data: flipX,
          hash: hashTileData(flipX),
          mask: TILE_COLOR_PROP_FLIP_HORIZONTAL,
        },
        {
          data: flipY,
          hash: hashTileData(flipY),
          mask: TILE_COLOR_PROP_FLIP_VERTICAL,
        },
        {
          data: flipXY,
          hash: hashTileData(flipXY),
          mask: TILE_COLOR_PROP_FLIP_HORIZONTAL | TILE_COLOR_PROP_FLIP_VERTICAL,
        },
      ];
 
      // Check if any variant already exists
      const matched = variants.find((v) => tileLookup[v.hash]);
 
      if (matched) {
        // If we found a match, use it and set flip attributes
        newTileData.push(matched.data);
        newTileColors[index] = clearedAttr | matched.mask;
      } else {
        // Otherwise, add the original
        tileLookup[origHash] = origData;
        newTileColors[index] = clearedAttr;
        newTileData.push(origData);
      }
    }
  }
 
  return {
    tileData: newTileData,
    tileAttrs: newTileColors,
    tilesetData: [...commonTileData, ...newTileData],
  };
};