Skip to content
This repository was archived by the owner on Apr 16, 2026. It is now read-only.

Commit 76a1643

Browse files
committed
convert OSD colors to RGBA format, make each element individually styleable
1 parent 7c05c7f commit 76a1643

6 files changed

Lines changed: 357 additions & 75 deletions

File tree

res/prudynt.json

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,9 @@
121121
"width": 1920,
122122
"osd": {
123123
"enabled": true,
124-
"font_color": 4294967295,
125124
"font_path": "/usr/share/fonts/default.ttf",
126125
"font_size": 32,
127126
"font_stroke": 1,
128-
"font_stroke_color": 4278190080,
129127
"font_stroke_enabled": true,
130128
"font_stroke_size": 32,
131129
"font_xscale": 100,
@@ -149,15 +147,18 @@
149147
"time_enabled": true,
150148
"time_format": "%F %T",
151149
"time_rotation": 0,
152-
"time_transparency": 128,
150+
"time_font_color": "#FFFFFF80",
151+
"time_font_stroke_color": "#00000080",
153152
"uptime_enabled": true,
154153
"uptime_format": "Uptime: %02lu:%02lu:%02lu",
155154
"uptime_rotation": 0,
156-
"uptime_transparency": 128,
155+
"uptime_font_color": "#FFFFFF80",
156+
"uptime_font_stroke_color": "#00000080",
157157
"user_text_enabled": true,
158158
"user_text_format": "%hostname",
159159
"user_text_rotation": 0,
160-
"user_text_transparency": 128
160+
"user_text_font_color": "#FFFFFF80",
161+
"user_text_font_stroke_color": "#00000080"
161162
}
162163
},
163164
"stream1": {
@@ -186,11 +187,9 @@
186187
"width": 640,
187188
"osd": {
188189
"enabled": true,
189-
"font_color": 4294967295,
190190
"font_path": "/usr/share/fonts/default.ttf",
191191
"font_size": 13,
192192
"font_stroke": 1,
193-
"font_stroke_color": 4278190080,
194193
"font_stroke_enabled": true,
195194
"font_stroke_size": 13,
196195
"font_xscale": 100,
@@ -214,15 +213,18 @@
214213
"time_enabled": true,
215214
"time_format": "%F %T",
216215
"time_rotation": 0,
217-
"time_transparency": 128,
216+
"time_font_color": "#FFFFFF80",
217+
"time_font_stroke_color": "#00000080",
218218
"uptime_enabled": true,
219219
"uptime_format": "Uptime: %02lu:%02lu:%02lu",
220220
"uptime_rotation": 0,
221-
"uptime_transparency": 128,
221+
"uptime_font_color": "#FFFFFF80",
222+
"uptime_font_stroke_color": "#00000080",
222223
"user_text_enabled": true,
223224
"user_text_format": "%hostname",
224225
"user_text_rotation": 0,
225-
"user_text_transparency": 128
226+
"user_text_font_color": "#FFFFFF80",
227+
"user_text_font_stroke_color": "#00000080"
226228
}
227229
},
228230
"stream2": {

src/Config.cpp

Lines changed: 264 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,67 @@ bool validateUint(const unsigned int &v)
8383
return true;
8484
}
8585

86+
// Utility function to validate hexadecimal color format (#RRGGBBAA)
87+
bool isValidHexColor(const char* str) {
88+
if (!str || strlen(str) != 9) {
89+
return false;
90+
}
91+
92+
if (str[0] != '#') {
93+
return false;
94+
}
95+
96+
for (int i = 1; i < 9; i++) {
97+
char c = str[i];
98+
if (!((c >= '0' && c <= '9') ||
99+
(c >= 'A' && c <= 'F') ||
100+
(c >= 'a' && c <= 'f'))) {
101+
return false;
102+
}
103+
}
104+
105+
return true;
106+
}
107+
108+
// Utility function to convert RGBA hexadecimal color string to unsigned int
109+
unsigned int hexColorToUint(const char* str) {
110+
if (!isValidHexColor(str)) {
111+
return 0;
112+
}
113+
114+
// Parse RGBA format: #RRGGBBAA
115+
// Extract each component separately to handle RGBA format correctly
116+
char rStr[3] = {str[1], str[2], '\0'}; // RR
117+
char gStr[3] = {str[3], str[4], '\0'}; // GG
118+
char bStr[3] = {str[5], str[6], '\0'}; // BB
119+
char aStr[3] = {str[7], str[8], '\0'}; // AA
120+
121+
unsigned int r = strtoul(rStr, nullptr, 16);
122+
unsigned int g = strtoul(gStr, nullptr, 16);
123+
unsigned int b = strtoul(bStr, nullptr, 16);
124+
unsigned int a = strtoul(aStr, nullptr, 16);
125+
126+
// Pack into ARGB format for internal use (A in bits 24-31, R in 16-23, G in 8-15, B in 0-7)
127+
// This matches the bit extraction logic used in OSD::drawText()
128+
return (a << 24) | (r << 16) | (g << 8) | b;
129+
}
130+
131+
// Validation function for OSD color fields that accepts both integer and hex string formats
132+
bool validateOSDColor(const unsigned int &v) {
133+
// For unsigned int values, any value is valid (same as validateUint)
134+
return true;
135+
}
136+
137+
// Validation function for OSD color fields when parsing from JSON string
138+
bool validateOSDColorString(const char* str) {
139+
if (!str) {
140+
return false;
141+
}
142+
143+
// Check if it's a valid hexadecimal color format
144+
return isValidHexColor(str);
145+
}
146+
86147
bool validateSampleRate(const int &v)
87148
{
88149
std::set<int> allowed_rates = {8000, 16000, 24000, 44100, 48000};
@@ -279,11 +340,8 @@ std::vector<ConfigItem<int>> CFG::getIntItems()
279340
{"stream0.osd.pos_user_text_y", stream0.osd.pos_user_text_y, OSD_AUTO_VALUE, validateInt15360},
280341
{"stream0.osd.start_delay", stream0.osd.start_delay, 0, [](const int &v) { return v >= 0 && v <= 5000; }},
281342
{"stream0.osd.time_rotation", stream0.osd.time_rotation, 0, validateInt360},
282-
{"stream0.osd.time_transparency", stream0.osd.time_transparency, 255, validateInt255},
283343
{"stream0.osd.uptime_rotation", stream0.osd.uptime_rotation, 0, validateInt360},
284-
{"stream0.osd.uptime_transparency", stream0.osd.uptime_transparency, 255, validateInt255},
285344
{"stream0.osd.user_text_rotation", stream0.osd.user_text_rotation, 0, validateInt360},
286-
{"stream0.osd.user_text_transparency", stream0.osd.user_text_transparency, 255, validateInt255},
287345
{"stream0.rotation", stream0.rotation, 0, validateInt2},
288346
{"stream0.width", stream0.width, 1920, validateIntGe0},
289347
{"stream0.profile", stream0.profile, 2, validateInt2},
@@ -312,10 +370,7 @@ std::vector<ConfigItem<int>> CFG::getIntItems()
312370
{"stream1.osd.pos_user_text_y", stream1.osd.pos_user_text_y, OSD_AUTO_VALUE, validateInt15360},
313371
{"stream1.osd.start_delay", stream1.osd.start_delay, 0, [](const int &v) { return v >= 0 && v <= 5000; }},
314372
{"stream1.osd.time_rotation", stream1.osd.time_rotation, 0, validateInt360},
315-
{"stream1.osd.time_transparency", stream1.osd.time_transparency, 255, validateInt255},
316373
{"stream1.osd.uptime_rotation", stream1.osd.uptime_rotation, 0, validateInt360},
317-
{"stream1.osd.uptime_transparency", stream1.osd.uptime_transparency, 255, validateInt255},
318-
{"stream1.osd.user_text_transparency", stream1.osd.user_text_transparency, 255, validateInt255},
319374
{"stream1.osd.user_text_rotation", stream1.osd.user_text_rotation, 0, validateInt360},
320375
{"stream1.rotation", stream1.rotation, 0, validateInt2},
321376
{"stream1.width", stream1.width, 640, validateIntGe0},
@@ -333,10 +388,20 @@ std::vector<ConfigItem<unsigned int>> CFG::getUintItems()
333388
{
334389
return {
335390
{"sensor.i2c_address", sensor.i2c_address, 0x37, [](const unsigned int &v) { return v <= 0x7F; }, false, "/proc/jz/sensor/i2c_addr"},
336-
{"stream0.osd.font_stroke_color", stream0.osd.font_stroke_color, 0xFF000000, validateUint},
337-
{"stream0.osd.font_color", stream0.osd.font_color, 0xFFFFFFFF, validateUint},
338-
{"stream1.osd.font_color", stream1.osd.font_color, 0xFFFFFFFF, validateUint},
339-
{"stream1.osd.font_stroke_color", stream1.osd.font_stroke_color, 0xFF000000, validateUint},
391+
// Individual color settings for stream0 text elements
392+
{"stream0.osd.time_font_color", stream0.osd.time_font_color, 0xFFFFFFFF, validateOSDColor},
393+
{"stream0.osd.time_font_stroke_color", stream0.osd.time_font_stroke_color, 0xFF000000, validateOSDColor},
394+
{"stream0.osd.uptime_font_color", stream0.osd.uptime_font_color, 0xFFFFFFFF, validateOSDColor},
395+
{"stream0.osd.uptime_font_stroke_color", stream0.osd.uptime_font_stroke_color, 0xFF000000, validateOSDColor},
396+
{"stream0.osd.user_text_font_color", stream0.osd.user_text_font_color, 0xFFFFFFFF, validateOSDColor},
397+
{"stream0.osd.user_text_font_stroke_color", stream0.osd.user_text_font_stroke_color, 0xFF000000, validateOSDColor},
398+
// Individual color settings for stream1 text elements
399+
{"stream1.osd.time_font_color", stream1.osd.time_font_color, 0xFFFFFFFF, validateOSDColor},
400+
{"stream1.osd.time_font_stroke_color", stream1.osd.time_font_stroke_color, 0xFF000000, validateOSDColor},
401+
{"stream1.osd.uptime_font_color", stream1.osd.uptime_font_color, 0xFFFFFFFF, validateOSDColor},
402+
{"stream1.osd.uptime_font_stroke_color", stream1.osd.uptime_font_stroke_color, 0xFF000000, validateOSDColor},
403+
{"stream1.osd.user_text_font_color", stream1.osd.user_text_font_color, 0xFFFFFFFF, validateOSDColor},
404+
{"stream1.osd.user_text_font_stroke_color", stream1.osd.user_text_font_stroke_color, 0xFF000000, validateOSDColor},
340405
};
341406
};
342407

@@ -508,6 +573,17 @@ void handleConfigItem(json_object *jsonConfig, ConfigItem<T> &item)
508573
item.value = static_cast<unsigned int>(val);
509574
readFromConfig = true;
510575
}
576+
} else if (json_object_is_type(valueObj, json_type_string)) {
577+
// Check if this is an OSD color field that might be in hex format
578+
std::string path = item.path;
579+
if (path.find("font_color") != std::string::npos ||
580+
path.find("font_stroke_color") != std::string::npos) {
581+
const char *str = json_object_get_string(valueObj);
582+
if (isValidHexColor(str)) {
583+
item.value = hexColorToUint(str);
584+
readFromConfig = true;
585+
}
586+
}
511587
}
512588
} else if constexpr (std::is_same_v<T, float>) {
513589
if (json_object_is_type(valueObj, json_type_double)) {
@@ -680,6 +756,181 @@ std::vector<ConfigItem<float>> CFG::getFloatItems()
680756
};
681757
};
682758

759+
void CFG::migrateOldColorSettings()
760+
{
761+
// Helper function to combine RGB color with alpha transparency
762+
// Creates ARGB format for internal use (matches OSD::drawText() bit extraction)
763+
auto combineColorWithAlpha = [](unsigned int rgb_color, int transparency) -> unsigned int {
764+
// Extract RGB components from the input color
765+
unsigned int r = (rgb_color >> 16) & 0xFF;
766+
unsigned int g = (rgb_color >> 8) & 0xFF;
767+
unsigned int b = rgb_color & 0xFF;
768+
769+
// Combine with alpha (transparency is 0-255, where 255 = opaque)
770+
unsigned int alpha = (unsigned int)transparency & 0xFF;
771+
772+
// Pack into ARGB format for internal use (A in bits 24-31)
773+
return (alpha << 24) | (r << 16) | (g << 8) | b;
774+
};
775+
776+
// Check for old configuration format and migrate
777+
json_object *stream0Obj = nullptr;
778+
json_object *stream1Obj = nullptr;
779+
780+
if (json_object_object_get_ex(jsonConfig, "stream0", &stream0Obj)) {
781+
json_object *osdObj = nullptr;
782+
if (json_object_object_get_ex(stream0Obj, "osd", &osdObj)) {
783+
// Check if old format exists (font_color + transparency fields)
784+
json_object *fontColorObj = nullptr;
785+
json_object *fontStrokeColorObj = nullptr;
786+
json_object *timeTransparencyObj = nullptr;
787+
json_object *uptimeTransparencyObj = nullptr;
788+
json_object *userTextTransparencyObj = nullptr;
789+
790+
bool hasOldFormat = json_object_object_get_ex(osdObj, "font_color", &fontColorObj) &&
791+
json_object_object_get_ex(osdObj, "font_stroke_color", &fontStrokeColorObj) &&
792+
(json_object_object_get_ex(osdObj, "time_transparency", &timeTransparencyObj) ||
793+
json_object_object_get_ex(osdObj, "uptime_transparency", &uptimeTransparencyObj) ||
794+
json_object_object_get_ex(osdObj, "user_text_transparency", &userTextTransparencyObj));
795+
796+
if (hasOldFormat) {
797+
unsigned int fontColor = 0xFFFFFFFF; // Default white
798+
unsigned int fontStrokeColor = 0xFF000000; // Default black
799+
int timeTransparency = 255;
800+
int uptimeTransparency = 255;
801+
int userTextTransparency = 255;
802+
803+
// Read old values
804+
if (json_object_is_type(fontColorObj, json_type_int)) {
805+
fontColor = json_object_get_int64(fontColorObj);
806+
} else if (json_object_is_type(fontColorObj, json_type_string)) {
807+
const char *str = json_object_get_string(fontColorObj);
808+
if (isValidHexColor(str)) {
809+
fontColor = hexColorToUint(str);
810+
}
811+
}
812+
813+
if (json_object_is_type(fontStrokeColorObj, json_type_int)) {
814+
fontStrokeColor = json_object_get_int64(fontStrokeColorObj);
815+
} else if (json_object_is_type(fontStrokeColorObj, json_type_string)) {
816+
const char *str = json_object_get_string(fontStrokeColorObj);
817+
if (isValidHexColor(str)) {
818+
fontStrokeColor = hexColorToUint(str);
819+
}
820+
}
821+
822+
if (timeTransparencyObj && json_object_is_type(timeTransparencyObj, json_type_int)) {
823+
timeTransparency = json_object_get_int(timeTransparencyObj);
824+
}
825+
if (uptimeTransparencyObj && json_object_is_type(uptimeTransparencyObj, json_type_int)) {
826+
uptimeTransparency = json_object_get_int(uptimeTransparencyObj);
827+
}
828+
if (userTextTransparencyObj && json_object_is_type(userTextTransparencyObj, json_type_int)) {
829+
userTextTransparency = json_object_get_int(userTextTransparencyObj);
830+
}
831+
832+
// Create new individual color settings
833+
json_object_object_add(osdObj, "time_font_color",
834+
json_object_new_int64(combineColorWithAlpha(fontColor, timeTransparency)));
835+
json_object_object_add(osdObj, "time_font_stroke_color",
836+
json_object_new_int64(combineColorWithAlpha(fontStrokeColor, timeTransparency)));
837+
json_object_object_add(osdObj, "uptime_font_color",
838+
json_object_new_int64(combineColorWithAlpha(fontColor, uptimeTransparency)));
839+
json_object_object_add(osdObj, "uptime_font_stroke_color",
840+
json_object_new_int64(combineColorWithAlpha(fontStrokeColor, uptimeTransparency)));
841+
json_object_object_add(osdObj, "user_text_font_color",
842+
json_object_new_int64(combineColorWithAlpha(fontColor, userTextTransparency)));
843+
json_object_object_add(osdObj, "user_text_font_stroke_color",
844+
json_object_new_int64(combineColorWithAlpha(fontStrokeColor, userTextTransparency)));
845+
846+
// Remove old settings
847+
json_object_object_del(osdObj, "font_color");
848+
json_object_object_del(osdObj, "font_stroke_color");
849+
json_object_object_del(osdObj, "time_transparency");
850+
json_object_object_del(osdObj, "uptime_transparency");
851+
json_object_object_del(osdObj, "user_text_transparency");
852+
}
853+
}
854+
}
855+
856+
// Repeat for stream1
857+
if (json_object_object_get_ex(jsonConfig, "stream1", &stream1Obj)) {
858+
json_object *osdObj = nullptr;
859+
if (json_object_object_get_ex(stream1Obj, "osd", &osdObj)) {
860+
// Similar migration logic for stream1
861+
json_object *fontColorObj = nullptr;
862+
json_object *fontStrokeColorObj = nullptr;
863+
json_object *timeTransparencyObj = nullptr;
864+
json_object *uptimeTransparencyObj = nullptr;
865+
json_object *userTextTransparencyObj = nullptr;
866+
867+
bool hasOldFormat = json_object_object_get_ex(osdObj, "font_color", &fontColorObj) &&
868+
json_object_object_get_ex(osdObj, "font_stroke_color", &fontStrokeColorObj) &&
869+
(json_object_object_get_ex(osdObj, "time_transparency", &timeTransparencyObj) ||
870+
json_object_object_get_ex(osdObj, "uptime_transparency", &uptimeTransparencyObj) ||
871+
json_object_object_get_ex(osdObj, "user_text_transparency", &userTextTransparencyObj));
872+
873+
if (hasOldFormat) {
874+
unsigned int fontColor = 0xFFFFFFFF;
875+
unsigned int fontStrokeColor = 0xFF000000;
876+
int timeTransparency = 255;
877+
int uptimeTransparency = 255;
878+
int userTextTransparency = 255;
879+
880+
// Read old values (similar to stream0)
881+
if (json_object_is_type(fontColorObj, json_type_int)) {
882+
fontColor = json_object_get_int64(fontColorObj);
883+
} else if (json_object_is_type(fontColorObj, json_type_string)) {
884+
const char *str = json_object_get_string(fontColorObj);
885+
if (isValidHexColor(str)) {
886+
fontColor = hexColorToUint(str);
887+
}
888+
}
889+
890+
if (json_object_is_type(fontStrokeColorObj, json_type_int)) {
891+
fontStrokeColor = json_object_get_int64(fontStrokeColorObj);
892+
} else if (json_object_is_type(fontStrokeColorObj, json_type_string)) {
893+
const char *str = json_object_get_string(fontStrokeColorObj);
894+
if (isValidHexColor(str)) {
895+
fontStrokeColor = hexColorToUint(str);
896+
}
897+
}
898+
899+
if (timeTransparencyObj && json_object_is_type(timeTransparencyObj, json_type_int)) {
900+
timeTransparency = json_object_get_int(timeTransparencyObj);
901+
}
902+
if (uptimeTransparencyObj && json_object_is_type(uptimeTransparencyObj, json_type_int)) {
903+
uptimeTransparency = json_object_get_int(uptimeTransparencyObj);
904+
}
905+
if (userTextTransparencyObj && json_object_is_type(userTextTransparencyObj, json_type_int)) {
906+
userTextTransparency = json_object_get_int(userTextTransparencyObj);
907+
}
908+
909+
// Create new individual color settings
910+
json_object_object_add(osdObj, "time_font_color",
911+
json_object_new_int64(combineColorWithAlpha(fontColor, timeTransparency)));
912+
json_object_object_add(osdObj, "time_font_stroke_color",
913+
json_object_new_int64(combineColorWithAlpha(fontStrokeColor, timeTransparency)));
914+
json_object_object_add(osdObj, "uptime_font_color",
915+
json_object_new_int64(combineColorWithAlpha(fontColor, uptimeTransparency)));
916+
json_object_object_add(osdObj, "uptime_font_stroke_color",
917+
json_object_new_int64(combineColorWithAlpha(fontStrokeColor, uptimeTransparency)));
918+
json_object_object_add(osdObj, "user_text_font_color",
919+
json_object_new_int64(combineColorWithAlpha(fontColor, userTextTransparency)));
920+
json_object_object_add(osdObj, "user_text_font_stroke_color",
921+
json_object_new_int64(combineColorWithAlpha(fontStrokeColor, userTextTransparency)));
922+
923+
// Remove old settings
924+
json_object_object_del(osdObj, "font_color");
925+
json_object_object_del(osdObj, "font_stroke_color");
926+
json_object_object_del(osdObj, "time_transparency");
927+
json_object_object_del(osdObj, "uptime_transparency");
928+
json_object_object_del(osdObj, "user_text_transparency");
929+
}
930+
}
931+
}
932+
}
933+
683934
CFG::CFG()
684935
{
685936
load();
@@ -696,6 +947,9 @@ void CFG::load()
696947
config_loaded = readConfig();
697948

698949
if (jsonConfig) {
950+
// Handle backward compatibility migration first
951+
migrateOldColorSettings();
952+
699953
for (auto &item : boolItems)
700954
handleConfigItem(jsonConfig, item);
701955
for (auto &item : charItems)

0 commit comments

Comments
 (0)