New app: Spectrum Painter (#988)

* added spectrum painter app
This commit is contained in:
Bernd Herzog 2023-05-14 23:09:43 +02:00 committed by GitHub
parent a27881ecd6
commit b4da86d491
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1457 additions and 21 deletions

View file

@ -29,6 +29,23 @@
#include "ui_record_view.hpp"
#include "ui_spectrum.hpp"
#define BW_OPTIONS \
{ " 8k5", 8500 }, \
{ " 11k", 11000 }, \
{ " 16k", 16000 }, \
{ " 25k", 25000 }, \
{ " 50k", 50000 }, \
{ " 100k", 100000 }, \
{ " 250k", 250000 }, \
{ " 500k", 500000 }, /* Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.*/ \
{ " 600k", 600000 }, /* That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec) */ \
{ " 750k", 750000 }, /* From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size) */ \
{ "1100k", 1100000 }, \
{ "1750k", 1750000 }, \
{ "2000k", 2000000 }, \
{ "2500k", 2500000 }, \
{ "2750k", 2750000 } // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC)
namespace ui {
class CaptureAppView : public View {
@ -88,21 +105,7 @@ private:
{ 5 * 8, 1 * 16 },
5,
{
{ " 8k5", 8500 },
{ " 11k ", 11000 },
{ " 16k ", 16000 },
{ " 25k ", 25000 },
{ " 50k ", 50000 },
{ "100k ", 100000 },
{ "250k ", 250000 },
{ "500k ", 500000 }, // Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.
{ "600k ", 600000 }, // That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec)
{ "750k ", 750000 }, // From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size)
{ "1100k", 1100000 },
{ "1750k", 1750000 },
{ "2000k", 2000000 },
{ "2500k", 2500000 },
{ "2750k", 2750000 } // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC)
BW_OPTIONS
}
};

View file

@ -59,6 +59,7 @@ public:
void focus() override;
std::string title() const override { return "Fileman"; };
void push_dir(const std::filesystem::path& path);
protected:
static constexpr size_t max_filename_length = 64;
@ -83,7 +84,6 @@ protected:
std::filesystem::path get_selected_full_path() const;
const fileman_entry& get_selected_entry() const;
void push_dir(const std::filesystem::path& path);
void pop_dir();
void refresh_list();
void reload_current();

View file

@ -0,0 +1,233 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_spectrum_painter.hpp"
#include "cpld_update.hpp"
#include "bmp.hpp"
#include "baseband_api.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "file.hpp"
#include "portapack_persistent_memory.hpp"
namespace ui {
SpectrumPainterView::SpectrumPainterView(
NavigationView& nav
) : nav_ (nav) {
baseband::run_image(portapack::spi_flash::image_tag_spectrum_painter);
add_children({
&labels,
&tab_view,
&input_image,
&input_text,
&progressbar,
&field_frequency,
&field_rfgain,
&field_rfamp,
&check_loop,
&button_play,
&option_bandwidth,
&field_duration,
&field_pause,
});
Rect view_rect = { 0, 3 * 8, 240, 80 };
input_image.set_parent_rect(view_rect);
input_text.set_parent_rect(view_rect);
field_frequency.set_value(target_frequency());
field_frequency.set_step(5000);
field_frequency.on_change = [this](rf::Frequency f) {
this->on_target_frequency_changed(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(this->target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_target_frequency_changed(f);
this->field_frequency.set_value(f);
};
};
tx_gain = 10;
field_rfgain.set_value(tx_gain); // Initial default value (-12 dB's max ).
field_rfgain.on_change = [this](int32_t v) { // allow initial value change just after opened file.
tx_gain = v;
portapack::transmitter_model.set_tx_gain(tx_gain);
};
field_rfamp.set_value(rf_amp ? 14 : 0); // Initial default value True. (TX RF amp on , +14dB's)
field_rfamp.on_change = [this](int32_t v) { // allow initial value change just after opened file.
rf_amp = (bool)v;
portapack::transmitter_model.set_rf_amp(rf_amp);
};
input_image.on_input_avaliable = [this]() {
image_input_avaliable = true;
};
button_play.on_select = [this](ImageButton&) {
if (tx_active == false) {
tx_mode = tab_view.selected();
if (tx_mode == 0 && image_input_avaliable == false)
return;
//Enable Bias Tee if selected
radio::set_antenna_bias(portapack::get_antenna_bias());
radio::enable({
portapack::receiver_model.tuning_frequency(),
3072000U,
1750000,
rf::Direction::Transmit,
rf_amp,
static_cast<int8_t>(portapack::receiver_model.lna()),
static_cast<int8_t>(portapack::receiver_model.vga())
});
if (portapack::persistent_memory::stealth_mode()){
DisplaySleepMessage message;
EventDispatcher::send_message(message);
}
button_play.set_bitmap(&bitmap_stop);
if (tx_mode == 0) {
tx_current_max_lines = input_image.get_height();
tx_current_width = input_image.get_width();
}
else {
tx_current_max_lines = input_text.get_height();
tx_current_width = input_text.get_width();
}
progressbar.set_max(tx_current_max_lines);
progressbar.set_value(0);
baseband::set_spectrum_painter_config(tx_current_width, tx_current_max_lines, false, option_bandwidth.selected_index_value());
}
else {
stop_tx();
}
};
option_bandwidth.on_change = [this](size_t, ui::OptionsField::value_t value) {
baseband::set_spectrum_painter_config(tx_current_width, tx_current_max_lines, true, value);
};
field_duration.set_value(10);
field_pause.set_value(5);
}
void SpectrumPainterView::start_tx() {
tx_current_line = 0;
tx_active = true;
tx_timestamp_start = chTimeNow();
}
void SpectrumPainterView::stop_tx() {
button_play.set_bitmap(&bitmap_play);
portapack::transmitter_model.disable();
tx_active = false;
tx_current_line = 0;
}
void SpectrumPainterView::frame_sync() {
if (tx_active) {
if (fifo->is_empty()) {
int32_t sequence_duration = (field_duration.value() + ( check_loop.value() ? field_pause.value() : 0)) * 1000;
int32_t sequence_time = tx_time_elapsed() % sequence_duration;
bool is_pausing = sequence_time > field_duration.value() * 1000;
if (is_pausing) {
fifo->in(std::vector<uint8_t>(tx_current_width));
} else {
auto current_time_line = sequence_time * tx_current_max_lines / (field_duration.value() * 1000);
if (tx_current_line > current_time_line && !check_loop.value()) {
fifo->in(std::vector<uint8_t>(tx_current_width));
stop_tx();
return;
}
tx_current_line = current_time_line;
progressbar.set_value(current_time_line);
if (tx_mode == 0) {
std::vector<uint8_t> line = input_image.get_line(current_time_line);
fifo->in(line);
}
else {
std::vector<uint8_t> line = input_text.get_line(current_time_line);
fifo->in(line);
}
}
}
}
}
SpectrumPainterView::~SpectrumPainterView() {
portapack::transmitter_model.disable();
hackrf::cpld::load_sram_no_verify();
baseband::shutdown();
}
void SpectrumPainterView::focus() {
tab_view.focus();
}
void SpectrumPainterView::on_target_frequency_changed(rf::Frequency f) {
set_target_frequency(f);
}
void SpectrumPainterView::set_target_frequency(const rf::Frequency new_value) {
portapack::persistent_memory::set_tuned_frequency(new_value);
}
rf::Frequency SpectrumPainterView::target_frequency() const {
return portapack::persistent_memory::tuned_frequency();
}
void SpectrumPainterView::paint(Painter& painter) {
View::paint(painter);
size_t c;
Point pos = { 0, screen_pos().y() + 8 + footer_location };
for (c = 0; c < 20; c++) {
painter.draw_bitmap(
pos,
bitmap_stripes,
ui::Color(191, 191, 0),
ui::Color::black()
);
if (c != 9)
pos += { 24, 0 };
else
pos = { 0, screen_pos().y() + 8 + footer_location + 32 + 8 };
}
}
}

View file

@ -0,0 +1,177 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_tabview.hpp"
#include "capture_app.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "message.hpp"
#include "ui_spectrum_painter_image.hpp"
#include "ui_spectrum_painter_text.hpp"
namespace ui {
class SpectrumPainterView : public View {
public:
SpectrumPainterView(NavigationView& nav);
~SpectrumPainterView();
SpectrumPainterView(const SpectrumPainterView&) = delete;
SpectrumPainterView(SpectrumPainterView&&) = delete;
SpectrumPainterView& operator=(const SpectrumPainterView&) = delete;
SpectrumPainterView& operator=(SpectrumPainterView&&) = delete;
void focus() override;
void paint(Painter& painter) override;
std::string title() const override { return "Spec.Painter"; };
private:
void on_target_frequency_changed(rf::Frequency f);
void set_target_frequency(const rf::Frequency new_value);
rf::Frequency target_frequency() const;
NavigationView& nav_;
bool image_input_avaliable { false };
bool tx_active { false };
uint32_t tx_mode { 0 };
uint16_t tx_current_line { 0 };
uint16_t tx_current_max_lines { 0 };
uint16_t tx_current_width { 0 };
systime_t tx_timestamp_start { 0 };
inline uint32_t tx_time_elapsed() {
auto now = chTimeNow();
return now - tx_timestamp_start;
}
int32_t tx_gain { 47 };
bool rf_amp { false };
SpectrumInputImageView input_image { nav_ };
SpectrumInputTextView input_text { nav_ };
std::array<View*, 2> input_views { { &input_image, &input_text } };
TabView tab_view {
{ "Image", Color::white(), input_views[0] },
{ "Text", Color::white(), input_views[1] }
};
static constexpr int32_t footer_location = 15 * 16 + 8;
ProgressBar progressbar {
{ 4, footer_location - 16, 240-8, 16 }
};
Labels labels {
{ { 10 * 8, footer_location + 1 * 16 }, "GAIN A:", Color::light_grey() },
{ { 1 * 8, footer_location + 2 * 16 }, "BW: Du: P:", Color::light_grey() },
};
FrequencyField field_frequency {
{ 0 * 8, footer_location + 1 * 16 },
};
NumberField field_rfgain {
{ 14 * 8, footer_location + 1 * 16 },
2,
{ 0, 47 },
1,
' '
};
NumberField field_rfamp {
{ 19 * 8, footer_location + 1 * 16 },
2,
{ 0, 14 },
14,
' '
};
Checkbox check_loop {
{ 21 * 8, footer_location + 1 * 16 },
4,
"Loop",
true
};
ImageButton button_play {
{ 28 * 8, footer_location + 1 * 16, 2 * 8, 1 * 16 },
&bitmap_play,
Color::green(),
Color::black()
};
OptionsField option_bandwidth {
{ 4 * 8, footer_location + 2 * 16 },
5,
{
BW_OPTIONS
}
};
NumberField field_duration {
{ 13 * 8, footer_location + 2 * 16 },
3,
{ 1, 999 },
1,
' '
};
NumberField field_pause {
{ 19 * 8, footer_location + 2 * 16 },
2,
{ 0, 99 },
1,
' '
};
SpectrumPainterFIFO* fifo { nullptr };
void start_tx();
void frame_sync();
void stop_tx();
MessageHandlerRegistration message_handler_fifo_signal {
Message::ID::SpectrumPainterBufferResponseConfigure,
[this](const Message* const p) {
const auto message = static_cast<const SpectrumPainterBufferConfigureResponseMessage*>(p);
this->fifo = message->fifo;
this->start_tx();
}
};
MessageHandlerRegistration message_handler_sample {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->frame_sync();
}
};
};
}

View file

@ -0,0 +1,265 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_spectrum_painter_image.hpp"
#include "cpld_update.hpp"
#include "bmp.hpp"
#include "baseband_api.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "file.hpp"
#include "portapack_persistent_memory.hpp"
namespace ui {
SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) {
hidden(true);
add_children({
&button_load_image
});
button_load_image.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".bmp");
constexpr auto data_directory = u"SPECTRUM";
if (std::filesystem::is_directory(data_directory) == false) {
if (make_new_directory(data_directory).ok())
open_view->push_dir(data_directory);
}
else
open_view->push_dir(data_directory);
open_view->on_changed = [this](std::filesystem::path new_file_path) {
this->file = new_file_path.string();
painted = false;
this->set_dirty();
this->on_input_avaliable();
};
};
}
SpectrumInputImageView::~SpectrumInputImageView() {
}
void SpectrumInputImageView::focus() {
button_load_image.focus();
}
bool SpectrumInputImageView::drawBMP_scaled(const ui::Rect r, const std::string file) {
File bmpimage;
size_t file_pos = 0;
uint16_t pointer = 0;
int16_t px = 0, py, zoom_factor = 0;
bmp_header_t bmp_header;
char buffer[257];
ui::Color line_buffer[240];
auto result = bmpimage.open(file);
if(result.is_valid())
return false;
bmpimage.seek(file_pos);
auto read_size = bmpimage.read(&bmp_header, sizeof(bmp_header));
if (!((bmp_header.signature == 0x4D42) && // "BM" Signature
(bmp_header.planes == 1) && // Seems always to be 1
(bmp_header.compression == 0 || bmp_header.compression == 3 ))) { // No compression
return false;
}
switch(bmp_header.bpp) {
case 16:
file_pos = 0x36;
memset(buffer, 0, 16);
bmpimage.read(buffer, 16);
if(buffer[1] == 0x7C)
type = 3; // A1R5G5B5
else
type = 0; // R5G6B5
break;
case 24:
type = 1;
break;
case 32:
default:
type = 2;
break;
}
width = bmp_header.width;
height = bmp_header.height;
data_start = file_pos = bmp_header.image_data;
while (r.width() < (width >> zoom_factor) || r.height() < (height >> zoom_factor)) {
zoom_factor++;
}
py = height + 16;
while(1) {
while(px < width) {
bmpimage.seek(file_pos);
memset(buffer, 0, 257);
read_size = bmpimage.read(buffer, 256);
if (read_size.is_error())
return false; // Read error
pointer = 0;
while(pointer < 256) {
if(pointer + 4 > 256)
break;
switch(type) {
case 0: // R5G6B5
if ((((1 << zoom_factor) - 1) & px) == 0x00)
line_buffer[px >> zoom_factor] = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9);
pointer += 2;
file_pos += 2;
break;
case 3: // A1R5G5B5
if ((((1 << zoom_factor) - 1) & px) == 0x00)
line_buffer[px >> zoom_factor] = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8));
pointer += 2;
file_pos += 2;
break;
case 1: // 24
default:
if ((((1 << zoom_factor) - 1) & px) == 0x00)
line_buffer[px >> zoom_factor] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
pointer += 3;
file_pos += 3;
break;
case 2: // 32
if ((((1 << zoom_factor) - 1) & px) == 0x00)
line_buffer[px >> zoom_factor] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
pointer += 4;
file_pos += 4;
break;
}
px++;
if(px >= width) {
break;
}
}
if(read_size.value() != 256)
break;
}
if ((((1 << zoom_factor) - 1) & py) == 0x00)
portapack::display.render_line({ r.left(), r.top() + (py >> zoom_factor) }, px >> zoom_factor, line_buffer);
px = 0;
py--;
if(read_size.value() < 256 || py < 0)
break;
}
return true;
}
uint16_t SpectrumInputImageView::get_width(){
return this->width;
}
uint16_t SpectrumInputImageView::get_height(){
return this->height;
}
std::vector<uint8_t> SpectrumInputImageView::get_line(uint16_t y) {
File bmpimage;
bmpimage.open(this->file);
//seek to line
uint32_t line_size = width * (type == 2 ? 4 : (type == 1 ? 3 : 2));
uint32_t line_offset = y * line_size;
bmpimage.seek(data_start + line_offset);
// allocate memory and read
auto buffer = new uint8_t[line_size];
auto bytes_read = bmpimage.read(buffer, line_size);
// greyscale
auto grey_buffer = new uint8_t[width];
int pointer = 0;
for (uint16_t px = 0; px < width; px++) {
ui::Color color;
switch(type) {
case 0: // R5G6B5
pointer = px * 2;
color = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9);
break;
case 3: // A1R5G5B5
pointer = px * 2;
color = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8));
break;
case 1: // 24
default:
pointer = px * 3;
color = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
break;
case 2: // 32
pointer = px * 4;
color = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
break;
}
grey_buffer[px] = color.to_greyscale();
}
delete buffer;
std::vector<uint8_t> values(width);
for(int i = 0; i < width; i++) {
values[i] = grey_buffer[i];
}
delete grey_buffer;
return values;
}
void SpectrumInputImageView::paint(Painter& painter) {
painter.fill_rectangle(
{{0, 40}, {240, 204}},
style().background
);
if (!painted) {
// This is very slow for big pictures. Do only once.
this->drawBMP_scaled({{ 0, 40 }, {240, 160}}, this->file);
painted = true;
}
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_tabview.hpp"
#include "ui_transmitter.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "message.hpp"
namespace ui {
class SpectrumInputImageView : public View {
public:
SpectrumInputImageView(NavigationView& nav);
~SpectrumInputImageView();
void focus() override;
void paint(Painter&) override;
uint16_t get_width();
uint16_t get_height();
std::vector<uint8_t> get_line(uint16_t);
std::function<void()> on_input_avaliable { };
private:
bool painted {false};
std::string file {""};
uint16_t width {0};
uint16_t height {0};
uint8_t type {0};
uint32_t data_start {0};
Button button_load_image {
{ 0 * 8, 11 * 16 - 4, 30 * 8, 28 },
"Load Image ..."
};
bool drawBMP_scaled(const ui::Rect r, const std::string file);
};
}

View file

@ -0,0 +1,111 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_spectrum_painter_text.hpp"
#include "cpld_update.hpp"
#include "bmp.hpp"
#include "baseband_api.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "file.hpp"
#include "portapack_persistent_memory.hpp"
#include "ui_font_fixed_8x16.hpp"
namespace ui {
SpectrumInputTextView::SpectrumInputTextView(NavigationView& nav) {
hidden(true);
add_children({
&text_message_0,
&text_message_1,
&text_message_2,
&text_message_3,
&text_message_4,
&text_message_5,
&text_message_6,
&text_message_7,
&text_message_8,
&text_message_9,
&button_message
});
button_message.on_select = [this, &nav](Button&) {
this->on_set_text(nav);
};
}
SpectrumInputTextView::~SpectrumInputTextView() {
}
void SpectrumInputTextView::on_set_text(NavigationView& nav) {
text_prompt(nav, buffer, 300);
}
void SpectrumInputTextView::focus() {
button_message.focus();
}
void SpectrumInputTextView::paint(Painter& painter) {
message = buffer;
for (uint32_t i = 0 ; i < text_message.size(); i++) {
if (message.length() > i * 30)
text_message[i]->set(message.substr(i * 30, 30));
else
text_message[i]->set("");
}
painter.fill_rectangle(
{{0, 40}, {240, 204}},
style().background
);
}
constexpr uint32_t pixel_repeat = 32;
uint16_t SpectrumInputTextView::get_width(){
return 16 * pixel_repeat;
}
uint16_t SpectrumInputTextView::get_height(){
return this->message.length() * 8;
}
std::vector<uint8_t> SpectrumInputTextView::get_line(uint16_t y) {
auto character_position = y / 8;
auto character = this->message[character_position];
auto glyph_data = ui::font::fixed_8x16.glyph(character).pixels();
auto line_in_character = y % 8;
std::vector<uint8_t> data(16 * pixel_repeat);
for (uint32_t index = 0; index < 16; index++) {
auto glyph_byte = index;
auto glyph_bit = line_in_character;
uint8_t glyph_pixel = (glyph_data[glyph_byte] & (1 << glyph_bit)) >> glyph_bit;
for (uint32_t j = 0; j < pixel_repeat; j++)
data[index*pixel_repeat + j] = glyph_pixel * 255;
}
return data;
}
}

View file

@ -0,0 +1,124 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_tabview.hpp"
#include "ui_transmitter.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "message.hpp"
namespace ui {
class SpectrumInputTextView : public View {
public:
SpectrumInputTextView(NavigationView& nav);
~SpectrumInputTextView();
void focus() override;
void paint(Painter&) override;
uint16_t get_width();
uint16_t get_height();
std::vector<uint8_t> get_line(uint16_t);
private:
std::string buffer { "PORTAPACK" };
std::string message { };
void on_set_text(NavigationView& nav);
Text text_message_0 {
{ 0 * 8, 0 * 16, 30 * 8, 16 },
""
};
Text text_message_1 {
{ 0 * 8, 1 * 16, 30 * 8, 16 },
""
};
Text text_message_2 {
{ 0 * 8, 2 * 16, 30 * 8, 16 },
""
};
Text text_message_3 {
{ 0 * 8, 3 * 16, 30 * 8, 16 },
""
};
Text text_message_4 {
{ 0 * 8, 4 * 16, 30 * 8, 16 },
""
};
Text text_message_5 {
{ 0 * 8, 5 * 16, 30 * 8, 16 },
""
};
Text text_message_6 {
{ 0 * 8, 6 * 16, 30 * 8, 16 },
""
};
Text text_message_7 {
{ 0 * 8, 7 * 16, 30 * 8, 16 },
""
};
Text text_message_8 {
{ 0 * 8, 8 * 16, 30 * 8, 16 },
""
};
Text text_message_9 {
{ 0 * 8, 9 * 16, 30 * 8, 16 },
""
};
std::array<Text*, 10> text_message { {
&text_message_0,
&text_message_1,
&text_message_2,
&text_message_3,
&text_message_4,
&text_message_5,
&text_message_6,
&text_message_7,
&text_message_8,
&text_message_9,
} };
Button button_message {
{ 0 * 8, 11 * 16 - 4, 30 * 8, 28 },
"Set message"
};
};
}