image-in-browser
Version:
Package for encoding / decoding images, transforming images, applying filters, drawing primitives on images on the client side (no need for server Node.js)
164 lines • 5.52 kB
JavaScript
import { InputBuffer } from '../../common/input-buffer.js';
import { OutputBuffer } from '../../common/output-buffer.js';
import { ExifData } from '../../exif/exif-data.js';
import { JpegMarker } from './jpeg-marker.js';
export class JpegUtils {
readExifData(block) {
if (block === undefined) {
return undefined;
}
const signature = block.readUint32();
if (signature !== JpegUtils._exifSignature) {
return undefined;
}
if (block.readUint16() !== 0) {
return undefined;
}
return ExifData.fromInputBuffer(block);
}
writeAPP1(out, exif) {
if (exif.isEmpty) {
return;
}
const exifData = new OutputBuffer();
exif.write(exifData);
const exifBytes = exifData.getBytes();
out.writeUint16(exifBytes.length + 8);
out.writeUint32(JpegUtils._exifSignature);
out.writeUint16(0);
out.writeBytes(exifBytes);
}
readBlock(input) {
const length = input.readUint16();
if (length < 2) {
return undefined;
}
return input.readRange(length - 2);
}
skipBlock(input, output) {
const length = input.readUint16();
output === null || output === void 0 ? void 0 : output.writeUint16(length);
if (length < 2) {
return false;
}
if (output !== undefined) {
output.writeBuffer(input.readRange(length - 2));
}
else {
input.skip(length - 2);
}
return true;
}
nextMarker(input, output) {
let c = 0;
if (input.isEOS) {
return c;
}
do {
do {
c = input.read();
output === null || output === void 0 ? void 0 : output.writeByte(c);
} while (c !== 0xff && !input.isEOS);
if (input.isEOS) {
return c;
}
do {
c = input.read();
output === null || output === void 0 ? void 0 : output.writeByte(c);
} while (c === 0xff && !input.isEOS);
} while (c === 0 && !input.isEOS);
return c;
}
decodeExif(data) {
const input = new InputBuffer({
buffer: data,
bigEndian: true,
});
const soiCheck = input.peek(2);
if (soiCheck.get(0) !== 0xff || soiCheck.get(1) !== 0xd8) {
return undefined;
}
let marker = this.nextMarker(input);
if (marker !== JpegMarker.soi) {
return undefined;
}
let exif = undefined;
marker = this.nextMarker(input);
while (marker !== JpegMarker.eoi && !input.isEOS) {
switch (marker) {
case JpegMarker.app1:
exif = this.readExifData(this.readBlock(input));
if (exif !== undefined) {
return exif;
}
break;
default:
this.skipBlock(input);
break;
}
marker = this.nextMarker(input);
}
return undefined;
}
injectExif(exif, data) {
const input = new InputBuffer({
buffer: data,
bigEndian: true,
});
const soiCheck = input.peek(2);
if (soiCheck.get(0) !== 0xff || soiCheck.get(1) !== 0xd8) {
return undefined;
}
const output = new OutputBuffer({
size: data.length,
bigEndian: true,
});
let marker = this.nextMarker(input, output);
if (marker !== JpegMarker.soi) {
return undefined;
}
let hasExifBlock = false;
const startOffset = input.offset;
marker = this.nextMarker(input);
while (!hasExifBlock && marker !== JpegMarker.eoi && !input.isEOS) {
if (marker === JpegMarker.app1) {
const block = this.readBlock(input);
const signature = block === null || block === void 0 ? void 0 : block.readUint32();
if (signature === JpegUtils._exifSignature) {
hasExifBlock = true;
break;
}
}
else {
this.skipBlock(input);
}
marker = this.nextMarker(input);
}
input.offset = startOffset;
if (!hasExifBlock) {
this.writeAPP1(output, exif);
output.writeBuffer(input.readRange(input.length));
return output.getBytes();
}
marker = this.nextMarker(input, output);
while (marker !== JpegMarker.eoi && !input.isEOS) {
if (marker === JpegMarker.app1) {
const saveOffset = input.offset;
input.skip(2);
const signature = input.readUint32();
input.offset = saveOffset;
if (signature === JpegUtils._exifSignature) {
this.skipBlock(input);
this.writeAPP1(output, exif);
output.writeBuffer(input.readRange(input.length));
return output.getBytes();
}
}
this.skipBlock(input, output);
marker = this.nextMarker(input, output);
}
return output.getBytes();
}
}
JpegUtils._exifSignature = 0x45786966;
//# sourceMappingURL=jpeg-utils.js.map