forked from AcademySoftwareFoundation/OpenImageIO
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathimageio.h
More file actions
4740 lines (4341 loc) · 223 KB
/
Copy pathimageio.h
File metadata and controls
4740 lines (4341 loc) · 223 KB
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
// Copyright Contributors to the OpenImageIO project.
// SPDX-License-Identifier: Apache-2.0
// https://github.com/AcademySoftwareFoundation/OpenImageIO
/////////////////////////////////////////////////////////////////////////////
/// \file
///
/// Provides a simple API that abstracts the reading and writing of
/// images. Subclasses, which may be found in DSO/DLL's, implement
/// particular formats.
///
/////////////////////////////////////////////////////////////////////////////
// clang-format off
#pragma once
#define OPENIMAGEIO_IMAGEIO_H
#if defined(_MSC_VER)
// Ignore warnings about DLL exported classes with member variables that are template classes.
// This happens with the std::vector<T> and std::string members of the classes below.
# pragma warning(disable : 4251)
#endif
#include <cmath>
#include <limits>
#include <string>
#include <vector>
#include <OpenImageIO/oiioversion.h>
#include <OpenImageIO/export.h>
#include <OpenImageIO/span.h>
#include <OpenImageIO/image_span.h>
#include <OpenImageIO/paramlist.h>
#include <OpenImageIO/platform.h>
#include <OpenImageIO/strutil.h>
#include <OpenImageIO/thread.h>
#include <OpenImageIO/typedesc.h>
#include <OpenImageIO/memory.h>
OIIO_NAMESPACE_3_1_BEGIN
/// Type we use to express how many pixels (or bytes) constitute an image,
/// tile, or scanline.
using imagesize_t = uint64_t;
/// Type we use for stride lengths between pixels, scanlines, or image
/// planes.
using stride_t = int64_t;
/// Special value to indicate a stride length that should be
/// auto-computed.
inline constexpr stride_t AutoStride = std::numeric_limits<stride_t>::min();
OIIO_NAMESPACE_3_1_END
// Compatibility
OIIO_NAMESPACE_BEGIN
#ifndef OIIO_DOXYGEN
using v3_1::imagesize_t;
using v3_1::stride_t;
using v3_1::AutoStride;
#endif
OIIO_NAMESPACE_END
OIIO_NAMESPACE_3_1_BEGIN
// Signal that this version of ImageBuf has constructors from spans
#define OIIO_IMAGEINPUT_IMAGE_SPAN_SUPPORT 1
#define OIIO_IMAGEOUTPUT_IMAGE_SPAN_SUPPORT 1
/// Pointer to a function called periodically by read_image and
/// write_image. This can be used to implement progress feedback, etc.
/// It takes an opaque data pointer (passed to read_image/write_image)
/// and a float giving the portion of work done so far. It returns a
/// bool, which if 'true' will STOP the read or write.
typedef bool (*ProgressCallback)(void *opaque_data, float portion_done);
/// ROI is a small helper struct describing a rectangular region of interest
/// in an image. The region is [xbegin,xend) x [begin,yend) x [zbegin,zend),
/// with the "end" designators signifying one past the last pixel in each
/// dimension, a la STL style.
///
struct ROI {
///@{
/// @name ROI data members
/// The data members are:
///
/// int xbegin, xend, ybegin, yend, zbegin, zend;
/// int chbegin, chend;
///
/// These describe the spatial extent
/// [xbegin,xend) x [ybegin,yend) x [zbegin,zend)
/// And the channel extent:
/// [chbegin,chend)]
int xbegin, xend;
int ybegin, yend;
int zbegin, zend;
int chbegin, chend;
///@}
/// Default constructor is an undefined region. Note that this is also
/// interpreted as All().
constexpr ROI () noexcept : xbegin(std::numeric_limits<int>::min()), xend(0),
ybegin(0), yend(0), zbegin(0), zend(0), chbegin(0), chend(0)
{ }
/// Constructor with an explicitly defined region.
///
constexpr ROI (int xbegin, int xend, int ybegin, int yend,
int zbegin=0, int zend=1, int chbegin=0, int chend=10000) noexcept
: xbegin(xbegin), xend(xend), ybegin(ybegin), yend(yend),
zbegin(zbegin), zend(zend), chbegin(chbegin), chend(chend)
{ }
/// Is a region defined?
constexpr bool defined () const noexcept { return (xbegin != std::numeric_limits<int>::min()); }
///@{
/// @name Spatial size functions.
/// The width, height, and depth of the region.
constexpr int width () const noexcept { return xend - xbegin; } ///< Height
constexpr int height () const noexcept { return yend - ybegin; } ///< Width
constexpr int depth () const noexcept { return zend - zbegin; } ///< Depth
///@}
/// Number of channels in the region. Beware -- this defaults to a
/// huge number, and to be meaningful you must consider
/// std::min (imagebuf.nchannels(), roi.nchannels()).
constexpr int nchannels () const noexcept { return chend - chbegin; }
/// Total number of pixels in the region.
constexpr imagesize_t npixels () const noexcept {
return defined()
? imagesize_t(width()) * imagesize_t(height()) * imagesize_t(depth())
: 0;
}
/// All() is an alias for the default constructor, which indicates that
/// it means "all" of the image, or no region restriction. For example,
/// float myfunc (ImageBuf &buf, ROI roi = ROI::All());
/// Note that this is equivalent to:
/// float myfunc (ImageBuf &buf, ROI roi = {});
static constexpr ROI All () noexcept { return ROI(); }
/// Test equality of two ROIs
friend constexpr bool operator== (const ROI &a, const ROI &b) noexcept {
return (a.xbegin == b.xbegin && a.xend == b.xend &&
a.ybegin == b.ybegin && a.yend == b.yend &&
a.zbegin == b.zbegin && a.zend == b.zend &&
a.chbegin == b.chbegin && a.chend == b.chend);
}
/// Test inequality of two ROIs
friend constexpr bool operator!= (const ROI &a, const ROI &b) noexcept {
return (a.xbegin != b.xbegin || a.xend != b.xend ||
a.ybegin != b.ybegin || a.yend != b.yend ||
a.zbegin != b.zbegin || a.zend != b.zend ||
a.chbegin != b.chbegin || a.chend != b.chend);
}
/// Test if the coordinate is within the ROI.
constexpr bool contains (int x, int y, int z=0, int ch=0) const noexcept {
return x >= xbegin && x < xend && y >= ybegin && y < yend
&& z >= zbegin && z < zend && ch >= chbegin && ch < chend;
}
/// Test if another ROI is entirely within our ROI.
constexpr bool contains (const ROI& other) const noexcept {
return (other.xbegin >= xbegin && other.xend <= xend &&
other.ybegin >= ybegin && other.yend <= yend &&
other.zbegin >= zbegin && other.zend <= zend &&
other.chbegin >= chbegin && other.chend <= chend);
}
/// Stream output of the range
friend std::ostream & operator<< (std::ostream &out, const ROI &roi) {
out << roi.xbegin << ' ' << roi.xend << ' ' << roi.ybegin << ' '
<< roi.yend << ' ' << roi.zbegin << ' ' << roi.zend << ' '
<< roi.chbegin << ' ' << roi.chend;
return out;
}
};
/// Union of two regions, the smallest region containing both.
inline constexpr ROI roi_union (const ROI &A, const ROI &B) noexcept {
return (A.defined() && B.defined())
? ROI (std::min (A.xbegin, B.xbegin), std::max (A.xend, B.xend),
std::min (A.ybegin, B.ybegin), std::max (A.yend, B.yend),
std::min (A.zbegin, B.zbegin), std::max (A.zend, B.zend),
std::min (A.chbegin, B.chbegin), std::max (A.chend, B.chend))
: (A.defined() ? A : B);
}
/// Intersection of two regions.
inline constexpr ROI roi_intersection (const ROI &A, const ROI &B) noexcept {
return (A.defined() && B.defined())
? ROI (std::max (A.xbegin, B.xbegin), std::min (A.xend, B.xend),
std::max (A.ybegin, B.ybegin), std::min (A.yend, B.yend),
std::max (A.zbegin, B.zbegin), std::min (A.zend, B.zend),
std::max (A.chbegin, B.chbegin), std::min (A.chend, B.chend))
: (A.defined() ? A : B);
}
/// ImageSpec describes the data format of an image -- dimensions, layout,
/// number and meanings of image channels.
///
/// The `width, height, depth` are the size of the data of this image, i.e.,
/// the number of pixels in each dimension. A ``depth`` greater than 1
/// indicates a 3D "volumetric" image. The `x, y, z` fields indicate the
/// *origin* of the pixel data of the image. These default to (0,0,0), but
/// setting them differently may indicate that this image is offset from the
/// usual origin.
/// Therefore the pixel data are defined over pixel coordinates
/// [`x` ... `x+width-1`] horizontally,
/// [`y` ... `y+height-1`] vertically,
/// and [`z` ... `z+depth-1`] in depth.
///
/// The analogous `full_width`, `full_height`, `full_depth` and `full_x`,
/// `full_y`, `full_z` fields define a "full" or "display" image window over
/// the region [`full_x` ... `full_x+full_width-1`] horizontally, [`full_y`
/// ... `full_y+full_height-1`] vertically, and [`full_z`...
/// `full_z+full_depth-1`] in depth.
///
/// Having the full display window different from the pixel data window can
/// be helpful in cases where you want to indicate that your image is a
/// *crop window* of a larger image (if the pixel data window is a subset of
/// the full display window), or that the pixels include *overscan* (if the
/// pixel data is a superset of the full display window), or may simply
/// indicate how different non-overlapping images piece together.
///
/// For tiled images, `tile_width`, `tile_height`, and `tile_depth` specify
/// that the image is stored in a file organized into rectangular *tiles*
/// of these dimensions. The default of 0 value for these fields indicates
/// that the image is stored in scanline order, rather than as tiles.
///
class OIIO_API ImageSpec {
public:
///@{
/// @name ImageSpec data members
///
/// The `ImageSpec` contains data fields for the values that are
/// required to describe nearly any image, and an extensible list of
/// arbitrary attributes that can hold metadata that may be user-defined
/// or specific to individual file formats.
///
/// Here are the hard-coded data fields:
int x; ///< origin (upper left corner) of pixel data
int y; ///< origin (upper left corner) of pixel data
int z; ///< origin (upper left corner) of pixel data
int width; ///< width of the pixel data window
int height; ///< height of the pixel data window
int depth; ///< depth of pixel data, >1 indicates a "volume"
int full_x; ///< origin of the full (display) window
int full_y; ///< origin of the full (display) window
int full_z; ///< origin of the full (display) window
int full_width; ///< width of the full (display) window
int full_height; ///< height of the full (display) window
int full_depth; ///< depth of the full (display) window
int tile_width; ///< tile width (0 for a non-tiled image)
int tile_height; ///< tile height (0 for a non-tiled image)
int tile_depth; ///< tile depth (0 for a non-tiled image,
///< 1 for a non-volume image)
int nchannels; ///< number of image channels, e.g., 4 for RGBA
TypeDesc format; ///< Data format of the channels.
///< Describes the native format of the pixel data values
/// themselves, as a `TypeDesc`. Typical values would be
/// `TypeDesc::UINT8` for 8-bit unsigned values, `TypeDesc::FLOAT`
/// for 32-bit floating-point values, etc.
std::vector<TypeDesc> channelformats;
///< Optional per-channel data formats. If all channels of the image
/// have the same data format, that will be described by `format`
/// and `channelformats` will be empty (zero length). If there are
/// different data formats for each channel, they will be described
/// in the `channelformats` vector, and the `format` field will
/// indicate a single default data format for applications that
/// don't wish to support per-channel formats (usually this will be
/// the format of the channel that has the most precision).
std::vector<std::string> channelnames;
///< The names of each channel, in order. Typically this will be "R",
///< "G", "B", "A" (alpha), "Z" (depth), or other arbitrary names.
int alpha_channel;
///< The index of the channel that represents *alpha* (pixel
///< coverage and/or transparency). It defaults to -1 if no alpha
///< channel is present, or if it is not known which channel
///< represents alpha.
int z_channel;
///< The index of the channel that represents *z* or *depth* (from
///< the camera). It defaults to -1 if no depth channel is present,
///< or if it is not know which channel represents depth.
bool deep; ///< True if the image contains deep data.
///< If `true`, this indicates that the image describes contains
///< "deep" data consisting of multiple samples per pixel. If
///< `false`, it's an ordinary image with one data value (per
///< channel) per pixel.
ParamValueList extra_attribs;
///< A list of arbitrarily-named and arbitrarily-typed additional
/// attributes of the image, for any metadata not described by the
/// hard-coded fields described above. This list may be manipulated
/// with the `attribute()` and `find_attribute()` methods.
///@}
/// Constructor: given just the data format, set all other fields to
/// something reasonable.
ImageSpec (TypeDesc format = TypeDesc::UNKNOWN) noexcept;
/// Constructor: given just the data format (as any type name recognized
/// by the `TypeDesc` constructor), set all other fields to something
/// reasonable.
ImageSpec (string_view format) noexcept : ImageSpec(TypeDesc(format)) {}
/// Constructs an `ImageSpec` with the given x and y resolution, number
/// of channels, and pixel data format.
///
/// All other fields are set to the obvious defaults -- the image is an
/// ordinary 2D image (not a volume), the image is not offset or a crop
/// of a bigger image, the image is scanline-oriented (not tiled),
/// channel names are "R", "G", "B"' and "A" (up to and including 4
/// channels, beyond that they are named "channel *n*"), the fourth
/// channel (if it exists) is assumed to be alpha.
ImageSpec (int xres, int yres, int nchans, TypeDesc fmt = TypeUInt8) noexcept;
/// Construct an `ImageSpec` with the given x and y resolution, number
/// of channels, and pixel data format name (as any type name recognized
/// by the `TypeDesc` constructor).
ImageSpec (int xres, int yres, int nchans, string_view fmt) noexcept
: ImageSpec(xres, yres, nchans, TypeDesc(fmt)) {}
/// Construct an `ImageSpec` whose dimensions (both data and "full") and
/// number of channels are given by the `ROI`, pixel data type by `fmt`,
/// and other fields are set to their default values.
explicit ImageSpec (const ROI &roi, TypeDesc fmt = TypeUInt8) noexcept;
/// Construct an `ImageSpec` from an ROI giving dimensions, and the name
/// of a data type that will be recognized by the `TypeDesc` constructor.
explicit ImageSpec (const ROI &roi, string_view fmt) noexcept
: ImageSpec(roi, TypeDesc(fmt)) {}
/// Set the data format, and clear any per-channel format information
/// in `channelformats`.
void set_format (TypeDesc fmt) noexcept;
/// Set the data format, and clear any per-channel format information
/// in `channelformats`. The `fmt` may be a string such as `"uint8"`,
/// or any other type name recognized by the TypeDesc constructor.
void set_format (string_view fmt) noexcept { set_format(TypeDesc(fmt)); }
/// Sets the `channelnames` to reasonable defaults for the number of
/// channels. Specifically, channel names are set to "R", "G", "B,"
/// and "A" (up to and including 4 channels, beyond that they are named
/// "channel*n*".
void default_channel_names () noexcept;
/// Returns the number of bytes comprising each channel of each pixel
/// (i.e., the size of a single value of the type described by the
/// `format` field).
size_t channel_bytes() const noexcept { return format.size(); }
/// Return the number of bytes needed for the single specified
/// channel. If native is false (default), compute the size of one
/// channel of `this->format`, but if native is true, compute the size
/// of the channel in terms of the "native" data format of that
/// channel as stored in the file.
size_t channel_bytes (int chan, bool native=false) const noexcept;
/// Return the number of bytes for each pixel (counting all channels).
/// If `native` is false (default), assume all channels are in
/// `this->format`, but if `native` is true, compute the size of a pixel
/// in the "native" data format of the file (these may differ in
/// the case of per-channel formats).
size_t pixel_bytes (bool native=false) const noexcept;
/// Return the number of bytes for just the subset of channels in each
/// pixel described by [chbegin,chend). If native is false (default),
/// assume all channels are in this->format, but if native is true,
/// compute the size of a pixel in the "native" data format of the file
/// (these may differ in the case of per-channel formats).
size_t pixel_bytes (int chbegin, int chend, bool native=false) const noexcept;
/// Returns the number of bytes comprising each scanline, i.e.,
/// `pixel_bytes(native) * width` This will return
/// `std::numeric_limits<imagesize_t>::max()` in the event of an
/// overflow where it's not representable in an `imagesize_t`.
imagesize_t scanline_bytes (bool native=false) const noexcept;
/// Returns the number of bytes comprising each scanline, if all channels
/// were of the given type. If `type` is `TypeUnknown`, then it returns
/// the bytes a scanline using each channel's native type. This will
/// return `std::numeric_limits<imagesize_t>::max()` in the event of an
/// overflow where it's not representable in an `imagesize_t`.
imagesize_t scanline_bytes(TypeDesc type) const noexcept;
/// Return the number of pixels comprising a tile (or 0 if it is not a
/// tiled image). This will return
/// `std::numeric_limits<imagesize_t>::max()` in the event of an
/// overflow where it's not representable in an `imagesize_t`.
imagesize_t tile_pixels () const noexcept;
/// Returns the number of bytes comprising an image tile, i.e.,
/// `pixel_bytes(native) * tile_width * tile_height * tile_depth`
/// If native is false (default), assume all channels are in
/// `this->format`, but if `native` is true, compute the size of a pixel
/// in the "native" data format of the file (these may differ in the
/// case of per-channel formats).
imagesize_t tile_bytes (bool native=false) const noexcept;
/// Returns the number of bytes comprising each tile, if all channels
/// were of the given type. If `type` is `TypeUnknown`, then it returns
/// the bytes a scanline using each channel's native type. This will
/// return `std::numeric_limits<imagesize_t>::max()` in the event of an
/// overflow where it's not representable in an `imagesize_t`.
imagesize_t tile_bytes(TypeDesc type) const noexcept;
/// Return the number of pixels for an entire image. This will
/// return `std::numeric_limits<imagesize_t>::max()` in the event of
/// an overflow where it's not representable in an `imagesize_t`.
imagesize_t image_pixels () const noexcept;
/// Returns the number of bytes comprising an entire image of these
/// dimensions, i.e.,
/// `pixel_bytes(native) * width * height * depth`
/// This will return `std::numeric_limits<image size_t>::max()` in the
/// event of an overflow where it's not representable in an
/// `imagesize_t`. If `native` is false (default), assume all channels
/// are in `this->format`, but if `native` is true, compute the size of
/// a pixel in the "native" data format of the file (these may differ in
/// the case of per-channel formats).
imagesize_t image_bytes (bool native=false) const noexcept;
/// Returns the number of bytes comprising an entire image of these
/// dimensions, if the values were all of type `datatype`. For the
/// special case of `datatype == `TypeUnknown`, compute the size of
/// the image in the "native" data types for all channels.
imagesize_t image_bytes(TypeDesc datatype) const noexcept;
/// Verify that on this platform, a `size_t` is big enough to hold the
/// number of bytes (and pixels) in a scanline, a tile, and the
/// whole image. If this returns false, the image is much too big
/// to allocate and read all at once, so client apps beware and check
/// these routines for overflows!
bool size_t_safe() const noexcept {
const imagesize_t big = std::numeric_limits<size_t>::max();
return image_bytes() < big && scanline_bytes() < big &&
tile_bytes() < big;
}
/// Adjust the stride values, if set to AutoStride, to be the right
/// sizes for contiguous data with the given format, channels,
/// width, height.
static void auto_stride (stride_t &xstride, stride_t &ystride,
stride_t &zstride, stride_t channelsize,
int nchannels, int width, int height) noexcept {
if (xstride == AutoStride)
xstride = nchannels * channelsize;
if (ystride == AutoStride)
ystride = xstride * width;
if (zstride == AutoStride)
zstride = ystride * height;
}
/// Adjust the stride values, if set to AutoStride, to be the right
/// sizes for contiguous data with the given format, channels,
/// width, height.
static void auto_stride (stride_t &xstride, stride_t &ystride,
stride_t &zstride, TypeDesc format,
int nchannels, int width, int height) noexcept {
auto_stride (xstride, ystride, zstride, format.size(),
nchannels, width, height);
}
/// Adjust xstride, if set to AutoStride, to be the right size for
/// contiguous data with the given format and channels.
static void auto_stride (stride_t &xstride, TypeDesc format, int nchannels) noexcept {
if (xstride == AutoStride)
xstride = nchannels * format.size();
}
/// Add a metadata attribute to `extra_attribs`, with the given name and
/// data type. The `value` span specifies the data to be copied. The data
/// type and total size of `value` must match the `type` (if not, an
/// assertion will be thrown for debug builds).
///
/// @version 3.1
template<typename T>
void attribute(string_view name, TypeDesc type, span<T> value) {
OIIO_DASSERT(BaseTypeFromC<T>::value == type.basetype
&& type.size() == value.size_bytes());
attribute(name, type, OIIO::as_bytes(value));
}
/// A version of `attribute()` that takes its value from a span of untyped
/// bytes. The total size of `value` must match the `type` (if not, an
/// assertion will be thrown for debug builds of OIIO, an error will be
/// printed for release builds).
///
/// @version 3.1
void attribute(string_view name, TypeDesc type, cspan<std::byte> value);
/// A version of `attribute()` where the `value` is only a pointer
/// specifying the beginning of the memory where the value should be copied
/// from. This is "unsafe" in the sense that there is no assurance that it
/// points to a sufficient amount of memory, so the span-based versions of
/// `attribute()` are preferred.
///
/// This was added in version 2.1.
void attribute(string_view name, TypeDesc type, const void *value);
/// Add an `unsigned int` attribute to `extra_attribs`.
void attribute (string_view name, unsigned int value) {
attribute (name, TypeDesc::UINT, &value);
}
/// Add an `int` attribute to `extra_attribs`.
void attribute (string_view name, int value) {
attribute (name, TypeDesc::INT, &value);
}
/// Add a `float` attribute to `extra_attribs`.
void attribute (string_view name, float value) {
attribute (name, TypeDesc::FLOAT, &value);
}
/// Add a string attribute to `extra_attribs`.
void attribute (string_view name, string_view value);
/// Add a string attribute (passed as a ustring) to `extra_attribs`.
void attribute (string_view name, ustring value);
/// Parse a string containing a textual representation of a value of
/// the given `type`, and add that as an attribute to `extra_attribs`.
/// Example:
///
/// spec.attribute ("temperature", TypeFloat, "-273.15");
///
void attribute (string_view name, TypeDesc type, string_view value);
/// Searches `extra_attribs` for any attributes matching `name` (as a
/// regular expression), removing them entirely from `extra_attribs`. If
/// `searchtype` is anything other than `TypeDesc::UNKNOWN`, matches
/// will be restricted only to attributes with the given type. The name
/// comparison will be case-sensitive if `casesensitive` is true,
/// otherwise in a case-insensitive manner.
void erase_attribute (string_view name,
TypeDesc searchtype=TypeDesc::UNKNOWN,
bool casesensitive=false);
/// Searches `extra_attribs` for an attribute matching `name`, returning
/// a pointer to the attribute record, or NULL if there was no match.
/// If `searchtype` is anything other than `TypeDesc::UNKNOWN`, matches
/// will be restricted only to attributes with the given type. The name
/// comparison will be exact if `casesensitive` is true, otherwise in a
/// case-insensitive manner if `caseinsensitive` is false.
ParamValue * find_attribute (string_view name,
TypeDesc searchtype=TypeDesc::UNKNOWN,
bool casesensitive=false);
const ParamValue *find_attribute (string_view name,
TypeDesc searchtype=TypeDesc::UNKNOWN,
bool casesensitive=false) const;
/// Search for the named attribute and return the pointer to its
/// `ParamValue` record, or NULL if not found. This variety of
/// `find_attribute(}` can retrieve items such as "width", which are
/// data members of the `ImageSpec`, but not in `extra_attribs`. The
/// `tmpparam` is a storage area owned by the caller, which is used as
/// temporary buffer in cases where the information does not correspond
/// to an actual `extra_attribs` (in this case, the return value will be
/// `&tmpparam`). The extra names it understands are:
///
/// - `"x"` `"y"` `"z"` `"width"` `"height"` `"depth"`
/// `"full_x"` `"full_y"` `"full_z"` `"full_width"` `"full_height"` `"full_depth"`
///
/// Returns the `ImageSpec` fields of those names (despite the
/// fact that they are technically not arbitrary named attributes
/// in `extra_attribs`). All are of type `int`.
///
/// - `"datawindow"`
///
/// Without a type, or if requested explicitly as an `int[4]`,
/// returns the OpenEXR-like pixel data min and max coordinates,
/// as a 4-element integer array: `{ x, y, x+width-1, y+height-1
/// }`. If instead you specifically request as an `int[6]`, it
/// will return the volumetric data window, `{ x, y, z, x+width-1,
/// y+height-1, z+depth-1 }`.
///
/// - `"displaywindow"`
///
/// Without a type, or if requested explicitly as an `int[4]`,
/// returns the OpenEXR-like pixel display min and max
/// coordinates, as a 4-element integer array: `{ full_x, full_y,
/// full_x+full_width-1, full_y+full_height-1 }`. If instead you
/// specifically request as an `int[6]`, it will return the
/// volumetric display window, `{ full_x, full_y, full_z,
/// full_x+full_width-1, full_y+full_height-1, full_z+full_depth-1 }`.
///
/// EXAMPLES
///
/// ImageSpec spec; // has the info
/// Imath::Box2i dw; // we want the displaywindow here
/// ParamValue tmp; // so we can retrieve pseudo-values
/// TypeDesc int4("int[4]"); // Equivalent: TypeDesc int4(TypeDesc::INT,4);
/// const ParamValue* p = spec.find_attribute ("displaywindow", int4);
/// if (p)
/// dw = Imath::Box2i(p->get<int>(0), p->get<int>(1),
/// p->get<int>(2), p->get<int>(3));
///
/// p = spec.find_attribute("temperature", TypeFloat);
/// if (p)
/// float temperature = p->get<float>();
///
const ParamValue * find_attribute (string_view name,
ParamValue &tmpparam,
TypeDesc searchtype=TypeDesc::UNKNOWN,
bool casesensitive=false) const;
/// If the named attribute can be found in the `ImageSpec`, return its
/// data type. If no such attribute exists, return `TypeUnknown`.
///
/// This was added in version 2.1.
TypeDesc getattributetype (string_view name,
bool casesensitive = false) const;
/// If the `ImageSpec` contains the named attribute and its type matches
/// `type`, copy the attribute value into the memory pointed to by the
/// span `value` and return `true`. If no such attribute is found, or if
/// it doesn't match the type, return `false` and do not modify `val`.
/// The data type and total size of `value` must match the `type` (if not,
/// an assertion will be thrown for debug builds).
///
/// EXAMPLES:
///
/// ImageSpec spec;
/// ...
/// // Retrieving an integer attribute:
/// int orientation = 0;
/// spec.getattribute ("orientation", TypeInt, make_span(orientation));
///
/// // Retrieving a string attribute with a char*:
/// const char* compression = nullptr;
/// spec.getattribute ("compression", TypeString, make_span(compression));
///
/// // Alternately, retrieving a string with a ustring:
/// ustring compression;
/// spec.getattribute ("compression", TypeString, make_span(compression));
///
/// Note that when retrieving a string, you need to pass a span of either
/// `const char*` or `ustring`, not a pointer to the first character of
/// the string. Because the returned values are ustrings, the caller does
/// not need to ever free the memory that contains the characters, which
/// are owned by the internal ustring table.
///
/// @version 3.1
template<typename T>
bool getattribute(string_view name, TypeDesc type, span<T> value,
bool casesensitive = false) const
{
OIIO_DASSERT(BaseTypeFromC<T>::value == type.basetype
&& type.size() == value.size_bytes());
return getattribute(name, type, OIIO::as_writable_bytes(value),
casesensitive);
}
/// A version of `getattribute()` that stores the value in a span of
/// untyped bytes. The total size of `value` must match the `type` (if
/// not, an assertion will be thrown for debug OIIO builds, an error will
/// be printed for release builds).
///
/// @version 3.1
bool getattribute(string_view name, TypeDesc type, span<std::byte> value,
bool casesensitive = false) const;
/// A version of `getattribute()` where the `value` is only a pointer
/// specifying the beginning of the memory where the value should be
/// copied. This is "unsafe" in the sense that there is no assurance that
/// it points to a sufficient amount of memory, so the span-based versions
/// of `getattribute()` are preferred.
///
/// EXAMPLES:
///
/// ImageSpec spec;
/// ...
/// // Retrieving an integer attribute:
/// int orientation = 0;
/// spec.getattribute ("orientation", TypeInt, &orientation);
///
/// // Retrieving a string attribute with a char*:
/// const char* compression = nullptr;
/// spec.getattribute ("compression", TypeString, &compression);
///
/// // Alternately, retrieving a string with a ustring:
/// ustring compression;
/// spec.getattribute ("compression", TypeString, &compression);
///
/// Note that when passing a string, you need to pass a pointer to the
/// `char*`, not a pointer to the first character. Also, the `char*`
/// will end up pointing to characters owned by the `ImageSpec`; the
/// caller does not need to ever free the memory that contains the
/// characters.
///
/// This was added in version 2.1.
bool getattribute (string_view name, TypeDesc type, void* value,
bool casesensitive = false) const;
/// Retrieve the named metadata attribute and return its value as an
/// `int`. Any integer type will convert to `int` by truncation or
/// expansion, string data will parsed into an `int` if its contents
/// consist of the text representation of one integer. Floating point
/// data will not succeed in converting to an `int`. If no such metadata
/// exists, or are of a type that cannot be converted, the `defaultval`
/// will be returned.
int get_int_attribute (string_view name, int defaultval=0) const;
/// Retrieve the named metadata attribute and return its value as a
/// `float`. Any integer or floating point type will convert to `float`
/// in the obvious way (like a C cast), and so will string metadata if
/// its contents consist of the text representation of one floating
/// point value. If no such metadata exists, or are of a type that cannot
/// be converted, the `defaultval` will be returned.
float get_float_attribute (string_view name, float defaultval=0) const;
/// Retrieve any metadata attribute, converted to a string.
/// If no such metadata exists, the `defaultval` will be returned.
string_view get_string_attribute (string_view name,
string_view defaultval = string_view()) const;
/// For a given parameter `p`, format the value nicely as a string. If
/// `human` is true, use especially human-readable explanations (units,
/// or decoding of values) for certain known metadata.
static std::string metadata_val (const ParamValue &p, bool human=false);
enum SerialFormat { SerialText, SerialXML };
enum SerialVerbose { SerialBrief, SerialDetailed, SerialDetailedHuman };
/// Returns, as a string, a serialized version of the `ImageSpec`. The
/// `format` may be either `ImageSpec::SerialText` or
/// `ImageSpec::SerialXML`. The `verbose` argument may be one of:
/// `ImageSpec::SerialBrief` (just resolution and other vital
/// statistics, one line for `SerialText`, `ImageSpec::SerialDetailed`
/// (contains all metadata in original form), or
/// `ImageSpec::SerialDetailedHuman` (contains all metadata, in many
/// cases with human-readable explanation).
std::string serialize (SerialFormat format,
SerialVerbose verbose = SerialDetailed) const;
/// Converts the contents of the `ImageSpec` as an XML string.
std::string to_xml () const;
/// Populates the fields of the `ImageSpec` based on the XML passed in.
void from_xml (const char *xml);
/// Hunt for the "Compression" and "CompressionQuality" settings in the
/// spec and turn them into the compression name and quality. This
/// handles compression name/qual combos of the form "name:quality".
std::pair<string_view, int>
decode_compression_metadata(string_view defaultcomp = "",
int defaultqual = -1) const;
/// Helper function to verify that the given pixel range exactly covers a
/// set of 2D tiles. Also returns false if the spec indicates that the
/// image isn't tiled at all.
bool valid_tile_range (int xbegin, int xend, int ybegin, int yend) noexcept {
return (tile_width &&
((xbegin-x) % tile_width) == 0 &&
((ybegin-y) % tile_height) == 0 &&
(((xend-x) % tile_width) == 0 || (xend-x) == width) &&
(((yend-y) % tile_height) == 0 || (yend-y) == height));
}
/// Helper function to verify that the given pixel range exactly covers a
/// set of 3D tiles. Also returns false if the spec indicates that the
/// image isn't tiled at all.
bool valid_tile_range (int xbegin, int xend, int ybegin, int yend,
int zbegin, int zend) noexcept {
return (tile_width &&
((xbegin-x) % tile_width) == 0 &&
((ybegin-y) % tile_height) == 0 &&
((zbegin-z) % tile_depth) == 0 &&
(((xend-x) % tile_width) == 0 || (xend-x) == width) &&
(((yend-y) % tile_height) == 0 || (yend-y) == height) &&
(((zend-z) % tile_depth) == 0 || (zend-z) == depth));
}
/// Return the channelformat of the given channel. This is safe even
/// if channelformats is not filled out.
TypeDesc channelformat (int chan) const {
return chan >= 0 && chan < (int)channelformats.size()
? channelformats[chan] : format;
}
/// Return the channel name of the given channel. This is safe even if
/// channelnames is not filled out.
string_view channel_name (int chan) const {
return chan >= 0 && chan < (int)channelnames.size()
? string_view(channelnames[chan]) : "";
}
/// Fill in an array of channel formats describing all channels in
/// the image. (Note that this differs slightly from the member
/// data channelformats, which is empty if there are not separate
/// per-channel formats.)
void get_channelformats (std::vector<TypeDesc> &formats) const {
formats = channelformats;
if ((int)formats.size() < nchannels)
formats.resize (nchannels, format);
}
/// Return the index of the channel with the given name, or -1 if no
/// such channel is present in `channelnames`.
int channelindex (string_view name) const;
/// Return pixel data window for this ImageSpec expressed as a ROI.
ROI roi () const noexcept {
return ROI (x, x+width, y, y+height, z, z+depth, 0, nchannels);
}
/// Return full/display window for this ImageSpec expressed as a ROI.
ROI roi_full () const noexcept {
return ROI (full_x, full_x+full_width, full_y, full_y+full_height,
full_z, full_z+full_depth, 0, nchannels);
}
/// Set pixel data window parameters (x, y, z, width, height, depth)
/// for this ImageSpec from an ROI.
/// Does NOT change the channels of the spec, regardless of r.
void set_roi (const ROI &r) noexcept {
x = r.xbegin;
y = r.ybegin;
z = r.zbegin;
width = r.width();
height = r.height();
depth = r.depth();
}
/// Set full/display window parameters (full_x, full_y, full_z,
/// full_width, full_height, full_depth) for this ImageSpec from an ROI.
/// Does NOT change the channels of the spec, regardless of r.
void set_roi_full (const ROI &r) noexcept {
full_x = r.xbegin;
full_y = r.ybegin;
full_z = r.zbegin;
full_width = r.width();
full_height = r.height();
full_depth = r.depth();
}
/// Copy from `other` the image dimensions (x, y, z, width, height,
/// depth, full*, nchannels, format) and data types. It does *not* copy
/// arbitrary named metadata or channel names (thus, for an `ImageSpec`
/// with lots of metadata, it is much less expensive than copying the
/// whole thing with `operator=()`).
void copy_dimensions (const ImageSpec &other) {
x = other.x;
y = other.y;
z = other.z;
width = other.width;
height = other.height;
depth = other.depth;
full_x = other.full_x;
full_y = other.full_y;
full_z = other.full_z;
full_width = other.full_width;
full_height = other.full_height;
full_depth = other.full_depth;
tile_width = other.tile_width;
tile_height = other.tile_height;
tile_depth = other.tile_depth;
nchannels = other.nchannels;
format = other.format;
channelformats = other.channelformats;
alpha_channel = other.alpha_channel;
z_channel = other.z_channel;
deep = other.deep;
}
/// Set the metadata to presume that color space is `name` (or to assume
/// nothing about the color space if `name` is empty). The core operation
/// is to set the "oiio:ColorSpace" attribute, but it also removes or
/// alters several other attributes that may hint color space in ways that
/// might be contradictory or no longer true.
///
/// @version 2.5
void set_colorspace(string_view name);
/// Returns `true` for a newly initialized (undefined) `ImageSpec`.
/// (Designated by no channels and undefined data type -- true of the
/// uninitialized state of an ImageSpec, and presumably not for any
/// ImageSpec that is useful or purposefully made.)
bool undefined () const noexcept {
return nchannels == 0 && format == TypeUnknown;
}
/// Array indexing by string will create an AttrDelegate that enables a
/// convenient shorthand for adding and retrieving values from the spec:
///
/// 1. Assigning to the delegate adds a metadata attribute:
///
/// ImageSpec spec;
/// spec["foo"] = 42; // int
/// spec["pi"] = float(M_PI); // float
/// spec["oiio:ColorSpace"] = "sRGB"; // string
/// spec["cameratoworld"] = Imath::Matrix44(...); // matrix
///
/// Be very careful, the attribute's type will be implied by the C++
/// type of what you assign.
///
/// 2. String data may be retrieved directly, and for other types, the
/// delegate supports a get<T>() that retrieves an item of type T:
///
/// std::string colorspace = spec["oiio:ColorSpace"];
/// int dither = spec["oiio:dither"].get<int>();
///
/// This was added in version 2.1.
AttrDelegate<ImageSpec> operator[](string_view name)
{
return { this, name };
}
AttrDelegate<const ImageSpec> operator[](string_view name) const
{
return { this, name };
}
};
/// ImageInput abstracts the reading of an image file in a file
/// format-agnostic manner.
class OIIO_API ImageInput {
public:
/// unique_ptr to an ImageInput
using unique_ptr = std::unique_ptr<ImageInput>;
/// @{
/// @name Creating an ImageInput
/// Create an ImageInput subclass instance that is able to read the
/// given file and open it, returning a `unique_ptr` to the ImageInput
/// if successful. The `unique_ptr` is set up with an appropriate
/// deleter so the ImageInput will be properly closed and deleted when
/// the `unique_ptr` goes out of scope or is reset. If the open fails,
/// return an empty `unique_ptr` and set an error that can be retrieved
/// by `OIIO::geterror()`.
///
/// The `config`, if not nullptr, points to an ImageSpec giving hints,
/// requests, or special instructions. ImageInput implementations are
/// free to not respond to any such requests, so the default
/// implementation is just to ignore `config`.
///
/// `open()` will first try to make an ImageInput corresponding to
/// the format implied by the file extension (for example, `"foo.tif"`
/// will try the TIFF plugin), but if one is not found or if the
/// inferred one does not open the file, every known ImageInput type
/// will be tried until one is found that will open the file.
///
/// @param filename
/// The name of the file to open, UTF-8 encoded.
///
/// @param config
/// Optional pointer to an ImageSpec whose metadata contains
/// "configuration hints."
///
/// @param ioproxy
/// Optional pointer to an IOProxy to use (not supported by all
/// formats, see `supports("ioproxy")`). The caller retains
/// ownership of the proxy.
///
/// @returns
/// A `unique_ptr` that will close and free the ImageInput when
/// it exits scope or is reset. The pointer will be empty if the
/// required writer was not able to be created.
static unique_ptr open (const std::string& filename,
const ImageSpec *config = nullptr,
Filesystem::IOProxy* ioproxy = nullptr);
/// Create and open an ImageInput using a UTF-16 encoded wstring filename.
static unique_ptr open (const std::wstring& filename,
const ImageSpec *config = nullptr,
Filesystem::IOProxy* ioproxy = nullptr) {
return open(Strutil::utf16_to_utf8(filename), config, ioproxy);
}
/// Create and return an ImageInput implementation that is able to read
/// the given file or format. If `do_open` is true (and the `filename`