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