You Are Here:

Community: Wiki

This page was last modified on 27 September 2009, at 12:09.

How to create a rounded rectangle

From Forum Nokia Wiki

Reviewer Approved   


ID Creation date May 7, 2009
Platform S60 3rd Edition, S60 5th Edition Tested on devices Nokia 5800 XpressMusic, Nokia E90
Category Symbian C++ Subcategory UI


Keywords (APIs, classes, methods, functions): CWindowGc::UseBrushPattern(), CWindowGc::DrawPolygon()

Overview

In many cases a rounded rectangle would look better than just a plain rectangle with sharp corners. You can use CWindowGc::DrawRoundRect() to draw a rounded rectangle, but it only allows you to have four symmetric corners.

This short article provides you a one solution to create a more complicated rounded rectangles, which have different properties for each of the corners.

Application running on 5800xpressMusic

How it is done

Since drawing of advanced rounded rectangles is not directly supported, one must do some hacking. You can think rounded rectangle to consist of four ellipses (or less if there is sharp corners) as presented in the picture below.

How rounded rectangle is formed

The outermost points of each ellipse are part of the border of the rounded rectangle. There is different ways to calculate ellipse points, but I used one that can be found from the end of the ellipse article in the wikipedia.

CWindowGc:: DrawPolygon() is used to draw the rectangle. It is quite handy as you can use gc.UseBrushPattern() to set background image and gc.SetPenXXX() properties to define how the border will look. Please note that it would be more efficient to calculate and store the rectangle vertice values before entering the draw function. In this example, calculation is done "on the fly".

Source code

Header file

/*
* Draws a rectangle. Each corner radius is limited to be half of rectangle height or width
*
* gc graphics context
* aX upper left corner x coordinate
* aY upper left corner y coordinate
* width rectangle width
* height rectangle height
* aBl bottom left corner properties TSize(ellipse horizontal radius, ellipse vertical radius)
* aTl top left corner properties
* aTr top right corner properties
* aBr bottom right corner properties
* aBodrderThickness thickness of the border
* aBorderColor border color
* aBackgroundColor color of the background (fill color)
* */

void drawRoundedRectangle(CWindowGc &gc,TInt aX, TInt aY, TInt width, TInt height,
TSize aBl, TSize aTl, TSize aTr, TSize aBr, TSize aBodrerThickness, TRgb aBorderColor, TRgb aBackgroundColor) const;
 
/*
* gc graphics context
* aX upper left corner x coordinate
* aY upper left corner y coordinate
* width rectangle width
* height rectangle height
* aBl bottom left corner properties TSize(ellipse horizontal radius, ellipse vertical radius)
* aTl top left corner properties
* aTr top right corner properties
* aBr bottom right corner properties
* aBodrderThickness thickness of the border
* aBorderColor border color
* aBackgroundImage Pointer to the background image
*/

void drawRoundedRectangle(CWindowGc &gc,TInt aX, TInt aY, TInt width, TInt height,
TSize aBl, TSize aTl, TSize aTr, TSize aBr, TSize aBodrerThickness, TRgb aBorderColor, CFbsBitmap* aBackgroundImage) const;
 
 
CArrayFix<TPoint>* calculateCornerPixels(TInt aX, TInt aY, TInt width, TInt height, TSize aBl, TSize aTl, TSize aTr, TSize aBr) const;
 
 
/*
* From CCoeControl, Draw
* Draw this CRoundedRectangleAppView to the screen.
* @param aRect the rectangle of this view that needs updating
*/

void Draw(const TRect& aRect) const;

Source file

void CRoundedRectangleAppView::drawRoundedRectangle(CWindowGc &gc, TInt aX, TInt aY, TInt aWidth, TInt aHeight, 
TSize aBl, TSize aTl, TSize aTr, TSize aBr, TSize aBorderThickness, TRgb aBorderColor, TRgb aBackgroundColor) const
{
gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
gc.SetPenStyle(CGraphicsContext::ESolidPen);
 
gc.SetPenSize(aBorderThickness);
gc.SetPenColor(aBorderColor);
 
gc.SetBrushColor(aBackgroundColor);
 
CArrayFix<TPoint>* points = calculateCornerPixels(aX,aY,aWidth,aHeight, aBl,aTl,aTr,aBr);
gc.DrawPolygon(points);
delete points;
 
}
 
void CRoundedRectangleAppView::drawRoundedRectangle(CWindowGc &gc, TInt aX, TInt aY, TInt aWidth, TInt aHeight,
TSize aBl, TSize aTl, TSize aTr, TSize aBr, TSize aBorderThickness, TRgb aBorderColor, CFbsBitmap* aBackgroundImage) const
{
gc.SetPenStyle(CGraphicsContext::ESolidPen);
gc.SetPenSize(aBorderThickness);
gc.SetPenColor(aBorderColor);
 
gc.UseBrushPattern(aBackgroundImage);
gc.SetBrushStyle(CGraphicsContext::EPatternedBrush);
 
CArrayFix<TPoint>* points = calculateCornerPixels(aX,aY,aWidth,aHeight, aBl,aTl,aTr,aBr);
 
gc.DrawPolygon(points);
 
gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
gc.DiscardBrushPattern();
 
delete points;
}
 
 
CArrayFix<TPoint>* CRoundedRectangleAppView::calculateCornerPixels(TInt aX, TInt aY, TInt width, TInt height, TSize aBl, TSize aTl, TSize aTr, TSize aBr) const
{
TReal sinalpha=0;
TReal cosalpha=0;
TReal alpha=0;
TReal X=0,Y=0;
const TReal piRad = KPi/180;
 
//simple sanity checks
TReal widthPerTwo = width /2;
TReal heightPerTwo = height /2;
 
aBl.iWidth = Min(aBl.iWidth, widthPerTwo);
aTl.iWidth = Min(aTl.iWidth, widthPerTwo);
aTr.iWidth = Min(aTr.iWidth, widthPerTwo);
aBr.iWidth = Min(aBr.iWidth, widthPerTwo);
 
aBl.iHeight = Min(aBl.iHeight, heightPerTwo);
aTl.iHeight = Min(aTl.iHeight, heightPerTwo);
aTr.iHeight = Min(aTr.iHeight, heightPerTwo);
aBr.iHeight = Min(aBr.iHeight, heightPerTwo);
 
 
//calculate ellipse middle points
TPoint bottomLeft(aX+aBl.iWidth, aY+height-aBl.iHeight);
TPoint topLeft(aX+aTl.iWidth, aY+aTl.iHeight);
TPoint topRight(aX+width-aTr.iWidth, aY+aTr.iHeight);
TPoint bottomRight(aX+width-aBr.iWidth, aY+height-aBr.iHeight);
 
//container for the calculated points
CArrayFix<TPoint>* rectanglePoints = new CArrayFixFlat<TPoint>(12);
CleanupStack::PushL(rectanglePoints);
 
//bottom left corner
if(aBl.iHeight==0 || aBl.iWidth==0)
{
rectanglePoints->AppendL(TPoint(aX,aY+height)); //sharp corner
}
else
{
//let's reduce points needed based on the corner properties
//no need for calculating fixed amount of points for every corner e.g. 2x2 corner
TReal stepBl=0;
Math::Sqrt(stepBl, aBl.iWidth*aBl.iWidth+aBl.iHeight*aBl.iHeight);
stepBl = ((90/stepBl)<1 ? 1: 90/stepBl); //limit the minium to be 1 --> max 90 points per corner
 
for (TReal i = 90; i <= 180 ; i += stepBl)
{
alpha = i * piRad ;
Math::Sin(sinalpha,alpha);
Math::Cos(cosalpha,alpha);
 
Math::Round(X,bottomLeft.iX + (aBl.iWidth * cosalpha),0);
Math::Round(Y,bottomLeft.iY + (aBl.iHeight * sinalpha),0);
 
rectanglePoints->AppendL(TPoint(X,Y));
}
}
 
 
// top left corner
if(aTl.iHeight==0 || aTl.iWidth==0)
{
rectanglePoints->AppendL(TPoint(aX,aY));
}
else
{
TReal stepTl=0;
Math::Sqrt(stepTl, aBl.iWidth*aTl.iWidth+aTl.iHeight*aTl.iHeight);
stepTl = ((90/stepTl)<1 ? 1: 90/stepTl);
 
for (TReal i = 180; i <= 270; i += stepTl)
{
alpha = i * piRad ;
Math::Sin(sinalpha,alpha);
Math::Cos(cosalpha,alpha);
 
Math::Round(X,topLeft.iX + (aTl.iWidth * cosalpha),0);
Math::Round(Y,topLeft.iY + (aTl.iHeight * sinalpha),0);
 
rectanglePoints->AppendL(TPoint(X,Y));
}
}
 
 
// top right corner
if(aTr.iHeight==0 || aTr.iWidth==0)
{
rectanglePoints->AppendL(TPoint(aX+width,aY));
}
else
{
TReal stepTr=0;
Math::Sqrt(stepTr, aTr.iWidth*aTr.iWidth+aTr.iHeight*aTr.iHeight);
stepTr = ((90/stepTr)<1 ? 1: 90/stepTr);
 
for (TReal i = 270; i <= 360; i += stepTr)
{
alpha = i * piRad ;
Math::Sin(sinalpha,alpha);
Math::Cos(cosalpha,alpha);
 
Math::Round(X,topRight.iX + (aTr.iWidth * cosalpha),0);
Math::Round(Y,topRight.iY + (aTr.iHeight * sinalpha),0);
 
rectanglePoints->AppendL(TPoint(X,Y));
}
}
 
 
 
// bottom right corner
if(aBr.iHeight==0 || aBr.iWidth==0)
{
rectanglePoints->AppendL(TPoint(aX+width,aY+height));
}
else{
TReal stepBr=0;
Math::Sqrt(stepBr, aBr.iWidth*aBr.iWidth+aBr.iHeight*aBr.iHeight);
stepBr = ((90/stepBr)<1 ? 1: 90/stepBr);
 
for (TReal i = 0; i <= 90; i += stepBr)
{
alpha = i * piRad ;
Math::Sin(sinalpha,alpha);
Math::Cos(cosalpha,alpha);
 
Math::Round(X,bottomRight.iX + (aBr.iWidth * cosalpha),0);
Math::Round(Y,bottomRight.iY + (aBr.iHeight * sinalpha),0);
 
rectanglePoints->AppendL(TPoint(X,Y));
}
}
 
CleanupStack::Pop(); // rectanglePoints
return rectanglePoints;
 
}
 
// -----------------------------------------------------------------------------
// CRoundedRectangleAppView::Draw()
// Draws the display.
// -----------------------------------------------------------------------------
//
void CRoundedRectangleAppView::Draw(const TRect& /*aRect*/) const
{
// Get the standard graphics context
CWindowGc& gc = SystemGc();
 
TRect drawRect(Rect());
gc.Clear(drawRect);
 
drawRoundedRectangle(gc,5,5,100,30,TSize(10,10),TSize(10,10),TSize(10,10),TSize(10,10),TSize(2,2),TRgb(0x00000),TRgb(0xffffff));
drawRoundedRectangle(gc,5,40,100,30,TSize(10,10),TSize(10,10),TSize(10,10),TSize(10,10),TSize(1,1),TRgb(0xD97821),TRgb(0x2148D9));
 
drawRoundedRectangle(gc,5,75,100,30,TSize(0,0),TSize(16,21),TSize(0,0),TSize(16,21),TSize(1,1),TRgb(0xD62AC4),TRgb(0xD62AC4));
drawRoundedRectangle(gc,5,110,100,30,TSize(14,19),TSize(0,0),TSize(14,19),TSize(0,0),TSize(1,1),TRgb(0xD62AC4),TRgb(0xD62AC4));
 
drawRoundedRectangle(gc,5,145,100,30,TSize(0,0),TSize(15,20),TSize(0,0),TSize(15,20),TSize(1,1),TRgb(0xD62AC4),TRgb(0x75AD3F));
drawRoundedRectangle(gc,5,180,100,30,TSize(15,20),TSize(0,0),TSize(15,20),TSize(0,0),TSize(1,1),TRgb(0xD62AC4),TRgb(0x75AD3F));
 
drawRoundedRectangle(gc,5,215,100,30,TSize(0,0),TSize(0,0),TSize(15,15),TSize(15,15),TSize(1,1),TRgb(0xD62AC4),TRgb(0x30D087));
drawRoundedRectangle(gc,5,250,100,30,TSize(15,15),TSize(15,15),TSize(0,0),TSize(0,0),TSize(1,1),TRgb(0xD62AC4),TRgb(0x30D087));
 
//commented out as these use images that are not loaded in this example
//drawRoundedRectangle(gc,110,5,150,150,TSize(300,200),TSize(400,200),TSize(400,300),TSize(400,300),TSize(0,0),TRgb(0xD62AC4), iBg1);
 
//drawRoundedRectangle(gc,110,200,200,200,TSize(30,20),TSize(150,150),TSize(40,30),TSize(40,30),TSize(2,2),TRgb(0xD62AC4),iBg2);
 
}

Considerations

  • Code is not fully tested.
  • Converting from calculated TReal values to TInt may cause some graphic bugs.
  • Since we are talking about graphics there is always room for optimization

Related Wiki Articles

No related wiki articles found

Rate This

 
Bookmark this page: DeliciousDiggFacebookGoogleYahooStumbleUponRedditDiigoTechnocratiTwitter  Share this page Share this page Print this Page Print this page Invite a friend Invite a friend
京ICP备05048969号    Email Newsletters Press Terms & Conditions Privacy Policy Sitemap Contact Us © 2009 Nokia 
RDF Facets: qdcZidentifierQSxhttpE3aE2fE2fwikiE2eforumE2enokiaE2ecomE2findeE78E2ephpE2fTalkE3aE4cargeE5fscreenE5fsaverX qdcZtypeQUqfnZE45E78cludedFromGeneralE4cistingsQ qdcZtypeQUqfntypeZCommunityContentQ qdcZtypeQUqfntypeZE52esourceQ qdcZtypeQUqfntypeZWebpageQ qdcZtypeQUqfntypeZWikiContentQ qdcZtypeQUqmarsZManagedE52esourceQ qdcZtypeQUqwebZInformationE52esourceQ qdcZtypeQUqwebZPageQ qdcZtypeQUqwebZE52esourceQ qdcZtypeQUqrdfsZE52esourceQ qfnZtypeQUqfntypeZCommunityContentQ qfnZtypeQUqfntypeZE52esourceQ qfnZtypeQUqfntypeZWebpageQ qfnZtypeQUqfntypeZWikiContentQ qmarsZlanguageQUxhttpE3aE2fE2fswE2enokiaE2ecomE2flanguageE2d1E2fenX qrdfZtypeQUqfnZE45E78cludedFromGeneralE4cistingsQ qrdfZtypeQUqfntypeZCommunityContentQ qrdfZtypeQUqfntypeZE52esourceQ qrdfZtypeQUqfntypeZWebpageQ qrdfZtypeQUqfntypeZWikiContentQ qrdfZtypeQUqmarsZManagedE52esourceQ qrdfZtypeQUqwebZInformationE52esourceQ qrdfZtypeQUqwebZPageQ qrdfZtypeQUqwebZE52esourceQ qrdfZtypeQUqrdfsZE52esourceQ