diff --git a/libraries/bitmap_fonts/bitmap_fonts.cpp b/libraries/bitmap_fonts/bitmap_fonts.cpp index 88c847d9..c06e856a 100644 --- a/libraries/bitmap_fonts/bitmap_fonts.cpp +++ b/libraries/bitmap_fonts/bitmap_fonts.cpp @@ -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; } } diff --git a/libraries/bitmap_fonts/bitmap_fonts.hpp b/libraries/bitmap_fonts/bitmap_fonts.hpp index a0cf9ba8..f4fa57de 100644 --- a/libraries/bitmap_fonts/bitmap_fonts.hpp +++ b/libraries/bitmap_fonts/bitmap_fonts.hpp @@ -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); } \ No newline at end of file diff --git a/libraries/pico_graphics/pico_graphics.cpp b/libraries/pico_graphics/pico_graphics.cpp index 82271696..557fc785 100644 --- a/libraries/pico_graphics/pico_graphics.cpp +++ b/libraries/pico_graphics/pico_graphics.cpp @@ -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; } diff --git a/micropython/modules/pngdec/pngdec.cpp b/micropython/modules/pngdec/pngdec.cpp index 2042aedc..90a34f3d 100644 --- a/micropython/modules/pngdec/pngdec.cpp +++ b/micropython/modules/pngdec/pngdec.cpp @@ -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 = {