]> git.lyx.org Git - features.git/blob - src/frontends/xforms/xformsImage.C
Really dull and boring header shit
[features.git] / src / frontends / xforms / xformsImage.C
1 /**
2  * \file xformsImage.C
3  * Read the file COPYING
4  *
5  * \author Angus Leeming 
6  *
7  * Full author contact details are available in file CREDITS
8  */
9
10 #include <config.h>
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #endif
15
16 #include "xformsImage.h"
17 #include "graphics/GraphicsParams.h"
18 #include "LColor.h"
19 #include "converter.h"              // formats
20 #include "debug.h"
21 #include "support/LAssert.h"
22 #include "support/lyxfunctional.h"  // compare_memfun
23
24 #include FORMS_H_LOCATION
25
26 #ifdef HAVE_FLIMAGE_H
27 # include <flimage.h>
28 #else
29 # ifdef HAVE_X11_FLIMAGE_H
30 # include <X11/flimage.h>
31 # endif
32 #endif
33
34 #include <boost/tuple/tuple.hpp>
35
36 using std::find_if;
37
38 namespace {
39
40 void init_graphics();
41
42 unsigned int packedcolor(LColor::color c);
43
44 } // namespace anon
45
46
47 namespace grfx {
48
49 /// Access to this class is through this static method.
50 Image::ImagePtr xformsImage::newImage()
51 {
52         init_graphics();
53
54         ImagePtr ptr;
55         ptr.reset(new xformsImage);
56         return ptr;
57 }
58
59
60 /// Return the list of loadable formats.
61 Image::FormatList xformsImage::loadableFormats()
62 {
63         static FormatList fmts;
64         if (!fmts.empty())
65                 return fmts;
66
67         init_graphics();
68
69         // The formats recognised by LyX
70         Formats::const_iterator begin = formats.begin();
71         Formats::const_iterator end   = formats.end();
72
73         lyxerr[Debug::GRAPHICS]
74                 << "\nThe image loader can load the following directly:\n";
75
76         // Don't forget the Fortran numbering used by xforms!
77         int const nformats = flimage_get_number_of_formats();
78         for (int i = 1; i <= nformats; ++i) {
79
80                 FLIMAGE_FORMAT_INFO const * info = flimage_get_format_info(i);
81                 string const formal_name =
82                         info->formal_name ? info->formal_name : string();
83                 string ext =
84                         info->extension   ? info->extension   : string();
85
86                 if (ext.empty() || ext == "gz")
87                         continue;
88
89                 if (ext == "rgb") ext = "sgi";
90
91                 lyxerr[Debug::GRAPHICS]
92                         << formal_name << ", extension \"" << ext << "\"\n";
93
94                 Formats::const_iterator it =
95                         find_if(begin, end,
96                                 lyx::compare_memfun(&Format::extension, ext));
97                 if (it != end)
98                         fmts.push_back(it->name());
99         }
100
101         lyxerr[Debug::GRAPHICS]
102                 << "\nOf these, LyX recognises the following formats:\n";
103
104         FormatList::const_iterator fbegin = fmts.begin();
105         FormatList::const_iterator fend   = fmts.end();
106         for (FormatList::const_iterator fit = fbegin; fit != fend; ++fit) {
107                 if (fit != fbegin)
108                         lyxerr[Debug::GRAPHICS] << ", ";
109                 lyxerr[Debug::GRAPHICS] << *fit;
110         }
111         lyxerr[Debug::GRAPHICS] << '\n' << std::endl;
112
113         return fmts;
114 }
115
116
117 xformsImage::xformsImage()
118         : image_(0),
119           pixmap_(0),
120           pixmap_status_(PIXMAP_UNINITIALISED)
121 {}
122
123
124 xformsImage::xformsImage(xformsImage const & other)
125         : Image(other),
126           image_(0),
127           pixmap_(0),
128           pixmap_status_(PIXMAP_UNINITIALISED)
129 {
130         if (other.image_) {
131                 image_ = flimage_dup(other.image_);
132                 image_->u_vdata = this;
133         }
134 }
135
136
137 xformsImage::~xformsImage()
138 {
139         if (image_)
140                 flimage_free(image_);
141         if (pixmap_)
142                 XFreePixmap(fl_get_display(), pixmap_);
143 }
144
145
146 Image * xformsImage::clone() const
147 {
148         return new xformsImage(*this);
149 }
150
151
152 unsigned int xformsImage::getWidth() const
153 {
154         if (!image_)
155                 return 0;
156
157         // Why, oh why do we need such hacks?
158         // Angus 12 July 2002
159         return image_->w + 2;
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         // FIXME: these values are unsigned so this makes NO sense
283  
284         int const xoffset_l = std::max(0U, params.bb.xl);
285         int const xoffset_r = std::max(0U, image_->w - params.bb.xr);
286         int const yoffset_t = std::max(0U, image_->h - params.bb.yt);
287         int const yoffset_b = std::max(0U, params.bb.yb);
288
289         flimage_crop(image_, xoffset_l, yoffset_t, xoffset_r, yoffset_b);
290 }
291
292
293 void xformsImage::rotate(Params const & params)
294 {
295         if (!image_)
296                 return ;
297
298         if (!params.angle)
299                 // No rotation is necessary.
300                 return;
301
302         // The angle passed to flimage_rotate is the angle in one-tenth of a
303         // degree units.
304
305         // Work around xforms bug when params.angle == 270
306         // the 'InternalError: bad special angle' error.
307         // This bug fix is not needed in xforms 1.0 and greater.
308         if (params.angle == 270) {
309                 flimage_rotate(image_,  900, FLIMAGE_SUBPIXEL);
310                 flimage_rotate(image_, 1800, FLIMAGE_SUBPIXEL);
311         } else {
312                 flimage_rotate(image_, params.angle * 10, FLIMAGE_SUBPIXEL);
313         }
314 }
315
316
317 void xformsImage::scale(Params const & params)
318 {
319         if (!image_)
320                 return;
321
322         unsigned int width;
323         unsigned int height;
324         boost::tie(width, height) = getScaledDimensions(params);
325
326         if (width == getWidth() && height == getHeight())
327                 // No scaling needed
328                 return;
329
330         flimage_scale(image_, width, height, FLIMAGE_SUBPIXEL);
331 }
332
333
334 void xformsImage::statusCB(string const & status_message)
335 {
336         if (status_message.empty())
337                 return;
338
339         if (prefixIs(status_message, "Done Reading")) {
340                 if (image_) {
341                         flimage_close(image_);
342                 }
343
344                 finishedLoading(true);
345         }
346 }
347
348
349 void xformsImage::errorCB(string const & error_message)
350 {
351         if (error_message.empty())
352                 return;
353
354         if (image_) {
355                 flimage_close(image_);
356         }
357
358         finishedLoading(false);
359 }
360
361 } // namespace grfx
362
363
364 namespace {
365
366 extern "C" {
367
368 int status_report(FL_IMAGE * ob, const char *s)
369 {
370         lyx::Assert(ob && ob->u_vdata);
371
372         string const str = s ? rtrim(s) : string();
373         if (str.empty())
374                 return 0;
375
376         lyxerr[Debug::GRAPHICS]
377                 << "xforms image loader. Status : " << str << std::endl;
378
379         grfx::xformsImage * ptr =
380                 static_cast<grfx::xformsImage *>(ob->u_vdata);
381         ptr->statusCB(str);
382
383         return 0;
384 }
385
386
387 static void error_report(FL_IMAGE * ob, const char *s)
388 {
389         lyx::Assert(ob && ob->u_vdata);
390
391         string const str = s ? rtrim(s) : string();
392         if (str.empty())
393                 return;
394
395         lyxerr[Debug::GRAPHICS]
396                 << "xforms image loader. Error : " << str << std::endl;
397
398         grfx::xformsImage * ptr =
399                 static_cast<grfx::xformsImage *>(ob->u_vdata);
400         ptr->errorCB(str);
401 }
402
403 } // extern "C"
404
405
406 void init_graphics()
407 {
408         // Paranoia check
409         static bool initialised = false;
410         if (initialised)
411                 return;
412         initialised = true;
413
414         flimage_enable_bmp();
415         flimage_enable_fits();
416         flimage_enable_gif();
417 #ifdef HAVE_FLIMAGE_ENABLE_JPEG
418         flimage_enable_jpeg();
419 #endif
420
421         // xforms itself uses pngtopnm to convert to a loadable format.
422         // We prefer to use our own conversion mechanism, therefore.
423         // flimage_enable_png();
424
425         flimage_enable_pnm();
426
427 #ifdef HAVE_FLIMAGE_ENABLE_PS
428         // xforms recognises PS but not EPS
429         // It dies horribly with lots of older PostScript files.
430         // Easiest, therefore, to disable PS support and insist that a PS-type
431         // file is converted to a bitmap format.
432         // flimage_enable_ps();
433 #endif
434
435         flimage_enable_sgi();
436         flimage_enable_tiff();
437         flimage_enable_xbm();
438         flimage_enable_xwd();
439         flimage_enable_xpm();
440
441         // xforms stores this permanently (does not make a copy) so
442         // this should never be destroyed.
443         static FLIMAGE_SETUP setup;
444         setup.visual_cue    = status_report;
445         setup.error_message = error_report;
446         flimage_setup(&setup);
447 }
448
449
450 unsigned int packedcolor(LColor::color c)
451 {
452         string const x11color = lcolor.getX11Name(c);
453
454         Display * display = fl_get_display();
455         Colormap cmap     = fl_state[fl_get_vclass()].colormap;
456         XColor xcol;
457         XColor ccol;
458         if (XLookupColor(display, cmap, x11color.c_str(), &xcol, &ccol) == 0)
459                 // Unable to parse x11color.
460                 return FL_PACK(255,255,255);
461
462         // Note that X stores the RGB values in the range 0 - 65535
463         // whilst we require them in the range 0 - 255.
464         unsigned int const r = xcol.red   / 256;
465         unsigned int const g = xcol.green / 256;
466         unsigned int const b = xcol.blue  / 256;
467
468         return FL_PACK(r, g, b);
469 }
470
471 } // namespace anon