web: keep a saved photo's look in EXIF and its own row
EXPORT no longer burns the caption strip: the pixels stay the photo's own and the look travels as metadata — ImageDescription (0x010e) for the tag, UserComment (0x9286, ASCII header) for the recipe JSON. SAVE PHOTO now stores the look with the frame (photos.recipe) and the uploader's consent for the community film strip (photos.consent, PATCH /api/photos/:id for the owner). The landing reel skips non-consented frames, and a new MY PHOTOS tab lists the account's saves, reopens one with the settings it was stored with, and carries the two consent switches.
This commit is contained in:
@@ -32,6 +32,11 @@ export interface ExifStamp {
|
||||
// Device identity for the Gallery watermark blob (ref. watermarkBlob). Only
|
||||
// used when the source photo carries no blob of its own.
|
||||
device?: DeviceIdentity | null;
|
||||
// What the studio would once have burned into the pixels: the frame's tagline
|
||||
// as ImageDescription (0x010e) and the whole look as JSON in UserComment
|
||||
// (0x9286). Kept in metadata only — an exported file must stay clean.
|
||||
imageDescription?: string | null;
|
||||
userComment?: string | null;
|
||||
}
|
||||
|
||||
/** Raw device identity for the HyperOS Gallery watermark tag (0x889e). */
|
||||
@@ -114,6 +119,16 @@ function undefinedEntry(tag: number, text: string): Entry {
|
||||
return { tag, type: UNDEFINED, count: data.length, data };
|
||||
}
|
||||
|
||||
// UserComment (0x9286) must open with an 8-byte character-code header; the rest
|
||||
// is UTF-8, which every reader that accepts "ASCII" here decodes correctly.
|
||||
function userCommentEntry(text: string): Entry {
|
||||
const body = new TextEncoder().encode(text);
|
||||
const data = new Uint8Array(8 + body.length);
|
||||
data.set([0x41, 0x53, 0x43, 0x49, 0x49, 0, 0, 0]); // "ASCII\0\0\0"
|
||||
data.set(body, 8);
|
||||
return { tag: 0x9286, type: UNDEFINED, count: data.length, data };
|
||||
}
|
||||
|
||||
function longEntry(tag: number, v: number): Entry {
|
||||
const data = new Uint8Array(4);
|
||||
u32w(data, 0, v);
|
||||
@@ -384,6 +399,13 @@ export function writeJpegExif(jpeg: Uint8Array, stamp: ExifStamp, sourceJpeg?: U
|
||||
ifd0.set(0x0131, asciiEntry(0x0131, stamp.software ?? SOFTWARE));
|
||||
ifd0.set(0x0112, shortEntry(0x0112, 1)); // upright — the pixels are already straight
|
||||
|
||||
// The frame's own labels, metadata-only: ImageDescription names the look and
|
||||
// UserComment carries the settings so the file reopens as it was saved.
|
||||
const desc = stamp.imageDescription?.trim();
|
||||
if (desc) ifd0.set(0x010e, asciiEntry(0x010e, desc));
|
||||
const comment = stamp.userComment?.trim();
|
||||
if (comment) exif.set(0x9286, userCommentEntry(comment));
|
||||
|
||||
// Baseline tags a conforming EXIF block is expected to carry. Framing apps
|
||||
// reject a file that omits them, and HALs (the emulator's especially) do not
|
||||
// always supply them, so fill the gaps and keep whatever the source had.
|
||||
|
||||
Reference in New Issue
Block a user