git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12761 a95241bf-73f2-0310-859d-f6bbb57e9c96
1535 lines
38 KiB
C++
1535 lines
38 KiB
C++
// Painter.cpp
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <Bitmap.h>
|
|
#include <GraphicsDefs.h>
|
|
#include <Region.h>
|
|
|
|
#include <agg_bezier_arc.h>
|
|
#include <agg_bounding_rect.h>
|
|
#include <agg_conv_curve.h>
|
|
#include <agg_conv_stroke.h>
|
|
#include <agg_ellipse.h>
|
|
#include <agg_path_storage.h>
|
|
#include <agg_rounded_rect.h>
|
|
#include <agg_span_image_filter_rgba32.h>
|
|
#include <agg_span_interpolator_linear.h>
|
|
|
|
#include "LayerData.h"
|
|
|
|
#include "AGGTextRenderer.h"
|
|
#include "DrawingMode.h"
|
|
#include "DrawingModeFactory.h"
|
|
#include "PatternHandler.h"
|
|
#include "RenderingBuffer.h"
|
|
#include "ShapeConverter.h"
|
|
#include "ServerBitmap.h"
|
|
#include "ServerFont.h"
|
|
|
|
#include "Painter.h"
|
|
|
|
|
|
#if ALIASED_DRAWING
|
|
// in this case, we _cannot_ use the outline rasterizer.
|
|
# define USE_OUTLINE_RASTERIZER 0
|
|
#else
|
|
// in this case, we can optionally use the outline rasterizer (faster).
|
|
// NOTE: The outline rasterizer is different from the "general purpose"
|
|
// rasterizer and can speed up the stroking of lines. It has some problems
|
|
// though, for example the butts of the lines are not anti-aliased. So we
|
|
// use the much more powerfull general purpose rasterizer, and live with the
|
|
// performance hit for now. See _StrokePath().
|
|
// NOTE: The outline rasterizer will still be used for lines with 1 pixel width!
|
|
# define USE_OUTLINE_RASTERIZER 0
|
|
#endif
|
|
|
|
int
|
|
roundf(float v)
|
|
{
|
|
if (v >= 0.0)
|
|
return (int)floorf(v + 0.5);
|
|
else
|
|
return (int)floorf(v - 0.5);
|
|
}
|
|
|
|
#define CHECK_CLIPPING if (!fValidClipping) return BRect(0, 0, -1, -1);
|
|
|
|
// constructor
|
|
Painter::Painter()
|
|
: fBuffer(NULL),
|
|
fPixelFormat(NULL),
|
|
fBaseRenderer(NULL),
|
|
fOutlineRenderer(NULL),
|
|
fOutlineRasterizer(NULL),
|
|
fScanline(NULL),
|
|
fRasterizer(NULL),
|
|
fRenderer(NULL),
|
|
fFontRendererSolid(NULL),
|
|
fFontRendererBin(NULL),
|
|
fLineProfile(),
|
|
fSubpixelPrecise(false),
|
|
fPenSize(1.0),
|
|
fClippingRegion(new BRegion()),
|
|
fValidClipping(false),
|
|
fDrawingMode(B_OP_COPY),
|
|
fAlphaSrcMode(B_PIXEL_ALPHA),
|
|
// fAlphaSrcMode(B_CONSTANT_ALPHA),
|
|
fAlphaFncMode(B_ALPHA_OVERLAY),
|
|
fPenLocation(0.0, 0.0),
|
|
fDrawingModeFactory(new DrawingModeFactory()),
|
|
fPatternHandler(new PatternHandler()),
|
|
fTextRenderer(new AGGTextRenderer()),
|
|
fLastFamilyAndStyle(0),
|
|
fLastRotation(0.0),
|
|
fLastShear(90.0)
|
|
{
|
|
if (fontserver && fontserver->GetSystemPlain())
|
|
fFont = *fontserver->GetSystemPlain();
|
|
|
|
_UpdateFont();
|
|
_UpdateLineWidth();
|
|
}
|
|
|
|
// destructor
|
|
Painter::~Painter()
|
|
{
|
|
_MakeEmpty();
|
|
|
|
delete fClippingRegion;
|
|
delete fDrawingModeFactory;
|
|
delete fPatternHandler;
|
|
delete fTextRenderer;
|
|
}
|
|
|
|
// #pragma mark -
|
|
|
|
// AttachToBuffer
|
|
void
|
|
Painter::AttachToBuffer(RenderingBuffer* buffer)
|
|
{
|
|
if (buffer && buffer->InitCheck() >= B_OK &&
|
|
// TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16, B_CMAP8 and B_GRAY8 :-[
|
|
(buffer->ColorSpace() == B_RGBA32 || buffer->ColorSpace() == B_RGB32)) {
|
|
// clean up previous stuff
|
|
_MakeEmpty();
|
|
|
|
fBuffer = new agg::rendering_buffer();
|
|
fBuffer->attach((uint8*)buffer->Bits(),
|
|
buffer->Width(),
|
|
buffer->Height(),
|
|
buffer->BytesPerRow());
|
|
|
|
fPixelFormat = new pixfmt(*fBuffer, fPatternHandler);
|
|
fPixelFormat->set_drawing_mode(fDrawingModeFactory->DrawingModeFor(fDrawingMode,
|
|
fAlphaSrcMode,
|
|
fAlphaFncMode,
|
|
false));
|
|
|
|
fBaseRenderer = new renderer_base(*fPixelFormat);
|
|
// attach our clipping region to the renderer, it keeps a pointer
|
|
fBaseRenderer->set_clipping_region(fClippingRegion);
|
|
|
|
// These are the AGG renderes and rasterizes which
|
|
// will be used for stroking paths
|
|
#if ALIASED_DRAWING
|
|
fOutlineRenderer = new outline_renderer_type(*fBaseRenderer);
|
|
fOutlineRasterizer = new outline_rasterizer_type(*fOutlineRenderer);
|
|
#else
|
|
fOutlineRenderer = new outline_renderer_type(*fBaseRenderer, fLineProfile);
|
|
fOutlineRasterizer = new outline_rasterizer_type(*fOutlineRenderer);
|
|
|
|
// attach our line profile to the renderer, it keeps a pointer
|
|
fOutlineRenderer->profile(fLineProfile);
|
|
#endif
|
|
// the renderer used for filling paths
|
|
fRenderer = new renderer_type(*fBaseRenderer);
|
|
fRasterizer = new rasterizer_type();
|
|
fScanline = new scanline_type();
|
|
|
|
#if ALIASED_DRAWING
|
|
fRasterizer->gamma(agg::gamma_threshold(0.5));
|
|
#endif
|
|
|
|
// These are renderers needed for drawing text
|
|
fFontRendererSolid = new font_renderer_solid_type(*fBaseRenderer);
|
|
fFontRendererBin = new font_renderer_bin_type(*fBaseRenderer);
|
|
|
|
_SetRendererColor(fPatternHandler->HighColor().GetColor32());
|
|
}
|
|
}
|
|
|
|
// DetachFromBuffer
|
|
void
|
|
Painter::DetachFromBuffer()
|
|
{
|
|
_MakeEmpty();
|
|
}
|
|
|
|
// SetDrawData
|
|
void
|
|
Painter::SetDrawData(const DrawData* data)
|
|
{
|
|
// for now...
|
|
SetPenSize(data->PenSize());
|
|
SetPenLocation(data->PenLocation());
|
|
SetFont(data->Font());
|
|
// if (data->clipReg) {
|
|
// ConstrainClipping(*data->clipReg);
|
|
// }
|
|
// any of these conditions means we need to use a different drawing
|
|
// mode instance
|
|
bool updateDrawingMode = !(data->GetPattern() == fPatternHandler->GetPattern()) ||
|
|
data->GetDrawingMode() != fDrawingMode ||
|
|
(data->GetDrawingMode() == B_OP_ALPHA && (data->AlphaSrcMode() != fAlphaSrcMode ||
|
|
data->AlphaFncMode() != fAlphaFncMode));
|
|
|
|
fDrawingMode = data->GetDrawingMode();
|
|
fAlphaSrcMode = data->AlphaSrcMode();
|
|
fAlphaFncMode = data->AlphaFncMode();
|
|
fPatternHandler->SetPattern(data->GetPattern());
|
|
|
|
if (updateDrawingMode)
|
|
_UpdateDrawingMode();
|
|
|
|
SetHighColor(data->HighColor().GetColor32());
|
|
SetLowColor(data->LowColor().GetColor32());
|
|
}
|
|
|
|
// #pragma mark -
|
|
|
|
// ConstrainClipping
|
|
void
|
|
Painter::ConstrainClipping(const BRegion& region)
|
|
{
|
|
*fClippingRegion = region;
|
|
fValidClipping = fClippingRegion->Frame().IsValid();
|
|
}
|
|
|
|
// SetHighColor
|
|
void
|
|
Painter::SetHighColor(const rgb_color& color)
|
|
{
|
|
fPatternHandler->SetHighColor(color);
|
|
if (*(fPatternHandler->GetR5Pattern()) == B_SOLID_HIGH)
|
|
_SetRendererColor(color);
|
|
}
|
|
|
|
// SetLowColor
|
|
void
|
|
Painter::SetLowColor(const rgb_color& color)
|
|
{
|
|
fPatternHandler->SetLowColor(color);;
|
|
if (*(fPatternHandler->GetR5Pattern()) == B_SOLID_LOW)
|
|
_SetRendererColor(color);
|
|
}
|
|
|
|
// SetPenSize
|
|
void
|
|
Painter::SetPenSize(float size)
|
|
{
|
|
if (fPenSize != size) {
|
|
fPenSize = size;
|
|
#if USE_OUTLINE_RASTERIZER
|
|
// NOTE: _UpdateLineWidth() updates the line profile which is quite a heavy resource!
|
|
// fortunately, we don't need it when using the general purpose rasterizer
|
|
_UpdateLineWidth();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// SetDrawingMode
|
|
void
|
|
Painter::SetDrawingMode(drawing_mode mode)
|
|
{
|
|
if (fDrawingMode != mode) {
|
|
fDrawingMode = mode;
|
|
_UpdateDrawingMode();
|
|
}
|
|
}
|
|
|
|
// SetBlendingMode
|
|
void
|
|
Painter::SetBlendingMode(source_alpha alphaSrcMode, alpha_function alphaFncMode)
|
|
{
|
|
if (fAlphaSrcMode != alphaSrcMode || fAlphaFncMode != alphaFncMode) {
|
|
fAlphaSrcMode = alphaSrcMode;
|
|
fAlphaFncMode = alphaFncMode;
|
|
if (fDrawingMode == B_OP_ALPHA)
|
|
_UpdateDrawingMode();
|
|
}
|
|
}
|
|
|
|
// SetPattern
|
|
void
|
|
Painter::SetPattern(const pattern& p)
|
|
{
|
|
if (!(p == *fPatternHandler->GetR5Pattern())) {
|
|
fPatternHandler->SetPattern(p);
|
|
_UpdateDrawingMode();
|
|
}
|
|
}
|
|
|
|
// SetPenLocation
|
|
void
|
|
Painter::SetPenLocation(const BPoint& location)
|
|
{
|
|
fPenLocation = location;
|
|
}
|
|
|
|
// SetFont
|
|
void
|
|
Painter::SetFont(const ServerFont& font)
|
|
{
|
|
fFont = font;
|
|
_UpdateFont();
|
|
}
|
|
|
|
// #pragma mark -
|
|
|
|
// StrokeLine
|
|
BRect
|
|
Painter::StrokeLine(BPoint a, BPoint b, DrawData* context)
|
|
{
|
|
// this happens independent of wether we actually draw something
|
|
context->SetPenLocation(b);
|
|
// NOTE: penlocation should be converted to the local
|
|
// coordinate of the view for which we draw here, after
|
|
// we have been used. Updating it could also be done somewhere
|
|
// else in the app_server code, but DrawString() needs to
|
|
// do this as well, and it is probably hard to calculate
|
|
// the correct location outside of AGGTextRenderer...
|
|
|
|
CHECK_CLIPPING
|
|
|
|
// "false" means not to do the pixel center offset,
|
|
// because it would mess up our optimized versions
|
|
_Transform(&a, false);
|
|
_Transform(&b, false);
|
|
|
|
SetPenSize(context->PenSize());
|
|
|
|
BRect touched(min_c(a.x, b.x), min_c(a.y, b.y),
|
|
max_c(a.x, b.x), max_c(a.y, b.y));
|
|
|
|
// This is supposed to stop right here if we can see
|
|
// that we're definitaly outside the clipping reagion.
|
|
// Extending by penSize like that is not really correct,
|
|
// but fast and only triggers unnecessary calculation
|
|
// in a few edge cases
|
|
touched.InsetBy(-(fPenSize - 1), -(fPenSize - 1));
|
|
if (!touched.Intersects(fClippingRegion->Frame())) {
|
|
touched.Set(0.0, 0.0, -1.0, -1.0);
|
|
return touched;
|
|
}
|
|
|
|
// SetHighColor(context->highcolor.GetColor32());
|
|
// SetLowColor(context->lowcolor.GetColor32());
|
|
// SetDrawingMode(context->draw_mode);
|
|
// SetBlendingMode(context->alphaSrcMode, context->alphaFncMode);
|
|
// fPatternHandler->SetPattern(context->patt);
|
|
SetDrawData(context);
|
|
|
|
// first, try an optimized version
|
|
if (fPenSize == 1.0 &&
|
|
(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
|
|
pattern pat = *fPatternHandler->GetR5Pattern();
|
|
if (pat == B_SOLID_HIGH &&
|
|
StraightLine(a, b, fPatternHandler->HighColor().GetColor32())) {
|
|
return _Clipped(touched);
|
|
} else if (pat == B_SOLID_LOW &&
|
|
StraightLine(a, b, fPatternHandler->LowColor().GetColor32())) {
|
|
return _Clipped(touched);
|
|
}
|
|
}
|
|
// do the pixel center offset here
|
|
a.x += 0.5;
|
|
a.y += 0.5;
|
|
b.x += 0.5;
|
|
b.y += 0.5;
|
|
|
|
agg::path_storage path;
|
|
path.move_to(a.x, a.y);
|
|
path.line_to(b.x, b.y);
|
|
|
|
touched = _StrokePath(path);
|
|
|
|
return _Clipped(touched);
|
|
}
|
|
|
|
// StrokeLine
|
|
BRect
|
|
Painter::StrokeLine(BPoint b, DrawData* context)
|
|
{
|
|
// TODO: move this function elsewhere
|
|
return StrokeLine(context->PenLocation(), b, context);
|
|
}
|
|
|
|
typedef union {
|
|
uint32 data32;
|
|
int8 data[4];
|
|
} color32;
|
|
|
|
// StraightLine
|
|
bool
|
|
Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const
|
|
{
|
|
if (fBuffer && fValidClipping) {
|
|
if (a.x == b.x) {
|
|
// vertical
|
|
uint8* dst = fBuffer->row(0);
|
|
uint32 bpr = fBuffer->stride();
|
|
int32 x = (int32)a.x;
|
|
dst += x * 4;
|
|
int32 y1 = (int32)min_c(a.y, b.y);
|
|
int32 y2 = (int32)max_c(a.y, b.y);
|
|
color32 color;
|
|
color.data[0] = c.blue;
|
|
color.data[1] = c.green;
|
|
color.data[2] = c.red;
|
|
color.data[3] = 255;
|
|
// draw a line, iterate over clipping boxes
|
|
fBaseRenderer->first_clip_box();
|
|
do {
|
|
if (fBaseRenderer->xmin() <= x &&
|
|
fBaseRenderer->xmax() >= x) {
|
|
int32 i = max_c(fBaseRenderer->ymin(), y1);
|
|
int32 end = min_c(fBaseRenderer->ymax(), y2);
|
|
uint8* handle = dst + i * bpr;
|
|
for (; i <= end; i++) {
|
|
*(uint32*)handle = color.data32;
|
|
handle += bpr;
|
|
}
|
|
}
|
|
} while (fBaseRenderer->next_clip_box());
|
|
|
|
return true;
|
|
|
|
} else if (a.y == b.y) {
|
|
// horizontal
|
|
uint8* dst = fBuffer->row(0);
|
|
uint32 bpr = fBuffer->stride();
|
|
int32 y = (int32)a.y;
|
|
dst += y * bpr;
|
|
int32 x1 = (int32)min_c(a.x, b.x);
|
|
int32 x2 = (int32)max_c(a.x, b.x);
|
|
color32 color;
|
|
color.data[0] = c.blue;
|
|
color.data[1] = c.green;
|
|
color.data[2] = c.red;
|
|
color.data[3] = 255;
|
|
// draw a line, iterate over clipping boxes
|
|
fBaseRenderer->first_clip_box();
|
|
do {
|
|
if (fBaseRenderer->ymin() <= y &&
|
|
fBaseRenderer->ymax() >= y) {
|
|
int32 i = max_c(fBaseRenderer->xmin(), x1);
|
|
int32 end = min_c(fBaseRenderer->xmax(), x2);
|
|
uint32* handle = (uint32*)(dst + i * 4);
|
|
for (; i <= end; i++) {
|
|
*handle++ = color.data32;
|
|
}
|
|
}
|
|
} while (fBaseRenderer->next_clip_box());
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// #pragma mark -
|
|
|
|
// StrokeTriangle
|
|
BRect
|
|
Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const
|
|
{
|
|
return _DrawTriangle(pt1, pt2, pt3, false);
|
|
}
|
|
|
|
// FillTriangle
|
|
BRect
|
|
Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const
|
|
{
|
|
return _DrawTriangle(pt1, pt2, pt3, true);
|
|
}
|
|
|
|
// StrokePolygon
|
|
BRect
|
|
Painter::StrokePolygon(const BPoint* ptArray, int32 numPts,
|
|
bool closed) const
|
|
{
|
|
return _DrawPolygon(ptArray, numPts, closed, false);
|
|
}
|
|
|
|
// FillPolygon
|
|
BRect
|
|
Painter::FillPolygon(const BPoint* ptArray, int32 numPts,
|
|
bool closed) const
|
|
{
|
|
return _DrawPolygon(ptArray, numPts, closed, true);
|
|
}
|
|
|
|
// StrokeBezier
|
|
BRect
|
|
Painter::StrokeBezier(const BPoint* controlPoints) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
agg::path_storage curve;
|
|
|
|
BPoint p1(controlPoints[0]);
|
|
BPoint p2(controlPoints[1]);
|
|
BPoint p3(controlPoints[2]);
|
|
BPoint p4(controlPoints[3]);
|
|
_Transform(&p1);
|
|
_Transform(&p2);
|
|
_Transform(&p3);
|
|
_Transform(&p4);
|
|
|
|
curve.move_to(p1.x, p1.y);
|
|
curve.curve4(p1.x, p1.y,
|
|
p2.x, p2.y,
|
|
p3.x, p3.y);
|
|
|
|
|
|
agg::conv_curve<agg::path_storage> path(curve);
|
|
|
|
return _StrokePath(path);
|
|
}
|
|
|
|
// FillBezier
|
|
BRect
|
|
Painter::FillBezier(const BPoint* controlPoints) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
agg::path_storage curve;
|
|
|
|
BPoint p1(controlPoints[0]);
|
|
BPoint p2(controlPoints[1]);
|
|
BPoint p3(controlPoints[2]);
|
|
BPoint p4(controlPoints[3]);
|
|
_Transform(&p1);
|
|
_Transform(&p2);
|
|
_Transform(&p3);
|
|
_Transform(&p4);
|
|
|
|
curve.move_to(p1.x, p1.y);
|
|
curve.curve4(p1.x, p1.y,
|
|
p2.x, p2.y,
|
|
p3.x, p3.y);
|
|
curve.close_polygon();
|
|
|
|
agg::conv_curve<agg::path_storage> path(curve);
|
|
|
|
return _FillPath(path);
|
|
}
|
|
|
|
// StrokeShape
|
|
BRect
|
|
Painter::StrokeShape(/*const */BShape* shape) const
|
|
{
|
|
return _DrawShape(shape, false);
|
|
}
|
|
|
|
// FillShape
|
|
BRect
|
|
Painter::FillShape(/*const */BShape* shape) const
|
|
{
|
|
return _DrawShape(shape, true);
|
|
}
|
|
|
|
// StrokeRect
|
|
BRect
|
|
Painter::StrokeRect(const BRect& r) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
// support invalid rects
|
|
BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom));
|
|
BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom));
|
|
_Transform(&a, false);
|
|
_Transform(&b, false);
|
|
|
|
// first, try an optimized version
|
|
if (fPenSize == 1.0 &&
|
|
(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
|
|
pattern p = *fPatternHandler->GetR5Pattern();
|
|
if (p == B_SOLID_HIGH) {
|
|
BRect rect(a, b);
|
|
StrokeRect(rect,
|
|
fPatternHandler->HighColor().GetColor32());
|
|
return _Clipped(rect);
|
|
} else if (p == B_SOLID_LOW) {
|
|
BRect rect(a, b);
|
|
StrokeRect(rect,
|
|
fPatternHandler->LowColor().GetColor32());
|
|
return _Clipped(rect);
|
|
}
|
|
}
|
|
|
|
if (fmodf(fPenSize, 2.0) != 0.0) {
|
|
// shift coords to center of pixels
|
|
a.x += 0.5;
|
|
a.y += 0.5;
|
|
b.x += 0.5;
|
|
b.y += 0.5;
|
|
}
|
|
|
|
agg::path_storage path;
|
|
path.move_to(a.x, a.y);
|
|
if (a.x == b.x || a.y == b.y) {
|
|
// special case rects with one pixel height or width
|
|
path.line_to(b.x, b.y);
|
|
} else {
|
|
path.line_to(b.x, a.y);
|
|
path.line_to(b.x, b.y);
|
|
path.line_to(a.x, b.y);
|
|
}
|
|
path.close_polygon();
|
|
|
|
return _StrokePath(path);
|
|
}
|
|
|
|
// StrokeRect
|
|
void
|
|
Painter::StrokeRect(const BRect& r, const rgb_color& c) const
|
|
{
|
|
StraightLine(BPoint(r.left, r.top),
|
|
BPoint(r.right - 1, r.top), c);
|
|
StraightLine(BPoint(r.right, r.top),
|
|
BPoint(r.right, r.bottom - 1), c);
|
|
StraightLine(BPoint(r.right, r.bottom),
|
|
BPoint(r.left + 1, r.bottom), c);
|
|
StraightLine(BPoint(r.left, r.bottom),
|
|
BPoint(r.left, r.top + 1), c);
|
|
}
|
|
|
|
// FillRect
|
|
BRect
|
|
Painter::FillRect(const BRect& r) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
// support invalid rects
|
|
BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom));
|
|
BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom));
|
|
_Transform(&a, false);
|
|
_Transform(&b, false);
|
|
|
|
// first, try an optimized version
|
|
if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) {
|
|
pattern p = *fPatternHandler->GetR5Pattern();
|
|
if (p == B_SOLID_HIGH) {
|
|
BRect rect(a, b);
|
|
FillRect(rect, fPatternHandler->HighColor().GetColor32());
|
|
return _Clipped(rect);
|
|
} else if (p == B_SOLID_LOW) {
|
|
BRect rect(a, b);
|
|
FillRect(rect, fPatternHandler->LowColor().GetColor32());
|
|
return _Clipped(rect);
|
|
}
|
|
}
|
|
|
|
// account for stricter interpretation of coordinates in AGG
|
|
// the rectangle ranges from the top-left (.0, .0)
|
|
// to the bottom-right (.9999, .9999) corner of pixels
|
|
b.x += 1.0;
|
|
b.y += 1.0;
|
|
|
|
agg::path_storage path;
|
|
path.move_to(a.x, a.y);
|
|
path.line_to(b.x, a.y);
|
|
path.line_to(b.x, b.y);
|
|
path.line_to(a.x, b.y);
|
|
path.close_polygon();
|
|
|
|
return _FillPath(path);
|
|
}
|
|
|
|
// FillRect
|
|
void
|
|
Painter::FillRect(const BRect& r, const rgb_color& c) const
|
|
{
|
|
if (fBuffer && fValidClipping) {
|
|
uint8* dst = fBuffer->row(0);
|
|
uint32 bpr = fBuffer->stride();
|
|
int32 left = (int32)r.left;
|
|
int32 top = (int32)r.top;
|
|
int32 right = (int32)r.right;
|
|
int32 bottom = (int32)r.bottom;
|
|
// get a 32 bit pixel ready with the color
|
|
color32 color;
|
|
color.data[0] = c.blue;
|
|
color.data[1] = c.green;
|
|
color.data[2] = c.red;
|
|
color.data[3] = 255;
|
|
// fill rects, iterate over clipping boxes
|
|
fBaseRenderer->first_clip_box();
|
|
do {
|
|
int32 x1 = max_c(fBaseRenderer->xmin(), left);
|
|
int32 x2 = min_c(fBaseRenderer->xmax(), right);
|
|
if (x1 <= x2) {
|
|
int32 y1 = max_c(fBaseRenderer->ymin(), top);
|
|
int32 y2 = min_c(fBaseRenderer->ymax(), bottom);
|
|
uint8* offset = dst + x1 * 4;
|
|
for (; y1 <= y2; y1++) {
|
|
uint32* handle = (uint32*)(offset + y1 * bpr);
|
|
for (int32 x = x1; x <= x2; x++) {
|
|
*handle++ = color.data32;
|
|
}
|
|
}
|
|
}
|
|
} while (fBaseRenderer->next_clip_box());
|
|
}
|
|
}
|
|
|
|
// StrokeRoundRect
|
|
BRect
|
|
Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
BPoint lt(r.left, r.top);
|
|
BPoint rb(r.right, r.bottom);
|
|
bool centerOffset = fPenSize == 1.0;
|
|
// TODO: use this when using _StrokePath()
|
|
// bool centerOffset = fmodf(fPenSize, 2.0) != 0.0;
|
|
_Transform(<, centerOffset);
|
|
_Transform(&rb, centerOffset);
|
|
|
|
if (fPenSize == 1.0) {
|
|
agg::rounded_rect rect;
|
|
rect.rect(lt.x, lt.y, rb.x, rb.y);
|
|
rect.radius(xRadius, yRadius);
|
|
|
|
return _StrokePath(rect);
|
|
} else {
|
|
// NOTE: This implementation might seem a little strange, but it makes
|
|
// stroked round rects look like on R5. A more correct way would be to use
|
|
// _StrokePath() as above (independent from fPenSize).
|
|
// The fact that the bounding box of the round rect is not enlarged
|
|
// by fPenSize/2 is actually on purpose, though one could argue it is unexpected.
|
|
|
|
// enclose the right and bottom edge
|
|
rb.x++;
|
|
rb.y++;
|
|
|
|
agg::rounded_rect outer;
|
|
outer.rect(lt.x, lt.y, rb.x, rb.y);
|
|
outer.radius(xRadius, yRadius);
|
|
|
|
fRasterizer->reset();
|
|
fRasterizer->add_path(outer);
|
|
|
|
// don't add an inner hole if the "size is negative", this avoids some
|
|
// defects that can be observed on R5 and could be regarded as a bug.
|
|
if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) {
|
|
agg::rounded_rect inner;
|
|
inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, rb.y - fPenSize);
|
|
inner.radius(max_c(0.0, xRadius - fPenSize), max_c(0.0, yRadius - fPenSize));
|
|
|
|
fRasterizer->add_path(inner);
|
|
}
|
|
|
|
// make the inner rect work as a hole
|
|
fRasterizer->filling_rule(agg::fill_even_odd);
|
|
|
|
agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer);
|
|
|
|
// reset to default
|
|
fRasterizer->filling_rule(agg::fill_non_zero);
|
|
|
|
return _Clipped(_BoundingBox(outer));
|
|
}
|
|
}
|
|
|
|
// FillRoundRect
|
|
BRect
|
|
Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
BPoint lt(r.left, r.top);
|
|
BPoint rb(r.right, r.bottom);
|
|
_Transform(<, false);
|
|
_Transform(&rb, false);
|
|
|
|
// account for stricter interpretation of coordinates in AGG
|
|
// the rectangle ranges from the top-left (.0, .0)
|
|
// to the bottom-right (.9999, .9999) corner of pixels
|
|
rb.x += 1.0;
|
|
rb.y += 1.0;
|
|
|
|
agg::rounded_rect rect;
|
|
rect.rect(lt.x, lt.y, rb.x, rb.y);
|
|
rect.radius(xRadius, yRadius);
|
|
|
|
return _FillPath(rect);
|
|
}
|
|
|
|
// StrokeEllipse
|
|
BRect
|
|
Painter::StrokeEllipse(BPoint center, float xRadius, float yRadius) const
|
|
{
|
|
return _DrawEllipse(center, xRadius, yRadius, false);
|
|
}
|
|
|
|
// FillEllipse
|
|
BRect
|
|
Painter::FillEllipse(BPoint center, float xRadius, float yRadius) const
|
|
{
|
|
return _DrawEllipse(center, xRadius, yRadius, true);
|
|
}
|
|
|
|
// StrokeArc
|
|
BRect
|
|
Painter::StrokeArc(BPoint center, float xRadius, float yRadius,
|
|
float angle, float span) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
_Transform(¢er);
|
|
|
|
double angleRad = (angle * PI) / 180.0;
|
|
double spanRad = (span * PI) / 180.0;
|
|
agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
|
|
-angleRad, -spanRad);
|
|
|
|
agg::conv_curve<agg::bezier_arc> path(arc);
|
|
path.approximation_scale(2.0);
|
|
|
|
return _StrokePath(path);
|
|
}
|
|
|
|
// FillArc
|
|
BRect
|
|
Painter::FillArc(BPoint center, float xRadius, float yRadius,
|
|
float angle, float span) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
_Transform(¢er);
|
|
|
|
double angleRad = (angle * PI) / 180.0;
|
|
double spanRad = (span * PI) / 180.0;
|
|
agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
|
|
-angleRad, -spanRad);
|
|
|
|
agg::conv_curve<agg::bezier_arc> segmentedArc(arc);
|
|
|
|
agg::path_storage path;
|
|
|
|
// build a new path by starting at the center point,
|
|
// then traversing the arc, then going back to the center
|
|
path.move_to(center.x, center.y);
|
|
|
|
segmentedArc.rewind(0);
|
|
double x;
|
|
double y;
|
|
unsigned cmd = segmentedArc.vertex(&x, &y);
|
|
while (!agg::is_stop(cmd)) {
|
|
path.line_to(x, y);
|
|
cmd = segmentedArc.vertex(&x, &y);
|
|
}
|
|
|
|
path.close_polygon();
|
|
|
|
return _FillPath(path);
|
|
}
|
|
|
|
// #pragma mark -
|
|
|
|
// DrawChar
|
|
BRect
|
|
Painter::DrawChar(char aChar)
|
|
{
|
|
// TODO: to be moved elsewhere
|
|
return DrawChar(aChar, fPenLocation);
|
|
}
|
|
|
|
// DrawChar
|
|
BRect
|
|
Painter::DrawChar(char aChar, BPoint baseLine)
|
|
{
|
|
// TODO: to be moved elsewhere
|
|
char wrapper[2];
|
|
wrapper[0] = aChar;
|
|
wrapper[1] = 0;
|
|
return DrawString(wrapper, 1, baseLine);
|
|
}
|
|
|
|
// DrawString
|
|
BRect
|
|
Painter::DrawString(const char* utf8String, uint32 length,
|
|
const escapement_delta* delta)
|
|
{
|
|
// TODO: to be moved elsewhere
|
|
return DrawString(utf8String, length, fPenLocation, delta);
|
|
}
|
|
|
|
// DrawString
|
|
BRect
|
|
Painter::DrawString(const char* utf8String, uint32 length,
|
|
BPoint baseLine, const escapement_delta* delta)
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
BRect bounds(0.0, 0.0, -1.0, -1.0);
|
|
|
|
SetPattern(B_SOLID_HIGH);
|
|
|
|
if (fBuffer) {
|
|
|
|
Transformable transform;
|
|
// TODO: convert BFont::Shear(), which is in degrees 45°...135° to whatever AGG is using
|
|
// transform.ShearBy(B_ORIGIN, fFont.Shear(), 0.0);
|
|
transform.RotateBy(B_ORIGIN, -fFont.Rotation());
|
|
transform.TranslateBy(baseLine);
|
|
|
|
BRect clippingFrame;
|
|
if (fClippingRegion)
|
|
clippingFrame = fClippingRegion->Frame();
|
|
|
|
bounds = fTextRenderer->RenderString(utf8String,
|
|
length,
|
|
fFontRendererSolid,
|
|
fFontRendererBin,
|
|
transform,
|
|
clippingFrame,
|
|
false,
|
|
&fPenLocation);
|
|
transform.Transform(&fPenLocation);
|
|
}
|
|
return _Clipped(bounds);
|
|
}
|
|
|
|
// DrawString
|
|
BRect
|
|
Painter::DrawString(const char* utf8String, const escapement_delta* delta)
|
|
{
|
|
// TODO: to be moved elsewhere
|
|
return DrawString(utf8String, strlen(utf8String), fPenLocation, delta);
|
|
}
|
|
|
|
// DrawString
|
|
BRect
|
|
Painter::DrawString(const char* utf8String, BPoint baseLine,
|
|
const escapement_delta* delta)
|
|
{
|
|
// TODO: to be moved elsewhere
|
|
return DrawString(utf8String, strlen(utf8String), baseLine, delta);
|
|
}
|
|
|
|
// #pragma mark -
|
|
|
|
// DrawBitmap
|
|
void
|
|
Painter::DrawBitmap(const BBitmap* bitmap,
|
|
BRect bitmapRect, BRect viewRect) const
|
|
{
|
|
if (bitmap && bitmap->IsValid()) {
|
|
// the native bitmap coordinate system
|
|
// (can have left top corner offset)
|
|
BRect actualBitmapRect(bitmap->Bounds());
|
|
|
|
agg::rendering_buffer srcBuffer;
|
|
srcBuffer.attach((uint8*)bitmap->Bits(),
|
|
(uint32)actualBitmapRect.IntegerWidth() + 1,
|
|
(uint32)actualBitmapRect.IntegerHeight() + 1,
|
|
bitmap->BytesPerRow());
|
|
|
|
_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect);
|
|
}
|
|
}
|
|
|
|
// DrawBitmap
|
|
void
|
|
Painter::DrawBitmap(const ServerBitmap* bitmap,
|
|
BRect bitmapRect, BRect viewRect) const
|
|
{
|
|
if (bitmap && bitmap->InitCheck()) {
|
|
// the native bitmap coordinate system
|
|
BRect actualBitmapRect(bitmap->Bounds());
|
|
|
|
agg::rendering_buffer srcBuffer;
|
|
srcBuffer.attach(bitmap->Bits(),
|
|
bitmap->Width(),
|
|
bitmap->Height(),
|
|
bitmap->BytesPerRow());
|
|
|
|
_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect);
|
|
}
|
|
}
|
|
|
|
// #pragma mark -
|
|
|
|
// FillRegion
|
|
BRect
|
|
Painter::FillRegion(const BRegion* region) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
BRegion copy(*region);
|
|
int32 count = copy.CountRects();
|
|
BRect touched = FillRect(copy.RectAt(0));
|
|
for (int32 i = 1; i < count; i++) {
|
|
touched = touched | FillRect(copy.RectAt(i));
|
|
}
|
|
return touched;
|
|
}
|
|
|
|
// InvertRect
|
|
BRect
|
|
Painter::InvertRect(const BRect& r) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
BRegion region(r);
|
|
if (fClippingRegion) {
|
|
region.IntersectWith(fClippingRegion);
|
|
}
|
|
// implementation only for B_RGB32 at the moment
|
|
int32 count = region.CountRects();
|
|
for (int32 i = 0; i < count; i++) {
|
|
_InvertRect32(region.RectAt(i));
|
|
}
|
|
return _Clipped(r);
|
|
}
|
|
|
|
// BoundingBox
|
|
BRect
|
|
Painter::BoundingBox(const char* utf8String, uint32 length,
|
|
const BPoint& baseLine) const
|
|
{
|
|
Transformable transform;
|
|
transform.TranslateBy(baseLine);
|
|
|
|
static BRect dummy;
|
|
return fTextRenderer->RenderString(utf8String,
|
|
length,
|
|
fFontRendererSolid,
|
|
fFontRendererBin,
|
|
transform, dummy, true);
|
|
}
|
|
|
|
// StringWidth
|
|
float
|
|
Painter::StringWidth(const char* utf8String, uint32 length) const
|
|
{
|
|
return fTextRenderer->StringWidth(utf8String, length);
|
|
}
|
|
|
|
// #pragma mark -
|
|
|
|
// _MakeEmpty
|
|
void
|
|
Painter::_MakeEmpty()
|
|
{
|
|
delete fBuffer;
|
|
fBuffer = NULL;
|
|
|
|
delete fPixelFormat;
|
|
fPixelFormat = NULL;
|
|
|
|
delete fBaseRenderer;
|
|
fBaseRenderer = NULL;
|
|
|
|
delete fOutlineRenderer;
|
|
fOutlineRenderer = NULL;
|
|
|
|
delete fOutlineRasterizer;
|
|
fOutlineRasterizer = NULL;
|
|
|
|
delete fScanline;
|
|
fScanline = NULL;
|
|
|
|
delete fRasterizer;
|
|
fRasterizer = NULL;
|
|
|
|
delete fRenderer;
|
|
fRenderer = NULL;
|
|
|
|
delete fFontRendererSolid;
|
|
fFontRendererSolid = NULL;
|
|
|
|
delete fFontRendererBin;
|
|
fFontRendererBin = NULL;
|
|
}
|
|
|
|
// _Transform
|
|
void
|
|
Painter::_Transform(BPoint* point, bool centerOffset) const
|
|
{
|
|
// rounding
|
|
if (!fSubpixelPrecise) {
|
|
// TODO: validate usage of floor() for values < 0
|
|
point->x = floorf(point->x);
|
|
point->y = floorf(point->y);
|
|
}
|
|
// this code is supposed to move coordinates to the center of pixels,
|
|
// as AGG considers (0,0) to be the "upper left corner" of a pixel,
|
|
// but BViews are less strict on those details
|
|
if (centerOffset) {
|
|
point->x += 0.5;
|
|
point->y += 0.5;
|
|
}
|
|
}
|
|
|
|
// _Transform
|
|
BPoint
|
|
Painter::_Transform(const BPoint& point, bool centerOffset) const
|
|
{
|
|
BPoint ret = point;
|
|
_Transform(&ret, centerOffset);
|
|
return ret;
|
|
}
|
|
|
|
// _Clipped
|
|
BRect
|
|
Painter::_Clipped(const BRect& rect) const
|
|
{
|
|
if (rect.IsValid()) {
|
|
return BRect(rect & fClippingRegion->Frame());
|
|
}
|
|
return BRect(rect);
|
|
}
|
|
|
|
// _UpdateFont
|
|
void
|
|
Painter::_UpdateFont()
|
|
{
|
|
// TODO: temporary work arround until ServerFont
|
|
// is guaranteed to be valid.
|
|
if (fFont.InitCheck() < B_OK)
|
|
return;
|
|
|
|
if (fLastFamilyAndStyle != fFont.GetFamilyAndStyle()
|
|
|| fFont.Rotation() != fLastRotation || fFont.Shear() != fLastShear) {
|
|
|
|
if (fTextRenderer->SetFont(fFont)) {
|
|
fLastFamilyAndStyle = fFont.GetFamilyAndStyle();
|
|
fLastRotation = fFont.Rotation();
|
|
fLastShear = fFont.Shear();
|
|
} else {
|
|
fprintf(stderr, "unable to set font\n");
|
|
}
|
|
} else {
|
|
// just update the size
|
|
fTextRenderer->SetPointSize(fFont.Size());
|
|
}
|
|
}
|
|
|
|
// _UpdateLineWidth
|
|
void
|
|
Painter::_UpdateLineWidth()
|
|
{
|
|
fLineProfile.width(fPenSize);
|
|
}
|
|
|
|
// _UpdateDrawingMode
|
|
void
|
|
Painter::_UpdateDrawingMode()
|
|
{
|
|
// The AGG renderers have their own color setting, however
|
|
// almost all drawing mode classes ignore the color given
|
|
// by the AGG renderer and use the colors from the PatternHandler
|
|
// instead. If we have a B_SOLID_* pattern, we can actually use
|
|
// the color in the renderer and special versions of drawing modes
|
|
// that don't use PatternHandler and are more efficient. This
|
|
// has been implemented for B_OP_COPY as of now, the last parameter
|
|
// to DrawingModeFor() is a flag if a special "solid" drawing mode
|
|
// should be used if available. In this case, _SetRendererColor()
|
|
// has to be called so that all internal colors in the renderes
|
|
// are up to date for use by the solid drawing mode version.
|
|
if (fPixelFormat) {
|
|
DrawingMode* mode = NULL;
|
|
pattern p = *fPatternHandler->GetR5Pattern();
|
|
if (p == B_SOLID_HIGH) {
|
|
// TODO: fix me! is already set in SetHighColor()
|
|
_SetRendererColor(fPatternHandler->HighColor().GetColor32());
|
|
mode = fDrawingModeFactory->DrawingModeFor(fDrawingMode,
|
|
fAlphaSrcMode,
|
|
fAlphaFncMode,
|
|
true);
|
|
} else if (p == B_SOLID_LOW) {
|
|
// TODO: fix me! is already set in SetLowColor()
|
|
_SetRendererColor(fPatternHandler->LowColor().GetColor32());
|
|
mode = fDrawingModeFactory->DrawingModeFor(fDrawingMode,
|
|
fAlphaSrcMode,
|
|
fAlphaFncMode,
|
|
true);
|
|
} else {
|
|
mode = fDrawingModeFactory->DrawingModeFor(fDrawingMode,
|
|
fAlphaSrcMode,
|
|
fAlphaFncMode,
|
|
false);
|
|
}
|
|
fPixelFormat->set_drawing_mode(mode);
|
|
}
|
|
|
|
}
|
|
|
|
// _SetRendererColor
|
|
void
|
|
Painter::_SetRendererColor(const rgb_color& color) const
|
|
{
|
|
|
|
if (fOutlineRenderer)
|
|
#if ALIASED_DRAWING
|
|
fOutlineRenderer->line_color(agg::rgba(color.red / 255.0,
|
|
color.green / 255.0,
|
|
color.blue / 255.0));
|
|
#else
|
|
fOutlineRenderer->color(agg::rgba(color.red / 255.0,
|
|
color.green / 255.0,
|
|
color.blue / 255.0));
|
|
#endif
|
|
if (fRenderer)
|
|
fRenderer->color(agg::rgba(color.red / 255.0,
|
|
color.green / 255.0,
|
|
color.blue / 255.0));
|
|
if (fFontRendererSolid)
|
|
fFontRendererSolid->color(agg::rgba(color.red / 255.0,
|
|
color.green / 255.0,
|
|
color.blue / 255.0));
|
|
if (fFontRendererBin)
|
|
fFontRendererBin->color(agg::rgba(color.red / 255.0,
|
|
color.green / 255.0,
|
|
color.blue / 255.0));
|
|
|
|
}
|
|
|
|
// #pragma mark -
|
|
|
|
// _DrawTriangle
|
|
inline BRect
|
|
Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
_Transform(&pt1);
|
|
_Transform(&pt2);
|
|
_Transform(&pt3);
|
|
|
|
agg::path_storage path;
|
|
|
|
path.move_to(pt1.x, pt1.y);
|
|
path.line_to(pt2.x, pt2.y);
|
|
path.line_to(pt3.x, pt3.y);
|
|
|
|
path.close_polygon();
|
|
|
|
if (fill)
|
|
return _FillPath(path);
|
|
else
|
|
return _StrokePath(path);
|
|
}
|
|
|
|
// _DrawEllipse
|
|
inline BRect
|
|
Painter::_DrawEllipse(BPoint center, float xRadius, float yRadius,
|
|
bool fill) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
// TODO: I think the conversion and the offset of
|
|
// pixel centers might not be correct here, and it
|
|
// might even be necessary to treat Fill and Stroke
|
|
// differently, as with Fill-/StrokeRect().
|
|
_Transform(¢er);
|
|
|
|
int32 divisions = (int32)max_c(12, (xRadius + yRadius + 2 * fPenSize) * PI / 2);
|
|
|
|
if (fill) {
|
|
agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions);
|
|
|
|
return _FillPath(path);
|
|
} else {
|
|
// NOTE: This implementation might seem a little strange, but it makes
|
|
// stroked ellipses look like on R5. A more correct way would be to use
|
|
// _StrokePath(), but it currently has its own set of problems with narrow
|
|
// ellipses (for small xRadii or yRadii).
|
|
float inset = fPenSize / 2.0;
|
|
agg::ellipse inner(center.x, center.y,
|
|
max_c(0.0, xRadius - inset),
|
|
max_c(0.0, yRadius - inset),
|
|
divisions);
|
|
agg::ellipse outer(center.x, center.y,
|
|
xRadius + inset,
|
|
yRadius + inset,
|
|
divisions);
|
|
|
|
fRasterizer->reset();
|
|
fRasterizer->add_path(outer);
|
|
fRasterizer->add_path(inner);
|
|
|
|
// make the inner ellipse work as a hole
|
|
fRasterizer->filling_rule(agg::fill_even_odd);
|
|
|
|
agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer);
|
|
|
|
// reset to default
|
|
fRasterizer->filling_rule(agg::fill_non_zero);
|
|
|
|
return _Clipped(_BoundingBox(outer));
|
|
}
|
|
}
|
|
|
|
// _DrawShape
|
|
inline BRect
|
|
Painter::_DrawShape(/*const */BShape* shape, bool fill) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
// TODO: untested
|
|
agg::path_storage path;
|
|
ShapeConverter converter(&path);
|
|
|
|
// offset locations to center of pixels
|
|
converter.TranslateBy(BPoint(0.5, 0.5));
|
|
|
|
converter.Iterate(shape);
|
|
|
|
if (fill)
|
|
return _FillPath(path);
|
|
else
|
|
return _StrokePath(path);
|
|
}
|
|
|
|
// _DrawPolygon
|
|
inline BRect
|
|
Painter::_DrawPolygon(const BPoint* ptArray, int32 numPts,
|
|
bool closed, bool fill) const
|
|
{
|
|
CHECK_CLIPPING
|
|
|
|
if (numPts > 0) {
|
|
|
|
agg::path_storage path;
|
|
BPoint point = _Transform(*ptArray);
|
|
path.move_to(point.x, point.y);
|
|
|
|
for (int32 i = 1; i < numPts; i++) {
|
|
ptArray++;
|
|
point = _Transform(*ptArray);
|
|
path.line_to(point.x, point.y);
|
|
}
|
|
|
|
if (closed)
|
|
path.close_polygon();
|
|
|
|
if (fill)
|
|
return _FillPath(path);
|
|
else
|
|
return _StrokePath(path);
|
|
}
|
|
return BRect(0.0, 0.0, -1.0, -1.0);
|
|
}
|
|
|
|
// _DrawBitmap
|
|
void
|
|
Painter::_DrawBitmap(const agg::rendering_buffer& srcBuffer, color_space format,
|
|
BRect actualBitmapRect, BRect bitmapRect, BRect viewRect) const
|
|
{
|
|
switch (format) {
|
|
case B_RGB32:
|
|
case B_RGBA32:
|
|
_DrawBitmap32(srcBuffer, actualBitmapRect, bitmapRect, viewRect);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Painter::_DrawBitmap() - non-native colorspace: %d\n", format);
|
|
#ifdef __HAIKU__
|
|
// TODO: this is only a temporary implementation,
|
|
// to really handle other colorspaces, one would
|
|
// rather do the conversion with much less overhead,
|
|
// for example in the nn filter (hm), or in the
|
|
// scanline generator
|
|
BBitmap temp(actualBitmapRect, 0, B_RGB32);
|
|
status_t err = temp.ImportBits(srcBuffer.buf(),
|
|
srcBuffer.height() * srcBuffer.stride(),
|
|
srcBuffer.stride(),
|
|
0, format);
|
|
if (err >= B_OK) {
|
|
agg::rendering_buffer convertedBuffer;
|
|
convertedBuffer.attach((uint8*)temp.Bits(),
|
|
(uint32)actualBitmapRect.IntegerWidth() + 1,
|
|
(uint32)actualBitmapRect.IntegerHeight() + 1,
|
|
temp.BytesPerRow());
|
|
_DrawBitmap32(convertedBuffer, actualBitmapRect, bitmapRect, viewRect);
|
|
} else {
|
|
fprintf(stderr, "Painter::_DrawBitmap() - colorspace conversion failed: %s\n", strerror(err));
|
|
}
|
|
#endif // __HAIKU__
|
|
break;
|
|
}
|
|
}
|
|
|
|
// _DrawBitmap32
|
|
void
|
|
Painter::_DrawBitmap32(const agg::rendering_buffer& srcBuffer,
|
|
BRect actualBitmapRect, BRect bitmapRect, BRect viewRect) const
|
|
{
|
|
typedef agg::span_allocator<agg::rgba8> span_alloc_type;
|
|
typedef agg::span_interpolator_linear<> interpolator_type;
|
|
typedef agg::span_image_filter_rgba32_nn<agg::order_bgra32,
|
|
interpolator_type> span_gen_type;
|
|
typedef agg::renderer_scanline_aa<renderer_base, span_gen_type> image_renderer_type;
|
|
|
|
if (bitmapRect.IsValid() && bitmapRect.Intersects(actualBitmapRect)
|
|
&& viewRect.IsValid()) {
|
|
|
|
// compensate for the lefttop offset the actualBitmapRect might have
|
|
// NOTE: I have no clue why enabling the next call gives a wrong result!
|
|
// According to the BeBook, bitmapRect is supposed to be in native
|
|
// bitmap space!
|
|
// bitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);
|
|
actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);
|
|
|
|
// calculate the scaling
|
|
double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1);
|
|
double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1);
|
|
|
|
// constrain rect to passed bitmap bounds
|
|
// and transfer the changes to the viewRect
|
|
if (bitmapRect.left < actualBitmapRect.left) {
|
|
float diff = actualBitmapRect.left - bitmapRect.left;
|
|
viewRect.left += diff * xScale;
|
|
bitmapRect.left = actualBitmapRect.left;
|
|
}
|
|
if (bitmapRect.top < actualBitmapRect.top) {
|
|
float diff = actualBitmapRect.top - bitmapRect.top;
|
|
viewRect.top += diff;
|
|
bitmapRect.top = actualBitmapRect.top;
|
|
}
|
|
if (bitmapRect.right > actualBitmapRect.right) {
|
|
float diff = bitmapRect.right - actualBitmapRect.right;
|
|
viewRect.right -= diff;
|
|
bitmapRect.right = actualBitmapRect.right;
|
|
}
|
|
if (bitmapRect.bottom > actualBitmapRect.bottom) {
|
|
float diff = bitmapRect.right - actualBitmapRect.bottom;
|
|
viewRect.bottom -= diff;
|
|
bitmapRect.bottom = actualBitmapRect.bottom;
|
|
}
|
|
|
|
float xOffset = viewRect.left - (bitmapRect.left * xScale);
|
|
float yOffset = viewRect.top - (bitmapRect.top * yScale);
|
|
|
|
agg::trans_affine srcMatrix;
|
|
// srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, -actualBitmapRect.top);
|
|
|
|
agg::trans_affine imgMatrix;
|
|
imgMatrix *= agg::trans_affine_scaling(xScale, yScale);
|
|
imgMatrix *= agg::trans_affine_translation(xOffset, yOffset);
|
|
imgMatrix.invert();
|
|
|
|
span_alloc_type sa;
|
|
interpolator_type interpolator(imgMatrix);
|
|
|
|
span_gen_type sg(sa, srcBuffer, agg::rgba(0, 0, 0, 0), interpolator);
|
|
|
|
image_renderer_type ri(*fBaseRenderer, sg);
|
|
|
|
agg::rasterizer_scanline_aa<> pf;
|
|
agg::scanline_u8 sl;
|
|
|
|
// path encloses image
|
|
agg::path_storage path;
|
|
path.move_to(viewRect.left, viewRect.top);
|
|
path.line_to(viewRect.right + 1, viewRect.top);
|
|
path.line_to(viewRect.right + 1, viewRect.bottom + 1);
|
|
path.line_to(viewRect.left, viewRect.bottom + 1);
|
|
path.close_polygon();
|
|
|
|
agg::conv_transform<agg::path_storage> tr(path, srcMatrix);
|
|
|
|
pf.add_path(tr);
|
|
agg::render_scanlines(pf, sl, ri);
|
|
}
|
|
}
|
|
|
|
// _InvertRect32
|
|
void
|
|
Painter::_InvertRect32(BRect r) const
|
|
{
|
|
if (fBuffer) {
|
|
int32 width = r.IntegerWidth() + 1;
|
|
for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) {
|
|
uint8* dst = fBuffer->row(y);
|
|
dst += (int32)r.left * 4;
|
|
for (int32 i = 0; i < width; i++) {
|
|
dst[0] = 255 - dst[0];
|
|
dst[1] = 255 - dst[1];
|
|
dst[2] = 255 - dst[2];
|
|
dst += 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// #pragma mark -
|
|
|
|
template<class VertexSource>
|
|
BRect
|
|
Painter::_BoundingBox(VertexSource& path) const
|
|
{
|
|
double left = 0.0;
|
|
double top = 0.0;
|
|
double right = -1.0;
|
|
double bottom = -1.0;
|
|
uint32 pathID[1];
|
|
pathID[0] = 0;
|
|
agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom);
|
|
return BRect(left, top, right, bottom);
|
|
}
|
|
|
|
|
|
// _StrokePath
|
|
template<class VertexSource>
|
|
BRect
|
|
Painter::_StrokePath(VertexSource& path) const
|
|
{
|
|
#if USE_OUTLINE_RASTERIZER
|
|
fOutlineRasterizer->add_path(path);
|
|
#else
|
|
// if (fPenSize > 1.0) {
|
|
agg::conv_stroke<VertexSource> stroke(path);
|
|
// TODO: Investigate this for shapes. Maybe we are supposed to
|
|
// use the settings from DrawData! Would make some sense!
|
|
// stroke.line_join(agg::round_join);
|
|
// stroke.line_cap(agg::butt_cap);
|
|
stroke.width(fPenSize);
|
|
|
|
fRasterizer->reset();
|
|
fRasterizer->add_path(stroke);
|
|
agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer);
|
|
// } else {
|
|
// TODO: update to AGG 2.3 to get rid of the remaining problems:
|
|
// rects which are 2 or 1 pixel high/wide don't render at all.
|
|
// fOutlineRasterizer->add_path(path);
|
|
// }
|
|
#endif
|
|
|
|
BRect touched = _BoundingBox(path);
|
|
float penSize = ceilf(fPenSize / 2.0);
|
|
touched.InsetBy(-penSize, -penSize);
|
|
|
|
return _Clipped(touched);
|
|
}
|
|
|
|
// _FillPath
|
|
template<class VertexSource>
|
|
BRect
|
|
Painter::_FillPath(VertexSource& path) const
|
|
{
|
|
fRasterizer->reset();
|
|
fRasterizer->add_path(path);
|
|
agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer);
|
|
|
|
return _Clipped(_BoundingBox(path));
|
|
}
|
|
|