Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/util/common/imageUtils.ts
13397 views
1
/*---------------------------------------------------------------------------------------------
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
7
export function getImageDimensions(base64: string) {
8
if (!base64.startsWith('data:image/')) {
9
throw new Error('Could not read image: invalid base64 image string');
10
}
11
const rawString = base64.split(',')[1];
12
switch (getMimeType(rawString)) {
13
case 'image/png':
14
return getPngDimensions(rawString);
15
case 'image/gif':
16
return getGifDimensions(rawString);
17
case 'image/jpeg':
18
case 'image/jpg':
19
return getJpegDimensions(rawString);
20
case 'image/webp':
21
return getWebPDimensions(rawString);
22
default:
23
throw new Error('Unsupported image format');
24
}
25
}
26
27
export function getPngDimensions(base64: string) {
28
const header = atob(base64.slice(0, 50)).slice(16, 24);
29
const uint8 = Uint8Array.from(header, c => c.charCodeAt(0));
30
const dataView = new DataView(uint8.buffer);
31
32
return {
33
width: dataView.getUint32(0, false),
34
height: dataView.getUint32(4, false)
35
};
36
}
37
38
export function getGifDimensions(base64: string) {
39
const header = atob(base64.slice(0, 50));
40
const uint8 = Uint8Array.from(header, c => c.charCodeAt(0));
41
const dataView = new DataView(uint8.buffer);
42
43
return {
44
width: dataView.getUint16(6, true),
45
height: dataView.getUint16(8, true)
46
};
47
}
48
49
export function getJpegDimensions(base64: string) {
50
const binary = atob(base64);
51
const uint8 = Uint8Array.from(binary, c => c.charCodeAt(0));
52
const length = uint8.length;
53
let offset = 2;
54
55
while (offset < length) {
56
const marker = (uint8[offset] << 8) | uint8[offset + 1];
57
const segmentLength = (uint8[offset + 2] << 8) | uint8[offset + 3];
58
59
if (marker >= 0xFFC0 && marker <= 0xFFC2) {
60
const dataView = new DataView(uint8.buffer, offset + 5, 4);
61
return {
62
height: dataView.getUint16(0, false),
63
width: dataView.getUint16(2, false)
64
};
65
}
66
67
offset += 2 + segmentLength;
68
}
69
70
throw new Error('JPEG dimensions not found');
71
}
72
73
export function getWebPDimensions(base64String: string) {
74
const binaryString = atob(base64String);
75
const binaryData = new Uint8Array(binaryString.length);
76
for (let i = 0; i < binaryString.length; i++) {
77
binaryData[i] = binaryString.charCodeAt(i);
78
}
79
80
if (binaryString.slice(0, 4) !== 'RIFF' || binaryString.slice(8, 12) !== 'WEBP') {
81
throw new Error('Not a valid WebP image.');
82
}
83
84
const chunkHeader = binaryString.slice(12, 16);
85
86
if (chunkHeader === 'VP8 ') {
87
const width = (binaryData[26] | (binaryData[27] << 8)) & 0x3FFF;
88
const height = (binaryData[28] | (binaryData[29] << 8)) & 0x3FFF;
89
return { width, height };
90
} else if (chunkHeader === 'VP8L') {
91
const width = (binaryData[21] | (binaryData[22] << 8)) & 0x3FFF;
92
const height = (binaryData[23] | (binaryData[24] << 8)) & 0x3FFF;
93
return { width, height };
94
} else if (chunkHeader === 'VP8X') {
95
const width = ((binaryData[24] | (binaryData[25] << 8) | (binaryData[26] << 16)) & 0xFFFFFF) + 1;
96
const height = ((binaryData[27] | (binaryData[28] << 8) | (binaryData[29] << 16)) & 0xFFFFFF) + 1;
97
return { width, height };
98
} else {
99
throw new Error('Unsupported WebP format.');
100
}
101
}
102
103
export function getMimeType(base64String: string): string | undefined {
104
const mimeTypes: { [key: string]: string } = {
105
'/9j/': 'image/jpeg',
106
'iVBOR': 'image/png',
107
'R0lGOD': 'image/gif',
108
'UklGR': 'image/webp',
109
};
110
111
for (const prefix of Object.keys(mimeTypes)) {
112
if (base64String.startsWith(prefix)) {
113
return mimeTypes[prefix];
114
}
115
}
116
}
117
118
export function extractImageAttributes(line: string, refineExisting?: boolean): string | undefined {
119
// Regex to match markdown image syntax ![alt text](<?image_path>?)
120
const markdownImageRegex = /!\[([^\]]*)\]\(<?([^)<>]+?)>?\)/;
121
// Updated regex to match HTML image syntax with alt and src in any order
122
const htmlImageRegex = /<img\s+(?:alt=["']([^"']*)["']\s*)?src=["']([^"']+)["'](?:\s*alt=["']([^"']*)["'])?/;
123
124
let match;
125
let imagePath = '';
126
let altText = '';
127
128
if ((match = markdownImageRegex.exec(line)) !== null) {
129
imagePath = match[2];
130
altText = match[1];
131
} else if ((match = htmlImageRegex.exec(line)) !== null) {
132
imagePath = match[2]; // src is always the second group
133
altText = match[1] || match[3] || ''; // alt is sometimes first or third
134
} else {
135
// Try Learn Markdown format - check if it's a Learn Markdown image
136
const learnMarkdownRegex = /:::image\s+.*?source=["']([^"']+)["'].*?:::/;
137
const sourceMatch = learnMarkdownRegex.exec(line);
138
if (sourceMatch) {
139
imagePath = sourceMatch[1];
140
// Check if there's an alt-text attribute
141
const altTextRegex = /alt-text=["']([^"']*?)["']/;
142
const altMatch = altTextRegex.exec(line);
143
altText = altMatch ? altMatch[1] : '';
144
} else {
145
return undefined;
146
}
147
}
148
149
if (refineExisting ? !altText : !!altText) {
150
return undefined;
151
}
152
153
return imagePath;
154
}
155
156