Repository navigation
Add Maps app (first version, tested on T-Deck only) #65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
1761875
Add Maps: offline vector maps (first version)
AdaSzi 4f55771
Maps: fix exit crash on newer firmware, text with built-in fonts, til…
AdaSzi bf61a4b
Maps 0.2.0. added labels and rotation, fixed bugs
AdaSzi b8e897e
Maps 0.3.0: points of interest, touch-only use, download fixes
AdaSzi 85baf20
Maps 0.4.0: offline navigation, faster map loading
AdaSzi 1287025
Maps: also for ESP32-P4
AdaSzi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # tactility-cmakelists-version: 1 | ||
| cmake_minimum_required(VERSION 3.20) | ||
|
|
||
| if (NOT DEFINED ENV{TACTILITY_SDK_PATH}) | ||
| message(FATAL_ERROR "TACTILITY_SDK_PATH environment variable is not set") | ||
| endif() | ||
|
|
||
| get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) | ||
| include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") | ||
|
|
||
| tactility_project_pre(maps) | ||
| project(maps) | ||
| tactility_project_post(maps) |
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Third-party code | ||
|
|
||
| | Component | Where | Licence | Notes | | ||
| |---|---|---|---| | ||
| | miniz 3.0.2 (Rich Geldreich, RAD Game Tools, Valve Software) | `host/third_party/miniz/` | MIT, see `host/third_party/miniz/LICENSE` | PC build only: inflate and PNG writing for the host tools and tests. The app uses the `tinfl_decompress` exported by Tactility. | | ||
| | Adwaita Sans (GNOME [adwaita-fonts](https://github.com/GNOME/adwaita-fonts), derived from Inter by the Inter Project Authors) | `assets/fonts/LabelSans.ttf` | SIL Open Font License 1.1, https://raw.githubusercontent.com/GNOME/adwaita-fonts/refs/heads/main/LICENSE | A subset (Basic Latin, Latin-1, Latin Extended-A, U+0218-021B, U+0259, Greek U+0386-03CE, Cyrillic U+0400-045F and U+0490-0491, plus the characters listed in `tools/label_codepoints.txt`, which map names use; weight 550, no hinting or layout tables) made by `host/make_label_font.py` from the font in Tactility's repository. The app makes its map label fonts from it on the device. The name table of the file names the licence. | | ||
|
|
||
| | [Maki](https://github.com/mapbox/maki) icons (Mapbox) | `main/Source/render/PoiIconData.h` | CC0 1.0 Universal | 89 icons turned into 15 x 15 alpha masks by `host/make_poi_icons.py`. | | ||
|
|
||
| `main/Source/app/RootCertificates.h` holds the public root certificates of Google Trust Services (GTS Root R1, R4) | ||
| and Let's Encrypt (ISRG Root X1, X2), as published at pki.goog and letsencrypt.org, used to verify the HTTPS | ||
| connection to OpenFreeMap. | ||
|
|
||
| The rasteriser in `main/Source/render/Raster.cpp` is written for this project. It uses the published signed-area | ||
| coverage technique (as described for font-rs and stb_truetype), no code was copied. | ||
|
|
||
| # Map data | ||
|
|
||
| Test fixtures in `tests/fixtures/` are vector tiles from [OpenFreeMap](https://openfreemap.org/), in the | ||
| OpenMapTiles schema. © OpenMapTiles © OpenStreetMap contributors. OpenStreetMap data is available under the | ||
| Open Database License (ODbL). |
Binary file not shown.
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| include("$ENV{TACTILITY_SDK_PATH}/TactilitySDK.cmake") | ||
|
|
||
| file(GLOB_RECURSE SOURCE_FILES Source/*.c*) | ||
| tactility_component_register( | ||
| SRCS ${SOURCE_FILES} | ||
| REQUIRES graphics-module binfont-module location-module http-module esp_http_client esp_rom mbedtls | ||
| ) | ||
| target_compile_options(${COMPONENT_LIB} PRIVATE -O2 -Wno-format-truncation) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,183 @@ | ||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||
| #include "BinFontText.h" | ||
|
|
||
| #include "../platform/Platform.h" | ||
|
|
||
| #include <binfont/binfont.h> | ||
| #include <binfont/render.h> | ||
| #include <graphics/pixel_buffer.h> | ||
| #include <lvgl.h> | ||
| #include <lvgl/fonts.h> | ||
|
|
||
| #include <cstdio> | ||
| #include <cstring> | ||
|
|
||
| namespace maps { | ||
|
|
||
| namespace { | ||
|
|
||
| struct FontSearch { | ||
| const char* prefix; | ||
| char name[96]; | ||
| }; | ||
|
|
||
| bool findFont(const char* name, bool isDirectory, void* context) { | ||
| auto* search = static_cast<FontSearch*>(context); | ||
| if (!isDirectory && strncmp(name, search->prefix, strlen(search->prefix)) == 0) { | ||
| snprintf(search->name, sizeof(search->name), "%s", name); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| /** Decodes one UTF-8 character and advances the pointer. */ | ||
| uint32_t nextCodepoint(const char*& text) { | ||
| const auto first = static_cast<uint8_t>(*text++); | ||
| if (first < 0x80) { | ||
| return first; | ||
| } | ||
| if ((first & 0xE0) == 0xC0 && *text != '\0') { | ||
| return ((first & 0x1Fu) << 6) | (static_cast<uint8_t>(*text++) & 0x3Fu); | ||
| } | ||
| // Longer sequences are skipped and shown as '?' | ||
| while ((static_cast<uint8_t>(*text) & 0xC0) == 0x80) { | ||
| text++; | ||
| } | ||
| return '?'; | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| bool BinFontText::open(const char* folder, const char* const* prefixes, int prefixCount) { | ||
| for (int i = 0; i < prefixCount; i++) { | ||
| FontSearch search { prefixes[i], "" }; | ||
| listDirectory(folder, findFont, &search); | ||
| if (search.name[0] == '\0') { | ||
| continue; | ||
| } | ||
| char path[260]; | ||
| snprintf(path, sizeof(path), "%s/%s", folder, search.name); | ||
| if (binfont_open_file(path, &font) == ERROR_NONE) { | ||
| owned = true; | ||
| BinFontMetrics metrics; | ||
| binfont_get_metrics(font, &metrics); | ||
| height = metrics.line_height; | ||
| ascentPixels = metrics.ascent; | ||
| logInfo("Font %s, line height %d", search.name, height); | ||
| return true; | ||
| } | ||
| font = nullptr; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| bool BinFontText::openFile(const char* path) { | ||
| close(); | ||
| if (binfont_open_file(path, &font) != ERROR_NONE) { | ||
| font = nullptr; | ||
| return false; | ||
| } | ||
| owned = true; | ||
| BinFontMetrics metrics; | ||
| binfont_get_metrics(font, &metrics); | ||
| height = metrics.line_height; | ||
| ascentPixels = metrics.ascent; | ||
| logInfo("Font file %s, line height %d", path, height); | ||
| return true; | ||
| } | ||
|
|
||
| bool BinFontText::borrow(int lvglFontSize) { | ||
| const lv_font_t* lvFont = lvgl_get_text_font(static_cast<LvglFontSize>(lvglFontSize)); | ||
| // Tactility's fonts come from lvgl_binfont_create(), which keeps the BinFont in dsc. Without a registered font | ||
| // LVGL's built-in one comes back, whose dsc is something else. | ||
| if (lvFont == nullptr || lvFont->dsc == nullptr || lvFont->get_glyph_dsc == lv_font_get_glyph_dsc_fmt_txt) { | ||
| return false; | ||
| } | ||
| font = static_cast<BinFont*>(const_cast<void*>(lvFont->dsc)); | ||
| owned = false; | ||
| BinFontMetrics metrics; | ||
| binfont_get_metrics(font, &metrics); | ||
| height = metrics.line_height; | ||
| ascentPixels = metrics.ascent; | ||
| logInfo("Font from the system, size %d, line height %d", lvglFontSize, height); | ||
| return true; | ||
| } | ||
|
|
||
| void BinFontText::close() { | ||
| if (target != nullptr) { | ||
| pixel_buffer_free(target); | ||
| target = nullptr; | ||
| targetPixels = nullptr; | ||
| } | ||
| if (font != nullptr && owned) { | ||
| binfont_close(font); | ||
| } | ||
| font = nullptr; | ||
| owned = false; | ||
| } | ||
|
|
||
| int BinFontText::textWidth(const char* text) { | ||
| if (font == nullptr) { | ||
| return 0; | ||
| } | ||
| // The same advances and kerning binfont_draw_text_rgb565() applies | ||
| int32_t widthX16 = 0; | ||
| uint32_t previous = 0; | ||
| bool hasPrevious = false; | ||
| while (*text != '\0') { | ||
| BinFontGlyph glyph; | ||
| if (!binfont_get_glyph(font, nextCodepoint(text), &glyph)) { | ||
| continue; | ||
| } | ||
| if (hasPrevious) { | ||
| widthX16 += binfont_get_kerning_x16(font, previous, glyph.glyph_id); | ||
| } | ||
| widthX16 += static_cast<int32_t>(glyph.advance_x16); | ||
| previous = glyph.glyph_id; | ||
| hasPrevious = true; | ||
| } | ||
| return (widthX16 + 8) >> 4; | ||
| } | ||
|
|
||
| void BinFontText::drawText(Canvas& canvas, int x, int y, const char* text, uint16_t color) { | ||
| if (font == nullptr) { | ||
| return; | ||
| } | ||
| if (target == nullptr || targetPixels != canvas.pixels) { | ||
| if (target != nullptr) { | ||
| pixel_buffer_free(target); | ||
| } | ||
| target = pixel_buffer_wrap(canvas.pixels, DISPLAY_COLOR_FORMAT_RGB565, canvas.width, canvas.height, 0); | ||
| targetPixels = target != nullptr ? canvas.pixels : nullptr; | ||
| if (target == nullptr) { | ||
| return; | ||
| } | ||
| } | ||
| binfont_draw_text_rgb565(target, x, y + ascentPixels, font, text, color, PIXEL_BUFFER_CONVERSION_AVERAGE); | ||
| } | ||
|
|
||
| bool BinFontText::glyph(uint32_t codepoint, LabelGlyph& out, bool withMask) { | ||
| BinFontGlyph found; | ||
| if (font == nullptr || !binfont_get_glyph(font, codepoint, &found)) { | ||
| return false; | ||
| } | ||
| out.id = found.glyph_id; | ||
| out.advanceX16 = static_cast<int>(found.advance_x16); | ||
| out.left = found.x; | ||
| // binfont gives the box's bottom above the baseline, labels want its top below it | ||
| out.top = -(found.y + found.height); | ||
| out.width = found.width; | ||
| out.height = found.height; | ||
| out.alpha = nullptr; | ||
| if (withMask && found.width > 0 && found.height > 0 && found.width * found.height <= MASK_BYTES && | ||
| binfont_get_glyph_bitmap(font, &found, mask, found.width) == ERROR_NONE) { | ||
| out.alpha = mask; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| int BinFontText::kerningX16(uint32_t leftId, uint32_t rightId) { | ||
| return font != nullptr ? binfont_get_kerning_x16(font, leftId, rightId) : 0; | ||
| } | ||
|
|
||
| } // namespace maps | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||
| #pragma once | ||
|
|
||
| #include "../render/LabelLayout.h" | ||
| #include "../ui/Canvas.h" | ||
|
|
||
| #include <cstdint> | ||
|
|
||
| struct BinFont; | ||
| struct PixelBuffer; | ||
|
|
||
| namespace maps { | ||
|
|
||
| /** | ||
| * Text in one of Tactility's cached fonts (<data>/cache/fonts), drawn with the binfont-module. As a LabelFont it gives | ||
| * glyph masks for map labels. | ||
| */ | ||
| class BinFontText final : public Font, public LabelFont { | ||
| public: | ||
| ~BinFontText() override { close(); } | ||
|
|
||
| /** | ||
| * Opens the first font whose file name starts with one of the prefixes. Call it on the I/O task: the binfont-module | ||
| * loads the whole file into PSRAM, so drawing never reads the card. | ||
| */ | ||
| bool open(const char* folder, const char* const* prefixes, int prefixCount); | ||
| /** Opens one font file (read whole into PSRAM, so on the I/O task). */ | ||
| bool openFile(const char* path); | ||
| /** | ||
| * Uses the text font Tactility's UI uses (an LvglFontSize), without a copy. Firmware builds with the fonts inside | ||
| * have no font files on the card, so this comes first. Fails when that font is not a BinFont. | ||
| */ | ||
| bool borrow(int lvglFontSize); | ||
| void close(); | ||
| bool isOpen() const { return font != nullptr; } | ||
|
|
||
| int lineHeight() const override { return font != nullptr ? height : 10; } | ||
| int ascent() const override { return ascentPixels; } | ||
| bool glyph(uint32_t codepoint, LabelGlyph& out, bool withMask) override; | ||
| int kerningX16(uint32_t leftId, uint32_t rightId) override; | ||
| int textWidth(const char* text) override; | ||
| void drawText(Canvas& canvas, int x, int y, const char* text, uint16_t color) override; | ||
|
|
||
| private: | ||
| BinFont* font = nullptr; | ||
| /** Borrowed fonts belong to Tactility and are not closed. */ | ||
| bool owned = false; | ||
| int height = 0; | ||
| int ascentPixels = 0; | ||
| /** The mask of the last glyph asked for with withMask; labels use fonts of at most about 30 px. */ | ||
| static constexpr int MASK_BYTES = 48 * 48; | ||
| uint8_t mask[MASK_BYTES] {}; | ||
| /** The canvas the text was last drawn on, wrapped once as a PixelBuffer. */ | ||
| PixelBuffer* target = nullptr; | ||
| uint16_t* targetPixels = nullptr; | ||
| }; | ||
|
|
||
| } // namespace maps |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.