Browse code

Initial implementation. Only supports HaldCLUTs and PNGs.

Dario Rodriguez authored on 16/01/2022 11:05:23
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,14 @@
1
+CC=gcc
2
+CFLAGS=-g -Wall
3
+LDFLAGS=-lpng
4
+
5
+all: luttool
6
+
7
+luttool.o: luttool.c
8
+	$(CC) $(CFLAGS) -c luttool.c -o luttool.o
9
+
10
+luttool: luttool.o
11
+	$(CC) $(LDFLAGS) luttool.o -o luttool
12
+
13
+clean:
14
+	rm -f luttool.o luttool
0 15
new file mode 100644
... ...
@@ -0,0 +1,408 @@
1
+/*
2
+ * luttool.c
3
+ *
4
+ * Utility to apply LUTs to images
5
+ *
6
+ * Author: Dario Rodriguez antartica@whereismybit.com
7
+ * This program is licensed under the terms of the MIT license,
8
+ * with an exception: the license text may be substituted for a
9
+ * link to it. MIT license text: https://spdx.org/licenses/MIT.html
10
+ */
11
+
12
+#include <stdio.h>
13
+#include <stdlib.h>
14
+#include <unistd.h>
15
+#include <string.h>
16
+#include <png.h>
17
+
18
+#define DEFAULT_IDENTITYSIZE 12 /* Same as RawTherapee's HaldCLUTs, 1728x1728 AKA (12*12)*12x12*(12*12), equivalent to a cube with 144px wide sides*/
19
+#define DEFAULT_QUALITY 85
20
+/* #define JPEG_SUPPORTED */
21
+
22
+int luttool_applylut(char *lutfilenamelist,char *infilename, char *outfilename,int quality,int identitysize);
23
+
24
+int util_pnginfo(char *filename, int *width, int *height);
25
+int util_pngload(char *filename, char **bufimg, int *sizebufimg, int *width, int *height);
26
+int util_pngsave(char *filename, char *bufimg, int width, int height);
27
+int util_identitygen(char **bufimg, int *sizebufimg, int identitysize, int *width, int *height);
28
+int luttool_rgba2lut4096(char *rgba, int width, int height, char **lut4096, int *sizelut4096);
29
+int luttool_rgba2lut4096_interpolated(char *rgba, int width, int height, char **lut4096, int *sizelut4096);
30
+int luttool_applylut4096(char *lut4096,char *imgbuf,int imgwidth,int imgheight);
31
+
32
+int
33
+main(int argc, char *argv[])
34
+{
35
+        int firstarg,outfilenamearg;
36
+        int identitysize;
37
+        int quality;
38
+        char *outfilename;
39
+        char *lutlist;
40
+        int i;
41
+        int flagseveralinfile;
42
+        char bufoutfilename[1024];
43
+        char *currentoutfilename;
44
+        if(argc<2 || strcmp(argv[argc-1],"--help")==0) {
45
+#ifdef JPEG_SUPPORTED
46
+                printf("Syntax: %s [-q jpegquality] [-n identitylevel] {identity | lutfile[,lutfile[,...]]} { identity | infile1 } [infile2 [...]] -o outfile\n",argv[0]);
47
+                printf("Example: %s haldclut.png sprite.png -o result.png\n",argv[0]);
48
+                printf("Example: %s -n 16 identity identity -o identity.png\n",argv[0]);
49
+                printf("Example: %s -q 85 haldclut.png infile.png -o outfile.jpg\n",argv[0]);
50
+#else
51
+                printf("Syntax: %s [-n identitylevel] {identity | lutfile[,lutfile[,...]]} { identity | infile1 } [infile2 [...]] -o outfile\n",argv[0]);
52
+                printf("Example: %s haldclut.png sprite.png -o result.png\n",argv[0]);
53
+                printf("Example: %s -n 16 identity identity -o identity.png\n",argv[0]);
54
+#endif
55
+                return(1);
56
+        }
57
+        /* parse arguments */
58
+        identitysize=DEFAULT_IDENTITYSIZE;
59
+        quality=DEFAULT_QUALITY;
60
+        for(firstarg=1;firstarg<(argc-1) && (strcmp(argv[firstarg],"-n")==0 || (strcmp(argv[firstarg],"-q")==0));) {
61
+                if(strcmp(argv[firstarg],"-n")==0) {
62
+                        identitysize=atoi(argv[firstarg+1]);
63
+                        if(identitysize<2 || identitysize>16) {
64
+                                fprintf(stderr,"%s: ERROR: the parameter to -n must be between 2 and 16 (default: %i)\n",argv[0],DEFAULT_IDENTITYSIZE);
65
+                                return(1);
66
+                        }
67
+                } else if(strcmp(argv[firstarg],"-q")==0) {
68
+                        quality=atoi(argv[firstarg+1]);
69
+                        if(quality<1 || quality>100) {
70
+                                fprintf(stderr,"%s: ERROR: the parameter to -q must be between 1 and 100 (default: %i)\n",argv[0],DEFAULT_QUALITY);
71
+                                return(1);
72
+                        }
73
+                }
74
+                firstarg+=2;
75
+        }
76
+        for(outfilenamearg=-1,outfilename=NULL,i=1;i<(argc-1);i++) {
77
+                if(strcmp(argv[i],"-o")==0) {
78
+                        outfilename=argv[i+1];
79
+                        outfilenamearg=i;
80
+                        break;
81
+                }
82
+        }
83
+        if(outfilename==NULL) {
84
+                fprintf(stderr,"%s: ERROR: no output file specified\n",argv[0]);
85
+                return(1);
86
+        }
87
+        firstarg+=((firstarg==outfilenamearg)?2:0);
88
+        lutlist=argv[firstarg];
89
+        firstarg++;
90
+        flagseveralinfile=((argc-firstarg-((outfilenamearg>=firstarg)?2:0))>1)?1:0;
91
+        /* process files sequentially, skipping the "-o outputfilename" */
92
+        for(i=firstarg;i<argc;i++) {
93
+                if(i==outfilenamearg) {
94
+                        i++;
95
+                        continue;
96
+                }
97
+                currentoutfilename=outfilename;
98
+                if(flagseveralinfile) {
99
+                        char *ext;
100
+                        int l;
101
+                        strncpy(bufoutfilename,argv[i],sizeof(bufoutfilename));
102
+                        bufoutfilename[sizeof(bufoutfilename)-1]='\0';
103
+                        if((ext=strrchr(bufoutfilename,'.'))!=NULL)
104
+                                *ext='\0';
105
+                        l=strlen(bufoutfilename);
106
+                        snprintf(bufoutfilename+l,sizeof(bufoutfilename)-l,"_%s",outfilename);
107
+                        bufoutfilename[sizeof(bufoutfilename)-1]='\0';
108
+                        currentoutfilename=bufoutfilename;
109
+                }
110
+                if(luttool_applylut(lutlist,argv[i],currentoutfilename,quality,identitysize)!=0) {
111
+                        fprintf(stderr,"%s: ERROR: couldn't apply LUTs \"%s\" to \"%s\" or couldn't write \"%s\"cified\n",argv[0],lutlist,argv[i],currentoutfilename);
112
+                        return(1);
113
+                }
114
+        }
115
+        return(0);
116
+}
117
+
118
+int
119
+luttool_applylut(char *lutlist,char *infilename, char *outfilename,int quality,int identitysize)
120
+{
121
+        char *curlut,*next;
122
+        char filename[1024];
123
+        int l;
124
+        int in_error;
125
+        char *lutbuf,*imgbuf,*rgblutbuf;
126
+        int sizelutbuf,lutwidth,lutheight;
127
+        int sizeimgbuf,imgwidth,imgheight;
128
+        int sizergblutbuf;
129
+        lutbuf=imgbuf=rgblutbuf=NULL;
130
+        sizelutbuf=sizeimgbuf=sizergblutbuf=0;
131
+        lutwidth=lutheight=imgwidth=imgheight=0;
132
+        if(lutlist==NULL || infilename==NULL || outfilename==NULL)
133
+                return(-1); /* sanity check error */
134
+        in_error=0;
135
+        /* load the image to process */
136
+        if(strcmp(infilename,"identity")==0) {
137
+                 if(util_identitygen(&imgbuf,&sizeimgbuf,identitysize,&imgwidth,&imgheight)==-1)
138
+                         in_error=1;
139
+        } else {
140
+                if(util_pngload(infilename,&imgbuf,&sizeimgbuf,&imgwidth,&imgheight)!=0)
141
+                        in_error=1;
142
+        }
143
+        /* apply all luts */
144
+        for(curlut=lutlist,next=strchr(curlut,','),next=((next==NULL)?(curlut+strlen(curlut)):next)
145
+          ;in_error==0 && *curlut!='\0'
146
+          ;curlut=next+((*next==',')?1:0),next=strchr(curlut,','),next=((next==NULL)?(curlut+strlen(curlut)):next)) {
147
+                l=next-curlut;
148
+                l=(l>(sizeof(filename)-1))?(sizeof(filename)-1):l;
149
+                memcpy(filename,curlut,l);
150
+                filename[l]='\0';
151
+                if(strcmp(filename,"identity")==0) {
152
+                        if(util_identitygen(&lutbuf,&sizelutbuf,identitysize,&lutwidth,&lutheight)==-1) {
153
+                                in_error=1;
154
+                                break; /* couldn't generate identity lut */
155
+                        }
156
+                } else {
157
+                        if(util_pngload(filename,&lutbuf,&sizelutbuf,&lutwidth,&lutheight)==-1) {
158
+                                in_error=1;
159
+                                break; /* couldn't load lut */
160
+                        }
161
+                }
162
+                if(luttool_rgba2lut4096(lutbuf, lutwidth, lutheight, &rgblutbuf, &sizergblutbuf)!=0) {
163
+                        in_error=1;
164
+                        break; /* couldn't process lut */
165
+                }
166
+                if(luttool_applylut4096(rgblutbuf,imgbuf,imgwidth,imgheight)!=0) {
167
+                        in_error=1;
168
+                        break; /* couldn't apply lut */
169
+                }
170
+        }
171
+        /* save the result */
172
+        if(in_error==0) {
173
+                if(util_pngsave(outfilename,imgbuf,imgwidth,imgheight)!=0)
174
+                        in_error=1; /* couldn't write */
175
+        }
176
+        /* cleanup and exit */
177
+        if(lutbuf!=NULL)
178
+                free(lutbuf),lutbuf=NULL;
179
+        if(imgbuf!=NULL)
180
+                free(imgbuf),imgbuf=NULL;
181
+        if(rgblutbuf!=NULL)
182
+                free(rgblutbuf),rgblutbuf=NULL;
183
+        return((in_error!=0)?-1:0);
184
+}
185
+
186
+
187
+
188
+int
189
+util_pnginfo(char *filename, int *width, int *height)
190
+{
191
+        return(util_pngload(filename,NULL,NULL,width,height));
192
+}
193
+
194
+int
195
+util_pngload(char *filename, char **bufimg, int *sizebufimg, int *width, int *height)
196
+{
197
+        FILE *f;
198
+        png_structp png;
199
+        png_infop info;
200
+        png_byte color_type,bit_depth;
201
+        png_bytep *rowpointers;
202
+        int reqsize;
203
+        int i,pitch;
204
+        if(filename==NULL || (bufimg==NULL && sizebufimg!=NULL) || (bufimg!=NULL && sizebufimg==NULL) || width==NULL || height==NULL)
205
+                return(-1); /* sanity check failed */
206
+        if((f=fopen(filename,"rb"))==NULL)
207
+                return(-1); /* couldn't open file */
208
+        if((png=png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL))==NULL
209
+          || (info=png_create_info_struct(png))==NULL
210
+          || setjmp(png_jmpbuf(png))) {
211
+                if(png!=NULL)
212
+                        png_destroy_read_struct(&png,&info,NULL),png=NULL,info=NULL;
213
+                fclose(f),f=NULL;
214
+                return(-1); /* couldn't init png library */
215
+        }
216
+        png_init_io(png,f);
217
+        png_read_info(png,info);
218
+        *width=png_get_image_width(png,info);
219
+        *height=png_get_image_height(png,info);
220
+        /* special case: if bufimg==NULL, we only want the width/height */
221
+        if(bufimg==NULL) {
222
+                png_destroy_read_struct(&png,&info,NULL),png=NULL,info=NULL;
223
+                fclose(f),f=NULL;
224
+                return(0); /* all done */
225
+        }
226
+        /* make sure we have enough space for the new image in 8-bit RGBA */
227
+        reqsize=(*width)*(*height)*4;
228
+        if(*sizebufimg<reqsize) {
229
+                char *newbufimg;
230
+                if((newbufimg=realloc(*bufimg,reqsize))==NULL) {
231
+                      png_destroy_read_struct(&png,&info,NULL),png=NULL,info=NULL;
232
+                      fclose(f),f=NULL;
233
+                      return(-1); /* insuf. mem. for resulting image */
234
+                }
235
+                *bufimg=newbufimg;
236
+                *sizebufimg=reqsize;
237
+        }
238
+        /* make sure we get 8-bit RGBA data, regardless of the file's bit_depth and color_type */
239
+        if((bit_depth=png_get_bit_depth(png,info))==16)
240
+                png_set_strip_16(png);
241
+        color_type=png_get_color_type(png,info);
242
+        if(color_type==PNG_COLOR_TYPE_PALETTE)
243
+                png_set_palette_to_rgb(png);
244
+        if(color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8)
245
+                png_set_expand_gray_1_2_4_to_8(png);
246
+        if(png_get_valid(png,info,PNG_INFO_tRNS))
247
+                png_set_tRNS_to_alpha(png);
248
+        if(color_type==PNG_COLOR_TYPE_RGB
249
+          || color_type==PNG_COLOR_TYPE_GRAY
250
+          || color_type==PNG_COLOR_TYPE_PALETTE) {
251
+                png_set_filler(png,0xff,PNG_FILLER_AFTER);
252
+        }
253
+        if(color_type==PNG_COLOR_TYPE_GRAY
254
+          || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
255
+                png_set_gray_to_rgb(png);
256
+        }
257
+        /* read the image */
258
+        png_read_update_info(png,info);         
259
+        if((rowpointers=(png_bytep *)malloc(sizeof(png_bytep)*(*height)))==NULL) {
260
+                png_destroy_read_struct(&png,&info,NULL),png=NULL,info=NULL;
261
+                fclose(f),f=NULL;
262
+                return(-1); /* insuf. mem. for temporary data */
263
+        }
264
+        for(i=0,pitch=(*width)*4;i<(*height);i++)
265
+                rowpointers[i]=(png_byte *)((*bufimg)+i*pitch);
266
+        png_read_image(png,rowpointers);
267
+        /* cleanup */
268
+        free(rowpointers),rowpointers=NULL;
269
+        fclose(f),f=NULL;
270
+        png_destroy_read_struct(&png,&info,NULL),png=NULL,info=NULL;
271
+        return(0);
272
+}
273
+
274
+int
275
+util_pngsave(char *filename, char *bufimg, int width, int height)
276
+{
277
+        FILE *f;
278
+        png_structp png;
279
+        png_infop info;
280
+        int i,pitch;
281
+        char *ptr;
282
+        if((f=fopen(filename,"wb"))==NULL)
283
+                return(-1); /* couldn't open file for writing */
284
+        if((png=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL))==NULL
285
+          || (info=png_create_info_struct(png))==NULL
286
+          || setjmp(png_jmpbuf(png))) {
287
+                if(png!=NULL)
288
+                        png_destroy_write_struct(&png,&info),png=NULL,info=NULL;
289
+                fclose(f),f=NULL;
290
+                return(-1); /* couldn't init png library */
291
+        }
292
+        png_init_io(png,f);
293
+        png_set_IHDR(png,info,width,height,8,PNG_COLOR_TYPE_RGBA,
294
+          PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_BASE,PNG_FILTER_TYPE_BASE);
295
+        png_write_info(png,info);
296
+        for(i=0,ptr=bufimg,pitch=width*4;i<height;i++,ptr+=pitch)
297
+                png_write_row(png,(png_const_bytep)ptr);
298
+        png_write_end(png,NULL);
299
+        fclose(f),f=NULL;
300
+        png_destroy_write_struct(&png,&info),png=NULL,info=NULL;
301
+        return(0);
302
+}
303
+
304
+int
305
+util_identitygen(char **bufimg, int *sizebufimg, int identitysize, int *width, int *height)
306
+{
307
+        int x,y,r,g,b;
308
+        int isizesq,isizesqminusone;
309
+        int osizesqminusone;
310
+        int reqsize;
311
+        int l;
312
+        unsigned char *ptr;
313
+        if(bufimg==NULL || sizebufimg==NULL || identitysize<=1 || identitysize>16 || width==NULL || height==NULL)
314
+                return(-1);
315
+        l=*width=*height=identitysize*identitysize*identitysize;
316
+        reqsize=(*width)*(*height)*4;
317
+        if(*sizebufimg<reqsize) {
318
+                char *newbufimg;
319
+                if((newbufimg=realloc(*bufimg,reqsize))==NULL)
320
+                      return(-1); /* insuf. mem. for resulting image */
321
+                *bufimg=newbufimg;
322
+                *sizebufimg=reqsize;
323
+        }
324
+        isizesq=identitysize*identitysize;
325
+        isizesqminusone=isizesq-1;
326
+        osizesqminusone=256-1;
327
+        for(y=0,ptr=(unsigned char *)(*bufimg);y<l;y++) {
328
+                for(x=0;x<l;x++,ptr+=4) {
329
+                        r=x%isizesq;
330
+                        g=(y%identitysize)*identitysize+(x/isizesq);
331
+                        b=y/identitysize;
332
+                        ptr[0]=r*osizesqminusone/isizesqminusone;
333
+                        ptr[1]=g*osizesqminusone/isizesqminusone;
334
+                        ptr[2]=b*osizesqminusone/isizesqminusone;
335
+                        ptr[3]=0xff;
336
+                }
337
+        }
338
+        return(0);
339
+}
340
+
341
+int
342
+luttool_rgba2lut4096(char *rgba, int width, int height, char **lut4096, int *sizelut4096)
343
+{
344
+        int n;
345
+        int reqsize;
346
+        int x,y;
347
+        int r,g,b;
348
+        int isize,isizesq,isizesqminusone;
349
+        int osize,osizesq,osizesqminusone;
350
+        unsigned char *orig,*dest;
351
+        if(rgba==NULL || width!=height || lut4096==NULL || sizelut4096==NULL)
352
+                return(-1); /* sanity check error */
353
+        /* check if size is a supported size */
354
+        for(n=2;n<=16 && width!=(n*n*n);n++)
355
+                ;
356
+        if(n>16)
357
+                return(-1); /* invalid size */
358
+        /* expand lut4096 as necessary */
359
+        reqsize=4096*4096*3;
360
+        if(*sizelut4096<reqsize) {
361
+                char *newlut4096;
362
+                if((newlut4096=realloc(*lut4096,reqsize))==NULL)
363
+                      return(-1); /* insuf. mem. for resulting lut */
364
+                *lut4096=newlut4096;
365
+                *sizelut4096=reqsize;
366
+        }
367
+        /* do the conversion */
368
+        isize=n;
369
+        isizesq=n*n;
370
+        isizesqminusone=isizesq-1;
371
+        osize=16;
372
+        osizesq=osize*osize;
373
+        osizesqminusone=osizesq-1;
374
+        for(r=0;r<osizesq;r++) {
375
+                for(g=0;g<osizesq;g++) {
376
+                        for(b=0;b<osizesq;b++) {
377
+                                x=((isizesqminusone*r)/osizesqminusone)+(((isizesqminusone*g)/osizesqminusone)%isize)*isizesq;
378
+                                y=((isizesqminusone*b)/osizesqminusone)*isize+(((isizesqminusone*g)/osizesqminusone)/isize);
379
+                                orig=(unsigned char *) (rgba+((y*width+x)<<2));
380
+                                dest=(unsigned char *) ((*lut4096)+((r<<16)+(g<<8)+b)*3);
381
+                                dest[0]=orig[0];
382
+                                dest[1]=orig[1];
383
+                                dest[2]=orig[2];
384
+                        }
385
+                }
386
+        }
387
+        return(0);
388
+}
389
+
390
+int
391
+luttool_applylut4096(char *lut4096,char *imgbuf,int imgwidth,int imgheight)
392
+{
393
+        int x,y;
394
+        unsigned char *orig;
395
+        unsigned char *lut;
396
+        if(lut4096==NULL || imgbuf==NULL || imgwidth<=0 || imgheight<=0)
397
+                return(-1);
398
+        for(y=0,orig=(unsigned char *)imgbuf;y<imgheight ;y++) {
399
+                for(x=0;x<imgwidth;x++,orig+=4) {
400
+                        lut=((unsigned char *)lut4096)+((((int)(orig[0]))<<16)+(((int)(orig[1]))<<8)+((int)(orig[2])))*3;
401
+                        orig[0]=lut[0];
402
+                        orig[1]=lut[1];
403
+                        orig[2]=lut[2];
404
+                }
405
+        }
406
+        return(0);
407
+}
408
+