*/}}
فهرست منبع

EDID support on linux

YimingWu 1 روز پیش
والد
کامیت
0733e07ba1
10فایلهای تغییر یافته به همراه225 افزوده شده و 37 حذف شده
  1. 1 1
      la_animation.c
  2. 6 0
      la_interface.h
  3. 103 14
      la_kernel.c
  4. 5 1
      la_tns.h
  5. 53 6
      la_tns_kernel.c
  6. 1 1
      resources/la_operators.c
  7. 27 3
      resources/la_properties.c
  8. 13 1
      resources/la_templates.c
  9. 12 6
      resources/la_tns_shaders.cpp
  10. 4 4
      resources/la_widgets.c

+ 1 - 1
la_animation.c

@@ -734,7 +734,7 @@ void la_AnimationActionDrawCanvas(laBoxedTheme *bt, laAction *aa, laUiItem* ui){
         cx=tl+(aa->PlayHead*aa->FrameCount+FrameFix)*FW*ex->ZoomX-ex->PanX; tnsVertex2d(cx, ui->U); tnsVertex2d(cx, ui->B);
         tnsPackAs(GL_LINES); glLineWidth(3); tnsFlush(); glLineWidth(1);
 
-        char buf[32]; sprintf(buf,"%d",curframe); real tlen=tnsStringGetDimension(buf,0,0,0,0,0)+LA_M+LA_M;
+        char buf[32]; sprintf(buf,"%d",curframe); real tlen=tnsStringGetDimension(buf,0,0,0,0,0,1)+LA_M+LA_M;
         tnsColor4dv(laAccentColor(LA_BT_TEXT_ACTIVE));
         tnsVertex2d(cx, ui->U+LA_RH); tnsVertex2d(cx+tlen, ui->U+LA_RH);
         tnsVertex2d(cx+tlen, ui->U); tnsVertex2d(cx, ui->U);

+ 6 - 0
la_interface.h

@@ -726,6 +726,10 @@ STRUCTURE(laScreen){
     int RoughDPI;
     int ColorSpace;
     int x,y,w,h;
+    real gamma;
+    real primaries[6];
+    real whitepoint[2];
+    float xyz2rgb[9];
 };
 
 NEED_STRUCTURE(laLayout);
@@ -759,6 +763,7 @@ STRUCTURE(laWindow){
     int IsFullScreen;
     int Redraw;
     int RedrawTouched;
+    laScreen* WhichScreen;
 
     laPropPack PP;
     laPropStep FakePS;
@@ -2445,6 +2450,7 @@ void laRemoveToolbox(laInputMapping* im);
 laCustomSignal* laNewCustomSignal(char* Name, int Signal);
 void laRemoveCustomSignal(laCustomSignal* cs);
 
+void la_EnsureScreenColorMatrix(laScreen* s);
 void la_RemoveScreen(laScreen*s);
 laScreen* laGetWindowScreen(laWindow* w);
 laWindow *laDesignWindow(int X, int Y, int W, int H);

+ 103 - 14
la_kernel.c

@@ -818,15 +818,16 @@ void la_PerfDraw(){
 }
 
 laScreen* la_EnsureScreen(char* Name, int mmw, int mmh, int x, int y, int w, int h,int dpi){ if(!Name || !Name[0]) return 0;
+    laScreen* uses=0;
     for(laScreen* s=MAIN.Screens.pFirst;s;s=s->Item.pNext){
-        if(strSame(SSTR(s->Name),Name)){ s->x=x;s->y=y;s->w=w;s->h=h; return s; }
+        if(strSame(SSTR(s->Name),Name)){ s->x=x;s->y=y;s->w=w;s->h=h; uses=s; break; }
     }
-    laScreen* s=memAcquire(sizeof(laScreen)); lstAppendItem(&MAIN.Screens,s);
-    strSafeSet(&s->Name,Name);
-    strSafePrint(&s->Description,"%dmm x %dmm with %dx%d ~%ddpi",mmw,mmh,w,h,dpi); s->RoughDPI=dpi;
-    s->x=x;s->y=y;s->w=w;s->h=h;
+    if(!uses){ uses=memAcquire(sizeof(laScreen)); lstAppendItem(&MAIN.Screens,uses); }
+    strSafeSet(&uses->Name,Name); strSafeDestroy(&uses->Description);
+    strSafePrint(&uses->Description,"%dmm x %dmm with %dx%d ~%ddpi",mmw,mmh,w,h,dpi); uses->RoughDPI=dpi;
+    uses->x=x;uses->y=y;uses->w=w;uses->h=h;
     laNotifyUsers("la.user_preferences.screens");
-    return s;
+    return uses;
 }
 void la_RemoveScreen(laScreen*s){
     strSafeDestroy(&s->Description); strSafeDestroy(&s->Name);
@@ -852,7 +853,84 @@ laScreen* laGetWindowScreen(laWindow* w){
     return maxs;
 }
 
+void la_EnsureScreenColorMatrix(laScreen* s){
+    real* usemat; tnsMatrix44d mat,matinv;
+    if(s->whitepoint[0]==0.0f || s->primaries[0]==0.0f || s->gamma==0.0f){
+        logPrint("    Fallback to sRGB configurations\n");
+        s->gamma=2.2;
+        static const tnsMatrix44d srgbmat={3.2404542,-1.5371385,-0.4985314,
+                                           -0.9692660,1.8760108,0.0415560,
+                                           0.0556434,-0.2040259,1.0572252};
+        usemat=srgbmat;
+    }else{
+        tnsComputeMatrixRGB2XYZ(mat,&s->primaries[0],&s->primaries[2],&s->primaries[4],s->whitepoint);
+        tnsInverse33d(matinv,mat); usemat=matinv;
+    }
+    for(int i=0;i<9;i++){ s->xyz2rgb[i]=usemat[i]; }
+}
+void la_UseWindowColorCorrection(laWindow* w){
+    if(w->OutputColorSpace==255){
+        if(!w->WhichScreen){ memAssignRef(w,&w->WhichScreen,laGetWindowScreen(w)); }
+        if(w->WhichScreen){
+            tnsUniformOutputColorCorrection(T->immShader,w->WhichScreen->gamma, w->WhichScreen->xyz2rgb);
+        }
+    }
+}
+
+#define EDID_MAX_SIZE 256
 #ifdef LA_LINUX
+void la_TryGettingEDIDColors(XRROutputInfo* output, laScreen* s){
+    if(s->gamma!=0){ return; }
+    Atom edid_atom = XInternAtom(MAIN.dpy, "EDID", True);
+    if (edid_atom != None) {
+        unsigned char *edid_data = NULL;
+        Atom actual_type;
+        int actual_format;
+        unsigned long nitems, bytes_after;
+
+        if (XRRGetOutputProperty(MAIN.dpy, output, edid_atom, 0, EDID_MAX_SIZE,
+                                False, False, AnyPropertyType, &actual_type, &actual_format,
+                                &nitems, &bytes_after, &edid_data) == Success &&
+            edid_data && nitems > 0) {
+            logPrint("    %d bytes of EDID data is available\n",nitems);
+            real gamma = edid_data[23]==255?0.0f:((real)(edid_data[23]+100))/100.0f;
+            uint8_t rx_low = (edid_data[0x19] >> 6) & 0x03;
+            uint8_t ry_low = (edid_data[0x19] >> 4) & 0x03;
+            uint8_t gx_low = (edid_data[0x19] >> 2) & 0x03;
+            uint8_t gy_low = edid_data[0x19] & 0x03;
+            uint8_t bx_low = (edid_data[0x1A] >> 6) & 0x03;
+            uint8_t by_low = (edid_data[0x1A] >> 4) & 0x03;
+            uint8_t wx_low = (edid_data[0x1A] >> 2) & 0x03;
+            uint8_t wy_low = edid_data[0x1A] & 0x03;
+            double red_x   = ((edid_data[0x1B] << 2) | rx_low) / 1024.0;
+            double red_y   = ((edid_data[0x1C] << 2) | ry_low) / 1024.0;
+            double green_x = ((edid_data[0x1D] << 2) | gx_low) / 1024.0;
+            double green_y = ((edid_data[0x1E] << 2) | gy_low) / 1024.0;
+            double blue_x  = ((edid_data[0x1F] << 2) | bx_low) / 1024.0;
+            double blue_y  = ((edid_data[0x20] << 2) | by_low) / 1024.0;
+            double white_x = ((edid_data[0x21] << 2) | wx_low) / 1024.0;
+            double white_y = ((edid_data[0x22] << 2) | wy_low) / 1024.0;
+            s->gamma=gamma;
+            s->primaries[0]=red_x;    s->primaries[1]=red_y;
+            s->primaries[2]=green_x;  s->primaries[3]=green_y;
+            s->primaries[4]=blue_x;   s->primaries[5]=blue_y;
+            s->whitepoint[0]=white_x; s->whitepoint[1]=white_y;
+            laSafeString* additional=0; strSafePrint(&additional,"\n"
+                "    Gamma: %0.4f\n"
+                "    Primaries:\n"
+                "        (%04lf, %04lf)\n"
+                "        (%04lf, %04lf)\n"
+                "        (%04lf, %04lf)\n"
+                "    White point:\n"
+                "        (%04lf, %04lf)\n", gamma, LA_COLOR3(s->primaries),LA_COLOR3(&s->primaries[3]),s->whitepoint[0],s->whitepoint[1]);
+            //strSafeAppend(&s->Description,SSTR(additional));
+            logPrint("Color calibration info:%s",SSTR(additional));
+            strSafeDestroy(&additional);
+            XFree(edid_data);
+            la_EnsureScreenColorMatrix(s); if(!s->ColorSpace)s->ColorSpace=255;
+        }
+    }
+}
 int la_GetDPI(Window* root_win){
     XRRScreenResources *screen;
     XRROutputInfo *info;
@@ -863,12 +941,13 @@ int la_GetDPI(Window* root_win){
     screen=XRRGetScreenResources(MAIN.dpy, root_win);
     for(iscres=0;iscres<screen->noutput;iscres++){
         info=XRRGetOutputInfo(MAIN.dpy,screen,screen->outputs[iscres]);
-        if(!info->mm_width || !info->mm_height){ continue; }
-        logPrint("    Xrandr reported output size: %dmm x %dmm\n",info->mm_width,info->mm_height);
+        if(!info->mm_width || !info->mm_height || info->connection!=RR_Connected){ continue; }
+        logPrint("    Output size: %dmm x %dmm\n",info->mm_width,info->mm_height);
         crtc=XRRGetCrtcInfo(MAIN.dpy,screen,info->crtc);
         dpi=(real)crtc->width/(real)info->mm_width*25.4;
         logPrint("    CRTC: %d x %d, around %ddpi\n",crtc->width,crtc->height,dpi);
         laScreen* s=la_EnsureScreen(info->name,info->mm_width,info->mm_height,crtc->x,crtc->y,crtc->width,crtc->height,dpi);
+        la_TryGettingEDIDColors(screen->outputs[iscres],s);
         XRRFreeCrtcInfo(crtc);
         //if(info->connection==RR_Connected){
         //    for (icrtc=0;icrtc<info->ncrtc;icrtc++) {
@@ -878,6 +957,12 @@ int la_GetDPI(Window* root_win){
         //    }
         //}
         XRRFreeOutputInfo(info);
+        
+        //real primaries[6]={0.64, 0.33, 0.3, 0.6, 0.15, 0.06};
+        //real white[2]={0.3127, 0.3290};
+        //tnsMatrix44d mat;
+        //tnsComputeMatrixRGB2XYZ(mat,&primaries[0],&primaries[2],&primaries[4],white);
+        //for(int i=0;i<9;i++){ printf("%lf ",mat[i]); } printf("\n");
     }
     XRRFreeScreenResources(screen);
     return dpi;
@@ -1974,9 +2059,9 @@ void la_CommandResizeWindow(SYSWINDOW hwnd, int x, int y, int w, int h){
     window->W = rcw.right - rcw.left;
     window->H = rcw.bottom - rcw.top;
     window->X = rcw.left; window->Y = rcw.top;
-    if (MAIN.AutoSwitchColorSpace) {
-        laScreen* s = laGetWindowScreen(window);
-        if (s) { window->OutputColorSpace = s->ColorSpace; }
+    if (MAIN.AutoSwitchColorSpace || window->OutputColorSpace==255) {
+        memAssignRef(window,&window->WhichScreen, laGetWindowScreen(window));
+        if (window->WhichScreen && MAIN.AutoSwitchColorSpace) { window->OutputColorSpace = window->WhichScreen->ColorSpace; }
     }
 #endif
 #ifdef LA_LINUX
@@ -1985,9 +2070,9 @@ void la_CommandResizeWindow(SYSWINDOW hwnd, int x, int y, int w, int h){
     window->CW = w; window->CH = h;
     window->W = w; window->H = h;
     window->X = x; window->Y = y;
-    if(MAIN.AutoSwitchColorSpace){
-        laScreen* s = laGetWindowScreen(window);
-        if(s){ window->OutputColorSpace = s->ColorSpace; }
+    if(MAIN.AutoSwitchColorSpace || window->OutputColorSpace==255){
+        memAssignRef(window,&window->WhichScreen, laGetWindowScreen(window));
+        if(window->WhichScreen && MAIN.AutoSwitchColorSpace){ window->OutputColorSpace = window->WhichScreen->ColorSpace; }
     }
 #endif
     la_UpdateUiPlacement(window);
@@ -2688,6 +2773,7 @@ void la_PanelDrawToWindow(laPanel *p, laWindow *w){
         tnsUniformOutputColorSpace(T->immShader,w->OutputColorSpace);
         tnsUniformShowColorOverflowStripes(T->immShader,w->OutputShowStripes);
         tnsUniformColorComposing(T->immShader,w->UseComposing,w->ComposingGamma,w->ComposingBlackpoint,w->OutputProofing);
+        la_UseWindowColorCorrection(w);
     }else{
         tnsUniformOutputColorSpace(T->immShader,0);
         tnsUniformShowColorOverflowStripes(T->immShader,0);
@@ -3089,6 +3175,7 @@ void la_BlockDefDrawSelf(laBlock *b, int CH){
         laWindow* w=MAIN.CurrentWindow;
         tnsUniformOutputColorSpace(T->immShader,w->OutputColorSpace);
         tnsUniformColorComposing(T->immShader,w->UseComposing,w->ComposingGamma,w->ComposingBlackpoint,w->OutputProofing);
+        la_UseWindowColorCorrection(w);
     }else{
         tnsUniformOutputColorSpace(T->immShader,0);
         tnsUniformColorComposing(T->immShader,0,0,0,0);
@@ -3150,6 +3237,7 @@ void la_BlockDefDrawSelfEmpty(laBlock *b, int CH){
         laWindow* w=MAIN.CurrentWindow;
         tnsUniformOutputColorSpace(T->immShader,w->OutputColorSpace);
         tnsUniformColorComposing(T->immShader,w->UseComposing,w->ComposingGamma,w->ComposingBlackpoint,w->OutputProofing);
+        la_UseWindowColorCorrection(w);
     }else{
         tnsUniformOutputColorSpace(T->immShader,0);
         tnsUniformColorComposing(T->immShader,0,0,0,0);
@@ -3304,6 +3392,7 @@ void la_WindowDefDraw(laWindow *w, laBoxedTheme *bt){
             if(MAIN.EnableColorManagement){
                 tnsUniformOutputColorSpace(T->immShader,w->OutputColorSpace);
                 tnsUniformColorComposing(T->immShader,w->UseComposing,w->ComposingGamma,w->ComposingBlackpoint,w->OutputProofing);
+                la_UseWindowColorCorrection(w);
             }else{
                 tnsUniformOutputColorSpace(T->immShader,0);
                 tnsUniformColorComposing(T->immShader,0,0,0,0);

+ 5 - 1
la_tns.h

@@ -118,6 +118,7 @@ struct _tnsShader{
     int iTextureMode,iColorMode,iHCYGamma;
     int iSampleAmount;
     int iInputColorSpace, iOutputColorSpace, iShowStripes, iTexLut, iUseLut;
+    int iOutputGamma, iOutputXYZ2RGB;
     int iComposing, iComposingGamma, iComposingBlackpoint;
     int iDoOffset;
     int iUseHalftone,iHalftoneSize;
@@ -1123,6 +1124,7 @@ void tnsMakeTranslationMatrix44d(tnsMatrix44d mTrans, real x, real y, real z);
 void tnsMakeRotationMatrix44d(tnsMatrix44d m, real angle_rad, real x, real y, real z);
 void tnsMakeScaleMatrix44d(tnsMatrix44d m, real x, real y, real z);
 void tnsMakeViewportMatrix44d(tnsMatrix44d m, real w, real h, real Far, real Near);
+void tnsInverse33d(tnsMatrix44d inverse, tnsMatrix44d mat);
 void tnsInverse44d(tnsMatrix44d inverse, tnsMatrix44d mat);
 void tnsMultiply44d(tnsMatrix44d result, tnsMatrix44d l, tnsMatrix44d r);
 void tnsMakeRotationXMatrix44d(tnsMatrix44d m, real angle_rad);
@@ -1435,6 +1437,7 @@ void tnsUniformColorMode(tnsShader *s, int mode);
 void tnsUniformHCYGamma(tnsShader* s, float Gamma);
 void tnsUniformInputColorSpace(tnsShader* s, int ColorSpace);
 void tnsUniformOutputColorSpace(tnsShader* s, int ColorSpace);
+void tnsUniformOutputColorCorrection(tnsShader* s, real gamma, float xyz2rgb[9]);
 void tnsUniformShowColorOverflowStripes(tnsShader* s, int Show);
 void tnsUniformColorComposing(tnsShader* s, int Composing, real gamma, real blackpoint, int UseLut);
 void tnsUniformUseMultiplyColor(tnsShader* s, int enable);
@@ -1480,7 +1483,7 @@ void tfntResizeFontTexture(tnsFont* f, int size);
 void tnsPushStringClip(int L, int R, int U, int B);
 void tnsPopStringClip();
 
-int tnsStringGetDimension(char* content, uint32_t* contentU, int Count, int WLimit, int* Rows, int UseMono);
+int tnsStringGetDimension(char* content, uint32_t* contentU, int Count, int WLimit, int* Rows, int UseMono, int LineWrap);
 int tnsStringGetWidth(char *content, int Count, int UseMono);
 int tnsStringGetWidthU(uint32_t *contentU, int Count, int UseMono);
 void tnsDrawStringM(char *content, uint32_t* contentU, real Color[4], int L, int R, int T, uint64_t Flags);
@@ -1522,6 +1525,7 @@ void tnsMakeBridgedIndex(unsigned int *result, int num, int revert, int begin);
 void DrawWireRect2dp(real x, real y, real x2, real y2);
 void tnsViewportWithScissor(int x, int y, int w, int h);
 
+void tnsComputeMatrixRGB2XYZ(tnsMatrix44d mat, real* r, real* g, real* b, real* w);
 void tnssRGB2XYZ(tnsVector3d rgb,tnsVector3d xyz);
 void tnsClay2XYZ(tnsVector3d rgb,tnsVector3d xyz);
 void tnsD65P32XYZ(tnsVector3d rgb,tnsVector3d xyz);

+ 53 - 6
la_tns_kernel.c

@@ -360,6 +360,8 @@ void tnsShaderMakeIndex(tnsShader *tns){
     tns->iUseNormal = glGetUniformLocation(program, "UseNormal");
     tns->iOutputColorSpace=glGetUniformLocation(program, "OutputColorSpace");
     tns->iInputColorSpace=glGetUniformLocation(program, "InputColorSpace");
+    tns->iOutputGamma = glGetUniformLocation(program, "OutputGamma");
+    tns->iOutputXYZ2RGB = glGetUniformLocation(program, "OutputXYZ2RGB");
     tns->iTexIsUI=glGetUniformLocation(program, "TexIsUI");
     tns->iTex2IsUI=glGetUniformLocation(program, "Tex2IsUI");
     tns->iTexPremultiplied=glGetUniformLocation(program, "TexPremultiplied");
@@ -959,6 +961,27 @@ void tnsMultiply44d(tnsMatrix44d result, tnsMatrix44d l, tnsMatrix44d r){
         P(i, 3) = ai0 * R(0, 3) + ai1 * R(1, 3) + ai2 * R(2, 3) + ai3 * R(3, 3);
     }
 };
+void tnsInverse33d(tnsMatrix44d inverse, tnsMatrix44d mat){
+#define m(a,b) mat[a*3+b]
+#define minv(a,b) inverse[a*3+b]
+    real det = m(0, 0) * (m(1, 1) * m(2, 2) - m(2, 1) * m(1, 2)) -
+               m(0, 1) * (m(1, 0) * m(2, 2) - m(1, 2) * m(2, 0)) +
+               m(0, 2) * (m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0));
+
+    real invdet = 1 / det;
+
+    minv(0, 0) = (m(1, 1) * m(2, 2) - m(2, 1) * m(1, 2)) * invdet;
+    minv(0, 1) = (m(0, 2) * m(2, 1) - m(0, 1) * m(2, 2)) * invdet;
+    minv(0, 2) = (m(0, 1) * m(1, 2) - m(0, 2) * m(1, 1)) * invdet;
+    minv(1, 0) = (m(1, 2) * m(2, 0) - m(1, 0) * m(2, 2)) * invdet;
+    minv(1, 1) = (m(0, 0) * m(2, 2) - m(0, 2) * m(2, 0)) * invdet;
+    minv(1, 2) = (m(1, 0) * m(0, 2) - m(0, 0) * m(1, 2)) * invdet;
+    minv(2, 0) = (m(1, 0) * m(2, 1) - m(2, 0) * m(1, 1)) * invdet;
+    minv(2, 1) = (m(2, 0) * m(0, 1) - m(0, 0) * m(2, 1)) * invdet;
+    minv(2, 2) = (m(0, 0) * m(1, 1) - m(1, 0) * m(0, 1)) * invdet;
+#undef m
+#undef minv
+}
 void tnsInverse44d(tnsMatrix44d inverse, tnsMatrix44d mat){
     int i, j, k;
     double temp;
@@ -1960,6 +1983,10 @@ void tnsUniformInputColorSpace(tnsShader* s, int ColorSpace){
 void tnsUniformOutputColorSpace(tnsShader* s, int ColorSpace){
     glUniform1i(s->iOutputColorSpace,ColorSpace);
 }
+void tnsUniformOutputColorCorrection(tnsShader* s, real gamma, float xyz2rgb[9]){
+    glUniform1f(s->iOutputGamma,1.0f/gamma);
+    glUniformMatrix3fv(s->iOutputXYZ2RGB,1,1,xyz2rgb);
+}
 void tnsUniformShowColorOverflowStripes(tnsShader* s, int Show){
     glUniform1i(s->iShowStripes,Show);
 }
@@ -3013,7 +3040,7 @@ void tnsPopStringClip(){
     FM->L=sc->L; FM->R=sc->R; FM->U=sc->U; FM->B=sc->B;
 };
 
-int tnsStringGetDimension(char* content, uint32_t* contentU, int Count, int WLimit, int* Rows, int UseMono){
+int tnsStringGetDimension(char* content, uint32_t* contentU, int Count, int WLimit, int* Rows, int UseMono, int LineWrap){
     if((!MAIN.CurrentWindow)||(!MAIN.CurrentWindow->win)||(!FM->UsingFont)) return 0;
     real sx = 0; int sy = FM->UsingFont->height; real MA=FM->UsingFont->MonoAdvance;
     int i, rows=1, advance=1;
@@ -3037,7 +3064,7 @@ int tnsStringGetDimension(char* content, uint32_t* contentU, int Count, int WLim
             fsc = tfntFetchCharacterW(UC, UseMono);
             if(!fsc){continue;};
             real dx=fsc->advx; if(UseMono){ dx/=MA; if(dx<1.01) dx=1; if(dx>1.01)dx=2; dx*=MA; }
-            if(sx+dx > WLimit){ sx = 0; sy += LA_RH; rows++; }
+            if(LineWrap && sx+dx > WLimit){ sx = 0; sy += LA_RH; rows++; }
             sx += dx;
             C += 1;
             if (Count && C == Count) break;
@@ -3049,10 +3076,10 @@ int tnsStringGetDimension(char* content, uint32_t* contentU, int Count, int WLim
     return MaxSX+1;
 }
 int tnsStringGetWidth(char *content, int Count, int UseMono){
-    return tnsStringGetDimension(content, 0, Count, 0, 0, UseMono);
+    return tnsStringGetDimension(content, 0, Count, 0, 0, UseMono,1);
 }
 int tnsStringGetWidthU(uint32_t *contentU, int Count, int UseMono){
-    return tnsStringGetDimension(0, contentU, Count, 0, 0, UseMono);
+    return tnsStringGetDimension(0, contentU, Count, 0, 0, UseMono,1);
 }
 
 int tnsDrawLCD7_ProgressSystem(real x, real y, real Percent){
@@ -3213,6 +3240,7 @@ void tnsDrawStringM(char *content, uint32_t* contentU, real Color[4], int L, int
                 sx=L; sy+=LA_RH;
             }else{
                 if(Flags&LA_TEXT_OVERFLOW_ARROW){ fsc = tfntFetchCharacterW(U'▷', 0); if(!fsc){continue;} sx=R-fsc->advx; BreakNow=1; }
+                elif(Flags&LA_TEXT_USE_NEWLINE){ while(UC && UC!='\n'){ i++; UC = contentU?contentU[i]:laToUnicode(&content[i], &advance); continue; } sx=L; sy+=LA_RH; continue;  }
                 else break;
             }
         }
@@ -3256,10 +3284,10 @@ void tnsDrawStringAutoM(char *content, uint32_t* contentU, real Color[4], int L,
     if (!content && !contentU) return;
     switch (al){
     case LA_TEXT_ALIGN_AUTO:
-    case LA_TEXT_ALIGN_CENTER: LL = L+(R-L-tnsStringGetDimension(content, contentU, 0, 0, 0, Flags&LA_TEXT_MONO)) / 2; if(LL<L) LL=L; break;
+    case LA_TEXT_ALIGN_CENTER: LL = L+(R-L-tnsStringGetDimension(content, contentU, 0, 0, 0, Flags&LA_TEXT_MONO,Flags&LA_TEXT_LINE_WRAP)) / 2; if(LL<L) LL=L; break;
     default:
     case LA_TEXT_ALIGN_LEFT: LL = L; break;
-    case LA_TEXT_ALIGN_RIGHT: LL = R-(tnsStringGetDimension(content, contentU, 0, 0, 0, Flags&LA_TEXT_MONO)); if(LL<L) LL=L; break;
+    case LA_TEXT_ALIGN_RIGHT: LL = R-(tnsStringGetDimension(content, contentU, 0, 0, 0, Flags&LA_TEXT_MONO,Flags&LA_TEXT_LINE_WRAP)); if(LL<L) LL=L; break;
     }
     tnsDrawStringM(content, contentU, Color, LL, R, T, Flags);
 }
@@ -4630,6 +4658,25 @@ void tnsPrintMaterials(){
 
 extern LA MAIN;
 
+void tnsComputeMatrixRGB2XYZ(tnsMatrix44d mat, real* r, real* g, real* b, real* w){
+    real xr=r[0]/r[1];
+    real yr=1;
+    real zr=(1.0f-r[0]-r[1])/r[1];
+    real xg=g[0]/g[1];
+    real yg=1;
+    real zg=(1.0f-g[0]-g[1])/g[1];
+    real xb=b[0]/b[1];
+    real yb=1;
+    real zb=(1.0f-b[0]-b[1])/b[1];
+    real xw=w[0]/w[1];
+    real yw=1;
+    real zw=(1.0f-w[0]-w[1])/w[1];
+    tnsMatrix44d mainv,ma={xr,xg,xb,yr,yg,yb,zr,zg,zb}; tnsInverse33d(mainv,ma);
+    real s[3],wp[3]={xw,yw,zw}; tnsApplyRotation33d(s,mainv,wp);
+    mat[0]=s[0]*xr; mat[3]=s[0]*yr; mat[6]=s[0]*zr;
+    mat[1]=s[1]*xg; mat[4]=s[1]*yg; mat[7]=s[1]*zg;
+    mat[2]=s[2]*xb; mat[5]=s[2]*yb; mat[8]=s[2]*zb;
+}
 void tnssRGB2XYZ(tnsVector3d rgb,tnsVector3d xyz){
 	tnsMatrix44d mat={0.4124564,0.3575761,0.1804375,
 				      0.2126729,0.7151522,0.0721750,

+ 1 - 1
resources/la_operators.c

@@ -2693,7 +2693,7 @@ static int la_InitProofLUT(void** lut, cmsHPROFILE cmyk_profile, cmsHPROFILE rgb
                 p[0]=((real)i)/LA_LUT_VAL; p[1]=((real)j)/LA_LUT_VAL; p[2]=((real)k)/LA_LUT_VAL;
             }
         }
-    }
+    }    
 
     *lut=malloc(sizeof(char)*3*LA_LUT_PIXCOUNT);
     char* table = *lut;

+ 27 - 3
resources/la_properties.c

@@ -562,7 +562,7 @@ void laset_AutoSwitchColorSpace(void* unused, int enabled){
     if(enabled){
         for(laWindow* w=MAIN.Windows.pFirst;w;w=w->Item.pNext){
             laScreen* s = laGetWindowScreen(w);
-            if(s){ w->OutputColorSpace = s->ColorSpace; laNotifyInstanceUsers(w); }
+            if(s){ w->OutputColorSpace = s->ColorSpace; laNotifyInstanceUsers(w); memAssignRef(w,&w->WhichScreen,s); }
         }
     }
     laNotifyUsers("la.user_preferences.auto_switch_color_space");
@@ -572,10 +572,22 @@ void laset_ScreenColorSpace(laScreen* s, int space){
     if(MAIN.AutoSwitchColorSpace){
         for(laWindow* w=MAIN.Windows.pFirst;w;w=w->Item.pNext){
             laScreen* s = laGetWindowScreen(w);
-            if(s){ w->OutputColorSpace = s->ColorSpace; laNotifyInstanceUsers(w); }
+            if(s){ w->OutputColorSpace = s->ColorSpace; laNotifyInstanceUsers(w); memAssignRef(w,&w->WhichScreen,s); }
         }
     }
 }
+void lasetarr_ScreenPrimaries(laScreen* s, real* arr){
+    memcpy(s->primaries,arr,sizeof(real)*6); la_EnsureScreenColorMatrix(s);
+}
+void lasetarr_ScreenWhitePoint(laScreen* s, real* arr){
+    memcpy(s->whitepoint,arr,sizeof(real)*2); la_EnsureScreenColorMatrix(s);
+}
+void laset_ScreenGamma(laScreen* s, real a){
+    s->gamma=a; la_EnsureScreenColorMatrix(s);
+}
+void lapost_Screen(laScreen* s){
+    la_EnsureScreenColorMatrix(s);
+}
 
 void laset_WindowColorSpace(laWindow* w, int space){ w->OutputColorSpace=space; MAIN.LutNeedsRefreshing=1; if(w->win) laRedrawCurrentWindow(); }
 void laset_WindowOutputProofing(laWindow* w, int v){ w->OutputProofing=v; MAIN.LutNeedsRefreshing=1; if(w->win) laRedrawCurrentWindow(); }
@@ -759,6 +771,7 @@ void lapost_Block(laBlock *b){
 void lapost_UserPreferences(void* unused){
     //MAIN.ScaledUiRowHeight=MAIN.UiRowHeight;
     //tnsInvalidateFontCache();
+    la_GetDPI(MAIN.win);
 }
 void laset_ColorPickerGamma(void* unused, real gamma){
     MAIN.ColorPickerGamma=gamma; laRedrawCurrentWindow();
@@ -2024,6 +2037,9 @@ void la_RegisterInternalProps(){
                 laAddEnumItemAs(ep, "SRGB", "sRGB", "Standard sRGB diplay", TNS_COLOR_SPACE_SRGB, 0);
                 laAddEnumItemAs(ep, "CLAY", "Clay", "Clay color space (AdobeRGB 1998 compatible)", TNS_COLOR_SPACE_CLAY, 0);
                 laAddEnumItemAs(ep, "D65_P3", "D65 P3", "D65 P3 color space", TNS_COLOR_SPACE_D65_P3, 0);
+#ifdef LA_LINUX
+                laAddEnumItemAs(ep, "EDID", "EDID", "Use the color space specified by the current monitor's EDID", 255, 0);
+#endif
             }
             ep = laAddEnumProperty(p, "output_proofing", "Proofing", "Show soft proofing in this window", 0,0,0,0,0,offsetof(laWindow, OutputProofing), 0,laset_WindowOutputProofing, 0,0,0,0,0,0,0,0);{
                 laAddEnumItemAs(ep, "NONE", "None", "Not doing soft proofing", 0, U'🖵');
@@ -2043,13 +2059,21 @@ void la_RegisterInternalProps(){
             laAddIntProperty(p, "is_fullscreen", "Is Fullscreen", "Is the window fullscreen", 0,0,0,0,0,0,0,0,offsetof(laWindow, IsFullScreen),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
         }
 
-        p = laAddPropertyContainer("la_screen","Screen","Screen detected from system",0,0,sizeof(laScreen),0,0,1);{
+        p = laAddPropertyContainer("la_screen","Screen","Screen detected from system",0,0,sizeof(laScreen),lapost_Screen,0,1);{
             laAddStringProperty(p,"name","Name","Name of the screen",LA_WIDGET_STRING_PLAIN,0,0,0,1,offsetof(laScreen,Name),0,0,0,0,LA_READ_ONLY|LA_AS_IDENTIFIER);
             laAddStringProperty(p,"description","Description","Desctiption of this screen",LA_WIDGET_STRING_MONO_PLAIN,0,0,0,1,offsetof(laScreen,Description),0,0,0,0,LA_READ_ONLY);
+            static const real d65[2]={0.3127, 0.3290};
+            static const real primaries[6]={0.64, 0.34, 0.30, 0.60, 0.15, 0.06};
+            laAddFloatProperty(p,"white_point","White Point","White point",0,"Xw,Yw",0,0,0,0.001,0,d65,offsetof(laScreen,whitepoint),0,0,2,0,0,0,0,lasetarr_ScreenWhitePoint,0,0,0);
+            laAddFloatProperty(p,"primaries","Primaries","Primaries",0,"Xr,Yr,Xg,Yg,Xb,yb",0,0,0,0.001,0,primaries,offsetof(laScreen,primaries),0,0,6,0,0,0,0,lasetarr_ScreenPrimaries,0,0,0);
+            laAddFloatProperty(p,"gamma","Gamma","Gamma",0,0,0,0,0,0.001,0,0,offsetof(laScreen,gamma),0,laset_ScreenGamma,0,0,0,0,0,0,0,0,0);
             ep = laAddEnumProperty(p, "color_space", "Output Color Space", "Hardware color space of this screen ", 0,0,0,0,0,offsetof(laScreen,ColorSpace), 0,laset_ScreenColorSpace, 0,0,0,0,0,0,0,0);{
                 laAddEnumItemAs(ep, "SRGB", "sRGB", "Standard sRGB diplay", TNS_COLOR_SPACE_SRGB, 0);
                 laAddEnumItemAs(ep, "CLAY", "Clay", "Clay color space (AdobeRGB 1998 compatible)", TNS_COLOR_SPACE_CLAY, 0);
                 laAddEnumItemAs(ep, "D65_P3", "D65 P3", "D65 P3 color space", TNS_COLOR_SPACE_D65_P3, 0);
+#ifdef LA_LINUX
+                laAddEnumItemAs(ep, "EDID", "EDID", "Use the color space specified by the current monitor's EDID", 255, 0);
+#endif
             }
             laAddOperatorProperty(p,"remove","Remove","Remove this screen config","LA_remove_screen_config",0,0);
         }

+ 13 - 1
resources/la_templates.c

@@ -1351,6 +1351,7 @@ void laui_NodeCategory(laUiList *uil, laPropPack *This, laPropPack *Extra, laCol
 
 void laui_Screen(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laSplitColumn(uil,c,0.25); laColumn*cl,*cr; cl=laLeftColumn(c,1); cr=laRightColumn(c,0);
+    laColumn* crl,*crr; laSplitColumn(uil,cr,0.5); crl=laLeftColumn(cr,0); crr=laRightColumn(cr,0);
 
     laUiItem* b=laBeginRow(uil,cr,0,0);
     laShowItem(uil,cr,This,"color_space");
@@ -1358,8 +1359,19 @@ void laui_Screen(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *U
     laShowItem(uil,cr,This,"remove")->Flags|=LA_UI_FLAGS_ICON;
     laEndRow(uil,b);
     b=laOnConditionToggle(uil,cl,0,0,0,0,0);
-    laShowItem(uil,cr,This,"description");
+    laShowItemFull(uil,cr,This,"description",LA_WIDGET_STRING_PLAIN,0,0,0)->Flags|=LA_TEXT_USE_NEWLINE;
+        laShowSeparator(uil,c);
+        laUiItem* b1=laOnConditionToggle(uil,cl,0,0,0,0,0);{ strSafeSet(&b1->ExtraInstructions,"icon=🖉;");
+            laShowItem(uil,crr,This,"primaries")->Flags|=LA_UI_FLAGS_TRANSPOSE;
+            laShowItem(uil,crl,This,"gamma");
+            laShowItem(uil,crl,This,"white_point")->Flags|=LA_UI_FLAGS_TRANSPOSE;
+        }laElse(uil,b1);{
+            laShowItem(uil,crr,This,"primaries")->Flags|=LA_UI_FLAGS_DISABLED|LA_UI_FLAGS_NO_EVENT|LA_UI_FLAGS_TRANSPOSE;
+            laShowItem(uil,crl,This,"gamma")->Flags|=LA_UI_FLAGS_DISABLED|LA_UI_FLAGS_NO_EVENT;
+            laShowItem(uil,crl,This,"white_point")->Flags|=LA_UI_FLAGS_DISABLED|LA_UI_FLAGS_NO_EVENT;
+        }laEndCondition(uil,b1);
     laEndCondition(uil,b);
+    
 }
 void laui_ProofingProfile(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laSplitColumn(uil,c,0.25); laColumn*cl,*cr; cl=laLeftColumn(c,1); cr=laRightColumn(c,0);

+ 12 - 6
resources/la_tns_shaders.cpp

@@ -713,6 +713,8 @@ uniform float ComposingGamma;
 uniform float ComposingBlackpoint;
 uniform int ShowStripes;
 uniform float HCYGamma;
+uniform float OutputGamma;
+uniform mat3 OutputXYZ2RGB;
 
 uniform vec3 uObjectPos;
 uniform vec3 uViewPos;
@@ -730,6 +732,9 @@ layout(location = 2) out vec3 outGPos;
 #with TNS_SHADER_LIBRARY
 #with TNS_GLES_UINT_TEXTURE
 
+vec3 XYZ2Output(vec3 xyz){ return OutputXYZ2RGB*xyz; }
+vec3 to_log_output(vec3 color){ return vec3(pow(color.r,OutputGamma),pow(color.g,OutputGamma),pow(color.b,OutputGamma)); }
+
 vec4 ConvertColorSpace(vec4 _rgba){
 	vec3 color=_rgba.rgb;
     if(InputColorSpace!=OutputColorSpace){
@@ -742,17 +747,18 @@ vec4 ConvertColorSpace(vec4 _rgba){
              if(InputColorSpace==1){ xyz=Clay2XYZ(color); }
         else if(InputColorSpace==0){ xyz=sRGB2XYZ(color); }
         else if(InputColorSpace==2){ xyz=D65P32XYZ(color); }
-             if(OutputColorSpace==0){ color=to_log_srgb(XYZ2sRGB(xyz)); /*_rgba.a=srgb_transfer_function(_rgba.a);*/ }
-        else if(OutputColorSpace==1){ color=to_log_clay(XYZ2Clay(xyz)); /*_rgba.a=pow(_rgba.a,1.0/2.19921875);*/ }
-    	else if(OutputColorSpace==2){ color=to_log_srgb(XYZ2D65P3(xyz)); /*_rgba.a=pow(_rgba.a,1.0/2.19921875);*/ }
+             if(OutputColorSpace==0){ color=to_log_srgb(XYZ2sRGB(xyz)); }
+        else if(OutputColorSpace==1){ color=to_log_clay(XYZ2Clay(xyz)); }
+    	else if(OutputColorSpace==2){ color=to_log_srgb(XYZ2D65P3(xyz)); }
+		else if(OutputColorSpace==255){ color=to_log_output(XYZ2Output(xyz)); }
     }else{
         if(ColorMode==1){ color.y=pow(color.y,max(HCYGamma,1.)); color=okhsl_to_srgb(color); }
         else if(ColorMode==0){
 			color = color;
 		}else{
-                 if(OutputColorSpace==0){ color=to_log_srgb(color); /*_rgba.a=srgb_transfer_function(_rgba.a);*/ }
-            else if(OutputColorSpace==1){ color=to_log_clay(color); /*_rgba.a=pow(_rgba.a,1.0/2.19921875);*/ }
-            else if(OutputColorSpace==2){ color=to_log_srgb(color); /*_rgba.a=pow(_rgba.a,1.0/2.19921875);*/ }
+                 if(OutputColorSpace==0){ color=to_log_srgb(color); }
+            else if(OutputColorSpace==1){ color=to_log_clay(color); }
+            else if(OutputColorSpace==2){ color=to_log_srgb(color); }
         }
     }
     if(ShowStripes!=0){

+ 4 - 4
resources/la_widgets.c

@@ -164,17 +164,17 @@ int la_GroupGetHeight(laUiItem *ui){
 }
 int la_StringPropGetHeight(laUiItem *ui){
     laBoxedTheme *bt = *ui->Type->Theme;
-    if(!(ui->Flags&LA_TEXT_LINE_WRAP)) return 1;
+    if(!(ui->Flags&(LA_TEXT_LINE_WRAP|LA_TEXT_USE_NEWLINE))) return 1;
     char _buf[LA_RAW_CSTR_MAX_LEN]={0}; char* buf=_buf;
     laGetString(&ui->PP, _buf, &buf); int rows=0;
-    int strw=tnsStringGetDimension(buf, 0, 0, ui->TR-ui->TL, &rows, ui->Flags&LA_TEXT_MONO);
+    int strw=tnsStringGetDimension(buf, 0, 0, ui->TR-ui->TL, &rows, ui->Flags&LA_TEXT_MONO, ui->Flags&LA_TEXT_LINE_WRAP);
     return rows;
 }
 int la_LabelHeight(laUiItem *ui){
     int Wrap=ui->Flags&(LA_TEXT_USE_NEWLINE|LA_TEXT_LINE_WRAP);
     if(!Wrap) return 1;
     int rows;
-    tnsStringGetDimension(transLate(ui->Display->Ptr), 0, 0, ui->TR-ui->TL, &rows, ui->Flags&LA_TEXT_MONO);
+    tnsStringGetDimension(transLate(ui->Display->Ptr), 0, 0, ui->TR-ui->TL, &rows, ui->Flags&LA_TEXT_MONO, ui->Flags&LA_TEXT_LINE_WRAP);
     return rows;
 }
 int la_SocketGetHeight(laUiItem *ui){
@@ -225,7 +225,7 @@ int la_StringPropGetMinWidth(laUiItem *ui){
     char _buf[LA_RAW_CSTR_MAX_LEN]={0}; int ExtraW=0; char* buf=_buf;
     if(ui->Type == _LA_UI_STRING_MULTI){ExtraW=2*LA_RH+LA_M;}
     laGetString(&ui->PP, _buf, &buf); int rows=0;
-    int strw=tnsStringGetDimension(buf, 0, 0, 0, &rows, ui->Flags&LA_TEXT_MONO);
+    int strw=tnsStringGetDimension(buf, 0, 0, 0, &rows, ui->Flags&LA_TEXT_MONO, 0);
     int Plain = ui->Flags&LA_UI_FLAGS_PLAIN;
     if((!Plain)&&strw<LA_RH*4){strw=LA_RH*4;}
     return (strw + LA_M + LA_M+ExtraW);