1Internal Architecture Overview
2==============================
3
4External API
5************
6
7Configs
8+++++++
9
10At the highest level, we have OCIO::Configs. This represents the entirety of the
11current color "universe".  Configs are serialized as .ocio files, read at runtime,
12and are often used in a 'read-only' context.
13
14Config are loaded at runtime to allow for customized color handling in a show-
15dependent manner.
16
17Example Configs:
18
19* ACES (Academy's standard color workflow)
20* spi-vfx (Used on some Imageworks VFX shows such as spiderman, etc).
21* and others
22
23
24ColorSpaces
25+++++++++++
26
27The meat of an OCIO::Config is a list of named ColorSpaces. ColorSpace often
28correspond to input image states, output image states, or image states used for
29internal processing.
30
31Example ColorSpaces (from ACES configuration):
32
33* aces (HDR, scene-linear)
34* adx10 (log-like density encoding space)
35* slogf35 (sony F35 slog camera encoding)
36* rrt_srgb (baked in display transform, suitable for srgb display)
37* rrt_p3dci (baked in display transform, suitable for dcip3 display)
38
39
40Transforms
41++++++++++
42
43ColorSpaces contain an ordered list of transforms, which define the conversion
44to and from the Config's "reference" space.
45
46Transforms are the atomic units available to the designer in order to specify a
47color conversion.
48
49Examples of OCIO::Transforms are:
50
51* File-based transforms (1d lut, 3d lut, mtx... anything, really.)
52* Math functions (gamma, log, mtx)
53* The 'meta' GroupTransform, which contains itself an ordered lists of transforms
54* The 'meta' LookTransform, which contains an ordered lists of transforms
55
56
57For example, the adx10 ColorSpace (in one particular ACES configuration)
58-Transform FROM adx, to our reference ColorSpace:
59
60#. Apply FileTransform adx_adx10_to_cdd.spimtx
61#. Apply FileTransform adx_cdd_to_cid.spimtx
62#. Apply FileTransform adx_cid_to_rle.spi1d
63#. Apply LogTransform base 10 (inverse)
64#. Apply FileTransform adx_exp_to_aces.spimtx
65
66
67If we have an image in the reference ColorSpace (unnamed), we can convert TO
68adx by applying each in the inverse direction:
69
70#. Apply FileTransform adx_exp_to_aces.spimtx (inverse)
71#. Apply LogTransform base 10 (forward)
72#. Apply FileTransform adx_cid_to_rle.spi1d (inverse)
73#. Apply FileTransform adx_cdd_to_cid.spimtx (inverse)
74#. Apply FileTransform adx_adx10_to_cdd.spimtx (inverse)
75
76
77Note that this isn't possible in all cases (what if a lut or matrix is not
78invertible?), but conceptually it's a simple way to think about the design.
79
80
81Summary
82+++++++
83
84Configs and ColorSpaces are just a bookkeeping device used to get and ordered
85lists of Transforms corresponding to image color transformation.
86
87Transforms are visible to the person AUTHORING the OCIO config, but are
88NOT visible to the client applications. Client apps need only concern themselves
89with Configs and Processors.
90
91
92OCIO::Processors
93++++++++++++++++
94
95A processor corresponds to a 'baked' color transformation. You specify two arguments
96when querying a processor: the :ref:`colorspace_section` you are coming from,
97and the :ref:`colorspace_section` you are going to.
98
99Once you have the processor, you can apply the color transformation using the
100"apply" function.  For the CPU veseion, first wrap your image in an
101ImageDesc class, and then call apply to process in place.
102
103Example:
104
105.. code-block:: cpp
106
107   #include <OpenColorIO/OpenColorIO.h>
108   namespace OCIO = OCIO_NAMESPACE;
109
110   try
111   {
112      // Get the global OpenColorIO config
113      // This will auto-initialize (using $OCIO) on first use
114      OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
115
116      // Get the processor corresponding to this transform.
117      // These strings, in this example, are specific to the above
118      // example. ColorSpace names should NEVER be hard-coded into client
119      // software, but should be dynamically queried at runtime from the library
120      OCIO::ConstProcessorRcPtr processor = config->getProcessor("adx10", "aces");
121
122      // Wrap the image in a light-weight ImageDescription
123      OCIO::PackedImageDesc img(imageData, w, h, 4);
124
125      // Apply the color transformation (in place)
126      processor->apply(img);
127   }
128   catch(OCIO::Exception & exception)
129   {
130      std::cerr << "OpenColorIO Error: " << exception.what() << std::endl;
131   }
132
133
134The GPU code path is similar.  You get the processor from the config, and then
135query the shaderText and the lut3d.  The client loads these to the GPU themselves,
136and then makes the appropriate calls to the newly defined function.
137
138See `src/apps/ociodisplay` for an example.
139
140
141Internal API
142************
143
144
145The Op Abstraction
146++++++++++++++++++
147
148It is a useful abstraction, both for code-reuse and optimization, to not relying
149on the transforms to do pixel processing themselves.
150
151Consider that the FileTransform represents a wide-range of image processing
152operations (basically all of em), many of which are really complex.  For example,
153the houdini lut format in a single file may contain a log convert, a 1d lut, and
154then a 3d lut; all of which need to be applied in a row!  If we don't want the
155FileTransform to know how to process all possible pixel operations, it's much
156simpler to make light-weight processing operations, which the transforms can
157create to do the dirty work as needed.
158
159All image processing operations (ops) are a class that present the same
160interface, and it's rather simple:
161
162.. code-block:: cpp
163
164   virtual void apply(float* rgbaBuffer, long numPixels)
165
166Basically, given a packed float array with the specified number of pixels, process em.
167
168Examples of ops include Lut1DOp, Lut3DOp, MtxOffsetOp, LogOp, etc.
169
170Thus, the job of a transform becomes much simpler and they're only responsible
171for converting themselves to a list of ops.  A simple FileTransform that only has
172a single 1D lut internally may just generate a single Lut1DOp, but a
173FileTransform that references a more complex format (such as the houdini lut case
174referenced above) may generate a few ops:
175
176.. code-block:: cpp
177
178   void FileFormatHDL::BuildFileOps(OpRcPtrVec & ops,
179                            const Config& /*config*/,
180                            const ConstContextRcPtr & /*context*/,
181                            CachedFileRcPtr untypedCachedFile,
182                            const FileTransform& fileTransform,
183                            TransformDirection dir) const {
184
185   // Code omitted which loads the lut file into the file cache...
186
187   CreateLut1DOp(ops, cachedFile->lut1D,
188                      fileTransform.getInterpolation(), dir);
189   CreateLut3DOp(ops, cachedFile->lut3D,
190                      fileTransform.getInterpolation(), dir);
191
192See (``src/core/*Ops.h``) for the available ops.
193
194Note that while compositors often have complex, branching trees of image processing
195operations, we just have a linear list of ops, lending itself very well to
196optimization.
197
198Before the ops are run, they are optimized. (Collapsed with appropriate neighbors, etc).
199
200
201An Example
202++++++++++
203
204Let us consider the internal steps when getProcessor() is called to convert from ColorSpace
205'adx10' to ColorSpace 'aces':
206
207* The first step is to turn this ColorSpace conversion into an ordered list of transforms.
208We do this by creating a single of the conversions from 'adx10' to reference, and then
209adding the transforms required to go from reference to 'aces'.
210* The Transform list is then converted into a list of ops.  It is during this stage luts,
211are loaded, etc.
212
213
214CPU CODE PATH
215+++++++++++++
216
217The master list of ops is then optimized, and stored internally in the processor.
218
219.. code-block:: cpp
220
221   FinalizeOpVec(m_cpuOps);
222
223
224During Processor::apply(...), a subunit of pixels in the image are formatted into a sequential rgba block.  (Block size is optimized for computational (SSE) simplicity and performance, and is typically similar in size to an image scanline)
225
226.. code-block:: cpp
227
228   float * rgbaBuffer = 0;
229   long numPixels = 0;
230   while(true) {
231      scanlineHelper.prepRGBAScanline(&rgbaBuffer, &numPixels);
232      ...
233
234
235Then for each op, op->apply is called in-place.
236
237.. code-block:: cpp
238
239   for(OpRcPtrVec::size_type i=0, size = m_cpuOps.size(); i<size; ++i)
240   {
241      m_cpuOps[i]->apply(rgbaBuffer, numPixels);
242   }
243
244
245After all ops have been applied, the results are copied back to the source
246
247.. code-block:: cpp
248
249   scanlineHelper.finishRGBAScanline();
250
251
252GPU CODE PATH
253+++++++++++++
254
255#. The master list of ops is partitioned into 3 ordered lists:
256
257- As many ops as possible from the BEGINNING of the op-list that can be done
258  analytically in shader text. (called gpu-preops)
259- As many ops as possible from the END of the op-list that can be done
260  analytically in shader text. (called gpu-postops)
261- The left-over ops in the middle that cannot support shader text, and thus
262  will be baked into a 3dlut. (called gpu-lattice)
263
264
265#. Between the first an the second lists (gpu-preops, and gpu-latticeops), we
266analyze the op-stream metadata and determine the appropriate allocation to use.
267(to minimize clamping, quantization, etc). This is accounted for here by
268interserting a forward allocation to the end of the pre-ops, and the inverse
269allocation to the start of the lattice ops.
270
271See https://github.com/imageworks/OpenColorIO/blob/master/src/core/NoOps.cpp#L183
272
273#. The 3 lists of ops are then optimized individually, and stored on the processor.
274The Lut3d is computed by applying the gpu-lattice ops, on the CPU, to a lut3d
275image.
276
277The shader text is computed by calculating the shader for the gpu-preops, adding
278a sampling function of the 3d lut, and then calculating the shader for the gpu
279post ops.
280
281