Skip to content

Commit 76276c3

Browse files
Laurence BankLaurence Bank
authored andcommitted
Added scaled+rotated text drawing
1 parent 783bb4d commit 76276c3

File tree

5 files changed

+174
-11
lines changed

5 files changed

+174
-11
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Features:<br>
3434
- Text cursor position with optional line wrap<br>
3535
- a function to load a Windows BMP file<br>
3636
- Pixel drawing on SH1106/7 without needing backing RAM<br>
37+
- Draw text into the back buffer at any scale and rotation (in 90 deg increments)<br>
3738
- Optimized Bresenham line drawing<br>
3839
- Optimized Bresenham outline and filled ellipse drawing<br>
3940
- Optimized outline and filled rectangle drawing<br>

examples/ss_oled_test/ss_oled_test.ino

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,35 @@
33
// Arduino Pro Mini
44
// Pin 8 (0xb0 = PORTB, bit 0)
55
// Pin 9 (0xb1 = PORTB, bit 1)
6-
#define SDA_PIN 0xb0
7-
#define SCL_PIN 0xb1
6+
// UnexpectedMaker TinyPICO
7+
#define SDA_PIN 21
8+
#define SCL_PIN 22
9+
810
#define RESET_PIN -1
11+
#define FLIPPED 1
12+
#define INVERTED 0
13+
// Use bit banging to get higher speed output
14+
#define HARDWARE_I2C 0
15+
#define WIDTH 128
16+
#define HEIGHT 64
917
int rc;
1018
SSOLED oled;
19+
20+
// Use a 1K back buffer to do access more complex features on systems with
21+
// more RAM available. This can work on AVR, but will use most of the RAM
22+
#ifndef __AVR__
23+
static uint8_t ucBuffer[1024];
24+
#endif // __AVR__
25+
1126
void setup() {
1227
uint8_t uc[8];
1328

14-
rc = oledInit(&oled, OLED_128x64, 0x3c, 0, 0, 0, SDA_PIN, SCL_PIN, RESET_PIN, 1000000L);
29+
rc = oledInit(&oled, OLED_128x64, 0x3c, FLIPPED, INVERTED, HARDWARE_I2C, SDA_PIN, SCL_PIN, RESET_PIN, 1000000L);
1530
if (rc != OLED_NOT_FOUND)
16-
{
31+
{
32+
#ifndef __AVR__
33+
oledSetBackBuffer(&oled, ucBuffer);
34+
#endif
1735
oledFill(&oled, 0,1);
1836
oledSetContrast(&oled, 127);
1937
oledWriteString(&oled, 0,0,0,(char *)"**************** ", FONT_8x8, 0, 1);
@@ -38,21 +56,21 @@ char szTemp[32];
3856
oledWriteString(&oled, 0,0,2,(char *)"16x32 and a new", FONT_8x8, 0, 1);
3957
oledWriteString(&oled, 0,0,3,(char *)"Stretched", FONT_12x16, 0, 1);
4058
oledWriteString(&oled, 0,0,5,(char *)"from 6x8", FONT_12x16, 0, 1);
41-
delay(10000);
59+
delay(5000);
4260

4361
oledFill(&oled, 0, 1);
4462
oledSetTextWrap(&oled, 1);
45-
oledWriteString(&oled, 0,-1,-1,"This is a test of text wrap", FONT_6x8, 0, 1);
63+
oledWriteString(&oled, 0,-1,-1,(char *)"This is a test of text wrap", FONT_6x8, 0, 1);
4664
delay(3000);
4765
oledFill(&oled, 0,1);
4866
// oledSetTextWrap(0);
49-
oledWriteString(&oled, 0,-1,-1,"This ", FONT_16x16, 0, 1);
50-
oledWriteString(&oled, 0,-1,-1,"is a ", FONT_16x16, 0, 1);
51-
oledWriteString(&oled, 0,-1,-1,"test of text wrap", FONT_16x16, 0, 1);
67+
oledWriteString(&oled, 0,-1,-1,(char *)"This ", FONT_16x16, 0, 1);
68+
oledWriteString(&oled, 0,-1,-1,(char *)"is a ", FONT_16x16, 0, 1);
69+
oledWriteString(&oled, 0,-1,-1,(char *)"test of text wrap", FONT_16x16, 0, 1);
5270
delay(3000);
5371
oledFill(&oled, 0,1);
5472
oledSetCursor(&oled, 40,4);
55-
oledWriteString(&oled, 0,-1,-1,"Middle", FONT_6x8,0,1);
73+
oledWriteString(&oled, 0,-1,-1,(char *)"Middle", FONT_6x8,0,1);
5674
delay(3000);
5775
if (rc >= OLED_SH1106_3C) // We can set pixels on the SH1106 without a back buffer
5876
{
@@ -67,6 +85,43 @@ char szTemp[32];
6785
}
6886
#ifndef __AVR__
6987

88+
for (int iRot=ROT_0; iRot <= ROT_270; iRot++) {
89+
for (int i=128; i<1280; i+=64) {
90+
oledFill(&oled, 0, 0);
91+
oledScaledString(&oled, WIDTH/2, HEIGHT/2, (char *)"Hello", FONT_SMALL, 0, i, i, iRot);
92+
oledDumpBuffer(&oled, NULL);
93+
}
94+
for (int i=1280; i>=128; i-=64) {
95+
oledFill(&oled, 0, 0);
96+
oledScaledString(&oled, WIDTH/2, HEIGHT/2, (char *)"Hello", FONT_SMALL, 0, 1280, i, iRot);
97+
oledDumpBuffer(&oled, NULL);
98+
}
99+
for (int i=1280; i>=128; i-=64) {
100+
oledFill(&oled, 0, 0);
101+
oledScaledString(&oled, WIDTH/2, HEIGHT/2, (char *)"Hello", FONT_SMALL, 0, i, 128, iRot);
102+
oledDumpBuffer(&oled, NULL);
103+
}
104+
}
105+
delay(2000);
106+
107+
// Rotated Text
108+
oledFill(&oled, 0, 1);
109+
oledWriteString(&oled, 0, 0, 0, (char *)"Rotated", FONT_NORMAL, 0, 1);
110+
oledWriteString(&oled, 0, 0, 1, (char *)"Text", FONT_NORMAL, 0, 1);
111+
delay(2000);
112+
113+
for (int iRot = ROT_0; iRot <= ROT_270; iRot++) {
114+
oledFill(&oled, 0, 1);
115+
for (i = 0; i<40; i++) {
116+
int x, y;
117+
x = random(WIDTH);
118+
y = random(HEIGHT);
119+
oledScaledString(&oled, x, y, (char *)"Rotated Text", FONT_8x8, 0, 256, 256, iRot);
120+
oledDumpBuffer(&oled, NULL);
121+
} // for i
122+
delay(2000);
123+
} // for iRot
124+
70125
for (i=0; i<8; i++)
71126
{
72127
sprintf(szTemp, "Line %d", i);

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=ss_oled
2-
version=4.2.1
2+
version=4.3.1
33
author=Larry Bank
44
maintainer=Larry Bank
55
sentence=Small and simple OLED display library.

src/ss_oled.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2045,6 +2045,98 @@ void oledDrawLine(SSOLED *pOLED, int x1, int y1, int x2, int y2, int bRender)
20452045
} // y major case
20462046
} /* oledDrawLine() */
20472047

2048+
//
2049+
// Draw a string with a fractional scale in both dimensions
2050+
// the scale is a 16-bit integer with and 8-bit fraction and 8-bit mantissa
2051+
// To draw at 1x scale, set the scale factor to 256. To draw at 2x, use 512
2052+
// The output must be drawn into a memory buffer, not directly to the display
2053+
//
2054+
int oledScaledString(SSOLED *pOLED, int x, int y, char *szMsg, int iSize, int bInvert, int iXScale, int iYScale, int iRotation)
2055+
{
2056+
uint32_t row, col, dx, dy;
2057+
uint32_t sx, sy;
2058+
uint8_t c, uc, color, *d;
2059+
const uint8_t *s;
2060+
uint8_t ucTemp[16];
2061+
int tx, ty, bit, iFontOff;
2062+
int iPitch, iOffset;
2063+
int iFontWidth;
2064+
2065+
if (iXScale == 0 || iYScale == 0 || szMsg == NULL || pOLED == NULL || pOLED->ucScreen == NULL || x < 0 || y < 0 || x >= pOLED->oled_x-1 || y >= pOLED->oled_y-1)
2066+
return -1; // invalid display structure
2067+
if (iSize != FONT_8x8 && iSize != FONT_6x8)
2068+
return -1; // only on the small fonts (for now)
2069+
iFontWidth = (iSize == FONT_6x8) ? 6:8;
2070+
s = (iSize == FONT_6x8) ? ucSmallFont : ucFont;
2071+
iPitch = pOLED->oled_x;
2072+
dx = (iFontWidth * iXScale) >> 8; // width of each character
2073+
dy = (8 * iYScale) >> 8; // height of each character
2074+
sx = 65536 / iXScale; // turn the scale into an accumulator value
2075+
sy = 65536 / iYScale;
2076+
while (*szMsg) {
2077+
c = *szMsg++; // debug - start with normal font
2078+
iFontOff = (int)(c-32) * (iFontWidth-1);
2079+
// we can't directly use the pointer to FLASH memory, so copy to a local buffer
2080+
ucTemp[0] = 0; // first column is blank
2081+
memcpy_P(&ucTemp[1], &s[iFontOff], iFontWidth-1);
2082+
if (bInvert) InvertBytes(ucTemp, iFontWidth);
2083+
col = 0;
2084+
for (tx=0; tx<(int)dx; tx++) {
2085+
row = 0;
2086+
uc = ucTemp[col >> 8];
2087+
for (ty=0; ty<(int)dy; ty++) {
2088+
int nx, ny;
2089+
bit = row >> 8;
2090+
color = (uc & (1 << bit)); // set or clear the pixel
2091+
switch (iRotation) {
2092+
case ROT_0:
2093+
nx = x + tx;
2094+
ny = y + ty;
2095+
break;
2096+
case ROT_90:
2097+
nx = x - ty;
2098+
ny = y + tx;
2099+
break;
2100+
case ROT_180:
2101+
nx = x - tx;
2102+
ny = y - ty;
2103+
break;
2104+
case ROT_270:
2105+
nx = x + ty;
2106+
ny = y - tx;
2107+
break;
2108+
} // switch on rotation direction
2109+
// plot the pixel if it's within the image boundaries
2110+
if (nx >= 0 && ny >= 0 && nx < pOLED->oled_x && ny < pOLED->oled_y) {
2111+
d = &pOLED->ucScreen[(ny >> 3) * iPitch + nx];
2112+
if (color)
2113+
d[0] |= (1 << (ny & 7));
2114+
else
2115+
d[0] &= ~(1 << (ny & 7));
2116+
}
2117+
row += sy; // add fractional increment to source row of character
2118+
} // for ty
2119+
col += sx; // add fractional increment to source column
2120+
} // for tx
2121+
// update the 'cursor' position
2122+
switch (iRotation) {
2123+
case ROT_0:
2124+
x += dx;
2125+
break;
2126+
case ROT_90:
2127+
y += dx;
2128+
break;
2129+
case ROT_180:
2130+
x -= dx;
2131+
break;
2132+
case ROT_270:
2133+
y -= dx;
2134+
break;
2135+
} // switch on rotation
2136+
} // while (*szMsg)
2137+
return 0;
2138+
} /* oledScaledString() */
2139+
20482140
//
20492141
// For drawing ellipses, a circle is drawn and the x and y pixels are scaled by a 16-bit integer fraction
20502142
// This function draws a single pixel and scales its position based on the x/y fraction of the ellipse

src/ss_oled.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ enum {
2929
FONT_16x16,
3030
FONT_16x32
3131
};
32+
// 4 possible rotation angles for oledScaledString()
33+
enum {
34+
ROT_0=0,
35+
ROT_90,
36+
ROT_180,
37+
ROT_270
38+
};
39+
3240
#define FONT_NORMAL FONT_8x8
3341
#define FONT_SMALL FONT_6x8
3442
#define FONT_LARGE FONT_16x32
@@ -128,6 +136,13 @@ void oledSetTextWrap(SSOLED *pOLED, int bWrap);
128136
//
129137
int oledWriteString(SSOLED *pOLED, int iScrollX, int x, int y, char *szMsg, int iSize, int bInvert, int bRender);
130138
//
139+
// Draw a string with a fractional scale in both dimensions
140+
// the scale is a 16-bit integer with and 8-bit fraction and 8-bit mantissa
141+
// To draw at 1x scale, set the scale factor to 256. To draw at 2x, use 512
142+
// The output must be drawn into a memory buffer, not directly to the display
143+
//
144+
int oledScaledString(SSOLED *pOLED, int x, int y, char *szMsg, int iSize, int bInvert, int iXScale, int iYScale, int iRotation);
145+
//
131146
// Fill the frame buffer with a byte pattern
132147
// e.g. all off (0x00) or all on (0xff)
133148
//

0 commit comments

Comments
 (0)