]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/xformsImage.C
The EnumLColor patch, free of macros.
[lyx.git] / src / frontends / xforms / xformsImage.C
1 /**
2  * \file xformsImage.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "xformsImage.h"
14 #include "Color.h"
15
16 #include "debug.h"
17 #include "format.h"
18 #include "LColor.h"
19
20 #include "graphics/GraphicsParams.h"
21
22 #include "support/lstrings.h"
23 #include "support/lyxfunctional.h"  // compare_memfun
24 #include "support/lyxlib.h"
25
26 #include "lyx_forms.h"
27
28 #ifdef HAVE_FLIMAGE_H
29 # include <flimage.h>
30 #else
31 # ifdef HAVE_X11_FLIMAGE_H
32 # include <X11/flimage.h>
33 # endif
34 #endif
35
36 #include <boost/tuple/tuple.hpp>
37
38 using lyx::support::float_equal;
39 using lyx::support::prefixIs;
40 using lyx::support::rtrim;
41
42 using std::find_if;
43
44
45 namespace {
46
47 void init_graphics();
48
49 unsigned int packedcolor(LColor::color c);
50
51 } // namespace anon
52
53
54 namespace lyx {
55 namespace graphics {
56
57 /// Access to this class is through this static method.
58 Image::ImagePtr xformsImage::newImage()
59 {
60         init_graphics();
61
62         ImagePtr ptr;
63         ptr.reset(new xformsImage);
64         return ptr;
65 }
66
67
68 /// Return the list of loadable formats.
69 Image::FormatList xformsImage::loadableFormats()
70 {
71         static FormatList fmts;
72         if (!fmts.empty())
73                 return fmts;
74
75         init_graphics();
76
77         // The formats recognised by LyX
78         Formats::const_iterator begin = formats.begin();
79         Formats::const_iterator end   = formats.end();
80
81         lyxerr[Debug::GRAPHICS]
82                 << "\nThe image loader can load the following directly:\n";
83
84         // Don't forget the Fortran numbering used by xforms!
85         int const nformats = flimage_get_number_of_formats();
86         for (int i = 1; i <= nformats; ++i) {
87
88                 FLIMAGE_FORMAT_INFO const * info = flimage_get_format_info(i);
89                 string const formal_name =
90                         info->formal_name ? info->formal_name : string();
91                 string ext =
92                         info->extension   ? info->extension   : string();
93
94                 if (ext.empty() || ext == "gz")
95                         continue;
96
97                 if (ext == "rgb") ext = "sgi";
98
99                 lyxerr[Debug::GRAPHICS]
100                         << formal_name << ", extension \"" << ext << "\"\n";
101
102                 Formats::const_iterator it =
103                         find_if(begin, end,
104                                 lyx::compare_memfun(&Format::extension, ext));
105                 if (it != end)
106                         fmts.push_back(it->name());
107         }
108
109         lyxerr[Debug::GRAPHICS]
110                 << "\nOf these, LyX recognises the following formats:\n";
111
112         FormatList::const_iterator fbegin = fmts.begin();
113         FormatList::const_iterator fend   = fmts.end();
114         for (FormatList::const_iterator fit = fbegin; fit != fend; ++fit) {
115                 if (fit != fbegin)
116                         lyxerr[Debug::GRAPHICS] << ", ";
117                 lyxerr[Debug::GRAPHICS] << *fit;
118         }
119         lyxerr[Debug::GRAPHICS] << '\n' << std::endl;
120
121         return fmts;
122 }
123
124
125 xformsImage::xformsImage()
126         : image_(0),
127           pixmap_(0),
128           pixmap_status_(PIXMAP_UNINITIALISED)
129 {}
130
131
132 xformsImage::xformsImage(xformsImage const & other)
133         : Image(other),
134           image_(0),
135           pixmap_(0),
136           pixmap_status_(PIXMAP_UNINITIALISED)
137 {
138         if (other.image_) {
139                 image_ = flimage_dup(other.image_);
140                 image_->u_vdata = this;
141         }
142 }
143
144
145 xformsImage::~xformsImage()
146 {
147         if (image_)
148                 flimage_free(image_);
149         if (pixmap_)
150                 XFreePixmap(fl_get_display(), pixmap_);
151 }
152
153
154 Image * xformsImage::clone() const
155 {
156         return new xformsImage(*this);
157 }
158
159
160 unsigned int xformsImage::getWidth() const
161 {
162         if (!image_)
163                 return 0;
164
165         return image_->w;
166 }
167
168
169 unsigned int xformsImage::getHeight() const
170 {
171         if (!image_)
172                 return 0;
173         return image_->h;
174 }
175
176
177 bool xformsImage::isDrawable() const
178 {
179         return pixmap_;
180 }
181
182
183 Pixmap xformsImage::getPixmap() const
184 {
185         if (!pixmap_status_ == PIXMAP_SUCCESS)
186                 return 0;
187         return pixmap_;
188 }
189
190
191 void xformsImage::load(string const & filename)
192 {
193         if (image_) {
194                 lyxerr[Debug::GRAPHICS]
195                         << "Image is loaded already!" << std::endl;
196                 finishedLoading(false);
197                 return;
198         }
199
200         image_ = flimage_open(filename.c_str());
201         if (!image_) {
202                 lyxerr[Debug::GRAPHICS]
203                         << "Unable to open image" << std::endl;
204                 finishedLoading(false);
205                 return;
206         }
207
208         // Set this now and we won't need to bother again.
209         image_->fill_color = packedcolor(LColor::graphicsbg);
210
211         // Used by the callback routines to return to this
212         image_->u_vdata = this;
213
214         // Begin the reading process.
215         flimage_read(image_);
216 }
217
218
219 bool xformsImage::setPixmap(Params const & params)
220 {
221         if (!image_ || params.display == NoDisplay)
222                 return false;
223
224         Display * display = fl_get_display();
225
226         if (pixmap_ && pixmap_status_ == PIXMAP_SUCCESS)
227                 XFreePixmap(display, pixmap_);
228
229         int color_key;
230         switch (params.display) {
231         case MonochromeDisplay:
232                 color_key = FL_IMAGE_MONO;
233                 break;
234         case GrayscaleDisplay:
235                 color_key = FL_IMAGE_GRAY;
236                 break;
237         case ColorDisplay:
238         default: // NoDisplay cannot happen!
239                 color_key = FL_IMAGE_RGB;
240                 break;
241         }
242
243         if (color_key != FL_IMAGE_RGB) {
244                 flimage_convert(image_, color_key, 0);
245         }
246
247         unsigned int fill = packedcolor(LColor::graphicsbg);
248         if (fill != image_->fill_color) {
249                 // the background color has changed.
250                 // Note that in grayscale/monochrome images the background is
251                 // grayed also, so this call will have no visible effect. Sorry!
252                 flimage_replace_pixel(image_, image_->fill_color, fill);
253                 image_->fill_color = fill;
254         }
255
256         image_->xdisplay = display;
257         Screen * screen  = ScreenOfDisplay(display, fl_screen);
258
259         pixmap_ = flimage_to_pixmap(image_, XRootWindowOfScreen(screen));
260         pixmap_status_ = pixmap_ ? PIXMAP_SUCCESS : PIXMAP_FAILED;
261
262         return pixmap_status_ == PIXMAP_SUCCESS;
263 }
264
265
266 void xformsImage::clip(Params const & params)
267 {
268         if (!image_)
269                 return;
270
271         if (params.bb.empty())
272                 // No clipping is necessary.
273                 return;
274
275         int const new_width  = params.bb.xr - params.bb.xl;
276         int const new_height = params.bb.yt - params.bb.yb;
277
278         // No need to check if the width, height are > 0 because the
279         // Bounding Box would be empty() in this case.
280         if (new_width > image_->w || new_height > image_->h)
281                 // Bounds are invalid.
282                 return;
283
284         if (new_width == image_->w && new_height == image_->h)
285                 // Bounds are unchanged.
286                 return;
287
288         // flimage.h: image_ members w and h are of type int
289         // (though always >= 0)
290         // GraphicsParams.h: params.bb members xl, xr, yt and yb are of
291         // type unsigned int.
292         // We must, therefore, be careful...
293         int const xoffset_l = params.bb.xl;
294         int const yoffset_b = params.bb.yb;
295         int const xoffset_r = image_->w > int(params.bb.xr) ?
296                 image_->w - params.bb.xr : 0;
297         int const yoffset_t = image_->h > int(params.bb.yt) ?
298                 image_->h - params.bb.yt : 0;
299
300         flimage_crop(image_, xoffset_l, yoffset_t, xoffset_r, yoffset_b);
301 }
302
303
304 void xformsImage::rotate(Params const & params)
305 {
306         if (!image_)
307                 return ;
308
309         if (float_equal(params.angle, 0.0, 0.1))
310                 // No rotation is necessary.
311                 return;
312
313         // The angle passed to flimage_rotate is the angle in one-tenth of a
314         // degree units.
315
316         // Work around xforms bug when params.angle == 270
317         // the 'InternalError: bad special angle' error.
318         // This bug fix is not needed in xforms 1.0 and greater.
319         if (float_equal(params.angle, 270.0, 0.1)) {
320                 flimage_rotate(image_,  900, FLIMAGE_SUBPIXEL);
321                 flimage_rotate(image_, 1800, FLIMAGE_SUBPIXEL);
322         } else {
323                 flimage_rotate(image_,
324                                int(params.angle * 10),
325                                FLIMAGE_SUBPIXEL);
326         }
327 }
328
329
330 void xformsImage::scale(Params const & params)
331 {
332         if (!image_)
333                 return;
334
335         unsigned int width;
336         unsigned int height;
337         boost::tie(width, height) = getScaledDimensions(params);
338
339         if (width == getWidth() && height == getHeight())
340                 // No scaling needed
341                 return;
342
343         flimage_scale(image_, width, height, FLIMAGE_SUBPIXEL);
344 }
345
346
347 void xformsImage::statusCB(string const & status_message)
348 {
349         if (status_message.empty())
350                 return;
351
352         if (prefixIs(status_message, "Done Reading")) {
353                 if (image_) {
354                         flimage_close(image_);
355                 }
356
357                 finishedLoading(true);
358         }
359 }
360
361
362 void xformsImage::errorCB(string const & error_message)
363 {
364         if (error_message.empty())
365                 return;
366
367         if (image_) {
368                 flimage_close(image_);
369         }
370
371         finishedLoading(false);
372 }
373
374 } // namespace graphics
375 } // namespace lyx
376
377
378 namespace {
379
380 extern "C" {
381
382 int status_report(FL_IMAGE * ob, const char *s)
383 {
384         BOOST_ASSERT(ob && ob->u_vdata);
385
386         string const str = s ? rtrim(s) : string();
387         if (str.empty())
388                 return 0;
389
390         lyxerr[Debug::GRAPHICS]
391                 << "xforms image loader. Status: " << str << std::endl;
392
393         lyx::graphics::xformsImage * ptr =
394                 static_cast<lyx::graphics::xformsImage *>(ob->u_vdata);
395         ptr->statusCB(str);
396
397         return 0;
398 }
399
400
401 static void error_report(FL_IMAGE * ob, const char *s)
402 {
403         BOOST_ASSERT(ob && ob->u_vdata);
404
405         string const str = s ? rtrim(s) : string();
406         if (str.empty())
407                 return;
408
409         lyxerr[Debug::GRAPHICS]
410                 << "xforms image loader. Error: " << str << std::endl;
411
412         lyx::graphics::xformsImage * ptr =
413                 static_cast<lyx::graphics::xformsImage *>(ob->u_vdata);
414         ptr->errorCB(str);
415 }
416
417 } // extern "C"
418
419
420 void init_graphics()
421 {
422         // Paranoia check
423         static bool initialised = false;
424         if (initialised)
425                 return;
426         initialised = true;
427
428         flimage_enable_bmp();
429         flimage_enable_fits();
430         flimage_enable_gif();
431 #ifdef USE_JPEG_IMAGE_LOADER
432         flimage_enable_jpeg();
433 #endif
434
435         // xforms itself uses pngtopnm to convert to a loadable format.
436         // We prefer to use our own conversion mechanism, therefore.
437         // flimage_enable_png();
438
439         flimage_enable_pnm();
440
441         // xforms recognises PS but not EPS
442         // It dies horribly with lots of older PostScript files.
443         // Easiest, therefore, to disable PS support and insist that a PS-type
444         // file is converted to a bitmap format.
445         // flimage_enable_ps();
446
447         flimage_enable_sgi();
448         flimage_enable_tiff();
449         flimage_enable_xbm();
450         flimage_enable_xwd();
451         // xforms can load most XPM files, but will occasionally crash
452         // with some files created by ImakeMagick's convert program.
453         // Turn off xpm support for the time being.
454         // flimage_enable_xpm();
455
456         // xforms stores this permanently (does not make a copy) so
457         // this should never be destroyed.
458         static FLIMAGE_SETUP setup;
459         setup.visual_cue    = status_report;
460         setup.error_message = error_report;
461         flimage_setup(&setup);
462 }
463
464
465 unsigned int packedcolor(LColor::color col)
466 {
467         unsigned int r, g, b;
468         bool const success = getRGBColor(col, r, g, b);
469         if (!success)
470                 // Set to black on failure
471                 return FL_PACK(255,255,255);
472
473         return FL_PACK(r, g, b);
474 }
475
476 } // namespace anon