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