#include "StdAfx.h" #include "myEPlotSection.h" #include "SLNKSymbolDefImpl.h" #include "../SLNKDWFImpl/CxImage\CxImage\ximage.h" #include "../SLNKDWFImpl/Whip2DCImpl.h" #include "SLNKContourImpl.h" #include // Bij EPlotsection (external DWF) CSLNKSymbolDefinition::CSLNKSymbolDefinition(CComQIPtr EPlotSection) { m_iEPlotSection = EPlotSection; CEPlotSectionImpl *epli; m_iEPlotSection->get_EPlotSectionImpl((BYTE **)&epli); m_wtFile.set_eplotsection(epli); m_AsBitmap = NULL; m_BuiltIn = false; calculateBoundary(); }; // 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 WT_Result CSLNKSymbolDefinition::calculateBoundary () { WT_Result result; m_wtFile.set_file_mode(WT_File::File_Read); result = m_wtFile.open(); if (result != WT_Result::Success) return result; // 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()); 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) { 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;icount(); 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.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_BoundingContour.set(sizeof(pts3)/sizeof(pts3[0]), pts3, true); m_Origin = CSLNKContourImpl::Centroid(m_BoundingContour); 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) { 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) return WT_Result::Success; 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 (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; }