#ifndef HYPERDRM_CALIBRATION_H #define HYPERDRM_CALIBRATION_H #include #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; 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 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 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 && iat(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;icount;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_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