Coverage Report

Created: 2026-09-13 06:10

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
663
    Image(ImageType::bmp, mdNone, std::move(io), params) {
28
663
}
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
653
void BmpImage::readMetadata() {
48
#ifdef EXIV2_DEBUG_MESSAGES
49
  std::cerr << "Exiv2::BmpImage::readMetadata: Reading Windows bitmap file " << io_->path() << "\n";
50
#endif
51
653
  if (io_->open() != 0) {
52
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
53
0
  }
54
653
  IoCloser closer(*io_);
55
56
  // Ensure that this is the correct image type
57
653
  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
653
  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
653
  byte buf[26];
96
653
  if (io_->read(buf, sizeof(buf)) == sizeof(buf)) {
97
653
    const uint32_t headerSize = getULong(buf + 14, littleEndian);
98
653
    if (headerSize == 12) {
99
      // OS/2 1.x / Windows 2.x BITMAPCOREHEADER: width and height are 16-bit fields.
100
10
      pixelWidth_ = getUShort(buf + 18, littleEndian);
101
10
      pixelHeight_ = getUShort(buf + 20, littleEndian);
102
643
    } else {
103
643
      pixelWidth_ = getULong(buf + 18, littleEndian);
104
643
      pixelHeight_ = getULong(buf + 22, littleEndian);
105
643
    }
106
653
  }
107
653
}
108
109
0
void BmpImage::writeMetadata() {
110
  /// \todo implement me!
111
0
  throw(Error(ErrorCode::kerWritingImageFormatUnsupported, "BMP"));
112
0
}
113
114
// *************************************************************************
115
// free functions
116
663
Image::UniquePtr newBmpInstance(BasicIo::UniquePtr io, const ImageCtorParams& params) {
117
663
  auto image = std::make_unique<BmpImage>(std::move(io), params);
118
663
  if (!image->good()) {
119
10
    return nullptr;
120
10
  }
121
653
  return image;
122
663
}
123
124
16.5k
bool isBmpType(BasicIo& iIo, bool advance) {
125
16.5k
  const int32_t len = 2;
126
16.5k
  const std::array<byte, len> BmpImageId{'B', 'M'};
127
16.5k
  std::array<byte, len> buf;
128
16.5k
  iIo.read(buf.data(), len);
129
16.5k
  if (iIo.error() || iIo.eof()) {
130
337
    return false;
131
337
  }
132
16.1k
  bool matched = buf == BmpImageId;
133
16.1k
  if (!advance || !matched) {
134
16.1k
    iIo.seek(-len, BasicIo::cur);
135
16.1k
  }
136
16.1k
  return matched;
137
16.5k
}
138
}  // namespace Exiv2