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