summaryrefslogtreecommitdiff
path: root/src/mari/1.4v1/ocio.py
blob: 4f0f61e995fb897659fc2c0870beea5947083d20 (plain)
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
#-------------------------------------------------------------------------------
# OpenColorIO (color management) related Mari scripts
# coding: utf-8
# Copyright (c) 2011 The Foundry Visionmongers Ltd.  All Rights Reserved.
#-------------------------------------------------------------------------------

import mari, time, PythonQt, os, math

##############################################################################################

# Enable to output extended debugging messages.
VERBOSE_ENABLED = False

# Message type identifiers.
class MessageType:
    DEBUG   = 1
    INFO    = 2
    WARNING = 3
    ERROR   = 4

def printMessage(type, message):
    if type == MessageType.DEBUG:
        if VERBOSE_ENABLED:
            mari.app.log('[ OpenColorIO ] %s' % message)
    elif type == MessageType.INFO:
        mari.app.log('[ OpenColorIO ] %s' % message)
    elif type == MessageType.WARNING:
        mari.app.log('[ OpenColorIO ] [ WARNING ] %s' % message)
    elif type == MessageType.ERROR:
        mari.app.log('[ OpenColorIO ] [ ERROR ] %s' % message)

##############################################################################################

def configFileFilter():
    return 'OpenColorIO Configuration (*.ocio)'

#---------------------------------------------------------------------------------------------

def lutFileFilter():
    result  = 'All LUT Files (*.3dl *.ccc *.cc *.csp *.cub *.cube *.hdl *.m3d *.mga *.spi1d *.spi3d *.spimtx *.vf);;'
    result += 'Autodesk LUT (*.3dl);;'
    result += 'ASC CDL Color Correction Collection LUT (*.ccc);;'
    result += 'ASC CDL Color Correction LUT (*.cc);;'
    result += 'Cinespace LUT (*.csp);;'
    result += 'Truelight LUT (*.cub);;'
    result += 'Iridas LUT (*.cube);;'
    result += 'Houdini LUT (*.hdl);;'
    result += 'Pandora LUT (*.m3d *.mga);;'
    result += 'Imageworks LUT (*.spi1d *.spi3d *.spimtx);;'
    result += 'Inventor LUT (*.vf)'
    return result

##############################################################################################

# Make sure the OpenColorIO python bindings are okay.
try:
    import PyOpenColorIO
    printMessage(MessageType.INFO, 'Loaded Python bindings \'%s\' successfully' % PyOpenColorIO.__file__)
except ImportError, e:
    PyOpenColorIO = None
    printMessage(MessageType.ERROR, 'Failed to load Python bindings \'%s\'' % e)

LUT_FILE_LIST_RESET = mari.FileList(mari.FileList.TYPE_SINGLE_FILE)
LUT_FILE_LIST_RESET.setAcceptNonExisting(True)
LUT_FILE_LIST_RESET.setFilter(lutFileFilter())

CONFIG_FILE_LIST_RESET = mari.FileList(mari.FileList.TYPE_SINGLE_FILE)
if mari.app.isRunning():
    CONFIG_FILE_LIST_RESET.append(mari.resources.path(mari.resources.COLOR) + '/OpenColorIO/nuke-default/config.ocio')
else:
    CONFIG_FILE_LIST_RESET.append('/OpenColorIO/nuke-default/config.ocio')
CONFIG_FILE_LIST_RESET.setPickedFile(CONFIG_FILE_LIST_RESET.at(0))
CONFIG_FILE_LIST_RESET.setAcceptNonExisting(True)
CONFIG_FILE_LIST_RESET.setFilter(configFileFilter())

SQRT_TWO                 = math.sqrt(2.0)
LUT_SIZE_TYPES           = ['Small', 'Medium', 'Large']
LUT_SIZE_VALUES          = {'Small': 16,
                           'Medium': 32,
                            'Large': 64}
LUT_SIZE_RESET           = LUT_SIZE_TYPES[1]
ENABLED_RESET            = True
PROFILE_RESET            = 'Color Space'
LUT_EXTRAPOLATE_RESET    = True
COLOR_SPACE_RESET        = 'sRGB'
DISPLAY_RESET            = 'default'
VIEW_RESET               = 'sRGB'
SWIZZLE_TYPES            = ['Luminance', 'RGB', 'R', 'G', 'B', 'A']
SWIZZLE_VALUES           = {'Luminance': ( True,  True,  True, False),
                                  'RGB': ( True,  True,  True,  True),
                                    'R': ( True, False, False, False),
                                    'G': (False,  True, False, False),
                                    'B': (False, False,  True, False),
                                    'A': (False, False, False,  True)}
SWIZZLE_RESET            = SWIZZLE_TYPES[1]
FSTOP_STEP_SIZE          = 0.5
FSTOP_CENTER_MIN         = 1.0
FSTOP_CENTER_MAX         = 64.0
FSTOP_CENTER_STEP_SIZE   = 0.001
FSTOP_CENTER_RESET       = 8.0
EXPOSURE_MIN             = -6.0
EXPOSURE_MAX             = +6.0
EXPOSURE_DELTA           = EXPOSURE_MAX - EXPOSURE_MIN
EXPOSURE_STEP_SIZE       = 0.1
GAIN_RESET               = 1.0
GAIN_MIN                 = 2.0 ** EXPOSURE_MIN
GAIN_MAX                 = 2.0 ** EXPOSURE_MAX
GAIN_STEP_SIZE           = 0.000001
GAIN_PRECISION           = 6
GAMMA_RESET              = 1.0
GAMMA_MIN                = 0.0
GAMMA_MAX                = 4.0
GAMMA_STEP_SIZE          = 0.01
GAMMA_PRECISION          = 2

enabled_default          = ENABLED_RESET
profile_default          = PROFILE_RESET
lut_file_list_default    = mari.FileList(LUT_FILE_LIST_RESET)
lut_extrapolate_default  = LUT_EXTRAPOLATE_RESET
config_file_list_default = mari.FileList(CONFIG_FILE_LIST_RESET)
color_space_default      = COLOR_SPACE_RESET
display_default          = DISPLAY_RESET
view_default             = VIEW_RESET
swizzle_default          = SWIZZLE_RESET
gain_default             = GAIN_RESET
gamma_default            = GAMMA_RESET
lut_size                 = LUT_SIZE_RESET
fstop_center             = FSTOP_CENTER_RESET

config_default           = None

lut_size_functions       = []
fstop_center_functions   = []

##############################################################################################

def registerLUTSizeChanged(function):
    global lut_size_functions
    lut_size_functions.append(function)

#---------------------------------------------------------------------------------------------

def registerFStopCenterChanged(function):
    global fstop_center_functions
    fstop_center_functions.append(function)

#---------------------------------------------------------------------------------------------

def _enabledDefaultChanged():
    global enabled_default
    enabled_default = mari.prefs.get('/Color/Color Management Defaults/colorEnabledDefault')
    _savePreferences()

#---------------------------------------------------------------------------------------------

def _profileDefaultChanged():
    global profile_default
    profile_default = mari.prefs.get('/Color/Color Management Defaults/colorProfileDefault')
    _savePreferences()

#---------------------------------------------------------------------------------------------

def _postFilterCollectionAdded(filter_collection):
    mari.prefs.setItemList('/Color/Color Management Defaults/colorProfileDefault', mari.gl_render.postFilterCollectionNames())

#---------------------------------------------------------------------------------------------

def _postFilterCollectionRemoved(name):
    collection_names = mari.gl_render.postFilterCollectionNames()
    mari.prefs.setItemList('/Color/Color Management Defaults/colorProfileDefault', collection_names)

    global PROFILE_RESET
    if name == PROFILE_RESET:
        PROFILE_RESET = collection_names[0]
        mari.prefs.setDefault('/Color/Color Management Defaults/colorProfileDefault', PROFILE_RESET)

    global profile_default
    if name == profile_default:
        profile_default = PROFILE_RESET
        mari.prefs.set('/Color/Color Management Defaults/colorProfileDefault', profile_default)
        _savePreferences()

#---------------------------------------------------------------------------------------------

def _lutPathDefaultChanged():
    global lut_file_list_default
    lut_file_list_default = mari.FileList(mari.prefs.get('/Color/LUT Defaults/lutPathDefault'))
    _savePreferences()

#---------------------------------------------------------------------------------------------

def _lutExtrapolateDefaultChanged():
    global lut_extrapolate_default
    lut_extrapolate_default = mari.prefs.get('/Color/LUT Defaults/lutExtrapolateDefault')
    _savePreferences()

#---------------------------------------------------------------------------------------------

def _configPathDefaultChanged():
    global config_file_list_default

    # Only replace the existing configuration file if the new one is valid!
    config_file_list = mari.FileList(mari.prefs.get('/Color/Display Defaults/displayConfigPathDefault'))

    if not config_file_list.isEmpty():
        config = loadConfig(config_file_list.at(0), True)
        if config is not None:
            config_file_list_default = config_file_list

            global config_default
            config_default = config

            _updateColorSpaceDefault()
            _updateDisplayDefault()
            _updateViewDefault()

            _savePreferences()

        else:
            # Put back the existing configuration file that works...
            mari.prefs.set('/Color/Display Defaults/displayConfigPathDefault', config_file_list_default)
    else:
        # Put back the existing configuration file that works...
        mari.prefs.set('/Color/Display Defaults/displayConfigPathDefault', config_file_list_default)

#---------------------------------------------------------------------------------------------

def _colorSpaceDefaultChanged():
    global color_space_default
    color_space_default = mari.prefs.get('/Color/Display Defaults/displayColorSpaceDefault')
    _updateDisplayDefault()
    _updateViewDefault()
    _savePreferences()

#---------------------------------------------------------------------------------------------

def _displayDefaultChanged():
    global display_default
    display_default = mari.prefs.get('/Color/Display Defaults/displayDisplayDefault')
    _updateViewDefault()
    _savePreferences()

#---------------------------------------------------------------------------------------------

def _viewDefaultChanged():
    global view_default
    view_default = mari.prefs.get('/Color/Display Defaults/displayViewDefault')
    _savePreferences()

#---------------------------------------------------------------------------------------------

def _swizzleDefaultChanged():
    global swizzle_default
    swizzle_default = mari.prefs.get('/Color/Display Defaults/displaySwizzleDefault')
    _savePreferences()

#---------------------------------------------------------------------------------------------

def _gainDefaultChanged():
    global gain_default
    gain_default = mari.prefs.get('/Color/Display Defaults/displayGainDefault')
    _savePreferences()

#---------------------------------------------------------------------------------------------

def _gammaDefaultChanged():
    global gamma_default
    gamma_default = mari.prefs.get('/Color/Display Defaults/displayGammaDefault')
    _savePreferences()

#---------------------------------------------------------------------------------------------

def _lutSizeChanged():
    global lut_size
    global lut_size_functions
    lut_size = mari.prefs.get('/Color/Display General/displayLutSize')
    _savePreferences()
    for function in lut_size_functions:
        function()

#---------------------------------------------------------------------------------------------

def _fstopCenterChanged():
    global fstop_center
    global fstop_center_functions
    fstop_center = mari.prefs.get('/Color/Display General/displayFStopCenter')
    _savePreferences()
    for function in fstop_center_functions:
        function()

#---------------------------------------------------------------------------------------------

def _registerPreferences():
    global enabled_default
    mari.prefs.set('/Color/Color Management Defaults/colorEnabledDefault', enabled_default)
    mari.prefs.setChangedScript('/Color/Color Management Defaults/colorEnabledDefault', 'mari.utils.ocio._enabledDefaultChanged()')
    mari.prefs.setDisplayName('/Color/Color Management Defaults/colorEnabledDefault', 'Enabled')
    mari.prefs.setDefault('/Color/Color Management Defaults/colorEnabledDefault', ENABLED_RESET)

    global profile_default
    mari.prefs.set('/Color/Color Management Defaults/colorProfileDefault', profile_default)
    mari.prefs.setChangedScript('/Color/Color Management Defaults/colorProfileDefault', 'mari.utils.ocio._profileDefaultChanged()')
    mari.prefs.setDisplayName('/Color/Color Management Defaults/colorProfileDefault', 'Color Profile')
    mari.prefs.setDefault('/Color/Color Management Defaults/colorProfileDefault', PROFILE_RESET)
    mari.prefs.setItemList('/Color/Color Management Defaults/colorProfileDefault', mari.gl_render.postFilterCollectionNames())

    global lut_file_list_default
    if not lut_file_list_default.isEmpty() and not os.path.isfile(lut_file_list_default.at(0)):
        message = 'LUT file \'%s\' does not exist' % lut_file_list_default.at(0)
        printMessage(MessageType.ERROR, '%s' % message)
        lut_file_list_default = mari.FileList(LUT_FILE_LIST_RESET)

    mari.prefs.set('/Color/LUT Defaults/lutPathDefault', lut_file_list_default)
    mari.prefs.setChangedScript('/Color/LUT Defaults/lutPathDefault', 'mari.utils.ocio._lutPathDefaultChanged()')
    mari.prefs.setDisplayName('/Color/LUT Defaults/lutPathDefault', 'File')
    mari.prefs.setDefault('/Color/LUT Defaults/lutPathDefault', LUT_FILE_LIST_RESET)

    global lut_extrapolate_default
    mari.prefs.set('/Color/LUT Defaults/lutExtrapolateDefault', lut_extrapolate_default)
    mari.prefs.setChangedScript('/Color/LUT Defaults/lutExtrapolateDefault', 'mari.utils.ocio._lutExtrapolateDefaultChanged()')
    mari.prefs.setDisplayName('/Color/LUT Defaults/lutExtrapolateDefault', 'Extrapolate')
    mari.prefs.setDefault('/Color/LUT Defaults/lutExtrapolateDefault', LUT_EXTRAPOLATE_RESET)

    global config_file_list_default
    global config_default
    if not config_file_list_default.isEmpty():
        config = loadConfig(config_file_list_default.at(0), False)
        if config is not None:
            config_default = config
        else:
            config_file_list_default = mari.FileList(CONFIG_FILE_LIST_RESET)
    else:
        config_file_list_default = mari.FileList(CONFIG_FILE_LIST_RESET)

    mari.prefs.set('/Color/Display Defaults/displayConfigPathDefault', config_file_list_default)
    mari.prefs.setChangedScript('/Color/Display Defaults/displayConfigPathDefault', 'mari.utils.ocio._configPathDefaultChanged()')
    mari.prefs.setDisplayName('/Color/Display Defaults/displayConfigPathDefault', 'Configuration File')
    mari.prefs.setDefault('/Color/Display Defaults/displayConfigPathDefault', CONFIG_FILE_LIST_RESET)

    color_spaces = [color_space.getName() for color_space in config_default.getColorSpaces()]

    color_space_reset = COLOR_SPACE_RESET
    if color_spaces.count(color_space_reset) == 0:
        color_space_reset = color_spaces[0]

    global color_space_default
    if color_spaces.count(color_space_default) == 0:
        color_space_default = color_space_reset

    mari.prefs.set('/Color/Display Defaults/displayColorSpaceDefault', color_space_default)
    mari.prefs.setChangedScript('/Color/Display Defaults/displayColorSpaceDefault', 'mari.utils.ocio._colorSpaceDefaultChanged()')
    mari.prefs.setDisplayName('/Color/Display Defaults/displayColorSpaceDefault', 'Input Color Space')
    mari.prefs.setDefault('/Color/Display Defaults/displayColorSpaceDefault', color_space_reset)
    mari.prefs.setItemList('/Color/Display Defaults/displayColorSpaceDefault', color_spaces)

    displays = config_default.getDisplays()

    display_reset = DISPLAY_RESET
    if displays.count(display_reset) == 0:
        display_reset = config_default.getDefaultDisplay()

    global display_default
    if displays.count(display_default) == 0:
        display_default = display_reset

    mari.prefs.set('/Color/Display Defaults/displayDisplayDefault', display_default)
    mari.prefs.setChangedScript('/Color/Display Defaults/displayDisplayDefault', 'mari.utils.ocio._displayDefaultChanged()')
    mari.prefs.setDisplayName('/Color/Display Defaults/displayDisplayDefault', 'Display')
    mari.prefs.setDefault('/Color/Display Defaults/displayDisplayDefault', display_reset)
    mari.prefs.setItemList('/Color/Display Defaults/displayDisplayDefault', displays)

    views = config_default.getViews(display_default)

    view_reset = VIEW_RESET
    if views.count(view_reset) == 0:
        view_reset = config_default.getDefaultView(display_default)

    global view_default
    if views.count(view_default) == 0:
        view_default = view_reset

    mari.prefs.set('/Color/Display Defaults/displayViewDefault', view_default)
    mari.prefs.setChangedScript('/Color/Display Defaults/displayViewDefault', 'mari.utils.ocio._viewDefaultChanged()')
    mari.prefs.setDisplayName('/Color/Display Defaults/displayViewDefault', 'View')
    mari.prefs.setDefault('/Color/Display Defaults/displayViewDefault', view_reset)
    mari.prefs.setItemList('/Color/Display Defaults/displayViewDefault', views)

    global swizzle_default
    if SWIZZLE_TYPES.count(swizzle_default) == 0:
        swizzle_default = SWIZZLE_RESET

    mari.prefs.set('/Color/Display Defaults/displaySwizzleDefault', swizzle_default)
    mari.prefs.setChangedScript('/Color/Display Defaults/displaySwizzleDefault', 'mari.utils.ocio._swizzleDefaultChanged()')
    mari.prefs.setDisplayName('/Color/Display Defaults/displaySwizzleDefault', 'Component')
    mari.prefs.setDefault('/Color/Display Defaults/displaySwizzleDefault', SWIZZLE_RESET)
    mari.prefs.setItemList('/Color/Display Defaults/displaySwizzleDefault', SWIZZLE_TYPES)

    global gain_default
    mari.prefs.set('/Color/Display Defaults/displayGainDefault', gain_default)
    mari.prefs.setChangedScript('/Color/Display Defaults/displayGainDefault', 'mari.utils.ocio._gainDefaultChanged()')
    mari.prefs.setDisplayName('/Color/Display Defaults/displayGainDefault', 'Gain')
    mari.prefs.setDefault('/Color/Display Defaults/displayGainDefault', GAIN_RESET)
    mari.prefs.setRange('/Color/Display Defaults/displayGainDefault', GAIN_MIN, GAIN_MAX)
    mari.prefs.setStep('/Color/Display Defaults/displayGainDefault', GAIN_STEP_SIZE)

    global gamma_default
    mari.prefs.set('/Color/Display Defaults/displayGammaDefault', gamma_default)
    mari.prefs.setChangedScript('/Color/Display Defaults/displayGammaDefault', 'mari.utils.ocio._gammaDefaultChanged()')
    mari.prefs.setDisplayName('/Color/Display Defaults/displayGammaDefault', 'Gamma')
    mari.prefs.setDefault('/Color/Display Defaults/displayGammaDefault', GAMMA_RESET)
    mari.prefs.setRange('/Color/Display Defaults/displayGammaDefault', GAMMA_MIN, GAMMA_MAX)
    mari.prefs.setStep('/Color/Display Defaults/displayGammaDefault', GAMMA_STEP_SIZE)

    global lut_size
    mari.prefs.set('/Color/Display General/displayLutSize', lut_size)
    mari.prefs.setChangedScript('/Color/Display General/displayLutSize', 'mari.utils.ocio._lutSizeChanged()')
    mari.prefs.setDisplayName('/Color/Display General/displayLutSize', 'LUT Size')
    mari.prefs.setDefault('/Color/Display General/displayLutSize', LUT_SIZE_RESET)
    mari.prefs.setItemList('/Color/Display General/displayLutSize', LUT_SIZE_TYPES)

    global fstop_center
    mari.prefs.set('/Color/Display General/displayFStopCenter', fstop_center)
    mari.prefs.setChangedScript('/Color/Display General/displayFStopCenter', 'mari.utils.ocio._fstopCenterChanged()')
    mari.prefs.setDisplayName('/Color/Display General/displayFStopCenter', 'Center F-Stop')
    mari.prefs.setDefault('/Color/Display General/displayFStopCenter', FSTOP_CENTER_RESET)
    mari.prefs.setRange('/Color/Display General/displayFStopCenter', FSTOP_CENTER_MIN, FSTOP_CENTER_MAX)
    mari.prefs.setStep('/Color/Display General/displayFStopCenter', FSTOP_CENTER_STEP_SIZE)

    # Attach ourselves to the appropriate project signals so we can update widgets.
    PythonQt.QtCore.QObject.connect(mari.gl_render.postFilterCollectionAdded.__self__,
                                    mari.gl_render.postFilterCollectionAdded.__name__,
                                    _postFilterCollectionAdded)
    PythonQt.QtCore.QObject.connect(mari.gl_render.postFilterCollectionRemoved.__self__,
                                    mari.gl_render.postFilterCollectionRemoved.__name__,
                                    _postFilterCollectionRemoved)

#---------------------------------------------------------------------------------------------

def _updateColorSpaceDefault():
    global config_default
    global color_space_default

    color_spaces = [color_space.getName() for color_space in config_default.getColorSpaces()]

    color_space_reset = COLOR_SPACE_RESET
    if color_spaces.count(color_space_reset) == 0:
        color_space_reset = color_spaces[0]

    if color_spaces.count(color_space_default) == 0:
        color_space_default = color_space_reset

    mari.prefs.setItemList('/Color/Display Defaults/displayColorSpaceDefault', color_spaces)
    mari.prefs.set('/Color/Display Defaults/displayColorSpaceDefault', color_space_default)
    mari.prefs.setDefault('/Color/Display Defaults/displayColorSpaceDefault', color_space_reset)


#---------------------------------------------------------------------------------------------

def _updateDisplayDefault():
    global config_default
    global display_default

    displays = config_default.getDisplays()

    display_reset = DISPLAY_RESET
    if displays.count(display_reset) == 0:
        display_reset = config_default.getDefaultDisplay()

    if displays.count(display_default) == 0:
        display_default = display_reset

    mari.prefs.setItemList('/Color/Display Defaults/displayDisplayDefault', displays)
    mari.prefs.set('/Color/Display Defaults/displayDisplayDefault', display_default)
    mari.prefs.setDefault('/Color/Display Defaults/displayDisplayDefault', display_reset)

#---------------------------------------------------------------------------------------------

def _updateViewDefault():
    global config_default
    global display_default
    global view_default

    views = config_default.getViews(display_default)

    view_reset = VIEW_RESET
    if views.count(view_reset) == 0:
        view_reset = config_default.getDefaultView(display_default)

    if views.count(view_default) == 0:
        view_default = view_reset

    mari.prefs.setItemList('/Color/Display Defaults/displayViewDefault', views)
    mari.prefs.set('/Color/Display Defaults/displayViewDefault', view_default)
    mari.prefs.setDefault('/Color/Display Defaults/displayViewDefault', view_reset)

#---------------------------------------------------------------------------------------------

def _loadPreferences():
    settings = mari.Settings()
    settings.beginGroup('OpenColorIO')

    try:
        global enabled_default
        global profile_default
        global lut_file_list_default
        global lut_extrapolate_default
        global config_file_list_default
        global color_space_default
        global display_default
        global view_default
        global swizzle_default
        global gain_default
        global gamma_default
        global lut_size
        global fstop_center

        enabled_default         =         False if int(settings.value('enabledDefault', ENABLED_RESET)) == 0 else True
        profile_default         =                  str(settings.value('profileDefault', PROFILE_RESET))
        lut_path                =    buildLoadPath(str(settings.value('lutPathDefault', '' if LUT_FILE_LIST_RESET.isEmpty() else LUT_FILE_LIST_RESET.at(0))))
        lut_extrapolate_default =  False if int(settings.value('lutExtrapolateDefault', LUT_EXTRAPOLATE_RESET)) == 0 else True
        config_path             = buildLoadPath(str(settings.value('configPathDefault', '' if CONFIG_FILE_LIST_RESET.isEmpty() else CONFIG_FILE_LIST_RESET.at(0))))
        color_space_default     =               str(settings.value('colorSpaceDefault', COLOR_SPACE_RESET))
        display_default         =                  str(settings.value('displayDefault', DISPLAY_RESET))
        view_default            =                     str(settings.value('viewDefault', VIEW_RESET))
        swizzle_default         =                  str(settings.value('swizzleDefault', SWIZZLE_RESET))
        gain_default            =           max(min(float(settings.value('gainDefault', GAIN_RESET)), GAIN_MAX), GAIN_MIN)
        gamma_default           =          max(min(float(settings.value('gammaDefault', GAMMA_RESET)), GAMMA_MAX), GAMMA_MIN)
        lut_size                =                         str(settings.value('lutSize', LUT_SIZE_RESET))
        fstop_center            =           max(min(float(settings.value('fstopCenter', FSTOP_CENTER_RESET)), FSTOP_CENTER_MAX), FSTOP_CENTER_MIN)

        if os.path.isfile(lut_path):
            lut_file_list_default.clear()
            lut_file_list_default.append(lut_path)
            lut_file_list_default.setPickedFile(lut_path)

        if os.path.isfile(config_path):
            config_file_list_default.clear()
            config_file_list_default.append(config_path)
            config_file_list_default.setPickedFile(config_path)

    except ValueError, e:
        printMessage(MessageType.ERROR, 'Failed to load preferences \'%s\'' % e)

    settings.endGroup()

    _printPreferences(MessageType.DEBUG, 'Loaded Preferences:')

#---------------------------------------------------------------------------------------------

def _savePreferences():
    settings = mari.Settings()
    settings.beginGroup('OpenColorIO')

    global enabled_default
    global profile_default
    global lut_file_list_default
    global lut_extrapolate_default
    global config_file_list_default
    global color_space_default
    global display_default
    global view_default
    global swizzle_default
    global gain_default
    global gamma_default
    global lut_size
    global fstop_center

    settings.setValue(       'enabledDefault', 1 if enabled_default else 0)
    settings.setValue(       'profileDefault', profile_default)
    settings.setValue(       'lutPathDefault', '' if lut_file_list_default.isEmpty() else buildSavePath(lut_file_list_default.at(0)))
    settings.setValue('lutExtrapolateDefault', 1 if lut_extrapolate_default else 0)
    settings.setValue(    'configPathDefault', '' if config_file_list_default.isEmpty() else buildSavePath(config_file_list_default.at(0)))
    settings.setValue(    'colorSpaceDefault', color_space_default)
    settings.setValue(       'displayDefault', display_default)
    settings.setValue(          'viewDefault', view_default)
    settings.setValue(       'swizzleDefault', swizzle_default)
    settings.setValue(          'gainDefault', gain_default)
    settings.setValue(         'gammaDefault', gamma_default)
    settings.setValue(              'lutSize', lut_size)
    settings.setValue(          'fstopCenter', fstop_center)

    settings.endGroup()

    _printPreferences(MessageType.DEBUG, 'Saved Preferences:')

#---------------------------------------------------------------------------------------------

def _printPreferences(type, title):
    global enabled_default
    global profile_default
    global lut_file_list_default
    global lut_extrapolate_default
    global config_file_list_default
    global color_space_default
    global display_default
    global view_default
    global swizzle_default
    global gain_default
    global fstop_center
    global lut_size
    global gamma_default

    printMessage(type, '==============================================================')
    printMessage(type, title)
    printMessage(type, '==============================================================')
    printMessage(type, '     Enabled: %s'             % enabled_default)
    printMessage(type, '     Profile: %s'             % profile_default)
    printMessage(type, '    LUT Path: %s'             % '' if lut_file_list_default.isEmpty() else lut_file_list_default.at(0))
    printMessage(type, ' Extrapolate: %s'             % lut_extrapolate_default)
    printMessage(type, ' Config Path: %s'             % '' if config_file_list_default.isEmpty() else config_file_list_default.at(0))
    printMessage(type, ' Color Space: %s'             % color_space_default)
    printMessage(type, '     Display: %s'             % display_default)
    printMessage(type, '        View: %s'             % view_default)
    printMessage(type, '     Swizzle: %s'             % swizzle_default)
    printMessage(type, '      F-Stop: %f; Center: %f' % (convertGainToFStop(gain_default), fstop_center))
    printMessage(type, '        Gain: %f'             % gain_default)
    printMessage(type, '       Gamma: %f'             % gamma_default)
    printMessage(type, '    LUT Size: %s'             % lut_size)
    printMessage(type, '==============================================================')

##############################################################################################

def convertExposureToGain(exposure):
    return 2.0 ** exposure

#---------------------------------------------------------------------------------------------

def convertGainToExposure(gain):
    return math.log(gain, 2.0)

#---------------------------------------------------------------------------------------------

def convertExposureToFStop(exposure):
    global fstop_center
    exposure_center = math.log(fstop_center, SQRT_TWO)
    return math.pow(SQRT_TWO, exposure_center - exposure)

#---------------------------------------------------------------------------------------------

def convertGainToFStop(gain):
    exposure = convertGainToExposure(gain)
    return convertExposureToFStop(exposure)

#---------------------------------------------------------------------------------------------

def buildProcessorFilter(processor, filter, filter_cache_id, texture_cache_id, extrapolate = False, force_shader_build = False):
    # Create a name, using the filter's name, that can be used in uniquely naming parameters and functions.
    name = filter.name();
    name = name.lower()
    name = name.replace(' ', '_')

    sampler_name  = 'ocio_' + name + '_lut_$ID_'
    function_name = 'ocio_' + name + '_$ID_'

    global lut_size
    shader_desc = {    'language': PyOpenColorIO.Constants.GPU_LANGUAGE_GLSL_1_3,
                   'functionName': function_name,
                   'lut3DEdgeLen': LUT_SIZE_VALUES[lut_size]}

    cache_id = processor.getGpuShaderTextCacheID(shader_desc)
    if cache_id != filter_cache_id or force_shader_build:
        filter_cache_id = cache_id
        printMessage(MessageType.DEBUG, 'Creating new GLSL filter...')

        desc  = 'uniform sampler3D ' + sampler_name + ';\n'
        desc += processor.getGpuShaderText(shader_desc)
        body  = ''
        if extrapolate:
            # The following code was taken from Nuke's 'LUT3D::Extrapolate' functionality. It attempts to estimate what
            # the corresponding color value would be when the incoming color value is outside the normal range of [0-1],
            # such as the case with HDR images.
            rcp_lut_edge_length = 1.0 / float(LUT_SIZE_VALUES[lut_size])

            body += '{\n'
            body += '    if( 1.0 < Out.r || 1.0 < Out.g || 1.0 < Out.b )\n'
            body += '    {\n'
            body += '        vec4 closest;\n'
            body += '        closest.rgb = clamp(Out.rgb, vec3(0.0), vec3(1.0));\n'
            body += '        closest.a = Out.a;\n'
            body += '\n'
            body += '        vec3 offset = Out.rgb - closest.rgb;\n'
            body += '        float offset_distance = length(offset);\n'
            body += '        offset = normalize(offset);\n'
            body += '\n'
            body += '        vec4 nbr_position;\n'
            body += '        nbr_position.rgb = closest.rgb - %f * offset;\n' % rcp_lut_edge_length
            body += '        nbr_position.a = Out.a;\n'
            body += '\n'
            body += '        Out = ' + function_name + '(closest, ' + sampler_name + ');\n'
            body += '        Out.rgb += (Out.rgb - ' + function_name + '(nbr_position, ' + sampler_name + ').rgb) / %f * offset_distance;\n' % rcp_lut_edge_length
            body += '    }\n'
            body += '    else\n'
            body += '    {\n'
            body += '        Out = ' + function_name + '(Out, ' + sampler_name + ');\n'
            body += '    }\n'
            body += '}\n'
        else:
            body += '{ Out = ' + function_name + '(Out, ' + sampler_name + '); }\n'

        filter.setDefinitionsSnippet(desc)
        filter.setBodySnippet(body)
    else:
        printMessage(MessageType.DEBUG, 'No GLSL filter update required')

    cache_id = processor.getGpuLut3DCacheID(shader_desc)
    if cache_id != texture_cache_id:
        lut = processor.getGpuLut3D(shader_desc)

        printMessage(MessageType.DEBUG, 'Updating LUT...')

        if texture_cache_id is None:
            filter.setTexture3D(sampler_name,
                                LUT_SIZE_VALUES[lut_size],
                                LUT_SIZE_VALUES[lut_size],
                                LUT_SIZE_VALUES[lut_size],
                                filter.FORMAT_RGB,
                                lut)
        else:
            filter.updateTexture3D(sampler_name, lut)

        texture_cache_id = cache_id
    else:
        printMessage(MessageType.DEBUG, 'No LUT update required')

    return (filter_cache_id, texture_cache_id, sampler_name)

#---------------------------------------------------------------------------------------------

def buildLUTFilter(config, path, filter, filter_cache_id, texture_cache_id, extrapolate, force_shader_build = False):
    file_transform = PyOpenColorIO.FileTransform()
    file_transform.setSrc(path)
    file_transform.setInterpolation('linear')

    processor = config.getProcessor(file_transform)

    return buildProcessorFilter(processor, filter, filter_cache_id, texture_cache_id, extrapolate, force_shader_build)

#---------------------------------------------------------------------------------------------

def loadConfig(path, display_message_box = True):
    try:
        config = PyOpenColorIO.Config.CreateFromFile(path)
        return config

    except Exception, e:
        message = 'Failed to load configuration file \'%s\' due to \'%s\'' % (path, e)
        printMessage(MessageType.ERROR, '%s' % message)
        if display_message_box and not mari.app.inTerminalMode():
            mari.utils.misc.message(message, 'Color Space', 1024, 2)

        return None

#---------------------------------------------------------------------------------------------

# This converts a path into a form which can be shared among different platforms and installations.
def buildSavePath(path):
    result = path.replace(mari.resources.path(mari.resources.COLOR), '$MARI_COLOR_PATH', 1)
    return result

#---------------------------------------------------------------------------------------------

# This converts a path saved out to disk back into a form which can used by the application.
def buildLoadPath(path):
    result = path.replace('$MARI_COLOR_PATH', mari.resources.path(mari.resources.COLOR), 1)
    return result

##############################################################################################

if mari.app.isRunning():
    if PyOpenColorIO is not None:
        # Attempt to load the default configuration file... without it we can't do nothing!
        config_file_lists = [config_file_list_default, CONFIG_FILE_LIST_RESET]
        for config_file_list in config_file_lists:
            if not config_file_list.isEmpty():
                config_default = loadConfig(config_file_list.at(0), False)
                if config_default is not None:
                    config_file_list_default = mari.FileList(config_file_list)
                    break

        if config_default is not None:
            _loadPreferences()
            _registerPreferences()
            _savePreferences()

        else:
            message = 'Failed to find a working configuration file. OpenColorIO will be disabled!'
            printMessage(MessageType.ERROR, message)
            if not mari.app.inTerminalMode():
                mari.utils.misc.message(message, 'OpenColorIO', 1024, 3)