Added PWM RSSI output for NBFM and WFM

This commit is contained in:
furrtek 2016-07-28 05:25:33 +02:00
parent 1beac3bdbd
commit 1d697d2201
22 changed files with 274 additions and 118 deletions

View File

@ -100,6 +100,15 @@ void set_afsk_data(const char message_data[], const uint32_t afsk_samples_per_bi
send_message(&message);
}
void set_pwmrssi(int32_t avg, bool enabled) {
const PWMRSSIConfigureMessage message {
enabled,
1000, // 1kHz
avg
};
send_message(&message);
}
static bool baseband_image_running = false;
void run_image(const portapack::spi_flash::image_tag_t image_tag) {

View File

@ -53,6 +53,7 @@ struct WFMConfig {
};
void set_xylos_data(const char ccir_message[]);
void set_pwmrssi(int32_t avg, bool enabled);
void set_afsk_data(const char message_data[], const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark,
const uint32_t afsk_phase_inc_space, const uint8_t afsk_repeat, const uint32_t afsk_bw,
const bool afsk_alt_format);

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
@ -26,6 +27,48 @@
namespace ui {
/*
00000000 00000000 00000000
11110001 11100111 10111110
10001010 00001000 00001000
10001010 00001000 00001000
11110001 11000111 00001000
10010000 00100000 10001000
10001000 00100000 10001000
10001011 11001111 00111110
00000000 00000000 00000000
11111100 01111000 01111000
10000100 01001000 01001000
10000100 01001000 01001000
10000100 01001000 01001000
10000100 01001000 01001000
10000100 01001000 01001000
10000111 11001111 11001110
*/
static constexpr uint8_t bitmap_rssipwm_data[] = {
0x00, 0x00, 0x00,
0x8F, 0xE7, 0x7D,
0x51, 0x10, 0x10,
0x51, 0x10, 0x10,
0x8F, 0xE3, 0x10,
0x09, 0x04, 0x11,
0x11, 0x04, 0x11,
0xD1, 0xF3, 0x7C,
0x00, 0x00, 0x00,
0x3F, 0x1E, 0x1E,
0x21, 0x12, 0x12,
0x21, 0x12, 0x12,
0x21, 0x12, 0x12,
0x21, 0x12, 0x12,
0x21, 0x12, 0x12,
0xE1, 0xF3, 0x73
};
static constexpr Bitmap bitmap_rssipwm {
{ 24, 16 }, bitmap_rssipwm_data
};
static constexpr uint8_t bitmap_keyboard_data[] = {
0x00, 0x00,
0x00, 0x00,

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*

View File

@ -21,22 +21,12 @@
*/
#include "ui_lcr.hpp"
#include "ui_receiver.hpp"
#include "ui_afsksetup.hpp"
#include "ui_debug.hpp"
#include "ch.h"
#include "ff.h"
#include "baseband_api.hpp"
#include "hackrf_gpio.hpp"
#include "portapack.hpp"
#include "event_m0.hpp"
#include "string_format.hpp"
#include "hackrf_hal.hpp"
#include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
@ -231,6 +221,7 @@ void LCRView::on_txdone(int n) {
char str[16];
if (n > 0) {
// Repeating...
repeat_index = n + 1;
if (tx_mode == SCAN) {
scan_progress++;
@ -239,9 +230,11 @@ void LCRView::on_txdone(int n) {
update_progress();
}
} else {
// Done transmitting
if ((tx_mode == SCAN) && (scan_index < (LCR_SCAN_COUNT - 1))) {
transmitter_model.disable();
if (abort_scan) {
// Kill scan process
strcpy(str, "Abort @");
strcat(str, rgsb);
text_status.set(str);
@ -415,16 +408,17 @@ LCRView::LCRView(NavigationView& nav) {
};
button_clear.on_select = [this, &nav](Button&) {
uint8_t n;
if (tx_mode == IDLE) {
memset(litteral, 0, 5 * 8);
options_ec.set_selected_index(0);
checkbox_am_a.set_value(true);
checkbox_am_b.set_value(true);
checkbox_am_c.set_value(true);
checkbox_am_d.set_value(true);
checkbox_am_e.set_value(true);
options_ec.set_selected_index(0); // Auto
for (n = 0; n < 5; n++)
checkboxes[n].set_value(true);
set_dirty();
start_tx(false);
} else if (tx_mode == SCAN) {
abort_scan = true;
}
};
}

View File

@ -22,16 +22,8 @@
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "ui_textentry.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "clock_manager.hpp"
#include "message.hpp"
#include "rf_path.hpp"
#include "max2837.hpp"
#include "volume.hpp"
#include "transmitter_model.hpp"
namespace ui {
@ -149,36 +141,6 @@ private:
"TX setup"
};
Checkbox checkbox_am_a {
{ 16, 64 },
0,
""
};
Checkbox checkbox_am_b {
{ 16, 96 },
0,
""
};
Checkbox checkbox_am_c {
{ 16, 128 },
0,
""
};
Checkbox checkbox_am_d {
{ 16, 160 },
0,
""
};
Checkbox checkbox_am_e {
{ 16, 192 },
0,
""
};
Button button_lcrdebug {
{ 168, 216, 56, 24 },
"DEBUG"

View File

@ -238,14 +238,15 @@ ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
/* SystemMenuView ********************************************************/
SystemMenuView::SystemMenuView(NavigationView& nav) {
add_items<12>({ {
add_items<11>({ {
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(false); } },
{ "Receiver RX", ui::Color::cyan(), [&nav](){ nav.push<ReceiverMenuView>(); } },
{ "Capture RX", ui::Color::orange(), [&nav](){ nav.push<CaptureAppView>(); } },
{ "Close Call RX", ui::Color::cyan(), [&nav](){ nav.push<CloseCallView>(); } },
{ "Pokemon GO Away TX", ui::Color::blue(), [&nav](){ nav.push<JammerView>(); } },
//{ "Pokemon GO Away TX", ui::Color::blue(), [&nav](){ nav.push<JammerView>(); } },
//{ "Soundboard TX", ui::Color::yellow(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, SoundBoard); } },
//{ "Audio TX", ui::Color::yellow(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, AudioTX); } },
{ "Frequency manager", ui::Color::white(), [&nav](){ nav.push<FreqManView>(); } },
//{ "Frequency manager", ui::Color::white(), [&nav](){ nav.push<FreqManView>(); } },
//{ "EPAR TX", ui::Color::green(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, EPAR); } },
{ "Xylos TX", ui::Color::green(), [&nav](){ nav.push<XylosView>(); } },
{ "TEDI/LCR TX", ui::Color::orange(), [&nav](){ nav.push<LCRView>(); } },

View File

@ -22,6 +22,8 @@
#include "ui_record_view.hpp"
#include "portapack.hpp"
#include "message.hpp"
#include "portapack_shared_memory.hpp"
using namespace portapack;
#include "file.hpp"
@ -176,6 +178,7 @@ RecordView::RecordView(
{
add_children({ {
&rect_background,
&button_pwmrssi,
&button_record,
&text_record_filename,
&text_record_dropped,
@ -183,6 +186,10 @@ RecordView::RecordView(
} });
rect_background.set_parent_rect({ { 0, 0 }, size() });
button_pwmrssi.on_select = [this](ImageButton&) {
this->toggle_pwmrssi();
};
button_record.on_select = [this](ImageButton&) {
this->toggle();
@ -228,6 +235,24 @@ void RecordView::toggle() {
}
}
void RecordView::toggle_pwmrssi() {
pwmrssi_enabled = !pwmrssi_enabled;
// Send to RSSI widget
const PWMRSSIConfigureMessage message {
pwmrssi_enabled,
1000,
0
};
shared_memory.application_queue.push(message);
if( !pwmrssi_enabled ) {
button_pwmrssi.set_foreground(Color::orange());
} else {
button_pwmrssi.set_foreground(Color::green());
}
}
void RecordView::start() {
stop();
@ -342,6 +367,10 @@ void RecordView::update_status_display() {
const auto s = to_string_dec_uint(dropped_percent, 2, ' ') + "\%";
text_record_dropped.set(s);
}
if (pwmrssi_enabled) {
button_pwmrssi.invert_colors();
}
if( sampling_rate ) {
const auto space_info = std::filesystem::space("");

View File

@ -64,6 +64,7 @@ public:
private:
void toggle();
void toggle_pwmrssi();
Optional<File::Error> write_metadata_file(const std::string& filename);
void on_tick_second();
@ -72,6 +73,7 @@ private:
void handle_capture_thread_done(const File::Error error);
void handle_error(const File::Error error);
bool pwmrssi_enabled = false;
const std::string filename_stem_pattern;
const FileType file_type;
const size_t write_size;
@ -82,16 +84,23 @@ private:
Rectangle rect_background {
Color::black()
};
ImageButton button_pwmrssi {
{ 2, 0 * 16, 3 * 8, 1 * 16 },
&bitmap_rssipwm,
Color::orange(),
Color::black()
};
ImageButton button_record {
{ 0 * 8, 0 * 16, 2 * 8, 1 * 16 },
{ 4 * 8, 0 * 16, 2 * 8, 1 * 16 },
&bitmap_record,
Color::red(),
Color::black()
};
Text text_record_filename {
{ 3 * 8, 0 * 16, 8 * 8, 16 },
{ 7 * 8, 0 * 16, 8 * 8, 16 },
"",
};

View File

@ -21,6 +21,7 @@
#include "ui_rssi.hpp"
#include "baseband_api.hpp"
#include "utility.hpp"
#include <algorithm>
@ -88,6 +89,17 @@ void RSSI::paint(Painter& painter) {
r4,
Color::black()
);
if (pwmrssi_enabled) {
const range_t<int> pwmrssi_avg_range { 0, 96 };
const auto pwmrssi_avg = pwmrssi_avg_range.clip((avg_ - raw_min) * 96 / raw_delta);
baseband::set_pwmrssi(pwmrssi_avg, true);
}
}
void RSSI::set_pwmrssi(bool enabled) {
pwmrssi_enabled = enabled;
if (!enabled) baseband::set_pwmrssi(0, false);
}
void RSSI::on_statistics_update(const RSSIStatistics& statistics) {

View File

@ -46,11 +46,13 @@ public:
}
void paint(Painter& painter) override;
private:
int32_t min_;
int32_t avg_;
int32_t max_;
bool pwmrssi_enabled = false;
MessageHandlerRegistration message_handler_stats {
Message::ID::RSSIStatistics,
@ -58,8 +60,17 @@ private:
this->on_statistics_update(static_cast<const RSSIStatisticsMessage*>(p)->statistics);
}
};
MessageHandlerRegistration message_handler_pwmrssi {
Message::ID::PWMRSSIConfigure,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const PWMRSSIConfigureMessage*>(p);
this->set_pwmrssi(message.enabled);
}
};
void on_statistics_update(const RSSIStatistics& statistics);
void set_pwmrssi(bool enabled);
};
}

View File

@ -21,20 +21,11 @@
*/
#include "ui_xylos.hpp"
#include "ch.h"
#include "hackrf_hal.hpp"
#include "event_m0.hpp"
#include "audio.hpp"
#include "ui_alphanum.hpp"
#include "ff.h"
#include "hackrf_gpio.hpp"
#include "portapack.hpp"
#include "baseband_api.hpp"
#include "hackrf_hal.hpp"
#include "portapack_shared_memory.hpp"
//#include "audio.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>

View File

@ -22,9 +22,7 @@
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_navigation.hpp"
#include "ui_menu.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "bulb_on_bmp.hpp"
@ -32,8 +30,7 @@
#include "bulb_ignore_bmp.hpp"
#include "message.hpp"
#include "max2837.hpp"
#include "volume.hpp"
//#include "volume.hpp"
#include "transmitter_model.hpp"
//#include "receiver_model.hpp"
#include "portapack.hpp"

View File

@ -32,7 +32,7 @@ void NarrowbandFMAudio::execute(const buffer_c8_t& buffer) {
if( !configured ) {
return;
}
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
@ -40,8 +40,23 @@ void NarrowbandFMAudio::execute(const buffer_c8_t& buffer) {
feed_channel_stats(channel_out);
channel_spectrum.feed(channel_out, channel_filter_pass_f, channel_filter_stop_f);
auto audio = demod.execute(channel_out, audio_buffer);
audio_output.write(audio);
if ( !pwmrssi_enabled ) {
auto audio = demod.execute(channel_out, audio_buffer);
audio_output.write(audio);
} else {
for (c = 0; c < 32; c++) {
if (synth_acc < pwmrssi_avg)
pwmrssi_audio_buffer.p[c] = 32767;
else
pwmrssi_audio_buffer.p[c] = -32768;
if (synth_acc < 30) // 24kHz / 30 = 800Hz (TODO: use pwmrssi_freq !)
synth_acc++;
else
synth_acc = 0;
}
audio_output.write(pwmrssi_audio_buffer);
}
}
void NarrowbandFMAudio::on_message(const Message* const message) {
@ -58,6 +73,10 @@ void NarrowbandFMAudio::on_message(const Message* const message) {
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
case Message::ID::PWMRSSIConfigure:
pwmrssi_config(*reinterpret_cast<const PWMRSSIConfigureMessage*>(message));
break;
default:
break;
@ -83,11 +102,19 @@ void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs;
channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs;
channel_spectrum.set_decimation_factor(std::floor(channel_filter_output_fs / (channel_filter_pass_f + channel_filter_stop_f)));
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, 0.8f);
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config); // , 0.8f
synth_acc = 0;
configured = true;
}
void NarrowbandFMAudio::pwmrssi_config(const PWMRSSIConfigureMessage& message) {
pwmrssi_enabled = message.enabled;
pwmrssi_freq = message.freq;
pwmrssi_avg = message.avg / 3;
}
void NarrowbandFMAudio::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
audio_output.set_stream(std::make_unique<StreamInput>(message.config));

View File

@ -56,6 +56,12 @@ private:
audio.data(),
audio.size()
};
std::array<int16_t, 32> pwm;
const buffer_s16_t pwmrssi_audio_buffer {
(int16_t*)pwm.data(),
sizeof(pwm) / sizeof(int16_t)
};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0;
dsp::decimate::FIRC16xR16x32Decim8 decim_1;
@ -68,8 +74,14 @@ private:
AudioOutput audio_output;
SpectrumCollector channel_spectrum;
unsigned int c, synth_acc = 0;
bool pwmrssi_enabled = false;
uint32_t pwmrssi_freq;
uint32_t pwmrssi_avg;
bool configured { false };
void pwmrssi_config(const PWMRSSIConfigureMessage& message);
void configure(const NBFMConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
};

View File

@ -31,7 +31,7 @@ void WidebandFMAudio::execute(const buffer_c8_t& buffer) {
if( !configured ) {
return;
}
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto channel = decim_1.execute(decim_0_out, dst_buffer);
@ -44,32 +44,49 @@ void WidebandFMAudio::execute(const buffer_c8_t& buffer) {
channel_spectrum.feed(channel, channel_filter_pass_f, channel_filter_stop_f);
}
/* 384kHz complex<int16_t>[256]
* -> FM demodulation
* -> 384kHz int16_t[256] */
/* TODO: To improve adjacent channel rejection, implement complex channel filter:
* pass < +/- 100kHz, stop > +/- 200kHz
*/
if ( !pwmrssi_enabled ) {
/* 384kHz complex<int16_t>[256]
* -> FM demodulation
* -> 384kHz int16_t[256] */
/* TODO: To improve adjacent channel rejection, implement complex channel filter:
* pass < +/- 100kHz, stop > +/- 200kHz
*/
auto audio_oversampled = demod.execute(channel, work_audio_buffer);
auto audio_oversampled = demod.execute(channel, work_audio_buffer);
/* 384kHz int16_t[256]
* -> 4th order CIC decimation by 2, gain of 1
* -> 192kHz int16_t[128] */
auto audio_4fs = audio_dec_1.execute(audio_oversampled, work_audio_buffer);
/* 384kHz int16_t[256]
* -> 4th order CIC decimation by 2, gain of 1
* -> 192kHz int16_t[128] */
auto audio_4fs = audio_dec_1.execute(audio_oversampled, work_audio_buffer);
/* 192kHz int16_t[128]
* -> 4th order CIC decimation by 2, gain of 1
* -> 96kHz int16_t[64] */
auto audio_2fs = audio_dec_2.execute(audio_4fs, work_audio_buffer);
/* 192kHz int16_t[128]
* -> 4th order CIC decimation by 2, gain of 1
* -> 96kHz int16_t[64] */
auto audio_2fs = audio_dec_2.execute(audio_4fs, work_audio_buffer);
/* 96kHz int16_t[64]
* -> FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop, gain of 1
* -> 48kHz int16_t[32] */
auto audio = audio_filter.execute(audio_2fs, work_audio_buffer);
/* 96kHz int16_t[64]
* -> FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop, gain of 1
* -> 48kHz int16_t[32] */
auto audio = audio_filter.execute(audio_2fs, work_audio_buffer);
/* -> 48kHz int16_t[32] */
audio_output.write(audio);
} else {
for (c = 0; c < 32; c++) {
if (synth_acc < pwmrssi_avg)
pwmrssi_audio_buffer.p[c] = 32767;
else
pwmrssi_audio_buffer.p[c] = -32768;
if (synth_acc < 96) // 48kHz / 96 = 500Hz (TODO: use pwmrssi_freq !)
synth_acc++;
else
synth_acc = 0;
}
/* -> 48kHz int16_t[32] */
audio_output.write(audio);
/* -> 48kHz int16_t[32] */
audio_output.write(pwmrssi_audio_buffer);
}
}
void WidebandFMAudio::on_message(const Message* const message) {
@ -86,6 +103,10 @@ void WidebandFMAudio::on_message(const Message* const message) {
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
case Message::ID::PWMRSSIConfigure:
pwmrssi_config(*reinterpret_cast<const PWMRSSIConfigureMessage*>(message));
break;
default:
break;
@ -114,9 +135,18 @@ void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
channel_spectrum.set_decimation_factor(1);
pwmrssi_enabled = false;
synth_acc = 0;
configured = true;
}
void WidebandFMAudio::pwmrssi_config(const PWMRSSIConfigureMessage& message) {
pwmrssi_enabled = message.enabled;
pwmrssi_freq = message.freq;
pwmrssi_avg = message.avg;
}
void WidebandFMAudio::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
audio_output.set_stream(std::make_unique<StreamInput>(message.config));

View File

@ -54,6 +54,12 @@ private:
(int16_t*)dst.data(),
sizeof(dst) / sizeof(int16_t)
};
std::array<int16_t, 32> pwm;
const buffer_s16_t pwmrssi_audio_buffer {
(int16_t*)pwm.data(),
sizeof(pwm) / sizeof(int16_t)
};
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0;
dsp::decimate::FIRC16xR16x16Decim2 decim_1;
@ -70,8 +76,14 @@ private:
SpectrumCollector channel_spectrum;
size_t spectrum_interval_samples = 0;
size_t spectrum_samples = 0;
unsigned int c, synth_acc = 0;
bool pwmrssi_enabled = false;
uint32_t pwmrssi_freq;
uint32_t pwmrssi_avg;
bool configured { false };
void pwmrssi_config(const PWMRSSIConfigureMessage& message);
void configure(const WFMConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
};

View File

@ -21,10 +21,6 @@
*/
#include "proc_xylos.hpp"
#include "dsp_iir_config.hpp"
#include "audio_output.hpp"
#include "portapack_shared_memory.hpp"
#include "sine_table_int8.hpp"
#include "event_m4.hpp"

View File

@ -69,7 +69,7 @@ public:
Retune = 21,
XylosConfigure = 22,
AFSKConfigure = 23,
ModuleID = 24,
PWMRSSIConfigure = 24,
FIFOSignal = 25,
FIFOData = 26,
MAX
@ -479,15 +479,24 @@ public:
int n = 0;
};
class ModuleIDMessage : public Message {
class PWMRSSIConfigureMessage : public Message {
public:
ModuleIDMessage(
) : Message { ID::ModuleID }
PWMRSSIConfigureMessage(
const bool enabled,
const uint32_t freq,
const int32_t avg
) : Message { ID::PWMRSSIConfigure },
enabled(enabled),
freq(freq),
avg(avg)
{
}
bool query;
char md5_signature[16];
const bool enabled;
const uint32_t freq;
const int32_t avg;
};
class XylosConfigureMessage : public Message {

View File

@ -369,13 +369,13 @@ void BigFrequency::paint(Painter& painter) {
digit_y = rect.pos.y;
if (digit < 16) {
digit_def = big_segment_font[(uint8_t)digit];
if (digit_def & 0x01) painter.fill_rectangle({{digit_x + 4, digit_y}, {20, 4}}, segment_color);
if (digit_def & 0x02) painter.fill_rectangle({{digit_x + 24, digit_y + 4}, {4, 20}}, segment_color);
if (digit_def & 0x04) painter.fill_rectangle({{digit_x + 24, digit_y + 28}, {4, 20}}, segment_color);
if (digit_def & 0x08) painter.fill_rectangle({{digit_x + 4, digit_y + 48}, {20, 4}}, segment_color);
if (digit_def & 0x10) painter.fill_rectangle({{digit_x, digit_y + 28}, {4, 20}}, segment_color);
if (digit_def & 0x20) painter.fill_rectangle({{digit_x, digit_y + 4}, {4, 20}}, segment_color);
if (digit_def & 0x40) painter.fill_rectangle({{digit_x + 4, digit_y + 24}, {20, 4}}, segment_color);
if (digit_def & 0x01) painter.fill_rectangle({{digit_x + 4, digit_y}, {20, 4}}, segment_color);
if (digit_def & 0x02) painter.fill_rectangle({{digit_x + 24, digit_y + 4}, {4, 20}}, segment_color);
if (digit_def & 0x04) painter.fill_rectangle({{digit_x + 24, digit_y + 28}, {4, 20}}, segment_color);
if (digit_def & 0x08) painter.fill_rectangle({{digit_x + 4, digit_y + 48}, {20, 4}}, segment_color);
if (digit_def & 0x10) painter.fill_rectangle({{digit_x, digit_y + 28}, {4, 20}}, segment_color);
if (digit_def & 0x20) painter.fill_rectangle({{digit_x, digit_y + 4}, {4, 20}}, segment_color);
if (digit_def & 0x40) painter.fill_rectangle({{digit_x + 4, digit_y + 24}, {20, 4}}, segment_color);
}
if (i == 3) {
// Dot
@ -665,6 +665,15 @@ void Image::set_background(const Color color) {
set_dirty();
}
void Image::invert_colors() {
Color temp;
temp = background_;
background_ = foreground_;
foreground_ = temp;
set_dirty();
}
void Image::paint(Painter& painter) {
if( bitmap_ ) {
// Code also handles ImageButton behavior.

View File

@ -289,6 +289,7 @@ public:
void set_bitmap(const Bitmap* bitmap);
void set_foreground(const Color color);
void set_background(const Color color);
void invert_colors();
void paint(Painter& painter) override;

Binary file not shown.