2002-09-05 14:10:50 +00:00
|
|
|
/**
|
2002-06-28 11:22:56 +00:00
|
|
|
* \file xformsImage.C
|
2002-09-05 15:14:23 +00:00
|
|
|
* This file is part of LyX, the document processor.
|
|
|
|
* Licence details can be found in the file COPYING.
|
2002-03-06 12:07:23 +00:00
|
|
|
*
|
2002-09-05 14:10:50 +00:00
|
|
|
* \author Angus Leeming
|
|
|
|
*
|
|
|
|
* Full author contact details are available in file CREDITS
|
2002-03-06 12:07:23 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#ifdef __GNUG__
|
|
|
|
#pragma implementation
|
|
|
|
#endif
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
#include "xformsImage.h"
|
2002-03-06 12:07:23 +00:00
|
|
|
#include "graphics/GraphicsParams.h"
|
|
|
|
#include "LColor.h"
|
|
|
|
#include "converter.h" // formats
|
|
|
|
#include "debug.h"
|
|
|
|
#include "support/LAssert.h"
|
|
|
|
#include "support/lyxfunctional.h" // compare_memfun
|
|
|
|
|
2002-06-13 13:43:51 +00:00
|
|
|
#include FORMS_H_LOCATION
|
|
|
|
|
|
|
|
#ifdef HAVE_FLIMAGE_H
|
|
|
|
# include <flimage.h>
|
|
|
|
#else
|
|
|
|
# ifdef HAVE_X11_FLIMAGE_H
|
|
|
|
# include <X11/flimage.h>
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
2002-07-09 13:39:46 +00:00
|
|
|
#include <boost/tuple/tuple.hpp>
|
|
|
|
|
2002-03-06 12:07:23 +00:00
|
|
|
using std::find_if;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
void init_graphics();
|
|
|
|
|
|
|
|
unsigned int packedcolor(LColor::color c);
|
|
|
|
|
|
|
|
} // namespace anon
|
|
|
|
|
|
|
|
|
|
|
|
namespace grfx {
|
|
|
|
|
|
|
|
/// Access to this class is through this static method.
|
2002-06-28 11:22:56 +00:00
|
|
|
Image::ImagePtr xformsImage::newImage()
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
|
|
|
init_graphics();
|
|
|
|
|
|
|
|
ImagePtr ptr;
|
2002-06-28 11:22:56 +00:00
|
|
|
ptr.reset(new xformsImage);
|
2002-03-06 12:07:23 +00:00
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Return the list of loadable formats.
|
2002-06-28 11:22:56 +00:00
|
|
|
Image::FormatList xformsImage::loadableFormats()
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
|
|
|
static FormatList fmts;
|
|
|
|
if (!fmts.empty())
|
|
|
|
return fmts;
|
|
|
|
|
|
|
|
init_graphics();
|
|
|
|
|
|
|
|
// The formats recognised by LyX
|
|
|
|
Formats::const_iterator begin = formats.begin();
|
|
|
|
Formats::const_iterator end = formats.end();
|
|
|
|
|
|
|
|
lyxerr[Debug::GRAPHICS]
|
|
|
|
<< "\nThe image loader can load the following directly:\n";
|
|
|
|
|
|
|
|
// Don't forget the Fortran numbering used by xforms!
|
|
|
|
int const nformats = flimage_get_number_of_formats();
|
|
|
|
for (int i = 1; i <= nformats; ++i) {
|
|
|
|
|
|
|
|
FLIMAGE_FORMAT_INFO const * info = flimage_get_format_info(i);
|
|
|
|
string const formal_name =
|
|
|
|
info->formal_name ? info->formal_name : string();
|
|
|
|
string ext =
|
|
|
|
info->extension ? info->extension : string();
|
|
|
|
|
|
|
|
if (ext.empty() || ext == "gz")
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ext == "rgb") ext = "sgi";
|
|
|
|
|
|
|
|
lyxerr[Debug::GRAPHICS]
|
|
|
|
<< formal_name << ", extension \"" << ext << "\"\n";
|
|
|
|
|
|
|
|
Formats::const_iterator it =
|
|
|
|
find_if(begin, end,
|
|
|
|
lyx::compare_memfun(&Format::extension, ext));
|
|
|
|
if (it != end)
|
|
|
|
fmts.push_back(it->name());
|
|
|
|
}
|
|
|
|
|
|
|
|
lyxerr[Debug::GRAPHICS]
|
|
|
|
<< "\nOf these, LyX recognises the following formats:\n";
|
|
|
|
|
|
|
|
FormatList::const_iterator fbegin = fmts.begin();
|
|
|
|
FormatList::const_iterator fend = fmts.end();
|
|
|
|
for (FormatList::const_iterator fit = fbegin; fit != fend; ++fit) {
|
|
|
|
if (fit != fbegin)
|
|
|
|
lyxerr[Debug::GRAPHICS] << ", ";
|
|
|
|
lyxerr[Debug::GRAPHICS] << *fit;
|
|
|
|
}
|
|
|
|
lyxerr[Debug::GRAPHICS] << '\n' << std::endl;
|
|
|
|
|
|
|
|
return fmts;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
xformsImage::xformsImage()
|
2002-03-06 12:07:23 +00:00
|
|
|
: image_(0),
|
|
|
|
pixmap_(0),
|
|
|
|
pixmap_status_(PIXMAP_UNINITIALISED)
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
xformsImage::xformsImage(xformsImage const & other)
|
|
|
|
: Image(other),
|
2002-03-06 12:07:23 +00:00
|
|
|
image_(0),
|
|
|
|
pixmap_(0),
|
|
|
|
pixmap_status_(PIXMAP_UNINITIALISED)
|
|
|
|
{
|
|
|
|
if (other.image_) {
|
|
|
|
image_ = flimage_dup(other.image_);
|
|
|
|
image_->u_vdata = this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
xformsImage::~xformsImage()
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
|
|
|
if (image_)
|
|
|
|
flimage_free(image_);
|
|
|
|
if (pixmap_)
|
2002-06-12 02:54:19 +00:00
|
|
|
XFreePixmap(fl_get_display(), pixmap_);
|
2002-03-06 12:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
Image * xformsImage::clone() const
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
2002-06-28 11:22:56 +00:00
|
|
|
return new xformsImage(*this);
|
2002-03-06 12:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
unsigned int xformsImage::getWidth() const
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
|
|
|
if (!image_)
|
|
|
|
return 0;
|
2002-07-09 13:39:46 +00:00
|
|
|
|
2002-10-21 11:59:46 +00:00
|
|
|
// Why, oh why, do we need such hacks?
|
2002-07-12 15:14:02 +00:00
|
|
|
// Angus 12 July 2002
|
2002-10-21 11:59:46 +00:00
|
|
|
return image_->w + 4;
|
2002-03-06 12:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
unsigned int xformsImage::getHeight() const
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
|
|
|
if (!image_)
|
|
|
|
return 0;
|
|
|
|
return image_->h;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-07-15 17:17:57 +00:00
|
|
|
bool xformsImage::isDrawable() const
|
|
|
|
{
|
|
|
|
return pixmap_;
|
|
|
|
}
|
|
|
|
|
2002-07-28 22:50:13 +00:00
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
Pixmap xformsImage::getPixmap() const
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
|
|
|
if (!pixmap_status_ == PIXMAP_SUCCESS)
|
|
|
|
return 0;
|
|
|
|
return pixmap_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
void xformsImage::load(string const & filename)
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
|
|
|
if (image_) {
|
|
|
|
lyxerr[Debug::GRAPHICS]
|
|
|
|
<< "Image is loaded already!" << std::endl;
|
2002-06-28 11:22:56 +00:00
|
|
|
finishedLoading(false);
|
2002-03-06 12:07:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
image_ = flimage_open(filename.c_str());
|
|
|
|
if (!image_) {
|
|
|
|
lyxerr[Debug::GRAPHICS]
|
|
|
|
<< "Unable to open image" << std::endl;
|
2002-06-28 11:22:56 +00:00
|
|
|
finishedLoading(false);
|
2002-03-06 12:07:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set this now and we won't need to bother again.
|
|
|
|
image_->fill_color = packedcolor(LColor::graphicsbg);
|
|
|
|
|
|
|
|
// Used by the callback routines to return to this
|
|
|
|
image_->u_vdata = this;
|
|
|
|
|
|
|
|
// Begin the reading process.
|
|
|
|
flimage_read(image_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
bool xformsImage::setPixmap(Params const & params)
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
2002-06-26 14:15:08 +00:00
|
|
|
if (!image_ || params.display == NoDisplay)
|
2002-03-06 12:07:23 +00:00
|
|
|
return false;
|
|
|
|
|
2002-06-12 02:54:19 +00:00
|
|
|
Display * display = fl_get_display();
|
2002-03-06 12:07:23 +00:00
|
|
|
|
|
|
|
if (pixmap_ && pixmap_status_ == PIXMAP_SUCCESS)
|
|
|
|
XFreePixmap(display, pixmap_);
|
|
|
|
|
|
|
|
int color_key;
|
|
|
|
switch (params.display) {
|
2002-06-26 14:15:08 +00:00
|
|
|
case MonochromeDisplay:
|
2002-03-06 12:07:23 +00:00
|
|
|
color_key = FL_IMAGE_MONO;
|
|
|
|
break;
|
2002-06-26 14:15:08 +00:00
|
|
|
case GrayscaleDisplay:
|
2002-03-06 12:07:23 +00:00
|
|
|
color_key = FL_IMAGE_GRAY;
|
|
|
|
break;
|
2002-06-26 14:15:08 +00:00
|
|
|
case ColorDisplay:
|
|
|
|
default: // NoDisplay cannot happen!
|
2002-03-06 12:07:23 +00:00
|
|
|
color_key = FL_IMAGE_RGB;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (color_key != FL_IMAGE_RGB) {
|
|
|
|
flimage_convert(image_, color_key, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int fill = packedcolor(LColor::graphicsbg);
|
|
|
|
if (fill != image_->fill_color) {
|
|
|
|
// the background color has changed.
|
|
|
|
// Note that in grayscale/monochrome images the background is
|
|
|
|
// grayed also, so this call will have no visible effect. Sorry!
|
|
|
|
flimage_replace_pixel(image_, image_->fill_color, fill);
|
|
|
|
image_->fill_color = fill;
|
|
|
|
}
|
|
|
|
|
|
|
|
image_->xdisplay = display;
|
2002-06-12 02:54:19 +00:00
|
|
|
Screen * screen = ScreenOfDisplay(display, fl_screen);
|
2002-03-06 12:07:23 +00:00
|
|
|
|
|
|
|
pixmap_ = flimage_to_pixmap(image_, XRootWindowOfScreen(screen));
|
|
|
|
pixmap_status_ = pixmap_ ? PIXMAP_SUCCESS : PIXMAP_FAILED;
|
|
|
|
|
|
|
|
return pixmap_status_ == PIXMAP_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
void xformsImage::clip(Params const & params)
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
|
|
|
if (!image_)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (params.bb.empty())
|
|
|
|
// No clipping is necessary.
|
|
|
|
return;
|
|
|
|
|
|
|
|
int const new_width = params.bb.xr - params.bb.xl;
|
|
|
|
int const new_height = params.bb.yt - params.bb.yb;
|
|
|
|
|
2002-04-08 16:51:46 +00:00
|
|
|
// No need to check if the width, height are > 0 because the
|
|
|
|
// Bounding Box would be empty() in this case.
|
|
|
|
if (new_width > image_->w || new_height > image_->h)
|
2002-03-06 12:07:23 +00:00
|
|
|
// Bounds are invalid.
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (new_width == image_->w && new_height == image_->h)
|
|
|
|
// Bounds are unchanged.
|
|
|
|
return;
|
|
|
|
|
2002-08-24 22:02:30 +00:00
|
|
|
// FIXME: these values are unsigned so this makes NO sense
|
|
|
|
|
|
|
|
int const xoffset_l = std::max(0U, params.bb.xl);
|
|
|
|
int const xoffset_r = std::max(0U, image_->w - params.bb.xr);
|
|
|
|
int const yoffset_t = std::max(0U, image_->h - params.bb.yt);
|
|
|
|
int const yoffset_b = std::max(0U, params.bb.yb);
|
2002-03-06 12:07:23 +00:00
|
|
|
|
|
|
|
flimage_crop(image_, xoffset_l, yoffset_t, xoffset_r, yoffset_b);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
void xformsImage::rotate(Params const & params)
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
|
|
|
if (!image_)
|
|
|
|
return ;
|
|
|
|
|
|
|
|
if (!params.angle)
|
|
|
|
// No rotation is necessary.
|
|
|
|
return;
|
|
|
|
|
|
|
|
// The angle passed to flimage_rotate is the angle in one-tenth of a
|
|
|
|
// degree units.
|
2002-05-07 10:03:07 +00:00
|
|
|
|
|
|
|
// Work around xforms bug when params.angle == 270
|
|
|
|
// the 'InternalError: bad special angle' error.
|
|
|
|
// This bug fix is not needed in xforms 1.0 and greater.
|
|
|
|
if (params.angle == 270) {
|
|
|
|
flimage_rotate(image_, 900, FLIMAGE_SUBPIXEL);
|
|
|
|
flimage_rotate(image_, 1800, FLIMAGE_SUBPIXEL);
|
|
|
|
} else {
|
2002-10-21 11:59:46 +00:00
|
|
|
flimage_rotate(image_,
|
|
|
|
int(params.angle * 10),
|
|
|
|
FLIMAGE_SUBPIXEL);
|
2002-05-07 10:03:07 +00:00
|
|
|
}
|
2002-03-06 12:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
void xformsImage::scale(Params const & params)
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
|
|
|
if (!image_)
|
|
|
|
return;
|
|
|
|
|
2002-07-09 13:39:46 +00:00
|
|
|
unsigned int width;
|
|
|
|
unsigned int height;
|
|
|
|
boost::tie(width, height) = getScaledDimensions(params);
|
2002-03-06 12:07:23 +00:00
|
|
|
|
|
|
|
if (width == getWidth() && height == getHeight())
|
|
|
|
// No scaling needed
|
|
|
|
return;
|
|
|
|
|
|
|
|
flimage_scale(image_, width, height, FLIMAGE_SUBPIXEL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
void xformsImage::statusCB(string const & status_message)
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
2002-06-28 11:22:56 +00:00
|
|
|
if (status_message.empty())
|
2002-03-06 12:07:23 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (prefixIs(status_message, "Done Reading")) {
|
2002-03-07 09:59:17 +00:00
|
|
|
if (image_) {
|
|
|
|
flimage_close(image_);
|
|
|
|
}
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
finishedLoading(true);
|
2002-03-06 12:07:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
void xformsImage::errorCB(string const & error_message)
|
2002-03-06 12:07:23 +00:00
|
|
|
{
|
2002-06-28 11:22:56 +00:00
|
|
|
if (error_message.empty())
|
2002-03-06 12:07:23 +00:00
|
|
|
return;
|
|
|
|
|
2002-03-07 09:59:17 +00:00
|
|
|
if (image_) {
|
|
|
|
flimage_close(image_);
|
|
|
|
}
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
finishedLoading(false);
|
2002-03-06 12:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace grfx
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
int status_report(FL_IMAGE * ob, const char *s)
|
|
|
|
{
|
|
|
|
lyx::Assert(ob && ob->u_vdata);
|
|
|
|
|
2002-07-28 22:50:13 +00:00
|
|
|
string const str = s ? rtrim(s) : string();
|
2002-03-06 12:07:23 +00:00
|
|
|
if (str.empty())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
lyxerr[Debug::GRAPHICS]
|
|
|
|
<< "xforms image loader. Status : " << str << std::endl;
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
grfx::xformsImage * ptr =
|
|
|
|
static_cast<grfx::xformsImage *>(ob->u_vdata);
|
2002-03-06 12:07:23 +00:00
|
|
|
ptr->statusCB(str);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void error_report(FL_IMAGE * ob, const char *s)
|
|
|
|
{
|
|
|
|
lyx::Assert(ob && ob->u_vdata);
|
|
|
|
|
2002-07-28 22:50:13 +00:00
|
|
|
string const str = s ? rtrim(s) : string();
|
2002-03-06 12:07:23 +00:00
|
|
|
if (str.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
lyxerr[Debug::GRAPHICS]
|
|
|
|
<< "xforms image loader. Error : " << str << std::endl;
|
|
|
|
|
2002-06-28 11:22:56 +00:00
|
|
|
grfx::xformsImage * ptr =
|
|
|
|
static_cast<grfx::xformsImage *>(ob->u_vdata);
|
2002-03-06 12:07:23 +00:00
|
|
|
ptr->errorCB(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // extern "C"
|
|
|
|
|
|
|
|
|
|
|
|
void init_graphics()
|
|
|
|
{
|
|
|
|
// Paranoia check
|
|
|
|
static bool initialised = false;
|
|
|
|
if (initialised)
|
|
|
|
return;
|
|
|
|
initialised = true;
|
|
|
|
|
|
|
|
flimage_enable_bmp();
|
|
|
|
flimage_enable_fits();
|
|
|
|
flimage_enable_gif();
|
2002-06-11 14:44:05 +00:00
|
|
|
#ifdef HAVE_FLIMAGE_ENABLE_JPEG
|
2002-03-06 12:07:23 +00:00
|
|
|
flimage_enable_jpeg();
|
2002-06-11 14:44:05 +00:00
|
|
|
#endif
|
2002-03-06 12:07:23 +00:00
|
|
|
|
|
|
|
// xforms itself uses pngtopnm to convert to a loadable format.
|
|
|
|
// We prefer to use our own conversion mechanism, therefore.
|
|
|
|
// flimage_enable_png();
|
|
|
|
|
|
|
|
flimage_enable_pnm();
|
|
|
|
|
|
|
|
#ifdef HAVE_FLIMAGE_ENABLE_PS
|
|
|
|
// xforms recognises PS but not EPS
|
2002-07-17 17:55:21 +00:00
|
|
|
// It dies horribly with lots of older PostScript files.
|
|
|
|
// Easiest, therefore, to disable PS support and insist that a PS-type
|
|
|
|
// file is converted to a bitmap format.
|
|
|
|
// flimage_enable_ps();
|
2002-03-06 12:07:23 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
flimage_enable_sgi();
|
|
|
|
flimage_enable_tiff();
|
|
|
|
flimage_enable_xbm();
|
|
|
|
flimage_enable_xwd();
|
|
|
|
flimage_enable_xpm();
|
|
|
|
|
2002-03-07 09:59:17 +00:00
|
|
|
// xforms stores this permanently (does not make a copy) so
|
|
|
|
// this should never be destroyed.
|
|
|
|
static FLIMAGE_SETUP setup;
|
2002-03-06 12:07:23 +00:00
|
|
|
setup.visual_cue = status_report;
|
|
|
|
setup.error_message = error_report;
|
|
|
|
flimage_setup(&setup);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int packedcolor(LColor::color c)
|
|
|
|
{
|
|
|
|
string const x11color = lcolor.getX11Name(c);
|
|
|
|
|
2002-06-12 02:54:19 +00:00
|
|
|
Display * display = fl_get_display();
|
|
|
|
Colormap cmap = fl_state[fl_get_vclass()].colormap;
|
2002-03-06 12:07:23 +00:00
|
|
|
XColor xcol;
|
|
|
|
XColor ccol;
|
|
|
|
if (XLookupColor(display, cmap, x11color.c_str(), &xcol, &ccol) == 0)
|
|
|
|
// Unable to parse x11color.
|
|
|
|
return FL_PACK(255,255,255);
|
|
|
|
|
|
|
|
// Note that X stores the RGB values in the range 0 - 65535
|
|
|
|
// whilst we require them in the range 0 - 255.
|
|
|
|
unsigned int const r = xcol.red / 256;
|
|
|
|
unsigned int const g = xcol.green / 256;
|
|
|
|
unsigned int const b = xcol.blue / 256;
|
|
|
|
|
|
|
|
return FL_PACK(r, g, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace anon
|