1 /** 2 * Copyright: Mike Wey 2011 3 * License: zlib (See accompanying LICENSE file) 4 * Authors: Mike Wey 5 */ 6 7 module dmagick.Geometry; 8 9 import std.conv; 10 import std.ascii; 11 import std.string; 12 13 import dmagick.c.geometry; 14 import dmagick.c.magickString; 15 import dmagick.c.magickType; 16 17 alias ptrdiff_t ssize_t; 18 19 /** 20 * Geometry provides a convenient means to specify a geometry argument. 21 */ 22 struct Geometry 23 { 24 size_t width; /// 25 size_t height; /// 26 ssize_t xOffset; /// 27 ssize_t yOffset; /// 28 bool percent; /// The width and/or height are percentages of the original. 29 bool minimum; /// The specified width and/or height is the minimum value. 30 bool keepAspect = true; ///Retain the aspect ratio. 31 bool greater; /// Resize only if the image is greater than the width and/or height. 32 bool less; /// Resize only if the image is smaller than the width and/or height. 33 34 /** 35 * Create a Geometry form a Imagemagick / X11 geometry string. 36 * 37 * The string constist of a size and a optional offset, the size 38 * can be a width, a height (prefixed with an x), or both 39 * ( $(D widthxheight) ). 40 * 41 * When a offset is needed ammend the size with an x an y offset 42 * ( signs are required ) like this: $(D {size}+x+Y). 43 * 44 * The way the size is interpreted can be determined by the 45 * following flags: 46 * 47 * $(TABLE 48 * $(HEADERS Flag, Explanation) 49 * $(ROW $(D %), Normally the attributes are treated as pixels. 50 * Use this flag when the width and height 51 * attributes represent percentages.) 52 * $(ROW $(D !), Use this flag when you want to force the new 53 * image to have exactly the size specified by the 54 * the width and height attributes.) 55 * $(ROW $(D <), Use this flag when you want to change the size 56 * of the image only if both its width and height 57 * are smaller the values specified by those 58 * attributes. The image size is changed 59 * proportionally.) 60 * $(ROW $(D >), Use this flag when you want to change the size 61 * of the image if either its width and height 62 * exceed the values specified by those attributes. 63 * The image size is changed proportionally.) 64 * $(ROW $(D ^), Use ^ to set a minimum image size limit. The 65 * geometry $(D 640x480^) means the image width 66 * will not be less than 640 and the image height 67 * will not be less than 480 pixels after the 68 * resize. One of those dimensions will match 69 * the requested size. But the image will likely 70 * overflow the space requested to preserve its 71 * aspect ratio.) 72 * ) 73 */ 74 this(string geometry) 75 { 76 MagickStatusType flags; 77 78 //If the string starts with a letter assume it's a Page Geometry. 79 if ( isAlpha(geometry[0]) ) 80 { 81 char* geo = GetPageGeometry(toStringz(geometry)); 82 83 if( geo !is null ) 84 { 85 geometry = to!(string)(geo); 86 DestroyString(geo); 87 } 88 } 89 90 flags = GetGeometry(toStringz(geometry), &xOffset, &yOffset, &width, &height); 91 92 percent = ( flags & GeometryFlags.PercentValue ) != 0; 93 minimum = ( flags & GeometryFlags.MinimumValue ) != 0; 94 keepAspect = ( flags & GeometryFlags.AspectValue ) == 0; 95 greater = ( flags & GeometryFlags.GreaterValue ) != 0; 96 less = ( flags & GeometryFlags.LessValue ) != 0; 97 } 98 99 unittest 100 { 101 Geometry geo = Geometry("200x150-50+25!"); 102 assert( geo.width == 200 && geo.xOffset == -50 ); 103 assert( geo.keepAspect == false ); 104 105 geo = Geometry("A4"); 106 assert( geo.width == 595 && geo.height == 842); 107 } 108 109 /** 110 * Initialize with width heigt and offsets. 111 */ 112 this(size_t width, size_t height, ssize_t xOffset = 0, ssize_t yOffset = 0) 113 { 114 this.width = width; 115 this.height = height; 116 this.xOffset = xOffset; 117 this.yOffset = yOffset; 118 } 119 120 /** */ 121 package this(RectangleInfo rectangle) 122 { 123 this.width = rectangle.width; 124 this.height = rectangle.height; 125 this.xOffset = rectangle.x; 126 this.yOffset = rectangle.y; 127 } 128 129 /** 130 * Convert Geometry into a Imagemagick geometry string. 131 */ 132 string toString() 133 { 134 string geometry; 135 136 if ( width > 0 ) 137 geometry ~= to!(string)(width); 138 139 if ( height > 0 ) 140 geometry ~= "x" ~ to!(string)(height); 141 142 geometry ~= format("%s%s%s%s%s", 143 percent ? "%" : "", 144 minimum ? "^" : "", 145 keepAspect ? "" : "!", 146 less ? "<" : "", 147 greater ? ">" : ""); 148 149 if ( xOffset != 0 && yOffset != 0 ) 150 geometry ~= format("%+s%+s", xOffset, yOffset); 151 152 return geometry; 153 } 154 155 unittest 156 { 157 Geometry geo = Geometry("200x150!-50+25"); 158 assert( geo.toString == "200x150!-50+25"); 159 } 160 161 /** 162 * Calculate the absolute width and height based on the flags, 163 * and the profided width and height. 164 */ 165 Geometry toAbsolute(size_t width, size_t height) 166 { 167 ssize_t x, y; 168 169 ParseMetaGeometry(toStringz(toString()), &x, &y, &width, &height); 170 171 return Geometry(width, height, x, y); 172 } 173 174 unittest 175 { 176 Geometry percentage = Geometry("50%"); 177 Geometry absolute = percentage.toAbsolute(100, 100); 178 179 assert(absolute.width == 50); 180 assert(absolute.height == 50); 181 } 182 183 /** */ 184 package RectangleInfo rectangleInfo() 185 { 186 RectangleInfo info; 187 188 info.width = width; 189 info.height = height; 190 info.x = xOffset; 191 info.y = yOffset; 192 193 return info; 194 } 195 196 /** */ 197 size_t opCmp(ref const Geometry geometry) 198 { 199 return width*height - geometry.width*geometry.height; 200 } 201 }