PNMPainter, PNMBrush

This commit is contained in:
David Rose 2007-02-02 19:49:14 +00:00
parent 07776f816b
commit 7732928c0e
12 changed files with 1344 additions and 81 deletions

View File

@ -12,23 +12,33 @@
#define SOURCES \
config_pnmimage.h \
pnmbitio.h \
pnmBrush.h pnmBrush.I \
pnmFileType.h pnmFileTypeRegistry.h pnmImage.I \
pnmImage.h pnmImageHeader.I pnmImageHeader.h pnmReader.I \
pnmImage.h pnmImageHeader.I pnmImageHeader.h \
pnmPainter.h pnmPainter.I \
pnmReader.I \
pnmReader.h pnmWriter.I pnmWriter.h pnmimage_base.h \
ppmcmap.h
#define INCLUDED_SOURCES \
config_pnmimage.cxx \
pnm-image-filter.cxx \
pnmbitio.cxx \
pnm-image-filter.cxx pnmFileType.cxx \
pnmBrush.cxx \
pnmFileType.cxx \
pnmFileTypeRegistry.cxx pnmImage.cxx pnmImageHeader.cxx \
pnmPainter.cxx \
pnmReader.cxx pnmWriter.cxx pnmimage_base.cxx \
ppmcmap.cxx
#define INSTALL_HEADERS \
config_pnmimage.h pnmFileType.h pnmFileTypeRegistry.h pnmImage.I \
pnmImage.h pnmImageHeader.I pnmImageHeader.h pnmReader.I \
pnmReader.h pnmWriter.I pnmWriter.h pnmimage_base.h
config_pnmimage.h \
pnmBrush.h pnmBrush.I \
pnmFileType.h pnmFileTypeRegistry.h pnmImage.I \
pnmImage.h pnmImageHeader.I pnmImageHeader.h \
pnmPainter.h pnmPainter.I \
pnmReader.I \
pnmReader.h pnmWriter.I pnmWriter.h pnmimage_base.h
#define IGATESCAN all

View File

@ -0,0 +1,55 @@
// Filename: pnmBrush.I
// Created by: drose (01Feb07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PNMBrush::Constructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
INLINE PNMBrush::
PNMBrush(double xc, double yc) : _xc(xc), _yc(yc) {
}
////////////////////////////////////////////////////////////////////
// Function: PNMBrush::get_xc
// Access: Protected
// Description: Returns the coordinates of the brush's center
// pixel. For a one-pixel brush, this will be (0.5,
// 0.5); for a centered two-pixel brush, this will be
// (1.0, 1.0); for a centered three-pixel brush, this
// will be (1.5, 1.5); and so on.
////////////////////////////////////////////////////////////////////
INLINE double PNMBrush::
get_xc() const {
return _xc;
}
////////////////////////////////////////////////////////////////////
// Function: PNMBrush::get_xc
// Access: Protected
// Description: Returns the coordinates of the brush's center
// pixel. For a one-pixel brush, this will be (0.5,
// 0.5); for a centered two-pixel brush, this will be
// (1.0, 1.0); for a centered three-pixel brush, this
// will be (1.5, 1.5); and so on.
////////////////////////////////////////////////////////////////////
INLINE double PNMBrush::
get_yc() const {
return _yc;
}

View File

@ -0,0 +1,422 @@
// Filename: pnmBrush.cxx
// Created by: drose (01Feb07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pnmBrush.h"
#include "config_pnmimage.h"
#include "cmath.h"
// A PNMTransparentBrush doesn't draw or fill anything.
class EXPCL_PANDA PNMTransparentBrush : public PNMBrush {
public:
PNMTransparentBrush() :
PNMBrush(0.0, 0.0) { }
virtual void draw(PNMImage &, int, int, double) {
}
virtual void fill(PNMImage &, int, int, int, int, int) {
}
};
// A PNMPixelBrush is a family of brushes that draw one pixel at a time.
class EXPCL_PANDA PNMPixelBrush : public PNMBrush {
protected:
PNMPixelBrush(const Colord &color) :
PNMBrush(0.5, 0.5), _rgb(color[0], color[1], color[2]), _a(color[3]) { }
RGBColord _rgb;
double _a;
};
// Arbitrarily sets the pixel to a particular color, with no antialiasing.
class EXPCL_PANDA PNMSetPixelBrush : public PNMPixelBrush {
public:
PNMSetPixelBrush(const Colord &color) : PNMPixelBrush(color) { }
virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
if (x >= 0 && x < image.get_x_size() &&
y >= 0 && y < image.get_y_size() &&
pixel_scale >= 0.5) {
image.set_xel(x, y, _rgb);
if (image.has_alpha()) {
image.set_alpha(x, y, _a);
}
}
}
virtual void fill(PNMImage &image, int xfrom, int xto, int y,
int xo, int yo) {
if (y >= 0 && y < image.get_y_size()) {
xfrom = max(xfrom, 0);
xto = min(xto, image.get_x_size() - 1);
for (int x = xfrom; x <= xto; ++x) {
image.set_xel(x, y, _rgb);
}
if (image.has_alpha()) {
for (int x = xfrom; x <= xto; ++x) {
image.set_alpha(x, y, _a);
}
}
}
}
};
// Blends the pixel in to the existing background.
class EXPCL_PANDA PNMBlendPixelBrush : public PNMPixelBrush {
public:
PNMBlendPixelBrush(const Colord &color) : PNMPixelBrush(color) { }
virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
if (x >= 0 && x < image.get_x_size() &&
y >= 0 && y < image.get_y_size()) {
image.blend(x, y, _rgb, _a * pixel_scale);
}
}
virtual void fill(PNMImage &image, int xfrom, int xto, int y,
int xo, int yo) {
if (y >= 0 && y < image.get_y_size()) {
xfrom = max(xfrom, 0);
xto = min(xto, image.get_x_size() - 1);
for (int x = xfrom; x <= xto; ++x) {
image.blend(x, y, _rgb, _a);
}
}
}
};
// Darkens the pixel in the existing background.
class EXPCL_PANDA PNMDarkenPixelBrush : public PNMPixelBrush {
public:
PNMDarkenPixelBrush(const Colord &color) : PNMPixelBrush(color) { }
virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
if (x >= 0 && x < image.get_x_size() &&
y >= 0 && y < image.get_y_size()) {
RGBColord rgb = image.get_xel(x, y);
RGBColord p;
p.set(min(1.0 - (1.0 - _rgb[0]) * pixel_scale, rgb[0]),
min(1.0 - (1.0 - _rgb[1]) * pixel_scale, rgb[1]),
min(1.0 - (1.0 - _rgb[2]) * pixel_scale, rgb[2]));
image.set_xel(x, y, p);
if (image.has_alpha()) {
double a = image.get_alpha(x, y);
image.set_alpha(x, y, min(1.0 - (1.0 - _a) * pixel_scale, a));
}
}
}
virtual void fill(PNMImage &image, int xfrom, int xto, int y,
int xo, int yo) {
if (y >= 0 && y < image.get_y_size()) {
xfrom = max(xfrom, 0);
xto = min(xto, image.get_x_size() - 1);
for (int x = xfrom; x <= xto; ++x) {
RGBColord rgb = image.get_xel(x, y);
RGBColord p;
p.set(min(_rgb[0], rgb[0]),
min(_rgb[1], rgb[1]),
min(_rgb[2], rgb[2]));
image.set_xel(x, y, p);
}
if (image.has_alpha()) {
for (int x = xfrom; x <= xto; ++x) {
double a = image.get_alpha(x, y);
image.set_alpha(x, y, min(_a, a));
}
}
}
}
};
// Lightens the pixel in the existing background.
class EXPCL_PANDA PNMLightenPixelBrush : public PNMPixelBrush {
public:
PNMLightenPixelBrush(const Colord &color) : PNMPixelBrush(color) { }
virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
if (x >= 0 && x < image.get_x_size() &&
y >= 0 && y < image.get_y_size()) {
RGBColord rgb = image.get_xel(x, y);
RGBColord p;
p.set(max(_rgb[0] * pixel_scale, rgb[0]),
max(_rgb[1] * pixel_scale, rgb[1]),
max(_rgb[2] * pixel_scale, rgb[2]));
image.set_xel(x, y, p);
if (image.has_alpha()) {
double a = image.get_alpha(x, y);
image.set_alpha(x, y, max(_a * pixel_scale, a));
}
}
}
virtual void fill(PNMImage &image, int xfrom, int xto, int y,
int xo, int yo) {
if (y >= 0 && y < image.get_y_size()) {
xfrom = max(xfrom, 0);
xto = max(xto, image.get_x_size() - 1);
for (int x = xfrom; x <= xto; ++x) {
RGBColord rgb = image.get_xel(x, y);
RGBColord p;
p.set(max(_rgb[0], rgb[0]),
max(_rgb[1], rgb[1]),
max(_rgb[2], rgb[2]));
image.set_xel(x, y, p);
}
if (image.has_alpha()) {
for (int x = xfrom; x <= xto; ++x) {
double a = image.get_alpha(x, y);
image.set_alpha(x, y, max(_a, a));
}
}
}
}
};
// A PNMImageBrush is a family of brushes that draw an image at a time.
class EXPCL_PANDA PNMImageBrush : public PNMBrush {
protected:
PNMImageBrush(const PNMImage &image, double xc, double yc) :
PNMBrush(xc, yc),
_image(image)
{
}
virtual void fill(PNMImage &image, int xfrom, int xto, int y,
int xo, int yo) {
if (y >= 0 && y < image.get_y_size()) {
xfrom = max(xfrom, 0);
xto = min(xto, image.get_x_size() - 1);
int x_pat = (xfrom + xo) % _image.get_x_size();
int y_pat = (y + yo) % _image.get_y_size();
// Copy the first (right half) of the image scanline.
int x = xfrom;
do_scanline(image, x, y, x_pat, y_pat, xto - x + 1, 1);
// Now repeatedly make more copies of the image scanline.
x += _image.get_x_size() - x_pat;
while (x <= xto) {
do_scanline(image, x, y, 0, y_pat, xto - x + 1, 1);
x += _image.get_x_size();
}
}
}
virtual void do_scanline(PNMImage &image, int xto, int yto,
int xfrom, int yfrom, int x_size, int y_size)=0;
PNMImage _image;
};
// Sets the pixels from the rectangular image, with no antialiasing.
class EXPCL_PANDA PNMSetImageBrush : public PNMImageBrush {
public:
PNMSetImageBrush(const PNMImage &image, double xc, double yc) :
PNMImageBrush(image, xc, yc) { }
virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
if (pixel_scale >= 0.5) {
image.copy_sub_image(_image, x, y);
}
}
virtual void do_scanline(PNMImage &image, int xto, int yto,
int xfrom, int yfrom, int x_size, int y_size) {
image.copy_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
}
};
// Blends the pixels in using alpha.
class EXPCL_PANDA PNMBlendImageBrush : public PNMImageBrush {
public:
PNMBlendImageBrush(const PNMImage &image, double xc, double yc) :
PNMImageBrush(image, xc, yc) { }
virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
image.blend_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
}
virtual void do_scanline(PNMImage &image, int xto, int yto,
int xfrom, int yfrom, int x_size, int y_size) {
image.blend_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
}
};
// Darkens the pixels
class EXPCL_PANDA PNMDarkenImageBrush : public PNMImageBrush {
public:
PNMDarkenImageBrush(const PNMImage &image, double xc, double yc) :
PNMImageBrush(image, xc, yc) { }
virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
image.darken_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
}
virtual void do_scanline(PNMImage &image, int xto, int yto,
int xfrom, int yfrom, int x_size, int y_size) {
image.darken_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
}
};
// Lightens the pixels
class EXPCL_PANDA PNMLightenImageBrush : public PNMImageBrush {
public:
PNMLightenImageBrush(const PNMImage &image, double xc, double yc) :
PNMImageBrush(image, xc, yc) { }
virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
image.lighten_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
}
virtual void do_scanline(PNMImage &image, int xto, int yto,
int xfrom, int yfrom, int x_size, int y_size) {
image.lighten_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
}
};
////////////////////////////////////////////////////////////////////
// Function: PNMBrush::Destructor
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
PNMBrush::
~PNMBrush() {
}
////////////////////////////////////////////////////////////////////
// Function: PNMBrush::make_transparent
// Access: Published, Static
// Description: Returns a new brush that does not paint anything.
// Can be used as either a pen or a fill brush to make
// borderless or unfilled shapes, respectively.
////////////////////////////////////////////////////////////////////
PT(PNMBrush) PNMBrush::
make_transparent() {
return new PNMTransparentBrush();
}
////////////////////////////////////////////////////////////////////
// Function: PNMBrush::make_pixel
// Access: Published, Static
// Description: Returns a new brush that paints a single pixel of the
// indicated color on a border, or paints a solid color
// in an interior.
////////////////////////////////////////////////////////////////////
PT(PNMBrush) PNMBrush::
make_pixel(const Colord &color, PNMBrush::BrushEffect effect) {
switch (effect) {
case BE_set:
return new PNMSetPixelBrush(color);
case BE_blend:
return new PNMBlendPixelBrush(color);
case BE_darken:
return new PNMDarkenPixelBrush(color);
case BE_lighten:
return new PNMLightenPixelBrush(color);
}
pnmimage_cat.error()
<< "**Invalid BrushEffect (" << (int)effect << ")**\n";
return new PNMSetPixelBrush(color);
}
////////////////////////////////////////////////////////////////////
// Function: PNMBrush::make_spot
// Access: Published, Static
// Description: Returns a new brush that paints a spot of the
// indicated color and radius. If fuzzy is true, the
// spot is fuzzy; otherwise, it is hard-edged.
////////////////////////////////////////////////////////////////////
PT(PNMBrush) PNMBrush::
make_spot(const Colord &color, double radius, bool fuzzy,
BrushEffect effect) {
Colord bg;
switch (effect) {
case BE_set:
bg.set(0, 0, 0, 0);
break;
case BE_blend:
bg.set(color[0], color[1], color[2], 0.0);
break;
case BE_darken:
bg.set(1, 1, 1, 1);
break;
case BE_lighten:
bg.set(0, 0, 0, 0);
break;
default:
pnmimage_cat.error()
<< "**Invalid BrushEffect (" << (int)effect << ")**\n";
}
int size = (int)cceil(radius * 2.0);
double half_size = (double)size * 0.5;
PNMImage spot(size, size, 4);
double r = half_size / radius;
if (fuzzy) {
spot.render_spot(color, bg, 0.0, r);
} else {
spot.render_spot(color, bg, r, r);
}
return make_image(spot, half_size, half_size, effect);
}
////////////////////////////////////////////////////////////////////
// Function: PNMBrush::make_image
// Access: Published, Static
// Description: Returns a new brush that paints with the indicated
// image. xc and yc indicate the pixel in the center of
// the brush.
//
// The brush makes a copy of the image; it is safe to
// deallocate or modify the image after making this
// call.
////////////////////////////////////////////////////////////////////
PT(PNMBrush) PNMBrush::
make_image(const PNMImage &image, double xc, double yc,
PNMBrush::BrushEffect effect) {
switch (effect) {
case BE_set:
return new PNMSetImageBrush(image, xc, yc);
case BE_blend:
return new PNMBlendImageBrush(image, xc, yc);
case BE_darken:
return new PNMDarkenImageBrush(image, xc, yc);
case BE_lighten:
return new PNMLightenImageBrush(image, xc, yc);
}
pnmimage_cat.error()
<< "**Invalid BrushEffect (" << (int)effect << ")**\n";
return new PNMSetImageBrush(image, xc, yc);
}

View File

@ -0,0 +1,80 @@
// Filename: pnmBrush.h
// Created by: drose (01Feb07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef PNMBRUSH_H
#define PNMBRUSH_H
#include "pandabase.h"
#include "referenceCount.h"
#include "pointerTo.h"
#include "luse.h"
class PNMImage;
////////////////////////////////////////////////////////////////////
// Class : PNMBrush
// Description : This class is used to control the shape and color of
// the drawing operations performed by a PNMPainter
// object.
//
// Normally, you don't create a PNMBrush directly;
// instead, use one of the static PNMBrush::make_*()
// methods provided here.
//
// A PNMBrush is used to draw the border of a polygon or
// rectangle, as well as for filling its interior. When
// it is used to draw a border, the brush is "smeared"
// over the border; when it is used to fill the
// interior, it is tiled through the interior.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PNMBrush : public ReferenceCount {
protected:
INLINE PNMBrush(double xc, double yc);
PUBLISHED:
virtual ~PNMBrush();
enum BrushEffect {
BE_set,
BE_blend,
BE_darken,
BE_lighten,
};
static PT(PNMBrush) make_transparent();
static PT(PNMBrush) make_pixel(const Colord &color, BrushEffect effect = BE_blend);
static PT(PNMBrush) make_spot(const Colord &color, double radius, bool fuzzy,
BrushEffect effect = BE_blend);
static PT(PNMBrush) make_image(const PNMImage &image, double xc, double yc,
BrushEffect effect = BE_blend);
public:
INLINE double get_xc() const;
INLINE double get_yc() const;
virtual void draw(PNMImage &image, int x, int y, double pixel_scale)=0;
virtual void fill(PNMImage &image, int xfrom, int xto, int y,
int xo, int yo)=0;
protected:
double _xc, _yc;
};
#include "pnmBrush.I"
#endif

View File

@ -805,3 +805,77 @@ INLINE xelval *PNMImage::
alpha_row(int y) const {
return _alpha + y * _x_size;
}
////////////////////////////////////////////////////////////////////
// Function: PNMImage::setup_sub_image
// Access: Private
// Description: Computes xmin, ymin, xmax, and ymax, based on the
// input parameters for copy_sub_image() and related
// methods.
////////////////////////////////////////////////////////////////////
INLINE void PNMImage::
setup_sub_image(const PNMImage &copy, int &xto, int &yto,
int &xfrom, int &yfrom, int &x_size, int &y_size,
int &xmin, int &ymin, int &xmax, int &ymax) {
if (x_size < 0) {
x_size = copy.get_x_size() - xfrom;
}
if (y_size < 0) {
y_size = copy.get_y_size() - yfrom;
}
if (xfrom < 0) {
xto += -xfrom;
x_size -= -xfrom;
xfrom = 0;
}
if (yfrom < 0) {
yto += -yfrom;
y_size -= -yfrom;
yfrom = 0;
}
if (xto < 0) {
xfrom += -xto;
x_size -= -xto;
xto = 0;
}
if (yto < 0) {
yfrom += -yto;
y_size -= -yto;
yto = 0;
}
x_size = min(x_size, copy.get_x_size() - xfrom);
y_size = min(y_size, copy.get_y_size() - yfrom);
xmin = xto;
ymin = yto;
xmax = min(xmin + x_size, get_x_size());
ymax = min(ymin + y_size, get_y_size());
}
////////////////////////////////////////////////////////////////////
// Function: PNMImage::compute_spot_pixel
// Access: Private, Static
// Description: Called by render_spot to compute the color of a
// single pixel, based in (the square of) its distance
// from the center.
////////////////////////////////////////////////////////////////////
INLINE void PNMImage::
compute_spot_pixel(Colord &c, double d2,
double min_radius, double max_radius,
const Colord &fg, const Colord &bg) {
double d = sqrt(d2);
if (d > max_radius) {
c = bg;
} else if (d > min_radius) {
d = (d - min_radius) / (max_radius - min_radius);
double d2 = d * d;
double t = (3.0 * d2) - (2.0 * d * d2);
c = fg + t * (bg - fg);
} else {
c = fg;
}
}

View File

@ -19,6 +19,7 @@
#include "pnmImage.h"
#include "pnmReader.h"
#include "pnmWriter.h"
#include "pnmBrush.h"
#include "config_pnmimage.h"
////////////////////////////////////////////////////////////////////
@ -100,10 +101,9 @@ clear(int x_size, int y_size, int num_channels,
void PNMImage::
copy_from(const PNMImage &copy) {
clear();
copy_header_from(copy);
if (copy.is_valid()) {
copy_header_from(copy);
if (has_alpha()) {
memcpy(_alpha, copy._alpha, sizeof(xelval) * _y_size * _x_size);
}
@ -132,6 +132,28 @@ copy_header_from(const PNMImageHeader &header) {
setup_rc();
}
////////////////////////////////////////////////////////////////////
// Function: PNMImage::take_from
// Access: Published
// Description: Move the contents of the other image into this one,
// and empty the other image.
////////////////////////////////////////////////////////////////////
void PNMImage::
take_from(PNMImage &orig) {
clear();
PNMImageHeader::operator = (orig);
setup_rc();
if (has_alpha()) {
_alpha = orig._alpha;
orig._alpha = NULL;
}
_array = orig._array;
orig._array = NULL;
orig.clear();
}
////////////////////////////////////////////////////////////////////
// Function: PNMImage::fill_val
// Access: Published
@ -581,39 +603,41 @@ blend(int x, int y, double r, double g, double b, double alpha) {
void PNMImage::
copy_sub_image(const PNMImage &copy, int xto, int yto,
int xfrom, int yfrom, int x_size, int y_size) {
if (xfrom < 0) {
xto += -xfrom;
xfrom = 0;
}
if (yfrom < 0) {
yto += -yfrom;
yfrom = 0;
}
int xmin, ymin, xmax, ymax;
setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
xmin, ymin, xmax, ymax);
x_size = (x_size < 0) ?
copy.get_x_size() :
min(x_size, copy.get_x_size() - xfrom);
y_size = (y_size < 0) ?
copy.get_y_size() :
min(y_size, copy.get_y_size() - yfrom);
int xmin = max(0, xto);
int ymin = max(0, yto);
int xmax = min(xmin + x_size, get_x_size());
int ymax = min(ymin + y_size, get_y_size());
int x, y;
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
set_xel(x, y, copy.get_xel(x - xmin + xfrom, y - ymin + yfrom));
}
}
if (has_alpha() && copy.has_alpha()) {
if (get_maxval() == copy.get_maxval()) {
// The simple case: no pixel value rescaling is required.
int x, y;
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
set_alpha(x, y, copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom));
set_xel_val(x, y, copy.get_xel_val(x - xmin + xfrom, y - ymin + yfrom));
}
}
if (has_alpha() && copy.has_alpha()) {
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
set_alpha_val(x, y, copy.get_alpha_val(x - xmin + xfrom, y - ymin + yfrom));
}
}
}
} else {
// The harder case: rescale pixel values according to maxval.
int x, y;
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
set_xel(x, y, copy.get_xel(x - xmin + xfrom, y - ymin + yfrom));
}
}
if (has_alpha() && copy.has_alpha()) {
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
set_alpha(x, y, copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom));
}
}
}
}
@ -627,44 +651,182 @@ copy_sub_image(const PNMImage &copy, int xto, int yto,
// the destination image, instead of overwriting pixels
// unconditionally.
//
// If the copy has no alpha channel, this degenerates
// into copy_sub_image().
// If pixel_scale is not 1.0, it specifies an amount to
// scale each *alpha* value of the source image before
// applying it to the target image.
//
// If pixel_scale is 1.0 and the copy has no alpha
// channel, this degenerates into copy_sub_image().
////////////////////////////////////////////////////////////////////
void PNMImage::
blend_sub_image(const PNMImage &copy, int xto, int yto,
int xfrom, int yfrom, int x_size, int y_size) {
if (!copy.has_alpha()) {
int xfrom, int yfrom, int x_size, int y_size,
double pixel_scale) {
if (!copy.has_alpha() && pixel_scale == 1.0) {
copy_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size);
return;
}
if (xfrom < 0) {
xto += -xfrom;
xfrom = 0;
}
if (yfrom < 0) {
yto += -yfrom;
yfrom = 0;
}
x_size = (x_size < 0) ?
copy.get_x_size() :
min(x_size, copy.get_x_size() - xfrom);
y_size = (y_size < 0) ?
copy.get_y_size() :
min(y_size, copy.get_y_size() - yfrom);
int xmin = max(0, xto);
int ymin = max(0, yto);
int xmax = min(xmin + x_size, get_x_size());
int ymax = min(ymin + y_size, get_y_size());
int xmin, ymin, xmax, ymax;
setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
xmin, ymin, xmax, ymax);
int x, y;
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
blend(x, y, copy.get_xel(x - xmin + xfrom, y - ymin + yfrom),
copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom));
if (copy.has_alpha()) {
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
blend(x, y, copy.get_xel(x - xmin + xfrom, y - ymin + yfrom),
copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale);
}
}
} else {
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
blend(x, y, copy.get_xel(x - xmin + xfrom, y - ymin + yfrom),
pixel_scale);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMImage::darken_sub_image
// Access: Published
// Description: Behaves like copy_sub_image(), but the resulting
// color will be the darker of the source and
// destination colors at each pixel (and at each R, G,
// B, A component value).
//
// If pixel_scale is not 1.0, it specifies an amount to
// scale each pixel value of the source image before
// applying it to the target image. The scale is
// applied with the center at 1.0: scaling the pixel
// value smaller brings it closer to 1.0.
////////////////////////////////////////////////////////////////////
void PNMImage::
darken_sub_image(const PNMImage &copy, int xto, int yto,
int xfrom, int yfrom, int x_size, int y_size,
double pixel_scale) {
int xmin, ymin, xmax, ymax;
setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
xmin, ymin, xmax, ymax);
if (get_maxval() == copy.get_maxval() && pixel_scale == 1.0) {
// The simple case: no pixel value rescaling is required.
int x, y;
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
xel c = copy.get_xel_val(x - xmin + xfrom, y - ymin + yfrom);
xel o = get_xel_val(x, y);
xel p;
PPM_ASSIGN(p, min(c.r, o.r), min(c.g, o.g), min(c.b, o.b));
set_xel_val(x, y, p);
}
}
if (has_alpha() && copy.has_alpha()) {
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
xelval c = copy.get_alpha_val(x - xmin + xfrom, y - ymin + yfrom);
xelval o = get_alpha_val(x, y);
set_alpha_val(x, y, min(c, o));
}
}
}
} else {
// The harder case: rescale pixel values according to maxval.
int x, y;
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
RGBColord c = copy.get_xel(x - xmin + xfrom, y - ymin + yfrom);
RGBColord o = get_xel(x, y);
RGBColord p;
p.set(min(1.0 - ((1.0 - c[0]) * pixel_scale), o[0]),
min(1.0 - ((1.0 - c[1]) * pixel_scale), o[1]),
min(1.0 - ((1.0 - c[2]) * pixel_scale), o[2]));
set_xel(x, y, p);
}
}
if (has_alpha() && copy.has_alpha()) {
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
double c = copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom);
double o = get_alpha(x, y);
set_alpha(x, y, min(1.0 - ((1.0 - c) * pixel_scale), o));
}
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMImage::lighten_sub_image
// Access: Published
// Description: Behaves like copy_sub_image(), but the resulting
// color will be the lighter of the source and
// destination colors at each pixel (and at each R, G,
// B, A component value).
//
// If pixel_scale is not 1.0, it specifies an amount to
// scale each pixel value of the source image before
// applying it to the target image.
////////////////////////////////////////////////////////////////////
void PNMImage::
lighten_sub_image(const PNMImage &copy, int xto, int yto,
int xfrom, int yfrom, int x_size, int y_size,
double pixel_scale) {
int xmin, ymin, xmax, ymax;
setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
xmin, ymin, xmax, ymax);
if (get_maxval() == copy.get_maxval() && pixel_scale == 1.0) {
// The simple case: no pixel value rescaling is required.
int x, y;
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
xel c = copy.get_xel_val(x - xmin + xfrom, y - ymin + yfrom);
xel o = get_xel_val(x, y);
xel p;
PPM_ASSIGN(p, max(c.r, o.r), max(c.g, o.g), max(c.b, o.b));
set_xel_val(x, y, p);
}
}
if (has_alpha() && copy.has_alpha()) {
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
xelval c = copy.get_alpha_val(x - xmin + xfrom, y - ymin + yfrom);
xelval o = get_alpha_val(x, y);
set_alpha_val(x, y, max(c, o));
}
}
}
} else {
// The harder case: rescale pixel values according to maxval.
int x, y;
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
RGBColord c = copy.get_xel(x - xmin + xfrom, y - ymin + yfrom);
RGBColord o = get_xel(x, y);
RGBColord p;
p.set(max(c[0] * pixel_scale, o[0]),
max(c[1] * pixel_scale, o[1]),
max(c[2] * pixel_scale, o[2]));
set_xel(x, y, p);
}
}
if (has_alpha() && copy.has_alpha()) {
for (y = ymin; y < ymax; y++) {
for (x = xmin; x < xmax; x++) {
double c = copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom);
double o = get_alpha(x, y);
set_alpha(x, y, max(c * pixel_scale, o));
}
}
}
}
}
@ -678,14 +840,18 @@ blend_sub_image(const PNMImage &copy, int xto, int yto,
//
// The min_radius and max_radius are in the scale 0..1,
// where 1.0 means the full width of the image. If
// min_radius == max_radius, there is no fuzzy edge;
// otherwise, the pixels between min_radius and
// max_radius are smoothly blended between fg and bg
// colors.
// min_radius == max_radius, the edge is sharp (but
// still antialiased); otherwise, the pixels between
// min_radius and max_radius are smoothly blended
// between fg and bg colors.
////////////////////////////////////////////////////////////////////
void PNMImage::
render_spot(const Colord &fg, const Colord &bg,
double min_radius, double max_radius) {
if (_x_size == 0 || _y_size == 0) {
return;
}
double x_scale = 2.0 / _x_size;
double y_scale = 2.0 / _y_size;
@ -701,27 +867,46 @@ render_spot(const Colord &fg, const Colord &bg,
for (int yi = 0; yi < y_center1; ++yi) {
double y = yi * y_scale;
double y2_inner = y * y;
double y2_outer = (y + y_scale) * (y + y_scale);
for (int xi = 0; xi < x_center1; ++xi) {
double x = xi * x_scale;
double d2 = (x * x + y * y);
if (d2 <= min_r2) {
double d2_inner = (x * x + y2_inner);
double d2_outer = ((x + x_scale) * (x + x_scale) + y2_outer);
double d2_a = ((x + x_scale) * (x + x_scale) + y2_inner);
double d2_b = (x * x + y2_outer);
if ((d2_inner <= min_r2) &&
(d2_outer <= min_r2) &&
(d2_a <= min_r2) &&
(d2_b <= min_r2)) {
// This pixel is solidly in the center of the spot.
set_xel_a(x_center1 - 1 - xi, y_center1 - 1 - yi, fg);
set_xel_a(x_center0 + xi, y_center1 - 1 - yi, fg);
set_xel_a(x_center1 - 1 - xi, y_center0 + yi, fg);
set_xel_a(x_center0 + xi, y_center0 + yi, fg);
} else if (d2 >= max_r2) {
} else if ((d2_inner > max_r2) &&
(d2_outer > max_r2) &&
(d2_a > max_r2) &&
(d2_b > max_r2)) {
// This pixel is solidly outside the spot.
set_xel_a(x_center1 - 1 - xi, y_center1 - 1 - yi, bg);
set_xel_a(x_center0 + xi, y_center1 - 1 - yi, bg);
set_xel_a(x_center1 - 1 - xi, y_center0 + yi, bg);
set_xel_a(x_center0 + xi, y_center0 + yi, bg);
} else {
double d = sqrt(d2);
d = (d - min_radius) / (max_radius - min_radius);
d2 = d * d;
double t = (3.0 * d2) - (2.0 * d * d2);
Colord c = fg + t * (bg - fg);
// This pixel is in a feathered area or along the antialiased edge.
Colord c_outer, c_inner, c_a, c_b;
compute_spot_pixel(c_outer, d2_outer, min_radius, max_radius, fg, bg);
compute_spot_pixel(c_inner, d2_inner, min_radius, max_radius, fg, bg);
compute_spot_pixel(c_a, d2_a, min_radius, max_radius, fg, bg);
compute_spot_pixel(c_b, d2_b, min_radius, max_radius, fg, bg);
// Now average all four pixels for the antialiased result.
Colord c;
c = (c_outer + c_inner + c_a + c_b) * 0.25;
set_xel_a(x_center1 - 1 - xi, y_center1 - 1 - yi, c);
set_xel_a(x_center0 + xi, y_center1 - 1 - yi, c);
@ -732,10 +917,34 @@ render_spot(const Colord &fg, const Colord &bg,
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMImage::expand_border
// Access: Published
// Description: Expands the image by the indicated number of pixels
// on each edge. The new pixels are set to the
// indicated color.
//
// If any of the values is negative, this actually crops
// the image.
////////////////////////////////////////////////////////////////////
void PNMImage::
expand_border(int left, int right, int bottom, int top,
const Colord &color) {
PNMImage new_image(get_x_size() + left + right,
get_y_size() + bottom + top,
get_num_channels(), get_maxval(), get_type());
new_image.fill(color[0], color[1], color[2]);
if (has_alpha()) {
new_image.alpha_fill(color[3]);
}
new_image.copy_sub_image(*this, left, top);
take_from(new_image);
}
////////////////////////////////////////////////////////////////////
// Function: PNMImage::setup_rc
// Access: Published
// Access: Private
// Description: Sets the _default_rc,bc,gc values appropriately
// according to the color type of the image, so that
// get_bright() will return a meaningful value for both

View File

@ -22,6 +22,7 @@
#include "pandabase.h"
#include "pnmImageHeader.h"
#include "pnmBrush.h"
#include "luse.h"
@ -77,6 +78,7 @@ PUBLISHED:
void copy_from(const PNMImage &copy);
void copy_header_from(const PNMImageHeader &header);
void take_from(PNMImage &orig);
INLINE void fill(double red, double green, double blue);
INLINE void fill(double gray = 0.0);
@ -181,17 +183,28 @@ PUBLISHED:
INLINE xel *operator [] (int y);
INLINE const xel *operator [] (int y) const;
void copy_sub_image(const PNMImage &copy, int xto, int yto,
int xfrom = 0, int yfrom = 0,
int x_size = -1, int y_size = -1);
void blend_sub_image(const PNMImage &copy, int xto, int yto,
int xfrom = 0, int yfrom = 0,
int x_size = -1, int y_size = -1);
int x_size = -1, int y_size = -1,
double pixel_scale = 1.0);
void darken_sub_image(const PNMImage &copy, int xto, int yto,
int xfrom = 0, int yfrom = 0,
int x_size = -1, int y_size = -1,
double pixel_scale = 1.0);
void lighten_sub_image(const PNMImage &copy, int xto, int yto,
int xfrom = 0, int yfrom = 0,
int x_size = -1, int y_size = -1,
double pixel_scale = 1.0);
void render_spot(const Colord &fg, const Colord &bg,
double min_radius, double max_radius);
void expand_border(int left, int right, int bottom, int top,
const Colord &color);
// The bodies for the non-inline *_filter() functions can be found
// in the file pnm-image-filter.cxx.
@ -210,6 +223,14 @@ private:
INLINE xel *row(int row) const;
INLINE xelval *alpha_row(int row) const;
INLINE void setup_sub_image(const PNMImage &copy, int &xto, int &yto,
int &xfrom, int &yfrom, int &x_size, int &y_size,
int &xmin, int &ymin, int &xmax, int &ymax);
INLINE static void compute_spot_pixel(Colord &c, double d2,
double min_radius, double max_radius,
const Colord &fg, const Colord &bg);
void setup_rc();
private:

View File

@ -0,0 +1,131 @@
// Filename: pnmPainter.I
// Created by: drose (02Feb07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::Destructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE PNMPainter::
~PNMPainter() {
}
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::set_pen
// Access: Published
// Description: Specifies a PNMBrush that will be used for drawing
// lines and edges. If the brush is a bitmap brush, its
// image will be smeared pixelwise along the line.
//
// Unlike the PNMImage passed to the constructor, the
// PNMPainter will take ownership of the pen. It is not
// necessary to keep a separate pointer to it.
////////////////////////////////////////////////////////////////////
INLINE void PNMPainter::
set_pen(PNMBrush *pen) {
_pen = pen;
}
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::get_pen
// Access: Published
// Description: Returns the current pen. See set_pen().
////////////////////////////////////////////////////////////////////
INLINE PNMBrush *PNMPainter::
get_pen() const {
return _pen;
}
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::set_fill
// Access: Published
// Description: Specifies a PNMBrush that will be used for filling
// in the interiors of objects. If the brush is a
// bitmap brush, its image will be tiled throughout the
// space.
//
// Unlike the PNMImage passed to the constructor, the
// PNMPainter will take ownership of the fill brush. It
// is not necessary to keep a separate pointer to it.
////////////////////////////////////////////////////////////////////
INLINE void PNMPainter::
set_fill(PNMBrush *fill) {
_fill = fill;
}
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::get_fill
// Access: Published
// Description: Returns the current fill brush. See set_fill().
////////////////////////////////////////////////////////////////////
INLINE PNMBrush *PNMPainter::
get_fill() const {
return _fill;
}
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::draw_point
// Access: Published
// Description: Draws an antialiased point on the PNMImage, using the
// current pen.
////////////////////////////////////////////////////////////////////
INLINE void PNMPainter::
draw_point(double x, double y) {
draw_line(x, y, x, y);
}
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::draw_hline_point
// Access: Private
// Description: Called within draw_line() to draw a single point of a
// mostly-horizontal line.
////////////////////////////////////////////////////////////////////
INLINE void PNMPainter::
draw_hline_point(int x, double xa, double ya, double xd, double yd,
double pixel_scale) {
double y = (yd * (x - xa) / xd) + ya;
int ymax = (int)cceil(y);
int ymin = (int)cfloor(y);
if (ymax == ymin) {
_pen->draw(_image, x, ymin, pixel_scale);
} else {
_pen->draw(_image, x, ymax, (y - ymin) * pixel_scale);
_pen->draw(_image, x, ymin, (ymax - y) * pixel_scale);
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::draw_vline_point
// Access: Private
// Description: Called within draw_line() to draw a single point of a
// mostly-vertical line.
////////////////////////////////////////////////////////////////////
INLINE void PNMPainter::
draw_vline_point(int y, double xa, double ya, double xd, double yd,
double pixel_scale) {
double x = (xd * (y - ya) / yd) + xa;
int xmax = (int)cceil(x);
int xmin = (int)cfloor(x);
if (xmax == xmin) {
_pen->draw(_image, xmin, y, pixel_scale);
} else {
_pen->draw(_image, xmax, y, (x - xmin) * pixel_scale);
_pen->draw(_image, xmin, y, (xmax - x) * pixel_scale);
}
}

View File

@ -0,0 +1,190 @@
// Filename: pnmPainter.cxx
// Created by: drose (02Feb07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pnmPainter.h"
#include "pnmBrush.h"
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::Constructor
// Access: Published
// Description: The constructor stores a pointer to the PNMImage you
// pass it, but it does not take ownership of the
// object; you are responsible for ensuring that the
// PNMImage does not destruct during the lifetime of the
// PNMPainter object.
//
// The xo, yo coordinates specify an optional offset for
// fill coordinates. If you are painting with a pattern
// fill, these specify the virtual coordinates of the
// upper-left corner of the image, which can allow you
// to adjust the pattern to line up with nested images,
// if necessary.
////////////////////////////////////////////////////////////////////
PNMPainter::
PNMPainter(PNMImage &image, int xo, int yo) :
_image(image),
_xo(xo), _yo(yo)
{
_pen = PNMBrush::make_pixel(Colord(0, 0, 0, 1));
_fill = PNMBrush::make_pixel(Colord(1, 1, 1, 1));
}
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::draw_line
// Access: Published
// Description: Draws an antialiased line on the PNMImage, using the
// current pen.
////////////////////////////////////////////////////////////////////
void PNMPainter::
draw_line(double xa, double ya, double xb, double yb) {
// Shift the line coordinates to position the center of the pen on
// the line.
xa -= (_pen->get_xc() - 0.5);
xb -= (_pen->get_xc() - 0.5);
ya -= (_pen->get_yc() - 0.5);
yb -= (_pen->get_yc() - 0.5);
// Compute the line delta.
double xd = xb - xa;
double yd = yb - ya;
if (xa == xb && ya == yb) {
// Just a single point. Treat it as a very short horizontal line.
xd = 1.0;
}
if (cabs(xd) > cabs(yd)) {
// This line is more horizontal than vertical.
if (xa < xb) {
// Draw the line from left to right.
int x_min = (int)cfloor(xa);
int x_max = (int)cceil(xb);
// The first point.
draw_hline_point(x_min, xa, ya, xd, yd, 1.0 - (xa - x_min));
// The middle points.
for (int x = x_min + 1; x < x_max; ++x) {
draw_hline_point(x, xa, ya, xd, yd, 1.0);
}
if (x_max != x_min) {
// The last point.
draw_hline_point(x_max, xa, ya, xd, yd, 1.0 - (x_max - xb));
}
} else {
// Draw the line from right to left.
int x_min = (int)cfloor(xb);
int x_max = (int)cceil(xa);
// The first point.
draw_hline_point(x_max, xa, ya, xd, yd, 1.0 - (x_max - xa));
// The middle points.
for (int x = x_max - 1; x > x_min; --x) {
draw_hline_point(x, xa, ya, xd, yd, 1.0);
}
if (x_max != x_min) {
// The last point.
draw_hline_point(x_min, xa, ya, xd, yd, 1.0 - (xb - x_min));
}
}
} else {
// This line is more vertical than horizontal.
if (ya < yb) {
// Draw the line from top to bottom.
int y_min = (int)cfloor(ya);
int y_max = (int)cceil(yb);
// The first point.
draw_vline_point(y_min, xa, ya, xd, yd, 1.0 - (ya - y_min));
// The middle points.
for (int y = y_min + 1; y < y_max; ++y) {
draw_vline_point(y, xa, ya, xd, yd, 1.0);
}
if (y_max != y_min) {
// The last point.
draw_vline_point(y_max, xa, ya, xd, yd, 1.0 - (y_max - yb));
}
} else {
// Draw the line from bottom to top.
int y_min = (int)cfloor(yb);
int y_max = (int)cceil(ya);
// The first point.
draw_vline_point(y_max, xa, ya, xd, yd, 1.0 - (y_max - ya));
// The middle points.
for (int y = y_max - 1; y > y_min; --y) {
draw_vline_point(y, xa, ya, xd, yd, 1.0);
}
if (y_max != y_min) {
// The last point.
draw_vline_point(y_min, xa, ya, xd, yd, 1.0 - (yb - y_min));
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMPainter::draw_rectangle
// Access: Published
// Description: Draws a filled rectangule on the PNMImage, using the
// current pen for the outline, and the current fill
// brush for the interior.
//
// The two coordinates specify any two diagonally
// opposite corners.
////////////////////////////////////////////////////////////////////
void PNMPainter::
draw_rectangle(double xa, double ya, double xb, double yb) {
// Make (xa, ya) be the upper-left corner, and (xb, yb) the
// lower-right.
if (xa > xb) {
double t = xa;
xa = xb;
xb = t;
}
if (ya > yb) {
double t = ya;
ya = yb;
yb = t;
}
// First, fill the interior.
int x_min = (int)cceil(xa);
int x_max = (int)cfloor(xb);
int y_min = (int)cceil(ya);
int y_max = (int)cfloor(yb);
for (int y = y_min; y <= y_max; ++y) {
_fill->fill(_image, x_min, x_max, y, _xo, _yo);
}
// Then, draw the outline.
draw_line(xa, ya, xa, yb);
draw_line(xa, yb, xb, yb);
draw_line(xb, yb, xb, ya);
draw_line(xb, ya, xa, ya);
}

View File

@ -0,0 +1,69 @@
// Filename: pnmPainter.h
// Created by: drose (02Feb07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef PNMPAINTER_H
#define PNMPAINTER_H
#include "pandabase.h"
class PNMImage;
////////////////////////////////////////////////////////////////////
// Class : PNMPainter
// Description : This class provides a number of convenient methods
// for painting drawings directly into a PNMImage.
//
// It stores a pointer to the PNMImage you pass it, but
// it does not take ownership of the object; you are
// responsible for ensuring that the PNMImage does not
// destruct during the lifetime of the PNMPainter
// object.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PNMPainter {
PUBLISHED:
PNMPainter(PNMImage &image, int xo = 0, int yo = 0);
INLINE ~PNMPainter();
INLINE void set_pen(PNMBrush *pen);
INLINE PNMBrush *get_pen() const;
INLINE void set_fill(PNMBrush *fill);
INLINE PNMBrush *get_fill() const;
INLINE void draw_point(double x, double y);
void draw_line(double xa, double ya, double xb, double yb);
void draw_rectangle(double xa, double ya, double xb, double yb);
private:
INLINE void draw_hline_point(int x, double xa, double ya,
double xd, double yd,
double pixel_scale);
INLINE void draw_vline_point(int y, double xa, double ya,
double xd, double yd,
double pixel_scale);
private:
PNMImage &_image;
int _xo, _yo;
PT(PNMBrush) _pen;
PT(PNMBrush) _fill;
};
#include "pnmPainter.I"
#endif

View File

@ -1,5 +1,6 @@
#include "config_pnmimage.cxx"
#include "pnm-image-filter.cxx"
#include "pnmbitio.cxx"
#include "pnmBrush.cxx"
#include "pnmFileType.cxx"

View File

@ -1,5 +1,6 @@
#include "pnmImage.cxx"
#include "pnmImageHeader.cxx"
#include "pnmPainter.cxx"
#include "pnmReader.cxx"
#include "pnmWriter.cxx"
#include "pnmFileTypeRegistry.cxx"