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