diff --git a/Documentation/content/docs/gallery/TGAReaderWithIcon.jpg b/Documentation/content/docs/gallery/TGAReaderWithIcon.jpg
new file mode 100644
index 00000000000..d0cd9a8f691
Binary files /dev/null and b/Documentation/content/docs/gallery/TGAReaderWithIcon.jpg differ
diff --git a/Documentation/content/examples/index.md b/Documentation/content/examples/index.md
index b3969aac5b1..c88f6b13c2a 100644
--- a/Documentation/content/examples/index.md
+++ b/Documentation/content/examples/index.md
@@ -1,392 +1,394 @@
-title: Examples
----
-
-
-
-This will allow you to see the some live code running in your browser. Just pick a class on the left menu or in the category grouping below. The [Simple Cone](SimpleCone.html) is a good example to start with if you are new to VTK.
-
-
-
-[![DepthTest Example][DepthTest]](./DepthTest.html "Capture and render the depth buffer of the scene")
-[![Texture Example][Texture]](./Texture.html "Apply a texture on a sphere")
-[![Convolution2DPass Example][Convolution2DPass]](./Convolution2DPass.html "Convolution2DPass")
-[![ManyRenderers Example][ManyRenderers]](./ManyRenderers.html "ManyRenderers")
-[![ManyRenderWindows Example][ManyRenderWindows]](./ManyRenderWindows.html "ManyRenderWindows")
-
-
-
-[![AngleWidget Example][AngleWidget]](./AngleWidget.html "Angle (radian, degree) widget example")
-[![Box Example][Box]](./Box.html "Box")
-[![ImageCroppingWidget Example][ImageCroppingWidget]](./ImageCroppingWidget.html "Crop/Clip volume rendering with a bounding box/cube/orthogonal planes")
-[![ImplicitPlaneWidget Example][ImplicitPlaneWidget]](./ImplicitPlaneWidget.html "Translate and orient an implicit plane with normal and origin")
-[![InteractiveOrientationWidget Example][InteractiveOrientationWidget]](./InteractiveOrientationWidget.html "Corner coordinate system orientation widget")
-[![LabelWidget Example][LabelWidget]](./LabelWidget.html "Place, edit text size and color of label widget")
-[![LineWidget Example][LineWidget]](./LineWidget.html "Place and edit line/distance widget with handles")
-[![PaintWidget Example][PaintWidget]](./PaintWidget.html "Draw strokes, create rectangle, square, ellipse and disk 2D widgets")
-[![PolyLineWidget Example][PolyLineWidget]](./PolyLineWidget.html "Place multiple connected handles with text")
-[![ResliceCursorWidget Example][ResliceCursorWidget]](./ResliceCursorWidget.html "Axial Coronal and Sagittal MPR/Oblique/Reformatted/Resliced/Slab/MIP views")
-[![ShapeWidget Example][ShapeWidget]](./ShapeWidget.html "2D shape widgets with text information")
-[![SphereWidget Example][SphereWidget]](./SphereWidget.html "2D sphere widget controlled with radius")
-[![SplineWidget Example][SplineWidget]](./PaintWidget.html "Widget to draw open or closed (triangularized) sharp/smooth polygon widget")
-
-
-
-[![AR Example][ARWithLogo]](./AR.html "AR with WebXR")
-[![VR Example][VRWithLogo]](./VR.html "VR with WebXR")
-[![LookingGlass Example][LookingGlassWithLogo]](./LookingGlass.html "Render scene into a LookingGlass device")
-[![ItkWasmVolume Example][ItkWasmVolumeWithLogo]](./ItkWasmVolume.html "ItkWasmVolume")
-[![RemoteView Example][RemoteViewWithLogos]](./RemoteView.html "Connect a VTK or ParaView Python backend server via WebSockets")
-[![ImageStream Example][ImageStreamWithLogos]](./ImageStream.html "Stream a ParaView Python backend server via WebSockets under a VTK.js rendering")
-
-
-
-[ARWithLogo]: ../docs/gallery/ArConeWithLogo.jpg
-[VRWithLogo]: ../docs/gallery/VrConeWithLogo.jpg
-[LookingGlassWithLogo]: ../docs/gallery/LookingGlassConeWithLogo.jpg
-[ItkWasmVolumeWithLogo]: ../docs/gallery/ItkWasmVolumeWithLogo.jpg
-[RemoteViewWithLogos]: ../docs/gallery/RemoteViewWithLogos.jpg
-[ImageStreamWithLogos]: ../docs/gallery/ImageStreamWithLogos.jpg
+title: Examples
+---
+
+
+
+This will allow you to see the some live code running in your browser. Just pick a class on the left menu or in the category grouping below. The [Simple Cone](SimpleCone.html) is a good example to start with if you are new to VTK.
+
+
+
+[![DepthTest Example][DepthTest]](./DepthTest.html "Capture and render the depth buffer of the scene")
+[![Texture Example][Texture]](./Texture.html "Apply a texture on a sphere")
+[![Convolution2DPass Example][Convolution2DPass]](./Convolution2DPass.html "Convolution2DPass")
+[![ManyRenderers Example][ManyRenderers]](./ManyRenderers.html "ManyRenderers")
+[![ManyRenderWindows Example][ManyRenderWindows]](./ManyRenderWindows.html "ManyRenderWindows")
+
+
+
+[![AngleWidget Example][AngleWidget]](./AngleWidget.html "Angle (radian, degree) widget example")
+[![Box Example][Box]](./Box.html "Box")
+[![ImageCroppingWidget Example][ImageCroppingWidget]](./ImageCroppingWidget.html "Crop/Clip volume rendering with a bounding box/cube/orthogonal planes")
+[![ImplicitPlaneWidget Example][ImplicitPlaneWidget]](./ImplicitPlaneWidget.html "Translate and orient an implicit plane with normal and origin")
+[![InteractiveOrientationWidget Example][InteractiveOrientationWidget]](./InteractiveOrientationWidget.html "Corner coordinate system orientation widget")
+[![LabelWidget Example][LabelWidget]](./LabelWidget.html "Place, edit text size and color of label widget")
+[![LineWidget Example][LineWidget]](./LineWidget.html "Place and edit line/distance widget with handles")
+[![PaintWidget Example][PaintWidget]](./PaintWidget.html "Draw strokes, create rectangle, square, ellipse and disk 2D widgets")
+[![PolyLineWidget Example][PolyLineWidget]](./PolyLineWidget.html "Place multiple connected handles with text")
+[![ResliceCursorWidget Example][ResliceCursorWidget]](./ResliceCursorWidget.html "Axial Coronal and Sagittal MPR/Oblique/Reformatted/Resliced/Slab/MIP views")
+[![ShapeWidget Example][ShapeWidget]](./ShapeWidget.html "2D shape widgets with text information")
+[![SphereWidget Example][SphereWidget]](./SphereWidget.html "2D sphere widget controlled with radius")
+[![SplineWidget Example][SplineWidget]](./PaintWidget.html "Widget to draw open or closed (triangularized) sharp/smooth polygon widget")
+
+
+
+[![AR Example][ARWithLogo]](./AR.html "AR with WebXR")
+[![VR Example][VRWithLogo]](./VR.html "VR with WebXR")
+[![LookingGlass Example][LookingGlassWithLogo]](./LookingGlass.html "Render scene into a LookingGlass device")
+[![ItkWasmVolume Example][ItkWasmVolumeWithLogo]](./ItkWasmVolume.html "ItkWasmVolume")
+[![RemoteView Example][RemoteViewWithLogos]](./RemoteView.html "Connect a VTK or ParaView Python backend server via WebSockets")
+[![ImageStream Example][ImageStreamWithLogos]](./ImageStream.html "Stream a ParaView Python backend server via WebSockets under a VTK.js rendering")
+
+
+
+[ARWithLogo]: ../docs/gallery/ArConeWithLogo.jpg
+[VRWithLogo]: ../docs/gallery/VrConeWithLogo.jpg
+[LookingGlassWithLogo]: ../docs/gallery/LookingGlassConeWithLogo.jpg
+[ItkWasmVolumeWithLogo]: ../docs/gallery/ItkWasmVolumeWithLogo.jpg
+[RemoteViewWithLogos]: ../docs/gallery/RemoteViewWithLogos.jpg
+[ImageStreamWithLogos]: ../docs/gallery/ImageStreamWithLogos.jpg
diff --git a/Sources/IO/Image/TGAReader/Constants.js b/Sources/IO/Image/TGAReader/Constants.js
new file mode 100644
index 00000000000..661acddd0ec
--- /dev/null
+++ b/Sources/IO/Image/TGAReader/Constants.js
@@ -0,0 +1,27 @@
+const TYPE_INDEXED = 1;
+const TYPE_RGB = 2;
+const TYPE_GREY = 3;
+const TYPE_RLE_INDEXED = 9;
+const TYPE_RLE_RGB = 10;
+const TYPE_RLE_GREY = 11;
+const ORIGIN_MASK = 0x30;
+const ORIGIN_SHIFT = 0x04;
+const ORIGIN_BL = 0x00;
+const ORIGIN_BR = 0x01;
+const ORIGIN_UL = 0x02;
+const ORIGIN_UR = 0x03;
+
+export default {
+ TYPE_INDEXED,
+ TYPE_RGB,
+ TYPE_GREY,
+ TYPE_RLE_INDEXED,
+ TYPE_RLE_RGB,
+ TYPE_RLE_GREY,
+ ORIGIN_MASK,
+ ORIGIN_SHIFT,
+ ORIGIN_BL,
+ ORIGIN_BR,
+ ORIGIN_UL,
+ ORIGIN_UR,
+};
diff --git a/Sources/IO/Image/TGAReader/example/index.js b/Sources/IO/Image/TGAReader/example/index.js
new file mode 100644
index 00000000000..5c951d58332
--- /dev/null
+++ b/Sources/IO/Image/TGAReader/example/index.js
@@ -0,0 +1,122 @@
+import '@kitware/vtk.js/favicon';
+
+// Load the rendering pieces we want to use (for both WebGL and WebGPU)
+import '@kitware/vtk.js/Rendering/Profiles/Geometry';
+
+import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow';
+import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
+import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
+import vtkPlaneSource from '@kitware/vtk.js/Filters/Sources/PlaneSource';
+import vtkTGAReader from '@kitware/vtk.js/IO/Image/TGAReader';
+import vtkTexture from '@kitware/vtk.js/Rendering/Core/Texture';
+import vtkURLExtract from '@kitware/vtk.js/Common/Core/URLExtract';
+
+// ----------------------------------------------------------------------------
+// Example code
+// ----------------------------------------------------------------------------
+const userParams = vtkURLExtract.extractURLParameters();
+
+const reader = vtkTGAReader.newInstance();
+const texture = vtkTexture.newInstance();
+const planeSource = vtkPlaneSource.newInstance();
+const mapper = vtkMapper.newInstance();
+const actor = vtkActor.newInstance();
+mapper.setInputConnection(planeSource.getOutputPort());
+actor.setMapper(mapper);
+
+// ----------------------------------------------------------------------------
+// Use a file reader to load a local file
+// ----------------------------------------------------------------------------
+
+const myContainer = document.querySelector('body');
+const fileContainer = document.createElement('div');
+fileContainer.innerHTML =
+ '
Select a tga file.
';
+myContainer.appendChild(fileContainer);
+
+const fileInput = fileContainer.querySelector('input');
+
+function zoomCameraToFitPlane(camera, planeWidth, planeHeight) {
+ const fov = 60; // Field of view in degrees
+
+ // Calculate the distance needed to fit the plane in view
+ const distance =
+ Math.max(planeWidth, planeHeight) /
+ (2 * Math.tan((fov * Math.PI) / 180 / 2));
+
+ // Set camera position
+ camera.setPosition(planeWidth / 2, planeHeight / 2, distance);
+ camera.setFocalPoint(planeWidth / 2, planeHeight / 2, 0);
+ camera.setViewUp(0, 1, 0);
+
+ // Set parallel scale for orthographic projection
+ camera.setParallelScale(planeHeight / 2);
+}
+
+function update() {
+ // Get the vtkImageData from the reader
+ const imageData = reader.getOutputData(0);
+
+ // Set the vtkImageData as the texture input
+ texture.setInputData(imageData);
+
+ // // Get the image's extent and spacing
+ const [xMin, xMax, yMin, yMax] = imageData.getExtent();
+ const [spacingX, spacingY] = imageData.getSpacing();
+
+ // // Calculate the plane's width and height based on the image's dimensions
+ const planeWidth = (xMax - xMin + 1) * spacingX;
+ const planeHeight = (yMax - yMin + 1) * spacingY;
+
+ // Set the plane's origin and corners based on calculated width and height
+ planeSource.setOrigin(0, 0, 0);
+ planeSource.setPoint1(planeWidth, 0, 0); // Horizontal edge
+ planeSource.setPoint2(0, planeHeight, 0); // Vertical edge
+
+ actor.addTexture(texture);
+
+ const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance();
+ const renderer = fullScreenRenderer.getRenderer();
+ const renderWindow = fullScreenRenderer.getRenderWindow();
+ const camera = renderer.getActiveCamera();
+ const interactor = renderWindow.getInteractor();
+
+ // Disable default interactor style
+ interactor.setInteractorStyle(null);
+
+ renderer.addActor(actor);
+
+ // Adjust the camera to fit the plane in the view
+ zoomCameraToFitPlane(camera, planeWidth, planeHeight);
+ renderer.resetCameraClippingRange();
+
+ renderWindow.render();
+}
+
+function handleFile(event) {
+ event.preventDefault();
+ const dataTransfer = event.dataTransfer;
+ const files = event.target.files || dataTransfer.files;
+ if (files.length === 1) {
+ const file = files[0];
+ const fileReader = new FileReader();
+ fileReader.onload = () => {
+ reader.parse(fileReader.result);
+ update();
+ };
+ fileReader.readAsArrayBuffer(file);
+ }
+}
+
+fileInput.addEventListener('change', handleFile);
+
+// ----------------------------------------------------------------------------
+// Use the reader to download a file
+// ----------------------------------------------------------------------------
+if (userParams.fileURL) {
+ reader.setUrl(userParams.fileURL).then(() => {
+ reader.loadData().then(() => {
+ update();
+ });
+ });
+}
diff --git a/Sources/IO/Image/TGAReader/index.d.ts b/Sources/IO/Image/TGAReader/index.d.ts
new file mode 100644
index 00000000000..8eac6145b54
--- /dev/null
+++ b/Sources/IO/Image/TGAReader/index.d.ts
@@ -0,0 +1,121 @@
+import { vtkAlgorithm, vtkObject } from '../../../interfaces';
+import HtmlDataAccessHelper from '../../Core/DataAccessHelper/HtmlDataAccessHelper';
+import HttpDataAccessHelper from '../../Core/DataAccessHelper/HttpDataAccessHelper';
+import JSZipDataAccessHelper from '../../Core/DataAccessHelper/JSZipDataAccessHelper';
+import LiteHttpDataAccessHelper from '../../Core/DataAccessHelper/LiteHttpDataAccessHelper';
+
+interface ITGAReaderOptions {
+ compression?: string;
+ progressCallback?: any;
+}
+
+/**
+ *
+ */
+export interface ITGAReaderInitialValues {}
+
+type vtkTGAReaderBase = vtkObject &
+ Omit<
+ vtkAlgorithm,
+ | 'getInputData'
+ | 'setInputData'
+ | 'setInputConnection'
+ | 'getInputConnection'
+ | 'addInputConnection'
+ | 'addInputData'
+ >;
+
+export interface vtkTGAReader extends vtkTGAReaderBase {
+ /**
+ * Get the base url.
+ */
+ getBaseURL(): string;
+
+ /**
+ * Get the dataAccess helper.
+ */
+ getDataAccessHelper():
+ | HtmlDataAccessHelper
+ | HttpDataAccessHelper
+ | JSZipDataAccessHelper
+ | LiteHttpDataAccessHelper;
+
+ /**
+ * Get the url of the object to load.
+ */
+ getUrl(): string;
+
+ /**
+ * Load the object data.
+ * @param {ITGAReaderOptions} [options]
+ */
+ loadData(options?: ITGAReaderOptions): Promise;
+
+ /**
+ * Parse data.
+ * @param {ArrayBuffer} content The content to parse.
+ */
+ parse(content: ArrayBuffer): void;
+
+ /**
+ * Parse data as ArrayBuffer.
+ * @param {ArrayBuffer} content The content to parse.
+ */
+ parseAsArrayBuffer(content: ArrayBuffer): void;
+
+ /**
+ *
+ * @param inData
+ * @param outData
+ */
+ requestData(inData: any, outData: any): void;
+
+ /**
+ *
+ * @param dataAccessHelper
+ */
+ setDataAccessHelper(
+ dataAccessHelper:
+ | HtmlDataAccessHelper
+ | HttpDataAccessHelper
+ | JSZipDataAccessHelper
+ | LiteHttpDataAccessHelper
+ ): boolean;
+
+ /**
+ * Set the url of the object to load.
+ * @param {String} url the url of the object to load.
+ * @param {ITGAReaderOptions} [option] The PLY reader options.
+ */
+ setUrl(url: string, option?: ITGAReaderOptions): Promise;
+}
+
+/**
+ * Method used to decorate a given object (publicAPI+model) with vtkTGAReader characteristics.
+ *
+ * @param publicAPI object on which methods will be bounds (public)
+ * @param model object on which data structure will be bounds (protected)
+ * @param {ITGAReaderInitialValues} [initialValues] (default: {})
+ */
+export function extend(
+ publicAPI: object,
+ model: object,
+ initialValues?: ITGAReaderInitialValues
+): void;
+
+/**
+ * Method used to create a new instance of vtkTGAReader
+ * @param {ITGAReaderInitialValues} [initialValues] for pre-setting some of its content
+ */
+export function newInstance(
+ initialValues?: ITGAReaderInitialValues
+): vtkTGAReader;
+
+/**
+ * vtkTGAReader is a source object that reads Truevision Targa(TGA) files.
+ */
+export declare const vtkTGAReader: {
+ newInstance: typeof newInstance;
+ extend: typeof extend;
+};
+export default vtkTGAReader;
diff --git a/Sources/IO/Image/TGAReader/index.js b/Sources/IO/Image/TGAReader/index.js
new file mode 100644
index 00000000000..f08c3b30d37
--- /dev/null
+++ b/Sources/IO/Image/TGAReader/index.js
@@ -0,0 +1,535 @@
+/* eslint-disable no-bitwise */
+// Enable data soure for DataAccessHelper
+import 'vtk.js/Sources/IO/Core/DataAccessHelper/LiteHttpDataAccessHelper'; // Just need HTTP
+// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // HTTP + zip
+// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HtmlDataAccessHelper'; // html + base64 + zip
+// import 'vtk.js/Sources/IO/Core/DataAccessHelper/JSZipDataAccessHelper'; // zip
+
+import macro from 'vtk.js/Sources/macros';
+import DataAccessHelper from 'vtk.js/Sources/IO/Core/DataAccessHelper';
+import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
+import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
+import Constants from 'vtk.js/Sources/IO/Image/TGAReader/Constants';
+
+const { vtkErrorMacro } = macro;
+
+// ----------------------------------------------------------------------------
+// vtkTGAReader methods
+// ----------------------------------------------------------------------------
+
+function vtkTGAReader(publicAPI, model) {
+ // Set our className
+ model.classHierarchy.push('vtkTGAReader');
+
+ // Create default dataAccessHelper if not available
+ if (!model.dataAccessHelper) {
+ model.dataAccessHelper = DataAccessHelper.get('http');
+ }
+
+ /**
+ * Gets the header of a TGA file
+ * @param data defines the TGA data
+ * @returns the header
+ */
+ function parseHeader(data) {
+ let offset = 0;
+
+ const header = {
+ idLength: data[offset++],
+ colormap_type: data[offset++],
+ imageType: data[offset++],
+ colormapIndex: data[offset++] | (data[offset++] << 8),
+ colormapLength: data[offset++] | (data[offset++] << 8),
+ colormapSize: data[offset++],
+ origin: [
+ data[offset++] | (data[offset++] << 8),
+ data[offset++] | (data[offset++] << 8),
+ ],
+ width: data[offset++] | (data[offset++] << 8),
+ height: data[offset++] | (data[offset++] << 8),
+ pixelSize: data[offset++],
+ flags: data[offset++],
+ };
+
+ return header;
+ }
+
+ const handlers = {
+ getImageData8bits(
+ header,
+ palettes,
+ pixeData,
+ yStart,
+ yStep,
+ yEnd,
+ xStart,
+ xStep,
+ xEnd
+ ) {
+ const image = pixeData;
+ const colormap = palettes;
+ const width = header.width;
+ const height = header.height;
+ let color;
+ let i = 0;
+ let x;
+ let y;
+
+ const imageData = new Uint8Array(width * height * 4);
+
+ for (y = yStart; y !== yEnd; y += yStep) {
+ for (x = xStart; x !== xEnd; x += xStep, i++) {
+ color = image[i];
+ imageData[(x + width * y) * 4 + 3] = 255;
+ imageData[(x + width * y) * 4 + 2] = colormap[color * 3 + 0];
+ imageData[(x + width * y) * 4 + 1] = colormap[color * 3 + 1];
+ imageData[(x + width * y) * 4 + 0] = colormap[color * 3 + 2];
+ }
+ }
+
+ return imageData;
+ },
+ getImageData16bits(
+ header,
+ palettes,
+ pixeData,
+ yStart,
+ yStep,
+ yEnd,
+ xStart,
+ xStep,
+ xEnd
+ ) {
+ const image = pixeData;
+ const width = header.width;
+ const height = header.height;
+ let color;
+ let i = 0;
+ let x;
+ let y;
+
+ const imageData = new Uint8Array(width * height * 4);
+
+ for (y = yStart; y !== yEnd; y += yStep) {
+ for (x = xStart; x !== xEnd; x += xStep, i += 2) {
+ color = image[i + 0] + (image[i + 1] << 8); // Inversed ?
+ const r = ((((color & 0x7c00) >> 10) * 255) / 0x1f) | 0;
+ const g = ((((color & 0x03e0) >> 5) * 255) / 0x1f) | 0;
+ const b = (((color & 0x001f) * 255) / 0x1f) | 0;
+
+ imageData[(x + width * y) * 4 + 0] = r;
+ imageData[(x + width * y) * 4 + 1] = g;
+ imageData[(x + width * y) * 4 + 2] = b;
+ imageData[(x + width * y) * 4 + 3] = color & 0x8000 ? 0 : 255;
+ }
+ }
+
+ return imageData;
+ },
+ getImageData24bits(
+ header,
+ palettes,
+ pixeData,
+ yStart,
+ yStep,
+ yEnd,
+ xStart,
+ xStep,
+ xEnd
+ ) {
+ const image = pixeData;
+ const width = header.width;
+ const height = header.height;
+ let i = 0;
+ let x;
+ let y;
+
+ const imageData = new Uint8Array(width * height * 4);
+
+ for (y = yStart; y !== yEnd; y += yStep) {
+ for (x = xStart; x !== xEnd; x += xStep, i += 3) {
+ imageData[(x + width * y) * 4 + 3] = 255;
+ imageData[(x + width * y) * 4 + 2] = image[i + 0];
+ imageData[(x + width * y) * 4 + 1] = image[i + 1];
+ imageData[(x + width * y) * 4 + 0] = image[i + 2];
+ }
+ }
+
+ return imageData;
+ },
+ getImageData32bits(
+ header,
+ palettes,
+ pixeData,
+ yStart,
+ yStep,
+ yEnd,
+ xStart,
+ xStep,
+ xEnd
+ ) {
+ const image = pixeData;
+ const width = header.width;
+ const height = header.height;
+ let i = 0;
+ let x;
+ let y;
+
+ const imageData = new Uint8Array(width * height * 4);
+
+ for (y = yStart; y !== yEnd; y += yStep) {
+ for (x = xStart; x !== xEnd; x += xStep, i += 4) {
+ imageData[(x + width * y) * 4 + 2] = image[i + 0];
+ imageData[(x + width * y) * 4 + 1] = image[i + 1];
+ imageData[(x + width * y) * 4 + 0] = image[i + 2];
+ imageData[(x + width * y) * 4 + 3] = image[i + 3];
+ }
+ }
+
+ return imageData;
+ },
+ getImageDataGrey8bits(
+ header,
+ palettes,
+ pixeData,
+ yStart,
+ yStep,
+ yEnd,
+ xStart,
+ xStep,
+ xEnd
+ ) {
+ const image = pixeData;
+ const width = header.width;
+ const height = header.height;
+ let color;
+ let i = 0;
+ let x;
+ let y;
+
+ const imageData = new Uint8Array(width * height * 4);
+
+ for (y = yStart; y !== yEnd; y += yStep) {
+ for (x = xStart; x !== xEnd; x += xStep, i++) {
+ color = image[i];
+ imageData[(x + width * y) * 4 + 0] = color;
+ imageData[(x + width * y) * 4 + 1] = color;
+ imageData[(x + width * y) * 4 + 2] = color;
+ imageData[(x + width * y) * 4 + 3] = 255;
+ }
+ }
+
+ return imageData;
+ },
+ getImageDataGrey16bits(
+ header,
+ palettes,
+ pixeData,
+ yStart,
+ yStep,
+ yEnd,
+ xStart,
+ xStep,
+ xEnd
+ ) {
+ const image = pixeData;
+ const width = header.width;
+ const height = header.height;
+ let i = 0;
+ let x;
+ let y;
+
+ const imageData = new Uint8Array(width * height * 4);
+
+ for (y = yStart; y !== yEnd; y += yStep) {
+ for (x = xStart; x !== xEnd; x += xStep, i += 2) {
+ imageData[(x + width * y) * 4 + 0] = image[i + 0];
+ imageData[(x + width * y) * 4 + 1] = image[i + 0];
+ imageData[(x + width * y) * 4 + 2] = image[i + 0];
+ imageData[(x + width * y) * 4 + 3] = image[i + 1];
+ }
+ }
+
+ return imageData;
+ },
+ };
+
+ // Internal method to fetch Array
+ function fetchData(url, option = {}) {
+ const { compression, progressCallback } = model;
+ return model.dataAccessHelper.fetchBinary(url, {
+ compression,
+ progressCallback,
+ });
+ }
+
+ // Set DataSet url
+ publicAPI.setUrl = (url, option = { binary: true }) => {
+ model.url = url;
+
+ // Remove the file in the URL
+ const path = url.split('/');
+ path.pop();
+ model.baseURL = path.join('/');
+
+ model.compression = option.compression;
+
+ // Fetch metadata
+ return publicAPI.loadData({
+ progressCallback: option.progressCallback,
+ });
+ };
+
+ // Fetch the actual data arrays
+ publicAPI.loadData = (option = {}) => {
+ const promise = fetchData(model.url, option);
+ promise.then(publicAPI.parse);
+ return promise;
+ };
+
+ publicAPI.parse = (content) => {
+ publicAPI.parseAsArrayBuffer(content);
+ };
+
+ publicAPI.parseAsArrayBuffer = (content) => {
+ if (!content) {
+ return;
+ }
+
+ const data = new Uint8Array(content);
+ // Not enough data to contain header ?
+ if (data.length < 19) {
+ vtkErrorMacro(
+ 'Unable to load TGA file - Not enough data to contain header'
+ );
+ return;
+ }
+
+ // Read Header
+ let offset = 18;
+ const header = parseHeader(data);
+
+ // Assume it's a valid Targa file.
+ if (header.idLength + offset > data.length) {
+ vtkErrorMacro('Unable to load TGA file - Not enough data');
+ return;
+ }
+
+ // Skip not needed data
+ offset += header.idLength;
+
+ let useRle = false;
+ let usePal = false;
+ let useGrey = false;
+
+ // Get some informations.
+ switch (header.imageType) {
+ case Constants.TYPE_RLE_INDEXED:
+ useRle = true;
+ // eslint-disable-next-line no-fallthrough
+ case Constants.TYPE_INDEXED:
+ usePal = true;
+ break;
+ case Constants.TYPE_RLE_RGB:
+ useRle = true;
+ // eslint-disable-next-line no-fallthrough
+ case Constants.TYPE_RGB:
+ // use_rgb = true;
+ break;
+ case Constants.TYPE_RLE_GREY:
+ useRle = true;
+ // eslint-disable-next-line no-fallthrough
+ case Constants.TYPE_GREY:
+ useGrey = true;
+ break;
+ default:
+ vtkErrorMacro('TGA file has unknown image type');
+ return;
+ }
+
+ let pixelData;
+
+ const pixelSize = header.pixelSize >> 3;
+ const pixelTotal = header.width * header.height * pixelSize;
+
+ // Read palettes
+ let palettes;
+
+ if (usePal) {
+ palettes = data.subarray(
+ offset,
+ (offset += header.colormapLength * (header.colormapSize >> 3))
+ );
+ }
+
+ // Read LRE
+ if (useRle) {
+ pixelData = new Uint8Array(pixelTotal);
+
+ let c;
+ let count;
+ let i;
+ let localOffset = 0;
+ const pixels = new Uint8Array(pixelSize);
+
+ while (offset < pixelTotal && localOffset < pixelTotal) {
+ c = data[offset++];
+ count = (c & 0x7f) + 1;
+
+ // RLE pixels
+ if (c & 0x80) {
+ // Bind pixel tmp array
+ for (i = 0; i < pixelSize; ++i) {
+ pixels[i] = data[offset++];
+ }
+
+ // Copy pixel array
+ for (i = 0; i < count; ++i) {
+ pixelData.set(pixels, localOffset + i * pixelSize);
+ }
+
+ localOffset += pixelSize * count;
+ }
+ // Raw pixels
+ else {
+ count *= pixelSize;
+ for (i = 0; i < count; ++i) {
+ pixelData[localOffset + i] = data[offset++];
+ }
+ localOffset += count;
+ }
+ }
+ }
+ // RAW Pixels
+ else {
+ pixelData = data.subarray(
+ offset,
+ (offset += usePal ? header.width * header.height : pixelTotal)
+ );
+ }
+
+ // Load to texture
+ let xStart;
+ let yStart;
+ let xStep;
+ let yStep;
+ let yEnd;
+ let xEnd;
+
+ switch ((header.flags & Constants.ORIGIN_MASK) >> Constants.ORIGIN_SHIFT) {
+ case Constants.ORIGIN_UL:
+ xStart = 0;
+ xStep = 1;
+ xEnd = header.width;
+ yStart = 0;
+ yStep = 1;
+ yEnd = header.height;
+ break;
+
+ case Constants.ORIGIN_BL:
+ xStart = 0;
+ xStep = 1;
+ xEnd = header.width;
+ yStart = 0;
+ yStep = 1;
+ yEnd = header.height;
+ break;
+
+ case Constants.ORIGIN_UR:
+ xStart = header.width - 1;
+ xStep = -1;
+ xEnd = -1;
+ yStart = 0;
+ yStep = 1;
+ yEnd = header.height;
+ break;
+
+ case Constants.ORIGIN_BR:
+ xStart = header.width - 1;
+ xStep = -1;
+ xEnd = -1;
+ yStart = header.height - 1;
+ yStep = -1;
+ yEnd = -1;
+ break;
+ default:
+ vtkErrorMacro('TGA file has unknown origin');
+ return;
+ }
+
+ const func = `getImageData${useGrey ? 'Grey' : ''}${header.pixelSize}bits`;
+
+ const output = handlers[func](
+ header,
+ palettes,
+ pixelData,
+ yStart,
+ yStep,
+ yEnd,
+ xStart,
+ xStep,
+ xEnd
+ );
+
+ const dataExtent = [0, header.width - 1, 0, header.height - 1];
+ const dataSpacing = [1, 1, 1];
+
+ const imageData = vtkImageData.newInstance();
+ imageData.setDimensions(header.width, header.height, 1);
+ imageData.setExtent(dataExtent);
+ imageData.setSpacing(dataSpacing);
+
+ const dataArray = vtkDataArray.newInstance({
+ name: 'TGAImage',
+ numberOfComponents: 4,
+ values: output,
+ });
+
+ imageData.getPointData().setScalars(dataArray);
+ model.output[0] = imageData;
+ };
+
+ publicAPI.requestData = (inData, outData) => {
+ publicAPI.parse(model.parseData);
+ };
+}
+
+// ----------------------------------------------------------------------------
+// Object factory
+// ----------------------------------------------------------------------------
+
+const DEFAULT_VALUES = {};
+
+// ----------------------------------------------------------------------------
+
+export function extend(publicAPI, model, initialValues = {}) {
+ Object.assign(model, DEFAULT_VALUES, initialValues);
+
+ // Make this a VTK object
+ macro.obj(publicAPI, model);
+
+ // Also make it an algorithm with one input and one output
+ macro.algo(publicAPI, model, 0, 1);
+
+ macro.get(publicAPI, model, ['url', 'baseURL']);
+ macro.setGet(publicAPI, model, ['dataAccessHelper']);
+
+ // Object specific methods
+ vtkTGAReader(publicAPI, model);
+
+ // To support destructuring
+ if (!model.compression) {
+ model.compression = null;
+ }
+ if (!model.progressCallback) {
+ model.progressCallback = null;
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+export const newInstance = macro.newInstance(extend, 'vtkTGAReader');
+
+// ----------------------------------------------------------------------------
+
+export default { newInstance, extend };
diff --git a/Sources/IO/Image/index.js b/Sources/IO/Image/index.js
index 0fa73fd4be6..3c54f2b35c2 100644
--- a/Sources/IO/Image/index.js
+++ b/Sources/IO/Image/index.js
@@ -1,5 +1,7 @@
import vtkHDRReader from './HDRReader';
+import vtkTGAReader from './TGAReader';
export default {
vtkHDRReader,
+ vtkTGAReader,
};