mirror of https://github.com/MLXXXp/Arduboy2.git
Add Team A.R.G. Arglib functionality
- Version changed to 2.1.0 - Added sprite, button and collision functions from ArduboyExtra - Added drawCompressed() function from Arglib - Updated LICENSE.txt - Removed CONTRIBUTORS.md (now covered by LICENSE.txt)
This commit is contained in:
parent
f9dc4cb0e5
commit
f763263d2b
|
@ -1,17 +0,0 @@
|
||||||
# Individual Contributors
|
|
||||||
|
|
||||||
- Kevin "Arduboy" Bates (@Arduboy)
|
|
||||||
- arduboychris (@arduboychris)
|
|
||||||
- Ross (@rogosher)
|
|
||||||
- Andrew (@ace-dent)
|
|
||||||
- Josh Goebel (@yyyc514)
|
|
||||||
- Scott Allen (@MLXXXp)
|
|
||||||
|
|
||||||
|
|
||||||
# Included code from other open source projects
|
|
||||||
|
|
||||||
- Original SSD1306 library
|
|
||||||
https://github.com/adafruit/Adafruit_SSD1306
|
|
||||||
BSD License
|
|
||||||
Copyright (c) 2012, Adafruit Industries
|
|
||||||
|
|
106
LICENSE.txt
106
LICENSE.txt
|
@ -1,14 +1,23 @@
|
||||||
Software License Agreement (BSD License)
|
Software License Agreements
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Licensed under the BSD 3-clause license:
|
||||||
|
|
||||||
|
Arduboy2 library:
|
||||||
|
Copyright (c) 2016, Scott Allen
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
The Arduboy 2 library was forked from the Arduboy library:
|
||||||
|
https://github.com/Arduboy/Arduboy
|
||||||
Copyright (c) 2016, Kevin "Arduboy" Bates
|
Copyright (c) 2016, Kevin "Arduboy" Bates
|
||||||
Copyright (c) 2016, Chris Martinez
|
Copyright (c) 2016, Chris Martinez
|
||||||
Copyright (c) 2016, Josh Goebel
|
Copyright (c) 2016, Josh Goebel
|
||||||
Copyright (c) 2016, Scott Allen
|
Copyright (c) 2016, Scott Allen
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
which is in turn partially based on the Adafruit_SSD1306 library
|
||||||
Please see CONTRIBUTORS.md for license information and copyright
|
https://github.com/adafruit/Adafruit_SSD1306
|
||||||
notices for any libraries we built on or redistribute.
|
Copyright (c) 2012, Adafruit Industries
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
@ -31,3 +40,90 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Licensed under the 2-clause BSD license:
|
||||||
|
|
||||||
|
Portions of the Arduboy library, and thus the Arduboy2 library, based on
|
||||||
|
the Adafruit-GFX library:
|
||||||
|
https://github.com/adafruit/Adafruit-GFX-Library
|
||||||
|
Copyright (c) 2012 Adafruit Industries
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
- Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Licensed under the MIT license:
|
||||||
|
|
||||||
|
Code from ArduboyExtra:
|
||||||
|
https://github.com/yyyc514/ArduboyExtra
|
||||||
|
Copyright (c) 2015 Josh Goebel
|
||||||
|
|
||||||
|
Code for drawing compressed bitmaps:
|
||||||
|
https://github.com/TEAMarg/drawCompressed
|
||||||
|
Copyright (c) 2016 TEAM a.r.g.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Licensed under the GNU LGPL license:
|
||||||
|
https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
|
||||||
|
|
||||||
|
ArduBreakout example sketch:
|
||||||
|
Original work:
|
||||||
|
Copyright (c) 2011 Sebastian Goscik
|
||||||
|
All rights reserved.
|
||||||
|
Modified work:
|
||||||
|
Copyright (c) 2016 Scott Allen
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Buttons and HelloWorld example sketches:
|
||||||
|
Copyright (c) 2015 David Martinez
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This work is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
|
|
@ -36,6 +36,11 @@ Main differences between Arduboy2 and Arduboy V1.1 are:
|
||||||
- The *beginNoLogo()* function is not included. This function could be used in Arduboy V1.1 in place of *begin()* to suppress the displaying of the ARDUBOY logo and thus free up the code that it required. Instead, Arduboy2 allows a sketch to call *boot()* and then add in any extra features that *begin()* provides by calling their functions directly after *boot()*, if desired.
|
- The *beginNoLogo()* function is not included. This function could be used in Arduboy V1.1 in place of *begin()* to suppress the displaying of the ARDUBOY logo and thus free up the code that it required. Instead, Arduboy2 allows a sketch to call *boot()* and then add in any extra features that *begin()* provides by calling their functions directly after *boot()*, if desired.
|
||||||
- The *ArduboyCore* and *ArduboyAudio* base classes, previously only available to, and used to derive, the *Arduboy* class, have been made publicly available for the benefit of developers who may wish to use them as the base of an entirely new library. This change doesn't affect the existing API.
|
- The *ArduboyCore* and *ArduboyAudio* base classes, previously only available to, and used to derive, the *Arduboy* class, have been made publicly available for the benefit of developers who may wish to use them as the base of an entirely new library. This change doesn't affect the existing API.
|
||||||
|
|
||||||
|
As of version 2.1.0 functionality from the [Team A.R.G.](http://www.team-arg.org/) *Arglib* library has been added:
|
||||||
|
|
||||||
|
- The sprite drawing functions, collision detection functions, and button handling functions that Team A.R.G. incorporated from the [ArduboyExtra](https://github.com/yyyc514/ArduboyExtra) project. The *poll()* function was renamed *pollButtons()* for clarity.
|
||||||
|
- The *drawCompressed()* function, which allows compressed bitmaps to be drawn. Saving bitmaps in compressed form may reduce overall sketch size.
|
||||||
|
|
||||||
## Start up features
|
## Start up features
|
||||||
|
|
||||||
The *begin()* function, used to initialize the library, includes features that are intended to be available to all sketches using the library (unless the sketch developer has chosen to disable one or more of them to free up some code space):
|
The *begin()* function, used to initialize the library, includes features that are intended to be available to all sketches using the library (unless the sketch developer has chosen to disable one or more of them to free up some code space):
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/MLXXXp/Arduboy2.git"
|
"url": "https://github.com/MLXXXp/Arduboy2.git"
|
||||||
},
|
},
|
||||||
"version": "2.0.5",
|
"version": "2.1.0",
|
||||||
"exclude": "extras",
|
"exclude": "extras",
|
||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
||||||
"platforms": "atmelavr"
|
"platforms": "atmelavr"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name=Arduboy2
|
name=Arduboy2
|
||||||
version=2.0.5
|
version=2.1.0
|
||||||
author=Chris J. Martinez, Kevin Bates, Josh Goebel, Scott Allen, Ross O. Shoger
|
author=Chris J. Martinez, Kevin Bates, Josh Goebel, Scott Allen, Ross O. Shoger
|
||||||
maintainer=Scott Allen saydisp-git@yahoo.ca
|
maintainer=Scott Allen saydisp-git@yahoo.ca
|
||||||
sentence=An alternative library for use with the Arduboy game system.
|
sentence=An alternative library for use with the Arduboy game system.
|
||||||
|
|
198
src/Arduboy2.cpp
198
src/Arduboy2.cpp
|
@ -10,12 +10,13 @@ uint8_t Arduboy2Base::sBuffer[];
|
||||||
|
|
||||||
Arduboy2Base::Arduboy2Base()
|
Arduboy2Base::Arduboy2Base()
|
||||||
{
|
{
|
||||||
|
currentButtonState = 0;
|
||||||
|
previousButtonState = 0;
|
||||||
// frame management
|
// frame management
|
||||||
setFrameRate(60);
|
setFrameRate(60);
|
||||||
frameCount = 0;
|
frameCount = 0;
|
||||||
nextFrameStart = 0;
|
nextFrameStart = 0;
|
||||||
post_render = false;
|
post_render = false;
|
||||||
|
|
||||||
// init not necessary, will be reset after first use
|
// init not necessary, will be reset after first use
|
||||||
// lastFrameStart
|
// lastFrameStart
|
||||||
// lastFrameDurationMs
|
// lastFrameDurationMs
|
||||||
|
@ -644,7 +645,7 @@ void Arduboy2Base::drawBitmap
|
||||||
(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h,
|
(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h,
|
||||||
uint8_t color)
|
uint8_t color)
|
||||||
{
|
{
|
||||||
// no need to dar at all of we're offscreen
|
// no need to draw at all if we're offscreen
|
||||||
if (x+w < 0 || x > WIDTH-1 || y+h < 0 || y > HEIGHT-1)
|
if (x+w < 0 || x > WIDTH-1 || y+h < 0 || y > HEIGHT-1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -664,14 +665,20 @@ void Arduboy2Base::drawBitmap
|
||||||
if (iCol + x > (WIDTH-1)) break;
|
if (iCol + x > (WIDTH-1)) break;
|
||||||
if (iCol + x >= 0) {
|
if (iCol + x >= 0) {
|
||||||
if (bRow >= 0) {
|
if (bRow >= 0) {
|
||||||
if (color == WHITE) this->sBuffer[ (bRow*WIDTH) + x + iCol ] |= pgm_read_byte(bitmap+(a*w)+iCol) << yOffset;
|
if (color == WHITE)
|
||||||
else if (color == BLACK) this->sBuffer[ (bRow*WIDTH) + x + iCol ] &= ~(pgm_read_byte(bitmap+(a*w)+iCol) << yOffset);
|
sBuffer[(bRow*WIDTH) + x + iCol] |= pgm_read_byte(bitmap+(a*w)+iCol) << yOffset;
|
||||||
else this->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;
|
||||||
}
|
}
|
||||||
if (yOffset && bRow<(HEIGHT/8)-1 && bRow > -2) {
|
if (yOffset && bRow<(HEIGHT/8)-1 && bRow > -2) {
|
||||||
if (color == WHITE) this->sBuffer[ ((bRow+1)*WIDTH) + x + iCol ] |= pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset);
|
if (color == WHITE)
|
||||||
else if (color == BLACK) this->sBuffer[ ((bRow+1)*WIDTH) + x + iCol ] &= ~(pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset));
|
sBuffer[((bRow+1)*WIDTH) + x + iCol] |= pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset);
|
||||||
else this->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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -697,10 +704,141 @@ void Arduboy2Base::drawSlowXYBitmap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct CSESSION {
|
||||||
|
int byte;
|
||||||
|
int bit;
|
||||||
|
const uint8_t *src;
|
||||||
|
int src_pos;
|
||||||
|
} CSESSION;
|
||||||
|
static CSESSION cs;
|
||||||
|
|
||||||
|
static int getval(int bits)
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < bits; i++)
|
||||||
|
{
|
||||||
|
if (cs.bit == 0x100)
|
||||||
|
{
|
||||||
|
cs.bit = 0x1;
|
||||||
|
cs.byte = pgm_read_byte(&cs.src[cs.src_pos]);
|
||||||
|
cs.src_pos ++;
|
||||||
|
}
|
||||||
|
if (cs.byte & cs.bit)
|
||||||
|
val += (1 << i);
|
||||||
|
cs.bit <<= 1;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Base::drawCompressed(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t color)
|
||||||
|
{
|
||||||
|
int bl, len;
|
||||||
|
int col;
|
||||||
|
int i;
|
||||||
|
int a, iCol;
|
||||||
|
int byte = 0;
|
||||||
|
int bit = 0;
|
||||||
|
int w, h;
|
||||||
|
|
||||||
|
// set up decompress state
|
||||||
|
|
||||||
|
cs.src = bitmap;
|
||||||
|
cs.bit = 0x100;
|
||||||
|
cs.byte = 0;
|
||||||
|
cs.src_pos = 0;
|
||||||
|
|
||||||
|
// read header
|
||||||
|
|
||||||
|
w = getval(8) + 1;
|
||||||
|
h = getval(8) + 1;
|
||||||
|
|
||||||
|
col = getval(1); // starting colour
|
||||||
|
|
||||||
|
// no need to draw at all if we're offscreen
|
||||||
|
if (sx + w < 0 || sx > WIDTH - 1 || sy + h < 0 || sy > HEIGHT - 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// sy = sy - (frame*h);
|
||||||
|
|
||||||
|
int yOffset = abs(sy) % 8;
|
||||||
|
int sRow = sy / 8;
|
||||||
|
if (sy < 0) {
|
||||||
|
sRow--;
|
||||||
|
yOffset = 8 - yOffset;
|
||||||
|
}
|
||||||
|
int rows = h / 8;
|
||||||
|
if (h % 8 != 0) rows++;
|
||||||
|
|
||||||
|
a = 0; // +(frame*rows);
|
||||||
|
iCol = 0;
|
||||||
|
|
||||||
|
byte = 0; bit = 1;
|
||||||
|
while (a < rows) // + (frame*rows))
|
||||||
|
{
|
||||||
|
bl = 1;
|
||||||
|
while (!getval(1))
|
||||||
|
bl += 2;
|
||||||
|
|
||||||
|
len = getval(bl) + 1; // span length
|
||||||
|
|
||||||
|
// draw the span
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (col)
|
||||||
|
byte |= bit;
|
||||||
|
bit <<= 1;
|
||||||
|
|
||||||
|
if (bit == 0x100) // reached end of byte
|
||||||
|
{
|
||||||
|
// draw
|
||||||
|
|
||||||
|
int bRow = sRow + a;
|
||||||
|
|
||||||
|
//if (byte) // possible optimisation
|
||||||
|
if (bRow <= (HEIGHT / 8) - 1)
|
||||||
|
if (bRow > -2)
|
||||||
|
if (iCol + sx <= (WIDTH - 1))
|
||||||
|
if (iCol + sx >= 0) {
|
||||||
|
|
||||||
|
if (bRow >= 0)
|
||||||
|
{
|
||||||
|
if (color)
|
||||||
|
sBuffer[(bRow * WIDTH) + sx + iCol] |= byte << yOffset;
|
||||||
|
else
|
||||||
|
sBuffer[(bRow * WIDTH) + sx + iCol] &= ~(byte << yOffset);
|
||||||
|
}
|
||||||
|
if (yOffset && bRow < (HEIGHT / 8) - 1 && bRow > -2)
|
||||||
|
{
|
||||||
|
if (color)
|
||||||
|
sBuffer[((bRow + 1)*WIDTH) + sx + iCol] |= byte >> (8 - yOffset);
|
||||||
|
else
|
||||||
|
sBuffer[((bRow + 1)*WIDTH) + sx + iCol] &= ~(byte >> (8 - yOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate
|
||||||
|
iCol ++;
|
||||||
|
if (iCol >= w)
|
||||||
|
{
|
||||||
|
iCol = 0;
|
||||||
|
a ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset byte
|
||||||
|
byte = 0; bit = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
col = 1 - col; // toggle colour for next span
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Arduboy2Base::display()
|
void Arduboy2Base::display()
|
||||||
{
|
{
|
||||||
this->paintScreen(sBuffer);
|
paintScreen(sBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* Arduboy2Base::getBuffer()
|
unsigned char* Arduboy2Base::getBuffer()
|
||||||
|
@ -718,13 +856,48 @@ bool Arduboy2Base::notPressed(uint8_t buttons)
|
||||||
return (buttonsState() & buttons) == 0;
|
return (buttonsState() & buttons) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
void Arduboy2Base::swap(int16_t& a, int16_t& b)
|
void Arduboy2Base::swap(int16_t& a, int16_t& b)
|
||||||
{
|
{
|
||||||
int temp = a;
|
int16_t temp = a;
|
||||||
a = b;
|
a = b;
|
||||||
b = temp;
|
b = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//====================================
|
||||||
|
//========== class Arduboy2 ==========
|
||||||
|
//====================================
|
||||||
|
|
||||||
Arduboy2::Arduboy2()
|
Arduboy2::Arduboy2()
|
||||||
{
|
{
|
||||||
cursor_x = 0;
|
cursor_x = 0;
|
||||||
|
@ -735,11 +908,6 @@ Arduboy2::Arduboy2()
|
||||||
textWrap = 0;
|
textWrap = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//====================================
|
|
||||||
//========== class Arduboy2 ==========
|
|
||||||
//====================================
|
|
||||||
|
|
||||||
size_t Arduboy2::write(uint8_t c)
|
size_t Arduboy2::write(uint8_t c)
|
||||||
{
|
{
|
||||||
if (c == '\n')
|
if (c == '\n')
|
||||||
|
|
101
src/Arduboy2.h
101
src/Arduboy2.h
|
@ -1,7 +1,9 @@
|
||||||
#ifndef ARDUBOY2_H
|
#ifndef ARDUBOY2_H
|
||||||
#define ARDUBOY2_H
|
#define ARDUBOY2_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
#include "ArduboyCore.h"
|
#include "ArduboyCore.h"
|
||||||
|
#include "Sprites.h"
|
||||||
#include <Print.h>
|
#include <Print.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
|
@ -9,7 +11,7 @@
|
||||||
// For a version number in the form of x.y.z the value of the define will be
|
// For a version number in the form of x.y.z the value of the define will be
|
||||||
// ((x * 10000) + (y * 100) + (z)) as a decimal number.
|
// ((x * 10000) + (y * 100) + (z)) as a decimal number.
|
||||||
// So, it will read as xxxyyzz, with no leading zeros on x.
|
// So, it will read as xxxyyzz, with no leading zeros on x.
|
||||||
#define ARDUBOY_LIB_VER 20005
|
#define ARDUBOY_LIB_VER 20100
|
||||||
|
|
||||||
// EEPROM settings
|
// EEPROM settings
|
||||||
#define EEPROM_VERSION 0
|
#define EEPROM_VERSION 0
|
||||||
|
@ -35,6 +37,22 @@
|
||||||
#define ADC_TEMP (_BV(REFS0) | _BV(REFS1) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0))
|
#define ADC_TEMP (_BV(REFS0) | _BV(REFS1) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0))
|
||||||
|
|
||||||
|
|
||||||
|
/// Rectangle for collision functions
|
||||||
|
struct Rect
|
||||||
|
{
|
||||||
|
int16_t x;
|
||||||
|
int16_t y;
|
||||||
|
uint8_t width;
|
||||||
|
uint8_t height;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Point for collision functions
|
||||||
|
struct Point
|
||||||
|
{
|
||||||
|
int16_t x;
|
||||||
|
int16_t y;
|
||||||
|
};
|
||||||
|
|
||||||
//==================================
|
//==================================
|
||||||
//========== Arduboy2Base ==========
|
//========== Arduboy2Base ==========
|
||||||
//==================================
|
//==================================
|
||||||
|
@ -46,18 +64,6 @@ public:
|
||||||
|
|
||||||
ArduboyAudio audio;
|
ArduboyAudio audio;
|
||||||
|
|
||||||
/// Returns true if the button mask passed in is pressed.
|
|
||||||
/**
|
|
||||||
* if (pressed(LEFT_BUTTON + A_BUTTON))
|
|
||||||
*/
|
|
||||||
bool pressed(uint8_t buttons);
|
|
||||||
|
|
||||||
/// Returns true if the button mask passed in not pressed.
|
|
||||||
/**
|
|
||||||
* if (notPressed(LEFT_BUTTON))
|
|
||||||
*/
|
|
||||||
bool notPressed(uint8_t buttons);
|
|
||||||
|
|
||||||
/// Initialize hardware, boot logo, boot utilities, etc.
|
/// Initialize hardware, boot logo, boot utilities, etc.
|
||||||
/**
|
/**
|
||||||
* To free up some code space for use by the sketch, you can use "boot()"
|
* To free up some code space for use by the sketch, you can use "boot()"
|
||||||
|
@ -176,6 +182,16 @@ public:
|
||||||
*/
|
*/
|
||||||
void drawSlowXYBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE);
|
void drawSlowXYBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE);
|
||||||
|
|
||||||
|
/// Draw a compressed bitmap
|
||||||
|
/**
|
||||||
|
* Draws a bitmap starting at the given coordinates that has been compressed
|
||||||
|
* using an algorthm implemented by Team A.R.G.
|
||||||
|
* For more information see:
|
||||||
|
* https://github.com/TEAMarg/drawCompressed
|
||||||
|
* https://github.com/TEAMarg/Cabi
|
||||||
|
*/
|
||||||
|
void drawCompressed(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t color = WHITE);
|
||||||
|
|
||||||
/// Get a pointer to the display buffer.
|
/// Get a pointer to the display buffer.
|
||||||
unsigned char* getBuffer();
|
unsigned char* getBuffer();
|
||||||
|
|
||||||
|
@ -214,6 +230,61 @@ public:
|
||||||
/// useful for getting raw approximate voltage values
|
/// useful for getting raw approximate voltage values
|
||||||
uint16_t rawADC(uint8_t adc_bits);
|
uint16_t rawADC(uint8_t adc_bits);
|
||||||
|
|
||||||
|
/// Test if the specified buttons are pressed
|
||||||
|
/**
|
||||||
|
* Returns "true" if all the buttons in the provided mask are currently pressed.
|
||||||
|
* (Can be a single button)
|
||||||
|
* E.g. `if (pressed(LEFT_BUTTON + A_BUTTON))`
|
||||||
|
*/
|
||||||
|
bool pressed(uint8_t buttons);
|
||||||
|
|
||||||
|
/// Test if the specified buttons are not pressed
|
||||||
|
/**
|
||||||
|
* Returns "true" if all the buttons in the provided mask are currently released.
|
||||||
|
* (Can be a single button)
|
||||||
|
* E.g. `if (notPressed(LEFT_BUTTON))`
|
||||||
|
*/
|
||||||
|
bool notPressed(uint8_t buttons);
|
||||||
|
|
||||||
|
/// Poll the hardware buttons and track state over time.
|
||||||
|
/**
|
||||||
|
* Should be called either in the main `loop()` or as part of the
|
||||||
|
* frame system at the start of rendering a frame.
|
||||||
|
*
|
||||||
|
* The `justPressed()` and `justReleased()` functions rely on this function.
|
||||||
|
*/
|
||||||
|
void pollButtons();
|
||||||
|
|
||||||
|
/// Check if a button has just been pressed
|
||||||
|
/**
|
||||||
|
* Will return "true" if the given button was pressed between the latest
|
||||||
|
* call to `pollButtons()` and previous call to `pollButtons()`.
|
||||||
|
* If the button has been held down over multiple polls this function will
|
||||||
|
* return "false". This function should only be used to test a single button.
|
||||||
|
*/
|
||||||
|
bool justPressed(uint8_t button);
|
||||||
|
|
||||||
|
/// Check if a button has just been released
|
||||||
|
/**
|
||||||
|
* Will return "true" if the given button was released between the latest
|
||||||
|
* call to `pollButtons()` and previous call to `pollButtons()`.
|
||||||
|
* If the button has been released over multiple polls this function will
|
||||||
|
* return "false". This function should only be used to test a single button.
|
||||||
|
*/
|
||||||
|
bool justReleased(uint8_t button);
|
||||||
|
|
||||||
|
/// Test if a point is within a rectangle
|
||||||
|
/**
|
||||||
|
* Returns "true" if the point is within the rectangle.
|
||||||
|
*/
|
||||||
|
bool collide(Point point, Rect rect);
|
||||||
|
|
||||||
|
/// Test if a rectangle is intersecting with another rectangle.
|
||||||
|
/**
|
||||||
|
* Returns "true" if the first rectangle is intersecting the second.
|
||||||
|
*/
|
||||||
|
bool collide(Rect rect1, Rect rect2);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// helper function for sound enable/disable system control
|
// helper function for sound enable/disable system control
|
||||||
void sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal);
|
void sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal);
|
||||||
|
@ -221,6 +292,10 @@ protected:
|
||||||
// Screen buffer
|
// Screen buffer
|
||||||
static uint8_t sBuffer[(HEIGHT*WIDTH)/8];
|
static uint8_t sBuffer[(HEIGHT*WIDTH)/8];
|
||||||
|
|
||||||
|
// For button handling
|
||||||
|
uint8_t currentButtonState;
|
||||||
|
uint8_t previousButtonState;
|
||||||
|
|
||||||
// For frame funcions
|
// For frame funcions
|
||||||
uint16_t frameCount;
|
uint16_t frameCount;
|
||||||
uint8_t eachFrameMillis;
|
uint8_t eachFrameMillis;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef ARDUBOY_CORE_H
|
#ifndef ARDUBOY_CORE_H
|
||||||
#define ARDUBOY_CORE_H
|
#define ARDUBOY_CORE_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
#include <avr/power.h>
|
#include <avr/power.h>
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <avr/sleep.h>
|
#include <avr/sleep.h>
|
||||||
|
|
|
@ -0,0 +1,353 @@
|
||||||
|
#include "Sprites.h"
|
||||||
|
|
||||||
|
Sprites::Sprites(uint8_t* buffer)
|
||||||
|
{
|
||||||
|
sBuffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
|
||||||
|
const uint8_t *mask, uint8_t frame, uint8_t mask_frame)
|
||||||
|
{
|
||||||
|
draw(x, y, bitmap, frame, mask, mask_frame, SPRITE_MASKED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
|
||||||
|
{
|
||||||
|
draw(x, y, bitmap, frame, NULL, 0, SPRITE_OVERWRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
|
||||||
|
{
|
||||||
|
draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK_ERASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
|
||||||
|
{
|
||||||
|
draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
|
||||||
|
{
|
||||||
|
draw(x, y, bitmap, frame, NULL, 0, SPRITE_PLUS_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//common functions
|
||||||
|
void Sprites::draw(int16_t x, int16_t y,
|
||||||
|
const uint8_t *bitmap, uint8_t frame,
|
||||||
|
const uint8_t *mask, uint8_t sprite_frame,
|
||||||
|
uint8_t drawMode)
|
||||||
|
{
|
||||||
|
unsigned int frame_offset;
|
||||||
|
|
||||||
|
if (bitmap == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t width = pgm_read_byte(bitmap);
|
||||||
|
uint8_t height = pgm_read_byte(++bitmap);
|
||||||
|
bitmap++;
|
||||||
|
if (frame > 0 || sprite_frame > 0) {
|
||||||
|
frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1)));
|
||||||
|
// sprite plus mask uses twice as much space for each frame
|
||||||
|
if (drawMode == SPRITE_PLUS_MASK) {
|
||||||
|
frame_offset *= 2;
|
||||||
|
} else if (mask != NULL) {
|
||||||
|
mask += sprite_frame * frame_offset;
|
||||||
|
}
|
||||||
|
bitmap += frame * frame_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're detecting the draw mode then base it on whether a mask
|
||||||
|
// was passed as a separate object
|
||||||
|
if (drawMode == SPRITE_AUTO_MODE) {
|
||||||
|
drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawBitmap(x, y, bitmap, mask, width, height, drawMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawBitmap(int16_t x, int16_t y,
|
||||||
|
const uint8_t *bitmap, const uint8_t *mask,
|
||||||
|
int8_t w, int8_t h, uint8_t draw_mode)
|
||||||
|
{
|
||||||
|
// no need to draw at all of we're offscreen
|
||||||
|
if (x + w <= 0 || x > WIDTH - 1 || y + h <= 0 || y > HEIGHT - 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (bitmap == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// xOffset technically doesn't need to be 16 bit but the math operations
|
||||||
|
// are measurably faster if it is
|
||||||
|
uint16_t xOffset, ofs;
|
||||||
|
int8_t yOffset = abs(y) % 8;
|
||||||
|
int8_t sRow = y / 8;
|
||||||
|
uint8_t loop_h, start_h, rendered_width;
|
||||||
|
|
||||||
|
if (y < 0 && yOffset > 0) {
|
||||||
|
sRow--;
|
||||||
|
yOffset = 8 - yOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the left side of the render is offscreen skip those loops
|
||||||
|
if (x < 0) {
|
||||||
|
xOffset = abs(x);
|
||||||
|
} else {
|
||||||
|
xOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the right side of the render is offscreen skip those loops
|
||||||
|
if (x + w > WIDTH - 1) {
|
||||||
|
rendered_width = ((WIDTH - x) - xOffset);
|
||||||
|
} else {
|
||||||
|
rendered_width = (w - xOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the top side of the render is offscreen skip those loops
|
||||||
|
if (sRow < -1) {
|
||||||
|
start_h = abs(sRow) - 1;
|
||||||
|
} else {
|
||||||
|
start_h = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop_h = h / 8 + (h % 8 > 0 ? 1 : 0); // divide, then round up
|
||||||
|
|
||||||
|
// if (sRow + loop_h - 1 > (HEIGHT/8)-1)
|
||||||
|
if (sRow + loop_h > (HEIGHT / 8)) {
|
||||||
|
loop_h = (HEIGHT / 8) - sRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare variables for loops later so we can compare with 0
|
||||||
|
// instead of comparing two variables
|
||||||
|
loop_h -= start_h;
|
||||||
|
|
||||||
|
sRow += start_h;
|
||||||
|
ofs = (sRow * WIDTH) + x + xOffset;
|
||||||
|
uint8_t *bofs = (uint8_t *)bitmap + (start_h * w) + xOffset;
|
||||||
|
uint8_t *mask_ofs;
|
||||||
|
if (mask != 0)
|
||||||
|
mask_ofs = (uint8_t *)mask + (start_h * w) + xOffset;
|
||||||
|
uint8_t data;
|
||||||
|
|
||||||
|
uint8_t mul_amt = 1 << yOffset;
|
||||||
|
uint16_t mask_data;
|
||||||
|
uint16_t bitmap_data;
|
||||||
|
|
||||||
|
switch (draw_mode) {
|
||||||
|
case SPRITE_UNMASKED:
|
||||||
|
// we only want to mask the 8 bits of our own sprite, so we can
|
||||||
|
// calculate the mask before the start of the loop
|
||||||
|
mask_data = ~(0xFF * mul_amt);
|
||||||
|
// really if yOffset = 0 you have a faster case here that could be
|
||||||
|
// optimized
|
||||||
|
for (uint8_t a = 0; a < loop_h; a++) {
|
||||||
|
for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
|
||||||
|
bitmap_data = pgm_read_byte(bofs) * mul_amt;
|
||||||
|
|
||||||
|
if (sRow >= 0) {
|
||||||
|
data = sBuffer[ofs];
|
||||||
|
data &= (uint8_t)(mask_data);
|
||||||
|
data |= (uint8_t)(bitmap_data);
|
||||||
|
sBuffer[ofs] = data;
|
||||||
|
}
|
||||||
|
if (yOffset != 0 && sRow < 7) {
|
||||||
|
data = sBuffer[ofs + WIDTH];
|
||||||
|
data &= (*((unsigned char *) (&mask_data) + 1));
|
||||||
|
data |= (*((unsigned char *) (&bitmap_data) + 1));
|
||||||
|
sBuffer[ofs + WIDTH] = data;
|
||||||
|
}
|
||||||
|
ofs++;
|
||||||
|
bofs++;
|
||||||
|
}
|
||||||
|
sRow++;
|
||||||
|
bofs += w - rendered_width;
|
||||||
|
ofs += WIDTH - rendered_width;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPRITE_IS_MASK:
|
||||||
|
for (uint8_t a = 0; a < loop_h; a++) {
|
||||||
|
for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
|
||||||
|
bitmap_data = pgm_read_byte(bofs) * mul_amt;
|
||||||
|
if (sRow >= 0) {
|
||||||
|
sBuffer[ofs] |= (uint8_t)(bitmap_data);
|
||||||
|
}
|
||||||
|
if (yOffset != 0 && sRow < 7) {
|
||||||
|
sBuffer[ofs + WIDTH] |= (*((unsigned char *) (&bitmap_data) + 1));
|
||||||
|
}
|
||||||
|
ofs++;
|
||||||
|
bofs++;
|
||||||
|
}
|
||||||
|
sRow++;
|
||||||
|
bofs += w - rendered_width;
|
||||||
|
ofs += WIDTH - rendered_width;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPRITE_IS_MASK_ERASE:
|
||||||
|
for (uint8_t a = 0; a < loop_h; a++) {
|
||||||
|
for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
|
||||||
|
bitmap_data = pgm_read_byte(bofs) * mul_amt;
|
||||||
|
if (sRow >= 0) {
|
||||||
|
sBuffer[ofs] &= ~(uint8_t)(bitmap_data);
|
||||||
|
}
|
||||||
|
if (yOffset != 0 && sRow < 7) {
|
||||||
|
sBuffer[ofs + WIDTH] &= ~(*((unsigned char *) (&bitmap_data) + 1));
|
||||||
|
}
|
||||||
|
ofs++;
|
||||||
|
bofs++;
|
||||||
|
}
|
||||||
|
sRow++;
|
||||||
|
bofs += w - rendered_width;
|
||||||
|
ofs += WIDTH - rendered_width;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPRITE_MASKED:
|
||||||
|
for (uint8_t a = 0; a < loop_h; a++) {
|
||||||
|
for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
|
||||||
|
// NOTE: you might think in the yOffset==0 case that this results
|
||||||
|
// in more effort, but in all my testing the compiler was forcing
|
||||||
|
// 16-bit math to happen here anyways, so this isn't actually
|
||||||
|
// compiling to more code than it otherwise would. If the offset
|
||||||
|
// is 0 the high part of the word will just never be used.
|
||||||
|
|
||||||
|
// load data and bit shift
|
||||||
|
// mask needs to be bit flipped
|
||||||
|
mask_data = ~(pgm_read_byte(mask_ofs) * mul_amt);
|
||||||
|
bitmap_data = pgm_read_byte(bofs) * mul_amt;
|
||||||
|
|
||||||
|
if (sRow >= 0) {
|
||||||
|
data = sBuffer[ofs];
|
||||||
|
data &= (uint8_t)(mask_data);
|
||||||
|
data |= (uint8_t)(bitmap_data);
|
||||||
|
sBuffer[ofs] = data;
|
||||||
|
}
|
||||||
|
if (yOffset != 0 && sRow < 7) {
|
||||||
|
data = sBuffer[ofs + WIDTH];
|
||||||
|
data &= (*((unsigned char *) (&mask_data) + 1));
|
||||||
|
data |= (*((unsigned char *) (&bitmap_data) + 1));
|
||||||
|
sBuffer[ofs + WIDTH] = data;
|
||||||
|
}
|
||||||
|
ofs++;
|
||||||
|
mask_ofs++;
|
||||||
|
bofs++;
|
||||||
|
}
|
||||||
|
sRow++;
|
||||||
|
bofs += w - rendered_width;
|
||||||
|
mask_ofs += w - rendered_width;
|
||||||
|
ofs += WIDTH - rendered_width;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case SPRITE_PLUS_MASK:
|
||||||
|
// *2 because we use double the bits (mask + bitmap)
|
||||||
|
bofs = (uint8_t *)(bitmap + ((start_h * w) + xOffset) * 2);
|
||||||
|
|
||||||
|
uint8_t xi = rendered_width; // used for x loop below
|
||||||
|
uint8_t yi = loop_h; // used for y loop below
|
||||||
|
|
||||||
|
asm volatile(
|
||||||
|
"push r28\n" // save Y
|
||||||
|
"push r29\n"
|
||||||
|
"mov r28, %A[buffer_page2_ofs]\n" // Y = buffer page 2 offset
|
||||||
|
"mov r29, %B[buffer_page2_ofs]\n"
|
||||||
|
"loop_y:\n"
|
||||||
|
"loop_x:\n"
|
||||||
|
// load bitmap and mask data
|
||||||
|
"lpm %A[bitmap_data], Z+\n"
|
||||||
|
"lpm %A[mask_data], Z+\n"
|
||||||
|
|
||||||
|
// shift mask and buffer data
|
||||||
|
"tst %[yOffset]\n"
|
||||||
|
"breq skip_shifting\n"
|
||||||
|
"mul %A[bitmap_data], %[mul_amt]\n"
|
||||||
|
"mov %A[bitmap_data], r0\n"
|
||||||
|
"mov %B[bitmap_data], r1\n"
|
||||||
|
"mul %A[mask_data], %[mul_amt]\n"
|
||||||
|
"mov %A[mask_data], r0\n"
|
||||||
|
// "mov %B[mask_data], r1\n"
|
||||||
|
|
||||||
|
|
||||||
|
// SECOND PAGE
|
||||||
|
// if yOffset != 0 && sRow < 7
|
||||||
|
"cpi %[sRow], 7\n"
|
||||||
|
"brge end_second_page\n"
|
||||||
|
// then
|
||||||
|
"ld %[data], Y\n"
|
||||||
|
// "com %B[mask_data]\n" // invert high byte of mask
|
||||||
|
"com r1\n"
|
||||||
|
"and %[data], r1\n" // %B[mask_data]
|
||||||
|
"or %[data], %B[bitmap_data]\n"
|
||||||
|
// update buffer, increment
|
||||||
|
"st Y+, %[data]\n"
|
||||||
|
|
||||||
|
"end_second_page:\n"
|
||||||
|
"skip_shifting:\n"
|
||||||
|
|
||||||
|
|
||||||
|
// FIRST PAGE
|
||||||
|
"ld %[data], %a[buffer_ofs]\n"
|
||||||
|
// if sRow >= 0
|
||||||
|
"tst %[sRow]\n"
|
||||||
|
"brmi end_first_page\n"
|
||||||
|
// then
|
||||||
|
"com %A[mask_data]\n"
|
||||||
|
"and %[data], %A[mask_data]\n"
|
||||||
|
"or %[data], %A[bitmap_data]\n"
|
||||||
|
|
||||||
|
"end_first_page:\n"
|
||||||
|
// update buffer, increment
|
||||||
|
"st %a[buffer_ofs]+, %[data]\n"
|
||||||
|
|
||||||
|
|
||||||
|
// "x_loop_next:\n"
|
||||||
|
"dec %[xi]\n"
|
||||||
|
"brne loop_x\n"
|
||||||
|
|
||||||
|
// increment y
|
||||||
|
"next_loop_y:\n"
|
||||||
|
"dec %[yi]\n"
|
||||||
|
"breq finished\n"
|
||||||
|
"mov %[xi], %[x_count]\n" // reset x counter
|
||||||
|
// sRow++;
|
||||||
|
"inc %[sRow]\n"
|
||||||
|
"clr __zero_reg__\n"
|
||||||
|
// sprite_ofs += (w - rendered_width) * 2;
|
||||||
|
"add %A[sprite_ofs], %A[sprite_ofs_jump]\n"
|
||||||
|
"adc %B[sprite_ofs], __zero_reg__\n"
|
||||||
|
// buffer_ofs += WIDTH - rendered_width;
|
||||||
|
"add %A[buffer_ofs], %A[buffer_ofs_jump]\n"
|
||||||
|
"adc %B[buffer_ofs], __zero_reg__\n"
|
||||||
|
// buffer_ofs_page_2 += WIDTH - rendered_width;
|
||||||
|
"add r28, %A[buffer_ofs_jump]\n"
|
||||||
|
"adc r29, __zero_reg__\n"
|
||||||
|
|
||||||
|
"rjmp loop_y\n"
|
||||||
|
"finished:\n"
|
||||||
|
// put the Y register back in place
|
||||||
|
"pop r29\n"
|
||||||
|
"pop r28\n"
|
||||||
|
"clr __zero_reg__\n" // just in case
|
||||||
|
: [xi] "+&r" (xi),
|
||||||
|
[yi] "+&r" (yi),
|
||||||
|
[sRow] "+&a" (sRow), // CPI requires an upper register
|
||||||
|
[data] "+&r" (data),
|
||||||
|
[mask_data] "+&r" (mask_data),
|
||||||
|
[bitmap_data] "+&r" (bitmap_data)
|
||||||
|
:
|
||||||
|
[x_count] "r" (rendered_width),
|
||||||
|
[y_count] "r" (loop_h),
|
||||||
|
[sprite_ofs] "z" (bofs),
|
||||||
|
[buffer_ofs] "x" (sBuffer+ofs),
|
||||||
|
[buffer_page2_ofs] "r" (sBuffer+ofs+WIDTH), // Y pointer
|
||||||
|
[buffer_ofs_jump] "r" (WIDTH-rendered_width),
|
||||||
|
[sprite_ofs_jump] "r" ((w-rendered_width)*2),
|
||||||
|
[yOffset] "r" (yOffset),
|
||||||
|
[mul_amt] "r" (mul_amt)
|
||||||
|
:
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
#ifndef Sprites_h
|
||||||
|
#define Sprites_h
|
||||||
|
|
||||||
|
#include "Arduboy2.h"
|
||||||
|
|
||||||
|
#define SPRITE_MASKED 1
|
||||||
|
#define SPRITE_UNMASKED 2
|
||||||
|
#define SPRITE_OVERWRITE 2
|
||||||
|
#define SPRITE_PLUS_MASK 3
|
||||||
|
#define SPRITE_IS_MASK 250
|
||||||
|
#define SPRITE_IS_MASK_ERASE 251
|
||||||
|
#define SPRITE_AUTO_MODE 255
|
||||||
|
|
||||||
|
class Sprites
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// The class constructor.
|
||||||
|
/// `buffer` is a pointer to the screen buffer where the draw functions
|
||||||
|
/// will write to.
|
||||||
|
///
|
||||||
|
Sprites(uint8_t* buffer);
|
||||||
|
|
||||||
|
/// drawExternalMask() uses a separate mask to mask image (MASKED)
|
||||||
|
///
|
||||||
|
/// image mask before after
|
||||||
|
///
|
||||||
|
/// ..... .OOO. ..... .....
|
||||||
|
/// ..O.. OOOOO ..... ..O..
|
||||||
|
/// OO.OO OO.OO ..... OO.OO
|
||||||
|
/// ..O.. OOOOO ..... ..O..
|
||||||
|
/// ..... .OOO. ..... .....
|
||||||
|
///
|
||||||
|
/// image mask before after
|
||||||
|
///
|
||||||
|
/// ..... .OOO. OOOOO O...O
|
||||||
|
/// ..O.. OOOOO OOOOO ..O..
|
||||||
|
/// OO.OO OOOOO OOOOO OO.OO
|
||||||
|
/// ..O.. OOOOO OOOOO ..O..
|
||||||
|
/// ..... .OOO. OOOOO O...O
|
||||||
|
///
|
||||||
|
void drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
|
||||||
|
const uint8_t *mask, uint8_t frame, uint8_t mask_frame);
|
||||||
|
|
||||||
|
/// drawPlusMask() has the same behavior as `drawExternalMask()` except
|
||||||
|
/// the data is arranged in byte tuples interposing the mask right along
|
||||||
|
/// with the image data (SPRITE_PLUS_MASK)
|
||||||
|
///
|
||||||
|
/// typical image data (8 bytes):
|
||||||
|
/// [I][I][I][I][I][I][I][I]
|
||||||
|
///
|
||||||
|
/// interposed image/mask data (8 byes):
|
||||||
|
/// [I][M][I][M][I][M][I][M]
|
||||||
|
///
|
||||||
|
/// The byte order does not change, just for every image byte you mix
|
||||||
|
/// in it's matching mask byte. Softare tools make easy work of this.
|
||||||
|
///
|
||||||
|
/// See: https://github.com/yyyc514/img2ard
|
||||||
|
///
|
||||||
|
void drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
|
||||||
|
|
||||||
|
/// drawOverwrite() replaces the existing content completely (UNMASKED)
|
||||||
|
///
|
||||||
|
/// image before after
|
||||||
|
///
|
||||||
|
/// ..... ..... .....
|
||||||
|
/// ..O.. ..... ..O..
|
||||||
|
/// OO.OO ..... OO.OO
|
||||||
|
/// ..O.. ..... ..O..
|
||||||
|
/// ..... ..... .....
|
||||||
|
///
|
||||||
|
/// image before after
|
||||||
|
///
|
||||||
|
/// ..... OOOOO .....
|
||||||
|
/// ..O.. OOOOO ..O..
|
||||||
|
/// OO.OO OOOOO OO.OO
|
||||||
|
/// ..O.. OOOOO ..O..
|
||||||
|
/// ..... OOOOO .....
|
||||||
|
///
|
||||||
|
void drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
|
||||||
|
|
||||||
|
/// drawErase() removes the lit pixels in the image from the display
|
||||||
|
/// (SPRITE_IS_MASK_ERASE)
|
||||||
|
///
|
||||||
|
/// image before after
|
||||||
|
///
|
||||||
|
/// ..... ..... .....
|
||||||
|
/// ..O.. ..... .....
|
||||||
|
/// OO.OO ..... .....
|
||||||
|
/// ..O.. ..... .....
|
||||||
|
/// ..... ..... .....
|
||||||
|
///
|
||||||
|
/// image before after
|
||||||
|
///
|
||||||
|
/// ..... OOOOO OOOOO
|
||||||
|
/// ..O.. OOOOO OO.OO
|
||||||
|
/// OO.OO OOOOO ..O..
|
||||||
|
/// ..O.. OOOOO OO.OO
|
||||||
|
/// ..... OOOOO OOOOO
|
||||||
|
///
|
||||||
|
void drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
|
||||||
|
|
||||||
|
/// drawSelfMasked() only draws lit pixels, black pixels in
|
||||||
|
/// your image are treated as "transparent" (SPRITE_IS_MASK)
|
||||||
|
///
|
||||||
|
/// image before after
|
||||||
|
///
|
||||||
|
/// ..... ..... .....
|
||||||
|
/// ..O.. ..... ..O..
|
||||||
|
/// OO.OO ..... OO.OO
|
||||||
|
/// ..O.. ..... ..O..
|
||||||
|
/// ..... ..... .....
|
||||||
|
///
|
||||||
|
/// image before after
|
||||||
|
///
|
||||||
|
/// ..... OOOOO OOOOO (no change because all pixels were
|
||||||
|
/// ..O.. OOOOO OOOOO already white)
|
||||||
|
/// OO.OO OOOOO OOOOO
|
||||||
|
/// ..O.. OOOOO OOOOO
|
||||||
|
/// ..... OOOOO OOOOO
|
||||||
|
///
|
||||||
|
void drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
|
||||||
|
|
||||||
|
/// Master function. Needs to be abstracted into separate function for
|
||||||
|
/// every render type
|
||||||
|
void draw(int16_t x, int16_t y,
|
||||||
|
const uint8_t *bitmap, uint8_t frame,
|
||||||
|
const uint8_t *mask, uint8_t sprite_frame,
|
||||||
|
uint8_t drawMode);
|
||||||
|
|
||||||
|
void drawBitmap(int16_t x, int16_t y,
|
||||||
|
const uint8_t *bitmap, const uint8_t *mask,
|
||||||
|
int8_t w, int8_t h, uint8_t draw_mode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char *sBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue