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