1 /** 2 This module defines the notion of an Image. 3 An Image is simply defined as a 2D array of elements, with 4 methods to set/get those elements. 5 6 The Image concept might be the basis of a generic software renderer. 7 */ 8 module gfm.image.image; 9 10 import gfm.math.vector; 11 12 // An image is a concept 13 14 15 /** 16 * Test if I is an Image. 17 * 18 * An image has the following features: 19 * $(UL 20 * $(LI defines element_t as the type of elements (eg. pixels).) 21 * $(LI has a dimension of type vec2i.) 22 * $(LI has getter/setter for individual elements.) 23 * ) 24 */ 25 template isImage(I) 26 { 27 enum bool isImage = is(typeof( 28 { 29 I i; // can be defined 30 const I ci; 31 I.element_t e; // defined the type element_t 32 vec2i dim = ci.dimension; // has dimension 33 I.element_t f = ci.get(0, 0); // can get element 34 i.set(0, 0, f); // can set element 35 }())); 36 } 37 38 /// Returns: true if an image contains the given point. 39 bool contains(I)(I img, int x, int y) if (isImage!I) 40 { 41 return cast(uint)x < img.dimension.x && cast(uint)y < img.dimension.y; 42 } 43 44 /// EdgeMode defines how images are sampled beyond their boundaries. 45 enum EdgeMode 46 { 47 BLACK, /// Return black. 48 CLAMP, /// Clamp to edge. 49 REPEAT, /// Repeat from the other side of the image. 50 CRASH /// Crash. 51 } 52 53 54 /// Draws a single pixel. 55 void drawPixel(I, P)(I img, int x, int y, P p) if (isImage!I && is(P : I.element_t)) 56 { 57 if (!img.contains(x, y)) 58 return; 59 img.set(x, y, p); 60 } 61 62 /// Returns: pixel of an Image at position (x, y). 63 /// At boundaries, what happens depends on em. 64 I.element_t getPixel(I)(I img, int x, int y, EdgeMode em) 65 { 66 if (!img.contains(x, y)) 67 { 68 final switch(em) 69 { 70 case EdgeMode.BLACK: 71 return I.element_t.init; 72 73 case EdgeMode.CLAMP: 74 { 75 if (x < 0) x = 0; 76 if (y < 0) y = 0; 77 if (x >= img.dimension.x) x = img.dimension.x - 1; 78 if (y >= img.dimension.y) y = img.dimension.y - 1; 79 break; 80 } 81 82 case EdgeMode.REPEAT: 83 { 84 x = moduloWrap!int(x, img.dimension.x); 85 y = moduloWrap!int(y, img.dimension.y); 86 break; 87 } 88 89 case EdgeMode.CRASH: 90 assert(false); 91 } 92 } 93 94 return img.get(x, y); 95 } 96 97 /// Draws a rectangle outline in an Image. 98 void drawRect(I, P)(I img, int x, int y, int width, int height, P e) if (isImage!I && is(P : I.element_t)) 99 { 100 drawHorizontalLine(img, x, x + width, y, e); 101 drawHorizontalLine(img, x, x + width, y + height - 1, e); 102 drawVerticalLine(img, x, y, y + height, e); 103 drawVerticalLine(img, x + width - 1, y, y + height, e); 104 } 105 106 /// Fills an uniform rectangle area in an Image. 107 void fillRect(I, P)(I img, int x, int y, int width, int height, P e) if (isImage!I && is(P : I.element_t)) 108 { 109 for (int j = 0; j < height; ++j) 110 for (int i = 0; i < width; ++i) 111 img.set(x + i, y + j, e); 112 } 113 114 /// Fills a whole image with a single element value. 115 void fillImage(I, P)(I img, P e) if (isImage!I && is(P : I.element_t)) 116 { 117 immutable int width = img.dimension.x; 118 immutable int height = img.dimension.y; 119 for (int j = 0; j < height; ++j) 120 for (int i = 0; i < width; ++i) 121 img.set(i, j, e); 122 } 123 124 /// Performs an image blit from src to dest. 125 void copyRect(I)(I dest, I src) if (isImage!I) 126 { 127 // check same size 128 assert(dest.dimension == src.dimension); 129 130 immutable int width = dest.dimension.x; 131 immutable int height = dest.dimension.y; 132 for (int j = 0; j < height; ++j) 133 for (int i = 0; i < width; ++i) 134 { 135 auto p = src.get(i, j); 136 dest.set(i, j, p); 137 } 138 } 139 140 /// Draws an horizontal line on an Image. 141 void drawHorizontalLine(I, P)(I img, int x1, int x2, int y, P p) if (isImage!I && is(P : I.element_t)) 142 { 143 for (int x = x1; x < x2; ++x) 144 img.drawPixel(x, y, p); 145 } 146 147 /// Draws a vertical line on an Image. 148 void drawVerticalLine(I, P)(I img, int x, int y1, int y2, P p) if (isImage!I && is(P : I.element_t)) 149 { 150 for (int y = y1; y < y2; ++y) 151 img.drawPixel(x, y, p); 152 }