Burnings draw2DRectangle

Discuss about anything related to the Irrlicht Engine, or read announcements about any significant features or usage changes.
Post Reply
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Burnings draw2DRectangle

Post by chronologicaldot »

I'm sure this topic may get old, and I hate starting a new thread, but this is different.

I changed the Burnings draw2DRectangle( four-corners coloring ) function to be accurate in the gradient color, and it looks very nice. This is good for if you want to use it for color-selection gradients (as I'm using it for). On the downside, it's slower (I didn't run an FPS check, but it's rather obvious when using a GUI skin), even after I inlined the color interpolation.

Anyone think they can optimize it, let me know. Here's the code I have so far (certain things have been left in but commented out so you can see the logic):

Code: Select all

 
//!Draws an 2d rectangle with a gradient.
void CBurningVideoDriver::draw2DRectangle(const core::rect<s32>& position,
    SColor colorLeftUp, SColor colorRightUp, SColor colorLeftDown, SColor colorRightDown,
    const core::rect<s32>* clip)
{
#if 1 // USE_BURNINGS_PRETTY_DRAW2DRECT
    if ( !RenderTargetSurface )
    {
        setRenderTarget(BackBuffer);
    }
 
    // Done in setRenderTarget
    //RenderTargetSize = RenderTargetSurface->getDimension();
 
    core::rect<s32> realClip( position );
    if ( clip )
        realClip.clipAgainst(*clip);
 
    if ( !realClip.isValid() )
        return;
 
    // Rectangle bounds given by %
    u32 x0 = (u32) ( realClip.UpperLeftCorner.X < 0 ? 0 : realClip.UpperLeftCorner.X );
    u32 y0 = (u32) ( realClip.UpperLeftCorner.Y < 0 ? 0 : realClip.UpperLeftCorner.Y );
    u32 x100 = (u32)realClip.LowerRightCorner.X < RenderTargetSize.Width ? (u32)realClip.LowerRightCorner.X : RenderTargetSize.Width-1;
    u32 y100 = (u32)realClip.LowerRightCorner.Y < RenderTargetSize.Height ? (u32)realClip.LowerRightCorner.Y : RenderTargetSize.Height-1;
 
    u32 x, y, w, h, rx, ry, rw, rh;
    f32 ryh, rxw;
    w = x100 - x0; // render area width
    h = y100 - y0; // render area height
    //s32 flatAlpha, flatRed, flatGreen, flatBlue;
 
    /* The rectangle colors should be drawn as though the rectangle were the same size,
    even if part of it is clipped. */
    rw = position.getWidth(); // real width
    rh = position.getHeight(); // real height
 
    u32 colorLeftUpColor = colorLeftUp.color;
    u32 colorLeftDownColor = colorLeftDown.color;
    u32 colorRightUpColor = colorRightUp.color;
    u32 colorRightDownColor = colorRightDown.color;
 
    //SColor pixelColor; = colorLeftUp;
    u32 pixelColor;
    //SColor leftColor, rightColor;
    u32 leftColor, rightColor;
 
    tVideoSample* dst; // render target surface (just a pointer to a u32 array of size RenderTargetSize)
 
    y=0;
    ry = position.UpperLeftCorner.Y < 0 ? (u32)( position.UpperLeftCorner.Y * -1 ) : 0;
    for ( ; y < h ; y++ )
    {
        ryh = (f32)ry/(f32)rh;
        //leftColor = colorLeftDown.getInterpolated( colorLeftUp, (f32)ry/(f32)rh );
 
        leftColor =
                    ( (u32( (colorLeftDownColor>>24)*ryh + (colorLeftUpColor>>24)*(1.f-ryh) )) << 24 )
                    + ( (u32( ((colorLeftDownColor>>16) & 0xff)*ryh + ((colorLeftUpColor>>16) & 0xff)*(1.f-ryh) )) << 16 )
                    + ( (u32( ((colorLeftDownColor>>8) & 0xff)*ryh + ((colorLeftUpColor>>8) & 0xff)*(1.f-ryh) )) << 8 )
                    + ( u32( (colorLeftDownColor & 0xff)*ryh + (colorLeftUpColor & 0xff)*(1.f-ryh) ) );
 
        //rightColor = colorRightDown.getInterpolated( colorRightUp, (f32)ry/(f32)rh );
        rightColor =
                    ( (u32( (colorRightDownColor>>24)*ryh + (colorRightUpColor>>24)*(1.f-ryh) )) << 24 )
                    + ( (u32( ((colorRightDownColor>>16) & 0xff)*ryh + ((colorRightUpColor>>16) & 0xff)*(1.f-ryh) )) << 16 )
                    + ( (u32( ((colorRightDownColor>>8) & 0xff)*ryh + ((colorRightUpColor>>8) & 0xff)*(1.f-ryh) )) << 8 )
                    + ( u32( (colorRightDownColor & 0xff)*ryh + (colorRightUpColor & 0xff)*(1.f-ryh) ) );
 
        // left and right side
        //RenderTargetSurface->setPixel( x0, y + y0, leftColor, false );
        //RenderTargetSurface->setPixel( x100, y + y0, rightColor, false );
        dst = (tVideoSample*)RenderTargetSurface->lock() + (y+y0)*RenderTargetSize.Width + x0;
        dst[0] = (tVideoSample)leftColor; //.color;
        dst[w-1] = (tVideoSample)rightColor; //.color;
 
        x = 1;
        rx = position.UpperLeftCorner.X < 0 ? (u32)( position.UpperLeftCorner.X * -1 ) : 0;
        for ( ; x < w-1; x++ )
        {
            rxw = (f32)x/(f32)rw;
 
            //pixelColor = rightColor.getInterpolated( leftColor, (f32)rx/(f32)rw );
            pixelColor =
                    ( (u32( (rightColor>>24)*rxw + (leftColor>>24)*(1.f-rxw) )) << 24 )
                    + ( (u32( ((rightColor>>16) & 0xff)*rxw + ((leftColor>>16) & 0xff)*(1.f-rxw) )) << 16 )
                    + ( (u32( ((rightColor>>8) & 0xff)*rxw + ((leftColor>>8) & 0xff)*(1.f-rxw) )) << 8 )
                    + ( u32( (rightColor & 0xff)*rxw + (leftColor & 0xff)*(1.f-rxw) ) );
 
            //RenderTargetSurface->setPixel( x + x0, y + y0, pixelColor, false ); // <-- Reeeally slow
            dst[x] = (tVideoSample)pixelColor; //.color;
 
            rx++;
        }
 
        ry++;
    }
    //RenderTargetSurface->unlock(); // unnecessary for Burnings
#endif
}
 
I used direct video samples (tVideoSamples) rather than SColor to make it faster, but I left in comments of where the color used to be - I hope that's not too distracting.
What I suppose I could do is make it optional by setting a video driver flag (now that I know about those, DOH!), but I'd rather make it faster if possible.
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Burnings draw2DRectangle

Post by hendu »

Well, the common optimization to such float multiplies is an adaptation of Bresenham's line algorithm. To change it to int math, you calculate when the pixels increase and decrease: instead of multiplying every iteration, you ++/-- every Nth iteration.
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: Burnings draw2DRectangle

Post by chronologicaldot »

I see. I had considered incrementing by a float, but my first implementation was messy. Thanks for the suggestion. I've probably seen Bresenham's line before and forgot. *sigh* I'll work at this again.
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: Burnings draw2DRectangle

Post by chronologicaldot »

Okay, so I tried setting it up and as I put it together, I realized it wasn't going to work. Bresenham's line assumes you want to draw a line, but I'm only trying to do 1-point-per-1-point, i.e. 1 color value per pixel position. I'd have to average point values, which is more work. Plus, what do I do in the case of steep slopes? - I'd end up incrementing every pixel anyways.
Turns out to be cleaner and faster to do it my first way and not try (at least what I had in mind for) the Bresenham's line implementation. I may keep working at it, but I may just make it optional somehow (e.g. I only need it for the color selection gradient - everything else can use the scanline method). Inlining the color calculations to one may help, so I think I'll try that just as a last check. hm...
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: Burnings draw2DRectangle

Post by mongoose7 »

He didn't mean to *use* the line algorithm, just use the concept of representing a (fixed-point) floating point number as an integer result plus an integer remainder, where the remainder is folded into the result when it exceeds one. May be a bridge too far, eh?
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: Burnings draw2DRectangle

Post by chronologicaldot »

Rereading my own post, I realize it's rather a mess. That's not what I meant. Oops. Sorry about that. My fault for rambling.

I got what she meant, and it looks like I tried relating it back to the original idea when I labeled it. My current "Bresenham's line" analogous implementation ran into some problems when dealing with steep slopes. When I have to change the color value over a range of 255 (the max) over a short distance, say 100 pixels, I end up incrementing up in an odd slope rather than have a nice, gradual change where I would only need to change the color every so-many pixels. Changing the color once (just incrementing by ++) every so-many pixels was the ideal I had in mind, and also of which I didn't clarify.
Anyways...

Clockspeed, my current algo is about half the normal speed of Burnings, *sigh*, even with optimizations. Maybe my PC is slow, but OpenGL came out to around 28 fps, Burnings normal came out to 14 fps, and Burnings with my algo came out to 8 fps, only doing GUI work with a skin.
Post Reply