1 /*
2 RGBAImage.cpp
3 Copyright (C) 2006-2011 Yangli Hector Yee
4 Copyright (C) 2011-2016 Steven Myint, Jeff Terrace
5 
6 (This entire file was rewritten by Jim Tilander)
7 
8 This program is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free Software
10 Foundation; either version 2 of the License, or (at your option) any later
11 version.
12 
13 This program is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License along with
18 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 
22 #include "rgba_image.h"
23 
24 #include <FreeImage.h>
25 
26 #include <cassert>
27 #include <ciso646>
28 #include <cstring>
29 #include <string>
30 
31 
32 namespace pdiff
33 {
34     struct FreeImageDeleter
35     {
operator ()pdiff::FreeImageDeleter36         inline void operator()(FIBITMAP *image)
37         {
38             if (image)
39             {
40                 FreeImage_Unload(image);
41             }
42         }
43     };
44 
45 
to_free_image(const RGBAImage & image)46     static std::shared_ptr<FIBITMAP> to_free_image(const RGBAImage &image)
47     {
48         const auto *data = image.get_data();
49 
50         std::shared_ptr<FIBITMAP> bitmap(
51             FreeImage_Allocate(image.get_width(), image.get_height(), 32,
52                                0x000000ff, 0x0000ff00, 0x00ff0000),
53             FreeImageDeleter());
54         assert(bitmap.get());
55 
56         for (auto y = 0u; y < image.get_height();
57              y++, data += image.get_width())
58         {
59             auto scanline = reinterpret_cast<unsigned int *>(
60                 FreeImage_GetScanLine(bitmap.get(), image.get_height() - y - 1));
61             memcpy(scanline, data, sizeof(data[0]) * image.get_width());
62         }
63 
64         return bitmap;
65     }
66 
67 
to_rgba_image(FIBITMAP * image,const std::string & filename="")68     static std::shared_ptr<RGBAImage> to_rgba_image(FIBITMAP *image,
69                                                     const std::string &filename="")
70     {
71         const auto w = FreeImage_GetWidth(image);
72         const auto h = FreeImage_GetHeight(image);
73 
74         auto result = std::make_shared<RGBAImage>(w, h, filename);
75         // Copy the image over to our internal format, FreeImage has the scanlines
76         // bottom to top though.
77         auto dest = result->get_data();
78         for (unsigned int y = 0; y < h; y++, dest += w)
79         {
80             const auto scanline = reinterpret_cast<const unsigned int *>(
81                 FreeImage_GetScanLine(image, h - y - 1));
82             memcpy(dest, scanline, sizeof(dest[0]) * w);
83         }
84 
85         return result;
86 
87     }
88 
down_sample(unsigned int w,unsigned int h) const89     std::shared_ptr<RGBAImage> RGBAImage::down_sample(unsigned int w,
90                                                       unsigned int h) const
91     {
92         if (w == 0)
93         {
94             w = width_ / 2;
95         }
96 
97         if (h == 0)
98         {
99             h = weight_ / 2;
100         }
101 
102         if (width_ <= 1 or weight_ <= 1)
103         {
104             return nullptr;
105         }
106         if (width_ == w and weight_ == h)
107         {
108             return nullptr;
109         }
110         assert(w <= width_);
111         assert(h <= weight_);
112 
113         auto bitmap = to_free_image(*this);
114         std::unique_ptr<FIBITMAP, FreeImageDeleter> converted(
115             FreeImage_Rescale(bitmap.get(), w, h, FILTER_BICUBIC));
116 
117         auto img = to_rgba_image(converted.get(), name_);
118 
119         return img;
120     }
121 
write_to_file(const std::string & filename) const122     void RGBAImage::write_to_file(const std::string &filename) const
123     {
124         const auto file_type = FreeImage_GetFIFFromFilename(filename.c_str());
125         if (FIF_UNKNOWN == file_type)
126         {
127             throw RGBImageException("Can't save to unknown filetype '" +
128                                     filename + "'");
129         }
130 
131         auto bitmap = to_free_image(*this);
132 
133         const bool result =
134             !!FreeImage_Save(file_type, bitmap.get(), filename.c_str());
135         if (not result)
136         {
137             throw RGBImageException("Failed to save to '" + filename + "'");
138         }
139     }
140 
read_from_file(const std::string & filename)141     std::shared_ptr<RGBAImage> read_from_file(const std::string &filename)
142     {
143         const auto file_type = FreeImage_GetFileType(filename.c_str());
144         if (FIF_UNKNOWN == file_type)
145         {
146             throw RGBImageException("Unknown filetype '" + filename + "'");
147         }
148 
149         FIBITMAP *free_image = nullptr;
150         if (auto temporary = FreeImage_Load(file_type, filename.c_str(), 0))
151         {
152             free_image = FreeImage_ConvertTo32Bits(temporary);
153             FreeImage_Unload(temporary);
154         }
155         if (not free_image)
156         {
157             throw RGBImageException("Failed to load the image " + filename);
158         }
159 
160         auto result = to_rgba_image(free_image);
161 
162         FreeImage_Unload(free_image);
163 
164         return result;
165     }
166 }
167