eve-hub 0.14.9 → 0.14.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # eve-hub
2
2
 
3
+ ## 0.14.10
4
+
5
+ ### Patch Changes
6
+
7
+ - Image sizes: `displaySize` gives an image's shown size, after a JPEG's EXIF orientation.
8
+
3
9
  ## 0.14.9
4
10
 
5
11
  ### Patch Changes
@@ -2,4 +2,5 @@ export const MAX_BYTES: number;
2
2
  export const MODEL_MAX_EDGE: number;
3
3
  export function sniffImage(bytes: Uint8Array): string | null;
4
4
  export function imageSize(bytes: Uint8Array, mediaType: string): { width: number; height: number } | null;
5
+ export function displaySize(bytes: Uint8Array, mediaType: string): { width: number; height: number } | null;
5
6
  export function modelUnsafe(bytes: Uint8Array): string | null;
package/image-limits.mjs CHANGED
@@ -45,6 +45,41 @@ export function imageSize(bytes, mediaType) {
45
45
  return null;
46
46
  }
47
47
 
48
+ // The size the image is shown at: a JPEG whose EXIF orientation turns it a quarter (5 to 8) is drawn with its sides swapped, as
49
+ // AppKit and browsers draw it. imageSize is the stored frame, which is what a model provider's limits apply to.
50
+ export function displaySize(bytes, mediaType) {
51
+ const size = imageSize(bytes, mediaType);
52
+ if (!size || mediaType !== "image/jpeg") return size;
53
+ const o = jpegOrientation(bytes);
54
+ return o >= 5 && o <= 8 ? { width: size.height, height: size.width } : size;
55
+ }
56
+
57
+ // The EXIF orientation (1 to 8) in a JPEG's APP1 segment; 1 when it has none.
58
+ function jpegOrientation(bytes) {
59
+ const v = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
60
+ let i = 2;
61
+ while (i + 4 <= bytes.length && bytes[i] === 0xff) {
62
+ const marker = bytes[i + 1];
63
+ if (marker === 0xda || marker === 0xd9) return 1;
64
+ const len = v.getUint16(i + 2);
65
+ const start = i + 4, end = Math.min(i + 2 + len, bytes.length);
66
+ if (marker === 0xe1 && end - start >= 14 && String.fromCharCode(...bytes.subarray(start, start + 4)) === "Exif" && bytes[start + 4] === 0 && bytes[start + 5] === 0) {
67
+ const tiff = start + 6, little = bytes[tiff] === 0x49;
68
+ const u16 = (o) => v.getUint16(o, little), u32 = (o) => v.getUint32(o, little);
69
+ const ifd = tiff + u32(tiff + 4);
70
+ if (ifd + 2 > end) return 1;
71
+ const n = u16(ifd);
72
+ for (let e = 0; e < n && ifd + 2 + e * 12 + 12 <= end; e++) {
73
+ const at = ifd + 2 + e * 12;
74
+ if (u16(at) === 0x0112) return u16(at + 8);
75
+ }
76
+ return 1;
77
+ }
78
+ i += 2 + len;
79
+ }
80
+ return 1;
81
+ }
82
+
48
83
  // Why a model provider would reject these bytes as an image part, or null if
49
84
  // it takes them: an unknown format, a size we can't read, a side over 8000px,
50
85
  // or more than MAX_BYTES. Checked before any file part is emitted.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve-hub",
3
- "version": "0.14.9",
3
+ "version": "0.14.10",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Bot-side code shared by every hub agent: registry tools, the roster hook, the eve channel, the host sandbox and browser, and model helpers.",
6
6
  "type": "module",