Finished the optimization for vertical gradients. This code is about
8.3 times faster than using the AGG pipeline, but about 3 times slower than drawing a solid color square. It can probably still be improved. The second version of _MakeGradient() is more powerful than the old one and should replace it. It handles some corner cases, which the other one does not. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29220 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -864,20 +864,36 @@ Painter::FillRect(const BRect& r, const BGradient& gradient) const
|
||||
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 (gradient.GetType() == BGradient::TYPE_LINEAR
|
||||
&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
|
||||
const BGradientLinear* linearGradient
|
||||
= dynamic_cast<const BGradientLinear*>(&gradient);
|
||||
if (linearGradient->Start().x == linearGradient->End().x
|
||||
// TODO: Remove this second check once the optimized method
|
||||
// handled "upside down" gradients as well...
|
||||
&& linearGradient->Start().y <= linearGradient->End().y) {
|
||||
// a vertical gradient
|
||||
BRect rect(a, b);
|
||||
FillRectVerticalGradient(rect, *linearGradient);
|
||||
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;
|
||||
|
||||
|
||||
fPath.remove_all();
|
||||
fPath.move_to(a.x, a.y);
|
||||
fPath.line_to(b.x, a.y);
|
||||
fPath.line_to(b.x, b.y);
|
||||
fPath.line_to(a.x, b.y);
|
||||
fPath.close_polygon();
|
||||
|
||||
|
||||
return _FillPath(fPath, gradient);
|
||||
}
|
||||
|
||||
@@ -920,32 +936,37 @@ gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4);
|
||||
} while (fBaseRenderer.next_clip_box());
|
||||
}
|
||||
|
||||
#if 0
|
||||
// FillRectVerticalGradient
|
||||
void
|
||||
Painter::FillRectVerticalGradient(const BRect& r,
|
||||
Painter::FillRectVerticalGradient(BRect r,
|
||||
const BGradientLinear& gradient) const
|
||||
{
|
||||
if (!fValidClipping)
|
||||
return;
|
||||
|
||||
// Make sure the color array is no larger than the screen height.
|
||||
r = r & fClippingRegion->Frame();
|
||||
|
||||
int32 gradientArraySize = r.IntegerHeight() + 1;
|
||||
rgb_color gradientArray[gradientArraySize];
|
||||
for (int32 i = 0; i < gradientArraySize; i++) {
|
||||
uint32 gradientArray[gradientArraySize];
|
||||
int32 gradientTop = gradient.Start().y;
|
||||
int32 gradientBottom = gradient.End().y;
|
||||
int32 colorCount = gradientBottom - gradientTop + 1;
|
||||
if (colorCount < 0) {
|
||||
// Gradient is upside down. That's currently not supported by this
|
||||
// method.
|
||||
return;
|
||||
}
|
||||
|
||||
_MakeGradient(gradient, colorCount, gradientArray,
|
||||
gradientTop - (int32)r.top, gradientArraySize);
|
||||
|
||||
uint8* dst = fBuffer.row_ptr(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
|
||||
pixel32 color;
|
||||
color.data8[0] = c.blue;
|
||||
color.data8[1] = c.green;
|
||||
color.data8[2] = c.red;
|
||||
color.data8[3] = c.alpha;
|
||||
// fill rects, iterate over clipping boxes
|
||||
fBaseRenderer.first_clip_box();
|
||||
do {
|
||||
@@ -958,14 +979,13 @@ Painter::FillRectVerticalGradient(const BRect& r,
|
||||
for (; y1 <= y2; y1++) {
|
||||
// uint32* handle = (uint32*)(offset + y1 * bpr);
|
||||
// for (int32 x = x1; x <= x2; x++) {
|
||||
// *handle++ = color.data32;
|
||||
// *handle++ = gradientArray[y1 - top];
|
||||
// }
|
||||
gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4);
|
||||
gfxset32(offset + y1 * bpr, gradientArray[y1 - top], (x2 - x1 + 1) * 4);
|
||||
}
|
||||
}
|
||||
} while (fBaseRenderer.next_clip_box());
|
||||
}
|
||||
#endif
|
||||
|
||||
// FillRectNoClipping
|
||||
void
|
||||
@@ -2608,6 +2628,85 @@ Painter::_FillPath(VertexSource& path, const BGradient& gradient) const
|
||||
return _Clipped(_BoundingBox(path));
|
||||
}
|
||||
|
||||
// _MakeGradient
|
||||
void
|
||||
Painter::_MakeGradient(const BGradient& gradient, int32 colorCount,
|
||||
uint32* colors, int32 arrayOffset, int32 arraySize) const
|
||||
{
|
||||
BGradient::ColorStop* from = gradient.ColorStopAt(0);
|
||||
|
||||
if (!from)
|
||||
return;
|
||||
|
||||
// current index into "colors" array
|
||||
// int32 index = (int32)floorf(colorCount * from->offset + 0.5)
|
||||
// + arrayOffset;
|
||||
int32 index = (int32)floorf(colorCount * from->offset / 255 + 0.5)
|
||||
+ arrayOffset;
|
||||
if (index > arraySize)
|
||||
index = arraySize;
|
||||
// Make sure we fill the entire array in case the gradient is outside.
|
||||
if (index > 0) {
|
||||
uint8* c = (uint8*)&colors[0];
|
||||
for (int32 i = 0; i < index; i++) {
|
||||
c[0] = from->color.blue;
|
||||
c[1] = from->color.green;
|
||||
c[2] = from->color.red;
|
||||
c[3] = from->color.alpha;
|
||||
c += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// interpolate "from" to "to"
|
||||
int32 stopCount = gradient.CountColorStops();
|
||||
for (int32 i = 1; i < stopCount; i++) {
|
||||
// find the step with the next offset
|
||||
BGradient::ColorStop* to = gradient.ColorStopAtFast(i);
|
||||
|
||||
// interpolate
|
||||
// int32 offset = (int32)floorf((colorCount - 1) * to->offset + 0.5);
|
||||
int32 offset = (int32)floorf((colorCount - 1)
|
||||
* to->offset / 255 + 0.5);
|
||||
if (offset > colorCount - 1)
|
||||
offset = colorCount - 1;
|
||||
offset += arrayOffset;
|
||||
int32 dist = offset - index;
|
||||
if (dist >= 0) {
|
||||
int32 startIndex = max_c(index, 0);
|
||||
int32 stopIndex = min_c(offset, arraySize - 1);
|
||||
uint8* c = (uint8*)&colors[startIndex];
|
||||
for (int32 i = startIndex; i <= stopIndex; i++) {
|
||||
float f = (float)(offset - i) / (float)(dist + 1);
|
||||
float t = 1.0 - f;
|
||||
c[0] = (uint8)floorf(from->color.blue * f
|
||||
+ to->color.blue * t + 0.5);
|
||||
c[1] = (uint8)floorf(from->color.green * f
|
||||
+ to->color.green * t + 0.5);
|
||||
c[2] = (uint8)floorf(from->color.red * f
|
||||
+ to->color.red * t + 0.5);
|
||||
c[3] = (uint8)floorf(from->color.alpha * f
|
||||
+ to->color.alpha * t + 0.5);
|
||||
c += 4;
|
||||
}
|
||||
}
|
||||
index = offset + 1;
|
||||
// the current "to" will be the "from" in the next interpolation
|
||||
from = to;
|
||||
}
|
||||
// make sure we fill the entire array
|
||||
if (index < arraySize - 1) {
|
||||
int32 startIndex = max_c(index, 0);
|
||||
uint8* c = (uint8*)&colors[startIndex];
|
||||
for (int32 i = startIndex; i < arraySize; i++) {
|
||||
c[0] = from->color.red;
|
||||
c[1] = from->color.green;
|
||||
c[2] = from->color.blue;
|
||||
c[3] = from->color.alpha;
|
||||
c += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _MakeGradient
|
||||
template<class Array>
|
||||
void
|
||||
|
||||
@@ -144,6 +144,14 @@ class Painter {
|
||||
// fills a solid rect with color c, no blending
|
||||
void FillRect(const BRect& r,
|
||||
const rgb_color& c) const;
|
||||
|
||||
// fills a rect with a linear gradient, the caller should be
|
||||
// sure that the gradient is indeed vertical. The start point of
|
||||
// the gradient should be above the end point, or this function
|
||||
// will not draw anything.
|
||||
void FillRectVerticalGradient(BRect r,
|
||||
const BGradientLinear& gradient) const;
|
||||
|
||||
// fills a solid rect with color c, no blending, no clipping
|
||||
void FillRectNoClipping(const clipping_rect& r,
|
||||
const rgb_color& c) const;
|
||||
@@ -278,6 +286,11 @@ class Painter {
|
||||
void _CalcLinearGradientTransform(BPoint startPoint,
|
||||
BPoint endPoint, agg::trans_affine& mtx,
|
||||
float gradient_d2 = 100.0f) const;
|
||||
|
||||
void _MakeGradient(const BGradient& gradient,
|
||||
int32 colorCount, uint32* colors,
|
||||
int32 arrayOffset, int32 arraySize) const;
|
||||
|
||||
template<class Array>
|
||||
void _MakeGradient(Array& array,
|
||||
const BGradient& gradient) const;
|
||||
|
||||
Reference in New Issue
Block a user