📄 scgdiplusutils.cpp
字号:
}
}
}
pIGraphics->SCReleaseDC(hOutputDC);
// Ensure that the brush is in destination dc
HBRUSH hBrush = NULL;
HBRUSH hOldBrush = NULL;
Brush* pBrush = pIGraphics->SCGetBrush(); // brush of destination
if (pBrush)
{
Color BrushColor;
switch(pBrush->GetType())
{
case BrushTypeSolidColor:
((SolidBrush*)pBrush)->GetColor(&BrushColor);
hBrush = CreateSolidBrush(BrushColor.ToCOLORREF());
break;
case BrushTypeHatchFill:
((HatchBrush*)pBrush)->GetBackgroundColor(&BrushColor);
hBrush = CreateSolidBrush(BrushColor.ToCOLORREF());
break;
}
} // else NULL brush, let default brush in place.
// TODO: check that the ROP doesn't use the brush
if (hBrush)
hOldBrush = (HBRUSH)SelectObject(hDestDC, hBrush);
// Merge source and dest (this is not smoothed)
StretchDIBits(hDestDC, 0, 0, iTmpWidth, iTmpHeight,
XSrc, YSrc, nSrcWidth, nSrcHeight,
lpBits, lpBitsInfo, iUsage, dwRop);
// We are done with dest DC
if (hBrush)
{
SelectObject(hDestDC, hOldBrush);
DeleteObject(hBrush);
}
SelectObject(hDestDC, hOldBmp);
DeleteDC(hDestDC);
// Create GDI+ bitmap
{
Bitmap Bmp(hDestBmp, (HPALETTE)NULL);
ASSERT(Bmp.GetLastStatus()==Ok);
// GDI+ drawing (smoothed a little according to graphics settings)
Rect destRect(XDest, YDest, nDestWidth, nDestHeight);
pIGraphics->SCDrawBitmap(&Bmp, destRect);
}
// Clean up
DeleteObject(hDestBmp);
return TRUE;
}
///
/// Compute a normalized rectangle to use in GDI+ functions.
/// (GDI's twisted rectangles would result in negative width/height, confusing GDI+)
///
void SCNormalizedRectFromRECT(Rect* pRect, LPCRECT pRc)
{
ASSERT(pRect && pRc);
if (pRc->right < pRc->left)
{
pRect->X = pRc->right;
pRect->Width = pRc->left - pRc->right;
} else
{
pRect->X = pRc->left;
pRect->Width = pRc->right - pRc->left;
}
if (pRc->bottom < pRc->top)
{
pRect->Y = pRc->bottom;
pRect->Height = pRc->top - pRc->bottom;
} else
{
pRect->Y = pRc->top;
pRect->Height = pRc->bottom - pRc->top;
}
}
///
/// Compute clockwise angles to use in GDI+ functions from GDI's basic information.
///
void SCBuildArcAngles(LPCRECTL pBox, LPCPOINTL pPtStart, LPCPOINTL pPtEnd, INT iArcDir,
double& dStartAngle, double& dSweepAngle)
{
double dCenterX = (pBox->right + pBox->left)/2;
double dCenterY = (pBox->bottom + pBox->top)/2;
dStartAngle = SC_DEGREES(atan2((pPtStart->y - dCenterY), pPtStart->x - dCenterX));
double dEndAngle = SC_DEGREES(atan2((pPtEnd->y - dCenterY), pPtEnd->x - dCenterX));
dSweepAngle = (dEndAngle - dStartAngle);
// arc direction is used (AD_COUNTERCLOCKWISE or AD_CLOCKWISE)
if (iArcDir==AD_CLOCKWISE)
{// AD_CLOCKWISE for GDI
if (dSweepAngle<=0)
{// Negative values are counterclockwise for GDI+. So get the angle's negative complement.
dSweepAngle = -(360 + dSweepAngle); // counterclockwise for GDI+
} // else clockwise for GDI+
} else
{// AD_COUNTERCLOCKWISE for GDI
if (dSweepAngle>=0)
{// Positive values are clockwise for GDI+. So get the angle's negative complement.
dSweepAngle = -(360 - dSweepAngle); // counterclockwise for GDI+
} // else clockwise for GDI+
}
}
///
/// Intersection of a cercle define by its center and radius, and a line passing by this center.
/// (Angle is in degrees)
///
void SCIntersectLineAndCircle(Point& PtRes, POINTL* pPtCenter, DWORD dwRadius, double fLineAngle)
{
double dAngle = -SC_RAD(fLineAngle);
PtRes.X = static_cast<INT>(pPtCenter->x + dwRadius*cos(dAngle));
PtRes.Y = static_cast<INT>(pPtCenter->y + dwRadius*sin(dAngle));
}
///
/// Intersection of an ellipse define by a rectangle, and a line passing by its center.
/// (Angle is in degrees)
///
void SCIntersectLineAndEllipse(Point& PtRes, LPCRECTL pBox, double fLineAngle)
{
int xPtCenter = (pBox->left + pBox->right)/2;
int yPtCenter = (pBox->top + pBox->bottom)/2;
double a = (pBox->right - pBox->left);
double b = (pBox->bottom - pBox->top);
double dAngle = -SC_RAD(fLineAngle);
double a_sinAngle = a*sin(dAngle);
double b_cosAngle = b*cos(dAngle);
double d = sqrt(a_sinAngle*a_sinAngle + b_cosAngle*b_cosAngle);
PtRes.X = static_cast<INT>(xPtCenter + (a*b_cosAngle/d));
PtRes.Y = static_cast<INT>(yPtCenter + (b*a_sinAngle/d));
}
HBRUSH SCBrushFromGdipImage(Image* pImage)
{
ASSERT(pImage);
ASSERT(pImage->GetType()==ImageTypeBitmap);
int iWidth = pImage->GetWidth();
int iHeight = pImage->GetHeight();
HDC hdc = CreateCompatibleDC(NULL);
HBITMAP hbmp = NULL;
BitmapData BmData;
Rect rect(0, 0, iWidth, iHeight);
Status iRes = ((Bitmap*)pImage)->LockBits(&rect, ImageLockModeRead,
PixelFormat32bppARGB, &BmData);
ASSERT(iRes==Ok);
if (iRes==Ok)
{
BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = iWidth;
bmih.biHeight = iHeight; // bottom-up (beware of sign)
bmih.biPlanes = 1;
bmih.biBitCount = 32;
bmih.biCompression = BI_RGB;
bmih.biClrUsed = 0;
// just set the rest to 0
bmih.biSizeImage = 0;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrImportant = 0;
bmih.biSizeImage = iWidth * iHeight * 4;
LPVOID pBits;
hbmp = CreateDIBSection(hdc, (BITMAPINFO*)&bmih, DIB_RGB_COLORS, &pBits, NULL, 0);
ASSERT(hbmp);
if (hbmp)
{
SetDIBits(hdc, hbmp, 0, iHeight, (CONST VOID *)BmData.Scan0, (BITMAPINFO*)&bmih, DIB_RGB_COLORS);
}
}
((Bitmap*)pImage)->UnlockBits(&BmData);
// Warning: May create resource leak, as we don't call DeleteObject on hbmp
HBRUSH hBrush = CreatePatternBrush(hbmp);
DeleteDC(hdc);
return hBrush;
}
///
/// Perfom a pattern blt opeartion using ROP.
///
INT SCMergeAreaWithPattern(
I_SCDCGraphics* pIGraphics, // pointer to destination graphics context
Rect& destRect, // dest. rectangle
DWORD dwRop) // raster operation code
{
ASSERT(pIGraphics);
// Prepare the temporary destination bitmap
HDC hOutputDC = pIGraphics->SCGetDC();
ASSERT(hOutputDC);
if (!hOutputDC)
return FALSE;
HDC hDestDC = CreateCompatibleDC(hOutputDC);
INT nDestWidth = destRect.Width; // width of destination rectangle
INT nDestHeight = destRect.Height; // height of destination rectangle
HBITMAP hDestBmp = CreateCompatibleBitmap(hOutputDC, nDestWidth, nDestHeight);
ASSERT(hDestBmp);
if (!hDestBmp)
{
DWORD dwError = GetLastError();
return FALSE;
}
HBITMAP hOldBmp = (HBITMAP)SelectObject(hDestDC, hDestBmp);
// Copy the destination rectangle
if (!StretchBlt(hDestDC, 0, 0, nDestWidth, nDestHeight,
hOutputDC, destRect.X, destRect.Y, nDestWidth, nDestHeight, SRCCOPY))
{// Maybe failure for rotation/shear found in hOutputDC
// (do a manual copy of the destination rectangle)
int ys = destRect.Y;
for (int y=0; (y<nDestHeight); y++, ys++)
{
int xs = destRect.X;
for (int x=0; (x<nDestWidth); x++, xs++)
{
COLORREF Color = GetPixel(hOutputDC, xs, ys);
SetPixel(hDestDC, x, y, Color);
}
}
}
pIGraphics->SCReleaseDC(hOutputDC);
// Ensure that the brush is in destination dc
HBRUSH hBrush = NULL;
Brush* pBrush = pIGraphics->SCGetBrush(); // brush of destination
if (pBrush)
{
Color BrushColor;
switch(pBrush->GetType())
{
case BrushTypeSolidColor:
((SolidBrush*)pBrush)->GetColor(&BrushColor);
hBrush = CreateSolidBrush(BrushColor.ToCOLORREF());
break;
case BrushTypeHatchFill:
((HatchBrush*)pBrush)->GetBackgroundColor(&BrushColor);
hBrush = CreateSolidBrush(BrushColor.ToCOLORREF());
break;
case BrushTypeTextureFill:
{
Image* pImage = ((TextureBrush*)pBrush)->GetImage();
ASSERT(pImage);
hBrush = SCBrushFromGdipImage(pImage);
delete pImage;
}
break;
default:
ASSERT(0); // TODO
}
} // else NULL brush, let default brush in place.
// TODO: check that the ROP doesn't use the brush
// Merge source and dest (this is not smoothed)
if (hBrush)
{
HBRUSH hOldBrush = (HBRUSH)SelectObject(hDestDC, hBrush);
PatBlt(hDestDC, 0, 0, nDestWidth, nDestHeight, dwRop);
SelectObject(hDestDC, hOldBrush);
DeleteObject(hBrush);
} else
{
PatBlt(hDestDC, 0, 0, nDestWidth, nDestHeight, dwRop);
}
// We are done with dest DC
SelectObject(hDestDC, hOldBmp);
DeleteDC(hDestDC);
// Create GDI+ bitmap
{
Bitmap Bmp(hDestBmp, (HPALETTE)NULL);
ASSERT(Bmp.GetLastStatus()==Ok);
pIGraphics->SCDrawBitmap(&Bmp, destRect);
}
// Clean up
DeleteObject(hDestBmp);
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////////////////
// The algorithm of this function was adapted from XPDF
// (Patrick Moreau, Martin P.J. Zinser, (c) Glyph & Cog, LLC)
//
// "kludge for polygon fills: First, it divides up the [subpolys] into
// non-overlapping polygons by simply comparing bounding rectangles.
// Then it connects [subpolys] within a single compound polygon to a single
// point so that [GDI+] can fill the polygon (sort of)."
//
Point* SCConvertPolyPoly(Point* pPolyPoints, DWORD& numPoints,
DWORD* pPolyVertices, DWORD& dwNbPolys)
{
Point* pPoints = pPolyPoints;
// allocate new points
int size = numPoints + dwNbPolys*2; // add 2 more points by poly
Point* pPolyPts = new Point[size];
// allocate bounding rectangles array
RECT *rects = new RECT[dwNbPolys];
// rebuild each subpoly
numPoints = 0;
Point Pt0(0, 0);
Point* pLimit = pPoints;
for (int iPoly = 0; (iPoly < (int)dwNbPolys); ++iPoly)
{
// add the points
int iFirstPt = numPoints;
pLimit += pPolyVertices[iPoly];
for (; (pPoints < pLimit); pPoints++)
{ pPolyPts[numPoints++] = *pPoints; }
// construct bounding rectangle
rects[iPoly].left = rects[iPoly].right = pPolyPts[iFirstPt].X;
rects[iPoly].top = rects[iPoly].bottom = pPolyPts[iFirstPt].Y;
for (int k = iFirstPt + 1; (k < (int)numPoints); ++k)
{
if (pPolyPts[k].X < rects[iPoly].left)
rects[iPoly].left = pPolyPts[k].X;
else if (pPolyPts[k].X > rects[iPoly].right)
rects[iPoly].right = pPolyPts[k].X;
if (pPolyPts[k].Y < rects[iPoly].top)
rects[iPoly].top = pPolyPts[k].Y;
else if (pPolyPts[k].Y > rects[iPoly].bottom)
rects[iPoly].bottom = pPolyPts[k].Y;
}
// close subpoly if necessary
if (pPolyPts[numPoints-1].X != pPolyPts[iFirstPt].X ||
pPolyPts[numPoints-1].Y != pPolyPts[iFirstPt].Y)
{
// add point
pPolyPts[numPoints++] = pPolyPts[iFirstPt];
}
// length of this subpoly
pPolyVertices[iPoly] = numPoints - iFirstPt;
// leave an extra point for compound fill hack
pPolyPts[numPoints++] = Pt0;
}
// combine compound polygons
RECT rect; // temp rect for overlaps detection
DWORD dwNewNbPolys = 0; // new polygons counter
int iCurPoly = 0; // index of current polygon to examine or merge
int iOverlap = 0; // index of a polygon overlapping iCurPoly
int iFirstPt = 0; // index of first point in iCurPoly
int iLastPt = 0; // index of last point in iCurPoly
while (iCurPoly < (int)dwNbPolys)
{
// start a new polygon with the iCurPoly subpoly
rect = rects[iCurPoly];
pPolyVertices[dwNewNbPolys] = pPolyVertices[iCurPoly];
iFirstPt = iLastPt;
iLastPt += pPolyVertices[iCurPoly] + 1; // +1 for the extra point
pPolyPts[iLastPt - 1] = pPolyPts[iFirstPt];
// combine overlapping polygons
++iCurPoly;
do
{
// look for the first subsequent subpoly, if any, which overlaps
for (iOverlap = iCurPoly; (iOverlap < (int)dwNbPolys); ++iOverlap)
{
if (rects[iOverlap].right > rects[iCurPoly].left &&
rects[iOverlap].left < rects[iCurPoly].right &&
rects[iOverlap].bottom > rects[iCurPoly].top &&
rects[iOverlap].top < rects[iCurPoly].bottom)
{
// there is an overlap, combine the polygons
// (by anchoring them at the first point of current polygon)
for (; (iCurPoly <= iOverlap); ++iCurPoly)
{
if (rects[iCurPoly].left < rect.left)
rect.left = rects[dwNewNbPolys].left;
if (rects[iCurPoly].right > rect.right)
rect.right = rects[dwNewNbPolys].right;
if (rects[iCurPoly].top < rect.top)
rect.top = rects[dwNewNbPolys].top;
if (rects[iCurPoly].bottom > rect.bottom)
rect.bottom = rects[dwNewNbPolys].bottom;
// merge points
pPolyVertices[dwNewNbPolys] += pPolyVertices[iCurPoly] + 1;
// anchor this subpoly
iLastPt += pPolyVertices[iCurPoly] + 1;
pPolyPts[iLastPt - 1] = pPolyPts[iFirstPt];
}
// exit to check next overlap with this compound polygon
break;
}
}
} while (iOverlap < (int)dwNbPolys && iCurPoly < (int)dwNbPolys);
++dwNewNbPolys;
}
// free bounding rectangles
delete [] rects;
dwNbPolys = dwNewNbPolys;
return pPolyPts;
}
//////////////////////////////////////////////////////////////////////////////////////////
///
/// Create a GDI+ list of PathPointTypes from a list of GDI point types.
///
BYTE* SCPathPointTypesFROMCurveTypes(LPCBYTE pGDITypes, DWORD dwCount)
{
ASSERT(pGDITypes);
BYTE *pTypes = new BYTE[dwCount];
if (pTypes)
{
memmove(pTypes, pGDITypes, dwCount);
SCConvertGDIPointTypes(pTypes, dwCount);
}
return pTypes;
}
///
/// Convert (in place) a list of GDI point types to GDI+ PathPointTypes.
///
void SCConvertGDIPointTypes(LPBYTE pTypes, DWORD dwCount)
{
ASSERT(pTypes);
// convert path point types
for (DWORD i=0; (i<dwCount); i++)
{
// GDI+ documentation states:
// "The PathPointType enumeration indicates point types and flags for the data points
// in a path. Bits 0 through 2 indicate the type of a point, and bits 3 through 7
// hold a set of flags that specify attributes of a point."
// Hence the dirty cast of an enum into BYTE;
// Note that no function takes an array of PathPointType in the GraphicsPath class.
switch (pTypes[i] & SC_PATH_PTMASK)
{
case PT_MOVETO:
pTypes[i] = (BYTE)PathPointTypeStart;
break;
case PT_LINETO:
if (pTypes[i] & SC_PATH_CLOSEFIG)
pTypes[i] = (BYTE)(PathPointTypeLine|PathPointTypeCloseSubpath);
else
pTypes[i] = (BYTE)PathPointTypeLine;
break;
case PT_BEZIERTO:
if (pTypes[i] & SC_PATH_CLOSEFIG)
pTypes[i] = (BYTE)(PathPointTypeBezier|PathPointTypeCloseSubpath);
else
pTypes[i] = (BYTE)PathPointTypeBezier;
break;
}
}
}
///
/// Subtype of an EMF: EmfTypeEmfOnly|EmfTypeEmfPlusOnly|EmfTypeEmfPlusDual
///
EmfType SCGdipGetEMFType(HENHMETAFILE hemf)
{
Metafile metafile(hemf);
MetafileHeader header;
metafile.GetMetafileHeader(&header);
switch(header.GetType())
{
case MetafileTypeEmfPlusOnly:
return EmfTypeEmfPlusOnly;
case MetafileTypeEmfPlusDual:
return EmfTypeEmfPlusDual;
}
return EmfTypeEmfOnly;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -