Merge pull request #811 from pimoroni/feature/rotation

Support for text and PNG 90-degree rotations
pull/813/head
Philip Howard 2023-08-04 10:42:17 +01:00 zatwierdzone przez GitHub
commit e1527c44d5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
4 zmienionych plików z 104 dodań i 27 usunięć

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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 = {