1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/**	\file	draw.c

 	\brief	Implementation of a library of graphic drawing functions.

	\note	All functions draw to the \ref framebuffer specified using the \ref framebuffer_draw_pixel function specified.
			So the implementation is independent of the actual \ref framebuffer properties.

 	\license 
 		MIT:	The MIT License (https://opensource.org/licenses/MIT)
 		.
 		Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 		and associated documentation files (the "Software"), to deal in the Software without restriction, 
 		including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 		and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
 		subject to the following conditions:
 		.
 		The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 		.
 		THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 		INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 		IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 		WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 
 		THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 	\copyright
 		DIT: 2017; Drechsler Information Technologies; www.drechsler-it.de
 
 	\authors
 		jrgdre: Joerg Drechsler; DIT
 
 	\versions
 		1.0.0: 2017-06-22 jrgdre initial release

 */
#include "Draw.h"
#include "Framebuffer.h"
#include "Stack.h"

/**
 * \brief Draw a circle around [x0,y0] with radius
 */
uint8_t draw_circle(
  struct Framebuffer *framebuffer       //< pointer to the framebuffer to draw the pixel to
,           uint32_t  pixel_value	//< value to set for the pixels
,           uint16_t  x0		//< x start position of the line
,           uint16_t  y0		//< y start position of the line
,           uint16_t  radius		//< radius in pixel to draw circle with
){

        if( framebuffer == NULL ){
                return STATUS_ERR_INVALID_ARG;
        }
        if( framebuffer->set_pixel == NULL){
                return STATUS_ERR_INVALID_ARG;
        }

        Framebuffer_Set_Pixel *set_pixel = framebuffer->set_pixel;

        int x	= radius;
        int y	= 0;
        int err = 0;

        while ( x >= y )
        {
                set_pixel( framebuffer, x0 + x, y0 + y, pixel_value );
                set_pixel( framebuffer, x0 + y, y0 + x, pixel_value );
                set_pixel( framebuffer, x0 - y, y0 + x, pixel_value );
                set_pixel( framebuffer, x0 - x, y0 + y, pixel_value );
                set_pixel( framebuffer, x0 - x, y0 - y, pixel_value );
                set_pixel( framebuffer, x0 - y, y0 - x, pixel_value );
                set_pixel( framebuffer, x0 + y, y0 - x, pixel_value );
                set_pixel( framebuffer, x0 + x, y0 - y, pixel_value );

                y += 1;
                if (err <= 0)
                {
                        err += ( y << 1 ) + 1;
                } else {
                        x -= 1;
                        err += ( ( y - x ) << 1 ) + 1;
                }
        }

        return STATUS_OK;
}

/**
 * \brief Draw an ellipse inside the rectangle specified by [x0,y0], [x1,y1]
 */
uint8_t draw_ellipse_rect(
  struct Framebuffer *framebuffer       //< pointer to the framebuffer to draw the pixel to
,           uint32_t  pixel_value	//< value to set for the pixels
,           uint16_t  x0		//< x start position of the enclosing rectangle
,           uint16_t  y0		//< y start position of the enclosing rectangle
,           uint16_t  x1		//< x end   position of the enclosing rectangle
,           uint16_t  y1		//< y end   position of the enclosing rectangle
){
        if( framebuffer == NULL ){
                return STATUS_ERR_INVALID_ARG;
        }
        if( framebuffer->set_pixel == NULL){
                return STATUS_ERR_INVALID_ARG;
        }

        Framebuffer_Set_Pixel *set_pixel = framebuffer->set_pixel;

	// values of diameter
	int  a   = abs( x1 - x0 );
	int  b   = abs( y1 - y0 );
	int  b1  = b&1           ;
	// error increment
	long dx  = ( ( 1  - a ) * b*b ) << 2;
	long dy  = ( ( b1 + 1 ) * a*a ) << 2;
	 // error of 1.step
	long err = dx + dy + b1 * a*a;
	long e2;

	if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points
	if (y0 > y1) { y0 = y1;          } // .. exchange them 

	// starting pixel
	y0 += ( b + 1 ) >> 1;
	y1  = y0 - b1;
	a  *= a << 3;
	b1  = ( b*b ) << 3;

	do {
		set_pixel( framebuffer, x1, y0, pixel_value); /*   I. Quadrant */
		set_pixel( framebuffer, x0, y0, pixel_value); /*  II. Quadrant */
		set_pixel( framebuffer, x0, y1, pixel_value); /* III. Quadrant */
		set_pixel( framebuffer, x1, y1, pixel_value); /*  IV. Quadrant */
		e2 = err << 1;
		if( e2 <= dy ){
			// y step 
			y0++;
			y1--;
			err += dy += a;
		}
		if( ( e2 >= dx ) || ( err << 1 ) > dy ){
			// x step
			x0++;
			x1--;
			err += dx += b1;
		}
	} while( x0 <= x1 );

	while( y0-y1 < b ) {  /* too early stop of flat ellipses a=1 */
		// finish tip of ellipse
		set_pixel( framebuffer, x0-1, y0  , pixel_value);
		set_pixel( framebuffer, x1+1, y0++, pixel_value);
		set_pixel( framebuffer, x0-1, y1  , pixel_value);
		set_pixel( framebuffer, x1+1, y1--, pixel_value);
	}

	return STATUS_OK;
}

/**
 * \brief Flood fill an area starting from point [x,y] with pixel_new_value
 */
uint8_t draw_fill(
  struct Framebuffer *framebuffer       //< pointer to the framebuffer to draw the pixel to
,           uint32_t  pixel_value	//< current value of pixels to change
,           uint32_t  pixel_value_new	//< new value pixels are set to
,           uint16_t  x			//< x start point
,           uint16_t  y			//< y start point
){
        if( framebuffer == NULL ){
                return STATUS_ERR_INVALID_ARG;
        }
        if( framebuffer->set_pixel == NULL ){
                return STATUS_ERR_INVALID_ARG;
        }
        if( framebuffer->get_pixel == NULL ){
                return STATUS_ERR_INVALID_ARG;
        }

        Framebuffer_Set_Pixel *set_pixel = framebuffer->set_pixel;
        Framebuffer_Get_Pixel *get_pixel = framebuffer->get_pixel;

        Stack    *stack;                                // a (self growing) stack for position of pixel we want to remember
        uint8_t   ret		        = STATUS_OK;	// status of this function
        uint8_t   ret_above	        = STATUS_OK;	// status of the pixel above test
        uint8_t   ret_below	        = STATUS_OK;	// status of the pixel below test
        uint8_t   ret_draw	        = STATUS_OK;	// status of the draw operation
        uint16_t  x_curr		= x;		// current x position
        uint16_t  y_curr		= y;		// current y position
        uint32_t  current_pixel_value	= 0;		// value of the current pixel
        uint32_t  pixel_value_above	= 0;		// value of the pixel above the current pixel
        uint32_t  pixel_value_below	= 0;		// value of the pixel below the current pixel
        bool	  push_next_valid_above	= true;		// the next valid pixel above should be pushed to the stack
        bool	  push_next_valid_below	= true;		// the next valid pixel below should be pushed to the stack

        // check the validity of the initial position
        ret = get_pixel( framebuffer, x, y, &current_pixel_value);
        if( ret == STATUS_ERR_INVALID_ARG ){
                return ret; // x or y out of bounds
        }

        // allocate a stack for x and y values to remember
        ret = stack_allocate( &stack, 1 ); // stack is self re-allocating, so we start with an initial_capcity of one
        if( ret != STATUS_OK ){
                return ret;
        }

        do{
                // move left until we find a pixel with a value != pixel_value or reach the end of the screen
                while(( current_pixel_value == pixel_value ) // pixel has the correct value
                   && ( ret != STATUS_ERR_INVALID_ARG      ) // not yet reached the end of the screen
                ){
                        x_curr--;
                        ret = get_pixel( framebuffer, x_curr, y_curr, &current_pixel_value);
                }
                // push back to the last pixel valid for fill
                x_curr++;
                ret = get_pixel( framebuffer, x_curr, y_curr, &current_pixel_value);

                // now move right until we find a pixel with a value != pixel_value or reach the end of the screen
                while(( ret != STATUS_ERR_INVALID_ARG      ) // pixel is within screen limits
                   && ( current_pixel_value == pixel_value ) // pixel has the correct value
                ){
                        // set the new value for the current pixel
                        ret_draw = set_pixel( framebuffer, x_curr, y_curr, pixel_value_new );
                        if( ret_draw != STATUS_OK){
                                break; // abort all fill operations
                        }

                        // test the pixel above the current pixel
                        ret_above = get_pixel( framebuffer, x_curr, y_curr - 1, &pixel_value_above);
                        if(( ret_above == STATUS_OK           )	// pixel is within screen limits
                        && ( pixel_value_above == pixel_value ) // pixel as the correct value
                        ){	// pixel above is valid for fill
                                if( push_next_valid_above ){
                                        // we only push the first pixel in a consecutive row of pixels valid for fill
                                        stack_push( stack, y_curr - 1 );
                                        stack_push( stack, x_curr     );
                                        push_next_valid_above = false;
                                }
                        } else {
                                // pixel above is not valid for fill
                                push_next_valid_above = true; // the next valid one above is to be pushed again
                        }

                        // test the pixel below the current pixel
                        ret_below = get_pixel( framebuffer, x_curr, y_curr + 1, &pixel_value_below);
                        if(( ret_below == STATUS_OK           )	// pixel is within screen limits
                        && ( pixel_value_below == pixel_value ) // pixel has the correct value
                        ){	// pixel below is valid for fill
                                if( push_next_valid_below ){
                                        // we only push the first pixel in a consecutive row of pixels valid for fill
                                        stack_push( stack, y_curr + 1 );
                                        stack_push( stack, x_curr     );
                                        push_next_valid_below = false;
                                }
                        } else {
                                // pixel below is not valid for fill
                                push_next_valid_below = true; // the next valid one below is to be pushed again
                        }

                        // move to next pixel in line and get it's value
                        x_curr++;
                        ret = get_pixel( framebuffer, x_curr, y_curr, &current_pixel_value);

                } // end while

                if( ret_draw == STATUS_OK ){
                        // check stack for more eligible pixels to fill
                        ret = stack_pop( stack, &x_curr );
                        if( ret != STATUS_OK ){
                                break; // looks like we are done
                        }
                        ret = stack_pop( stack, &y_curr );
                        if( ret != STATUS_OK ){
                                break; // looks like we are done
                        }
                        // we found another pixel
                        current_pixel_value   = pixel_value;    // that's why we stored it in the first place
                        push_next_valid_above = true;		// the next valid one above is to be pushed again
                        push_next_valid_below = true;		// the next valid one below is to be pushed again
                }

        } while( ret_draw == STATUS_OK );

        // always deallocate the stack
        ret = stack_deallocate( &stack );
        if( ret != STATUS_OK ){
                return ret;
        }

        return STATUS_OK;
}

/**
 * \brief Set pixel_value for a line of pixels form [x0,y0] to [x1,y1]
 */
uint8_t draw_line(
  struct Framebuffer *framebuffer       //< pointer to the framebuffer to draw the pixel to
,           uint32_t  pixel_value	//< value to set for the pixels
,           uint16_t  x0		//< x start position of the line
,           uint16_t  y0		//< y start position of the line
,           uint16_t  x1		//< x end   position of the line
,           uint16_t  y1		//< y end   position of the line
){
        if( framebuffer == NULL ){
                return STATUS_ERR_INVALID_ARG;
        }
        if( framebuffer->set_pixel == NULL){
                return STATUS_ERR_INVALID_ARG;
        }

        Framebuffer_Set_Pixel *set_pixel = framebuffer->set_pixel;

        int32_t dx =  abs( x1 - x0 ), sx = x0 < x1 ? 1 : -1;
        int32_t dy = -abs( y1 - y0 ), sy = y0 < y1 ? 1 : -1;
        int32_t err = dx + dy, e2; /* error value e_xy */

        for(;;){  /* loop */
                set_pixel( framebuffer, x0, y0, pixel_value );
                if ( x0 == x1 && y0 == y1 ) break;
                e2 = err << 1;
                if( e2 >= dy ) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */
                if( e2 <= dx ) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */
        }

        return STATUS_OK;
}

/**
 * \brief Draw a rectangle form [x0,y0] to [x1,y1]
 */
uint8_t draw_rect(
  struct Framebuffer *framebuffer       //< pointer to the framebuffer to draw the pixel to
,           uint32_t  pixel_value	//< value to set for the pixels
,           uint16_t  x0		//< x start position of the line
,           uint16_t  y0		//< y start position of the line
,           uint16_t  x1		//< x end   position of the line
,           uint16_t  y1		//< y end   position of the line
){
	uint8_t result = STATUS_OK;

	result = draw_line( framebuffer, pixel_value, x0, y0, x1, y0); if( result != STATUS_OK ){ return result; }
	result = draw_line( framebuffer, pixel_value, x1, y0, x1, y1); if( result != STATUS_OK ){ return result; }
	result = draw_line( framebuffer, pixel_value, x1, y1, x0, y1); if( result != STATUS_OK ){ return result; }
	result = draw_line( framebuffer, pixel_value, x0, y1, x0, y0); if( result != STATUS_OK ){ return result; }

	return result;
}