-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkoliba.h
5945 lines (4909 loc) · 236 KB
/
koliba.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
koliba.h
Copyright 2019-2022 G. Adam Stanislav
All rights reserved
Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:
1. Redistributions of source code must retain the
above copyright notice, this list of conditions
and the following disclaimer.
2. Redistributions in binary form must reproduce the
above copyright notice, this list of conditions and
the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or
promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _KOLIBA_H_
#define _KOLIBA_H_
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#ifdef NOKLIBLIB // Should you need a static library...
# define KLBHID
# define KLBDC
#elif defined _WIN32
# define KLBHID
#ifdef DLL
# define KLBDC __declspec(dllexport)
#else // !DLL
# define KLBDC __declspec(dllimport)
#endif // DLL
#elif defined __APPLE__ || defined linux || defined __FreeBSD__
# define KLBDC __attribute__((visibility("default")))
# define KLBHID __attribute__((visibility("hidden")))
#else // !_WIN32 !__APPLE__ !linux !__FreeBSD__
# define KLBDC
# define KLBHID
#endif // _WIN32
#ifdef __cplusplus
extern "C" {
#endif
#define SLTCFILEHEADERBYTES 24
// Minimum char buffer sizes (including the terminating NUL)
// to produce default marshal strings of various structures.
//
// The initial 6 is for the four character identifier, followed
// by a line feed, and the terminating NUL. If it is more than
// 6, it is for any extra line feeds separating portions
// of data (e.g. twin matrices).
//
// The 17 multiplier is the number of hexadecimal characters
// of a double followed by either a blank space or a line feed.
//
// The multiplicand is the count of hexadecimal numbers.
//
// (An) optional final number(s) may be used for values specific
// to certain structures, such as a digit for a boolean value.
#define SLTAMINCHARS (6+17*24)
#define MATAMINCHARS (6+17*12)
#define CHRAMINCHARS (6+17*8)
#define GMXAMINCHARS (7+17*24)
#define CBLAMINCHARS (6+17*13+5)
typedef enum {
KOLIBA_ftnoslut,
KOLIBA_ftnofile,
KOLIBA_ftunknown,
KOLIBA_ftslut,
KOLIBA_ftmatrix,
KOLIBA_ftchrm,
KOLIBA_ftcflt,
KOLIBA_ftsltt,
KOLIBA_ftm34t,
KOLIBA_ftchrt,
KOLIBA_ftdicr,
KOLIBA_ftdcrt,
KOLIBA_ftpalette,
KOLIBA_ftkptt,
KOLIBA_ftcftt,
KOLIBA_ftgmnx,
KOLIBA_ftcbln
} KOLIBA_ftype;
typedef enum {
KOLIBA_MalletAll = 0,
KOLIBA_MalletSvit,
KOLIBA_MalletBlack,
KOLIBA_MalletWhite,
KOLIBA_MalletFarba,
KOLIBA_MalletPrimary,
KOLIBA_MalletSecondary,
KOLIBA_MalletWarm,
KOLIBA_MalletCold,
KOLIBA_MalletSelene,
KOLIBA_MalletNyx,
KOLIBA_MalletOrion,
KOLIBA_MalletBetelgeuse,
KOLIBA_MalletRed,
KOLIBA_MalletGreen,
KOLIBA_MalletBlue,
KOLIBA_MalletCyan,
KOLIBA_MalletMagenta,
KOLIBA_MalletYellow,
KOLIBA_FUNDAMENTALMALLETS
} fundmal;
typedef enum {
KOLIBA_PlutBlack,
KOLIBA_PlutWhite,
KOLIBA_PlutRed,
KOLIBA_PlutGreen,
KOLIBA_PlutBlue,
KOLIBA_PlutCyan,
KOLIBA_PlutMagenta,
KOLIBA_PlutYellow
} KOLIBA_Pluts;
typedef enum {
KQC_red,
KQC_scarlet,
KQC_vermilion,
KQC_persimmon,
KQC_orange,
KQC_orangepeel,
KQC_amber,
KQC_goldenyellow,
KQC_yellow,
KQC_lemon,
KQC_lime,
KQC_springbud,
KQC_chartreuse,
KQC_brightgreen,
KQC_harlequin,
KQC_neongreen,
KQC_green,
KQC_jade,
KQC_erin,
KQC_emerald,
KQC_springgreen,
KQC_mint,
KQC_aquamarine,
KQC_turquoise,
KQC_cyan,
KQC_skyblue,
KQC_capri,
KQC_cornflower,
KQC_azure,
KQC_cobalt,
KQC_cerulean,
KQC_sapphire,
KQC_blue,
KQC_iris,
KQC_indigo,
KQC_veronica,
KQC_violet,
KQC_amethyst,
KQC_purple,
KQC_phlox,
KQC_magenta,
KQC_fuchsia,
KQC_cerise,
KQC_deeppink,
KQC_rose,
KQC_raspberry,
KQC_crimson,
KQC_amaranth,
KQC_COUNT
} KOLIBA_QUINTARYCOLORS;
typedef enum {
KAU_degrees,
KAU_radians,
KAU_turns,
KAU_pis,
KAU_COUNT
} KOLIBA_ANGLEUNITS;
/****************************************************************************/
/******************* **********************/
/******************* T H E KOLIBA D A T A T Y P E S **********************/
/******************* **********************/
/****************************************************************************/
// Entia non praeter necessitatem multiplicanda sint.
// All look-up tables used by the Koliba library consist of several (mostly 8)
// vertices. The library was originally developed to help create color-grading
// software. Hence, the use of {r, g, b} for the vertex. But the same math can
// be used for interpolation of any 3D data, where an {x, y, z} vertex would
// make more sense. That is why we define two identical structures using two
// different sets of labels. Use whichever is more appropriate to your work,
// and just typecast it to (KOLIBA_VERTEX *), or such, as necessary. So we can
// stay parsimonious with our entia and generous with our titularia.
typedef struct _KOLIBA_RGB {
double r;
double g;
double b;
} KOLIBA_RGB, KOLIBA_VERTEX;
typedef struct _KOLIBA_XYZ {
double x;
double y;
double z;
} KOLIBA_XYZ;
// The workhorse of the Koliba library is the FLUT, representing the
// multiplication factors of a simple look-up table.
typedef struct _KOLIBA_FLUT {
KOLIBA_VERTEX Black;
KOLIBA_VERTEX Red;
KOLIBA_VERTEX Green;
KOLIBA_VERTEX Blue;
KOLIBA_VERTEX Yellow;
KOLIBA_VERTEX Magenta;
KOLIBA_VERTEX Cyan;
KOLIBA_VERTEX White;
} KOLIBA_FLUT;
// The most important support type in the Koliba library is a list of 24
// bit flags, which tell Koliba which factors in the FLUT to use and
// which to ignore. This serves two purposes: (1) to speed up the processing,
// and (2) to allow the user to skip certain values within a FLUT to create
// some special effect. We keep the 24 bit flags in the lower 24 bits of
// a 32-bit integer.
typedef unsigned int KOLIBA_FLAGS;
// In some cases we need to pass a pointer to an array of FLUTs with their
// corresponding FLAGS. We use the following combined structure for those
// arrays.
typedef struct _KOLIBA_FFLUT {
KOLIBA_FLUT *fLut;
KOLIBA_FLAGS flags;
} KOLIBA_FFLUT;
// While the FLUT is our workhorse, traditionally people have been sharing
// look-up tables that do not contain the multiplication factors, but the
// values of red, green, and blue pixels to be interpolated. We need to be
// able to convert those to our FLUT and FLAGS. For that, we declare a simple
// look-up table, SLUT (pronounced in English the same as "salute").
//
// We might also say the FLUT is the heart of the Koliba library, and SLUT is
// its brain. When developing effects, we always go through various steps of
// producing a SLUT first. It is intuitive, it is logical, it is mathematical.
// It can be applied directly to pixel manipulation (and in the original
// Koliba Palette Mallet OFX plug-in it was), but doing that is slower than
// converting the SLUT into a FLUT (and the corresponding flags), which can,
// and does, work much faster.
typedef struct _KOLIBA_SLUT {
KOLIBA_VERTEX Black;
KOLIBA_VERTEX Blue;
KOLIBA_VERTEX Green;
KOLIBA_VERTEX Cyan;
KOLIBA_VERTEX Red;
KOLIBA_VERTEX Magenta;
KOLIBA_VERTEX Yellow;
KOLIBA_VERTEX White;
} KOLIBA_SLUT;
// Since, in the .sLut file (defined later), this is followed by a checksum,
// we may want to read it all into this structure:
typedef struct _KOLIBA_SLUT2 {
KOLIBA_SLUT sLut;
double checksum;
} KOLIBA_SLUT2;
// The vertices in the SLUT structure are sorted in a mathematically
// meaningful order. We do not use that order when presenting a LUT in user
// interfaces, simply because we tend to think of the palette the vertices
// represent as black, white, red, green, blue, cyan, magenta, yellow.
// So we have a presentation LUT (or PLUT) structure.
//
// In addition to the vertices, this structure also contains a divisor
// (which is expected to be greater than or equal to 1) to accomodate
// systems that present color ranges as {0-255} and such. It also
// contains an efficacy value to make it possible to fade a LUT in
// and out.
typedef struct _KOLIBA_PLUT {
KOLIBA_VERTEX Black;
KOLIBA_VERTEX White;
KOLIBA_VERTEX Red;
KOLIBA_VERTEX Green;
KOLIBA_VERTEX Blue;
KOLIBA_VERTEX Cyan;
KOLIBA_VERTEX Magenta;
KOLIBA_VERTEX Yellow;
double divisor; // >= 1.0, so we can deal with 8-bit LUTs and such
double efficacy;
} KOLIBA_PLUT;
// While we use a certain order of vertices in a SLUT, we may deal with LUT
// data organized differently depending on the source of such data. Assuming
// it still presents the data as a series of red, green, blue (or x, y, z)
// vertices, we can easily use it thanks to our VERTICES pointers.
typedef struct _KOLIBA_VERTICES {
KOLIBA_VERTEX *black;
KOLIBA_VERTEX *blue;
KOLIBA_VERTEX *green;
KOLIBA_VERTEX *cyan;
KOLIBA_VERTEX *red;
KOLIBA_VERTEX *magenta;
KOLIBA_VERTEX *yellow;
KOLIBA_VERTEX *white;
} KOLIBA_VERTICES;
// For the sake of converting .sLut files to .cube files easily, we can use
// a different pointer structure, which uses the less-intuitive ordering of
// vertices used by those files.
typedef struct _KOLIBA_CUBE_VERTICES {
KOLIBA_VERTEX *black;
KOLIBA_VERTEX *red;
KOLIBA_VERTEX *green;
KOLIBA_VERTEX *yellow;
KOLIBA_VERTEX *blue;
KOLIBA_VERTEX *magenta;
KOLIBA_VERTEX *cyan;
KOLIBA_VERTEX *white;
} KOLIBA_CUBE_VERTICES;
// As much as we love to keep our LUTs parsimoniously simple, not everyone
// does (some people even brag about their LUTs being bigger than other
// people's LUTs).
//
// For such larger LUTs, we may use an indexed FLUT. Each index entry consists
// of an x, y, z (or red, green, blue) unsigned 32-bit integer:
typedef struct _KOLIBA_XYZINDEX {
unsigned int x, y, z;
} KOLIBA_XYZINDEX;
// We then arrange the indices to indicate individual members of a KOLIBA_FLUT
// as doubles distanced from a base.
typedef struct _KOLIBA_FLINDEX {
KOLIBA_XYZINDEX Black;
KOLIBA_XYZINDEX Red;
KOLIBA_XYZINDEX Green;
KOLIBA_XYZINDEX Blue;
KOLIBA_XYZINDEX Yellow;
KOLIBA_XYZINDEX Magenta;
KOLIBA_XYZINDEX Cyan;
KOLIBA_XYZINDEX White;
} KOLIBA_FLINDEX;
// It is possible (indeed, that is the reason to have indices) for several
// indices to indicate the same double. For example, the identity FLUT
// consists of only two values, 0.0 and 1.0, so if we placed a 0.0 at some
// base, and placed a 1.0 right after it, an "Identity FLIndex" would look
// like this:
//
// KOLIBA_FLINDEX fli = {
// {0, 0, 0}, // black
// {1, 0, 0}, // red
// {0, 1, 0}, // green
// ... etc ...
// };
//
// And to actually find the proper value of any FLUT member, we would have a
// base like this:
//
// double base[2] = {0.0, 1.0};
//
// And then to find the value of, say, green.r, which we would call green.x,
// we would take it from base[fli.green.x] (which happens to be 0.0 in this
// case, but of course can be any value in any other case).
//
// This sacrifices some speed for size, so it makes no sense to condense a
// SLUT into an indexed fLut, but it is quite useful to cram a huge LUT (up to
// 256 x 256 x 256 subtables) into a much smaller space.
//
// The index takes some space. The 32-bit unsigned integer is one half the
// size of a double, so we break even if we can reduce the size of a LUT by
// half. In other words, it is useful to crowd the doubles into a smaller
// space when we can shrink the total number of the doubles to much less than
// one half of the original size.
//
// Alternately, if the total number of unique doubles is no more than 256, we
// can compact the indices to one quarter of their size by using 8-bit bytes
// instead of 32-bit unsigned integers.
typedef struct _KOLIBA_XYZBINDEX {
unsigned char x, y, z;
} KOLIBA_XYZBINDEX;
typedef struct _KOLIBA_FLBINDEX {
KOLIBA_XYZBINDEX Black;
KOLIBA_XYZBINDEX Red;
KOLIBA_XYZBINDEX Green;
KOLIBA_XYZBINDEX Blue;
KOLIBA_XYZBINDEX Yellow;
KOLIBA_XYZBINDEX Magenta;
KOLIBA_XYZBINDEX Cyan;
KOLIBA_XYZBINDEX White;
} KOLIBA_FLBINDEX;
//
// As a compromise, we can use 16-bit unsigned short integers if the total
// number of groups is more than 256 but not more than 65536.
typedef struct _KOLIBA_XYZWINDEX {
unsigned short x, y, z;
} KOLIBA_XYZWINDEX;
typedef struct _KOLIBA_FLWINDEX {
KOLIBA_XYZWINDEX Black;
KOLIBA_XYZWINDEX Red;
KOLIBA_XYZWINDEX Green;
KOLIBA_XYZWINDEX Blue;
KOLIBA_XYZWINDEX Yellow;
KOLIBA_XYZWINDEX Magenta;
KOLIBA_XYZWINDEX Cyan;
KOLIBA_XYZWINDEX White;
} KOLIBA_FLWINDEX;
//
// Similarly, we may index the FLAGS required for each indexed FLUT. Such an
// index may be an array of unsigned bytes if there are no more than 256 of
// them. Or it may be an unsigned short if there are no more than 65536 of
// them. (By them I mean indices, not FLAGS.) If there are more than 65536,
// there is no point indexing them because we would have to use 32-bit
// indices for 24-bit numbers. A special case may exist when all of the FLAGS
// are the same regardless of which indexed FLUT is used. There is no need for
// an index then, we just need to have a suitable flag to let us know we have
// a "universal" set of FLAGS.
//
// We only need to flag such a special case for the FLAGS, never for the FLUTs
// because if all the base contains is a list of identical values, we would be
// dealing with a simple LUT, and we never index those.
//
// We can summarize all these options in just two dibits, one for the indexed
// FLUTs, the other for the indexed FLAGS. The values within either dibit are
// mutually exclusive, so we may pick one from the one dibit, one from the
// other dibit, and or them together.
//
// So, our options for the fLuts are:
//
// 0 = no indexing used, the entire fLut array is available;
// 1 = a byte index is used;
// 2 = a two-byte (unsigned short or uint16_t) is used;
// 3 = a four-byte (unsigned int or uint32_t) is used.
//
#define KOLIBA_NOFLINDEX 0
#define KOLIBA_USEFLBINDEX 1
#define KOLIBA_USEFLWINDEX 2
#define KOLIBA_USEFLINDEX 3
//
// And our options for the flags are:
//
// 0 = no indexing, the entire flag array is available;
// 1 = byte index;
// 2 = two-byte index;
// 3 = no index, one 24-bit flag used for everything.
//
#define KOLIBA_NOFFLINDEX (0 << 2)
#define KOLIBA_USEFFLBINDEX (1 << 2)
#define KOLIBA_USEFFLWINDEX (2 << 2)
#define KOLIBA_USEONEFFLAG (3 << 2)
// And just for consistency, even if not quite clear
// but perhaps easier to remember:
#define KOLIBA_USEFFLINDEX KOLIBA_USEONEFLAG
// To apply a FLUT to a 32-bit floating-point pixel, we need to define a
// suitable 32-bit pixel structure.
typedef struct _KOLIBA_PIXEL {
float red;
float green;
float blue;
} KOLIBA_PIXEL;
// Strangely, many video editors are still using pixels with 8 bits per
// channel. We might need to deal with them.
typedef union _KOLIBA_RGBA8PIXEL {
struct {uint8_t r, g, b, a;};
uint32_t px;
} KOLIBA_RGBA8PIXEL;
// And some, e.g. Vegas Pro, prefer BGRA.
typedef union _KOLIBA_BGRA8PIXEL {
struct {uint8_t b, g, r, a;};
uint32_t px;
} KOLIBA_BGRA8PIXEL;
// Additionally, ARGB and ABGR are possible.
typedef union _KOLIBA_ARGB8PIXEL {
struct {uint8_t a, r, g, b;};
uint32_t px;
} KOLIBA_ARGB8PIXEL;
typedef union _KOLIBA_ABGR8PIXEL {
struct {uint8_t a, g, b, r;};
uint32_t px;
} KOLIBA_ABGR8PIXEL;
// PNG bitmaps may also use unsigned 16-bit integers per channel.
typedef struct _KOLIBA_RGBA16PIXEL {
uint16_t r, g, b, a;
} KOLIBA_RGBA16PIXEL;
// Most professional video editors use 32-bit floats for color grading to
// reduce crushing and clipping.
typedef struct _KOLIBA_RGBA32PIXEL {
float r, g, b, a;
} KOLIBA_RGBA32PIXEL;
typedef struct _KOLIBA_BGRA32PIXEL {
float b, g, r, a;
} KOLIBA_BGRA32PIXEL;
typedef struct _KOLIBA_ARGB32PIXEL {
float a, r, g, b;
} KOLIBA_ARGB32PIXEL;
typedef struct _KOLIBA_ABGR32PIXEL {
float a, g, b, r;
} KOLIBA_ABGR32PIXEL;
// A matrix is a common tool used in color grading. Strictly mathematically
// speaking, we should work with 4x4 square matrices. In reality, however, the
// last row of any of our matrices happens to always be {0, 0, 0, 1}. Always.
// We, therefore, only need to keep track of the first three rows.
typedef struct _KOLIBA_ROW {
double r; // = red
double g; // = green
double b; // = blue
double o; // = offset
} KOLIBA_ROW;
typedef struct _KOLIBA_MATRIX {
KOLIBA_ROW Red;
KOLIBA_ROW Green;
KOLIBA_ROW Blue;
} KOLIBA_MATRIX;
// When stored in a file, we use this:
typedef struct _KOLIBA_MATRIX2 {
KOLIBA_MATRIX mat;
double checksum;
} KOLIBA_MATRIX2;
// A "channel blend" is a way to present a matrix to the end user, while
// offering the option to normalize any or all rows, as well as have an
// efficacy to bring it closer to or further away from an identity matrix
// (after applying any normalizations that may be needed).
//
// N.B. The original definition had unsigned char instead of bool.
// I hesitated to change those to bool because as of this writing
// different C compilers are free to use a different sizeof(bool).
//
// The only reason I did change it because when ported to scripting
// languages, such as python, users could not type in boolean
// names, as some of those languages have very strong types. :(
//
// Luckily, I suppose, this structure is used internally by the
// library. But be careful if you want to save this structure
// in a binary file, as that may not be portable. Of course, we
// can save a marshaling text in a file, and that should be
// portable.
typedef struct _KOLIBA_CHANNELBLEND {
KOLIBA_MATRIX mat;
double efficacy;
bool nr; // normalize red row
bool ng; // normalize green row
bool nb; // normalize blue row
bool na; // normalize all rows, overruling nr, ng, nb
} KOLIBA_CHANNELBLEND;
// An angle structure (or should we say class) helps converting among degrees,
// radians, turns, pis, and potentially other units added in the future.
typedef struct _KOLIBA_ANGLE {
double angle;
KOLIBA_ANGLEUNITS units;
} KOLIBA_ANGLE;
// A "chroma matrix" is the base type of several other "matrix" types that
// define changes to apply to a pixel in the YCbCr color space. That is to
// say, it is not a matrix per se, but it can be unequivocally converted into
// our matrix type, providing we use an RGB model in the conversion.
typedef struct _KOLIBA_CHROMA {
double angle; // in degrees
double magnitude; // angle efficacy
double saturation;
double black;
double white;
} KOLIBA_CHROMA;
// A "chromatic matrix" combines the chroma matrix with the RGB model to use
// for the conversion from the RGB space to the YCbCr space and then back. It
// also is a base type of several other types.
//
// We refer to it as a chromatic matrix because while it is based on the YCbCr
// idea of luma and chroma, ultimately it is easy to convert to an RGBO matrix
// used in Koliba.
//
// In our own terminology, angle, magnitude and saturation affect the farba;
// black and white affect the svit; and the model affects both, albeit
// indirectly as it normally does not change.
typedef struct _KOLIBA_CHROMAT {
KOLIBA_RGB model;
KOLIBA_CHROMA chroma;
} KOLIBA_CHROMAT;
typedef struct _KOLIBA_CHROMAT2 {
KOLIBA_CHROMAT chromat;
double checksum;
} KOLIBA_CHROMAT2;
typedef struct _KOLIBA_DICHROMA {
KOLIBA_CHROMAT chr;
double rotation;
double efficacy;
} KOLIBA_DICHROMA;
typedef struct _KOLIBA_DICHROMA2 {
KOLIBA_DICHROMA dichroma;
double checksum;
} KOLIBA_DICHROMA2;
// The KOLIBA_DICHROMA structure is really just a special case of the
// KOLIBA_ANACHROMA structure.
typedef struct _KOLIBA_ANACHROMA {
KOLIBA_RGB model;
KOLIBA_CHROMA chr[2];
double rotation;
double efficacy;
} KOLIBA_ANACHROMA;
// And the KOLIBA_ANACHROMA structure is really just a special case of the
// KOLIBA_DIACHROMA structure.
typedef struct _KOLIBA_DIACHROMA {
KOLIBA_RGB model;
KOLIBA_CHROMA chr[3];
double rotation;
double efficacy;
} KOLIBA_DIACHROMA;
// A color filter (CFLT) allows us to emulate a color filter attached to the
// front of a camera objective (lens assembly). The "d" member defines the
// density of the filter.
typedef struct _KOLIBA_CFLT {
double r;
double g;
double b;
double d;
} KOLIBA_CFLT;
typedef struct _KOLIBA_CFLT2 {
KOLIBA_CFLT cFlt;
double checksum;
} KOLIBA_CFLT2;
// It may be useful to treat a SLUT as a twin matrix, where one matrix
// corresponds to black, red, green and blue (the primary farba plus black),
// and the other to cyan, magenta, yellow and white (the secondary farba
// plus white). That way we can use matrix multiplication to chain SLUTs
// as opposed to just trying to concatenate SLUTs and hoping for the best.
//
// Also, technically, the rows in the s matrix should be labeled
// cyan, magenta, yellow, but that would complicate things and force
// us into unnecessary typecasting.
typedef struct _KOLIBA_GEMINIX {
KOLIBA_MATRIX p;
KOLIBA_MATRIX s;
} KOLIBA_GEMINIX;
// Sometimes a simple LUT (SLUT) does not seem enough to represent the desired
// color change, and we might be tempted to create a large LUT in its stead.
//
// In many such cases it is simpler to create a suitable mask and combine the
// result of applying a SLUT with the original image. This mask may need to be
// based either on the original (usually) or on the modified image (rarely).
//
// And quite often such a mask needs to be based on the luminance of each pixel
// (whether increasing or decreasing luminance) or on either the chrominance
// (usually) or the saturation (rarely) of the pixel (never both, though, since
// saturation derives from chrominance). And sometimes even a combination of
// luminance with either the chrominance or the saturation.
//
// Now, from now on, instead of saying "chrominance or saturation" we will use
// "saturance" (a portmanteau word of saturation and chrominance).
//
// Surprisingly, we could not find a way of combining the original and the
// changed image based on these criteria, at least not in the several video
// editing programs we use. So, we came up with Lumidux, which does just that.
//
// The Lumidux structure contains the following data.
//
// A byte, set to either 0, 1, or -1 indicating whether the luminance
// is ascending (0), descending (1), or off (-1).
//
// A byte, set to either 0 or 1, indicating whether to base luma on the
// background (0) or foreground (1) pixel. When used with an effect, the
// original pixel is the background, the processed pixel is the
// foreground.
//
// A byte set to values betwen 0 and 6, indicating luminous speed.
//
// A byte, set to 1 if the mask is to be shown, 0 otherwise.
//
// A byte set to 0, 1, or -1 indicating whether the saturance is
// ascending (0), descending (1), or off (-1).
//
// A byte set to 0 or 1, indicating whether to base saturance on the
// background or foreground pixel.
//
// A byte set to values between 0 and 6, indicating saturous speed.
//
// A byte set to 0 to indicate the use of saturation, or 1 for
// chrominance (collectively referred to by the saturance portmanteau).
//
// A 64-bit floating point (double) with a low luminance threshold.
//
// A 64-bit double with a high luminance threshold.
//
// A 64-bit double with a low saturance threshold.
//
// A 64-bit double with a high saturance threshold.
//
// When used the way mentioned above (before and after modification of an
// image), the background refers to the original image, foreground to the same
// image after it has been modified by applying a LUT.
//
// Naturally, you can use Lumidux to combine any two images. That said, we
// suggest that whenever used to combine an original image with a modified
// version thereof, you may want to place the original underneath the modified
// version, i.e., use the background for the original and foreground for the
// newly produced mutation. That way you will be using it consistently every
// time, and dish out no surprises.
typedef struct _KOLIBA_LDX {
signed char ldirection; // 0, 1, -1
unsigned char lbase; // 0, 1
unsigned char lspeed; // 0-6
unsigned char mask; // 0, 1
signed char sdirection; // 0, 1, -1
unsigned char sbase; // 0, 1
unsigned char sspeed; // 0-6
unsigned char schroma; // 0, 1
double llow;
double lhigh;
double slow;
double shigh;
} KOLIBA_LDX;
// A Koliba Palette is a structure used in the Koliba Palette Mallet OFX
// plug-in, and now here.
//
// It consists of eight "pigments", black, white, red, green, blue, cyan,
// magenta, and yellow. It was conceptually inspired by the automaton in the
// movie Hugo, but in our case the automaton does not draw a picture with a
// pen, it paints an image with a palette of eight pigments (like those used
// in an oil painting). If we change the color of any of the pigments, the
// automaton will not know. It will just paint with the new palette. The
// painting would, on one hand, be the same as the one painted with the
// original palette, yet, on the other hand, it would have a different mood.
//
// This is precisely what a simple LUT (SLUT) does. But it is more intuitive
// to do so with the palette.
//
// Because the palette was created for an OFX plug-in, and the OFX standard
// only allows an RGB color to have values from 0 to 1, while our SLUT does
// not have such limitations, we have added an efficacy field to our pigment
// definition. It allows us to stay within those limitations but convert the
// palette to a SLUT without such limitations. But for portability we impose
// that limit to a pigment and reject it as invalid if any of its RGB channels
// is outside the [0..1] range.
//
// So, we start by defining a pigment, which is a double RGB color with an
// efficacy added.
typedef struct _KOLIBA_PIGMENT {
double rp; // [0..1] only.
double gp; // [0..1] only.
double bp; // [0..1] only.
double efficacy; // Any real value, though keep it realistic.
} KOLIBA_PIGMENT;
// The palette consists of eight pigments, an overall efficacy, but also with
// an erythropy flag. What is erythropy you ask?
//
// Strictly speaking the word should be erythrotropy, but I took the liberty
// to shorten it to erythropy. It tells us to take the red pigment (including
// its efficacy) and ignore all the other pigments in the palette and rotate
// them the same way red is rotated.
//
// Well, actually not all of them. For the purposes of the Koliba color model,
// we refer to the black and white pigments as the svit and to the rest of
// them as the farba. Erythropy only affects the farba (and only if the
// erythropy flag is not 0).
//
// So here is our palette definition.
typedef struct _KOLIBA_PALETTE {
KOLIBA_PIGMENT Black;
KOLIBA_PIGMENT White;
KOLIBA_PIGMENT Red;
KOLIBA_PIGMENT Green;
KOLIBA_PIGMENT Blue;
KOLIBA_PIGMENT Cyan;
KOLIBA_PIGMENT Magenta;
KOLIBA_PIGMENT Yellow;
double efficacy;
bool erythropy;
} KOLIBA_PALETTE;
// For the use a Koliba palette (.kPlt) file, we use a slightly modified
// structure (this is explained further later in the section describing
// various file formats).
typedef struct _KOLIBA_PALETTE2 {
KOLIBA_PIGMENT Black;
KOLIBA_PIGMENT White;
KOLIBA_PIGMENT Red;
KOLIBA_PIGMENT Green;
KOLIBA_PIGMENT Blue;
KOLIBA_PIGMENT Cyan;
KOLIBA_PIGMENT Magenta;
KOLIBA_PIGMENT Yellow;
double efficacy;
double checksum;
} KOLIBA_PALETTE2;
// A way to manipulate a SLUT is the palette mallet. The mallet can manipulate
// all vertices at the same time, or just one of them, or a group of them. We
// use several flags to determine which vertices a mallet applies to. And
// since there are only eight vertices in a SLUT, all of these can fit in an
// eight-bit byte, or a C unsigned char.
#define KOLIBA_SLUTBLACK 0x01
#define KOLIBA_SLUTBLUE 0x02
#define KOLIBA_SLUTGREEN 0x04
#define KOLIBA_SLUTCYAN 0x08
#define KOLIBA_SLUTRED 0x10
#define KOLIBA_SLUTMAGENTA 0x20
#define KOLIBA_SLUTYELLOW 0x40
#define KOLIBA_SLUTWHITE 0x80
#define KOLIBA_SLUTALL 0xFF
#define KOLIBA_SLUTREDS (KOLIBA_SLUTRED | KOLIBA_SLUTMAGENTA | KOLIBA_SLUTYELLOW)
#define KOLIBA_SLUTGREENS (KOLIBA_SLUTGREEN | KOLIBA_SLUTCYAN | KOLIBA_SLUTYELLOW)
#define KOLIBA_SLUTBLUES (KOLIBA_SLUTBLUE | KOLIBA_SLUTCYAN | KOLIBA_SLUTMAGENTA)
#define KOLIBA_SLUTCYANS (KOLIBA_SLUTCYAN | KOLIBA_SLUTGREEN | KOLIBA_SLUTBLUE)
#define KOLIBA_SLUTMAGENTAS (KOLIBA_SLUTMAGENTA | KOLIBA_SLUTRED | KOLIBA_SLUTBLUE)
#define KOLIBA_SLUTYELLOWS (KOLIBA_SLUTYELLOW | KOLIBA_SLUTRED | KOLIBA_SLUTGREEN)
#define KOLIBA_SLUTSVIT (KOLIBA_SLUTBLACK | KOLIBA_SLUTWHITE)
#define KOLIBA_SLUTPRIMARY (KOLIBA_SLUTRED | KOLIBA_SLUTGREEN | KOLIBA_SLUTBLUE)
#define KOLIBA_SLUTSECONDARY (KOLIBA_SLUTCYAN | KOLIBA_SLUTMAGENTA | KOLIBA_SLUTYELLOW)
#define KOLIBA_SLUTFARBA (KOLIBA_SLUTPRIMARY | KOLIBA_SLUTSECONDARY)
#define KOLIBA_SLUTCOLD (KOLIBA_SLUTCYANS)
#define KOLIBA_SLUTWARM (KOLIBA_SLUTREDS)
#define KOLIBA_SLUTSELENE (KOLIBA_SLUTYELLOWS)
#define KOLIBA_SLUTNYX (KOLIBA_SLUTBLUES)
#define KOLIBA_SLUTORION (KOLIBA_SLUTGREENS)
#define KOLIBA_SLUTBETELGEUSE (KOLIBA_SLUTMAGENTAS)
//
// The above flags are used in the palette mallet, defined by the following
// structure (which is adjusted for a better packing order, which is different
// from its pecking order).
typedef struct _KOLIBA_MALLET { // the squiggles show KOLIBA_IdentityMallet
KOLIBA_RGB center; // the colors of the contrast center {0.5, 0.5, 0.5}
double adjustment; // how much to adjust the contrast {0.0}
double natcon; // how much to adjust the natural contrast {0.0}
double lift; // how much closer to get to white {0.0}
double gain; // how much to move away from black {1.0}
double offset; // how much to add to all channels {0.0}
double efficacy; // the efficacy of everything but saturation {1.0}
double saturation; // how much to increase/decrease saturation {1.0}
unsigned char invert; // a boolean to tell whether to invert {0}
unsigned char flags; // which vertices to apply the mallet to {0}
} KOLIBA_MALLET;
// Unlike the packing order, the pecking order determines what to do first
// (since some of the options sort of contradict each other).
//
// Flags have the highest pecking order since they decide which SLUT vertices
// to affect.
//
// Invert (produce a negative) is applied to those vertices first.
//
// Lift is next, except if the flags are *equal* to KOLIBA_SLUTWHITE, in which
// case gain takes its place.
//
// Contrast adjustment is next. It does its job by moving the vertices closer
// to or farther from the contrast center.
//
// Natural contrast is next. It moves vertices closer to or farther away from
// their own natural contrast centers (each vertex has its own immutable
// natural center).
//
// This is followed by applying gain, except for KOLIBA_SLUTWHITE, which gets
// its lift.
//
// Offset is added next.
//
// All of the above is adjusted according to the requested efficacy.
//
// Finally, saturation is adjusted.
//
// Despite calling it the pecking order, with the exception of the flags, the
// things that happen later may override the effect of things that happen
// earlier, so we might say (except for the flags, again), he who pecks last
// pecks best...
//
// Just kidding, of course, as they all affect the result. They just need to
// be evaluated in a consistent order because otherwise we would be getting
// inconsistent results!
// The concept of efficacy appears all over the Koliba library. The efficacy
// is usually a single number used to interpolate a LUT with whatever default
// exists for that type of LUT. When the number is lesser than 0 or greater
// than 1, it results in an extrapolation instead of interpolation. It is
// surprising how often video effects allow interpolation but not
// extrapolation, even though that extends the usefulness of most effects. Of
// course, it may occasionally result in producing garbage, but surely we
// trust our users to decide on their own what is useful and what is grabage
// (especially since they may be making a video on garbage).
//
// Another possible interpolation is applying a separate efficacy to each
// vertex of a LUT for maximum flexibility. To make it easy, we define a "LUT"
// of efficacies.
typedef struct _KOLIBA_EFFILUT {
double black;
double blue;
double green;
double cyan;
double red;
double magenta;
double yellow;
double white;
} KOLIBA_EFFILUT;
// In addition to different data types, some Koliba routines accept function
// pointers among their arguments. These need to point at specific function
// types, as defined here.
typedef KOLIBA_XYZ * (*KOLIBA_INDEXEDXYZ)(KOLIBA_XYZ *xout, const KOLIBA_XYZ * const xin, const double * const base, const KOLIBA_FLAGS * const flags, const unsigned int dim[3], const void * const flindex, const void * const findex);
typedef KOLIBA_PIXEL * (*KOLIBAXYZTOPIXEL)(KOLIBA_PIXEL *pixelout, const KOLIBA_XYZ * const xyzin);
typedef KOLIBA_XYZ *(*KOLIBAPIXELTOXYZ)(KOLIBA_XYZ *xyzout, const KOLIBA_PIXEL * const pixelin);
typedef KOLIBA_XYZ * (*KOLIBA_EXTERNAL)(KOLIBA_XYZ *xyz, const void * const params);
typedef KOLIBA_RGB * (*KOLIBA_MAKEVERTEX)(KOLIBA_RGB * rgbout, const KOLIBA_RGB * const rgbin, const void * const params);
typedef double (*KOLIBA_DBLCONV)(double);
/****************************************************************************/
/********************* ***********************/
/********************* T H E KOLIBA D E F A U L T S ***********************/
/********************* ***********************/
/****************************************************************************/
// Many Koliba data types can (and do) have default values. Such defaults are
// used to set a starting value in an effect or to interpolate and extrapolate
// various data items with/from their defaults (or in some cases, such as
// saturation interpolate the default with the change). The default are often
// called identities (for example, a default matrix is the identity matrix).
// The term identity in these cases comes from mathematics.
//
// All these defaults are included in the Koliba library.
KLBDC extern const KOLIBA_FLUT KOLIBA_IdentityFlut;
KLBDC extern const KOLIBA_SLUT KOLIBA_IdentitySlut;
KLBDC extern const KOLIBA_PLUT KOLIBA_IdentityPlut;
KLBDC extern const KOLIBA_CHANNELBLEND KOLIBA_IdentityChannelBlend;
#define KOLIBA_IdentityMatrix (KOLIBA_IdentityChannelBlend.mat)
KLBDC extern const KOLIBA_CHROMA KOLIBA_IdentityChroma;
KLBDC extern const KOLIBA_PALETTE KOLIBA_IdentityPalette;
KLBHID extern const KOLIBA_MALLET KOLIBA_IdentityMallet;
// Not exactly a default, but useful, a sLut with all zeros
// except in the white vertex.
KLBDC extern const KOLIBA_SLUT KOLIBA_NoFarbaSlut;
// And one with all channels of primary farba at 0, and of
// secondary farba at 1, which are their "home" values.
KLBDC extern const KOLIBA_SLUT KOLIBA_HomeSlut;
/****************************************************************************/
/************** ****************/
/************** T H E KOLIBA D A T A ( C O N S T A N T S ) ****************/
/************** ****************/
/****************************************************************************/
// Here are some useful constants. They are as precise as the 64-bit floating
// point format allows.
KLBDC extern const double KOLIBA_Pi;
KLBDC extern const double KOLIBA_2Pi;
KLBDC extern const double KOLIBA_PiDiv2;
KLBDC extern const double KOLIBA_PiDiv180;
KLBDC extern const double KOLIBA_180DivPi;