PNMPainter, PNMBrush
This commit is contained in:
parent
07776f816b
commit
7732928c0e
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 ©, 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ©) {
|
||||
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 ©, 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 ©, 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 ©, 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 ©, 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 ©, 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 ©, 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
|
||||
|
|
|
|||
|
|
@ -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 ©);
|
||||
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 ©, int xto, int yto,
|
||||
int xfrom = 0, int yfrom = 0,
|
||||
int x_size = -1, int y_size = -1);
|
||||
void blend_sub_image(const PNMImage ©, 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 ©, 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 ©, 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 ©, 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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#include "config_pnmimage.cxx"
|
||||
#include "pnm-image-filter.cxx"
|
||||
#include "pnmbitio.cxx"
|
||||
#include "pnmBrush.cxx"
|
||||
#include "pnmFileType.cxx"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "pnmImage.cxx"
|
||||
#include "pnmImageHeader.cxx"
|
||||
#include "pnmPainter.cxx"
|
||||
#include "pnmReader.cxx"
|
||||
#include "pnmWriter.cxx"
|
||||
#include "pnmFileTypeRegistry.cxx"
|
||||
|
|
|
|||
Loading…
Reference in New Issue