/** * \file bitmap.c * \brief Bitmap manipulation support * * \subsection Copyright * Copyright (c) 2013 Michael Buesch * * \subsection License * Licensed under the terms of the GNU General Public License version 2. */ #include "bitmap.h" #include "font.h" #include /** \brief Gibt die Breite eines Bitmaps zurueck. * Beruecksichtigt die Rotation. */ uint8_t bitmap_width(struct bitmap *bitmap) { if (bitmap->rotation == BITMAP_ROT_0DEG || bitmap->rotation == BITMAP_ROT_180DEG) return bitmap->width; return bitmap->height; } /** \brief Gibt die Hoehe eines Bitmaps zurueck. * Beruecksichtigt die Rotation. */ uint8_t bitmap_height(struct bitmap *bitmap) { if (bitmap->rotation == BITMAP_ROT_0DEG || bitmap->rotation == BITMAP_ROT_180DEG) return bitmap->height; return bitmap->width; } /** \brief Pixel in Bitmap beschreiben. * * \param bitmap Das Bitmap. * * \param x X Koordinate des Pixels. * * \param y Y Koordinate des Pixels. * * \param on Der neue Pixelwert. (1/0) */ void bitmap_pixel_write(struct bitmap *bitmap, uint8_t x, uint8_t y, bool on) { uint8_t y_bit, *b, tmp_x, mask; /* Koordinaten nach Rotation uebersetzen. */ switch (bitmap->rotation) { case BITMAP_ROT_0DEG: default: break; case BITMAP_ROT_90DEG: tmp_x = x; x = bitmap->width - y - 1; y = tmp_x; break; case BITMAP_ROT_180DEG: x = bitmap->width - x - 1; y = bitmap->height - y - 1; break; case BITMAP_ROT_270DEG: tmp_x = x; x = y; y = bitmap->height - tmp_x - 1; break; } /* Zeiger auf Byte auswaehlen. */ y_bit = y % 8; b = bitmap_column_byte(bitmap, x, y); /* Pixel-Bit setzen oder loeschen. */ mask = BITMASK8(y_bit); if (on) *b |= mask; else *b &= ~mask; } /** \brief Rechteckigen Bereich im Bitmap fuellen. * * \param bitmap Das Bitmap. * * \param area_x X Koordinate des Bereichs. * * \param area_y Y Koordinate des Bereichs. * * \param area_width Breite des Bereichs. * * \param area_height Hoehe des Bereichs. * * \param black 1 -> Schwarz zeichnen. 0 -> Weiss zeichnen. */ void bitmap_area_fill(struct bitmap *bitmap, uint8_t area_x, uint8_t area_y, uint8_t area_width, uint8_t area_height, bool black) { uint8_t x, y, tmp_x, tmp_width; uint8_t *b; /* Koordinaten nach Rotation uebersetzen. */ switch (bitmap->rotation) { case BITMAP_ROT_0DEG: default: break; case BITMAP_ROT_90DEG: tmp_x = x; x = bitmap->width - y - 1; y = tmp_x; tmp_width = area_width; area_width = area_height; area_height = tmp_width; break; case BITMAP_ROT_180DEG: x = bitmap->width - x - 1; y = bitmap->height - y - 1; break; case BITMAP_ROT_270DEG: tmp_x = x; x = y; y = bitmap->height - tmp_x - 1; tmp_width = area_width; area_width = area_height; area_height = tmp_width; break; } y = area_y; if (y % 8) { /* TODO: Spezialbehandlung wenn area_y kein Vielfaches von 8 ist. * Kommt in diesem Projekt nicht vor. */ } /* Y-Bereich durchlaufen. */ while (area_height >= 8) { /* X-Bereich durchlaufen. */ for (x = area_x; x < area_x + area_width; x++) { b = bitmap_column_byte(bitmap, x, y); /* Pixel-Bits loeschen oder setzen. */ if (black) *b = 0xFF; else *b = 0; } area_height -= 8; y += 8; } if (area_height) { /* TODO: Spezialbehandlung wenn area_height kein Vielfaches von 8 ist. * Kommt in diesem Projekt nicht vor. */ } } /** \brief Linie in Bitmap zeichnen. * * \param bitmap Das Bitmap. * * \param x_from X Startkoordinate der Linie. * * \param y_from Y Startkoordinate der Linie. * * \param x_to X Endkoordinate der Linie. * * \param y_to Y Endkoordinate der Linie. * * \param black 1 -> Schwarz zeichnen. 0 -> Weiss zeichnen. */ void bitmap_draw_line(struct bitmap *bitmap, uint8_t x_from, uint8_t y_from, uint8_t x_to, uint8_t y_to, bool black) { uint8_t x, y, error = 0; uint8_t dist_x, dist_y; int8_t x_inc, y_inc; /* Richtung und Distanz ermitteln. */ if (x_to < x_from) { dist_x = x_from - x_to; x_inc = -1; } else { dist_x = x_to - x_from; x_inc = 1; } if (y_to < y_from) { dist_y = y_from - y_to; y_inc = -1; } else { dist_y = y_to - y_from; y_inc = 1; } x = x_from; y = y_from; /* Erstes Pixel schreiben. */ bitmap_pixel_write(bitmap, x, y, black); /* Richtung abfragen. */ if (dist_x >= dist_y) { /* Alle Pixel zeichnen. */ while (x != x_to) { x += x_inc; error += dist_y * 2; if (error > dist_x) { y += y_inc; error -= dist_x * 2; } bitmap_pixel_write(bitmap, x, y, black); } } else { /* Alle Pixel zeichnen. */ while (y != y_to) { y += y_inc; error += dist_x * 2; if (error > dist_y) { x += x_inc; error -= dist_y * 2; } bitmap_pixel_write(bitmap, x, y, black); } } } /** \brief XBM in Bitmap zeichnen. * * \param bitmap Das Bitmap. * * \param x_pos X Zielkoordinate. * * \param y_pos Y Zielkoordinate. * * \param xbm Das XBM Bitmap. * * \param xbm_width Breite des XBM Bitmaps. * * \param xbm_height Hoehe des XBM Bitmaps. */ void bitmap_draw_xbm(struct bitmap *bitmap, uint8_t x_pos, uint8_t y_pos, const uint8_t __flash *xbm, unsigned int xbm_width, unsigned int xbm_height) { uint8_t x, y, mask; uint8_t bm_width = bitmap_width(bitmap); uint8_t bm_height = bitmap_height(bitmap); if (x_pos >= bm_width || y_pos >= bm_height || x_pos + xbm_width > bm_width || y_pos + xbm_height > bm_height) { /* Ungueltige Koordinaten. */ return; } /* Das XBM Pixelweise durchlaufen und Pixel zeichnen. */ for (y = 0; y < xbm_height; y++) { mask = 0x01; for (x = 0; x < xbm_width; x++) { bitmap_pixel_write(bitmap, x_pos + x, y_pos + y, !!(*xbm & mask)); mask <<= 1; if (!mask) { mask = 0x01; xbm++; } } if (mask != 0x01) xbm++; } } /** \brief ASCII Zeichen in Bitmap zeichnen. * * \param bitmap Das Bitmap. * * \param x_pos X Zielkoordinate. * * \param y_pos Y Zielkoordinate. * * \param font_desc Der Font der zum Zeichnen verwendet werden soll. * * \param ascii Das ASCII Zeichen das gezeichnet werden soll. */ void bitmap_draw_char(struct bitmap *bitmap, uint8_t x_pos, uint8_t y_pos, const struct font_descriptor __flash *font_desc, char ascii) { const uint8_t __flash *xbm; uint8_t font_width, font_height, font_xbm_size; font_width = font_get_width(font_desc); font_height = font_get_height(font_desc); font_xbm_size = font_height * (div_round_up(font_width, 8)); /* ASCII Zeichensatz auswaehlen. */ if (ascii >= '0' && ascii <= '9') { xbm = font_get_numbers(font_desc); xbm += font_xbm_size * (ascii - '0'); } else if (ascii >= 'a' && ascii <= 'z') { xbm = font_get_lowercase(font_desc); xbm += font_xbm_size * (ascii - 'a'); } else if (ascii >= 'A' && ascii <= 'Z') { xbm = font_get_uppercase(font_desc); xbm += font_xbm_size * (ascii - 'A'); } else return; /* Zeichen in Bitmap zeichnen. */ bitmap_draw_xbm(bitmap, x_pos, y_pos, xbm, font_width, font_height); } /** \brief ASCII String in Bitmap zeichnen. * * \param bitmap Das Bitmap. * * \param x_pos X Zielkoordinate. * * \param y_pos Y Zielkoordinate. * * \param font_desc Der Font der zum Zeichnen verwendet werden soll. * * \param string Der NUL-terminierte String, der gezeichnet werden soll. */ void bitmap_draw_string(struct bitmap *bitmap, uint8_t x_pos, uint8_t y_pos, const struct font_descriptor __flash *font_desc, const char __memx *string) { uint8_t x = 0, y = 0, font_width, font_height; char c; uint8_t bm_width = bitmap_width(bitmap); uint8_t bm_height = bitmap_height(bitmap); font_width = font_get_width(font_desc); font_height = font_get_height(font_desc); /* Jedes Zeichen im Bitmap zeichnen. */ while (1) { c = *string; if (!c) { /* Stringende. */ break; } /* Das ASCII Zeichen zeichnen. */ bitmap_draw_char(bitmap, x_pos + x, y_pos + y, font_desc, c); string++; /* Spalte inkrementieren. */ x += font_width + 2; /* Zeilenumbruch? */ if (x + font_width > bm_width) { x = 0; /* Zeile Inkrementieren. */ y += font_height + 2; if (y + font_height > bm_height) break; } } }