*/}}
Browse Source

Big image compatibility and ui fixes

YimingWu 3 weeks ago
parent
commit
52044cb693
3 changed files with 119 additions and 52 deletions
  1. 108 52
      ouroperations.c
  2. 2 0
      ourpaint.h
  3. 9 0
      ourtranslations.c

+ 108 - 52
ouroperations.c

@@ -544,8 +544,8 @@ void our_CanvasDrawCropping(OurCanvasDraw* ocd){
     if(Our->Tool==OUR_TOOL_CROP){
         tnsColor4dv(laAccentColor(LA_BT_TEXT));
         tnsVertex2d(Our->X,Our->Y); tnsVertex2d(Our->X+Our->W,Our->Y); tnsVertex2d(Our->X+Our->W,Our->Y-Our->H); tnsVertex2d(Our->X,Our->Y-Our->H);
-        tnsPackAs(GL_LINE_LOOP);
-        glLineWidth(3); tnsFlush(); glLineWidth(1);
+        tnsLineWidth(3); tnsPackAs(GL_LINE_LOOP);
+        tnsLineWidth(1); tnsFlush(); 
     }
 }
 void our_CanvasGetRefString(char* ref){
@@ -591,7 +591,7 @@ void our_CanvasDrawReferenceBlock(OurCanvasDraw* ocd){
     tnsFlush();
 }
 void our_CanvasDrawBrushCircle(OurCanvasDraw* ocd){
-    real colorw[4]={1,1,1,0.3}; real colork[4]={0,0,0,0.3};
+    real colorw[4]={1,1,1,0.5}; real colork[4]={0,0,0,0.5};
     if(Our->Tool==OUR_TOOL_MOVE || (Our->Tool==OUR_TOOL_CROP && Our->ShowBorder)){
         tnsUseImmShader();
         tnsDrawStringM("🤚",0,colork,ocd->Base.OnX-LA_RH,ocd->Base.OnX+10000,ocd->Base.OnY-LA_RH,0);
@@ -604,14 +604,15 @@ void our_CanvasDrawBrushCircle(OurCanvasDraw* ocd){
     if (!Our->CurrentBrush || !l || l->Hide || l->Transparency==1 || l->Lock ||
         (l->AsSketch && Our->SketchMode==2)|| ocd->Base.SelectThrough || (Our->Tool==OUR_TOOL_CROP && !Our->ShowBorder)){
         real d = Radius * 0.707;
-        tnsColor4d(0,0,0,0.3);
+        tnsColor4d(0,0,0,0.5);
         tnsVertex2d(ocd->Base.OnX-d+1, ocd->Base.OnY+d-1); tnsVertex2d(ocd->Base.OnX+d+1, ocd->Base.OnY-d-1);
         tnsVertex2d(ocd->Base.OnX-d+1, ocd->Base.OnY-d-1); tnsVertex2d(ocd->Base.OnX+d+1, ocd->Base.OnY+d-1);
         tnsPackAs(GL_LINES);
-        tnsColor4d(1,1,1,0.3);
+        tnsColor4d(1,1,1,0.5);
         tnsVertex2d(ocd->Base.OnX-d, ocd->Base.OnY+d-1); tnsVertex2d(ocd->Base.OnX+d, ocd->Base.OnY-d-1);
         tnsVertex2d(ocd->Base.OnX-d, ocd->Base.OnY-d-1); tnsVertex2d(ocd->Base.OnX+d, ocd->Base.OnY+d-1);
         tnsPackAs(GL_LINES);
+        tnsLineWidth(1.0);
         return;
     }
     if(Our->ShowBrushName){
@@ -620,29 +621,29 @@ void our_CanvasDrawBrushCircle(OurCanvasDraw* ocd){
         tnsUseNoTexture();
     }
     tnsMakeCircle2d(v,48,ocd->Base.OnX,ocd->Base.OnY,Radius+0.5,0);
-    tnsColor4d(1,1,1,0.3); tnsVertexArray2d(v,48); tnsPackAs(GL_LINE_LOOP);
+    tnsColor4d(1,1,1,0.5); tnsVertexArray2d(v,48); tnsPackAs(GL_LINE_LOOP);
     tnsMakeCircle2d(v,48,ocd->Base.OnX,ocd->Base.OnY,Radius-0.5,0);
-    tnsColor4d(0,0,0,0.3); tnsVertexArray2d(v,48); tnsPackAs(GL_LINE_LOOP);
+    tnsColor4d(0,0,0,0.5); tnsVertexArray2d(v,48); tnsPackAs(GL_LINE_LOOP);
     if(Our->EventHasTwist){
-        tnsColor4d(0,0,0,0.3);
+        tnsColor4d(0,0,0,0.5);
         tnsVertex2d(ocd->Base.OnX+sin(Our->EventTwistAngle+gap)*Radius,ocd->Base.OnY+cos(Our->EventTwistAngle+gap)*Radius);
         tnsVertex2d(ocd->Base.OnX-sin(Our->EventTwistAngle-gap)*Radius,ocd->Base.OnY-cos(Our->EventTwistAngle-gap)*Radius);
         tnsVertex2d(ocd->Base.OnX+sin(Our->EventTwistAngle-gap)*Radius,ocd->Base.OnY+cos(Our->EventTwistAngle-gap)*Radius);
         tnsVertex2d(ocd->Base.OnX-sin(Our->EventTwistAngle+gap)*Radius,ocd->Base.OnY-cos(Our->EventTwistAngle+gap)*Radius);
         tnsPackAs(GL_LINES);
-        tnsColor4d(1,1,1,0.3);
+        tnsColor4d(1,1,1,0.5);
         tnsVertex2d(ocd->Base.OnX+sin(Our->EventTwistAngle)*Radius,ocd->Base.OnY+cos(Our->EventTwistAngle)*Radius);
         tnsVertex2d(ocd->Base.OnX-sin(Our->EventTwistAngle)*Radius,ocd->Base.OnY-cos(Our->EventTwistAngle)*Radius);
         tnsPackAs(GL_LINES);
     }
     if(Our->CurrentBrush && Our->CurrentBrush->VisualOffset > 1e-4){
         tnsMakeCircle2d(v,48,ocd->PointerX,ocd->PointerY,Radius/4+0.5,0);
-        tnsColor4d(1,1,1,0.3); tnsVertexArray2d(v,48); tnsPackAs(GL_LINE_LOOP);
+        tnsColor4d(1,1,1,0.5); tnsVertexArray2d(v,48); tnsPackAs(GL_LINE_LOOP);
         tnsMakeCircle2d(v,48,ocd->PointerX,ocd->PointerY,Radius/4-0.5,0);
-        tnsColor4d(0,0,0,0.3); tnsVertexArray2d(v,48); tnsPackAs(GL_LINE_LOOP);
+        tnsColor4d(0,0,0,0.5); tnsVertexArray2d(v,48); tnsPackAs(GL_LINE_LOOP);
         tnsVertex2d(ocd->PointerX,ocd->PointerY);
         tnsVertex2d(ocd->Base.OnX,ocd->Base.OnY);
-        real vcolor[8]={1,1,1,0.3,0,0,0,0.3};
+        real vcolor[8]={1,1,1,0.3,0,0,0,0.5};
         tnsColorArray4d(vcolor,2); tnsPackAs(GL_LINES);
     }
     tnsLineWidth(1.0);
@@ -719,6 +720,7 @@ void our_CanvasDrawOverlay(laUiItem* ui,int h){
     if(Our->EnableBrushCircle && (!ocd->HideBrushCircle)){ our_CanvasDrawBrushCircle(ocd); }
 
     if(!(ui->Flags&LA_UI_FLAGS_NO_OVERLAY)){
+        real colorw[4]={1,1,1,0.5}; real colork[4]={0,0,0,0.5};
         if(Our->ShowStripes){ int UH=TNS_MIN2(LA_RH,(ui->B-ui->U)/8); real varr[8]; real carr[16];
             tnsUseNoTexture();
             tnsVectorSet4(&varr[0], ui->L,ui->B-UH,ui->R,ui->B-UH);
@@ -739,7 +741,8 @@ void our_CanvasDrawOverlay(laUiItem* ui,int h){
             }
         }
         char buf[128]; sprintf(buf,"%.1lf%%",100.0f/e->ZoomX);
-        tnsDrawStringAuto(buf,laThemeColor(bt,LA_BT_TEXT),ui->L+bt->LM,ui->R-bt->RM,ui->B-LA_RH-bt->BM,0);
+        tnsDrawStringAuto(buf,colork,ui->L+bt->LM+1,ui->R-bt->RM,ui->B-LA_RH-bt->BM+1,0);
+        tnsDrawStringAuto(buf,colorw,ui->L+bt->LM,ui->R-bt->RM,ui->B-LA_RH-bt->BM,0);
     }
     
     la_CanvasDefaultOverlay(ui, h);
@@ -1032,15 +1035,15 @@ void our_TileTextureToImage(OurTexTile* ot, int SX, int SY, int composite, int B
         for(int row=0;row<OUR_TILE_W_USE;row++){
             for(int col=0;col<OUR_TILE_W_USE;col++){
                 if(BlendMode==OUR_BLEND_NORMAL){
-                    our_CanvasAlphaMix(&Our->ImageBuffer[((SY+row)*Our->ImageW+SX+col)*4], &ot->Data[(row*OUR_TILE_W_USE+col)*4],alpha);
+                    our_CanvasAlphaMix(&Our->ImageBuffer[((int64_t)(SY+row)*Our->ImageW+SX+col)*4], &ot->Data[(row*OUR_TILE_W_USE+col)*4],alpha);
                 }elif(BlendMode==OUR_BLEND_ADD){
-                    our_CanvasAdd(&Our->ImageBuffer[((SY+row)*Our->ImageW+SX+col)*4], &ot->Data[(row*OUR_TILE_W_USE+col)*4],alpha);
+                    our_CanvasAdd(&Our->ImageBuffer[((int64_t)(SY+row)*Our->ImageW+SX+col)*4], &ot->Data[(row*OUR_TILE_W_USE+col)*4],alpha);
                 }
             }
         }
     }else{
         for(int row=0;row<OUR_TILE_W_USE;row++){
-            memcpy(&Our->ImageBuffer[((SY+row)*Our->ImageW+SX)*4],&ot->Data[(row*OUR_TILE_W_USE)*4],sizeof(uint16_t)*4*OUR_TILE_W_USE);
+            memcpy(&Our->ImageBuffer[((int64_t)(SY+row)*Our->ImageW+SX)*4],&ot->Data[(row*OUR_TILE_W_USE)*4],sizeof(uint16_t)*4*OUR_TILE_W_USE);
         }
     }
     free(ot->Data); ot->Data=0;
@@ -1052,7 +1055,7 @@ void our_TileImageToTexture(OurTexTile* ot, int SX, int SY){
     int bufsize=sizeof(uint16_t)*(OUR_TILE_W+pl+pr)*(OUR_TILE_W+pu+pb)*4;
     ot->Data=malloc(bufsize); int width=OUR_TILE_W_USE+pl+pr, height=OUR_TILE_W_USE+pu+pb;
     for(int row=0;row<height;row++){
-        memcpy(&ot->Data[((row)*width)*4],&Our->ImageBuffer[((SY+row-pu)*Our->ImageW+SX-pl)*4],sizeof(uint16_t)*4*width);
+        memcpy(&ot->Data[((row)*width)*4],&Our->ImageBuffer[((int64_t)(SY+row-pu)*Our->ImageW+SX-pl)*4],sizeof(uint16_t)*4*width);
     }
     if(!our_BufferAnythingVisible(ot->Data, bufsize/sizeof(uint16_t)/4)){ tnsDeleteTexture(ot->Texture); ot->Texture=0; }
     else{
@@ -1075,6 +1078,7 @@ int our_LayerEnsureImageBuffer(OurLayer* ol, int OnlyCalculate){
     if(!OnlyCalculate){
         if(Our->ImageBuffer) free(Our->ImageBuffer);
         Our->ImageBuffer = calloc(Our->ImageW*4,Our->ImageH*sizeof(uint16_t));
+        if(!Our->ImageBuffer){ return 0; }
     }
     return 1;
 }
@@ -1091,15 +1095,16 @@ int our_CanvasEnsureImageBuffer(){
     Our->ImageX=x; Our->ImageY=y; Our->ImageW=w; Our->ImageH=h;
     if(Our->ImageBuffer) free(Our->ImageBuffer);
     Our->ImageBuffer = calloc(Our->ImageW*4,Our->ImageH*sizeof(uint16_t));
+    if(!Our->ImageBuffer){ return 0; }
     return 1;
 }
 void our_CanvasFillImageBufferBackground(){
-    int count=Our->ImageW*Our->ImageH;
+    int64_t count=Our->ImageW*Our->ImageH;
     real bk[4]; tnsVectorSet3v(bk,Our->BackgroundColor); bk[3]=1;
     Our->BColorU16[0]=bk[0]*65535; Our->BColorU16[1]=bk[1]*65535; Our->BColorU16[2]=bk[2]*65535; Our->BColorU16[3]=65535;
     Our->BColorU8[0]=0.5+bk[0]*255; Our->BColorU8[1]=0.5+bk[1]*255; Our->BColorU8[2]=0.5+bk[2]*255; Our->BColorU8[3]=255;
-    for(int i=0;i<count;i++){
-        uint16_t* p=&Our->ImageBuffer[i*4]; tnsVectorSet4v(p,Our->BColorU16);
+    for(int64_t i=0;i<count;i++){
+        uint16_t* p=&Our->ImageBuffer[(int64_t)i*4]; tnsVectorSet4v(p,Our->BColorU16);
     }
 }
 void our_LayerToImageBuffer(OurLayer* ol, int composite){
@@ -1126,14 +1131,14 @@ void our_GetFinalDimension(int UseFrame, int* x, int* y, int* w, int* h){
 }
 #define GET_FINAL_ROW_TYPE(TYPE,BCOLOR) \
 TYPE* our_GetFinalRow_##TYPE(int UseFrame, int row, int x, int y, int w, int h, TYPE* temp){\
-    if(!UseFrame) return &((TYPE*)Our->ImageBuffer)[Our->ImageW*(Our->ImageH-row-1)*4];\
+    if(!UseFrame) return &((TYPE*)Our->ImageBuffer)[(int64_t)Our->ImageW*(Our->ImageH-row-1)*4];\
     int userow=(h-row-1)-(Our->ImageY-(y-h));\
-    if(userow<0 || userow>=Our->ImageH){ for(int i=0;i<w;i++){ tnsVectorSet4v(&temp[i*4],BCOLOR); } return temp; }\
+    if(userow<0 || userow>=Our->ImageH){ for(int i=0;i<w;i++){ tnsVectorSet4v(&temp[(int64_t)i*4],BCOLOR); } return temp; }\
     int sstart=x>Our->ImageX?(x-Our->ImageX):0, tstart=x>Our->ImageX?0:(Our->ImageX-x);\
     int slen=(x+w>Our->ImageX+Our->ImageW)?(Our->ImageW-sstart):(Our->ImageW-sstart-(Our->ImageX+Our->ImageW-x-w));\
-    for(int i=0;i<tstart;i++){ tnsVectorSet4v(&temp[i*4],BCOLOR); }\
-    for(int i=sstart+slen;i<w;i++){ tnsVectorSet4v(&temp[i*4],BCOLOR); }\
-    memcpy(&temp[tstart*4],&((TYPE*)Our->ImageBuffer)[(Our->ImageW*(userow)+sstart)*4],slen*sizeof(TYPE)*4);\
+    for(int i=0;i<tstart;i++){ tnsVectorSet4v(&temp[(int64_t)i*4],BCOLOR); }\
+    for(int i=sstart+slen;i<w;i++){ tnsVectorSet4v(&temp[(int64_t)i*4],BCOLOR); }\
+    memcpy(&temp[(int64_t)tstart*4],&((TYPE*)Our->ImageBuffer)[(int64_t)(Our->ImageW*(userow)+sstart)*4],slen*sizeof(TYPE)*4);\
     return temp;\
 }
 GET_FINAL_ROW_TYPE(uint16_t,Our->BColorU16)
@@ -1159,25 +1164,33 @@ void our_ImageConvertForExport(int BitDepth, int ColorProfile){
         cmsOpenProfileFromMem(Our->icc_Clay,Our->icc_Clay):cmsOpenProfileFromMem(Our->icc_sRGB,Our->iccsize_sRGB);
 
     NewImage=calloc(Our->ImageW*sizeof(uint8_t),Our->ImageH*4);
-    if(ColorProfile!=OUR_EXPORT_COLOR_MODE_FLAT){
-        if(ColorProfile==OUR_EXPORT_COLOR_MODE_SRGB){ output_buffer_profile=cmsOpenProfileFromMem(Our->icc_sRGB,Our->iccsize_sRGB); }
-        elif(ColorProfile==OUR_EXPORT_COLOR_MODE_CLAY){ output_buffer_profile=cmsOpenProfileFromMem(Our->icc_Clay,Our->iccsize_Clay); }
-        cmsTransform = cmsCreateTransform(input_buffer_profile, TYPE_RGBA_16, input_gamma_profile, TYPE_RGBA_8,
-            INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_COPY_ALPHA|cmsFLAGS_HIGHRESPRECALC);
-        cmsDoTransform(cmsTransform,Our->ImageBuffer,NewImage,Our->ImageW*Our->ImageH);
-        if(input_gamma_profile!=output_buffer_profile){
-            cmsTransform = cmsCreateTransform(input_gamma_profile, TYPE_RGBA_8, output_buffer_profile, TYPE_RGBA_8,
+    if(NewImage){
+        int64_t total_pixels = (int64_t)Our->ImageW*Our->ImageH;
+        if(ColorProfile!=OUR_EXPORT_COLOR_MODE_FLAT && total_pixels<=UINT32_MAX){
+            if(ColorProfile==OUR_EXPORT_COLOR_MODE_SRGB){ output_buffer_profile=cmsOpenProfileFromMem(Our->icc_sRGB,Our->iccsize_sRGB); }
+            elif(ColorProfile==OUR_EXPORT_COLOR_MODE_CLAY){ output_buffer_profile=cmsOpenProfileFromMem(Our->icc_Clay,Our->iccsize_Clay); }
+            cmsTransform = cmsCreateTransform(input_buffer_profile, TYPE_RGBA_16, input_gamma_profile, TYPE_RGBA_8,
                 INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_COPY_ALPHA|cmsFLAGS_HIGHRESPRECALC);
-            cmsDoTransform(cmsTransform,NewImage,NewImage,Our->ImageW*Our->ImageH);
-        }
-    }else{
-        for(int row=0;row<Our->ImageH;row++){
-            for(int col=0;col<Our->ImageW;col++){ uint8_t* p=&NewImage[(row*Our->ImageW+col)*4]; uint16_t* p0=&Our->ImageBuffer[(row*Our->ImageW+col)*4];
-                p[0]=((real)p0[0])/256; p[1]=((real)p0[1])/256; p[2]=((real)p0[2])/256; p[3]=((real)p0[3])/256;
+            cmsDoTransform(cmsTransform,Our->ImageBuffer,NewImage,total_pixels);
+            cmsDeleteTransform(cmsTransform);
+            if(input_gamma_profile!=output_buffer_profile){
+                cmsTransform = cmsCreateTransform(input_gamma_profile, TYPE_RGBA_8, output_buffer_profile, TYPE_RGBA_8,
+                    INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_COPY_ALPHA|cmsFLAGS_HIGHRESPRECALC);
+                cmsDoTransform(cmsTransform,NewImage,NewImage,total_pixels);
+                cmsDeleteTransform(cmsTransform);
+            }
+        }else{
+            if(total_pixels>UINT32_MAX){
+                logPrintNew("Export: [TODO] Image pixel count exceeds UINT32_MAX, not doing any transforms.\n");
+            }
+            for(int row=0;row<Our->ImageH;row++){
+                for(int col=0;col<Our->ImageW;col++){ uint8_t* p=&NewImage[((int64_t)row*Our->ImageW+col)*4]; uint16_t* p0=&Our->ImageBuffer[((int64_t)row*Our->ImageW+col)*4];
+                    p[0]=((real)p0[0])/256; p[1]=((real)p0[1])/256; p[2]=((real)p0[2])/256; p[3]=((real)p0[3])/256;
+                }
             }
         }
     }
-    cmsCloseProfile(input_buffer_profile);cmsCloseProfile(input_gamma_profile);cmsCloseProfile(output_buffer_profile); cmsDeleteTransform(cmsTransform);
+    cmsCloseProfile(input_buffer_profile);cmsCloseProfile(input_gamma_profile);cmsCloseProfile(output_buffer_profile);
     free(Our->ImageBuffer); Our->ImageBuffer=NewImage;
 }
 int our_ImageExportPNG(FILE* fp, int WriteToBuffer, void** buf, int* sizeof_buf, int UseFrame, int BitDepth, int ColorProfile){
@@ -1386,7 +1399,7 @@ int our_LayerImportPNG(OurLayer* l, FILE* fp, void* buf, int InputProfileMode, i
 
     int prog=0,lastprog=0;
     for(int i=0;i<H;i++){
-        png_read_row(png_ptr, &Our->ImageBuffer[((H-i-1+Our->LoadY)*Our->ImageW+Our->LoadX)*4], NULL);
+        png_read_row(png_ptr, &Our->ImageBuffer[((int64_t)(H-i-1+Our->LoadY)*Our->ImageW+Our->LoadX)*4], NULL);
         lastprog=i/100; if(lastprog!=prog){ prog=lastprog; laShowProgress(-1,(real)i/H); }
     }
 
@@ -1405,7 +1418,7 @@ int our_LayerImportPNG(OurLayer* l, FILE* fp, void* buf, int InputProfileMode, i
         output_buffer_profile=cmsOpenProfileFromMem(icc, iccsize);
         if(input_buffer_profile && output_buffer_profile){
             cmsTransform = cmsCreateTransform(input_buffer_profile, TYPE_RGBA_16, output_buffer_profile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0);
-            cmsDoTransform(cmsTransform,Our->ImageBuffer,Our->ImageBuffer,Our->ImageW*Our->ImageH);
+            cmsDoTransform(cmsTransform,Our->ImageBuffer,Our->ImageBuffer,(int64_t)Our->ImageW*Our->ImageH);
         }
     }
 
@@ -1652,7 +1665,7 @@ int our_RenderThumbnail(uint8_t** buf, int* sizeof_buf){
     our_CanvasDrawTextures();
 
     if(Our->ImageBuffer){ free(Our->ImageBuffer); }
-    int bufsize=w*h*sizeof(uint16_t)*4;
+    int bufsize=use_w*use_h*sizeof(uint16_t)*4;
     Our->ImageBuffer=malloc(bufsize);
     tnsBindTexture(off->pColor[0]); glPixelStorei(GL_PACK_ALIGNMENT, 2);
     glGetTextureSubImage(off->pColor[0]->GLTexHandle, 0, 0, 0, 0, use_w, use_h, 1, GL_RGBA, GL_UNSIGNED_SHORT, bufsize, Our->ImageBuffer);
@@ -1660,6 +1673,7 @@ int our_RenderThumbnail(uint8_t** buf, int* sizeof_buf){
     tnsDrawToScreen();
     tnsDelete2DOffscreen(off);
 
+    Our->ImageW = use_w; Our->ImageH = use_h;
     our_ImageConvertForExport(OUR_EXPORT_BIT_DEPTH_8,OUR_EXPORT_COLOR_MODE_CLAY);
 
     png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0);
@@ -1682,9 +1696,9 @@ int our_RenderThumbnail(uint8_t** buf, int* sizeof_buf){
 
     *buf=LayerWrite.data; *sizeof_buf=LayerWrite.NextData;
 
-    free(Our->ImageBuffer);
-    Our->ImageBuffer=0;
+    free(Our->ImageBuffer); Our->ImageBuffer=0;
 
+    Our->ImageW = w; Our->ImageH = h;
     return 1;
 }
 int our_GetFileThumbnail(char* file, uint8_t** buf, int* sizeof_buf){
@@ -1792,6 +1806,17 @@ void our_DoMoving(OurCanvasDraw* cd, real x, real y, int *movedx, int *movedy){
     }
 }
 
+void our_ShowAllocationError(laEvent* e){
+    char buf[256];
+    Our->SaveFailed=1;
+    sprintf(buf, "%s %dx%d.\n",transLate("Can't allocate memory for size"),Our->ImageW,Our->ImageH);
+    logPrintNew("Export: %s",buf);
+    if(e){
+        strcat(buf,transLate("Try erasing some contents to make the canvas smaller.\n"));
+        laEnableMessagePanel(0,0,"Export Error",buf,e->x,e->y,200,e);
+    }
+}
+
 int ourinv_ShowSplash(laOperator* a, laEvent* e){
     our_EnableSplashPanel();return LA_FINISHED;
 }
@@ -1856,13 +1881,14 @@ int ourmod_ExportLayer(laOperator* a, laEvent* e){
     if (a->ConfirmData){
         if (a->ConfirmData->StrData){
             our_LayerClearEmptyTiles(ol);
-            if(!our_LayerEnsureImageBuffer(ol, 0)) return LA_FINISHED;
+            if(!our_LayerEnsureImageBuffer(ol, 0)){ our_ShowAllocationError(e); return LA_FINISHED; }
             FILE* fp=fopen(a->ConfirmData->StrData,"wb");
             if(!fp) return LA_FINISHED;
             laShowProgress(0,-1);
             our_LayerToImageBuffer(ol, 0);
             laShowProgress(0.5,-1);
             our_ImageExportPNG(fp, 0, 0, 0, 0, OUR_EXPORT_BIT_DEPTH_16, OUR_EXPORT_COLOR_MODE_FLAT);
+            if(Our->ImageBuffer){ free(Our->ImageBuffer); Our->ImageBuffer=0; }
             laHideProgress();
             fclose(fp);
         }
@@ -1971,7 +1997,7 @@ int ourmod_ExportImage(laOperator* a, laEvent* e){
     }else{
          if (a->ConfirmData){
             if (a->ConfirmData->Mode==LA_CONFIRM_OK){
-                if(!our_CanvasEnsureImageBuffer()) return LA_FINISHED;
+                if(!our_CanvasEnsureImageBuffer()){ our_ShowAllocationError(e); return LA_FINISHED; }
                 FILE* fp=fopen(ex->FilePath->Ptr,"wb");
                 if(!fp) return LA_FINISHED;
                 static int LayerCount=0; static int CurrentLayer=0; LayerCount=lstCountElements(&Our->Layers); CurrentLayer=0;
@@ -1982,7 +2008,9 @@ int ourmod_ExportImage(laOperator* a, laEvent* e){
                     CurrentLayer++; laShowProgress((real)CurrentLayer/LayerCount,-1);
                 }
                 our_ImageConvertForExport(ex->BitDepth, ex->ColorProfile);
+                if(!Our->ImageBuffer){ our_ShowAllocationError(e); fclose(fp); return LA_FINISHED; }
                 our_ImageExportPNG(fp, 0, 0, 0, Our->ShowBorder, ex->BitDepth, ex->ColorProfile);
+                if(Our->ImageBuffer){ free(Our->ImageBuffer); Our->ImageBuffer=0; }
                 laHideProgress();
                 fclose(fp);
             }
@@ -2224,9 +2252,10 @@ OurColorPallette* our_NewPallette(char* Name){
 OurColorItem* our_PalletteNewColor(OurColorPallette* cp,tnsVector3d Color){
     OurColorItem* ci=memAcquire(sizeof(OurColorItem)); memAssignRef(ci,&ci->Parent,cp);
     tnsVectorSet3v(ci->Color,Color); lstAppendItem(&cp->Colors,ci); return ci;
+    laMarkMemChanged(cp);
 }
 void our_PalletteRemoveColor(OurColorItem* ci){
-    lstRemoveItem(&ci->Parent->Colors,ci); memLeave(ci);
+    lstRemoveItem(&ci->Parent->Colors,ci); memLeave(ci); laMarkMemChanged(ci->Parent);
 }
 void our_RemovePallette(OurColorPallette* cp){
     strSafeDestroy(&cp->Name); while(cp->Colors.pFirst){ our_PalletteRemoveColor(cp->Colors.pFirst); }
@@ -2308,6 +2337,21 @@ int ourinv_ClearEmptyTiles(laOperator* a, laEvent* e){
     return LA_FINISHED;
 }
 
+int ourgetstate_Canvas(void* unused_canvas){
+    int level; laMemNodeHyper* m=memGetHead(Our->CanvasSaverDummyList.pFirst,&level); if(!m || level!=2) return -1;
+    if(m->Modified || !m->FromFile) return LA_BT_WARNING;
+    return -1;
+}
+int ourgetstate_Brush(OurBrush* brush){
+    int level; laMemNodeHyper* m=memGetHead(brush,&level); if(!m || level!=2) return -1;
+    if(m->Modified || !m->FromFile) return LA_BT_WARNING;
+    return -1;
+}
+int ourgetstate_Pallette(OurColorPallette* pallette){
+    int level; laMemNodeHyper* m=memGetHead(pallette,&level); if(!m || level!=2) return -1;
+    if(m->Modified || !m->FromFile) return LA_BT_WARNING;
+    return -1;
+}
 void* ourgetraw_FileThumbnail(void* unused, int* r_size, int* r_is_copy){
     void* buf=0;
     if(our_RenderThumbnail(&buf, r_size)){ *r_is_copy=1; return buf; }
@@ -2343,7 +2387,7 @@ void* ourget_LayerImage(OurLayer* l, int* r_size, int* r_is_copy){
     void* buf=0; if(!l->Item.pPrev){ LayerCount=lstCountElements(&Our->Layers); CurrentLayer=0; }
     CurrentLayer++; laShowProgress((real)CurrentLayer/LayerCount,-1);
     our_LayerClearEmptyTiles(l);
-    if(!our_LayerEnsureImageBuffer(l, 0)){ *r_is_copy=0; return 0; }
+    if(!our_LayerEnsureImageBuffer(l, 0)){ our_ShowAllocationError(0); *r_is_copy=0; return 0; }
     our_LayerToImageBuffer(l, 0);
     if(our_ImageExportPNG(0,1,&buf,r_size, 0, OUR_EXPORT_BIT_DEPTH_16, OUR_EXPORT_COLOR_MODE_FLAT)){ *r_is_copy=1; return buf; }
     *r_is_copy=0; return buf;
@@ -2551,6 +2595,17 @@ void ourPushEverything(){
     laFreeOlderDifferences(0);
     for(OurLayer* ol=Our->Layers.pFirst;ol;ol=ol->Item.pNext){ our_LayerRefreshLocal(ol); }
 }
+void ourPreSave(){
+    Our->SaveFailed=0;
+}
+void ourPostSave(){
+    if(Our->SaveFailed){
+        laMarkMemChanged(Our->CanvasSaverDummyList.pFirst);
+        laEvent e={0}; e.type=LA_MOUSEMOVE;
+        our_ShowAllocationError(&e);
+    }
+    Our->SaveFailed=0;
+}
 void ourCleanUp(){
     while(Our->Layers.pFirst){ our_RemoveLayer(Our->Layers.pFirst,1); }
     while(Our->Brushes.pFirst){ our_RemoveBrush(Our->Brushes.pFirst); }
@@ -2634,7 +2689,7 @@ void ourRegisterEverything(){
 
     pc=laAddPropertyContainer("our_paint","Our Paint","OurPaint main",0,0,sizeof(OurPaint),0,0,1);
     laAddRawProperty(pc,"thumbnail","Thumbnail","Thumbnail of this file",0,0,ourgetraw_FileThumbnail,oursetraw_FileThumbnail,LA_READ_ONLY);
-    laAddSubGroup(pc,"canvas","Canvas","OurPaint canvas","our_canvas",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
+    laAddSubGroup(pc,"canvas","Canvas","OurPaint canvas","our_canvas",0,0,0,0,0,0,0,0,ourgetstate_Canvas,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc,"tools","Tools","OurPaint tools","our_tools",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc,"preferences","Preferences","OurPaint preferences","our_preferences",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddFloatProperty(pc,"current_color","Current Color","Current color used to paint",0,"R,G,B",0,1,0,0.05,0.8,0,offsetof(OurPaint,CurrentColor),0,0,3,0,0,0,0,0,0,0,LA_PROP_IS_LINEAR_SRGB);
@@ -2692,10 +2747,10 @@ void ourRegisterEverything(){
     
     pc=laAddPropertyContainer("our_tools","Our Tools","OurPaint tools",0,0,sizeof(OurPaint),0,0,1);
     laPropContainerExtraFunctions(pc,0,0,0,ourpropagate_Tools,0);
-    sp=laAddSubGroup(pc,"brushes","Brushes","Brushes","our_brush",0,0,ourui_Brush,offsetof(OurPaint,CurrentBrush),0,0,0,ourset_CurrentBrush,0,0,offsetof(OurPaint,Brushes),0);
+    sp=laAddSubGroup(pc,"brushes","Brushes","Brushes","our_brush",0,0,ourui_Brush,offsetof(OurPaint,CurrentBrush),0,0,0,ourset_CurrentBrush,ourgetstate_Brush,0,offsetof(OurPaint,Brushes),0);
     sp->UiFilter=ourfilter_BrushInPage;
     laAddSubGroup(pc,"current_brush","Current Brush","Current brush","our_brush",0,0,0,offsetof(OurPaint,CurrentBrush),ourget_FirstBrush,0,laget_ListNext,ourset_CurrentBrush,0,0,0,LA_UDF_REFER);
-    sp=laAddSubGroup(pc,"pallettes","Pallettes","Pallettes","our_pallette",0,0,ourui_Pallette,offsetof(OurPaint,CurrentPallette),0,0,0,ourset_CurrentPallette,0,0,offsetof(OurPaint,Pallettes),0);
+    sp=laAddSubGroup(pc,"pallettes","Pallettes","Pallettes","our_pallette",0,0,ourui_Pallette,offsetof(OurPaint,CurrentPallette),0,0,0,ourset_CurrentPallette,ourgetstate_Pallette,0,offsetof(OurPaint,Pallettes),0);
     //sp->UiFilter=ourfilter_BrushInPage;
     laAddSubGroup(pc,"current_pallette","Current Pallette","Current pallette","our_pallette",0,0,0,offsetof(OurPaint,CurrentPallette),ourget_FirstPallette,0,laget_ListNext,ourset_CurrentPallette,0,0,0,LA_UDF_REFER);
     
@@ -2893,6 +2948,7 @@ void ourRegisterEverything(){
 
     laSetFrameCallbacks(ourPreFrame,0,0);
     laSetDiffCallback(ourPushEverything);
+    laSetSaveCallback(ourPreSave, ourPostSave);
     laSetCleanupCallback(ourCleanUp);
 
     ourMakeTranslations();

+ 2 - 0
ourpaint.h

@@ -408,6 +408,8 @@ STRUCTURE(OurPaint){
 
     real xmin,xmax,ymin,ymax; // stroke bbox for undo region
     int ResetBrush;
+    
+    int SaveFailed;
 
     uint16_t *ImageBuffer;
     int ImageW,ImageH,ImageX,ImageY,LoadX,LoadY,TempLoadX,TempLoadY;

+ 9 - 0
ourtranslations.c

@@ -23,6 +23,15 @@ extern tnsMain* T;
 extern OurPaint *Our;
 
 static const char *entries[]={
+"Can't allocate memory for size", "无法为该尺寸分配内存"
+"Export Error","导出错误",
+"Try erasing some contents to make the canvas smaller.\n","请尝试擦除一些内容以缩小画布尺寸。\n",
+"Moving","移动",
+"Offset","偏移",
+"Visual Offset:","视觉偏移:",
+"Notes","笔记本",
+"Pallettes","调色板",
+"Thumbnail","缩略图",
 "Factor","程度",
 "Paper","纸张",
 "Pattern:","图案:",