diff --git a/package.json b/package.json index 2bbc846..a24785d 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,8 @@ "react-redux": "^7.2.6", "recorder-core": "^1.3.25011100", "redux": "^4.1.2", - "remark-gfm": "^4.0.1" + "remark-gfm": "^4.0.1", + "utif": "^3.1.0" }, "devDependencies": { "@svgr/webpack": "^5.5.0", diff --git a/src/pages/flowEditor/components/nodeContentImage.tsx b/src/pages/flowEditor/components/nodeContentImage.tsx index 33f27bc..4a44eec 100644 --- a/src/pages/flowEditor/components/nodeContentImage.tsx +++ b/src/pages/flowEditor/components/nodeContentImage.tsx @@ -3,6 +3,7 @@ import styles from '@/components/FlowEditor/node/style/baseOther.module.less'; import { Handle, Position } from '@xyflow/react'; import { Image } from '@arco-design/web-react'; import { formatDataType } from '@/utils/common'; +import UTIF from 'utif'; interface NodeContentData { parameters?: { @@ -120,6 +121,44 @@ const getImageBlobType = (url: string, blobType: string) => { return blobType || 'application/octet-stream'; }; +const isTiffImage = (url: string, blobType: string) => { + const imageType = getImageBlobType(url, blobType).toLowerCase(); + const cleanUrl = url.split('?')[0].toLowerCase(); + + return ( + imageType === 'image/tiff' || + cleanUrl.endsWith('.tif') || + cleanUrl.endsWith('.tiff') + ); +}; + +const createTiffPreviewUrl = async (url: string, blob: Blob) => { + const buffer = await blob.arrayBuffer(); + const ifds = UTIF.decode(buffer); + const firstImage = ifds[0]; + + if (!firstImage) { + throw new Error('empty tiff image'); + } + + UTIF.decodeImage(buffer, firstImage); + const rgba = UTIF.toRGBA8(firstImage); + const canvas = document.createElement('canvas'); + canvas.width = firstImage.width; + canvas.height = firstImage.height; + + const context = canvas.getContext('2d'); + if (!context) { + throw new Error('canvas context unavailable'); + } + + const imageData = context.createImageData(firstImage.width, firstImage.height); + imageData.data.set(rgba); + context.putImageData(imageData, 0, 0); + + return canvas.toDataURL('image/png'); +}; + const NodeContent = ({ data, imageUrl = '' }: { data: NodeContentData; imageUrl?: string }) => { const [previewUrl, setPreviewUrl] = useState(''); const [imageLoadError, setImageLoadError] = useState(false); @@ -152,6 +191,14 @@ const NodeContent = ({ data, imageUrl = '' }: { data: NodeContentData; imageUrl? } const blob = await response.blob(); + if (isTiffImage(imageUrl, blob.type)) { + const tiffPreviewUrl = await createTiffPreviewUrl(imageUrl, blob); + if (!canceled) { + setPreviewUrl(tiffPreviewUrl); + } + return; + } + const typedBlob = new Blob([blob], { type: getImageBlobType(imageUrl, blob.type), });