📄 scgdiplusutils.cpp
字号:
/*
* This file is part of the EMFexplorer projet.
* Copyright (C) 2004 Smith Charles.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* Extension: for commercial use, apply the Equity Public License, which
* adds to the normal terms of the GLPL a condition of donation to the author.
* If you are interested in support for this source code,
* contact Smith Charles <smith.charles@free.fr> for more information.
*/
#include "stdafx.h"
#include "SCGdiplusUtils.h"
#include "SCEMF.h"
#include "SCEMFRasterizer.h"
#include "SCFileTypes.h"
#include "SCGenInclude.h"
#include SC_INC_WINLIB(SCMemDC.h)
#include SC_INC_WINLIB(SCBitmap.h)
#include SC_INC_WINLIB(MSDib.h)
#include SC_INC_WINLIB(SCGDIUtils.h)
#include SC_INC_GENLIB(SCGenMath.h)
#include <float.h>
// GDI+ won't allow DEBUG_NEW to work
// #ifdef _DEBUG
// #define new DEBUG_NEW
// #undef THIS_FILE
// static char THIS_FILE[] = __FILE__;
// #endif
///
/// Create a GDI+ bitmap from a packed DIB (a pointer to a
/// BITMAPINFO structure immediately followed by the array of bytes that define
/// the pixels of the bitmap).
///
Bitmap* SCBitmapFromPtr(LPCBYTE pBytes)
{
ASSERT(pBytes);
if (!pBytes)
return NULL;
// get the BITMAPINFO
BITMAPINFO* pBmi = (BITMAPINFO*)pBytes;
// Locate the bits after the color table:
// Since the DIB is packed, the biClrUsed member must be either zero or
// the actual size of the color table
BYTE* pBits = (BYTE*)pBmi + pBmi->bmiHeader.biSize + pBmi->bmiHeader.biClrUsed;
// create GDI+ object
Bitmap* pBitmap = new Bitmap(pBmi, pBits);
if (!pBitmap || (pBitmap->GetLastStatus()!=Ok))
{
ASSERT(0);
delete pBitmap;
return NULL;
}
return pBitmap;
}
///
/// Create a GDI+ bitmap from a packed DIB (a global memory chunk pointing to a
/// BITMAPINFO structure immediately followed by the array of bytes that define
/// the pixels of the bitmap).
/// Note: on 32-bit systems, the HGLOBAL is not quite different from a pointer
///
Bitmap* SCBitmapFromHGLOBAL(HGLOBAL hMem, SCShortDCState& rDCState)
{
ASSERT(hMem);
if (!hMem)
return NULL;
// get the BITMAPINFO
BITMAPINFO* pBmi = (BITMAPINFO*)hMem; //GlobalLock(hMem);
// Locate the bits after the color table:
// Since the DIB is packed, the biClrUsed member must be either zero or
// the actual size of the color table
INT iNbColors = DIBNumColors(LPBITMAPINFOHEADER(pBmi));
BYTE* pBits = (BYTE*)pBmi + pBmi->bmiHeader.biSize + iNbColors*sizeof(RGBQUAD);
// create GDI+ object
Bitmap* pBitmap = NULL;
if (32==pBmi->bmiHeader.biBitCount)
pBitmap = new Bitmap(pBmi->bmiHeader.biWidth, pBmi->bmiHeader.biHeight, 4*pBmi->bmiHeader.biWidth,
PixelFormat32bppARGB, pBits);
else
{
pBitmap = new Bitmap(pBmi, pBits);
INT iRop2 = rDCState.dwRop2;
// Update color palette for 1-bpp.
if (1==pBmi->bmiHeader.biBitCount)
{
RGBQUAD* pColors;
RGBQUAD MonoColors[2];
if (rDCState.bMonoDC)
{// Slight difference for DIB? Is it ONLY when the DC IS MONOCHROME that
// background and text colors come into play?
// "When an application selects a two-color DIB pattern brush into a monochrome
// device context, Windows ignores the colors specified in the DIB and instead
// displays the pattern brush using the current text and background colors of
// the device context. Pixels mapped to the first color
// (at offset 0 in the DIB color table) of the DIB are displayed using the
// text color. Pixels mapped to the second color (at offset 1 in the color table)
// are displayed using the background color."
pColors = (RGBQUAD*)&MonoColors;
SCFillMonochromePalette(rDCState, (PPALETTEENTRY)pColors);
} else
{// ignore background and text colors, and use the DIB color table
pColors = (RGBQUAD*)&pBmi->bmiColors;
}
#define SC_ISWHITE_RGBQUAD(q) (255==q.rgbRed && 255==q.rgbGreen && 255==q.rgbBlue)
ColorPalette* pPal = (ColorPalette*)new BYTE[sizeof(ColorPalette) + iNbColors*sizeof(ARGB)];
pPal->Flags = PaletteFlagsHasAlpha;
pPal->Count = iNbColors;
INT iAlpha = 255;
INT i;
switch (iRop2)
{
case R2_MASKPEN:
// The normal code is "black=>opaque, and white=>transparent". But we
// make black pixels half-black and make white pixels transparent.
// take it as some hand-tuned "gamma corection".
for (i=0; (i<iNbColors); i++)
{
if (SC_ISWHITE_RGBQUAD(pColors[i]))
iAlpha = 0; // fully transparent;
else
iAlpha = 255; // fully opaque (128 works better; why?)
pPal->Entries[i] = Color::MakeARGB(iAlpha, pColors[i].rgbRed,
pColors[i].rgbGreen,
pColors[i].rgbBlue);
}
break;
default:
// normal palette
for (i=0; (i<iNbColors); i++)
{
pPal->Entries[i] = Color::MakeARGB(255, pColors[i].rgbRed,
pColors[i].rgbGreen,
pColors[i].rgbBlue);
}
}
pBitmap->SetPalette(pPal);
delete [] pPal;
}
}
//GlobalUnlock(hMem);
if (pBitmap && (pBitmap->GetLastStatus()!=Ok))
{
ASSERT(0);
delete pBitmap;
return NULL;
}
return pBitmap;
}
///
/// Create a GDI+ bitmap from a handle to a Windows bitmap.
///
Bitmap* SCBitmapFromMemBmp(HBITMAP hBM)
{
ASSERT(hBM);
// GDI+ 1.0: we don't have a simple constructor Bitmap::Bitmap(HBITMAP)
// But a palette residing outside the bitmap was carefully cast in. Jeez!
Bitmap* pBitmap = new Bitmap(hBM, (HPALETTE)NULL);
if (pBitmap && (pBitmap->GetLastStatus()!=Ok))
{
ASSERT(0);
delete pBitmap;
return NULL;
}
return pBitmap;
}
///
/// Create a two-color palette to use with the monochrome brush currently selected into a DC.
/// "A brush created by using a monochrome (1 bit per pixel) bitmap has
/// the text and background colors of the device context to which it is drawn.
/// Pixels represented by a 0 bit are drawn with the current text color;
/// pixels represented by a 1 bit are drawn with the current background color."
///
HPALETTE SCCreatePatternPalette(SCShortDCState& rDCState)
{
// Create the logical palette based on the entries
// Allocate memory for the palette.
LPLOGPALETTE lpLogPal = (LPLOGPALETTE)new BYTE [sizeof(LOGPALETTE) + 2*sizeof(PALETTEENTRY)];
if (!lpLogPal)
return NULL;
// Initialize.
lpLogPal->palVersion = 0x300;
lpLogPal->palNumEntries = 2;
// Update colors in palette.
SCFillMonochromePalette(rDCState, lpLogPal->palPalEntry);
// Create the palette.
HPALETTE hPal = CreatePalette(lpLogPal);
// Clean up.
delete [] (BYTE*)lpLogPal;
return hPal;
}
///
/// Create a two-color bitmap representing a monochrome brush.
/// "A brush created by using a monochrome (1 bit per pixel) bitmap has
/// the text and background colors of the device context to which it is drawn.
/// Pixels represented by a 0 bit are drawn with the current text color;
/// pixels represented by a 1 bit are drawn with the current background color."
///
// Direct method, with external palette
Bitmap* SCPatternFromMemBmp(HBITMAP hBM, SCShortDCState& rDCState)
{
ASSERT(hBM);
rDCState.bMonoBrush = FALSE;
#if 0
// Won't work: GDI+ ignores the palette this time (try to understand)
HPALETTE hPal = SCCreatePatternPalette(rDCState);
ASSERT(hPal);
Bitmap* pBitmap = new Bitmap(hBM, hPal);
if (!pBitmap)
return NULL;
if (pBitmap->GetLastStatus()!=Ok)
{
ASSERT(0);
delete pBitmap;
pBitmap = NULL;
}
DeleteObject(hPal);
#else
BITMAP bm;
int iRes = GetObject(hBM, sizeof(BITMAP), &bm);
ASSERT(iRes);
// Trying to set a GDI+ palette after bitmap creation
Bitmap* pBitmap = new Bitmap(hBM, (HPALETTE)NULL);
if (!pBitmap)
return NULL;
if (pBitmap->GetLastStatus()!=Ok)
{
ASSERT(0);
delete pBitmap;
return NULL;
}
if (bm.bmBitsPixel!=1)
return pBitmap;
rDCState.bMonoBrush = TRUE;
// Update color palette for 1-bpp.
if (!SCSetMonochromeBrushPalette(pBitmap, rDCState))
{
ASSERT(0);
delete pBitmap;
pBitmap = NULL;
}
#endif
return pBitmap;
}
///
/// Create a GDI+ brush from a GDI LOGBRUSH.
///
Brush* SCBrushFromLogBrush(LOGBRUSH& rLogBrush, SCShortDCState& rDCState)
{
Brush* pBrush = NULL;
rDCState.bMonoBrush = FALSE;
switch (rLogBrush.lbStyle)
{
case BS_SOLID:
{// Solid brush
Color BrushColor;
BrushColor.SetFromCOLORREF(rLogBrush.lbColor);
pBrush = new SolidBrush(BrushColor);
}
break;
case BS_HATCHED:
{// Hatched brush
HatchStyle hatchStyle;
switch (rLogBrush.lbHatch)
{
case HS_BDIAGONAL:
hatchStyle = HatchStyleBackwardDiagonal;
break;
case HS_CROSS:
hatchStyle = HatchStyleCross;
break;
case HS_DIAGCROSS:
hatchStyle = HatchStyleDiagonalCross;
break;
case HS_FDIAGONAL:
hatchStyle = HatchStyleForwardDiagonal;
break;
case HS_HORIZONTAL:
hatchStyle = HatchStyleHorizontal;
break;
case HS_VERTICAL:
hatchStyle = HatchStyleVertical;
break;
default:
ASSERT(0); // not reached
}
Color FgColor;
FgColor.SetFromCOLORREF(rLogBrush.lbColor);
// Background color is controlled by current BkColor and BkMode in DC
COLORREF ColorRef = rDCState.crBkColor;
BYTE bAlpha = (TRANSPARENT==rDCState.dwBkMode) ? 0 : 255;
Color BkColor(bAlpha, GetRValue(ColorRef), GetGValue(ColorRef), GetBValue(ColorRef));
pBrush = new HatchBrush(hatchStyle, FgColor, BkColor);
}
break;
case BS_PATTERN:
case BS_PATTERN8X8:
{// Patterned brush (memory bitmap) referenced by a HBITMAP in lbHatch
// "A brush created by using a monochrome (1 bit per pixel) bitmap has
// the text and background colors of the device context to which it is drawn.
// Pixels represented by a 0 bit are drawn with the current text color;
// pixels represented by a 1 bit are drawn with the current background color."
//
Bitmap* pBitmap = SCPatternFromMemBmp((HBITMAP)rLogBrush.lbHatch, rDCState);
if (pBitmap)
{
pBrush = new TextureBrush(pBitmap);
delete pBitmap;
}
}
break;
case BS_DIBPATTERNPT:
{// Patterned brush (packed DIB) referenced by a pointer in lbHatch
Bitmap* pBitmap = SCBitmapFromPtr((LPBYTE)rLogBrush.lbHatch);
if (pBitmap)
{
pBrush = new TextureBrush(pBitmap);
delete pBitmap;
}
}
break;
case BS_DIBPATTERN:
case BS_DIBPATTERN8X8:
{// Patterned brush (packed DIB) referenced by an HGLOBAL in lbHatch
Bitmap* pBitmap = SCBitmapFromHGLOBAL((HGLOBAL)rLogBrush.lbHatch, rDCState);
if (pBitmap)
{
pBrush = new TextureBrush(pBitmap);
delete pBitmap;
}
}
break;
case BS_NULL:
// pBrush = NULL; // already done
break;
case BS_INDEXED:
case BS_MONOPATTERN:
// Undocumented (maybe driver specific)
ASSERT(0);
break;
default:
ASSERT(0);
}
ASSERT(!pBrush || (pBrush->GetLastStatus()==Ok));
return pBrush;
}
///
/// Create a GDI+ brush from a GDI LOGPEN.
///
Pen* SCPenFromLogPen(LOGPEN& rLogPen)
{
if (PS_NULL==rLogPen.lopnStyle) // no bit masking is required
return NULL;
Color PenColor;
PenColor.SetFromCOLORREF(rLogPen.lopnColor);
Pen* pPen = new Pen(PenColor, (REAL)rLogPen.lopnWidth.x);
// Line style
switch (rLogPen.lopnStyle)
{
case PS_SOLID:
pPen->SetDashStyle(DashStyleSolid);
break;
case PS_DASH:
pPen->SetDashStyle(DashStyleDash);
break;
case PS_DOT:
pPen->SetDashStyle(DashStyleDot);
break;
case PS_DASHDOT:
pPen->SetDashStyle(DashStyleDashDot);
break;
case PS_DASHDOTDOT:
pPen->SetDashStyle(DashStyleDashDotDot);
break;
case PS_INSIDEFRAME:
// The pen is solid, geometric
pPen->SetDashStyle(DashStyleSolid);
// GDI:"When this pen is used in any graphics device interface (GDI) drawing
// function that takes a bounding rectangle, the dimensions of the figure
// are shrunk so that it fits entirely in the bounding rectangle, taking into
// account the width of the pen."
// GDI+:"PenAlignmentInset Specifies, when drawing a polygon,
// that the pen is aligned on the inside of the edge of the polygon."
pPen->SetAlignment(PenAlignmentInset);
// One of the documentations is clearer than the other: make your choice.
break;
case PS_ALTERNATE:
// fall through
default:
ASSERT(0);
}
// Investigate to find out if a default GDI+ pen should be considered GEOMETRIC.
// If so apply default join and cap styles
#pragma message( __FILE__ "(449): TODO: Review default GDI+ pen")
#if 0
// Default values (cf. MSDN "Pens in Win32")
// Lines joint (applies only to PS_GEOMETRIC pens in GDI)
pPen->SetLineJoin(LineJoinRound);
// End cap (applies only to PS_GEOMETRIC pens in GDI)
pPen->SetLineCap(LineCapRound, LineCapRound, DashCapRound);
#endif
ASSERT(!pPen || (pPen->GetLastStatus()==Ok));
return pPen;
}
///
/// Create a GDI+ brush from a GDI EXTLOGPEN.
///
Pen* SCPenFromExtLogPen(EXTLOGPEN* pExtLogPen, SCShortDCState& rDCState)
{
ASSERT(pExtLogPen);
INT iPenStyle = pExtLogPen->elpPenStyle & PS_STYLE_MASK; // style bits masking is required
if (PS_NULL==iPenStyle)
return NULL;
// Note: don't know how to tell GDI+ about PS_COSMETIC or PS_GEOMETRIC pens
INT iPenType = pExtLogPen->elpPenStyle & PS_TYPE_MASK;
// Default pen width and color
INT iWidth = (PS_GEOMETRIC==iPenType) ? pExtLogPen->elpWidth : 1;
Color PenColor;
PenColor.SetFromCOLORREF(pExtLogPen->elpColor);
Pen* pPen = new Pen(PenColor, (REAL)iWidth);
// Line style
switch (iPenStyle)
{
case PS_SOLID:
pPen->SetDashStyle(DashStyleSolid);
break;
case PS_DASH:
pPen->SetDashStyle(DashStyleDash);
break;
case PS_DOT:
pPen->SetDashStyle(DashStyleDot);
break;
case PS_DASHDOT:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -