-
Notifications
You must be signed in to change notification settings - Fork 0
/
NewStrategies.tex
2332 lines (2002 loc) · 103 KB
/
NewStrategies.tex
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
% -*- latex -*-
\chapter{Implementing New Strategies}
\label{sec:New_Strategies}
The \IceT API was written while its strategies were being developed. As
such, the design yields for the relatively simplistic addition of both new
multi-tile strategies and new single-image strategies. This chapter will
provide the basic overview of how to add a new strategy for those
interested in adding new compositing algorithms to \IceT. It is probably
easiest to start by modifying your \IceT source to insert your own strategy
in the \textC{src/strategies} directory of the \IceT source distribution.
A strategy in \IceT is created by simply defining a function that performs
the operation. A multi-tile strategy (one selected with
\CFunc{icetStrategy}) should take no arguments and return an
\CType{IceTImage}. Thus, a new multi-tile strategy function would look
something like this. (The following sections will provide details on
performing the individual tasks of the implementation.)
\begin{code}
IceTImage icetCustomMultiTileCompose(void)
{
/* Render images. */
/* Transfer data. */
/* Composite pixels. */
/* Store results in image. */
return image;
}
\end{code}
To expose the strategy from the \IceT interface, add an identifier to
\textC{IceT.h} starting with \textC{ICET\_STRATEGY\_} to the list of
existing strategy identifiers. Then modify the functions in
\textC{src/strategies/select.c} to expose this new identifier to the rest
of the \IceT library. In particular, add your new identifier to the switch
statements in the following functions.
\begin{description}
\item[\CFuncExternal{icetStrategyValid}] Simply add your identifier to the
list so that \IceT can verify that your strategy is defined.
\item[\CFuncExternal{icetStrategyNameFromEnum}] Add a short human-readable
name for your strategy. This is the string returned from
\CFunc{icetGetStrategyName}.
\item[\CFuncExternal{icetStrategySupportsOrder}] Return \CEnum{ICET\_TRUE}
if your strategy can properly composite based on the ordering given in
\CEnum{ICET\_COMPOSITE\_ORDER}. Return \CEnum{ICET\_FALSE} otherwise.
This value gets stored in the \CEnum{ICET\_STRATEGY\_SUPPORTS\_ORDERING}.
\item[\CFuncExternal{icetInvokeStrategy}] Call the function that invokes
your strategy's image compositing (\textC{icetCustomMultiTileCompose} in
the example above).
\end{description}
The process for creating a single-image strategy (one selected with
\CFunc{icetSingleImageStrategy} is similar. The first step is define a
function that performs the compositing. However, the single-image
composite function takes arguments that define the image to composite and
the group of processes contributing. A new single-image strategy function
would look something like this.
\begin{code}
void icetCustomSingleImageCompose(const IceTInt *compose_group,
IceTInt group_size,
IceTInt image_dest,
IceTSparseImage input_image,
IceTSparseImage *result_image,
IceTSizeType *piece_offset)
{
/* Transfer data. */
/* Composite pixels. */
/* Point result_image to image object with results. */
/* Store offset of local piece in piece_offset. */
}
\end{code}
The first argument, \CArg{compose\_group}, is an array of process ranks.
The pixels are to be composited in the order specified in this array. The
second argument, \CArg{group\_size}, specifies how many processes are
contributing to the image and also specifies the length of
\CArg{compose\_group}.
The third argument, \CArg{image\_dest}, specifies the process in which the
final composed image should be placed. It is an index into
\CArg{compose\_group}, not the actual rank of the process.
\CArg{image\_dest} is really just a hint and can be ignored. The single
image composite does not need to collect the composited pixels to a single
process. It can (and usually does) return with pieces of the composited
image distributed amongst nodes in the group. Any distribution is
supported so long as the pieces are continuous, non-overlapping, and
collectively define all the pixels in the image.
The fourth argument, \CArg{input\_image}, contains the input image to be
composited with the corresponding images in the other processes. The
resulting image is returned via the final two arguments.
\CArg{result\_image} gets the sparse image object containing the composited
image piece of the local process. It can have zero pixels if the local
process has no image data. \CArg{piece\_offset} gets the offset, in pixels,
of the local image piece in the entire image.
To expose the single-image strategy from the \IceT interface, add an
identifier to \textC{IceT.h} starting with
\textC{ICET\_SINGLE\_IMAGE\_STRATEGY\_} to the list of existing
single-image strategy identifiers. Then modify the functions in
\textC{src/strategies/select.c} to expose this new identifier to the rest
of the \IceT library. In particular, add your new identifier to the switch
statements in the following functions.
\begin{description}
\item[\CFuncExternal{icetSingleImageStrategyValid}] Simply add your
identifier to the list so that \IceT can verify that your strategy is
defined.
\item[\CFuncExternal{icetSingleImageStrategyNameFromEnum}] Add a short
human-readable name for your strategy. This is the string returned from
\CFunc{icetGetSingleImageStrategyName}.
\item[\CFuncExternal{icetInvokeSingleImageStrategy}] Call the function that
invokes your strategy's image compositing
(\textC{icetCustomSingleImageCompose} in the example above).
\end{description}
\section{Internal State Variables for Compositing}
The strategy compose functions are expected to get many of its parameters
and other relevant information from the \IceT state. Many of the relevant
state variables are described in the documentation for the \CFunc{icetGet}
functions (as well as elsewhere throughout this document). There are also
several ``hidden'' state variables for internal use. The ones specifically
useful for within a composite function are listed here (along with the
variable type, number of entries, and a description). Note that these
state variables generally should be read from, not written to.
\begin{Description}[xxxxxxxx]
\item[\CEnum{ICET\_ALL\_CONTAINED\_TILES\_MASKS}] (boolean,
\CEnum{ICET\_NUM\_TILES} $\times$ \CEnum{ICET\_NUM\_PROCESSES}) Contains
an appended list of \CEnum{ICET\_CONTAINED\_TILES\_MASK} variables for
all processes. Given process $p$ and tile $t$, the entry at
$(\CEnum{ICET\_NUM\_TILES} \times p)+t$ contains the flag describing
whether process $p$ renders a non-blank image for tile $t$. This
variable is the same on all processes. This state variable is \emph{not}
set when using the \index{strategy!sequential}sequential strategy.
\item[\CEnum{ICET\_CONTAINED\_TILES\_LIST}] (integer,
\CEnum{ICET\_NUM\_CONTAINED\_TILES}) All the tiles into which the local
geometry projects. In other words, this is the list of tiles which will
not be empty after local rendering. The local processor should generate
images for these tiles and participate in the composition of them.
\item[\CEnum{ICET\_CONTAINED\_TILES\_MASK}] (boolean,
\CEnum{ICET\_NUM\_TILES}) This is a list of boolean flags, one per tile.
The flag is true if the local geometry projects onto the tile (that is,
the local render will not be empty for that tile) and false otherwise.
This gives the same information as \CEnum{ICET\_CONTAINED\_TILES\_LIST},
but in a different way that can be more convenient in some circumstances.
\item[\CEnum{ICET\_CONTAINED\_VIEWPORT}] (integer, 4) Describes the region
of the viewport that the geometry being rendered locally projects onto.
The bounds of the data (given by \CFunc{icetBoundingBox} or
\CFunc{icetBoundingVertices}) projected onto the tiled display determines
the region of the tiled display the data covers. The values in the
four-tuple correspond to x, y, width, and height, respectively, of the
projection in global pixel coordinates. This variable in conjunction
with the \CEnum{ICET\_NEAR\_DEPTH} and \CEnum{ICET\_FAR\_DEPTH} give the
full 3D projection of the local data in window space.
\item[\CEnum{ICET\_FAR\_DEPTH}] (double, 1) The maximum depth value of the
local geometry after projection. See \CEnum{ICET\_CONTAINED\_VIEWPORT}
for more details.
\item[\CEnum{ICET\_IS\_DRAWING\_FRAME}] (boolean, 1) Set to true while in a
call to \CFunc{icetDrawFrame}, \CFunc{icetCompositeImage}, or
\CFunc{icetGLDrawFrame} and set to false otherwise. This should always
be set to true while the compose function is being executed.
\item[\CEnum{ICET\_MODELVIEW\_MATRIX}] (double, 16) The current modelview
matrix as passed to \CFunc{icetDrawFrame} or \CFunc{icetCompositeImage}
or read from \OpenGL at the invocation of \CFunc{icetGLDrawFrame}.
\item[\CEnum{ICET\_NEAR\_DEPTH}] (double, 1) The minimum depth value of the
local geometry after projection. See \CEnum{ICET\_CONTAINED\_VIEWPORT}
for more details.
\item[\CEnum{ICET\_NEED\_BACKGROUND\_CORRECTION}] (boolean, 1) If the
resulting composited image needs to have its background corrected. That
is, the final image should be blended with the color specified in
\CEnum{ICET\_TRUE\_BACKGROUND\_COLOR} or
\CEnum{ICET\_TRUE\_BACKGROUND\_COLOR}.
\item[\CEnum{ICET\_NUM\_CONTAINED\_TILES}] (integer, 1) The number of tiles
into which the local geometry projects. This is the length of the
\CEnum{ICET\_CONTAINED\_TILES\_LIST} variable.
\item[\CEnum{ICET\_PROJECTION\_MATRIX}] (double, 16) The current projection
matrix as passed to \CFunc{icetDrawFrame} or \CFunc{icetCompositeImage}
or read from \OpenGL at the invocation of \CFunc{icetGLDrawFrame}.
\item[\CEnum{ICET\_TILE\_CONTRIB\_COUNTS}] (integer,
\CEnum{ICET\_NUM\_TILES}) For each tile, provides the number of processes
that will produce a non-empty image for that tile. This state variable
is \emph{not} set when using the \index{strategy!sequential}sequential
strategy.
\item[\CEnum{ICET\_TOTAL\_IMAGE\_COUNT}] (integer, 1) The total number of
non-empty images produced by all processes for all tiles. This variable
is the sum of all entries in \CEnum{ICET\_TILE\_CONTRIB\_COUNTS}. This
state variable is \emph{not} set when using the
\index{strategy!sequential}sequential strategy.
\item[\CEnum{ICET\_TRUE\_BACKGROUND\_COLOR}] (float, 4) An RGBA color
identifying the ``true'' or final background color. If
\CEnum{ICET\_NEED\_BACKGROUND\_CORRECTION} is true, then this color
should be blended as the background to all pixels in the final image.
\item[\CEnum{ICET\_TRUE\_BACKGROUND\_COLOR\_WORD}] (integer, 1) Same as
\CEnum{ICET\_TRUE\_BACKGROUND\_COLOR} but stored as 8-bit values packed
in an integer.
\end{Description}
\label{manpage:icetUnsafeStateGet}
In addition to several internal state variables, \IceT also has several
internal functions for accessing them. The most important set for
implementing a strategy is the \CFunc{icetUnsafeStateGet} suite of
functions, which are defined in the
\index{IceTDevState.h}\textC{IceTDevState.h} header file.
\begin{Table}{4}
\textC{const IceTDouble *}&\icetUnsafeStateGetDouble\textC{(}&\textC{IceTEnum}&\CArg{pname}\quad\textC{);} \\
\textC{const IceTFloat *}&\icetUnsafeStateGetFloat\textC{(}&\textC{IceTEnum}&\CArg{pname}\quad\textC{);} \\
\textC{const IceTInt *}&\icetUnsafeStateGetInteger\textC{(}&\textC{IceTEnum}&\CArg{pname}\quad\textC{);} \\
\textC{const IceTBoolean *}&\icetUnsafeStateGetBoolean\textC{(}&\textC{IceTEnum}&\CArg{pname}\quad\textC{);} \\
\textC{const IceTVoid **}&\icetUnsafeStateGetPointer\textC{(}&\textC{IceTEnum}&\CArg{pname}\quad\textC{);}
\end{Table}
The implementation for the \CFunc{icetGet} functions is to copy the data
into a memory buffer you provide, performing type conversion as necessary.
The \CFunc{icetUnsafeStateGet} functions simply return the internal pointer
where the data is stored. This can be faster and more convenient (since
you do not have to allocate your own memory), but is unsafe in two ways.
First, if the state variable is changed, the pointer you receive can become
invalid. Second, no type conversion is performed. You have to make sure
that you request a pointer of the correct type (or you will get an error).
Since the state setting functions are hidden from the end user API, it is
possible to manage these erroneous conditions. These functions return
\textC{const} pointers to discourage you from changing state values by
directly manipulating the data in the pointers.
\section{Memory Management}
\label{sec:New_Strategies:Memory_Management}
Compositing algorithms by their nature require buffers of memory of
non-trivial size to hold images, among other data, that are not needed in
between calls to the compositing. One approach is to simply use the
standard C \CFuncExternal{malloc} and \CFuncExternal{free} functions.
However, some implementations of
\CFuncExternal{malloc}/\CFuncExternal{free} are not always efficient, and
even the best implementations can have a tendency to fragment memory over
time as large buffers are allocated and released.
During typical \IceT operation, a strategy (whether it be a multi-tile
strategy or a single-image strategy) is invoked multiple times. Each
invocation will require multiple buffers to manipulate images and other
data. One way to do this is to allocate these buffers as needed and
free them by the end of the invocation. However, this can lead to
the inefficiencies and memory fragmentation previously mentioned. It is
also problematic when returning an image buffer as the responsibility for
deallocating the buffer becomes undefined.
A better approach is to allocate the buffers as needed and then keep the
buffers around for the next invocation of the strategy. This approach
requires a certain amount of overhead to check when buffers need to be
allocated or resized and when they can be freed. \IceT uses its own state
mechanism to assist in managing memory buffers. You do this by creating a
\index{state~buffer}\keyterm{state buffer}, a buffer attached to a state
variable. This is done with the \CFunc{icetGetStateBuffer} function, which
is defined in \index{IceTDevState.h}\textC{IceTDevState.h}.
\label{manpage:icetGetStateBuffer}
\begin{Table}{3}
\textC{IceTVoid *}\CFunc{icetGetStateBuffer}\textC{(}&\textC{IceTEnum}&\CArg{pname}\textC{,} \\
&\textC{IceTSizeType}&\CArg{num\_bytes}\quad\textC{);}
\end{Table}
The \CFunc{icetGetStateBuffer} takes a state variable and a buffer size in
bytes. It then checks to see if a buffer of the appropriate size has
already been allocated to that state variable. If so, it is returned. If
not, a new buffer is allocated and returned. There are also similar
functions called \CFunc{icetGetStateBufferImage} and
\CFunc{icetGetStateBufferSparseImage}, described in the following section,
that allocate image buffers.
Because each buffer is assigned to a state variable, it is important to
assign the buffer to a state variable that is both valid and unused by
other \IceT components. To this end, there are several state variables
reserved for multi-tile strategies or single-image strategies. The state
variables for multi-tile strategies are named
\CEnum{ICET\_STRATEGY\_BUFFER}\textC{\_$i$} numbered from 0 to 15. That
is, \CEnum{ICET\_STRATEGY\_BUFFER\_0}, \CEnum{ICET\_STRATEGY\_BUFFER\_1},
and so on up to \CEnum{ICET\_STRATEGY\_BUFFER\_15}.
By convention, your multi-tile strategy implementation should start by
creating \textC{\#define} enumerations that alias these variables to
logical names for the buffers. This will help prevent confusion or
accidental sharing of buffers. Also by convention, try to make the largest
buffers ``first'' (that is, \CEnum{ICET\_STRATEGY\_BUFFER\_0} has the
largest buffer, \CEnum{ICET\_STRATEGY\_BUFFER\_1} has the next largest, and
so on) so that if the strategy is changed, the large buffers will most
likely be shared.
As an example, consider the following code taken from the virtual trees
strategy implementation that aliases the buffer state variables it uses.
\begin{code}
#define VTREE_IMAGE_BUFFER ICET_STRATEGY_BUFFER_0
#define VTREE_IN_SPARSE_IMAGE_BUFFER ICET_STRATEGY_BUFFER_1
#define VTREE_OUT_SPARSE_IMAGE_BUFFER ICET_STRATEGY_BUFFER_2
#define VTREE_INFO_BUFFER ICET_STRATEGY_BUFFER_3
#define VTREE_ALL_CONTAINED_TMASKS_BUFFER ICET_STRATEGY_BUFFER_4
\end{code}
And here is an example of these buffers being allocated.
\begin{code}
sparseImageSize = icetSparseImageBufferSize(max_width, max_height);
image = icetGetStateBufferImage(VTREE_IMAGE_BUFFER,
max_width, max_height);
inSparseImageBuffer = icetGetStateBuffer(VTREE_IN_SPARSE_IMAGE_BUFFER,
sparseImageSize);
outSparseImage = icetGetStateBufferSparseImage(
VTREE_OUT_SPARSE_IMAGE_BUFFER,
max_width, max_height);
info = icetGetStateBuffer(VTREE_INFO_BUFFER,
sizeof(struct node_info)*num_proc);
all_contained_tmasks = icetGetStateBuffer(VTREE_ALL_CONTAINED_TMASKS_BUFFER,
sizeof(IceTBoolean)*num_proc*num_tiles);
\end{code}
Once allocated, these buffers can be used and never need to be freed.
\IceT will handle the memory management. However, do not expect any of
these buffers to contain the same data or even exist on the next invocation
of the strategy. Each invocation of the strategy should call
\CFunc{icetGetStateBuffer}, \CFunc{icetGetStateBufferImage}, and
\CFunc{icetGetStateBufferSparseImage} to ensure that it has a valid buffer.
There is a separate set of state variables reserved for buffers used in
single-image strategies. These are named
\CEnum{ICET\_SI\_STRATEGY\_BUFFER}\textC{\_$i$} numbered from 0 to 15.
That is, \CEnum{ICET\_SI\_STRATEGY\_BUFFER\_0},
\CEnum{ICET\_SI\_STRATEGY\_BUFFER\_1}, and so on up to
\CEnum{ICET\_SI\_STRATEGY\_BUFFER\_15}. It is important \emph{not} to use
the multi-tile strategy buffer variables in a single-image strategy because
the multi-tile strategy will call the single-image strategy while it is
still operating and the single-image strategy can invalidate the buffers of
the multi-tile strategy.
\section{Image Manipulation Functions}
\IceT defines two image types: \CType{IceTImage} and
\CType{IceTSparseImage}. Both image types can hold color data or depth
data or both. The \CType{IceTImage} type stores pixels as raw data, simple
2D arrays holding color or pixel data in horizontal-major order. The
\CType{IceTSparseImage} stores images using
\index{active-pixel~encoding}active-pixel encoding, the run length encoding
described in the Active-Pixel Encoding section of
Chapter~\ref{sec:Customizing_Compositing:Active_Pixel_Encoding}.
Both the \CType{IceTImage} type and the \CType{IceTSparseImage} type are
opaque to compositing algorithms. \IceT provides functions for creating
and manipulating images. Some of these functions are defined in
\index{IceT.h}\textC{IceT.h} and exposed to user code. These exposed
functions are documented in the Image Objects section of
Chapter~\ref{sec:Basic_Usage:Image_Objects} starting on
page~\pageref{sec:Basic_Usage:Image_Objects}. Other functions are
protected from the user level code and reserved for use by the compositing
algorithms and other parts of \IceT. These functions are defined in
\index{IceTDevImage.h}\textC{IceTDevImage.h} and are documented here. When
creating a compositing strategy, be sure to include both of these header
files.
\subsection{Creating Images}
\label{manpage:icetGetStateBufferImage}
\label{manpage:icetGetStateBufferSparseImage}
The easiest and safest way to create an image is to use the
\CFunc{icetGetStateBufferImage} function (or
\CFunc{icetGetStateBufferSparseImage} for sparse images).
\begin{Table}{3}
\CType{IceTImage}\textC{ }\CFunc{icetGetStateBufferImage}\textC{(}&\textC{IceTEnum}&\CArg{pname}\textC{,} \\
&\textC{IceTSizeType}&\CArg{width}\textC{,} \\
&\textC{IceTSizeType}&\CArg{height}\quad\textC{);}
\end{Table}
\begin{Table}{3}
\multicolumn{3}{l}{
\CType{IceTSparseImage}\textC{ }\CFunc{icetGetStateBufferSparseImage}\textC{(}
} \\
\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad
&\textC{IceTEnum}&\CArg{pname}\textC{,} \\
&\textC{IceTSizeType}&\CArg{width}\textC{,} \\
&\textC{IceTSizeType}&\CArg{height}\quad\textC{);}
\end{Table}
Each of these functions allocates a \index{state~buffer}state buffer
(described in the previous section on Memory Management) for an image of
size \CArg{width} by \CArg{height} on the given state variable
(\CArg{pname}), and returns an initialized image object. The image object
is allocated and initialized for the color and depth formats specified by
the \CEnum{ICET\_COLOR\_FORMAT} and \CEnum{ICET\_DEPTH\_FORMAT} state
variables. Here is some code taken from the virtual trees strategy
implementation that demonstrates the use of these functions.
\begin{code}
image = icetGetStateBufferImage(VTREE_IMAGE_BUFFER,
max_width, max_height);
outSparseImage = icetGetStateBufferSparseImage(
VTREE_OUT_SPARSE_IMAGE_BUFFER,
max_width, max_height);
\end{code}
\label{manpage:icetRetrieveStateImage}
Most of the time after creating an image from a buffer with
\CFunc{icetGetStateBufferImage}, you just keep a reference to the
\CType{IceTImage} object returned and pass that object around as
necessary. Occasionally, the image will need to be used by two different
functions that do not directly communicate. Instead, they can share an
image through a state variable. This can be done by first creating the
image with \CFunc{icetGetStateBufferImage} and then later retrieving the
image with \CFunc{icetRetrieveStateImage} using the same state
\CArg{pname}.
\begin{Table}{3}
\CType{IceTImage}\textC{ }\CFunc{icetRetrieveStateImage}\textC{(}&\textC{IceTEnum}&\CArg{pname}\quad\textC{);}
\end{Table}
\label{manpage:icetImageSetDimensions}
\label{manpage:icetSparseImageSetDimensions}
After an image is allocated, it is possible to resize the image, but only
to dimensions that are less than or equal to those for which the image was
created. This is done with the \CFunc{icetImageSetDimensions} or
\CFunc{icetSparseImageSetDimensions} function.
\begin{Table}{3}
\textC{void }\CFunc{icetImageSetDimensions}\textC{(}&\CType{IceTImage}&\CArg{image}\textC{,} \\
&\textC{IceTSizeType}&\CArg{width}\textC{,} \\
&\textC{IceTSizeType}&\CArg{height}\quad\textC{);} \\
\end{Table}
\begin{Table}{3}
\textC{void }\CFunc{icetSparseImageSetDimensions}\textC{(}&\CType{IceTSparseImage}&\CArg{image}\textC{,} \\
&\textC{IceTSizeType}&\CArg{width}\textC{,} \\
&\textC{IceTSizeType}&\CArg{height}\quad\textC{);} \\
\end{Table}
These functions do not resize the internal buffer of the image. Rather,
they set the internal width and height parameters of the image and reuse
the original (and potentially larger than necessary) buffer. This is why
they cannot be used to size the image larger than the original buffer
allocation. It is for this reason that it is typical for a multi-tile
strategy to create images of size \CEnum{ICET\_TILE\_MAX\_WIDTH} and
\CEnum{ICET\_TILE\_MAX\_HEIGHT}. A well designed compositing algorithm
should never need more space than that. Likewise, it is typical for a
single-image strategy to create images of the same size as the input
image.
\label{manpage:icetImageBufferSize}
\label{manpage:icetSparseImageBufferSize}
It is sometimes necessary to know the size of buffer required to store
image data. This most often occurs when allocating buffers to receive
images (as described in detail in the following section on Transferring
Images starting on
page~\pageref{sec:New_Strategies:Communications:Transferring_Images}).
Getting the necessary buffer size is done with the
\CFunc{icetImageBufferSize} and \CFunc{icetSparseImageBufferSize}
functions.
\begin{Table}{3}
\textC{IceTSizeType }\CFunc{icetImageBufferSize}\textC{(}&\textC{IceTSizeType}&\CArg{width}\textC{,} \\
&\textC{IceTSizeType}&\CArg{height}\quad\textC{);}
\end{Table}
\begin{Table}{3}
\textC{IceTSizeType }\CFunc{icetSparseImageBufferSize}\textC{(}&\textC{IceTSizeType}&\CArg{width}\textC{,} \\
&\textC{IceTSizeType}&\CArg{height}\quad\textC{);}
\end{Table}
Each of these functions returns the \emph{maximum} number of bytes required
to store the image of the given dimensions and the formats specified by the
\CEnum{ICET\_COLOR\_FORMAT} and \CEnum{ICET\_DEPTH\_FORMAT} state
variables.
\label{manpage:icetImageAssignBuffer}
\label{manpage:icetSparseImageAssignBuffer}
It is also possible, although discouraged, to convert a previously
allocated buffer into an image object with one of the following functions.
\begin{Table}{3}
\CType{IceTImage}\textC{ }\CFunc{icetImageAssignBuffer}\textC{(}&\textC{IceTVoid *}&\CArg{buffer}\textC{,} \\
&\textC{IceTSizeType}&\CArg{width}\textC{,} \\
&\textC{IceTSizeType}&\CArg{height}\quad\textC{);}
\end{Table}
\begin{Table}{3}
\multicolumn{3}{l}{
\CType{IceTSparseImage}\textC{ }\CFunc{icetSparseImageAssignBuffer}\textC{(}
} \\
\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad
&\textC{IceTVoid *}&\CArg{buffer}\textC{,} \\
&\textC{IceTSizeType}&\CArg{width}\textC{,} \\
&\textC{IceTSizeType}&\CArg{height}\quad\textC{);}
\end{Table}
In each case it is assumed that the buffer is at least as large as that
indicated by the \CFunc{icetImageBufferSize} or
\CFunc{icetSparseImageBufferSize} function.
% label manpage:icetImageNull already in man pages.
\label{manpage:icetSparseImageNull}
\IceT also defines a special \index{image!null}\keyterm{null image} that
can be used as a place holder when no image data is available. Null images
for both regular and sparse images are available. They are retreived with
the following functions.
\begin{Table}{3}
\CType{IceTImage}\textC{ }\CFunc{icetImageNull}\textC{(}&\textC{void}&\textC{);}
\end{Table}
\begin{Table}{3}
\CType{IceTSparseImage}\textC{ }\CFunc{icetSparseImageNull}\textC{(}&\textC{void}&\textC{);}
\end{Table}
\subsection{Querying Images}
\IceT contains several functions that allow you to query basic information
about an image object such as dimensions and data formats. Each function
takes an information object and returns the appropriate size or identifier.
(More detail for the functions that work on \CType{IceTImage} objects is
given in the Image Objects section of
Chapter~\ref{sec:Basic_Usage:Image_Objects} starting on
page~\pageref{sec:Basic_Usage:Image_Objects}.)
\begin{Table}{5}
\textC{IceTSizeType}&\icetImageGetWidth&\textC{(}\quad\textC{const }\CType{IceTImage}&\CArg{image}&\textC{);} \\
\textC{IceTSizeType}&\icetImageGetHeight&\textC{(}\quad\textC{const }\CType{IceTImage}&\CArg{image}&\textC{);} \\
\textC{IceTSizeType}&\icetImageGetNumPixels&\textC{(}\quad\textC{const }\CType{IceTImage}&\CArg{image}&\textC{);}
\end{Table}
\begin{Table}{5}
\textC{IceTEnum}&\icetImageGetColorFormat\textC{(}&\textC{const }\CType{IceTImage}&\CArg{image}&\textC{);} \\
\textC{IceTEnum}&\icetImageGetDepthFormat\textC{(}&\textC{const }\CType{IceTImage}&\CArg{image}&\textC{);}
\end{Table}
\label{manpage:icetSparseImageGetWidth}
\label{manpage:icetSparseImageGetHeight}
\label{manpage:icetSparseImageGetNumPixels}
\begin{Table}{4}
\multicolumn{4}{l}{
\textC{IceTSizeType }\CFunc{icetSparseImageGetWidth}\textC{(}
} \\
\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad
&\textC{const }\CType{IceTSparseImage}&\CArg{image}&\textC{);} \\
\multicolumn{4}{l}{
\textC{IceTSizeType }\CFunc{icetSparseImageGetHeight}\textC{(}
} \\
&\textC{const }\CType{IceTSparseImage}&\CArg{image}&\textC{);} \\
\multicolumn{4}{l}{
\textC{IceTSizeType }\CFunc{icetSparseImageGetNumPixels}\textC{(}
} \\
&\textC{const }\CType{IceTSparseImage}&\CArg{image}&\textC{);}
\end{Table}
\label{manpage:icetSparseImageGetColorFormat}
\label{manpage:icetSparseImageGetDepthFormat}
\begin{Table}{4}
\multicolumn{4}{l}{
\textC{IceTEnum }\CFunc{icetSparseImageGetColorFormat}\textC{(}
} \\
\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad
&\textC{const }\CType{IceTSparseImage}&\CArg{image}&\textC{);} \\
\multicolumn{4}{l}{
\textC{IceTEnum }\CFunc{icetSparseImageGetDepthFormat}\textC{(}
} \\
&\textC{const }\CType{IceTSparseImage}&\CArg{image}&\textC{);}
\end{Table}
\IceT also provides several functions for retrieving data from
\CType{IceTImage} objects. These functions are described in the Image
Objects section of Chapter~\ref{sec:Basic_Usage:Image_Objects} starting on
page~\pageref{sec:Basic_Usage:Image_Objects}. There is no mechanism for
directly accessing the data in an \CType{IceTSparseImage}. Instead, data
is indirectly manipulated via compression and compositing functions, which
are described in the subsequent sections.
As implied in the previous section on creating images, an \CType{IceTImage}
or \CType{IceTSparseImage} object has a pointer to a buffer containing the
actual image data. It is sometimes helpful to determine if two images have
the same buffer.
\label{manpage:icetImageEqual}
\begin{Table}{3}
\textC{IceTBoolean }\CFunc{icetImageEqual}\textC{(}&\textC{const }\CType{IceTImage}&\CArg{image1}\textC{,}\\
&\textC{const }\CType{IceTImage}&\CArg{image1}\quad\textC{);}
\end{Table}
\label{manpage:icetSparseImageEqual}
\begin{Table}{3}
\textC{IceTBoolean }\CFunc{icetSparseImageEqual}\textC{(}&\textC{const }\CType{IceTSparseImage}&\CArg{image1}\textC{,}\\
&\textC{const }\CType{IceTSparseImage}&\CArg{image1}\quad\textC{);}
\end{Table}
Both \CFunc{icetImageEqual} and \CFunc{icetSparseImageEqual} take two image
objects and returns whether these two images point to the same buffer. If
two images are equal, then changing the pixel data of one image also
changes the data of the other. Two images may have the same data but still
be considered different if they have separate buffers.
% label manpage:icetImageIsNull already defined in man pages.
\label{manpage:icetSparseImageIsNull}
There are also special functions for testing whether an image is the
\index{image!null}null image.
\begin{Table}{3}
\textC{IceTBoolean}\textC{ }\CFunc{icetImageIsNull}\textC{(}&\CType{IceTImage}&\CArg{image}\quad\textC{);}
\end{Table}
\begin{Table}{3}
\textC{IceTBoolean}\textC{ }\CFunc{icetSparseImageIsNull}\textC{(}&\CType{IceTSparseImage}&\CArg{image}\quad\textC{);}
\end{Table}
\subsection{Setting Pixel Data}
There are several mechanisms for setting, changing, or copying pixel data
in \CType{IceTImage} objects. Foremost are the \icetImageGetColor and
\icetImageGetDepth functions that return the data buffer containing the
color or depth values.
\begin{Table}{4}
\textC{IceTUByte *}&\icetImageGetColorub&\textC{(}\quad\CType{IceTImage}&\CArg{image}\quad\textC{)}; \\
\textC{IceTUInt *}&\icetImageGetColorui&\textC{(}\quad\CType{IceTImage}&\CArg{image}\quad\textC{)}; \\
\textC{IceTFloat *}&\icetImageGetColorf&\textC{(}\quad\CType{IceTImage}&\CArg{image}\quad\textC{)}; \\
\\
\textC{IceTFloat *}&\icetImageGetDepthf&\textC{(}\quad\CType{IceTImage}&\CArg{image}\quad\textC{)};
\end{Table}
The pointers returned from these functions are shared with the
\CType{IceTImage} object itself, so writing data into the buffer will
change the image object as well.
There are no equivalent mechanisms for changing pixel data in
\CType{IceTSparseImage} objects. Instead, data is indirectly manipulated
via compression, copy, and compositing functions, which are described in
the subsequent sections.
It is fairly common to need to clear an image. This is common in a
multi-tile strategy when returning an image for which no geometry is
rendered. \IceT provides convenience functions for setting all the data in
an image to the background color.
\label{manpage:icetClearImage}
\begin{Table}{3}
\textC{void }\CFunc{icetClearImage}\textC{(}&\CType{IceTImage}&\CArg{image}\quad\textC{);}
\end{Table}
\label{manpage:icetClearSparseImage}
\begin{Table}{3}
\textC{void }\CFunc{icetClearSparseImage}\textC{(}&\CType{IceTSparseImage}&\CArg{image}\quad\textC{);}
\end{Table}
\subsection{Copying Full Pixel Data}
It is common to need to copy pixel data from one image to another. \IceT
provides multiple helper functions to copy data amongst images. The
first function is \CFunc{icetImageCopyPixels}, which copies a contiguous
section of pixels.
\label{manpage:icetImageCopyPixels}
\begin{Table}{3}
\textC{void }\CFunc{icetImageCopyPixels}\textC{(}&\textC{const }\CType{IceTImage}&\CArg{in\_image}\textC{,} \\
&\textC{IceTSizeType}&\CArg{in\_offset}\textC{,} \\
&\CType{IceTImage}&\CArg{out\_image}\textC{,} \\
&\textC{IceTSizeType}&\CArg{out\_offset}\textC{,} \\
&\textC{IceTSizeType}&\CArg{num\_pixels}\quad\textC{);}
\end{Table}
\CFunc{icetImageCopyPixels} copies pixel data from \CArg{in\_image} to
\CArg{out\_image}. \CArg{in\_image} and \CArg{out\_image} must have the
same format. Both color and depth values are copied when available. The
data is taken from the input starting at index \CArg{in\_offset} and are
placed in the output starting at index \CArg{out\_offset}.
\CArg{num\_pixels} are copied in all. The following example code copies
the entire contents from \textC{in\_image} to \textC{out\_image} (assuming
they have the same sizes and formats).
\begin{code}
icetImageCopyPixels(in_image, 0, out_image, 0, icetImageGetNumPixels(in_image));
\end{code}
This example copies the third row of data from the input image to the fifth
row of data in the output image.
\begin{code}
width = icetImageGetWidth(in_image);
icetImageCopyPixels(in_image, 3*width, out_image, 5*width, width);
\end{code}
This example copies the second half of pixels in \textC{in\_image} and
places it in the first part of \textC{out\_image}. Notice that coping a
contiguous region of pixels makes it easy to divide images in halves (or
thirds, or whatevers), which is a common operation in image compositing,
without having to worry about image dimensions.
\begin{code}
num_pixels = icetImageGetNumPixels(in_image);
icetImageCopyPixels(in_image, num_pixels/2, out_image, 0, num_pixels/2);
\end{code}
A second convenience function for copying data amongst arrays is
\CFunc{icetImageCopyRegion}. This function works much like
\CFunc{icetImageCopyPixels}, except that you specify a rectangular 2D
viewport window to copy rather than a 1D array region of pixels.
\label{manpage:icetImageCopyRegion}
\begin{Table}{3}
\textC{void }\CFunc{icetImageCopyRegion}\textC{(}&\textC{const }\CType{IceTImage}&\CArg{in\_image}\textC{,} \\
&\textC{const IceTInt *}&\CArg{in\_viewport}\textC{,} \\
&\CType{IceTImage}&\CArg{out\_image}\textC{,} \\
&\textC{const IceTInt *}&\CArg{out\_viewport}\quad\textC{);}
\end{Table}
\CArg{in\_viewport} is an array containing 4 values that specify the
rectangular region from which to copy. The first 2 values specify the $x$
and $y$ position of the lower left corner of the region. The second 2
values specify the width and height of the region. \CArg{out\_viewport} is
a similar array that specifies the destination region. The width and
height of \CArg{in\_viewport} and \CArg{out\_viewport} must be the same.
Here is a simple example of copying all the pixels from \textC{in\_image}
to \textC{out\_image}.
\begin{code}
IceTInt full_viewport[4];
full_viewport[0] = 0;
full_viewport[1] = 0;
full_viewport[2] = icetImageGetWidth(in_image);
full_viewport[3] = icetImageGetHeight(out_image);
icetImageCopyRegion(in_image, full_viewport, out_image, full_viewport);
\end{code}
This example copies a $50 \times 50$ region of pixels from the lower left
corner of \textC{in\_image} to the upper right corner of
\textC{out\_image}.
\begin{code}
IceTInt in_viewport[4], out_viewport[3];
in_viewport[0] = 0; in_viewport[1] = 0;
in_viewport[2] = 50; in_viewport[3] = 50;
out_viewport[0] = icetImageGetWidth(in_image) - 50;
out_viewport[1] = icetImageGetHeight(in_image) - 50;
out_viewport[2] = 50;
out_viewport[3] = 50;
icetImageCopyRegion(in_image, in_viewport, out_image, out_viewport);
\end{code}
As mentioned previously, there is a \CFunc{icetClearImage} function to
clear the contents of an image to background. There is also another
function called \CFunc{icetImageClearAroundRegion} that sets the image to
background everywhere but in a specified 2D viewport window.
\label{manpage:icetImageClearAroundRegion}
\begin{Table}{3}
\textC{void }\CFunc{icetImageClearAroundRegion}\textC{(}&\CType{IceTImage}&\CArg{image}\textC{,} \\
&\textC{const IceTInt *}&\CArg{region}\quad\textC{);}
\end{Table}
Expanding on the previous example, here is code that copies a region of
pixels and then clears everything outside of this region in the
destination.
\begin{code}
icetImageCopyRegion(in_image, in_viewport, out_image, out_viewport);
icetImageClearAroundRegion(out_image, out_viewport);
\end{code}
\subsection{Copy Sparse Image Data}
\IceT also conatins several functions for efficiently copying pixels in
sparse images. Because of the differences in implementation and use, the
copy functions differ significantly between the full image and sparse image
copy functions.
\subsubsection{Basic Sparse Image Copy}
\label{manpage:icetSparseImageCopyPixels}
\begin{Table}{3}
\textC{void }\CFunc{icetSparseImageCopyPixels}\textC{(}&\textC{const }\CType{IceTSparseImage}&\CArg{in\_image}\textC{,}\\
&\textC{IceTSizeType}&\CArg{in\_offset}\textC{,}\\
&\textC{IceTSizeType}&\CArg{num\_pixels}\textC{,}\\
&\CType{IceTSparseImage}&\CArg{out\_image}\textC{);}
\end{Table}
\CFunc{icetSparseImageCopyPixels} copies a region of continuous pixels from
\CArg{in\_image} to \CArg{out\_image}. The region starts a pixel offset
\CArg{in\_offset} and contains \CArg{num\_pixels}. \CArg{out\_image} is
resized to contain only the copied pixels. The new size will have a width
of \CArg{num\_pixels} and a height of $1$.
\subsubsection{Sparse Image Split}
Parallel compositing algorithms often involve splitting an image into a
number of (approximately) equal sized pieces and distributing them amongst
processes. This can be achieved by iteratively calling
\CFunc{icetSparseImageCopyPixels}. However, each call to
\CFunc{icetSparseImageCopyPixels} has to search through the pixels in
\CArg{in\_image} to the appropriate \CArg{in\_offset}. It is a bit more
efficient (and convenient) to iterate over the image once and copy to
multiple different output images as you go. The
\CFunc{icetSparseImageSplit} function does just that.
\label{manpage:icetSparseImageSplit}
\begin{Table}{3}
\multicolumn{3}{l}{
\textC{void }\CFunc{icetSparseImageSplit}\textC{(}
}\\
\makebox[1.5in]{}
&\textC{const }\CType{IceTSparseImage}&\CArg{in\_image}\textC{,}\\
&\textC{IceTSizeType}&\CArg{in\_image\_offset}\textC{,}\\
&\textC{IceTInt}&\CArg{num\_partitions}\textC{,}\\
&\textC{IceTInt}&\CArg{eventual\_num\_partitions}\textC{,}\\
&\CType{IceTSparseImage}\textC{ *}&\CArg{out\_images}\textC{,}\\
&\textC{IceTSizeType *}&\CArg{offsets}\quad\textC{);}
\end{Table}
\CFunc{icetSparseImageSplit} takes \CArg{in\_image}, partitions it into
\CArg{num\_partitions}, and stores the results in the array of
pre-allocated images \CArg{out\_images}. As an optimization, the image in
the first index of \CArg{out\_images} may be the same as \CArg{in\_image}.
In this case the first partition will be copied ``in place.'' It is an
error to have \CArg{in\_image} in any other index of \CArg{out\_images}.
The offset of each image with respect to the original image is stored in
the corresponding index of the array \CArg{offsets}.
The \CArg{in\_image\_offset} and \CArg{eventual\_num\_partitions} are for
recursive splits, described in the following section. For a single
invocation of \CFunc{icetSparseImageSplit} for an image,
\CArg{in\_image\_offset} and \CArg{eventual\_num\_partitions} should be set
to $0$ and the same value as \CArg{num\_partitions}, respectively.
\label{manpage:icetSparseImageSplitPartitionNumPixels}
Before calling \CFunc{icetSparseImageSplit}, it is important to allocate
images with enough pixel space. To allocate these images, you first need
to know how big each partition will be.
\CFunc{icetSparseImageSplitPartitionNumPixels} returns the maximum size of
each partition in pixels. Do not assume that a partition size will be the
total number of pixels divided by the number of partitions. This assumption
is wrong when the number of pixels in the original image does not divide by
the number of partitions.
\begin{Table}{3}
\multicolumn{3}{l}{
\textC{void }\CFunc{icetSparseImageSplitPartitionNumPixels}\textC{(}
}\\
\makebox[2.5in]{}
&\textC{IceTSizeType}&\CArg{input\_num\_pixels}\textC{,}\\
&\textC{IceTInt}&\CArg{num\_partitions}\textC{,}\\
&\textC{IceTInt}&\CArg{eventual\_num\_partitions}\quad\textC{);}
\end{Table}
Because the number of partitions may be large or unknown at compile time,
it can be problematic to fill the array of output images to
\CFunc{icetSparseImageSplit} with images created with
\CFunc{icetGetStateBufferSparseImage} due to the limited number of
available state variables. In this case, it is prudent to create a large
enough buffer with \CFunc{icetGetStateBuffer} and break it up into pieces
to make sparse image objects with \CFunc{icetSparseImageAssignBuffer}. The
following code gives an example of using \CFunc{icetSparseImageSplit}.
This example uses copy-in-place for the first partition, but a trivial
change makes a copy to this buffer.
\index{icetSparseImageGetNumPixels}
\index{icetSparseImageSplitPartitionNumPixels}
\index{icetSparseImageBufferSize}
\index{icetGetStateBuffer}
\index{icetSparseImageAssignBuffer}
\index{icetSparseImageSplit}
\begin{code}
#define NUM_SPLITS 8
/* original_image is image to be split. */
original_num_pixels = icetSparseImageGetNumPixels(original_pixels);
partition_num_pixels
= icetSparseImageSplitPartitionNumPixels(original_num_pixels,
NUM_SPLITS,
NUM_SPLITS);
partition_buffer_size = icetSparseImageBufferSize(partition_num_pixels, 1);
split_image_buffer = icetGetStateBuffer(MYCOMPOSITE_SPLIT_IMAGE_BUFFER,
(NUM_SPLITS-1)*partition_buffer_size);
out_images[0] = original_image;
for (i = 1; i < NUM_SPLITS; i++) {
out_images[i] = icetSparseImageAssignBuffer(split_image_buffer,
partition_num_pixels, 1);
split_image_buffer += partition_buffer_size;
}
icetSparseImageSplit(original_image,
0,
NUM_SPLITS,
NUM_SPLITS,
out_images,
offsets);
for (i = 0; i < NUM_SPLITS; i++) {
DoSomething(out_images[i], offsets[i]);
}
\end{code}
\subsubsection{Recursive Sparse Image Split}
Some image compositing algorithms, such as binary swap and radix-k,
recursively split their images in subsequent rounds. It is also sometimes
the case, such as when telescoping, that different processes will split
images with different factors. For example, one process might split its
image into eight pieces with three recursive calls of two partitions while
another process creates the same partition with one split of two partitions
and another split of four partitions while yet another makes one split of
eight partitions.
Regardless of how the image is split, it is often necessary for the final
partitions to match exactly with respect to offset and size.
Unfortunately, if the size of the original image is not evenly divisible by
the eventual number of partitions, different recursive partitions could
lead to different image pieces.
To get around this problem, \CFunc{icetSparseImageSplit} has the
\CArg{in\_image\_offset} and \CArg{eventual\_num\_partitions} arguments.
The \CArg{in\_image\_offset} declares that the \CArg{in\_image} came from a
previous call to \CFunc{icetSparseImageSplit} with the given offset. The
\CArg{eventual\_num\_partitions} argument declares the total number of
partitions that will be made with this call to \CFunc{icetSparseImageSplit}
and all subsequent calls to \CFunc{icetSparseImageSplit}. Obviously,
\CArg{num\_partitions} must be a factor of
\CArg{eventual\_num\_partitions}, or otherwise
\CArg{eventual\_num\_partitions} could never be created. As long as the
recursive calls to \CFunc{icetSparseImageSplit} are consistent with the
\CArg{in\_image\_offset} and \CArg{eventual\_num\_partitions} arguments,
the partitions will match up exactly.
The following code example will result in the exact same image partitions
as those in the previous example, but does it with two recursive calls.
\index{icetSparseImageGetNumPixels}
\index{icetSparseImageSplitPartitionNumPixels}
\index{icetSparseImageBufferSize}
\index{icetGetStateBuffer}
\index{icetSparseImageAssignBuffer}
\index{icetSparseImageSplit}
\begin{code}
#define FIRST_NUM_SPLITS 2
#define SECOND_NUM_SPLITS 4
#define TOTAL_NUM_SPLITS (FIRST_NUM_SPLITS * SECOND_NUM_SPLITS)
/* original_image is image to be split. */
/* Perform first level image split. */
original_num_pixels = icetSparseImageGetNumPixels(original_pixels);
partition_num_pixels
= icetSparseImageSplitPartitionNumPixels(original_num_pixels,
FIRST_NUM_SPLITS,
TOTAL_NUM_SPLITS);
partition_buffer_size = icetSparseImageBufferSize(partition_num_pixels, 1);
split_image_buffer = icetGetStateBuffer(
MYCOMPOSITE_FIRST_SPLIT_IMAGE_BUFFER,
(FIRST_NUM_SPLITS-1)*partition_buffer_size);
intermediate_images[0] = original_image;
for (i = 1; i < FIRST_NUM_SPLITS; i++) {
intermediate_images[i] = icetSparseImageAssignBuffer(split_image_buffer,
partition_num_pixels,
1);
split_image_buffer += partition_buffer_size;
}
icetSparseImageSplit(original_image,
0,
FIRST_NUM_SPLITS,
TOTAL_NUM_SPLITS,
intermediate_images,
interpediate_offsets);
/* Perform second level image split. */
for (j = 0; j < FIRST_NUM_SPLITS; j++) {
intermediate_num_pixels = icetSparseImageGetNumPixels(original_pixels);
partition_num_pixels = icetSparseImageSplitPartitionNumPixels(
original_num_pixels,
SECOND_NUM_SPLITS,
TOTAL_NUM_SPLITS/FIRST_NUM_SPLITS);
partition_buffer_size = icetSparseImageBufferSize(partition_num_pixels, 1);
split_image_buffer = icetGetStateBuffer(
MYCOMPOSITE_FIRST_SPLIT_IMAGE_BUFFER,
(SECOND_NUM_SPLITS-1)*partition_buffer_size);
out_images[0] = original_image;
for (i = 1; i < SECOND_NUM_SPLITS; i++) {