*/}}
Ver Fonte

Optimization, slash, image widget

YimingWu há 1 ano atrás
pai
commit
0d60d3d2b2
9 ficheiros alterados com 194 adições e 17 exclusões
  1. 7 0
      la_interface.h
  2. 35 7
      la_kernel.c
  3. 1 0
      la_resource.c
  4. 15 3
      la_tns.h
  5. 60 0
      la_tns_kernel.c
  6. 2 2
      lagui-config.cmake
  7. 16 5
      resources/la_operators.c
  8. 1 0
      resources/la_translations.c
  9. 57 0
      resources/la_widgets.c

+ 7 - 0
la_interface.h

@@ -376,6 +376,7 @@ STRUCTURE(LA){
     laListHandle PropContainers;
     laRoot DataRoot;
     laPanel *PropMatcherContextP;
+    laPanel* PendingSplash;
 
     laAnimationGlobal Animation;
 
@@ -1008,6 +1009,8 @@ STRUCTURE(laWidget){
 #define LA_TEXT_LCD_16         (1<<27)
 #define LA_TEXT_LCD_7          (1<<28)
 #define LA_UI_FLAGS_NO_TOOLTIP LA_UI_FLAGS_PREFER_BOTTOM
+#define LA_UI_IMAGE_FULL_W     LA_TEXT_MONO
+#define LA_UI_MIN_WIDTH        LA_UI_FLAGS_COLOR_SPACE_CLAY
 
 #define LA_UI_FLAGS_INT_ICON  (LA_UI_FLAGS_NO_DECAL|LA_UI_FLAGS_NO_EVENT|LA_UI_FLAGS_ICON)
 #define LA_UI_FLAGS_PLAIN     (LA_UI_FLAGS_NO_DECAL|LA_UI_FLAGS_NO_EVENT)
@@ -1059,6 +1062,7 @@ extern laWidget *LA_WIDGET_NODE_SOCKET;
 extern laWidget *LA_WIDGET_HEIGHT_ADJUSTER;
 extern laWidget *LA_WIDGET_RAW;
 extern laWidget *LA_WIDGET_MAPPER;
+extern laWidget *LA_WIDGET_IMAGE;
 
 #define LA_CONDITION_TRUE 1
 #define LA_CONDITION_FALSE 2
@@ -1804,6 +1808,7 @@ void laEnsurePanelInBound(laPanel *p,laUiList* uil);
 int laEnclosePanelContent(laPanel *p, laUiList *uil);
 laPanel *laEnableIdlePanel(laPanel *Attachment, laOperator *a, laPropPack *OperatorProps, laUiDefineFunc ReplaceUiDefine, laPropPack *This,
                                int L, int R, int B, int MaxGH, int MaxW, laEvent *e);
+laPanel *laEnableSplashPanel(laUiDefineFunc ReplaceUiDefine, int L, int R, int B, int MaxGH, int MaxW, laEvent* e);
 laPanel *laEnablePropertyPanel(laPanel *Attachment, laOperator *a, laPropPack *OperatorProps, laUiDefineFunc ReplaceUiDefine, laUiDefineFunc FallBackUiDefine, laPropPack *This,
                                int L, int R, int B, int MaxGH, int MaxW, laEvent *e);
 laPanel *laEnableEmptyPropertyPanel(laPanel *Attachment, laOperator *a, int L, int R, int U, int MaxGH, laEvent *e);
@@ -1877,6 +1882,7 @@ laUiItem *laShowLabelDynamic(laUiList *uil, laColumn *c, const char *Content, la
 laUiItem *laShowIcon(laUiList *uil, laColumn *c, laPropPack *Base, const char *Path, laWidget* Widget);
 laUiItem *laShowItem(laUiList *uil, laColumn *c, laPropPack *Base, const char *Path);
 laUiItem *laShowItemFull(laUiList *uil, laColumn *c, laPropPack *Base, const char *Path, laWidget* Widget, char* instructions, laUiDefineFunc Template, int TemplateContext);
+laUiItem *laShowImage(laUiList *uil, laColumn *c, tnsImage* Image, int Height);
 laUiItem *laShowNodeSocket(laUiList *uil, laColumn *c, laPropPack *Base, const char *Path, char* instructions);
 laUiItem *laShowHeightAdjuster(laUiList *uil, laColumn *c, laPropPack *Base, const char *Path, char* instructions);
 laUiItem *laShowDetachedItem(laPanel *p, laUiList *uil, laColumn *c, laPropPack *Base, const char *Path, const char *Rename, laUiDefineFunc Template, laWidget* Widget);
@@ -2077,6 +2083,7 @@ extern laUiType *_LA_UI_NODE_SOCKET;
 extern laUiType *_LA_UI_HEIGHT_ADJUSTER;
 extern laUiType *_LA_UI_RAW;
 extern laUiType *_LA_UI_MAPPER;
+extern laUiType *_LA_UI_IMAGE;
  
 extern laUiDescriptor _LA_UI_DESCRIPTOR_REF_UI;
 extern laUiDescriptor _LA_UI_DESCRIPTOR_REF_UIE;

+ 35 - 7
la_kernel.c

@@ -1174,10 +1174,8 @@ void laEnsurePanelInBound(laPanel *p, laUiList *uil){
     int uih=uil->B + bt->TM+bt->BM;
     if (p->BoundUi && !(p->SB||p->ST)) PH = TNS_MAX2(uih, p->MinH);
     if (p->MaxH && PH > p->MaxH) p->TH = p->MaxH;
-    else if (p->MinH && PH < p->MinH)
-        p->TH = p->MinH;
-    else
-        p->TH = PH;
+    else if (p->MinH && PH < p->MinH) p->TH = p->MinH;
+    else p->TH = PH;
 
     p->H = p->TH;
 
@@ -1200,6 +1198,8 @@ void laEnsurePanelInBound(laPanel *p, laUiList *uil){
     if (p->SB && p->TH != ch - p->TY - p->SB) p->Refresh = LA_TAG_RECALC;
     if (p->SB) p->TH = ch - p->TY - p->SB;
 
+    if(p->CloseWhenMovedOut==2){ p->X=(cw-p->W)/2; p->Y=(ch-p->H)/2; }
+
     p->TX = p->X; p->TY = p->Y;
     p->TW = p->W; p->TH = p->H;
 
@@ -2496,6 +2496,29 @@ laPanel *laEnableIdlePanel(laPanel *Attachment, laOperator *a, laPropPack *Opera
 
     return p;
 }
+laPanel *laEnableSplashPanel(laUiDefineFunc ReplaceUiDefine, int L, int R, int B, int MaxGH, int MaxW, laEvent* e){
+    laPanel *p;
+    int GX, GY, GW, t = 0;
+    int b;
+    laUiDefineFunc def = ReplaceUiDefine;
+    int MinW;
+    if (!def) return;
+    GX = L; GY = B; GW = (R - L) > MaxW ? MaxW : (R - L);
+    p = laDesignPropPanel("TMP", GX, GY, GW, MaxGH, def, 0, 0);
+    p->Mode = LA_PANEL_FLOATING_TOP;
+    p->CloseWhenMovedOut=2; MAIN.PendingSplash=p;
+    laEnclosePanelContent(p, &p->UI);
+    if(MAIN.CurrentWindow->Operators.pFirst){
+        laSetOperatorLocalizer(MAIN.PendingSplash);
+        laInvokeUi(0, "LA_panel_operator", e, MAIN.PendingSplash, 0, 1);
+        MAIN.PendingSplash=0;
+    }
+
+    //laShowPanelWithExpandEffect(p);
+    lstPushItem(&MAIN.CurrentWindow->Panels, p);
+
+    return p;
+}
 laPanel *laEnablePropertyPanel(laPanel *Attachment, laOperator *a, laPropPack *OperatorProps, laUiDefineFunc ReplaceUiDefine, laUiDefineFunc FallBackUiDefine, laPropPack *This,
                                int L, int R, int B, int MaxGH, int MaxW, laEvent *e){
     laOperator *ai = a;
@@ -3287,6 +3310,14 @@ laUiItem *laShowItemFull(laUiList *uil, laColumn *c, laPropPack *Base, const cha
 
     return ui;
 }
+laUiItem *laShowImage(laUiList *uil, laColumn *c, tnsImage* Image, int Height){
+    laUiItem *ui = memAcquireSimple(sizeof(laUiItem));
+    ui->C = c; ui->Extra=Image;
+    ui->Type=_LA_UI_IMAGE; ui->Type->Init(ui);
+    ui->SymbolID=Height;
+    lstAppendItem(&uil->UiItems, ui);
+    return ui;
+}
 laUiItem *laShowNodeSocket(laUiList *uil, laColumn *c, laPropPack *Base, const char *Path, char* instructions){
     return laShowItemFull(uil,c,Base,Path,LA_WIDGET_NODE_SOCKET,instructions,0,0);
 }
@@ -6321,11 +6352,8 @@ int la_HandleEvents(laWindow *w){
     //pthread_spin_lock(&MAIN.csNotifier);
     while (tn = lstPopItem(&MAIN.ThreadNotifiers)){
         //pthread_spin_unlock(&MAIN.csNotifier);
-
         laNotifyUsers(tn->Path);
-
         FreeMem(tn);
-
         //if (MAIN.ThreadNotifiers.pFirst)
             //pthread_spin_lock(&MAIN.csNotifier);
     }

+ 1 - 0
la_resource.c

@@ -55,6 +55,7 @@ laUiType *_LA_UI_NODE_SOCKET;
 laUiType *_LA_UI_HEIGHT_ADJUSTER;
 laUiType *_LA_UI_RAW;
 laUiType *_LA_UI_MAPPER;
+laUiType *_LA_UI_IMAGE;
 
 laUiDefineFunc _LA_SUBPROP_DONT_CARE;
 

+ 15 - 3
la_tns.h

@@ -165,8 +165,14 @@ STRUCTURE(tnsTriangulateEdgeNode){
     tnsRenderLine *RL;
 };
 
-struct _tnsMain
-{
+STRUCTURE(tnsImage){
+    laListItem Item;
+    void* MemPNG;
+    tnsTexture* Texture;
+    int UserCount;
+};
+
+struct _tnsMain {
     laListItem Item;
 
     tnsMatrixStack stack;
@@ -257,6 +263,8 @@ struct _tnsMain
 
     laListHandle RenderBuffers;
     tnsRenderBuffer *ActiveRenderBuffer;
+
+    laListHandle Images;
 };
 
 typedef struct _tnsLineStripPoint tnsLineStripPoint;
@@ -1223,6 +1231,10 @@ int tnsGetTextureMemoryComponetCount(tnsTexture *t);
 
 void tnsUseNormal(int Use);
 
+tnsImage* tnsNewImage(void* MemPNG);
+void tnsUseImage(tnsImage* im);
+void tnsStopUsingImage(tnsImage* im);
+
 void tnsSetRayShaderUniformTextures(tnsOffscreen* doff);
 int tnsInit2DTexture(tnsTexture *t, GLint glInternalFormat, int w, int h, int Multisample);
 tnsTexture *tnsCreate2DTexture(GLint glInternalFormat, int w, int h, int Multisample);
@@ -1237,7 +1249,7 @@ void tnsActiveTexture(GLenum tex);
 void tnsBindTexture(tnsTexture *t);
 void tnsUnbindTexture();
 void tnsUniformUseTexture(tnsShader* s, int mode, int sample);
-void tnsUniformColorMode(tnsShader* s, int mode);
+void tnsUniformColorMode(tnsShader *s, int mode);
 void tnsUniformHCYGamma(tnsShader* s, float Gamma);
 void tnsUniformInputColorSpace(tnsShader* s, int ColorSpace);
 void tnsUniformOutputColorSpace(tnsShader* s, int ColorSpace);

+ 60 - 0
la_tns_kernel.c

@@ -20,6 +20,7 @@
 
 #include <math.h>
 #include "freetype/ftadvanc.h"
+#include <png.h>
 
 char TNS_VERTEX_SIMPLE_MATCAP[] = "#version 330\n"
 "uniform mat4 mProjection;\n"
@@ -2224,6 +2225,65 @@ void tnsReadbackTexture(tnsTexture *t){
     //}
 }
 
+tnsImage* tnsNewImage(void* MemPNG){
+    tnsImage* im=memAcquire(sizeof(tnsImage));
+    im->MemPNG=MemPNG; return im;
+}
+STRUCTURE(tnsPNGRead){
+    unsigned char* data;
+    size_t NextData;
+};
+static void _tns_png_read(png_struct *ps, png_byte *data, png_size_t length){
+    tnsPNGRead *PNGRead = (tnsPNGRead*)png_get_io_ptr(ps);
+    memcpy(data,&PNGRead->data[PNGRead->NextData],length);
+    PNGRead->NextData+=length;
+}
+void tnsUseImage(tnsImage* im){
+    png_structp png_ptr=0;
+    png_infop info_ptr=0;
+    if(im->UserCount==0){
+        tnsPNGRead PNGRead={0};
+
+        png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { goto cleanup_png_read; }
+        info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { goto cleanup_png_read; }
+        if (setjmp(png_jmpbuf(png_ptr))) { goto cleanup_png_read; }
+        PNGRead.data=im->MemPNG; png_set_read_fn(png_ptr, &PNGRead, _tns_png_read);
+        png_read_info(png_ptr, info_ptr);
+        png_set_swap(png_ptr);
+        if (png_get_interlace_type (png_ptr, info_ptr) != PNG_INTERLACE_NONE){ goto cleanup_png_read; }
+
+        png_byte ColorType = png_get_color_type(png_ptr, info_ptr);
+        png_byte BitDepth = png_get_bit_depth(png_ptr, info_ptr);
+        int HasAlpha = ColorType & PNG_COLOR_MASK_ALPHA;
+        if (ColorType == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); }
+        //if (ColorType == PNG_COLOR_TYPE_GRAY && BitDepth < 8) { png_set_expand_gray_1_2_4_to_8(png_ptr); }
+        if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); HasAlpha = 1; }
+        if (BitDepth>8) {png_set_strip_16(png_ptr);} if (BitDepth<8) { png_set_expand(png_ptr); }
+        if (!HasAlpha) { png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER); }
+        if (ColorType == PNG_COLOR_TYPE_GRAY || ColorType == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); }
+        png_read_update_info(png_ptr, info_ptr);
+        if (png_get_bit_depth(png_ptr, info_ptr)!=8) { goto cleanup_png_read; }
+        if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_RGB_ALPHA){ goto cleanup_png_read; }
+        if (png_get_channels(png_ptr, info_ptr) != 4) { goto cleanup_png_read; }
+
+        int W = png_get_image_width(png_ptr, info_ptr);
+        int H = png_get_image_height(png_ptr, info_ptr);
+        
+        unsigned char* buf=calloc(W*4,H*sizeof(unsigned char));
+
+        for(int i=0;i<H;i++){ png_read_row(png_ptr, &buf[((H-i-1)*W)*4], NULL); }
+        im->Texture=tnsCreate2DTexture(GL_RGBA8,W,H,0);
+        tnsBindTexture(im->Texture); glTexSubImage2D(GL_TEXTURE_2D,0,0,0,W,H,GL_RGBA,GL_UNSIGNED_BYTE,buf);
+    }
+cleanup_png_read:
+    if(png_ptr && info_ptr) png_destroy_read_struct(&png_ptr,&info_ptr,0);
+    im->UserCount++;
+}
+void tnsStopUsingImage(tnsImage* im){
+    im->UserCount--;
+    if(im->UserCount<=0){ im->UserCount=0; tnsDeleteTexture(im->Texture); im->Texture=0; }
+}
+
 //====================================================[NEW RENDER KERNEL]
 //=================[Immediate-style api]
 

+ 2 - 2
lagui-config.cmake

@@ -12,7 +12,7 @@ find_package(OpenGL REQUIRED)
 find_package(X11 REQUIRED)
 find_package(Freetype REQUIRED)
 find_package(GLEW REQUIRED)
-#find_package(PNG REQUIRED)
+find_package(PNG REQUIRED)
 
 set(LAGUI_SHARED_LIBS
     ${X11_LIBRARIES}
@@ -21,7 +21,7 @@ set(LAGUI_SHARED_LIBS
     ${OPENGL_LIBRARY}
     ${FREETYPE_LIBRARIES}
     ${X11_Xfixes_LIB}
-    #${PNG_LIBRARY}
+    ${PNG_LIBRARY}
     m X11 Xi Xcursor
     lagui
     CACHE INTERNAL "LaGUI shared libs"

+ 16 - 5
resources/la_operators.c

@@ -1521,6 +1521,12 @@ int OPMOD_Window(laOperator *a, laEvent *e){
         if(e->Type==LA_TIME_DELAY){
             laHideMenuBar(); }
     }else{
+        if(MAIN.PendingSplash){
+            laSetOperatorLocalizer(MAIN.PendingSplash);
+            laInvokeUi(a, "LA_panel_operator", e, MAIN.PendingSplash, 0, 1);
+            laRetriggerOperators();
+            MAIN.PendingSplash=0; return LA_RUNNING;
+        }
 
         laBlock* RootBlock=w->MaximizedBlock?w->MaximizedBlock:w->CurrentLayout->FirstBlock;
         if (la_ProcessBlockEdgeEvent(a, w->CurrentLayout, RootBlock, e)) return LA_RUNNING;
@@ -1529,8 +1535,7 @@ int OPMOD_Window(laOperator *a, laEvent *e){
         if (b && la_ProcessBlockEvent(a, b, e)) return LA_RUNNING;
         
         for (p = w->Panels.pFirst; p; p = p->Item.pNext){
-            int x = e->x;
-            int y = e->y;
+            int x = e->x; int y = e->y;
             laWindowToLocal(0, p, &x, &y);
             if (laIsInPanel(p, x, y) && p->Show){
                 laSetOperatorLocalizer(p);
@@ -1624,6 +1629,12 @@ int OPMOD_Panel(laOperator *a, laEvent *e){
     if(MAIN.DockingPanel){return LA_FINISHED; }
 
     if (!p->Show || (!laIsInPanel(p, x, y) && !uid->TargetIndexVali)){
+        if(p->CloseWhenMovedOut==2){
+            if(e->Type&LA_STATE_DOWN){
+                la_StopUiOperatorService(p); laDestroySinglePanel(p,0); return LA_FINISHED;
+            }
+            return LA_RUNNING;
+        }
         p->ShowCorner=0; laSetWindowCursor(LA_ARROW); return LA_FINISHED_PASS;
     }
 
@@ -1673,8 +1684,8 @@ int OPMOD_Panel(laOperator *a, laEvent *e){
     }
     lstClearPointer(&Locals);
     
-    if (p->Mode && e->Type&LA_MOUSE_EVENT && !uid->TargetIndexVali){ p->ShowCorner=0;
-        if (!p->IsMenuPanel && e->x + e->y > p->W + p->H - LA_SCROLL_W*2){ if(!a->Item.pPrev){ p->ShowCorner=1; laSetWindowCursor(LA_CORNER); }
+    if (p->Mode && e->Type&LA_MOUSE_EVENT && !uid->TargetIndexVali && !p->IsMenuPanel){ p->ShowCorner=0;
+        if (e->x + e->y > p->W + p->H - LA_SCROLL_W*2){ if(!a->Item.pPrev){ p->ShowCorner=1; laSetWindowCursor(LA_CORNER); }
             if(e->Type==LA_L_MOUSE_DOWN){ uid->TargetIndexVali = 2; uid->LastX=e->x;uid->LastY=e->y; }
             return LA_RUNNING;
         }else{
@@ -1737,7 +1748,7 @@ int OPMOD_MenuPanel(laOperator *a, laEvent *e){
     int IsClose=laIsCloseToPanel(p,x,y);
     int IsIn=laIsInPanel(p, x, y);
 
-    if(p->CloseWhenMovedOut && (!IsClose || (!IsIn && e->Type==LA_TIME_IDLE) || e->Type==LA_L_MOUSE_DOWN||e->Type==LA_R_MOUSE_DOWN)){ 
+    if(p->CloseWhenMovedOut && ((!IsClose) || (!IsIn && e->Type==LA_TIME_IDLE) || e->Type==LA_L_MOUSE_DOWN||e->Type==LA_R_MOUSE_DOWN)){ 
         la_StopUiOperatorService(p);
         laDestroySinglePanel(p,0);
         return LA_FINISHED_PASS;

+ 1 - 0
resources/la_translations.c

@@ -19,6 +19,7 @@
 #include "la_5.h"
 
 static const char *entries[]={
+"Show Splash","显示闪屏",
 "🡻 Minimized","🡻 隐藏的面板",
 "Length","长度",
 "Fullscreen","全屏",

+ 57 - 0
resources/la_widgets.c

@@ -61,6 +61,7 @@ laWidget _LA_WIDGET_NODE_SOCKET={0};
 laWidget _LA_WIDGET_HEIGHT_ADJUSTER={0};
 laWidget _LA_WIDGET_RAW={0};
 laWidget _LA_WIDGET_MAPPER={0};
+laWidget _LA_WIDGET_IMAGE={0};
 
 laWidget *LA_WIDGET_FIXED_GROUP=&_LA_WIDGET_FIXED_GROUP;
 laWidget *LA_WIDGET_TAB=&_LA_WIDGET_TAB;
@@ -102,6 +103,7 @@ laWidget *LA_WIDGET_NODE_SOCKET=&_LA_WIDGET_NODE_SOCKET;
 laWidget *LA_WIDGET_HEIGHT_ADJUSTER=&_LA_WIDGET_HEIGHT_ADJUSTER;
 laWidget *LA_WIDGET_RAW=&_LA_WIDGET_RAW;
 laWidget *LA_WIDGET_MAPPER=&_LA_WIDGET_MAPPER;
+laWidget *LA_WIDGET_IMAGE=&_LA_WIDGET_IMAGE;
 
 //============================================== [Draw]
 
@@ -182,6 +184,20 @@ int la_LabelHeight(laUiItem *ui){
 int la_SocketGetHeight(laUiItem *ui){
     if(ui->Flags&(LA_UI_SOCKET_LABEL_N|LA_UI_SOCKET_LABEL_S))return 2; return 1;
 }
+int la_ImageGetHeight(laUiItem *ui){
+    if(ui->Flags&LA_UI_IMAGE_FULL_W){
+        tnsImage* im=ui->Extra;
+        int W=im->Texture->Width, H=im->Texture->Height;
+        real CW=MAIN.CurrentWindow->CW-LA_RH*4, CH=MAIN.CurrentWindow->CH-LA_RH*4;
+        real ra=1;
+        if(W>CW){ real r=W/CW; ra=TNS_MAX2(r,ra); }
+        if(H>CH){ real r=H/CH; ra=TNS_MAX2(r,ra); }
+        W/=ra; H/=ra;
+        return H/LA_RH+1;
+    }
+    if(!ui->SymbolID) ui->SymbolID=3;
+    return ui->SymbolID;
+}
 
 int la_ColorSelectorGetMinWidth(laUiItem *ui){
     return 5*LA_RH;
@@ -193,12 +209,14 @@ int la_ValueGetMinWidth(laUiItem *ui){
 }
 int la_LabelGetMinWidth(laUiItem *ui){
     laBoxedTheme *bt = *ui->Type->Theme;
+    if(ui->Flags&LA_UI_MIN_WIDTH){ return LA_RH; }
     int strw=tnsStringGetWidth(transLate(ui->Display->Ptr), 0, ui->Flags&LA_TEXT_MONO);
     if(ui->Type==_LA_UI_MENU_ROOT && strw<LA_RH)strw=LA_RH;
     return (strw + bt->LM + bt->RM);
 }
 int la_StringPropGetMinWidth(laUiItem *ui){
     laBoxedTheme *bt = *ui->Type->Theme;
+    if(ui->Flags&LA_UI_MIN_WIDTH){ return LA_RH; }
     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+bt->LM;}
     laGetString(&ui->PP, _buf, &buf); int rows=0;
@@ -303,6 +321,20 @@ int la_SocketGetMinWidth(laUiItem *ui){
     if(ui->Flags&(LA_UI_SOCKET_LABEL_S|LA_UI_SOCKET_LABEL_N))return LA_RH*2;
     return LA_RH;
 }
+int la_ImageGetMinWidth(laUiItem *ui){
+    tnsImage* im=ui->Extra;
+    if(!im->Texture) return LA_RH;
+    int W=im->Texture->Width, H=im->Texture->Height;
+    int UseW=LA_RH;
+    if(ui->Flags&LA_UI_IMAGE_FULL_W) UseW=W;
+    else UseW=(int)((real)LA_RH*ui->SymbolID/H*W);
+    real CW=MAIN.CurrentWindow->CW-LA_RH*4, CH=MAIN.CurrentWindow->CH-LA_RH*4;
+    real ra=1;
+    if(UseW>CW){ real r=UseW/CW; ra=TNS_MAX2(r,ra); }
+    if(H>CH){ real r=H/CH; ra=TNS_MAX2(r,ra); }
+    UseW/=ra; H/=ra;
+    return UseW;
+}
 
 void la_SingleLineStringDrawSelection(laUiItem *ui, int Begin, int U, laBoxedTheme *bt, uint32_t *str, laStringEdit *se);
 
@@ -1347,6 +1379,21 @@ void la_MapperDraw(laUiItem *ui, int h){
 
     if(any){ glLineWidth(2); glPointSize(5); tnsFlush(); glPointSize(1); glLineWidth(1); }
 }
+void la_ImageDraw(laUiItem *ui, int h){
+    laBoxedTheme *bt = (*ui->Type->Theme);
+    tnsImage* im=ui->Extra;
+    if(!im->Texture) return;
+    int W=im->Texture->Width, H=im->Texture->Height;
+    int Full=ui->Flags&LA_UI_IMAGE_FULL_W;
+    real r=(real)(ui->R-ui->L)/W; if(r<1){W*=r;H*=r;}
+    real UseW=Full?(W):((real)LA_RH*ui->SymbolID/H*W);
+    real L=0,U=0,UseH=Full?H:(ui->B-ui->U);
+    if(Full){ U=(ui->B-ui->U-H)/2; }
+    if(ui->Flags&LA_TEXT_ALIGN_LEFT){ L=(ui->R-ui->L-UseW)/2; }
+    elif(ui->Flags&LA_TEXT_ALIGN_RIGHT){ L=ui->R-ui->L-UseW; }
+    tnsDraw2DTextureDirectly(im->Texture,ui->L+L,ui->U+U,UseW,UseH);
+    tnsFlush();
+}
 
 void la_ValueMeterDraw(laUiItem *ui, int h){
     laBoxedTheme *bt = (*ui->Type->Theme);
@@ -1497,6 +1544,12 @@ void la_MultiStringInit(laUiItem *ui){
 
     e->HeightCoeff = 10;
 }
+void la_ImageUiInit(laUiItem *ui){
+    tnsImage* im=ui->Extra; tnsUseImage(im);
+}
+void la_ImageUiDestroy(laUiItem *ui){
+    tnsImage* im=ui->Extra; tnsStopUsingImage(im);
+}
 
 void la_RegisterUiTypesBasic(){
     laKeyMapper* km;
@@ -1638,6 +1691,10 @@ void la_RegisterUiTypesBasic(){
     LA_WIDGET_MAPPER->Type=
     _LA_UI_MAPPER = la_RegisterUiType("LA_mapper_default", 0, "LA_value_mapper", &_LA_THEME_COLLECTION_GROUP, la_MapperDraw, la_ColorPickerGetHeight, la_GeneralUiInit, la_GeneralUiDestroy);
 
+    LA_WIDGET_IMAGE->Type=
+    _LA_UI_IMAGE = la_RegisterUiType("LA_image_default", 0, 0, &_LA_THEME_COLLECTION_GROUP, la_ImageDraw, la_ImageGetHeight, la_ImageUiInit, la_ImageUiDestroy);
+    _LA_UI_IMAGE->GetMinWidth=la_ImageGetMinWidth;
+
     _LA_UI_ROW_BEGIN.Theme=&_LA_THEME_BUTTON;
     _LA_UI_ROW_END.Theme=&_LA_THEME_BUTTON;
 }