summaryrefslogtreecommitdiff
path: root/src/photos/JfifSupport.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/photos/JfifSupport.vala')
-rw-r--r--src/photos/JfifSupport.vala108
1 files changed, 92 insertions, 16 deletions
diff --git a/src/photos/JfifSupport.vala b/src/photos/JfifSupport.vala
index 5ea64a5..0de45f8 100644
--- a/src/photos/JfifSupport.vala
+++ b/src/photos/JfifSupport.vala
@@ -103,17 +103,78 @@ public class JfifSniffer : GdkSniffer {
}
public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error {
- // Rely on GdkSniffer to detect corruption
is_corrupted = false;
-
- if (!Jpeg.is_jpeg(file))
- return null;
-
- DetectedPhotoInformation? detected = base.sniff(out is_corrupted);
- if (detected == null)
+ if (!calc_md5) {
+ return fast_sniff (out is_corrupted);
+ } else {
+ if (!Jpeg.is_jpeg(file)) {
+ return null;
+ }
+
+ // Rely on GdkSniffer to detect corruption
+
+ DetectedPhotoInformation? detected = base.sniff(out is_corrupted);
+ if (detected == null)
+ return null;
+
+ return (detected.file_format == PhotoFileFormat.JFIF) ? detected : null;
+ }
+ }
+
+ private DetectedPhotoInformation? fast_sniff(out bool is_corrupted) throws Error {
+ is_corrupted = false;
+ var detected = new DetectedPhotoInformation();
+
+ detected.metadata = new PhotoMetadata();
+ try {
+ detected.metadata.read_from_file(file);
+ } catch (Error err) {
+ // no metadata detected
+ detected.metadata = null;
+ }
+
+ var fins = file.read(null);
+ var dins = new DataInputStream(fins);
+ dins.set_byte_order(DataStreamByteOrder.BIG_ENDIAN);
+ var seekable = (Seekable) dins;
+
+ var marker = Jpeg.Marker.INVALID;
+ var length = Jpeg.read_marker_2(dins, out marker);
+
+ if (marker != Jpeg.Marker.SOI) {
return null;
-
- return (detected.file_format == PhotoFileFormat.JFIF) ? detected : null;
+ }
+
+ length = Jpeg.read_marker_2(dins, out marker);
+ while (!marker.is_sof() && length > 0) {
+ seekable.seek(length, SeekType.CUR, null);
+ length = Jpeg.read_marker_2(dins, out marker);
+ }
+
+ if (marker.is_sof()) {
+ if (length < 6) {
+ is_corrupted = true;
+ return null;
+ }
+
+ // Skip precision
+ dins.read_byte();
+
+ // Next two 16 bytes are image dimensions
+ uint16 height = dins.read_uint16();
+ uint16 width = dins.read_uint16();
+
+ detected.image_dim = Dimensions(width, height);
+ detected.colorspace = Gdk.Colorspace.RGB;
+ detected.channels = 3;
+ detected.bits_per_channel = 8;
+ detected.format_name = "jpeg";
+ detected.file_format = PhotoFileFormat.from_pixbuf_name(detected.format_name);
+ } else {
+ is_corrupted = true;
+ }
+
+ return detected;
}
}
@@ -159,6 +220,16 @@ namespace Jpeg {
public uint8 get_byte() {
return (uint8) this;
}
+
+ public bool is_sof() {
+ // FFCn is SOF unless n is a multiple of 4 > 0 (FFC4, FFC8, FFCC)
+ if ((this & 0xC0) != 0xC0) {
+ return false;
+ }
+
+ var variant = this & 0x0F;
+ return variant == 0 || variant % 4 != 0;
+ }
}
public enum Quality {
@@ -219,12 +290,9 @@ namespace Jpeg {
return is_jpeg_stream(mins);
}
- private int read_marker(InputStream fins, out Jpeg.Marker marker) throws Error {
+ private int32 read_marker_2(DataInputStream dins, out Jpeg.Marker marker) throws Error {
marker = Jpeg.Marker.INVALID;
-
- DataInputStream dins = new DataInputStream(fins);
- dins.set_byte_order(DataStreamByteOrder.BIG_ENDIAN);
-
+
if (dins.read_byte() != Jpeg.MARKER_PREFIX)
return -1;
@@ -235,9 +303,10 @@ namespace Jpeg {
}
uint16 length = dins.read_uint16();
- if (length < 2 && fins is Seekable) {
+ var seekable = dins as Seekable;
+ if (length < 2 && dins != null) {
debug("Invalid length %Xh at ofs %" + int64.FORMAT + "Xh", length,
- (fins as Seekable).tell() - 2);
+ seekable.tell() - 2);
return -1;
}
@@ -245,5 +314,12 @@ namespace Jpeg {
// account for two length bytes already read
return length - 2;
}
+
+ private int read_marker(InputStream fins, out Jpeg.Marker marker) throws Error {
+ DataInputStream dins = new DataInputStream(fins);
+ dins.set_byte_order(DataStreamByteOrder.BIG_ENDIAN);
+
+ return read_marker_2(dins, out marker);
+ }
}