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