2016-11-21 21:31:10 +00:00
|
|
|
/**
|
|
|
|
* @file Arduboy2.cpp
|
|
|
|
* \brief
|
|
|
|
* The Arduboy2Base and Arduboy2 classes and support objects and definitions.
|
|
|
|
*/
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
#include "Arduboy2.h"
|
2015-05-01 05:26:58 +00:00
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
//========================================
|
|
|
|
//========== class Arduboy2Base ==========
|
|
|
|
//========================================
|
|
|
|
|
2016-08-04 00:16:54 +00:00
|
|
|
uint8_t Arduboy2Base::sBuffer[];
|
2016-06-30 23:59:56 +00:00
|
|
|
|
2020-09-24 21:31:27 +00:00
|
|
|
uint16_t Arduboy2Base::frameCount = 0;
|
|
|
|
|
2020-09-27 18:33:00 +00:00
|
|
|
uint8_t Arduboy2Base::currentButtonState = 0;
|
|
|
|
uint8_t Arduboy2Base::previousButtonState = 0;
|
|
|
|
|
2020-09-24 21:31:27 +00:00
|
|
|
uint8_t Arduboy2Base::eachFrameMillis = 16;
|
|
|
|
uint8_t Arduboy2Base::thisFrameStart;
|
|
|
|
uint8_t Arduboy2Base::lastFrameDurationMs;
|
|
|
|
bool Arduboy2Base::justRendered = false;
|
|
|
|
|
2016-02-21 04:18:02 +00:00
|
|
|
// functions called here should be public so users can create their
|
|
|
|
// own init functions if they need different behavior than `begin`
|
2020-09-24 21:31:27 +00:00
|
|
|
// provides by default.
|
|
|
|
//
|
|
|
|
// This code and it's documentation should be kept in sync with
|
|
|
|
// Aruduboy2::begin()
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::begin()
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2020-09-24 21:31:27 +00:00
|
|
|
beginDoFirst();
|
2017-02-06 18:34:46 +00:00
|
|
|
|
|
|
|
bootLogo();
|
2018-03-14 14:24:53 +00:00
|
|
|
// alternative logo functions. Work the same as bootLogo() but may reduce
|
2020-09-24 21:31:27 +00:00
|
|
|
// memory size if the sketch uses the same bitmap drawing function or
|
|
|
|
// `Sprites`/`SpritesB` class
|
2017-04-26 14:08:07 +00:00
|
|
|
// bootLogoCompressed();
|
|
|
|
// bootLogoSpritesSelfMasked();
|
|
|
|
// bootLogoSpritesOverwrite();
|
2018-03-14 14:24:53 +00:00
|
|
|
// bootLogoSpritesBSelfMasked();
|
|
|
|
// bootLogoSpritesBOverwrite();
|
2017-02-06 19:37:45 +00:00
|
|
|
|
2018-02-19 22:01:32 +00:00
|
|
|
waitNoButtons(); // wait for all buttons to be released
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
2020-09-24 21:31:27 +00:00
|
|
|
void Arduboy2Base::beginDoFirst()
|
|
|
|
{
|
|
|
|
boot(); // raw hardware
|
|
|
|
|
|
|
|
display(); // blank the display (sBuffer is global, so cleared automatically)
|
|
|
|
|
|
|
|
flashlight(); // light the RGB LED and screen if UP button is being held.
|
|
|
|
|
|
|
|
// check for and handle buttons held during start up for system control
|
|
|
|
systemButtons();
|
|
|
|
|
|
|
|
audio.begin();
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::flashlight()
|
2016-02-20 19:43:36 +00:00
|
|
|
{
|
2017-03-24 19:53:35 +00:00
|
|
|
if (!pressed(UP_BUTTON)) {
|
2016-06-15 17:25:57 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sendLCDCommand(OLED_ALL_PIXELS_ON); // smaller than allPixelsOn()
|
2016-06-14 20:27:21 +00:00
|
|
|
digitalWriteRGB(RGB_ON, RGB_ON, RGB_ON);
|
2016-06-15 17:25:57 +00:00
|
|
|
|
2018-03-20 21:30:46 +00:00
|
|
|
#ifndef ARDUBOY_CORE // for Arduboy core timer 0 should remain enabled
|
2017-03-24 19:53:35 +00:00
|
|
|
// prevent the bootloader magic number from being overwritten by timer 0
|
|
|
|
// when a timer variable overlaps the magic number location, for when
|
|
|
|
// flashlight mode is used for upload problem recovery
|
|
|
|
power_timer0_disable();
|
2018-03-20 21:30:46 +00:00
|
|
|
#endif
|
2017-03-24 19:53:35 +00:00
|
|
|
|
|
|
|
while (true) {
|
2016-02-20 19:43:36 +00:00
|
|
|
idle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-24 19:53:35 +00:00
|
|
|
void Arduboy2Base::systemButtons()
|
|
|
|
{
|
2016-06-13 18:21:45 +00:00
|
|
|
while (pressed(B_BUTTON)) {
|
2017-03-15 18:10:19 +00:00
|
|
|
digitalWriteRGB(BLUE_LED, RGB_ON); // turn on blue LED
|
2016-06-13 18:21:45 +00:00
|
|
|
sysCtrlSound(UP_BUTTON + B_BUTTON, GREEN_LED, 0xff);
|
|
|
|
sysCtrlSound(DOWN_BUTTON + B_BUTTON, RED_LED, 0);
|
2017-04-17 21:34:58 +00:00
|
|
|
delayShort(200);
|
2016-06-13 18:21:45 +00:00
|
|
|
}
|
|
|
|
|
2017-03-15 18:10:19 +00:00
|
|
|
digitalWriteRGB(BLUE_LED, RGB_OFF); // turn off blue LED
|
2016-06-13 18:21:45 +00:00
|
|
|
}
|
|
|
|
|
2017-03-24 19:53:35 +00:00
|
|
|
void Arduboy2Base::sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal)
|
|
|
|
{
|
2016-06-13 18:21:45 +00:00
|
|
|
if (pressed(buttons)) {
|
2017-03-15 18:10:19 +00:00
|
|
|
digitalWriteRGB(BLUE_LED, RGB_OFF); // turn off blue LED
|
2017-04-17 21:34:58 +00:00
|
|
|
delayShort(200);
|
2017-03-15 18:10:19 +00:00
|
|
|
digitalWriteRGB(led, RGB_ON); // turn on "acknowledge" LED
|
2020-06-24 20:14:26 +00:00
|
|
|
EEPROM.update(eepromAudioOnOff, eeVal);
|
2017-04-17 21:34:58 +00:00
|
|
|
delayShort(500);
|
2017-03-15 18:10:19 +00:00
|
|
|
digitalWriteRGB(led, RGB_OFF); // turn off "acknowledge" LED
|
2016-06-13 18:21:45 +00:00
|
|
|
|
2017-04-07 19:02:11 +00:00
|
|
|
while (pressed(buttons)) { } // Wait for button release
|
2016-06-13 18:21:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::bootLogo()
|
2017-04-12 21:36:28 +00:00
|
|
|
{
|
|
|
|
bootLogoShell(drawLogoBitmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::drawLogoBitmap(int16_t y)
|
|
|
|
{
|
|
|
|
drawBitmap(20, y, arduboy_logo, 88, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::bootLogoCompressed()
|
|
|
|
{
|
|
|
|
bootLogoShell(drawLogoCompressed);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::drawLogoCompressed(int16_t y)
|
|
|
|
{
|
|
|
|
drawCompressed(20, y, arduboy_logo_compressed);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::bootLogoSpritesSelfMasked()
|
|
|
|
{
|
|
|
|
bootLogoShell(drawLogoSpritesSelfMasked);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::drawLogoSpritesSelfMasked(int16_t y)
|
|
|
|
{
|
|
|
|
Sprites::drawSelfMasked(20, y, arduboy_logo_sprite, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::bootLogoSpritesOverwrite()
|
|
|
|
{
|
|
|
|
bootLogoShell(drawLogoSpritesOverwrite);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::drawLogoSpritesOverwrite(int16_t y)
|
|
|
|
{
|
|
|
|
Sprites::drawOverwrite(20, y, arduboy_logo_sprite, 0);
|
|
|
|
}
|
|
|
|
|
2018-03-14 14:24:53 +00:00
|
|
|
void Arduboy2Base::bootLogoSpritesBSelfMasked()
|
|
|
|
{
|
|
|
|
bootLogoShell(drawLogoSpritesBSelfMasked);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::drawLogoSpritesBSelfMasked(int16_t y)
|
|
|
|
{
|
|
|
|
SpritesB::drawSelfMasked(20, y, arduboy_logo_sprite, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::bootLogoSpritesBOverwrite()
|
|
|
|
{
|
|
|
|
bootLogoShell(drawLogoSpritesBOverwrite);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::drawLogoSpritesBOverwrite(int16_t y)
|
|
|
|
{
|
|
|
|
SpritesB::drawOverwrite(20, y, arduboy_logo_sprite, 0);
|
|
|
|
}
|
|
|
|
|
2017-04-12 21:36:28 +00:00
|
|
|
// bootLogoText() should be kept in sync with bootLogoShell()
|
|
|
|
// if changes are made to one, equivalent changes should be made to the other
|
2020-09-24 21:31:27 +00:00
|
|
|
bool Arduboy2Base::bootLogoShell(void (&drawLogo)(int16_t))
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2018-04-06 16:51:52 +00:00
|
|
|
bool showLEDs = readShowBootLogoLEDsFlag();
|
|
|
|
|
2018-03-19 19:51:21 +00:00
|
|
|
if (!readShowBootLogoFlag()) {
|
2020-09-24 21:31:27 +00:00
|
|
|
return false;
|
2018-03-19 19:51:21 +00:00
|
|
|
}
|
|
|
|
|
2018-04-06 16:51:52 +00:00
|
|
|
if (showLEDs) {
|
|
|
|
digitalWriteRGB(RED_LED, RGB_ON);
|
|
|
|
}
|
2016-06-14 20:27:21 +00:00
|
|
|
|
2020-06-24 19:53:13 +00:00
|
|
|
for (int16_t y = -15; y <= 24; y++) {
|
2017-02-06 19:37:45 +00:00
|
|
|
if (pressed(RIGHT_BUTTON)) {
|
|
|
|
digitalWriteRGB(RGB_OFF, RGB_OFF, RGB_OFF); // all LEDs off
|
2020-09-24 21:31:27 +00:00
|
|
|
return false;
|
2017-02-06 19:37:45 +00:00
|
|
|
}
|
|
|
|
|
2018-04-06 16:51:52 +00:00
|
|
|
if (showLEDs && y == 4) {
|
2017-03-15 18:10:19 +00:00
|
|
|
digitalWriteRGB(RED_LED, RGB_OFF); // red LED off
|
|
|
|
digitalWriteRGB(GREEN_LED, RGB_ON); // green LED on
|
2016-06-14 20:27:21 +00:00
|
|
|
}
|
2015-05-01 05:26:58 +00:00
|
|
|
|
2018-01-21 22:33:41 +00:00
|
|
|
// Using display(CLEAR_BUFFER) instead of clear() may save code space.
|
|
|
|
// The extra time it takes to repaint the previous logo isn't an issue.
|
|
|
|
display(CLEAR_BUFFER);
|
2017-04-12 21:36:28 +00:00
|
|
|
(*drawLogo)(y); // call the function that actually draws the logo
|
2015-05-01 05:26:58 +00:00
|
|
|
display();
|
2018-01-21 22:33:41 +00:00
|
|
|
delayShort(15);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
2018-04-06 16:51:52 +00:00
|
|
|
if (showLEDs) {
|
|
|
|
digitalWriteRGB(GREEN_LED, RGB_OFF); // green LED off
|
|
|
|
digitalWriteRGB(BLUE_LED, RGB_ON); // blue LED on
|
|
|
|
}
|
2018-01-21 22:33:41 +00:00
|
|
|
delayShort(400);
|
2017-03-15 18:10:19 +00:00
|
|
|
digitalWriteRGB(BLUE_LED, RGB_OFF);
|
2017-02-06 18:34:46 +00:00
|
|
|
|
2020-09-24 21:31:27 +00:00
|
|
|
return true;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
2018-02-19 22:01:32 +00:00
|
|
|
// wait for all buttons to be released
|
2020-09-24 21:31:27 +00:00
|
|
|
void Arduboy2Base::waitNoButtons()
|
|
|
|
{
|
2018-02-19 22:01:32 +00:00
|
|
|
do {
|
|
|
|
delayShort(50); // simple button debounce
|
|
|
|
} while (buttonsState());
|
|
|
|
}
|
|
|
|
|
2015-05-01 05:26:58 +00:00
|
|
|
/* Frame management */
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::setFrameRate(uint8_t rate)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2016-09-15 22:12:27 +00:00
|
|
|
eachFrameMillis = 1000 / rate;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
2018-02-09 23:18:19 +00:00
|
|
|
void Arduboy2Base::setFrameDuration(uint8_t duration)
|
|
|
|
{
|
|
|
|
eachFrameMillis = duration;
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
bool Arduboy2Base::everyXFrames(uint8_t frames)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
return frameCount % frames == 0;
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
bool Arduboy2Base::nextFrame()
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2018-02-09 23:18:19 +00:00
|
|
|
uint8_t now = (uint8_t) millis();
|
|
|
|
uint8_t frameDurationMs = now - thisFrameStart;
|
2015-05-01 05:26:58 +00:00
|
|
|
|
2017-01-19 08:04:11 +00:00
|
|
|
if (justRendered) {
|
2018-02-09 23:18:19 +00:00
|
|
|
lastFrameDurationMs = frameDurationMs;
|
2017-01-19 08:04:11 +00:00
|
|
|
justRendered = false;
|
|
|
|
return false;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
2018-02-09 23:18:19 +00:00
|
|
|
else if (frameDurationMs < eachFrameMillis) {
|
|
|
|
// Only idle if at least a full millisecond remains, since idle() may
|
|
|
|
// sleep the processor until the next millisecond timer interrupt.
|
|
|
|
if (++frameDurationMs < eachFrameMillis) {
|
2015-05-01 05:26:58 +00:00
|
|
|
idle();
|
2018-02-09 23:18:19 +00:00
|
|
|
}
|
2017-01-19 08:04:11 +00:00
|
|
|
|
2015-05-01 05:26:58 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// pre-render
|
2017-01-19 08:04:11 +00:00
|
|
|
justRendered = true;
|
2018-02-09 23:18:19 +00:00
|
|
|
thisFrameStart = now;
|
2017-01-19 08:04:11 +00:00
|
|
|
frameCount++;
|
|
|
|
|
|
|
|
return true;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
2017-04-07 19:02:11 +00:00
|
|
|
bool Arduboy2Base::nextFrameDEV()
|
|
|
|
{
|
2017-02-06 21:03:15 +00:00
|
|
|
bool ret = nextFrame();
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
if (lastFrameDurationMs > eachFrameMillis)
|
|
|
|
TXLED1;
|
|
|
|
else
|
|
|
|
TXLED0;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
int Arduboy2Base::cpuLoad()
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
return lastFrameDurationMs*100 / eachFrameMillis;
|
|
|
|
}
|
|
|
|
|
2018-04-18 00:33:30 +00:00
|
|
|
void Arduboy2Base::initRandomSeed()
|
|
|
|
{
|
2018-04-23 19:44:15 +00:00
|
|
|
randomSeed(generateRandomSeed());
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Graphics */
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::clear()
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
fillScreen(BLACK);
|
|
|
|
}
|
|
|
|
|
2016-09-14 21:50:05 +00:00
|
|
|
void Arduboy2Base::drawPixel(int16_t x, int16_t y, uint8_t color)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
if (x < 0 || x > (WIDTH-1) || y < 0 || y > (HEIGHT-1))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-04-01 18:44:27 +00:00
|
|
|
uint16_t row_offset;
|
|
|
|
uint8_t bit;
|
2017-04-18 00:06:20 +00:00
|
|
|
|
2017-06-16 22:05:19 +00:00
|
|
|
asm volatile
|
|
|
|
(
|
2018-11-09 20:10:05 +00:00
|
|
|
// bit = 1 << (y & 7)
|
|
|
|
"ldi %[bit], 1 \n" //bit = 1;
|
|
|
|
"sbrc %[y], 1 \n" //if (y & _BV(1)) bit = 4;
|
|
|
|
"ldi %[bit], 4 \n"
|
|
|
|
"sbrc %[y], 0 \n" //if (y & _BV(0)) bit = bit << 1;
|
|
|
|
"lsl %[bit] \n"
|
|
|
|
"sbrc %[y], 2 \n" //if (y & _BV(2)) bit = (bit << 4) | (bit >> 4);
|
2018-11-13 16:51:55 +00:00
|
|
|
"swap %[bit] \n"
|
2018-11-09 20:10:05 +00:00
|
|
|
//row_offset = y / 8 * WIDTH + x;
|
|
|
|
"andi %A[y], 0xf8 \n" //row_offset = (y & 0xF8) * WIDTH / 8
|
|
|
|
"mul %[width_offset], %A[y] \n"
|
|
|
|
"movw %[row_offset], r0 \n"
|
|
|
|
"clr __zero_reg__ \n"
|
|
|
|
"add %A[row_offset], %[x] \n" //row_offset += x
|
|
|
|
#if WIDTH != 128
|
|
|
|
"adc %B[row_offset], __zero_reg__ \n" // only non 128 width can overflow
|
|
|
|
#endif
|
|
|
|
: [row_offset] "=&x" (row_offset), // upper register (ANDI)
|
|
|
|
[bit] "=&d" (bit), // upper register (LDI)
|
|
|
|
[y] "+d" (y) // upper register (ANDI), must be writable
|
|
|
|
: [width_offset] "r" ((uint8_t)(WIDTH/8)),
|
|
|
|
[x] "r" ((uint8_t)x)
|
2017-06-16 22:05:19 +00:00
|
|
|
:
|
|
|
|
);
|
2018-11-09 20:10:05 +00:00
|
|
|
uint8_t data = sBuffer[row_offset] | bit;
|
|
|
|
if (!(color & _BV(0))) data ^= bit;
|
|
|
|
sBuffer[row_offset] = data;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
2018-11-13 16:51:55 +00:00
|
|
|
#if 0
|
|
|
|
// For reference, this is the C++ equivalent
|
|
|
|
void Arduboy2Base::drawPixel(int16_t x, int16_t y, uint8_t color)
|
|
|
|
{
|
|
|
|
if (x < 0 || x > (WIDTH-1) || y < 0 || y > (HEIGHT-1))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t row_offset;
|
|
|
|
uint8_t bit;
|
|
|
|
|
|
|
|
bit = 1 << (y & 7);
|
|
|
|
row_offset = (y & 0xF8) * WIDTH / 8 + x;
|
|
|
|
uint8_t data = sBuffer[row_offset] | bit;
|
|
|
|
if (!color) data ^= bit;
|
|
|
|
sBuffer[row_offset] = data;
|
|
|
|
}
|
|
|
|
#endif
|
2015-05-01 05:26:58 +00:00
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
uint8_t Arduboy2Base::getPixel(uint8_t x, uint8_t y)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
uint8_t row = y / 8;
|
|
|
|
uint8_t bit_position = y % 8;
|
|
|
|
return (sBuffer[(row*WIDTH) + x] & _BV(bit_position)) >> bit_position;
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::drawCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
int16_t f = 1 - r;
|
|
|
|
int16_t ddF_x = 1;
|
|
|
|
int16_t ddF_y = -2 * r;
|
|
|
|
int16_t x = 0;
|
|
|
|
int16_t y = r;
|
|
|
|
|
|
|
|
drawPixel(x0, y0+r, color);
|
|
|
|
drawPixel(x0, y0-r, color);
|
|
|
|
drawPixel(x0+r, y0, color);
|
|
|
|
drawPixel(x0-r, y0, color);
|
|
|
|
|
|
|
|
while (x<y)
|
|
|
|
{
|
|
|
|
if (f >= 0)
|
|
|
|
{
|
|
|
|
y--;
|
|
|
|
ddF_y += 2;
|
|
|
|
f += ddF_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
x++;
|
|
|
|
ddF_x += 2;
|
|
|
|
f += ddF_x;
|
|
|
|
|
|
|
|
drawPixel(x0 + x, y0 + y, color);
|
|
|
|
drawPixel(x0 - x, y0 + y, color);
|
|
|
|
drawPixel(x0 + x, y0 - y, color);
|
|
|
|
drawPixel(x0 - x, y0 - y, color);
|
|
|
|
drawPixel(x0 + y, y0 + x, color);
|
|
|
|
drawPixel(x0 - y, y0 + x, color);
|
|
|
|
drawPixel(x0 + y, y0 - x, color);
|
|
|
|
drawPixel(x0 - y, y0 - x, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::drawCircleHelper
|
2016-11-21 21:31:10 +00:00
|
|
|
(int16_t x0, int16_t y0, uint8_t r, uint8_t corners, uint8_t color)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
int16_t f = 1 - r;
|
|
|
|
int16_t ddF_x = 1;
|
|
|
|
int16_t ddF_y = -2 * r;
|
|
|
|
int16_t x = 0;
|
|
|
|
int16_t y = r;
|
|
|
|
|
|
|
|
while (x<y)
|
|
|
|
{
|
|
|
|
if (f >= 0)
|
|
|
|
{
|
|
|
|
y--;
|
|
|
|
ddF_y += 2;
|
|
|
|
f += ddF_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
x++;
|
|
|
|
ddF_x += 2;
|
|
|
|
f += ddF_x;
|
|
|
|
|
2016-11-21 21:31:10 +00:00
|
|
|
if (corners & 0x4) // lower right
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
drawPixel(x0 + x, y0 + y, color);
|
|
|
|
drawPixel(x0 + y, y0 + x, color);
|
|
|
|
}
|
2016-11-21 21:31:10 +00:00
|
|
|
if (corners & 0x2) // upper right
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
drawPixel(x0 + x, y0 - y, color);
|
|
|
|
drawPixel(x0 + y, y0 - x, color);
|
|
|
|
}
|
2016-11-21 21:31:10 +00:00
|
|
|
if (corners & 0x8) // lower left
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
drawPixel(x0 - y, y0 + x, color);
|
|
|
|
drawPixel(x0 - x, y0 + y, color);
|
|
|
|
}
|
2016-11-21 21:31:10 +00:00
|
|
|
if (corners & 0x1) // upper left
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
drawPixel(x0 - y, y0 - x, color);
|
|
|
|
drawPixel(x0 - x, y0 - y, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::fillCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
drawFastVLine(x0, y0-r, 2*r+1, color);
|
|
|
|
fillCircleHelper(x0, y0, r, 3, 0, color);
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::fillCircleHelper
|
2016-11-21 21:31:10 +00:00
|
|
|
(int16_t x0, int16_t y0, uint8_t r, uint8_t sides, int16_t delta,
|
2015-05-01 05:26:58 +00:00
|
|
|
uint8_t color)
|
|
|
|
{
|
|
|
|
int16_t f = 1 - r;
|
|
|
|
int16_t ddF_x = 1;
|
|
|
|
int16_t ddF_y = -2 * r;
|
|
|
|
int16_t x = 0;
|
|
|
|
int16_t y = r;
|
|
|
|
|
|
|
|
while (x < y)
|
|
|
|
{
|
|
|
|
if (f >= 0)
|
|
|
|
{
|
|
|
|
y--;
|
|
|
|
ddF_y += 2;
|
|
|
|
f += ddF_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
x++;
|
|
|
|
ddF_x += 2;
|
|
|
|
f += ddF_x;
|
|
|
|
|
2016-11-21 21:31:10 +00:00
|
|
|
if (sides & 0x1) // right side
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
drawFastVLine(x0+x, y0-y, 2*y+1+delta, color);
|
|
|
|
drawFastVLine(x0+y, y0-x, 2*x+1+delta, color);
|
|
|
|
}
|
|
|
|
|
2016-11-21 21:31:10 +00:00
|
|
|
if (sides & 0x2) // left side
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
drawFastVLine(x0-x, y0-y, 2*y+1+delta, color);
|
|
|
|
drawFastVLine(x0-y, y0-x, 2*x+1+delta, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::drawLine
|
2015-05-01 05:26:58 +00:00
|
|
|
(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color)
|
|
|
|
{
|
|
|
|
// bresenham's algorithm - thx wikpedia
|
2016-06-15 18:15:47 +00:00
|
|
|
bool steep = abs(y1 - y0) > abs(x1 - x0);
|
2015-05-01 05:26:58 +00:00
|
|
|
if (steep) {
|
2020-06-18 21:37:33 +00:00
|
|
|
swapInt16(x0, y0);
|
|
|
|
swapInt16(x1, y1);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (x0 > x1) {
|
2020-06-18 21:37:33 +00:00
|
|
|
swapInt16(x0, x1);
|
|
|
|
swapInt16(y0, y1);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int16_t dx, dy;
|
|
|
|
dx = x1 - x0;
|
|
|
|
dy = abs(y1 - y0);
|
|
|
|
|
|
|
|
int16_t err = dx / 2;
|
|
|
|
int8_t ystep;
|
|
|
|
|
|
|
|
if (y0 < y1)
|
|
|
|
{
|
|
|
|
ystep = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ystep = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; x0 <= x1; x0++)
|
|
|
|
{
|
|
|
|
if (steep)
|
|
|
|
{
|
|
|
|
drawPixel(y0, x0, color);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
drawPixel(x0, y0, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
err -= dy;
|
|
|
|
if (err < 0)
|
|
|
|
{
|
|
|
|
y0 += ystep;
|
|
|
|
err += dx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::drawRect
|
2015-05-01 05:26:58 +00:00
|
|
|
(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color)
|
|
|
|
{
|
|
|
|
drawFastHLine(x, y, w, color);
|
|
|
|
drawFastHLine(x, y+h-1, w, color);
|
|
|
|
drawFastVLine(x, y, h, color);
|
|
|
|
drawFastVLine(x+w-1, y, h, color);
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::drawFastVLine
|
2015-05-01 05:26:58 +00:00
|
|
|
(int16_t x, int16_t y, uint8_t h, uint8_t color)
|
|
|
|
{
|
|
|
|
int end = y+h;
|
|
|
|
for (int a = max(0,y); a < min(end,HEIGHT); a++)
|
|
|
|
{
|
|
|
|
drawPixel(x,a,color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::drawFastHLine
|
2015-05-01 05:26:58 +00:00
|
|
|
(int16_t x, int16_t y, uint8_t w, uint8_t color)
|
|
|
|
{
|
2016-09-14 21:50:05 +00:00
|
|
|
int16_t xEnd; // last x point + 1
|
|
|
|
|
|
|
|
// Do y bounds checks
|
|
|
|
if (y < 0 || y >= HEIGHT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
xEnd = x + w;
|
|
|
|
|
|
|
|
// Check if the entire line is not on the display
|
|
|
|
if (xEnd <= 0 || x >= WIDTH)
|
2016-02-23 05:51:54 +00:00
|
|
|
return;
|
|
|
|
|
2016-09-14 21:50:05 +00:00
|
|
|
// Don't start before the left edge
|
|
|
|
if (x < 0)
|
2016-02-23 05:51:54 +00:00
|
|
|
x = 0;
|
|
|
|
|
2016-09-14 21:50:05 +00:00
|
|
|
// Don't end past the right edge
|
|
|
|
if (xEnd > WIDTH)
|
|
|
|
xEnd = WIDTH;
|
2016-02-23 05:51:54 +00:00
|
|
|
|
2016-09-14 21:50:05 +00:00
|
|
|
// calculate actual width (even if unchanged)
|
|
|
|
w = xEnd - x;
|
2016-02-23 05:51:54 +00:00
|
|
|
|
|
|
|
// buffer pointer plus row offset + x offset
|
2016-09-14 21:50:05 +00:00
|
|
|
register uint8_t *pBuf = sBuffer + ((y / 8) * WIDTH) + x;
|
2016-02-23 05:51:54 +00:00
|
|
|
|
|
|
|
// pixel mask
|
2016-09-14 21:50:05 +00:00
|
|
|
register uint8_t mask = 1 << (y & 7);
|
2016-02-23 05:51:54 +00:00
|
|
|
|
|
|
|
switch (color)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2016-02-23 05:51:54 +00:00
|
|
|
case WHITE:
|
2016-09-14 21:50:05 +00:00
|
|
|
while (w--)
|
|
|
|
{
|
2016-02-23 05:51:54 +00:00
|
|
|
*pBuf++ |= mask;
|
2016-09-14 21:50:05 +00:00
|
|
|
}
|
2016-02-23 05:51:54 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BLACK:
|
|
|
|
mask = ~mask;
|
2016-09-14 21:50:05 +00:00
|
|
|
while (w--)
|
|
|
|
{
|
2016-02-23 05:51:54 +00:00
|
|
|
*pBuf++ &= mask;
|
2016-09-14 21:50:05 +00:00
|
|
|
}
|
2016-02-23 05:51:54 +00:00
|
|
|
break;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::fillRect
|
2015-05-01 05:26:58 +00:00
|
|
|
(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color)
|
|
|
|
{
|
|
|
|
// stupidest version - update in subclasses if desired!
|
|
|
|
for (int16_t i=x; i<x+w; i++)
|
|
|
|
{
|
|
|
|
drawFastVLine(i, y, h, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::fillScreen(uint8_t color)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2017-06-16 22:37:19 +00:00
|
|
|
// C version:
|
2015-05-01 05:26:58 +00:00
|
|
|
//
|
2017-06-16 22:37:19 +00:00
|
|
|
// if (color != BLACK)
|
|
|
|
// {
|
|
|
|
// color = 0xFF; // all pixels on
|
|
|
|
// }
|
|
|
|
// for (int16_t i = 0; i < WIDTH * HEIGTH / 8; i++)
|
|
|
|
// {
|
|
|
|
// sBuffer[i] = color;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// This asm version is hard coded for 1024 bytes. It doesn't use the defined
|
|
|
|
// WIDTH and HEIGHT values. It will have to be modified for a different
|
|
|
|
// screen buffer size.
|
|
|
|
// It also assumes color value for BLACK is 0.
|
|
|
|
|
|
|
|
// local variable for screen buffer pointer,
|
|
|
|
// which can be declared a read-write operand
|
|
|
|
uint8_t* bPtr = sBuffer;
|
2016-02-20 19:43:14 +00:00
|
|
|
|
2015-05-01 05:26:58 +00:00
|
|
|
asm volatile
|
|
|
|
(
|
|
|
|
// if value is zero, skip assigning to 0xff
|
2017-06-16 22:37:19 +00:00
|
|
|
"cpse %[color], __zero_reg__\n"
|
|
|
|
"ldi %[color], 0xFF\n"
|
2015-05-01 05:26:58 +00:00
|
|
|
// counter = 0
|
2017-06-16 22:37:19 +00:00
|
|
|
"clr __tmp_reg__\n"
|
2019-02-12 07:26:32 +00:00
|
|
|
"1:\n"
|
2015-05-01 05:26:58 +00:00
|
|
|
// (4x) push zero into screen buffer,
|
|
|
|
// then increment buffer position
|
2017-06-16 22:37:19 +00:00
|
|
|
"st Z+, %[color]\n"
|
|
|
|
"st Z+, %[color]\n"
|
|
|
|
"st Z+, %[color]\n"
|
|
|
|
"st Z+, %[color]\n"
|
2015-05-01 05:26:58 +00:00
|
|
|
// increase counter
|
2017-06-16 22:37:19 +00:00
|
|
|
"inc __tmp_reg__\n"
|
2015-05-01 05:26:58 +00:00
|
|
|
// repeat for 256 loops
|
|
|
|
// (until counter rolls over back to 0)
|
2019-02-12 07:26:32 +00:00
|
|
|
"brne 1b\n"
|
2017-06-16 22:37:19 +00:00
|
|
|
: [color] "+d" (color),
|
|
|
|
"+z" (bPtr)
|
|
|
|
:
|
2015-05-01 05:26:58 +00:00
|
|
|
:
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::drawRoundRect
|
2015-05-01 05:26:58 +00:00
|
|
|
(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color)
|
|
|
|
{
|
|
|
|
// smarter version
|
|
|
|
drawFastHLine(x+r, y, w-2*r, color); // Top
|
|
|
|
drawFastHLine(x+r, y+h-1, w-2*r, color); // Bottom
|
|
|
|
drawFastVLine(x, y+r, h-2*r, color); // Left
|
|
|
|
drawFastVLine(x+w-1, y+r, h-2*r, color); // Right
|
|
|
|
// draw four corners
|
|
|
|
drawCircleHelper(x+r, y+r, r, 1, color);
|
|
|
|
drawCircleHelper(x+w-r-1, y+r, r, 2, color);
|
|
|
|
drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
|
|
|
|
drawCircleHelper(x+r, y+h-r-1, r, 8, color);
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::fillRoundRect
|
2015-05-01 05:26:58 +00:00
|
|
|
(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color)
|
|
|
|
{
|
|
|
|
// smarter version
|
|
|
|
fillRect(x+r, y, w-2*r, h, color);
|
|
|
|
|
|
|
|
// draw four corners
|
|
|
|
fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
|
|
|
|
fillCircleHelper(x+r, y+r, r, 2, h-2*r-1, color);
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::drawTriangle
|
2015-05-01 05:26:58 +00:00
|
|
|
(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
|
|
|
|
{
|
|
|
|
drawLine(x0, y0, x1, y1, color);
|
|
|
|
drawLine(x1, y1, x2, y2, color);
|
|
|
|
drawLine(x2, y2, x0, y0, color);
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::fillTriangle
|
2015-05-01 05:26:58 +00:00
|
|
|
(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
|
|
|
|
{
|
|
|
|
|
|
|
|
int16_t a, b, y, last;
|
|
|
|
// Sort coordinates by Y order (y2 >= y1 >= y0)
|
|
|
|
if (y0 > y1)
|
|
|
|
{
|
2020-06-18 21:37:33 +00:00
|
|
|
swapInt16(y0, y1); swapInt16(x0, x1);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
if (y1 > y2)
|
|
|
|
{
|
2020-06-18 21:37:33 +00:00
|
|
|
swapInt16(y2, y1); swapInt16(x2, x1);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
if (y0 > y1)
|
|
|
|
{
|
2020-06-18 21:37:33 +00:00
|
|
|
swapInt16(y0, y1); swapInt16(x0, x1);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(y0 == y2)
|
|
|
|
{ // Handle awkward all-on-same-line case as its own thing
|
|
|
|
a = b = x0;
|
|
|
|
if(x1 < a)
|
|
|
|
{
|
|
|
|
a = x1;
|
|
|
|
}
|
|
|
|
else if(x1 > b)
|
|
|
|
{
|
|
|
|
b = x1;
|
|
|
|
}
|
|
|
|
if(x2 < a)
|
|
|
|
{
|
|
|
|
a = x2;
|
|
|
|
}
|
|
|
|
else if(x2 > b)
|
|
|
|
{
|
|
|
|
b = x2;
|
|
|
|
}
|
|
|
|
drawFastHLine(a, y0, b-a+1, color);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int16_t dx01 = x1 - x0,
|
|
|
|
dy01 = y1 - y0,
|
|
|
|
dx02 = x2 - x0,
|
|
|
|
dy02 = y2 - y0,
|
|
|
|
dx12 = x2 - x1,
|
|
|
|
dy12 = y2 - y1,
|
|
|
|
sa = 0,
|
|
|
|
sb = 0;
|
|
|
|
|
|
|
|
// For upper part of triangle, find scanline crossings for segments
|
|
|
|
// 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
|
|
|
|
// is included here (and second loop will be skipped, avoiding a /0
|
|
|
|
// error there), otherwise scanline y1 is skipped here and handled
|
|
|
|
// in the second loop...which also avoids a /0 error here if y0=y1
|
|
|
|
// (flat-topped triangle).
|
|
|
|
if (y1 == y2)
|
|
|
|
{
|
|
|
|
last = y1; // Include y1 scanline
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
last = y1-1; // Skip it
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for(y = y0; y <= last; y++)
|
|
|
|
{
|
|
|
|
a = x0 + sa / dy01;
|
|
|
|
b = x0 + sb / dy02;
|
|
|
|
sa += dx01;
|
|
|
|
sb += dx02;
|
|
|
|
|
|
|
|
if(a > b)
|
|
|
|
{
|
2020-06-18 21:37:33 +00:00
|
|
|
swapInt16(a,b);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
drawFastHLine(a, y, b-a+1, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
// For lower part of triangle, find scanline crossings for segments
|
|
|
|
// 0-2 and 1-2. This loop is skipped if y1=y2.
|
|
|
|
sa = dx12 * (y - y1);
|
|
|
|
sb = dx02 * (y - y0);
|
|
|
|
|
|
|
|
for(; y <= y2; y++)
|
|
|
|
{
|
|
|
|
a = x1 + sa / dy12;
|
|
|
|
b = x0 + sb / dy02;
|
|
|
|
sa += dx12;
|
|
|
|
sb += dx02;
|
|
|
|
|
|
|
|
if(a > b)
|
|
|
|
{
|
2020-06-18 21:37:33 +00:00
|
|
|
swapInt16(a,b);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
drawFastHLine(a, y, b-a+1, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::drawBitmap
|
2016-02-20 19:43:14 +00:00
|
|
|
(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h,
|
2015-05-01 05:26:58 +00:00
|
|
|
uint8_t color)
|
|
|
|
{
|
2016-10-20 00:23:34 +00:00
|
|
|
// no need to draw at all if we're offscreen
|
2020-06-23 18:40:30 +00:00
|
|
|
if (x + w <= 0 || x > WIDTH - 1 || y + h <= 0 || y > HEIGHT - 1)
|
2015-05-01 05:26:58 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
int yOffset = abs(y) % 8;
|
|
|
|
int sRow = y / 8;
|
|
|
|
if (y < 0) {
|
|
|
|
sRow--;
|
|
|
|
yOffset = 8 - yOffset;
|
|
|
|
}
|
|
|
|
int rows = h/8;
|
|
|
|
if (h%8!=0) rows++;
|
|
|
|
for (int a = 0; a < rows; a++) {
|
|
|
|
int bRow = sRow + a;
|
|
|
|
if (bRow > (HEIGHT/8)-1) break;
|
|
|
|
if (bRow > -2) {
|
|
|
|
for (int iCol = 0; iCol<w; iCol++) {
|
|
|
|
if (iCol + x > (WIDTH-1)) break;
|
|
|
|
if (iCol + x >= 0) {
|
|
|
|
if (bRow >= 0) {
|
2016-10-20 00:23:34 +00:00
|
|
|
if (color == WHITE)
|
|
|
|
sBuffer[(bRow*WIDTH) + x + iCol] |= pgm_read_byte(bitmap+(a*w)+iCol) << yOffset;
|
|
|
|
else if (color == BLACK)
|
|
|
|
sBuffer[(bRow*WIDTH) + x + iCol] &= ~(pgm_read_byte(bitmap+(a*w)+iCol) << yOffset);
|
|
|
|
else
|
|
|
|
sBuffer[(bRow*WIDTH) + x + iCol] ^= pgm_read_byte(bitmap+(a*w)+iCol) << yOffset;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
if (yOffset && bRow<(HEIGHT/8)-1 && bRow > -2) {
|
2016-10-20 00:23:34 +00:00
|
|
|
if (color == WHITE)
|
|
|
|
sBuffer[((bRow+1)*WIDTH) + x + iCol] |= pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset);
|
|
|
|
else if (color == BLACK)
|
|
|
|
sBuffer[((bRow+1)*WIDTH) + x + iCol] &= ~(pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset));
|
|
|
|
else
|
|
|
|
sBuffer[((bRow+1)*WIDTH) + x + iCol] ^= pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::drawSlowXYBitmap
|
2015-05-01 05:26:58 +00:00
|
|
|
(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color)
|
|
|
|
{
|
2016-05-27 21:43:58 +00:00
|
|
|
// no need to draw at all of we're offscreen
|
2020-06-23 18:40:30 +00:00
|
|
|
if (x + w <= 0 || x > WIDTH - 1 || y + h <= 0 || y > HEIGHT - 1)
|
2015-05-01 05:26:58 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
int16_t xi, yi, byteWidth = (w + 7) / 8;
|
|
|
|
for(yi = 0; yi < h; yi++) {
|
|
|
|
for(xi = 0; xi < w; xi++ ) {
|
|
|
|
if(pgm_read_byte(bitmap + yi * byteWidth + xi / 8) & (128 >> (xi & 7))) {
|
|
|
|
drawPixel(x + xi, y + yi, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-17 18:56:25 +00:00
|
|
|
// Helper for drawCompressed()
|
2020-08-28 23:39:30 +00:00
|
|
|
class Arduboy2Base::BitStreamReader
|
2018-01-17 18:56:25 +00:00
|
|
|
{
|
2020-08-28 23:39:30 +00:00
|
|
|
private:
|
2018-01-17 18:51:34 +00:00
|
|
|
const uint8_t *source;
|
|
|
|
uint16_t sourceIndex;
|
|
|
|
uint8_t bitBuffer;
|
|
|
|
uint8_t byteBuffer;
|
2018-01-17 18:56:25 +00:00
|
|
|
|
2020-08-28 23:39:30 +00:00
|
|
|
public:
|
|
|
|
BitStreamReader(const uint8_t *bitmap)
|
|
|
|
: source(bitmap), sourceIndex(), bitBuffer(), byteBuffer()
|
2018-01-17 18:51:34 +00:00
|
|
|
{
|
|
|
|
}
|
2018-01-17 18:56:25 +00:00
|
|
|
|
2018-01-17 18:51:34 +00:00
|
|
|
uint16_t readBits(uint16_t bitCount)
|
2016-10-20 00:23:34 +00:00
|
|
|
{
|
2018-01-17 18:51:34 +00:00
|
|
|
uint16_t result = 0;
|
|
|
|
for (uint16_t i = 0; i < bitCount; i++)
|
2016-10-20 00:23:34 +00:00
|
|
|
{
|
2020-08-28 23:39:30 +00:00
|
|
|
if (bitBuffer == 0)
|
2018-01-17 18:51:34 +00:00
|
|
|
{
|
2020-08-28 23:39:30 +00:00
|
|
|
bitBuffer = 0x1;
|
|
|
|
byteBuffer = pgm_read_byte(&source[sourceIndex]);
|
|
|
|
++sourceIndex;
|
2018-01-17 18:51:34 +00:00
|
|
|
}
|
2018-01-17 18:56:25 +00:00
|
|
|
|
2020-08-28 23:39:30 +00:00
|
|
|
if ((byteBuffer & bitBuffer) != 0)
|
2018-11-09 20:10:05 +00:00
|
|
|
result |= (1 << i);
|
2018-01-17 18:56:25 +00:00
|
|
|
|
2020-08-28 23:39:30 +00:00
|
|
|
bitBuffer <<= 1;
|
2016-10-20 00:23:34 +00:00
|
|
|
}
|
2018-01-17 18:51:34 +00:00
|
|
|
return result;
|
2016-10-20 00:23:34 +00:00
|
|
|
}
|
2018-01-17 18:51:34 +00:00
|
|
|
};
|
|
|
|
|
2016-10-20 00:23:34 +00:00
|
|
|
void Arduboy2Base::drawCompressed(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t color)
|
|
|
|
{
|
|
|
|
// set up decompress state
|
2020-08-28 23:39:30 +00:00
|
|
|
BitStreamReader cs(bitmap);
|
2016-10-20 00:23:34 +00:00
|
|
|
|
|
|
|
// read header
|
2018-01-17 18:51:34 +00:00
|
|
|
int width = (int)cs.readBits(8) + 1;
|
|
|
|
int height = (int)cs.readBits(8) + 1;
|
|
|
|
uint8_t spanColour = (uint8_t)cs.readBits(1); // starting colour
|
2016-10-20 00:23:34 +00:00
|
|
|
|
|
|
|
// no need to draw at all if we're offscreen
|
2020-06-23 18:40:30 +00:00
|
|
|
if ((sx + width <= 0) || (sx > WIDTH - 1) || (sy + height <= 0) || (sy > HEIGHT - 1))
|
2016-10-20 00:23:34 +00:00
|
|
|
return;
|
|
|
|
|
2018-01-17 18:51:34 +00:00
|
|
|
// sy = sy - (frame * height);
|
2016-10-20 00:23:34 +00:00
|
|
|
int yOffset = abs(sy) % 8;
|
2018-01-17 18:51:34 +00:00
|
|
|
int startRow = sy / 8;
|
2016-10-20 00:23:34 +00:00
|
|
|
if (sy < 0) {
|
2018-01-17 18:51:34 +00:00
|
|
|
startRow--;
|
2016-10-20 00:23:34 +00:00
|
|
|
yOffset = 8 - yOffset;
|
|
|
|
}
|
2018-01-17 18:51:34 +00:00
|
|
|
int rows = height / 8;
|
|
|
|
if ((height % 8) != 0)
|
|
|
|
++rows;
|
2016-10-20 00:23:34 +00:00
|
|
|
|
2018-01-17 18:51:34 +00:00
|
|
|
int rowOffset = 0; // +(frame*rows);
|
|
|
|
int columnOffset = 0;
|
2018-01-17 18:56:25 +00:00
|
|
|
|
2018-01-17 18:51:34 +00:00
|
|
|
uint8_t byte = 0x00;
|
|
|
|
uint8_t bit = 0x01;
|
|
|
|
while (rowOffset < rows) // + (frame*rows))
|
2016-10-20 00:23:34 +00:00
|
|
|
{
|
2018-01-17 18:51:34 +00:00
|
|
|
uint16_t bitLength = 1;
|
|
|
|
while (cs.readBits(1) == 0)
|
|
|
|
bitLength += 2;
|
2016-10-20 00:23:34 +00:00
|
|
|
|
2018-01-17 18:51:34 +00:00
|
|
|
uint16_t len = cs.readBits(bitLength) + 1; // span length
|
2016-10-20 00:23:34 +00:00
|
|
|
|
|
|
|
// draw the span
|
2018-01-17 18:51:34 +00:00
|
|
|
for (uint16_t i = 0; i < len; ++i)
|
2016-10-20 00:23:34 +00:00
|
|
|
{
|
2018-01-17 18:51:34 +00:00
|
|
|
if (spanColour != 0)
|
2016-10-20 00:23:34 +00:00
|
|
|
byte |= bit;
|
|
|
|
bit <<= 1;
|
|
|
|
|
2018-01-17 18:51:34 +00:00
|
|
|
if (bit == 0) // reached end of byte
|
2016-10-20 00:23:34 +00:00
|
|
|
{
|
|
|
|
// draw
|
2018-01-17 18:51:34 +00:00
|
|
|
int bRow = startRow + rowOffset;
|
2016-10-20 00:23:34 +00:00
|
|
|
|
|
|
|
//if (byte) // possible optimisation
|
2018-01-17 18:56:25 +00:00
|
|
|
if ((bRow <= (HEIGHT / 8) - 1) && (bRow > -2) &&
|
|
|
|
(columnOffset + sx <= (WIDTH - 1)) && (columnOffset + sx >= 0))
|
2018-01-17 18:51:34 +00:00
|
|
|
{
|
|
|
|
int16_t offset = (bRow * WIDTH) + sx + columnOffset;
|
|
|
|
if (bRow >= 0)
|
|
|
|
{
|
|
|
|
int16_t index = offset;
|
|
|
|
uint8_t value = byte << yOffset;
|
2018-01-17 18:56:25 +00:00
|
|
|
|
2018-01-17 18:51:34 +00:00
|
|
|
if (color != 0)
|
|
|
|
sBuffer[index] |= value;
|
|
|
|
else
|
|
|
|
sBuffer[index] &= ~value;
|
|
|
|
}
|
|
|
|
if ((yOffset != 0) && (bRow < (HEIGHT / 8) - 1))
|
|
|
|
{
|
|
|
|
int16_t index = offset + WIDTH;
|
|
|
|
uint8_t value = byte >> (8 - yOffset);
|
2018-01-17 18:56:25 +00:00
|
|
|
|
2018-01-17 18:51:34 +00:00
|
|
|
if (color != 0)
|
|
|
|
sBuffer[index] |= value;
|
|
|
|
else
|
|
|
|
sBuffer[index] &= ~value;
|
|
|
|
}
|
|
|
|
}
|
2016-10-20 00:23:34 +00:00
|
|
|
|
|
|
|
// iterate
|
2018-01-17 18:51:34 +00:00
|
|
|
++columnOffset;
|
|
|
|
if (columnOffset >= width)
|
2016-10-20 00:23:34 +00:00
|
|
|
{
|
2018-01-17 18:51:34 +00:00
|
|
|
columnOffset = 0;
|
|
|
|
++rowOffset;
|
2016-10-20 00:23:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// reset byte
|
2018-01-17 18:51:34 +00:00
|
|
|
byte = 0x00;
|
|
|
|
bit = 0x01;
|
2016-10-20 00:23:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-17 18:51:34 +00:00
|
|
|
spanColour ^= 0x01; // toggle colour bit (bit 0) for next span
|
2016-10-20 00:23:34 +00:00
|
|
|
}
|
|
|
|
}
|
2015-05-01 05:26:58 +00:00
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2Base::display()
|
|
|
|
{
|
2016-10-20 00:23:34 +00:00
|
|
|
paintScreen(sBuffer);
|
2016-05-27 21:43:58 +00:00
|
|
|
}
|
|
|
|
|
2016-11-22 19:05:52 +00:00
|
|
|
void Arduboy2Base::display(bool clear)
|
|
|
|
{
|
|
|
|
paintScreen(sBuffer, clear);
|
|
|
|
}
|
|
|
|
|
2016-11-21 21:31:10 +00:00
|
|
|
uint8_t* Arduboy2Base::getBuffer()
|
2016-05-27 21:43:58 +00:00
|
|
|
{
|
|
|
|
return sBuffer;
|
|
|
|
}
|
|
|
|
|
2016-06-15 18:15:47 +00:00
|
|
|
bool Arduboy2Base::pressed(uint8_t buttons)
|
2016-05-27 21:43:58 +00:00
|
|
|
{
|
|
|
|
return (buttonsState() & buttons) == buttons;
|
|
|
|
}
|
|
|
|
|
2020-06-20 23:44:10 +00:00
|
|
|
bool Arduboy2Base::anyPressed(uint8_t buttons)
|
|
|
|
{
|
|
|
|
return (buttonsState() & buttons) != 0;
|
|
|
|
}
|
|
|
|
|
2016-06-15 18:15:47 +00:00
|
|
|
bool Arduboy2Base::notPressed(uint8_t buttons)
|
2016-05-27 21:43:58 +00:00
|
|
|
{
|
|
|
|
return (buttonsState() & buttons) == 0;
|
|
|
|
}
|
|
|
|
|
2016-10-20 00:23:34 +00:00
|
|
|
void Arduboy2Base::pollButtons()
|
|
|
|
{
|
|
|
|
previousButtonState = currentButtonState;
|
|
|
|
currentButtonState = buttonsState();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Arduboy2Base::justPressed(uint8_t button)
|
|
|
|
{
|
|
|
|
return (!(previousButtonState & button) && (currentButtonState & button));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Arduboy2Base::justReleased(uint8_t button)
|
|
|
|
{
|
|
|
|
return ((previousButtonState & button) && !(currentButtonState & button));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Arduboy2Base::collide(Point point, Rect rect)
|
|
|
|
{
|
|
|
|
return ((point.x >= rect.x) && (point.x < rect.x + rect.width) &&
|
|
|
|
(point.y >= rect.y) && (point.y < rect.y + rect.height));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Arduboy2Base::collide(Rect rect1, Rect rect2)
|
|
|
|
{
|
|
|
|
return !(rect2.x >= rect1.x + rect1.width ||
|
|
|
|
rect2.x + rect2.width <= rect1.x ||
|
|
|
|
rect2.y >= rect1.y + rect1.height ||
|
|
|
|
rect2.y + rect2.height <= rect1.y);
|
|
|
|
}
|
|
|
|
|
2017-02-06 18:34:46 +00:00
|
|
|
uint16_t Arduboy2Base::readUnitID()
|
|
|
|
{
|
2020-06-24 20:14:26 +00:00
|
|
|
return EEPROM.read(eepromUnitID) |
|
|
|
|
(((uint16_t)(EEPROM.read(eepromUnitID + 1))) << 8);
|
2017-02-06 18:34:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::writeUnitID(uint16_t id)
|
|
|
|
{
|
2020-06-24 20:14:26 +00:00
|
|
|
EEPROM.update(eepromUnitID, (uint8_t)(id & 0xff));
|
|
|
|
EEPROM.update(eepromUnitID + 1, (uint8_t)(id >> 8));
|
2017-02-06 18:34:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Arduboy2Base::readUnitName(char* name)
|
|
|
|
{
|
|
|
|
char val;
|
|
|
|
uint8_t dest;
|
2020-06-24 20:14:26 +00:00
|
|
|
uint8_t src = eepromUnitName;
|
2017-02-06 18:34:46 +00:00
|
|
|
|
|
|
|
for (dest = 0; dest < ARDUBOY_UNIT_NAME_LEN; dest++)
|
|
|
|
{
|
2017-03-21 00:28:51 +00:00
|
|
|
val = EEPROM.read(src);
|
2017-02-06 18:34:46 +00:00
|
|
|
name[dest] = val;
|
|
|
|
src++;
|
2017-03-21 00:28:51 +00:00
|
|
|
if (val == 0x00 || (byte)val == 0xFF) {
|
|
|
|
break;
|
|
|
|
}
|
2017-02-06 18:34:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
name[dest] = 0x00;
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
2020-07-10 01:46:05 +00:00
|
|
|
void Arduboy2Base::writeUnitName(const char* name)
|
2017-02-06 18:34:46 +00:00
|
|
|
{
|
|
|
|
bool done = false;
|
2020-06-24 20:14:26 +00:00
|
|
|
uint8_t dest = eepromUnitName;
|
2017-02-06 18:34:46 +00:00
|
|
|
|
|
|
|
for (uint8_t src = 0; src < ARDUBOY_UNIT_NAME_LEN; src++)
|
|
|
|
{
|
2017-03-21 00:01:15 +00:00
|
|
|
if (name[src] == 0x00) {
|
2017-02-06 18:34:46 +00:00
|
|
|
done = true;
|
|
|
|
}
|
2017-03-21 00:01:15 +00:00
|
|
|
// write character or 0 pad if finished
|
|
|
|
EEPROM.update(dest, done ? 0x00 : name[src]);
|
2017-02-06 18:34:46 +00:00
|
|
|
dest++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-19 19:51:21 +00:00
|
|
|
bool Arduboy2Base::readShowBootLogoFlag()
|
|
|
|
{
|
2020-06-24 20:14:26 +00:00
|
|
|
return (EEPROM.read(eepromSysFlags) & sysFlagShowLogoMask);
|
2018-03-19 19:51:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::writeShowBootLogoFlag(bool val)
|
|
|
|
{
|
2020-06-24 20:14:26 +00:00
|
|
|
uint8_t flags = EEPROM.read(eepromSysFlags);
|
2018-03-19 19:51:21 +00:00
|
|
|
|
2020-06-24 20:14:26 +00:00
|
|
|
bitWrite(flags, sysFlagShowLogoBit, val);
|
|
|
|
EEPROM.update(eepromSysFlags, flags);
|
2018-03-19 19:51:21 +00:00
|
|
|
}
|
|
|
|
|
2017-03-29 21:36:13 +00:00
|
|
|
bool Arduboy2Base::readShowUnitNameFlag()
|
|
|
|
{
|
2020-06-24 20:14:26 +00:00
|
|
|
return (EEPROM.read(eepromSysFlags) & sysFlagUnameMask);
|
2017-03-29 21:36:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::writeShowUnitNameFlag(bool val)
|
|
|
|
{
|
2020-06-24 20:14:26 +00:00
|
|
|
uint8_t flags = EEPROM.read(eepromSysFlags);
|
2017-03-29 21:36:13 +00:00
|
|
|
|
2020-06-24 20:14:26 +00:00
|
|
|
bitWrite(flags, sysFlagUnameBit, val);
|
|
|
|
EEPROM.update(eepromSysFlags, flags);
|
2017-03-29 21:36:13 +00:00
|
|
|
}
|
|
|
|
|
2018-04-06 16:51:52 +00:00
|
|
|
bool Arduboy2Base::readShowBootLogoLEDsFlag()
|
|
|
|
{
|
2020-06-24 20:14:26 +00:00
|
|
|
return (EEPROM.read(eepromSysFlags) & sysFlagShowLogoLEDsMask);
|
2018-04-06 16:51:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2Base::writeShowBootLogoLEDsFlag(bool val)
|
|
|
|
{
|
2020-06-24 20:14:26 +00:00
|
|
|
uint8_t flags = EEPROM.read(eepromSysFlags);
|
2018-04-06 16:51:52 +00:00
|
|
|
|
2020-06-24 20:14:26 +00:00
|
|
|
bitWrite(flags, sysFlagShowLogoLEDsBit, val);
|
|
|
|
EEPROM.update(eepromSysFlags, flags);
|
2018-04-06 16:51:52 +00:00
|
|
|
}
|
|
|
|
|
2020-06-18 21:37:33 +00:00
|
|
|
void Arduboy2Base::swapInt16(int16_t& a, int16_t& b)
|
2016-05-27 21:43:58 +00:00
|
|
|
{
|
2016-10-20 00:23:34 +00:00
|
|
|
int16_t temp = a;
|
2016-05-27 21:43:58 +00:00
|
|
|
a = b;
|
|
|
|
b = temp;
|
|
|
|
}
|
|
|
|
|
2016-10-20 00:23:34 +00:00
|
|
|
|
|
|
|
//====================================
|
|
|
|
//========== class Arduboy2 ==========
|
|
|
|
//====================================
|
|
|
|
|
2020-09-24 21:31:27 +00:00
|
|
|
int16_t Arduboy2::cursor_x = 0;
|
|
|
|
int16_t Arduboy2::cursor_y = 0;
|
|
|
|
uint8_t Arduboy2::textColor = WHITE;
|
|
|
|
uint8_t Arduboy2::textBackground = BLACK;
|
|
|
|
uint8_t Arduboy2::textSize = 1;
|
|
|
|
bool Arduboy2::textWrap = false;
|
|
|
|
bool Arduboy2::textRaw = false;
|
|
|
|
|
|
|
|
// functions called here should be public so users can create their
|
|
|
|
// own init functions if they need different behavior than `begin`
|
|
|
|
// provides by default.
|
|
|
|
//
|
|
|
|
// This code and it's documentation should be kept in sync with
|
|
|
|
// Aruduboy2Base::begin()
|
|
|
|
void Arduboy2::begin()
|
2016-05-27 21:43:58 +00:00
|
|
|
{
|
2020-09-24 21:31:27 +00:00
|
|
|
beginDoFirst();
|
|
|
|
|
|
|
|
bootLogo();
|
|
|
|
// alternative logo functions. Work the same as bootLogo() but may reduce
|
|
|
|
// memory size if the sketch uses the same bitmap drawing function or
|
|
|
|
// `Sprites`/`SpritesB` class
|
|
|
|
// bootLogoCompressed();
|
|
|
|
// bootLogoSpritesSelfMasked();
|
|
|
|
// bootLogoSpritesOverwrite();
|
|
|
|
// bootLogoSpritesBSelfMasked();
|
|
|
|
// bootLogoSpritesBOverwrite();
|
|
|
|
|
|
|
|
waitNoButtons();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2::bootLogo()
|
|
|
|
{
|
|
|
|
if (bootLogoShell(drawLogoBitmap))
|
|
|
|
{
|
|
|
|
bootLogoExtra();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2::bootLogoCompressed()
|
|
|
|
{
|
|
|
|
if (bootLogoShell(drawLogoCompressed))
|
|
|
|
{
|
|
|
|
bootLogoExtra();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2::bootLogoSpritesSelfMasked()
|
|
|
|
{
|
|
|
|
if (bootLogoShell(drawLogoSpritesSelfMasked))
|
|
|
|
{
|
|
|
|
bootLogoExtra();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2::bootLogoSpritesOverwrite()
|
|
|
|
{
|
|
|
|
if (bootLogoShell(drawLogoSpritesOverwrite))
|
|
|
|
{
|
|
|
|
bootLogoExtra();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2::bootLogoSpritesBSelfMasked()
|
|
|
|
{
|
|
|
|
if (bootLogoShell(drawLogoSpritesBSelfMasked))
|
|
|
|
{
|
|
|
|
bootLogoExtra();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2::bootLogoSpritesBOverwrite()
|
|
|
|
{
|
|
|
|
if (bootLogoShell(drawLogoSpritesBOverwrite))
|
|
|
|
{
|
|
|
|
bootLogoExtra();
|
|
|
|
}
|
2016-05-27 21:43:58 +00:00
|
|
|
}
|
|
|
|
|
2017-04-12 21:36:28 +00:00
|
|
|
// bootLogoText() should be kept in sync with bootLogoShell()
|
2017-03-27 19:58:17 +00:00
|
|
|
// if changes are made to one, equivalent changes should be made to the other
|
|
|
|
void Arduboy2::bootLogoText()
|
|
|
|
{
|
2018-04-06 16:51:52 +00:00
|
|
|
bool showLEDs = readShowBootLogoLEDsFlag();
|
|
|
|
|
2018-03-19 19:51:21 +00:00
|
|
|
if (!readShowBootLogoFlag()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-04-06 16:51:52 +00:00
|
|
|
if (showLEDs) {
|
|
|
|
digitalWriteRGB(RED_LED, RGB_ON);
|
|
|
|
}
|
2017-03-27 19:58:17 +00:00
|
|
|
|
2020-09-24 21:31:27 +00:00
|
|
|
for (int16_t y = -15; y <= 24; y++) {
|
2017-03-27 19:58:17 +00:00
|
|
|
if (pressed(RIGHT_BUTTON)) {
|
|
|
|
digitalWriteRGB(RGB_OFF, RGB_OFF, RGB_OFF); // all LEDs off
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-04-06 16:51:52 +00:00
|
|
|
if (showLEDs && y == 4) {
|
2017-03-27 19:58:17 +00:00
|
|
|
digitalWriteRGB(RED_LED, RGB_OFF); // red LED off
|
|
|
|
digitalWriteRGB(GREEN_LED, RGB_ON); // green LED on
|
|
|
|
}
|
|
|
|
|
2018-01-21 22:33:41 +00:00
|
|
|
// Using display(CLEAR_BUFFER) instead of clear() may save code space.
|
|
|
|
// The extra time it takes to repaint the previous logo isn't an issue.
|
|
|
|
display(CLEAR_BUFFER);
|
2017-03-27 19:58:17 +00:00
|
|
|
cursor_x = 23;
|
|
|
|
cursor_y = y;
|
2018-01-21 22:33:41 +00:00
|
|
|
textSize = 2;
|
|
|
|
print(F("ARDUBOY"));
|
|
|
|
textSize = 1;
|
2017-03-27 19:58:17 +00:00
|
|
|
display();
|
2018-01-21 22:33:41 +00:00
|
|
|
delayShort(11);
|
2017-03-27 19:58:17 +00:00
|
|
|
}
|
|
|
|
|
2018-04-06 16:51:52 +00:00
|
|
|
if (showLEDs) {
|
|
|
|
digitalWriteRGB(GREEN_LED, RGB_OFF); // green LED off
|
|
|
|
digitalWriteRGB(BLUE_LED, RGB_ON); // blue LED on
|
|
|
|
}
|
2018-01-21 22:33:41 +00:00
|
|
|
delayShort(400);
|
2017-03-27 19:58:17 +00:00
|
|
|
digitalWriteRGB(BLUE_LED, RGB_OFF);
|
|
|
|
|
|
|
|
bootLogoExtra();
|
|
|
|
}
|
|
|
|
|
2017-02-06 18:34:46 +00:00
|
|
|
void Arduboy2::bootLogoExtra()
|
|
|
|
{
|
2017-03-29 21:36:13 +00:00
|
|
|
uint8_t c;
|
|
|
|
|
|
|
|
if (!readShowUnitNameFlag())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-24 20:14:26 +00:00
|
|
|
c = EEPROM.read(eepromUnitName);
|
2017-02-06 18:34:46 +00:00
|
|
|
|
|
|
|
if (c != 0xFF && c != 0x00)
|
|
|
|
{
|
2020-06-24 20:14:26 +00:00
|
|
|
uint8_t i = eepromUnitName;
|
2017-02-06 18:34:46 +00:00
|
|
|
cursor_x = 50;
|
|
|
|
cursor_y = 56;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
write(c);
|
|
|
|
c = EEPROM.read(++i);
|
2017-03-29 21:36:13 +00:00
|
|
|
}
|
2020-06-24 20:14:26 +00:00
|
|
|
while (i < eepromUnitName + ARDUBOY_UNIT_NAME_LEN);
|
2017-02-06 18:34:46 +00:00
|
|
|
|
|
|
|
display();
|
2017-04-17 21:34:58 +00:00
|
|
|
delayShort(1000);
|
2017-02-06 18:34:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
size_t Arduboy2::write(uint8_t c)
|
|
|
|
{
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
if ((c == '\r') && !textRaw)
|
2016-05-27 21:43:58 +00:00
|
|
|
{
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
return 1;
|
2016-05-27 21:43:58 +00:00
|
|
|
}
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
|
|
|
|
if (((c == '\n') && !textRaw) ||
|
|
|
|
(textWrap && (cursor_x > (WIDTH - (characterWidth * textSize)))))
|
2016-05-27 21:43:58 +00:00
|
|
|
{
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
cursor_x = 0;
|
|
|
|
cursor_y += fullCharacterHeight * textSize;
|
2016-05-27 21:43:58 +00:00
|
|
|
}
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
|
|
|
|
if ((c != '\n') || textRaw)
|
2016-05-27 21:43:58 +00:00
|
|
|
{
|
|
|
|
drawChar(cursor_x, cursor_y, c, textColor, textBackground, textSize);
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
cursor_x += fullCharacterWidth * textSize;
|
2016-05-27 21:43:58 +00:00
|
|
|
}
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
|
2016-09-15 22:12:27 +00:00
|
|
|
return 1;
|
2016-05-27 21:43:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2::drawChar
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
(int16_t x, int16_t y, uint8_t c, uint8_t color, uint8_t bg, uint8_t size)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
// It is assumed that rendering characters fully off screen will be rare,
|
|
|
|
// so let drawPixel() handle off screen checks, to reduce code size at the
|
|
|
|
// expense of slower off screen character handling.
|
|
|
|
#if 0
|
2016-05-27 21:43:58 +00:00
|
|
|
if ((x >= WIDTH) || // Clip right
|
|
|
|
(y >= HEIGHT) || // Clip bottom
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
((x + characterWidth * size - 1) < 0) || // Clip left
|
|
|
|
((y + characterHeight * size - 1) < 0) // Clip top
|
2016-05-27 21:43:58 +00:00
|
|
|
)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
bool drawBackground = bg != color;
|
|
|
|
const uint8_t* bitmap =
|
|
|
|
&font5x7[c * characterWidth * ((characterHeight + 8 - 1) / 8)];
|
2015-05-01 05:26:58 +00:00
|
|
|
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
for (uint8_t i = 0; i < fullCharacterWidth; i++)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
uint8_t column;
|
|
|
|
|
|
|
|
if (characterHeight <= 8)
|
|
|
|
{
|
|
|
|
column = (i < characterWidth) ? pgm_read_byte(bitmap++) : 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
column = 0;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
// draw the character by columns. Top to bottom, left to right
|
|
|
|
// including character spacing on the right
|
|
|
|
for (uint8_t j = 0; j < characterHeight; j++)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
if (characterHeight > 8)
|
|
|
|
{
|
|
|
|
// at this point variable "column" will be 0, either from initialization
|
|
|
|
// or by having eight 0 bits shifted in by the >>= operation below
|
|
|
|
if ((j % 8 == 0) && (i < characterWidth))
|
|
|
|
{
|
|
|
|
column = pgm_read_byte(bitmap++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// pixelIsSet should be a bool but at the time of writing,
|
|
|
|
// the GCC AVR compiler generates less code if it's a uint8_t
|
|
|
|
uint8_t pixelIsSet = column & 0x01;
|
|
|
|
|
|
|
|
if (pixelIsSet || drawBackground)
|
|
|
|
{
|
|
|
|
for (uint8_t a = 0; a < size; a++)
|
|
|
|
{
|
|
|
|
for (uint8_t b = 0; b < size; b++)
|
|
|
|
{
|
|
|
|
drawPixel(x + (i * size) + a, y + (j * size) + b,
|
|
|
|
pixelIsSet ? color : bg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
column >>= 1;
|
|
|
|
}
|
2015-05-01 05:26:58 +00:00
|
|
|
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
// draw the inter-line spacing pixels for this column if required
|
|
|
|
if (drawBackground)
|
|
|
|
{
|
|
|
|
for (uint8_t j = characterHeight; j < fullCharacterHeight; j++)
|
|
|
|
{
|
|
|
|
for (uint8_t a = 0; a < size; a++)
|
|
|
|
{
|
|
|
|
for (uint8_t b = 0; b < size; b++)
|
|
|
|
{
|
|
|
|
drawPixel(x + (i * size) + a, y + (j * size) + b, bg);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2::setCursor(int16_t x, int16_t y)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2016-05-27 21:43:58 +00:00
|
|
|
cursor_x = x;
|
|
|
|
cursor_y = y;
|
|
|
|
}
|
|
|
|
|
2020-06-20 23:44:10 +00:00
|
|
|
void Arduboy2::setCursorX(int16_t x)
|
|
|
|
{
|
|
|
|
cursor_x = x;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2::setCursorY(int16_t y)
|
|
|
|
{
|
|
|
|
cursor_y = y;
|
|
|
|
}
|
|
|
|
|
2017-04-07 19:02:11 +00:00
|
|
|
int16_t Arduboy2::getCursorX()
|
|
|
|
{
|
2016-05-27 21:43:58 +00:00
|
|
|
return cursor_x;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
2017-04-07 19:02:11 +00:00
|
|
|
int16_t Arduboy2::getCursorY()
|
|
|
|
{
|
2016-05-27 21:43:58 +00:00
|
|
|
return cursor_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arduboy2::setTextColor(uint8_t color)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2016-05-27 21:43:58 +00:00
|
|
|
textColor = color;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
2017-07-31 20:57:28 +00:00
|
|
|
uint8_t Arduboy2::getTextColor()
|
|
|
|
{
|
|
|
|
return textColor;
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2::setTextBackground(uint8_t bg)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2016-05-27 21:43:58 +00:00
|
|
|
textBackground = bg;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
2017-07-31 20:57:28 +00:00
|
|
|
uint8_t Arduboy2::getTextBackground()
|
|
|
|
{
|
|
|
|
return textBackground;
|
|
|
|
}
|
|
|
|
|
2016-05-27 21:43:58 +00:00
|
|
|
void Arduboy2::setTextSize(uint8_t s)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2016-05-27 21:43:58 +00:00
|
|
|
// size must always be 1 or higher
|
|
|
|
textSize = max(1, s);
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
2017-07-31 20:57:28 +00:00
|
|
|
uint8_t Arduboy2::getTextSize()
|
|
|
|
{
|
|
|
|
return textSize;
|
|
|
|
}
|
|
|
|
|
2016-06-15 18:15:47 +00:00
|
|
|
void Arduboy2::setTextWrap(bool w)
|
2015-05-01 05:26:58 +00:00
|
|
|
{
|
2016-05-27 21:43:58 +00:00
|
|
|
textWrap = w;
|
|
|
|
}
|
|
|
|
|
2017-07-31 20:57:28 +00:00
|
|
|
bool Arduboy2::getTextWrap()
|
|
|
|
{
|
|
|
|
return textWrap;
|
|
|
|
}
|
|
|
|
|
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
When set, the write() function will render the value \n (0x0a) as
an "inverse white circle" instead of handling it as a newline and
render value \r (0x0d) as a "musical eighth note" instead of
ignoring it.
- Spacing added at the end of each character is no longer factored in
when determining if a character will fit on screen at the end of
a line.
- Checking if line wrap is neccessary is done before drawing a
character, instead of doing it after drawing to prepare for the
next character. This fixed a problem with requested line feeds
interacting with wrapping, resulting in unexpected blank lines.
- Fixed a bug that caused the drawing of characters with a transparent
background to not work properly. (This is selected by setting the
background color to be the same as the text color.)
- Added new functions getCharacterWidth() getCharacterHeight()
getCharacterSpacing() getLineSpacing() to programmatically get
the dimensions of a character, given the desired text size.
- Functions write() and drawChar() are now able to render a font of
any character size, and with any character and line spacing, by
setting variables characterWidth characterHeight characterSpacing
and lineSpacing, and providing a matching font array. This doesn't
affect their use with this library but has been done for the sake
of code portability.
2020-08-25 20:40:34 +00:00
|
|
|
void Arduboy2::setTextRawMode(bool raw)
|
|
|
|
{
|
|
|
|
textRaw = raw;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Arduboy2::getTextRawMode()
|
|
|
|
{
|
|
|
|
return textRaw;
|
|
|
|
}
|
|
|
|
|
2017-04-07 19:02:11 +00:00
|
|
|
void Arduboy2::clear()
|
|
|
|
{
|
2016-05-27 21:43:58 +00:00
|
|
|
Arduboy2Base::clear();
|
|
|
|
cursor_x = cursor_y = 0;
|
2015-05-01 05:26:58 +00:00
|
|
|
}
|
2016-02-20 19:43:14 +00:00
|
|
|
|