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