From afa34d83b2e753e301d21c6d9dd377ca4ff984a6 Mon Sep 17 00:00:00 2001 From: Mark Thompson <129641948+NotherNgineer@users.noreply.github.com> Date: Thu, 4 Jan 2024 00:32:44 -0600 Subject: [PATCH] Added Zoom-Out capability to Geomap (#1710) --- firmware/application/ui/ui_geomap.cpp | 106 +++++++++++++++++--------- firmware/application/ui/ui_geomap.hpp | 5 +- 2 files changed, 76 insertions(+), 35 deletions(-) diff --git a/firmware/application/ui/ui_geomap.cpp b/firmware/application/ui/ui_geomap.cpp index e3a6d420..edf3d758 100644 --- a/firmware/application/ui/ui_geomap.cpp +++ b/firmware/application/ui/ui_geomap.cpp @@ -145,15 +145,28 @@ GeoMap::GeoMap( } bool GeoMap::on_encoder(const EncoderEvent delta) { - if ((delta > 0) && (map_zoom < 5)) { - map_zoom++; + // Valid map_zoom values are -2 to -MAX_MAP_ZOOM_OUT, and +1 to +MAX_MAP_ZOOM_IN (values of 0 and -1 are not permitted) + if (delta > 0) { + if (map_zoom < MAX_MAP_ZOOM_IN) { + if (map_zoom == -2) { + map_zoom = 1; + } else { + map_zoom++; - // Ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function - if (geomap_rect_width % map_zoom != 0) { - map_zoom--; + // When zooming in, ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function + if ((map_zoom > 1) && (geomap_rect_width % map_zoom != 0)) { + map_zoom--; + } + } + } + } else if (delta < 0) { + if (map_zoom > -MAX_MAP_ZOOM_OUT) { + if (map_zoom == 1) { + map_zoom = -2; + } else { + map_zoom--; + } } - } else if ((delta < 0) && (map_zoom > 1)) { - map_zoom--; } else { return false; } @@ -164,17 +177,31 @@ bool GeoMap::on_encoder(const EncoderEvent delta) { return true; } -void GeoMap::map_zoom_line(ui::Color* buffer) { - if (map_zoom <= 1) { - return; - } +void GeoMap::map_read_line(ui::Color* buffer, uint16_t pixels) { + if (map_zoom == 1) { + map_file.read(buffer, pixels << 1); + } else if (map_zoom > 1) { + map_file.read(buffer, (pixels / map_zoom) << 1); - // As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel; - // For 240 width, than means no check is needed for map_zoom values up to 6 - for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) { - for (int j = 0; j < map_zoom; j++) { - buffer[(i * map_zoom) + j] = buffer[i]; + // Zoom in: Expand each pixel to "map_zoom" number of pixels. + // Future TODO: Add dithering to smooth out the pixelation. + // As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel; + // For 240 width, than means no check is needed for map_zoom values up to 6 + for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) { + for (int j = 0; j < map_zoom; j++) { + buffer[(i * map_zoom) + j] = buffer[i]; + } } + } else { + ui::Color* zoom_out_buffer = new ui::Color[(pixels * (-map_zoom))]; + map_file.read(zoom_out_buffer, (pixels * (-map_zoom)) << 1); + + // Zoom out: Collapse each group of "-map_zoom" pixels into one pixel. + // Future TODO: Average each group of pixels (in both X & Y directions if possible). + for (int i = 0; i < geomap_rect_width; i++) { + buffer[i] = zoom_out_buffer[i * (-map_zoom)]; + } + delete zoom_out_buffer; } } @@ -187,9 +214,12 @@ void GeoMap::draw_markers(Painter& painter) { int x = (map_width * (item.lon + 180) / 360) - x_pos; int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI - if (map_zoom != 1) { + if (map_zoom > 1) { x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 2); y = ((y - (r.height() / 2)) * map_zoom) + (r.height() / 2); + } else if (map_zoom < 0) { + x = ((x - (r.width() / 2)) / (-map_zoom)) + (r.width() / 2); + y = ((y - (r.height() / 2)) / (-map_zoom)) + (r.height() / 2); } if ((x >= 0) && (x < r.width()) && @@ -208,31 +238,39 @@ void GeoMap::draw_markers(Painter& painter) { } void GeoMap::paint(Painter& painter) { - uint16_t line, j; - uint32_t zoom_seek_x, zoom_seek_y; - std::array map_line_buffer; const auto r = screen_rect(); + std::array map_line_buffer; // Ony redraw map if it moved by at least 1 pixel // or the markers list was updated int x_diff = abs(x_pos - prev_x_pos); int y_diff = abs(y_pos - prev_y_pos); - if (markerListUpdated || (x_diff >= map_zoom * 3) || (y_diff >= map_zoom * 3)) { - if (map_zoom == 1) { - zoom_seek_x = zoom_seek_y = 0; - } else { - zoom_seek_x = (r.width() - (r.width() / map_zoom)) / 2; - zoom_seek_y = (r.height() - (r.height() / map_zoom)) / 2; + + if (markerListUpdated || (x_diff >= 3) || (y_diff >= 3)) { + int32_t zoom_seek_x = x_pos; + int32_t zoom_seek_y = y_pos; + + // Adjust starting corner position of map per zoom setting + if (map_zoom > 1) { + zoom_seek_x += (r.width() - (r.width() / map_zoom)) / 2; + zoom_seek_y += (r.height() - (r.height() / map_zoom)) / 2; + } else if (map_zoom < 0) { + zoom_seek_x += (r.width() - (r.width() * (-map_zoom))) / 2; + zoom_seek_y += (r.height() - (r.height() * (-map_zoom))) / 2; } - for (line = 0; line < (r.height() / map_zoom); line++) { - map_file.seek(4 + ((x_pos + zoom_seek_x + (map_width * (y_pos + line + zoom_seek_y))) << 1)); - map_file.read(map_line_buffer.data(), (r.width() / map_zoom) << 1); - map_zoom_line(map_line_buffer.data()); - for (j = 0; j < map_zoom; j++) { - display.draw_pixels({0, r.top() + (line * map_zoom) + j, r.width(), 1}, map_line_buffer); + // Read from map file and display to zoomed scale + int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom; + for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) { + uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom)); + map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1)); + map_read_line(map_line_buffer.data(), r.width()); + + for (uint16_t j = 0; j < duplicate_lines; j++) { + display.draw_pixels({0, r.top() + (line * duplicate_lines) + j, r.width(), 1}, map_line_buffer); } } + prev_x_pos = x_pos; prev_y_pos = y_pos; @@ -323,8 +361,8 @@ bool GeoMap::manual_panning() { } void GeoMap::draw_scale(Painter& painter) { - uint16_t km = 100; - uint16_t scale_width = km * pixels_per_km * map_zoom; + uint16_t km = 800; + uint16_t scale_width = (map_zoom > 0) ? km * pixels_per_km * map_zoom : km * pixels_per_km / (-map_zoom); while (scale_width > screen_width / 2) { scale_width /= 2; diff --git a/firmware/application/ui/ui_geomap.hpp b/firmware/application/ui/ui_geomap.hpp index 04e8bfef..9a6833e1 100644 --- a/firmware/application/ui/ui_geomap.hpp +++ b/firmware/application/ui/ui_geomap.hpp @@ -29,6 +29,9 @@ #include "portapack.hpp" +#define MAX_MAP_ZOOM_IN 5 +#define MAX_MAP_ZOOM_OUT 10 + namespace ui { enum GeoMapMode { @@ -187,7 +190,7 @@ class GeoMap : public Widget { void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color); void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black()); void draw_markers(Painter& painter); - void map_zoom_line(ui::Color* buffer); + void map_read_line(ui::Color* buffer, uint16_t pixels); bool manual_panning_{false}; GeoMapMode mode_{};