Files
Slnkdwf/SlnkDWFCom/Whip2PNG.cpp
Jos Groot Lipman dcf444798e v4.29 ZLMV#78395 Whip2DC ook correctie voor niet orthogonale Units
Whip2PNG.flags en Whipfile.flags ingevoerd. Bitje +1 om bovenstaande te activeren

svn path=/Slnkdwf/trunk/; revision=61777
2023-09-12 09:25:39 +00:00

707 lines
18 KiB
C++

// Whip2PNG.cpp : Implementation of CWhip2PNG
#include "stdafx.h"
#include "CxImage\CxImage\ximage.h"
#include "Whip2DCImpl.h"
#include "Whip2PNG.h"
#include "metafile.h"
CWhip2PNG::CWhip2PNG()
{
m_sizeX = 800;
m_sizeY = 600;
m_offsetX = 0;
m_offsetY = 0;
m_dScale = 1.0;
m_lRotation = 0;
m_AAFactor = 1;
m_AAMethod = 0; // ximatran.cpp resample function
// 0 for slow (bilinear) method
// 1 for fast (nearest pixel) method, or
// 2 for accurate (bicubic spline interpolation) method.
m_RegExp = ".*";
m_forcePaper = FALSE;
m_forceBW = FALSE;
m_markers = 0;
m_flags = 0;
m_forceGray = FALSE;
m_Maximize = FALSE;
m_dwgScale = 0.0;
}
STDMETHODIMP CWhip2PNG::Load(BSTR WhipPath)
{
myDoTRACE("\nCWhip2PNG::Load('%ls')", WhipPath);
m_WhipPath = WhipPath;
return S_OK;
}
STDMETHODIMP CWhip2PNG::LoadStream(VARIANT EPlotStream)
{
myDoTRACE("\nCWhip2PNG::LoadStream()");
if (EPlotStream.vt!=VT_ERROR)
{
VARIANT *var2 = &EPlotStream;
if (var2->vt==(VT_VARIANT|VT_BYREF)) // ByRef
var2 = (VARIANT *)var2->pvarVal;
if (var2->vt==VT_DISPATCH)
{
m_iEPlotSection = var2->pdispVal;
if (!m_iEPlotSection)
return E_INVALIDARG;
}
else
return E_INVALIDARG;
}
return S_OK;
}
// Werkt nog niet echt goed?
STDMETHODIMP CWhip2PNG::SaveAsWMF(BSTR WMFPath)
{
CmyTimer timer("CWhip2PNG::SaveAsWMF");
#if 0
HDC myDC = CreateEnhMetaFile(NULL,CString(WMFPath),NULL,NULL); // In memory
InitDC(myDC);
if (m_forcePaper)
HRESULT res = m_iWhip2DC.set_paperColor(m_paperColor);
res = m-iWhip2DC->Paint(m_forceBW, m_markers);
HENHMETAFILE hMF = CloseEnhMetaFile(myDC);
DeleteEnhMetaFile(hMF);
DeleteDC(myDC);
#endif
#if 1
HDC myDC = CreateMetaFile(NULL); // In memory
InitDC(myDC);
HRESULT res;
if (m_forcePaper)
res = m_iWhip2DC.put_paperColor(m_paperColor);
res = m_iWhip2DC.Paint(m_forceBW, m_markers);
HMETAFILE hMF = CloseMetaFile(myDC);
CMetafile::WriteMetaFileAsPlaceable(hMF, CSize(m_sizeX, m_sizeY),m_sizeX/4,CString(WMFPath));
DeleteMetaFile(hMF);
DeleteDC(myDC);
#endif
putDWGInfo(); // Generieke info naar het event
return S_OK;
}
bool CWhip2PNG::CreateCxImage(CxImage *img)
{
CmyTimer timer("CWhip2PNG::CreateCXImage");
bool res = true;
//----
HDC pDC = ::GetDC(0);
HDC myDC = CreateCompatibleDC(pDC);
//ReleaseDC(pDC);
m_sizeX *= m_AAFactor;
m_sizeY *= m_AAFactor;
m_offsetX *= m_AAFactor;
m_offsetY *= m_AAFactor;
InitDC(myDC); // Stelt ook definitieve m_SizeX en m_SizeY vast
#if 1
BITMAPINFO bmInfo;
memset(&bmInfo.bmiHeader,0,sizeof(BITMAPINFOHEADER));
bmInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmInfo.bmiHeader.biWidth=m_sizeX;
bmInfo.bmiHeader.biHeight=m_sizeY;
bmInfo.bmiHeader.biPlanes=1;
bmInfo.bmiHeader.biCompression=BI_RGB; // Geen compressie
bmInfo.bmiHeader.biBitCount=24;
//create a new bitmap and select it in the memory dc
BYTE *pbase;
HBITMAP TmpBmp = CreateDIBSection(pDC, &bmInfo,DIB_RGB_COLORS,(void**)&pbase,0,0);
//----
#else
// Deze werkt ook wel maar is 10% langzamer? Je zou sneller verwachten?
// Bovendien ben je nu afhankelijk van de display instellingen?
// En het converteren CreateFromHBITMAP is veel langzamer
HBITMAP TmpBmp = CreateCompatibleBitmap ( pDC, m_sizeX, m_sizeY );
//BITMAP bm;
//GetObject(TmpBmp, sizeof(BITMAP), (LPSTR) &bm);
//als bm.bmBits nu NULL is hebben we wel een echte DFB;
#endif
if (!TmpBmp)
{
myDoTRACE("\nTmpBmp==NULL when creating (%d, %d)??", m_sizeX, m_sizeY);
return false;
}
HGDIOBJ TmpObj=SelectObject(myDC,TmpBmp);
if (m_forcePaper)
m_iWhip2DC.put_paperColor(m_paperColor);
res = res && SUCCEEDED(m_iWhip2DC.Paint(m_forceBW, m_markers));
if (!res)myDoTRACE("Paint faalt??");
if (!img->CreateFromHBITMAP(TmpBmp))
{
myDoTRACE("CreateFromHBITMAP faalt??");
return false;
}
// Voor het gehele plaatje ondersteunen we alleen rechte hoeken
// Wil je andere hoeken dan moet het via een geroteerd symbool
if (m_lRotation%360 == 90 || m_lRotation%360 == -270)
img->RotateLeft();
if (m_lRotation%360 == -90 || m_lRotation%360 == 270)
img->RotateRight();
if (m_lRotation == -180 || m_lRotation == 180)
img->Rotate180();
putDWGInfo(); // Generieke info naar het event
DeleteObject(TmpBmp);
DeleteDC(myDC);
ReleaseDC( NULL, pDC ); //Do not forget!
if (m_AAFactor != 1)
{
m_sizeX /= m_AAFactor;
m_sizeY /= m_AAFactor;
m_offsetX /= m_AAFactor;
m_offsetY /= m_AAFactor;
// Let op dat hij al geroteerd kan zijn en dan is m_sizeX != img->GetWidth()
img->Resample(img->GetWidth()/m_AAFactor, img->GetHeight()/m_AAFactor, m_AAMethod);
}
return res;
}
// Alleen gebruikt in testscripts
STDMETHODIMP CWhip2PNG::SaveAsPNG(BSTR PNGPath)
{
myDoTRACE("\nCWhip2PNG::SaveAsPNG('%ls')", PNGPath);
CxImage img;
bool res;
try {
res = CreateCxImage(&img);
}
catch (CString& err)
{
return myAtlReportError (GetObjectCLSID(), "\nCWhip2PNG::SaveAsPNG('%ls')\n%s", (LPCSTR)PNGPath, err);
}
if (!res)
{
CString err; err.Format("Could not CreateCxImage: %s", img.GetLastError());
return myAtlReportError (GetObjectCLSID(), "\nCWhip2PNG::SaveAsPNG('%ls')\n%s", (LPCSTR)PNGPath, err);
}
{
CmyTimer timer("PNG Save");
if (!img.Save(CString(PNGPath), CXIMAGE_FORMAT_PNG))
return myAtlReportError (GetObjectCLSID(), "\nSaveAsPNG save failed: %s", img.GetLastError());
}
return S_OK;
}
// Handig om GetIMG.asp errormessages te laten opleveren
// Ondersteunt ook m_Maximize: dan wordt de witruimte geminimaliseerd;
STDMETHODIMP CWhip2PNG::TextAsPNG(BSTR Message, VARIANT *ImageData)
{
// Indirect via een tempDC omdat anders de CompatibleBitmap Z/W wordt
// TODO: Als de server Windows in 256 kleuren draait, wat krijg je dan?
HDC tempDC = CreateDC("DISPLAY",NULL,NULL,NULL);
HDC myDC = CreateCompatibleDC ( tempDC );
CString tok(Message);
tok.Replace("~", "\n"); // We ondersteunen ook ~ als newline
RECT rc = { 0, 0, m_sizeX, 0};
DrawText(myDC, tok, tok.GetLength(), &rc, DT_CALCRECT); // Grootte bepalen
if (m_Maximize)
{
m_sizeX = rc.right;
m_sizeY = rc.bottom;
}
HBITMAP memBM = CreateCompatibleBitmap ( tempDC, m_sizeX, m_sizeY );
DeleteDC(tempDC);
SelectObject(myDC, memBM);
HBRUSH tempBrush = CreateSolidBrush(RGB(255,255,255));
HBRUSH oldbrush = (HBRUSH) SelectObject(myDC,tempBrush);
FillRect(myDC,CRect(0,0,m_sizeX, m_sizeY),tempBrush);
SelectObject(myDC,oldbrush);
DeleteObject(tempBrush);
// Nu het echte tekenen
rc.top = m_sizeY/2 - rc.bottom/2; // Vertikaal centreren
rc.bottom = m_sizeY/2 + rc.bottom/2;
DrawText(myDC, tok, tok.GetLength(), &rc, DT_TOP);
CxImage img;
if (!img.CreateFromHBITMAP(memBM))
return myAtlReportError (GetObjectCLSID(), "\nTextAsPNG CreateFromHBITMAP failed: %s", img.GetLastError());
DeleteObject(memBM);
DeleteDC(tempDC);
DeleteDC(myDC);
return StreamPNG(img, ImageData, TRUE);
}
// Als we dan toch door de tekening zijn gegaan is het soms
// wel handig informatie over de tekening te hebben
// Dat stoppen we grotendeels in m_SLNKEvent
void CWhip2PNG::putDWGInfo()
{
HRESULT res;
res = m_iWhip2DC.get_paperColor((LONG*)&m_paperColor); // Weten we nu ook
double resX, resY;
res = m_iWhip2DC.DPtoDWG(0, m_sizeY, &resX, &resY);
CComQIPtr<IBoundingBox> bbView;
m_SLNKEvent->get_viewExtents(&bbView);
CComQIPtr<IDWGPoint> pt;
bbView->get_min(&pt);
pt->put_DwgX(resX);
pt->put_DwgY(resY);
pt.Release(); // Voor re-use
res = m_iWhip2DC.DPtoDWG(m_sizeX, 0, &resX, &resY);
bbView->get_max(&pt);
pt->put_DwgX(resX);
pt->put_DwgY(resY);
pt.Release();
double resMX, resMY;
res = m_iWhip2DC.DWGExtents(&resX, &resY, &resMX, &resMY);
CComQIPtr<IBoundingBox> bbDWG;
m_SLNKEvent->get_dwgExtents(&bbDWG);
bbDWG->get_min(&pt);
pt->put_DwgX(resX);
pt->put_DwgY(resY);
pt.Release();
bbDWG->get_max(&pt);
pt->put_DwgX(resMX);
pt->put_DwgY(resMY);
pt.Release();
}
// Zoek een punt dat de gebruiker in de tekening geklikt heeft.
// Er wordt alleen een simpele tekst opgeleverd (hetzij aangeklikte truetype tekst
// of een Contour key(!)). Meer details zijn via get_SLNKEvent te verkrijgen
STDMETHODIMP CWhip2PNG::Find(LONG findX, LONG findY, BSTR* foundLabel)
{
CmyTimer timer("CWhip2PNG::Find");
HDC pDC = ::GetDC(0);
HDC myDC = CreateCompatibleDC(pDC);
//ReleaseDC(pDC);
// Er hoeft geen bitmap naar myDC omdat we toch niet painten
InitDC(myDC);
CString ContourKey, ContourLayer, TextLabel, TextLayer;
double EdgeAngle, EdgeDistance;
HRESULT res = m_iWhip2DC.Find(findX, findY,
FALSE,
ContourKey, ContourLayer, TextLabel, TextLayer,
EdgeAngle, EdgeDistance);
if (TextLabel != "")
(*foundLabel) = TextLabel.AllocSysString();
else if (ContourKey != "")
(*foundLabel) = ContourKey.AllocSysString();
double resX, resY;
res = m_iWhip2DC.DPtoDWG(findX, findY, &resX, &resY);
m_SLNKEvent->put_DwgX(resX);
m_SLNKEvent->put_DwgY(resY);
m_SLNKEvent->put_ContourKey(CComBSTR(ContourKey));
m_SLNKEvent->put_ContourLayer(CComBSTR(ContourLayer));
m_SLNKEvent->put_TextLabel(CComBSTR(TextLabel));
m_SLNKEvent->put_TextLayer(CComBSTR(TextLayer));
m_SLNKEvent->put_EdgeAngle(EdgeAngle);
m_SLNKEvent->put_EdgeDistance(EdgeDistance);
putDWGInfo(); // Generieke info naar het event
DeleteDC(myDC);
ReleaseDC( NULL, pDC ); //Do not forget!
return S_OK;
}
STDMETHODIMP CWhip2PNG::SetAntialias(LONG lFactor, LONG lMethod)
{
if (lFactor < 1 ||
lFactor > 4 || // Boven de 4 loop je al risico op out-of-memory
lMethod < 0 ||
lMethod > 2)
return myAtlReportError (GetObjectCLSID(), "\nCWhip2PNG::SetAntialias invalid parameters");
m_AAFactor = lFactor;
m_AAMethod = lMethod;
return S_OK;
};
STDMETHODIMP CWhip2PNG::SetDimensions(LONG sizeX, LONG sizeY, // PNG image size
LONG offsetX, LONG offsetY, // Panning
DOUBLE dScale, // Zooming, 1.0 default
LONG lRotation) // Rotation, 0 default, multiple of 90
{
if (sizeX*dScale <=0 ||
sizeY*dScale <=0 ||
sizeX*dScale >=LONG_MAX+0.5 ||
sizeY*dScale >=LONG_MAX+0.5)
return myAtlReportError (GetObjectCLSID(), "\nCWhip2PNG::SetDimensions invalid zoom");
m_sizeX = sizeX;
m_sizeY = sizeY;
m_offsetX = offsetX;
m_offsetY = offsetY;
m_dScale = dScale;
m_lRotation = lRotation;
return S_OK;
}
STDMETHODIMP CWhip2PNG::GetAsMap(BSTR* AsMap)
{
myDoTRACE("\nCWhip2PNG::GetAsMAP()");
CmyTimer timer("CWhip2PNG::GetAsMap");
HDC pDC = ::GetDC(0);
HDC myDC = CreateCompatibleDC(pDC);
// Er hoeft geen bitmap naar myDC omdat we toch niet painten
// Let wel: daardoor kunnen we niet optimaliseren met RectVisible?
InitDC(myDC);
#if 0
// Werkt nog steeds niet voor CWhip2DCImpl::PolyToMap
HRGN hrgn = CreateRectRgn(0, 0, m_sizeX, m_sizeY);
ATLASSERT(hrgn);
SelectClipRgn(myDC, hrgn);
// ::SetViewportExtEx(myDC, m_sizeX, m_sizeY, NULL);
#endif
//ReleaseDC(pDC);
CString result, dummy;
double dDummy;
HRESULT res = m_iWhip2DC.Find(0, 0,
TRUE, /*AsMap*/
result, dummy, dummy, dummy, dDummy, dDummy);
// Als we dan toch door de tekening zijn gegaan is dit wel handig om te hebben
putDWGInfo(); // Generieke info naar het event
(*AsMap) = result.AllocSysString();
DeleteDC(myDC);
ReleaseDC( NULL, pDC ); //Do not forget!
return S_OK;
}
// Testfunctie om RegExp's te testen
STDMETHODIMP CWhip2PNG::get_AtlRegExp(BSTR RegExp, BSTR TestExp, BSTR* pVal)
{
CAtlRegExp<> reLayers; // Welke layers moeten aan
REParseError status = reLayers.Parse( CString(RegExp), false );
if (REPARSE_ERROR_OK != status)
{
myTRACE("\nSorry, kan reguliere expressie layersOn niet parsen");
// Unexpected error.
return S_FALSE;
}
CAtlREMatchContext<> mcUrl;
//CAtlREMatchContext<> mcUrl2;
BOOL b1 = reLayers.Match(CString(TestExp), &mcUrl);
// b &= (mcUrl.m_uNumGroups > 0);
CString res;
res.Format("Testing: %ls for %ls: %d", RegExp, TestExp, b1);
CComBSTR bstrString(res);
return bstrString.CopyTo(pVal);
return S_OK;
}
// Stream een CImage als PNG (bPNG) of als GIF (!bPNG)
HRESULT CWhip2PNG::StreamPNG(CxImage &img, VARIANT *ImageData, BOOL bPNG)
{
SAFEARRAY *psaData; BYTE *pData;
SAFEARRAYBOUND rgsabound[1];
long size=0;
BYTE* pBuffer=0;
{
{
CmyTimer timer("CWhip2PNG::StreamPNG/de PNG encoden");
if (!img.Encode(pBuffer,size, bPNG?CXIMAGE_FORMAT_PNG:CXIMAGE_FORMAT_GIF))
return myAtlReportError (GetObjectCLSID(), "\nStreamPNG encode failed: %s", img.GetLastError());
}
// TODO: Echt netjes zou zijn om hier een IStream op te leveren?
// create safe array and copy image data
rgsabound[0].lLbound = 0; rgsabound[0].cElements = size;
psaData = SafeArrayCreate(VT_UI1, 1, rgsabound);
SafeArrayAccessData(psaData, (void **)&pData);
memcpy(pData,pBuffer,size);
SafeArrayUnaccessData(psaData);
img.FreeMemory (pBuffer);
// put data in variant
ImageData->vt = (VT_ARRAY | VT_UI1);
ImageData->parray = psaData;
}
myDoTRACE("\nCWhip2PNG::StreamPNG done, sending %d bytes", size);
return S_OK;
}
// Draggable symbolen
STDMETHODIMP CWhip2PNG::GetAsTransGIF(VARIANT *ImageData)
{
return CWhip2PNG::GetAsXXX(ImageData, FALSE);
}
STDMETHODIMP CWhip2PNG::GetAsPNG(VARIANT *ImageData)
{
return CWhip2PNG::GetAsXXX(ImageData, TRUE);
}
STDMETHODIMP CWhip2PNG::GetAsXXX(VARIANT *ImageData, BOOL bPNG)
{
try
{
myDoTRACE("\nCWhip2PNG::GetAsPNG()");
CxImage img;
BOOL res = CreateCxImage(&img);
if (!res)
{
CString err; err.Format("Could not CreateCxImage: %s", img.GetLastError());
throw err;
}
if (!bPNG)
{ // Transparante GIF.
RGBQUAD back = {GetBValue(m_paperColor), // RGBQUAD is BGR!!
GetGValue(m_paperColor),
GetRValue(m_paperColor),0};
if (m_forceGray)
{
img.GrayScale();
BYTE gray = RGB2GRAY(GetRValue(m_paperColor),
GetGValue(m_paperColor),
GetBValue(m_paperColor));
back.rgbBlue = back.rgbGreen = back.rgbRed = gray;
}
else
{
}
// GIF is 8 bit.
img.DecreaseBpp(8, FALSE);
img.SetTransIndex(img.GetNearestIndex(back));
// GIF kent geen Alpha, IE6 geen PNG transparantie
//img.AlphaCreate();
//img.AlphaSet(192);
}
return StreamPNG(img, ImageData, bPNG);
}
catch (DWFException& ex)
{
CString err;
err.Format("%ls\n%ls\n%s\n%ls(%d)",ex.type(),ex.message(),ex.function(),ex.file(),ex.line());
return myAtlReportError (GetObjectCLSID(), "\nCWhip2PNG::GetAsPNG('%s')\n%s", m_WhipPath, err);
}
catch (CString& err)
{
return myAtlReportError (GetObjectCLSID(), "\nCWhip2PNG::GetAsPNG('%s')\n%s", m_WhipPath, err);
}
}
STDMETHODIMP CWhip2PNG::SetLayers(BSTR RegExp)
{
m_RegExp = RegExp;
return S_OK;
}
STDMETHODIMP CWhip2PNG::get_SLNKEvent(ISLNKEvent** pVal)
{
return m_SLNKEvent->QueryInterface(IID_ISLNKEvent, (void**)pVal);
}
void CWhip2PNG::InitDC(HDC &myDC)
{
SetViewportOrgEx(myDC, m_offsetX, m_offsetY, NULL);
CEPlotSectionImpl *epli = NULL;
if (m_iEPlotSection) // Via stream?
m_iEPlotSection->get_EPlotSectionImpl((BYTE **)&epli);
m_iWhip2DC.Load(myDC, epli,
m_WhipPath, m_RegExp,
myRound(m_sizeX*m_dScale), myRound(m_sizeY*m_dScale),
VARIANT_TRUE /*center*/, m_Maximize, m_dwgScale, m_flags);
// Als m_dwgScale was gespecificeerd moeten we wel een goede bitmapgrootte kiezen
if (m_dwgScale > 0.0)
{
CSize newsize = m_iWhip2DC.get_Size();
m_sizeX = newsize.cx; // Veronderstel m_dScale = 1.0
m_sizeY = newsize.cy;
}
}
STDMETHODIMP CWhip2PNG::get_paperColor(LONG* pVal)
{
(*pVal) = m_paperColor;
return S_OK;
}
STDMETHODIMP CWhip2PNG::put_paperColor(LONG newVal)
{
m_paperColor = newVal;
m_forcePaper = TRUE;
return S_OK;
}
// Zet een vertaal kleur (afhankelijk van papierkleur)
STDMETHODIMP CWhip2PNG::ReplaceColor(LONG paperColor, LONG oldColor, LONG newColor)
{
m_iWhip2DC.ReplaceColor(paperColor, oldColor, newColor);
return S_OK;
}
STDMETHODIMP CWhip2PNG::get_Maximize(VARIANT_BOOL* pVal)
{
(*pVal) = m_Maximize;
return S_OK;
}
STDMETHODIMP CWhip2PNG::put_Maximize(VARIANT_BOOL newVal)
{
m_Maximize = newVal;
return S_OK;
}
STDMETHODIMP CWhip2PNG::get_dwgScale(DOUBLE* pVal)
{
(*pVal) = m_dwgScale;
return S_OK;
}
STDMETHODIMP CWhip2PNG::put_dwgScale(DOUBLE newVal)
{
if (newVal <= 0.0)
return myAtlReportError (GetObjectCLSID(), "\nInvalid dwgScale");
m_dwgScale = newVal;
return S_OK;
}
STDMETHODIMP CWhip2PNG::get_forceBW(VARIANT_BOOL* pVal)
{
(*pVal) = m_forceBW;
return S_OK;
}
STDMETHODIMP CWhip2PNG::put_forceBW(VARIANT_BOOL newVal)
{
m_forceBW = newVal;
if (m_forceBW)
{
m_paperColor = 0xFFFFFF; // Op wit alvast
m_forcePaper = TRUE;
}
return S_OK;
}
STDMETHODIMP CWhip2PNG::get_markers(LONG* pVal)
{
(*pVal) = m_markers;
return S_OK;
}
STDMETHODIMP CWhip2PNG::put_markers(LONG newVal)
{
m_markers = newVal;
return S_OK;
}
STDMETHODIMP CWhip2PNG::get_flags(LONG* pVal)
{
(*pVal) = m_flags;
return S_OK;
}
STDMETHODIMP CWhip2PNG::put_flags(LONG newVal)
{
m_flags = newVal;
return S_OK;
}
STDMETHODIMP CWhip2PNG::get_forceGray(VARIANT_BOOL* pVal)
{
(*pVal) = m_forceGray;
return S_OK;
}
STDMETHODIMP CWhip2PNG::put_forceGray(VARIANT_BOOL newVal)
{
m_forceGray = newVal;
return S_OK;
}
STDMETHODIMP CWhip2PNG::get_LayerCount(LONG* pVal)
{
return m_iWhip2DC.get_LayerCount(pVal);
}
STDMETHODIMP CWhip2PNG::get_LayerItem(ULONG i, BSTR* pVal)
{
CString result;
HRESULT ret = m_iWhip2DC.get_LayerItem(i, &result);
if (ret == S_OK)
(*pVal) = result.AllocSysString();
return ret;
}