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