src/vs/base/common/image.ts

150 LOC · 130 covered · 20 uncovered · 10 ranges · 1 concepts · 1 introducers · 1 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- image.ts ×10
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { VSBuffer } from './buffer.js';
7 >
8 > export interface IImageDimensions {
9 > readonly width: number;
10 > readonly height: number;
11 > }
12 >
13 > /**
14 > * Read the pixel dimensions of an image by inspecting the bytes of its encoded
15 > * buffer. The format is auto-detected from the magic-number prefix.
16 > *
17 > * Supports JPEG, PNG, GIF and WebP (lossy, lossless and extended).
18 > *
19 > * Returns `undefined` if the buffer does not start with a recognized signature
20 > * or if the dimensions could not be parsed.
21 > */
22 > export function readImageDimensions(buffer: VSBuffer): IImageDimensions | undefined {
23 > const bytes = buffer.buffer;
24 > if (bytes.length < 12) {
25 > return undefined;
26 > }
27 > // JPEG: FF D8
28 > if (bytes[0] === 0xFF && bytes[1] === 0xD8) {
29 > return readJpegDimensions(bytes);
30 > }
31 > // PNG: 89 50 4E 47 0D 0A 1A 0A
32 > if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47 &&
33 > bytes[4] === 0x0D && bytes[5] === 0x0A && bytes[6] === 0x1A && bytes[7] === 0x0A) {
34 > return readPngDimensions(bytes);
35 > }
36 > // GIF: "GIF87a" or "GIF89a"
37 > if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x38 &&
38 > (bytes[4] === 0x37 || bytes[4] === 0x39) && bytes[5] === 0x61) {
39 > return readGifDimensions(bytes);
40 > }
41 > // WebP: "RIFF" <size> "WEBP"
42 > if (bytes[0] === 0x52 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x46 &&
43 > bytes[8] === 0x57 && bytes[9] === 0x45 && bytes[10] === 0x42 && bytes[11] === 0x50) {
44 > return readWebPDimensions(bytes);
45 > }
46 > return undefined;
47 > }
48 >
49 > function readJpegDimensions(bytes: Uint8Array): IImageDimensions | undefined {
50 > let i = 2;
51 > while (i < bytes.length - 9) {
52 > // Skip fill bytes (0xFF) before the marker.
53 > while (i < bytes.length && bytes[i] === 0xFF) {
54 > i++;
55 > }
56 > if (i >= bytes.length) {
57 return undefined;
58 }
59 > const marker = bytes[i]; image.ts ×10
60 > i++;
61 > // Standalone markers without a length field.
62 > if (marker === 0xD8 || marker === 0xD9 || marker === 0x01 || (marker >= 0xD0 && marker <= 0xD7)) {
63 continue;
64 }
65 > if (i + 1 >= bytes.length) { image.ts ×10
66 return undefined;
67 }
68 > const segLength = (bytes[i] << 8) | bytes[i + 1]; image.ts ×10
69 > // The 2-byte length field includes itself, so anything < 2 is corrupt.
70 > if (segLength < 2) {
71 > return undefined;
72 > }
73 > // SOFn markers (0xC0..0xCF) carry the frame dimensions, except DHT (0xC4), JPG (0xC8) and DAC (0xCC).
74 > if (marker >= 0xC0 && marker <= 0xCF && marker !== 0xC4 && marker !== 0xC8 && marker !== 0xCC) {
75 > if (i + 6 >= bytes.length) {
76 return undefined;
77 }
78 > const height = (bytes[i + 3] << 8) | bytes[i + 4]; image.ts ×10
79 > const width = (bytes[i + 5] << 8) | bytes[i + 6];
80 > return { width, height };
81 > }
82 > i += segLength;
83 > }
84 return undefined;
85 }
87 > function readPngDimensions(bytes: Uint8Array): IImageDimensions | undefined {
88 > // The IHDR chunk immediately follows the 8-byte signature: 4-byte length, 4-byte type "IHDR",
89 > // then 4-byte width (BE) and 4-byte height (BE).
90 > if (bytes.length < 24 ||
91 > bytes[12] !== 0x49 || bytes[13] !== 0x48 || bytes[14] !== 0x44 || bytes[15] !== 0x52) {
92 return undefined;
93 }
94 > const width = (bytes[16] << 24 | bytes[17] << 16 | bytes[18] << 8 | bytes[19]) >>> 0; image.ts ×10
95 > const height = (bytes[20] << 24 | bytes[21] << 16 | bytes[22] << 8 | bytes[23]) >>> 0;
96 > return { width, height };
97 > }
98 >
99 > function readGifDimensions(bytes: Uint8Array): IImageDimensions | undefined {
100 > // Logical screen width/height are at bytes 6..9, little-endian.
101 > if (bytes.length < 10) {
102 return undefined;
103 }
104 > const width = bytes[6] | (bytes[7] << 8); image.ts ×10
105 > const height = bytes[8] | (bytes[9] << 8);
106 > return { width, height };
107 > }
108 >
109 > function readWebPDimensions(bytes: Uint8Array): IImageDimensions | undefined {
110 > // Chunk header at bytes 12..15 selects the WebP variant.
111 > if (bytes.length < 30) {
112 return undefined;
113 }
114 > // The RIFF chunk payload size is declared at bytes 16..19 (LE) and must be large enough image.ts ×10
115 > // to contain the bytes each variant reads, and must fit within the buffer.
116 > const chunkSize = (bytes[16] | (bytes[17] << 8) | (bytes[18] << 16) | (bytes[19] << 24)) >>> 0;
117 > if (chunkSize > bytes.length - 20) {
118 return undefined;
119 }
120 > // VP8 (lossy, "VP8 " with trailing space). The frame tag occupies bytes 23..25 and must be the image.ts ×10
121 > // 3-byte start code 0x9D 0x01 0x2A; without it the buffer is not a valid VP8 keyframe.
122 > if (bytes[12] === 0x56 && bytes[13] === 0x50 && bytes[14] === 0x38 && bytes[15] === 0x20) {
123 > if (chunkSize < 10 || bytes[23] !== 0x9D || bytes[24] !== 0x01 || bytes[25] !== 0x2A) {
124 > return undefined;
125 > }
126 > const width = (bytes[26] | (bytes[27] << 8)) & 0x3FFF;
127 > const height = (bytes[28] | (bytes[29] << 8)) & 0x3FFF;
128 > return { width, height };
129 > }
130 > // VP8L (lossless). The bitstream starts at byte 21 (after the 0x2F signature). Width-1
131 > // occupies bits 0..13 and height-1 occupies bits 14..27, both little-endian.
132 > if (bytes[12] === 0x56 && bytes[13] === 0x50 && bytes[14] === 0x38 && bytes[15] === 0x4C) {
133 > if (chunkSize < 5 || bytes[20] !== 0x2F) {
134 > return undefined;
135 > }
136 > const width = ((bytes[21] | (bytes[22] << 8)) & 0x3FFF) + 1;
137 > const height = (((bytes[22] >> 6) | (bytes[23] << 2) | ((bytes[24] & 0x0F) << 10)) & 0x3FFF) + 1;
138 > return { width, height };
139 > }
140 > // VP8X (extended)
141 > if (bytes[12] === 0x56 && bytes[13] === 0x50 && bytes[14] === 0x38 && bytes[15] === 0x58) {
142 > if (chunkSize < 10) {
143 > return undefined;
144 > }
145 > const width = ((bytes[24] | (bytes[25] << 8) | (bytes[26] << 16)) & 0xFFFFFF) + 1;
146 > const height = ((bytes[27] | (bytes[28] << 8) | (bytes[29] << 16)) & 0xFFFFFF) + 1;
147 > return { width, height };
148 > }
149 return undefined;
150 }