Richard R wrote:
> Hello. I recently made a custom progress bar, but it's kinda slow and I
> figure I'd need to use assembly to speed it up. Even if assembly is used i'm
> not sure if it'd do any good, what slows it down is the drawing of the
> progress bar and the caption on it. During the increment phase, it has to
> redraw all the graphics on the control. I had to turn on double buffering to
> reduce flicker.
That's because the Repaint function invalidates the entire control
before painting it. A better way to go would be to use the setter for
the Position property to determine how much of the control actually
needs to be repainted. Then use InvalidateRect to invalidate only that
portion. When the window next needs repainting, the OS will only accept
commands that modify that small portion -- it silently ignores commands
that affect areas outside the window's update region. To tell the window
to paint itself, use update instead of Repaint. Update won't do anything
if the current update region is still empty.
> I did a bench mark with a TProgressBar and my
> TtsxProgressBar and the timing ratio is 2:27 / 2:43 so mine was 16 seconds
> slower, at other tests it would be over a minute slower. But I think the
> trade off is having a visual on the progress bar which works very well for
> my project (the user can turn it off). Do you have any suggestions on how I
> can increase performance? In my SetPos and StepIt code I call Repaint after
> i do the calculation.
At the very most, your control should only call Invalidate, not Repaint.
Repaint forces the control to repaint everything, even if the program
was just about to set the Position property to something else right
away. Let the application be in charge of when its forms get painted.
> Here is my Paint code. the Gradient function DrawImage paints a gradient
> effect if turned on. It's rather fast since it uses math but I havnt noticed
> any performance degrade between turning gradient on and off.
>
> procedure TtsxProgressBar.Paint;
> var
> cx, cy, p: integer;
> r: TRect;
> t: string;
> begin
> Inherited Paint;
Does the painting code below leave any part of the window unpainted? If
not, then you don't need the inherited call since you're just going to
paint over everything anyway.
> with Canvas do begin
> // draw outer border
> Pen.Color:= RGB(150, 107, 48);
What are these magic numbers for the color?
If you're trying to draw a sunken 3D border, you can call the
DrawFrameControl API function. It will automatically use the system's
configured highlight and shadow colors. You can then skip all the
line-drawing routines below.
> MoveTo(0, Height - 2);
> LineTo(0, 0);
> LineTo(Width - 2, 0);
> Pen.Color:= RGB(245, 241, 224);
> MoveTo(Width - 1, 0);
You leave one pixel uncolored. The LineTo function does not paint the
last pixel in the range. Why? Because it's leaving it for the next
LineTo call to handle.
If you want to paint the inside border of a rectangle, then the
coordinates to use are (0,0), (Width-1,0), (Width-1,Height-1), (0,Height-1).
> LineTo(Width - 1, Height - 1);
> LineTo(0, Height - 1);
> // draw progress
> p:= 0;
What's p?
> r.Left:= 3;
> r.Top:= 2;
> r.Right:= Width - 3;
> r.Bottom:= Height - 3;
> case FOrientation of
> pbHorizontal:
> p:= Round((FPos - FMin) / (FMax - FMin) * (r.Right - r.Left) +
> r.Left);
> pbVertical:
> p:= Round((FPos - FMax) / (FMin - FMax) * (r.Bottom - r.Top) +
> r.Top);
> end;
> if Gradient.Apply then
> Gradient.DrawImage(Canvas, r)
Since r is a rectangle based on the size of the control, r will not
change unless the entire control is resized. Since the gradient is
always drawn in the full dimensions of the control, you can just draw
the gradient once to a bitmap of your own. Then, when it comes time to
paint the control, just use Canvas.Draw to copy that bitmap to the
canvas instead of recalculating the gradient image each time.
You can calculate r and the gradient bitmap in your control's Resize
method. You can also update it in the property setter for
Gradient.Apply, so when the property is False, you just paint an
FPColor-colored rectangle into your bitmap.
> else begin
> Brush.Color:= FPColor;
> FillRect(r);
> end;
> case FOrientation of
> pbHorizontal: r.Left:= p;
> pbVertical: r.Bottom:= p;
> end;
> Brush.Color:= Self.Color;
> FillRect(r);
According to this, it appears as though you draw the entire "progress"
portion of the control, and then you "unpaint" the part that shouldn't
have been painted at all.
> // draw caption
> t:= Caption;
> if FShowValue then
> t:= t + ' : ' + Format('%d / %d', [FPos, FMax]);
Include the " : " part of the string in your format string. There's no
use having a Format call *and* two string concatenations.
t := t + Format(' : %d / %d', [FPos, FMax]);
> Brush.Style:= bsClear;
> cx:= Width div 2 - TextWidth(t) div 2;
> cy:= Height div 2 - TextHeight(t) div 2;
The TextWidth and TextHeight functions both call the TextSize function
internally, which returns both the height and the width of the given
text. If you use that function directly, you can cut the work in half.
Better yet, you can use the DrawText API function. It can draw text that
is horizontally and vertically centered in a given rectangle, so you
don't need to do any calculations yourself.
> if (not Enabled) then begin
> Font.Color:= RGB(245, 241, 244);
More magic numbers for the colors?
Is disabled text still drawn sunken these days?
> cx:= cx + 1;
> cy:= cy + 1;
> end
> else
> Font.Color:= Self.Font.Color;
> TextOut(cx, cy, t);
> if (not Enabled) then begin
> cx:= cx - 1;
> cy:= cy - 1;
> Font.Color:= RGB(150, 107, 48);
> TextOut(cx, cy, t);
> end;
> Brush.Style:= bsSolid;
> end;
> end;
--
Rob
__________________________________________________
Delphi-Talk mailing list -> [email protected]
http://www.elists.org/mailman/listinfo/delphi-talk