Root cause: Panel_ED2208's epd_quality mode applies _dither_row_rgb_pair (diagonal bias pattern with dither=140) during _exec_transfer(). Since our ImagePipeline already does proper Floyd-Steinberg dithering, the driver's additional dithering was creating visible diagonal artifacts. Fix: setEpdMode(epd_fastest) selects _dither_row_none (clean nearest-color lookup, no spatial bias). On Panel_ED2208 this only affects the dither function — NOT refresh quality or waveform. Also: default dither_noise setting to OFF since error scatter was targeting the wrong layer (it made gradient areas fuzzier without fixing the real problem in the display driver). Co-authored-by: Cursor <cursoragent@cursor.com>
130 lines
3.8 KiB
C++
130 lines
3.8 KiB
C++
#include <unity.h>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
// Minimal test of queue cycling logic (extracted, platform-independent)
|
|
// We test the shuffling and cycling algorithms without hardware dependencies
|
|
|
|
struct QueueState {
|
|
std::vector<std::string> ids;
|
|
size_t cursor;
|
|
};
|
|
|
|
// Simulate random cycling: advance through shuffled list
|
|
std::string advanceRandom(QueueState& state) {
|
|
if (state.ids.empty()) return "";
|
|
if (state.cursor >= state.ids.size()) {
|
|
state.cursor = 0; // Wrap around (would reshuffle in real impl)
|
|
}
|
|
return state.ids[state.cursor++];
|
|
}
|
|
|
|
// Simulate chronological: just advance sequentially (assumes pre-sorted)
|
|
std::string advanceChrono(QueueState& state) {
|
|
if (state.ids.empty()) return "";
|
|
if (state.cursor >= state.ids.size()) {
|
|
state.cursor = 0;
|
|
}
|
|
return state.ids[state.cursor++];
|
|
}
|
|
|
|
// Favorites weighting: insert duplicates
|
|
std::vector<std::string> applyFavoritesWeight(
|
|
const std::vector<std::string>& all,
|
|
const std::vector<std::string>& favorites,
|
|
int weight) {
|
|
|
|
std::vector<std::string> result = all;
|
|
for (const auto& fav : favorites) {
|
|
for (int i = 1; i < weight; i++) {
|
|
result.push_back(fav);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void test_advance_random_no_repeats_until_wrap() {
|
|
QueueState state;
|
|
state.ids = {"a", "b", "c", "d", "e"};
|
|
state.cursor = 0;
|
|
|
|
std::vector<std::string> seen;
|
|
for (size_t i = 0; i < state.ids.size(); i++) {
|
|
std::string id = advanceRandom(state);
|
|
// Should not have seen this one yet
|
|
TEST_ASSERT_TRUE(std::find(seen.begin(), seen.end(), id) == seen.end());
|
|
seen.push_back(id);
|
|
}
|
|
TEST_ASSERT_EQUAL(5, seen.size());
|
|
}
|
|
|
|
void test_advance_wraps_at_end() {
|
|
QueueState state;
|
|
state.ids = {"a", "b", "c"};
|
|
state.cursor = 0;
|
|
|
|
advanceRandom(state); // a
|
|
advanceRandom(state); // b
|
|
advanceRandom(state); // c
|
|
std::string wrapped = advanceRandom(state); // wraps to a
|
|
TEST_ASSERT_EQUAL_STRING("a", wrapped.c_str());
|
|
}
|
|
|
|
void test_empty_queue_returns_empty() {
|
|
QueueState state;
|
|
state.cursor = 0;
|
|
std::string result = advanceRandom(state);
|
|
TEST_ASSERT_EQUAL_STRING("", result.c_str());
|
|
}
|
|
|
|
void test_favorites_weighting() {
|
|
std::vector<std::string> all = {"a", "b", "c"};
|
|
std::vector<std::string> favs = {"b"};
|
|
auto weighted = applyFavoritesWeight(all, favs, 3);
|
|
// "b" should appear 3 times total (1 original + 2 extra)
|
|
int count = 0;
|
|
for (const auto& id : weighted) {
|
|
if (id == "b") count++;
|
|
}
|
|
TEST_ASSERT_EQUAL(3, count);
|
|
TEST_ASSERT_EQUAL(5, weighted.size()); // 3 original + 2 extra
|
|
}
|
|
|
|
void test_nearest_color_black();
|
|
void test_nearest_color_white();
|
|
void test_nearest_color_red();
|
|
void test_nearest_color_green();
|
|
void test_nearest_color_blue();
|
|
void test_nearest_color_yellow();
|
|
void test_dither_solid_black();
|
|
void test_dither_solid_white();
|
|
void test_dither_produces_valid_indices();
|
|
void test_blue_noise_breaks_pattern_regularity();
|
|
void test_blue_noise_dither_valid_indices();
|
|
|
|
void setUp() {}
|
|
void tearDown() {}
|
|
|
|
int main() {
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_advance_random_no_repeats_until_wrap);
|
|
RUN_TEST(test_advance_wraps_at_end);
|
|
RUN_TEST(test_empty_queue_returns_empty);
|
|
RUN_TEST(test_favorites_weighting);
|
|
RUN_TEST(test_nearest_color_black);
|
|
RUN_TEST(test_nearest_color_white);
|
|
RUN_TEST(test_nearest_color_red);
|
|
RUN_TEST(test_nearest_color_green);
|
|
RUN_TEST(test_nearest_color_blue);
|
|
RUN_TEST(test_nearest_color_yellow);
|
|
RUN_TEST(test_dither_solid_black);
|
|
RUN_TEST(test_dither_solid_white);
|
|
RUN_TEST(test_dither_produces_valid_indices);
|
|
RUN_TEST(test_blue_noise_breaks_pattern_regularity);
|
|
RUN_TEST(test_blue_noise_dither_valid_indices);
|
|
UNITY_END();
|
|
return 0;
|
|
}
|