]> git.lyx.org Git - lyx.git/blob - src/frontends/gtk/LyXGdkImage.C
enable Font cache only for MacOSX and inline width() for other platform.
[lyx.git] / src / frontends / gtk / LyXGdkImage.C
1 /**
2  * \file LyXGdkImage.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 (original Qt version)
7  * \author John Levon (original Qt version)
8  * \author John Spray
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 // Too hard to make concept checks work with this file
16 #ifdef _GLIBCXX_CONCEPT_CHECKS
17 #undef _GLIBCXX_CONCEPT_CHECKS
18 #endif
19 #ifdef _GLIBCPP_CONCEPT_CHECKS
20 #undef _GLIBCPP_CONCEPT_CHECKS
21 #endif
22
23
24
25 #include "LyXGdkImage.h"
26
27 #include "debug.h"
28 #include "format.h"
29
30 #include "graphics/GraphicsParams.h"
31
32 #include "support/lstrings.h"       // lowercase
33
34 #include <boost/bind.hpp>
35 #include <boost/tuple/tuple.hpp>
36
37 using lyx::support::lowercase;
38
39 using boost::bind;
40
41 using std::endl;
42 using std::equal_to;
43 using std::find_if;
44 using std::string;
45
46
47 namespace lyx {
48 namespace graphics {
49
50 /// Access to this class is through this static method.
51 Image::ImagePtr LyXGdkImage::newImage()
52 {
53         ImagePtr ptr;
54         ptr.reset(new LyXGdkImage);
55         return ptr;
56 }
57
58
59 /// Return the list of loadable formats.
60 Image::FormatList LyXGdkImage::loadableFormats()
61 {
62         static FormatList fmts;
63
64         if (!fmts.empty())
65                 return fmts;
66
67         // The formats recognised by LyX
68         Formats::const_iterator begin = formats.begin();
69         Formats::const_iterator end   = formats.end();
70
71         lyxerr[Debug::GRAPHICS]
72                 << "\nThe image loader can load the following directly:\n";
73
74         Gdk::Pixbuf::SListHandle_PixbufFormat gdkformats = Gdk::Pixbuf::get_formats();
75         Gdk::Pixbuf::SListHandle_PixbufFormat::iterator it = gdkformats.begin();
76         Gdk::Pixbuf::SListHandle_PixbufFormat::iterator gdk_end = gdkformats.end();
77
78         for (; it != gdk_end; ++it) {
79                 Gdk::PixbufFormat thisformat = (*it);
80                 lyxerr[Debug::GRAPHICS] << thisformat.get_name() << endl;
81
82                 std::vector<Glib::ustring> extensions = thisformat.get_extensions();
83                 std::vector<Glib::ustring>::const_iterator ext_end = extensions.end();
84                 std::vector<Glib::ustring>::iterator ext_it = extensions.begin();
85                 for (; ext_it != ext_end; ++ext_it) {
86                         std::string ext = lowercase(*ext_it);
87                         Formats::const_iterator fit =
88                                 find_if(begin, end,
89                                         bind(equal_to<string>(),
90                                              bind(&Format::extension, _1),
91                                              ext));
92                         if (fit != end)
93                                 fmts.push_back(fit->name());
94                 }
95         }
96
97         if (lyxerr.debugging()) {
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' << endl;
109         }
110
111         return fmts;
112 }
113
114
115 LyXGdkImage::LyXGdkImage()
116         : Image()
117 {
118 }
119
120
121 LyXGdkImage::LyXGdkImage(LyXGdkImage const & other)
122         : Image(other), original_(other.original_),
123           transformed_(other.transformed_)
124 {}
125
126
127 Image * LyXGdkImage::clone_impl() const
128 {
129         return new LyXGdkImage(*this);
130 }
131
132
133 unsigned int LyXGdkImage::getWidth_impl() const
134 {
135         return transformed_->get_width();
136 }
137
138
139 unsigned int LyXGdkImage::getHeight_impl() const
140 {
141         return transformed_->get_height();
142 }
143
144
145 void LyXGdkImage::load_impl(string const & filename)
146 {
147         if (original_) {
148                 lyxerr[Debug::GRAPHICS]
149                         << "Image is loaded already!" << endl;
150                 finishedLoading(false);
151                 return;
152         }
153
154         original_ = Gdk::Pixbuf::create_from_file(filename);
155
156         if (!original_){
157                 lyxerr[Debug::GRAPHICS]
158                         << "Unable to open image" << endl;
159                 finishedLoading(false);
160                 return;
161         }
162
163         transformed_ = original_;
164         finishedLoading(true);
165 }
166
167
168 bool LyXGdkImage::setPixmap_impl(Params const & params)
169 {
170         if (!original_ || params.display == NoDisplay)
171                 return false;
172
173         switch (params.display) {
174         case GrayscaleDisplay: {
175                 transformed_->saturate_and_pixelate (transformed_, 0.0, false);
176                 break;
177         }
178
179         case MonochromeDisplay: {
180                 int const rowstride = transformed_->get_rowstride();
181                 int const n_channels = transformed_->get_n_channels();
182                 int const width = transformed_->get_width();
183                 int const height = transformed_->get_height();
184                 int const bps = transformed_->get_bits_per_sample();
185                 guint8 * const pixels = transformed_->get_pixels();
186                 guint8 * p; // the current pixel
187                 
188                 guint8 const threshold = 50; // Completely arbitrary
189
190                 for( int y = 0; y < height; ++y) {
191                         for( int x = 0; x < width; ++x) {
192                                 p = pixels + y * rowstride + x * n_channels;
193                                 if ( (p[0] + p[1] + p[2])/bps < threshold)
194                                         p[0] = p[1] = p[2] = 0;
195                                 else
196                                         p[0] = p[1] = p[2] = 255;
197                         }
198                 }
199                 
200                 break;
201         }
202
203         default:
204                 break;
205         }
206
207         return true;
208 }
209
210
211 void LyXGdkImage::clip_impl(Params const & params)
212 {
213         if (!transformed_)
214                 return;
215
216         if (params.bb.empty())
217                 // No clipping is necessary.
218                 return;
219
220         int const new_width  = params.bb.xr - params.bb.xl;
221         int const new_height = params.bb.yt - params.bb.yb;
222
223         // No need to check if the width, height are > 0 because the
224         // Bounding Box would be empty() in this case.
225         if (new_width > original_->get_width() || new_height > original_->get_height()) {
226                 // Bounds are invalid.
227                 return;
228         }
229
230         if (new_width == original_->get_width() && new_height == original_->get_height())
231                 return;
232
233         int const xoffset_l = params.bb.xl;
234         int const yoffset_t = (original_->get_height() > int(params.bb.yt) ?
235                                original_->get_height() - params.bb.yt : 0);
236
237         transformed_ = Gdk::Pixbuf::create_subpixbuf(original_,
238                 xoffset_l, yoffset_t, new_width, new_height);
239 }
240
241
242 void LyXGdkImage::rotate_impl(Params const & params)
243 {
244         if (!transformed_)
245                 return;
246
247         if (!params.angle)
248                 return;
249
250         // TODO: allow free rotation
251         Gdk::PixbufRotation rotation = Gdk::PIXBUF_ROTATE_NONE;
252         if (params.angle == 90.0)
253                 rotation = Gdk::PIXBUF_ROTATE_COUNTERCLOCKWISE;
254         else if (params.angle == 180.0)
255                 rotation = Gdk::PIXBUF_ROTATE_UPSIDEDOWN;
256         else if (params.angle == 270.0)
257                 rotation = Gdk::PIXBUF_ROTATE_CLOCKWISE;
258
259         transformed_ = transformed_->rotate_simple(rotation);
260 }
261
262
263 void LyXGdkImage::scale_impl(Params const & params)
264 {
265         if (!transformed_)
266                 return;
267
268         unsigned int width;
269         unsigned int height;
270         boost::tie(width, height) = getScaledDimensions(params);
271
272         if (width == getWidth() && height == getHeight())
273                 return;
274
275         transformed_ = transformed_->scale_simple(
276                 width, height, Gdk::INTERP_BILINEAR);
277 }
278
279 } // namespace graphics
280 } // lyx