char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Aleksander Grygierui: Constants refactor (#26908)e21152dc9

char/aliceai-t5-moe
3.6 KiB136 linesraw
1import { EXIF } from '$lib/constants';
2import { MimeTypeImage } from '$lib/enums';
3
4/**
5 * Read the EXIF orientation tag from a JPEG base64 data URL
6 *
7 * Only a bounded prefix of the base64 payload is decoded, the APP1 segment
8 * always sits near the start of the file.
9 * @param base64UrlJpeg - The JPEG base64 data URL to inspect
10 * @returns The orientation value (1 to 8), or 1 when absent or unreadable
11 */
12export function getJpegOrientationFromDataURL(base64UrlJpeg: string): number {
13	try {
14		const payloadStart = base64UrlJpeg.indexOf(',') + 1;
15
16		if (payloadStart <= 0) {
17			return 1;
18		}
19
20		// Keep the slice a multiple of 4 characters so atob accepts it
21		const charLimit = Math.ceil(EXIF.SCAN_BYTE_LIMIT / 3) * 4;
22		const slice = base64UrlJpeg.slice(payloadStart, payloadStart + charLimit);
23		const binary = atob(slice.slice(0, slice.length - (slice.length % 4)));
24		const bytes = new Uint8Array(binary.length);
25
26		for (let i = 0; i < binary.length; i++) {
27			bytes[i] = binary.charCodeAt(i);
28		}
29
30		return findExifOrientation(new DataView(bytes.buffer));
31	} catch {
32		return 1;
33	}
34}
35
36/**
37 * Walk the JPEG segments of a header buffer looking for the APP1 EXIF block
38 * @param view - DataView over the JPEG header bytes
39 * @returns The orientation value (1 to 8), or 1 when absent or malformed
40 */
41function findExifOrientation(view: DataView): number {
42	if (view.byteLength < 4 || view.getUint16(0) !== EXIF.JPEG_SOI_MARKER) {
43		return 1;
44	}
45
46	let offset = 2;
47
48	while (offset + 4 <= view.byteLength) {
49		if (view.getUint8(offset) !== 0xff) {
50			return 1;
51		}
52
53		const marker = view.getUint8(offset + 1);
54
55		// Compressed image data starts here: no EXIF past this point
56		if (marker === EXIF.SOS_MARKER) {
57			return 1;
58		}
59
60		const segmentLength = view.getUint16(offset + 2);
61
62		if (marker === EXIF.APP1_MARKER) {
63			return parseExifOrientation(view, offset + 4, segmentLength);
64		}
65
66		offset += 2 + segmentLength;
67	}
68
69	return 1;
70}
71
72/**
73 * Parse the orientation tag from an APP1 EXIF payload
74 * @param view - DataView over the JPEG header bytes
75 * @param start - Offset of the APP1 payload, right after the segment length
76 * @param segmentLength - Declared APP1 segment length
77 * @returns The orientation value (1 to 8), or 1 when absent or malformed
78 */
79function parseExifOrientation(view: DataView, start: number, segmentLength: number): number {
80	const end = Math.min(start + segmentLength, view.byteLength);
81
82	// The payload opens with the "Exif\0\0" signature
83	if (
84		start + 6 > end ||
85		view.getUint32(start) !== EXIF.EXIF_SIGNATURE ||
86		view.getUint16(start + 4) !== 0
87	) {
88		return 1;
89	}
90
91	const tiff = start + 6;
92
93	if (tiff + 8 > end) {
94		return 1;
95	}
96
97	const littleEndian = view.getUint16(tiff) === EXIF.TIFF_LITTLE_ENDIAN;
98
99	if (view.getUint16(tiff + 2, littleEndian) !== EXIF.TIFF_MAGIC) {
100		return 1;
101	}
102
103	const ifdOffset = view.getUint32(tiff + 4, littleEndian);
104
105	if (tiff + ifdOffset + 2 > end) {
106		return 1;
107	}
108
109	const entryCount = view.getUint16(tiff + ifdOffset, littleEndian);
110
111	// Scan IFD0 entries for the orientation tag
112	for (let i = 0; i < entryCount; i++) {
113		const entry = tiff + ifdOffset + 2 + i * EXIF.IFD_ENTRY_SIZE;
114
115		if (entry + EXIF.IFD_ENTRY_SIZE > end) {
116			return 1;
117		}
118
119		if (view.getUint16(entry, littleEndian) === EXIF.ORIENTATION_TAG) {
120			const orientation = view.getUint16(entry + 8, littleEndian);
121
122			return orientation >= 1 && orientation <= 8 ? orientation : 1;
123		}
124	}
125
126	return 1;
127}
128
129/**
130 * Check if a MIME type represents a JPEG
131 * @param mimeType - The MIME type to check
132 * @returns True if the MIME type is a JPEG variant
133 */
134export function isJpegMimeType(mimeType: string): boolean {
135	return mimeType === MimeTypeImage.JPEG || mimeType === MimeTypeImage.JPG;
136}