Celestia

Форк
0
173 строки · 5.1 Кб
1
// bmp.cpp
2
//
3
// Copyright (C) 2001-present, the Celestia Development Team
4
// Original version by Chris Laurel <claurel@shatters.net>
5
//
6
// This program is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU General Public License
8
// as published by the Free Software Foundation; either version 2
9
// of the License, or (at your option) any later version.
10

11
#include <cstdint>
12
#include <fstream>
13
#include <istream>
14
#include <memory>
15
#include <vector>
16

17
#include <celutil/binaryread.h>
18
#include <celutil/logger.h>
19
#include "image.h"
20

21
namespace celestia::engine
22
{
23
namespace
24
{
25

26
// BMP file definitions--can't use windows.h because we might not be
27
// built on Windows!
28
typedef struct
29
{
30
    unsigned char magic[2];
31
    std::uint32_t size;
32
    std::uint32_t reserved;
33
    std::uint32_t offset;
34
} BMPFileHeader;
35

36
typedef struct
37
{
38
    std::uint32_t size;
39
    std::int32_t width;
40
    std::int32_t height;
41
    std::uint16_t planes;
42
    std::uint16_t bpp;
43
    std::uint32_t compression;
44
    std::uint32_t imageSize;
45
    std::int32_t widthPPM;
46
    std::int32_t heightPPM;
47
    std::uint32_t colorsUsed;
48
    std::uint32_t colorsImportant;
49
} BMPImageHeader;
50

51

52
Image* LoadBMPImage(std::istream& in)
53
{
54
    BMPFileHeader fileHeader;
55
    BMPImageHeader imageHeader;
56

57
    if (!util::readLE<unsigned char>(in, fileHeader.magic[0])
58
        || fileHeader.magic[0] != 'B'
59
        || !util::readLE<unsigned char>(in, fileHeader.magic[1])
60
        || fileHeader.magic[1] != 'M'
61
        || !util::readLE<std::uint32_t>(in, fileHeader.size)
62
        || !util::readLE<std::uint32_t>(in, fileHeader.reserved)
63
        || !util::readLE<std::uint32_t>(in, fileHeader.offset)
64
        || !util::readLE<std::uint32_t>(in, imageHeader.size)
65
        || !util::readLE<std::int32_t>(in, imageHeader.width)
66
        || imageHeader.width <= 0
67
        || !util::readLE<std::int32_t>(in, imageHeader.height)
68
        || imageHeader.height <= 0
69
        || !util::readLE<std::uint16_t>(in, imageHeader.planes)
70
        || !util::readLE<std::uint16_t>(in, imageHeader.bpp)
71
        // We don't handle 1-, 2-, or 4-bpp images
72
        || (imageHeader.bpp != 8 && imageHeader.bpp != 24 && imageHeader.bpp != 32)
73
        || !util::readLE<std::uint32_t>(in, imageHeader.compression)
74
        // We currently don't support compressed BMPs
75
        || imageHeader.compression != 0
76
        || !util::readLE<std::uint32_t>(in, imageHeader.imageSize)
77
        || !util::readLE<std::int32_t>(in, imageHeader.widthPPM)
78
        || !util::readLE<std::int32_t>(in, imageHeader.heightPPM)
79
        || !util::readLE<std::uint32_t>(in, imageHeader.colorsUsed)
80
        || !util::readLE<std::uint32_t>(in, imageHeader.colorsImportant))
81
    {
82
        return nullptr;
83
    }
84

85
    std::vector<uint8_t> palette;
86
    if (imageHeader.bpp == 8)
87
    {
88
        util::GetLogger()->debug("Reading {} color palette\n", imageHeader.colorsUsed);
89
        palette.resize(imageHeader.colorsUsed * 4);
90
        if (!in.read(reinterpret_cast<char*>(palette.data()), imageHeader.colorsUsed * 4).good())
91
        {
92
            return nullptr;
93
        }
94
    }
95

96
    if (!in.seekg(fileHeader.offset, std::ios::beg)) { return nullptr; }
97

98
    std::size_t bytesPerRow =
99
        (imageHeader.width * imageHeader.bpp / 8 + 1) & ~1;
100
    std::size_t imageBytes = bytesPerRow * imageHeader.height;
101

102
    // slurp the image data
103
    std::vector<std::uint8_t> pixels(imageBytes);
104
    if (!in.read(reinterpret_cast<char*>(pixels.data()), imageBytes).good())
105
    {
106
        return nullptr;
107
    }
108

109
    // check for truncated file
110

111
    auto img = std::make_unique<Image>(PixelFormat::RGB, imageHeader.width, imageHeader.height);
112

113
    // Copy the image and perform any necessary conversions
114
    for (std::int32_t y = 0; y < imageHeader.height; y++)
115
    {
116
        const std::uint8_t* src = pixels.data() + y * bytesPerRow;
117
        uint8_t* dst = img->getPixelRow(y);
118

119
        switch (imageHeader.bpp)
120
        {
121
        case 8:
122
            for (std::int32_t x = 0; x < imageHeader.width; x++)
123
            {
124
                const uint8_t* color = palette.data() + (*src << 2);
125
                dst[0] = color[2];
126
                dst[1] = color[1];
127
                dst[2] = color[0];
128
                src++;
129
                dst += 3;
130
            }
131
            break;
132
        case 24:
133
            for (std::int32_t x = 0; x < imageHeader.width; x++)
134
            {
135
                dst[0] = src[2];
136
                dst[1] = src[1];
137
                dst[2] = src[0];
138
                src += 3;
139
                dst += 3;
140
            }
141
            break;
142
        case 32:
143
            for (std::int32_t x = 0; x < imageHeader.width; x++)
144
            {
145
                dst[0] = src[2];
146
                dst[1] = src[1];
147
                dst[2] = src[0];
148
                src += 4;
149
                dst += 3;
150
            }
151
            break;
152
        }
153
    }
154

155
    return img.release();
156
}
157
} // anonymous namespace
158

159
Image* LoadBMPImage(const fs::path& filename)
160
{
161
    std::ifstream bmpFile(filename.string(), std::ios::in | std::ios::binary);
162

163
    if (bmpFile.good())
164
    {
165
        Image* img = LoadBMPImage(bmpFile);
166
        bmpFile.close();
167
        return img;
168
    }
169

170
    return nullptr;
171
}
172

173
} // namespace celestia::engine
174

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.