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