1 /**
2 Gamut public API. This is the main image abstraction.
3 
4 Copyright: Copyright Guillaume Piolat 2022
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module gamut.image;
8 
9 import core.stdc.stdio;
10 import core.stdc.stdlib: malloc, free, realloc;
11 import core.stdc.string: strlen;
12 
13 import gamut.types;
14 import gamut.io;
15 import gamut.plugin;
16 import gamut.internals.cstring;
17 import gamut.internals.errors;
18 import gamut.internals.types;
19 
20 public import gamut.types: ImageFormat;
21 
22 nothrow @nogc @safe:
23 
24 /// Deallocate pixel data. Everything allocated with `allocatePixelStorage` or disowned eventually needs
25 /// to be through that function.
26 void freeImageData(void* mallocArea) @trusted
27 {
28     deallocatePixelStorage(mallocArea);
29 }
30 
31 /// Image type.
32 /// Image has disabled copy ctor and postblit, to avoid accidental allocations.
33 /// 
34 /// IMPORTANT
35 ///
36 /// Images are subtyped like this:
37 ///
38 ///                 Image                              All image can also be: errored() or not.
39 ///                /     \
40 ///          no-type  or  hasType()                    
41 ///                      /         \                          
42 ///                hasData()  or    no-data            Also: hasNonZeroSize(). 
43 ///            ___/     |   \______                          Images with a type have a width and height (that can be zero).
44 ///           /         |          \                   Also: isOwned() exist for image that are hasData().
45 ///   isPlanar or hasPlainPixels or isCompressed       Planar and compressed images are not implemented.
46 ///                                                    Only image with hasData() have to follow the LayoutConstraints,
47 ///                                                    though all image have a LayoutConstraints.
48 ///
49 /// Public Functions are labelled this way:
50 ///   #type     => the calling Image must have a type.
51 ///   #data     => the calling Image must have data (implies #type)
52 ///   #plain    => the calling Image must have plain-pixels.
53 ///   #own      => the calling Image must have data AND own it.
54 /// It is a programming error to call a function that doesn't follow the tag constraints.
55 ///
56 struct Image
57 {
58 nothrow @nogc @safe:
59 public:
60 
61     //
62     // <BASIC STORAGE>
63     //
64 
65     /// Get the pixel type.
66     /// See_also: `PixelType`.
67     /// Tags: none.
68     PixelType type() pure const
69     {
70         return _type;
71     }
72 
73     /// Returns: Width of image in pixels.
74     /// Tags: #type
75     int width() pure const
76     {
77         assert(hasType());
78         return _width;
79     }
80 
81     /// Returns: Height of image in pixels.
82     /// Tags: #type
83     int height() pure const
84     {
85         assert(hasType());
86         return _height;
87     }  
88 
89     /// Get the image pitch (byte distance between rows), in bytes.
90     ///
91     /// Warning: This pitch can be, or not be, a negative integer.
92     ///          When the image has layout constraint LAYOUT_VERT_FLIPPED, 
93     ///             it is always kept <= 0 (if the image has data).
94     ///          When the image has layout constraint LAYOUT_VERT_STRAIGHT, 
95     ///             it is always kept >= 0 (if the image has data).
96     ///
97     /// See_also: `scanlineInBytes`.
98     /// Tags: #type #data
99     int pitchInBytes() pure const
100     {
101         assert(hasType() && hasData());
102 
103         bool forceVFlip   = (_layoutConstraints & LAYOUT_VERT_FLIPPED) != 0;
104         bool forceNoVFlip = (_layoutConstraints & LAYOUT_VERT_STRAIGHT) != 0;
105         if (forceVFlip)
106             assert(_pitch <= 0); // Note if height were zero, _pitch could perhaps be zero.
107         if (forceNoVFlip)
108             assert(_pitch >= 0);
109 
110         return _pitch;
111     }
112 
113     /// Length of the managed scanline pixels, in bytes.
114     /// 
115     /// This is NOT the pointer offset between two scanlines (`pitchInBytes`).
116     /// This is just `width() * size-of-one-pixel`.
117     /// Those bytes are "part of the image", while the trailing and border pixels are not.
118     ///
119     /// See_also: `pitchInBytes`.
120     /// Tags: #type #data
121     int scanlineInBytes() pure const
122     {
123         assert(hasData());
124         return _width * pixelTypeSize(type);
125     }
126 
127     /// A compressed image doesn't have its pixels available.
128     /// Warning: only makes sense for image that `hasData()`, with non-zero height.
129     /// Tags: #type #data
130     bool isStoredUpsideDown() pure const
131     {
132         assert(hasData());
133         return _pitch < 0;
134     }
135 
136     /// Returns a pointer to the `y` nth line of pixels.
137     /// Only possible if the image has plain pixels.
138     /// What pixel format it points to, depends on the image `type()`.
139     ///
140     /// ---
141     /// Guarantees by layout constraints:
142     ///  * next scanline (if any) is returned pointer + pitchInBytes() bytes.
143     ///  * scanline pointer are aligned by given scanline alignment flags (at least).
144     ///  * after each scanline there is at least a number of trailing pixels given by layout flags
145     ///  * scanline pixels can be processed by multiplicity given by layout flags
146     ///  * around the image, there is a border whose width is at least the one given by layout flags.
147     /// ---
148     ///
149     /// For each scanline pointer, you can _always_ READ `ptr[0..pitchInBytes()]` without memory error.
150     /// However, WRITING to this scanline doesn't guarantee anything by itself since the image 
151     /// could be a sub-image, and the underlying buffer could be shared. 
152     ///
153     /// Returns: The scanline start.
154     ///          Next scanline (if any) is returned pointer + pitchInBytes() bytes
155     /// Tags: #type #data #plain
156     inout(ubyte)* scanline(int y) inout pure @trusted
157     {
158         assert(isPlainPixels());
159         assert(y >= 0 && y < _height);
160         return _data + _pitch * y;
161     }
162 
163     /// Returns a slice of all pixels at once in O(1). 
164     /// This is only possible if the image is stored non-flipped, and without space
165     /// between scanline.
166     /// To avoid accidental correctness, the image need the layout constraints:
167     /// `LAYOUT_GAPLESS | LAYOUT_VERT_STRAIGHT`.
168     /// Tags: #type #data #plain
169     inout(ubyte)[] allPixelsAtOnce() inout pure @trusted
170     {
171         assert(isPlainPixels());
172 
173         // the image need the LAYOUT_GAPLESS flag.
174         assert(isGapless());
175 
176         // the image need the LAYOUT_VERT_STRAIGHT flag.
177         assert(mustNotBeStoredUpsideDown());
178 
179         // Why there is no #overflow here:
180         int psize = pixelTypeSize(_type);
181         assert(psize < GAMUT_MAX_PIXEL_SIZE);
182         assert(cast(long)_width * _height < GAMUT_MAX_IMAGE_WIDTH_x_HEIGHT);
183         assert(cast(long)GAMUT_MAX_IMAGE_WIDTH_x_HEIGHT * GAMUT_MAX_PIXEL_SIZE < 0x7fffffffUL);
184         int ofs = _width * _height * psize;
185         return _data[0..ofs];
186     }
187 
188     //
189     // </BASIC STORAGE>
190     //
191 
192     //
193     // <RESOLUTION>
194     //
195 
196     /// Returns: Horizontal resolution in Dots Per Inch (DPI).
197     ///          `GAMUT_UNKNOWN_RESOLUTION` if unknown.
198     /// Tags: none.
199     float dotsPerInchX() pure const
200     {
201         if (_resolutionY == GAMUT_UNKNOWN_RESOLUTION || _pixelAspectRatio == GAMUT_UNKNOWN_ASPECT_RATIO)
202             return GAMUT_UNKNOWN_RESOLUTION;
203         return _resolutionY * _pixelAspectRatio;
204     }
205 
206     /// Returns: Vertical resolution in Dots Per Inch (DPI).
207     ///          `GAMUT_UNKNOWN_RESOLUTION` if unknown.
208     /// Tags: none.
209     float dotsPerInchY() pure const
210     {
211         return _resolutionY;
212     }
213 
214     /// Returns: Pixel Aspect Ratio for the image (PAR).
215     ///          `GAMUT_UNKNOWN_ASPECT_RATIO` if unknown.
216     ///
217     /// This is physical width of a pixel / physical height of a pixel.
218     ///
219     /// Reference: https://en.wikipedia.org/wiki/Pixel_aspect_ratio
220     /// Tags: none.
221     float pixelAspectRatio() pure const
222     {
223         return _pixelAspectRatio;
224     }
225 
226     /// Returns: Horizontal resolution in Pixels Per Meters (PPM).
227     ///          `GAMUT_UNKNOWN_RESOLUTION` if unknown.
228     /// Tags: none.
229     float pixelsPerMeterX() pure const
230     {
231         float dpi = dotsPerInchX();
232         if (dpi == GAMUT_UNKNOWN_RESOLUTION)
233             return GAMUT_UNKNOWN_RESOLUTION;
234         return convertMetersToInches(dpi);
235     }
236 
237     /// Returns: Vertical resolution in Pixels Per Meters (PPM).
238     ///          `GAMUT_UNKNOWN_RESOLUTION` if unknown.
239     /// Tags: none.
240     float pixelsPerMeterY() pure const
241     {
242         float dpi = dotsPerInchY();
243         if (dpi == GAMUT_UNKNOWN_RESOLUTION)
244             return GAMUT_UNKNOWN_RESOLUTION;
245         return convertMetersToInches(dpi);
246     }
247 
248     //
249     // </RESOLUTION>
250     //
251 
252 
253     //
254     // <GETTING STATUS AND CAPABILITIES>
255     //
256 
257     /// Was there an error as a result of calling a public method of `Image`?
258     /// It is now unusable.
259     /// Tags: none.
260     bool errored() pure const
261     {
262         return _error !is null;
263     }
264 
265     /// The error message (null if no error currently held).
266     /// This slice is followed by a '\0' zero terminal character, so
267     /// it can be safely given to `print`.
268     /// Tags: none.
269     const(char)[] errorMessage() pure const @trusted
270     {
271         if (_error is null)
272             return null;
273         return _error[0..strlen(_error)];
274     }
275 
276     /// An image can have a pixel type (usually pixels), or not.
277     /// Not a lot of operations are available if there is no type.
278     /// Note: An image that has no must necessarily have no data.
279     /// Tags: none.
280     bool hasType() pure const
281     {
282         return _type != PixelType.unknown;
283     }
284 
285     /// An image can have data (usually pixels), or not.
286     /// "Data" refers to pixel content, that can be in a decoded form, but also in more
287     /// complicated forms such as planar, compressed, etc. (FUTURE)
288     ///
289     /// Note: An image that has no data doesn't have to follow its `LayoutConstraints`.
290     ///       But an image with zero size must.
291     /// An image that "has data" also "has a type".
292     /// Tags: none.
293     bool hasData() pure const
294     {
295         if (_data)
296         {
297             // Having _data is a superset of having a _type. Else, it's a gamut internal error.
298             assert(hasType());
299         }
300 
301         return _data !is null;
302     }
303 
304     /// An that has data can own it (will free it in destructor) or can borrow it.
305     /// An image that has no data, cannot own it.
306     /// Tags: none.
307     bool isOwned() pure const
308     {
309         return hasData() && (_allocArea !is null);
310     }
311 
312     /// Disown the image allocation data.
313     /// This return both the pixel _data (same as and the allocation data
314     /// The data MUST be freed with `freeImageData`.
315     /// The image still points into that data, and you must ensure the data lifetime exceeeds
316     /// the image lifetime.
317     /// Tags: #type #own #data 
318     /// Warning: this return the malloc'ed area, NOT the image data itself.
319     ///          However, with the constraints
320     ubyte* disownData() pure 
321     {
322         assert(isOwned());
323         ubyte* r = _allocArea;
324         _allocArea = null;
325         assert(!isOwned());
326         return r;
327     }
328 
329     /// An image can have plain pixels, which means:
330     ///   1. it has data (and as such, a type)
331     ///   2. those are in a plain decoded format (not a compressed texture, not planar, etc).
332     /// Tags: none.
333     deprecated alias hasPlainPixels = isPlainPixels;
334     bool isPlainPixels() pure const
335     {
336         return hasData() && pixelTypeIsPlain(_type); // Note: all formats are plain, for now.
337     }
338 
339     /// A planar image is for example YUV420.
340     /// If the image is planar, its lines are not accessible like that.
341     /// Currently not supported.
342     /// Tags: none.
343     bool isPlanar() pure const
344     {
345         return hasData() && pixelTypeIsPlanar(_type);
346     }
347 
348     /// A compressed image doesn't have its pixels available.
349     /// Currently not supported.
350     /// Tags: none.
351     bool isCompressed() pure const
352     {
353         return hasData() && pixelTypeIsCompressed(_type);
354     }
355 
356     /// An image for which width > 0 and height > 0.
357     /// Tags: none.
358     bool hasNonZeroSize() pure const
359     {
360         return width() != 0 && height() != 0;
361     }
362 
363     //
364     // </GETTING STATUS AND CAPABILITIES>
365     //
366 
367 
368     //
369     // <INITIALIZE>
370     //
371 
372     /// Clear the image and initialize a new image, with given dimensions and plain pixels.
373     /// Tags: none.
374     this(int width, 
375          int height, 
376          PixelType type = PixelType.rgba8,
377          LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
378     {
379         setSize(width, height, type, layoutConstraints);
380     }
381     ///ditto
382     void setSize(int width, 
383                  int height, 
384                  PixelType type = PixelType.rgba8,
385                  LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
386     {
387         // PERF: Pessimized, because we don't know if we have been borrowed from...
388         //       Not sure what to do.
389         cleanupBitmapAndTypeIfAny();
390 
391         clearError();
392 
393         if (width < 0 || height < 0)
394         {
395             error(kStrIllegalNegativeDimension);
396             return;
397         }
398 
399         if (!imageIsValidSize(width, height))
400         {
401             error(kStrImageTooLarge);
402             return;
403         }
404 
405         if (!setStorage(width, height, type, layoutConstraints))
406         {
407             // precise error set by setStorage
408             return;
409         }
410     }
411 
412     /// Initialize an image with no data, for example if you wanted an image without the pixel content.
413     /// Tags: none.
414     void initWithNoData(int width, 
415                         int height, 
416                         PixelType type = PixelType.rgba8,
417                         LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
418     {
419         cleanupBitmapAndTypeIfAny();
420         clearError();
421 
422         if (width < 0 || height < 0)
423         {
424             error(kStrIllegalNegativeDimension);
425             return;
426         }
427 
428         if (!imageIsValidSize(width, height))
429         {
430             error(kStrImageTooLarge);
431             return;
432         }
433 
434         if (!layoutConstraintsValid(layoutConstraints))
435         {
436             error(kStrIllegalLayoutConstraints);
437             return;
438         }
439 
440         _data = null;      // no data
441         _allocArea = null; // not owned
442         _type = type;
443         _width = width;
444         _height = height;
445         _pitch = 0;
446         _layoutConstraints = layoutConstraints;
447     }
448 
449     /// Clone an existing image. 
450     /// This image should have plain pixels.
451     /// Tags: #type #data #plain.
452     Image clone() const
453     {
454         assert(isPlainPixels());
455 
456         Image r;
457         r.setSize(_width, _height, _type, _layoutConstraints);
458         if (r.errored)
459             return r;
460 
461         copyPixelsTo(r);
462         return r;
463     }
464 
465     /// Copy pixels to an image with same size and type. Both images should have plain pixels.
466     /// Tags: #type #data #plain.
467     void copyPixelsTo(ref Image img) const @trusted
468     {
469         assert(isPlainPixels());
470 
471         assert(img._width  == _width);
472         assert(img._height == _height);
473         assert(img._type   == _type);
474 
475         // PERF: if both are gapless, can do a single memcpy
476 
477         int scanlineLen = _width * pixelTypeSize(type);
478 
479         const(ubyte)* dataSrc = _data;
480         ubyte* dataDst = img._data;
481 
482         for (int y = 0; y < _height; ++y)
483         {
484             dataDst[0..scanlineLen] = dataSrc[0..scanlineLen];
485             dataSrc += _pitch;
486             dataDst += img._pitch;
487         }
488     }
489 
490     //
491     // </INITIALIZE>
492     //
493 
494 
495     //
496     // <SAVING AND LOADING IMAGES>
497     //
498 
499     /// Load an image from a file location.
500     ///
501     /// Params:
502     ///    path  A string containing the file path.
503     ///    flags Flags can contain LOAD_xxx flags and LAYOUT_xxx flags.
504     ///
505     /// Returns: `true` if successfull. The image will be in errored state if there is a problem.
506     /// See_also: `LoadFlags`, `LayoutConstraints`.
507     /// Tags: none.
508     bool loadFromFile(const(char)[] path, int flags = 0) @trusted
509     {
510         cleanupBitmapAndTypeIfAny();
511 
512         CString cstr = CString(path);
513 
514         // Deduce format.
515         ImageFormat fif = identifyFormatFromFile(cstr.storage);
516         if (fif == ImageFormat.unknown) 
517         {
518             fif = identifyImageFormatFromFilename(cstr.storage); // try to guess the file format from the file extension
519         }
520         
521         loadFromFileInternal(fif, cstr.storage, flags);
522         return !errored();
523     }
524 
525     /// Load an image from a memory location.
526     ///
527     /// Params:
528     ///    bytes Arrays containing the encoded image to decode.
529     ///    flags Flags can contain LOAD_xxx flags and LAYOUT_xxx flags.
530     ///
531     /// Returns: `true` if successfull. The image will be in errored state if there is a problem.
532     ///
533     /// See_also: `LoadFlags`, `LayoutConstraints`.
534     /// Tags: none.
535     bool loadFromMemory(const(ubyte)[] bytes, int flags = 0) @trusted
536     {
537         cleanupBitmapAndTypeIfAny();
538 
539         MemoryFile mem;
540         mem.initFromExistingSlice(bytes);
541 
542         // Deduce format.
543         ImageFormat fif = identifyFormatFromMemoryFile(mem);
544 
545         IOStream io;
546         io.setupForMemoryIO();
547         loadFromStreamInternal(fif, io, cast(IOHandle)&mem, flags);
548 
549         return !errored();
550     }
551     ///ditto
552     bool loadFromMemory(const(void)[] bytes, int flags = 0) @trusted
553     {
554         return loadFromMemory(cast(const(ubyte)[])bytes, flags);
555     }
556 
557     /// Load an image from a set of user-defined I/O callbacks.
558     ///
559     /// Params:
560     ///    fif The target image format.
561     ///    io The user-defined callbacks.
562     ///    handle A void* user pointer to pass to I/O callbacks.
563     ///    flags Flags can contain LOAD_xxx flags and LAYOUT_xxx flags.
564     ///
565     /// Tags: none.
566     bool loadFromStream(ref IOStream io, IOHandle handle, int flags = 0) @system
567     {
568         cleanupBitmapAndTypeIfAny();
569 
570         // Deduce format from stream.
571         ImageFormat fif = identifyFormatFromStream(io, handle);
572 
573         loadFromStreamInternal(fif, io, handle, flags);
574         return !errored();
575     }
576     
577     /// Saves an image to a file, detecting the format from the path extension.
578     ///
579     /// Params:
580     ///     path The path of output file.
581     ///      
582     /// Returns: `true` if file successfully written.
583     /// Tags: none.
584     bool saveToFile(const(char)[] path, int flags = 0) @trusted
585     {
586         assert(!errored); // else, nothing to save
587         CString cstr = CString(path);
588 
589         ImageFormat fif = identifyImageFormatFromFilename(cstr.storage);
590         
591         return saveToFileInternal(fif, cstr.storage, flags);
592     }
593     /// Save the image into a file, with a given file format.
594     ///
595     /// Params:
596     ///     fif The `ImageFormat` to use.
597     ///     path The path of output file.
598     ///
599     /// Returns: `true` if file successfully written.
600     /// Tags: none.
601     bool saveToFile(ImageFormat fif, const(char)[] path, int flags = 0) const @trusted
602     {
603         assert(!errored); // else, nothing to save
604         CString cstr = CString(path);
605         return saveToFileInternal(fif, cstr.storage, flags);
606     }
607 
608     /// Saves the image into a new memory location.
609     /// The returned data must be released with a call to `free`.
610     /// Returns: `null` if saving failed.
611     /// Warning: this is NOT GC-allocated.
612     /// Tags: none.
613     ubyte[] saveToMemory(ImageFormat fif, int flags = 0) const @trusted
614     {
615         assert(!errored); // else, nothing to save
616 
617         // Open stream for read/write access.
618         MemoryFile mem;
619         mem.initEmpty();
620 
621         IOStream io;
622         io.setupForMemoryIO();
623         if (saveToStream(fif, io, cast(IOHandle)&mem, flags))
624             return mem.releaseData();
625         else
626             return null;
627     }
628 
629     /// Save an image with a set of user-defined I/O callbacks.
630     ///
631     /// Params:
632     ///     fif The `ImageFormat` to use.
633     ///     io User-defined stream object.
634     ///     handle User provided `void*` pointer  passed to the I/O callbacks.
635     ///
636     /// Returns: `true` if file successfully written.
637     /// Tags: none.
638     bool saveToStream(ImageFormat fif, ref IOStream io, IOHandle handle, int flags = 0) const @trusted
639     {
640         assert(!errored); // else, nothing to save
641 
642         if (fif == ImageFormat.unknown)
643         {
644             // No format given for save.
645             return false;
646         }
647 
648         if (!isPlainPixels)
649             return false; // no data that is pixels, impossible to save that.
650 
651         const(ImageFormatPlugin)* plugin = &g_plugins[fif];
652         void* data = null; // probably exist to pass metadata stuff
653         if (plugin.saveProc is null)
654             return false;
655         bool r = plugin.saveProc(this, &io, handle, 0, flags, data);
656         return r;
657     }
658 
659     //
660     // </SAVING AND LOADING IMAGES>
661     //
662 
663 
664     // 
665     // <FILE FORMAT IDENTIFICATION>
666     //
667 
668     /// Identify the format of an image by minimally reading it.
669     /// Read first bytes of a file to identify it.
670     /// You can use a filename, a memory location, or your own `IOStream`.
671     /// Returns: Its `ImageFormat`, or `ImageFormat.unknown` in case of identification failure or input error.
672     static ImageFormat identifyFormatFromFile(const(char)*filename) @trusted
673     {
674         FILE* f = fopen(filename, "rb");
675         if (f is null)
676             return ImageFormat.unknown;
677         IOStream io;
678         io.setupForFileIO();
679         ImageFormat type = identifyFormatFromStream(io, cast(IOHandle)f);    
680         fclose(f); // TODO: Note sure what to do if fclose fails here.
681         return type;
682     }
683     ///ditto
684     static ImageFormat identifyFormatFromMemory(const(ubyte)[] bytes) @trusted
685     {
686         MemoryFile mem;
687         mem.initFromExistingSlice(bytes);
688         return identifyFormatFromMemoryFile(mem);
689     }
690     ///ditto
691     static ImageFormat identifyFormatFromStream(ref IOStream io, IOHandle handle)
692     {
693         for (ImageFormat fif = ImageFormat.first; fif <= ImageFormat.max; ++fif)
694         {
695             if (detectFormatFromStream(fif, io, handle))
696                 return fif;
697         }
698         return ImageFormat.unknown;
699     }
700 
701     /// Identify the format of an image by looking at its extension.
702     /// Returns: Its `ImageFormat`, or `ImageFormat.unknown` in case of identification failure or input error.
703     ///          Maybe then you can try `identifyFormatFromFile` instead, which minimally reads the input.
704     static ImageFormat identifyFormatFromFileName(const(char) *filename)
705     {
706         return identifyImageFormatFromFilename(filename);
707     }
708 
709     // 
710     // </FILE FORMAT IDENTIFICATION>
711     //
712 
713 
714     //
715     // <CONVERSION>
716     //
717 
718     /// Get the image layout constraints.
719     /// Tags: none.
720     LayoutConstraints layoutConstraints() pure const
721     {
722         return _layoutConstraints;
723     }
724 
725     /// Keep the same pixels and type, but change how they are arranged in memory to fit some constraints.
726     /// Tags: #type
727     bool changeLayout(LayoutConstraints layoutConstraints)
728     {
729         return convertTo(_type, layoutConstraints);
730     }
731 
732     /// Convert the image to greyscale, using a greyscale transformation (all channels weighted equally).
733     /// Alpha is preserved if existing.
734     /// Tags: #type
735     bool convertToGreyscale(LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
736     {
737         return convertTo( convertPixelTypeToGreyscale(_type), layoutConstraints);
738     }
739 
740     /// Convert the image to a greyscale + alpha equivalent, using duplication and/or adding an opaque alpha channel.
741     /// Tags: #type
742     bool convertToGreyscaleAlpha(LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
743     {
744         return convertTo( convertPixelTypeToAddAlphaChannel( convertPixelTypeToGreyscale(_type) ), layoutConstraints);
745     }
746 
747     /// Convert the image to a RGB equivalent, using duplication if greyscale.
748     /// Alpha is preserved if existing.
749     /// Tags: #type
750     bool convertToRGB(LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
751     {
752         return convertTo( convertPixelTypeToRGB(_type), layoutConstraints);
753     }
754 
755     /// Convert the image to a RGBA equivalent, using duplication and/or adding an opaque alpha channel.
756     /// Tags: #type
757     bool convertToRGBA(LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
758     {
759         return convertTo( convertPixelTypeToAddAlphaChannel( convertPixelTypeToRGB(_type) ), layoutConstraints);
760     }
761 
762     /// Add an opaque alpha channel if not-existing already.
763     /// Tags: #type
764     bool addAlphaChannel(LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
765     {
766         return convertTo( convertPixelTypeToAddAlphaChannel(_type), layoutConstraints);
767     }
768 
769     /// Removes the alpha channel if not-existing already.
770     /// Tags: #type
771     bool dropAlphaChannel(LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
772     {
773         return convertTo( convertPixelTypeToDropAlphaChannel(_type), layoutConstraints);
774     }
775 
776     /// Convert the image bit-depth to 8-bit per component.
777     /// Tags: #type
778     bool convertTo8Bit(LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
779     {
780         return convertTo( convertPixelTypeTo8Bit(_type), layoutConstraints);
781     }
782 
783     /// Convert the image bit-depth to 16-bit per component.
784     /// Tags: #type.
785     bool convertTo16Bit(LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
786     {
787         return convertTo( convertPixelTypeTo16Bit(_type), layoutConstraints);
788     }
789 
790     /// Convert the image bit-depth to 32-bit float per component.
791     /// Tags: #type.
792     bool convertToFP32(LayoutConstraints layoutConstraints = LAYOUT_DEFAULT)
793     {
794         return convertTo( convertPixelTypeToFP32(_type), layoutConstraints);
795     }
796 
797     /// Convert the image to the following format.
798     /// This can destruct channels, loose precision, etc.
799     /// You can also change the layout constraints at the same time.
800     ///
801     /// Returns: true on success.
802     /// Tags: #type.
803     bool convertTo(PixelType targetType, LayoutConstraints layoutConstraints = LAYOUT_DEFAULT) @trusted
804     {
805         assert(!errored()); // this should have been caught before.
806         assert(hasType());
807 
808         if (targetType == PixelType.unknown)
809         {
810             error(kStrUnsupportedTypeConversion);
811             return false;
812         }
813 
814         // The asked for layout must be valid itself.
815         assert(layoutConstraintsValid(layoutConstraints));
816 
817         if (!hasData())
818         {
819             _layoutConstraints = layoutConstraints;
820             return true; // success, no pixel data, so everything was "converted", layout constraints do not hold
821         }
822 
823         // Detect if the particular hazard of allocation have given the image "ad-hoc" constraints
824         // we didn't strictly require. Typically, if the image is already vertically straight, no need to 
825         // reallocate just for that.
826         LayoutConstraints adhocConstraints = getAdHocLayoutConstraints();
827 
828         enum bool useAdHoc = true; // FUTURE: remove once deemed harmless
829 
830         // Are the new layout constraints already valid?
831         bool compatibleLayout = layoutConstraintsCompatible(layoutConstraints, useAdHoc ? adhocConstraints : _layoutConstraints);
832 
833         if (_type == targetType && compatibleLayout)
834         {
835             // PERF: it would be possible, if the layout only differ for stance with Vflip, to flip
836             // lines in place here. But this can be handled below with reallocation.
837             _layoutConstraints = layoutConstraints;
838             return true; // success, same type already, and compatible constraints
839         }
840 
841         if ((width() == 0 || height()) == 0 && compatibleLayout)
842         {
843             // Image dimension is zero, and compatible constraints, everything fine
844             // No need for reallocation or copy.
845             _layoutConstraints = layoutConstraints;
846             return true;
847         }
848 
849         ubyte* source = _data;
850         int sourcePitch = _pitch;
851 
852         // Do not realloc the same block to avoid invalidating previous data.
853         // We'll manage this manually.
854         assert(_data !is null);
855 
856         // PERF: do some conversions in place? if target type is smaller then input, always possible
857 
858         // Do we need to convert scanline by scanline, using a scratch buffer?
859         bool needConversionWithIntermediateType = targetType != _type;
860         PixelType interType = intermediateConversionType(_type, targetType);
861         int interBufSize = width * pixelTypeSize(interType);
862         int bonusBytes = needConversionWithIntermediateType ? interBufSize : 0;
863 
864         ubyte* dest; // first scanline
865         ubyte* newAllocArea;  // the result of realloc-ed
866         int destPitch;
867         bool err;
868         allocatePixelStorage(null, // so that the former allocation keep existing for the copy
869                              targetType,
870                              width,
871                              height,
872                              layoutConstraints,
873                              bonusBytes,
874                              dest,
875                              newAllocArea,
876                              destPitch,
877                              err);
878         
879         if (err)
880         {
881             error(kStrOutOfMemory);
882             return false;
883         }
884 
885         // Do we need a conversion of just a memcpy?
886         bool ok = false;
887         if (targetType == _type)
888         {
889             ok = copyScanlines(targetType, 
890                                source, sourcePitch,
891                                dest, destPitch,
892                                width, height);
893         }
894         else
895         {
896             // Need an intermediate buffer. We allocated one in the new image buffer.
897             // After that conversion, noone will ever talk about it, and the bonus bytes will stay unused.
898             ubyte* interBuf = newAllocArea;
899 
900             ok = convertScanlines(_type, source, sourcePitch, 
901                                   targetType, dest, destPitch,
902                                   width, height,
903                                   interType, interBuf);
904         }
905 
906         if (!ok)
907         {
908             // Keep former image
909             deallocatePixelStorage(newAllocArea);
910             error(kStrUnsupportedTypeConversion);
911             return false;
912         }
913 
914         cleanupBitmapAndTypeIfAny(); // forget about former image
915 
916         _layoutConstraints = layoutConstraints;
917         _data = dest;
918         _allocArea = newAllocArea; // now own the new one.
919         _type = targetType;
920         _pitch = destPitch;
921         return true;
922     }
923 
924     /// Reinterpret cast the image content.
925     /// For example if you want to consider a RGBA8 image to be uint8, but with a 4x larger width.
926     /// This doesn't allocates new data storage.
927     ///
928     /// Warning: This fails if the cast is impossible, for example casting a uint8 image to RGBA8 only
929     /// works if the width is a multiple of 4.
930     ///
931     /// So it is a bit like casting slices in D.
932     /// TODO: castTo breaks layout constraints, what to do with them?
933     /// Tags: #type.
934     bool castTo(PixelType targetType) @trusted
935     {
936         assert(hasType());
937         assert(!errored()); // this should have been caught before.
938         if (targetType == PixelType.unknown)
939         {
940             error(kStrInvalidPixelTypeCast);
941             return false;
942         }
943 
944         if (_type == targetType)
945             return true; // success, nothing to do
946 
947         if (!hasData())
948         {
949             _type = targetType;
950             return true; // success, no pixel data, so everything was "cast"
951         }
952 
953         if (width() == 0 || height() == 0)
954         {
955             return true; // image dimension is zero, everything fine
956         }
957 
958         // Basically, you can cast if the source type size is a multiple of the dest type.
959         int srcBytes = pixelTypeSize(_type);
960         int destBytes = pixelTypeSize(_type);
961 
962         // Byte length of source line.
963         int sourceLineSize = width * srcBytes;
964         assert(sourceLineSize >= 0);
965 
966         // Is it dividable by destBytes? If yes, cast is successful.
967         if ( (sourceLineSize % destBytes) == 0)
968         {
969             _width = sourceLineSize / destBytes;
970             _type = targetType;
971             return true;
972         }
973         else
974         {
975             error(kStrInvalidPixelTypeCast);
976             return false;
977         }
978     }
979 
980     //
981     // </CONVERSION>
982     //
983 
984 
985     //
986     // <LAYOUT>
987     //
988 
989     /// On how many bytes each scanline is aligned.
990     /// Useful to know for SIMD.
991     /// The actual alignment could be higher than what the layout constraints strictly tells.
992     /// See_also: `LayoutConstraints`.
993     /// Tags: none.
994     int scanlineAlignment()
995     {
996         return layoutScanlineAlignment(_layoutConstraints);
997     }
998 
999     /// Get the number of border pixels around the image.
1000     /// This is an area that can be safely accessed, using -pitchInBytes() and pointer offsets.
1001     /// The actual border width could well be higher, but there is no way of safely knowing that.
1002     /// See_also: `LayoutConstraints`.
1003     /// Tags: none.
1004     int borderWidth() pure
1005     {
1006         return layoutBorderWidth(_layoutConstraints);
1007     }
1008 
1009     /// Get the multiplicity of pixels in a single scanline.
1010     /// The actual multiplicity could well be higher.
1011     /// See_also: `LayoutConstraints`.
1012     /// Tags: none.
1013     int pixelMultiplicity()
1014     {
1015         return layoutMultiplicity(_layoutConstraints);
1016     }
1017 
1018     /// Get the guaranteed number of scanline trailing pixels, from the layout constraints.
1019     /// Each scanline is followed by at least that much out-of-image pixels, that can be safely
1020     /// READ.
1021     /// The actual number of trailing pixels can well be larger than what the layout strictly tells,
1022     /// but we'll never know.
1023     /// See_also: `LayoutConstraints`.
1024     /// Tags: none.
1025     int trailingPixels() pure
1026     {
1027         return layoutTrailingPixels(_layoutConstraints);
1028     }
1029 
1030     /// Get if being gapless is guaranteed by the layout constraints.
1031     /// Note that this only holds if there is some data in the first place.
1032     /// See_also: `allPixels()`, `LAYOUT_GAPLESS`, `LAYOUT_VERT_STRAIGHT`.
1033     /// Tags: none.
1034     bool isGapless() pure const
1035     {
1036         return layoutGapless(_layoutConstraints);
1037     }
1038 
1039     /// Returns: `true` is the image is constrained to be stored upside-down.
1040     /// Tags: none.
1041     bool mustBeStoredUpsideDown() pure const
1042     {
1043         return (_layoutConstraints & LAYOUT_VERT_FLIPPED) != 0;
1044     }
1045 
1046     /// Returns: `true` is the image is constrained to NOT be stored upside-down.
1047     /// Tags: none.
1048     bool mustNotBeStoredUpsideDown() pure const
1049     {
1050         return (_layoutConstraints & LAYOUT_VERT_STRAIGHT) != 0;
1051     }
1052 
1053     //
1054     // </LAYOUT>
1055     //
1056 
1057     //
1058     // <TRANSFORM>
1059     //
1060 
1061     /// Flip the image data horizontally.
1062     /// If the image has no data, the operation is successful.
1063     /// Tags: #type.
1064     bool flipHorizontally() pure @trusted
1065     {
1066         assert(hasType());
1067 
1068         if (!hasData())
1069             return true; // Nothing to do
1070 
1071         ubyte[GAMUT_MAX_PIXEL_SIZE] temp;
1072 
1073         int W = width();
1074         int H = height();
1075         int Xdiv2 = W / 2;
1076         int scanBytes = scanlineInBytes();
1077         int psize = pixelTypeSize(type);
1078 
1079         // Stupid pixel per pixel swap
1080         for (int y = 0; y < H; ++y)
1081         {
1082             ubyte* scan = scanline(y);
1083             for (int x = 0; x < Xdiv2; ++x)
1084             {
1085                 ubyte* pixelA = &scan[x * psize];
1086                 ubyte* pixelB = &scan[(W - 1 - x) * psize];
1087                 temp[0..psize] = pixelA[0..psize];
1088                 pixelA[0..psize] = pixelB[0..psize];
1089                 pixelB[0..psize] = temp[0..psize];
1090             }
1091         }
1092         return true;
1093     }
1094 
1095     /// Flip the image vertically.
1096     /// If the image has no data, the operation is successful.
1097     ///
1098     /// - If the layout allows it, `flipVerticallyLogical` is called. The scanline pointers are 
1099     ///   inverted, and pitch is negated. This just flips the "view" of the image.
1100     ///
1101     /// - If there is a constraint to keep the image strictly upside-down, or strictly not 
1102     ///   upside-down, then `flipVerticallyPhysical` is called instead.
1103     ///
1104     /// Returns: `true` on success, sets an error else and return `false`.
1105     /// Tags: #type.
1106     bool flipVertically() pure
1107     {
1108         assert(hasType());
1109 
1110         if (mustBeStoredUpsideDown() || mustNotBeStoredUpsideDown())
1111             return flipVerticallyPhysical();
1112         else
1113             return flipVerticallyLogical();
1114     }
1115     ///ditto
1116     bool flipVerticallyLogical() pure @trusted
1117     {
1118         if (!hasData())
1119             return true; // Nothing to do
1120 
1121         if (mustBeStoredUpsideDown() || mustNotBeStoredUpsideDown())
1122         {
1123             error(kStrUnsupportedVFlip);
1124             return false;
1125         }
1126 
1127         // Note: flipping the image preserve all layout properties! 
1128         // What a nice design here.
1129         // Border, trailing pixels, scanline alignment... they all survive vertical flip.
1130         flipScanlinePointers(_width, _height, _data, _pitch);
1131 
1132         return true;
1133     }
1134     ///ditto
1135     bool flipVerticallyPhysical() pure @trusted
1136     {
1137         if (!hasData())
1138             return true; // Nothing to do
1139 
1140         int H = height();
1141         int Ydiv2 = H / 2;
1142         int scanBytes = scanlineInBytes();
1143 
1144         // Stupid byte per byte swap
1145         for (int y = 0; y < Ydiv2; ++y)
1146         {
1147             ubyte* scanA = scanline(y);
1148             ubyte* scanB = scanline(H - 1 - y);
1149             for (int b = 0; b < scanBytes; ++b)
1150             {
1151                 ubyte ch = scanA[b];
1152                 scanA[b] = scanB[b]; 
1153                 scanB[b] = ch;
1154             }
1155         }
1156         return true;
1157     }
1158 
1159     //
1160     // </TRANSFORM>
1161     //
1162 
1163     @disable this(this); // Non-copyable. This would clone the image, and be expensive.
1164 
1165 
1166     /// Destructor. Everything is reclaimed.
1167     ~this()
1168     {
1169         cleanupBitmapAndTypeIfAny();
1170     }
1171 
1172 package:
1173 
1174     // Available only inside gamut.
1175 
1176     /// Clear the error, if any. This is only for use inside Gamut.
1177     /// Each operations that "recreates" the image, such a loading, clear the existing error and leave 
1178     /// the Image in a clean-up state.
1179     void clearError() pure
1180     {
1181         _error = null;
1182     }
1183 
1184     /// Set the image in an errored state, with `msg` as a message.
1185     /// Note: `msg` MUST be zero-terminated.
1186     void error(const(char)[] msg) pure
1187     {
1188         assert(msg !is null);
1189         _error = assumeZeroTerminated(msg);
1190     }
1191 
1192     /// The type of the data pointed to.
1193     PixelType _type = PixelType.unknown;
1194 
1195     /// The data layout constraints, in flags.
1196     /// See_also: `LayoutConstraints`.
1197     LayoutConstraints _layoutConstraints = LAYOUT_DEFAULT;
1198 
1199     /// Pointer to the pixel data. What is pointed to depends on `_type`.
1200     /// The amount of what is pointed to depends upon the dimensions.
1201     /// it is possible to have `_data` null but `_type` is known.
1202     ubyte* _data = null;
1203 
1204     /// Pointer to the `malloc` area holding the data.
1205     /// _allocArea being null signify that there is no data, or that the data is borrowed.
1206     /// _allocArea not being null signify that the image is owning its data.
1207     ubyte* _allocArea = null;
1208 
1209     /// Width of the image in pixels, when pixels makes sense.
1210     /// By default, this width is 0 (but as the image has no pixel data, this doesn't matter).
1211     int _width = 0;
1212 
1213     /// Height of the image in pixels, when pixels makes sense.
1214     /// By default, this height is 0 (but as the image has no pixel data, this doesn't matter).
1215     int _height = 0;
1216 
1217     /// Pitch in bytes between lines, when a pitch makes sense. This pitch can be, or not be, a negative integer.
1218     /// When the image has layout constraint LAYOUT_VERT_FLIPPED, it is always kept <= 0.
1219     /// When the image has layout constraint LAYOUT_VERT_STRAIGHT, it is always kept >= 0.
1220     int _pitch = 0; 
1221 
1222     /// Pointer to last known error. `null` means "no errors".
1223     /// Once an error has occured, continuing to use the image is Undefined Behaviour.
1224     /// Must be zero-terminated.
1225     /// By default, a T.init image is errored().
1226     const(char)* _error = kStrImageNotInitialized;
1227 
1228     /// Pixel aspect ratio.
1229     /// https://en.wikipedia.org/wiki/Pixel_aspect_ratio
1230     float _pixelAspectRatio = GAMUT_UNKNOWN_ASPECT_RATIO;
1231 
1232     /// Physical image resolution in vertical pixel-per-inch.
1233     float _resolutionY = GAMUT_UNKNOWN_RESOLUTION;
1234 
1235 private:
1236 
1237     /// Compute a suitable pitch when making an image.
1238     /// FUTURE some flags that change alignment constraints?
1239     deprecated int computePitch(PixelType type, int width)
1240     {
1241         return width * pixelTypeSize(type);
1242     }
1243 
1244     void cleanupBitmapAndTypeIfAny() @safe
1245     {
1246         cleanupBitmapIfAny();
1247         cleanupTypeIfAny();
1248     }
1249 
1250     void cleanupBitmapIfAny() @trusted
1251     {
1252         cleanupBitmapIfOwned();
1253         _data = null;        
1254         assert(!hasData());
1255     }
1256 
1257     void cleanupTypeIfAny()
1258     {
1259         _type = PixelType.unknown;
1260         assert(!hasType());
1261     }
1262 
1263     // If owning an allocation, free it, else keep it.
1264     void cleanupBitmapIfOwned() @trusted
1265     {   
1266         if (isOwned())
1267         {
1268             deallocatePixelStorage(_allocArea);
1269             _allocArea = null;
1270             _data = null;
1271         }
1272     }
1273 
1274     /// Discard ancient data, and reallocate stuff.
1275     /// Returns true on success, false on OOM.
1276     /// When failing, sets the errored state.
1277     bool setStorage(int width, 
1278                     int height,
1279                     PixelType type, 
1280                     LayoutConstraints constraints) @trusted
1281     {
1282         if (!layoutConstraintsValid(constraints))
1283         {
1284             error(kStrIllegalLayoutConstraints);
1285             return false;
1286         }
1287 
1288         ubyte* dataPointer;
1289         ubyte* mallocArea;
1290         int pitchBytes;
1291         bool err;
1292 
1293         allocatePixelStorage(_allocArea,
1294                              type, 
1295                              width,
1296                              height,
1297                              constraints,
1298                              0,
1299                              dataPointer,
1300                              mallocArea,
1301                              pitchBytes,
1302                              err);
1303         if (err)
1304         {
1305             error(kStrOutOfMemory);
1306             return false;
1307         }
1308 
1309         _data = dataPointer;
1310         _allocArea = mallocArea;
1311         _type = type;
1312         _width = width;
1313         _height = height;
1314         _pitch = pitchBytes;
1315         _layoutConstraints = constraints;
1316         return true;
1317     }
1318 
1319     void loadFromFileInternal(ImageFormat fif, const(char)* filename, int flags = 0) @system
1320     {
1321         FILE* f = fopen(filename, "rb");
1322         if (f is null)
1323         {
1324             error(kStrCannotOpenFile);
1325             return;
1326         }
1327 
1328         IOStream io;
1329         io.setupForFileIO();
1330         loadFromStreamInternal(fif, io, cast(IOHandle)f, flags);
1331 
1332         if (0 != fclose(f))
1333         {
1334             // TODO cleanup image?
1335             error(kStrFileCloseFailed);
1336         }
1337     }
1338 
1339     void loadFromStreamInternal(ImageFormat fif, ref IOStream io, IOHandle handle, int flags = 0) @system
1340     {
1341         // By loading an image, we agreed to forget about past mistakes.
1342         clearError();
1343 
1344         if (fif == ImageFormat.unknown)
1345         {
1346             error(kStrImageFormatUnidentified);
1347             return;
1348         }
1349 
1350         const(ImageFormatPlugin)* plugin = &g_plugins[fif];   
1351 
1352         int page = 0;
1353         void *data = null;
1354         if (plugin.loadProc is null)
1355         {        
1356             error(kStrImageFormatNoLoadSupport);
1357             return;
1358         }
1359         plugin.loadProc(this, &io, handle, page, flags, data);
1360     }
1361 
1362     bool saveToFileInternal(ImageFormat fif, const(char)* filename, int flags = 0) const @trusted
1363     {
1364         FILE* f = fopen(filename, "wb");
1365         if (f is null)
1366             return false;
1367 
1368         IOStream io;
1369         io.setupForFileIO();
1370         bool r = saveToStream(fif, io, cast(IOHandle)f, flags);
1371         bool fcloseOK = fclose(f) == 0;
1372         return r && fcloseOK;
1373     }
1374 
1375     static ImageFormat identifyFormatFromMemoryFile(ref MemoryFile mem) @trusted
1376     {
1377         IOStream io;
1378         io.setupForMemoryIO();
1379         return identifyFormatFromStream(io, cast(IOHandle)&mem);
1380     }  
1381 
1382     static bool detectFormatFromStream(ImageFormat fif, ref IOStream io, IOHandle handle) @trusted
1383     {
1384         assert(fif != ImageFormat.unknown);
1385         const(ImageFormatPlugin)* plugin = &g_plugins[fif];
1386         assert(plugin.detectProc !is null);
1387         if (plugin.detectProc(&io, handle))
1388             return true;
1389         return false;
1390     }
1391 
1392     // When we look at this Image, what are some constraints that it could spontaneously follow?
1393     // Also look at existing _layoutConstraints.
1394     // Params:
1395     //     preferGapless Generates a LayoutConstraints with LAYOUT_GAPLESS rather than other things.
1396     //
1397     // Warning: the LayoutConstraints it returns is not necessarilly user-valid, it can contain both
1398     //          scanline alignment and gapless constraints. This should NEVER be kept as actual constraints.
1399     LayoutConstraints getAdHocLayoutConstraints()
1400     {
1401         assert(hasData());
1402 
1403         // An image that doesn't own its data can't infer some adhoc constraints, or the conditions are stricter.        
1404         bool owned = isOwned;
1405 
1406         int pitch = pitchInBytes();
1407         int absPitch = pitch >= 0 ? pitch : -pitch;
1408         int scanLen = scanlineInBytes();
1409         int pixelSize = pixelTypeSize(type);
1410         int width = _width;
1411         int excessBytes = scanLen - absPitch;
1412         int excessPixels = excessBytes / pixelSize;
1413         assert(excessBytes >= 0 && excessPixels >= 0);
1414         
1415         LayoutConstraints c = 0;
1416 
1417         // Multiplicity constraint: take largest of inferred, and _layoutConstraints-related.
1418         {
1419             int multi = pixelMultiplicity(); // as much is guaranteed by the _constraint
1420 
1421             // the multiplicity inferred by looking at how many pixel can fit at the end of the scanline
1422             int inferredWithGap = 1; 
1423             if (excessPixels >= 7)
1424                 inferredWithGap = 8;
1425             else if (excessPixels >= 3)
1426                 inferredWithGap = 4;
1427             else if (excessPixels >= 1)
1428                 inferredWithGap = 2;
1429 
1430             // the multiplicity inferred by looking at width divisibility
1431             // Slight note: this is not fully complete, a 2-width + 2 trailing pixels => 4-multiplicity
1432             int inferredWithWidth = 1;
1433             if ( (width % 2) == 0) inferredWithWidth = 2;
1434             if ( (width % 4) == 0) inferredWithWidth = 4;
1435             if ( (width % 8) == 0) inferredWithWidth = 8;
1436 
1437             // take max
1438             if (multi < inferredWithGap)   multi = inferredWithGap;
1439             if (multi < inferredWithWidth) multi = inferredWithWidth;
1440             assert(multi == 1 || multi == 2 || multi == 4 || multi == 8);
1441 
1442             if (multi == 8)
1443                 c |= LAYOUT_MULTIPLICITY_8;
1444             else if (multi == 4)
1445                 c |= LAYOUT_MULTIPLICITY_4;
1446             else if (multi == 2)
1447                 c |= LAYOUT_MULTIPLICITY_2;
1448         }
1449 
1450         // Trailing bytes constraint: infer is the largest, no need to look at _layoutConstraints.
1451         {
1452             if (excessPixels >= 7)
1453                 c |= LAYOUT_TRAILING_7;
1454             else if (excessPixels >= 3)
1455                 c |= LAYOUT_TRAILING_3;
1456             else if (excessPixels >= 1)
1457                 c |= LAYOUT_TRAILING_1;
1458         }
1459 
1460         // scanline alignment: infer is the largest, since the constraints shows in pitch and pointer address
1461         {
1462             LayoutConstraints firstScanAlign = getPointerAlignment(cast(size_t)_data);
1463             LayoutConstraints pitchAlign = getPointerAlignment(cast(size_t)absPitch);
1464             LayoutConstraints allScanlinesAlign = firstScanAlign < pitchAlign ? firstScanAlign : pitchAlign;
1465             c |= allScanlinesAlign;
1466         }
1467 
1468         // vertical
1469         if (pitch >= 0)
1470             c |= LAYOUT_VERT_STRAIGHT;
1471         if (pitch <= 0)
1472             c |= LAYOUT_VERT_FLIPPED;
1473 
1474         // gapless
1475         if (pitch == absPitch)
1476             c |= LAYOUT_GAPLESS;
1477 
1478         // Border constraint: can only trust the _constraint. Cannot infer more.
1479         c |= (_layoutConstraints & LAYOUT_BORDER_MASK);
1480 
1481         return c;
1482     }
1483 }
1484 
1485 
1486 private:
1487 
1488 // FUTURE: this will also manage color conversion.
1489 PixelType intermediateConversionType(PixelType srcType, PixelType destType)
1490 {
1491     if (pixelTypeExpressibleInRGBA8(srcType) && pixelTypeExpressibleInRGBA8(destType))
1492         return PixelType.rgba8;
1493 
1494     return PixelType.rgbaf32;
1495 }
1496 
1497 // This converts scanline per scanline, using an intermediate format to lessen the number of conversions.
1498 bool convertScanlines(PixelType srcType, const(ubyte)* src, int srcPitch, 
1499                       PixelType destType, ubyte* dest, int destPitch,
1500                       int width, int height,
1501                       PixelType interType, ubyte* interBuf) @system
1502 {
1503     assert(srcType != destType);
1504     assert(srcType != PixelType.unknown && destType != PixelType.unknown);
1505 
1506     if (pixelTypeIsPlanar(srcType) || pixelTypeIsPlanar(destType))
1507         return false; // No support
1508     if (pixelTypeIsCompressed(srcType) || pixelTypeIsCompressed(destType))
1509         return false; // No support
1510 
1511     if (srcType == interType)
1512     {
1513         // Source type is already in the intermediate type format.
1514         // Do not use the interbuf.
1515         for (int y = 0; y < height; ++y)
1516         {
1517             convertFromIntermediate(srcType, src, destType, dest, width);
1518             src += srcPitch;
1519             dest += destPitch;
1520         }
1521     }
1522     else if (destType == interType)
1523     {
1524         // Destination type is the intermediate type.
1525         // Do not use the interbuf.
1526         for (int y = 0; y < height; ++y)
1527         {
1528             convertToIntermediateScanline(srcType, src, destType, dest, width);
1529             src += srcPitch;
1530             dest += destPitch;
1531         }
1532     }
1533     else
1534     {
1535         // For each scanline
1536         for (int y = 0; y < height; ++y)
1537         {
1538             convertToIntermediateScanline(srcType, src, interType, interBuf, width);
1539             convertFromIntermediate(interType, interBuf, destType, dest, width);
1540             src += srcPitch;
1541             dest += destPitch;
1542         }
1543     }
1544     return true;
1545 }
1546 
1547 // This copy scanline per scanline of the same type
1548 bool copyScanlines(PixelType type, 
1549                    const(ubyte)* src, int srcPitch, 
1550                    ubyte* dest, int destPitch,
1551                    int width, int height) @system
1552 {
1553     if (pixelTypeIsPlanar(type))
1554         return false; // No support
1555     if (pixelTypeIsCompressed(type))
1556         return false; // No support
1557 
1558     int scanlineBytes = pixelTypeSize(type) * width;
1559     for (int y = 0; y < height; ++y)
1560     {
1561         dest[0..scanlineBytes] = src[0..scanlineBytes];
1562         src += srcPitch;
1563         dest += destPitch;
1564     }
1565     return true;
1566 }
1567 
1568 
1569 /// See_also: OpenGL ES specification 2.3.5.1 and 2.3.5.2 for details about converting from 
1570 /// floating-point to integers, and the other way around.
1571 void convertToIntermediateScanline(PixelType srcType, 
1572                                    const(ubyte)* src, 
1573                                    PixelType dstType, 
1574                                    ubyte* dest, int width) @system
1575 {
1576     if (dstType == PixelType.rgba8)
1577     {
1578         ubyte* outb = dest;
1579         switch(srcType) with (PixelType)
1580         {
1581             case l8:
1582             {
1583                 for (int x = 0; x < width; ++x)
1584                 {
1585                     ubyte b = src[x];
1586                     *outb++ = b;
1587                     *outb++ = b;
1588                     *outb++ = b;
1589                     *outb++ = 255;
1590                 }
1591                 break;
1592             }
1593             case la8:
1594             {
1595                 for (int x = 0; x < width; ++x)
1596                 {
1597                     ubyte b = src[x*2];
1598                     *outb++ = b;
1599                     *outb++ = b;
1600                     *outb++ = b;
1601                     *outb++ = src[x*2+1];
1602                 }
1603                 break;
1604             }
1605             case rgb8:
1606             {
1607                 for (int x = 0; x < width; ++x)
1608                 {
1609                     *outb++ = src[x*3+0];
1610                     *outb++ = src[x*3+1];
1611                     *outb++ = src[x*3+2];
1612                     *outb++ = 255;
1613                 }
1614                 break;
1615             }
1616             case rgba8:
1617             {
1618                 for (int x = 0; x < width; ++x)
1619                 {
1620                     *outb++ = src[x*4+0];
1621                     *outb++ = src[x*4+1];
1622                     *outb++ = src[x*4+2];
1623                     *outb++ = src[x*4+3];
1624                 }
1625                 break;
1626             }
1627 
1628             default:
1629                 assert(false); // should not use rgba8 as intermediate type
1630         }
1631     }
1632     else if (dstType == PixelType.rgbaf32)
1633     {
1634         float* outp = cast(float*) dest;
1635 
1636         final switch(srcType) with (PixelType)
1637         {
1638             case unknown: assert(false);
1639             case l8:
1640             {
1641                 const(ubyte)* s = src;
1642                 for (int x = 0; x < width; ++x)
1643                 {
1644                     float b = s[x] / 255.0f;
1645                     *outp++ = b;
1646                     *outp++ = b;
1647                     *outp++ = b;
1648                     *outp++ = 1.0f;
1649                 }
1650                 break;
1651             }
1652             case l16:
1653             {
1654                 const(ushort)* s = cast(const(ushort)*) src;
1655                 for (int x = 0; x < width; ++x)
1656                 {
1657                     float b = s[x] / 65535.0f;
1658                     *outp++ = b;
1659                     *outp++ = b;
1660                     *outp++ = b;
1661                     *outp++ = 1.0f;
1662                 }
1663                 break;
1664             }
1665             case lf32:
1666             {
1667                 const(float)* s = cast(const(float)*) src;
1668                 for (int x = 0; x < width; ++x)
1669                 {
1670                     float b = s[x];
1671                     *outp++ = b;
1672                     *outp++ = b;
1673                     *outp++ = b;
1674                     *outp++ = 1.0f;
1675                 }
1676                 break;
1677             }
1678             case la8:
1679             {
1680                 const(ubyte)* s = src;
1681                 for (int x = 0; x < width; ++x)
1682                 {
1683                     float b = *s++ / 255.0f;
1684                     float a = *s++ / 255.0f;
1685                     *outp++ = b;
1686                     *outp++ = b;
1687                     *outp++ = b;
1688                     *outp++ = a;
1689                 }
1690                 break;
1691             }
1692             case la16:
1693             {
1694                 const(ushort)* s = cast(const(ushort)*) src;
1695                 for (int x = 0; x < width; ++x)
1696                 {
1697                     float b = *s++ / 65535.0f;
1698                     float a = *s++ / 65535.0f;
1699                     *outp++ = b;
1700                     *outp++ = b;
1701                     *outp++ = b;
1702                     *outp++ = a;
1703                 }
1704                 break;
1705             }
1706             case laf32:
1707             {
1708                 const(float)* s = cast(const(float)*) src;
1709                 for (int x = 0; x < width; ++x)
1710                 {
1711                     float b = *s++;
1712                     float a = *s++;
1713                     *outp++ = b;
1714                     *outp++ = b;
1715                     *outp++ = b;
1716                     *outp++ = a;
1717                 }
1718                 break;
1719             }
1720             case rgb8:
1721             {
1722                 const(ubyte)* s = src;
1723                 for (int x = 0; x < width; ++x)
1724                 {
1725                     float r = *s++ / 255.0f;
1726                     float g = *s++ / 255.0f;
1727                     float b = *s++ / 255.0f;
1728                     *outp++ = r;
1729                     *outp++ = g;
1730                     *outp++ = b;
1731                     *outp++ = 1.0f;
1732                 }
1733                 break;
1734             }
1735             case rgb16:
1736             {
1737                 const(ushort)* s = cast(const(ushort)*) src;
1738                 for (int x = 0; x < width; ++x)
1739                 {
1740                     float r = *s++ / 65535.0f;
1741                     float g = *s++ / 65535.0f;
1742                     float b = *s++ / 65535.0f;
1743                     *outp++ = r;
1744                     *outp++ = g;
1745                     *outp++ = b;
1746                     *outp++ = 1.0f;
1747                 }
1748                 break;
1749             }
1750             case rgbf32:
1751             {
1752                 const(float)* s = cast(const(float)*) src;
1753                 for (int x = 0; x < width; ++x)
1754                 {
1755                     float r = *s++;
1756                     float g = *s++;
1757                     float b = *s++;
1758                     *outp++ = r;
1759                     *outp++ = g;
1760                     *outp++ = b;
1761                     *outp++ = 1.0f;
1762                 }
1763                 break;
1764             }
1765             case rgba8:
1766             {
1767                 const(ubyte)* s = src;
1768                 for (int x = 0; x < width; ++x)
1769                 {
1770                     float r = *s++ / 255.0f;
1771                     float g = *s++ / 255.0f;
1772                     float b = *s++ / 255.0f;
1773                     float a = *s++ / 255.0f;
1774                     *outp++ = r;
1775                     *outp++ = g;
1776                     *outp++ = b;
1777                     *outp++ = a;
1778                 }
1779                 break;
1780             }
1781             case rgba16:
1782             {
1783                 const(ushort)* s = cast(const(ushort)*) src;
1784                 for (int x = 0; x < width; ++x)
1785                 {
1786                     float r = *s++ / 65535.0f;
1787                     float g = *s++ / 65535.0f;
1788                     float b = *s++ / 65535.0f;
1789                     float a = *s++ / 65535.0f;
1790                     *outp++ = r;
1791                     *outp++ = g;
1792                     *outp++ = b;
1793                     *outp++ = a;
1794                 }
1795                 break;
1796             }
1797             case rgbaf32:
1798             {
1799                 const(float)* s = cast(const(float)*) src;
1800                 for (int x = 0; x < width; ++x)
1801                 {
1802                     float r = *s++;
1803                     float g = *s++;
1804                     float b = *s++;
1805                     float a = *s++;
1806                     *outp++ = r;
1807                     *outp++ = g;
1808                     *outp++ = b;
1809                     *outp++ = a;
1810                 }
1811                 break;
1812             }
1813         }
1814     }
1815     else
1816         assert(false);
1817 
1818 }
1819 
1820 void convertFromIntermediate(PixelType srcType, const(ubyte)* src, PixelType dstType, ubyte* dest, int width) @system
1821 {
1822     if (srcType == PixelType.rgba8)
1823     {
1824         alias inp = src;
1825         switch(dstType) with (PixelType)
1826         {
1827             case l8:
1828             {
1829                 for (int x = 0; x < width; ++x)
1830                     dest[x] = inp[4*x];
1831                 break;
1832             }
1833             case la8:
1834             {
1835                 for (int x = 0; x < width; ++x)
1836                 {
1837                     dest[2*x+0] = inp[4*x+0];
1838                     dest[2*x+1] = inp[4*x+3];
1839                 }
1840                 break;
1841             }
1842             case rgb8:
1843             {
1844                 for (int x = 0; x < width; ++x)
1845                 {
1846                     dest[3*x+0] = inp[4*x+0];
1847                     dest[3*x+1] = inp[4*x+1];
1848                     dest[3*x+2] = inp[4*x+2];
1849                 }
1850                 break;
1851             }
1852             case rgba8:
1853             {
1854                 for (int x = 0; x < width; ++x)
1855                 {
1856                     dest[4*x+0] = inp[4*x+0];
1857                     dest[4*x+1] = inp[4*x+1];
1858                     dest[4*x+2] = inp[4*x+2];
1859                     dest[4*x+3] = inp[4*x+3];
1860                 }
1861                 break;
1862             }
1863 
1864             default:
1865                 assert(false); // should not use rgba8 as intermediate type
1866         }
1867     }
1868     else if (srcType == PixelType.rgbaf32)
1869     {    
1870         const(float)* inp = cast(const(float)*) src;
1871 
1872         final switch(dstType) with (PixelType)
1873         {
1874             case unknown: assert(false);
1875             case l8:
1876             {
1877                 ubyte* s = dest;
1878                 for (int x = 0; x < width; ++x)
1879                 {
1880                     ubyte b = cast(ubyte)(0.5f + (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) * 255.0f / 3.0f);
1881                     *s++ = b;
1882                 }
1883                 break;
1884             }
1885             case l16:
1886             {
1887                 ushort* s = cast(ushort*) dest;
1888                 for (int x = 0; x < width; ++x)
1889                 {
1890                     ushort b = cast(ushort)(0.5f + (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) * 65535.0f / 3.0f);
1891                     *s++ = b;
1892                 }
1893                 break;
1894             }
1895             case lf32:
1896             {
1897                 float* s = cast(float*) dest;
1898                 for (int x = 0; x < width; ++x)
1899                 {
1900                     float b = (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) / 3.0f;
1901                     *s++ = b;
1902                 }
1903                 break;
1904             }
1905             case la8:
1906             {
1907                 ubyte* s = dest;
1908                 for (int x = 0; x < width; ++x)
1909                 {
1910                     ubyte b = cast(ubyte)(0.5f + (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) * 255.0f / 3.0f);
1911                     ubyte a = cast(ubyte)(0.5f + inp[4*x+3] * 255.0f);
1912                     *s++ = b;
1913                     *s++ = a;
1914                 }
1915                 break;
1916             }
1917             case la16:
1918             {
1919                 ushort* s = cast(ushort*) dest;
1920                 for (int x = 0; x < width; ++x)
1921                 {
1922                     ushort b = cast(ushort)(0.5f + (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) * 65535.0f / 3.0f);
1923                     ushort a = cast(ushort)(0.5f + inp[4*x+3] * 65535.0f);
1924                     *s++ = b;
1925                     *s++ = a;
1926                 }
1927                 break;
1928             }
1929             case laf32:
1930             {
1931                 float* s = cast(float*) dest;
1932                 for (int x = 0; x < width; ++x)
1933                 {
1934                     float b = (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) / 3.0f;
1935                     float a = inp[4*x+3];
1936                     *s++ = b;
1937                     *s++ = a;
1938                 }
1939                 break;
1940             }
1941             case rgb8:
1942             {
1943                 ubyte* s = dest;
1944                 for (int x = 0; x < width; ++x)
1945                 {
1946                     ubyte r = cast(ubyte)(0.5f + inp[4*x+0] * 255.0f);
1947                     ubyte g = cast(ubyte)(0.5f + inp[4*x+1] * 255.0f);
1948                     ubyte b = cast(ubyte)(0.5f + inp[4*x+2] * 255.0f);
1949                     *s++ = r;
1950                     *s++ = g;
1951                     *s++ = b;
1952                 }
1953                 break;
1954             }
1955             case rgb16:
1956             {
1957                 ushort* s = cast(ushort*) dest;
1958                 for (int x = 0; x < width; ++x)
1959                 {
1960                     ushort r = cast(ushort)(0.5f + inp[4*x+0] * 65535.0f);
1961                     ushort g = cast(ushort)(0.5f + inp[4*x+1] * 65535.0f);
1962                     ushort b = cast(ushort)(0.5f + inp[4*x+2] * 65535.0f);
1963                     *s++ = r;
1964                     *s++ = g;
1965                     *s++ = b;
1966                 }
1967                 break;
1968             }
1969             case rgbf32:
1970             {
1971                 float* s = cast(float*) dest;
1972                 for (int x = 0; x < width; ++x)
1973                 {
1974                     *s++ = inp[4*x+0];
1975                     *s++ = inp[4*x+1];
1976                     *s++ = inp[4*x+2];
1977                 }
1978                 break;
1979             }
1980             case rgba8:
1981             {
1982                 ubyte* s = dest;
1983                 for (int x = 0; x < width; ++x)
1984                 {
1985                     ubyte r = cast(ubyte)(0.5f + inp[4*x+0] * 255.0f);
1986                     ubyte g = cast(ubyte)(0.5f + inp[4*x+1] * 255.0f);
1987                     ubyte b = cast(ubyte)(0.5f + inp[4*x+2] * 255.0f);
1988                     ubyte a = cast(ubyte)(0.5f + inp[4*x+3] * 255.0f);
1989                     *s++ = r;
1990                     *s++ = g;
1991                     *s++ = b;
1992                     *s++ = a;
1993                 }
1994                 break;
1995             }
1996             case rgba16:
1997             {
1998                 ushort* s = cast(ushort*)dest;
1999                 for (int x = 0; x < width; ++x)
2000                 {
2001                     ushort r = cast(ushort)(0.5f + inp[4*x+0] * 65535.0f);
2002                     ushort g = cast(ushort)(0.5f + inp[4*x+1] * 65535.0f);
2003                     ushort b = cast(ushort)(0.5f + inp[4*x+2] * 65535.0f);
2004                     ushort a = cast(ushort)(0.5f + inp[4*x+3] * 65535.0f);
2005                     *s++ = r;
2006                     *s++ = g;
2007                     *s++ = b;
2008                     *s++ = a;
2009                 }
2010                 break;
2011             }
2012             case rgbaf32:
2013             {
2014                 float* s = cast(float*) dest;
2015                 for (int x = 0; x < width; ++x)
2016                 {
2017                     *s++ = inp[4*x+0];
2018                     *s++ = inp[4*x+1];
2019                     *s++ = inp[4*x+2];
2020                     *s++ = inp[4*x+3];
2021                 }
2022                 break;
2023             }
2024         }
2025     }
2026     else
2027         assert(false);
2028 }
2029 
2030 
2031 // Test gapless pixel access
2032 unittest
2033 {
2034     Image image;
2035     image.setSize(16, 16, PixelType.rgba8, LAYOUT_GAPLESS | LAYOUT_VERT_STRAIGHT);
2036     assert(image.isGapless);
2037 
2038     ubyte[] all = image.allPixelsAtOnce();
2039     assert(all !is null);
2040 }
2041 
2042 // Semantics for image without pixel data type.
2043 // You can do very little with it apart from calling an initializing function.
2044 unittest
2045 {
2046     Image image;
2047 
2048     // An image that is uninitialized as pixel type.
2049     assert(image.type() == PixelType.unknown);
2050     assert(!image.hasType());
2051 
2052     // No type implies "no-data".
2053     assert(!image.hasData());
2054 
2055     // You can load an image. If it fails, it will have no type.
2056     image.loadFromFile("unkonwn-special-file");
2057     assert(!image.hasType());
2058     assert(image.errored());
2059     assert(!image.hasData());
2060 
2061     assert(!image.hasPlainPixels());
2062     assert(!image.isPlanar());
2063     assert(!image.isCompressed());
2064 }
2065 
2066 // Semantics for image without data (but with a type).
2067 unittest
2068 {
2069     Image image;
2070     image.initWithNoData(450, 614, PixelType.rgba8);
2071     assert(!image.hasData());
2072     assert(!image.isOwned());
2073     assert(image.hasType());
2074     assert(image.width == 450);
2075     assert(image.height == 614);
2076     assert(image.hasType());
2077     assert(!image.errored());
2078     assert(!image.hasData());
2079     assert(!image.hasPlainPixels());
2080     assert(!image.isPlanar());
2081     assert(!image.isCompressed());
2082     assert(image.hasNonZeroSize());
2083 }
2084 
2085 // Semantics for image with plain pixels
2086 unittest
2087 {
2088     Image image;
2089     image.setSize(3, 5, PixelType.rgba8);
2090     assert(image.hasType());
2091     assert(image.isOwned());
2092     assert(image.width == 3);
2093     assert(image.height == 5);
2094     assert(image.hasType());
2095     assert(!image.errored());
2096     assert(image.hasData());
2097     assert(image.hasPlainPixels());
2098     assert(!image.isPlanar());
2099     assert(!image.isCompressed());
2100     assert(image.hasNonZeroSize());
2101     image.convertTo16Bit();
2102     Image B = image.clone();
2103 }
2104 
2105 // Semantics for image with plain pixels, but with zero width and height.
2106 // Basically all operations are available to it.
2107 unittest
2108 {
2109     Image image;
2110     image.setSize(0, 0, PixelType.rgba8);
2111 
2112     static void zeroSizeChecks(ref Image image) @safe
2113     {
2114         assert(image.hasType());
2115         assert(image.isOwned());
2116         assert(image.width == 0);
2117         assert(image.height == 0);
2118         assert(image.hasType());
2119         assert(!image.errored());
2120         assert(image.hasData()); // It has data, just, it has a zero size.
2121         assert(image.hasPlainPixels());
2122         assert(!image.isPlanar());
2123         assert(!image.isCompressed()); 
2124         assert(!image.hasNonZeroSize());
2125     }
2126     zeroSizeChecks(image);
2127     image.convertTo16Bit();    
2128     zeroSizeChecks(image);
2129     Image B = image.clone();
2130     zeroSizeChecks(B);
2131 }