Coverage Report

Created: 2026-10-28 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/src/bmpimage.cpp
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
  File:      bmpimage.cpp
4
  Author(s): Marco Piovanelli, Ovolab (marco)
5
  History:   05-Mar-2007, marco: created
6
 */
7
8
#include "bmpimage.hpp"
9
#include "basicio.hpp"
10
#include "config.h"
11
#include "error.hpp"
12
#include "futils.hpp"
13
#include "image.hpp"
14
15
// + standard includes
16
#include <array>
17
#include <string>
18
19
#ifdef EXIV2_DEBUG_MESSAGES
20
#include <iostream>
21
#endif
22
23
// *****************************************************************************
24
// class member definitions
25
namespace Exiv2 {
26
BmpImage::BmpImage(BasicIo::UniquePtr io, const ImageCtorParams& params) :
27
817
    Image(ImageType::bmp, mdNone, std::move(io), params) {
28
817
}
29
30
6
std::string BmpImage::mimeType() const {
31
  // "image/bmp" is a Generic Bitmap
32
6
  return "image/x-ms-bmp";  // Microsoft Bitmap
33
6
}
34
35
0
void BmpImage::setExifData(const ExifData& /*exifData*/) {
36
0
  throw(Error(ErrorCode::kerInvalidSettingForImage, "Exif metadata", "BMP"));
37
0
}
38
39
0
void BmpImage::setIptcData(const IptcData& /*iptcData*/) {
40
0
  throw(Error(ErrorCode::kerInvalidSettingForImage, "IPTC metadata", "BMP"));
41
0
}
42
43
0
void BmpImage::setComment(const std::string&) {
44
0
  throw(Error(ErrorCode::kerInvalidSettingForImage, "Image comment", "BMP"));
45
0
}
46
47
799
void BmpImage::readMetadata() {
48
#ifdef EXIV2_DEBUG_MESSAGES
49
  std::cerr << "Exiv2::BmpImage::readMetadata: Reading Windows bitmap file " << io_->path() << "\n";
50
#endif
51
799
  if (io_->open() != 0) {
52
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
53
0
  }
54
799
  IoCloser closer(*io_);
55
56
  // Ensure that this is the correct image type
57
799
  if (!isBmpType(*io_, false)) {
58
0
    if (io_->error() || io_->eof())
59
0
      throw Error(ErrorCode::kerFailedToReadImageData);
60
0
    throw Error(ErrorCode::kerNotAnImage, "BMP");
61
0
  }
62
799
  clearMetadata();
63
64
  /*
65
    The Windows bitmap header goes as follows -- all numbers are in little-endian byte order:
66
67
    offset  length   name                   description
68
    ======  =======  =====================  =======
69
     0      2 bytes  signature              always 'BM'
70
     2      4 bytes  bitmap size
71
     6      4 bytes  reserved
72
    10      4 bytes  bitmap offset
73
    14      4 bytes  header size
74
    18      4 bytes  bitmap width
75
    22      4 bytes  bitmap height
76
    26      2 bytes  plane count
77
    28      2 bytes  depth
78
    30      4 bytes  compression            0 = none; 1 = RLE, 8 bits/pixel; 2 = RLE, 4 bits/pixel; 3 = bitfield;
79
    4 = JPEG; 5 = PNG 34      4 bytes  image size             size of the raw bitmap data, in bytes 38      4
80
    bytes  horizontal resolution  (in pixels per meter) 42      4 bytes  vertical resolution    (in pixels per
81
    meter) 46      4 bytes  color count 50      4 bytes  important colors       number of "important" colors
82
83
    The layout above describes the BITMAPINFOHEADER (and later) variants, identified by a header size
84
    of 40 bytes or more. The older OS/2 1.x / Windows 2.x format uses the shorter, 12-byte
85
    BITMAPCOREHEADER instead, in which the width and height fields are only 2 bytes wide:
86
87
    offset  length   name                   description
88
    ======  =======  =====================  =======
89
    14      4 bytes  header size            always 12 for BITMAPCOREHEADER
90
    18      2 bytes  bitmap width
91
    20      2 bytes  bitmap height
92
    22      2 bytes  plane count
93
    24      2 bytes  depth
94
  */
95
799
  byte buf[26];
96
799
  if (io_->read(buf, sizeof(buf)) == sizeof(buf)) {
97
799
    const uint32_t headerSize = getULong(buf + 14, littleEndian);
98
799
    if (headerSize == 12) {
99
      // OS/2 1.x / Windows 2.x BITMAPCOREHEADER: width and height are 16-bit fields.
100
36
      pixelWidth_ = getUShort(buf + 18, littleEndian);
101
36
      pixelHeight_ = getUShort(buf + 20, littleEndian);
102
763
    } else {
103
763
      pixelWidth_ = getULong(buf + 18, littleEndian);
104
763
      pixelHeight_ = getULong(buf + 22, littleEndian);
105
763
    }
106
799
  }
107
799
}
108
109
2
void BmpImage::writeMetadata() {
110
  /// \todo implement me!
111
2
  throw(Error(ErrorCode::kerWritingImageFormatUnsupported, "BMP"));
112
2
}
113
114
// *************************************************************************
115
// free functions
116
817
Image::UniquePtr newBmpInstance(BasicIo::UniquePtr io, const ImageCtorParams& params) {
117
817
  auto image = std::make_unique<BmpImage>(std::move(io), params);
118
817
  if (!image->good()) {
119
18
    return nullptr;
120
18
  }
121
799
  return image;
122
817
}
123
124
55.8k
bool isBmpType(BasicIo& iIo, bool advance) {
125
55.8k
  const int32_t len = 2;
126
55.8k
  const std::array<byte, len> BmpImageId{'B', 'M'};
127
55.8k
  std::array<byte, len> buf;
128
55.8k
  iIo.read(buf.data(), len);
129
55.8k
  if (iIo.error() || iIo.eof()) {
130
1.44k
    return false;
131
1.44k
  }
132
54.3k
  bool matched = buf == BmpImageId;
133
54.3k
  if (!advance || !matched) {
134
54.3k
    iIo.seek(-len, BasicIo::cur);
135
54.3k
  }
136
54.3k
  return matched;
137
55.8k
}
138
}  // namespace Exiv2