/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007-2009 Joshua Simmons * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "board.h" #include "goban.h" #include "display.h" #include "game.h" #include "sgf.h" /* for xlcd_filltriangle */ #include "lib/xlcd.h" unsigned int intersection_size = 0; #define LINE_OFFSET (intersection_size / 2) /* pixel offsets for the board on the LCD */ int board_x = 0; int board_y = 0; int board_pixel_width = 0; int board_pixel_height = 0; /* current cursor position in board coordinates (intersections, not pixels) */ unsigned short cursor_pos = POS (0, 0); /* we way need to "move" our notion of which intersections to draw when the cursor moves, since we can be zoomed in (and often will be) this is used to signal when we need to do that */ unsigned short last_cursor_pos = INVALID_POS; unsigned int last_int_size = MAX_INT_SIZE + 1; unsigned int min_x_int = 0; unsigned int min_y_int = 0; unsigned int num_x_ints = 0; unsigned int num_y_ints = 0; int extend_t = 0, extend_b = 0, extend_l = 0, extend_r = 0; bool draw_variations = true; unsigned int saved_circle_size = 0; bool has_comment = false; unsigned char display_marks[MAX_BOARD_SIZE * MAX_BOARD_SIZE]; /* function prototypes */ static int pixel_x (unsigned short pos); static int pixel_y (unsigned short pos); static void draw_circle (int c_x, int c_y, int r, bool filled); static void draw_cursor (unsigned short pos); static void draw_stone_raw (int pixel_x, int pixel_y, bool black); static void draw_stone (unsigned short pos, bool black); static void draw_all_stones (void); static void draw_hoshi (unsigned short pos); static void draw_footer (void); static void draw_all_marks (void); static void draw_all_hoshi (void); static unsigned int unzoomed_int_size (void); static void cursor_updated (void); void clear_marks_display (void) { rb->memset (display_marks, ' ', sizeof (display_marks)); has_comment = false; } void set_mark_display (unsigned short pos, unsigned char mark_char) { if (!on_board (pos)) { return; } if ((mark_char == 'b' || mark_char == 'w') && display_marks[I (pos) + J (pos) * board_width] != ' ') { /* don't overwrite real board marks with last-move or variation marks */ return; } display_marks[I (pos) + J (pos) * board_width] = mark_char; } void set_comment_display (bool new_val) { has_comment = new_val; } static void draw_all_marks (void) { unsigned int x, y; for (x = MIN_X; x < MAX_X; ++x) { for (y = MIN_Y; y < MAX_Y; ++y) { if (display_marks[x + y * board_width] != ' ') { #if LCD_DEPTH > 1 if (display_marks[x + y * board_width] != 'b' && display_marks[x + y * board_width] != 'w') { rb->lcd_set_foreground (MARK_COLOR); } else { rb->lcd_set_foreground (CURSOR_COLOR); } rb->lcd_set_drawmode (DRMODE_FG); #else rb->lcd_set_drawmode (DRMODE_FG + DRMODE_COMPLEMENT); #endif if (display_marks[x + y * board_width] & (1 << 7)) { char to_display[2]; int width, height; to_display[0] = display_marks[x + y * board_width] & (~(1 << 7)); to_display[1] = '\0'; rb->lcd_getstringsize (to_display, &width, &height); int display_x = pixel_x (POS (x, y)) + LINE_OFFSET - (width / 2); int display_y = pixel_y (POS (x, y)) + LINE_OFFSET - (height / 2); if (display_x < 0) { display_x = 0; } if (display_y < 0) { display_y = 0; } if (display_x + width >= LCD_WIDTH) { display_x = LCD_WIDTH - 1 - width; } if (display_y + height >= LCD_HEIGHT) { display_y = LCD_HEIGHT - 1 - height; } rb->lcd_putsxy (display_x, display_y, to_display); continue; } switch (display_marks[x + y * board_width]) { /* moves, 'mark', 'square' */ case 'b': case 'w': if (intersection_size <= 5) { DEBUGF ("screen is too small to mark current move\n"); break; } case 'm': if (intersection_size <= 5) { rb->lcd_drawpixel (pixel_x (POS (x, y)) + LINE_OFFSET + 1, pixel_y (POS (x, y)) + LINE_OFFSET + 1); rb->lcd_drawpixel (pixel_x (POS (x, y)) + LINE_OFFSET - 1, pixel_y (POS (x, y)) + LINE_OFFSET - 1); } else { rb->lcd_drawrect (pixel_x (POS (x, y)) + LINE_OFFSET - intersection_size / 6, pixel_y (POS (x, y)) + LINE_OFFSET - intersection_size / 6, (intersection_size / 6) * 2 + 1, (intersection_size / 6) * 2 + 1); } break; case 's': if (intersection_size <= 5) { rb->lcd_drawpixel (pixel_x (POS (x, y)) + LINE_OFFSET + 1, pixel_y (POS (x, y)) + LINE_OFFSET + 1); rb->lcd_drawpixel (pixel_x (POS (x, y)) + LINE_OFFSET - 1, pixel_y (POS (x, y)) + LINE_OFFSET - 1); } else { rb->lcd_fillrect (pixel_x (POS (x, y)) + LINE_OFFSET - intersection_size / 6, pixel_y (POS (x, y)) + LINE_OFFSET - intersection_size / 6, (intersection_size / 6) * 2 + 1, (intersection_size / 6) * 2 + 1); } break; case 'c': if (intersection_size > 7) { draw_circle (pixel_x (POS (x, y)) + LINE_OFFSET, pixel_y (POS (x, y)) + LINE_OFFSET, (intersection_size - 1) / 4, true); break; } /* purposely don't break here, draw small the same as a triangle */ case 't': if (intersection_size <= 7) { rb->lcd_drawpixel (pixel_x (POS (x, y)) + LINE_OFFSET - 1, pixel_y (POS (x, y)) + LINE_OFFSET + 1); rb->lcd_drawpixel (pixel_x (POS (x, y)) + LINE_OFFSET + 1, pixel_y (POS (x, y)) + LINE_OFFSET - 1); } else { xlcd_filltriangle (pixel_x (POS (x, y)) + LINE_OFFSET, pixel_y (POS (x, y)) + LINE_OFFSET - intersection_size / 4, pixel_x (POS (x, y)) + LINE_OFFSET + intersection_size / 4, pixel_y (POS (x, y)) + LINE_OFFSET + intersection_size / 4, pixel_x (POS (x, y)) + LINE_OFFSET - intersection_size / 4, pixel_y (POS (x, y)) + LINE_OFFSET + intersection_size / 4); } break; default: DEBUGF ("tried to display unknown mark '%c' %d\n", display_marks[x + y * board_width], display_marks[x + y * board_width]); break; }; rb->lcd_set_drawmode (DRMODE_SOLID); /* don't have to undo the colors for LCD_DEPTH > 1, most functions assume bg and fg get clobbered */ } } } } static void draw_circle (int c_x, int c_y, int r, bool filled) { int f = 1 - r; int x = 0; int y = r; /* draw the points on the axes to make the loop easier */ rb->lcd_drawpixel (c_x, c_y + r); rb->lcd_drawpixel (c_x, c_y - r); if (filled) { rb->lcd_hline (c_x - r, c_x + r, c_y); } else { rb->lcd_drawpixel (c_x + r, c_y); rb->lcd_drawpixel (c_x - r, c_y); } /* Now walk from the very top of the circle to 1/8th of the way around to the right. For each point, draw the 8 symmetrical points. */ while (x < y) { /* walk one pixel to the right */ ++x; /* And then adjust our discriminant, and adjust y if we've ventured outside of the circle. This boils down to walking a tightrope between being inside and outside the circle. The updating functions are taken from expanding the discriminant function f(x, y) = x^2 + y^2 - r^2 after substituting in x + 1 and y - 1 and then subtracting out f(x, y) */ if (f <= 0) { f += 2 * x + 1; } else { --y; f += (x - y) * 2 + 1; } if (filled) { /* each line takes care of 2 points on the circle so we only need 4 */ rb->lcd_hline (c_x - y, c_x + y, c_y + x); rb->lcd_hline (c_x - y, c_x + y, c_y - x); rb->lcd_hline (c_x - x, c_x + x, c_y + y); rb->lcd_hline (c_x - x, c_x + x, c_y - y); } else { /* Draw all 8 symmetrical points */ rb->lcd_drawpixel (c_x + x, c_y + y); rb->lcd_drawpixel (c_x + y, c_y + x); rb->lcd_drawpixel (c_x + y, c_y - x); rb->lcd_drawpixel (c_x + x, c_y - y); rb->lcd_drawpixel (c_x - x, c_y + y); rb->lcd_drawpixel (c_x - y, c_y + x); rb->lcd_drawpixel (c_x - y, c_y - x); rb->lcd_drawpixel (c_x - x, c_y - y); } } } void draw_screen_display (void) { #if LCD_DEPTH > 1 int saved_fg = rb->lcd_get_foreground (); int saved_bg = rb->lcd_get_background (); #endif int saved_drmode = rb->lcd_get_drawmode (); if (cursor_pos != last_cursor_pos || intersection_size != last_int_size) { cursor_updated (); } #if LCD_DEPTH > 1 rb->lcd_set_backdrop (NULL); rb->lcd_set_foreground (BOARD_COLOR); rb->lcd_set_background (BACKGROUND_COLOR); rb->lcd_set_drawmode (DRMODE_SOLID); #else rb->lcd_set_drawmode (DRMODE_SOLID + DRMODE_INVERSEVID); #endif rb->lcd_clear_display (); rb->lcd_fillrect (pixel_x (POS (MIN_X, MIN_Y)), pixel_y (POS (MIN_X, MIN_Y)), (MAX_X - MIN_X) * intersection_size, (MAX_Y - MIN_Y) * intersection_size); #if LCD_DEPTH > 1 rb->lcd_set_foreground (LINE_COLOR); #else rb->lcd_set_drawmode (DRMODE_SOLID); #endif unsigned int i; for (i = MIN_Y; i < MAX_Y; ++i) { rb->lcd_hline (pixel_x (POS (MIN_X, i)) + LINE_OFFSET + extend_l, pixel_x (POS (MAX_X - 1, i)) + LINE_OFFSET + extend_r, pixel_y (POS (MIN_X, i)) + LINE_OFFSET); } for (i = MIN_X; i < MAX_X; ++i) { rb->lcd_vline (pixel_x (POS (i, MIN_Y)) + LINE_OFFSET, pixel_y (POS (i, MIN_Y)) + LINE_OFFSET + extend_t, pixel_y (POS (i, MAX_Y - 1)) + LINE_OFFSET + extend_b); } draw_all_hoshi (); draw_all_stones (); draw_cursor (cursor_pos); if (draw_variations) { mark_child_variations_sgf (); } draw_all_marks (); draw_footer (); rb->lcd_update (); #if LCD_DEPTH > 1 rb->lcd_set_foreground (saved_fg); rb->lcd_set_background (saved_bg); #endif rb->lcd_set_drawmode (saved_drmode); } #if defined(GBN_WIDE_SCREEN) /* the size of the string, in pixels, when drawn vertically */ static void vert_string_size (char *string, int *width, int *height) { int temp_width = 0; int temp_height = 0; int ret_width = 0; int ret_height = 0; char temp_buffer[2]; temp_buffer[0] = temp_buffer[1] = 0; if (!string) { return; } while (*string) { temp_buffer[0] = *string; rb->lcd_getstringsize (temp_buffer, &temp_width, &temp_height); ret_height += temp_height; if (ret_width < temp_width) { ret_width = temp_width; } ++string; } if (width) { *width = ret_width; } if (height) { *height = ret_height; } } static void putsxy_vertical (int x, int y, int width, char *str) { int temp_width = 0; int temp_height = 0; char temp_buffer[2]; temp_buffer[0] = temp_buffer[1] = 0; if (!str) { return; } while (*str) { temp_buffer[0] = *str; rb->lcd_getstringsize (temp_buffer, &temp_width, &temp_height); DEBUGF ("putting %s at %d %d\n", temp_buffer, x + (width - temp_width) / 2, y); rb->lcd_putsxy (x + (width - temp_width) / 2, y, temp_buffer); y += temp_height; ++str; } } #endif /* GBN_WIDE_SCREEN */ static void draw_footer (void) { char captures_buffer[16]; char display_flags[16] = ""; int size_x, size_y; int vert_x, vert_y; (void) vert_x; (void) vert_y; #if LCD_DEPTH > 1 rb->lcd_set_background (BACKGROUND_COLOR); rb->lcd_set_foreground (BLACK_COLOR); #else rb->lcd_set_drawmode (DRMODE_SOLID + DRMODE_INVERSEVID); #endif rb->snprintf (captures_buffer, sizeof (captures_buffer), "%d", white_captures); rb->lcd_getstringsize (captures_buffer, &size_x, &size_y); #if defined(GBN_TALL_SCREEN) rb->lcd_putsxy (size_y + 2, LCD_HEIGHT - size_y, captures_buffer); #else vert_string_size (captures_buffer, &vert_x, &vert_y); if (board_pixel_width + size_x <= LCD_WIDTH) { rb->lcd_putsxy (LCD_WIDTH - size_x - 1, vert_x + 2, captures_buffer); } else { putsxy_vertical (LCD_WIDTH - vert_x - 1, vert_x + 2, vert_x, captures_buffer); } #endif #if LCD_DEPTH == 1 rb->lcd_set_drawmode (DRMODE_SOLID); #endif #if defined(GBN_TALL_SCREEN) draw_circle (size_y / 2, LCD_HEIGHT - (size_y / 2), (size_y - 1) / 2, true); #if LCD_DEPTH == 1 rb->lcd_set_drawmode (DRMODE_SOLID + DRMODE_INVERSEVID); draw_circle (size_y / 2, LCD_HEIGHT - (size_y / 2), (size_y - 1) / 2, false); #endif /* LCD_DEPTH */ #else /* !GBN_TALL_SCREEN */ draw_circle (LCD_WIDTH - 1 - vert_x / 2, (vert_x / 2), (vert_x - 1) / 2, true); #if LCD_DEPTH == 1 rb->lcd_set_drawmode (DRMODE_SOLID + DRMODE_INVERSEVID); draw_circle (LCD_WIDTH - 1 - vert_x / 2, (vert_x / 2), (vert_x - 1) / 2, false); #endif /* LCD_DEPTH */ #endif /* GBN_TALL_SCREEN */ #if LCD_DEPTH > 1 rb->lcd_set_foreground (WHITE_COLOR); #endif rb->snprintf (captures_buffer, sizeof (captures_buffer), "%d", black_captures); rb->lcd_getstringsize (captures_buffer, &size_x, &size_y); #if defined(GBN_TALL_SCREEN) rb->lcd_putsxy (LCD_WIDTH - (size_y + 1) - size_x, LCD_HEIGHT - size_y, captures_buffer); draw_circle (LCD_WIDTH - (size_y / 2), LCD_HEIGHT - (size_y / 2), (size_y - 1) / 2, true); #else vert_string_size (captures_buffer, &vert_x, &vert_y); if (board_pixel_width + size_x <= LCD_WIDTH) { rb->lcd_putsxy (LCD_WIDTH - size_x - 1, LCD_HEIGHT - vert_x - size_y - 3, captures_buffer); } else { putsxy_vertical (LCD_WIDTH - vert_x - 1, LCD_HEIGHT - vert_x - 3 - vert_y, vert_x, captures_buffer); } draw_circle (LCD_WIDTH - 1 - vert_x / 2, LCD_HEIGHT - 1 - vert_x / 2, (vert_x - 1) / 2, true); #endif #if LCD_DEPTH > 1 rb->lcd_set_foreground (BLACK_COLOR); #endif if (has_comment) { rb->strcat (display_flags, "C"); } if (has_more_nodes_sgf ()) { rb->strcat (display_flags, "+"); } if (num_variations_sgf () > 1) { rb->strcat (display_flags, "*"); } rb->snprintf (captures_buffer, sizeof (captures_buffer), "%d%s", move_num, display_flags); rb->lcd_getstringsize (captures_buffer, &size_x, &size_y); #if defined(GBN_TALL_SCREEN) rb->lcd_putsxy ((LCD_WIDTH - size_x) / 2, LCD_HEIGHT - size_y, captures_buffer); #else if (board_pixel_width + size_x <= LCD_WIDTH) { rb->lcd_putsxy (LCD_WIDTH - size_x - 1, (LCD_HEIGHT - size_y) / 2, captures_buffer); } else { vert_string_size (captures_buffer, &vert_x, &vert_y); putsxy_vertical (LCD_WIDTH - vert_x - 1, (LCD_HEIGHT - vert_y) / 2, vert_x, captures_buffer); } #endif rb->lcd_set_drawmode (DRMODE_SOLID); } static int pixel_x (unsigned short pos) { return board_x + (I (pos) - min_x_int) * intersection_size; } static int pixel_y (unsigned short pos) { return board_y + (J (pos) - min_y_int) * intersection_size; } void move_display (unsigned short pos) { if (!on_board (pos)) { return; } while ((unsigned) I (pos) >= REAL_MAX_X) { cursor_pos = EAST (cursor_pos); cursor_updated (); } while ((unsigned) I (pos) < REAL_MIN_X) { cursor_pos = WEST (cursor_pos); cursor_updated (); } while ((unsigned) J (pos) >= REAL_MAX_Y) { cursor_pos = SOUTH (cursor_pos); cursor_updated (); } while ((unsigned) J (pos) < REAL_MIN_Y) { cursor_pos = NORTH (cursor_pos); cursor_updated (); } } static void cursor_updated (void) { if (!on_board(cursor_pos)) { cursor_pos = WRAP (cursor_pos); } if (intersection_size != last_int_size || ((unsigned) I (cursor_pos)) < REAL_MIN_X + 1 || ((unsigned) I (cursor_pos)) > REAL_MAX_X - 2 || ((unsigned) J (cursor_pos)) < REAL_MIN_Y + 1 || ((unsigned) J (cursor_pos)) > REAL_MAX_Y - 2) { if ((unsigned) I (cursor_pos) < (num_x_ints / 2)) { min_x_int = 0; } else { min_x_int = min (I (cursor_pos) - (num_x_ints / 2), board_width - num_x_ints); } if ((unsigned) J (cursor_pos) < (num_y_ints / 2)) { min_y_int = 0; } else { min_y_int = min (J (cursor_pos) - (num_y_ints / 2), board_height - num_y_ints); } } /* these are used in line drawing to extend the lines if there is more board in that direction */ if (MIN_X) { extend_l = -1 * LINE_OFFSET; } else { extend_l = 0; } if (MIN_Y) { extend_t = -1 * LINE_OFFSET; } else { extend_t = 0; } if (MAX_X != board_width) { extend_r = LINE_OFFSET; } else { extend_r = 0; } if (MAX_Y != board_height) { extend_b = LINE_OFFSET; } else { extend_b = 0; } last_cursor_pos = cursor_pos; last_int_size = intersection_size; } static unsigned int unzoomed_int_size (void) { int int_size = min ((LCD_BOARD_WIDTH / board_width), (LCD_BOARD_HEIGHT / board_height)); if (!(int_size & 1)) { --int_size; } if (int_size < 0) { int_size = 1; } return max(int_size, MIN_INT_SIZE); } unsigned int current_zoom_display (void) { return (intersection_size - unzoomed_int_size ()) / 2 + 1; } unsigned int max_zoom_display (void) { return (MAX_INT_SIZE - unzoomed_int_size ()) / 2 + 1; } unsigned int min_zoom_display (void) { if (MIN_INT_SIZE >= unzoomed_int_size()) { return (MIN_INT_SIZE - unzoomed_int_size ()) / 2 + 1; } else { return 1; } } void set_zoom_display (unsigned int zoom_level) { unsigned int unzoomed = unzoomed_int_size (); if (saved_circle_size < MIN_INT_SIZE || saved_circle_size > MAX_INT_SIZE) { saved_circle_size = MIN_DEFAULT_INT_SIZE; } if (zoom_level == 0) { /* default zoom, we get to set it however we want */ intersection_size = max (unzoomed, saved_circle_size); } else { intersection_size = unzoomed + 2 * (zoom_level - 1); } if (intersection_size > MAX_INT_SIZE) { intersection_size = MAX_INT_SIZE; } /* now intersection_size has been set appropriately, so set up all of the derived values used for display */ num_x_ints = min (LCD_BOARD_WIDTH / intersection_size, board_width); num_y_ints = min (LCD_BOARD_HEIGHT / intersection_size, board_height); board_pixel_width = num_x_ints * intersection_size; board_pixel_height = num_y_ints * intersection_size; #if defined(GBN_TALL_SCREEN) board_x = (LCD_WIDTH - board_pixel_width) / 2; board_y = 0; #elif defined(GBN_WIDE_SCREEN) board_x = 0; board_y = (LCD_HEIGHT - board_pixel_height) / 2; #else #error screen dimensions have not been evaluated properly #endif } /* Call every time the board size might have changed! */ void setup_display (void) { set_zoom_display (0); /* 0 means set to default */ /* The cursor starts out in the top right of the board * (on the hoshi point for most board sizes), unless the board * is really small in which case the cursor starts at the center * of the board. */ int start_x, start_y; if (board_width >= 7) { start_x = board_width - 4; } else { start_x = board_width / 2; } if (board_height >= 7) { start_y = 3; } else { start_y = board_height / 2; } cursor_pos = POS (start_x, start_y); last_cursor_pos = INVALID_POS; last_int_size = -1; clear_marks_display (); } static void draw_cursor (unsigned short pos) { if (!on_board (pos)) { return; } #if LCD_DEPTH > 1 rb->lcd_set_foreground (CURSOR_COLOR); #else rb->lcd_set_drawmode (DRMODE_COMPLEMENT); #endif rb->lcd_drawrect (pixel_x (pos), pixel_y (pos), intersection_size, intersection_size); rb->lcd_set_drawmode (DRMODE_SOLID); } static void draw_stone_raw (int pixel_x, int pixel_y, bool black) { #if LCD_DEPTH > 1 rb->lcd_set_foreground (black ? BLACK_COLOR : WHITE_COLOR); #else int draw_mode; /* check whether foreground is bright or dark */ #if defined(HAVE_NEGATIVE_LCD) draw_mode = DRMODE_SOLID | (black ? DRMODE_INVERSEVID : 0); #else draw_mode = DRMODE_SOLID | (black ? 0 : DRMODE_INVERSEVID); #endif /* HAVE_NEGATIVE_LCD */ rb->lcd_set_drawmode (draw_mode); #endif draw_circle (pixel_x + LINE_OFFSET, pixel_y + LINE_OFFSET, LINE_OFFSET, true); #if defined(OUTLINE_STONES) #if LCD_DEPTH > 1 rb->lcd_set_foreground (black ? WHITE_COLOR : BLACK_COLOR); #else rb->lcd_set_drawmode (draw_mode ^ DRMODE_INVERSEVID); #endif /* LCD_DEPTH > 1 */ /* outline stones of background color only */ if (draw_mode & DRMODE_INVERSEVID) { draw_circle (pixel_x + LINE_OFFSET, pixel_y + LINE_OFFSET, LINE_OFFSET, false); } #endif /* OUTLINE_STONES */ rb->lcd_set_drawmode (DRMODE_SOLID); } static void draw_stone (unsigned short pos, bool black) { if (!on_board (pos)) { return; } draw_stone_raw (pixel_x (pos), pixel_y (pos), black); } static void draw_all_stones (void) { unsigned int x, y; unsigned short temp_pos; for (x = MIN_X; x < MAX_X; ++x) { for (y = MIN_Y; y < MAX_Y; ++y) { temp_pos = POS (x, y); if (get_point_board (temp_pos) == EMPTY) { continue; } draw_stone (temp_pos, get_point_board (temp_pos) == BLACK); } } } static void draw_hoshi (unsigned short pos) { /* color and drawmode are already set before this function (all lines and hoshi and stuff are drawn together) */ if (!on_board(pos)) { return; } if ((unsigned) I (pos) < MIN_X || (unsigned) I (pos) >= MAX_X || (unsigned) J (pos) < MIN_Y || (unsigned) J (pos) >= MAX_Y) { return; } if (intersection_size > 8) { rb->lcd_fillrect (pixel_x (pos) + LINE_OFFSET - 1, pixel_y (pos) + LINE_OFFSET - 1, 3, 3); } else { rb->lcd_drawpixel (pixel_x (pos) + LINE_OFFSET - 1, pixel_y (pos) + LINE_OFFSET - 1); rb->lcd_drawpixel (pixel_x (pos) + LINE_OFFSET + 1, pixel_y (pos) + LINE_OFFSET + 1); } } static void draw_all_hoshi (void) { if (board_width != board_height || board_width <= 2 || board_width == 4 || board_width == 6) { return; } if (board_width == 3) { draw_hoshi (POS (1, 1)); return; } if (board_width == 5) { draw_hoshi (POS (1, 1)); draw_hoshi (POS (1, 3)); draw_hoshi (POS (2, 2)); draw_hoshi (POS (3, 1)); draw_hoshi (POS (3, 3)); return; } /* special case boards taken care of, handle the general case * * Note: board_width == board_height here (or we bailed out) so the two * are interchangeable */ int low_edge = (board_width < 12) ? 2 : 3; int high_edge = board_width - low_edge - 1; int mid = board_width / 2; /* corner hoshi, for big enough boards */ if (board_width > 7) { draw_hoshi (POS (low_edge, low_edge)); draw_hoshi (POS (low_edge, high_edge)); draw_hoshi (POS (high_edge, low_edge)); draw_hoshi (POS (high_edge, high_edge)); } /* side hoshi, for big enough boards (only makes sense on odd) */ if (board_width > 12 && board_width % 2 == 1) { draw_hoshi (POS (low_edge, mid)); draw_hoshi (POS (mid, low_edge)); draw_hoshi (POS (high_edge, mid)); draw_hoshi (POS (mid, high_edge)); } /* center hoshi */ if (board_width % 2 == 1) { draw_hoshi (POS (mid, mid)); } }