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 }