diff --git a/SSD1306.cpp b/SSD1306.cpp index 93d6d93..2b4b869 100644 --- a/SSD1306.cpp +++ b/SSD1306.cpp @@ -63,21 +63,39 @@ constexpr uint16_t WIRE_MAX = 342; // Because command calls are often grouped, SPI transaction and selection // must be started/ended in calling function for efficiency. // This is a private function, not exposed (see ssd1306_command() instead). -template -void Adafruit_SSD1306::ssd1306_command1(uint8_t c) { - uint8_t cmd = 0x00; // Co = 0, D/C = 0 - TWI_Start_Transceiver_With_Data(cmd, &c, 1); +template +void Adafruit_SSD1306::ssd1306_command1(uint8_t c) { + if(DISPLAY_MODE == I2C_MODE) { + uint8_t cmd = 0x00; // Co = 0, D/C = 0 + TWI_Start_Transceiver_With_Data(cmd, &c, 1); + } else { + digitalWrite(dcPin, LOW); + digitalWrite(csPin, LOW); + SPDR = c; + while (!(SPSR & _BV(SPIF))) ; // wait + digitalWrite(csPin, HIGH); + } } // Issue list of commands to SSD1306, same rules as above re: transactions. // This is a private function, not exposed. -template -void Adafruit_SSD1306::ssd1306_commandList(const uint8_t *c, uint8_t n) { - uint8_t cmd = 0x00; // Co = 0, D/C = 0 - uint8_t tmp[n]; - - uint8_t * tmpptr = static_cast(memcpy_P(tmp, c, n)); - TWI_Start_Transceiver_With_Data(cmd, tmpptr, n); +template +void Adafruit_SSD1306::ssd1306_commandList(const uint8_t *c, uint8_t n) { + if(DISPLAY_MODE == I2C_MODE) { + uint8_t cmd = 0x00; // Co = 0, D/C = 0 + uint8_t tmp[n]; + + uint8_t * tmpptr = static_cast(memcpy_P(tmp, c, n)); + TWI_Start_Transceiver_With_Data(cmd, tmpptr, n); + } else { + digitalWrite(dcPin, LOW); + digitalWrite(csPin, LOW); + while(n--) { + SPDR = pgm_read_byte(c++); + while (!(SPSR & _BV(SPIF))) ; // wait + } + digitalWrite(csPin, HIGH); + } } // ALLOCATE & INIT DISPLAY ------------------------------------------------- @@ -117,22 +135,27 @@ void Adafruit_SSD1306::ssd1306_commandList(const uint8_t *c, uint proceeding. @note MUST call this function before any drawing or updates! */ -template -bool Adafruit_SSD1306::begin(uint8_t vcs, uint8_t addr) { +template +bool Adafruit_SSD1306::begin(uint8_t vcs) { clearDisplay(); vccstate = vcs; - // Setup pin directions - // If I2C address is unspecified, use default - // (0x3C for 32-pixel-tall displays, 0x3D for all others). - i2caddr = addr ? addr : ((HEIGHT == 32) ? 0x3C : 0x3D); - // TwoWire begin() function might be already performed by the calling - // function if it has unusual circumstances (e.g. TWI variants that - // can accept different SDA/SCL pins, or if two SSD1306 instances - // with different addresses -- only a single begin() is needed). - TWI_Master_Initialise(); + // TwoWire begin() function might be already performed by the calling + // function if it has unusual circumstances (e.g. TWI variants that + // can accept different SDA/SCL pins, or if two SSD1306 instances + // with different addresses -- only a single begin() is needed). + if(DISPLAY_MODE == I2C_MODE) { + TWI_Master_Initialise(); + } else { // SPI + SPI.beginTransaction(SPISettings(160000000UL, MSBFIRST, SPI_MODE3)); + SPI.begin(); + pinMode(11, OUTPUT); // MOSI and SCLK outputs + pinMode(13 , OUTPUT); + pinMode(dcPin, OUTPUT); // Set data/command pin as output + pinMode(csPin, OUTPUT); // Same for chip select + } // Init sequence static const uint8_t PROGMEM init1[] = { @@ -200,80 +223,28 @@ bool Adafruit_SSD1306::begin(uint8_t vcs, uint8_t addr) { // DRAWING FUNCTIONS ------------------------------------------------------- -/*! - @brief Set/clear/invert a single pixel. This is also invoked by the - Adafruit_GFX library in generating many higher-level graphics - primitives. - @param x - Column of display -- 0 at left to (screen width - 1) at right. - @param y - Row of display -- 0 at top to (screen height -1) at bottom. - @param color - Pixel color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERT. - @return None (void). - @note Changes buffer contents only, no immediate effect on display. - Follow up with a call to display(), or with other graphics - commands as needed by one's own application. -*/ -template -void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) { - if((x >= 0) && (x < WIDTH) && (y >= 0) && (y < HEIGHT)) { - // Pixel is in-bounds. Rotate coordinates if needed. - switch(color) { - case SSD1306_WHITE: buffer[x + (y/8)*WIDTH] |= (1 << (y&7)); break; - case SSD1306_BLACK: buffer[x + (y/8)*WIDTH] &= ~(1 << (y&7)); break; - case SSD1306_INVERSE: buffer[x + (y/8)*WIDTH] ^= (1 << (y&7)); break; - } +template +void Adafruit_SSD1306::drawPixel(uint8_t x, uint8_t y, bool color, bool raycasterViewport) { + // prevent write out of screen buffer + if (x >= SCREEN_WIDTH || y >= (raycasterViewport ? RENDER_HEIGHT : SCREEN_HEIGHT)) { + return; } -} -/*! - @brief Clear contents of display buffer (set all pixels to off). - @return None (void). - @note Changes buffer contents only, no immediate effect on display. - Follow up with a call to display(), or with other graphics - commands as needed by one's own application. -*/ -template -void Adafruit_SSD1306::clearDisplay(void) { - memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8)); -} - -/*! - @brief Draw a vertical line. This is also invoked by the Adafruit_GFX - library in generating many higher-level graphics primitives. - @param x - Column of display -- 0 at left to (screen width -1) at right. - @param y - Topmost row -- 0 at top to (screen height - 1) at bottom. - @param h - Height of line, in pixels. - @param color - Line color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERT. - @return None (void). - @note Changes buffer contents only, no immediate effect on display. - Follow up with a call to display(), or with other graphics - commands as needed by one's own application. -*/ -template -void Adafruit_SSD1306::drawFastVLine( - int16_t x, int16_t y, int16_t h, uint16_t color) { - drawFastVLineInternal(x, y, h, color); + if (color) { + // white + buffer[x + (y / 8)*SCREEN_WIDTH] |= (1 << (y & 7)); + } else { + // black + buffer[x + (y / 8)*SCREEN_WIDTH] &= ~(1 << (y & 7)); + } } -template -void Adafruit_SSD1306::drawFastVLineInternal( - int16_t x, int16_t __y, int16_t __h, uint16_t color) { - - if((x >= 0) && (x < WIDTH)) { // X coord in bounds? - if(__y < 0) { // Clip top - __h += __y; - __y = 0; - } +template +void Adafruit_SSD1306::drawFastVLineInternal( + uint8_t x, uint8_t __y, uint8_t __h) { if((__y + __h) > HEIGHT) { // Clip bottom __h = (HEIGHT - __y); } - if(__h > 0) { // Proceed only if height is now positive // this display doesn't need ints for coordinates, // use local byte registers for faster juggling uint8_t y = __y, h = __h; @@ -293,11 +264,8 @@ void Adafruit_SSD1306::drawFastVLineInternal( // adjust the mask if we're not going to reach the end of this byte if(h < mod) mask &= (0XFF >> (mod - h)); - switch(color) { - case SSD1306_WHITE: *pBuf |= mask; break; - case SSD1306_BLACK: *pBuf &= ~mask; break; - case SSD1306_INVERSE: *pBuf ^= mask; break; - } + *pBuf &= ~mask; + pBuf += WIDTH; } @@ -305,23 +273,13 @@ void Adafruit_SSD1306::drawFastVLineInternal( h -= mod; // Write solid bytes while we can - effectively 8 rows at a time if(h >= 8) { - if(color == SSD1306_INVERSE) { - // separate copy of the code so we don't impact performance of - // black/white write version with an extra comparison per loop - do { - *pBuf ^= 0xFF; // Invert byte - pBuf += WIDTH; // Advance pointer 8 rows - h -= 8; // Subtract 8 rows from height - } while(h >= 8); - } else { // store a local value to work with - uint8_t val = (color != SSD1306_BLACK) ? 255 : 0; + //uint8_t val = (color != SSD1306_BLACK) ? 255 : 0; do { - *pBuf = val; // Set byte + *pBuf = 0; // Set byte pBuf += WIDTH; // Advance pointer 8 rows h -= 8; // Subtract 8 rows from height } while(h >= 8); - } } if(h) { // Do the final partial byte, if necessary @@ -334,33 +292,27 @@ void Adafruit_SSD1306::drawFastVLineInternal( static const uint8_t PROGMEM postmask[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; uint8_t mask = pgm_read_byte(&postmask[mod]); - switch(color) { - case SSD1306_WHITE: *pBuf |= mask; break; - case SSD1306_BLACK: *pBuf &= ~mask; break; - case SSD1306_INVERSE: *pBuf ^= mask; break; - } + *pBuf &= ~mask; } } - } // endif positive height - } // endif x in bounds } -template -void Adafruit_SSD1306::clearRect(uint8_t x, uint8_t y, uint8_t w , uint8_t h) { - for (int16_t i=x; i +void Adafruit_SSD1306::clearRect(uint8_t x, uint8_t y, uint8_t w , uint8_t h) { + for (uint8_t i=x; i -void Adafruit_SSD1306::drawBitmap(int16_t x, int16_t y, - const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color) { +template +void Adafruit_SSD1306::drawBitmap(uint8_t x, uint8_t y, + const uint8_t bitmap[], uint8_t w, uint8_t h, uint8_t color) { - int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte uint8_t byte = 0; - for(int16_t j=0; j::drawBitmap(int16_t x, int16_t y, } } -/*! - @brief Return color of a single pixel in display buffer. - @param x - Column of display -- 0 at left to (screen width - 1) at right. - @param y - Row of display -- 0 at top to (screen height -1) at bottom. - @return true if pixel is set (usually SSD1306_WHITE, unless display invert mode - is enabled), false if clear (SSD1306_BLACK). - @note Reads from buffer contents; may not reflect current contents of - screen if display() has not been called. -*/ -template -bool Adafruit_SSD1306::getPixel(int16_t x, int16_t y) { - if((x >= 0) && (x < WIDTH) && (y >= 0) && (y < HEIGHT)) { - // Pixel is in-bounds. Rotate coordinates if needed. - return (buffer[x + (y / 8) * WIDTH] & (1 << (y & 7))); - } - return false; // Pixel out of bounds -} - -/*! - @brief Get base address of display buffer for direct reading or writing. - @return Pointer to an unsigned 8-bit array, column-major, columns padded - to full byte boundary if needed. -*/ -template -uint8_t *Adafruit_SSD1306::getBuffer(void) { - return buffer; -} - // REFRESH DISPLAY --------------------------------------------------------- /*! @@ -407,8 +329,8 @@ uint8_t *Adafruit_SSD1306::getBuffer(void) { called. Call after each graphics command, or after a whole set of graphics commands, as best needed by one's own application. */ -template -void Adafruit_SSD1306::display(void) { +template +void Adafruit_SSD1306::display(void) { static const uint8_t PROGMEM dlist1[] = { SSD1306_PAGEADDR, 0, // Page start address @@ -420,33 +342,23 @@ void Adafruit_SSD1306::display(void) { uint16_t count = WIDTH * ((HEIGHT + 7) / 8); uint8_t *ptr = buffer; - uint8_t cmd = 0x40; - while(count >= (WIRE_MAX)){ - TWI_Start_Transceiver_With_Data(cmd, ptr, WIRE_MAX); - count -= (WIRE_MAX); - ptr += (WIRE_MAX); - } - if(count > 0) { - TWI_Start_Transceiver_With_Data(cmd, ptr, count); + if(DISPLAY_MODE == I2C_MODE) { + uint8_t cmd = 0x40; + while(count >= (WIRE_MAX)){ + TWI_Start_Transceiver_With_Data(cmd, ptr, WIRE_MAX); + count -= (WIRE_MAX); + ptr += (WIRE_MAX); + } + if(count > 0) { + TWI_Start_Transceiver_With_Data(cmd, ptr, count); + } + } else { + digitalWrite(dcPin, HIGH); + digitalWrite(csPin, LOW); + while(count--) { + SPDR = *ptr++; + while (!(SPSR & _BV(SPIF))) ; // wait + } + digitalWrite(csPin, HIGH); } } - -// OTHER HARDWARE SETTINGS ------------------------------------------------- - -/*! - @brief Enable or disable display invert mode (white-on-black vs - black-on-white). - @param i - If true, switch to invert mode (black-on-white), else normal - mode (white-on-black). - @return None (void). - @note This has an immediate effect on the display, no need to call the - display() function -- buffer contents are not changed, rather a - different pixel mode of the display hardware is used. When - enabled, drawing SSD1306_BLACK (value 0) pixels will actually draw white, - SSD1306_WHITE (value 1) will draw black. -*/ -template -void Adafruit_SSD1306::invertDisplay(bool i) { - ssd1306_command1(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY); -} diff --git a/SSD1306.h b/SSD1306.h index 5c641e0..9d0e2f4 100644 --- a/SSD1306.h +++ b/SSD1306.h @@ -25,6 +25,7 @@ #define _Adafruit_SSD1306_H_ #include "TWI_Master.h" +#include #include "string.h" #include "constants.h" @@ -81,35 +82,65 @@ @brief Class that stores state and functions for interacting with SSD1306 OLED displays. */ -template +template class Adafruit_SSD1306 { public: Adafruit_SSD1306() = default; ~Adafruit_SSD1306(void) = default; - bool begin(uint8_t switchvcc=SSD1306_SWITCHCAPVCC, - uint8_t i2caddr=0); - void display(void); - void clearDisplay(void); - void invertDisplay(bool i); - void drawPixel(int16_t x, int16_t y, uint16_t color); - void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); - bool getPixel(int16_t x, int16_t y); - uint8_t *getBuffer(void); - void clearRect(uint8_t, uint8_t, uint8_t, uint8_t); - void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color); + bool begin(uint8_t switchvcc=SSD1306_SWITCHCAPVCC); + void display(void) __attribute__ ((optimize(3))); + + /*! + @brief Clear contents of display buffer (set all pixels to off). + @return None (void). + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. + */ + void clearDisplay(void) { memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8)); } + + // Faster drawPixel than display.drawPixel. + // Avoids some checks to make it faster. + void drawPixel(uint8_t x, uint8_t y, bool color, bool raycasterViewport = false); + /*! + @brief Enable or disable display invert mode (white-on-black vs + black-on-white). + @param i + If true, switch to invert mode (black-on-white), else normal + mode (white-on-black). + @return None (void). + @note This has an immediate effect on the display, no need to call the + display() function -- buffer contents are not changed, rather a + different pixel mode of the display hardware is used. When + enabled, drawing SSD1306_BLACK (value 0) pixels will actually draw white, + SSD1306_WHITE (value 1) will draw black. + */ + void invertDisplay(bool i) { ssd1306_command1(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY); } + + /*! + @brief Get base address of display buffer for direct reading or writing. + @return Pointer to an unsigned 8-bit array, column-major, columns padded + to full byte boundary if needed. + */ + uint8_t *getBuffer(void) { return buffer; } + void clearRect(uint8_t, uint8_t, uint8_t, uint8_t); + void drawBitmap(uint8_t x, uint8_t y, const uint8_t bitmap[], uint8_t w, uint8_t h, uint8_t color) __attribute__ ((optimize(3))); + // Faster way to render vertical bits + void drawByte(uint8_t x, uint8_t y, uint8_t b) { + buffer[(y / 8)*SCREEN_WIDTH + x] = b; + } private: - void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, - uint16_t color); + void drawFastVLineInternal(uint8_t x, uint8_t y, uint8_t h) __attribute__ ((optimize(3))); void ssd1306_command1(uint8_t c); void ssd1306_commandList(const uint8_t *c, uint8_t n); uint8_t buffer[WIDTH * ((HEIGHT + 7) / 8)]; - int8_t i2caddr, vccstate, page_end; + int8_t vccstate; }; -template class Adafruit_SSD1306; +template class Adafruit_SSD1306; #endif // _Adafruit_SSD1306_H_ diff --git a/constants.h b/constants.h index 4c59e60..97446b6 100644 --- a/constants.h +++ b/constants.h @@ -11,18 +11,17 @@ // SNES Controller // uncomment following line to enable snes controller support -// #define SNES_CONTROLLER -constexpr uint8_t DATA_CLOCK = 11; -constexpr uint8_t DATA_LATCH = 12; -constexpr uint8_t DATA_SERIAL = 13; + #define SNES_CONTROLLER +constexpr uint8_t DATA_CLOCK = 2; +constexpr uint8_t DATA_LATCH = 3; +constexpr uint8_t DATA_SERIAL = 4; // Sound constexpr uint8_t SOUND_PIN = 9; // do not change, belongs to used timer // GFX settings -#define OPTIMIZE_SSD1306 // Optimizations for SSD1366 displays -#define FRAME_TIME 66.666666 // Desired time per frame in ms (66.666666 is ~15 fps) +#define FRAME_TIME 40 // Desired time per frame in ms (40 is 25 fps) #define RES_DIVIDER 2 // Higher values will result in lower horizontal resolution when rasterize and lower process and memory usage // Lower will require more process and memory, but looks nicer #define Z_RES_DIVIDER 2 // Zbuffer resolution divider. We sacrifice resolution to save memory @@ -77,5 +76,13 @@ constexpr uint8_t SCREEN_HEIGHT = 64; constexpr uint8_t HALF_WIDTH = SCREEN_WIDTH/2; constexpr uint8_t RENDER_HEIGHT = 56; // raycaster working height (the rest is for the hud) constexpr uint8_t HALF_HEIGHT = SCREEN_HEIGHT/2; +constexpr uint8_t dcPin = 5; +constexpr uint8_t csPin = 10; +constexpr uint8_t I2C_ADDR = ((SCREEN_HEIGHT == 32) ? 0x3C : 0x3D); // (0x3C for 32-pixel-tall displays, 0x3D for all others). +enum DisplayMode { + I2C_MODE, + SPI_MODE +}; +constexpr DisplayMode MODE = SPI_MODE; #endif diff --git a/display.h b/display.h index 4b722cb..062f7ce 100644 --- a/display.h +++ b/display.h @@ -13,29 +13,21 @@ const static uint8_t PROGMEM bit_mask[8] = { 128, 64, 32, 16, 8, 4, 2, 1 }; void setupDisplay(); void fps(); -bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i); +bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i) __attribute__ ((optimize(3))); void fadeScreen(uint8_t intensity, bool color); -void drawByte(uint8_t x, uint8_t y, uint8_t b); -uint8_t getByte(uint8_t x, uint8_t y); -void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport); -void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity); -void drawSprite(int8_t x, int8_t y, const uint8_t bitmap[], const uint8_t mask[], int16_t w, int16_t h, uint8_t sprite, double distance); -void drawChar(int8_t x, int8_t y, char ch); -void drawText(int8_t x, int8_t y, char *txt, uint8_t space = 1); -void drawText(int8_t x, int8_t y, const __FlashStringHelper txt, uint8_t space = 1); +void drawVLine(uint8_t x, uint8_t start_y, uint8_t end_y, uint8_t intensity) __attribute__ ((optimize(3))); +void drawSprite(uint8_t x, uint8_t y, const uint8_t bitmap[], const uint8_t mask[], int16_t w, int16_t h, uint8_t sprite, double distance) __attribute__ ((optimize(3))); +void drawChar(uint8_t x, uint8_t y, char ch) __attribute__ ((optimize(3))); +void drawText(uint8_t x, uint8_t y, char *txt, uint8_t space = 1) __attribute__ ((optimize(3))); +void drawText(uint8_t x, uint8_t y, const __FlashStringHelper txt, uint8_t space = 1) __attribute__ ((optimize(3))); // Initialize screen. Following line is for OLED 128x64 connected by I2C -Adafruit_SSD1306 display; +Adafruit_SSD1306 display; // FPS control double delta = 1; uint32_t lastFrameTime = 0; -#ifdef OPTIMIZE_SSD1306 -// Optimizations for SSD1306 handles buffer directly -uint8_t *display_buf; -#endif - // We don't handle more than MAX_RENDER_DEPTH depth, so we can safety store // z values in a byte with 1 decimal and save some memory, uint8_t zbuffer[ZBUFFER_SIZE]; @@ -43,15 +35,11 @@ uint8_t zbuffer[ZBUFFER_SIZE]; void setupDisplay() { // Setup display // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally - if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Fixed from 0x3D + if (!display.begin(SSD1306_SWITCHCAPVCC)) { Serial.println(F("SSD1306 allocation failed")); while (1); // Don't proceed, loop forever } -#ifdef OPTIMIZE_SSD1306 - display_buf = display.getBuffer(); -#endif - // initialize z buffer memset(zbuffer, 0xFF, ZBUFFER_SIZE); } @@ -68,13 +56,6 @@ double getActualFps() { return 1000 / (FRAME_TIME * delta); } -// Faster way to render vertical bits -void drawByte(uint8_t x, uint8_t y, uint8_t b) { -#ifdef OPTIMIZE_SSD1306 - display_buf[(y / 8)*SCREEN_WIDTH + x] = b; -#endif -} - boolean getGradientPixel(uint8_t x, uint8_t y, uint8_t i) { if (i == 0) return 0; if (i >= GRADIENT_COUNT - 1) return 1; @@ -91,83 +72,44 @@ void fadeScreen(uint8_t intensity, bool color = 0) { for (uint8_t x = 0; x < SCREEN_WIDTH; x++) { for (uint8_t y = 0; y < SCREEN_HEIGHT; y++) { if (getGradientPixel(x, y, intensity)) - drawPixel(x, y, color, false); + display.drawPixel(x, y, color, false); } } } -// Faster drawPixel than display.drawPixel. -// Avoids some checks to make it faster. -void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport = false) { - // prevent write out of screen buffer - if (x < 0 || x >= SCREEN_WIDTH || y < 0 || y >= (raycasterViewport ? RENDER_HEIGHT : SCREEN_HEIGHT)) { - return; - } - -#ifdef OPTIMIZE_SSD1306 - if (color) { - // white - display_buf[x + (y / 8)*SCREEN_WIDTH] |= (1 << (y & 7)); - } else { - // black - display_buf[x + (y / 8)*SCREEN_WIDTH] &= ~(1 << (y & 7)); - } -#else - display.drawPixel(x, y, color); -#endif -} - // For raycaster only // Custom draw Vertical lines that fills with a pattern to simulate // different brightness. Affected by RES_DIVIDER -void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity) { - int8_t y; - int8_t lower_y = max(min(start_y, end_y), 0); - int8_t higher_y = min(max(start_y, end_y), RENDER_HEIGHT - 1); - uint8_t c; - -#ifdef OPTIMIZE_SSD1306 - uint8_t bp; - uint8_t b; - for (c = 0; c < RES_DIVIDER; c++) { - y = lower_y; - b = 0; +void drawVLine(uint8_t x, uint8_t start_y, uint8_t end_y, uint8_t intensity) { + uint8_t higher_y = min(end_y, RENDER_HEIGHT - 1); + + for (uint8_t c = 0; c < RES_DIVIDER; ++c) { + uint8_t y = start_y; + uint8_t b = 0; + uint8_t bp; while (y <= higher_y) { - bp = y % 8; - b = b | getGradientPixel(x + c, y, intensity) << bp; + bp = y & 0x07; + b |= getGradientPixel(x + c, y, intensity) << bp; if (bp == 7) { // write the whole byte - drawByte(x + c, y, b); + display.drawByte(x + c, y, b); b = 0; } - y++; + ++y; } // draw last byte if (bp != 7) { - drawByte(x + c, y - 1, b); + display.drawByte(x + c, y - 1, b); } } -#else - y = lower_y; - while (y <= higher_y) { - for (c = 0; c < RES_DIVIDER; c++) { - // bypass black pixels - if (getGradientPixel(x + c, y, intensity)) { - drawPixel(x + c, y, 1, true); - } - } - - y++; - } -#endif } // Custom drawBitmap method with scale support, mask, zindex and pattern filling void drawSprite( - int8_t x, int8_t y, + uint8_t x, uint8_t y, const uint8_t bitmap[], const uint8_t mask[], int16_t w, int16_t h, uint8_t sprite, @@ -211,7 +153,7 @@ void drawSprite( pixel = read_bit(pgm_read_byte(bitmap + byte_offset), sx % 8); for (uint8_t ox = 0; ox < pixel_size; ox++) { for (uint8_t oy = 0; oy < pixel_size; oy++) { - drawPixel(x + tx + ox, y + ty + oy, pixel, true); + display.drawPixel(x + tx + ox, y + ty + oy, pixel, true); } } } @@ -222,7 +164,7 @@ void drawSprite( // Draw a single character. // Made for a custom font with some useful sprites. Char size 4 x 6 // Uses less memory than display.print() -void drawChar(int8_t x, int8_t y, char ch) { +void drawChar(uint8_t x, uint8_t y, char ch) { uint8_t c = 0; uint8_t n; uint8_t bOffset; @@ -237,27 +179,20 @@ void drawChar(int8_t x, int8_t y, char ch) { b = pgm_read_byte(bmp_font + (line * bmp_font_width + bOffset)); for (n = 0; n < CHAR_WIDTH; n++) if (read_bit(b, (c % 2 == 0 ? 0 : 4) + n)) - drawPixel(x + n, y + line, 1, false); + display.drawPixel(x + n, y + line, 1, false); } } // Draw a string -void drawText(int8_t x, int8_t y, char *txt, uint8_t space) { - uint8_t pos = x; - uint8_t i = 0; - char ch; - while ((ch = txt[i]) != '\0') { - drawChar(pos, y, ch); - i++; - pos += CHAR_WIDTH + space; - - // shortcut on end of screen - if (pos > SCREEN_WIDTH) return; - } +void drawText(uint8_t x, uint8_t y, char *txt, uint8_t space) { + do { + drawChar(x, y, *txt++); + x += CHAR_WIDTH + space; + } while(*txt); } // Draw a F() string -void drawText(int8_t x, int8_t y, const __FlashStringHelper *txt_p, uint8_t space = 1) { +void drawText(uint8_t x, uint8_t y, const __FlashStringHelper *txt_p, uint8_t space = 1) { uint8_t pos = x; uint8_t i = 0; char ch; diff --git a/doom-nano.ino b/doom-nano.ino index 52d5e7b..c1fdd0e 100644 --- a/doom-nano.ino +++ b/doom-nano.ino @@ -701,7 +701,7 @@ void loopGamePlay() { fps(); // Clear only the 3d view - memset(display_buf, 0, SCREEN_WIDTH * (RENDER_HEIGHT / 8)); + memset(display.getBuffer(), 0, SCREEN_WIDTH * (RENDER_HEIGHT / 8)); #ifdef SNES_CONTROLLER getControllerData();