1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-11-30 08:24:11 +01:00
LibrePilot/flight/modules/Osd/osdgen/osdgen.c

2497 lines
91 KiB
C

/**
******************************************************************************
* @addtogroup OpenPilotModules OpenPilot Modules
* @{
* @addtogroup OSDgenModule osdgen Module
* @brief Process OSD information
* @{
*
* @file osdgen.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @brief OSD gen module, handles OSD draw. Parts from CL-OSD and SUPEROSD projects
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// ****************
#include <openpilot.h>
#include "osdgen.h"
#include "attitudestate.h"
#include "gpsposition.h"
#include "homelocation.h"
#include "gpstime.h"
#include "gpssatellites.h"
#include "osdsettings.h"
#include "barosensor.h"
#include "taskinfo.h"
#include "flightstatus.h"
#include "fonts.h"
#include "font12x18.h"
#include "font8x10.h"
#include "WMMInternal.h"
#include "splash.h"
/*
static uint16_t angleA=0;
static int16_t angleB=90;
static int16_t angleC=0;
static int16_t sum=2;
static int16_t m_pitch=0;
static int16_t m_roll=0;
static int16_t m_yaw=0;
static int16_t m_batt=0;
static int16_t m_alt=0;
static uint8_t m_gpsStatus=0;
static int32_t m_gpsLat=0;
static int32_t m_gpsLon=0;
static float m_gpsAlt=0;
static float m_gpsSpd=0;*/
extern uint8_t *draw_buffer_level;
extern uint8_t *draw_buffer_mask;
extern uint8_t *disp_buffer_level;
extern uint8_t *disp_buffer_mask;
TTime timex;
// ****************
// Private functions
static void osdgenTask(void *parameters);
// ****************
// Private constants
#define LONG_TIME 0xffff
xSemaphoreHandle osdSemaphore = NULL;
#define STACK_SIZE_BYTES 4096
#define TASK_PRIORITY (tskIDLE_PRIORITY + 4)
#define UPDATE_PERIOD 100
// ****************
// Private variables
static xTaskHandle osdgenTaskHandle;
struct splashEntry {
unsigned int width, height;
const uint16_t *level;
const uint16_t *mask;
};
struct splashEntry splash[3] = {
{ oplogo_width,
oplogo_height,
oplogo_bits,
oplogo_mask_bits },
{ level_width,
level_height,
level_bits,
level_mask_bits },
{ llama_width,
llama_height,
llama_bits,
llama_mask_bits }
};
uint16_t mirror(uint16_t source)
{
int result = ((source & 0x8000) >> 7) | ((source & 0x4000) >> 5) | ((source & 0x2000) >> 3) | ((source & 0x1000) >> 1) | ((source & 0x0800) << 1)
| ((source & 0x0400) << 3) | ((source & 0x0200) << 5) | ((source & 0x0100) << 7) | ((source & 0x0080) >> 7) | ((source & 0x0040) >> 5)
| ((source & 0x0020) >> 3) | ((source & 0x0010) >> 1) | ((source & 0x0008) << 1) | ((source & 0x0004) << 3) | ((source & 0x0002) << 5)
| ((source & 0x0001) << 7);
return result;
}
void clearGraphics()
{
memset((uint8_t *)draw_buffer_mask, 0, GRAPHICS_WIDTH * GRAPHICS_HEIGHT);
memset((uint8_t *)draw_buffer_level, 0, GRAPHICS_WIDTH * GRAPHICS_HEIGHT);
}
void copyimage(uint16_t offsetx, uint16_t offsety, int image)
{
// check top/left position
if (!validPos(offsetx, offsety)) {
return;
}
struct splashEntry splash_info;
splash_info = splash[image];
offsetx = offsetx / 8;
for (uint16_t y = offsety; y < ((splash_info.height) + offsety); y++) {
uint16_t x1 = offsetx;
for (uint16_t x = offsetx; x < (((splash_info.width) / 16) + offsetx); x++) {
draw_buffer_level[y * GRAPHICS_WIDTH + x1 + 1] = (uint8_t)(
mirror(splash_info.level[(y - offsety) * ((splash_info.width) / 16) + (x - offsetx)]) >> 8);
draw_buffer_level[y * GRAPHICS_WIDTH + x1] = (uint8_t)(
mirror(splash_info.level[(y - offsety) * ((splash_info.width) / 16) + (x - offsetx)]) & 0xFF);
draw_buffer_mask[y * GRAPHICS_WIDTH + x1 + 1] = (uint8_t)(
mirror(splash_info.mask[(y - offsety) * ((splash_info.width) / 16) + (x - offsetx)]) >> 8);
draw_buffer_mask[y * GRAPHICS_WIDTH + x1] = (uint8_t)(mirror(splash_info.mask[(y - offsety) * ((splash_info.width) / 16) + (x - offsetx)]) & 0xFF);
x1 += 2;
}
}
}
uint8_t validPos(uint16_t x, uint16_t y)
{
if (x < GRAPHICS_HDEADBAND || x >= GRAPHICS_WIDTH_REAL || y >= GRAPHICS_HEIGHT_REAL) {
return 0;
}
return 1;
}
// Credit for this one goes to wikipedia! :-)
void drawCircle(uint16_t x0, uint16_t y0, uint16_t radius)
{
int f = 1 - radius;
int ddF_x = 1;
int ddF_y = -2 * radius;
int x = 0;
int y = radius;
write_pixel_lm(x0, y0 + radius, 1, 1);
write_pixel_lm(x0, y0 - radius, 1, 1);
write_pixel_lm(x0 + radius, y0, 1, 1);
write_pixel_lm(x0 - radius, y0, 1, 1);
while (x < y) {
// ddF_x == 2 * x + 1;
// ddF_y == -2 * y;
// f == x*x + y*y - radius*radius + 2*x - y + 1;
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
write_pixel_lm(x0 + x, y0 + y, 1, 1);
write_pixel_lm(x0 - x, y0 + y, 1, 1);
write_pixel_lm(x0 + x, y0 - y, 1, 1);
write_pixel_lm(x0 - x, y0 - y, 1, 1);
write_pixel_lm(x0 + y, y0 + x, 1, 1);
write_pixel_lm(x0 - y, y0 + x, 1, 1);
write_pixel_lm(x0 + y, y0 - x, 1, 1);
write_pixel_lm(x0 - y, y0 - x, 1, 1);
}
}
void swap(uint16_t *a, uint16_t *b)
{
uint16_t temp = *a;
*a = *b;
*b = temp;
}
static const int8_t sinData[91] =
{ 0, 2, 3, 5, 7, 9, 10, 12, 14, 16, 17, 19, 21, 22, 24, 26, 28, 29, 31, 33, 34, 36, 37, 39, 41, 42, 44, 45, 47, 48, 50, 52, 53, 54, 56, 57, 59, 60, 62, 63, 64,
66, 67, 68, 69, 71, 72, 73, 74, 75, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 87, 88, 89, 90, 91, 91, 92, 93, 93, 94, 95, 95, 96, 96, 97, 97, 97, 98,
98, 98, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100 };
static int8_t mySin(uint16_t angle)
{
uint16_t pos = 0;
pos = angle % 360;
int8_t mult = 1;
// 180-359 is same as 0-179 but negative.
if (pos >= 180) {
pos = pos - 180;
mult = -1;
}
// 0-89 is equal to 90-179 except backwards.
if (pos >= 90) {
pos = 180 - pos;
}
return mult * (int8_t)(sinData[pos]);
}
static int8_t myCos(uint16_t angle)
{
return mySin(angle + 90);
}
/// Draws four points relative to the given center point.
///
/// \li centerX + X, centerY + Y
/// \li centerX + X, centerY - Y
/// \li centerX - X, centerY + Y
/// \li centerX - X, centerY - Y
///
/// \param centerX the x coordinate of the center point
/// \param centerY the y coordinate of the center point
/// \param deltaX the difference between the centerX coordinate and each pixel drawn
/// \param deltaY the difference between the centerY coordinate and each pixel drawn
/// \param color the color to draw the pixels with.
void plotFourQuadrants(int32_t centerX, int32_t centerY, int32_t deltaX, int32_t deltaY)
{
write_pixel_lm(centerX + deltaX, centerY + deltaY, 1, 1); // Ist Quadrant
write_pixel_lm(centerX - deltaX, centerY + deltaY, 1, 1); // IInd Quadrant
write_pixel_lm(centerX - deltaX, centerY - deltaY, 1, 1); // IIIrd Quadrant
write_pixel_lm(centerX + deltaX, centerY - deltaY, 1, 1); // IVth Quadrant
}
/// Implements the midpoint ellipse drawing algorithm which is a bresenham
/// style DDF.
///
/// \param centerX the x coordinate of the center of the ellipse
/// \param centerY the y coordinate of the center of the ellipse
/// \param horizontalRadius the horizontal radius of the ellipse
/// \param verticalRadius the vertical radius of the ellipse
/// \param color the color of the ellipse border
void ellipse(int centerX, int centerY, int horizontalRadius, int verticalRadius)
{
int64_t doubleHorizontalRadius = horizontalRadius * horizontalRadius;
int64_t doubleVerticalRadius = verticalRadius * verticalRadius;
int64_t error = doubleVerticalRadius - doubleHorizontalRadius * verticalRadius + (doubleVerticalRadius >> 2);
int x = 0;
int y = verticalRadius;
int deltaX = 0;
int deltaY = (doubleHorizontalRadius << 1) * y;
plotFourQuadrants(centerX, centerY, x, y);
while (deltaY >= deltaX) {
x++;
deltaX += (doubleVerticalRadius << 1);
error += deltaX + doubleVerticalRadius;
if (error >= 0) {
y--;
deltaY -= (doubleHorizontalRadius << 1);
error -= deltaY;
}
plotFourQuadrants(centerX, centerY, x, y);
}
error = (int64_t)(doubleVerticalRadius * (x + 1 / 2.0f) * (x + 1 / 2.0f) + doubleHorizontalRadius * (y - 1) * (y - 1) - doubleHorizontalRadius * doubleVerticalRadius);
while (y >= 0) {
error += doubleHorizontalRadius;
y--;
deltaY -= (doubleHorizontalRadius << 1);
error -= deltaY;
if (error <= 0) {
x++;
deltaX += (doubleVerticalRadius << 1);
error += deltaX;
}
plotFourQuadrants(centerX, centerY, x, y);
}
}
void drawArrow(uint16_t x, uint16_t y, uint16_t angle, uint16_t size)
{
int16_t a = myCos(angle);
int16_t b = mySin(angle);
a = (a * (size / 2)) / 100;
b = (b * (size / 2)) / 100;
write_line_lm((x) - 1 - b, (y) - 1 + a, (x) - 1 + b, (y) - 1 - a, 1, 1); // Direction line
// write_line_lm((GRAPHICS_SIZE/2)-1 + a/2, (GRAPHICS_SIZE/2)-1 + b/2, (GRAPHICS_SIZE/2)-1 - a/2, (GRAPHICS_SIZE/2)-1 - b/2, 1, 1); //Arrow bottom line
write_line_lm((x) - 1 + b, (y) - 1 - a, (x) - 1 - a / 2, (y) - 1 - b / 2, 1, 1); // Arrow "wings"
write_line_lm((x) - 1 + b, (y) - 1 - a, (x) - 1 + a / 2, (y) - 1 + b / 2, 1, 1);
}
void drawBox(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
write_line_lm(x1, y1, x2, y1, 1, 1); // top
write_line_lm(x1, y1, x1, y2, 1, 1); // left
write_line_lm(x2, y1, x2, y2, 1, 1); // right
write_line_lm(x1, y2, x2, y2, 1, 1); // bottom
}
// simple routines
// SUPEROSD routines, modified
/**
* write_pixel: Write a pixel at an x,y position to a given surface.
*
* @param buff pointer to buffer to write in
* @param x x coordinate
* @param y y coordinate
* @param mode 0 = clear bit, 1 = set bit, 2 = toggle bit
*/
void write_pixel(uint8_t *buff, unsigned int x, unsigned int y, int mode)
{
CHECK_COORDS(x, y);
// Determine the bit in the word to be set and the word
// index to set it in.
int bitnum = CALC_BIT_IN_WORD(x);
int wordnum = CALC_BUFF_ADDR(x, y);
// Apply a mask.
uint16_t mask = 1 << (7 - bitnum);
WRITE_WORD_MODE(buff, wordnum, mask, mode);
}
/**
* write_pixel_lm: write the pixel on both surfaces (level and mask.)
* Uses current draw buffer.
*
* @param x x coordinate
* @param y y coordinate
* @param mmode 0 = clear, 1 = set, 2 = toggle
* @param lmode 0 = black, 1 = white, 2 = toggle
*/
void write_pixel_lm(unsigned int x, unsigned int y, int mmode, int lmode)
{
CHECK_COORDS(x, y);
// Determine the bit in the word to be set and the word
// index to set it in.
int bitnum = CALC_BIT_IN_WORD(x);
int wordnum = CALC_BUFF_ADDR(x, y);
// Apply the masks.
uint16_t mask = 1 << (7 - bitnum);
WRITE_WORD_MODE(draw_buffer_mask, wordnum, mask, mmode);
WRITE_WORD_MODE(draw_buffer_level, wordnum, mask, lmode);
}
/**
* write_hline: optimised horizontal line writing algorithm
*
* @param buff pointer to buffer to write in
* @param x0 x0 coordinate
* @param x1 x1 coordinate
* @param y y coordinate
* @param mode 0 = clear, 1 = set, 2 = toggle
*/
void write_hline(uint8_t *buff, unsigned int x0, unsigned int x1, unsigned int y, int mode)
{
CLIP_COORDS(x0, y);
CLIP_COORDS(x1, y);
if (x0 > x1) {
SWAP(x0, x1);
}
if (x0 == x1) {
return;
}
/* This is an optimised algorithm for writing horizontal lines.
* We begin by finding the addresses of the x0 and x1 points. */
int addr0 = CALC_BUFF_ADDR(x0, y);
int addr1 = CALC_BUFF_ADDR(x1, y);
int addr0_bit = CALC_BIT_IN_WORD(x0);
int addr1_bit = CALC_BIT_IN_WORD(x1);
int mask, mask_l, mask_r, i;
/* If the addresses are equal, we only need to write one word
* which is an island. */
if (addr0 == addr1) {
mask = COMPUTE_HLINE_ISLAND_MASK(addr0_bit, addr1_bit);
WRITE_WORD_MODE(buff, addr0, mask, mode);
} else {
/* Otherwise we need to write the edges and then the middle. */
mask_l = COMPUTE_HLINE_EDGE_L_MASK(addr0_bit);
mask_r = COMPUTE_HLINE_EDGE_R_MASK(addr1_bit);
WRITE_WORD_MODE(buff, addr0, mask_l, mode);
WRITE_WORD_MODE(buff, addr1, mask_r, mode);
// Now write 0xffff words from start+1 to end-1.
for (i = addr0 + 1; i <= addr1 - 1; i++) {
uint8_t m = 0xff;
WRITE_WORD_MODE(buff, i, m, mode);
}
}
}
/**
* write_hline_lm: write both level and mask buffers.
*
* @param x0 x0 coordinate
* @param x1 x1 coordinate
* @param y y coordinate
* @param lmode 0 = clear, 1 = set, 2 = toggle
* @param mmode 0 = clear, 1 = set, 2 = toggle
*/
void write_hline_lm(unsigned int x0, unsigned int x1, unsigned int y, int lmode, int mmode)
{
// TODO: an optimisation would compute the masks and apply to
// both buffers simultaneously.
write_hline(draw_buffer_level, x0, x1, y, lmode);
write_hline(draw_buffer_mask, x0, x1, y, mmode);
}
/**
* write_hline_outlined: outlined horizontal line with varying endcaps
* Always uses draw buffer.
*
* @param x0 x0 coordinate
* @param x1 x1 coordinate
* @param y y coordinate
* @param endcap0 0 = none, 1 = single pixel, 2 = full cap
* @param endcap1 0 = none, 1 = single pixel, 2 = full cap
* @param mode 0 = black outline, white body, 1 = white outline, black body
* @param mmode 0 = clear, 1 = set, 2 = toggle
*/
void write_hline_outlined(unsigned int x0, unsigned int x1, unsigned int y, int endcap0, int endcap1, int mode, int mmode)
{
int stroke, fill;
SETUP_STROKE_FILL(stroke, fill, mode)
if (x0 > x1) {
SWAP(x0, x1);
}
// Draw the main body of the line.
write_hline_lm(x0 + 1, x1 - 1, y - 1, stroke, mmode);
write_hline_lm(x0 + 1, x1 - 1, y + 1, stroke, mmode);
write_hline_lm(x0 + 1, x1 - 1, y, fill, mmode);
// Draw the endcaps, if any.
DRAW_ENDCAP_HLINE(endcap0, x0, y, stroke, fill, mmode);
DRAW_ENDCAP_HLINE(endcap1, x1, y, stroke, fill, mmode);
}
/**
* write_vline: optimised vertical line writing algorithm
*
* @param buff pointer to buffer to write in
* @param x x coordinate
* @param y0 y0 coordinate
* @param y1 y1 coordinate
* @param mode 0 = clear, 1 = set, 2 = toggle
*/
void write_vline(uint8_t *buff, unsigned int x, unsigned int y0, unsigned int y1, int mode)
{
unsigned int a;
CLIP_COORDS(x, y0);
CLIP_COORDS(x, y1);
if (y0 > y1) {
SWAP(y0, y1);
}
if (y0 == y1) {
return;
}
/* This is an optimised algorithm for writing vertical lines.
* We begin by finding the addresses of the x,y0 and x,y1 points. */
unsigned int addr0 = CALC_BUFF_ADDR(x, y0);
unsigned int addr1 = CALC_BUFF_ADDR(x, y1);
/* Then we calculate the pixel data to be written. */
unsigned int bitnum = CALC_BIT_IN_WORD(x);
uint16_t mask = 1 << (7 - bitnum);
/* Run from addr0 to addr1 placing pixels. Increment by the number
* of words n each graphics line. */
for (a = addr0; a <= addr1; a += GRAPHICS_WIDTH_REAL / 8) {
WRITE_WORD_MODE(buff, a, mask, mode);
}
}
/**
* write_vline_lm: write both level and mask buffers.
*
* @param x x coordinate
* @param y0 y0 coordinate
* @param y1 y1 coordinate
* @param lmode 0 = clear, 1 = set, 2 = toggle
* @param mmode 0 = clear, 1 = set, 2 = toggle
*/
void write_vline_lm(unsigned int x, unsigned int y0, unsigned int y1, int lmode, int mmode)
{
// TODO: an optimisation would compute the masks and apply to
// both buffers simultaneously.
write_vline(draw_buffer_level, x, y0, y1, lmode);
write_vline(draw_buffer_mask, x, y0, y1, mmode);
}
/**
* write_vline_outlined: outlined vertical line with varying endcaps
* Always uses draw buffer.
*
* @param x x coordinate
* @param y0 y0 coordinate
* @param y1 y1 coordinate
* @param endcap0 0 = none, 1 = single pixel, 2 = full cap
* @param endcap1 0 = none, 1 = single pixel, 2 = full cap
* @param mode 0 = black outline, white body, 1 = white outline, black body
* @param mmode 0 = clear, 1 = set, 2 = toggle
*/
void write_vline_outlined(unsigned int x, unsigned int y0, unsigned int y1, int endcap0, int endcap1, int mode, int mmode)
{
int stroke, fill;
if (y0 > y1) {
SWAP(y0, y1);
}
SETUP_STROKE_FILL(stroke, fill, mode);
// Draw the main body of the line.
write_vline_lm(x - 1, y0 + 1, y1 - 1, stroke, mmode);
write_vline_lm(x + 1, y0 + 1, y1 - 1, stroke, mmode);
write_vline_lm(x, y0 + 1, y1 - 1, fill, mmode);
// Draw the endcaps, if any.
DRAW_ENDCAP_VLINE(endcap0, x, y0, stroke, fill, mmode);
DRAW_ENDCAP_VLINE(endcap1, x, y1, stroke, fill, mmode);
}
/**
* write_filled_rectangle: draw a filled rectangle.
*
* Uses an optimised algorithm which is similar to the horizontal
* line writing algorithm, but optimised for writing the lines
* multiple times without recalculating lots of stuff.
*
* @param buff pointer to buffer to write in
* @param x x coordinate (left)
* @param y y coordinate (top)
* @param width rectangle width
* @param height rectangle height
* @param mode 0 = clear, 1 = set, 2 = toggle
*/
void write_filled_rectangle(uint8_t *buff, unsigned int x, unsigned int y, unsigned int width, unsigned int height, int mode)
{
unsigned int yy, addr0_old, addr1_old;
CHECK_COORDS(x, y);
CHECK_COORD_X(x + width);
CHECK_COORD_Y(y + height);
if (width <= 0 || height <= 0) {
return;
}
// Calculate as if the rectangle was only a horizontal line. We then
// step these addresses through each row until we iterate `height` times.
unsigned int addr0 = CALC_BUFF_ADDR(x, y);
unsigned int addr1 = CALC_BUFF_ADDR(x + width, y);
unsigned int addr0_bit = CALC_BIT_IN_WORD(x);
unsigned int addr1_bit = CALC_BIT_IN_WORD(x + width);
unsigned int mask, mask_l, mask_r, i;
// If the addresses are equal, we need to write one word vertically.
if (addr0 == addr1) {
mask = COMPUTE_HLINE_ISLAND_MASK(addr0_bit, addr1_bit);
while (height--) {
WRITE_WORD_MODE(buff, addr0, mask, mode);
addr0 += GRAPHICS_WIDTH_REAL / 8;
}
} else {
// Otherwise we need to write the edges and then the middle repeatedly.
mask_l = COMPUTE_HLINE_EDGE_L_MASK(addr0_bit);
mask_r = COMPUTE_HLINE_EDGE_R_MASK(addr1_bit);
// Write edges first.
yy = 0;
addr0_old = addr0;
addr1_old = addr1;
while (yy < height) {
WRITE_WORD_MODE(buff, addr0, mask_l, mode);
WRITE_WORD_MODE(buff, addr1, mask_r, mode);
addr0 += GRAPHICS_WIDTH_REAL / 8;
addr1 += GRAPHICS_WIDTH_REAL / 8;
yy++;
}
// Now write 0xffff words from start+1 to end-1 for each row.
yy = 0;
addr0 = addr0_old;
addr1 = addr1_old;
while (yy < height) {
for (i = addr0 + 1; i <= addr1 - 1; i++) {
uint8_t m = 0xff;
WRITE_WORD_MODE(buff, i, m, mode);
}
addr0 += GRAPHICS_WIDTH_REAL / 8;
addr1 += GRAPHICS_WIDTH_REAL / 8;
yy++;
}
}
}
/**
* write_filled_rectangle_lm: draw a filled rectangle on both draw buffers.
*
* @param x x coordinate (left)
* @param y y coordinate (top)
* @param width rectangle width
* @param height rectangle height
* @param lmode 0 = clear, 1 = set, 2 = toggle
* @param mmode 0 = clear, 1 = set, 2 = toggle
*/
void write_filled_rectangle_lm(unsigned int x, unsigned int y, unsigned int width, unsigned int height, int lmode, int mmode)
{
write_filled_rectangle(draw_buffer_mask, x, y, width, height, mmode);
write_filled_rectangle(draw_buffer_level, x, y, width, height, lmode);
}
/**
* write_rectangle_outlined: draw an outline of a rectangle. Essentially
* a convenience wrapper for draw_hline_outlined and draw_vline_outlined.
*
* @param x x coordinate (left)
* @param y y coordinate (top)
* @param width rectangle width
* @param height rectangle height
* @param mode 0 = black outline, white body, 1 = white outline, black body
* @param mmode 0 = clear, 1 = set, 2 = toggle
*/
void write_rectangle_outlined(unsigned int x, unsigned int y, int width, int height, int mode, int mmode)
{
// CHECK_COORDS(x, y);
// CHECK_COORDS(x + width, y + height);
// if((x + width) > DISP_WIDTH) width = DISP_WIDTH - x;
// if((y + height) > DISP_HEIGHT) height = DISP_HEIGHT - y;
write_hline_outlined(x, x + width, y, ENDCAP_ROUND, ENDCAP_ROUND, mode, mmode);
write_hline_outlined(x, x + width, y + height, ENDCAP_ROUND, ENDCAP_ROUND, mode, mmode);
write_vline_outlined(x, y, y + height, ENDCAP_ROUND, ENDCAP_ROUND, mode, mmode);
write_vline_outlined(x + width, y, y + height, ENDCAP_ROUND, ENDCAP_ROUND, mode, mmode);
}
/**
* write_circle: draw the outline of a circle on a given buffer,
* with an optional dash pattern for the line instead of a normal line.
*
* @param buff pointer to buffer to write in
* @param cx origin x coordinate
* @param cy origin y coordinate
* @param r radius
* @param dashp dash period (pixels) - zero for no dash
* @param mode 0 = clear, 1 = set, 2 = toggle
*/
void write_circle(uint8_t *buff, unsigned int cx, unsigned int cy, unsigned int r, unsigned int dashp, int mode)
{
CHECK_COORDS(cx, cy);
int error = -r, x = r, y = 0;
while (x >= y) {
if (dashp == 0 || (y % dashp) < (dashp / 2)) {
CIRCLE_PLOT_8(buff, cx, cy, x, y, mode);
}
error += (y * 2) + 1;
y++;
if (error >= 0) {
--x;
error -= x * 2;
}
}
}
/**
* write_circle_outlined: draw an outlined circle on the draw buffer.
*
* @param cx origin x coordinate
* @param cy origin y coordinate
* @param r radius
* @param dashp dash period (pixels) - zero for no dash
* @param bmode 0 = 4-neighbour border, 1 = 8-neighbour border
* @param mode 0 = black outline, white body, 1 = white outline, black body
* @param mmode 0 = clear, 1 = set, 2 = toggle
*/
void write_circle_outlined(unsigned int cx, unsigned int cy, unsigned int r, unsigned int dashp, int bmode, int mode, int mmode)
{
int stroke, fill;
CHECK_COORDS(cx, cy);
SETUP_STROKE_FILL(stroke, fill, mode);
// This is a two step procedure. First, we draw the outline of the
// circle, then we draw the inner part.
int error = -r, x = r, y = 0;
while (x >= y) {
if (dashp == 0 || (y % dashp) < (dashp / 2)) {
CIRCLE_PLOT_8(draw_buffer_mask, cx, cy, x + 1, y, mmode);
CIRCLE_PLOT_8(draw_buffer_level, cx, cy, x + 1, y, stroke);
CIRCLE_PLOT_8(draw_buffer_mask, cx, cy, x, y + 1, mmode);
CIRCLE_PLOT_8(draw_buffer_level, cx, cy, x, y + 1, stroke);
CIRCLE_PLOT_8(draw_buffer_mask, cx, cy, x - 1, y, mmode);
CIRCLE_PLOT_8(draw_buffer_level, cx, cy, x - 1, y, stroke);
CIRCLE_PLOT_8(draw_buffer_mask, cx, cy, x, y - 1, mmode);
CIRCLE_PLOT_8(draw_buffer_level, cx, cy, x, y - 1, stroke);
if (bmode == 1) {
CIRCLE_PLOT_8(draw_buffer_mask, cx, cy, x + 1, y + 1, mmode);
CIRCLE_PLOT_8(draw_buffer_level, cx, cy, x + 1, y + 1, stroke);
CIRCLE_PLOT_8(draw_buffer_mask, cx, cy, x - 1, y - 1, mmode);
CIRCLE_PLOT_8(draw_buffer_level, cx, cy, x - 1, y - 1, stroke);
}
}
error += (y * 2) + 1;
y++;
if (error >= 0) {
--x;
error -= x * 2;
}
}
error = -r;
x = r;
y = 0;
while (x >= y) {
if (dashp == 0 || (y % dashp) < (dashp / 2)) {
CIRCLE_PLOT_8(draw_buffer_mask, cx, cy, x, y, mmode);
CIRCLE_PLOT_8(draw_buffer_level, cx, cy, x, y, fill);
}
error += (y * 2) + 1;
y++;
if (error >= 0) {
--x;
error -= x * 2;
}
}
}
/**
* write_circle_filled: fill a circle on a given buffer.
*
* @param buff pointer to buffer to write in
* @param cx origin x coordinate
* @param cy origin y coordinate
* @param r radius
* @param mode 0 = clear, 1 = set, 2 = toggle
*/
void write_circle_filled(uint8_t *buff, unsigned int cx, unsigned int cy, unsigned int r, int mode)
{
CHECK_COORDS(cx, cy);
int error = -r, x = r, y = 0, xch = 0;
// It turns out that filled circles can take advantage of the midpoint
// circle algorithm. We simply draw very fast horizontal lines across each
// pair of X,Y coordinates. In some cases, this can even be faster than
// drawing an outlined circle!
//
// Due to multiple writes to each set of pixels, we have a special exception
// for when using the toggling draw mode.
while (x >= y) {
if (y != 0) {
write_hline(buff, cx - x, cx + x, cy + y, mode);
write_hline(buff, cx - x, cx + x, cy - y, mode);
if (mode != 2 || (mode == 2 && xch && (cx - x) != (cx - y))) {
write_hline(buff, cx - y, cx + y, cy + x, mode);
write_hline(buff, cx - y, cx + y, cy - x, mode);
xch = 0;
}
}
error += (y * 2) + 1;
y++;
if (error >= 0) {
--x;
xch = 1;
error -= x * 2;
}
}
// Handle toggle mode.
if (mode == 2) {
write_hline(buff, cx - r, cx + r, cy, mode);
}
}
/**
* write_line: Draw a line of arbitrary angle.
*
* @param buff pointer to buffer to write in
* @param x0 first x coordinate
* @param y0 first y coordinate
* @param x1 second x coordinate
* @param y1 second y coordinate
* @param mode 0 = clear, 1 = set, 2 = toggle
*/
void write_line(uint8_t *buff, unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1, int mode)
{
// Based on http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
unsigned int steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
SWAP(x0, y0);
SWAP(x1, y1);
}
if (x0 > x1) {
SWAP(x0, x1);
SWAP(y0, y1);
}
int deltax = x1 - x0;
unsigned int deltay = abs(y1 - y0);
int error = deltax / 2;
int ystep;
unsigned int y = y0;
unsigned int x; // , lasty = y, stox = 0;
if (y0 < y1) {
ystep = 1;
} else {
ystep = -1;
}
for (x = x0; x < x1; x++) {
if (steep) {
write_pixel(buff, y, x, mode);
} else {
write_pixel(buff, x, y, mode);
}
error -= deltay;
if (error < 0) {
y += ystep;
error += deltax;
}
}
}
/**
* write_line_lm: Draw a line of arbitrary angle.
*
* @param x0 first x coordinate
* @param y0 first y coordinate
* @param x1 second x coordinate
* @param y1 second y coordinate
* @param mmode 0 = clear, 1 = set, 2 = toggle
* @param lmode 0 = clear, 1 = set, 2 = toggle
*/
void write_line_lm(unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1, int mmode, int lmode)
{
write_line(draw_buffer_mask, x0, y0, x1, y1, mmode);
write_line(draw_buffer_level, x0, y0, x1, y1, lmode);
}
/**
* write_line_outlined: Draw a line of arbitrary angle, with an outline.
*
* @param buff pointer to buffer to write in
* @param x0 first x coordinate
* @param y0 first y coordinate
* @param x1 second x coordinate
* @param y1 second y coordinate
* @param endcap0 0 = none, 1 = single pixel, 2 = full cap
* @param endcap1 0 = none, 1 = single pixel, 2 = full cap
* @param mode 0 = black outline, white body, 1 = white outline, black body
* @param mmode 0 = clear, 1 = set, 2 = toggle
*/
void write_line_outlined(unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1,
__attribute__((unused)) int endcap0, __attribute__((unused)) int endcap1,
int mode, int mmode)
{
// Based on http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
// This could be improved for speed.
int omode, imode;
if (mode == 0) {
omode = 0;
imode = 1;
} else {
omode = 1;
imode = 0;
}
int steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
SWAP(x0, y0);
SWAP(x1, y1);
}
if (x0 > x1) {
SWAP(x0, x1);
SWAP(y0, y1);
}
int deltax = x1 - x0;
unsigned int deltay = abs(y1 - y0);
int error = deltax / 2;
int ystep;
unsigned int y = y0;
unsigned int x;
if (y0 < y1) {
ystep = 1;
} else {
ystep = -1;
}
// Draw the outline.
for (x = x0; x < x1; x++) {
if (steep) {
write_pixel_lm(y - 1, x, mmode, omode);
write_pixel_lm(y + 1, x, mmode, omode);
write_pixel_lm(y, x - 1, mmode, omode);
write_pixel_lm(y, x + 1, mmode, omode);
} else {
write_pixel_lm(x - 1, y, mmode, omode);
write_pixel_lm(x + 1, y, mmode, omode);
write_pixel_lm(x, y - 1, mmode, omode);
write_pixel_lm(x, y + 1, mmode, omode);
}
error -= deltay;
if (error < 0) {
y += ystep;
error += deltax;
}
}
// Now draw the innards.
error = deltax / 2;
y = y0;
for (x = x0; x < x1; x++) {
if (steep) {
write_pixel_lm(y, x, mmode, imode);
} else {
write_pixel_lm(x, y, mmode, imode);
}
error -= deltay;
if (error < 0) {
y += ystep;
error += deltax;
}
}
}
/**
* write_word_misaligned: Write a misaligned word across two addresses
* with an x offset.
*
* This allows for many pixels to be set in one write.
*
* @param buff buffer to write in
* @param word word to write (16 bits)
* @param addr address of first word
* @param xoff x offset (0-15)
* @param mode 0 = clear, 1 = set, 2 = toggle
*/
void write_word_misaligned(uint8_t *buff, uint16_t word, unsigned int addr, unsigned int xoff, int mode)
{
int16_t firstmask = word >> xoff;
int16_t lastmask = word << (16 - xoff);
WRITE_WORD_MODE(buff, addr + 1, firstmask && 0x00ff, mode);
WRITE_WORD_MODE(buff, addr, (firstmask & 0xff00) >> 8, mode);
if (xoff > 0) {
WRITE_WORD_MODE(buff, addr + 2, (lastmask & 0xff00) >> 8, mode);
}
}
/**
* write_word_misaligned_NAND: Write a misaligned word across two addresses
* with an x offset, using a NAND mask.
*
* This allows for many pixels to be set in one write.
*
* @param buff buffer to write in
* @param word word to write (16 bits)
* @param addr address of first word
* @param xoff x offset (0-15)
*
* This is identical to calling write_word_misaligned with a mode of 0 but
* it doesn't go through a lot of switch logic which slows down text writing
* a lot.
*/
void write_word_misaligned_NAND(uint8_t *buff, uint16_t word, unsigned int addr, unsigned int xoff)
{
uint16_t firstmask = word >> xoff;
uint16_t lastmask = word << (16 - xoff);
WRITE_WORD_NAND(buff, addr + 1, firstmask & 0x00ff);
WRITE_WORD_NAND(buff, addr, (firstmask & 0xff00) >> 8);
if (xoff > 0) {
WRITE_WORD_NAND(buff, addr + 2, (lastmask & 0xff00) >> 8);
}
}
/**
* write_word_misaligned_OR: Write a misaligned word across two addresses
* with an x offset, using an OR mask.
*
* This allows for many pixels to be set in one write.
*
* @param buff buffer to write in
* @param word word to write (16 bits)
* @param addr address of first word
* @param xoff x offset (0-15)
*
* This is identical to calling write_word_misaligned with a mode of 1 but
* it doesn't go through a lot of switch logic which slows down text writing
* a lot.
*/
void write_word_misaligned_OR(uint8_t *buff, uint16_t word, unsigned int addr, unsigned int xoff)
{
uint16_t firstmask = word >> xoff;
uint16_t lastmask = word << (16 - xoff);
WRITE_WORD_OR(buff, addr + 1, firstmask & 0x00ff);
WRITE_WORD_OR(buff, addr, (firstmask & 0xff00) >> 8);
if (xoff > 0) {
WRITE_WORD_OR(buff, addr + 2, (lastmask & 0xff00) >> 8);
}
}
/**
* write_word_misaligned_lm: Write a misaligned word across two
* words, in both level and mask buffers. This is core to the text
* writing routines.
*
* @param buff buffer to write in
* @param word word to write (16 bits)
* @param addr address of first word
* @param xoff x offset (0-15)
* @param lmode 0 = clear, 1 = set, 2 = toggle
* @param mmode 0 = clear, 1 = set, 2 = toggle
*/
void write_word_misaligned_lm(uint16_t wordl, uint16_t wordm, unsigned int addr, unsigned int xoff, int lmode, int mmode)
{
write_word_misaligned(draw_buffer_level, wordl, addr, xoff, lmode);
write_word_misaligned(draw_buffer_mask, wordm, addr, xoff, mmode);
}
/**
* fetch_font_info: Fetch font info structs.
*
* @param ch character
* @param font font id
*/
int fetch_font_info(uint8_t ch, int font, struct FontEntry *font_info, char *lookup)
{
// First locate the font struct.
if ((unsigned int)font > SIZEOF_ARRAY(fonts)) {
return 0; // font does not exist, exit.
}
// Load the font info; IDs are always sequential.
*font_info = fonts[font];
// Locate character in font lookup table. (If required.)
if (lookup != NULL) {
*lookup = font_info->lookup[ch];
if (*lookup == 0xff) {
return 0; // character doesn't exist, don't bother writing it.
}
}
return 1;
}
/**
* write_char16: Draw a character on the current draw buffer.
* Currently supports outlined characters and characters with
* a width of up to 8 pixels.
*
* @param ch character to write
* @param x x coordinate (left)
* @param y y coordinate (top)
* @param flags flags to write with (see gfx.h)
* @param font font to use
*/
void write_char16(char ch, unsigned int x, unsigned int y, int font)
{
unsigned int yy, addr_temp, row, row_temp, xshift;
uint16_t and_mask, or_mask, levels;
struct FontEntry font_info;
// char lookup = 0;
fetch_font_info(0, font, &font_info, NULL);
// Compute starting address (for x,y) of character.
int addr = CALC_BUFF_ADDR(x, y);
int wbit = CALC_BIT_IN_WORD(x);
// If font only supports lowercase or uppercase, make the letter
// lowercase or uppercase.
// How big is the character? We handle characters up to 8 pixels
// wide for now. Support for large characters may be added in future.
{
// Ensure we don't overflow.
if (x + wbit > GRAPHICS_WIDTH_REAL) {
return;
}
// Load data pointer.
row = ch * font_info.height;
row_temp = row;
addr_temp = addr;
xshift = 16 - font_info.width;
// We can write mask words easily.
for (yy = y; yy < y + font_info.height; yy++) {
if (font == 3) {
write_word_misaligned_OR(draw_buffer_mask, font_mask12x18[row] << xshift, addr, wbit);
} else {
write_word_misaligned_OR(draw_buffer_mask, font_mask8x10[row] << xshift, addr, wbit);
}
addr += GRAPHICS_WIDTH_REAL / 8;
row++;
}
// Level bits are more complicated. We need to set or clear
// level bits, but only where the mask bit is set; otherwise,
// we need to leave them alone. To do this, for each word, we
// construct an AND mask and an OR mask, and apply each individually.
row = row_temp;
addr = addr_temp;
for (yy = y; yy < y + font_info.height; yy++) {
if (font == 3) {
levels = font_frame12x18[row];
// if(!(flags & FONT_INVERT)) // data is normally inverted
levels = ~levels;
or_mask = font_mask12x18[row] << xshift;
and_mask = (font_mask12x18[row] & levels) << xshift;
} else {
levels = font_frame8x10[row];
// if(!(flags & FONT_INVERT)) // data is normally inverted
levels = ~levels;
or_mask = font_mask8x10[row] << xshift;
and_mask = (font_mask8x10[row] & levels) << xshift;
}
write_word_misaligned_OR(draw_buffer_level, or_mask, addr, wbit);
// If we're not bold write the AND mask.
// if(!(flags & FONT_BOLD))
write_word_misaligned_NAND(draw_buffer_level, and_mask, addr, wbit);
addr += GRAPHICS_WIDTH_REAL / 8;
row++;
}
}
}
/**
* write_char: Draw a character on the current draw buffer.
* Currently supports outlined characters and characters with
* a width of up to 8 pixels.
*
* @param ch character to write
* @param x x coordinate (left)
* @param y y coordinate (top)
* @param flags flags to write with (see gfx.h)
* @param font font to use
*/
void write_char(char ch, unsigned int x, unsigned int y, int flags, int font)
{
unsigned int yy, addr_temp, row, row_temp, xshift;
uint16_t and_mask, or_mask, levels;
struct FontEntry font_info;
char lookup = 0;
fetch_font_info(ch, font, &font_info, &lookup);
// Compute starting address (for x,y) of character.
unsigned int addr = CALC_BUFF_ADDR(x, y);
unsigned int wbit = CALC_BIT_IN_WORD(x);
// If font only supports lowercase or uppercase, make the letter
// lowercase or uppercase.
/*if(font_info.flags & FONT_LOWERCASE_ONLY)
ch = tolower(ch);
if(font_info.flags & FONT_UPPERCASE_ONLY)
ch = toupper(ch);*/
fetch_font_info(ch, font, &font_info, &lookup);
// How big is the character? We handle characters up to 8 pixels
// wide for now. Support for large characters may be added in future.
if (font_info.width <= 8) {
// Ensure we don't overflow.
if (x + wbit > GRAPHICS_WIDTH_REAL) {
return;
}
// Load data pointer.
row = lookup * font_info.height * 2;
row_temp = row;
addr_temp = addr;
xshift = 16 - font_info.width;
// We can write mask words easily.
for (yy = y; yy < y + font_info.height; yy++) {
write_word_misaligned_OR(draw_buffer_mask, font_info.data[row] << xshift, addr, wbit);
addr += GRAPHICS_WIDTH_REAL / 8;
row++;
}
// Level bits are more complicated. We need to set or clear
// level bits, but only where the mask bit is set; otherwise,
// we need to leave them alone. To do this, for each word, we
// construct an AND mask and an OR mask, and apply each individually.
row = row_temp;
addr = addr_temp;
for (yy = y; yy < y + font_info.height; yy++) {
levels = font_info.data[row + font_info.height];
if (!(flags & FONT_INVERT)) {
// data is normally inverted
levels = ~levels;
}
or_mask = font_info.data[row] << xshift;
and_mask = (font_info.data[row] & levels) << xshift;
write_word_misaligned_OR(draw_buffer_level, or_mask, addr, wbit);
// If we're not bold write the AND mask.
// if(!(flags & FONT_BOLD))
write_word_misaligned_NAND(draw_buffer_level, and_mask, addr, wbit);
addr += GRAPHICS_WIDTH_REAL / 8;
row++;
}
}
}
/**
* calc_text_dimensions: Calculate the dimensions of a
* string in a given font. Supports new lines and
* carriage returns in text.
*
* @param str string to calculate dimensions of
* @param font_info font info structure
* @param xs horizontal spacing
* @param ys vertical spacing
* @param dim return result: struct FontDimensions
*/
void calc_text_dimensions(char *str, struct FontEntry font, int xs, int ys, struct FontDimensions *dim)
{
int max_length = 0, line_length = 0, lines = 1;
while (*str != 0) {
line_length++;
if (*str == '\n' || *str == '\r') {
if (line_length > max_length) {
max_length = line_length;
}
line_length = 0;
lines++;
}
str++;
}
if (line_length > max_length) {
max_length = line_length;
}
dim->width = max_length * (font.width + xs);
dim->height = lines * (font.height + ys);
}
/**
* write_string: Draw a string on the screen with certain
* alignment parameters.
*
* @param str string to write
* @param x x coordinate
* @param y y coordinate
* @param xs horizontal spacing
* @param ys horizontal spacing
* @param va vertical align
* @param ha horizontal align
* @param flags flags (passed to write_char)
* @param font font
*/
void write_string(char *str, unsigned int x, unsigned int y, unsigned int xs, unsigned int ys, int va, int ha, int flags, int font)
{
int xx = 0, yy = 0, xx_original = 0;
struct FontEntry font_info;
struct FontDimensions dim;
// Determine font info and dimensions/position of the string.
fetch_font_info(0, font, &font_info, NULL);
calc_text_dimensions(str, font_info, xs, ys, &dim);
switch (va) {
case TEXT_VA_TOP:
yy = y;
break;
case TEXT_VA_MIDDLE:
yy = y - (dim.height / 2);
break;
case TEXT_VA_BOTTOM:
yy = y - dim.height;
break;
}
switch (ha) {
case TEXT_HA_LEFT:
xx = x;
break;
case TEXT_HA_CENTER:
xx = x - (dim.width / 2);
break;
case TEXT_HA_RIGHT:
xx = x - dim.width;
break;
}
// Then write each character.
xx_original = xx;
while (*str != 0) {
if (*str == '\n' || *str == '\r') {
yy += ys + font_info.height;
xx = xx_original;
} else {
if (xx >= 0 && xx < GRAPHICS_WIDTH_REAL) {
if (font_info.id < 2) {
write_char(*str, xx, yy, flags, font);
} else {
write_char16(*str, xx, yy, font);
}
}
xx += font_info.width + xs;
}
str++;
}
}
/**
* write_string_formatted: Draw a string with format escape
* sequences in it. Allows for complex text effects.
*
* @param str string to write (with format data)
* @param x x coordinate
* @param y y coordinate
* @param xs default horizontal spacing
* @param ys default horizontal spacing
* @param va vertical align
* @param ha horizontal align
* @param flags flags (passed to write_char)
*/
void write_string_formatted(char *str, unsigned int x, unsigned int y, unsigned int xs, unsigned int ys,
__attribute__((unused)) int va, __attribute__((unused)) int ha, int flags)
{
int fcode = 0, fptr = 0, font = 0, fwidth = 0, fheight = 0, xx = x, yy = y, max_xx = 0, max_height = 0;
struct FontEntry font_info;
// Retrieve sizes of the fonts: bigfont and smallfont.
fetch_font_info(0, 0, &font_info, NULL);
int smallfontwidth = font_info.width, smallfontheight = font_info.height;
fetch_font_info(0, 1, &font_info, NULL);
int bigfontwidth = font_info.width, bigfontheight = font_info.height;
// 11 byte stack with last byte as NUL.
char fstack[11];
fstack[10] = '\0';
// First, we need to parse the string for format characters and
// work out a bounding box. We'll parse again for the final output.
// This is a simple state machine parser.
char *ostr = str;
while (*str) {
if (*str == '<' && fcode == 1) {
// escape code: skip
fcode = 0;
}
if (*str == '<' && fcode == 0) {
// begin format code?
fcode = 1;
fptr = 0;
}
if (*str == '>' && fcode == 1) {
fcode = 0;
if (strcmp(fstack, "B")) {
// switch to "big" font (font #1)
fwidth = bigfontwidth;
fheight = bigfontheight;
} else if (strcmp(fstack, "S")) {
// switch to "small" font (font #0)
fwidth = smallfontwidth;
fheight = smallfontheight;
}
if (fheight > max_height) {
max_height = fheight;
}
// Skip over this byte. Go to next byte.
str++;
continue;
}
if (*str != '<' && *str != '>' && fcode == 1) {
// Add to the format stack (up to 10 bytes.)
if (fptr > 10) {
// stop adding bytes
str++; // go to next byte
continue;
}
fstack[fptr++] = *str;
fstack[fptr] = '\0'; // clear next byte (ready for next char or to terminate string.)
}
if (fcode == 0) {
// Not a format code, raw text.
xx += fwidth + xs;
if (*str == '\n') {
if (xx > max_xx) {
max_xx = xx;
}
xx = x;
yy += fheight + ys;
}
}
str++;
}
// Reset string pointer.
str = ostr;
// Now we've parsed it and got a bbox, we need to work out the dimensions of it
// and how to align it.
/*int width = max_xx - x;
int height = yy - y;
int ay, ax;
switch(va)
{
case TEXT_VA_TOP: ay = yy; break;
case TEXT_VA_MIDDLE: ay = yy - (height / 2); break;
case TEXT_VA_BOTTOM: ay = yy - height; break;
}
switch(ha)
{
case TEXT_HA_LEFT: ax = x; break;
case TEXT_HA_CENTER: ax = x - (width / 2); break;
case TEXT_HA_RIGHT: ax = x - width; break;
}*/
// So ax,ay is our new text origin. Parse the text format again and paint
// the text on the display.
fcode = 0;
fptr = 0;
font = 0;
xx = 0;
yy = 0;
while (*str) {
if (*str == '<' && fcode == 1) {
// escape code: skip
fcode = 0;
}
if (*str == '<' && fcode == 0) {
// begin format code?
fcode = 1;
fptr = 0;
}
if (*str == '>' && fcode == 1) {
fcode = 0;
if (strcmp(fstack, "B")) {
// switch to "big" font (font #1)
fwidth = bigfontwidth;
fheight = bigfontheight;
font = 1;
} else if (strcmp(fstack, "S")) {
// switch to "small" font (font #0)
fwidth = smallfontwidth;
fheight = smallfontheight;
font = 0;
}
// Skip over this byte. Go to next byte.
str++;
continue;
}
if (*str != '<' && *str != '>' && fcode == 1) {
// Add to the format stack (up to 10 bytes.)
if (fptr > 10) {
// stop adding bytes
str++; // go to next byte
continue;
}
fstack[fptr++] = *str;
fstack[fptr] = '\0'; // clear next byte (ready for next char or to terminate string.)
}
if (fcode == 0) {
// Not a format code, raw text. So we draw it.
// TODO - different font sizes.
write_char(*str, xx, yy + (max_height - fheight), flags, font);
xx += fwidth + xs;
if (*str == '\n') {
if (xx > max_xx) {
max_xx = xx;
}
xx = x;
yy += fheight + ys;
}
}
str++;
}
}
// SUPEROSD-
// graphics
void drawAttitude(uint16_t x, uint16_t y, int16_t pitch, int16_t roll, uint16_t size)
{
int16_t a = mySin(roll + 360);
int16_t b = myCos(roll + 360);
int16_t c = mySin(roll + 90 + 360) * 5 / 100;
int16_t d = myCos(roll + 90 + 360) * 5 / 100;
int16_t k;
int16_t l;
int16_t indi30x1 = myCos(30) * (size / 2 + 1) / 100;
int16_t indi30y1 = mySin(30) * (size / 2 + 1) / 100;
int16_t indi30x2 = myCos(30) * (size / 2 + 4) / 100;
int16_t indi30y2 = mySin(30) * (size / 2 + 4) / 100;
int16_t indi60x1 = myCos(60) * (size / 2 + 1) / 100;
int16_t indi60y1 = mySin(60) * (size / 2 + 1) / 100;
int16_t indi60x2 = myCos(60) * (size / 2 + 4) / 100;
int16_t indi60y2 = mySin(60) * (size / 2 + 4) / 100;
pitch = pitch % 90;
if (pitch > 90) {
pitch = pitch - 90;
}
if (pitch < -90) {
pitch = pitch + 90;
}
a = (a * (size / 2)) / 100;
b = (b * (size / 2)) / 100;
if (roll < -90 || roll > 90) {
pitch = pitch * -1;
}
k = a * pitch / 90;
l = b * pitch / 90;
// scale
// 0
// drawLine((x)-1-(size/2+4), (y)-1, (x)-1 - (size/2+1), (y)-1);
// drawLine((x)-1+(size/2+4), (y)-1, (x)-1 + (size/2+1), (y)-1);
write_line_outlined((x) - 1 - (size / 2 + 4), (y) - 1, (x) - 1 - (size / 2 + 1), (y) - 1, 0, 0, 0, 1);
write_line_outlined((x) - 1 + (size / 2 + 4), (y) - 1, (x) - 1 + (size / 2 + 1), (y) - 1, 0, 0, 0, 1);
// 30
// drawLine((x)-1+indi30x1, (y)-1-indi30y1, (x)-1 + indi30x2, (y)-1 - indi30y2);
// drawLine((x)-1-indi30x1, (y)-1-indi30y1, (x)-1 - indi30x2, (y)-1 - indi30y2);
write_line_outlined((x) - 1 + indi30x1, (y) - 1 - indi30y1, (x) - 1 + indi30x2, (y) - 1 - indi30y2, 0, 0, 0, 1);
write_line_outlined((x) - 1 - indi30x1, (y) - 1 - indi30y1, (x) - 1 - indi30x2, (y) - 1 - indi30y2, 0, 0, 0, 1);
// 60
// drawLine((x)-1+indi60x1, (y)-1-indi60y1, (x)-1 + indi60x2, (y)-1 - indi60y2);
// drawLine((x)-1-indi60x1, (y)-1-indi60y1, (x)-1 - indi60x2, (y)-1 - indi60y2);
write_line_outlined((x) - 1 + indi60x1, (y) - 1 - indi60y1, (x) - 1 + indi60x2, (y) - 1 - indi60y2, 0, 0, 0, 1);
write_line_outlined((x) - 1 - indi60x1, (y) - 1 - indi60y1, (x) - 1 - indi60x2, (y) - 1 - indi60y2, 0, 0, 0, 1);
// 90
// drawLine((x)-1, (y)-1-(size/2+4), (x)-1, (y)-1 - (size/2+1));
write_line_outlined((x) - 1, (y) - 1 - (size / 2 + 4), (x) - 1, (y) - 1 - (size / 2 + 1), 0, 0, 0, 1);
// roll
// drawLine((x)-1 - b, (y)-1 + a, (x)-1 + b, (y)-1 - a); //Direction line
write_line_outlined((x) - 1 - b, (y) - 1 + a, (x) - 1 + b, (y) - 1 - a, 0, 0, 0, 1); // Direction line
// "wingtips"
// drawLine((x)-1 - b, (y)-1 + a, (x)-1 - b + d, (y)-1 + a - c);
// drawLine((x)-1 + b + d, (y)-1 - a - c, (x)-1 + b, (y)-1 - a);
write_line_outlined((x) - 1 - b, (y) - 1 + a, (x) - 1 - b + d, (y) - 1 + a - c, 0, 0, 0, 1);
write_line_outlined((x) - 1 + b + d, (y) - 1 - a - c, (x) - 1 + b, (y) - 1 - a, 0, 0, 0, 1);
// pitch
// drawLine((x)-1, (y)-1, (x)-1 - k, (y)-1 - l);
write_line_outlined((x) - 1, (y) - 1, (x) - 1 - k, (y) - 1 - l, 0, 0, 0, 1);
// drawCircle(x-1, y-1, 5);
// write_circle_outlined(x-1, y-1, 5,0,0,0,1);
// drawCircle(x-1, y-1, size/2+4);
// write_circle_outlined(x-1, y-1, size/2+4,0,0,0,1);
}
void drawBattery(uint16_t x, uint16_t y, uint8_t battery, uint16_t size)
{
int i = 0;
int batteryLines;
// top
/*drawLine((x)-1+(size/2-size/4), (y)-1, (x)-1 + (size/2+size/4), (y)-1);
drawLine((x)-1+(size/2-size/4), (y)-1+1, (x)-1 + (size/2+size/4), (y)-1+1);
drawLine((x)-1, (y)-1+2, (x)-1 + size, (y)-1+2);
//bottom
drawLine((x)-1, (y)-1+size*3, (x)-1 + size, (y)-1+size*3);
//left
drawLine((x)-1, (y)-1+2, (x)-1, (y)-1+size*3);
//right
drawLine((x)-1+size, (y)-1+2, (x)-1+size, (y)-1+size*3);*/
write_rectangle_outlined((x) - 1, (y) - 1 + 2, size, size * 3, 0, 1);
write_vline_lm((x) - 1 + (size / 2 + size / 4) + 1, (y) - 2, (y) - 1 + 1, 0, 1);
write_vline_lm((x) - 1 + (size / 2 - size / 4) - 1, (y) - 2, (y) - 1 + 1, 0, 1);
write_hline_lm((x) - 1 + (size / 2 - size / 4), (x) - 1 + (size / 2 + size / 4), (y) - 2, 0, 1);
write_hline_lm((x) - 1 + (size / 2 - size / 4), (x) - 1 + (size / 2 + size / 4), (y) - 1, 1, 1);
write_hline_lm((x) - 1 + (size / 2 - size / 4), (x) - 1 + (size / 2 + size / 4), (y) - 1 + 1, 1, 1);
batteryLines = battery * (size * 3 - 2) / 100;
for (i = 0; i < batteryLines; i++) {
write_hline_lm((x) - 1, (x) - 1 + size, (y) - 1 + size * 3 - i, 1, 1);
}
}
void printTime(uint16_t x, uint16_t y)
{
char temp[9] =
{ 0 };
sprintf(temp, "%02d:%02d:%02d", timex.hour, timex.min, timex.sec);
// printTextFB(x,y,temp);
write_string(temp, x, y, 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 3);
}
/*
void drawAltitude(uint16_t x, uint16_t y, int16_t alt, uint8_t dir) {
char temp[9]={0};
char updown=' ';
uint16_t charx=x/16;
if(dir==0)
updown=24;
if(dir==1)
updown=25;
sprintf(temp,"%c%6dm",updown,alt);
printTextFB(charx,y+2,temp);
// frame
drawBox(charx*16-3,y,charx*16+strlen(temp)*8+3,y+11);
}*/
/**
* hud_draw_vertical_scale: Draw a vertical scale.
*
* @param v value to display as an integer
* @param range range about value to display (+/- range/2 each direction)
* @param halign horizontal alignment: -1 = left, +1 = right.
* @param x x displacement (typ. 0)
* @param y y displacement (typ. half display height)
* @param height height of scale
* @param mintick_step how often a minor tick is shown
* @param majtick_step how often a major tick is shown
* @param mintick_len minor tick length
* @param majtick_len major tick length
* @param boundtick_len boundary tick length
* @param max_val maximum expected value (used to compute size of arrow ticker)
* @param flags special flags (see hud.h.)
*/
void hud_draw_vertical_scale(int v, int range, int halign, int x, int y, int height, int mintick_step, int majtick_step, int mintick_len, int majtick_len,
int boundtick_len, __attribute__((unused)) int max_val, int flags)
{
char temp[15]; // , temp2[15];
struct FontEntry font_info;
struct FontDimensions dim;
// Halign should be in a small span.
// MY_ASSERT(halign >= -1 && halign <= 1);
// Compute the position of the elements.
int majtick_start = 0, majtick_end = 0, mintick_start = 0, mintick_end = 0, boundtick_start = 0, boundtick_end = 0;
if (halign == -1) {
majtick_start = x;
majtick_end = x + majtick_len;
mintick_start = x;
mintick_end = x + mintick_len;
boundtick_start = x;
boundtick_end = x + boundtick_len;
} else if (halign == +1) {
x = x - GRAPHICS_HDEADBAND;
majtick_start = GRAPHICS_WIDTH_REAL - x - 1;
majtick_end = GRAPHICS_WIDTH_REAL - x - majtick_len - 1;
mintick_start = GRAPHICS_WIDTH_REAL - x - 1;
mintick_end = GRAPHICS_WIDTH_REAL - x - mintick_len - 1;
boundtick_start = GRAPHICS_WIDTH_REAL - x - 1;
boundtick_end = GRAPHICS_WIDTH_REAL - x - boundtick_len - 1;
}
// Retrieve width of large font (font #0); from this calculate the x spacing.
fetch_font_info(0, 0, &font_info, NULL);
int arrow_len = (font_info.height / 2) + 1; // FIXME, font info being loaded correctly??
int text_x_spacing = arrow_len;
int max_text_y = 0, text_length = 0;
int small_font_char_width = font_info.width + 1; // +1 for horizontal spacing = 1
// For -(range / 2) to +(range / 2), draw the scale.
int range_2 = range / 2; // , height_2 = height / 2;
int r = 0, rr = 0, rv = 0, ys = 0, style = 0; // calc_ys = 0,
// Iterate through each step.
for (r = -range_2; r <= +range_2; r++) {
style = 0;
rr = r + range_2 - v; // normalise range for modulo, subtract value to move ticker tape
rv = -rr + range_2; // for number display
if (flags & HUD_VSCALE_FLAG_NO_NEGATIVE) {
rr += majtick_step / 2;
}
if (rr % majtick_step == 0) {
style = 1; // major tick
} else if (rr % mintick_step == 0) {
style = 2; // minor tick
} else {
style = 0;
}
if (flags & HUD_VSCALE_FLAG_NO_NEGATIVE && rv < 0) {
continue;
}
if (style) {
// Calculate y position.
ys = ((long int)(r * height) / (long int)range) + y;
// sprintf(temp, "ys=%d", ys);
// con_puts(temp, 0);
// Depending on style, draw a minor or a major tick.
if (style == 1) {
write_hline_outlined(majtick_start, majtick_end, ys, 2, 2, 0, 1);
memset(temp, ' ', 10);
// my_itoa(rv, temp);
sprintf(temp, "%d", rv);
text_length = (strlen(temp) + 1) * small_font_char_width; // add 1 for margin
if (text_length > max_text_y) {
max_text_y = text_length;
}
if (halign == -1) {
write_string(temp, majtick_end + text_x_spacing, ys, 1, 0, TEXT_VA_MIDDLE, TEXT_HA_LEFT, 0, 1);
} else {
write_string(temp, majtick_end - text_x_spacing + 1, ys, 1, 0, TEXT_VA_MIDDLE, TEXT_HA_RIGHT, 0, 1);
}
} else if (style == 2) {
write_hline_outlined(mintick_start, mintick_end, ys, 2, 2, 0, 1);
}
}
}
// Generate the string for the value, as well as calculating its dimensions.
memset(temp, ' ', 10);
// my_itoa(v, temp);
sprintf(temp, "%d", v);
// TODO: add auto-sizing.
calc_text_dimensions(temp, font_info, 1, 0, &dim);
int xx = 0, i = 0;
if (halign == -1) {
xx = majtick_end + text_x_spacing;
} else {
xx = majtick_end - text_x_spacing;
}
// Draw an arrow from the number to the point.
for (i = 0; i < arrow_len; i++) {
if (halign == -1) {
write_pixel_lm(xx - arrow_len + i, y - i - 1, 1, 1);
write_pixel_lm(xx - arrow_len + i, y + i - 1, 1, 1);
write_hline_lm(xx + dim.width - 1, xx - arrow_len + i + 1, y - i - 1, 0, 1);
write_hline_lm(xx + dim.width - 1, xx - arrow_len + i + 1, y + i - 1, 0, 1);
} else {
write_pixel_lm(xx + arrow_len - i, y - i - 1, 1, 1);
write_pixel_lm(xx + arrow_len - i, y + i - 1, 1, 1);
write_hline_lm(xx - dim.width - 1, xx + arrow_len - i - 1, y - i - 1, 0, 1);
write_hline_lm(xx - dim.width - 1, xx + arrow_len - i - 1, y + i - 1, 0, 1);
}
// FIXME
// write_hline_lm(xx - dim.width - 1, xx + (arrow_len - i), y - i - 1, 1, 1);
// write_hline_lm(xx - dim.width - 1, xx + (arrow_len - i), y + i - 1, 1, 1);
}
if (halign == -1) {
write_hline_lm(xx, xx + dim.width - 1, y - arrow_len, 1, 1);
write_hline_lm(xx, xx + dim.width - 1, y + arrow_len - 2, 1, 1);
write_vline_lm(xx + dim.width - 1, y - arrow_len, y + arrow_len - 2, 1, 1);
} else {
write_hline_lm(xx, xx - dim.width - 1, y - arrow_len, 1, 1);
write_hline_lm(xx, xx - dim.width - 1, y + arrow_len - 2, 1, 1);
write_vline_lm(xx - dim.width - 1, y - arrow_len, y + arrow_len - 2, 1, 1);
}
// Draw the text.
if (halign == -1) {
write_string(temp, xx, y, 1, 0, TEXT_VA_MIDDLE, TEXT_HA_LEFT, 0, 0);
} else {
write_string(temp, xx, y, 1, 0, TEXT_VA_MIDDLE, TEXT_HA_RIGHT, 0, 0);
}
// Then, add a slow cut off on the edges, so the text doesn't sharply
// disappear. We simply clear the areas above and below the ticker, and we
// use little markers on the edges.
if (halign == -1) {
write_filled_rectangle_lm(majtick_end + text_x_spacing, y + (height / 2) - (font_info.height / 2), max_text_y - boundtick_start, font_info.height, 0,
0);
write_filled_rectangle_lm(majtick_end + text_x_spacing, y - (height / 2) - (font_info.height / 2), max_text_y - boundtick_start, font_info.height, 0,
0);
} else {
write_filled_rectangle_lm(majtick_end - text_x_spacing - max_text_y, y + (height / 2) - (font_info.height / 2), max_text_y, font_info.height, 0, 0);
write_filled_rectangle_lm(majtick_end - text_x_spacing - max_text_y, y - (height / 2) - (font_info.height / 2), max_text_y, font_info.height, 0, 0);
}
write_hline_outlined(boundtick_start, boundtick_end, y + (height / 2), 2, 2, 0, 1);
write_hline_outlined(boundtick_start, boundtick_end, y - (height / 2), 2, 2, 0, 1);
}
/**
* hud_draw_compass: Draw a compass.
*
* @param v value for the compass
* @param range range about value to display (+/- range/2 each direction)
* @param width length in pixels
* @param x x displacement (typ. half display width)
* @param y y displacement (typ. bottom of display)
* @param mintick_step how often a minor tick is shown
* @param majtick_step how often a major tick (heading "xx") is shown
* @param mintick_len minor tick length
* @param majtick_len major tick length
* @param flags special flags (see hud.h.)
*/
void hud_draw_linear_compass(int v, int range, int width, int x, int y, int mintick_step, int majtick_step, int mintick_len, int majtick_len, __attribute__((unused)) int flags)
{
v %= 360; // wrap, just in case.
struct FontEntry font_info;
int majtick_start = 0, majtick_end = 0, mintick_start = 0, mintick_end = 0, textoffset = 0;
char headingstr[4];
majtick_start = y;
majtick_end = y - majtick_len;
mintick_start = y;
mintick_end = y - mintick_len;
textoffset = 8;
int r, style, rr, xs; // rv,
int range_2 = range / 2;
for (r = -range_2; r <= +range_2; r++) {
style = 0;
rr = (v + r + 360) % 360; // normalise range for modulo, add to move compass track
// rv = -rr + range_2; // for number display
if (rr % majtick_step == 0) {
style = 1; // major tick
} else if (rr % mintick_step == 0) {
style = 2; // minor tick
}
if (style) {
// Calculate x position.
xs = ((long int)(r * width) / (long int)range) + x;
// Draw it.
if (style == 1) {
write_vline_outlined(xs, majtick_start, majtick_end, 2, 2, 0, 1);
// Draw heading above this tick.
// If it's not one of north, south, east, west, draw the heading.
// Otherwise, draw one of the identifiers.
if (rr % 90 != 0) {
// We abbreviate heading to two digits. This has the side effect of being easy to compute.
headingstr[0] = '0' + (rr / 100);
headingstr[1] = '0' + ((rr / 10) % 10);
headingstr[2] = 0;
headingstr[3] = 0; // nul to terminate
} else {
switch (rr) {
case 0:
headingstr[0] = 'N';
break;
case 90:
headingstr[0] = 'E';
break;
case 180:
headingstr[0] = 'S';
break;
case 270:
headingstr[0] = 'W';
break;
}
headingstr[1] = 0;
headingstr[2] = 0;
headingstr[3] = 0;
}
// +1 fudge...!
write_string(headingstr, xs + 1, majtick_start + textoffset, 1, 0, TEXT_VA_MIDDLE, TEXT_HA_CENTER, 0, 1);
} else if (style == 2) {
write_vline_outlined(xs, mintick_start, mintick_end, 2, 2, 0, 1);
}
}
}
// Then, draw a rectangle with the present heading in it.
// We want to cover up any other markers on the bottom.
// First compute font size.
fetch_font_info(0, 3, &font_info, NULL);
int text_width = (font_info.width + 1) * 3;
int rect_width = text_width + 2;
write_filled_rectangle_lm(x - (rect_width / 2), majtick_start + 2, rect_width, font_info.height + 2, 0, 1);
write_rectangle_outlined(x - (rect_width / 2), majtick_start + 2, rect_width, font_info.height + 2, 0, 1);
headingstr[0] = '0' + (v / 100);
headingstr[1] = '0' + ((v / 10) % 10);
headingstr[2] = '0' + (v % 10);
headingstr[3] = 0;
write_string(headingstr, x + 1, majtick_start + textoffset + 2, 0, 0, TEXT_VA_MIDDLE, TEXT_HA_CENTER, 1, 3);
}
// CORE draw routines end here
void draw_artificial_horizon(float angle, float pitch, int16_t l_x, int16_t l_y, int16_t size)
{
float alpha;
uint8_t vertical = 0, horizontal = 0;
int16_t x1, x2;
int16_t y1, y2;
int16_t refx, refy;
alpha = DEG2RAD(angle);
refx = l_x + size / 2;
refy = l_y + size / 2;
//
float k = 0;
float dx = sinf(alpha) * (pitch / 90.0f * (size / 2));
float dy = cosf(alpha) * (pitch / 90.0f * (size / 2));
int16_t x0 = (size / 2) - dx;
int16_t y0 = (size / 2) + dy;
// calculate the line function
if ((angle < 90.0f) && (angle > -90)) {
vertical = 0;
if (fabsf(angle) < 1e-5f) {
horizontal = 1;
} else {
k = tanf(alpha);
}
} else {
vertical = 1;
}
// crossing point of line
if (!vertical && !horizontal) {
// y-y0=k(x-x0)
int16_t x = 0;
int16_t y = k * (x - x0) + y0;
// find right crossing point
x1 = x;
y1 = y;
if (y < 0) {
y1 = 0;
x1 = ((y1 - y0) + k * x0) / k;
}
if (y > size) {
y1 = size;
x1 = ((y1 - y0) + k * x0) / k;
}
// left crossing point
x = size;
y = k * (x - x0) + y0;
x2 = x;
y2 = y;
if (y < 0) {
y2 = 0;
x2 = ((y2 - y0) + k * x0) / k;
}
if (y > size) {
y2 = size;
x2 = ((y2 - y0) + k * x0) / k;
}
// move to location
// horizon line
write_line_outlined(x1 + l_x, y1 + l_y, x2 + l_x, y2 + l_y, 0, 0, 0, 1);
// fill
if (angle <= 0.0f && angle > -90.0f) {
// write_string("1", APPLY_HDEADBAND((GRAPHICS_RIGHT/2)),APPLY_VDEADBAND(GRAPHICS_BOTTOM-10), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_CENTER, 0, 3);
for (int i = y2; i < size; i++) {
x2 = ((i - y0) + k * x0) / k;
if (x2 > size) {
x2 = size;
}
if (x2 < 0) {
x2 = 0;
}
write_hline_lm(x2 + l_x, size + l_x, i + l_y, 1, 1);
}
} else if (angle < -90.0f) {
// write_string("2", APPLY_HDEADBAND((GRAPHICS_RIGHT/2)),APPLY_VDEADBAND(GRAPHICS_BOTTOM-10), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_CENTER, 0, 3);
for (int i = 0; i < y2; i++) {
x2 = ((i - y0) + k * x0) / k;
if (x2 > size) {
x2 = size;
}
if (x2 < 0) {
x2 = 0;
}
write_hline_lm(size + l_x, x2 + l_x, i + l_y, 1, 1);
}
} else if (angle > 0.0f && angle < 90.0f) {
// write_string("3", APPLY_HDEADBAND((GRAPHICS_RIGHT/2)),APPLY_VDEADBAND(GRAPHICS_BOTTOM-10), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_CENTER, 0, 3);
for (int i = y1; i < size; i++) {
x2 = ((i - y0) + k * x0) / k;
if (x2 > size) {
x2 = size;
}
if (x2 < 0) {
x2 = 0;
}
write_hline_lm(0 + l_x, x2 + l_x, i + l_y, 1, 1);
}
} else if (angle > 90.0f) {
// write_string("4", APPLY_HDEADBAND((GRAPHICS_RIGHT/2)),APPLY_VDEADBAND(GRAPHICS_BOTTOM-10), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_CENTER, 0, 3);
for (int i = 0; i < y1; i++) {
x2 = ((i - y0) + k * x0) / k;
if (x2 > size) {
x2 = size;
}
if (x2 < 0) {
x2 = 0;
}
write_hline_lm(x2 + l_x, 0 + l_x, i + l_y, 1, 1);
}
}
} else if (vertical) {
// horizon line
write_line_outlined(x0 + l_x, 0 + l_y, x0 + l_x, size + l_y, 0, 0, 0, 1);
if (angle >= 90.0f) {
// write_string("5", APPLY_HDEADBAND((GRAPHICS_RIGHT/2)),APPLY_VDEADBAND(GRAPHICS_BOTTOM-10), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_CENTER, 0, 3);
for (int i = 0; i < size; i++) {
write_hline_lm(0 + l_x, x0 + l_x, i + l_y, 1, 1);
}
} else {
// write_string("6", APPLY_HDEADBAND((GRAPHICS_RIGHT/2)),APPLY_VDEADBAND(GRAPHICS_BOTTOM-10), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_CENTER, 0, 3);
for (int i = 0; i < size; i++) {
write_hline_lm(size + l_x, x0 + l_x, i + l_y, 1, 1);
}
}
} else if (horizontal) {
// horizon line
write_hline_outlined(0 + l_x, size + l_x, y0 + l_y, 0, 0, 0, 1);
if (angle < 0) {
// write_string("7", APPLY_HDEADBAND((GRAPHICS_RIGHT/2)),APPLY_VDEADBAND(GRAPHICS_BOTTOM-10), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_CENTER, 0, 3);
for (int i = 0; i < y0; i++) {
write_hline_lm(0 + l_x, size + l_x, i + l_y, 1, 1);
}
} else {
// write_string("8", APPLY_HDEADBAND((GRAPHICS_RIGHT/2)),APPLY_VDEADBAND(GRAPHICS_BOTTOM-10), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_CENTER, 0, 3);
for (int i = y0; i < size; i++) {
write_hline_lm(0 + l_x, size + l_x, i + l_y, 1, 1);
}
}
}
// sides
write_line_outlined(l_x, l_y, l_x, l_y + size, 0, 0, 0, 1);
write_line_outlined(l_x + size, l_y, l_x + size, l_y + size, 0, 0, 0, 1);
// plane
write_line_outlined(refx - 5, refy, refx + 6, refy, 0, 0, 0, 1);
write_line_outlined(refx, refy, refx, refy - 3, 0, 0, 0, 1);
}
void introText()
{
write_string("ver 0.2", APPLY_HDEADBAND((GRAPHICS_RIGHT / 2)), APPLY_VDEADBAND(GRAPHICS_BOTTOM - 10), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_CENTER, 0, 3);
}
void introGraphics()
{
/* logo */
int image = 0;
struct splashEntry splash_info;
splash_info = splash[image];
copyimage(APPLY_HDEADBAND(GRAPHICS_RIGHT / 2 - (splash_info.width) / 2), APPLY_VDEADBAND(GRAPHICS_BOTTOM / 2 - (splash_info.height) / 2), image);
/* frame */
drawBox(APPLY_HDEADBAND(0), APPLY_VDEADBAND(0), APPLY_HDEADBAND(GRAPHICS_RIGHT - 8), APPLY_VDEADBAND(GRAPHICS_BOTTOM));
// Must mask out last half-word because SPI keeps clocking it out otherwise
for (uint32_t i = 0; i < 8; i++) {
write_vline(draw_buffer_level, GRAPHICS_WIDTH_REAL - i - 1, 0, GRAPHICS_HEIGHT_REAL - 1, 0);
write_vline(draw_buffer_mask, GRAPHICS_WIDTH_REAL - i - 1, 0, GRAPHICS_HEIGHT_REAL - 1, 0);
}
}
void calcHomeArrow(int16_t m_yaw)
{
HomeLocationData home;
HomeLocationGet(&home);
GPSPositionData gpsData;
GPSPositionGet(&gpsData);
/** http://www.movable-type.co.uk/scripts/latlong.html **/
float lat1, lat2, lon1, lon2, a, c, d, x, y, brng, u2g;
float elevation;
float gcsAlt = home.Altitude; // Home MSL altitude
float uavAlt = gpsData.Altitude; // UAV MSL altitude
float dAlt = uavAlt - gcsAlt; // Altitude difference
// Convert to radians
lat1 = DEG2RAD(home.Latitude) / 10000000.0f; // Home lat
lon1 = DEG2RAD(home.Longitude) / 10000000.0f; // Home lon
lat2 = DEG2RAD(gpsData.Latitude) / 10000000.0f; // UAV lat
lon2 = DEG2RAD(gpsData.Longitude) / 10000000.0f; // UAV lon
// Bearing
/**
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) -
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = Math.atan2(y, x).toDeg();
**/
y = sinf(lon2 - lon1) * cosf(lat2);
x = cosf(lat1) * sinf(lat2) - sinf(lat1) * cosf(lat2) * cosf(lon2 - lon1);
brng = RAD2DEG(atan2f(y, x));
if (brng < 0) {
brng += 360.0f;
}
// yaw corrected bearing, needs compass
u2g = brng - 180.0f - m_yaw;
if (u2g < 0) {
u2g += 360.0f;
}
// Haversine formula for distance
/**
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
**/
a = sinf((lat2 - lat1) / 2) * sinf((lat2 - lat1) / 2) + cosf(lat1) * cosf(lat2) * sinf((lon2 - lon1) / 2) * sinf((lon2 - lon1) / 2);
c = 2.0f * atan2f(sqrtf(a), sqrtf(1.0f - a));
d = 6371.0f * 1000.0f * c;
// Elevation v depends servo direction
if (d > 0.0f) {
elevation = 90.0f - RAD2DEG(atanf(dAlt / d));
} else {
elevation = 0.0f;
}
// ! TODO: sanity check
char temp[50] =
{ 0 };
sprintf(temp, "hea:%d", (int)brng);
write_string(temp, APPLY_HDEADBAND(GRAPHICS_RIGHT / 2 - 30), APPLY_VDEADBAND(30), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 2);
sprintf(temp, "ele:%d", (int)elevation);
write_string(temp, APPLY_HDEADBAND(GRAPHICS_RIGHT / 2 - 30), APPLY_VDEADBAND(30 + 10), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 2);
sprintf(temp, "dis:%d", (int)d);
write_string(temp, APPLY_HDEADBAND(GRAPHICS_RIGHT / 2 - 30), APPLY_VDEADBAND(30 + 10 + 10), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 2);
sprintf(temp, "u2g:%d", (int)u2g);
write_string(temp, APPLY_HDEADBAND(GRAPHICS_RIGHT / 2 - 30), APPLY_VDEADBAND(30 + 10 + 10 + 10), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 2);
sprintf(temp, "%c%c", (int)(u2g / 22.5f) * 2 + 0x90, (int)(u2g / 22.5f) * 2 + 0x91);
write_string(temp, APPLY_HDEADBAND(250), APPLY_VDEADBAND(40 + 10 + 10), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 3);
}
int lama = 10;
int lama_loc[2][30];
void lamas(void)
{
char temp[10] =
{ 0 };
lama++;
if (lama % 10 == 0) {
for (int z = 0; z < 30; z++) {
lama_loc[0][z] = rand() % (GRAPHICS_RIGHT - 10);
lama_loc[1][z] = rand() % (GRAPHICS_BOTTOM - 10);
}
}
for (int z = 0; z < 30; z++) {
sprintf(temp, "%c", 0xe8 + (lama_loc[0][z] % 2));
write_string(temp, APPLY_HDEADBAND(lama_loc[0][z]), APPLY_VDEADBAND(lama_loc[1][z]), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 2);
}
}
// main draw function
void updateGraphics()
{
OsdSettingsData OsdSettings;
OsdSettingsGet(&OsdSettings);
AttitudeStateData attitude;
AttitudeStateGet(&attitude);
GPSPositionData gpsData;
GPSPositionGet(&gpsData);
HomeLocationData home;
HomeLocationGet(&home);
BaroSensorData baro;
BaroSensorGet(&baro);
FlightStatusData status;
FlightStatusGet(&status);
PIOS_Servo_Set(0, OsdSettings.White);
PIOS_Servo_Set(1, OsdSettings.Black);
switch (OsdSettings.Screen) {
case 0: // Dave simple
{
if (home.Set == HOMELOCATION_SET_FALSE) {
char temps[20] =
{ 0 };
sprintf(temps, "HOME NOT SET");
// printTextFB(x,y,temp);
write_string(temps, APPLY_HDEADBAND(GRAPHICS_RIGHT / 2), (GRAPHICS_BOTTOM / 2), 0, 0, TEXT_VA_TOP, TEXT_HA_CENTER, 0, 3);
}
char temp[50] =
{ 0 };
memset(temp, ' ', 40);
// Note: cast to double required due to -Wdouble-promotion compiler option is
// being used, and there is no way in C to pass a float to a variadic function like sprintf()
sprintf(temp, "Lat:%11.7f", (double)(gpsData.Latitude / 10000000.0f));
write_string(temp, APPLY_HDEADBAND(20), APPLY_VDEADBAND(GRAPHICS_BOTTOM - 30), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_LEFT, 0, 3);
sprintf(temp, "Lon:%11.7f", (double)(gpsData.Longitude / 10000000.0f));
write_string(temp, APPLY_HDEADBAND(20), APPLY_VDEADBAND(GRAPHICS_BOTTOM - 10), 0, 0, TEXT_VA_BOTTOM, TEXT_HA_LEFT, 0, 3);
sprintf(temp, "Sat:%d", (int)gpsData.Satellites);
write_string(temp, APPLY_HDEADBAND(GRAPHICS_RIGHT - 40), APPLY_VDEADBAND(30), 0, 0, TEXT_VA_TOP, TEXT_HA_RIGHT, 0, 2);
/* Print ADC voltage FLIGHT*/
sprintf(temp, "V:%5.2fV", (double)(PIOS_ADC_PinGet(2) * 3 * 6.1f / 4096));
write_string(temp, APPLY_HDEADBAND(20), APPLY_VDEADBAND(20), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 3);
if (gpsData.Heading > 180) {
calcHomeArrow((int16_t)(gpsData.Heading - 360));
} else {
calcHomeArrow((int16_t)(gpsData.Heading));
}
}
break;
case 1:
{
/*drawBox(2,2,GRAPHICS_WIDTH_REAL-4,GRAPHICS_HEIGHT_REAL-4);
write_filled_rectangle(draw_buffer_mask,0,0,GRAPHICS_WIDTH_REAL-2,GRAPHICS_HEIGHT_REAL-2,0);
write_filled_rectangle(draw_buffer_mask,2,2,GRAPHICS_WIDTH_REAL-4-2,GRAPHICS_HEIGHT_REAL-4-2,2);
write_filled_rectangle(draw_buffer_mask,3,3,GRAPHICS_WIDTH_REAL-4-1,GRAPHICS_HEIGHT_REAL-4-1,0);*/
// write_filled_rectangle(draw_buffer_mask,5,5,GRAPHICS_WIDTH_REAL-4-5,GRAPHICS_HEIGHT_REAL-4-5,0);
// write_rectangle_outlined(10,10,GRAPHICS_WIDTH_REAL-20,GRAPHICS_HEIGHT_REAL-20,0,0);
// drawLine(GRAPHICS_WIDTH_REAL-1, GRAPHICS_HEIGHT_REAL-1,(GRAPHICS_WIDTH_REAL/2)-1, GRAPHICS_HEIGHT_REAL-1 );
// drawCircle((GRAPHICS_WIDTH_REAL/2)-1, (GRAPHICS_HEIGHT_REAL/2)-1, (GRAPHICS_HEIGHT_REAL/2)-1);
// drawCircle((GRAPHICS_SIZE/2)-1, (GRAPHICS_SIZE/2)-1, (GRAPHICS_SIZE/2)-2);
// drawLine(0, (GRAPHICS_SIZE/2)-1, GRAPHICS_SIZE-1, (GRAPHICS_SIZE/2)-1);
// drawLine((GRAPHICS_SIZE/2)-1, 0, (GRAPHICS_SIZE/2)-1, GRAPHICS_SIZE-1);
/*angleA++;
if(angleB<=-90)
{
sum=2;
}
if(angleB>=90)
{
sum=-2;
}
angleB+=sum;
angleC+=2;*/
// GPS HACK
if (gpsData.Heading > 180) {
calcHomeArrow((int16_t)(gpsData.Heading - 360));
} else {
calcHomeArrow((int16_t)(gpsData.Heading));
}
/* Draw Attitude Indicator */
if (OsdSettings.Attitude == OSDSETTINGS_ATTITUDE_ENABLED) {
drawAttitude(APPLY_HDEADBAND(OsdSettings.AttitudeSetup[OSDSETTINGS_ATTITUDESETUP_X]),
APPLY_VDEADBAND(OsdSettings.AttitudeSetup[OSDSETTINGS_ATTITUDESETUP_Y]), attitude.Pitch, attitude.Roll, 96);
}
// write_string("Hello OP-OSD", 60, 12, 1, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 0);
// printText16( 60, 12,"Hello OP-OSD");
char temp[50] =
{ 0 };
memset(temp, ' ', 40);
sprintf(temp, "Lat:%11.7f", (double)(gpsData.Latitude / 10000000.0f));
write_string(temp, APPLY_HDEADBAND(5), APPLY_VDEADBAND(5), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 2);
sprintf(temp, "Lon:%11.7f", (double)(gpsData.Longitude / 10000000.0f));
write_string(temp, APPLY_HDEADBAND(5), APPLY_VDEADBAND(15), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 2);
sprintf(temp, "Fix:%d", (int)gpsData.Status);
write_string(temp, APPLY_HDEADBAND(5), APPLY_VDEADBAND(25), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 2);
sprintf(temp, "Sat:%d", (int)gpsData.Satellites);
write_string(temp, APPLY_HDEADBAND(5), APPLY_VDEADBAND(35), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 2);
/* Print RTC time */
if (OsdSettings.Time == OSDSETTINGS_TIME_ENABLED) {
printTime(APPLY_HDEADBAND(OsdSettings.TimeSetup[OSDSETTINGS_TIMESETUP_X]), APPLY_VDEADBAND(OsdSettings.TimeSetup[OSDSETTINGS_TIMESETUP_Y]));
}
/* Print Number of detected video Lines */
sprintf(temp, "Lines:%4d", PIOS_Video_GetOSDLines());
write_string(temp, APPLY_HDEADBAND((GRAPHICS_RIGHT - 8)), APPLY_VDEADBAND(5), 0, 0, TEXT_VA_TOP, TEXT_HA_RIGHT, 0, 2);
/* Print ADC voltage */
// sprintf(temp,"Rssi:%4dV",(int)(PIOS_ADC_PinGet(4)*3000/4096));
// write_string(temp, (GRAPHICS_WIDTH_REAL - 2),15, 0, 0, TEXT_VA_TOP, TEXT_HA_RIGHT, 0, 2);
sprintf(temp, "Rssi:%4.2fV", (double)(PIOS_ADC_PinGet(5) * 3.0f / 4096.0f));
write_string(temp, APPLY_HDEADBAND((GRAPHICS_RIGHT - 8)), APPLY_VDEADBAND(15), 0, 0, TEXT_VA_TOP, TEXT_HA_RIGHT, 0, 2);
/* Print CPU temperature */
sprintf(temp, "Temp:%4.2fC", (double)(PIOS_ADC_PinGet(3) * 0.29296875f - 264));
write_string(temp, APPLY_HDEADBAND((GRAPHICS_RIGHT - 8)), APPLY_VDEADBAND(25), 0, 0, TEXT_VA_TOP, TEXT_HA_RIGHT, 0, 2);
/* Print ADC voltage FLIGHT*/
sprintf(temp, "FltV:%4.2fV", (double)(PIOS_ADC_PinGet(2) * 3.0f * 6.1f / 4096.0f));
write_string(temp, APPLY_HDEADBAND((GRAPHICS_RIGHT - 8)), APPLY_VDEADBAND(35), 0, 0, TEXT_VA_TOP, TEXT_HA_RIGHT, 0, 2);
/* Print ADC voltage VIDEO*/
sprintf(temp, "VidV:%4.2fV", (double)(PIOS_ADC_PinGet(4) * 3.0f * 6.1f / 4096.0f));
write_string(temp, APPLY_HDEADBAND((GRAPHICS_RIGHT - 8)), APPLY_VDEADBAND(45), 0, 0, TEXT_VA_TOP, TEXT_HA_RIGHT, 0, 2);
/* Print ADC voltage RSSI */
// sprintf(temp,"Curr:%4dA",(int)(PIOS_ADC_PinGet(0)*300*61/4096));
// write_string(temp, (GRAPHICS_WIDTH_REAL - 2),60, 0, 0, TEXT_VA_TOP, TEXT_HA_RIGHT, 0, 2);
/* Draw Battery Gauge */
/*m_batt++;
uint8_t dir=3;
if(m_batt==101)
m_batt=0;
if(m_pitch>0)
{
dir=0;
m_alt+=m_pitch/2;
}
else if(m_pitch<0)
{
dir=1;
m_alt+=m_pitch/2;
}*/
/*if(OsdSettings.Battery == OSDSETTINGS_BATTERY_ENABLED)
{
drawBattery(APPLY_HDEADBAND(OsdSettings.BatterySetup[OSDSETTINGS_BATTERYSETUP_X]),APPLY_VDEADBAND(OsdSettings.BatterySetup[OSDSETTINGS_BATTERYSETUP_Y]),m_batt,16);
}*/
// drawAltitude(200,50,m_alt,dir);
// drawArrow(96,GRAPHICS_HEIGHT_REAL/2,angleB,32);
// Draw airspeed (left side.)
if (OsdSettings.Speed == OSDSETTINGS_SPEED_ENABLED) {
hud_draw_vertical_scale((int)gpsData.Groundspeed, 100, -1, APPLY_HDEADBAND(OsdSettings.SpeedSetup[OSDSETTINGS_SPEEDSETUP_X]),
APPLY_VDEADBAND(OsdSettings.SpeedSetup[OSDSETTINGS_SPEEDSETUP_Y]), 100, 10, 20, 7, 12, 15, 1000, HUD_VSCALE_FLAG_NO_NEGATIVE);
}
// Draw altimeter (right side.)
if (OsdSettings.Altitude == OSDSETTINGS_ALTITUDE_ENABLED) {
hud_draw_vertical_scale((int)gpsData.Altitude, 200, +1, APPLY_HDEADBAND(OsdSettings.AltitudeSetup[OSDSETTINGS_ALTITUDESETUP_X]),
APPLY_VDEADBAND(OsdSettings.AltitudeSetup[OSDSETTINGS_ALTITUDESETUP_Y]), 100, 20, 100, 7, 12, 15, 500, 0);
}
// Draw compass.
if (OsdSettings.Heading == OSDSETTINGS_HEADING_ENABLED) {
if (attitude.Yaw < 0) {
hud_draw_linear_compass(360 + attitude.Yaw, 150, 120, APPLY_HDEADBAND(OsdSettings.HeadingSetup[OSDSETTINGS_HEADINGSETUP_X]),
APPLY_VDEADBAND(OsdSettings.HeadingSetup[OSDSETTINGS_HEADINGSETUP_Y]), 15, 30, 7, 12, 0);
} else {
hud_draw_linear_compass(attitude.Yaw, 150, 120, APPLY_HDEADBAND(OsdSettings.HeadingSetup[OSDSETTINGS_HEADINGSETUP_X]),
APPLY_VDEADBAND(OsdSettings.HeadingSetup[OSDSETTINGS_HEADINGSETUP_Y]), 15, 30, 7, 12, 0);
}
}
}
break;
case 2:
{
int size = 64;
int x = ((GRAPHICS_RIGHT / 2) - (size / 2)), y = (GRAPHICS_BOTTOM - size - 2);
draw_artificial_horizon(-attitude.Roll, attitude.Pitch, APPLY_HDEADBAND(x), APPLY_VDEADBAND(y), size);
hud_draw_vertical_scale((int)gpsData.Groundspeed, 20, +1, APPLY_HDEADBAND(GRAPHICS_RIGHT - (x - 1)), APPLY_VDEADBAND(y + (size / 2)), size, 5, 10, 4, 7,
10, 100, HUD_VSCALE_FLAG_NO_NEGATIVE);
if (OsdSettings.AltitudeSource == OSDSETTINGS_ALTITUDESOURCE_BARO) {
hud_draw_vertical_scale((int)baro.Altitude, 50, -1, APPLY_HDEADBAND((x + size + 1)), APPLY_VDEADBAND(y + (size / 2)), size, 10, 20, 4, 7, 10, 500, 0);
} else {
hud_draw_vertical_scale((int)gpsData.Altitude, 50, -1, APPLY_HDEADBAND((x + size + 1)), APPLY_VDEADBAND(y + (size / 2)), size, 10, 20, 4, 7, 10, 500,
0);
}
char temp[50] =
{ 0 };
memset(temp, ' ', 50);
switch (status.FlightMode) {
case FLIGHTSTATUS_FLIGHTMODE_MANUAL:
sprintf(temp, "Man");
break;
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED1:
sprintf(temp, "Stab1");
break;
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED2:
sprintf(temp, "Stab2");
break;
case FLIGHTSTATUS_FLIGHTMODE_STABILIZED3:
sprintf(temp, "Stab3");
break;
case FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD:
sprintf(temp, "PH");
break;
case FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE:
sprintf(temp, "RTB");
break;
case FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER:
sprintf(temp, "PATH");
break;
default:
sprintf(temp, "Mode: %d", status.FlightMode);
break;
}
write_string(temp, APPLY_HDEADBAND(5), APPLY_VDEADBAND(5), 0, 0, TEXT_VA_TOP, TEXT_HA_LEFT, 0, 2);
}
break;
case 3:
{
lamas();
}
break;
case 4:
case 5:
case 6:
{
int image = OsdSettings.Screen - 4;
struct splashEntry splash_info;
splash_info = splash[image];
copyimage(APPLY_HDEADBAND(GRAPHICS_RIGHT / 2 - (splash_info.width) / 2), APPLY_VDEADBAND(GRAPHICS_BOTTOM / 2 - (splash_info.height) / 2), image);
}
break;
default:
write_vline_lm(APPLY_HDEADBAND(GRAPHICS_RIGHT / 2), APPLY_VDEADBAND(0), APPLY_VDEADBAND(GRAPHICS_BOTTOM), 1, 1);
write_hline_lm(APPLY_HDEADBAND(0), APPLY_HDEADBAND(GRAPHICS_RIGHT), APPLY_VDEADBAND(GRAPHICS_BOTTOM / 2), 1, 1);
break;
}
// Must mask out last half-word because SPI keeps clocking it out otherwise
for (uint32_t i = 0; i < 8; i++) {
write_vline(draw_buffer_level, GRAPHICS_WIDTH_REAL - i - 1, 0, GRAPHICS_HEIGHT_REAL - 1, 0);
write_vline(draw_buffer_mask, GRAPHICS_WIDTH_REAL - i - 1, 0, GRAPHICS_HEIGHT_REAL - 1, 0);
}
}
void updateOnceEveryFrame()
{
clearGraphics();
updateGraphics();
}
// ****************
/**
* Initialise the gps module
* \return -1 if initialisation failed
* \return 0 on success
*/
int32_t osdgenStart(void)
{
// Start gps task
vSemaphoreCreateBinary(osdSemaphore);
xTaskCreate(osdgenTask, (signed char *)"OSDGEN", STACK_SIZE_BYTES / 4, NULL, TASK_PRIORITY, &osdgenTaskHandle);
PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_OSDGEN, osdgenTaskHandle);
#ifdef PIOS_INCLUDE_WDG
PIOS_WDG_RegisterFlag(PIOS_WDG_OSDGEN);
#endif
return 0;
}
/**
* Initialise the osd module
* \return -1 if initialisation failed
* \return 0 on success
*/
int32_t osdgenInitialize(void)
{
AttitudeStateInitialize();
#ifdef PIOS_INCLUDE_GPS
GPSPositionInitialize();
#if !defined(PIOS_GPS_MINIMAL)
GPSTimeInitialize();
GPSSatellitesInitialize();
#endif
#ifdef PIOS_GPS_SETS_HOMELOCATION
HomeLocationInitialize();
#endif
#endif
OsdSettingsInitialize();
BaroSensorInitialize();
FlightStatusInitialize();
return 0;
}
MODULE_INITCALL(osdgenInitialize, osdgenStart)
// ****************
/**
* Main osd task. It does not return.
*/
static void osdgenTask(__attribute__((unused)) void *parameters)
{
// portTickType lastSysTime;
// Loop forever
// lastSysTime = xTaskGetTickCount();
OsdSettingsData OsdSettings;
OsdSettingsGet(&OsdSettings);
PIOS_Servo_Set(0, OsdSettings.White);
PIOS_Servo_Set(1, OsdSettings.Black);
// intro
for (int i = 0; i < 63; i++) {
if (xSemaphoreTake(osdSemaphore, LONG_TIME) == pdTRUE) {
#ifdef PIOS_INCLUDE_WDG
PIOS_WDG_UpdateFlag(PIOS_WDG_OSDGEN);
#endif
clearGraphics();
introGraphics();
}
}
for (int i = 0; i < 63; i++) {
if (xSemaphoreTake(osdSemaphore, LONG_TIME) == pdTRUE) {
#ifdef PIOS_INCLUDE_WDG
PIOS_WDG_UpdateFlag(PIOS_WDG_OSDGEN);
#endif
clearGraphics();
introGraphics();
introText();
}
}
while (1) {
if (xSemaphoreTake(osdSemaphore, LONG_TIME) == pdTRUE) {
#ifdef PIOS_INCLUDE_WDG
PIOS_WDG_UpdateFlag(PIOS_WDG_OSDGEN);
#endif
updateOnceEveryFrame();
}
// xSemaphoreTake(osdSemaphore, portMAX_DELAY);
// vTaskDelayUntil(&lastSysTime, 10 / portTICK_RATE_MS);
}
}
// ****************
/**
* @}
* @}
*/