1 /*
2  Copyright (C) 2010-2014 Kristian Duske
3 
4  This file is part of TrenchBroom.
5 
6  TrenchBroom is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  TrenchBroom is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "ImageLoaderImpl.h"
21 
22 #include "Exceptions.h"
23 #include "Macros.h"
24 #include "IO/Path.h"
25 
26 namespace TrenchBroom {
27     namespace IO {
InitFreeImage()28         ImageLoaderImpl::InitFreeImage::InitFreeImage() {
29             FreeImage_Initialise(true);
30         }
31 
~InitFreeImage()32         ImageLoaderImpl::InitFreeImage::~InitFreeImage() {
33             FreeImage_DeInitialise();
34         }
35 
ImageLoaderImpl(const ImageLoader::Format format,const Path & path)36         ImageLoaderImpl::ImageLoaderImpl(const ImageLoader::Format format, const Path& path) :
37         m_stream(NULL),
38         m_bitmap(NULL),
39         m_paletteInitialized(false),
40         m_indicesInitialized(false),
41         m_pixelsInitialized(false) {
42             initialize();
43             const FREE_IMAGE_FORMAT fifFormat = translateFormat(format);
44             if (fifFormat == FIF_UNKNOWN)
45                 throw FileFormatException("Unknown image format");
46 
47             m_bitmap = FreeImage_Load(fifFormat, path.asString().c_str());
48         }
49 
ImageLoaderImpl(const ImageLoader::Format format,const char * begin,const char * end)50         ImageLoaderImpl::ImageLoaderImpl(const ImageLoader::Format format, const char* begin, const char* end) :
51         m_stream(NULL),
52         m_bitmap(NULL),
53         m_paletteInitialized(false),
54         m_indicesInitialized(false),
55         m_pixelsInitialized(false) {
56             initialize();
57             const FREE_IMAGE_FORMAT fifFormat = translateFormat(format);
58             if (fifFormat == FIF_UNKNOWN)
59                 throw FileFormatException("Unknown image format");
60 
61             // this is supremely evil, but FreeImage guarantees that it will not modify wrapped memory
62             BYTE* address = reinterpret_cast<BYTE*>(const_cast<char*>(begin));
63             DWORD length = static_cast<DWORD>(end - begin);
64             m_stream = FreeImage_OpenMemory(address, length);
65             m_bitmap = FreeImage_LoadFromMemory(fifFormat, m_stream);
66         }
67 
~ImageLoaderImpl()68         ImageLoaderImpl::~ImageLoaderImpl() {
69             if (m_bitmap != NULL) {
70                 FreeImage_Unload(m_bitmap);
71                 m_bitmap = NULL;
72             }
73             if (m_stream != NULL) {
74                 FreeImage_CloseMemory(m_stream);
75                 m_stream = NULL;
76             }
77         }
78 
paletteSize() const79         size_t ImageLoaderImpl::paletteSize() const {
80             return static_cast<size_t>(FreeImage_GetColorsUsed(m_bitmap));
81         }
82 
bitsPerPixel() const83         size_t ImageLoaderImpl::bitsPerPixel() const {
84             return static_cast<size_t>(FreeImage_GetBPP(m_bitmap));
85         }
86 
width() const87         size_t ImageLoaderImpl::width() const {
88             return static_cast<size_t>(FreeImage_GetWidth(m_bitmap));
89         }
90 
height() const91         size_t ImageLoaderImpl::height() const {
92             return static_cast<size_t>(FreeImage_GetHeight(m_bitmap));
93         }
94 
byteWidth() const95         size_t ImageLoaderImpl::byteWidth() const {
96             return static_cast<size_t>(FreeImage_GetLine(m_bitmap));
97         }
98 
scanWidth() const99         size_t ImageLoaderImpl::scanWidth() const {
100             return static_cast<size_t>(FreeImage_GetPitch(m_bitmap));
101         }
102 
hasPalette() const103         bool ImageLoaderImpl::hasPalette() const {
104             return FreeImage_GetPalette(m_bitmap) != NULL;
105         }
106 
hasIndices() const107         bool ImageLoaderImpl::hasIndices() const {
108             return FreeImage_GetColorType(m_bitmap) == FIC_PALETTE;
109         }
110 
hasPixels() const111         bool ImageLoaderImpl::hasPixels() const {
112             return static_cast<bool>(FreeImage_HasPixels(m_bitmap) == TRUE);
113         }
114 
palette() const115         const Buffer<unsigned char>& ImageLoaderImpl::palette() const {
116             assert(hasPalette());
117             if (!m_paletteInitialized) {
118                 const RGBQUAD* pal = FreeImage_GetPalette(m_bitmap);
119                 if (pal != NULL) {
120                     m_palette = Buffer<unsigned char>(paletteSize() * 3);
121                     for (size_t i = 0; i < paletteSize(); ++i) {
122                         m_palette[i * 3 + 0] = static_cast<unsigned char>(pal[i].rgbRed);
123                         m_palette[i * 3 + 1] = static_cast<unsigned char>(pal[i].rgbGreen);
124                         m_palette[i * 3 + 2] = static_cast<unsigned char>(pal[i].rgbBlue);
125                     }
126 
127                     m_paletteInitialized = true;
128                 }
129             }
130 
131             return m_palette;
132         }
133 
indices() const134         const Buffer<unsigned char>& ImageLoaderImpl::indices() const {
135             assert(hasIndices());
136             if (!m_indicesInitialized) {
137                 m_indices = Buffer<unsigned char>(width() * height());
138                 for (unsigned y = 0; y < height(); ++y) {
139                     for (unsigned x = 0; x < width(); ++x) {
140                         BYTE index = 0;
141                         const bool success = (FreeImage_GetPixelIndex(m_bitmap, x, y, &index) == TRUE);
142                         assert(success);
143                         unused(success);
144                         m_indices[(height() - y - 1) * width() + x] = static_cast<unsigned char>(index);
145                     }
146                 }
147 
148                 m_indicesInitialized = true;
149             }
150 
151             return m_indices;
152         }
153 
pixels(const ImageLoader::PixelFormat format) const154         const Buffer<unsigned char>& ImageLoaderImpl::pixels(const ImageLoader::PixelFormat format) const {
155             assert(hasPixels());
156             if (!m_pixelsInitialized) {
157                 const size_t pSize = pixelSize(format);
158                 m_pixels = Buffer<unsigned char>(width() * height() * pSize);
159                 if (hasIndices())
160                     initializeIndexedPixels(pSize);
161                 else
162                     initializePixels(pSize);
163                 m_pixelsInitialized = true;
164             }
165 
166             return m_pixels;
167         }
168 
initialize()169         void ImageLoaderImpl::initialize() {
170             static InitFreeImage initFreeImage;
171         }
172 
initializeIndexedPixels(const size_t pSize) const173         void ImageLoaderImpl::initializeIndexedPixels(const size_t pSize) const {
174             assert(pSize == 3);
175             const RGBQUAD* pal = FreeImage_GetPalette(m_bitmap);
176             assert(pal != NULL);
177 
178             for (unsigned y = 0; y < height(); ++y) {
179                 for (unsigned x = 0; x < width(); ++x) {
180                     BYTE paletteIndex = 0;
181                     const bool success = (FreeImage_GetPixelIndex(m_bitmap, x, y, &paletteIndex) == TRUE);
182                     assert(success);
183                     unused(success);
184 
185                     assert(paletteIndex < paletteSize());
186 
187                     const size_t pixelIndex = ((height() - y - 1) * width() + x) * pSize;
188                     m_pixels[pixelIndex + 0] = static_cast<unsigned char>(pal[paletteIndex].rgbRed);
189                     m_pixels[pixelIndex + 1] = static_cast<unsigned char>(pal[paletteIndex].rgbGreen);
190                     m_pixels[pixelIndex + 2] = static_cast<unsigned char>(pal[paletteIndex].rgbBlue);
191                 }
192             }
193         }
194 
initializePixels(const size_t pSize) const195         void ImageLoaderImpl::initializePixels(const size_t pSize) const {
196             for (unsigned y = 0; y < height(); ++y) {
197                 for (unsigned x = 0; x < width(); ++x) {
198                     RGBQUAD pixel;
199                     const bool success = (FreeImage_GetPixelColor(m_bitmap, x, y, &pixel) == TRUE);
200                     assert(success);
201                     unused(success);
202 
203                     const size_t pixelIndex = ((height() - y - 1) * width() + x) * pSize;
204                     m_pixels[pixelIndex + 0] = static_cast<unsigned char>(pixel.rgbRed);
205                     m_pixels[pixelIndex + 1] = static_cast<unsigned char>(pixel.rgbGreen);
206                     m_pixels[pixelIndex + 2] = static_cast<unsigned char>(pixel.rgbBlue);
207                     if (pSize > 3)
208                         m_pixels[pixelIndex + 3] = static_cast<unsigned char>(pixel.rgbReserved);
209                 }
210             }
211         }
212 
translateFormat(const ImageLoader::Format format)213         FREE_IMAGE_FORMAT ImageLoaderImpl::translateFormat(const ImageLoader::Format format) {
214             switch (format) {
215                 case ImageLoader::PCX:
216                     return FIF_PCX;
217                 switchDefault()
218             }
219         }
220 
pixelSize(const ImageLoader::PixelFormat format)221         size_t ImageLoaderImpl::pixelSize(const ImageLoader::PixelFormat format) {
222             switch (format) {
223                 case ImageLoader::RGB:
224                     return 3;
225                 case ImageLoader::RGBA:
226                     return 4;
227                 switchDefault()
228             }
229         }
230     }
231 }
232