1 #include "exif_parser.h"
3 #include <movit/colorspace_conversion_effect.h>
12 uint32_t read32be(const uint8_t *data)
14 return (uint32_t(data[0]) << 24) |
15 (uint32_t(data[1]) << 16) |
16 (uint32_t(data[2]) << 8) |
20 uint16_t read16be(const uint8_t *data)
22 return (uint16_t(data[0]) << 8) | uint16_t(data[1]);
25 RGBTriplet get_neutral_color(const string &exif)
28 return {1.0f, 1.0f, 1.0f};
31 const uint8_t *data = reinterpret_cast<const uint8_t *>(exif.data());
33 // Very rudimentary Exif parser (and probably integer-overflowable);
34 // we really only care about what Nageru sends us (MJPEGEncoder::init_jpeg_422()),
35 // but it would be nice to have a little bit of future-proofing, just in case.
36 if (exif.size() < 14 || memcmp(data, "Exif\0\0MM\0\x2a", 10) != 0) {
37 fprintf(stderr, "WARNING: Truncated or malformed Exif header, ignoring.\n");
38 return {1.0f, 1.0f, 1.0f};
41 // We only care about the first IFD.
42 uint32_t ifd_offset = read32be(data + 10);
43 ifd_offset += 6; // Relative to the MM.
45 if (ifd_offset < 14 || ifd_offset >= exif.size()) {
46 fprintf(stderr, "WARNING: Truncated or malformed Exif IFD, ignoring.\n");
47 return {1.0f, 1.0f, 1.0f};
50 // Skip over number of tags (16 bits); if the white point is not the first one,
51 // we're bailing anyway.
52 if (ifd_offset + 2 > exif.size() || ifd_offset + 2 < ifd_offset) {
53 fprintf(stderr, "WARNING: Exif IFD has no rom for number of tags, ignoring.\n");
54 return {1.0f, 1.0f, 1.0f};
57 if (ifd_offset + 4 > exif.size() || ifd_offset + 4 < ifd_offset) {
58 fprintf(stderr, "WARNING: Exif IFD has no rom for tag, ignoring.\n");
59 return {1.0f, 1.0f, 1.0f};
61 uint16_t tag = read16be(data + ifd_offset + 2);
62 if (tag != 0x13e) { // WhitePoint.
63 fprintf(stderr, "WARNING: Unexpected first Exif tag, ignoring.\n");
64 return {1.0f, 1.0f, 1.0f};
67 if (ifd_offset + 14 > exif.size() || ifd_offset + 14 < ifd_offset) {
68 fprintf(stderr, "WARNING: WhitePoint Exif tag was truncated, ignoring.\n");
69 return {1.0f, 1.0f, 1.0f};
72 // Just assume we're rational type and two values...
73 uint32_t white_point_offset = read32be(data + ifd_offset + 10);
74 white_point_offset += 6; // Relative to the MM.
76 if (white_point_offset >= exif.size()) {
77 fprintf(stderr, "WARNING: WhitePoint Exif tag was out of bounds, ignoring.\n");
78 return {1.0f, 1.0f, 1.0f};
80 if (white_point_offset + 16 > exif.size()) {
81 fprintf(stderr, "WARNING: WhitePoint Exif tag was truncated, ignoring.\n");
82 return {1.0f, 1.0f, 1.0f};
85 uint32_t x_nom = read32be(data + white_point_offset);
86 uint32_t x_den = read32be(data + white_point_offset + 4);
87 uint32_t y_nom = read32be(data + white_point_offset + 8);
88 uint32_t y_den = read32be(data + white_point_offset + 12);
90 double x = double(x_nom) / x_den;
91 double y = double(y_nom) / y_den;
92 double z = 1.0 - x - y;
94 Matrix3d rgb_to_xyz_matrix = movit::ColorspaceConversionEffect::get_xyz_matrix(COLORSPACE_sRGB);
95 Vector3d rgb = rgb_to_xyz_matrix.inverse() * Vector3d(x, y, z);
97 return RGBTriplet(rgb[0], rgb[1], rgb[2]);