1 /** 2 * Module for picture manipulation. 3 * 4 * Authors: 5 * Jacob Jensen 6 * License: 7 * https://github.com/PoisonEngine/poison-ui/blob/master/LICENSE 8 */ 9 module poison.ui.picture; 10 11 import dsfml.graphics : Image, Texture, RenderWindow; 12 13 import poison.ui.paint; 14 import poison.ui.sprite; 15 import poison.core : Size, Point; 16 17 /// Paint graphics for a picture. 18 private class PictureGraphics { 19 /// The position. 20 Point _position; 21 /// The size. 22 Size _size; 23 /// The paint. 24 Paint _paint; 25 26 /** 27 * Creates a new picture graphic. 28 * Params: 29 * position = The position of the graphic. 30 * size = The size of the graphic. 31 * paint = The paint of the graphic. 32 */ 33 this(Point position, Size size, Paint paint) { 34 _position = position; 35 _size = size; 36 _paint = paint; 37 } 38 39 @property { 40 /// Gets the position of the graphic. 41 Point position() { return _position; } 42 43 /// Gets the size of the graphic. 44 Size size() { return _size; } 45 46 /// Gets the paint of the graphic. 47 Paint paint() { return _paint; } 48 } 49 } 50 51 /// A picture for image manipulation. 52 final class Picture { 53 private: 54 /// The background sprite. 55 TextureSprite _backgroundSprite; 56 57 /// The drawing sprite. 58 TextureSprite _drawingSprite; 59 60 /// The original filename. 61 string _fileName; 62 63 /// The size. 64 Size _size; 65 66 /// The fill paint. 67 Paint _fillPaint; 68 69 /// The image buffer. 70 ubyte[] _imageBuffer; 71 72 /// The graphics to render onto it. 73 PictureGraphics[] _graphics; 74 75 /// The position. 76 Point _position; 77 78 /// Renders the picture. 79 void render() { 80 assert(_fileName || _imageBuffer || _size); 81 82 Image backgroundImage; 83 auto drawingImage = new Image(); 84 85 if (_fileName || _imageBuffer) { 86 backgroundImage = new Image(); 87 88 if (_fileName) { 89 backgroundImage.loadFromFile(_fileName); 90 } 91 else { 92 backgroundImage.loadFromMemory(_imageBuffer); 93 } 94 95 auto imgSize = backgroundImage.getSize(); 96 _size = new Size(cast(size_t)imgSize.x, cast(size_t)imgSize.y); 97 98 drawingImage.create(_size.width, _size.height, transparent.sfmlColor); 99 } 100 else if (_size) { 101 drawingImage.create(_size.width, _size.height, _fillPaint.sfmlColor); 102 } 103 104 if (_graphics) { 105 foreach (graphic; _graphics) { 106 auto xFrom = graphic.position.x; 107 auto xTo = graphic.position.x + graphic.size.width; 108 auto yFrom = graphic.position.y; 109 auto yTo = graphic.position.y + graphic.size.height; 110 auto color = graphic.paint.sfmlColor; 111 112 foreach (x; xFrom .. xTo) { 113 foreach (y; yFrom .. yTo) { 114 drawingImage.setPixel(cast(uint)x, cast(uint)y, color); 115 } 116 } 117 } 118 } 119 120 if (backgroundImage) { 121 auto texture = new Texture(); 122 texture.loadFromImage(backgroundImage); 123 texture.setSmooth(true); 124 125 _backgroundSprite = new TextureSprite(texture); 126 } 127 128 if (drawingImage) { 129 auto texture = new Texture(); 130 texture.loadFromImage(drawingImage); 131 texture.setSmooth(true); 132 133 _drawingSprite = new TextureSprite(texture); 134 } 135 136 position = _position; 137 } 138 139 public: 140 final: 141 /** 142 * Creates a new picture. 143 * Params: 144 * imageFile = The file for an image to load onto the picture. 145 */ 146 this(string imageFile) { 147 _fileName = imageFile; 148 } 149 150 /** 151 * Creates a new picture. 152 * Params: 153 * size = The size of the picture. 154 * fillPaint = The initialization paint. 155 */ 156 this(Size size, Paint fillPaint) { 157 _size = size; 158 _fillPaint = fillPaint; 159 } 160 161 /** 162 * Creates a new picture. 163 * Params: 164 * imageBuffer = The buffer to load the picture from. 165 */ 166 this(ubyte[] imageBuffer) { 167 _imageBuffer = imageBuffer; 168 } 169 170 /** 171 * Creates a new picture, copied from another. 172 * Params: 173 * copyImage = The image to copy. 174 */ 175 this(Picture copyImage) { 176 if (copyImage._imageBuffer) { 177 this(copyImage._imageBuffer.dup); 178 } 179 else if (copyImage._fileName) { 180 this(copyImage._fileName.dup); 181 } 182 else { 183 this(new Size(copyImage._size.width, copyImage._size.height), copyImage._fillPaint); 184 } 185 186 if (copyImage._graphics) { 187 _graphics = copyImage._graphics.dup; 188 } 189 } 190 191 /// Clears the graphics. 192 void clearGraphics() { 193 _graphics = null; 194 } 195 196 /** 197 * Draws paint onto the picture. 198 * Params: 199 * position = The position within the picture to draw the paint. 200 * size = The size of the paint. 201 * paint = The paint to draw with. 202 */ 203 void draw(Point position, Size size, Paint paint) { 204 _graphics ~= new PictureGraphics(position, size, paint); 205 } 206 207 /** 208 * Paints a horizontal gradient on the picture. 209 * Params: 210 * position = The position within the picture to paint the gradient. 211 * size = The size of the gradient. 212 * from = The paint to gradient from. 213 * to = The paint to gradient to. 214 * a = The alpha channel of the gradient. 215 */ 216 void gradientHorizontal(Point position, Size size, Paint from, Paint to, ubyte a = 0xff) { 217 bool decreaseR = from.r > to.r; 218 bool decreaseG = from.g > to.g; 219 bool decreaseB = from.b > to.b; 220 221 auto block = new Size(1, size.height); 222 223 ubyte rInc = cast(ubyte)(cast(float)(decreaseR ? from.r - to.r : to.r - from.r) / (cast(float)size.width / 1.5)); 224 ubyte gInc = cast(ubyte)(cast(float)(decreaseG ? from.g - to.g : to.g - from.g) / (cast(float)size.width / 1.5)); 225 ubyte bInc = cast(ubyte)(cast(float)(decreaseB ? from.b - to.b : to.b - from.b) / (cast(float)size.width / 1.5)); 226 227 for (auto x = position.x; x < (position.x + size.width); x++) { 228 draw(new Point(x, position.y), block, paintFromRGBA(from.r, from.g, from.b, a)); 229 230 if (decreaseR && from.r > (to.r + rInc)) { 231 from.r -= rInc; 232 } 233 else if (!decreaseR && from.r < (to.r - rInc)) { 234 from.r += rInc; 235 } 236 237 if (decreaseG && from.g > (to.g + gInc)) { 238 from.g -= gInc; 239 } 240 else if (!decreaseG && from.g < (to.g - gInc)) { 241 from.g += gInc; 242 } 243 244 if (decreaseB && from.b > (to.b + bInc)) { 245 from.b -= bInc; 246 } 247 else if (!decreaseB && from.b < (to.b - bInc)) { 248 from.b += bInc; 249 } 250 } 251 } 252 253 /** 254 * Paints a vertical gradient on the picture. 255 * Params: 256 * position = The position within the picture to paint the gradient. 257 * size = The size of the gradient. 258 * from = The paint to gradient from. 259 * to = The paint to gradient to. 260 * a = The alpha channel of the gradient. 261 */ 262 void gradientVertical(Point position, Size size, Paint from, Paint to, ubyte a = 0xff) { 263 bool decreaseR = from.r > to.r; 264 bool decreaseG = from.g > to.g; 265 bool decreaseB = from.b > to.b; 266 267 auto block = new Size(size.width, 1); 268 269 ubyte rInc = cast(ubyte)(cast(float)(decreaseR ? from.r - to.r : to.r - from.r) / (cast(float)size.height / 1.5)); 270 ubyte gInc = cast(ubyte)(cast(float)(decreaseG ? from.g - to.g : to.g - from.g) / (cast(float)size.height / 1.5)); 271 ubyte bInc = cast(ubyte)(cast(float)(decreaseB ? from.b - to.b : to.b - from.b) / (cast(float)size.height / 1.5)); 272 273 for (auto y = position.y; y < (position.y + size.height); y++) { 274 draw(new Point(position.x, y), block, paintFromRGBA(from.r, from.g, from.b, a)); 275 276 if (decreaseR && from.r > (to.r + rInc)) { 277 from.r -= rInc; 278 } 279 else if (!decreaseR && from.r < (to.r - rInc)) { 280 from.r += rInc; 281 } 282 283 if (decreaseG && from.g > (to.g + gInc)) { 284 from.g -= gInc; 285 } 286 else if (!decreaseG && from.g < (to.g - gInc)) { 287 from.g += gInc; 288 } 289 290 if (decreaseB && from.b > (to.b + bInc)) { 291 from.b -= bInc; 292 } 293 else if (!decreaseB && from.b < (to.b - bInc)) { 294 from.b += bInc; 295 } 296 } 297 } 298 299 /** 300 * Resizes the picture. 301 * Params: 302 * newSize = Resizes the picture. 303 * Bug: 304 * Currently only work for pictures with no image file. 305 * The fix is to use scale(). 306 */ 307 void resize(Size newSize) { 308 _size = newSize; 309 } 310 311 /// Finalizes the picture and its graphics. 312 void finalize() { 313 render(); 314 } 315 316 @property { 317 /// Gets the size of the picture. 318 Size size() { return _size; } 319 320 /// Gets the file name. 321 string fileName() { return _fileName; } 322 323 /// Sets the file name. 324 void fileName(string newFileName) { 325 _fileName = newFileName; 326 } 327 328 /// Gets the image buffer. 329 ubyte[] imageBuffer() { return _imageBuffer; } 330 331 /// Sets the image buffer. 332 void imageBuffer(ubyte[] imageBuffer) { 333 _imageBuffer = imageBuffer; 334 } 335 } 336 337 package(poison): 338 @property { 339 /// Gets the background sprite of the picture. 340 TextureSprite backgroundSprite() { return _backgroundSprite; } 341 342 /// Gets the drawing sprite of the picture. 343 TextureSprite drawingSprite() { return _drawingSprite; } 344 345 /// Sets the position of the picture's sprite. 346 void position(Point newPosition) { 347 _position = newPosition; 348 349 if (_position) { 350 if (_backgroundSprite) { 351 _backgroundSprite.position = _position; 352 } 353 354 if (_drawingSprite) { 355 _drawingSprite.position = _position; 356 } 357 } 358 } 359 } 360 }