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