RawSpeed
fast raw decoding library
Loading...
Searching...
No Matches
rstest.cpp
Go to the documentation of this file.
1/*
2 RawSpeed - RAW file decoder.
3
4 Copyright (C) 2017 Axel Waggershauser
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#include "RawSpeed-API.h"
22#include "adt/Array1DRef.h"
23#include "adt/Array2DRef.h"
24#include "adt/Casts.h"
25#include "adt/NotARational.h"
26#include "io/FileIOException.h"
27#include "md5.h"
28#include <array>
29#include <bit>
30#include <cassert>
31#include <chrono>
32#include <cstdarg>
33#include <cstddef>
34#include <cstdint>
35#include <cstdio>
36#include <cstdlib>
37#include <fstream>
38#include <functional>
39#include <iostream>
40#include <iterator>
41#include <map>
42#include <memory>
43#include <sstream>
44#include <string>
45#include <string_view>
46#include <type_traits>
47#include <vector>
48
49#if !defined(_WIN32) && \
50 !(__has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__))
51#include "io/MMapReader.h"
52#else
55#include <tuple>
56#endif
57
58#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
59#include <iomanip>
60#endif
61
62using std::chrono::steady_clock;
63
73using std::cerr;
74using std::cout;
75using std::ifstream;
76using std::istreambuf_iterator;
77using std::map;
78using std::ofstream;
79using std::ostringstream;
80using std::vector;
81
82#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
83using std::internal;
84using std::left;
85using std::setw;
86#endif
87
89
90namespace {
91
92struct options final {
93 bool create;
94 bool force;
95 bool dump;
96};
97
99 void anchor() const override;
100
101public:
102 int64_t time;
103
104 explicit RAWSPEED_UNLIKELY_FUNCTION RAWSPEED_NOINLINE
105 RstestHashMismatch(const char* msg, int64_t time_)
106 : RawspeedException(msg), time(time_) {}
107};
108
110 // Empty out-of-line definition for the purpose of anchoring
111 // the class's vtable to this Translational Unit.
112}
113
114struct Timer final {
115 mutable std::chrono::steady_clock::time_point start =
116 std::chrono::steady_clock::now();
117 int64_t operator()() const {
118 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
119 std::chrono::steady_clock::now() - start)
120 .count();
121 start = std::chrono::steady_clock::now();
122 return ms;
123 }
124};
125
126// yes, this is not cool. but i see no way to compute the hash of the
127// full image, without duplicating image, and copying excluding padding
131
132 vector<md5::MD5Hasher::state_type> line_hashes(img.height());
133
134 for (int j = 0; j < img.height(); j++) {
135 line_hashes[j] = md5::md5_hash(reinterpret_cast<const uint8_t*>(&img(j, 0)),
136 img.width());
137 }
138
139 auto ret = md5::md5_hash(reinterpret_cast<const uint8_t*>(&line_hashes[0]),
140 sizeof(line_hashes[0]) * line_hashes.size());
141
142 return ret;
143}
144
145#pragma GCC diagnostic push
146#pragma GCC diagnostic ignored "-Wpragmas"
147#pragma GCC diagnostic ignored "-Wunknown-warning-option"
148#pragma GCC diagnostic ignored "-Wunknown-pragmas"
149#pragma GCC diagnostic ignored "-Wframe-larger-than="
150#pragma GCC diagnostic ignored "-Wstack-usage="
151
152void __attribute__((format(printf, 2, 3))) APPEND(ostringstream* oss,
153 const char* format, ...) {
154 std::array<char, 1024> line;
155
156 va_list args;
158 vsnprintf(line.data(), sizeof(line), format, args);
160
161 *oss << line.data();
162}
163
164std::string img_hash(const RawImage& r, bool noSamples) {
165 ostringstream oss;
166
167 if (noSamples)
168 APPEND(&oss, "camera support status is unknown due to lack of samples\n");
169 APPEND(&oss, "make: %s\n", r->metadata.make.c_str());
170 APPEND(&oss, "model: %s\n", r->metadata.model.c_str());
171 APPEND(&oss, "mode: %s\n", r->metadata.mode.c_str());
172
173 APPEND(&oss, "canonical_make: %s\n", r->metadata.canonical_make.c_str());
174 APPEND(&oss, "canonical_model: %s\n", r->metadata.canonical_model.c_str());
175 APPEND(&oss, "canonical_alias: %s\n", r->metadata.canonical_alias.c_str());
176 APPEND(&oss, "canonical_id: %s\n", r->metadata.canonical_id.c_str());
177
178 APPEND(&oss, "isoSpeed: %d\n", r->metadata.isoSpeed);
179 APPEND(&oss, "blackLevel: %d\n", r->blackLevel);
180
181 APPEND(&oss, "whitePoint: ");
182 if (!r->whitePoint)
183 APPEND(&oss, "unknown");
184 else
185 APPEND(&oss, "%d", *r->whitePoint);
186 APPEND(&oss, "\n");
187
188 APPEND(&oss, "blackLevelSeparate: ");
189 if (!r->blackLevelSeparate) {
190 APPEND(&oss, "none");
191 } else {
192 APPEND(&oss, "(%i x %i)", r->blackLevelSeparate->width(),
193 r->blackLevelSeparate->height());
194 if (auto blackLevelSeparate1D = r->blackLevelSeparate->getAsArray1DRef();
195 blackLevelSeparate1D && blackLevelSeparate1D->size() != 0) {
196 for (auto l : *blackLevelSeparate1D)
197 APPEND(&oss, " %d", l);
198 }
199 }
200 APPEND(&oss, "\n");
201
202 APPEND(&oss, "wbCoeffs:");
203 if (!r->metadata.wbCoeffs)
204 APPEND(&oss, " (none)");
205 else {
206 for (const auto& e : *r->metadata.wbCoeffs)
207 APPEND(&oss, " %f", implicit_cast<double>(e));
208 }
209 APPEND(&oss, "\n");
210
211 APPEND(&oss, "colorMatrix:");
212 if (r->metadata.colorMatrix.empty())
213 APPEND(&oss, " (none)");
214 else {
215 for (const NotARational<int>& e : r->metadata.colorMatrix)
216 APPEND(&oss, " %i/%i", e.num, e.den);
217 }
218 APPEND(&oss, "\n");
219
220 APPEND(&oss, "isCFA: %d\n", r->isCFA);
221 APPEND(&oss, "cfa: %s\n", r->cfa.asString().c_str());
222 APPEND(&oss, "filters: 0x%x\n", r->cfa.getDcrawFilter());
223 APPEND(&oss, "bpp: %u\n", r->getBpp());
224 APPEND(&oss, "cpp: %u\n", r->getCpp());
225 APPEND(&oss, "dataType: %u\n", static_cast<unsigned>(r->getDataType()));
226
227 const iPoint2D dimUncropped = r->getUncroppedDim();
228 APPEND(&oss, "dimUncropped: %dx%d\n", dimUncropped.x, dimUncropped.y);
229 APPEND(&oss, "dimCropped: %dx%d\n", r->dim.x, r->dim.y);
231 APPEND(&oss, "cropOffset: %dx%d\n", cropTL.x, cropTL.y);
232
233 // NOTE: pitch is internal property, a function of dimUncropped.x, bpp and
234 // some additional padding overhead, to align each line length to be a
235 // multiple of (currently) 16 bytes. And maybe with some additional
236 // const offset. there is no point in showing it here, it may differ.
237 // APPEND(&oss, "pitch: %d\n", r->pitch);
238
239 APPEND(&oss, "blackAreas: ");
240 for (auto ba : r->blackAreas)
241 APPEND(&oss, "%d:%ux%u, ", ba.isVertical, ba.offset, ba.size);
242 APPEND(&oss, "\n");
243
244 APPEND(&oss, "fuji_rotation_pos: %u\n", r->metadata.fujiRotationPos);
245 APPEND(&oss, "pixel_aspect_ratio: %f\n", r->metadata.pixelAspectRatio);
246
247 APPEND(&oss, "badPixelPositions: ");
248 {
249 MutexLocker guard(&r->mBadPixelMutex);
250 for (uint32_t p : r->mBadPixelPositions)
251 APPEND(&oss, "%u, ", p);
252 }
253
254 APPEND(&oss, "\n");
255
257 APPEND(&oss, "md5sum of per-line md5sums: %s\n",
259
260 for (const auto errors = r->getErrors(); const std::string& e : errors)
261 APPEND(&oss, "WARNING: [rawspeed] %s\n", e.c_str());
262
263 return oss.str();
264}
265
266auto fclose = [](std::FILE* fp) { std::fclose(fp); };
267using file_ptr = std::unique_ptr<FILE, decltype(fclose)>;
268
269void writePPM(const RawImage& raw, const std::string& fn) {
270 file_ptr f(fopen((fn + ".ppm").c_str(), "wb"), fclose);
271
272 const iPoint2D dimUncropped = raw->getUncroppedDim();
273 int width = dimUncropped.x;
274 int height = dimUncropped.y;
275 std::string format = raw->getCpp() == 1 ? "P5" : "P6";
276
277 // Write PPM header
278 fprintf(f.get(), "%s\n%d %d\n65535\n", format.c_str(), width, height);
279 if (ferror(f.get()))
280 ThrowFIE("Could not write file");
281
282 width *= raw->getCpp();
283
284 // Write pixels
286 for (int y = 0; y < height; ++y) {
287 // PPM is big-endian
288 for (int x = 0; x < width; ++x)
289 img(y, x) = getU16BE(&img(y, x));
290
291 fwrite(&img(y, 0), sizeof(decltype(img)::value_type), width, f.get());
292 if (ferror(f.get()))
293 ThrowFIE("Could not write file");
294 }
295}
296
297void writePFM(const RawImage& raw, const std::string& fn) {
298 file_ptr f(fopen((fn + ".pfm").c_str(), "wb"), fclose);
299
300 const iPoint2D dimUncropped = raw->getUncroppedDim();
301 int width = dimUncropped.x;
302 int height = dimUncropped.y;
303 std::string format = raw->getCpp() == 1 ? "Pf" : "PF";
304
305 // Write PFM header. if scale < 0, it is little-endian, if >= 0 - big-endian
306 int len = fprintf(f.get(), "%s\n%d %d\n-1.0", format.c_str(), width, height);
307 if (ferror(f.get()))
308 ThrowFIE("Could not write file");
309
310 // make sure that data starts at aligned offset. for sse
311 static const auto dataAlignment = 16;
312
313 // regardless of padding, we need to write \n separator
314 const int realLen = len + 1;
315 // the first byte after that \n will be aligned
316 const auto paddedLen =
317 rawspeed::implicit_cast<int>(roundUp(realLen, dataAlignment));
318 assert(paddedLen > len);
319 assert(rawspeed::isAligned(paddedLen, dataAlignment));
320
321 // how much padding?
322 const int padding = paddedLen - realLen;
323 assert(padding >= 0);
324 assert(rawspeed::isAligned(realLen + padding, dataAlignment));
325
326 // and actually write padding + new line
327 len += fprintf(f.get(), "%0*i\n", padding, 0);
328 if (ferror(f.get()))
329 ThrowFIE("Could not write file");
330 assert(paddedLen == len);
331
332 // did we write a multiple of an alignment value?
333 assert(rawspeed::isAligned(len, dataAlignment));
334 assert(ftell(f.get()) == len);
335 assert(rawspeed::isAligned(ftell(f.get()), dataAlignment));
336
337 width *= raw->getCpp();
338
339 // Write pixels
341 for (int y = 0; y < height; ++y) {
342 // NOTE: pfm has rows in reverse order
343 const int row_in = height - 1 - y;
344
345 // PFM can have any endianness, let's write little-endian
346 for (int x = 0; x < width; ++x)
347 img(row_in, x) = std::bit_cast<float>(getU32LE(&img(row_in, x)));
348
349 fwrite(&img(row_in, 0), sizeof(decltype(img)::value_type), width, f.get());
350 if (ferror(f.get()))
351 ThrowFIE("Could not write file");
352 }
353}
354
355void writeImage(const RawImage& raw, const std::string& fn) {
356 switch (raw->getDataType()) {
358 writePPM(raw, fn);
359 return;
361 writePFM(raw, fn);
362 return;
363 }
364 __builtin_unreachable();
365}
366
367int64_t process(const std::string& filename, const CameraMetaData* metadata,
368 const options& o) {
369
370 const std::string hashfile(filename + ".hash");
371
372 // if creating hash and hash exists -> skip current file
373 // if not creating and hash is missing -> skip as well
374 // unless in force mode
375 ifstream hf(hashfile);
376 if (hf.good() == o.create && !o.force) {
377#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
378#ifdef HAVE_OPENMP
379#pragma omp critical(io)
380#endif
381 cout << left << setw(55) << filename << ": hash "
382 << (o.create ? "exists" : "missing") << ", skipping" << '\n';
383#endif
384 return 0;
385 }
386
387// to narrow down the list of files that could have causes the crash
388#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
389#ifdef HAVE_OPENMP
390#pragma omp critical(io)
391#endif
392 cout << left << setw(55) << filename << ": starting decoding ... " << '\n';
393#endif
394
395#if !defined(_WIN32) && \
396 !(__has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__))
397 MMapReader reader(filename);
398
400#else
401 FileReader reader(filename.c_str());
402
403 std::unique_ptr<std::vector<
406 storage;
408 std::tie(storage, buf) = reader.readFile();
409#endif
410
411 Timer t;
412
413 RawParser parser(buf);
414 auto decoder(parser.getDecoder(metadata));
415 // RawDecoder* decoder = parseRaw( map );
416
417 decoder->failOnUnknown = false;
418 decoder->checkSupport(metadata);
419 bool noSamples = decoder->noSamples;
420
421 decoder->decodeRaw();
422 decoder->decodeMetaData(metadata);
423 RawImage raw = decoder->mRaw;
424 // RawImage raw = decoder->decode();
425
426 auto time = t();
427#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
428#ifdef HAVE_OPENMP
429#pragma omp critical(io)
430#endif
431 cout << left << setw(55) << filename << ": " << internal << setw(3)
432 << buf.getSize() / 1000000 << " MB / " << setw(4) << time << " ms"
433 << '\n';
434#endif
435
436 if (o.create) {
437 // write the hash. if force is set, then we are potentially overwriting here
438 ofstream f(hashfile);
439 f << img_hash(raw, noSamples);
440 if (o.dump)
441 writeImage(raw, filename);
442 } else {
443 // do generate the hash string regardless.
444 std::string h = img_hash(raw, noSamples);
445
446 // normally, here we would compare the old hash with the new one
447 // but if the force is set, and the hash does not exist, do nothing.
448 if (!hf.good() && o.force)
449 return time;
450
451 std::string truth((istreambuf_iterator<char>(hf)),
452 istreambuf_iterator<char>());
453 if (h != truth) {
454 ofstream f(filename + ".hash.failed");
455 f << h;
456 if (o.dump)
457 writeImage(raw, filename + ".failed");
458 throw RstestHashMismatch("hash/metadata mismatch", time);
459 }
460 }
461
462 return time;
463}
464
465#pragma GCC diagnostic pop
466
467int results(const map<std::string, std::string, std::less<>>& failedTests,
468 const options& o) {
469 if (failedTests.empty()) {
470 cout << "All good, ";
471 if (!o.create)
472 cout << "no tests failed!" << '\n';
473 else
474 cout << "all hashes created!" << '\n';
475 return 0;
476 }
477
478 cerr << "WARNING: the following " << failedTests.size()
479 << " tests have failed:\n";
480
481 bool rstestlog = false;
482 for (const auto& [test, msg] : failedTests) {
483 cerr << msg << "\n";
484#ifndef WIN32
485 const std::string oldhash(test + ".hash");
486 const std::string newhash(oldhash + ".failed");
487
488 // if neither hashes exist, nothing to append...
489 if (!(ifstream(oldhash).good() || ifstream(newhash).good()))
490 continue;
491
492 rstestlog = true;
493
494 // DIFF(1): -N, --new-file treat absent files as empty
495 std::string cmd(R"(diff -N -u0 ")");
496 cmd += oldhash;
497 cmd += R"(" ")";
498 cmd += newhash;
499 cmd += R"(" >> rstest.log)";
500 // NOLINTNEXTLINE(concurrency-mt-unsafe): we are a single thread here only.
501 if (system(cmd.c_str())) {
502 }
503 }
504#endif
505
506 if (rstestlog)
507 cerr << "See rstest.log for details.\n";
508
509 return 1;
510}
511
512int usage(const char* progname) {
513 cout << "usage: " << progname << R"(
514 [-h] print this help
515 [-c] for each file: decode, compute hash and store it.
516 If hash exists, it does not recompute it, unless option -f is set!
517 [-f] if -c is set, then it will final the existing hashes.
518 If -c is not set, and the hash does not exist, then just decode,
519 but do not write the hash!
520 [-d] store decoded image as PPM
521 <FILE[S]> the file[s] to work on.
522
523 With no options given, each raw with an accompanying hash will be decoded
524 and compared (unless option -f is set!) to the existing hash. A summary of
525 all errors/failed hash comparisons will be reported at the end.
526
527 Suggested workflow for easy regression testing:
528 1. remove all .hash files and build 'trusted' version of this program
529 2. run with option '-c' -> creates .hash for all supported files
530 3. build new version to test for regressions
531 4. run with no option -> checks files with existing .hash
532 If the second run shows no errors, you have no regressions,
533 otherwise, the diff between hashes is appended to rstest.log
534)";
535 return 0;
536}
537
538} // namespace
539
540} // namespace rawspeed::rstest
541
542using rawspeed::rstest::options;
543using rawspeed::rstest::process;
544using rawspeed::rstest::results;
545using rawspeed::rstest::usage;
546
547int main(int argc_, char** argv_) {
548 auto argv = rawspeed::Array1DRef(argv_, argc_);
549
550 int remaining_argc = argv.size();
551
552 auto hasFlag = [&remaining_argc, argv](std::string_view flag) {
553 bool found = false;
554 for (int i = 1; i < argv.size(); ++i) {
555 if (!argv(i) || argv(i) != flag)
556 continue;
557 found = true;
558 argv(i) = nullptr;
559 remaining_argc--;
560 }
561 return found;
562 };
563
564 if (1 == argv.size() || hasFlag("-h"))
565 return usage(argv(0));
566
567 options o;
568 o.create = hasFlag("-c");
569 o.force = hasFlag("-f");
570 o.dump = hasFlag("-d");
571
572#ifdef HAVE_PUGIXML
573 const CameraMetaData metadata(RAWSPEED_SOURCE_DIR "/data/cameras.xml");
574#else
575 const CameraMetaData metadata{};
576#endif
577
578 int64_t time = 0;
579 map<std::string, std::string, std::less<>> failedTests;
580#ifdef HAVE_OPENMP
581#pragma omp parallel for default(none) firstprivate(argv, o) shared(metadata) \
582 shared(cerr, failedTests) schedule(dynamic, 1) \
583 reduction(+ : time) if (remaining_argc > 2)
584#endif
585 for (int i = 1; i < argv.size(); ++i) {
586 if (!argv(i))
587 continue;
588
589 try {
590 try {
591 time += process(argv(i), &metadata, o);
592 } catch (const rawspeed::rstest::RstestHashMismatch& e) {
593 time += e.time;
594 throw;
595 }
596 } catch (const RawspeedException& e) {
597#ifdef HAVE_OPENMP
598#pragma omp critical(io)
599#endif
600 {
601 std::string msg = std::string(argv(i)) + " failed: " + e.what();
602#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
603 cerr << msg << '\n';
604#endif
605 failedTests.try_emplace(argv(i), msg);
606 }
607 } catch (...) {
608 // We should not get any other exception type here.
609 __builtin_unreachable();
610 }
611 }
612
613 cout << "Total decoding time: "
614 << rawspeed::implicit_cast<double>(time) / 1000.0 << "s" << '\n'
615 << '\n';
616
617 return results(failedTests, o);
618}
#define ThrowFIE(...)
assert(dim.area() >=area)
dim y
Definition Common.cpp:51
dim x
Definition Common.cpp:50
int RAWSPEED_READONLY height() const
int RAWSPEED_READONLY width() const
std::vector< std::string > && getErrors() REQUIRES(!mutex)
Definition ErrorLog.cpp:47
std::string canonical_alias
Definition RawImage.h:104
Optional< std::array< float, 4 > > wbCoeffs
Definition RawImage.h:86
std::string canonical_model
Definition RawImage.h:103
std::string canonical_make
Definition RawImage.h:102
std::string canonical_id
Definition RawImage.h:105
std::vector< NotARational< int > > colorMatrix
Definition RawImage.h:91
Buffer getAsBuffer() const
rawspeed::RawImageType getDataType() const
Definition RawImage.h:125
uint32_t RAWSPEED_READONLY getCpp() const
Definition RawImage.h:118
Optional< Array2DRef< int > > blackLevelSeparate
Definition RawImage.h:165
iPoint2D RAWSPEED_READONLY getCropOffset() const
Definition RawImage.cpp:171
iPoint2D RAWSPEED_READONLY getUncroppedDim() const
Definition RawImage.cpp:167
Optional< int > whitePoint
Definition RawImage.h:172
Array2DRef< std::byte > getByteDataAsUncroppedArray2DRef() noexcept
Definition RawImage.h:330
Array2DRef< float > getF32DataAsUncroppedArray2DRef() noexcept
Definition RawImage.h:310
std::vector< BlackArea > blackAreas
Definition RawImage.h:174
Array2DRef< uint16_t > getU16DataAsUncroppedArray2DRef() noexcept
Definition RawImage.h:290
uint32_t RAWSPEED_READONLY getBpp() const
Definition RawImage.h:119
ImageMetaData metadata
Definition RawImage.h:184
ColorFilterArray cfa
Definition RawImage.h:162
virtual std::unique_ptr< RawDecoder > getDecoder(const CameraMetaData *meta=nullptr)
Definition RawParser.cpp:42
RAWSPEED_UNLIKELY_FUNCTION RAWSPEED_NOINLINE RawspeedException(const char *msg)
value_type x
Definition Point.h:102
value_type y
Definition Point.h:103
std::array< uint32_t, 4 > state_type
Definition md5.h:52
RAWSPEED_UNLIKELY_FUNCTION RAWSPEED_NOINLINE RstestHashMismatch(const char *msg, int64_t time_)
Definition rstest.cpp:105
int main()
static const rawspeed::CameraMetaData metadata
Definition main.cpp:54
__attribute__((always_inline)) inline void MD5 std::string hash_to_string(const MD5Hasher::state_type &hash) noexcept
int results(const map< std::string, std::string, std::less<> > &failedTests, const options &o)
Definition rstest.cpp:467
void writeImage(const RawImage &raw, const std::string &fn)
Definition rstest.cpp:355
int64_t process(const std::string &filename, const CameraMetaData *metadata, const options &o)
Definition rstest.cpp:367
md5::MD5Hasher::state_type imgDataHash(const RawImage &raw)
Definition rstest.cpp:128
std::unique_ptr< FILE, decltype(fclose)> file_ptr
Definition rstest.cpp:267
void writePFM(const RawImage &raw, const std::string &fn)
Definition rstest.cpp:297
rawspeed::md5::MD5Hasher::state_type hash_of_line_hashes
Definition rstest.cpp:256
void writePPM(const RawImage &raw, const std::string &fn)
Definition rstest.cpp:269
constexpr uint64_t RAWSPEED_READNONE roundUp(uint64_t value, uint64_t multiple)
Definition Common.h:134
va_start(val, fmt)
constexpr RAWSPEED_READNONE Ttgt implicit_cast(Tsrc value)
Definition Casts.h:32
vsnprintf(buf.data(), sizeof(buf), fmt, val)
uint16_t getU16BE(const void *data)
Definition Endianness.h:124
va_end(val)
__attribute__((noinline)) __attribute__((visibility("default"))) JPEGStuffedByteStreamGenerator
constexpr RAWSPEED_READNONE bool isAligned(T value, size_t multiple)
Definition Common.h:151
void RAWSPEED_UNLIKELY_FUNCTION RAWSPEED_NOINLINE static char buf[bufSize]
Array1DRef(T *data_, int numElts_) -> Array1DRef< T >
uint32_t getU32LE(const void *data)
Definition Endianness.h:127
std::chrono::steady_clock::time_point start
Definition rstest.cpp:115