Merge 4.x-dev

Fri, 09 Jul 2021 10:04:09 +0300

author
yakupbeyoglu
date
Fri, 09 Jul 2021 10:04:09 +0300
branch
4.x-dev
changeset 1706
539703473fd2
parent 1705
6e693ce168c9 (current diff)
parent 1704
a0fdf33ceefe (diff)
child 1707
0b5b95d558f9

Merge

Source/Gorgon/Types.h file | annotate | diff | comparison | revisions
--- a/Source/Gorgon/Containers/Image.h	Fri Jul 09 09:55:29 2021 +0300
+++ b/Source/Gorgon/Containers/Image.h	Fri Jul 09 10:04:09 2021 +0300
@@ -10,293 +10,320 @@
 #include "../IO/Stream.h"
 
 namespace Gorgon {
-	namespace Containers {
+    namespace Containers {
+        
+        enum class InterpolationMethod {
+            None,
+            NearestNeighbor = None,
+            Linear,
+            Bilinear = Linear,
+            Cubic,
+            Bicubic = Cubic,
+        };
 
-		/// This class is a container for image data. It supports different color modes and access to the
-		/// underlying data through () operator. This object implements move semantics. Since copy constructor is
-		/// expensive, it is deleted against accidental use. If a copy of the object is required, use Duplicate function.
-		template<class T_>
-		class basic_Image {
-		public:
-
-			/// Constructs an empty image data
-			basic_Image() {
-			}
+        template<class T_, class V_ = void>
+        struct FixImageValue;
 
-			/// Constructs a new image data with the given width, height and color mode. This constructor 
-			/// does not initialize data inside the image
-			basic_Image(const Geometry::Size &size, Graphics::ColorMode mode) : size(size), mode(mode) {
-				cpp=Graphics::GetChannelsPerPixel(mode);
-				data=(Byte*)malloc(size.Area()*cpp*sizeof(T_));
+        template<class T_>
+        struct FixImageValue<T_, typename std::enable_if<std::is_integral<T_>::value>::type> {
+            static T_ Fix(float val) {
+                return T_(Clamp<float>(std::round(val), std::numeric_limits<T_>::lowest(), std::numeric_limits<T_>::max()));
+            }
+        };
 
-				alphaloc = Graphics::HasAlpha(mode) ? Graphics::GetAlphaIndex(mode) : -1;
-			}
+        template<>
+        struct FixImageValue<float, void> {
+            static float Fix(float val) {
+                return val;
+            }
+        };
 
-			/// Copy constructor is disabled
-			basic_Image(const basic_Image &) = delete;
+        /// This class is a container for image data. It supports different color modes and access to the
+        /// underlying data through () operator. This object implements move semantics. Since copy constructor is
+        /// expensive, it is deleted against accidental use. If a copy of the object is required, use Duplicate function.
+        /// TODO Separate non-rgba related images, export/import with rgbaf
+        template<class T_>
+        class basic_Image {
+        public:
 
-			/// Move constructor
-			basic_Image(basic_Image &&data) : basic_Image() {
-				Swap(data);
-			}
+            /// Constructs an empty image data
+            basic_Image() {
+            }
 
-			/// Copy assignment is disabled
-			basic_Image &operator=(const basic_Image &) = delete;
+            /// Constructs a new image data with the given width, height and color mode. This constructor 
+            /// does not initialize data inside the image
+            basic_Image(const Geometry::Size &size, Graphics::ColorMode mode) : size(size), mode(mode) {
+                cpp=Graphics::GetChannelsPerPixel(mode);
+                data=(Byte*)malloc(size.Area()*cpp*sizeof(T_));
+
+                alphaloc = Graphics::HasAlpha(mode) ? Graphics::GetAlphaIndex(mode) : -1;
+            }
 
-			/// Move assignment
-			basic_Image &operator=(basic_Image &&other) { 
-				if(this == &other) return *this;
-				
-				Destroy();
-				Swap(other);
-				
-				return *this;
-			}
+            /// Copy constructor is disabled
+            basic_Image(const basic_Image &) = delete;
+
+            /// Move constructor
+            basic_Image(basic_Image &&data) : basic_Image() {
+                Swap(data);
+            }
+
+            /// Copy assignment is disabled
+            basic_Image &operator=(const basic_Image &) = delete;
 
-			/// Destructor
-			~basic_Image() {
-				Destroy();
-			}
+            /// Move assignment
+            basic_Image &operator=(basic_Image &&other) { 
+                if(this == &other) return *this;
+                
+                Destroy();
+                Swap(other);
+                
+                return *this;
+            }
 
-			/// Duplicates this image, essentially performing the work of copy constructor
-			basic_Image Duplicate() const {
-				basic_Image n;
-				n.Assign(data, size, mode);
+            /// Destructor
+            ~basic_Image() {
+                Destroy();
+            }
 
-				return n;
-			}
+            /// Duplicates this image, essentially performing the work of copy constructor
+            basic_Image Duplicate() const {
+                basic_Image n;
+                n.Assign(data, size, mode);
 
-			/// Resizes the image to the given size and color mode. This function discards the contents
-			/// of the image and does not perform any initialization.
-			void Resize(const Geometry::Size &size, Graphics::ColorMode mode) {
+                return n;
+            }
+
+            /// Resizes the image to the given size and color mode. This function discards the contents
+            /// of the image and does not perform any initialization.
+            void Resize(const Geometry::Size &size, Graphics::ColorMode mode) {
 #ifndef NDEBUG
-				if(!size.IsValid())
-					throw std::runtime_error("basic_Image size cannot be negative");
+                if(!size.IsValid())
+                    throw std::runtime_error("basic_Image size cannot be negative");
 #endif
 
-				// Check if resize is really necessary
-				if(this->size==size && this->cpp==Graphics::GetChannelsPerPixel(mode))
-					return;
+                // Check if resize is really necessary
+                if(this->size==size && this->cpp==Graphics::GetChannelsPerPixel(mode))
+                    return;
 
-				this->size     = size;
-				this->mode     = mode;
-				this->cpp      = Graphics::GetChannelsPerPixel(mode);
-				this->alphaloc = Graphics::HasAlpha(mode) ? Graphics::GetAlphaIndex(mode) : -1;
+                this->size     = size;
+                this->mode     = mode;
+                this->cpp      = Graphics::GetChannelsPerPixel(mode);
+                this->alphaloc = Graphics::HasAlpha(mode) ? Graphics::GetAlphaIndex(mode) : -1;
 
-				if(data) {
-					free(data);
-				}
-				
-				data=(Byte*)malloc(size.Area()*cpp*sizeof(T_));
-			}
+                if(data) {
+                    free(data);
+                }
+                
+                data=(Byte*)malloc(size.Area()*cpp*sizeof(T_));
+            }
 
-			/// Assigns the image to the copy of the given data. Ownership of the given data
-			/// is not transferred. If the given data is not required elsewhere, consider using
-			/// Assume function. This variant performs resize and copy at the same time. The given 
-			/// data should have the size of width*height*Graphics::GetBytesPerPixel(mode)*sizeof(T_). 
-			/// This function does not perform any checks for the data size while copying it.
-			/// If width or height is 0, the newdata is not accessed and this method effectively
-			/// Destroys the current image. In this case, both width and height should be specified as 0.
-			void Assign(Byte *newdata, const Geometry::Size &size, Graphics::ColorMode mode) {
+            /// Assigns the image to the copy of the given data. Ownership of the given data
+            /// is not transferred. If the given data is not required elsewhere, consider using
+            /// Assume function. This variant performs resize and copy at the same time. The given 
+            /// data should have the size of width*height*Graphics::GetBytesPerPixel(mode)*sizeof(T_). 
+            /// This function does not perform any checks for the data size while copying it.
+            /// If width or height is 0, the newdata is not accessed and this method effectively
+            /// Destroys the current image. In this case, both width and height should be specified as 0.
+            void Assign(Byte *newdata, const Geometry::Size &size, Graphics::ColorMode mode) {
 #ifndef NDEBUG
-				if(!size.IsValid())
-					throw std::runtime_error("basic_Image size cannot be negative");
+                if(!size.IsValid())
+                    throw std::runtime_error("basic_Image size cannot be negative");
 #endif
-				this->size     = size;
-				this->mode     = mode;
-				this->cpp      = Graphics::GetChannelsPerPixel(mode);
-				this->alphaloc = Graphics::HasAlpha(mode) ? Graphics::GetAlphaIndex(mode) : -1;
+                this->size     = size;
+                this->mode     = mode;
+                this->cpp      = Graphics::GetChannelsPerPixel(mode);
+                this->alphaloc = Graphics::HasAlpha(mode) ? Graphics::GetAlphaIndex(mode) : -1;
 
-				if(data && data!=newdata) {
-					free(data);
-				}
-				
-				if(size.Area()*cpp>0) {
-					data=(Byte*)malloc(size.Area()*cpp*sizeof(T_));
-					memcpy(data, newdata, size.Area()*cpp*sizeof(T_));
-				}
-				else {
-					data=nullptr;
-				}
-			}
+                if(data && data!=newdata) {
+                    free(data);
+                }
+                
+                if(size.Area()*cpp>0) {
+                    data=(Byte*)malloc(size.Area()*cpp*sizeof(T_));
+                    memcpy(data, newdata, size.Area()*cpp*sizeof(T_));
+                }
+                else {
+                    data=nullptr;
+                }
+            }
 
-			/// Assigns the image to the copy of the given data. Ownership of the given data
-			/// is not transferred. If the given data is not required elsewhere, consider using
-			/// Assume function. The size and color mode of the image stays the same. The given 
-			/// data should have the size of width*height*Graphics::GetBytesPerPixel(mode)*sizeof(T_). 
-			/// This function does not perform any checks for the data size while copying it.t
-			void Assign(Byte *newdata) {
-				memcpy(data, newdata, size.Area()*cpp*sizeof(T_));
-			}
+            /// Assigns the image to the copy of the given data. Ownership of the given data
+            /// is not transferred. If the given data is not required elsewhere, consider using
+            /// Assume function. The size and color mode of the image stays the same. The given 
+            /// data should have the size of width*height*Graphics::GetBytesPerPixel(mode)*sizeof(T_). 
+            /// This function does not perform any checks for the data size while copying it.t
+            void Assign(Byte *newdata) {
+                memcpy(data, newdata, size.Area()*cpp*sizeof(T_));
+            }
 
-			/// Assumes the ownership of the given data. This variant changes the size and
-			/// color mode of the image. The given data should have the size of 
-			/// width*height*Graphics::GetBytesPerPixel(mode)*sizeof(T_). This function
-			/// does not perform any checks for the data size while assuming it.
-			/// newdata could be nullptr however, in this case
-			/// width, height should be 0. mode is not assumed to be ColorMode::Invalid while
-			/// the image is empty, therefore it could be specified as any value.
-			void Assume(Byte *newdata, const Geometry::Size &size, Graphics::ColorMode mode) {
+            /// Assumes the ownership of the given data. This variant changes the size and
+            /// color mode of the image. The given data should have the size of 
+            /// width*height*Graphics::GetBytesPerPixel(mode)*sizeof(T_). This function
+            /// does not perform any checks for the data size while assuming it.
+            /// newdata could be nullptr however, in this case
+            /// width, height should be 0. mode is not assumed to be ColorMode::Invalid while
+            /// the image is empty, therefore it could be specified as any value.
+            void Assume(Byte *newdata, const Geometry::Size &size, Graphics::ColorMode mode) {
 #ifndef NDEBUG
-				if(!size.IsValid())
-					throw std::runtime_error("basic_Image size cannot be negative");
+                if(!size.IsValid())
+                    throw std::runtime_error("basic_Image size cannot be negative");
 #endif
-				this->size   = size;
-				this->mode   = mode;
-				this->cpp    = Graphics::GetChannelsPerPixel(mode);
-				this->alphaloc = Graphics::HasAlpha(mode) ? Graphics::GetAlphaIndex(mode) : -1;
+                this->size   = size;
+                this->mode   = mode;
+                this->cpp    = Graphics::GetChannelsPerPixel(mode);
+                this->alphaloc = Graphics::HasAlpha(mode) ? Graphics::GetAlphaIndex(mode) : -1;
 
-				if(data && data!=newdata) {
-					free(data);
-				}
-				
-				data=newdata;
-			}
+                if(data && data!=newdata) {
+                    free(data);
+                }
+                
+                data=newdata;
+            }
 
-			/// Assumes the ownership of the given data. The size and color mode of the image stays the same.
-			/// The given data should have the size of width*height*Graphics::GetBytesPerPixel(mode)*sizeof(T_).
-			/// This function does not perform any checks for the data size while assuming it.
-			void Assume(Byte *newdata) {
-				if(data && data!=newdata) {
-					free(data);
-				}
-				
-				data=newdata;
-			}
+            /// Assumes the ownership of the given data. The size and color mode of the image stays the same.
+            /// The given data should have the size of width*height*Graphics::GetBytesPerPixel(mode)*sizeof(T_).
+            /// This function does not perform any checks for the data size while assuming it.
+            void Assume(Byte *newdata) {
+                if(data && data!=newdata) {
+                    free(data);
+                }
+                
+                data=newdata;
+            }
 
-			/// Returns and disowns the current data buffer. If image is empty, this method will return a nullptr.
-			Byte *Release() {
-				auto temp=data;
-				data=nullptr;
-				Destroy();
+            /// Returns and disowns the current data buffer. If image is empty, this method will return a nullptr.
+            Byte *Release() {
+                auto temp=data;
+                data=nullptr;
+                Destroy();
 
-				return temp;
-			}
+                return temp;
+            }
 
-			/// Cleans the contents of the buffer by setting every byte it contains to 0.
-			void Clear() {
+            /// Cleans the contents of the buffer by setting every byte it contains to 0.
+            void Clear() {
 #ifndef NDEBUG
-				if(!data) {
-					throw std::runtime_error("basic_Image data is empty");
-				}
+                if(!data) {
+                    throw std::runtime_error("basic_Image data is empty");
+                }
 #endif
 
-				memset(data, 0, size.Area()*cpp*sizeof(T_));
-			}
+                memset(data, 0, size.Area()*cpp*sizeof(T_));
+            }
 
-			/// Destroys this image by setting width and height to 0 and freeing the memory
-			/// used by its data. Also color mode is set to ColorMode::Invalid
-			void Destroy() {
-				if(data) {
-					free(data);
-					data=nullptr;
-				}
-				size   = {0, 0};
-				cpp    = 0;
-				mode   = Graphics::ColorMode::Invalid;
-			}
+            /// Destroys this image by setting width and height to 0 and freeing the memory
+            /// used by its data. Also color mode is set to ColorMode::Invalid
+            void Destroy() {
+                if(data) {
+                    free(data);
+                    data=nullptr;
+                }
+                size   = {0, 0};
+                cpp    = 0;
+                mode   = Graphics::ColorMode::Invalid;
+            }
 
-			/// Swaps this image with another. This function is used to implement move semantics.
-			void Swap(basic_Image &other) {
-				using std::swap;
+            /// Swaps this image with another. This function is used to implement move semantics.
+            void Swap(basic_Image &other) {
+                using std::swap;
 
-				swap(data,     other.data);
-				swap(size,     other.size);
-				swap(mode,     other.mode);
-				swap(cpp,      other.cpp);
-				swap(alphaloc, other.alphaloc);
-			}
+                swap(data,     other.data);
+                swap(size,     other.size);
+                swap(mode,     other.mode);
+                swap(cpp,      other.cpp);
+                swap(alphaloc, other.alphaloc);
+            }
 
-			/// Returns the raw data pointer
-			Byte *RawData() {
-				return data;
-			}
+            /// Returns the raw data pointer
+            Byte *RawData() {
+                return data;
+            }
 
-			/// Returns the raw data pointer
-			const Byte *RawData() const {
-				return data;
-			}
+            /// Returns the raw data pointer
+            const Byte *RawData() const {
+                return data;
+            }
 
-			/// Converts this image data to RGBA buffer
-			void ConvertToRGBA() {
-				if(!data) return;
+            /// Converts this image data to RGBA buffer
+            void ConvertToRGBA() {
+                if(!data) return;
 
-				switch(mode) {
-				case Graphics::ColorMode::BGRA:
-					for(int i=0; i<size.Area(); i++) {
-						std::swap(data[i*4+2], data[i*4+0]);
-					}
-					break;
+                switch(mode) {
+                case Graphics::ColorMode::BGRA:
+                    for(int i=0; i<size.Area(); i++) {
+                        std::swap(data[i*4+2], data[i*4+0]);
+                    }
+                    break;
 
-				case Graphics::ColorMode::Grayscale_Alpha: {
-					auto pdata = data;
-					data = (Byte*)malloc(size.Area()*4*sizeof(T_));
+                case Graphics::ColorMode::Grayscale_Alpha: {
+                    auto pdata = data;
+                    data = (Byte*)malloc(size.Area()*4*sizeof(T_));
 
-					for(int i=0; i<size.Area(); i++) {
-						data[i*4+0] = pdata[i*2+0];
-						data[i*4+1] = pdata[i*2+0];
-						data[i*4+2] = pdata[i*2+0];
-						data[i*4+3] = pdata[i*2+1];
-					}
-					delete pdata;
-				}
-				break;
+                    for(int i=0; i<size.Area(); i++) {
+                        data[i*4+0] = pdata[i*2+0];
+                        data[i*4+1] = pdata[i*2+0];
+                        data[i*4+2] = pdata[i*2+0];
+                        data[i*4+3] = pdata[i*2+1];
+                    }
+                    delete pdata;
+                }
+                break;
 
-				case Graphics::ColorMode::Grayscale: {
-					auto pdata = data;
-					data = (Byte*)malloc(size.Area()*4*sizeof(T_));
+                case Graphics::ColorMode::Grayscale: {
+                    auto pdata = data;
+                    data = (Byte*)malloc(size.Area()*4*sizeof(T_));
 
-					for(int i=0; i<size.Area(); i++) {
-						data[i*4+0] = pdata[i+0];
-						data[i*4+1] = pdata[i+0];
-						data[i*4+2] = pdata[i+0];
-						data[i*4+3] = 255;
-					}
-					delete pdata;
-				}
-				break;
+                    for(int i=0; i<size.Area(); i++) {
+                        data[i*4+0] = pdata[i+0];
+                        data[i*4+1] = pdata[i+0];
+                        data[i*4+2] = pdata[i+0];
+                        data[i*4+3] = 255;
+                    }
+                    delete pdata;
+                }
+                break;
 
-				case Graphics::ColorMode::Alpha: {
-					auto pdata = data;
-					data = (Byte*)malloc(size.Area()*4*sizeof(T_));
+                case Graphics::ColorMode::Alpha: {
+                    auto pdata = data;
+                    data = (Byte*)malloc(size.Area()*4*sizeof(T_));
 
-					for(int i=0; i<size.Area(); i++) {
-						data[i*4+0] = 255;
-						data[i*4+1] = 255;
-						data[i*4+2] = 255;
-						data[i*4+3] = pdata[i+0];
-					}
-					delete pdata;
-				}
-				break;
+                    for(int i=0; i<size.Area(); i++) {
+                        data[i*4+0] = 255;
+                        data[i*4+1] = 255;
+                        data[i*4+2] = 255;
+                        data[i*4+3] = pdata[i+0];
+                    }
+                    delete pdata;
+                }
+                break;
 
-				case Graphics::ColorMode::RGB: {
-					auto pdata = data;
-					data = (Byte*)malloc(size.Area()*4*sizeof(T_));
+                case Graphics::ColorMode::RGB: {
+                    auto pdata = data;
+                    data = (Byte*)malloc(size.Area()*4*sizeof(T_));
 
-					for(int i=0; i<size.Area(); i++) {
-						data[i*4+0] = pdata[i*3+0];
-						data[i*4+1] = pdata[i*3+1];
-						data[i*4+2] = pdata[i*3+2];
-						data[i*4+3] = 255;
-					}
-					delete pdata;
-				}
-				break;
+                    for(int i=0; i<size.Area(); i++) {
+                        data[i*4+0] = pdata[i*3+0];
+                        data[i*4+1] = pdata[i*3+1];
+                        data[i*4+2] = pdata[i*3+2];
+                        data[i*4+3] = 255;
+                    }
+                    delete pdata;
+                }
+                break;
 
-				case Graphics::ColorMode::BGR: {
-					auto pdata = data;
-					data = (Byte*)malloc(size.Area()*4*sizeof(T_));
+                case Graphics::ColorMode::BGR: {
+                    auto pdata = data;
+                    data = (Byte*)malloc(size.Area()*4*sizeof(T_));
 
-					for(int i=0; i<size.Area(); i++) {
-						data[i*4+0] = pdata[i*3+2];
-						data[i*4+1] = pdata[i*3+1];
-						data[i*4+2] = pdata[i*3+0];
-						data[i*4+3] = 255;
-					}
-					delete pdata;
-				}
-				break;
+                    for(int i=0; i<size.Area(); i++) {
+                        data[i*4+0] = pdata[i*3+2];
+                        data[i*4+1] = pdata[i*3+1];
+                        data[i*4+2] = pdata[i*3+0];
+                        data[i*4+3] = 255;
+                    }
+                    delete pdata;
+                }
+                break;
                 
                 case Graphics::ColorMode::RGBA:
                     //do nothing
@@ -305,17 +332,17 @@
                 default:
                     throw std::runtime_error("Invalid color mode");
 
-				}
+                }
 
-				mode = Graphics::ColorMode::RGBA;
-			}
-			
+                mode = Graphics::ColorMode::RGBA;
+            }
+            
             // cppcheck-suppress constParameter
-			/// Copies data from one image to another. This operation does not perform
-			/// blending. Additionally, color modes should be the same. However, this
-			/// function will do clipping for overflows. Do not use negative values for
-			/// target. Will return false if nothing is copied.
-			bool CopyTo(basic_Image &dest, Geometry::Point target = {0, 0}) const {
+            /// Copies data from one image to another. This operation does not perform
+            /// blending. Additionally, color modes should be the same. However, this
+            /// function will do clipping for overflows. Do not use negative values for
+            /// target. Will return false if nothing is copied.
+            bool CopyTo(basic_Image &dest, Geometry::Point target = {0, 0}) const {
                 if(dest.GetMode() != mode || size.Area() == 0 || dest.GetSize().Area() == 0) 
                     return false;
                 
@@ -342,13 +369,13 @@
                 
                 return true;
             }
-			
+            
             // cppcheck-suppress constParameter
-			/// Copies data from one image to another. This operation does not perform
-			/// blending. Additionally, color modes should be the same. However, this
-			/// function will do clipping. Source bounds should be within the image.
-			/// Will return false if nothing is copied.
-			bool CopyTo(basic_Image &dest, Geometry::Bounds source, Geometry::Point target = {0, 0}) const {
+            /// Copies data from one image to another. This operation does not perform
+            /// blending. Additionally, color modes should be the same. However, this
+            /// function will do clipping. Source bounds should be within the image.
+            /// Will return false if nothing is copied.
+            bool CopyTo(basic_Image &dest, Geometry::Bounds source, Geometry::Point target = {0, 0}) const {
                 if(dest.GetMode() != mode || size.Area() == 0 || dest.GetSize().Area() == 0) 
                     return false;
                 
@@ -390,816 +417,1360 @@
                 return true;
             }
 
-			/// Copies this image to a RGBA buffer, buffer should be resize before calling this function
-			void CopyToRGBABuffer(Byte *buffer) const {
-				if(!data) return;
+            /// Copies this image to a RGBA buffer, buffer should be resize before calling this function
+            void CopyToRGBABuffer(Byte *buffer) const {
+                if(!data) return;
 
-				int i;
+                int i;
 
-				switch(mode) {
-					case Graphics::ColorMode::RGBA:
-						memcpy(buffer, data, size.Area()*4);
-						break;
+                switch(mode) {
+                    case Graphics::ColorMode::RGBA:
+                        memcpy(buffer, data, size.Area()*4);
+                        break;
 
-					case Graphics::ColorMode::BGRA:
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = data[i*4+2];
-							buffer[i*4+1] = data[i*4+1];
-							buffer[i*4+2] = data[i*4+0];
-							buffer[i*4+3] = data[i*4+3];
-						}
-						break;
+                    case Graphics::ColorMode::BGRA:
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = data[i*4+2];
+                            buffer[i*4+1] = data[i*4+1];
+                            buffer[i*4+2] = data[i*4+0];
+                            buffer[i*4+3] = data[i*4+3];
+                        }
+                        break;
 
-					case Graphics::ColorMode::Grayscale_Alpha:
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = data[i*2+0];
-							buffer[i*4+1] = data[i*2+0];
-							buffer[i*4+2] = data[i*2+0];
-							buffer[i*4+3] = data[i*2+1];
-						}
-						break;
+                    case Graphics::ColorMode::Grayscale_Alpha:
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = data[i*2+0];
+                            buffer[i*4+1] = data[i*2+0];
+                            buffer[i*4+2] = data[i*2+0];
+                            buffer[i*4+3] = data[i*2+1];
+                        }
+                        break;
 
-					case Graphics::ColorMode::Grayscale:
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = data[i+0];
-							buffer[i*4+1] = data[i+0];
-							buffer[i*4+2] = data[i+0];
-							buffer[i*4+3] = 255;
-						}
-						break;
+                    case Graphics::ColorMode::Grayscale:
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = data[i+0];
+                            buffer[i*4+1] = data[i+0];
+                            buffer[i*4+2] = data[i+0];
+                            buffer[i*4+3] = 255;
+                        }
+                        break;
 
-					case Graphics::ColorMode::Alpha:
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = 255;
-							buffer[i*4+1] = 255;
-							buffer[i*4+2] = 255;
-							buffer[i*4+3] = data[i+0];
-						}
-						break;
+                    case Graphics::ColorMode::Alpha:
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = 255;
+                            buffer[i*4+1] = 255;
+                            buffer[i*4+2] = 255;
+                            buffer[i*4+3] = data[i+0];
+                        }
+                        break;
 
-					case Graphics::ColorMode::RGB: 
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = data[i*3+0];
-							buffer[i*4+1] = data[i*3+1];
-							buffer[i*4+2] = data[i*3+2];
-							buffer[i*4+3] = 255;
-						}
-						break;
+                    case Graphics::ColorMode::RGB: 
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = data[i*3+0];
+                            buffer[i*4+1] = data[i*3+1];
+                            buffer[i*4+2] = data[i*3+2];
+                            buffer[i*4+3] = 255;
+                        }
+                        break;
 
-					case Graphics::ColorMode::BGR:
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = data[i*3+2];
-							buffer[i*4+1] = data[i*3+1];
-							buffer[i*4+2] = data[i*3+0];
-							buffer[i*4+3] = 255;
-						}
-						break;
+                    case Graphics::ColorMode::BGR:
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = data[i*3+2];
+                            buffer[i*4+1] = data[i*3+1];
+                            buffer[i*4+2] = data[i*3+0];
+                            buffer[i*4+3] = 255;
+                        }
+                        break;
                     
                     default:
                         throw std::runtime_error("Invalid mode");
-				}
-			}
+                }
+            }
 
-			/// Copies this image to a RGBA buffer, buffer should be resize before calling this function.
-			/// This function is here mostly to create icon for Win32
-			void CopyToBGRABuffer(Byte *buffer) const {
-				if(!data) return;
+            /// Copies this image to a RGBA buffer, buffer should be resize before calling this function.
+            /// This function is here mostly to create icon for Win32
+            void CopyToBGRABuffer(Byte *buffer) const {
+                if(!data) return;
 
-				int i;
+                int i;
 
-				switch(mode) {
-					case Graphics::ColorMode::BGRA:
-						memcpy(buffer, data, size.Area()*4);
-						break;
+                switch(mode) {
+                    case Graphics::ColorMode::BGRA:
+                        memcpy(buffer, data, size.Area()*4);
+                        break;
 
-					case Graphics::ColorMode::RGBA:
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = data[i*4+2];
-							buffer[i*4+1] = data[i*4+1];
-							buffer[i*4+2] = data[i*4+0];
-							buffer[i*4+3] = data[i*4+3];
-						}
-						break;
+                    case Graphics::ColorMode::RGBA:
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = data[i*4+2];
+                            buffer[i*4+1] = data[i*4+1];
+                            buffer[i*4+2] = data[i*4+0];
+                            buffer[i*4+3] = data[i*4+3];
+                        }
+                        break;
 
-					case Graphics::ColorMode::Grayscale_Alpha:
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = data[i*2+0];
-							buffer[i*4+1] = data[i*2+0];
-							buffer[i*4+2] = data[i*2+0];
-							buffer[i*4+3] = data[i*2+1];
-						}
-						break;
+                    case Graphics::ColorMode::Grayscale_Alpha:
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = data[i*2+0];
+                            buffer[i*4+1] = data[i*2+0];
+                            buffer[i*4+2] = data[i*2+0];
+                            buffer[i*4+3] = data[i*2+1];
+                        }
+                        break;
 
-					case Graphics::ColorMode::Grayscale:
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = data[i+0];
-							buffer[i*4+1] = data[i+0];
-							buffer[i*4+2] = data[i+0];
-							buffer[i*4+3] = 255;
-						}
-						break;
+                    case Graphics::ColorMode::Grayscale:
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = data[i+0];
+                            buffer[i*4+1] = data[i+0];
+                            buffer[i*4+2] = data[i+0];
+                            buffer[i*4+3] = 255;
+                        }
+                        break;
 
-					case Graphics::ColorMode::Alpha:
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = 255;
-							buffer[i*4+1] = 255;
-							buffer[i*4+2] = 255;
-							buffer[i*4+3] = data[i+0];
-						}
-						break;
+                    case Graphics::ColorMode::Alpha:
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = 255;
+                            buffer[i*4+1] = 255;
+                            buffer[i*4+2] = 255;
+                            buffer[i*4+3] = data[i+0];
+                        }
+                        break;
 
-					case Graphics::ColorMode::BGR: 
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = data[i*3+0];
-							buffer[i*4+1] = data[i*3+1];
-							buffer[i*4+2] = data[i*3+2];
-							buffer[i*4+3] = 255;
-						}
-						break;
+                    case Graphics::ColorMode::BGR: 
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = data[i*3+0];
+                            buffer[i*4+1] = data[i*3+1];
+                            buffer[i*4+2] = data[i*3+2];
+                            buffer[i*4+3] = 255;
+                        }
+                        break;
 
-					case Graphics::ColorMode::RGB:
-						for(i=0; i<size.Area(); i++) {
-							buffer[i*4+0] = data[i*3+2];
-							buffer[i*4+1] = data[i*3+1];
-							buffer[i*4+2] = data[i*3+0];
-							buffer[i*4+3] = 255;
-						}
-						break;
+                    case Graphics::ColorMode::RGB:
+                        for(i=0; i<size.Area(); i++) {
+                            buffer[i*4+0] = data[i*3+2];
+                            buffer[i*4+1] = data[i*3+1];
+                            buffer[i*4+2] = data[i*3+0];
+                            buffer[i*4+3] = 255;
+                        }
+                        break;
                     
                     default:
                         throw std::runtime_error("Invalid mode");
 
-				}
-			}
-			/// Copies this image to a RGBA buffer, buffer should be resize before calling this function.
-			/// This function is here mostly to create icon for X11
-			void CopyToBGRABufferLong(unsigned long *buffer) const {
-				if(!data) return;
+                }
+            }
+            /// Copies this image to a RGBA buffer, buffer should be resize before calling this function.
+            /// This function is here mostly to create icon for X11
+            void CopyToBGRABufferLong(unsigned long *buffer) const {
+                if(!data) return;
 
-				int i;
+                int i;
 
-				switch(mode) {
-					case Graphics::ColorMode::BGRA:
-						for(i=0; i<size.Area(); i++) {
+                switch(mode) {
+                    case Graphics::ColorMode::BGRA:
+                        for(i=0; i<size.Area(); i++) {
                             buffer[i] = (data[i*4+0]<<0) | (data[i*4+1]<<8) | (data[i*4+2]<<16) | (data[i*4+3]<<24);
                         }
-						break;
+                        break;
 
-					case Graphics::ColorMode::RGBA:
-						for(i=0; i<size.Area(); i++) {
+                    case Graphics::ColorMode::RGBA:
+                        for(i=0; i<size.Area(); i++) {
                             buffer[i] = (data[i*4+2]<<0) | (data[i*4+1]<<8) | (data[i*4+0]<<16) | (data[i*4+3]<<24);
                         }
-						break;
+                        break;
 
-					case Graphics::ColorMode::Grayscale_Alpha:
-						for(i=0; i<size.Area(); i++) {
+                    case Graphics::ColorMode::Grayscale_Alpha:
+                        for(i=0; i<size.Area(); i++) {
                             buffer[i] = (data[i*2+0]<<0) | (data[i*2+0]<<8) | (data[i*2+0]<<16) | (data[i*2+1]<<24);
                         }
-						break;
+                        break;
 
-					case Graphics::ColorMode::Grayscale:
-						for(i=0; i<size.Area(); i++) {
+                    case Graphics::ColorMode::Grayscale:
+                        for(i=0; i<size.Area(); i++) {
                             buffer[i] = (data[i*1+0]<<0) | (data[i*1+0]<<8) | (data[i*1+0]<<16) | (255<<24);
                         }
-						break;
+                        break;
 
-					case Graphics::ColorMode::Alpha: 
-						for(i=0; i<size.Area(); i++) {
+                    case Graphics::ColorMode::Alpha: 
+                        for(i=0; i<size.Area(); i++) {
                             buffer[i] = (255<<0) | (255<<8) | (255<<16) | (data[i*1+0]<<24);
-						}
-						break;
+                        }
+                        break;
 
-					case Graphics::ColorMode::BGR: 
-						for(i=0; i<size.Area(); i++) {
+                    case Graphics::ColorMode::BGR: 
+                        for(i=0; i<size.Area(); i++) {
                             buffer[i] = (data[i*3+0]<<0) | (data[i*3+1]<<8) | (data[i*3+2]<<16) | (255<<24);
-						}
-						break;
+                        }
+                        break;
 
-					case Graphics::ColorMode::RGB:
-						for(i=0; i<size.Area(); i++) {
+                    case Graphics::ColorMode::RGB:
+                        for(i=0; i<size.Area(); i++) {
                             buffer[i] = (data[i*3+2]<<0) | (data[i*3+1]<<8) | (data[i*3+0]<<16) | (255<<24);
-						}
-						break;
+                        }
+                        break;
                     
                     default:
                         throw std::runtime_error("Invalid mode");
 
-				}
-			}
+                }
+            }
+
+            /// Shrinks the size of the image using integer area interpolation.
+            basic_Image ShrinkMultiple(const Geometry::Size& factor) const {
+                Geometry::Size newsize = {(int)std::ceil((float)size.Width/factor.Width), (int)std::ceil((float)size.Height/factor.Height)};
+                basic_Image target(newsize, GetMode());
+
+                for(int y=0; y<newsize.Height; y++) {
+                    auto th = std::min(y*factor.Height+factor.Height, size.Height);
+                    auto ys = y * factor.Height;
+
+                    for(int x=0; x<newsize.Width; x++) {
+                        auto tw = std::min(x*factor.Width+factor.Width, size.Width);
+                        auto xs = x * factor.Width;
+
+                        int   count = 0;
+                        float sum[4]= {};
+
+                        for(int yy=ys; yy<th; yy++) {
+                            for(int xx=xs; xx<tw; xx++) {
+                                count++;
 
-			/// Imports a given bitmap file. BMP RLE compression and colorspaces are not supported.
-			bool ImportBMP(const std::string &filename, bool dib = false) {
-				std::ifstream file(filename, std::ios::binary);
+                                for(unsigned c=0; c<cpp; c++) {
+                                    sum[c] += operator()(xx, yy, c);
+                                }
+                            }
+                        }
+
+                        for(unsigned c=0; c<cpp; c++) {
+                            target(x, y, c) = FixImageValue<T_>::Fix(sum[c] / count);
+                        }
+                    }
+                }
+
+                return target;
+            }
 
-				if(!file.is_open()) return false;
+            /// Scales this image to the given size. In the image is shrunk more than 2x its original size
+            /// Area interpolation is used along with the specified interpolation method.
+            basic_Image Scale(const Geometry::Size &newsize, InterpolationMethod method = InterpolationMethod::Cubic) const {
+                basic_Image target(newsize, GetMode());
+
+                float fx = (float)size.Width / newsize.Width;
+                float fy = (float)size.Height / newsize.Height;
 
-				return ImportBMP(file, dib);
-			}
+                if(fx > 2 || fy > 2) {
+                    if(fx < 2)
+                        fx = 1;
+                    if(fy < 2)
+                        fy = 1;
+
+                    auto img = ShrinkMultiple({int(fx), int(fy)});
 
-			/// Imports a given bitmap file. BMP RLE compression and colorspaces are not supported.
-			bool ImportBMP(std::istream &file, bool dib = false) {
-				using namespace IO;
+                    if(img.GetSize() == newsize)
+                        return img;
+                    else
+                        return img.Scale(newsize, method);
+                }
 
-				unsigned long off = 0;
+                if(method == InterpolationMethod::NearestNeighbor) {
+                    float yy = fy/2;
+                    for(int y=0; y<newsize.Height; y++) {
+                        float xx = fx/2;
+                        for(int x=0; x<newsize.Width; x++) {
+                            for(unsigned c=0; c<cpp; c++) {
+                                target(x, y, c) = operator()((int)xx, (int)yy, c);
+                            }
 
-				if(!dib) {
-					if(ReadString(file, 2) != "BM") return false;
+                            xx += fx;
+                        }
 
-					IO::ReadUInt32(file);
+                        yy += fy;
+                    }
+                }
+                else if(method == InterpolationMethod::Linear) {
+                    float yy = 0.5f;
+                    for(int y=0; y<newsize.Height; y++) {
+                        float ly  = yy - (int)yy;
+                        float ly1 = 1 - ly;
+                        int   y1 = (int)yy;
+                        int   y2 = y1 + 1;
 
-					ReadUInt16(file); //reserved 1
-					ReadUInt16(file); //reserved 2
+                        y1 = y1 < 0 ? 0 : y1;
+                        y2 = y2 >= size.Height ? size.Height - 1 : y2;
 
-					off = ReadUInt32(file);
-				}
+                        float xx = 0.5f;
+                        for(int x=0; x<newsize.Width; x++) {
+                            float lx  = xx - (int)xx;
+                            float lx1 = 1 - lx;
+                            int   x1 = (int)xx;
+                            int   x2 = x1 + 1;
+
+                            x1 = x1 < 0 ? 0 : x1;
+                            x2 = x2 >= size.Width ? size.Width - 1 : x2;
 
-				auto headersize = IO::ReadUInt32(file);
+                            for(unsigned c=0; c<cpp; c++) {
+                                target(x, y, c) = FixImageValue<T_>::Fix(
+                                    lx1 * ly1 * operator()(x1, y1, c) +
+                                    lx  * ly1 * operator()(x2, y1, c) +
+                                    lx  * ly  * operator()(x2, y2, c) +
+                                    lx1 * ly  * operator()(x1, y2, c)
+                                );
+                            }
+
+                            xx += fx;
+                        }
 
-				int width = 0, height = 0;
-				int bpp = 0;
-				bool upsidedown = false;
-				bool grayscalepalette = true;
-				int colorsused = 0;
-				bool alpha = false;
-				int redshift, greenshift, blueshift, alphashift;
-				float redmult = 1, greenmult = 1, bluemult = 1, alphamult = 1;
-				uint32_t redmask = 0, greenmask = 0, bluemask = 0, alphamask = 0;
+                        yy += fy;
+                    }
+                }
+                else if(method == InterpolationMethod::Cubic) {
+                    float yy = 0.f;
+                    int ys[4];
+                    float wy[4], yf[4];
+                    int xs[4];
+                    float wx[4], xf[4];
+                    const float a = -0.5;
+                    for(int y=0; y<newsize.Height; y++) {
+                        yf[1]  = yy - (int)yy;
+                        yf[2] = 1 - yf[1];
+                        yf[3] = 2 - yf[1];
+                        yf[0] = 1 + yf[1];
+
+                        for(int i=0; i<4; i++) {
+                            if(i == 1 || i == 2) {
+                                wy[i] = (a+2) * yf[i]*yf[i]*yf[i] - (a+3) * yf[i]*yf[i] + 1;
+                            }
+                            else {
+                                wy[i] = a * yf[i]*yf[i]*yf[i] - 5*a * yf[i]*yf[i] + 8*a * yf[i] - 4*a;
+                            }
+                            ys[i]  = Clamp((int)yy-1 + i, 0, size.Height-1);
+                        }
 
-				std::vector<Graphics::RGBA> palette;
+                        float xx = 0.f;
+                        for(int x=0; x<newsize.Width; x++) {
+                            xf[1]  = xx - (int)xx;
+                            xf[2] = 1 - xf[1];
+                            xf[3] = 2 - xf[1];
+                            xf[0] = 1 + xf[1];
 
-				auto shiftcalc=[](uint32_t v) {
-					int pos = 0;
-					while(v) {
-						if(v&1) return pos;
-						v=v>>1;
-						pos++;
-					}
+                            for(int i=0; i<4; i++) {
+                                if(i == 1 || i == 2) {
+                                    wx[i] = (a+2) * xf[i]*xf[i]*xf[i] - (a+3) * xf[i]*xf[i] + 1;
+                                }
+                                else {
+                                    wx[i] = a * xf[i]*xf[i]*xf[i] - 5*a * xf[i]*xf[i] + 8*a * xf[i] - 4*a;
+                                }
+                                xs[i]  = Clamp((int)xx-1 + i, 0, size.Width-1);
+                            }
 
-					return pos;
-				};
+                            for(unsigned c=0; c<cpp; c++) {
+                                float v = 0;
+                                for(int j=0; j<4; j++) {
+                                    for(int i=0; i<4; i++) {
+                                        v += wx[i] * wy[j] * operator()(xs[i], ys[j], c);
+                                    }
+                                }
+
+                                target(x, y, c) = FixImageValue<T_>::Fix(v);
+                            }
+
+                            xx += fx;
+                        }
 
-				if(headersize == 12) { //2x bmp
-					width = ReadInt16(file);
-					height = ReadInt16(file);
-					ReadUInt16(file); //planes
-					bpp = ReadUInt16(file);
-				}
+                        yy += fy;
+                    }
+                }
+                else {
+                    throw std::runtime_error("Unknown interpolation method");
+                }
 
-				if(headersize >= 40) { //3x
-					width = ReadInt32(file);
-					height = ReadInt32(file);
-					ReadUInt16(file); //planes
-					bpp = ReadUInt16(file);
+                return target;
+            }
+            
+            /// Rotates this image with the given angle.
+            basic_Image Rotate(Float angle, InterpolationMethod method = InterpolationMethod::Cubic) const {
+                return Rotate(angle, {size.Width/2.f, size.Height/2.f}, method);
+            }
+
+            /// Rotates this image with the given angle.
+            basic_Image Rotate(Float angle, const Geometry::Pointf origin, InterpolationMethod method = InterpolationMethod::Cubic) const {
+                Geometry::Boundsf bnds = {0,0, Geometry::Sizef(size)};
+                Geometry::Rotate(bnds, angle, origin);
+                Geometry::Bounds b{
+                    (int)std::floor(bnds.Left)-1, (int)std::floor(bnds.Top)-1, 
+                    (int)std::ceil(bnds.Right)+1, (int)std::ceil(bnds.Bottom)+1, 
+                };
+
+                auto newsize = b.GetSize();
+
+                basic_Image target(newsize, GetMode());
 
-					auto compress = ReadUInt32(file);
+                Float cosa = std::cos(-angle); //inverse transform
+                Float sina = std::sin(-angle);
 
-					if(compress != 0 && compress != 3) return false;
+                if(method == InterpolationMethod::NearestNeighbor) {
+                    for(int y=0; y<newsize.Height; y++) {
+                        float yn = y - origin.Y + b.Top;
 
-					bool bitcompress = compress != 0;
+                        for(int x=0; x<newsize.Width; x++) {
+                            for(unsigned c=0; c<cpp; c++) {
+                                float xn = x - origin.X + b.Left;
 
-					ReadUInt32(file); //size of bitmap
+                                target(x, y, c) = Get((int)std::round(xn * cosa - yn * sina + origin.X), (int)std::round(xn * sina + yn * cosa + origin.Y), c);
+                            }
+                        }
+                    }
+                }
+                else if(method == InterpolationMethod::Linear) {
+                    for(int y=0; y<newsize.Height; y++) {
+                        float yn = y - origin.Y + b.Top;
 
-					ReadInt32(file); //horz resolution
-					ReadInt32(file); //vert resolution
+                        for(int x=0; x<newsize.Width; x++) {
+                            float xn = x - origin.X + b.Left;
 
-					colorsused = ReadUInt32(file);
-					ReadUInt32(file); //colors important
+                            float xx = xn * cosa - yn * sina + origin.X;
+                            float yy = xn * sina + yn * cosa + origin.Y;
+
+                            int   y1 = (int)std::floor(yy);
+                            float ly  = yy - y1;
+                            float ly1 = 1 - ly;
+                            int   y2 = y1 + 1;
 
-					if(bitcompress && headersize == 40) {
-						redmask = (uint32_t)ReadUInt32(file);
-						greenmask = (uint32_t)ReadUInt32(file);
-						bluemask = (uint32_t)ReadUInt32(file);
-					}
-					else if(bpp == 16) {
-						redmask   = 0x7c00;
-						greenmask = 0x03e0;
-						bluemask  = 0x001f;
-					}
-					else if(bpp == 32) {
-						redmask   = 0x00ff0000;
-						greenmask = 0x0000ff00;
-						bluemask  = 0x000000ff;
-					}
-				}
+                            int   x1 = (int)std::floor(xx);
+                            float lx  = xx - x1;
+                            float lx1 = 1 - lx;
+                            int   x2 = x1 + 1;
+
+                            for(unsigned c=0; c<cpp; c++) {
+                                target(x, y, c) = FixImageValue<T_>::Fix(
+                                    lx1 * ly1 * Get(x1, y1, c) +
+                                    lx  * ly1 * Get(x2, y1, c) +
+                                    lx  * ly  * Get(x2, y2, c) +
+                                    lx1 * ly  * Get(x1, y2, c)
+                                );
+                            }
+                        }
+                    }
+                }
+                else if(method == InterpolationMethod::Cubic) {
+                    int ys[4];
+                    float wy[4], yf[4];
+                    int xs[4];
+                    float wx[4], xf[4];
+                    const float a = -0.5;
+                    for(int y=0; y<newsize.Height; y++) {
+                        float yn = y - origin.Y + b.Top;
+
+                        for(int x=0; x<newsize.Width; x++) {
+                            float xn = x - origin.X + b.Left;
+
+                            float xx = xn * cosa - yn * sina + origin.X;
+                            float yy = xn * sina + yn * cosa + origin.Y;
+
+                            xf[1]  = xx - std::floor(xx);
+                            xf[2] = 1 - xf[1];
+                            xf[3] = 2 - xf[1];
+                            xf[0] = 1 + xf[1];
+
+                            for(int i=0; i<4; i++) {
+                                if(i == 1 || i == 2) {
+                                    wx[i] = (a+2) * xf[i]*xf[i]*xf[i] - (a+3) * xf[i]*xf[i] + 1;
+                                }
+                                else {
+                                    wx[i] = a * xf[i]*xf[i]*xf[i] - 5*a * xf[i]*xf[i] + 8*a * xf[i] - 4*a;
+                                }
+                                xs[i]  = (int)floor(xx)-1 + i;
+                            }
+
+                            yf[1]  = yy - std::floor(yy);
+                            yf[2] = 1 - yf[1];
+                            yf[3] = 2 - yf[1];
+                            yf[0] = 1 + yf[1];
 
-				if(headersize >= 108) {
-					redmask = (uint32_t)ReadUInt32(file);
-					greenmask = (uint32_t)ReadUInt32(file);
-					bluemask = (uint32_t)ReadUInt32(file);
-					alphamask = (uint32_t)ReadUInt32(file);
+                            for(int i=0; i<4; i++) {
+                                if(i == 1 || i == 2) {
+                                    wy[i] = (a+2) * yf[i]*yf[i]*yf[i] - (a+3) * yf[i]*yf[i] + 1;
+                                }
+                                else {
+                                    wy[i] = a * yf[i]*yf[i]*yf[i] - 5*a * yf[i]*yf[i] + 8*a * yf[i] - 4*a;
+                                }
+                                ys[i]  = (int)floor(yy)-1 + i;
+                            }
+
+                            for(unsigned c=0; c<cpp; c++) {
+                                float v = 0;
+                                for(int j=0; j<4; j++) {
+                                    for(int i=0; i<4; i++) {
+                                        v += wx[i] * wy[j] * Get(xs[i], ys[j], c);
+                                    }
+                                }
+
+                                target(x, y, c) = FixImageValue<T_>::Fix(v);
+                            }
+                        }
+                    }
+                }
+                else {
+                    throw std::runtime_error("Unknown interpolation method");
+                }
 
-					file.seekg(13*4, std::ios::cur); //colorspace information
-				}
+                return target;
+            }
+
+            /// Rotates this image with the given angle.
+            basic_Image SkewX(Float perpixel, InterpolationMethod method = InterpolationMethod::Cubic) const {
+                return SkewX(perpixel, {0.f, 0.f}, method);
+            }
 
-				redshift = shiftcalc(redmask);
-				greenshift = shiftcalc(greenmask);
-				blueshift = shiftcalc(bluemask);
-				alphashift = shiftcalc(alphamask);
+            /// Rotates this image with the given angle.
+            basic_Image SkewX(Float perpixel, const Geometry::Pointf origin, InterpolationMethod method = InterpolationMethod::Cubic) const {
+                Geometry::Boundsf bnds = {0,0, Geometry::Sizef(size)};
+                Geometry::SkewX(bnds, perpixel, origin);
+                Geometry::Bounds b{
+                    (int)std::floor(bnds.Left)-1, (int)std::floor(bnds.Top)-1, 
+                    (int)std::ceil(bnds.Right)+1, (int)std::ceil(bnds.Bottom)+1, 
+                };
+
+                auto newsize = b.GetSize();
+
+                basic_Image target(newsize, GetMode());
+
+                if(method == InterpolationMethod::NearestNeighbor) {
+                    for(int y=0; y<newsize.Height; y++) {
+                        float yn = y - origin.Y + b.Top;
+
+                        for(int x=0; x<newsize.Width; x++) {
+                            for(unsigned c=0; c<cpp; c++) {
+                                float xn = x + b.Left;
 
-				if(redmask) {
-					redmult = 255.f / (redmask>>redshift);
-				}
-				if(greenmask) {
-					greenmult = 255.f / (greenmask>>greenshift);
-				}
-				if(bluemask) {
-					bluemult = 255.f / (bluemask>>blueshift);
-				}
-				if(alphamask) {
-					alphamult = 255.f / (alphamask>>alphashift);
-				}
+                                target(x, y, c) = Get((int)std::round(xn - yn * perpixel), y, c);
+                            }
+                        }
+                    }
+                }
+                else if(method == InterpolationMethod::Linear) {
+                    for(int y=0; y<newsize.Height; y++) {
+                        float yn = y - origin.Y + b.Top;
+
+                        for(int x=0; x<newsize.Width; x++) {
+                            float xn = x + b.Left;
+
+                            float xx = xn - yn * perpixel;
+
+                            int   x1 = (int)std::floor(xx);
+                            float lx  = xx - x1;
+                            float lx1 = 1 - lx;
+                            int   x2 = x1 + 1;
 
-				if(headersize > 108) {
-					file.seekg(headersize - 108, std::ios::cur);
-				}
+                            for(unsigned c=0; c<cpp; c++) {
+                                target(x, y, c) = FixImageValue<T_>::Fix(
+                                    lx1 * Get(x1, y, c) +
+                                    lx  * Get(x2, y, c)
+                                );
+                            }
+                        }
+                    }
+                }
+                else if(method == InterpolationMethod::Cubic) {
+                    int xs[4];
+                    float wx[4], xf[4];
+                    const float a = -0.5;
+                    for(int y=0; y<newsize.Height; y++) {
+                        float yn = y - origin.Y + b.Top;
 
-				if(height > 0)
-					upsidedown = true;
-				else
-					height *= -1;
+                        for(int x=0; x<newsize.Width; x++) {
+                            float xn = x + b.Left;
+
+                            float xx = xn - yn * perpixel;
+
+                            xf[1]  = xx - std::floor(xx);
+                            xf[2] = 1 - xf[1];
+                            xf[3] = 2 - xf[1];
+                            xf[0] = 1 + xf[1];
 
-				if(bpp <= 8) { //paletted
-					if(colorsused == 0)
-						colorsused = 1 << bpp;
+                            for(int i=0; i<4; i++) {
+                                if(i == 1 || i == 2) {
+                                    wx[i] = (a+2) * xf[i]*xf[i]*xf[i] - (a+3) * xf[i]*xf[i] + 1;
+                                }
+                                else {
+                                    wx[i] = a * xf[i]*xf[i]*xf[i] - 5*a * xf[i]*xf[i] + 8*a * xf[i] - 4*a;
+                                }
+                                xs[i]  = (int)floor(xx)-1 + i;
+                            }
 
-					palette.reserve(colorsused);
+                            for(unsigned c=0; c<cpp; c++) {
+                                float v = 0;
+                                
+                                for(int i=0; i<4; i++) {
+                                    v += wx[i] * Get(xs[i], y, c);
+                                }
+                                target(x, y, c) = FixImageValue<T_>::Fix(v);
+                            }
+                        }
+                    }
+                }
+                else {
+                    throw std::runtime_error("Unknown interpolation method");
+                }
 
-					for(int i=0; i<colorsused; i++) {
-						Byte r, g, b, a = 255;
-						
-						b = ReadUInt8(file);
-						g = ReadUInt8(file);
-						r = ReadUInt8(file);
+                return target;
+            }
 
-						if(r!=b || b!=g) grayscalepalette = false;
+            /// Rotates this image with the given angle.
+            basic_Image SkewY(Float perpixel, InterpolationMethod method = InterpolationMethod::Cubic) const {
+                return SkewY(perpixel, {0.f, 0.f}, method);
+            }
 
-						if(headersize > 12) {
-							a = ReadUInt8(file); //reserved
-						}
+            /// Rotates this image with the given angle.
+            basic_Image SkewY(Float perpixel, const Geometry::Pointf origin, InterpolationMethod method = InterpolationMethod::Cubic) const {
+                Geometry::Boundsf bnds = {0,0, Geometry::Sizef(size)};
+                Geometry::SkewY(bnds, perpixel, origin);
+                Geometry::Bounds b{
+                    (int)std::floor(bnds.Left)-1, (int)std::floor(bnds.Top)-1, 
+                    (int)std::ceil(bnds.Right)+1, (int)std::ceil(bnds.Bottom)+1, 
+                };
 
-						palette.emplace_back(r, g, b, a);
-					}
+                auto newsize = b.GetSize();
+
+                basic_Image target(newsize, GetMode());
+
+                if(method == InterpolationMethod::NearestNeighbor) {
+                    for(int x=0; x<newsize.Width; x++) {
+                        float xn = x - origin.X + b.Left;
 
-					for(int i=0; i<colorsused; i++) {
-						if(palette[i].A) {
-							alpha = true;
-							break;
-						}
-					}
-				}
+                        for(int y=0; y<newsize.Height; y++) {
+                            for(unsigned c=0; c<cpp; c++) {
+                                float yn = y + b.Top;
 
-				if(!dib)
-					file.seekg(off, std::ios::beg);
+                                target(x, y, c) = Get(x, (int)std::round(yn - xn * perpixel), c);
+                            }
+                        }
+                    }
+                }
+                else if(method == InterpolationMethod::Linear) {
+                    for(int x=0; x<newsize.Width; x++) {
+                        float xn = x - origin.X + b.Left;
+
+                        for(int y=0; y<newsize.Height; y++) {
+                            float yn = y + b.Top;
+
+                            float yy = yn - xn * perpixel;
+
+                            int   y1 = (int)std::floor(yy);
+                            float ly  = yy - y1;
+                            float ly1 = 1 - ly;
+                            int   y2 = y1 + 1;
 
-				if((bpp == 32 || bpp == 16) && alphamask != 0) {
-					Resize({width, height}, Graphics::ColorMode::RGBA);
-					alpha = true;
-				}
-				else if(bpp <= 8 && grayscalepalette) {
-					if(alpha)
-						Resize({width, height}, Graphics::ColorMode::Grayscale_Alpha);
-					else 
-						Resize({width, height}, Graphics::ColorMode::Grayscale);
-				}
-				else if(alpha) {
-					if(redmask == 0 && greenmask == 0 && bluemask == 0) {
-						Resize({width, height}, Graphics::ColorMode::Alpha);
-					}
-					else {
-						Resize({width, height}, Graphics::ColorMode::RGBA);
-					}
-				}
-				else {
-					Resize({width, height}, Graphics::ColorMode::RGB);
-				}
+                            for(unsigned c=0; c<cpp; c++) {
+                                target(x, y, c) = FixImageValue<T_>::Fix(
+                                    ly1 * Get(x, y1, c) +
+                                    ly  * Get(x, y2, c)
+                                );
+                            }
+                        }
+                    }
+                }
+                else if(method == InterpolationMethod::Cubic) {
+                    int ys[4];
+                    float wy[4], yf[4];
+                    const float a = -0.5;
+                    for(int x=0; x<newsize.Width; x++) {
+                        float xn = x - origin.X + b.Left;
+
+                        for(int y=0; y<newsize.Height; y++) {
+                            float yn = y + b.Top;
+
+                            float yy = yn - xn * perpixel;
+
+                            yf[1]  = yy - std::floor(yy);
+                            yf[2] = 1 - yf[1];
+                            yf[3] = 2 - yf[1];
+                            yf[0] = 1 + yf[1];
+
+                            for(int i=0; i<4; i++) {
+                                if(i == 1 || i == 2) {
+                                    wy[i] = (a+2) * yf[i]*yf[i]*yf[i] - (a+3) * yf[i]*yf[i] + 1;
+                                }
+                                else {
+                                    wy[i] = a * yf[i]*yf[i]*yf[i] - 5*a * yf[i]*yf[i] + 8*a * yf[i] - 4*a;
+                                }
+                                ys[i]  = (int)floor(yy)-1 + i;
+                            }
+
+                            for(unsigned c=0; c<cpp; c++) {
+                                float v = 0;
+                                
+                                for(int i=0; i<4; i++) {
+                                    v += wy[i] * Get(x, ys[i], c);
+                                }
+                                target(x, y, c) = FixImageValue<T_>::Fix(v);
+                            }
+                        }
+                    }
+                }
+                else {
+                    throw std::runtime_error("Unknown interpolation method");
+                }
 
-				int ys, ye, yc;
+                return target;
+            }
+            
+            /// Mirrors this bitmap along X axis as a new one.
+            basic_Image MirrorX() const {
+                basic_Image target(size, GetMode());
+                int yy = size.Height - 1;
+                for(int y=0; y<size.Height; y++) {
+                    for(int x=0; x<size.Width; x++) {
+                        for(int c=0; c<cpp; c++) {
+                            target(x, y, c) = operator()(x, yy, c);
+                        }
+                    }
+                    
+                    yy--;
+                }
+                
+                return target;
+            }
+            
+            /// Mirrors this bitmap along Y axis as a new one.
+            basic_Image MirrorY() const {
+                basic_Image target(size, GetMode());
+                int xx = size.Width- 1;
+                for(int x=0; x<size.Width; x++) {
+                    for(int y=0; y<size.Height; y++) {
+                        for(int c=0; c<cpp; c++) {
+                            target(x, y, c) = operator()(xx, y, c);
+                        }
+                    }
+                    
+                    xx--;
+                }
+                
+                return target;
+            }
 
-				if(upsidedown) {
-					ys = height-1;
-					ye = -1;
-					yc = -1;
-				}
-				else {
-					ys = 0;
-					ye = height;
-					yc = 1;
-				}
+            /// Flips this bitmap along X axis as a new one.
+            basic_Image FlipX() const {
+                return MirrorY();
+            }
+            
+            /// Flips this bitmap along Y axis as a new one.
+            basic_Image FlipY() const {
+                return MirrorX();
+            }
+
+            /// Imports a given bitmap file. BMP RLE compression and colorspaces are not supported.
+            bool ImportBMP(const std::string &filename, bool dib = false) {
+                std::ifstream file(filename, std::ios::binary);
 
-				if(bpp == 24) {
-					for(int y = ys; y!=ye; y += yc) {
-						int bytes = 0;
-						for(int x=0; x<width; x++) {
-							this->operator ()({x, y}, 0) = ReadUInt8(file);
-							this->operator ()({x, y}, 1) = ReadUInt8(file);
-							this->operator ()({x, y}, 2) = ReadUInt8(file);
+                if(!file.is_open()) return false;
+
+                return ImportBMP(file, dib);
+            }
 
-							bytes += 3;
-						}
+            /// Imports a given bitmap file. BMP RLE compression and colorspaces are not supported.
+            bool ImportBMP(std::istream &file, bool dib = false) {
+                using namespace IO;
+
+                unsigned long off = 0;
+
+                if(!dib) {
+                    if(ReadString(file, 2) != "BM") return false;
 
-						if(bytes%4) {
-							file.seekg(4-bytes%4, std::ios::cur);
-						}
-					}
-				}
-				else if(bpp == 16 || bpp == 32) {
-					for(int y = ys; y!=ye; y += yc) {
-						int bytes = 0;
-						for(int x=0; x<width; x++) {
-							uint32_t pix;
-							
-							if(bpp == 16)
-								pix = ReadUInt16(file);
-							else
-								pix = ReadUInt32(file);
+                    IO::ReadUInt32(file);
+
+                    ReadUInt16(file); //reserved 1
+                    ReadUInt16(file); //reserved 2
+
+                    off = ReadUInt32(file);
+                }
+
+                auto headersize = IO::ReadUInt32(file);
+
+                int width = 0, height = 0;
+                int bpp = 0;
+                bool upsidedown = false;
+                bool grayscalepalette = true;
+                int colorsused = 0;
+                bool alpha = false;
+                int redshift, greenshift, blueshift, alphashift;
+                float redmult = 1, greenmult = 1, bluemult = 1, alphamult = 1;
+                uint32_t redmask = 0, greenmask = 0, bluemask = 0, alphamask = 0;
+
+                std::vector<Graphics::RGBA> palette;
 
-							if(redmask != 0 || greenmask != 0 || bluemask != 0) {
-								this->operator ()({x, y}, 0) = (Byte)std::round(((pix&redmask)>>redshift) * redmult);
-								this->operator ()({x, y}, 1) = (Byte)std::round(((pix&greenmask)>>greenshift) * greenmult);
-								this->operator ()({x, y}, 2) = (Byte)std::round(((pix&bluemask)>>blueshift) * bluemult);
-							}
+                auto shiftcalc=[](uint32_t v) {
+                    int pos = 0;
+                    while(v) {
+                        if(v&1) return pos;
+                        v=v>>1;
+                        pos++;
+                    }
+
+                    return pos;
+                };
+
+                if(headersize == 12) { //2x bmp
+                    width = ReadInt16(file);
+                    height = ReadInt16(file);
+                    ReadUInt16(file); //planes
+                    bpp = ReadUInt16(file);
+                }
+
+                if(headersize >= 40) { //3x
+                    width = ReadInt32(file);
+                    height = ReadInt32(file);
+                    ReadUInt16(file); //planes
+                    bpp = ReadUInt16(file);
+
+                    auto compress = ReadUInt32(file);
+
+                    if(compress != 0 && compress != 3) return false;
+
+                    bool bitcompress = compress != 0;
+
+                    ReadUInt32(file); //size of bitmap
+
+                    ReadInt32(file); //horz resolution
+                    ReadInt32(file); //vert resolution
 
-							if(alpha) {
-								if(redmask != 0 || greenmask != 0 || bluemask != 0) {
-									this->operator ()({x, y}, 3) = (Byte)std::round(((pix&alphamask)>>alphashift) * alphamult);
-								}
-								else {
-									this->operator ()({x, y}, 0) = (Byte)std::round(((pix&alphamask)>>alphashift) * alphamult);
-								}
-							}
-							
-							bytes += bpp/8;
-						}
+                    colorsused = ReadUInt32(file);
+                    ReadUInt32(file); //colors important
+
+                    if(bitcompress && headersize == 40) {
+                        redmask = (uint32_t)ReadUInt32(file);
+                        greenmask = (uint32_t)ReadUInt32(file);
+                        bluemask = (uint32_t)ReadUInt32(file);
+                    }
+                    else if(bpp == 16) {
+                        redmask   = 0x7c00;
+                        greenmask = 0x03e0;
+                        bluemask  = 0x001f;
+                    }
+                    else if(bpp == 32) {
+                        redmask   = 0x00ff0000;
+                        greenmask = 0x0000ff00;
+                        bluemask  = 0x000000ff;
+                    }
+                }
+
+                if(headersize >= 108) {
+                    redmask = (uint32_t)ReadUInt32(file);
+                    greenmask = (uint32_t)ReadUInt32(file);
+                    bluemask = (uint32_t)ReadUInt32(file);
+                    alphamask = (uint32_t)ReadUInt32(file);
+
+                    file.seekg(13*4, std::ios::cur); //colorspace information
+                }
 
-						if(bytes%4) {
-							file.seekg(4-bytes%4, std::ios::cur);
-						}
-					}
-				}
-				else if(bpp < 8) {
-					Byte bitmask = (1 << bpp) - 1;
-					bitmask = bitmask << (8-bpp);
-					for(int y = ys; y!=ye; y += yc) {
-						int bytes = 0;
-						int bits  = 0;
-						Byte v = 0;
-						for(int x=0; x<width; x++) {
-							int ind;
-							if(bits == 0) {
-								v = ReadUInt8(file);
-								bits = 8;
-								bytes++;
-							}
+                redshift = shiftcalc(redmask);
+                greenshift = shiftcalc(greenmask);
+                blueshift = shiftcalc(bluemask);
+                alphashift = shiftcalc(alphamask);
+
+                if(redmask) {
+                    redmult = 255.f / (redmask>>redshift);
+                }
+                if(greenmask) {
+                    greenmult = 255.f / (greenmask>>greenshift);
+                }
+                if(bluemask) {
+                    bluemult = 255.f / (bluemask>>blueshift);
+                }
+                if(alphamask) {
+                    alphamult = 255.f / (alphamask>>alphashift);
+                }
+
+                if(headersize > 108) {
+                    file.seekg(headersize - 108, std::ios::cur);
+                }
+
+                if(height > 0)
+                    upsidedown = true;
+                else
+                    height *= -1;
+
+                if(bpp <= 8) { //paletted
+                    if(colorsused == 0)
+                        colorsused = 1 << bpp;
 
-							ind = (v&bitmask)>>(8-bpp);
-							if(ind >= colorsused)
-								continue;
+                    palette.reserve(colorsused);
+
+                    for(int i=0; i<colorsused; i++) {
+                        Byte r, g, b, a = 255;
+                        
+                        b = ReadUInt8(file);
+                        g = ReadUInt8(file);
+                        r = ReadUInt8(file);
+
+                        if(r!=b || b!=g) grayscalepalette = false;
 
-							auto col = palette[ind];
-							
-							if(grayscalepalette) {
-								this->operator ()({x, y}, 0) = col.R;
+                        if(headersize > 12) {
+                            a = ReadUInt8(file); //reserved
+                        }
+
+                        palette.emplace_back(r, g, b, a);
+                    }
+
+                    for(int i=0; i<colorsused; i++) {
+                        if(palette[i].A) {
+                            alpha = true;
+                            break;
+                        }
+                    }
+                }
 
-								if(alpha)
-									this->operator ()({x, y}, 1) = col.A;
-							}
-							else {
-								this->operator ()({x, y}, 0) = col.R;
-								this->operator ()({x, y}, 1) = col.G;
-								this->operator ()({x, y}, 2) = col.B;
-								
+                if(!dib)
+                    file.seekg(off, std::ios::beg);
 
-								if(alpha) {
-									this->operator ()({x, y}, 3) = col.A;
-								}
-							}
+                if((bpp == 32 || bpp == 16) && alphamask != 0) {
+                    Resize({width, height}, Graphics::ColorMode::RGBA);
+                    alpha = true;
+                }
+                else if(bpp <= 8 && grayscalepalette) {
+                    if(alpha)
+                        Resize({width, height}, Graphics::ColorMode::Grayscale_Alpha);
+                    else 
+                        Resize({width, height}, Graphics::ColorMode::Grayscale);
+                }
+                else if(alpha) {
+                    if(redmask == 0 && greenmask == 0 && bluemask == 0) {
+                        Resize({width, height}, Graphics::ColorMode::Alpha);
+                    }
+                    else {
+                        Resize({width, height}, Graphics::ColorMode::RGBA);
+                    }
+                }
+                else {
+                    Resize({width, height}, Graphics::ColorMode::RGB);
+                }
+
+                int ys, ye, yc;
 
-							v = v<<bpp;
-							bits -= bpp;
-						}
+                if(upsidedown) {
+                    ys = height-1;
+                    ye = -1;
+                    yc = -1;
+                }
+                else {
+                    ys = 0;
+                    ye = height;
+                    yc = 1;
+                }
 
-						if(bytes%4) {
-							file.seekg(4-bytes%4, std::ios::cur);
-						}
-					}
-				}
+                if(bpp == 24) {
+                    for(int y = ys; y!=ye; y += yc) {
+                        int bytes = 0;
+                        for(int x=0; x<width; x++) {
+                            this->operator ()({x, y}, 0) = ReadUInt8(file);
+                            this->operator ()({x, y}, 1) = ReadUInt8(file);
+                            this->operator ()({x, y}, 2) = ReadUInt8(file);
+
+                            bytes += 3;
+                        }
 
-				return true;
-			}
+                        if(bytes%4) {
+                            file.seekg(4-bytes%4, std::ios::cur);
+                        }
+                    }
+                }
+                else if(bpp == 16 || bpp == 32) {
+                    for(int y = ys; y!=ye; y += yc) {
+                        int bytes = 0;
+                        for(int x=0; x<width; x++) {
+                            uint32_t pix;
+                            
+                            if(bpp == 16)
+                                pix = ReadUInt16(file);
+                            else
+                                pix = ReadUInt32(file);
 
-			/// Exports the image as a bitmap. RGB is exported as 24-bit, RGBA, BGR, BGRA is exported
-			/// as 32-bit, Grayscale exported as 8-bit, Grayscale alpha, alpha only is exported as
-			/// 16-bit
-			bool ExportBMP(const std::string &filename, bool usev4 = false, bool dib = false) {
-				std::ofstream file(filename, std::ios::binary);
+                            if(redmask != 0 || greenmask != 0 || bluemask != 0) {
+                                this->operator ()({x, y}, 0) = (Byte)std::round(((pix&redmask)>>redshift) * redmult);
+                                this->operator ()({x, y}, 1) = (Byte)std::round(((pix&greenmask)>>greenshift) * greenmult);
+                                this->operator ()({x, y}, 2) = (Byte)std::round(((pix&bluemask)>>blueshift) * bluemult);
+                            }
 
-				if(!file.is_open()) return false;
-
-				return ExportBMP(file, usev4, dib);
-			}
+                            if(alpha) {
+                                if(redmask != 0 || greenmask != 0 || bluemask != 0) {
+                                    this->operator ()({x, y}, 3) = (Byte)std::round(((pix&alphamask)>>alphashift) * alphamult);
+                                }
+                                else {
+                                    this->operator ()({x, y}, 0) = (Byte)std::round(((pix&alphamask)>>alphashift) * alphamult);
+                                }
+                            }
+                            
+                            bytes += bpp/8;
+                        }
 
-			/// Exports the image as a bitmap. RGB is exported as 24-bit, RGBA, BGR, BGRA is exported
-			/// as 32-bit, Grayscale exported as 8-bit, Grayscale alpha, alpha only is exported as
-			/// 16-bit
-			bool ExportBMP(std::ostream &file, bool usev4 = false, bool dib = false) {
-				using namespace IO;
-				using Graphics::ColorMode;
+                        if(bytes%4) {
+                            file.seekg(4-bytes%4, std::ios::cur);
+                        }
+                    }
+                }
+                else if(bpp < 8) {
+                    Byte bitmask = (1 << bpp) - 1;
+                    bitmask = bitmask << (8-bpp);
+                    for(int y = ys; y!=ye; y += yc) {
+                        int bytes = 0;
+                        int bits  = 0;
+                        Byte v = 0;
+                        for(int x=0; x<width; x++) {
+                            int ind;
+                            if(bits == 0) {
+                                v = ReadUInt8(file);
+                                bits = 8;
+                                bytes++;
+                            }
+
+                            ind = (v&bitmask)>>(8-bpp);
+                            if(ind >= colorsused)
+                                continue;
+
+                            auto col = palette[ind];
+                            
+                            if(grayscalepalette) {
+                                this->operator ()({x, y}, 0) = col.R;
 
-				long datasize = 0;
-				long headersize = 0;
-				long extraspace = 0;
-				int bpp = 0;
-				int compression = 0;
-				int stride = 0;
+                                if(alpha)
+                                    this->operator ()({x, y}, 1) = col.A;
+                            }
+                            else {
+                                this->operator ()({x, y}, 0) = col.R;
+                                this->operator ()({x, y}, 1) = col.G;
+                                this->operator ()({x, y}, 2) = col.B;
+                                
+
+                                if(alpha) {
+                                    this->operator ()({x, y}, 3) = col.A;
+                                }
+                            }
 
-				switch(mode) {
-				case ColorMode::RGB:
-				case ColorMode::BGR:
-					stride = 3 * size.Width;
-					if(stride%4) stride += (4-(stride%4));
+                            v = v<<bpp;
+                            bits -= bpp;
+                        }
 
-					datasize = stride * size.Height;
+                        if(bytes%4) {
+                            file.seekg(4-bytes%4, std::ios::cur);
+                        }
+                    }
+                }
 
-					headersize = 40;
-					bpp = 24;
-					break;
+                return true;
+            }
+
+            /// Exports the image as a bitmap. RGB is exported as 24-bit, RGBA, BGR, BGRA is exported
+            /// as 32-bit, Grayscale exported as 8-bit, Grayscale alpha, alpha only is exported as
+            /// 16-bit
+            bool ExportBMP(const std::string &filename, bool usev4 = false, bool dib = false) {
+                std::ofstream file(filename, std::ios::binary);
 
-				case ColorMode::RGBA:
-				case ColorMode::BGRA:
-				case ColorMode::Grayscale_Alpha:
-					stride = 4 * size.Width;
-					if(stride%4) stride += (4-(stride%4));
+                if(!file.is_open()) return false;
+
+                return ExportBMP(file, usev4, dib);
+            }
+
+            /// Exports the image as a bitmap. RGB is exported as 24-bit, RGBA, BGR, BGRA is exported
+            /// as 32-bit, Grayscale exported as 8-bit, Grayscale alpha, alpha only is exported as
+            /// 16-bit
+            bool ExportBMP(std::ostream &file, bool usev4 = false, bool dib = false) {
+                using namespace IO;
+                using Graphics::ColorMode;
 
-					datasize = stride * size.Height;
+                long datasize = 0;
+                long headersize = 0;
+                long extraspace = 0;
+                int bpp = 0;
+                int compression = 0;
+                int stride = 0;
 
-					headersize = 108;
-					bpp = 32;
-					compression = 3;
-					break;
+                switch(mode) {
+                case ColorMode::RGB:
+                case ColorMode::BGR:
+                    stride = 3 * size.Width;
+                    if(stride%4) stride += (4-(stride%4));
+
+                    datasize = stride * size.Height;
+
+                    headersize = 40;
+                    bpp = 24;
+                    break;
 
-				case ColorMode::Grayscale:
-					stride = size.Width;
-					if(stride%4) stride += (4-(stride%4));
+                case ColorMode::RGBA:
+                case ColorMode::BGRA:
+                case ColorMode::Grayscale_Alpha:
+                    stride = 4 * size.Width;
+                    if(stride%4) stride += (4-(stride%4));
 
-					datasize = stride * size.Height;
+                    datasize = stride * size.Height;
+
+                    headersize = 108;
+                    bpp = 32;
+                    compression = 3;
+                    break;
 
-					headersize = 40;
-					extraspace = 256 * 4; //palette
-					bpp = 8;
-					break;
+                case ColorMode::Grayscale:
+                    stride = size.Width;
+                    if(stride%4) stride += (4-(stride%4));
+
+                    datasize = stride * size.Height;
 
-				case ColorMode::Alpha:
-					stride = 2 * size.Width;
-					if(stride%4) stride += (4-(stride%4));
+                    headersize = 40;
+                    extraspace = 256 * 4; //palette
+                    bpp = 8;
+                    break;
 
-					datasize = stride * size.Height;
+                case ColorMode::Alpha:
+                    stride = 2 * size.Width;
+                    if(stride%4) stride += (4-(stride%4));
 
-					headersize = 108;
+                    datasize = stride * size.Height;
+
+                    headersize = 108;
 
                     compression = 3;
-					bpp = 16;
-					break;
+                    bpp = 16;
+                    break;
 
-				default:
-					throw std::runtime_error("Unsupported color mode");
-				}
+                default:
+                    throw std::runtime_error("Unsupported color mode");
+                }
 
-				
-				//header
-				if(!dib) {
-					WriteString(file, "BM");
-					WriteUInt32(file, 14 + headersize + extraspace + datasize);
-					WriteUInt16(file, 0); //reserved 1
-					WriteUInt16(file, 0); //reserved 2
-					WriteUInt32(file, 14 + headersize + extraspace);
-				}
+                
+                //header
+                if(!dib) {
+                    WriteString(file, "BM");
+                    WriteUInt32(file, 14 + headersize + extraspace + datasize);
+                    WriteUInt16(file, 0); //reserved 1
+                    WriteUInt16(file, 0); //reserved 2
+                    WriteUInt32(file, 14 + headersize + extraspace);
+                }
 
-				WriteUInt32(file, headersize);
-				WriteInt32 (file, size.Width);
-				WriteInt32 (file, size.Height);
-				WriteUInt16(file, 1);
-				WriteUInt16(file, bpp);
-				WriteUInt32(file, compression);
-				WriteUInt32(file, datasize);
-				WriteInt32 (file, 2834);
-				WriteInt32 (file, 2834);
-				WriteInt32(file, 0); //colors used
-				WriteInt32(file, 0); //colors important
+                WriteUInt32(file, headersize);
+                WriteInt32 (file, size.Width);
+                WriteInt32 (file, size.Height);
+                WriteUInt16(file, 1);
+                WriteUInt16(file, bpp);
+                WriteUInt32(file, compression);
+                WriteUInt32(file, datasize);
+                WriteInt32 (file, 2834);
+                WriteInt32 (file, 2834);
+                WriteInt32(file, 0); //colors used
+                WriteInt32(file, 0); //colors important
 
-				if(compression == 3) {
-					if(mode == ColorMode::Alpha) {
-						WriteUInt32(file, 0x00000001);
-						WriteUInt32(file, 0x00000002);
-						WriteUInt32(file, 0x00000004);
-					}
-					else if(mode == ColorMode::BGRA) {
-						WriteUInt32(file, 0x00ff0000);
-						WriteUInt32(file, 0x0000ff00);
-						WriteUInt32(file, 0x000000ff);
-					}
-					else {	
-						WriteUInt32(file, 0x000000ff);
-						WriteUInt32(file, 0x0000ff00);
-						WriteUInt32(file, 0x00ff0000);
-					}
-				}
+                if(compression == 3) {
+                    if(mode == ColorMode::Alpha) {
+                        WriteUInt32(file, 0x00000001);
+                        WriteUInt32(file, 0x00000002);
+                        WriteUInt32(file, 0x00000004);
+                    }
+                    else if(mode == ColorMode::BGRA) {
+                        WriteUInt32(file, 0x00ff0000);
+                        WriteUInt32(file, 0x0000ff00);
+                        WriteUInt32(file, 0x000000ff);
+                    }
+                    else {	
+                        WriteUInt32(file, 0x000000ff);
+                        WriteUInt32(file, 0x0000ff00);
+                        WriteUInt32(file, 0x00ff0000);
+                    }
+                }
 
-				if(headersize == 108 || usev4) {
-					if(compression != 3) {
-						WriteUInt32(file, 0x00000001);
-						WriteUInt32(file, 0x00000002);
-						WriteUInt32(file, 0x00000004);
-					}
-					if(mode == ColorMode::Alpha) {
-						WriteUInt32(file, 0x0000ff00);
-					}
-					else {
-						WriteUInt32(file, 0xff000000);
-					}
-					WriteUInt32(file, 1); //device dependent RGB
-					for(int i=0; i<12; i++)  //color profile settings, all 0
-						WriteUInt32(file, 0);
-				}
+                if(headersize == 108 || usev4) {
+                    if(compression != 3) {
+                        WriteUInt32(file, 0x00000001);
+                        WriteUInt32(file, 0x00000002);
+                        WriteUInt32(file, 0x00000004);
+                    }
+                    if(mode == ColorMode::Alpha) {
+                        WriteUInt32(file, 0x0000ff00);
+                    }
+                    else {
+                        WriteUInt32(file, 0xff000000);
+                    }
+                    WriteUInt32(file, 1); //device dependent RGB
+                    for(int i=0; i<12; i++)  //color profile settings, all 0
+                        WriteUInt32(file, 0);
+                }
 
-				int ostride;
-				switch(mode) {
-					case ColorMode::RGB:
-						ostride = size.Width*3;
-						for(int y=size.Height-1; y>=0; y--) {
-							WriteArray(file, data+y*ostride, ostride);
+                int ostride;
+                switch(mode) {
+                    case ColorMode::RGB:
+                        ostride = size.Width*3;
+                        for(int y=size.Height-1; y>=0; y--) {
+                            WriteArray(file, data+y*ostride, ostride);
 
-							for(int j=0; j<stride-ostride; j++)
-								WriteUInt8(file, 0);
-						}
-						break;
+                            for(int j=0; j<stride-ostride; j++)
+                                WriteUInt8(file, 0);
+                        }
+                        break;
 
-					case ColorMode::BGR:
-						ostride = size.Width*3;
+                    case ColorMode::BGR:
+                        ostride = size.Width*3;
 
-						for(int y=size.Height-1; y>=0; y--) {
-							WriteArray(file, data+y*ostride, ostride);
+                        for(int y=size.Height-1; y>=0; y--) {
+                            WriteArray(file, data+y*ostride, ostride);
 
-							for(int j=0; j<stride-ostride; j++)
-								WriteUInt8(file, 0);
-						}
-						break;
+                            for(int j=0; j<stride-ostride; j++)
+                                WriteUInt8(file, 0);
+                        }
+                        break;
 
 
-					case ColorMode::RGBA:
-					case ColorMode::BGRA:
-						ostride = size.Width*4;
-						for(int y=size.Height-1; y>=0; y--) {
-							WriteArray(file, data+y*ostride, ostride);
-						}
-						break;
+                    case ColorMode::RGBA:
+                    case ColorMode::BGRA:
+                        ostride = size.Width*4;
+                        for(int y=size.Height-1; y>=0; y--) {
+                            WriteArray(file, data+y*ostride, ostride);
+                        }
+                        break;
 
-					case ColorMode::Grayscale:
-						//write palette
-						for(int i=0; i<256; i++) {
-							WriteUInt8(file, (Byte)i);
-							WriteUInt8(file, (Byte)i);
-							WriteUInt8(file, (Byte)i);
-							WriteUInt8(file, 0);
-						}
+                    case ColorMode::Grayscale:
+                        //write palette
+                        for(int i=0; i<256; i++) {
+                            WriteUInt8(file, (Byte)i);
+                            WriteUInt8(file, (Byte)i);
+                            WriteUInt8(file, (Byte)i);
+                            WriteUInt8(file, 0);
+                        }
 
-						//write data
-						ostride = size.Width;
-						for(int y=size.Height-1; y>=0; y--) {
-							WriteArray(file, data+y*ostride, ostride);
+                        //write data
+                        ostride = size.Width;
+                        for(int y=size.Height-1; y>=0; y--) {
+                            WriteArray(file, data+y*ostride, ostride);
 
-							for(int j=0; j<stride-ostride; j++)
-								WriteUInt8(file, 0);
-						}
+                            for(int j=0; j<stride-ostride; j++)
+                                WriteUInt8(file, 0);
+                        }
 
 
-						break;
-						
-					
-					case ColorMode::Grayscale_Alpha:
-						ostride = size.Width*2;
+                        break;
+                        
+                    
+                    case ColorMode::Grayscale_Alpha:
+                        ostride = size.Width*2;
 
-						for(int y=size.Height-1; y>=0; y--) {
-							for(int x=0; x<size.Width; x++) {
-								WriteUInt8(file, data[y*ostride+x*2]);
-								WriteUInt8(file, data[y*ostride+x*2]);
-								WriteUInt8(file, data[y*ostride+x*2]);
-								WriteUInt8(file, data[y*ostride+x*2+1]);
-							}
-						}
-						break;
+                        for(int y=size.Height-1; y>=0; y--) {
+                            for(int x=0; x<size.Width; x++) {
+                                WriteUInt8(file, data[y*ostride+x*2]);
+                                WriteUInt8(file, data[y*ostride+x*2]);
+                                WriteUInt8(file, data[y*ostride+x*2]);
+                                WriteUInt8(file, data[y*ostride+x*2+1]);
+                            }
+                        }
+                        break;
 
 
-					case ColorMode::Alpha:
-						ostride = size.Width;
+                    case ColorMode::Alpha:
+                        ostride = size.Width;
 
-						for(int y=size.Height-1; y>=0; y--) {
-							for(int x=0; x<size.Width; x++) {
-								WriteUInt8(file, 0xff);
-								WriteUInt8(file, data[y*ostride+x]);
-							}
+                        for(int y=size.Height-1; y>=0; y--) {
+                            for(int x=0; x<size.Width; x++) {
+                                WriteUInt8(file, 0xff);
+                                WriteUInt8(file, data[y*ostride+x]);
+                            }
 
-							for(int j=0; j<stride-ostride*2; j++)
-								WriteUInt8(file, 0);
-						}
-						break;
+                            for(int j=0; j<stride-ostride*2; j++)
+                                WriteUInt8(file, 0);
+                        }
+                        break;
                     
                     default:
                         throw std::runtime_error("Invalid mode");
-				}
+                }
 
-				return true;
-			}
+                return true;
+            }
 
-			/// Provides access to the given component in x and y coordinates. This
-			/// function performs bounds checking only on debug mode.
-			Byte &operator()(const Geometry::Point &p, unsigned component=0) {
+            /// Provides access to the given component in x and y coordinates. This
+            /// function performs bounds checking only on debug mode.
+            Byte &operator()(const Geometry::Point &p, unsigned component=0) {
 #ifndef NDEBUG
-				if(p.X<0 || p.Y<0 || p.X>=size.Width || p.Y>=size.Height || component>=cpp) {
-					throw std::runtime_error("Index out of bounds");
-				}
+                if(p.X<0 || p.Y<0 || p.X>=size.Width || p.Y>=size.Height || component>=cpp) {
+                    throw std::runtime_error("Index out of bounds");
+                }
 #endif
-				return data[cpp*(size.Width*p.Y+p.X)+component];
-			}
+                return data[cpp*(size.Width*p.Y+p.X)+component];
+            }
 
-			/// Provides access to the given component in x and y coordinates. This
-			/// function performs bounds checking only on debug mode.
-			Byte operator()(const Geometry::Point &p, unsigned component=0) const {
+            /// Provides access to the given component in x and y coordinates. This
+            /// function performs bounds checking only on debug mode.
+            Byte operator()(const Geometry::Point &p, unsigned component=0) const {
 #ifndef NDEBUG
-				if(p.X<0 || p.Y<0 || p.X>=size.Width || p.Y>=size.Height || component>=cpp) {
-					throw std::runtime_error("Index out of bounds");
-				}
+                if(p.X<0 || p.Y<0 || p.X>=size.Width || p.Y>=size.Height || component>=cpp) {
+                    throw std::runtime_error("Index out of bounds");
+                }
 #endif
-				return data[cpp*(size.Width*p.Y+p.X)+component];
-			}
+                return data[cpp*(size.Width*p.Y+p.X)+component];
+            }
 
-			/// Provides access to the given component in x and y coordinates. This
-			/// function returns 0 if the given coordinates are out of bounds. This
-			/// function works slower than the () operator.
-			Byte Get(const Geometry::Point &p, unsigned component = 0) const {
-				if (p.X < 0 || p.Y < 0 || p.X >= size.Width || p.Y >= size.Height || component >= cpp) {
-					return 0;
-				}
+            /// Provides access to the given component in x and y coordinates. This
+            /// function returns 0 if the given coordinates are out of bounds. This
+            /// function works slower than the () operator.
+            Byte Get(const Geometry::Point &p, unsigned component = 0) const {
+                if (p.X < 0 || p.Y < 0 || p.X >= size.Width || p.Y >= size.Height || component >= cpp) {
+                    return 0;
+                }
 
-				return data[cpp*(size.Width*p.Y + p.X) + component];
-			}
+                return data[cpp*(size.Width*p.Y + p.X) + component];
+            }
 
-			/// Provides access to the given component in x and y coordinates. This
-			/// function returns 0 if the given coordinates are out of bounds. This
-			/// function works slower than the () operator.
-			Byte Get(const Geometry::Point &p, Byte def, unsigned component = 0) const {
-				if (p.X < 0 || p.Y < 0 || p.X >= size.Width || p.Y >= size.Height || component >= cpp) {
-					return def;
-				}
+            /// Provides access to the given component in x and y coordinates. This
+            /// function returns 0 if the given coordinates are out of bounds. This
+            /// function works slower than the () operator.
+            Byte Get(const Geometry::Point &p, Byte def, unsigned component = 0) const {
+                if (p.X < 0 || p.Y < 0 || p.X >= size.Width || p.Y >= size.Height || component >= cpp) {
+                    return def;
+                }
 
-				return data[cpp*(size.Width*p.Y + p.X) + component];
-			}
+                return data[cpp*(size.Width*p.Y + p.X) + component];
+            }
 
-			/// Provides access to the given component in x and y coordinates. This
-			/// function performs bounds checking only on debug mode.
-			Byte &operator()(int x, int y, unsigned component=0) {
+            /// Provides access to the given component in x and y coordinates. This
+            /// function performs bounds checking only on debug mode.
+            Byte &operator()(int x, int y, unsigned component=0) {
 #ifndef NDEBUG
-				if(x<0 || y<0 || x>=size.Width || y>=size.Height || component>=cpp) {
-					throw std::runtime_error("Index out of bounds");
-				}
+                if(x<0 || y<0 || x>=size.Width || y>=size.Height || component>=cpp) {
+                    throw std::runtime_error("Index out of bounds");
+                }
 #endif
-				return data[cpp*(size.Width*y+x)+component];
-			}
+                return data[cpp*(size.Width*y+x)+component];
+            }
 
-			/// Provides access to the given component in x and y coordinates. This
-			/// function performs bounds checking only on debug mode.
-			Byte operator()(int x, int y, unsigned component=0) const {
+            /// Provides access to the given component in x and y coordinates. This
+            /// function performs bounds checking only on debug mode.
+            Byte operator()(int x, int y, unsigned component=0) const {
 #ifndef NDEBUG
-				if(x<0 || y<0 || x>=size.Width || y>=size.Height || component>=cpp) {
-					throw std::runtime_error("Index out of bounds");
-				}
+                if(x<0 || y<0 || x>=size.Width || y>=size.Height || component>=cpp) {
+                    throw std::runtime_error("Index out of bounds");
+                }
 #endif
-				return data[cpp*(size.Width*y+x)+component];
-			}
+                return data[cpp*(size.Width*y+x)+component];
+            }
 
-			/// Provides access to the given component in x and y coordinates. This
-			/// function returns 0 if the given coordinates are out of bounds. This
-			/// function works slower than the () operator.
-			Byte Get(int x, int y, unsigned component=0) const {
-				if(x<0 || y<0 || x>=size.Width || y>=size.Height || component>=cpp) {
-					return 0;
-				}
+            /// Provides access to the given component in x and y coordinates. This
+            /// function returns 0 if the given coordinates are out of bounds. This
+            /// function works slower than the () operator.
+            Byte Get(int x, int y, unsigned component=0) const {
+                if(x<0 || y<0 || x>=size.Width || y>=size.Height || component>=cpp) {
+                    return 0;
+                }
 
-				return data[cpp*(size.Width*y+x)+component];
-			}
+                return data[cpp*(size.Width*y+x)+component];
+            }
 
-			/// Returns the alpha at the given location. If the given location does not exits
-			/// this function will return 0. If there is no alpha channel, image is assumed
-			/// to be opaque.
-			Byte GetAlphaAt(int x, int y) const {
-				if(x<0 || y<0 || x>=size.Width || y>=size.Height) {
-					return 0;
-				}
+            /// Returns the alpha at the given location. If the given location does not exits
+            /// this function will return 0. If there is no alpha channel, image is assumed
+            /// to be opaque.
+            Byte GetAlphaAt(int x, int y) const {
+                if(x<0 || y<0 || x>=size.Width || y>=size.Height) {
+                    return 0;
+                }
 
-				if(alphaloc == -1)
-					return 255;
+                if(alphaloc == -1)
+                    return 255;
 
-				return data[cpp*(size.Width*y+x)+alphaloc];
-			}
+                return data[cpp*(size.Width*y+x)+alphaloc];
+            }
 
             /// Returns the alpha at the given location. If the given location does not exits
             /// this function will return 0. If there is no alpha channel, image is assumed
@@ -1287,81 +1858,81 @@
                 SetRGBAAt(p.X, p.Y, color);
             }
         
-			/// Returns the size of the image
-			Geometry::Size GetSize() const {
-				return size;
-			}
+            /// Returns the size of the image
+            Geometry::Size GetSize() const {
+                return size;
+            }
 
-			/// Returns the width of the image
-			int GetWidth() const {
-				return size.Width;
-			}
+            /// Returns the width of the image
+            int GetWidth() const {
+                return size.Width;
+            }
 
-			/// Returns the height of the image
-			int GetHeight() const {
-				return size.Height;
-			}
+            /// Returns the height of the image
+            int GetHeight() const {
+                return size.Height;
+            }
 
-			/// Total size of this image in number units
-			unsigned long GetTotalSize() const {
-				return size.Area()*cpp;
-			}
+            /// Total size of this image in number units
+            unsigned long GetTotalSize() const {
+                return size.Area()*cpp;
+            }
 
-			/// Returns the color mode of the image
-			Graphics::ColorMode GetMode() const {
-				return mode;
-			}
-			
-			/// Changes the color mode of the image. Only works if the bits/pixel 
-			/// of the target mode is the same as the original
-			void ChangeMode(Graphics::ColorMode value) {
+            /// Returns the color mode of the image
+            Graphics::ColorMode GetMode() const {
+                return mode;
+            }
+            
+            /// Changes the color mode of the image. Only works if the bits/pixel 
+            /// of the target mode is the same as the original
+            void ChangeMode(Graphics::ColorMode value) {
                 if(Graphics::GetChannelsPerPixel(mode) != Graphics::GetChannelsPerPixel(value))
                     throw std::runtime_error("Modes differ in number of bits/pixel");
                 
                 mode = value;
-				this->alphaloc = Graphics::HasAlpha(mode) ? Graphics::GetAlphaIndex(mode) : -1;
-			}
+                this->alphaloc = Graphics::HasAlpha(mode) ? Graphics::GetAlphaIndex(mode) : -1;
+            }
 
-			/// Returns the number units occupied by a single pixel of this image
-			unsigned GetChannelsPerPixel() const {
-				return cpp;
-			}
+            /// Returns the number units occupied by a single pixel of this image
+            unsigned GetChannelsPerPixel() const {
+                return cpp;
+            }
 
-			/// Returns if this image has alpha channel
-			bool HasAlpha() const {
-				return alphaloc != -1;
-			}
+            /// Returns if this image has alpha channel
+            bool HasAlpha() const {
+                return alphaloc != -1;
+            }
 
-			/// Returns the index of alpha channel. Value of -1 denotes no alpha channel
-			int GetAlphaIndex() const {
-				return alphaloc;
-			}
+            /// Returns the index of alpha channel. Value of -1 denotes no alpha channel
+            int GetAlphaIndex() const {
+                return alphaloc;
+            }
 
-		protected:
-			/// Data that stores pixels of the image
-			T_ *data = nullptr;
+        protected:
+            /// Data that stores pixels of the image
+            T_ *data = nullptr;
 
-			/// Width of the image
-			Geometry::Size size = {0, 0};
+            /// Width of the image
+            Geometry::Size size = {0, 0};
 
-			/// Color mode of the image
-			Graphics::ColorMode mode = Graphics::ColorMode::Invalid;
+            /// Color mode of the image
+            Graphics::ColorMode mode = Graphics::ColorMode::Invalid;
 
-			/// Channels per pixel information
-			unsigned cpp = 0;
+            /// Channels per pixel information
+            unsigned cpp = 0;
 
-			/// Location of the alpha channel, -1 means it does not exits
-			int alphaloc = -1;
-		};
+            /// Location of the alpha channel, -1 means it does not exits
+            int alphaloc = -1;
+        };
 
-		/// Swaps two images. Should be used unqualified for ADL.
-		template <class T_>
-		inline void swap(basic_Image<T_> &l, basic_Image<T_> &r) {
-			l.Swap(r);
-		}
+        /// Swaps two images. Should be used unqualified for ADL.
+        template <class T_>
+        inline void swap(basic_Image<T_> &l, basic_Image<T_> &r) {
+            l.Swap(r);
+        }
 
 
-		using Image = basic_Image<Byte>;
+        using Image = basic_Image<Byte>;
 
-	}
+    }
 }
--- a/Source/Gorgon/Graphics/AdvancedPrinter.cpp	Fri Jul 09 09:55:29 2021 +0300
+++ b/Source/Gorgon/Graphics/AdvancedPrinter.cpp	Fri Jul 09 10:04:09 2021 +0300
@@ -72,6 +72,12 @@
             },
             text, l, 0, false
         );
+        
+        if(l.X > sz.Width)
+            sz.Width = l.X;
+
+        if(l.Y > sz.Height)
+            sz.Height = l.Y;
 
         return sz;
     }
@@ -84,27 +90,34 @@
             [&sz](
                 const GlyphRenderer &renderer, Glyph g,
                 const Geometry::Point &location, const RGBAf &, long
-                ) {
-            if(g != 0xffff) {
-                auto p = location + (Geometry::Point)renderer.GetSize(g) + renderer.GetOffset(g);
-                p.Y += int(renderer.GetBaseLine());
+            ) {
+                if(g != 0xffff) {
+                    auto p = location + (Geometry::Point)renderer.GetSize(g) + renderer.GetOffset(g);
+                    p.Y += int(renderer.GetBaseLine());
 
-                if(p.X > sz.Width)
-                    sz.Width = p.X;
+                    if(p.X > sz.Width)
+                        sz.Width = p.X;
 
-                if(p.Y > sz.Height)
-                    sz.Height = p.Y;
-            }
-            return true;
-        },
+                    if(p.Y > sz.Height)
+                        sz.Height = p.Y;
+                }
+                return true;
+            },
             [](const Geometry::Bounds &, const RGBAf &, int, RGBAf) {
-        },
+            },
             [](int, int, int, int, RGBAf) {
-        },
+            },
             [](Byte, const Geometry::Bounds &, const RGBAf &, bool) {
-        },
+            },
             text, l, width, true
-            );
+        );
+        
+        
+        if(l.X > sz.Width)
+            sz.Width = l.X;
+
+        if(l.Y > sz.Height)
+            sz.Height = l.Y;
 
         return sz;
     }
--- a/Source/Gorgon/Graphics/AdvancedPrinterImpl.h	Fri Jul 09 09:55:29 2021 +0300
+++ b/Source/Gorgon/Graphics/AdvancedPrinterImpl.h	Fri Jul 09 10:04:09 2021 +0300
@@ -932,7 +932,8 @@
             ind = acc.size();
             newline = ind == 0;
 
-            cur.Y += lineh;
+            if(nl != -1)
+                cur.Y += lineh;
 
             int nextlinexstart = location.X + indent + hangingindent * beginparag;
 
@@ -1075,7 +1076,7 @@
             //END
 
             //if requested do paragraph
-            if(beginparag)
+            if(nl != -1 && beginparag)
                 cur.Y += paragraphspacing(maxh, printer->GetParagraphSpacing());
 
             //BEGIN Reset
--- a/Source/Gorgon/Graphics/Bitmap.cpp	Fri Jul 09 09:55:29 2021 +0300
+++ b/Source/Gorgon/Graphics/Bitmap.cpp	Fri Jul 09 10:04:09 2021 +0300
@@ -279,7 +279,7 @@
 			}
 		}
 
-		Assume(img);
+		Assume(std::move(img));
 	}
 
 	void Bitmap::StripAlpha() {
@@ -302,7 +302,7 @@
 			}
 		}
 
-		Assume(img);
+		Assume(std::move(img));
 	}
 
 	void Bitmap::Grayscale(float ratio, GrayscaleConversionMethod method) {
@@ -385,7 +385,7 @@
             }
         }
         
-        Assume(img);
+        Assume(std::move(img));
     }
 	
 	std::vector<Geometry::Bounds> Bitmap::CreateLinearAtlas(Containers::Collection<const Bitmap> list, AtlasMargin margins) {
@@ -581,7 +581,7 @@
 			yy++;
 		}
 
-		Assume(img);
+		Assume(std::move(img));
 
 		return ret;
 	}
--- a/Source/Gorgon/Graphics/Bitmap.h	Fri Jul 09 09:55:29 2021 +0300
+++ b/Source/Gorgon/Graphics/Bitmap.h	Fri Jul 09 10:04:09 2021 +0300
@@ -221,10 +221,16 @@
 			data->Assign(newdata);
 		}
 
-		/// Assumes the contents of the given image as image data. The given parameter is moved from
-		/// and will become an empty image. Notice that assuming data does not prepare the data to be drawn, 
+		/// Assumes the contents of the given image as image data. Notice that assuming data does not prepare the data to be drawn, 
 		/// a separate call to Prepare function is necessary.
 		void Assume(Containers::Image &image) {
+			delete data;
+			data = &image;
+		}
+
+		/// Assumes the contents of the given image as image data by moving it into the bitmap buffer. Notice that assuming data 
+		/// does not prepare the data to be drawn, a separate call to Prepare function is necessary.
+		void Assume(Containers::Image &&image) {
 			if(!data) {
 				data=new Containers::Image;
 			}
@@ -695,6 +701,133 @@
             return ret;
         }
 
+        /// Scales this bitmap as a new one using the supplied interpolation method. If the size is reduced more
+        /// than twice, integer part of the size reduction is done using area interpolation.
+		Bitmap Scale(int width, int height, Containers::InterpolationMethod method = Containers::InterpolationMethod::Cubic) const {        
+			return Scale({width, height}, method);
+		}
+
+        /// Scales this bitmap as a new one using the supplied interpolation method. If the size is reduced more
+        /// than twice, integer part of the size reduction is done using area interpolation.
+		Bitmap Scale(const Geometry::Size &newsize, Containers::InterpolationMethod method = Containers::InterpolationMethod::Cubic) const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->Scale(newsize, method));
+
+			return ret;
+		}
+
+        /// Rotates this bitmap as a new one using the supplied interpolation method.
+		Bitmap Rotate(Float ang, const Geometry::Pointf &origin, Containers::InterpolationMethod method = Containers::InterpolationMethod::Cubic) const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->Rotate(ang, origin, method));
+
+			return ret;
+		}
+
+        /// Rotates this bitmap as a new one using the supplied interpolation method.
+		Bitmap Rotate(Float ang, Containers::InterpolationMethod method = Containers::InterpolationMethod::Cubic) const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->Rotate(ang, method));
+
+			return ret;
+		}
+
+		/// Shrinks the bitmap size to integer multiples. This method uses Area interpolation
+		Bitmap ShrinkMultiple(const Geometry::Size& factor) const {
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->ShrinkMultiple(factor));
+
+			return ret;
+		}
+
+        /// Skews this bitmap as a new one using the supplied interpolation method. Origin is assumed to be 0, 0
+		Bitmap SkewX(Float perpixel, Containers::InterpolationMethod method = Containers::InterpolationMethod::Cubic) const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->SkewX(perpixel, method));
+
+			return ret;
+		}
+
+        /// Skews this bitmap as a new one using the supplied interpolation method.
+		Bitmap SkewX(Float perpixel, const Geometry::Pointf &origin, Containers::InterpolationMethod method = Containers::InterpolationMethod::Cubic) const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->SkewX(perpixel, origin, method));
+
+			return ret;
+		}
+
+        /// Skews this bitmap as a new one using the supplied interpolation method. Origin is assumed to be 0, 0
+		Bitmap SkewY(Float perpixel, Containers::InterpolationMethod method = Containers::InterpolationMethod::Cubic) const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->SkewY(perpixel, method));
+
+			return ret;
+		}
+
+        /// Skews this bitmap as a new one using the supplied interpolation method.
+		Bitmap SkewY(Float perpixel, const Geometry::Pointf &origin, Containers::InterpolationMethod method = Containers::InterpolationMethod::Cubic) const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->SkewY(perpixel, origin, method));
+
+			return ret;
+		}
+
+        /// Mirrors this bitmap along X axis as a new one using the supplied interpolation method.
+		Bitmap MirrorX() const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->MirrorX());
+
+			return ret;
+		}
+
+        /// Mirrors this bitmap along Y axis as a new one using the supplied interpolation method.
+		Bitmap MirrorY() const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->MirrorY());
+
+			return ret;
+		}
+
+        /// Flips this bitmap along X axis as a new one using the supplied interpolation method.
+		Bitmap FlipX() const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->FlipX());
+
+			return ret;
+		}
+
+        /// Mirrors this bitmap along Y axis as a new one using the supplied interpolation method.
+		Bitmap FlipY() const {        
+			ASSERT(data, "Bitmap data is not set");
+
+			Bitmap ret;
+			ret.Assume(data->FlipY());
+
+			return ret;
+		}
+
 	protected:
 		/// When used as animation, an image is always persistent and it never finishes.
 		bool Progress(unsigned &) override { return true; }
--- a/Source/Gorgon/Types.h	Fri Jul 09 09:55:29 2021 +0300
+++ b/Source/Gorgon/Types.h	Fri Jul 09 10:04:09 2021 +0300
@@ -161,6 +161,11 @@
             return MultiLess(rest...);
     }
 
+    /// Converts the given degrees to radians
+    inline Float Angle(Float degrees) {
+        return degrees / 180 * PI;
+    }
+
 	/// Where acceptable, denotes that the object will assume the ownership
 	class AssumeOwnershipTag { };
     
--- a/Source/Gorgon/Widgets/Textarea.cpp	Fri Jul 09 09:55:29 2021 +0300
+++ b/Source/Gorgon/Widgets/Textarea.cpp	Fri Jul 09 10:04:09 2021 +0300
@@ -415,7 +415,6 @@
     }
 
     void Textarea::updateselection() {
-        std::cout << selstart.glyph << " : " << sellen.glyph << std::endl;
         updatecursor();
         
         if(sellen.byte != 0 && IsFocused()) {
@@ -696,7 +695,7 @@
         }
         
         if(text == "") {
-            stack.RemoveTagLocation(UI::ComponentTemplate::CaretTag);
+            stack.SetTagLocation(UI::ComponentTemplate::CaretTag, Geometry::Point{cursorlocation.X, cursorlocation.Y} - scrolloffset);
         }
         else {
             stack.SetTagLocation(UI::ComponentTemplate::CaretTag, Geometry::Point{cursorlocation.X, cursorlocation.Y} - scrolloffset);
@@ -741,8 +740,7 @@
         
         return byte;
     }
-
-
+    
     void Textarea::SetWordWrap(const bool &value) {
         if(wrap == value)
             return;
--- a/Source/Gorgon/WindowManager/DWM/DWM.h	Fri Jul 09 09:55:29 2021 +0300
+++ b/Source/Gorgon/WindowManager/DWM/DWM.h	Fri Jul 09 10:04:09 2021 +0300
@@ -16,6 +16,8 @@
 
 #	undef CreateWindow
 #	undef Rectangle
+#	undef max
+#	undef min
 
 ///@cond internal
 namespace Gorgon {
--- a/Source/Gorgon/WindowManager/DWM/DnD.cpp	Fri Jul 09 09:55:29 2021 +0300
+++ b/Source/Gorgon/WindowManager/DWM/DnD.cpp	Fri Jul 09 10:04:09 2021 +0300
@@ -6,6 +6,8 @@
 
 #include <ShlObj.h>
 
+#undef max
+
 namespace Gorgon { namespace WindowManager {
 
 	HRESULT __stdcall GGEDropTarget::DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
--- a/Testing/Source/Manual/Generic.cpp	Fri Jul 09 09:55:29 2021 +0300
+++ b/Testing/Source/Manual/Generic.cpp	Fri Jul 09 10:04:09 2021 +0300
@@ -1,5 +1,5 @@
 #include "GraphicsHelper.h"
-#include <Gorgon/Graphics/ColorSpaces.h>
+#include <Gorgon/Graphics/Bitmap.h>
 
 
 std::string helptext = 
@@ -12,21 +12,21 @@
 
 int main() {
     Application app("generictest", "Test", helptext, 10);
+    app.wind.Resize(1000, 850);
 
     Graphics::Layer layer;
     app.wind.Add(layer);
     
-    for(int c=0; c<=125; c+=25) {
-        for(int l=0; l<=100; l+=10) {
-            for(int h=0; h<=360; h+=15) {
-                auto col = LChAf(l, c, h);
-                layer.Draw(h, l+c/25*110, 15, 10, col);
-                std::cout << col << RGBAf(col) << std::endl;
-            }
-        }
-    }
+    Bitmap bmp;
+    bmp.Import("../../Resources/Logo-large.png");
 
-    std::cout << LChAf(0xff0000ff) << std::endl;
+    bmp = bmp.FlipX();
+    bmp.Prepare();
+
+    bmp.Draw(layer, 0,0);
+    bmp.Export("test.png");
+
+
     while(true) {
         Gorgon::NextFrame();
     }

mercurial