Coverage Report

Created: 2025-06-16 07:00

/src/libjxl/lib/jxl/base/exif.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
2
//
3
// Use of this source code is governed by a BSD-style
4
// license that can be found in the LICENSE file.
5
6
#ifndef LIB_JXL_BASE_EXIF_H_
7
#define LIB_JXL_BASE_EXIF_H_
8
9
// Basic parsing of Exif (just enough for the render-impacting things
10
// like orientation)
11
12
#include <jxl/codestream_header.h>
13
14
#include <cstddef>
15
#include <cstdint>
16
#include <vector>
17
18
#include "lib/jxl/base/byte_order.h"
19
#include "lib/jxl/base/compiler_specific.h"
20
21
namespace jxl {
22
23
constexpr uint16_t kExifOrientationTag = 274;
24
25
// Checks if a blob looks like Exif, and if so, sets bigendian
26
// according to the tiff endianness
27
0
JXL_INLINE bool IsExif(const std::vector<uint8_t>& exif, bool* bigendian) {
28
0
  if (exif.size() < 12) return false;  // not enough bytes for a valid exif blob
29
0
  const uint8_t* t = exif.data();
30
0
  if (LoadLE32(t) == 0x2A004D4D) {
31
0
    *bigendian = true;
32
0
    return true;
33
0
  } else if (LoadLE32(t) == 0x002A4949) {
34
0
    *bigendian = false;
35
0
    return true;
36
0
  }
37
0
  return false;  // not a valid tiff header
38
0
}
39
40
// Finds the position of an Exif tag, or 0 if it is not found
41
JXL_INLINE size_t FindExifTagPosition(const std::vector<uint8_t>& exif,
42
0
                                      uint16_t tagname) {
43
0
  bool bigendian;
44
0
  if (!IsExif(exif, &bigendian)) return 0;
45
0
  const uint8_t* t = exif.data() + 4;
46
0
  uint64_t offset = (bigendian ? LoadBE32(t) : LoadLE32(t));
47
0
  if (exif.size() < 12 + offset + 2 || offset < 8) return 0;
48
0
  t += offset - 4;
49
0
  if (offset + 2 >= exif.size()) return 0;
50
0
  uint16_t nb_tags = (bigendian ? LoadBE16(t) : LoadLE16(t));
51
0
  t += 2;
52
0
  while (nb_tags > 0) {
53
0
    if (t + 12 >= exif.data() + exif.size()) return 0;
54
0
    uint16_t tag = (bigendian ? LoadBE16(t) : LoadLE16(t));
55
0
    t += 2;
56
0
    if (tag == tagname) return static_cast<size_t>(t - exif.data());
57
0
    t += 10;
58
0
    nb_tags--;
59
0
  }
60
0
  return 0;
61
0
}
62
63
// TODO(jon): tag 1 can be used to represent Adobe RGB 1998 if it has value
64
// "R03"
65
// TODO(jon): set intrinsic dimensions according to
66
// https://discourse.wicg.io/t/proposal-exif-image-resolution-auto-and-from-image/4326/24
67
// Parses the Exif data just enough to extract any render-impacting info.
68
// If the Exif data is invalid or could not be parsed, then it is treated
69
// as a no-op.
70
JXL_INLINE void InterpretExif(const std::vector<uint8_t>& exif,
71
0
                              JxlOrientation* orientation) {
72
0
  bool bigendian;
73
0
  if (!IsExif(exif, &bigendian)) return;
74
0
  size_t o_pos = FindExifTagPosition(exif, kExifOrientationTag);
75
0
  if (o_pos) {
76
0
    const uint8_t* t = exif.data() + o_pos;
77
0
    uint16_t type = (bigendian ? LoadBE16(t) : LoadLE16(t));
78
0
    t += 2;
79
0
    uint32_t count = (bigendian ? LoadBE32(t) : LoadLE32(t));
80
0
    t += 4;
81
0
    uint16_t value = (bigendian ? LoadBE16(t) : LoadLE16(t));
82
0
    if (type == 3 && count == 1 && value >= 1 && value <= 8) {
83
0
      *orientation = static_cast<JxlOrientation>(value);
84
0
    }
85
0
  }
86
0
}
87
88
}  // namespace jxl
89
90
#endif  // LIB_JXL_BASE_EXIF_H_