1 /**
2  * This module provides the public interface for Daffodil.
3  */
4 module daffodil;
5 
6 import std.path;
7 import std.stdio;
8 import std.typecons;
9 import std.algorithm;
10 
11 import daffodil.util.data;
12 import daffodil.util.range;
13 
14 public {
15     import daffodil.meta;
16     import daffodil.image;
17     import daffodil.pixel;
18     import daffodil.colorspace;
19     import daffodil.util.errors;
20 
21     // Submodules
22     static {
23         import filter = daffodil.filter;
24         import transform = daffodil.transform;
25         // Image Formats
26         import bmp = daffodil.bmp;
27     }
28 }
29 
30 /**
31  * Detects the :d:struct:`Format` a given input is in.
32  */
33 Format detectFormat(T)(T data) if (isDataRange!T) {
34     auto range = data.inputRangeObject;
35     foreach (format; formats) {
36         try {
37             if (format.check(range)) {
38                 return format;
39             }
40         } catch (ImageException e) {
41             continue;
42         }
43 
44     }
45     throw new NotSupported("Unknown Format");
46 }
47 /// Ditto
48 auto detectFormat(T)(T loadeable) if (isLoadeable!T) {
49     return detectFormat(dataLoad(loadeable));
50 }
51 /// Ditto
52 Format detectFormat(V = real)(const Image!V image) if (isColorValue!V) {
53     auto typeInfo = typeid(image.meta);
54     foreach (format; formats) {
55         if (format.metaType && format.metaType == typeInfo) {
56             return format;
57         }
58     }
59     throw new NotSupported("Unknown Format");
60 }
61 
62 /**
63  * Loads the metadata from a given input.
64  */
65 auto loadMeta(T)(T data) if (isDataRange!T) {
66     auto format = detectFormat(data);
67     return format.loadMeta(data.inputRangeObject);
68 }
69 /// Ditto
70 auto loadMeta(T)(T loadeable) if (isLoadeable!T) {
71     return loadMeta(dataLoad(loadeable));
72 }
73 
74 /**
75  * Loads a :d:class:`Image` from a given input.
76  */
77 auto load(V = real, T)(T data) if (isColorValue!V && isDataRange!T) {
78     auto range = data.inputRangeObject;
79     auto format = detectFormat(data);
80     auto meta = format.loadMeta(range);
81     return new Image!V(format.loadImage(range, meta), meta);
82 }
83 /// Ditto
84 auto load(V = real, T)(T loadeable) if (isColorValue!V && isLoadeable!T) {
85     return load!V(dataLoad(loadeable));
86 }
87 
88 /**
89  * Saves a particular :d:class:`Image` to a given output.
90  */
91 void save(V = real, T)(const Image!V image, T data) if (isColorValue!V && isOutRange!T) {
92     auto format = detectFormat(image);
93     format.save(data.outputRangeObject!ubyte, image.range.imageRangeObject, image.meta);
94 }
95 /// Ditto
96 void save(V = real)(const Image!V image, string path) if (isColorValue!V) {
97     // Specialcase for paths, to match by extension
98     Nullable!Format format;
99     foreach (f; formats) {
100         if (f.extensions.canFind(path.extension)) {
101             format = f;
102             break;
103         }
104     }
105 
106     if (format.isNull) {
107         format = detectFormat(image);
108     }
109 
110     auto data = dataSave(path).outputRangeObject!ubyte;
111     format.save(data, image.range.imageRangeObject, image.meta);
112 }
113 /// Ditto
114 void save(V = real, T)(const Image!V image, T saveable) if (isColorValue!V && isSaveable!T && !is(T == string)) {
115     return save(image, dataSave(saveable));
116 }
117 
118 ///
119 alias DataRange = InputRange!ubyte;
120 ///
121 template isDataRange(T) {
122     enum isDataRange = isInputRange!T && is(ElementType!T == ubyte);
123 }
124 ///
125 alias OutRange = OutputRange!ubyte;
126 ///
127 alias isOutRange(T) = isOutputRange!(T, ubyte);
128 
129 /**
130  * A struct for metadata about an Image Format. See :d:func:`registerFormat` for
131  * more information.
132  */
133 struct Format {
134     ///
135     string name;
136     ///
137     bool function(DataRange) check;
138     ///
139     MetaData function(DataRange) loadMeta;
140     ///
141     ImageRange!PixelData function(DataRange, MetaData) loadImage;
142     ///
143     void function(OutRange, RandomAccessImageRange!(real[]), const MetaData) save;
144     ///
145     string[] extensions;
146     ///
147     TypeInfo metaType;
148 }
149 
150 private Format[] formats;
151 private Format[string] formatsByExt;
152 
153 /**
154  * Register a new format for auto-detection with :d:func:`detectFormat`,
155  * :d:func:`loadMeta` and :d:func:`load` functions.
156  */
157 void registerFormat(Format format) {
158     formats ~= format;
159 
160     foreach (ext; format.extensions) {
161         assert(ext !in formatsByExt);
162         formatsByExt[ext] = format;
163     }
164 }