<个人使用记录> 前端pgm格式的图片使用canvas渲染

620 阅读1分钟

因为浏览器原生不支持PGM格式的图片,所以需要先将pgm图片转换为浏览器可以理解的格式。

demo 使用 vue3 + ts <个人使用记录>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
const canvas = ref();
onMounted(()=>{
    // 获取远程的 pgm 地址 获取方式随意
    // 获取成功后转换 arrayBuffer
    fetch('pgmUrl').then(res=>res.arrayBuffer()).then(arrBufffer=>{
        // 处理buffer 并获取数据
        const pgmData = parsePGM(arrBufffer);
        // 渲染buffer到canvas
        drawPGMToCanvas(canvas, pgmData);
    })
});


const parsePGM => (arrBufffer: ArrayBufferLike) {
      const view = new DataView(arrBufffer);
      // 读取文件头
      let offset = 0;
      while (offset < view.byteLength) {
      const line = readLine(view, offset);
      offset += line.length + 1; // 加上换行符的长度
      if (line.startsWith('P5')) {
       // P5 是 PGM 的二进制格式标识
       break;
      }
      // 读取宽度、高度和最大灰度值
      const width = readNumber(view, offset);
      offset += width.length + 1;
      const height = readNumber(view, offset);
      offset += height.length + 1;
      const maxValue = readNumber(view, offset);
      offset += maxValue.length + 1;

      // 读取图像数据
      const data = new Uint8Array(view.buffer, offset);

      return { width: parseInt(width), height: parseInt(height), maxValue: parseInt(maxValue), data };
  }
}


const drawPGMToCanvas = (canvas: HTMLCanvasElement, pgmData: { width: number; height: number; maxValue: number; data: Uint8Array }) => {
    const ctx = canvas.getContext('2d');
    if (!ctx)  return;
    const { width, height, data } = pgmData;
    const imageData = ctx.createImageData(width, height);
    for (let i = 0; i < data.length; i++) {
        imageData.data[i * 4] = data[i];
        imageData.data[i * 4 + 1] = data[i];
        imageData.data[i * 4 + 2] = data[i];
        imageData.data[i * 4 + 3] = 255;
    }
    canvas.width = width;
    canvas.height = height;
    ctx.putImageData(imageData, 0, 0);
};

const readLine = (view: DataView, offset: number) => {
  const chars = [];
  const flag = true;
  while (flag) { // eslint 限制while 不能直接使用字面量
    const char = view.getUint8(offset);
    if (char === 10 || char === 13) {
      // 换行符或回车符
      // flag = false;
      break;
    }
    chars.push(String.fromCharCode(char));
    offset++;
  }
  return chars.join('');
};

const readNumber = (view: DataView, offset: number) => {
  const chars = [];
  let flag = true; // eslint 限制while 不能直接使用字面量
  while (flag) {
    const char = view.getUint8(offset);
    if (char === 32 || char === 10 || char === 13) {
      // 空格、换行符或回车符
      break;
    }
    chars.push(String.fromCharCode(char));
    offset++;
  }
  return chars.join('');
};

</script>

<template>
    <canvas :ref="canvas"><canvas>
<template>