119 lines
4.4 KiB
JavaScript
119 lines
4.4 KiB
JavaScript
/* exported getImageSize */
|
|
/* global BlobView */
|
|
/* global parseJPEGMetadata */
|
|
|
|
/*
|
|
* Determine the pixel dimensions of an image without actually
|
|
* decoding the image. Passes an object of metadata to the callback
|
|
* function on success or an error message to the error function on
|
|
* failure. The metadata object will include type, width and height
|
|
* properties. Supported image types are GIF, PNG and JPEG. JPEG
|
|
* metadata may also include information about an EXIF preview image.
|
|
*
|
|
* Because of shortcomings in the way Gecko handles images, the
|
|
* Gallery app will crash with an OOM error if it attempts to decode
|
|
* and display an image that is too big. Images require 4 bytes per
|
|
* pixel, so a 10 megapixel photograph requires 40 megabytes of image
|
|
* memory. This function gives the gallery app a way to reject images
|
|
* that are too large.
|
|
*
|
|
* Requires the BlobView class from shared/js/blobview.js and the
|
|
* parseJPEGMetadata() function from shared/js/media/jpeg_metadata_parser.js
|
|
*/
|
|
function getImageSize(blob, callback, error) {
|
|
'use strict';
|
|
|
|
BlobView.get(blob, 0, Math.min(1024, blob.size), function(data) {
|
|
// Make sure we are at least 8 bytes long before reading the first 8 bytes
|
|
if (data.byteLength <= 8) {
|
|
error('corrupt image file');
|
|
return;
|
|
}
|
|
var magic = data.getBinaryText(0, 8);
|
|
if (magic.substring(0, 4) === 'GIF8') {
|
|
try {
|
|
callback({
|
|
type: 'gif',
|
|
width: data.getUint16(6, true),
|
|
height: data.getUint16(8, true)
|
|
});
|
|
}
|
|
catch (e) {
|
|
error(e.toString());
|
|
}
|
|
}
|
|
else if (magic.substring(0, 8) === '\x89PNG\r\n\x1A\n') {
|
|
try {
|
|
callback({
|
|
type: 'png',
|
|
width: data.getUint32(16, false),
|
|
height: data.getUint32(20, false)
|
|
});
|
|
}
|
|
catch (e) {
|
|
error(e.toString());
|
|
}
|
|
}
|
|
else if (magic.substring(0, 2) === 'BM' &&
|
|
data.getUint32(2, true) === blob.size) {
|
|
// This is a BMP file
|
|
try {
|
|
var width, height;
|
|
|
|
if (data.getUint16(14, true) === 12) { // check format version
|
|
width = data.getUint16(18, true); // 16-bit little endian width
|
|
height = data.getUint16(20, true); // 16-bit little endian height
|
|
}
|
|
else { // newer versions of the format use 32-bit ints
|
|
width = data.getUint32(18, true); // 32-bit little endian width
|
|
height = data.getUint32(22, true); // 32-bit little endian height
|
|
}
|
|
|
|
callback({
|
|
type: 'bmp',
|
|
width: width,
|
|
height: height
|
|
});
|
|
}
|
|
catch (e) {
|
|
error(e.toString());
|
|
}
|
|
}
|
|
else if (magic.substring(0, 2) === '\xFF\xD8') {
|
|
parseJPEGMetadata(blob,
|
|
function(metadata) {
|
|
if (metadata.progressive) {
|
|
// If the jpeg parser tells us that this is
|
|
// a progressive jpeg, then treat that as a
|
|
// distinct image type because pjpegs have
|
|
// such different memory requirements than
|
|
// regular jpegs.
|
|
delete metadata.progressive;
|
|
metadata.type = 'pjpeg';
|
|
}
|
|
else {
|
|
metadata.type = 'jpeg';
|
|
}
|
|
// Modern Gecko decodes EXIF orientation itself, including
|
|
// canvas.drawImage(). Gaia must not rotate those pixels twice.
|
|
if (metadata.rotation || metadata.mirrored) {
|
|
if (metadata.rotation === 90 || metadata.rotation === 270) {
|
|
var width = metadata.width;
|
|
metadata.width = metadata.height;
|
|
metadata.height = width;
|
|
}
|
|
metadata.rotation = 0;
|
|
metadata.mirrored = false;
|
|
// Embedded thumbnails need not carry the parent's EXIF tag.
|
|
// Generate a matching preview from the oriented full image.
|
|
delete metadata.preview;
|
|
}
|
|
callback(metadata);
|
|
},
|
|
error);
|
|
}
|
|
else {
|
|
error('unknown image type');
|
|
}
|
|
});
|
|
}
|