]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/xformsImage.C
Nothing but a changed email address (trying to factor my tree in in small steps)
[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 <leeming@lyx.org>
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 bool xformsImage::isDrawable() const
171 {
172         return pixmap_;
173 }
174
175
176 Pixmap xformsImage::getPixmap() const
177 {
178         if (!pixmap_status_ == PIXMAP_SUCCESS)
179                 return 0;
180         return pixmap_;
181 }
182
183
184 void xformsImage::load(string const & filename)
185 {
186         if (image_) {
187                 lyxerr[Debug::GRAPHICS]
188                         << "Image is loaded already!" << std::endl;
189                 finishedLoading(false);
190                 return;
191         }
192
193         image_ = flimage_open(filename.c_str());
194         if (!image_) {
195                 lyxerr[Debug::GRAPHICS]
196                         << "Unable to open image" << std::endl;
197                 finishedLoading(false);
198                 return;
199         }
200
201         // Set this now and we won't need to bother again.
202         image_->fill_color = packedcolor(LColor::graphicsbg);
203
204         // Used by the callback routines to return to this
205         image_->u_vdata = this;
206
207         // Begin the reading process.
208         flimage_read(image_);
209 }
210
211
212 bool xformsImage::setPixmap(Params const & params)
213 {
214         if (!image_ || params.display == NoDisplay)
215                 return false;
216
217         Display * display = fl_get_display();
218
219         if (pixmap_ && pixmap_status_ == PIXMAP_SUCCESS)
220                 XFreePixmap(display, pixmap_);
221
222         int color_key;
223         switch (params.display) {
224         case MonochromeDisplay:
225                 color_key = FL_IMAGE_MONO;
226                 break;
227         case GrayscaleDisplay:
228                 color_key = FL_IMAGE_GRAY;
229                 break;
230         case ColorDisplay:
231         default: // NoDisplay cannot happen!
232                 color_key = FL_IMAGE_RGB;
233                 break;
234         }
235
236         if (color_key != FL_IMAGE_RGB) {
237                 flimage_convert(image_, color_key, 0);
238         }
239
240         unsigned int fill = packedcolor(LColor::graphicsbg);
241         if (fill != image_->fill_color) {
242                 // the background color has changed.
243                 // Note that in grayscale/monochrome images the background is
244                 // grayed also, so this call will have no visible effect. Sorry!
245                 flimage_replace_pixel(image_, image_->fill_color, fill);
246                 image_->fill_color = fill;
247         }
248
249         image_->xdisplay = display;
250         Screen * screen  = ScreenOfDisplay(display, fl_screen);
251
252         pixmap_ = flimage_to_pixmap(image_, XRootWindowOfScreen(screen));
253         pixmap_status_ = pixmap_ ? PIXMAP_SUCCESS : PIXMAP_FAILED;
254
255         return pixmap_status_ == PIXMAP_SUCCESS;
256 }
257
258
259 void xformsImage::clip(Params const & params)
260 {
261         if (!image_)
262                 return;
263
264         if (params.bb.empty())
265                 // No clipping is necessary.
266                 return;
267
268         int const new_width  = params.bb.xr - params.bb.xl;
269         int const new_height = params.bb.yt - params.bb.yb;
270
271         // No need to check if the width, height are > 0 because the
272         // Bounding Box would be empty() in this case.
273         if (new_width > image_->w || new_height > image_->h)
274                 // Bounds are invalid.
275                 return;
276
277         if (new_width == image_->w && new_height == image_->h)
278                 // Bounds are unchanged.
279                 return;
280
281         int const xoffset_l = std::max(0, params.bb.xl);
282         int const xoffset_r = std::max(0, image_->w - params.bb.xr);
283         int const yoffset_t = std::max(0, image_->h - params.bb.yt);
284         int const yoffset_b = std::max(0, params.bb.yb);
285
286         flimage_crop(image_, xoffset_l, yoffset_t, xoffset_r, yoffset_b);
287 }
288
289
290 void xformsImage::rotate(Params const & params)
291 {
292         if (!image_)
293                 return ;
294
295         if (!params.angle)
296                 // No rotation is necessary.
297                 return;
298
299         // The angle passed to flimage_rotate is the angle in one-tenth of a
300         // degree units.
301
302         // Work around xforms bug when params.angle == 270
303         // the 'InternalError: bad special angle' error.
304         // This bug fix is not needed in xforms 1.0 and greater.
305         if (params.angle == 270) {
306                 flimage_rotate(image_,  900, FLIMAGE_SUBPIXEL);
307                 flimage_rotate(image_, 1800, FLIMAGE_SUBPIXEL);
308         } else {
309                 flimage_rotate(image_, params.angle * 10, FLIMAGE_SUBPIXEL);
310         }
311 }
312
313
314 void xformsImage::scale(Params const & params)
315 {
316         if (!image_)
317                 return;
318
319         unsigned int width;
320         unsigned int height;
321         boost::tie(width, height) = getScaledDimensions(params);
322
323         if (width == getWidth() && height == getHeight())
324                 // No scaling needed
325                 return;
326
327         flimage_scale(image_, width, height, FLIMAGE_SUBPIXEL);
328 }
329
330
331 void xformsImage::statusCB(string const & status_message)
332 {
333         if (status_message.empty())
334                 return;
335
336         if (prefixIs(status_message, "Done Reading")) {
337                 if (image_) {
338                         flimage_close(image_);
339                 }
340
341                 finishedLoading(true);
342         }
343 }
344
345
346 void xformsImage::errorCB(string const & error_message)
347 {
348         if (error_message.empty())
349                 return;
350
351         if (image_) {
352                 flimage_close(image_);
353         }
354
355         finishedLoading(false);
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 ? rtrim(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::xformsImage * ptr =
377                 static_cast<grfx::xformsImage *>(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 ? rtrim(s) : string();
389         if (str.empty())
390                 return;
391
392         lyxerr[Debug::GRAPHICS]
393                 << "xforms image loader. Error : " << str << std::endl;
394
395         grfx::xformsImage * ptr =
396                 static_cast<grfx::xformsImage *>(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         // It dies horribly with lots of older PostScript files.
427         // Easiest, therefore, to disable PS support and insist that a PS-type
428         // file is converted to a bitmap format.
429         // flimage_enable_ps();
430 #endif
431
432         flimage_enable_sgi();
433         flimage_enable_tiff();
434         flimage_enable_xbm();
435         flimage_enable_xwd();
436         flimage_enable_xpm();
437
438         // xforms stores this permanently (does not make a copy) so
439         // this should never be destroyed.
440         static FLIMAGE_SETUP setup;
441         setup.visual_cue    = status_report;
442         setup.error_message = error_report;
443         flimage_setup(&setup);
444 }
445
446
447 unsigned int packedcolor(LColor::color c)
448 {
449         string const x11color = lcolor.getX11Name(c);
450
451         Display * display = fl_get_display();
452         Colormap cmap     = fl_state[fl_get_vclass()].colormap;
453         XColor xcol;
454         XColor ccol;
455         if (XLookupColor(display, cmap, x11color.c_str(), &xcol, &ccol) == 0)
456                 // Unable to parse x11color.
457                 return FL_PACK(255,255,255);
458
459         // Note that X stores the RGB values in the range 0 - 65535
460         // whilst we require them in the range 0 - 255.
461         unsigned int const r = xcol.red   / 256;
462         unsigned int const g = xcol.green / 256;
463         unsigned int const b = xcol.blue  / 256;
464
465         return FL_PACK(r, g, b);
466 }
467
468 } // namespace anon