Merge pull request #811 from pimoroni/feature/rotation
Support for text and PNG 90-degree rotations
This commit is contained in:
commit
e1527c44d5
|
@ -42,7 +42,7 @@ namespace bitmap {
|
|||
return text_width;
|
||||
}
|
||||
|
||||
void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale, unicode_sorta::codepage_t codepage) {
|
||||
void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale, int32_t rotation, unicode_sorta::codepage_t codepage) {
|
||||
if(c < 32 || c > 127 + 64) { // + 64 char remappings defined in unicode_sorta.hpp
|
||||
return;
|
||||
}
|
||||
|
@ -89,7 +89,8 @@ namespace bitmap {
|
|||
uint8_t accent_offset = char_index < 65 ? offset_upper : offset_lower;
|
||||
|
||||
// Offset our y position to account for our column canvas being 32 pixels
|
||||
int y_offset = y - (8 * scale);
|
||||
// this gives us 8 "pixels" of headroom above the letters for diacritic marks
|
||||
int font_offset = (8 * scale);
|
||||
|
||||
// Iterate through each horizontal column of font (and accent) data
|
||||
for(uint8_t cx = 0; cx < font->widths[char_index]; cx++) {
|
||||
|
@ -98,6 +99,8 @@ namespace bitmap {
|
|||
// We shift the char down 8 pixels to make room for an accent above.
|
||||
uint32_t data = *d << 8;
|
||||
|
||||
int32_t o_x = cx * scale;
|
||||
|
||||
// For fonts that are taller than 8 pixels (up to 16) they need two bytes
|
||||
if(two_bytes_per_column) {
|
||||
d++;
|
||||
|
@ -113,7 +116,28 @@ namespace bitmap {
|
|||
// Draw the 32 pixel column
|
||||
for(uint8_t cy = 0; cy < 32; cy++) {
|
||||
if((1U << cy) & data) {
|
||||
rectangle(x + (cx * scale), y_offset + (cy * scale), scale, scale);
|
||||
int32_t o_y = cy * scale;
|
||||
int32_t px = 0;
|
||||
int32_t py = 0;
|
||||
switch (rotation) {
|
||||
case 0:
|
||||
px = x + o_x;
|
||||
py = y - font_offset + o_y;
|
||||
break;
|
||||
case 90:
|
||||
px = x + font_offset - o_y;
|
||||
py = y + o_x;
|
||||
break;
|
||||
case 180:
|
||||
px = x - o_x;
|
||||
py = y + font_offset - o_y;
|
||||
break;
|
||||
case 270:
|
||||
px = x - font_offset + o_y;
|
||||
py = y - o_x;
|
||||
break;
|
||||
}
|
||||
rectangle(px, py, scale, scale);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,8 +147,9 @@ namespace bitmap {
|
|||
}
|
||||
}
|
||||
|
||||
void text(const font_t *font, rect_func rectangle, const std::string_view &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale, const uint8_t letter_spacing, bool fixed_width) {
|
||||
uint32_t co = 0, lo = 0; // character and line (if wrapping) offset
|
||||
void text(const font_t *font, rect_func rectangle, const std::string_view &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale, const uint8_t letter_spacing, bool fixed_width, int32_t rotation) {
|
||||
uint32_t char_offset = 0;
|
||||
uint32_t line_offset = 0; // line (if wrapping) offset
|
||||
unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195;
|
||||
|
||||
size_t i = 0;
|
||||
|
@ -153,37 +178,52 @@ namespace bitmap {
|
|||
continue;
|
||||
}
|
||||
word_width += measure_character(font, t[j], scale, codepage, fixed_width);
|
||||
word_width += letter_spacing * scale;
|
||||
codepage = unicode_sorta::PAGE_195;
|
||||
}
|
||||
|
||||
// if this word would exceed the wrap limit then
|
||||
// move to the next line
|
||||
if(co != 0 && co + word_width > (uint32_t)wrap) {
|
||||
co = 0;
|
||||
lo += (font->height + 1) * scale;
|
||||
if(char_offset != 0 && char_offset + word_width > (uint32_t)wrap) {
|
||||
char_offset = 0;
|
||||
line_offset += (font->height + 1) * scale;
|
||||
}
|
||||
|
||||
// draw word
|
||||
for(size_t j = i; j < next_break; j++) {
|
||||
for(size_t j = i; j < std::min(next_break + 1, t.length()); j++) {
|
||||
if (t[j] == unicode_sorta::PAGE_194_START) {
|
||||
codepage = unicode_sorta::PAGE_194;
|
||||
continue;
|
||||
} else if (t[j] == unicode_sorta::PAGE_195_START) {
|
||||
continue;
|
||||
}
|
||||
if (t[j] == '\n') {
|
||||
lo += (font->height + 1) * scale;
|
||||
co = 0;
|
||||
if (t[j] == '\n') { // Linebreak
|
||||
line_offset += (font->height + 1) * scale;
|
||||
char_offset = 0;
|
||||
} else if (t[j] == ' ') { // Space
|
||||
char_offset += font->widths[0] * scale;
|
||||
} else {
|
||||
character(font, rectangle, t[j], x + co, y + lo, scale, codepage);
|
||||
co += measure_character(font, t[j], scale, codepage, fixed_width);
|
||||
co += letter_spacing * scale;
|
||||
switch(rotation) {
|
||||
case 0:
|
||||
character(font, rectangle, t[j], x + char_offset, y + line_offset, scale, rotation, codepage);
|
||||
break;
|
||||
case 90:
|
||||
character(font, rectangle, t[j], x - line_offset, y + char_offset, scale, rotation, codepage);
|
||||
break;
|
||||
case 180:
|
||||
character(font, rectangle, t[j], x - char_offset, y - line_offset, scale, rotation, codepage);
|
||||
break;
|
||||
case 270:
|
||||
character(font, rectangle, t[j], x + line_offset, y - char_offset, scale, rotation, codepage);
|
||||
break;
|
||||
}
|
||||
char_offset += measure_character(font, t[j], scale, codepage, fixed_width);
|
||||
char_offset += letter_spacing * scale;
|
||||
}
|
||||
codepage = unicode_sorta::PAGE_195;
|
||||
}
|
||||
|
||||
// move character offset to end of word and add a space
|
||||
co += font->widths[0] * scale;
|
||||
// move character offset
|
||||
i = next_break += 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,6 @@ namespace bitmap {
|
|||
int32_t measure_character(const font_t *font, const char c, const uint8_t scale, unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195, bool fixed_width = false);
|
||||
int32_t measure_text(const font_t *font, const std::string_view &t, const uint8_t scale = 2, const uint8_t letter_spacing = 1, bool fixed_width = false);
|
||||
|
||||
void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale = 2, unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195);
|
||||
void text(const font_t *font, rect_func rectangle, const std::string_view &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale = 2, const uint8_t letter_spacing = 1, bool fixed_width = false);
|
||||
void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale = 2, int32_t rotation = 0, unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195);
|
||||
void text(const font_t *font, rect_func rectangle, const std::string_view &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale = 2, const uint8_t letter_spacing = 1, bool fixed_width = false, int32_t rotation = 0);
|
||||
}
|
|
@ -132,7 +132,7 @@ namespace pimoroni {
|
|||
if (bitmap_font) {
|
||||
bitmap::character(bitmap_font, [this](int32_t x, int32_t y, int32_t w, int32_t h) {
|
||||
rectangle(Rect(x, y, w, h));
|
||||
}, c, p.x, p.y, std::max(1.0f, s));
|
||||
}, c, p.x, p.y, std::max(1.0f, s), int32_t(a) % 360);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ namespace pimoroni {
|
|||
if (bitmap_font) {
|
||||
bitmap::text(bitmap_font, [this](int32_t x, int32_t y, int32_t w, int32_t h) {
|
||||
rectangle(Rect(x, y, w, h));
|
||||
}, t, p.x, p.y, wrap, std::max(1.0f, s), letter_spacing, fixed_width);
|
||||
}, t, p.x, p.y, wrap, std::max(1.0f, s), letter_spacing, fixed_width, int32_t(a) % 360);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ typedef struct _PNG_decode_target {
|
|||
Point position = {0, 0};
|
||||
Rect source = {0, 0, 0, 0};
|
||||
Point scale = {1, 1};
|
||||
int rotation = 0;
|
||||
} _PNG_decode_target;
|
||||
|
||||
typedef struct _PNG_obj_t {
|
||||
|
@ -125,14 +126,37 @@ MICROPY_EVENT_POLL_HOOK
|
|||
Point current_position = target->position;
|
||||
uint8_t current_mode = target->mode;
|
||||
Point scale = target->scale;
|
||||
int rotation = target->rotation;
|
||||
Point step = {0, 0};
|
||||
|
||||
// "pixel" is slow and clipped,
|
||||
// guaranteeing we wont draw png data out of the framebuffer..
|
||||
// Can we clip beforehand and make this faster?
|
||||
|
||||
if(pDraw->y < target->source.y || pDraw->y >= target->source.y + target->source.h) return;
|
||||
|
||||
current_position.y += pDraw->y * scale.y;
|
||||
current_position -= Point(0, target->source.y);
|
||||
switch (rotation) {
|
||||
case 0:
|
||||
current_position.y += (pDraw->y - target->source.y) * scale.y;
|
||||
step = {scale.x, 0};
|
||||
break;
|
||||
case 90:
|
||||
current_position.y += target->source.w * scale.y;
|
||||
current_position.x += target->source.h * scale.x;
|
||||
current_position.x += (pDraw->y - target->source.y) * -scale.x;
|
||||
step = {0, -scale.y};
|
||||
break;
|
||||
case 180:
|
||||
current_position.x += target->source.w * scale.x;
|
||||
current_position.y += target->source.h * scale.y;
|
||||
current_position.y += (pDraw->y - target->source.y) * -scale.y;
|
||||
step = {-scale.x, 0};
|
||||
break;
|
||||
case 270:
|
||||
current_position.x += (pDraw->y - target->source.y) * scale.x;
|
||||
step = {0, scale.y};
|
||||
break;
|
||||
}
|
||||
|
||||
//mp_printf(&mp_plat_print, "Drawing scanline at %d, %dbpp, type: %d, width: %d pitch: %d alpha: %d\n", y, pDraw->iBpp, pDraw->iPixelType, pDraw->iWidth, pDraw->iPitch, pDraw->iHasAlpha);
|
||||
uint8_t *pixel = (uint8_t *)pDraw->pPixels;
|
||||
|
@ -144,7 +168,7 @@ MICROPY_EVENT_POLL_HOOK
|
|||
if(x < target->source.x || x >= target->source.x + target->source.w) continue;
|
||||
current_graphics->set_pen(r, g, b);
|
||||
current_graphics->rectangle({current_position.x, current_position.y, scale.x, scale.y});
|
||||
current_position.x += scale.x;
|
||||
current_position += step;
|
||||
}
|
||||
} else if (pDraw->iPixelType == PNG_PIXEL_TRUECOLOR_ALPHA) {
|
||||
for(int x = 0; x < pDraw->iWidth; x++) {
|
||||
|
@ -157,7 +181,7 @@ MICROPY_EVENT_POLL_HOOK
|
|||
current_graphics->set_pen(r, g, b);
|
||||
current_graphics->rectangle({current_position.x, current_position.y, scale.x, scale.y});
|
||||
}
|
||||
current_position.x += scale.x;
|
||||
current_position += step;
|
||||
}
|
||||
} else if (pDraw->iPixelType == PNG_PIXEL_INDEXED) {
|
||||
for(int x = 0; x < pDraw->iWidth; x++) {
|
||||
|
@ -219,7 +243,7 @@ MICROPY_EVENT_POLL_HOOK
|
|||
current_graphics->rectangle({current_position.x, current_position.y, scale.x, scale.y});
|
||||
}
|
||||
}
|
||||
current_position.x += scale.x;
|
||||
current_position += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -292,7 +316,7 @@ mp_obj_t _PNG_openRAM(mp_obj_t self_in, mp_obj_t buffer) {
|
|||
|
||||
// decode
|
||||
mp_obj_t _PNG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_self, ARG_x, ARG_y, ARG_scale, ARG_mode, ARG_source };
|
||||
enum { ARG_self, ARG_x, ARG_y, ARG_scale, ARG_mode, ARG_source, ARG_rotate };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_x, MP_ARG_INT, {.u_int = 0} },
|
||||
|
@ -300,6 +324,7 @@ mp_obj_t _PNG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
|||
{ MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = nullptr} },
|
||||
{ MP_QSTR_mode, MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_source, MP_ARG_OBJ, {.u_obj = nullptr} },
|
||||
{ MP_QSTR_rotate, MP_ARG_INT, {.u_int = 0} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
|
@ -322,6 +347,18 @@ mp_obj_t _PNG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
|||
self->decode_target->source = {0, 0, self->width, self->height};
|
||||
}
|
||||
|
||||
self->decode_target->rotation = args[ARG_rotate].u_int;
|
||||
switch(self->decode_target->rotation) {
|
||||
case 0:
|
||||
case 90:
|
||||
case 180:
|
||||
case 270:
|
||||
break;
|
||||
default:
|
||||
mp_raise_ValueError("decode(): rotation must be one of 0, 90, 180 or 270");
|
||||
break;
|
||||
}
|
||||
|
||||
// Scale is a single int, corresponds to both width/height
|
||||
if (mp_obj_is_int(args[ARG_scale].u_obj)) {
|
||||
self->decode_target->scale = {
|
||||
|
|
Loading…
Reference in New Issue