Skip to content

Commit 5c4d580

Browse files
committed
Add heatshrink compression
Not sure if this is worth it or not, it adds a couple KBs of program size and more RAM usage but it does reduce Pawn program size by more than half so it could be worth it in the long run. The Seek method is very inefficient for obvious reasons, ideally we would compress each Pawn overlay independently instead of the whole amx file. We should also probably move the heatshrink source files to a different folder.
1 parent db17adf commit 5c4d580

8 files changed

Lines changed: 681 additions & 194 deletions

File tree

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ set(LVGL_SRC
358358
list(APPEND PAWN_SRC
359359
pawn/amx.c
360360
pawn/amxpool.c
361+
pawn/heatshrink_decoder.c
361362
)
362363

363364
list(APPEND IMAGE_FILES

src/displayapp/screens/Pawn.h

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "pawn/amx.h"
1212
#include "pawn/amxpool.h"
13+
#include "pawn/heatshrink_decoder.h"
1314

1415
namespace Pinetime {
1516
namespace Applications {
@@ -24,12 +25,18 @@ namespace Pinetime {
2425
return nullptr;
2526
}
2627

27-
virtual size_t Read(uint8_t* buffer, size_t size, size_t offset) = 0;
28+
virtual void Seek(size_t position) = 0;
29+
virtual size_t Read(uint8_t* buffer, size_t size) = 0;
30+
31+
size_t Read(uint8_t* buffer, size_t size, size_t position) {
32+
Seek(position);
33+
return Read(buffer, size);
34+
}
2835
};
2936

3037
class ConstFile : public File {
3138
const uint8_t* backing;
32-
size_t size;
39+
size_t size, position;
3340

3441
public:
3542
ConstFile(const uint8_t* backing, size_t size) : backing(backing), size(size) {
@@ -39,10 +46,17 @@ namespace Pinetime {
3946
return backing;
4047
}
4148

42-
size_t Read(uint8_t* buffer, size_t size, size_t offset) override {
43-
if (size + offset > this->size)
49+
void Seek(size_t position) override {
50+
this->position = position;
51+
}
52+
53+
size_t Read(uint8_t* buffer, size_t size) override {
54+
if (position >= this->size)
4455
return 0;
45-
memcpy(buffer, backing + offset, size);
56+
if (position + size > this->size)
57+
size = this->size - position;
58+
memcpy(buffer, backing + position, size);
59+
position += size;
4660
return size;
4761
}
4862
};
@@ -62,15 +76,76 @@ namespace Pinetime {
6276
fs.FileClose(&file);
6377
}
6478

65-
size_t Read(uint8_t* buffer, size_t size, size_t offset) override {
79+
void Seek(size_t position) override {
80+
fs.FileSeek(&file, position);
81+
}
82+
83+
size_t Read(uint8_t* buffer, size_t size) override {
6684
if (!ok)
6785
return 0;
68-
69-
fs.FileSeek(&file, offset);
7086
return fs.FileRead(&file, buffer, size);
7187
}
7288
};
7389

90+
class HeatshrinkFile : public File {
91+
std::unique_ptr<File> inner;
92+
heatshrink_decoder decoder;
93+
94+
size_t real_pos;
95+
uint8_t pending_inner_read[100];
96+
size_t pending_pos = 0, pending_size = 0;
97+
98+
void Reset() {
99+
heatshrink_decoder_reset(&decoder);
100+
pending_size = 0;
101+
pending_pos = 0;
102+
real_pos = 0;
103+
inner->Seek(0);
104+
}
105+
106+
public:
107+
HeatshrinkFile(std::unique_ptr<File> inner) : inner(std::move(inner)) {
108+
Reset();
109+
}
110+
111+
/**
112+
* Seek to a specified position in the *uncompressed* file.
113+
*/
114+
void Seek(size_t position) override {
115+
if (position < this->real_pos) // We have to rewind
116+
Reset();
117+
118+
uint8_t discard[50];
119+
while (this->real_pos < position) {
120+
size_t remaining = position - this->real_pos;
121+
Read(discard, remaining > sizeof(discard) ? sizeof(discard) : remaining);
122+
}
123+
}
124+
125+
size_t Read(uint8_t* buffer, size_t size) override {
126+
size_t actual_read, total_read = 0;
127+
128+
while (total_read < size) {
129+
HSD_poll_res res = heatshrink_decoder_poll(&decoder, buffer + total_read, size - total_read, &actual_read);
130+
total_read += actual_read;
131+
real_pos += actual_read;
132+
133+
if (res == HSDR_POLL_EMPTY) {
134+
if (pending_size == 0) {
135+
pending_size = inner->Read(pending_inner_read, sizeof(pending_inner_read));
136+
pending_pos = 0;
137+
}
138+
139+
heatshrink_decoder_sink(&decoder, pending_inner_read + pending_pos, pending_size, &actual_read);
140+
pending_size -= actual_read;
141+
pending_pos += actual_read;
142+
}
143+
}
144+
145+
return total_read;
146+
}
147+
};
148+
74149
Pawn(AppControllers& controllers);
75150
Pawn(AppControllers& controllers, std::unique_ptr<File> file);
76151
~Pawn() override;

src/displayapp/screens/WatchFaceDigital.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55

66
using namespace Pinetime::Applications::Screens;
77

8-
WatchFaceDigital::WatchFaceDigital(AppControllers& controllers) : Pawn(controllers, std::make_unique<Pawn::ConstFile>(watchface_digital, watchface_digital_len)) {
8+
WatchFaceDigital::WatchFaceDigital(AppControllers& controllers) : Pawn(controllers, std::make_unique<Pawn::HeatshrinkFile>(std::make_unique<Pawn::ConstFile>(watchface_digital, watchface_digital_len))) {
99
}

0 commit comments

Comments
 (0)