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