Browse code

Background loading of thumbnails

Dario Rodriguez authored on 29/03/2025 20:03:59
Showing 1 changed files
... ...
@@ -30,6 +30,7 @@
30 30
  *      20250319 Add-dirdata button.
31 31
  *      20250320 Fix add-dirdata button appearance.
32 32
  *               Dirdata colors. Select dirdata.
33
+ *      20250329 Background loading of thumbnails.
33 34
  *
34 35
  * Author: Dario Rodriguez dario@darionomono.com
35 36
  * (c) Dario Rodriguez 2025
... ...
@@ -44,6 +45,7 @@
44 45
 #include <dirent.h>
45 46
 #include <sys/stat.h>
46 47
 #include <sys/time.h>
48
+#include <pthread.h>
47 49
 
48 50
 #include "raylib.h"
49 51
 #include "roboto_regular.c"
... ...
@@ -96,6 +98,7 @@
96 98
 #define BLOCKLISTINGBUF 2048
97 99
 #define BLOCKLISTINGELEMS 1024
98 100
 #define BLOCKDIRDATA 16
101
+#define SIZEBGLOAD 256
99 102
 
100 103
 #ifndef FILLXY
101 104
 #define FILLXY(xywh,valx,valy) (xywh).x=(valx),(xywh).y=(valy)
... ...
@@ -117,6 +120,15 @@
117 120
 #define UNROLLWHXY(xywh) (xywh).w,(xywh).h,(xywh).x,(xywh).y
118 121
 #endif
119 122
 
123
+#ifndef RD
124
+#define RD 0
125
+#endif
126
+
127
+#ifndef WR
128
+#define WR 1
129
+#endif
130
+
131
+
120 132
 #if !defined(__linux__) && !defined(ANDROID)
121 133
 /* the old raylib used in the windows build lacks this function */
122 134
 bool IsImageValid(Image image)
... ...
@@ -206,6 +218,28 @@ typedef struct texture_t {
206 218
         xywh_t source; /* be able to detect a "double click" */
207 219
 } texture_t;
208 220
 
221
+typedef struct bgload_t {
222
+        /* main/thread ownership management */
223
+        int lended_to_thread; // written from main, read from thread
224
+        int thread_finished; // written from thread, read from main
225
+        int is_todo;  // written from main, read from thread
226
+        int has_mark;  // written/read from main
227
+        /* data only accessed from owner */
228
+        char path[1024]; // to use only from owner
229
+        Image image; // to use only from owner
230
+        int has_data; // to use only from owner
231
+        int has_failedload; // to use only from owner
232
+} bgload_t;
233
+
234
+typedef struct bg_t {
235
+        int sizebgload;
236
+        bgload_t *bgload;
237
+        int pipe[2];
238
+        pthread_t thread;
239
+        pthread_attr_t tattr;
240
+        int flag_threadstarted;
241
+} bg_t;
242
+
209 243
 typedef struct body_t {
210 244
         char *rootdir;
211 245
         xywh_t xywh;
... ...
@@ -224,6 +258,7 @@ typedef struct body_t {
224 258
         int flag_drawbigtexture;
225 259
         xywh_t dirdataadd;
226 260
         Font roundedbox;
261
+        bg_t *bg;
227 262
 } body_t;
228 263
 
229 264
 typedef struct im_t {
... ...
@@ -262,7 +297,7 @@ int listing_get(listing_t *listing, char *pathprefix, char *path, int flag_sort)
262 297
 void listing_freedata(listing_t *listing);
263 298
 int listing_fillxywh(listing_t *listing, font_t *font, int w, int sidelen, int is_left);
264 299
 
265
-int texture_load(texture_t *texture, char *fullpath, int maxw, int maxh);
300
+int texture_load(texture_t *texture, char *fullpath, int maxw, int maxh, bg_t *bg);
266 301
 int texture_draw(texture_t *texture, int x0, int y0, int maxw, int maxh);
267 302
 int texture_freedata(texture_t *texture);
268 303
 
... ...
@@ -281,6 +316,15 @@ void imutil_fpsreset(void);
281 316
 int imutil_fpsleft(void);
282 317
 long long imutil_milliseconds(void);
283 318
 
319
+bg_t *bg_init(int sizebgload);
320
+void bg_free(bg_t *bg);
321
+int bg_resetmarks(bg_t *bg);
322
+bgload_t *bg_get(bg_t *bg, char *path);
323
+int bg_add(bg_t *bg, char *path);
324
+int bg_freeunmarked(bg_t *bg);
325
+
326
+void *bg_thread(void *);
327
+
284 328
 int
285 329
 main(int argc, char *argv[])
286 330
 {
... ...
@@ -412,7 +456,7 @@ fprintf(stderr,"SELECTED: \"%s\"->\"%s\"\n",sel_menu,sel_submenu);
412 456
  }
413 457
 }
414 458
 #endif
415
-                if(has_mousechanges==0 && needs_nextredraw==0) {
459
+                if(has_mousechanges==0 && needs_nextredraw==0 && scrollspeed==0) {
416 460
                         /* Wait for new events when calling EndDrawing() */
417 461
                         EnableEventWaiting();
418 462
                 } else {
... ...
@@ -750,6 +794,7 @@ im_body_init(int x, int y, font_t *font, font_t *fontbig, font_t *fonthuge, int
750 794
                 return(NULL); /* sanity check failed */
751 795
         if((body=calloc(1,sizeof(body_t)))==NULL
752 796
           || (body->rootdir=strdup(rootdir))==NULL
797
+          || (body->bg=bg_init(SIZEBGLOAD))==NULL
753 798
         ) {
754 799
                 im_body_free(body),body=NULL;
755 800
                 return(NULL);
... ...
@@ -792,6 +837,8 @@ im_body_free(body_t *body)
792 837
         if(body->rootdir!=NULL)
793 838
                 free(body->rootdir),body->rootdir=NULL;
794 839
         /* NOTE: Cannot call UnloadFont(body->roundedbox) as the data was not malloc'd; see https://github.com/raysan5/raylib/blob/master/examples/others/embedded_files_loading.c */
840
+        if(body->bg!=NULL)
841
+                bg_free(body->bg),body->bg=NULL;
795 842
         free(body),body=NULL;
796 843
         return;
797 844
 }
... ...
@@ -958,7 +1005,7 @@ im_body_draw(body_t *body, Vector2 mousepos, int lmbdown, int rmbdown, int windo
958 1005
                 maxw=windowwidth;
959 1006
                 maxh=windowheight-body->xywh.y;
960 1007
                 if(body->bigtexture.has_texture==0 || strcmp(body->bigtexture.currentpath,body->texture.currentpath)!=0)
961
-                        texture_load(&(body->bigtexture),body->texture.currentpath,maxw,maxh);
1008
+                        texture_load(&(body->bigtexture),body->texture.currentpath,maxw,maxh,body->bg);
962 1009
                 if(body->bigtexture.has_texture && strcmp(body->bigtexture.currentpath,body->texture.currentpath)==0) {
963 1010
                         int x0,y0;
964 1011
                         x0=0;
... ...
@@ -988,6 +1035,8 @@ im_body_draw(body_t *body, Vector2 mousepos, int lmbdown, int rmbdown, int windo
988 1035
         DrawRectangle(body->xywh.x,body->xywh.y,body->leftsize, body->xywh.h, (Color){ 215, 215, 215, 255 } );
989 1036
         /* draw right side background */
990 1037
         DrawRectangle(body->xywh.x+body->leftsize,body->xywh.y,body->xywh.w-body->leftsize, body->xywh.h, (Color){ 227, 227, 227, 255 } );
1038
+        /* reset lazy load marks */
1039
+        bg_resetmarks(body->bg);
991 1040
         /* first pass, draw leftside, second pass, draw all of rightside */
992 1041
         statustooltip[0]='\0';
993 1042
         for(is_leftside=1,flag_skiprightside=flag_skipall=0;is_leftside>=0 && flag_skiprightside==0 && flag_skipall==0;is_leftside--) {
... ...
@@ -1094,40 +1143,45 @@ DrawRectangle(UNROLLXYWH(body->backxywh),((Color){ 0,255,0,255 })); /* hit zone
1094 1143
                                 /* show image */
1095 1144
                                 has_imagedrawn=0;
1096 1145
                                 if(is_imagefilename(elem->name+1)) {
1097
-                                        if(thumb->has_texture==0 && thumb->has_failedload==0 && imutil_fpsleft()==0) {
1098
-                                                *needs_nextredraw=1;
1099
-                                        } else if(thumb->has_texture==0 && thumb->has_failedload==0) {
1100
-                                                Image im;
1146
+                                        if(thumb->has_texture==0 && thumb->has_failedload==0) {
1147
+                                                bgload_t *bgload;
1101 1148
                                                 char fullpath[2048];
1149
+                                                *needs_nextredraw=1;
1102 1150
                                                 snprintf(fullpath,sizeof(fullpath),"%s/%s/%s",body->rootdir,dirdata->dirname,elem->name+1);
1103 1151
                                                 fullpath[sizeof(fullpath)-1]='\0';
1104
-                                                im=imutil_loadimage(fullpath);
1105
-                                                if(IsImageValid(im)) {
1106
-                                                        int neww,newh;
1107
-                                                        Image im2,impixel;
1152
+                                                bgload=bg_get(body->bg,fullpath);
1153
+                                                if(bgload!=NULL && bgload->has_data!=0) {
1154
+                                                        Image im;
1155
+                                                        im=ImageCopy(bgload->image);
1156
+                                                        if(IsImageValid(im)) {
1157
+                                                                int neww,newh;
1158
+                                                                Image im2,impixel;
1108 1159
 #if 1
1109 1160
 fprintf(stderr,"Loaded %s\n",fullpath);
1110 1161
 {
1111 1162
 int oldw=im.width,oldh=im.height;
1112 1163
 #endif
1113
-                                                        im2=GenImageColor(sidelen,sidelen,(Color){0,0,0,255});
1114
-                                                        imutil_aspectmaximize(im.width,im.height,sidelen,sidelen,&neww,&newh);
1115
-                                                        ImageResize(&im,neww,newh);
1116
-                                                        impixel=ImageCopy(im);
1117
-                                                        ImageResize(&impixel,1,1);
1164
+                                                                im2=GenImageColor(sidelen,sidelen,(Color){0,0,0,255});
1165
+                                                                imutil_aspectmaximize(im.width,im.height,sidelen,sidelen,&neww,&newh);
1166
+                                                                ImageResize(&im,neww,newh);
1167
+                                                                impixel=ImageCopy(im);
1168
+                                                                ImageResize(&impixel,1,1);
1118 1169
 #if 1
1119 1170
 if(neww>sidelen || newh>sidelen)
1120 1171
 fprintf(stderr,"elem:\"%s\" sidelen:%i old:%ix%i new:%ix%i\n",elem->name+1,sidelen,oldw,oldh,neww,newh);
1121 1172
 }
1122 1173
 #endif
1123
-                                                        ImageDraw(&im2,impixel,(Rectangle) {0,0,1,1},(Rectangle) {0,0,sidelen,sidelen},(Color){255,255,255,255});
1124
-                                                        ImageDraw(&im2,im,(Rectangle) {0,0,neww,newh},(Rectangle) {(sidelen-neww)/2,(sidelen-newh)/2,neww,newh},(Color){255,255,255,255});
1125
-                                                        *te=LoadTextureFromImage(im2);
1126
-                                                        UnloadImage(im);
1127
-                                                        UnloadImage(im2);
1128
-                                                        thumb->has_texture=1;
1129
-                                                } else {
1130
-                                                        thumb->has_failedload=1;
1174
+                                                                ImageDraw(&im2,impixel,(Rectangle) {0,0,1,1},(Rectangle) {0,0,sidelen,sidelen},(Color){255,255,255,255});
1175
+                                                                ImageDraw(&im2,im,(Rectangle) {0,0,neww,newh},(Rectangle) {(sidelen-neww)/2,(sidelen-newh)/2,neww,newh},(Color){255,255,255,255});
1176
+                                                                *te=LoadTextureFromImage(im2);
1177
+                                                                UnloadImage(im);
1178
+                                                                UnloadImage(im2);
1179
+                                                                thumb->has_texture=1;
1180
+                                                        } else {
1181
+                                                                thumb->has_failedload=1;
1182
+                                                        }
1183
+                                                } else if(bgload==NULL) {
1184
+                                                        bg_add(body->bg,fullpath);
1131 1185
                                                 }
1132 1186
                                         }
1133 1187
                                         if(thumb->has_texture!=0) {
... ...
@@ -1146,7 +1200,7 @@ fprintf(stderr,"elem:\"%s\" sidelen:%i old:%ix%i new:%ix%i\n",elem->name+1,sidel
1146 1200
                                                         if((body->texture.has_texture==0 && !(body->texture.has_failedload && strcmp(body->texture.currentpath,fullpath)==0))
1147 1201
                                                           || strcmp(body->texture.currentpath,fullpath)!=0
1148 1202
                                                         ) {
1149
-                                                                texture_load(&(body->texture),fullpath,maxw,maxh);
1203
+                                                                texture_load(&(body->texture),fullpath,maxw,maxh,body->bg);
1150 1204
                                                         }
1151 1205
                                                         if(body->texture.has_texture && strcmp(body->texture.currentpath,fullpath)==0) {
1152 1206
                                                                 int x0,y0;
... ...
@@ -1237,14 +1291,17 @@ fprintf(stderr,"elem:\"%s\" sidelen:%i old:%ix%i new:%ix%i\n",elem->name+1,sidel
1237 1291
                 DrawRectangle(0,windowheight-1-margin*2-font->height,m2.x+margin*2,font->height+margin*2,((Color){0,0,0,96}));
1238 1292
                 DrawTextEx(font->font,statustooltip,(Vector2){margin,windowheight-1-margin-font->height},font->height,0,(Color){ 255,255,255,128 });
1239 1293
         }
1294
+        /* free not used bg load items */
1295
+        bg_freeunmarked(body->bg);
1240 1296
         return(0);
1241 1297
 }
1242 1298
 
1243 1299
 int
1244
-texture_load(texture_t *texture, char *fullpath, int maxw, int maxh)
1300
+texture_load(texture_t *texture, char *fullpath, int maxw, int maxh, bg_t *bg)
1245 1301
 { 
1246 1302
         Image im;
1247 1303
         int neww,newh;
1304
+        bgload_t *bgload;
1248 1305
         if(texture==NULL || fullpath==NULL)
1249 1306
                 return(-1); /* sanity check failed */
1250 1307
         if(texture->has_texture) {
... ...
@@ -1254,7 +1311,11 @@ texture_load(texture_t *texture, char *fullpath, int maxw, int maxh)
1254 1311
                 texture->has_failedload=0;
1255 1312
         }
1256 1313
         texture->currentpath[0]='\0';
1257
-        im=imutil_loadimage(fullpath);
1314
+        if(bg!=NULL && (bgload=bg_get(bg,texture->currentpath))!=NULL && bgload->has_data!=0) {
1315
+                im=ImageCopy(bgload->image);
1316
+        } else {
1317
+                im=imutil_loadimage(fullpath);
1318
+        }
1258 1319
         if(IsImageValid(im)) {
1259 1320
                 imutil_aspectmaximize(im.width,im.height,maxw,maxh,&neww,&newh);
1260 1321
                 ImageResize(&im,neww,newh);
... ...
@@ -1782,3 +1843,176 @@ imutil_milliseconds(void)
1782 1843
         return(res);
1783 1844
 }
1784 1845
 
1846
+bg_t *
1847
+bg_init(int sizebgload)
1848
+{
1849
+        bg_t *bg;
1850
+        bg=NULL;
1851
+        if((bg=calloc(1,sizeof(bg_t)))==NULL
1852
+          || (bg->pipe[0]=bg->pipe[1]=-1)!=-1
1853
+          || pipe(bg->pipe)!=0
1854
+          || (bg->bgload=calloc(sizebgload,sizeof(bgload_t)))==NULL
1855
+          || (bg->sizebgload=sizebgload)!=sizebgload
1856
+          || pthread_attr_init(&(bg->tattr))!=0
1857
+          || pthread_create(&(bg->thread),&(bg->tattr),bg_thread,(void *)bg)!=0
1858
+          || (bg->flag_threadstarted=1)!=1
1859
+        ) {
1860
+                bg_free(bg);
1861
+                return(NULL);
1862
+        }
1863
+        return(bg);
1864
+}
1865
+
1866
+
1867
+void
1868
+bg_free(bg_t *bg)
1869
+{
1870
+        int i;
1871
+        if(bg==NULL)
1872
+                return; /* nothing to do */
1873
+        if(bg->flag_threadstarted) {
1874
+                char dummy=1;
1875
+                write(bg->pipe[WR],&dummy,1);
1876
+                pthread_join(bg->thread,NULL);
1877
+                bg->flag_threadstarted=0;
1878
+        }
1879
+        if(bg->pipe[0]!=-1)
1880
+                close(bg->pipe[0]),bg->pipe[0]=-1;
1881
+        if(bg->pipe[1]!=-1)
1882
+                close(bg->pipe[1]),bg->pipe[1]=-1;
1883
+        if(bg->bgload!=NULL) {
1884
+                bgload_t *bgload;
1885
+                for(i=0,bgload=bg->bgload;i<bg->sizebgload;i++,bgload++) {
1886
+                        if(bgload->has_data) {
1887
+                                UnloadImage(bgload->image);
1888
+                                bgload->has_data=0;
1889
+                        }
1890
+                }
1891
+                free(bg->bgload),bg->bgload=NULL,bg->sizebgload=0;
1892
+        }
1893
+        return;
1894
+}
1895
+
1896
+int
1897
+bg_resetmarks(bg_t *bg)
1898
+{
1899
+        int i;
1900
+        bgload_t *bgload;
1901
+        if(bg==NULL)
1902
+                return(-1);
1903
+        for(i=0,bgload=bg->bgload;i<bg->sizebgload;i++,bgload++)
1904
+                bgload->has_mark=0;
1905
+        return(0);
1906
+}
1907
+
1908
+bgload_t *
1909
+bg_get(bg_t *bg, char *path)
1910
+{
1911
+        int i;
1912
+        bgload_t *bgload;
1913
+        if(bg==NULL)
1914
+                return(NULL);
1915
+        for(i=0,bgload=bg->bgload;i<bg->sizebgload;i++,bgload++) {
1916
+                if(bgload->thread_finished && bgload->has_data && strcmp(path,bgload->path)==0) {
1917
+                        bgload->has_mark=1;
1918
+#if 1
1919
+fprintf(stderr,"bg_get: \"%s\"\n",bgload->path);
1920
+#endif
1921
+                        return(bgload);
1922
+                }
1923
+        }
1924
+        return(NULL);
1925
+}
1926
+
1927
+int
1928
+bg_add(bg_t *bg, char *path)
1929
+{
1930
+        int i;
1931
+        bgload_t *bgload;
1932
+        int dummy;
1933
+        if(bg==NULL)
1934
+                return(-1);
1935
+        for(i=0,bgload=bg->bgload;i<bg->sizebgload;i++,bgload++) {
1936
+                if(bgload->lended_to_thread && strcmp(path,bgload->path)==0) {
1937
+                        bgload->is_todo=1;
1938
+                        bgload->has_mark=1;
1939
+                        dummy=0;
1940
+                        write(bg->pipe[WR],&dummy,1);
1941
+                        return(0); /* already on list */
1942
+                }
1943
+        }
1944
+        for(i=0,bgload=bg->bgload;i<bg->sizebgload;i++,bgload++) {
1945
+                if(bgload->lended_to_thread==0) {
1946
+                        memset(bgload,0,sizeof(bgload_t));
1947
+                        strncpy(bgload->path,path,sizeof(bgload->path));
1948
+                        bgload->path[sizeof(bgload->path)-1]='\0';
1949
+                        bgload->is_todo=1;
1950
+                        bgload->has_mark=1;
1951
+                        dummy=0;
1952
+                        bgload->lended_to_thread=1;
1953
+                        write(bg->pipe[WR],&dummy,1);
1954
+                        return(0); /* added to list */
1955
+                }
1956
+        }
1957
+        return(-1); /* couldn't add */
1958
+}
1959
+
1960
+int
1961
+bg_freeunmarked(bg_t *bg)
1962
+{
1963
+        int i;
1964
+        bgload_t *bgload;
1965
+        if(bg==NULL)
1966
+                return(-1);
1967
+        for(i=0,bgload=bg->bgload;i<bg->sizebgload;i++,bgload++) {
1968
+                if(bgload->lended_to_thread && bgload->thread_finished && bgload->has_mark==0) {
1969
+                        if(bgload->has_data) {
1970
+#if 1
1971
+fprintf(stderr,"bg: Unloading: \"%s\"\n",bgload->path);
1972
+#endif
1973
+                                UnloadImage(bgload->image);
1974
+                                bgload->has_data=0;
1975
+                        }
1976
+#if 1
1977
+else {
1978
+fprintf(stderr,"bg: Cancelling: \"%s\"\n",bgload->path);
1979
+}
1980
+#endif
1981
+                        memset(bgload,0,sizeof(bgload_t));
1982
+                }
1983
+        }
1984
+        return(0);
1985
+}
1986
+
1987
+void *
1988
+bg_thread(void *parambg)
1989
+{
1990
+        bg_t *bg;
1991
+        char dummy;
1992
+        bg=(bg_t *)parambg;
1993
+        int i;
1994
+        bgload_t *bgload;
1995
+        while(1) {
1996
+                read(bg->pipe[RD],&dummy,1);
1997
+                if(dummy!=0)
1998
+                        break; /* was told to exit */
1999
+                for(i=0,bgload=bg->bgload;i<bg->sizebgload;i++,bgload++) {
2000
+                        if(bgload->lended_to_thread==0)
2001
+                                continue;
2002
+                        if(bgload->is_todo==0) {
2003
+                                bgload->thread_finished=1;
2004
+                                continue;
2005
+                        }
2006
+                        if(bgload->has_data==0 && bgload->has_failedload==0) {
2007
+                                bgload->image=imutil_loadimage(bgload->path);
2008
+                                if(IsImageValid(bgload->image))
2009
+                                        bgload->has_data=1;
2010
+                                else
2011
+                                        bgload->has_failedload=1;
2012
+                                bgload->thread_finished=1;
2013
+                        }
2014
+                }
2015
+        }
2016
+        pthread_exit(NULL);
2017
+}
2018
+