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