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:
Stephan Aßmus
2009-02-15 18:14:35 +00:00
parent 3176cdc759
commit 700ab492a9
2 changed files with 128 additions and 16 deletions
+115 -16
View File
@@ -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
+13
View File
@@ -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;