1 /**
2  * \file
3  *
4  * \brief Generic monochrome LCD graphic primitives
5  *
6  * Copyright (c) 2011-2015 Atmel Corporation. All rights reserved.
7  *
8  * \asf_license_start
9  *
10  * \page License
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright notice,
16  *    this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright notice,
19  *    this list of conditions and the following disclaimer in the documentation
20  *    and/or other materials provided with the distribution.
21  *
22  * 3. The name of Atmel may not be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * 4. This software may only be redistributed and used in connection with an
26  *    Atmel microcontroller product.
27  *
28  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  *
40  * \asf_license_stop
41  *
42  */
43 
44 /**
45  * \ingroup asfdoc_common2_gfx_mono_generic_group
46  * @{
47  */
48 /*
49  * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
50  */
51 #include "gfx_mono_generic.h"
52 
53 /**
54  * \brief Draw a horizontal line, one pixel wide (generic implementation)
55  *
56  * \note This function does a very simple bounds checking that does not
57  * check if the line is placed outside the screen. If you supply an
58  * x- or y-coordinate outside the display the behaviour is undefined,
59  * and you risk overwriting portions of internal SRAM.
60  *
61  * \param[in]  x          X coordinate of leftmost pixel.
62  * \param[in]  y          Y coordinate of the line.
63  * \param[in]  length     Length of the line in pixels.
64  * \param[in]  color      Pixel operation of the line.
65  */
gfx_mono_generic_draw_horizontal_line(gfx_coord_t x,gfx_coord_t y,gfx_coord_t length,enum gfx_mono_color color)66 void gfx_mono_generic_draw_horizontal_line(gfx_coord_t x, gfx_coord_t y,
67 		gfx_coord_t length, enum gfx_mono_color color)
68 {
69 	uint8_t page;
70 	uint8_t pixelmask;
71 	uint8_t temp;
72 
73 	/* Clip line length if too long */
74 	if (x + length > GFX_MONO_LCD_WIDTH) {
75 		length = GFX_MONO_LCD_WIDTH - x;
76 	}
77 
78 	page = y / 8;
79 	pixelmask = (1 << (y - (page * 8)));
80 
81 	if (length == 0) {
82 		/* Nothing to do. Move along. */
83 		return;
84 	}
85 
86 	switch (color) {
87 	case GFX_PIXEL_SET:
88 		while (length-- > 0) {
89 			temp = gfx_mono_get_byte(page, x + length);
90 			temp |= pixelmask;
91 			gfx_mono_put_byte(page, x + length, temp);
92 		}
93 		break;
94 
95 	case GFX_PIXEL_CLR:
96 		while (length-- > 0) {
97 			temp = gfx_mono_get_byte(page, x + length);
98 			temp &= ~pixelmask;
99 			gfx_mono_put_byte(page, x + length, temp);
100 		}
101 		break;
102 
103 	case GFX_PIXEL_XOR:
104 		while (length-- > 0) {
105 			temp = gfx_mono_get_byte(page, x + length);
106 			temp ^= pixelmask;
107 			gfx_mono_put_byte(page, x + length, temp);
108 		}
109 		break;
110 
111 	default:
112 		break;
113 	}
114 }
115 
116 /**
117  * \brief Draw a vertical line, one pixel wide (generic implementation)
118  *
119  * \note This function does a very simple bounds checking that does not
120  * check if the line is placed outside the screen. If you supply an
121  * x- or y-coordinate outside the display the behaviour is undefined,
122  * and you risk overwriting portions of internal SRAM.
123  *
124  * \param[in]  x          X coordinate of the line.
125  * \param[in]  y          Y coordinate of the topmost pixel.
126  * \param[in]  length     Length of the line in pixels.
127  * \param[in]  color      Pixel operation of the line.
128  */
gfx_mono_generic_draw_vertical_line(gfx_coord_t x,gfx_coord_t y,gfx_coord_t length,enum gfx_mono_color color)129 void gfx_mono_generic_draw_vertical_line(gfx_coord_t x, gfx_coord_t y,
130 		gfx_coord_t length, enum gfx_mono_color color)
131 {
132 	if (length == 0) {
133 		return;
134 	}
135 
136 	gfx_coord_t y2 = y + length - 1;
137 
138 	if (y == y2) {
139 		gfx_mono_draw_pixel(x, y, color);
140 		return;
141 	}
142 
143 	if (y2 >= GFX_MONO_LCD_HEIGHT - 1) {
144 		y2 = GFX_MONO_LCD_HEIGHT - 1;
145 	}
146 
147 	gfx_coord_t y1page = y / 8;
148 	gfx_coord_t y2page = y2 / 8;
149 
150 	uint8_t y1bitpos = y & 0x07;
151 	uint8_t y2bitpos = y2 & 0x07;
152 
153 	uint8_t y1pixelmask = 0xFF << y1bitpos;
154 	uint8_t y2pixelmask = 0xFF >> (7 - y2bitpos);
155 
156 	/* The pixels are on the same page; combine masks */
157 	if (y1page == y2page) {
158 		uint8_t pixelmask = y1pixelmask & y2pixelmask;
159 		gfx_mono_mask_byte(y1page, x, pixelmask, color);
160 	} else {
161 		gfx_mono_mask_byte(y1page, x, y1pixelmask, color);
162 
163 		while (++y1page < y2page) {
164 			gfx_mono_mask_byte(y1page, x, 0xFF, color);
165 		}
166 
167 		gfx_mono_mask_byte(y2page, x, y2pixelmask, color);
168 	}
169 }
170 
171 /**
172  * \brief Draw a line between two arbitrary points (generic implementation).
173  *
174  * \param[in]  x1          Start X coordinate.
175  * \param[in]  y1          Start Y coordinate.
176  * \param[in]  x2          End X coordinate.
177  * \param[in]  y2          End Y coordinate.
178  * \param[in]  color       Pixel operation of the line.
179  */
gfx_mono_generic_draw_line(gfx_coord_t x1,gfx_coord_t y1,gfx_coord_t x2,gfx_coord_t y2,enum gfx_mono_color color)180 void gfx_mono_generic_draw_line(gfx_coord_t x1, gfx_coord_t y1,
181 		gfx_coord_t x2, gfx_coord_t y2,
182 		enum gfx_mono_color color)
183 {
184 	uint8_t i;
185 	uint8_t x;
186 	uint8_t y;
187 	int8_t xinc;
188 	int8_t yinc;
189 	int8_t dx;
190 	int8_t dy;
191 	int8_t e;
192 
193 	/* swap x1,y1  with x2,y2 */
194 	if (x1 > x2) {
195 		dx = x1;
196 		x1 = x2;
197 		x2 = dx;
198 		dy = y1;
199 		y1 = y2;
200 		y2 = dy;
201 	}
202 
203 	dx = x2 - x1;
204 	dy = y2 - y1;
205 
206 	x = x1;
207 	y = y1;
208 
209 	if (dx < 0) {
210 		xinc = -1;
211 		dx = -dx;
212 	} else {
213 		xinc = 1;
214 	}
215 
216 	if (dy < 0) {
217 		yinc = -1;
218 		dy = -dy;
219 	} else {
220 		yinc = 1;
221 	}
222 
223 	if (dx > dy) {
224 		e = dy - dx;
225 		for (i = 0; i <= dx; i++) {
226 			gfx_mono_draw_pixel(x, y, color);
227 			if (e >= 0) {
228 				e -= dx;
229 				y += yinc;
230 			}
231 
232 			e += dy;
233 			x += xinc;
234 		}
235 	} else {
236 		e = dx - dy;
237 		for (i = 0; i <= dy; i++) {
238 			gfx_mono_draw_pixel(x, y, color);
239 			if (e >= 0) {
240 				e -= dy;
241 				x += xinc;
242 			}
243 
244 			e += dx;
245 			y += yinc;
246 		}
247 	}
248 }
249 
250 /**
251  * \brief Draw an outline of a rectangle (generic implementation).
252  *
253  * \param[in]  x           X coordinate of the left side.
254  * \param[in]  y           Y coordinate of the top side.
255  * \param[in]  width       Width of the rectangle.
256  * \param[in]  height      Height of the rectangle.
257  * \param[in] color       Pixel operation of the line.
258  */
gfx_mono_generic_draw_rect(gfx_coord_t x,gfx_coord_t y,gfx_coord_t width,gfx_coord_t height,enum gfx_mono_color color)259 void gfx_mono_generic_draw_rect(gfx_coord_t x, gfx_coord_t y,
260 		gfx_coord_t width, gfx_coord_t height,
261 		enum gfx_mono_color color)
262 {
263 	gfx_mono_draw_horizontal_line(x, y, width, color);
264 	gfx_mono_draw_horizontal_line(x, y + height - 1, width, color);
265 
266 	gfx_mono_draw_vertical_line(x, y, height, color);
267 	gfx_mono_draw_vertical_line(x + width - 1, y, height, color);
268 }
269 
270 /**
271  * \brief Draw a filled rectangle (generic implementation).
272  *
273  * \param[in]  x           X coordinate of the left side.
274  * \param[in]  y           Y coordinate of the top side.
275  * \param[in]  width       Width of the rectangle.
276  * \param[in]  height      Height of the rectangle.
277  * \param[in]  color       Pixel operation of the line
278  */
gfx_mono_generic_draw_filled_rect(gfx_coord_t x,gfx_coord_t y,gfx_coord_t width,gfx_coord_t height,enum gfx_mono_color color)279 void gfx_mono_generic_draw_filled_rect(gfx_coord_t x, gfx_coord_t y,
280 		gfx_coord_t width, gfx_coord_t height,
281 		enum gfx_mono_color color)
282 {
283 	if (height == 0) {
284 		/* Nothing to do. Move along. */
285 		return;
286 	}
287 
288 	while (height-- > 0) {
289 		gfx_mono_draw_horizontal_line(x, y + height, width, color);
290 	}
291 }
292 
293 /**
294  * \brief Draw an outline of a circle or arc (generic implementation).
295  *
296  * The radius is the distance from the center to the circumference,
297  * which means that the total width or height of a circle will be
298  * (radius*2+1).
299  *
300  * The octant_mask parameter is a bitmask that decides which octants of
301  * the circle to draw. Use the GFX_OCTANTn, GFX_QUADRANTn, GFX_xHALF and
302  * GFX_WHOLE constants and OR them together if required. Radius equal to
303  * zero gives a single pixel.
304  *
305  * \param[in]  x           X coordinate of center.
306  * \param[in]  y           Y coordinate of center.
307  * \param[in]  radius      Circle radius in pixels.
308  * \param[in]  color       Pixel operation.
309  * \param[in]  octant_mask Bitmask indicating which octants to draw.
310  */
gfx_mono_generic_draw_circle(gfx_coord_t x,gfx_coord_t y,gfx_coord_t radius,enum gfx_mono_color color,uint8_t octant_mask)311 void gfx_mono_generic_draw_circle(gfx_coord_t x, gfx_coord_t y,
312 		gfx_coord_t radius, enum gfx_mono_color color,
313 		uint8_t octant_mask)
314 {
315 	gfx_coord_t offset_x;
316 	gfx_coord_t offset_y;
317 	int16_t error;
318 
319 	/* Draw only a pixel if radius is zero. */
320 	if (radius == 0) {
321 		gfx_mono_draw_pixel(x, y, color);
322 		return;
323 	}
324 
325 	/* Set up start iterators. */
326 	offset_x = 0;
327 	offset_y = radius;
328 	error = 3 - 2 * radius;
329 
330 	/* Iterate offsetX from 0 to radius. */
331 	while (offset_x <= offset_y) {
332 		/* Draw one pixel for each octant enabled in octant_mask. */
333 		if (octant_mask & GFX_OCTANT0) {
334 			gfx_mono_draw_pixel(x + offset_y, y - offset_x, color);
335 		}
336 
337 		if (octant_mask & GFX_OCTANT1) {
338 			gfx_mono_draw_pixel(x + offset_x, y - offset_y, color);
339 		}
340 
341 		if (octant_mask & GFX_OCTANT2) {
342 			gfx_mono_draw_pixel(x - offset_x, y - offset_y, color);
343 		}
344 
345 		if (octant_mask & GFX_OCTANT3) {
346 			gfx_mono_draw_pixel(x - offset_y, y - offset_x, color);
347 		}
348 
349 		if (octant_mask & GFX_OCTANT4) {
350 			gfx_mono_draw_pixel(x - offset_y, y + offset_x, color);
351 		}
352 
353 		if (octant_mask & GFX_OCTANT5) {
354 			gfx_mono_draw_pixel(x - offset_x, y + offset_y, color);
355 		}
356 
357 		if (octant_mask & GFX_OCTANT6) {
358 			gfx_mono_draw_pixel(x + offset_x, y + offset_y, color);
359 		}
360 
361 		if (octant_mask & GFX_OCTANT7) {
362 			gfx_mono_draw_pixel(x + offset_y, y + offset_x, color);
363 		}
364 
365 		/* Update error value and step offset_y when required. */
366 		if (error < 0) {
367 			error += ((offset_x << 2) + 6);
368 		} else {
369 			error += (((offset_x - offset_y) << 2) + 10);
370 			--offset_y;
371 		}
372 
373 		/* Next X. */
374 		++offset_x;
375 	}
376 }
377 
378 /**
379  * \brief Draw a filled circle or sector (generic implementation).
380  *
381  * The radius is the distance from the center to the circumference,
382  * which means that the total width or height of a circle will be
383  * (radius*2+1).
384  *
385  * The quadrant_mask parameter is a bitmask that decides which quadrants
386  * of the circle to draw. Use the GFX_QUADRANTn, GFX_xHALF and
387  * GFX_WHOLE constants and OR them together if required. Radius equal to
388  * zero gives a single pixel.
389  *
390  * \note This function only supports quadrants while gfx_draw_circle()
391  *       supports octants. This is to improve performance on drawing
392  *       filled circles.
393  *
394  * \param[in]  x           X coordinate of center.
395  * \param[in]  y           Y coordinate of center.
396  * \param[in]  radius      Circle radius in pixels.
397  * \param[in]  color       Pixel operation.
398  * \param[in]  quadrant_mask Bitmask indicating which quadrants to draw.
399  */
gfx_mono_generic_draw_filled_circle(gfx_coord_t x,gfx_coord_t y,gfx_coord_t radius,enum gfx_mono_color color,uint8_t quadrant_mask)400 void gfx_mono_generic_draw_filled_circle(gfx_coord_t x, gfx_coord_t y,
401 		gfx_coord_t radius, enum gfx_mono_color color,
402 		uint8_t quadrant_mask)
403 {
404 	gfx_coord_t offset_x;
405 	gfx_coord_t offset_y;
406 	int16_t error;
407 
408 	/* Draw only a pixel if radius is zero. */
409 	if (radius == 0) {
410 		gfx_mono_draw_pixel(x, y, color);
411 		return;
412 	}
413 
414 	/* Set up start iterators. */
415 	offset_x = 0;
416 	offset_y = radius;
417 	error = 3 - 2 * radius;
418 
419 	/* Iterate offset_x from 0 to radius. */
420 	while (offset_x <= offset_y) {
421 		/* Draw vertical lines tracking each quadrant. */
422 		if (quadrant_mask & GFX_QUADRANT0) {
423 			gfx_mono_draw_vertical_line(x + offset_y,
424 					y - offset_x, offset_x + 1, color);
425 			gfx_mono_draw_vertical_line(x + offset_x,
426 					y - offset_y, offset_y + 1, color);
427 		}
428 
429 		if (quadrant_mask & GFX_QUADRANT1) {
430 			gfx_mono_draw_vertical_line(x - offset_y,
431 					y - offset_x, offset_x + 1, color);
432 			gfx_mono_draw_vertical_line(x - offset_x,
433 					y - offset_y, offset_y + 1, color);
434 		}
435 
436 		if (quadrant_mask & GFX_QUADRANT2) {
437 			gfx_mono_draw_vertical_line(x - offset_y,
438 					y, offset_x + 1, color);
439 			gfx_mono_draw_vertical_line(x - offset_x,
440 					y, offset_y + 1, color);
441 		}
442 
443 		if (quadrant_mask & GFX_QUADRANT3) {
444 			gfx_mono_draw_vertical_line(x + offset_y,
445 					y, offset_x + 1, color);
446 			gfx_mono_draw_vertical_line(x + offset_x,
447 					y, offset_y + 1, color);
448 		}
449 
450 		/* Update error value and step offset_y when required. */
451 		if (error < 0) {
452 			error += ((offset_x << 2) + 6);
453 		} else {
454 			error += (((offset_x - offset_y) << 2) + 10);
455 			--offset_y;
456 		}
457 
458 		/* Next X. */
459 		++offset_x;
460 	}
461 }
462 
463 /**
464  * \brief Put bitmap from FLASH or RAM to display
465  *
466  * This function will output bitmap data from FLASH or RAM.
467  * The bitmap y-coordinate will be aligned with display pages, rounded down.
468  * Ie: placing a bitmap at x=10, y=5 will put the bitmap at x = 10,y = 0 and
469  * placing a bitmap at x = 10, y = 10 will put the bitmap at x = 10, y = 8
470  *
471  */
gfx_mono_generic_put_bitmap(struct gfx_mono_bitmap * bitmap,gfx_coord_t x,gfx_coord_t y)472 void gfx_mono_generic_put_bitmap(struct gfx_mono_bitmap *bitmap, gfx_coord_t x,
473 		gfx_coord_t y)
474 {
475 	gfx_coord_t num_pages = bitmap->height / 8;
476 	gfx_coord_t page = y / 8;
477 	gfx_coord_t column;
478 	gfx_coord_t i;
479 	gfx_mono_color_t temp;
480 
481 	switch (bitmap->type) {
482 	case GFX_MONO_BITMAP_PROGMEM:
483 		for (i = 0; i < num_pages; i++) {
484 			for (column = 0; column < bitmap->width; column++) {
485 				temp = PROGMEM_READ_BYTE(bitmap->data.progmem
486 						+ (i * bitmap->width)
487 						+ column);
488 				gfx_mono_put_byte(i + page, column + x, temp);
489 			}
490 		}
491 		break;
492 
493 	case GFX_MONO_BITMAP_RAM:
494 		for (i = 0; i < num_pages; i++) {
495 			gfx_mono_put_page(bitmap->data.pixmap
496 					+ (i * bitmap->width), page + i, x,
497 					bitmap->width);
498 		}
499 		break;
500 
501 	default:
502 		break;
503 	}
504 }
505 
506 /** @} */
507