//
// Layers.cpp: source file for the Layers class.
//
// Author: Darren Sessions
//
//
// Description:
//
// The Layers class contains all the structures and arrays used by the
// Style Toolkit.
//
// History
// Version 1.1 - 2008 August 1
// - More features added
//
// Version 1.1 - 2008 July 22
// - Initial public release
//
// License:
// This software is released under the Code Project Open License (CPOL),
// which may be found here: http://www.codeproject.com/info/eula.aspx
// You are free to use this software in any way you like, except that you
// may not sell this source code.
//
// This software is provided "as is" with no expressed or implied warranty.
// I accept no liability for any damage or loss of business that this
// software may cause.
//
///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Layers.h"
#include "math.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//=============================================================================
// Construction/Destruction
//=============================================================================
//=============================================================================
// empty constructor
Layers::Layers()
//=============================================================================
{
Init();
}
//=============================================================================
// pre allocate alloc number of layers and strings
Layers::Layers(int alloc)
//=============================================================================
{
Init(alloc);
}
//=============================================================================
// Layers Initializer
void Layers::Init(int alloc)
//=============================================================================
{
::ZeroMemory(&Frame, sizeof(FRAME));
m_Layers.reserve(alloc);
m_Strings.reserve(alloc);
}
//=============================================================================
// destructor
Layers::~Layers()
//=============================================================================
{
// delete the image objects
for(image = m_Images.begin(); image != m_Images.end(); ++image)
{
if((*image).pImage) delete (*image).pImage;
}
// delete the region objects
for(layer = m_Layers.begin(); layer != m_Layers.end(); ++layer)
{
if((*layer).pRegion) delete (*layer).pRegion;
}
}
//=============================================================================
// copy constructor
Layers::Layers(const Layers& other)
//=============================================================================
{
Copy(other);
}
//=============================================================================
// copy function
void Layers::Copy(const Layers& other)
//=============================================================================
{
// copy frame
memcpy(&Frame, &other.Frame, sizeof(FRAME));
// Copy layers and strings
CopyLayers(other);
}
//=============================================================================
// copy function helper
void Layers::CopyLayers(const Layers& other)
//=============================================================================
{
// copy OPERATION
for(clayer = other.m_Layers.begin(); clayer != other.m_Layers.end(); ++clayer)
{
m_Layers.push_back(*clayer);
}
// copy TEXTINFO
for(cwsIter = other.m_Strings.begin(); cwsIter != other.m_Strings.end(); ++cwsIter)
{
m_Strings.push_back(*cwsIter);
}
// copy IMAGEINFO
for(cimage = other.m_Images.begin(); cimage != other.m_Images.end(); ++cimage)
{
m_Images.push_back(*cimage);
}
}
//=============================================================================
// operator =
Layers& Layers::operator=(const Layers& other)
//=============================================================================
{
m_Layers.clear();
m_Strings.clear();
m_Images.clear();
Copy(other);
return *this;
}
//=============================================================================
void Layers::MakeFrame(SRect sr)
//=============================================================================
{
Frame.frect = sr.rect;
// default to rects
Frame.OuterBorder.shape = RECTANGLE;
Frame.MiddleBorder.shape = RECTANGLE;
Frame.InnerBorder.shape = RECTANGLE;
MakeFrameRgn();
}
//=============================================================================
void Layers::MakeFrameRgn()
//=============================================================================
{
Region rgn(Frame.frect);
Frame.clipRgn.MakeEmpty();
Frame.clipRgn.Union(&rgn);
}
//=============================================================================
//
// AddLayer()
//
// Purpose: Adds a layer to the stack. A transparent layer will be created
// and set to the Frames rect just prior to painting
//
// Parameters: None
//
// Returns: None
//
int Layers::AddLayer()
{
OPERATION Layer;
::ZeroMemory(&Layer, sizeof(OPERATION));
m_Layers.push_back(Layer);
return (int)(m_Layers.size() - 1);
}
//=============================================================================
//
// AddLayer()
//
// Purpose: Adds a layer to the stack. If only the rect is passed
// a transparent layer will be created
//
// Parameters: sr - [in] Rect boundary of this layer
// clr - [in] optional - color of this layer
//
// Returns: None
//
int Layers::AddLayer(SRect sr, Clr clr)
{
OPERATION Layer;
::ZeroMemory(&Layer, sizeof(OPERATION));
Layer.lrect = sr.rect;
Layer.clr1 = clr.value;
m_Layers.push_back(Layer);
return (int)(m_Layers.size() - 1);
}
//=============================================================================
//
// SetShape()
//
// Purpose: Sets the border style and radius
//
// Parameters: style - [in] the border style
// radius - [in] optional - the corner radius for round borders
//
// Returns: None
//
void Layers::SetShape(int shape, int radius)
{
MakeFrameRgn();
// make all the borders the same
Frame.OuterBorder.radius = radius;
Frame.MiddleBorder.radius = radius;
Frame.InnerBorder.radius = radius;
Frame.OuterBorder.shape = shape;
Frame.MiddleBorder.shape = shape;
Frame.InnerBorder.shape = shape;
// only the outer border can be transition
if(shape == TRANSITION)
{
Frame.MiddleBorder.shape = ROUNDRECT;
Frame.InnerBorder.shape = ROUNDRECT;
}
}
//=============================================================================
//
// SetClipping()
//
// Purpose: Sets the Graphics clipping region to the frames tlclr border
//
// Parameters: None
//
// Returns: None
//
void Layers::SetClipping(Graphics* pGraphics)
{
Frame.OuterBorder.SetBorderClip(pGraphics, Frame.frect);
}
//=============================================================================
//
// RestoreClipping()
//
// Purpose: Restores the Graphics clipping region
//
// Parameters: None
//
// Returns: None
//
void Layers::RestoreClipping(Graphics* pGraphics)
{
pGraphics->ResetClip();
}
//=============================================================================
//
// PaintBorders()
//
// Purpose: Paints the Outer, Middle and Inner borders based on the
// borders paramaters.
//
// Parameters: None
//
// Returns: None
//
void Layers::PaintBorders()
{
Rect bdr(Frame.frect);
int width, ovlap;
if( (width = Frame.OuterBorder.width) )
{
ovlap = width;
if( (Frame.MiddleBorder.shape != RECTANGLE) && (Frame.MiddleBorder.width > 0) &&
(Frame.MiddleBorder.ulclr.GetA() == 255) ) ovlap++;
Frame.OuterBorder.Draw(m_pGdi, bdr, Frame.OuterBorder.ulclr, Frame.OuterBorder.brclr, ovlap);
bdr.Inflate(-width, -width);
}
if( (width = Frame.MiddleBorder.width) )
{
ovlap = width;
if( (Frame.InnerBorder.shape != RECTANGLE) && (Frame.InnerBorder.width > 0) &&
(Frame.InnerBorder.ulclr.GetA() == 255) ) ovlap++;
Frame.MiddleBorder.Draw(m_pGdi, bdr, Frame.MiddleBorder.ulclr, Frame.MiddleBorder.brclr, ovlap);
bdr.Inflate(-width, -width);
}
if( (width = Frame.InnerBorder.width) )
{
Frame.InnerBorder.Draw(m_pGdi, bdr, Frame.InnerBorder.ulclr, Frame.InnerBorder.brclr, width);
}
}
//=============================================================================
//
// PaintLayers()
//
// Purpose: Paints the solid or gradient base rectangle
// The rectangle is clipped with the Frame struct
//
//=============================================================================
void Layers::PaintLayers()
{
Color colors[3];
if(m_pLayer->LayerType == SOLID)
{
SolidBrush br(m_pLayer->clr1);
Fill(&br);
}
else if(m_pLayer->LayerType == TRIGRAD)
{
colors[0] = m_pLayer->clr1;
colors[1] = m_pLayer->clr2;
colors[2] = m_pLayer->clr3;
REAL positions[] = { 0.0f, m_pLayer->rParams[0], 1.0f };
LinearGradientMode mode = (LinearGradientMode)m_pLayer->gradMode;
LinearGradientBrush lgbr(m_pLayer->lrect, Color::Black, Color::White, mode);
lgbr.SetInterpolationColors(colors, positions, 3);
Fill(&lgbr);
}
else if(m_pLayer->LayerType == BIGRAD)
{
// check center point for minus flag
float dp = m_pLayer->rParams[0];
colors[0] = m_pLayer->clr1;
if(dp < 0.0f)
{
colors[1] = m_pLayer->clr2;
dp = 1.0f + dp;
}
else
{
colors[1] = m_pLayer->clr1;
}
colors[2] = m_pLayer->clr2;
REAL positions[] = { 0.0f, dp, 1.0f };
LinearGradientMode mode = (LinearGradientMode)m_pLayer->gradMode;
LinearGradientBrush lgbr( m_pLayer->lrect, m_pLayer->clr1,
m_pLayer->clr2, mode);
lgbr.SetWrapMode(WrapModeClamp);
lgbr.SetInterpolationColors(colors, positions, 3);
Fill(&lgbr);
}
else if(m_pLayer->LayerType == RADIAL)
{
GraphicsPath path;
Rect pr = m_pLayer->lrect;
path.AddEllipse(pr);
path.CloseFigure();
PathGradientBrush pgr(&path);
int count = 1;
Color clr = m_pLayer->clr1;
Color t[1]; t[0] = m_pLayer->clr2;
pgr.SetCenterPoint( Point(pr.X + pr.Width/2, pr.Y + pr.Height/2) );
pgr.SetSurroundColors(t, &count);
pgr.SetCenterColor(clr);
m_pGdi->FillPath(&pgr, &path);
}
else if(m_pLayer->LayerType == IMAGE)
{
PaintImage();
}
else if(m_pLayer->LayerType == STRING)
{
PaintString();
}
else if(m_pLayer->LayerType == EFFECTSTRING)
{
PaintEffectString();
}
else if(m_pLayer->LayerType >= TOP_EDGE && m_pLayer->LayerType <= BOTTOM_EDGE_BEVEL)
{
PaintBar();
}
else if(m_pLayer->LayerType == CREATERGN)
{
GraphicsPath path;
GetPath(&path);
Region rgn(&path);
m_pLayer->pRegion = rgn.Clone();
}
else if(m_pLayer->LayerType == COMBINEMOD)
{
Region *pR0, *pR1;
pR0 = m_Layers[m_pLayer->rgnIndex[0]].pRegion;
pR1 = m_Layers[m_pLayer->rgnIndex[1]].pRegion;
CombineRgns(pR0, pR1, m_pLayer->clipMode);
}
else if(m_pLayer->LayerType == COMBINENEW)
{
Region *pR0, *pR1;
pR0 = m_Layers[m_pLayer->rgnIndex[0]].pRegion;
pR1 = m_Layers[m_pLayer->rgnIndex[1]].pRegion;
m_pLayer->pRegion = pR0->Clone();
CombineRgns(m_pLayer->pRegion, pR1, m_pLayer->clipMode);
}
else if(m_pLayer->LayerType == APPLYCLIP)
{
m_pGdi->SetClip(m_Layers[m_pLayer->rgnIndex[0]].pRegion);
}
else if(m_pLayer->LayerType == RESTORECLIP)
{
Frame.OuterBorder.SetBorderClip(m_pGdi, Frame.frect);
}
else if(m_pLayer->LayerType == REGIONBLUR)
{
RegionBlur(m_Layers[m_pLayer->rgnIndex[0]].pRegion, m_pLayer->width);
}
else if(m_pLayer->LayerType == WRAP)
{
colors[0] = m_pLayer->clr1;
colors[1] = m_pLayer->clr2;
colors[2] = m_pLayer->clr3;
REAL positions[] = { 0.0f, 0.5, 1.0f };
//LinearGradientMode mode = (LinearGradientMode)m_pLayer->gradMode;
LinearGradientMode mode = (LinearGradientMode)HORIZ;
Rect sr = m_pLayer->lrect; sr.Width = m_pLayer->width;
LinearGradientBrush lgbr(sr, Color::Black, Color::White, mode);
lgbr.SetInterpolationColors(colors, positions, 3);
Fill(&lgbr);
}
else if(m_pLayer->LayerType == SETSMOOTH)
{
SmoothingMode sm = SmoothingMode(m_pLayer->clipMode);
m_pGdi->SetSmoothingMode(sm);
}
else if(m_pLayer->LayerType == RESTORESMOOTH)
{
// this is the default smoothing mode
m_pGdi->SetSmoothingMode(SmoothingModeAntiAlias);
}
else if(m_pLayer->LayerType == APPLYXFORM)
{
Transform(m_pLayer->gradMode);
}
}
//
// fills based on the layer shape
//
void Layers::Fill(Brush* pBrush)
{
Rect r = m_pLayer->lrect;
int style = Frame.OuterBorder.shape;
if(style == RECTANGLE)
{
m_pGdi->FillRectangle(pBrush, r);
}
else if(style == ELLIPSE)
{
m_pGdi->FillEllipse(pBrush, r);
}
else if( (style == ROUNDRECT) || (style == TRANSITION) )
{
Color brdr = m_pLayer->clr1;
int radius = Frame.OuterBorder.radius;
Frame.OuterBorder.FillRoundRect(m_pGdi, pBrush, r, brdr, radius);
}
}
//
// Get a path based on the layer shape
//
void Layers::GetPath(GraphicsPath *pPath)
{
if(m_pLayer->LayerShape == RECTANGLE)
{
pPath->AddRectangle(m_pLayer->lrect);
}
else if(m_pLayer->LayerShape == ELLIPSE)
{
pPath->AddEllipse(m_pLayer->lrect);
}
else if(m_pLayer->LayerShape == ROUNDRECT)
{
Frame.OuterBorder.GetRoundRectPath(pPath, m_pLayer->lrect);
}
}
//
// CombineRgns()
//
void Layers::CombineRgns(Region* pRgn1, Region* pRgn2, int mode)
{
if(m_pLayer->clipMode == INTERSECT)
{
pRgn1->Intersect(pRgn2);
}
else if(m_pLayer->clipMode == UNION)
{
pRgn1->Union(pRgn2);
}
else if(m_pLayer->clipMode == XOR)
{
pRgn1->Xor(pRgn2);
}
else if(m_pLayer->clipMode == EXCLUDE)
{
pRgn1->Exclude(pRgn2);
}
else if(m_pLayer->clipMode == COMPLEMENT)
{
pRgn1->Complement(pRgn2);
}
}
void Layers::RegionBlur(Region* pRgn, int amount)
{
// blur the frame rect
Rect rc = Frame.frect;
// size for bitmaps
int width = rc.Width;
int height = rc.Height;
// move rect to origin for bitmaps
Rect bmrc = rc; bmrc.X = 0; bmrc.Y = 0;
// create a bitmap for src data
Bitmap sbm(width, height);
// create a bitmap for dst data
Bitmap dbm(width, height);
// create a graphics object
Graphics gr(&sbm);
// fill the region
pRgn->Translate(-rc.X, -rc.Y);
gr.SetClip(pRgn);
SolidBrush gbr(m_pLayer->clr1);
gr.FillRectangle(&gbr, bmrc);
gr.ResetClip();
// call blur function
SigmoidBlur(&dbm, &sbm, amount);
// draw image
m_pGdi->DrawImage(&dbm, rc, 0, 0, width, height, UnitPixel);
}
//
// This is an optimized version of the blur method from
// this article: http://www.codeproject.com/KB/GDI-plus/aqualize.aspx
//
// Use this function with caution! As the bitmap size and blur amount
// increase, so will the computation time.
//
void Layers::SigmoidBlur(Bitmap* pDst, Bitmap* pSrc, int boxw)
{
int width = pSrc->GetWidth();
int height = pSrc->GetHeight();
// these bitmpas need to be the same size
ASSERT(width == pDst->GetWidth() && height == pSrc->GetHeight());
Rect bmrc(0, 0, width, height);
// lock the src bits
BitmapData sbmData;
pSrc->LockBits(&bmrc, ImageLockModeWrite | ImageLockModeRead, PixelFormat32bppARGB, &sbmData);
// access variables
int line = sbmData.Stride/4;
UINT32* src = (UINT32*)sbmData.Scan0;
// lock the dst bits
BitmapData dbmData;
pDst->LockBits(&bmrc, ImageLockModeWrite | ImageLockModeRead, PixelFormat32bppARGB, &dbmData);
UINT32* dst = (UINT32*)dbmData.Scan0;
float a, r, g, b, weight, weightsum;
UINT32 ia, ir, ig, ib, c;
float* weights = new float[2*boxw+1];
weights[boxw] = 1.0f;
for(int i = 0; i < boxw; i++)
{
float y = Sigmoid((float)i, 0.0f, (float)((boxw + 1)/2), (float)(boxw + 1));
weights[i] = y;
weights[boxw * 2 - i] = y;
}
// horizontal blur
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
r = 0; g = 0; b = 0; a = 0; weightsum = 0;
for (int i = 0; i < boxw * 2 + 1; i++)
{
int x = col - boxw + i;
if (x < 0)
{
i += -x;
x = 0;
}
if (x > width - 1)
break;
c = src[x + row*line];
weight = weights[i];
a += ((c >> 24) & 0x000000FF) * weight;
r += ((c >> 16) & 0x000000FF) * weight;
g += ((c >> 8) & 0x000000FF) * weight;
b += ((c >> 0) & 0x000000FF) * weight;
weightsum += weight;
}
ia = min((UINT32)(a/weightsum), 255);
ir = min((UINT32)(r/weightsum), 255);
ig = min((UINT32)(g/weightsum), 255);
ib = min((UINT32)(b/weightsum), 255);
dst[col + row*line] = ((ia<<24) | (ir<<16) | (ig<<8) | (ib<<0));
}
}
// vertical blur
for (int col = 0; col < width; col++)
{
for (int row = 0; row < height; row++)
{
r = 0; g = 0; b = 0; a = 0; weightsum = 0;
for (int i = 0; i < boxw * 2 + 1; i++)
{
int y = row - boxw + i;
if (y < 0)
{
i += -y;
y = 0;
}
if (y > height - 1)
break;
c = dst[col + y * line];
weight = weights[i];
a += ((c >> 24) & 0x000000FF) * weight;
r += ((c >> 16) & 0x000000FF) * weight;
g += ((c >> 8) & 0x000000FF) * weight;
b += ((c >> 0) & 0x000000FF) * weight;
weightsum += weight;
}
ia = min((UINT32)(a/weightsum), 255);
ir = min((UINT32)(r/weightsum), 255);
ig = min((UINT32)(g/weightsum), 255);
ib = min((UINT32)(b/weightsum), 255);
dst[col + row*line] = ((ia<<24) | (ir<<16) | (ig<<8) | (ib<<0));
}
}
pDst->UnlockBits(&dbmData);
pSrc->UnlockBits(&sbmData);
delete [] weights;
}
//
// This blur method uses a constant weight for the sliding
// box, which makes the calculation independant of the box
// size. Also only the endpoints need to be calculated as
// the box slides. This makes the algorithm much faster,
// but it doesn't look as nice as the SigmoidBlur.
//
void Layers::FastBlur(Bitmap* pDst, Bitmap* pSrc, int boxw)
{
// this function is not finished :)
ASSERT(FALSE);
#if 0
// the approach is here
// http://www.gamasutra.com/features/20010209/Listing3.cpp
int width = pSrc->GetWidth();
int height = pSrc->GetHeight();
// these bitmpas need to be the same size
ASSERT(width == pDst->GetWidth() && height == pSrc->GetHeight());
Rect bmrc(0, 0, width, height);
// lock the src bits
BitmapData sbmData;
pSrc->LockBits(&bmrc, ImageLockModeWrite | ImageLockModeRead, PixelFormat32bppARGB, &sbmData);
// access variables
int line = sbmData.Stride/4;
UINT32* src = (UINT32*)sbmData.Scan0;
// lock the dst bits
BitmapData dbmData;
pDst->LockBits(&bmrc, ImageLockModeWrite | ImageLockModeRead, PixelFormat32bppARGB, &dbmData);
UINT32* dst = (UINT32*)dbmData.Scan0;
float a, r, g, b, weight, weightsum;
UINT32 ia, ir, ig, ib, c, alpha;
if (boxw >= width) boxw = width-1;
weightsum = 2*boxw + 1;
for(int row = 0; row < height; row++)
{
//int tot=0;
r = 0; g = 0; b = 0; a = 0; //weightsum = 0;
for(int col = 0; col < boxw; col++)
{
// tot += src[col];
c = src[col + row*line];
a += ((c >> 24) & 0x000000FF);
r += ((c >> 16) & 0x000000FF);
g += ((c >> 8) & 0x000000FF);
b += ((c >> 0) & 0x000000FF);
}
for(int col = 0; col < width; col++)
{
if( col > boxw)
{
// tot -= src[-boxw-1];
c = src[(col-boxw-1) + row*line];
a -= ((c >> 24) & 0x000000FF);
r -= ((c >> 16) & 0x000000FF);
g -= ((c >> 8) & 0x000000FF);
b -= ((c >> 0) & 0x000000FF);
}
if( (col + boxw) < width)
{
// tot += src[boxw];
c = src[boxw + row*line];
a += ((c >> 24) & 0x000000FF);
r += ((c >> 16) & 0x000000FF);
g += ((c >> 8) & 0x000000FF);
b += ((c >> 0) & 0x000000FF);
alpha = ((c >> 24) & 0x000000FF);
r = alpha * r/255.0f;
g = alpha * g/255.0f;
b = alpha * b/255.0f;
}
//*dst++ = UINT(tot*mul);
ia = min((UINT32)(a/weightsum), 255);
ir = min((UINT32)(r/weightsum), 255);
ig = min((UINT32)(g/weightsum), 255);
ib = min((UINT32)(b/weightsum), 255);
UINT32 clr = ((ia<<24) | (ir<<16) | (ig<<8) | (ib<<0));
if(clr == 0xff000000)
int tmp=1;
dst[col + row*line] = clr;
//src++;
}
}
pDst->UnlockBits(&dbmData);
pSrc->UnlockBits(&sbmData);
#endif
}
//=============================================================================
//
// PaintBar()
//
// Purpose: Paints a bar at the edge of a frame
//
//=============================================================================
void Layers::PaintBar()
{
// various modes leave artifacts
SmoothingMode oldMode = m_pGdi->GetSmoothingMode();
m_pGdi->SetSmoothingMode(SmoothingModeNone);
LinearGradientMode gtype;
Rect rc = m_pLayer->lrect;
int type = m_pLayer->LayerType;
int ewidth = m_pLayer->width;
GraphicsPath path;
if(type == LEFT_EDGE || type == LEFT_EDGE_BEVEL)
{
gtype = (LinearGradientMode)HORIZ;
rc.Width = ewidth;
if(type == LEFT_EDGE)
{
path.AddRectangle(rc);
}
else
{
Point corners[] = { Point(rc.X, rc.Y),
Point(rc.X + ewidth, rc.Y + ewidth),
Point(rc.X + ewidth, rc.Y + rc.Height - ewidth),
Point(rc.X, rc.Y + rc.Height) };
path.AddPolygon(corners, 4);
}
}
else if(type == TOP_EDGE || type == TOP_EDGE_BEVEL)
{
gtype = (LinearGradientMode)VERT;
rc.Height = ewidth;
if(type == TOP_EDGE)
{
path.AddRectangle(rc);
}
else
{
Point corners[] = { Point(rc.X, rc.Y),
Point(rc.X + rc.Width, rc.Y),
Point(rc.X + rc.Width - ewidth, rc.Y + ewidth),
Point(rc.X + ewidth, rc.Y + ewidth) };
path.AddPolygon(corners, 4);
}
}
else if(type == RIGHT_EDGE || type == RIGHT_EDGE_BEVEL)
{
gtype = (LinearGradientMode)HORIZ;
rc.X = rc.X + rc.Width - ewidth;
rc.Width = ewidth;
if(type == RIGHT_EDGE)
{
path.AddRectangle(rc);
}
else
{
Point corners[] = { Point(rc.X, rc.Y + ewidth),
Point(rc.X + rc.Width, rc.Y),
Point(rc.X + rc.Width, rc.Y + rc.Height),
Point(rc.X, rc.Y + rc.Height - ewidth) };
path.AddPolygon(corners, 4);
}
}
else if(type == BOTTOM_EDGE || type == BOTTOM_EDGE_BEVEL)
{
gtype = (LinearGradientMode)VERT;
rc.Y = rc.Y + rc.Height - ewidth;
rc.Height = ewidth;
if(type == BOTTOM_EDGE)
{
path.AddRectangle(rc);
}
else
{
Point corners[] = { Point(rc.X + ewidth, rc.Y),
Point(rc.X + rc.Width - ewidth, rc.Y),
Point(rc.X + rc.Width, rc.Y + rc.Height),
Point(rc.X, rc.Y + rc.Height) };
path.AddPolygon(corners, 4);
}
}
Color colors[5];
colors[0] = m_pLayer->clr1;
colors[1] = m_pLayer->clr2;
colors[2] = m_pLayer->clr3;
LinearGradientBrush lgbr(rc, colors[0], colors[2], gtype);
// the extra interpolation point appears to get rid of some artifacts
if(m_pLayer->rParams[1] != NULL)
{
float profile[3];
profile[0] = m_pLayer->rParams[0];
profile[1] = m_pLayer->rParams[1];
profile[2] = m_pLayer->rParams[2];
lgbr.SetInterpolationColors(colors, profile, 3);
}
// relies on clipping region for round rects
m_pGdi->FillPath(&lgbr, &path);
m_pGdi->SetSmoothingMode(oldMode);
}
//=============================================================================
//
// PaintString()
//
// Purpose: Draws the strings
//
//=============================================================================
void Layers::PaintString()
{
TEXTINFO* pTI = &m_Strings[m_pLayer->strIndex];
Font font(pTI->fname, (float)pTI->fsize, pTI->fstyle, UnitPixel);
SolidBrush br(m_pLayer->clr1);
RectF rf = MakeRectF(m_pLayer->lrect);
// add in any offset
rf.X += pTI->foffset.X;
rf.Y += pTI->foffset.Y;
rf.Width -= pTI->foffset.X;
rf.Height -= pTI->foffset.Y;
StringFormat sf;
if(pTI->align == ALIGN_NEAR)
{
sf.SetAlignment(StringAlignmentNear);
sf.SetLineAlignment(StringAlignmentNear);
}
else if (pTI->align == ALIGN_CENTER)
{
sf.SetAlignment(StringAlignmentCenter);
sf.SetLineAlignment(StringAlignmentCenter);
}
else if (pTI->align == ALIGN_FAR)
{
sf.SetAlignment(StringAlignmentFar);
sf.SetLineAlignment(StringAlignmentFar);
}
m_pGdi->DrawString(pTI->string.c_str(), -1, &font, rf, &sf, &br);
}
//=============================================================================
//
// PaintEffectString()
//
// Purpose: Draws the strings
//
//=============================================================================
void Layers::PaintEffectString()
{
TEXTINFO* pTI = &m_Strings[m_pLayer->strIndex];
Font font(pTI->fname, (float)pTI->fsize, pTI->fstyle, UnitPixel);
SolidBrush br(m_pLayer->clr1);
WCHAR* wstr = (WCHAR*)pTI->string.c_str();
SolidBrush br2(m_pLayer->clr2);
PointF pt(pTI->foffset.X + m_pLayer->lrect.X, pTI->foffset.Y + m_pLayer->lrect.Y);
if(m_pLayer->rParams[0] == OUTLINE)
{
int width = (int)m_pLayer->rParams[1];
int span = 2*width + 1;
GraphicsPath path;
StringFormat sf;
FontFamily ff(pTI->fname);
path.SetFillMode(FillModeWinding);
for(int x = 0; x < span; x++)
{
for(int y = 0; y < span; y++)
{
PointF dp(pt.X + x - width, pt.Y + y - width);
m_pGdi->DrawString(wstr, -1, &font, dp, &br2);
}
}
// draw face
m_pGdi->DrawString(wstr, -1, &font, pt, &br);
}
}
//=============================================================================
//
// PaintImage()
//
// Purpose: Draws the image set by SetImage
//
//=============================================================================
void Layers::PaintImage()
{
IMAGEINFO* pII = &m_Images[m_pLayer->imgIndex];
pII->pImage = new GResource;
RectF srcF, destF, paneF; Unit unit;
// convert pane offset point to float
PointF dptF((float)pII->destpt.X, (float)pII->destpt.Y);
// convert src offset point to float
PointF sptF((float)pII->srcpt.X, (float)pII->srcpt.Y);
// convert pane rect to float
Rect rc = Frame.frect;
paneF.X = (float)rc.X; paneF.Y = (float)rc.Y;
paneF.Width = (float)rc.Width; paneF.Height = (float)rc.Height;
if( pII->pImage->Load(pII->resID, pII->resType) )
{
// get image dimensions
pII->pImage->m_pBitmap->GetBounds(&srcF, &unit);
// offset into src
srcF.Offset(sptF); srcF.Width -= sptF.X; srcF.Height -= sptF.Y;
// destination is pane rect + destpt offset
destF = paneF; destF.Offset(dptF);
// if src is smaller, adjust dest
if(destF.Width > srcF.Width) destF.Width = srcF.Width;
if(destF.Height > srcF.Height) destF.Height = srcF.Height;
// if dest is smaller and clipping is on, adjust src
if(pII->clip == TRUE)
{
if(srcF.Width > destF.Width) srcF.Width = destF.Width;
if(srcF.Height > destF.Height) srcF.Height = destF.Height;
}
if(pII->xform == NOXFORM)
{
m_pGdi->DrawImage(*pII->pImage, destF, srcF.X, srcF.Y, srcF.Width, srcF.Height, unit);
}
else if(pII->xform == LIGHTEN)
{
ColorMatrix HotMat = { 1.05f, 0.00f, 0.00f, 0.00f, 0.00f,
0.00f, 1.05f, 0.00f, 0.00f, 0.00f,
0.00f, 0.00f, 1.05f, 0.00f, 0.00f,
0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
0.05f, 0.05f, 0.05f, 0.00f, 1.00f };
ImageAttributes ia;
ia.SetColorMatrix(&HotMat);
m_pGdi->DrawImage(*pII->pImage, destF, srcF.X, srcF.Y, srcF.Width, srcF.Height, unit, &ia);
}
else if(pII->xform == GRAYSCALE)
{
ColorMatrix GrayMat = { 0.30f, 0.30f, 0.30f, 0.00f, 0.00f,
0.59f, 0.59f, 0.59f, 0.00f, 0.00f,
0.11f, 0.11f, 0.11f, 0.00f, 0.00f,
0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
0.00f, 0.00f, 0.00f, 0.00f, 1.00f };
ImageAttributes ia;
ia.SetColorMatrix(&GrayMat);
m_pGdi->DrawImage(*pII->pImage, destF, srcF.X, srcF.Y, srcF.Width, srcF.Height, unit, &ia);
}
}
}
void Layers::Transform(int xform)
{
Rect r = m_pLayer->lrect;
Bitmap *pBmp = m_pDC->GetBitmap();
if(xform == LIGHTEN)
{
ColorMatrix HotMat = { 1.05f, 0.00f, 0.00f, 0.00f, 0.00f,
0.00f, 1.05f, 0.00f, 0.00f, 0.00f,
0.00f, 0.00f, 1.05f, 0.00f, 0.00f,
0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
0.05f, 0.05f, 0.05f, 0.00f, 1.00f };
ImageAttributes ia;
ia.SetColorMatrix(&HotMat);
m_pGdi->DrawImage((Image*)pBmp, r, 0, 0, r.Width, r.Height, UnitPixel, &ia);
}
else if(xform == GRAYSCALE)
{
ColorMatrix GrayMat = { 0.30f, 0.30f, 0.30f, 0.00f, 0.00f,
0.59f, 0.59f, 0.59f, 0.00f, 0.00f,
0.11f, 0.11f, 0.11f, 0.00f, 0.00f,
0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
0.00f, 0.00f, 0.00f, 0.00f, 1.00f };
ImageAttributes ia;
ia.SetColorMatrix(&GrayMat);
m_pGdi->DrawImage((Image*)pBmp, r, 0, 0, r.Width, r.Height, UnitPixel, &ia);
}
delete pBmp;
}
//=============================================================================
//
// Regenerate
//
// Purpose: Marks the stack to be regenerated on the next paint operation.
// If a layer index is passed, it will reset all of the layer rects
// above and including the index to zero, causing them to be
// recalcculated from the frame rect
//
// Parameters: x - [in] the x offset
// y - [in] the y offset
//
// Returns: None
//
void Layers::Regenerate(int layerIdx)
{
Frame.bUseBitmap = FALSE;
if(layerIdx != -1)
{
for(layer = m_Layers.begin() + layerIdx; layer != m_Layers.end(); ++layer)
{
(*layer).lrect.Width = 0; (*layer).lrect.Height = 0;
}
}
}
//=============================================================================
//
// PaintStack()
//
// Procedure: 1. Create the borders and clipping region
// 2. Paint all the layers into the device context
// 3. Flatten the layers into a bitmap
//
//=============================================================================
void Layers::PaintStack(MemDC* pDC, Graphics* pGraphics)
{
// the Frame must have a rect
ASSERT(!(Frame.frect.Width == 0 && Frame.frect.Height == 0));
// set the Graphics object
m_pGdi = pGraphics;
// set the MemDC object
m_pDC = pDC;
// set clip region
SetClipping(m_pGdi);
for(layer = m_Layers.begin(); layer != m_Layers.end(); ++layer)
{
// pointer to this layer
m_pLayer = &(*layer);
// if the layer rect is not defined, it uses the Frame rect
if( (m_pLayer->lrect.Width == 0) && (m_pLayer->lrect.Height == 0) )
{
m_pLayer->lrect = Frame.frect;
}
if(m_pLayer->LayerShape == UNDEFINED)
{
m_pLayer->LayerShape = Frame.OuterBorder.shape;
}
// paint the layer
PaintLayers();
}
// restore the clip region
RestoreClipping(m_pGdi);
// paint the borders
PaintBorders();
// delete dc in case of regeneration
m_dc.DeleteDC();
int x = Frame.frect.X;
int y = Frame.frect.Y;
int width = Frame.frect.Width;
int height = Frame.frect.Height;
// store into m_dc
CBitmap bmp;
m_dc.CreateCompatibleDC(pDC);
bmp.CreateCompatibleBitmap(pDC, width, height);
m_dc.SelectObject(&bmp);
m_dc.BitBlt(0, 0, width, height, pDC, x, y, SRCCOPY);
bmp.DeleteObject();
Frame.bUseBitmap = TRUE;
}