]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/xformsImage.C
Apply that width fudge irrespective of library version. Make it slightly more
[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         // Why, oh why do we need such hacks?
157         // Angus 12 July 2002
158         return image_->w + 2;
159 }
160
161
162 unsigned int xformsImage::getHeight() const
163 {
164         if (!image_)
165                 return 0;
166         return image_->h;
167 }
168
169
170 Pixmap xformsImage::getPixmap() const
171 {
172         if (!pixmap_status_ == PIXMAP_SUCCESS)
173                 return 0;
174         return pixmap_;
175 }
176
177
178 void xformsImage::load(string const & filename)
179 {
180         if (image_) {
181                 lyxerr[Debug::GRAPHICS]
182                         << "Image is loaded already!" << std::endl;
183                 finishedLoading(false);
184                 return;
185         }
186
187         image_ = flimage_open(filename.c_str());
188         if (!image_) {
189                 lyxerr[Debug::GRAPHICS]
190                         << "Unable to open image" << std::endl;
191                 finishedLoading(false);
192                 return;
193         }
194
195         // Set this now and we won't need to bother again.
196         image_->fill_color = packedcolor(LColor::graphicsbg);
197
198         // Used by the callback routines to return to this
199         image_->u_vdata = this;
200
201         // Begin the reading process.
202         flimage_read(image_);
203 }
204
205
206 bool xformsImage::setPixmap(Params const & params)
207 {
208         if (!image_ || params.display == NoDisplay)
209                 return false;
210
211         Display * display = fl_get_display();
212
213         if (pixmap_ && pixmap_status_ == PIXMAP_SUCCESS)
214                 XFreePixmap(display, pixmap_);
215
216         int color_key;
217         switch (params.display) {
218         case MonochromeDisplay:
219                 color_key = FL_IMAGE_MONO;
220                 break;
221         case GrayscaleDisplay:
222                 color_key = FL_IMAGE_GRAY;
223                 break;
224         case ColorDisplay:
225         default: // NoDisplay cannot happen!
226                 color_key = FL_IMAGE_RGB;
227                 break;
228         }
229
230         if (color_key != FL_IMAGE_RGB) {
231                 flimage_convert(image_, color_key, 0);
232         }
233
234         unsigned int fill = packedcolor(LColor::graphicsbg);
235         if (fill != image_->fill_color) {
236                 // the background color has changed.
237                 // Note that in grayscale/monochrome images the background is
238                 // grayed also, so this call will have no visible effect. Sorry!
239                 flimage_replace_pixel(image_, image_->fill_color, fill);
240                 image_->fill_color = fill;
241         }
242
243         image_->xdisplay = display;
244         Screen * screen  = ScreenOfDisplay(display, fl_screen);
245
246         pixmap_ = flimage_to_pixmap(image_, XRootWindowOfScreen(screen));
247         pixmap_status_ = pixmap_ ? PIXMAP_SUCCESS : PIXMAP_FAILED;
248
249         return pixmap_status_ == PIXMAP_SUCCESS;
250 }
251
252
253 void xformsImage::clip(Params const & params)
254 {
255         if (!image_)
256                 return;
257
258         if (params.bb.empty())
259                 // No clipping is necessary.
260                 return;
261
262         int const new_width  = params.bb.xr - params.bb.xl;
263         int const new_height = params.bb.yt - params.bb.yb;
264
265         // No need to check if the width, height are > 0 because the
266         // Bounding Box would be empty() in this case.
267         if (new_width > image_->w || new_height > image_->h)
268                 // Bounds are invalid.
269                 return;
270
271         if (new_width == image_->w && new_height == image_->h)
272                 // Bounds are unchanged.
273                 return;
274
275         int const xoffset_l = std::max(0, params.bb.xl);
276         int const xoffset_r = std::max(0, image_->w - params.bb.xr);
277         int const yoffset_t = std::max(0, image_->h - params.bb.yt);
278         int const yoffset_b = std::max(0, params.bb.yb);
279
280         flimage_crop(image_, xoffset_l, yoffset_t, xoffset_r, yoffset_b);
281 }
282
283
284 void xformsImage::rotate(Params const & params)
285 {
286         if (!image_)
287                 return ;
288
289         if (!params.angle)
290                 // No rotation is necessary.
291                 return;
292
293         // The angle passed to flimage_rotate is the angle in one-tenth of a
294         // degree units.
295
296         // Work around xforms bug when params.angle == 270
297         // the 'InternalError: bad special angle' error.
298         // This bug fix is not needed in xforms 1.0 and greater.
299         if (params.angle == 270) {
300                 flimage_rotate(image_,  900, FLIMAGE_SUBPIXEL);
301                 flimage_rotate(image_, 1800, FLIMAGE_SUBPIXEL);
302         } else {
303                 flimage_rotate(image_, params.angle * 10, FLIMAGE_SUBPIXEL);
304         }
305 }
306
307
308 void xformsImage::scale(Params const & params)
309 {
310         if (!image_)
311                 return;
312
313         unsigned int width;
314         unsigned int height;
315         boost::tie(width, height) = getScaledDimensions(params);
316
317         if (width == getWidth() && height == getHeight())
318                 // No scaling needed
319                 return;
320
321         flimage_scale(image_, width, height, FLIMAGE_SUBPIXEL);
322 }
323
324
325 void xformsImage::statusCB(string const & status_message)
326 {
327         if (status_message.empty())
328                 return;
329
330         if (prefixIs(status_message, "Done Reading")) {
331                 if (image_) {
332                         flimage_close(image_);
333                 }
334
335                 finishedLoading(true);
336         }
337 }
338
339
340 void xformsImage::errorCB(string const & error_message)
341 {
342         if (error_message.empty())
343                 return;
344
345         if (image_) {
346                 flimage_close(image_);
347         }
348
349         finishedLoading(false);
350 }
351
352 } // namespace grfx
353
354
355 namespace {
356
357 extern "C" {
358
359 int status_report(FL_IMAGE * ob, const char *s)
360 {
361         lyx::Assert(ob && ob->u_vdata);
362
363         string const str = s ? strip(s) : string();
364         if (str.empty())
365                 return 0;
366
367         lyxerr[Debug::GRAPHICS]
368                 << "xforms image loader. Status : " << str << std::endl;
369
370         grfx::xformsImage * ptr =
371                 static_cast<grfx::xformsImage *>(ob->u_vdata);
372         ptr->statusCB(str);
373
374         return 0;
375 }
376
377
378 static void error_report(FL_IMAGE * ob, const char *s)
379 {
380         lyx::Assert(ob && ob->u_vdata);
381
382         string const str = s ? strip(s) : string();
383         if (str.empty())
384                 return;
385
386         lyxerr[Debug::GRAPHICS]
387                 << "xforms image loader. Error : " << str << std::endl;
388
389         grfx::xformsImage * ptr =
390                 static_cast<grfx::xformsImage *>(ob->u_vdata);
391         ptr->errorCB(str);
392 }
393
394 } // extern "C"
395
396
397 void init_graphics()
398 {
399         // Paranoia check
400         static bool initialised = false;
401         if (initialised)
402                 return;
403         initialised = true;
404
405         flimage_enable_bmp();
406         flimage_enable_fits();
407         flimage_enable_gif();
408 #ifdef HAVE_FLIMAGE_ENABLE_JPEG
409         flimage_enable_jpeg();
410 #endif
411
412         // xforms itself uses pngtopnm to convert to a loadable format.
413         // We prefer to use our own conversion mechanism, therefore.
414         // flimage_enable_png();
415
416         flimage_enable_pnm();
417
418 #ifdef HAVE_FLIMAGE_ENABLE_PS
419         // xforms recognises PS but not EPS
420         flimage_enable_ps();
421 #endif
422
423         flimage_enable_sgi();
424         flimage_enable_tiff();
425         flimage_enable_xbm();
426         flimage_enable_xwd();
427         flimage_enable_xpm();
428
429         // xforms stores this permanently (does not make a copy) so
430         // this should never be destroyed.
431         static FLIMAGE_SETUP setup;
432         setup.visual_cue    = status_report;
433         setup.error_message = error_report;
434         flimage_setup(&setup);
435 }
436
437
438 unsigned int packedcolor(LColor::color c)
439 {
440         string const x11color = lcolor.getX11Name(c);
441
442         Display * display = fl_get_display();
443         Colormap cmap     = fl_state[fl_get_vclass()].colormap;
444         XColor xcol;
445         XColor ccol;
446         if (XLookupColor(display, cmap, x11color.c_str(), &xcol, &ccol) == 0)
447                 // Unable to parse x11color.
448                 return FL_PACK(255,255,255);
449
450         // Note that X stores the RGB values in the range 0 - 65535
451         // whilst we require them in the range 0 - 255.
452         unsigned int const r = xcol.red   / 256;
453         unsigned int const g = xcol.green / 256;
454         unsigned int const b = xcol.blue  / 256;
455
456         return FL_PACK(r, g, b);
457 }
458
459 } // namespace anon