// -*- C++ -*-
// $RCSfile: imagecanvas.C,v $
// $Revision: 1.6 $
// $Author: langer $
// $Date: 2001/01/12 19:17:31 $
/* This software was produced by NIST, an agency of the U.S. government,
* and by statute is not subject to copyright in the United States.
* Recipients of this software assume all responsibilities associated
* with its operation, modification and maintenance. However, to
* facilitate maintenance we ask that before distributing modifed
* versions of this software, you first contact the authors at
* oof_manager@ctcms.nist.gov.
*/
#include "adaptmesh.h"
#include "colorutils.h"
#include "dashboard.h"
#include "goof.h"
#include "image.h"
#include "imagecanvas.h"
#include "imageform.h"
#include "mouseclick.h"
#include "noderubberband.h"
#include "rubberband.h"
#include <math.h>
#include <X11/Xlib.h>
ImageCanvas::ImageCanvas(ImageFormGfx *iform, int x, int y, int w, int h,
int iw, int ih, double scalefactor,
void (*scb)(const MeshCoord&, const MeshCoord&, void*))
: ScrollCanvas(iform->the_form, x, y, w, h, scb, iform),
imagew(iw), imageh(ih),
imageform(iform),
rubberband(0),
pixind_outofdate(true)
{
// make pixmap the same size as the image
resizepixmap(iw*scalefactor, ih*scalefactor);
setcoords(0, iw, 0, ih);
}
// Copy an Image onto the canvas
void ImageCanvas::loadimage(Image *image) {
imagew = image->query_width();
imageh = image->query_height();
ArrayIterator iter(*image);
Cell_coordinate cell;
if(imagew == pwidth() && imageh == pheight()) { // can copy point by point
while(iter(cell)) {
XSetForeground(gfxinfo.display(), gc(), (*image)[cell].pixel);
XPoint xp = xpoint(cell.x, cell.y);
XDrawPoint(gfxinfo.display(), pixmap(), gc(), xp.x, xp.y);
}
}
else {
while(iter(cell)) {
XSetForeground(gfxinfo.display(), gc(), (*image)[cell].pixel);
XPoint ul = xpoint(cell.x, cell.y+1);
XPoint lr = xpoint(cell.x+1, cell.y);
XFillRectangle(gfxinfo.display(), pixmap(), gc(),
ul.x, ul.y, lr.x - ul.x, lr.y - ul.y);
}
}
}
void ImageCanvas::draw_pixels(const Vec<Cell_coordinate> &pixels,
const Color &color)
{
XSetForeground(gfxinfo.display(), gc(), color.pixel);
if(imagew == pwidth() && imageh == pheight()) {
for(int i=0; i<pixels.capacity(); i++) {
XPoint xp = xpoint(pixels[i].x, pixels[i].y);
XDrawPoint(gfxinfo.display(), pixmap(), gc(), xp.x, xp.y);
}
}
else {
for(int i=0; i<pixels.capacity(); i++) {
XPoint ul = xpoint(pixels[i].x, pixels[i].y+1);
XPoint lr = xpoint(pixels[i].x+1, pixels[i].y);
XFillRectangle(gfxinfo.display(), pixmap(), gc(),
ul.x, ul.y, lr.x - ul.x, lr.y - ul.y);
}
}
}
void ImageCanvas::draw_pixel(const Cell_coordinate &pixel,
const Color &color)
{
XSetForeground(gfxinfo.display(), gc(), color.pixel);
if(imagew == pwidth() && imageh == pheight()) {
XPoint xp = xpoint(pixel.x, pixel.y);
XDrawPoint(gfxinfo.display(), pixmap(), gc(), xp.x, xp.y);
}
else {
XPoint ul = xpoint(pixel.x, pixel.y+1);
XPoint lr = xpoint(pixel.x+1, pixel.y);
XFillRectangle(gfxinfo.display(), pixmap(), gc(),
ul.x, ul.y, lr.x - ul.x, lr.y - ul.y);
}
}
void ImageCanvas::redraw() { // called by ScrollCanvas when image needs redrawn
imageform->draw();
}
//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//
// Mouse events are passed back to the ImageForm. The ImageForm only
// needs one mouse handler because it uses a MouseClick, although for
// generality the ScrollCanvas has three...
// min # of pixels the mouse has to move for a drag to be noticed
const int ImageCanvas::dragmove = 2;
void ImageCanvas::mouse(const MouseClick &click) {
garcon()->log(imageform->menucommand() + "mouse click = " +
to_charstring(click));
if(holding) return;
// Variables used during mouse motion don't have to be class
// members, since there is only one mouse.
static int panning = 0; // currently panning?
if(click.event == MOUSE_DN)
return; // don't do anything until mouse mv or up
if(click.event == MOUSE_UP) {
if(!downclick()) return; // up but not down?
if(rubberband) { // stop rubberbanding
delete rubberband;
rubberband = 0;
}
if(panning) { // stop panning
panning = 0;
return;
}
if(click.window != downclick().window) return;
if(click.button != downclick().button) return;
if(abs(click.xpoint.x - downclick().xpoint.x) > dragmove ||
abs(click.xpoint.y - downclick().xpoint.y) > dragmove)
imageform->current_dashboard()->draghandler(downclick(), click);
else
imageform->current_dashboard()->clickhandler(click);
return;
}
if(click.event == MOUSE_MV) {
if(click.button == 0) return; // ignore motion w/out button
switch(dragmode) {
case DM_RUBBERBAND:
if(!rubberband) {
switch(imageform->current_dashboard()->rubberbandtype) {
case RB_RECTANGLE:
rubberband =
new RectangleRubberBand(downclick().xpoint, click.xpoint,
window(),
imageform->rubberband_color.pixel);
break;
case RB_CIRCLE:
rubberband =
new CircleRubberBand(downclick().xpoint, click.xpoint,
window(), imageform->rubberband_color.pixel);
break;
case RB_ELLIPSE:
rubberband = new EllipseRubberBand(downclick().xpoint, click.xpoint,
window(),
imageform->rubberband_color.pixel);
break;
case RB_NODE:
{
AdaptiveMesh *mesh = imageform->goof->mesh();
if(!mesh)
rubberband = 0;
else
rubberband =
new NodeRubberBand(mesh, this,
downclick().coord, downclick().xpoint,
click.xpoint, window(),
imageform->rubberband_color.pixel);
break;
}
default:
rubberband = 0;
}
}
else
rubberband->redraw(click.xpoint);
break;
case DM_PAN:
if(click.button == 2) { // middle mouse
static int lastx, lasty;
if(!panning) {
lastx = downclick().xpoint.x;
lasty = downclick().xpoint.y;
panning = 1;
}
int x = click.xpoint.x;
int y = click.xpoint.y;
scroll(lastx-x, lasty-y);
lastx = x;
lasty = y;
}
break;
}
imageform->current_dashboard()->movehandler(downclick(), click);
}
return;
}
//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//
// MouseClick::coord or ScrollCanvas::physicalcoords() are inaccurate
// due to round off errors, and aren't good for precise single pixel
// positioning in the original image. So keep arrays of which pixel in
// the pixmap corresponds to which pixel in the image. Construct these
// arrays with the same arithmetic used to draw the pixmap.
Cell_coordinate ImageCanvas::mouselocation(const MouseClick &click) {
// make sure that arrays of pixel indices are up to date
if(pixind_x.capacity() != pwidth() || pixind_outofdate) {
pixind_x.resize(pwidth());
if(pwidth() == imagew)
for(int i=0; i<imagew; i++)
pixind_x[i] = i;
else
for(int i=0; i<imagew; i++) { // loop over pixels in image
// range of pixels in pixmap corresponding to this image pixel.
int jmin = int(((double)i)*pwidth()/imagew);
int jmax = int((i+1.0)*pwidth()/imagew);
for(int j=jmin; j<jmax; j++)
pixind_x[j] = i;
}
}
if(pixind_y.capacity() != pheight() || pixind_outofdate) {
pixind_y.resize(pheight());
if(pheight() == imageh)
for(int i=0; i<imageh; i++)
pixind_y[i] = imageh-i;
else
for(int i=0; i<imageh; i++) {
int jmax = int(pheight() - ((double) i)*pheight()/imageh);
int jmin = int(pheight() - (i+1.0)*pheight()/imageh);
for(int j=jmin; j<jmax; j++)
pixind_y[j] = i;
}
}
pixind_outofdate = false;
// make sure click is in bounds, or array lookup won't work.
Cell_coordinate where;
if(click.cell.x < 0 || click.cell.x > pwidth())
where.x = (short) click.coord.x;
else
where.x = pixind_x[click.cell.x];
if(click.cell.y < 0 || click.cell.y > pheight())
where.y = (short) click.coord.y;
else
where.y = pixind_y[click.cell.y];
return where;
}
void ImageCanvas::resizepixmap(int w, int h) {
pixind_outofdate = true;
ScrollCanvas::resizepixmap(w, h);
}
syntax highlighted by Code2HTML, v. 0.9.1