GCC Code Coverage Report


Directory: ./
File: lib/geogram/image/image_serializer_xpm.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 2 143 1.4%
Functions: 1 10 10.0%
Branches: 0 153 0.0%

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_serializer_xpm.h>
41 #include <geogram/image/image.h>
42 #include <geogram/basic/string.h>
43 #include <geogram/basic/logger.h>
44
45 #include <sstream>
46 #include <string.h>
47
48 namespace {
49 using namespace GEO;
50
51 /**
52 * \brief Converts an hexadecimal digit into an integer.
53 * \param[in] digit the character representation of the digit.
54 * \return the corresonding integer value, in [0,15].
55 */
56 inline int htoi(char digit) {
57 if(digit >= '0' && digit <= '9') {
58 return digit - '0';
59 }
60 if(digit >= 'a' && digit <= 'f') {
61 return digit - 'a' + 10;
62 }
63 if(digit >= 'A' && digit <= 'F') {
64 return digit - 'A' + 10;
65 }
66 Logger::err("Image")
67 << "XPM Image reader: hex digit to integer: invalid digit: \'"
68 << digit << "\'" << std::endl;
69 abort();
70 }
71
72 /**
73 * \brief Decodes an XPM colormap entry.
74 * \param[in] colormap_entry the string with the colormap entry.
75 * \param[out] colorcell the corresponding color.
76 */
77 bool decode_colormap_entry(
78 const char* colormap_entry, Colormap::ColorCell& colorcell
79 ) {
80 const char* colorcode = strstr(colormap_entry, "c #");
81 if(colorcode == nullptr) {
82 if(strstr(colormap_entry, "None") != nullptr) {
83 colorcell = Colormap::ColorCell(0,0,0,0);
84 return true;
85 } else {
86 Logger::err("Image")
87 << "XPM Image reader: Colormap entry without any color"
88 << std::endl;
89 Logger::err("Image")
90 << " entry = \'" << colormap_entry << "\'" << std::endl;
91 return false;
92 }
93 }
94 colorcode += 3;
95
96 Memory::byte r,g,b,a;
97
98 if(strlen(colorcode) == 12) {
99 r = Memory::byte(16 * htoi(colorcode[0]) + htoi(colorcode[1]));
100 g = Memory::byte(16 * htoi(colorcode[4]) + htoi(colorcode[5]));
101 b = Memory::byte(16 * htoi(colorcode[8]) + htoi(colorcode[9]));
102 a = 255;
103 } else {
104 r = Memory::byte(16 * htoi(colorcode[0]) + htoi(colorcode[1]));
105 g = Memory::byte(16 * htoi(colorcode[2]) + htoi(colorcode[3]));
106 b = Memory::byte(16 * htoi(colorcode[4]) + htoi(colorcode[5]));
107 a = 255;
108 }
109 colorcell = Colormap::ColorCell(r,g,b,a);
110 return true;
111 }
112
113 }
114
115 /******************************************************************************/
116
117 namespace GEO {
118
119 Image* ImageSerializer_xpm::serialize_read_from_stream(
120 std::istream& stream
121 ) {
122 return serialize_read_static(stream);
123 }
124
125 Image* ImageSerializer_xpm::create_image_from_xpm_data(const char* s) {
126 std::istringstream in(s);
127 return serialize_read_static(in);
128 }
129
130 Image* ImageSerializer_xpm::serialize_read_static(std::istream& stream) {
131
132 Image* result = nullptr;
133
134 int num_colors;
135 int chars_per_pixels;
136 int width;
137 int height;
138
139 // *********************** header
140 {
141 char* header = next_xpm_data(stream);
142 if(header == nullptr) {
143 Logger::err("Image")
144 << "XPM image input: unexpected end of file" << std::endl;
145 return nullptr;
146 }
147 std::istringstream in(header);
148 in >> width >> height >> num_colors >> chars_per_pixels;
149 if(num_colors > 1024) {
150 Logger::err("Image")
151 << "XPM image input: too many colors ("
152 << num_colors
153 << ")" << std::endl;
154 Logger::err("Image")
155 << " should not be greater than 1024" << std::endl;
156 return nullptr;
157 }
158
159 switch(chars_per_pixels) {
160 case 1:
161 result=read_xpm_1_byte_per_pixel(
162 width, height, num_colors, stream
163 );
164 break;
165 case 2:
166 result=read_xpm_2_bytes_per_pixel(
167 width, height, num_colors, stream
168 );
169 break;
170 default:
171 Logger::err("Image")
172 << "XPM image input: invalid chars per pixels ("
173 << chars_per_pixels << ")" << std::endl;
174 Logger::err("Image") << " should be 2" << std::endl;
175 return nullptr;
176 }
177
178 // Convert colormapped to RGBA.
179 // We do that because texturing functions are not implemented
180 // for colormapped.
181 // TODO: move function to Image library.
182 Image* result_rgba = new Image(
183 Image::RGBA, Image::BYTE, result->width(), result->height()
184 );
185 for(index_t y=0; y<result->height(); ++y) {
186 for(index_t x=0; x<result->width(); ++x) {
187 index_t c = index_t(*result->pixel_base(x,y));
188 result_rgba->pixel_base(x,y)[0] =
189 result->colormap()->color_cell(c).r();
190 result_rgba->pixel_base(x,y)[1] =
191 result->colormap()->color_cell(c).g();
192 result_rgba->pixel_base(x,y)[2] =
193 result->colormap()->color_cell(c).b();
194 result_rgba->pixel_base(x,y)[3] =
195 result->colormap()->color_cell(c).a();
196 }
197 }
198 delete result;
199
200 return result_rgba;
201 }
202 }
203
204 Image* ImageSerializer_xpm::read_xpm_2_bytes_per_pixel(
205 int width, int height, int num_colors, std::istream& stream
206 ) {
207
208 // Converts a two-digit XPM color code into
209 // a color index.
210 static int conv_table[256][256];
211
212 // For checking, put a negative value to
213 // detect invalid color codes.
214 for(int k1=0; k1 < 256; k1++) {
215 for(int k2=0; k2 < 256; k2++) {
216 conv_table[k1][k2] = -1;
217 }
218 }
219
220 // ********************** colormap
221
222 typedef Numeric::uint8 byte;
223
224 Colormap* colormap = new Colormap(index_t(num_colors));
225
226 for(int entry_num=0; entry_num<num_colors; entry_num++) {
227 char* entry = next_xpm_data(stream);
228 if(entry == nullptr) {
229 Logger::err("Image")
230 << "XPM Image reader: Unexpected end of file"
231 << std::endl;
232 delete colormap;
233 return nullptr;
234 }
235
236 int key1 = entry[0];
237 int key2 = entry[1];
238
239 Colormap::ColorCell cell;
240 if(!decode_colormap_entry(entry, cell)) {
241 return nullptr;
242 }
243
244 colormap-> color_cell(index_t(entry_num)) = cell;
245 conv_table[key1][key2] = (unsigned char)entry_num;
246 }
247
248 // ****************** image
249
250 Image* result = new Image(
251 Image::INDEXED, Image::BYTE, index_t(width), index_t(height)
252 );
253 result-> set_colormap(colormap);
254
255 for(int y=0; y<height; y++) {
256 char* scan_line = next_xpm_data(stream);
257 if(scan_line == nullptr) {
258 Logger::err("Image")
259 << "XPM Image reader: Unexpected end of file"
260 << std::endl;
261 delete result;
262 return nullptr;
263 }
264 for(int x=0; x<width; x++) {
265 int key1 = scan_line[2*x];
266 int key2 = scan_line[2*x+1];
267 int color_index = conv_table[key1][key2];
268 if(color_index < 0 || color_index > num_colors) {
269 Logger::err("Image")
270 << "XPM Image reader: Invalid color index in image"
271 << std::endl;
272 delete result;
273 return nullptr;
274 }
275 result-> base_mem()[y * width + x] = byte(color_index);
276 }
277 }
278
279 return result;
280 }
281
282
283 Image* ImageSerializer_xpm::read_xpm_1_byte_per_pixel(
284 int width, int height, int num_colors, std::istream& stream
285 ) {
286
287 // Converts a two-digit XPM color code into
288 // a color index.
289 static int conv_table[256];
290
291 // For checking, put a negative value to
292 // detect invalid color codes.
293 for(int k1=0; k1 < 256; k1++) {
294 conv_table[k1] = -1;
295 }
296
297 // ********************* colormap
298
299 typedef Numeric::uint8 byte;
300
301 Colormap* colormap = new Colormap(index_t(num_colors));
302
303 for(int entry_num=0; entry_num<num_colors; entry_num++) {
304 char* entry = next_xpm_data(stream);
305 if(entry == nullptr) {
306 Logger::err("Image")
307 << "XPM Image reader: Unexpected end of file"
308 << std::endl;
309 delete colormap;
310 return nullptr;
311 }
312
313 int key1 = entry[0];
314
315 Colormap::ColorCell cell;
316 if(!decode_colormap_entry(entry, cell)) {
317 return nullptr;
318 }
319 colormap-> color_cell(index_t(entry_num)) = cell;
320 conv_table[key1] = (unsigned char)entry_num;
321 }
322
323 // ********************* image
324
325 Image* result = new Image(
326 Image::INDEXED, Image::BYTE, index_t(width), index_t(height)
327 );
328 result-> set_colormap(colormap);
329
330 for(int y=0; y<height; y++) {
331 char* scan_line = next_xpm_data(stream);
332 if(scan_line == nullptr) {
333 Logger::err("Image")
334 << "XPM Image reader: Unexpected end of file"
335 << std::endl;
336 delete result;
337 return nullptr;
338 }
339 for(int x=0; x<width; x++) {
340 int key1 = scan_line[x];
341 int color_index = conv_table[key1];
342 if(color_index < 0 || color_index > num_colors) {
343 Logger::err("Image")
344 << "XPM Image reader: Invalid color index in image"
345 << std::endl;
346 delete result;
347 return nullptr;
348 }
349 result-> base_mem()[y * width + x] = byte(color_index);
350 }
351 }
352
353 return result;
354 }
355
356 bool ImageSerializer_xpm::binary() const {
357 return false;
358 }
359
360 char* ImageSerializer_xpm::next_xpm_data(std::istream& input) {
361 static char line_buffer[4096];
362 char* result = nullptr;
363 bool found = false;
364 while(!found && !input.eof()) {
365 input.getline(line_buffer,4096);
366 char* p1 = strchr(line_buffer,'\"');
367 char* p2 = strchr(line_buffer + 1, '\"');
368 found = (p1 != nullptr && p2 != nullptr);
369 if(found) {
370 result = p1 + 1;
371 *p2 = '\0';
372 }
373 }
374 return result;
375 }
376
377 502 bool ImageSerializer_xpm::read_supported() const {
378 502 return true;
379 }
380
381 /**********************************************************************/
382
383 }
384