RawSpeed
fast raw decoding library
Loading...
Searching...
No Matches
Cr2Decoder.cpp
Go to the documentation of this file.
1/*
2 RawSpeed - RAW file decoder.
3
4 Copyright (C) 2009-2014 Klaus Post
5 Copyright (C) 2015-2017 Roman Lebedev
6 Copyright (C) 2017 Axel Waggershauser
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21*/
22
23#include "decoders/Cr2Decoder.h"
24#include "MemorySanitizer.h"
25#include "adt/Array1DRef.h"
26#include "adt/Array2DRef.h"
27#include "adt/Bit.h"
28#include "adt/Casts.h"
29#include "adt/Optional.h"
30#include "adt/Point.h"
31#include "common/RawImage.h"
36#include "io/Buffer.h"
37#include "io/ByteStream.h"
38#include "io/Endianness.h"
39#include "metadata/Camera.h"
42#include "tiff/TiffEntry.h"
43#include "tiff/TiffIFD.h"
44#include "tiff/TiffTag.h"
45#include <array>
46#include <cassert>
47#include <cstdint>
48#include <memory>
49#include <string>
50#include <string_view>
51#include <utility>
52#include <vector>
53
54namespace rawspeed {
55class CameraMetaData;
56
58 [[maybe_unused]] Buffer file) {
59 const auto id = rootIFD->getID();
60 const std::string& make = id.make;
61 const std::string& model = id.model;
62
63 // FIXME: magic
64
65 return make == "Canon" ||
66 (make == "Kodak" && (model == "DCS520C" || model == "DCS560C"));
67}
68
70 uint32_t offset = 0;
71 if (mRootIFD->getEntryRecursive(TiffTag::CANON_RAW_DATA_OFFSET)) {
72 offset =
73 mRootIFD->getEntryRecursive(TiffTag::CANON_RAW_DATA_OFFSET)->getU32();
74 } else {
75 // D2000 is oh so special...
76 const auto* ifd = mRootIFD->getIFDWithTag(TiffTag::CFAPATTERN);
77 if (!ifd->hasEntry(TiffTag::STRIPOFFSETS))
78 ThrowRDE("Couldn't find offset");
79
80 offset = ifd->getEntry(TiffTag::STRIPOFFSETS)->getU32();
81 }
82
83 ByteStream b(DataBuffer(mFile.getSubView(offset), Endianness::big));
84 b.skipBytes(41);
85 int height = b.getU16();
86 int width = b.getU16();
87
88 // some old models (1D/1DS/D2000C) encode two lines as one
89 // see: FIX_CANON_HALF_HEIGHT_DOUBLE_WIDTH
90 if (width > 2 * height) {
91 height *= 2;
92 width /= 2;
93 }
94 width *= 2; // components
95
96 mRaw->dim = {width, height};
97
98 const ByteStream bs(DataBuffer(mFile.getSubView(offset), Endianness::little));
99
100 Cr2LJpegDecoder l(bs, mRaw);
101 mRaw->createData();
102
103 Cr2SliceWidths slicing(/*numSlices=*/1, /*sliceWidth=don't care*/ 0,
104 /*lastSliceWidth=*/implicit_cast<uint16_t>(width));
105 l.decode(slicing);
107
108 // deal with D2000 GrayResponseCurve
109 if (const TiffEntry* curve =
110 mRootIFD->getEntryRecursive(static_cast<TiffTag>(0x123));
111 curve && curve->type == TiffDataType::SHORT && curve->count == 4096) {
112 auto table = curve->getU16Array(curve->count);
113 RawImageCurveGuard curveHandler(&mRaw, table, uncorrectedRawValues);
114
115 // Apply table
117 mRaw->sixteenBitLookup();
118 }
119
120 return mRaw;
121}
122
123// for technical details about Cr2 mRAW/sRAW, see http://lclevy.free.fr/cr2/
124
126 const TiffEntry* sensorInfoE =
127 mRootIFD->getEntryRecursive(TiffTag::CANON_SENSOR_INFO);
128 if (!sensorInfoE)
129 ThrowTPE("failed to get SensorInfo from MakerNote");
130
131 assert(sensorInfoE != nullptr);
132
133 if (isSubSampled() != (getSubSampling() != iPoint2D{1, 1}))
134 ThrowTPE("Subsampling sanity check failed");
135
136 mRaw->dim = {sensorInfoE->getU16(1), sensorInfoE->getU16(2)};
137 mRaw->setCpp(1);
138 mRaw->isCFA = !isSubSampled();
139
140 if (isSubSampled()) {
141 iPoint2D& subSampling = mRaw->metadata.subsampling;
142 subSampling = getSubSampling();
143 if (subSampling.x <= 1 && subSampling.y <= 1)
144 ThrowRDE("RAW is expected to be subsampled, but it's not");
145
146 if (mRaw->dim.x % subSampling.x != 0)
147 ThrowRDE("Raw width is not a multiple of horizontal subsampling factor");
148 mRaw->dim.x /= subSampling.x;
149
150 if (mRaw->dim.y % subSampling.y != 0)
151 ThrowRDE("Raw height is not a multiple of vertical subsampling factor");
152 mRaw->dim.y /= subSampling.y;
153
154 mRaw->dim.x *= 2 + subSampling.x * subSampling.y;
155 }
156
157 const TiffIFD* raw = mRootIFD->getSubIFDs()[3].get();
158
159 Cr2SliceWidths slicing;
160 // there are four cases:
161 // * there is a tag with three components,
162 // $ last two components are non-zero: all fine then.
163 // $ first two components are zero, last component is non-zero
164 // we let Cr2LJpegDecoder guess it (it'll throw if fails)
165 // $ else the image is considered corrupt.
166 // * there is a tag with not three components, the image is considered
167 // corrupt. $ there is no tag, we let Cr2LJpegDecoder guess it (it'll throw if
168 // fails)
169 if (const TiffEntry* cr2SliceEntry =
171 cr2SliceEntry) {
172 if (cr2SliceEntry->count != 3) {
173 ThrowRDE("Found RawImageSegmentation tag with %u elements, should be 3.",
174 cr2SliceEntry->count);
175 }
176
177 if (cr2SliceEntry->getU16(1) != 0 && cr2SliceEntry->getU16(2) != 0) {
178 // first component can be either zero or non-zero, don't care
179 slicing = Cr2SliceWidths(/*numSlices=*/1 + cr2SliceEntry->getU16(0),
180 /*sliceWidth=*/cr2SliceEntry->getU16(1),
181 /*lastSliceWidth=*/cr2SliceEntry->getU16(2));
182 } else if (cr2SliceEntry->getU16(0) == 0 && cr2SliceEntry->getU16(1) == 0 &&
183 cr2SliceEntry->getU16(2) != 0) {
184 // PowerShot G16, PowerShot S120, let Cr2LJpegDecoder guess.
185 } else {
186 ThrowRDE("Strange RawImageSegmentation tag: (%d, %d, %d), image corrupt.",
187 cr2SliceEntry->getU16(0), cr2SliceEntry->getU16(1),
188 cr2SliceEntry->getU16(2));
189 }
190 } // EOS 20D, EOS-1D Mark II, let Cr2LJpegDecoder guess.
191
192 const uint32_t offset = raw->getEntry(TiffTag::STRIPOFFSETS)->getU32();
193 const uint32_t count = raw->getEntry(TiffTag::STRIPBYTECOUNTS)->getU32();
194
195 const ByteStream bs(
196 DataBuffer(mFile.getSubView(offset, count), Endianness::little));
197
198 Cr2LJpegDecoder d(bs, mRaw);
199 mRaw->createData();
200 d.decode(slicing);
202
203 assert(getSubSampling() == mRaw->metadata.subsampling);
204
205 if (mRaw->metadata.subsampling.x > 1 || mRaw->metadata.subsampling.y > 1)
207
208 return mRaw;
209}
210
212 if (mRootIFD->getSubIFDs().size() < 4)
213 return decodeOldFormat();
214 else // NOLINT(readability-else-after-return): ok, here it make sense
215 return decodeNewFormat();
216}
217
219 auto id = mRootIFD->getID();
220 // Check for sRaw mode
221 if (isSubSampled()) {
222 checkCameraSupported(meta, id, "sRaw1");
223 return;
224 }
225
226 checkCameraSupported(meta, id, "");
227}
228
229namespace {
230
241
244 // The original ColorData, detect by it's fixed size.
245 if (ccd->count == 582)
246 return {{ColorDataFormat::ColorData1, {}}};
247 // Second incarnation of ColorData, still size-only detection.
248 if (ccd->count == 653)
249 return {{ColorDataFormat::ColorData2, {}}};
250 // From now onwards, Canon has finally added a `version` field, use it.
251 switch (int colorDataVersion = static_cast<int16_t>(ccd->getU16(0));
252 colorDataVersion) {
253 case 1:
254 return {{ColorDataFormat::ColorData3, colorDataVersion}};
255 case 2:
256 case 3:
257 case 4:
258 case 5:
259 case 6:
260 case 7:
261 case 9:
262 return {{ColorDataFormat::ColorData4, colorDataVersion}};
263 case -4:
264 case -3:
265 return {{ColorDataFormat::ColorData5, colorDataVersion}};
266 case 10: {
267 ColorDataFormat f = [count = ccd->count]() {
268 switch (count) {
269 case 1273:
270 case 1275:
272 default:
274 }
275 }();
276 return {{f, colorDataVersion}};
277 }
278 case 11:
279 return {{ColorDataFormat::ColorData7, colorDataVersion}};
280 case 12:
281 case 13:
282 case 14:
283 case 15:
284 return {{ColorDataFormat::ColorData8, colorDataVersion}};
285 default:
286 break;
287 }
288 return std::nullopt;
289}
290
292 switch (f) {
293 using enum ColorDataFormat;
294 case ColorData1:
295 return 50;
296 case ColorData2:
297 return 68;
298 case ColorData3:
299 case ColorData4:
300 case ColorData6:
301 case ColorData7:
302 case ColorData8:
303 return 126;
304 case ColorData5:
305 return 142;
306 }
307 __builtin_unreachable();
308}
309
310[[nodiscard]] Optional<std::pair<int, int>>
312 Optional<int> colorDataVersion) {
313 switch (f) {
314 using enum ColorDataFormat;
315 case ColorData1:
316 case ColorData2:
317 case ColorData3:
318 // These seemingly did not contain `SpecularWhiteLevel` yet.
319 return std::nullopt;
320 case ColorData4:
321 switch (*colorDataVersion) {
322 case 2:
323 case 3:
324 return std::nullopt; // Still no `SpecularWhiteLevel`.
325 case 4:
326 case 5:
327 return {{692, 697}};
328 case 6:
329 case 7:
330 return {{715, 720}};
331 case 9:
332 return {{719, 724}};
333 default:
334 __builtin_unreachable();
335 }
336 case ColorData5:
337 switch (*colorDataVersion) {
338 case -4:
339 return {{333, 1386}};
340 case -3:
341 return {{264, 662}};
342 default:
343 __builtin_unreachable();
344 }
345 case ColorData6:
346 switch (*colorDataVersion) {
347 case 10:
348 return {{479, 484}};
349 default:
350 __builtin_unreachable();
351 }
352 case ColorData7:
353 switch (*colorDataVersion) {
354 case 10:
355 return {{504, 509}};
356 case 11:
357 return {{728, 733}};
358 default:
359 __builtin_unreachable();
360 }
361 case ColorData8:
362 switch (*colorDataVersion) {
363 case 12:
364 case 13:
365 case 15:
366 return {{778, 783}};
367 case 14:
368 return {{556, 561}};
369 default:
370 __builtin_unreachable();
371 }
372 }
373 __builtin_unreachable();
374}
375
377 Optional<int> colorDataVersion) {
378 return f != ColorDataFormat::ColorData5 || colorDataVersion != -3;
379}
380
381} // namespace
382
384 const TiffEntry* wb = mRootIFD->getEntryRecursive(TiffTag::CANONCOLORDATA);
385 if (!wb)
386 return false;
387
388 auto dsc = deduceColorDataFormat(wb);
389 if (!dsc)
390 return false;
391
392 auto [f, ver] = *dsc;
393
394 int offset = getWhiteBalanceOffsetInColorData(f);
395
396 offset /= 2;
397
398 std::array<float, 4> wbCoeffs = {};
399 wbCoeffs[0] = static_cast<float>(wb->getU16(offset + 0));
400 wbCoeffs[1] = static_cast<float>(wb->getU16(offset + 1));
401 wbCoeffs[2] = static_cast<float>(wb->getU16(offset + 3));
402 mRaw->metadata.wbCoeffs = wbCoeffs;
403
404 auto levelOffsets = getBlackAndWhiteLevelOffsetsInColorData(f, ver);
405 if (!levelOffsets)
406 return false;
407
408 mRaw->whitePoint = wb->getU16(levelOffsets->second);
409
410 mRaw->blackLevelSeparate =
411 Array2DRef(mRaw->blackLevelSeparateStorage.data(), 2, 2);
412 auto blackLevelSeparate1D = *mRaw->blackLevelSeparate->getAsArray1DRef();
413 for (int c = 0; c != 4; ++c)
414 blackLevelSeparate1D(c) = wb->getU16(c + levelOffsets->first);
415
416 // In Canon MakerNotes, the levels are always unscaled, and are 14-bit,
417 // and so if the LJpeg precision was lower, we need to adjust.
418 constexpr int makernotesPrecision = 14;
419 if (makernotesPrecision > ljpegSamplePrecision) {
420 int bitDepthDiff = makernotesPrecision - ljpegSamplePrecision;
421 assert(bitDepthDiff >= 1 && bitDepthDiff <= 12);
422 if (shouldRescaleBlackLevels(f, ver)) {
423 for (int c = 0; c != 4; ++c)
424 blackLevelSeparate1D(c) >>= bitDepthDiff;
425 }
426 mRaw->whitePoint = *mRaw->whitePoint >> bitDepthDiff;
427 }
428
429 return true;
430}
431
433 // Default white point is LJpeg sample precision.
434 mRaw->whitePoint = (1U << ljpegSamplePrecision) - 1;
435
437 return;
438
439 if (mRootIFD->hasEntryRecursive(TiffTag::CANONSHOTINFO) &&
440 mRootIFD->hasEntryRecursive(TiffTag::CANONPOWERSHOTG9WB)) {
441 const TiffEntry* shot_info =
442 mRootIFD->getEntryRecursive(TiffTag::CANONSHOTINFO);
443 const TiffEntry* g9_wb =
444 mRootIFD->getEntryRecursive(TiffTag::CANONPOWERSHOTG9WB);
445
446 uint16_t wb_index = shot_info->getU16(7);
447 int wb_offset = (wb_index < 18)
448 ? std::string_view("012347800000005896")[wb_index] - '0'
449 : 0;
450 wb_offset = wb_offset * 8 + 2;
451
452 std::array<float, 4> wbCoeffs = {};
453 wbCoeffs[0] = static_cast<float>(g9_wb->getU32(wb_offset + 1));
454 wbCoeffs[1] = (static_cast<float>(g9_wb->getU32(wb_offset + 0)) +
455 static_cast<float>(g9_wb->getU32(wb_offset + 3))) /
456 2.0F;
457 wbCoeffs[2] = static_cast<float>(g9_wb->getU32(wb_offset + 2));
458 mRaw->metadata.wbCoeffs = wbCoeffs;
459 } else if (mRootIFD->hasEntryRecursive(static_cast<TiffTag>(0xa4))) {
460 // WB for the old 1D and 1DS
461 const TiffEntry* wb =
462 mRootIFD->getEntryRecursive(static_cast<TiffTag>(0xa4));
463 if (wb->count >= 3) {
464 std::array<float, 4> wbCoeffs = {};
465 wbCoeffs[0] = wb->getFloat(0);
466 wbCoeffs[1] = wb->getFloat(1);
467 wbCoeffs[2] = wb->getFloat(2);
468 mRaw->metadata.wbCoeffs = wbCoeffs;
469 }
470 }
471}
472
474 int iso = 0;
475 mRaw->cfa.setCFA(iPoint2D(2, 2), CFAColor::RED, CFAColor::GREEN,
477
478 std::string mode;
479
480 if (mRaw->metadata.subsampling.y == 2 && mRaw->metadata.subsampling.x == 2)
481 mode = "sRaw1";
482
483 if (mRaw->metadata.subsampling.y == 1 && mRaw->metadata.subsampling.x == 2)
484 mode = "sRaw2";
485
486 if (mRootIFD->hasEntryRecursive(TiffTag::ISOSPEEDRATINGS))
487 iso = mRootIFD->getEntryRecursive(TiffTag::ISOSPEEDRATINGS)->getU32();
488 if (65535 == iso) {
489 // ISOSPEEDRATINGS is a SHORT EXIF value. For larger values, we have to look
490 // at RECOMMENDEDEXPOSUREINDEX (maybe Canon specific).
491 if (mRootIFD->hasEntryRecursive(TiffTag::RECOMMENDEDEXPOSUREINDEX))
492 iso = mRootIFD->getEntryRecursive(TiffTag::RECOMMENDEDEXPOSUREINDEX)
493 ->getU32();
494 }
495
496 // Fetch the white balance
497 try {
499 } catch (const RawspeedException& e) {
500 mRaw->setError(e.what());
501 // We caught an exception reading WB, just ignore it
502 }
503 setMetaData(meta, mode, iso);
506 mRaw->blackLevel = 0;
507 mRaw->blackLevelSeparate = std::nullopt;
508 }
509 if (mShiftUpScaleForExif != 0 && isPowerOfTwo(1 + *mRaw->whitePoint))
510 mRaw->whitePoint = ((1 + *mRaw->whitePoint) << mShiftUpScaleForExif) - 1;
511 else
512 mRaw->whitePoint = *mRaw->whitePoint << mShiftUpScaleForExif;
513}
514
516 if (mRootIFD->getSubIFDs().size() != 4)
517 return false;
518 const TiffEntry* typeE =
519 mRootIFD->getSubIFDs()[3]->getEntryRecursive(TiffTag::CANON_SRAWTYPE);
520 return typeE && typeE->getU32() == 4;
521}
522
524 const TiffEntry* CCS =
525 mRootIFD->getEntryRecursive(TiffTag::CANON_CAMERA_SETTINGS);
526 if (!CCS)
527 ThrowRDE("CanonCameraSettings entry not found.");
528
529 if (CCS->type != TiffDataType::SHORT)
530 ThrowRDE("Unexpected CanonCameraSettings entry type encountered ");
531
532 if (CCS->count < 47)
533 return {1, 1};
534
535 switch (uint16_t qual = CCS->getU16(46)) {
536 case 0:
537 return {1, 1};
538 case 1:
539 return {2, 2};
540 case 2:
541 return {2, 1};
542 default:
543 ThrowRDE("Unexpected SRAWQuality value found: %u", qual);
544 }
545}
546
548 if (hints.contains("old_sraw_hue"))
549 return (mRaw->metadata.subsampling.y * mRaw->metadata.subsampling.x);
550
551 if (!mRootIFD->hasEntryRecursive(static_cast<TiffTag>(0x10))) {
552 return 0;
553 }
554 if (uint32_t model_id =
555 mRootIFD->getEntryRecursive(static_cast<TiffTag>(0x10))->getU32();
556 model_id >= 0x80000281 || model_id == 0x80000218 ||
557 (hints.contains("force_new_sraw_hue"))) {
558 return ((mRaw->metadata.subsampling.y * mRaw->metadata.subsampling.x) -
559 1) >>
560 1;
561 }
562
563 return (mRaw->metadata.subsampling.y * mRaw->metadata.subsampling.x);
564}
565
566// Interpolate and convert sRaw data.
568 const TiffEntry* wb = mRootIFD->getEntryRecursive(TiffTag::CANONCOLORDATA);
569 if (!wb)
570 ThrowRDE("Unable to locate WB info.");
571
572 // Offset to sRaw coefficients used to reconstruct uncorrected RGB data.
573 uint32_t offset = 78;
574
575 std::array<int, 3> sraw_coeffs;
576
577 assert(wb != nullptr);
578 sraw_coeffs[0] = wb->getU16(offset + 0);
579 sraw_coeffs[1] = (wb->getU16(offset + 1) + wb->getU16(offset + 2) + 1) >> 1;
580 sraw_coeffs[2] = wb->getU16(offset + 3);
581
582 if (hints.contains("invert_sraw_wb")) {
583 sraw_coeffs[0] = static_cast<int>(
584 1024.0F / (static_cast<float>(sraw_coeffs[0]) / 1024.0F));
585 sraw_coeffs[2] = static_cast<int>(
586 1024.0F / (static_cast<float>(sraw_coeffs[2]) / 1024.0F));
587 }
588
589 MSan::CheckMemIsInitialized(mRaw->getByteDataAsUncroppedArray2DRef());
590 RawImage subsampledRaw = mRaw;
591 int hue = getHue();
592
593 iPoint2D interpolatedDims = {
594 subsampledRaw->metadata.subsampling.x *
595 (subsampledRaw->dim.x /
596 (2 + subsampledRaw->metadata.subsampling.x *
597 subsampledRaw->metadata.subsampling.y)),
598 subsampledRaw->metadata.subsampling.y * subsampledRaw->dim.y};
599
600 mRaw = RawImage::create(interpolatedDims, RawImageType::UINT16, 3);
601 mRaw->metadata.subsampling = subsampledRaw->metadata.subsampling;
602 mRaw->isCFA = false;
603
605 sraw_coeffs, hue);
606
607 /* Determine sRaw coefficients */
608 bool isOldSraw = hints.contains("sraw_40d");
609 bool isNewSraw = hints.contains("sraw_new");
610
611 int version;
612 if (isOldSraw)
613 version = 0;
614 else {
615 if (isNewSraw) {
616 version = 2;
617 } else {
618 version = 1;
619 }
620 }
621
622 i.interpolate(version);
623
625}
626
627} // namespace rawspeed
#define ThrowRDE(...)
#define ThrowTPE(...)
assert(dim.area() >=area)
bool checkCameraSupported(const CameraMetaData *meta, const TiffID &id, const std::string &mode)
void setMetaData(const CameraMetaData *meta, const TiffID &id, const std::string &mode, int iso_speed)
void skipBytes(size_type nbytes)
Definition ByteStream.h:130
bool isSubSampled() const
RawImage decodeRawInternal() override
void checkSupportInternal(const CameraMetaData *meta) override
void parseWhiteBalance() const
RawImage decodeOldFormat()
static bool isAppropriateDecoder(const TiffRootIFD *rootIFD, Buffer file)
RawImage decodeNewFormat()
bool decodeCanonColorData() const
void decodeMetaDataInternal(const CameraMetaData *meta) override
iPoint2D getSubSampling() const
void decode(const Cr2SliceWidths &slicing)
Array2DRef< uint16_t > getU16DataAsUncroppedArray2DRef() noexcept
Definition RawImage.h:290
ImageMetaData metadata
Definition RawImage.h:184
static RawImage create(RawImageType type=RawImageType::UINT16)
Definition RawImage.h:265
Definition TiffEntry.h:62
float getFloat(uint32_t index=0) const
uint32_t getU32(uint32_t index=0) const
uint32_t count
Definition TiffEntry.h:84
uint16_t getU16(uint32_t index=0) const
TiffDataType type
Definition TiffEntry.h:83
TiffEntry *RAWSPEED_READONLY getEntryRecursive(TiffTag tag) const
Definition TiffIFD.cpp:246
TiffEntry * getEntry(TiffTag tag) const
Definition TiffIFD.cpp:313
TiffID getID() const
Definition TiffIFD.cpp:325
value_type x
Definition Point.h:102
value_type y
Definition Point.h:103
Optional< std::pair< int, int > > getBlackAndWhiteLevelOffsetsInColorData(ColorDataFormat f, Optional< int > colorDataVersion)
bool shouldRescaleBlackLevels(ColorDataFormat f, Optional< int > colorDataVersion)
int getWhiteBalanceOffsetInColorData(ColorDataFormat f)
Optional< std::pair< ColorDataFormat, Optional< int > > > deduceColorDataFormat(const TiffEntry *ccd)
constexpr RAWSPEED_READNONE Ttgt implicit_cast(Tsrc value)
Definition Casts.h:32
constexpr bool RAWSPEED_READNONE isPowerOfTwo(T val)
Definition Bit.h:38
Array2DRef(Array1DRef< T > data, int width, int height, int pitch) -> Array2DRef< T >
static void CheckMemIsInitialized(const void *addr, size_t size)
std::string make
Definition TiffIFD.h:134