Files
Slnkdwf/SlnkDWFCom/SLNKSymbolDefImpl.cpp
Jos Groot Lipman 0b9083fc17 SLNKDWF 2.11
svn path=/Slnkdwf/trunk/; revision=12487
2008-02-12 07:07:56 +00:00

459 lines
17 KiB
C++

#include "StdAfx.h"
#include "myEPlotSection.h"
#include "SLNKSymbolDefImpl.h"
#include "../SLNKDWFImpl/CxImage\CxImage\ximage.h"
#include "../SLNKDWFImpl/Whip2DCImpl.h"
#include "SLNKContourImpl.h"
#include <math.h>
// Bij EPlotsection (external DWF)
CSLNKSymbolDefinition::CSLNKSymbolDefinition(CComQIPtr<IEPlotSection> EPlotSection)
{
m_iEPlotSection = EPlotSection;
CEPlotSectionImpl *epli;
m_iEPlotSection->get_EPlotSectionImpl((BYTE **)&epli);
m_wtFile.set_eplotsection(epli);
m_AsBitmap = NULL;
m_BuiltIn = false;
calculateBoundary(m_BoundingContour);
//m_Origin = m_Center;
m_Origin = CSLNKContourImpl::Centroid(m_BoundingContour);
};
// Bij Contour list (builtin symbol)
CSLNKSymbolDefinition::CSLNKSymbolDefinition(int count, WT_Logical_Point const *points)
{
m_BuiltIn = true;
m_wtFile.set_eplotsection(NULL);
this->m_dwgScale = 1.0;
m_AsBitmap = NULL;
m_BoundingContour.set(count, points, false);
m_Origin = CSLNKContourImpl::Centroid(m_BoundingContour);
};
// By bitmap
CSLNKSymbolDefinition::CSLNKSymbolDefinition(CString const filepath, double const height)
{
m_wtFile.set_eplotsection(NULL);
m_AsBitmap = NULL;
m_BuiltIn = false;
CxImage img;
if (!img.Load(filepath))
{ // Rood kruisje?
img.Create(32, 32, 24);
img.Clear(255); // Wit
img.DrawLine(0,31,0,31,RGB(255,0,0));
img.DrawLine(0,31,31,0,RGB(255,0,0));
};
BYTE *m_Buffer=NULL;
long m_size;
img.Encode(m_Buffer, m_size, CXIMAGE_FORMAT_PNG);
this->m_dwgScale = (double)img.GetHeight()/ height;
//myDoTRACE("\nCreates symbol bitmap (%d,%d)->PNG %d byte (W2D was %d)", pixeldx,pixeldy,m_size, epli->size());
m_AsBitmap = new
WT_PNG_Group4_Image(
(int)img.GetHeight(), /*rows*/
(int)img.GetWidth(), /*cols*/
WT_PNG_Group4_Image::PNG,
3,
NULL,
m_size,
(WT_Byte*)m_Buffer,
WT_Logical_Point(0,0),
WT_Logical_Point(img.GetWidth(),img.GetHeight()),
WD_False); // Geen copy, we verwijderen zelf expliciet
WT_Logical_Point pts4[4] = { WT_Logical_Point(0, 0),
WT_Logical_Point(img.GetWidth(), 0),
WT_Logical_Point(img.GetWidth(), img.GetHeight()),
WT_Logical_Point(0, img.GetHeight())};
m_BoundingContour.set(4, pts4, true);
m_Origin = CSLNKContourImpl::Centroid(m_BoundingContour);
};
CSLNKSymbolDefinition::~CSLNKSymbolDefinition(void)
{
if (m_AsBitmap)
{
CxImage xx;xx.FreeMemory((void *)m_AsBitmap->data()); // We hebben een copy gedaan dus dit mag/moet
delete m_AsBitmap;
}
}
// Bepaal een bounding contour van ons symbool. Die is eventueel later leuk te gebruiken voor een HitTest
// v0: bounding box
// v1: bounding octagon
// Algoritm: search google for Bounding Octagon
// http://geometryalgorithms.com/Archive/algorithm_0107/algorithm_0107.htm
// (v2: bounding convex hull waarschijnlijk overkill)
#include <assert.h>
WT_Result CSLNKSymbolDefinition::calculateBoundary (WT_Polygon &BoundingContour, WT_Transform *tm/*=null*/)
{
WT_Result result;
if (m_BuiltIn || m_AsBitmap != NULL)
{
return WT_Result::Success;
}
m_wtFile.set_file_mode(WT_File::File_Read);
result = m_wtFile.open();
if (result != WT_Result::Success)
return result;
if (tm != NULL)
{
m_wtFile.heuristics().set_transform(*tm);
m_wtFile.heuristics().set_apply_transform(true);
}
// Door de enorm grote dwf-coordinaten lopen we een integer overflow risico tijdens
// onze bepaling van Pmin en Pmax (die min en max van x+y bepalen) en in theorie
// ook van Qmin en Qmax (die over het verschil x-y gaan) hoewel dat in de praktijk rond 0 zal liggen
// Daarom rekenen we systematisch met de helft. Dat moet risico's voorkomen en de afrondfout nemen
// we voor lief
// Merk op dat de resulterende Octagon kleiner is dan de de bounding box, die zeker binnen de coordinaat
// ruimte valt dus voor het eindresultaat is er geen risico. Alleen oppassen voor tussenresultaten dus
// Ging concreet mis met 41610ev_symbols.dwf
WT_Integer32 Xmin, Xmax, Ymin, Ymax;
WT_Integer32 Pmin2, Pmax2, Qmin2, Qmax2;
// Signal invalid
Xmin = Ymin = Pmin2 = Qmin2 = INT_MAX;
Xmax = Ymax = Pmax2 = Qmax2 = INT_MIN;
BOOL ViewportDone = FALSE; // We doen alleen de eerste viewport.
// Gaf problemen bij symbolen die vanuit paperspace waren geexporteerd
// Do the actual reading.
while ((result = m_wtFile.process_next_object()) == WT_Result::Success)
{
//myTRACE("Attribute '%s'\n", wtFile->file_stats()->descriptions());
if (!m_wtFile.rendition().visibility().visible())
continue;
switch(m_wtFile.current_object()->object_type())
{
case WT_Object::Attribute:
{
const WT_Attribute *obj = (WT_Attribute *)m_wtFile.current_object();
if (obj->object_id() == WT_Object::Viewport_ID)
{
if (ViewportDone)
{
myTRACE("\nSkipping second viewport in symbol");
break;
}
ViewportDone = TRUE;
WT_Viewport *viewport = (WT_Viewport *)m_wtFile.current_object();
WT_Units symbolunits = viewport->viewport_units();
m_dwgScale = symbolunits.application_to_dwf_transform()(0,0); // Symbol's dScale
break;
}
break;
}
case WT_Object::Drawable:
{
WT_Drawable *obj = (WT_Drawable *)m_wtFile.current_object();
if (obj->object_id() != WT_Object::Origin_ID) // Belachelijk dat die Drawable is?
{
WT_Logical_Box bx = obj->bounds(&m_wtFile);
Xmin = min(Xmin, bx.minpt().m_x);
Ymin = min(Ymin, bx.minpt().m_y);
Xmax = max(Xmax, bx.maxpt().m_x);
Ymax = max(Ymax, bx.maxpt().m_y);
// Voor echte (deel)cirkels gaan we de diagonaal bounding box scherper berekenen
if (obj->object_id() == WT_Object::Outline_Ellipse_ID || obj->object_id() == WT_Object::Filled_Ellipse_ID)
{//continue;
WT_Ellipse *elp = (WT_Ellipse *)obj;
if (elp->minor() == elp->major()) // Anders te ingewikkeld
{ // Bepaal het ingeschreven vierkant van de cirkel
WT_Integer32 dr = myRound(elp->minor() / sqrt(2.0) + 0.5); // Altijd omhoog afronden
WT_Logical_Box bx2;
bx2.minpt() = WT_Logical_Point(elp->position().m_x - dr, elp->position().m_y - dr);
bx2.maxpt() = WT_Logical_Point(elp->position().m_x + dr, elp->position().m_y + dr);
// Houdt er wel rekening mee dat bij partiele cirkels het origineel scherper kan
// zijn omdat wij er geen rekening houden dat de cirkel misschien partieel is
Pmin2 = min(Pmin2, max(bx.minpt().m_x/2 + bx.minpt().m_y/2,bx2.minpt().m_x/2 + bx2.minpt().m_y/2)); // Linksonder
Qmax2 = max(Qmax2, min(bx.maxpt().m_x/2 - bx.minpt().m_y/2,bx2.maxpt().m_x/2 - bx2.minpt().m_y/2)); // Rechtsonder
Pmax2 = max(Pmax2, min(bx.maxpt().m_x/2 + bx.maxpt().m_y/2,bx2.maxpt().m_x/2 + bx2.maxpt().m_y/2)); // Rechtsboven
Qmin2 = min(Qmin2, max(bx.minpt().m_x/2 - bx.maxpt().m_y/2,bx2.minpt().m_x/2 - bx2.maxpt().m_y/2)); // Linksboven
break;
}
// else gewoon 'doorvallen' naar standaard Pmin2 etc.
}
// Voor WT_Pointset gebaseerde objecten kunnen we het ook wel beter
if (obj->object_id() == WT_Object::Polygon_ID ||
obj->object_id() == WT_Object::Polyline_ID ||
obj->object_id() == WT_Object::Polytriangle_ID)
{
WT_Point_Set *pts;
// Rechtstreekse cast naar (WT_Point_Set *) gaf memory violations?
switch (obj->object_id())
{
case WT_Object::Polygon_ID : pts = (WT_Polygon *)m_wtFile.current_object(); break;
case WT_Object::Polyline_ID : pts = (WT_Polyline *)m_wtFile.current_object(); break;
case WT_Object::Polytriangle_ID: pts = (WT_Polytriangle *)m_wtFile.current_object(); break;
}
for (int i=0;i<pts->count(); i++)
{
WT_Logical_Point &pt = pts->points()[i];
Pmin2 = min(Pmin2, pt.m_x/2 + pt.m_y/2); // Linksonder
Qmax2 = max(Qmax2, pt.m_x/2 - pt.m_y/2); // Rechtsonder
Pmax2 = max(Pmax2, pt.m_x/2 + pt.m_y/2); // Rechtsboven
Qmin2 = min(Qmin2, pt.m_x/2 - pt.m_y/2); // Linksboven
}
break; // we zijn wel klaar
}
Pmin2 = min(Pmin2, bx.minpt().m_x/2 + bx.minpt().m_y/2); // Linksonder
Qmax2 = max(Qmax2, bx.maxpt().m_x/2 - bx.minpt().m_y/2); // Rechtsonder
Pmax2 = max(Pmax2, bx.maxpt().m_x/2 + bx.maxpt().m_y/2); // Rechtsboven
Qmin2 = min(Qmin2, bx.minpt().m_x/2 - bx.maxpt().m_y/2); // Linksboven
}
}
break;
default:
//myTRACE("Skipping '%s'\n", wtFile->file_stats()->descriptions());
break;
}
}
// if (result == WT_Result::End_Of_DWF_Opcode_Found)
// dwfresult = DwfResult::Success;
m_wtFile.heuristics().set_apply_transform(false);
m_wtFile.close();
// Rechthoek
// WT_Logical_Point pts1[4] = { WT_Logical_Point(Xmin, Ymin),
// WT_Logical_Point(Xmax, Ymin),
// WT_Logical_Point(Xmax, Ymax),
// WT_Logical_Point(Xmin, Ymax)};
//m_BoundingBox = WT_Logical_Box(Xmin, Ymin, Xmax, Ymax);
// Diamant heeft overflow risico
// WT_Logical_Point pts2[4] = { WT_Logical_Point(Pmin2+Qmin2, Pmin2-Qmin2),
// WT_Logical_Point(Pmin2+Qmax2, Pmin2-Qmax2),
// WT_Logical_Point(Pmax2+Qmax2, Pmax2-Qmax2),
// WT_Logical_Point(Pmax2+Qmin2, Pmax2-Qmin2)};
// Octagon
// Expressievolgorde is telkens belangrijk om overflow te voorkomen
WT_Logical_Point pts3[8] = { WT_Logical_Point(Pmin2-Ymin+Pmin2, Ymin),
WT_Logical_Point(Qmax2+Ymin+Qmax2, Ymin),
WT_Logical_Point(Xmax, Xmax-Qmax2-Qmax2),
WT_Logical_Point(Xmax, Pmax2-Xmax+Pmax2),
WT_Logical_Point(Pmax2-Ymax+Pmax2, Ymax),
WT_Logical_Point(Qmin2+Ymax+Qmin2, Ymax),
WT_Logical_Point(Xmin, Xmin-Qmin2-Qmin2),
WT_Logical_Point(Xmin, Pmin2-Xmin+Pmin2)};
m_Center = WT_Logical_Point(Xmin/2+Xmax/2,Ymin/2+Ymax/2);
BoundingContour.set(sizeof(pts3)/sizeof(pts3[0]), pts3, true);
return result;
}
#ifdef NO_BITMAPPEREN
WT_Drawable *CSLNKSymbolDefinition::asBitmap(int pixeldx, int pixeldy, long paperColor)
{
if (m_AsBitmap)
{
// TODO: Wat als pixeldy != m_AsBitmap->rows()
// Dat dwfdx anders is is minder boeiend, dat verschaalt wel
WT_PNG_Group4_Image *newBitmap = new
WT_PNG_Group4_Image(
m_AsBitmap->rows(), /*rows*/
m_AsBitmap->columns(), /*cols*/
WT_PNG_Group4_Image::PNG,
3,
NULL,
m_AsBitmap->data_size(),
(WT_Byte *)m_AsBitmap->data(),
m_BoundingBox.minpt(),
m_BoundingBox.maxpt(),
WD_False); // Don't copy
m_AsBitmap = newBitmap;
return m_AsBitmap; // Dan zijn we rap klaar. Dat is onze winst
}
// Blijkbaar de eerste keer hier. Tijd om de bitmap te bakken
CWhip2DCImpl iWhip2DC;
HDC pDC = ::GetDC(0);
HDC myDC = CreateCompatibleDC(pDC);
//ReleaseDC(pDC);
CEPlotSectionImpl *epli = NULL;
m_iEPlotSection->get_EPlotSectionImpl((BYTE **)&epli);
iWhip2DC.Load(myDC, epli,
"", ".*",
myRound(pixeldx), myRound(pixeldy),
VARIANT_TRUE /*center*/, VARIANT_TRUE /*m_Maximize*/);
BITMAPINFO bmInfo;
memset(&bmInfo.bmiHeader,0,sizeof(BITMAPINFOHEADER));
bmInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmInfo.bmiHeader.biWidth=pixeldx;
bmInfo.bmiHeader.biHeight=pixeldy;
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);
HGDIOBJ TmpObj=SelectObject(myDC,TmpBmp);
iWhip2DC.put_paperColor(paperColor); // Op zich maken we hem transparant maar onze zwart/wit toggle moet wel kloppen
SUCCEEDED(iWhip2DC.Paint(VARIANT_FALSE /*m_forceBW*/));
CxImage img;
img.CreateFromHBITMAP(TmpBmp);
img.DecreaseBpp(8, FALSE); // Werkt averechts voor kleine plaatjes (pallette is groot?)
// Maar CxImage::Draw2 lijkt alleen te werken met palette achtergrond dus het moet maar
RGBQUAD back = {GetBValue(paperColor), // RGBQUAD is BGR!!
GetGValue(paperColor),
GetRValue(paperColor),0};
///RGBQUAD back = {0,0,0,0};
img.SetTransIndex(img.GetNearestIndex(back));
// img.SetTransColor(back); // Voor als we zonder pallette werken
BYTE *m_Buffer=NULL;
long m_size;
img.Encode(m_Buffer, m_size, CXIMAGE_FORMAT_PNG);
myDoTRACE("\nCreates symbol bitmap (%d,%d)->PNG %d byte (W2D was %d)", pixeldx,pixeldy,m_size, epli->size());
DeleteObject(TmpBmp);
DeleteDC(myDC);
ReleaseDC( NULL, pDC ); //Do not forget!
m_AsBitmap = new
WT_PNG_Group4_Image(
pixeldy, /*rows*/
pixeldy, /*cols*/
WT_PNG_Group4_Image::PNG,
3,
NULL,
m_size,
(WT_Byte*)m_Buffer,
m_BoundingBox.minpt(),
m_BoundingBox.maxpt(),
WD_False); // Geen copy, we verwijderen zelf expliciet
return m_AsBitmap;
}
#endif
WT_Result CSLNKSymbolDefinition::serialize(WT_File & file, WT_Color pColor, BOOL pColorSet)
{
if (m_AsBitmap)
{ // Wel even kopietje trekken omdat anders niet opnieuw getransformeerd wordt
WT_PNG_Group4_Image(
m_AsBitmap->rows(), /*rows*/
m_AsBitmap->columns(), /*cols*/
WT_PNG_Group4_Image::PNG,
3,
NULL,
m_AsBitmap->data_size(),
(WT_Byte *)m_AsBitmap->data(),
m_AsBitmap->bounds().minpt(),
m_AsBitmap->bounds().maxpt(),
WD_False).serialize(file);
return WT_Result::Success;
}
if (m_BuiltIn) // Die hebben alleen een bounding contour
return WT_Result::Success;
// Vertrouw de huidige rendition niet! Het eerste lijntje van een symbool kan default wit verwachten (CO206.DWF)
// Tijdens het serializen van het symbool hebben we niets met (desired)_rendition te maken
if (pColorSet)
pColor.serialize(file);
else
WT_Color(0,0,0).serialize(file); // Op de default zetten. TODO: Moet eigenlijk voor alle attributen
WT_Result result;
// Open onze symbool DWF
m_wtFile.set_file_mode(WT_File::File_Read);
result = m_wtFile.open();
if (result != WT_Result::Success)
return result; // Waarschijnlijk builtin symbol?
// Op zich is er minder kans op overflow als we het symbool tijden het inlezen
// al richting (0,0) zetten. Dan wordt echter niet meer de definitieve transform
// uitgevoerd omdat m_transformed op WD_True staat.
// WT_Logical_Point lshift(- m_Origin.m_x,- m_Origin.m_y);
//
// m_wtFile.heuristics().set_transform(WT_Transform (lshift, 0, 0, 0));
// m_wtFile.heuristics().set_apply_transform(true);
// Do the actual reading.
// Een apply_transform is door de aanroeper al op file gezet.
// Dankzij set_apply_transform gaat de rest miraculeus vanzelf
while ((result = m_wtFile.process_next_object()) == WT_Result::Success)
{
//myTRACE("Attribute '%s'\n", wtFile->file_stats()->descriptions());
switch(m_wtFile.current_object()->object_type())
{
case WT_Object::Drawable:
#ifdef _DEBUG
if (m_wtFile.current_object()->object_id() == WT_Object::Origin_ID
&& !file.heuristics().allow_binary_data()
)
{ // Origin is niet toegestaan bij ASCII
break;
}
#endif
m_wtFile.current_object()->serialize(file);
break;
case WT_Object::Attribute:
{
const WT_Attribute *obj = (WT_Attribute *)m_wtFile.current_object();
// myTRACE("Attribute '%s'\n", my_plan_file.file_stats()->descriptions());
// Hoewel een Attibute gaat hij toch niet via desired_rendition
// Omdat we weten dat we net uit uit een DWF komen kunnen we best
// wel rechtstreeks serializen
// Let wel: desired_rendition is dan niet meer te vertrouwen. Zie @@@
if (obj->object_id() == WT_Object::View_ID)
{
// De view komt VOOR de Viewport dus wordt niet goed vertaald
// bovendien doen we er toch niets mee
break;
}
if (obj->object_id() == WT_Object::Layer_ID)
{
// Zou toch wel erg complex worden. Werkt ook maar matig
break;
}
if (obj->object_id() == WT_Object::Color_ID)
{ // Misschien een zwart-wit toggle doorvoeren? Of zelfs highlight?
// m_iEPlotSection->get_PaperColor()
if (pColorSet) // Hoeven we niet steeds weer te zetten
{
break;
}
//else gewoon naar de default doorvallen
}
if (obj->object_id() == WT_Object::Viewport_ID)
{
break; // Voorkom dat het Viewport object in de DWF komt
// Dan krijgen we last omdat we er op clippen
// eigenlijk moeten we de originele viewport
// terug zetten na afloop
}
obj->serialize(file);
}
break;
default:
//myTRACE("Skipping '%s'\n", wtFile->file_stats()->descriptions());
break;
}
}
m_wtFile.close();
return WT_Result::Success;
}