/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 |