GCC Code Coverage Report


Directory: ./
File: lib/geogram/image/image_library.cpp
Date: 2026-09-07 02:37:58
Exec Total Coverage
Lines: 55 117 47.0%
Functions: 9 15 60.0%
Branches: 46 208 22.1%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram/image/image_library.h>
41 #include <geogram/image/image.h>
42 #include <geogram/image/image_serializer.h>
43
44 #include <geogram/basic/file_system.h>
45 #include <geogram/basic/logger.h>
46 #include <geogram/basic/string.h>
47
48 // For clipboard
49 #ifdef GEO_OS_WINDOWS
50 #include <windows.h>
51 #endif
52
53 namespace GEO {
54
55 //_________________________________________________________
56
57 ImageLibrary* ImageLibrary::instance_ = nullptr ;
58
59 1757 ImageLibrary* ImageLibrary::instance() {
60 1757 return instance_;
61 }
62
63 251 void ImageLibrary::initialize() {
64
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 251 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
251 geo_assert(instance_ == nullptr) ;
65
2/6
✓ Branch 2 taken 251 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 251 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
251 instance_ = new ImageLibrary() ;
66 251 Environment::instance()->add_environment(instance_) ;
67 251 }
68
69 251 void ImageLibrary::terminate() {
70 // Note: instance is not deleted here. Since it
71 // is declared in the Environment, now Environment
72 // has the ownership and will delete it.
73
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 251 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
251 geo_assert(instance_ != nullptr) ;
74 251 instance_ = nullptr ;
75 251 }
76
77 251 ImageLibrary::ImageLibrary() {
78 251 }
79
80 1004 ImageLibrary::~ImageLibrary() {
81 1004 }
82
83
84 1757 bool ImageLibrary::bind_image_serializer(
85 const std::string& extension, ImageSerializer* serializer
86 ) {
87
1/2
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
1757 std::string upper_extension = extension ;
88
1/2
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
1757 String::to_uppercase(upper_extension) ;
89 1757 if(
90
3/6
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1757 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1757 times.
3514 resolve_image_serializer(extension) != nullptr ||
91
2/4
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1757 times.
1757 resolve_image_serializer(upper_extension) != nullptr
92 ) {
93 return false ;
94 }
95
2/4
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1757 times.
✗ Branch 5 not taken.
1757 image_serializers_[extension] = serializer ;
96
2/4
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1757 times.
✗ Branch 5 not taken.
1757 image_serializers_[upper_extension] = serializer ;
97
2/4
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1757 times.
✗ Branch 5 not taken.
3514 Environment::notify_observers("image_read_extensions") ;
98
2/4
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1757 times.
✗ Branch 5 not taken.
3514 Environment::notify_observers("image_write_extensions") ;
99 1757 return true ;
100 1757 }
101
102 3514 ImageSerializer* ImageLibrary::resolve_image_serializer(
103 const std::string& extension
104 ) const {
105
1/2
✓ Branch 1 taken 3514 times.
✗ Branch 2 not taken.
3514 auto it = image_serializers_.find(extension) ;
106
1/2
✓ Branch 2 taken 3514 times.
✗ Branch 3 not taken.
3514 if(it == image_serializers_.end()) {
107 3514 return nullptr ;
108 }
109 return it->second ;
110 }
111
112 bool ImageLibrary::bind_image(const std::string& name, Image* image) {
113 if(resolve_image(name) != nullptr) {
114 return false ;
115 }
116 images_[name] = image ;
117 return true ;
118 }
119
120 bool ImageLibrary::unbind_image(const std::string& name) {
121 auto it = images_.find(name) ;
122 if(it == images_.end()) {
123 return false ;
124 }
125 images_.erase(it) ;
126 return true ;
127 }
128
129 Image* ImageLibrary::resolve_image(const std::string& name) const {
130 auto it = images_.find(name) ;
131 if(it == images_.end()) {
132 return nullptr ;
133 }
134 return it->second ;
135 }
136
137 Image* ImageLibrary::load_image(const std::string& file_name) {
138 std::string extension = FileSystem::extension(file_name) ;
139 if(extension.length() == 0) {
140 Image* result = resolve_image(file_name) ;
141 if(result != nullptr) { result->acquire(); return result ; }
142 Logger::err("ImageLibrary")
143 << "no extension in file name and no such registered image" << std::endl ;
144 return nullptr ;
145 }
146
147 ImageSerializer* serializer = resolve_image_serializer(extension) ;
148 if(serializer == nullptr) {
149 Logger::err("ImageLibrary")
150 << "could not find serializer for extension \'"
151 << extension << "\'" << std::endl ;
152 return nullptr ;
153 }
154
155 if(!serializer->read_supported()) {
156 Logger::err("ImageLibrary")
157 << "serializer for extension \'"
158 << extension << "\' does not have a \'read\' function"
159 << std::endl ;
160 return nullptr ;
161 }
162
163 return serializer->serialize_read(file_name) ;
164 }
165
166 bool ImageLibrary::save_image(
167 const std::string& file_name, Image* image
168 ) {
169
170 std::string extension = FileSystem::extension(file_name) ;
171 if(extension.length() == 0) {
172 Logger::err("ImageLibrary")
173 << "no extension in file name" << std::endl ;
174 return false ;
175 }
176
177 ImageSerializer* serializer = resolve_image_serializer(extension) ;
178 if(serializer == nullptr) {
179 Logger::err("ImageLibrary")
180 << "could not find serializer for extension \'"
181 << extension << "\'" << std::endl ;
182 return false ;
183 }
184
185 if(!serializer->write_supported()) {
186 Logger::err("ImageLibrary")
187 << "serializer for extension \'"
188 << extension << "\' does not have a \'write\' function"
189 << std::endl ;
190 return false ;
191 }
192
193 return serializer->serialize_write(file_name, image) ;
194 }
195
196 void ImageLibrary::copy_image_to_clipboard(Image* image) {
197 geo_argused(image);
198
199 #ifdef GEO_OS_WINDOWS
200
201 if(image->color_encoding() != Image::RGB) {
202 Logger::err("ImageLibrary")
203 << "copy_image_to_clipboard() "
204 << "not implemented for this color encoding"
205 << std::endl ;
206 return ;
207 }
208
209 // Thanks to Pierre Alliez for his help with
210 // Windows clipboard programming.
211
212 // Step 1: Try to open Window's clipboard
213 // nullptr -> bind to current process
214 if(!::OpenClipboard( nullptr )) {
215 return ;
216 }
217
218 index_t h = image->height() ;
219 index_t w = image->width() ;
220
221 // Step 2: Prepare the image for Windows:
222 // flip the image and flip rgb -> bgr
223 {
224 index_t row_len = image->width() * 3 ;
225 for(index_t j=0; j< h/2; j++) {
226 Memory::pointer row1 =
227 image->base_mem() + j * row_len ;
228 Memory::pointer row2 =
229 image->base_mem() + (h - 1 - j) * row_len ;
230 for(index_t i=0; i<w; i++) {
231 std::swap(row1[3*i+2], row2[3*i ]) ;
232 std::swap(row1[3*i+1], row2[3*i+1]) ;
233 std::swap(row1[3*i ], row2[3*i+2]) ;
234 }
235 }
236 }
237
238 // Step 3: create a shared memory segment, with
239 // a DIB (Device Independent Bitmap) in it.
240 HANDLE handle;
241
242 index_t image_size = 3 * image->width() * image->height();
243 index_t size = index_t(sizeof(BITMAPINFOHEADER)) + image_size ;
244
245 handle = (HANDLE)::GlobalAlloc(GHND,size);
246 if(handle != nullptr) {
247 char *pData = (char *) ::GlobalLock((HGLOBAL)handle);
248 BITMAPINFOHEADER header ;
249 header.biSize = sizeof(BITMAPINFOHEADER);
250 header.biWidth = (LONG)(image->width()) ;
251 header.biHeight = (LONG)(image->height()) ;
252 header.biPlanes = 1 ;
253 header.biBitCount = 24 ;
254 header.biCompression = BI_RGB ;
255 header.biSizeImage = 0 ;
256 header.biXPelsPerMeter = 1000000 ;
257 header.biYPelsPerMeter = 1000000 ;
258 header.biClrUsed = 0 ;
259 header.biClrImportant = 0 ;
260 ::memcpy(pData,&header,sizeof(BITMAPINFOHEADER));
261 ::memcpy(
262 pData+sizeof(BITMAPINFOHEADER),image->base_mem(),image_size
263 ) ;
264
265 // Step 4: put the data in the clipboard.
266 ::GlobalUnlock((HGLOBAL)handle);
267 ::EmptyClipboard() ;
268 ::SetClipboardData(CF_DIB,handle);
269 ::CloseClipboard();
270
271 // Step 5: restore the image
272 {
273 index_t row_len = image->width() * 3 ;
274 for(index_t j=0; j< h/2; j++) {
275 Memory::pointer
276 row1 = image->base_mem() + j * row_len ;
277 Memory::pointer
278 row2 = image->base_mem() + (h - 1 - j) * row_len ;
279 for(index_t i=0; i<w; i++) {
280 std::swap(row1[3*i+2], row2[3*i ]) ;
281 std::swap(row1[3*i+1], row2[3*i+1]) ;
282 std::swap(row1[3*i ], row2[3*i+2]) ;
283 }
284 }
285 }
286 }
287 #else
288 Logger::err("ImageLibrary") << "copy_image_to_clipboard() "
289 << "not implemented for this OS"
290 << std::endl ;
291 #endif
292 }
293
294 3560 bool ImageLibrary::get_local_value(
295 const std::string& name, std::string& value
296 ) const {
297
2/2
✓ Branch 1 taken 1757 times.
✓ Branch 2 taken 1803 times.
3560 if(name == "image_read_extensions") {
298 1757 value = "" ;
299
2/2
✓ Branch 5 taken 7028 times.
✓ Branch 6 taken 1757 times.
8785 for(auto& it : image_serializers_) {
300
3/6
✓ Branch 1 taken 7028 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7028 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7028 times.
✗ Branch 7 not taken.
7028 if(it.second->read_supported()) {
301
2/2
✓ Branch 1 taken 5271 times.
✓ Branch 2 taken 1757 times.
7028 if(value.length() != 0) {
302
1/2
✓ Branch 1 taken 5271 times.
✗ Branch 2 not taken.
5271 value += ";" ;
303 }
304
1/2
✓ Branch 1 taken 7028 times.
✗ Branch 2 not taken.
7028 value += "*." ;
305
1/2
✓ Branch 1 taken 7028 times.
✗ Branch 2 not taken.
7028 value += it.first ;
306 }
307 }
308 1757 return true ;
309
2/2
✓ Branch 1 taken 1757 times.
✓ Branch 2 taken 46 times.
1803 } else if(name=="image_write_extensions") {
310 1757 value = "" ;
311
2/2
✓ Branch 5 taken 7028 times.
✓ Branch 6 taken 1757 times.
8785 for(auto& it : image_serializers_) {
312
4/6
✓ Branch 1 taken 7028 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7028 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6275 times.
✓ Branch 7 taken 753 times.
7028 if(it.second->write_supported()) {
313
2/2
✓ Branch 1 taken 4518 times.
✓ Branch 2 taken 1757 times.
6275 if(value.length() != 0) {
314
1/2
✓ Branch 1 taken 4518 times.
✗ Branch 2 not taken.
4518 value += ";" ;
315 }
316
1/2
✓ Branch 1 taken 6275 times.
✗ Branch 2 not taken.
6275 value += "*." ;
317
1/2
✓ Branch 1 taken 6275 times.
✗ Branch 2 not taken.
6275 value += it.first ;
318 }
319 }
320 1757 return true ;
321 } else {
322 46 return false ;
323 }
324 }
325
326 10884 bool ImageLibrary::set_local_value(const std::string& name, const std::string& value) {
327 10884 geo_argused(name);
328 10884 geo_argused(value);
329 10884 return false;
330 }
331
332
333 //_________________________________________________________
334
335 }
336