3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Angus Leeming (original Qt version)
7 * \author John Levon (original Qt version)
10 * Full author contact details are available in file CREDITS.
15 // Too hard to make concept checks work with this file
16 #ifdef _GLIBCXX_CONCEPT_CHECKS
17 #undef _GLIBCXX_CONCEPT_CHECKS
19 #ifdef _GLIBCPP_CONCEPT_CHECKS
20 #undef _GLIBCPP_CONCEPT_CHECKS
25 #include "LyXGdkImage.h"
30 #include "graphics/GraphicsParams.h"
32 #include "support/lstrings.h" // lowercase
34 #include <boost/bind.hpp>
35 #include <boost/tuple/tuple.hpp>
37 using lyx::support::lowercase;
50 /// Access to this class is through this static method.
51 Image::ImagePtr LyXGdkImage::newImage()
54 ptr.reset(new LyXGdkImage);
59 /// Return the list of loadable formats.
60 Image::FormatList LyXGdkImage::loadableFormats()
62 static FormatList fmts;
67 // The formats recognised by LyX
68 Formats::const_iterator begin = formats.begin();
69 Formats::const_iterator end = formats.end();
71 lyxerr[Debug::GRAPHICS]
72 << "\nThe image loader can load the following directly:\n";
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();
78 for (; it != gdk_end; ++it) {
79 Gdk::PixbufFormat thisformat = (*it);
80 lyxerr[Debug::GRAPHICS] << thisformat.get_name() << endl;
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 =
89 bind(equal_to<string>(),
90 bind(&Format::extension, _1),
93 fmts.push_back(fit->name());
97 if (lyxerr.debugging()) {
98 lyxerr[Debug::GRAPHICS]
99 << "\nOf these, LyX recognises the following formats:\n";
101 FormatList::const_iterator fbegin = fmts.begin();
102 FormatList::const_iterator fend = fmts.end();
103 for (FormatList::const_iterator fit = fbegin; fit != fend; ++fit) {
105 lyxerr[Debug::GRAPHICS] << ", ";
106 lyxerr[Debug::GRAPHICS] << *fit;
108 lyxerr[Debug::GRAPHICS] << '\n' << endl;
115 LyXGdkImage::LyXGdkImage()
121 LyXGdkImage::LyXGdkImage(LyXGdkImage const & other)
122 : Image(other), original_(other.original_),
123 transformed_(other.transformed_)
127 Image * LyXGdkImage::clone_impl() const
129 return new LyXGdkImage(*this);
133 unsigned int LyXGdkImage::getWidth_impl() const
135 return transformed_->get_width();
139 unsigned int LyXGdkImage::getHeight_impl() const
141 return transformed_->get_height();
145 void LyXGdkImage::load_impl(string const & filename)
148 lyxerr[Debug::GRAPHICS]
149 << "Image is loaded already!" << endl;
150 finishedLoading(false);
154 original_ = Gdk::Pixbuf::create_from_file(filename);
157 lyxerr[Debug::GRAPHICS]
158 << "Unable to open image" << endl;
159 finishedLoading(false);
163 transformed_ = original_;
164 finishedLoading(true);
168 bool LyXGdkImage::setPixmap_impl(Params const & params)
170 if (!original_ || params.display == NoDisplay)
173 switch (params.display) {
174 case GrayscaleDisplay: {
175 transformed_->saturate_and_pixelate (transformed_, 0.0, false);
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
188 guint8 const threshold = 50; // Completely arbitrary
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;
196 p[0] = p[1] = p[2] = 255;
211 void LyXGdkImage::clip_impl(Params const & params)
216 if (params.bb.empty())
217 // No clipping is necessary.
220 int const new_width = params.bb.xr - params.bb.xl;
221 int const new_height = params.bb.yt - params.bb.yb;
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.
230 if (new_width == original_->get_width() && new_height == original_->get_height())
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);
237 transformed_ = Gdk::Pixbuf::create_subpixbuf(original_,
238 xoffset_l, yoffset_t, new_width, new_height);
242 void LyXGdkImage::rotate_impl(Params const & params)
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;
259 transformed_ = transformed_->rotate_simple(rotation);
263 void LyXGdkImage::scale_impl(Params const & params)
270 boost::tie(width, height) = getScaledDimensions(params);
272 if (width == getWidth() && height == getHeight())
275 transformed_ = transformed_->scale_simple(
276 width, height, Gdk::INTERP_BILINEAR);
279 } // namespace graphics