Private
Public Access
Release 0.12.0
This commit is contained in:
1 parent
f74a658097
commit
7f6eeab39e
48 files changed
+3617
-1266
No files matched your search
+140
-68
@@ -2,83 +2,155 @@
|
||||
#define HYPERDRM_CALIBRATION_H
|
||||
#include <math.h>
|
||||
#include "playback.h"
|
||||
#define CALIBRATION_PREF "/media/developer/apps/usr/palm/services/org.webosbrew.histogramhyperion.service/calibration.json"
|
||||
typedef struct {double position;unsigned char region;} RimPoint;
|
||||
typedef enum {CAL_AUTO,CAL_G4,CAL_AVERAGE,CAL_CUSTOM} CalibrationKind;
|
||||
typedef struct {CalibrationKind kind;char model[64];unsigned count;RimPoint points[16];} Calibration;
|
||||
#define CALIBRATION_PREF \
|
||||
"/media/developer/apps/usr/palm/services/org.webosbrew.histogramhyperion.service/" \
|
||||
"calibration.json"
|
||||
typedef struct {
|
||||
double position;
|
||||
unsigned char region;
|
||||
} RimPoint;
|
||||
typedef enum { CAL_AUTO, CAL_G4, CAL_AVERAGE, CAL_CUSTOM } CalibrationKind;
|
||||
typedef struct {
|
||||
CalibrationKind kind;
|
||||
char model[64];
|
||||
unsigned count;
|
||||
RimPoint points[16];
|
||||
} Calibration;
|
||||
static Calibration calibration;
|
||||
static char tv_model[64];
|
||||
static unsigned calibration_generation=1;
|
||||
static inline unsigned calibration_revision(void){return calibration_generation;}
|
||||
static const RimPoint g4_rim[]={
|
||||
{0.2078431373,7},{0.3176470588,3},{0.5490196078,11},{0.7529411765,12},
|
||||
{0.8392156863,13},{1.4941176471,10},{2.1843137255,9},{2.5490196078,1},
|
||||
{2.8666666667,5},{3.4,15}
|
||||
};
|
||||
static inline const char *calibration_key(CalibrationKind kind){
|
||||
const char *keys[]={"auto","g4","average","custom"};return keys[kind];
|
||||
static unsigned calibration_generation = 1;
|
||||
static inline unsigned calibration_revision(void) {
|
||||
return calibration_generation;
|
||||
}
|
||||
static inline bool calibration_model_valid(const char *model){
|
||||
if(!model[0] || strlen(model)>63)return false;
|
||||
for(const unsigned char *p=(const unsigned char*)model;*p;p++)
|
||||
if(!((*p>='A'&&*p<='Z')||(*p>='a'&&*p<='z')||(*p>='0'&&*p<='9')||*p=='.'||*p=='-'||*p=='_'||*p==' '))return false;
|
||||
return true;
|
||||
static const RimPoint g4_rim[] = {{0.2078431373, 7}, {0.3176470588, 3}, {0.5490196078, 11},
|
||||
{0.7529411765, 12}, {0.8392156863, 13}, {1.4941176471, 10},
|
||||
{2.1843137255, 9}, {2.5490196078, 1}, {2.8666666667, 5},
|
||||
{3.4, 15}};
|
||||
static inline const char *calibration_key(CalibrationKind kind) {
|
||||
const char *keys[] = {"auto", "g4", "average", "custom"};
|
||||
return keys[kind];
|
||||
}
|
||||
static inline bool calibration_model_valid(const char *model) {
|
||||
if (!model[0] || strlen(model) > 63)
|
||||
return false;
|
||||
for (const unsigned char *p = (const unsigned char *)model; *p; p++)
|
||||
if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') ||
|
||||
*p == '.' || *p == '-' || *p == '_' || *p == ' '))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
/* LG retail model identifiers: OLED + two-digit panel size + G4 series. */
|
||||
static inline bool calibration_is_g4(const char *model){
|
||||
return strlen(model)>=8 && !strncmp(model,"OLED",4) && model[4]>='0' && model[4]<='9' && model[5]>='0' && model[5]<='9' && model[6]=='G' && model[7]=='4';
|
||||
static inline bool calibration_is_g4(const char *model) {
|
||||
return strlen(model) >= 8 && !strncmp(model, "OLED", 4) && model[4] >= '0' && model[4] <= '9' &&
|
||||
model[5] >= '0' && model[5] <= '9' && model[6] == 'G' && model[7] == '4';
|
||||
}
|
||||
static inline CalibrationKind calibration_effective(void){
|
||||
if(calibration.kind!=CAL_AUTO)return calibration.kind;
|
||||
if(calibration.count>=2 && tv_model[0] && !strcmp(calibration.model,tv_model))return CAL_CUSTOM;
|
||||
return calibration_is_g4(tv_model)?CAL_G4:CAL_AVERAGE;
|
||||
static inline CalibrationKind calibration_effective(void) {
|
||||
if (calibration.kind != CAL_AUTO)
|
||||
return calibration.kind;
|
||||
if (calibration.count >= 2 && tv_model[0] && !strcmp(calibration.model, tv_model))
|
||||
return CAL_CUSTOM;
|
||||
return calibration_is_g4(tv_model) ? CAL_G4 : CAL_AVERAGE;
|
||||
}
|
||||
static inline bool calibration_parse(PlaybackJson *j,const char *text,Calibration *result){
|
||||
void *root=j->parse(text);if(!root)return false;
|
||||
Calibration next=*result;bool valid=false;
|
||||
for(unsigned k=0;k<4;k++)if(playback_is(j,root,"profile",calibration_key(k))){next.kind=k;valid=true;break;}
|
||||
void *points=NULL;
|
||||
if(j->type(root)==4 && j->get(root,"points",&points)){
|
||||
void *model=playback_field(j,root,"model");
|
||||
valid=valid && points && j->type(points)==5 && model && j->type(model)==6 && calibration_model_valid(j->string(model));
|
||||
size_t count=valid?j->length(points):0;valid=valid && count>=2 && count<=16;
|
||||
if(valid){strcpy(next.model,j->string(model));next.count=count;memset(next.points,0,sizeof(next.points));}
|
||||
for(size_t i=0;valid && i<count;i++){
|
||||
void *point=j->at(points,i),*position=playback_field(j,point,"position"),*region=playback_field(j,point,"region");
|
||||
valid=position && (j->type(position)==2 || j->type(position)==3) && region && j->type(region)==3;
|
||||
if(valid){char *end;double x=strtod(j->string(position),&end);valid=end && !*end && isfinite(x) && x>=0 && x<4;
|
||||
long r=strtol(j->string(region),&end,10);valid=valid && end && !*end && r>=0 && r<16 && (!i || x>next.points[i-1].position);
|
||||
if(valid){next.points[i].position=x;next.points[i].region=r;}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(next.kind==CAL_CUSTOM && next.count<2)valid=false;
|
||||
j->put(root);if(valid)*result=next;return valid;
|
||||
static inline bool calibration_parse(PlaybackJson *j, const char *text, Calibration *result) {
|
||||
void *root = j->parse(text);
|
||||
if (!root)
|
||||
return false;
|
||||
Calibration next = *result;
|
||||
bool valid = false;
|
||||
for (unsigned k = 0; k < 4; k++)
|
||||
if (playback_is(j, root, "profile", calibration_key(k))) {
|
||||
next.kind = k;
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
void *points = NULL;
|
||||
if (j->type(root) == 4 && j->get(root, "points", &points)) {
|
||||
void *model = playback_field(j, root, "model");
|
||||
valid = valid && points && j->type(points) == 5 && model && j->type(model) == 6 &&
|
||||
calibration_model_valid(j->string(model));
|
||||
size_t count = valid ? j->length(points) : 0;
|
||||
valid = valid && count >= 2 && count <= 16;
|
||||
if (valid) {
|
||||
strcpy(next.model, j->string(model));
|
||||
next.count = count;
|
||||
memset(next.points, 0, sizeof(next.points));
|
||||
}
|
||||
for (size_t i = 0; valid && i < count; i++) {
|
||||
void *point = j->at(points, i), *position = playback_field(j, point, "position"),
|
||||
*region = playback_field(j, point, "region");
|
||||
valid = position && (j->type(position) == 2 || j->type(position) == 3) && region &&
|
||||
j->type(region) == 3;
|
||||
if (valid) {
|
||||
char *end;
|
||||
double x = strtod(j->string(position), &end);
|
||||
valid = end && !*end && isfinite(x) && x >= 0 && x < 4;
|
||||
long r = strtol(j->string(region), &end, 10);
|
||||
valid = valid && end && !*end && r >= 0 && r < 16 &&
|
||||
(!i || x > next.points[i - 1].position);
|
||||
if (valid) {
|
||||
next.points[i].position = x;
|
||||
next.points[i].region = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next.kind == CAL_CUSTOM && next.count < 2)
|
||||
valid = false;
|
||||
j->put(root);
|
||||
if (valid)
|
||||
*result = next;
|
||||
return valid;
|
||||
}
|
||||
static inline bool calibration_save(const char *path,const Calibration *value){
|
||||
char temp[512],data[2048];int used=snprintf(data,sizeof(data),"{\"profile\":\"%s\"",calibration_key(value->kind));
|
||||
if(value->count){
|
||||
used+=snprintf(data+used,sizeof(data)-used,",\"model\":\"%s\",\"points\":[",value->model);
|
||||
for(unsigned i=0;i<value->count;i++)used+=snprintf(data+used,sizeof(data)-used,"%s{\"position\":%.12g,\"region\":%u}",i?",":"",value->points[i].position,value->points[i].region);
|
||||
used+=snprintf(data+used,sizeof(data)-used,"]");
|
||||
}
|
||||
used+=snprintf(data+used,sizeof(data)-used,"}\n");
|
||||
if(snprintf(temp,sizeof(temp),"%s.tmp",path)>=(int)sizeof(temp))return false;
|
||||
int fd=open(temp,O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC,0600);if(fd<0)return false;
|
||||
bool ok=write(fd,data,used)==used && fsync(fd)==0;if(close(fd)!=0)ok=false;
|
||||
if(ok)ok=rename(temp,path)==0;
|
||||
if(!ok)unlink(temp);
|
||||
return ok;
|
||||
static inline bool calibration_save(const char *path, const Calibration *value) {
|
||||
char temp[512], data[2048];
|
||||
int used = snprintf(data, sizeof(data), "{\"profile\":\"%s\"", calibration_key(value->kind));
|
||||
if (value->count) {
|
||||
used += snprintf(data + used, sizeof(data) - used, ",\"model\":\"%s\",\"points\":[",
|
||||
value->model);
|
||||
for (unsigned i = 0; i < value->count; i++)
|
||||
used +=
|
||||
snprintf(data + used, sizeof(data) - used, "%s{\"position\":%.12g,\"region\":%u}",
|
||||
i ? "," : "", value->points[i].position, value->points[i].region);
|
||||
used += snprintf(data + used, sizeof(data) - used, "]");
|
||||
}
|
||||
used += snprintf(data + used, sizeof(data) - used, "}\n");
|
||||
if (snprintf(temp, sizeof(temp), "%s.tmp", path) >= (int)sizeof(temp))
|
||||
return false;
|
||||
int fd = open(temp, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, 0600);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
bool ok = write(fd, data, used) == used && fsync(fd) == 0;
|
||||
if (close(fd) != 0)
|
||||
ok = false;
|
||||
if (ok)
|
||||
ok = rename(temp, path) == 0;
|
||||
if (!ok)
|
||||
unlink(temp);
|
||||
return ok;
|
||||
}
|
||||
static inline void calibration_load(PlaybackJson *j,const char *path){
|
||||
int fd=open(path,O_RDONLY|O_NOFOLLOW|O_CLOEXEC);if(fd<0)return;
|
||||
char data[2048];ssize_t n=read(fd,data,sizeof(data)-1);close(fd);
|
||||
if(n<=0 || n==(ssize_t)sizeof(data)-1)return;
|
||||
data[n]=0;calibration_parse(j,data,&calibration);
|
||||
static inline void calibration_load(PlaybackJson *j, const char *path) {
|
||||
int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return;
|
||||
char data[2048];
|
||||
ssize_t n = read(fd, data, sizeof(data) - 1);
|
||||
close(fd);
|
||||
if (n <= 0 || n == (ssize_t)sizeof(data) - 1)
|
||||
return;
|
||||
data[n] = 0;
|
||||
calibration_parse(j, data, &calibration);
|
||||
}
|
||||
static inline void calibration_colors(const unsigned char input[16][3],unsigned char output[16][3]){
|
||||
memcpy(output,input,48);if(calibration_effective()!=CAL_AVERAGE)return;
|
||||
unsigned sum[3]={0};for(unsigned i=0;i<16;i++)for(unsigned c=0;c<3;c++)sum[c]+=input[i][c];
|
||||
for(unsigned i=0;i<16;i++)for(unsigned c=0;c<3;c++)output[i][c]=sum[c]/16;
|
||||
static inline void calibration_colors(const unsigned char input[16][3],
|
||||
unsigned char output[16][3]) {
|
||||
memcpy(output, input, 48);
|
||||
if (calibration_effective() != CAL_AVERAGE)
|
||||
return;
|
||||
unsigned sum[3] = {0};
|
||||
for (unsigned i = 0; i < 16; i++)
|
||||
for (unsigned c = 0; c < 3; c++)
|
||||
sum[c] += input[i][c];
|
||||
for (unsigned i = 0; i < 16; i++)
|
||||
for (unsigned c = 0; c < 3; c++)
|
||||
output[i][c] = sum[c] / 16;
|
||||
}
|
||||
#endif
|
||||
Reference in new issue
Block a user