The OpenD Programming Language

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