]> git.sesse.net Git - nageru/blob - futatabi/exif_parser.cpp
Log a warning when we kill a client that is not keeping up.
[nageru] / futatabi / exif_parser.cpp
1 #include "exif_parser.h"
2
3 #include <movit/colorspace_conversion_effect.h>
4 #include <stdint.h>
5 #include <Eigen/Core>
6 #include <Eigen/LU>
7
8 using namespace Eigen;
9 using namespace movit;
10 using namespace std;
11
12 uint32_t read32be(const uint8_t *data)
13 {
14         return (uint32_t(data[0]) << 24) |
15                 (uint32_t(data[1]) << 16) |
16                 (uint32_t(data[2]) <<  8) |
17                  uint32_t(data[3]);
18 }
19
20 uint16_t read16be(const uint8_t *data)
21 {
22         return (uint16_t(data[0]) << 8) | uint16_t(data[1]);
23 }
24
25 RGBTriplet get_neutral_color(const string &exif)
26 {
27         if (exif.empty()) {
28                 return {1.0f, 1.0f, 1.0f};
29         }
30
31         const uint8_t *data = reinterpret_cast<const uint8_t *>(exif.data());
32
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};
39         }
40
41         // We only care about the first IFD.
42         uint32_t ifd_offset = read32be(data + 10);
43         ifd_offset += 6;  // Relative to the MM.
44
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};
48         }
49
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};
55         }
56
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};
60         }
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};
65         }
66
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};
70         }
71
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.
75
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};
79         }
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};
83         }
84
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);
89
90         double x = double(x_nom) / x_den;
91         double y = double(y_nom) / y_den;
92         double z = 1.0 - x - y;
93
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);
96
97         return RGBTriplet(rgb[0], rgb[1], rgb[2]);
98 }
99