summaryrefslogtreecommitdiff
path: root/src/photos/RawSupport.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/photos/RawSupport.vala')
-rw-r--r--src/photos/RawSupport.vala350
1 files changed, 350 insertions, 0 deletions
diff --git a/src/photos/RawSupport.vala b/src/photos/RawSupport.vala
new file mode 100644
index 0000000..98bc982
--- /dev/null
+++ b/src/photos/RawSupport.vala
@@ -0,0 +1,350 @@
+/* Copyright 2010-2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+public class RawFileFormatDriver : PhotoFileFormatDriver {
+ private static RawFileFormatDriver instance = null;
+
+ public static void init() {
+ instance = new RawFileFormatDriver();
+ RawFileFormatProperties.init();
+ }
+
+ public static RawFileFormatDriver get_instance() {
+ return instance;
+ }
+
+ public override PhotoFileFormatProperties get_properties() {
+ return RawFileFormatProperties.get_instance();
+ }
+
+ public override PhotoFileReader create_reader(string filepath) {
+ return new RawReader(filepath);
+ }
+
+ public override PhotoMetadata create_metadata() {
+ return new PhotoMetadata();
+ }
+
+ public override bool can_write_image() {
+ return false;
+ }
+
+ public override bool can_write_metadata() {
+ return false;
+ }
+
+ public override PhotoFileWriter? create_writer(string filepath) {
+ return null;
+ }
+
+ public override PhotoFileMetadataWriter? create_metadata_writer(string filepath) {
+ return null;
+ }
+
+ public override PhotoFileSniffer create_sniffer(File file, PhotoFileSniffer.Options options) {
+ return new RawSniffer(file, options);
+ }
+}
+
+public class RawFileFormatProperties : PhotoFileFormatProperties {
+ private static string[] KNOWN_EXTENSIONS = {
+ "3fr", "arw", "srf", "sr2", "bay", "crw", "cr2", "cap", "iiq", "eip", "dcs", "dcr", "drf",
+ "k25", "kdc", "dng", "erf", "fff", "mef", "mos", "mrw", "nef", "nrw", "orf", "ptx", "pef",
+ "pxn", "r3d", "raf", "raw", "rw2", "raw", "rwl", "rwz", "x3f", "srw"
+ };
+
+ private static string[] KNOWN_MIME_TYPES = {
+ /* a catch-all MIME type for all formats supported by the dcraw command-line
+ tool (and hence libraw) */
+ "image/x-dcraw",
+
+ /* manufacturer blessed MIME types */
+ "image/x-canon-cr2",
+ "image/x-canon-crw",
+ "image/x-fuji-raf",
+ "image/x-adobe-dng",
+ "image/x-panasonic-raw",
+ "image/x-raw",
+ "image/x-minolta-mrw",
+ "image/x-nikon-nef",
+ "image/x-olympus-orf",
+ "image/x-pentax-pef",
+ "image/x-sony-arw",
+ "image/x-sony-srf",
+ "image/x-sony-sr2",
+ "image/x-samsung-raw",
+
+ /* generic MIME types for file extensions*/
+ "image/x-3fr",
+ "image/x-arw",
+ "image/x-srf",
+ "image/x-sr2",
+ "image/x-bay",
+ "image/x-crw",
+ "image/x-cr2",
+ "image/x-cap",
+ "image/x-iiq",
+ "image/x-eip",
+ "image/x-dcs",
+ "image/x-dcr",
+ "image/x-drf",
+ "image/x-k25",
+ "image/x-kdc",
+ "image/x-dng",
+ "image/x-erf",
+ "image/x-fff",
+ "image/x-mef",
+ "image/x-mos",
+ "image/x-mrw",
+ "image/x-nef",
+ "image/x-nrw",
+ "image/x-orf",
+ "image/x-ptx",
+ "image/x-pef",
+ "image/x-pxn",
+ "image/x-r3d",
+ "image/x-raf",
+ "image/x-raw",
+ "image/x-rw2",
+ "image/x-raw",
+ "image/x-rwl",
+ "image/x-rwz",
+ "image/x-x3f",
+ "image/x-srw"
+ };
+
+ private static RawFileFormatProperties instance = null;
+
+ public static void init() {
+ instance = new RawFileFormatProperties();
+ }
+
+ public static RawFileFormatProperties get_instance() {
+ return instance;
+ }
+
+ public override PhotoFileFormat get_file_format() {
+ return PhotoFileFormat.RAW;
+ }
+
+ public override string get_user_visible_name() {
+ return _("RAW");
+ }
+
+ public override PhotoFileFormatFlags get_flags() {
+ return PhotoFileFormatFlags.NONE;
+ }
+
+ public override string get_default_extension() {
+ // Because RAW is a smorgasbord of file formats and exporting to a RAW file is
+ // not expected, this function should probably never be called. However, need to pick
+ // one, so here it is.
+ return "raw";
+ }
+
+ public override string[] get_known_extensions() {
+ return KNOWN_EXTENSIONS;
+ }
+
+ public override string get_default_mime_type() {
+ return KNOWN_MIME_TYPES[0];
+ }
+
+ public override string[] get_mime_types() {
+ return KNOWN_MIME_TYPES;
+ }
+}
+
+public class RawSniffer : PhotoFileSniffer {
+ public RawSniffer(File file, PhotoFileSniffer.Options options) {
+ base (file, options);
+ }
+
+ public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error {
+ // this sniffer doesn't detect corrupted files
+ is_corrupted = false;
+
+ DetectedPhotoInformation detected = new DetectedPhotoInformation();
+
+ GRaw.Processor processor = new GRaw.Processor();
+ processor.output_params->user_flip = GRaw.Flip.NONE;
+
+ try {
+ processor.open_file(file.get_path());
+ processor.unpack();
+ processor.adjust_sizes_info_only();
+ } catch (GRaw.Exception exception) {
+ if (exception is GRaw.Exception.UNSUPPORTED_FILE)
+ return null;
+
+ throw exception;
+ }
+
+ detected.image_dim = Dimensions(processor.get_sizes().iwidth, processor.get_sizes().iheight);
+ detected.colorspace = Gdk.Colorspace.RGB;
+ detected.channels = 3;
+ detected.bits_per_channel = 8;
+
+ RawReader reader = new RawReader(file.get_path());
+ try {
+ detected.metadata = reader.read_metadata();
+ } catch (Error err) {
+ // ignored
+ }
+
+ if (detected.metadata != null) {
+ uint8[]? flattened_sans_thumbnail = detected.metadata.flatten_exif(false);
+ if (flattened_sans_thumbnail != null && flattened_sans_thumbnail.length > 0)
+ detected.exif_md5 = md5_binary(flattened_sans_thumbnail, flattened_sans_thumbnail.length);
+
+ uint8[]? flattened_thumbnail = detected.metadata.flatten_exif_preview();
+ if (flattened_thumbnail != null && flattened_thumbnail.length > 0)
+ detected.thumbnail_md5 = md5_binary(flattened_thumbnail, flattened_thumbnail.length);
+ }
+
+ if (calc_md5)
+ detected.md5 = md5_file(file);
+
+ detected.format_name = "raw";
+ detected.file_format = PhotoFileFormat.RAW;
+
+ return detected;
+ }
+}
+
+public class RawReader : PhotoFileReader {
+ public RawReader(string filepath) {
+ base (filepath, PhotoFileFormat.RAW);
+ }
+
+ public override PhotoMetadata read_metadata() throws Error {
+ PhotoMetadata metadata = new PhotoMetadata();
+ metadata.read_from_file(get_file());
+
+ return metadata;
+ }
+
+ public override Gdk.Pixbuf unscaled_read() throws Error {
+ GRaw.Processor processor = new GRaw.Processor();
+ processor.configure_for_rgb_display(false);
+ processor.output_params->user_flip = GRaw.Flip.NONE;
+
+ processor.open_file(get_filepath());
+ processor.unpack();
+ processor.process();
+
+ return processor.make_mem_image().get_pixbuf_copy();
+ }
+
+ public override Gdk.Pixbuf scaled_read(Dimensions full, Dimensions scaled) throws Error {
+ double width_proportion = (double) scaled.width / (double) full.width;
+ double height_proportion = (double) scaled.height / (double) full.height;
+ bool half_size = width_proportion < 0.5 && height_proportion < 0.5;
+
+ GRaw.Processor processor = new GRaw.Processor();
+ processor.configure_for_rgb_display(half_size);
+ processor.output_params->user_flip = GRaw.Flip.NONE;
+
+ processor.open_file(get_filepath());
+ processor.unpack();
+ processor.process();
+
+ GRaw.ProcessedImage image = processor.make_mem_image();
+
+ return resize_pixbuf(image.get_pixbuf_copy(), scaled, Gdk.InterpType.BILINEAR);
+ }
+}
+
+// Development mode of a RAW photo.
+public enum RawDeveloper {
+ SHOTWELL = 0, // Developed internally by Shotwell
+ CAMERA, // JPEG from RAW+JPEG pair (if available)
+ EMBEDDED; // Largest-size
+
+ public static RawDeveloper[] as_array() {
+ return { SHOTWELL, CAMERA, EMBEDDED };
+ }
+
+ public string to_string() {
+ switch (this) {
+ case SHOTWELL:
+ return "SHOTWELL";
+ case CAMERA:
+ return "CAMERA";
+ case EMBEDDED:
+ return "EMBEDDED";
+ default:
+ assert_not_reached();
+ }
+ }
+
+ public static RawDeveloper from_string(string value) {
+ switch (value) {
+ case "SHOTWELL":
+ return SHOTWELL;
+ case "CAMERA":
+ return CAMERA;
+ case "EMBEDDED":
+ return EMBEDDED;
+ default:
+ assert_not_reached();
+ }
+ }
+
+ public string get_label() {
+ switch (this) {
+ case SHOTWELL:
+ return _("Shotwell");
+ case CAMERA:
+ case EMBEDDED:
+ return _("Camera");
+ default:
+ assert_not_reached();
+ }
+ }
+
+ // Determines if two RAW developers are equivalent, treating camera and embedded
+ // as the same.
+ public bool is_equivalent(RawDeveloper d) {
+ if (this == d)
+ return true;
+
+ if ((this == RawDeveloper.CAMERA && d == RawDeveloper.EMBEDDED) ||
+ (this == RawDeveloper.EMBEDDED && d == RawDeveloper.CAMERA))
+ return true;
+
+ return false;
+ }
+
+ // Creates a backing JPEG.
+ // raw_filepath is the full path of the imported RAW file.
+ public BackingPhotoRow create_backing_row_for_development(string raw_filepath,
+ string? camera_development_filename = null) throws Error {
+ BackingPhotoRow ns = new BackingPhotoRow();
+ File master = File.new_for_path(raw_filepath);
+ string name, ext;
+ disassemble_filename(master.get_basename(), out name, out ext);
+
+ string basename;
+
+ // If this image is coming in with an existing development, use its existing
+ // filename instead.
+ if (camera_development_filename == null) {
+ basename = name + "_" + ext +
+ (this != CAMERA ? ("_" + this.to_string().down()) : "") + ".jpg";
+ } else {
+ basename = camera_development_filename;
+ }
+
+ bool c;
+ File? new_back = generate_unique_file(master.get_parent(), basename, out c);
+ claim_file(new_back);
+ ns.file_format = PhotoFileFormat.JFIF;
+ ns.filepath = new_back.get_path();
+
+ return ns;
+ }
+}