[Date Prev][Date Next][Thread Prev][Thread Next][Thread Index]

[XaraXtreme-commits] Commit Complete



Commit by  : gerry
Repository : xara
Revision   : 1708
Date       : Thu Aug 17 18:13:43 BST 2006

Changed paths:
   M /Trunk/XaraLX/Kernel/bitmap.cpp
   M /Trunk/XaraLX/Kernel/bitmap.h
   M /Trunk/XaraLX/Kernel/bmpcomp.cpp
   M /Trunk/XaraLX/Kernel/camfiltr.cpp
   M /Trunk/XaraLX/Kernel/cxfile.cpp
   M /Trunk/XaraLX/Kernel/exjpeg.cpp
   M /Trunk/XaraLX/Kernel/fillattr.cpp
   M /Trunk/XaraLX/Kernel/fillattr2.h
   M /Trunk/XaraLX/Kernel/imjpeg.cpp
   M /Trunk/XaraLX/Kernel/node.h
   M /Trunk/XaraLX/Kernel/nodebmp.cpp
   M /Trunk/XaraLX/Kernel/nodebmp.h
   M /Trunk/XaraLX/Kernel/xpfcaps.h
   M /Trunk/XaraLX/Kernel/xpfilter.cpp
   M /Trunk/XaraLX/Kernel/xpfilter.h
   M /Trunk/XaraLX/Kernel/xpfrgn.cpp
   M /Trunk/XaraLX/wxOil/xpoilflt.cpp

Implementation of compression and resample conversion options


Diff:
Index: Trunk/XaraLX/Kernel/nodebmp.h
===================================================================
--- Trunk/XaraLX/Kernel/nodebmp.h	(revision 1707)
+++ Trunk/XaraLX/Kernel/nodebmp.h	(revision 1708)
@@ -142,6 +142,8 @@
 	virtual String Describe(BOOL Plural, BOOL Verbose);
 
 	virtual KernelBitmap *EnumerateBitmaps(UINT32 Count);
+	virtual double GetEffectiveBitmapMinDPI(KernelBitmap* pBitmap);
+	virtual BOOL ReplaceBitmap(KernelBitmap* pOrigBitmap, KernelBitmap* pNewBitmap);
 
 	// Function for interrogation by clipboard to determine exportable data types
 	virtual BOOL SupportsClipboardFormat(InternalClipboardFormat *Format) const;
Index: Trunk/XaraLX/Kernel/fillattr2.h
===================================================================
--- Trunk/XaraLX/Kernel/fillattr2.h	(revision 1707)
+++ Trunk/XaraLX/Kernel/fillattr2.h	(revision 1708)
@@ -1845,6 +1845,8 @@
 	virtual BOOL IsAGradFill() const { return TRUE; } 
 
 	virtual KernelBitmap *EnumerateBitmaps(UINT32 Count);
+	virtual double GetEffectiveBitmapMinDPI(KernelBitmap* pBitmap);
+	virtual BOOL ReplaceBitmap(KernelBitmap* pOrigBitmap, KernelBitmap* pNewBitmap);
 
 	virtual BOOL NeedsForceToSimpleMapping() { return(FALSE); }
 	
Index: Trunk/XaraLX/Kernel/xpfilter.h
===================================================================
--- Trunk/XaraLX/Kernel/xpfilter.h	(revision 1707)
+++ Trunk/XaraLX/Kernel/xpfilter.h	(revision 1708)
@@ -202,6 +202,7 @@
 
 	BOOL GenerateExportData(CapabilityTree* pPlugCaps);
 	BOOL DoConversionPassN(CapabilityTree* pPlugCaps, INT32 ConvertPass);
+	BOOL DoBitmapResamplePass(CapabilityTree* pPlugCaps);
 	virtual Node* GetExportNode();
 	virtual BOOL BeginDocumentExport();
 	virtual BOOL EndDocumentExport();
Index: Trunk/XaraLX/Kernel/xpfcaps.h
===================================================================
--- Trunk/XaraLX/Kernel/xpfcaps.h	(revision 1707)
+++ Trunk/XaraLX/Kernel/xpfcaps.h	(revision 1708)
@@ -1033,6 +1033,7 @@
 		m_RasteriseDPI = 96.0;
 		m_bRasteriseAlpha = TRUE;
 		m_BitmapCompression = 200;
+		m_bResample = FALSE;
 		m_SpreadType = XPFCONVTYPE_NATIVE;
 		m_pObjects = NULL;
 		m_ObjectsType = XPFCONVTYPE_NATIVE;
@@ -1082,12 +1083,13 @@
 	
 	void SetSpreadType(XPFConvertType Type) { m_SpreadType = Type; }
 
-	void SetRasterise(double DPI, BOOL bAlpha, INT32 Compression, const String_256& CommonTrans)
+	void SetRasterise(double DPI, BOOL bAlpha, INT32 Compression, const String_256& CommonTrans, BOOL bResample)
 	{
 		m_RasteriseDPI = DPI;
 		m_bRasteriseAlpha = bAlpha;
 		m_BitmapCompression = Compression;
 		m_CommonTrans = CommonTrans;
+		m_bResample = bResample;
 	}
 
 	void SetObjectsTree(XPFCapability* pObjects, XPFConvertType ObjectsType)
@@ -1121,6 +1123,7 @@
 	BOOL HasRasteriseCommonTrans() { return(!m_CommonTrans.IsEmpty()); }
 	// Move this into cpp file when implemented
 	BOOL IsRasteriseCommonTrans(UINT32 Type);
+	BOOL GetBitmapResample() { return(m_bResample); }
 
 	XPFCapability* GetObjects(void) const { return(m_pObjects); }
 	XPFCapability* GetAttributes(void) const { return(m_pAttributes); }
@@ -1140,6 +1143,7 @@
 	BOOL m_bRasteriseAlpha;
 	INT32 m_BitmapCompression;
 	String_256 m_CommonTrans;
+	BOOL m_bResample;
 
 	XPFConvertType m_SpreadType;
 	
Index: Trunk/XaraLX/Kernel/cxfile.cpp
===================================================================
--- Trunk/XaraLX/Kernel/cxfile.cpp	(revision 1707)
+++ Trunk/XaraLX/Kernel/cxfile.cpp	(revision 1708)
@@ -2014,7 +2014,7 @@
 
 	if (ReadNextRecordHeader())
 	{
-		TRACEUSER("Gerry", _T("HandleRecord %d - %d [%d]"), RecordNumber, ReadTag, ReadSize);
+//		TRACEUSER("Gerry", _T("HandleRecord %d - %d [%d]"), RecordNumber, ReadTag, ReadSize);
 
 		CXaraFileRecordHandler* pCXaraFileRecordHandler = FindHandler(ReadTag);
 		if (pCXaraFileRecordHandler != NULL)
Index: Trunk/XaraLX/Kernel/fillattr.cpp
===================================================================
--- Trunk/XaraLX/Kernel/fillattr.cpp	(revision 1707)
+++ Trunk/XaraLX/Kernel/fillattr.cpp	(revision 1708)
@@ -5607,6 +5607,98 @@
 	return NULL;
 }
 
+
+/****************************************************************************
+
+>	BOOL AttrBitmapFill::ReplaceBitmap(KernelBitmap* pOrigBitmap, KernelBitmap* pNewBitmap)
+
+	Author:		Gerry
+	Created:	07/08/2006
+
+	Inputs:		pOrigBitmap	- pointer to a KernelBitmap
+				pNewBitmap	- pointer to a KernelBitmap
+	Returns:	TRUE if ok, FALSE if bother
+	Purpose:	
+
+****************************************************************************/
+
+BOOL AttrBitmapFill::ReplaceBitmap(KernelBitmap* pOrigBitmap, KernelBitmap* pNewBitmap)
+{
+	if (!IsAFractalFill())
+	{
+		if (GetBitmap() == pOrigBitmap)
+		{
+			BitmapFillAttribute* pVal = (BitmapFillAttribute*)GetAttributeValue();
+			if (pVal)
+				pVal->GetBitmapRef()->Attach(pNewBitmap);
+
+			return(TRUE);
+		}
+	}
+
+	return FALSE;
+}
+
+
+/****************************************************************************
+
+>	double AttrBitmapFill::GetEffectiveBitmapMinDPI(KernelBitmap* pBitmap)
+
+	Author:		Gerry
+	Created:	07/08/2006
+
+	Inputs:		pBitmap		- pointer to a KernelBitmap
+	Returns:	
+	Purpose:	Returns the minimum effective dpi this bitmap is used at
+
+****************************************************************************/
+
+double AttrBitmapFill::GetEffectiveBitmapMinDPI(KernelBitmap* pBitmap)
+{
+	if (!IsAFractalFill() && GetBitmap() == pBitmap)
+	{
+		// Do we have a valid bitmap ?
+		OILBitmap *OilBM = pBitmap->ActualBitmap;
+		if (OilBM != NULL)
+		{
+			BitmapInfo Info;
+			OilBM->GetInfo(&Info);
+
+			// Get the Width of the Bitmap in Pixels
+			INT32 PixWidth  = Info.PixelWidth;
+			INT32 PixHeight = Info.PixelHeight;
+
+			DocCoord Start(*GetStartPoint());
+			DocCoord End(*GetEndPoint());
+			DocCoord End2(*GetEndPoint2());
+
+			// Get the Width of the Bitmap in Millipoints
+			INT32 Width  = INT32(Start.Distance(End));
+			INT32 Height = INT32(Start.Distance(End2));
+
+			// Use doubles so that we can round up as well as down. This improves
+			// the dpi calculated.
+			double HDpi = 0;
+			double VDpi = 0;
+
+			if (Width > 0)
+				HDpi = ((double)PixWidth * 72000.0)/(double)Width;
+
+			if (Height > 0)
+				VDpi = ((double)PixHeight * 72000.0)/(double)Height;
+
+			// Use the smaller of the two dpi values
+			if (HDpi < VDpi)
+				return(HDpi);
+			else
+				return(VDpi);
+		}
+	}
+
+	return(1e9);
+}
+
+
 /********************************************************************************************
 
 >	virtual NodeAttribute* AttrBitmapFill::GetOtherAttrToApply(BOOL* IsMutate)
Index: Trunk/XaraLX/Kernel/xpfilter.cpp
===================================================================
--- Trunk/XaraLX/Kernel/xpfilter.cpp	(revision 1707)
+++ Trunk/XaraLX/Kernel/xpfilter.cpp	(revision 1708)
@@ -116,6 +116,8 @@
 #include "progress.h"
 
 #include "camprocess.h"
+#include "bmpcomp.h"
+#include "grndbmp.h"
 
 // An implement to match the Declare in the .h file.
 CC_IMPLEMENT_DYNAMIC(PluginNativeFilter, CamelotNativeFilter);
@@ -492,7 +494,7 @@
 
 	String_64 Str(_R(IDS_CONVERTING_DOCUMENT));
 	StartProgressBar(&Str);
-	SetTotalProgressBarCount(500);
+	SetTotalProgressBarCount(600);
 	m_ProgressOffset = 0;
 
 //	UINT32 ThisPassCount = 0;
@@ -628,6 +630,14 @@
 		ok = DoConversionPassN(pPlugCaps, 5);
 	}
 
+	if (ok)
+	{
+		// Now handle the bitmap resampling pass
+		m_ProgressOffset = 500;
+		SetProgressBarCount(0);
+		ok = DoBitmapResamplePass(pPlugCaps);
+	}
+
 	SetProgressBarCount(100);
 
 	EndProgressBar();					// Kill progess bar
@@ -707,6 +717,211 @@
 
 /****************************************************************************
 
+>	BOOL PluginNativeFilter::DoBitmapResamplePass(CapabilityTree* pPlugCaps)
+
+	Author:		Gerry
+	Created:	04/08/2006
+
+	Inputs:		pPlugCaps	- pointer to a CapabilityTree
+	Returns:	TRUE if ok, FALSE if bother
+	Purpose:
+
+****************************************************************************/
+
+BOOL PluginNativeFilter::DoBitmapResamplePass(CapabilityTree* pPlugCaps)
+{
+	if (!pPlugCaps->GetBitmapResample())
+		return(TRUE);
+
+	TRACE(_T("DoBitmapResamplePass"));
+
+	Document* pDoc = Document::GetCurrent();
+	ERROR2IF(pDoc == NULL, FALSE, "No current document in DoBitmapResamplePass");
+
+	BitmapList* pBmpList = pDoc->GetBitmapList();
+	ERROR2IF(pDoc == NULL, FALSE, "No bitmap list in DoBitmapResamplePass");
+
+	// For each bitmap in the document's bitmap list
+	KernelBitmap* pBitmap = (KernelBitmap*)(pBmpList->GetHead());
+
+	while (pBitmap)
+	{
+		double MinDPI = 1e9;
+
+		List NodeList;
+
+		// Scan through new document tree finding all uses of the bitmap
+		Node* pNode = m_pNewTree->FindFirstDepthFirst();
+		while (pNode)
+		{
+			UINT32 Count = 0;
+			BOOL bAdd = FALSE;
+			KernelBitmap* pTestBitmap = pNode->EnumerateBitmaps(Count);
+			while (pTestBitmap)
+			{
+				if (pTestBitmap == pBitmap)
+				{
+					bAdd = TRUE;		// Add this node to the list
+					double DPI = pNode->GetEffectiveBitmapMinDPI(pTestBitmap);
+					if (DPI < MinDPI)
+						MinDPI = DPI;
+				}
+
+				// Get the next bitmap from the node
+				Count++;
+				pTestBitmap = pNode->EnumerateBitmaps(Count);
+			}
+
+			if (bAdd)
+			{
+				NodeListItem* pItem = new NodeListItem(pNode);
+				if (pItem)
+					NodeList.AddTail(pItem);
+			}
+
+			// Get the next node
+			pNode = pNode->FindNextDepthFirst(m_pNewTree);
+		}
+
+		// If a minimum has been found for this bitmap
+		if (MinDPI < 1e9)
+		{
+			TRACE(_T("MinDPI = %f"), MinDPI);
+
+			double ratio = pPlugCaps->GetRasteriseDPI() / MinDPI;
+			// If the minimum dpi is above the resample dpi then
+			if (ratio < 0.9)
+			{
+				TRACE(_T("Resample bitmap"));
+				// Generate new bitmap with size so that minimum dpi is resample dpi
+
+				TRACE(_T("Actual size = (%d, %d)"), pBitmap->GetWidth(), pBitmap->GetHeight());
+
+				INT32 OutWidth = (INT32)(((double)pBitmap->GetWidth() * ratio) + 0.5);
+				INT32 OutHeight = (INT32)(((double)pBitmap->GetHeight() * ratio) + 0.5);
+				TRACE(_T("Needed size = (%d, %d)"), OutWidth, OutHeight);
+
+				KernelBitmap* pNewBitmap = NULL;
+				{
+					View* pView = View::GetCurrent();
+					Spread* pSpread = NULL;
+					BOOL bAlpha = pBitmap->IsTransparent();
+					Matrix ViewTrans;
+					FIXED16 TempScale(1.0);
+					double Dpi = 96.0;
+					DocRect BoundsRect(0, 0, OutWidth * 750, OutHeight * 750);
+
+					GRenderBitmap BitmapRR(BoundsRect, ViewTrans, TempScale, 32, Dpi);
+					if (bAlpha)
+						BitmapRR.m_DoCompression = TRUE;
+					BitmapRR.SetUsingSmoothedBitmaps(TRUE);		// Make sure we do high quality
+					BitmapRR.AttachDevice(pView, NULL, pSpread);
+
+					// Start rendering into the bitmap
+					if (!BitmapRR.StartRender())
+					{
+						ERROR2(FALSE, "StartRender failed in DoBitmapResamplePass");
+					}
+
+					BitmapRR.SaveContext();
+
+					// Best quality please
+					QualityAttribute *pQualAttr = new QualityAttribute();
+					pQualAttr->QualityValue.SetQuality(QUALITY_MAX);
+					BitmapRR.SetQuality(pQualAttr, TRUE);
+
+					// Simple bitmap fill which fills the whole shape
+					BitmapFillAttribute* pBitmapAttr = new BitmapFillAttribute;
+					pBitmapAttr->GetBitmapRef()->SetBitmap(pBitmap);
+					pBitmapAttr->StartPoint = BoundsRect.lo;
+					pBitmapAttr->EndPoint   = DocCoord(BoundsRect.hi.x, BoundsRect.lo.y);
+					pBitmapAttr->EndPoint2 	= DocCoord(BoundsRect.lo.x, BoundsRect.hi.y);
+
+					// Set bitmap attribute, and get the render region to throw it away when it's finished
+					// with (hence the TRUE parameter).
+					BitmapRR.SetFillGeometry(pBitmapAttr, TRUE);
+
+					BitmapRR.SetLineColour(COLOUR_NONE);
+
+					Path RectPath;
+					if (RectPath.Initialise())
+					{
+						// Start at bottom left corner
+						RectPath.InsertMoveTo(BoundsRect.lo);
+						RectPath.InsertLineTo(DocCoord(BoundsRect.hi.x, BoundsRect.lo.y));
+						RectPath.InsertLineTo(BoundsRect.hi);
+						RectPath.InsertLineTo(DocCoord(BoundsRect.lo.x, BoundsRect.hi.y));
+						RectPath.InsertLineTo(BoundsRect.lo);
+
+						// Close the path properly
+						RectPath.CloseSubPath();
+						RectPath.IsFilled = TRUE;
+						RectPath.IsStroked = FALSE;
+						BitmapRR.DrawPath(&RectPath);
+					}
+
+					BitmapRR.RestoreContext();
+
+					// Stop rendering
+					BitmapRR.StopRender();
+
+					OILBitmap* pFullBitmap = BitmapRR.ExtractBitmap();
+					String_256 sName = GetNewBitmapName();
+					pFullBitmap->SetName(sName);
+					pNewBitmap = KernelBitmap::MakeKernelBitmap(pFullBitmap);
+
+					// Attach the bitmap to this document or a copy will be created when
+					// it is used in the attribute
+					BitmapList* pBmpList = NULL;
+					Document* pCurDoc = Document::GetCurrent();
+					if (pCurDoc)
+						pBmpList = pCurDoc->GetBitmapList();
+
+					// and then attach the bitmap (doesn't matter if BmpList is NULL)
+					pNewBitmap->Attach(pBmpList);
+
+					// Make sure we preserve the lossy flag
+					pNewBitmap->SetAsLossy(pBitmap->IsLossy());
+				}
+
+				if (pNewBitmap)
+				{
+					TRACE(_T("New bitmap = (%d, %d)"), pNewBitmap->GetWidth(), pNewBitmap->GetHeight());
+
+					// Loop through NodeList asking each node to replace the
+					// specified bitmap with this new one
+
+					NodeListItem* pItem = (NodeListItem*)(NodeList.GetHead());
+					while (pItem)
+					{
+						if (pItem->pNode)
+						{
+							if (!pItem->pNode->ReplaceBitmap(pBitmap, pNewBitmap))
+							{
+								TRACE(_T("Failed to replace bitmap"));
+							}
+						}
+
+						pItem = (NodeListItem*)(NodeList.GetNext(pItem));
+					}
+				}
+				else
+				{
+					TRACE(_T("Failed to create new bitmap"));
+				}
+			}
+		}
+
+		NodeList.DeleteAll();
+		pBitmap = (KernelBitmap*)(pBmpList->GetNext(pBitmap));
+	}
+
+	return(TRUE);
+}
+
+
+/****************************************************************************
+
 >	virtual Node* PluginNativeFilter::GetExportNode()
 
 	Author:		Gerry_Iles (Xara Group Ltd) <camelotdev@xxxxxxxx>
Index: Trunk/XaraLX/Kernel/bmpcomp.cpp
===================================================================
--- Trunk/XaraLX/Kernel/bmpcomp.cpp	(revision 1707)
+++ Trunk/XaraLX/Kernel/bmpcomp.cpp	(revision 1708)
@@ -1474,6 +1474,7 @@
 			if (pSource != NULL)
 			{
 				pBitmap->SetOriginalSource(pSource, pBitmapFilter);
+				pBitmap->SetAsLossy();
 			}
 		}
 		else
@@ -1757,48 +1758,51 @@
 	// Find out whether we need to make an XPE record or not...
 PORTNOTE("other","KernelBitmap::GetXPEInfo removed")
 #ifndef EXCLUDE_FROM_XARALX
-	KernelBitmap* pMaster = NULL;
-	IXMLDOMDocumentPtr pEditList = NULL;
-	pBitmap->GetXPEInfo(pMaster, pEditList);
-	if (pMaster!=NULL && pEditList!=NULL)
+	if (pFilter->GetSaveXPEBitmaps() == FALSE)
 	{
-		// Get master bitmap reference number (writing out the bitmap if necessary)
-		INT32 MasterRecord = 0;
-		MasterRecord = GetWriteBitmapReference(pMaster, pFilter);
-
-		// Only write out new record style if we really have to...
-		// (Helps forward compatibility)
-		if (MasterRecord!=0)
+		KernelBitmap* pMaster = NULL;
+		IXMLDOMDocumentPtr pEditList = NULL;
+		pBitmap->GetXPEInfo(pMaster, pEditList);
+		if (pMaster!=NULL && pEditList!=NULL)
 		{
-			// Create a new style XPE bitmap properties record
-			CXaraFileRecord Rec(TAG_XPE_BITMAP_PROPERTIES);
-
-			BSTR bstrValue;
-			HRESULT hr;
-			hr = pEditList->get_xml(&bstrValue);
-			if (SUCCEEDED(hr))
+			// Get master bitmap reference number (writing out the bitmap if necessary)
+			INT32 MasterRecord = 0;
+			MasterRecord = GetWriteBitmapReference(pMaster, pFilter);
+	
+			// Only write out new record style if we really have to...
+			// (Helps forward compatibility)
+			if (MasterRecord!=0)
 			{
-				ok = Rec.Init();
-				if (ok) ok = Rec.WriteReference(BmpRef);		// bitmap reference
-				if (ok) ok = Rec.WriteBYTE(Flags);				// flags
-				
-				// TODO: write GIF animation properties here
-
-				// reserved bytes
-				for( INT32 i=0; i<7; i++ )
+				// Create a new style XPE bitmap properties record
+				CXaraFileRecord Rec(TAG_XPE_BITMAP_PROPERTIES);
+	
+				BSTR bstrValue;
+				HRESULT hr;
+				hr = pEditList->get_xml(&bstrValue);
+				if (SUCCEEDED(hr))
 				{
-					if (ok) ok = Rec.WriteBYTE(0);
+					ok = Rec.Init();
+					if (ok) ok = Rec.WriteReference(BmpRef);		// bitmap reference
+					if (ok) ok = Rec.WriteBYTE(Flags);				// flags
+					
+					// TODO: write GIF animation properties here
+	
+					// reserved bytes
+					for( INT32 i=0; i<7; i++ )
+					{
+						if (ok) ok = Rec.WriteBYTE(0);
+					}
+	
+					// Now write out XPE stuff
+					if (ok) ok = Rec.WriteReference(MasterRecord);		// Master bitmap record number
+					if (ok) ok = Rec.WriteUnicode(pBitmap->GetName());	// Oil Bitmap name
+					if (ok) ok = Rec.WriteBSTR(bstrValue);				// UNICODE xml string edits list
+	
+					// Write the record
+					if (ok) ok = pFilter->Write(&Rec);
+	
+					return ok;
 				}
-
-				// Now write out XPE stuff
-				if (ok) ok = Rec.WriteReference(MasterRecord);		// Master bitmap record number
-				if (ok) ok = Rec.WriteUnicode(pBitmap->GetName());	// Oil Bitmap name
-				if (ok) ok = Rec.WriteBSTR(bstrValue);				// UNICODE xml string edits list
-
-				// Write the record
-				if (ok) ok = pFilter->Write(&Rec);
-
-				return ok;
 			}
 		}
 	}
@@ -2008,6 +2012,22 @@
 					Tag = TAG_DEFINEBITMAP_JPEG;
 					SearchFilter = FILTERID_EXPORT_JPEG;
 				}
+				else if (Compression >= 201 && Compression <= 300)
+				{
+					// If compression is between 201 and 300 then use JPEG
+					// where the bitmap came from a lossy source
+					if (pBitmap->IsLossy())
+					{
+						Tag = TAG_DEFINEBITMAP_JPEG;
+						SearchFilter = FILTERID_EXPORT_JPEG;
+						Compression -= 200;		// Get quality in the correct range
+					}
+					else
+					{
+						Tag = TAG_DEFINEBITMAP_PNG;
+						SearchFilter = FILTERID_PNG;
+					}
+				}
 				else
 				{
 					Tag = TAG_DEFINEBITMAP_PNG;
@@ -2015,10 +2035,29 @@
 				}
 				break;
 			case 32:
-				// No choice but to use PNG or BMP in all cases as these are the only
-				// formats that support 32bpp.
-				Tag = TAG_DEFINEBITMAP_PNG;
-				SearchFilter = FILTERID_PNG;
+				if (Compression >= 0 && Compression <= 200)
+				{
+					// Definitely use PNG is this case
+					Tag = TAG_DEFINEBITMAP_PNG;
+					SearchFilter = FILTERID_PNG;
+				}
+				else
+				{
+					// If compression indicates JPEG where possible then use 
+					// JPEG where the bitmap came from a lossy source and has 
+					// no transparency in its alpha channel
+					if (pBitmap->IsLossy() && !pBitmap->IsTransparent())
+					{
+						Tag = TAG_DEFINEBITMAP_JPEG;
+						SearchFilter = FILTERID_EXPORT_JPEG;
+						Compression -= 200;		// Get quality in the correct range
+					}
+					else
+					{
+						Tag = TAG_DEFINEBITMAP_PNG;
+						SearchFilter = FILTERID_PNG;
+					}
+				}
 				break;
 			default:
 				ERROR2(0,"BitmapListComponent::SaveBitmapDefinition bad pixel depth");
Index: Trunk/XaraLX/Kernel/bitmap.h
===================================================================
--- Trunk/XaraLX/Kernel/bitmap.h	(revision 1707)
+++ Trunk/XaraLX/Kernel/bitmap.h	(revision 1708)
@@ -360,6 +360,7 @@
 	BOOL IsDefaultBitmap() { return this == OILBitmap::Default || HasBeenDeleted(); }
 	BOOL IsTemp() { return m_bTemp; }		
 	BOOL IsInGlobalList() { return !m_bTemp; }
+	BOOL IsLossy() { return m_bIsLossy; }
 
 	void SetAsFractal() { m_bIsAFractal = TRUE;
 						  m_bIsGreyscale = TRUE; }
@@ -368,6 +369,7 @@
 		// Informs this object that the bitmap it contains *is* a greyscale 8bpp bitmap, and that
 		// it therefore doesn't need to make a copy in GetGreyscaleVersion()
 
+	void SetAsLossy(BOOL bLossy = TRUE) { m_bIsLossy = bLossy; };
 	BOOL IsAFractal() { return m_bIsAFractal; }
 
 	virtual BOOL IsBrowserPalette() = 0;
@@ -431,6 +433,7 @@
 	BOOL m_bUsedByBrush : 1;
 	BOOL m_bNeedsXPERebuild : 1;	// Bitmap data has been removed from memory, can be rebuilt from m_pMasterBitmap and m_pEditList
 	BOOL m_bHidden : 1;				// Should not show up in bitmap gallery
+	BOOL m_bIsLossy : 1;
 
 #ifdef _DEBUG
 	// Debug flag, so we can detect multiple references to temp bitmaps
@@ -555,6 +558,7 @@
 	BOOL IsDefaultBitmap();
 	BOOL HasBeenDeleted();
 	BOOL IsGreyscale();
+	BOOL IsLossy();
 
 	// Functions to access the orginal bitmap file
 	void SetOriginalSource(BitmapSource* pFileBuffer = NULL, BaseBitmapFilter* pImportFilter = NULL);
@@ -570,6 +574,7 @@
 
 	BOOL SetAsFractal();
 	BOOL SetAsGreyscale();
+	BOOL SetAsLossy(BOOL bLossy = TRUE);
 
 	UINT32 GetDelay();
 	void SetDelay(UINT32 Delay);
Index: Trunk/XaraLX/Kernel/nodebmp.cpp
===================================================================
--- Trunk/XaraLX/Kernel/nodebmp.cpp	(revision 1707)
+++ Trunk/XaraLX/Kernel/nodebmp.cpp	(revision 1708)
@@ -2440,7 +2440,88 @@
 }
 
 
+/****************************************************************************
 
+>	double NodeBitmap::GetEffectiveBitmapMinDPI(KernelBitmap* pBitmap)
+
+	Author:		Gerry_Iles (Xara Group Ltd) <camelotdev@xxxxxxxx>
+	Created:	07/08/2006
+
+	Inputs:		pBitmap		- pointer to a KernelBitmap
+	Returns:	
+	Purpose:	
+
+****************************************************************************/
+
+double NodeBitmap::GetEffectiveBitmapMinDPI(KernelBitmap* pBitmap)
+{
+	if (GetBitmap() == pBitmap)
+	{
+		// Do we have a valid bitmap ?
+		OILBitmap *OilBM = pBitmap->ActualBitmap;
+		if (OilBM != NULL)
+		{
+			BitmapInfo Info;
+			OilBM->GetInfo(&Info);
+
+			// Get the Width of the Bitmap in Pixels
+			INT32 PixWidth  = Info.PixelWidth;
+			INT32 PixHeight = Info.PixelHeight;
+
+			// Get the Width of the Bitmap in Millipoints
+			INT32 Width  = INT32(Parallel[0].Distance(Parallel[1]));
+			INT32 Height = INT32(Parallel[1].Distance(Parallel[2]));
+
+			// Use doubles so that we can round up as well as down. This improves
+			// the dpi calculated.
+			double HDpi = 0;
+			double VDpi = 0;
+
+			if (Width > 0)
+				HDpi = ((double)PixWidth * 72000.0)/(double)Width;
+
+			if (Height > 0)
+				VDpi = ((double)PixHeight * 72000.0)/(double)Height;
+
+			// Use the smaller of the two dpi values
+			if (HDpi < VDpi)
+				return(HDpi);
+			else
+				return(VDpi);
+		}
+	}
+
+	return(1e9);
+}
+
+
+
+/****************************************************************************
+
+>	BOOL NodeBitmap::ReplaceBitmap(KernelBitmap* pOrigBitmap, KernelBitmap* pNewBitmap)
+
+	Author:		Gerry_Iles (Xara Group Ltd) <camelotdev@xxxxxxxx>
+	Created:	07/08/2006
+
+	Inputs:		pOrigBitmap	- pointer to a KernelBitmap
+				pNewBitmap	- pointer to a KernelBitmap
+	Returns:	TRUE if ok, FALSE if bother
+	Purpose:	
+
+****************************************************************************/
+
+BOOL NodeBitmap::ReplaceBitmap(KernelBitmap* pOrigBitmap, KernelBitmap* pNewBitmap)
+{
+	if (GetBitmap() == pOrigBitmap)
+	{
+		BitmapRef.Attach(pNewBitmap);
+		return(TRUE);
+	}
+
+	return FALSE;
+}
+
+
 /********************************************************************************************
 >	BOOL NodeBitmap::IsABitmap() const
 
Index: Trunk/XaraLX/Kernel/exjpeg.cpp
===================================================================
--- Trunk/XaraLX/Kernel/exjpeg.cpp	(revision 1707)
+++ Trunk/XaraLX/Kernel/exjpeg.cpp	(revision 1708)
@@ -945,6 +945,7 @@
 				break;
 
 			case 24:
+			case 32:
 				pOptions->SetColourModel(libJPEG::JCS_RGB);
 				break;
 	
Index: Trunk/XaraLX/Kernel/camfiltr.cpp
===================================================================
--- Trunk/XaraLX/Kernel/camfiltr.cpp	(revision 1707)
+++ Trunk/XaraLX/Kernel/camfiltr.cpp	(revision 1708)
@@ -6994,54 +6994,31 @@
 BOOL BaseCamelotFilter::WriteRemainingAtomicTagDefinitions ()
 {
 	BOOL ok = TRUE;
-	
+
 	CXaraFileRecord atomicRec(TAG_ATOMICTAGS, TAG_ATOMICTAGS_SIZE);
 	if (ok) ok = atomicRec.Init();
 	if (ok) ok = atomicRec.WriteUINT32(TAG_BEVEL);						// NodeBevelController
-	if (ok)	ok = Write(&atomicRec);										// And write out the record
+	if (ok) ok = atomicRec.WriteUINT32(TAG_BEVELINK);					// NodeBevel
+	if (ok) ok = atomicRec.WriteUINT32(TAG_CONTOURCONTROLLER);			// NodeContourController
+	if (ok) ok = atomicRec.WriteUINT32(TAG_CONTOUR);					// NodeContour
+	if (ok) ok = atomicRec.WriteUINT32(TAG_SHADOWCONTROLLER);			// NodeShadowController
+	if (ok) ok = atomicRec.WriteUINT32(TAG_SHADOW);						// NodeShadow
+	if (ok) ok = atomicRec.WriteUINT32(TAG_CLIPVIEWCONTROLLER);			// NodeClipViewController
+	if (ok) ok = atomicRec.WriteUINT32(TAG_CLIPVIEW);					// NodeClipView
+	if (ok) ok = atomicRec.WriteUINT32(TAG_CURRENTATTRIBUTES);			// Current Attributes container/component
+	// --------------------------------------------------------------
+	// Effect nodes marked as atomic in "Xara X2", 28/06/2005
+	if (ok) ok = atomicRec.WriteUINT32(TAG_LIVE_EFFECT);				// NodeLiveEffect
+	if (ok) ok = atomicRec.WriteUINT32(TAG_LOCKED_EFFECT);				// NodeLockedEffect
+	if (ok) ok = atomicRec.WriteUINT32(TAG_FEATHER_EFFECT);				// NodeFeatherEffect
+//	if (ok) ok = atomicRec.WriteUINT32(TAG_CURRENTATTRIBUTES_PHASE2);	// Current Attributes container/component
+//	if (ok) ok = atomicRec.WriteUINT32(TAG_SPREAD_PHASE2);				// NodeSpread (in multi-spread docs)
 
-	CXaraFileRecord atomicRec2(TAG_ATOMICTAGS, TAG_ATOMICTAGS_SIZE);
-	if (ok) ok = atomicRec2.Init();
-	if (ok) ok = atomicRec2.WriteUINT32(TAG_BEVELINK);					// NodeBevel
-	if (ok)	ok = Write(&atomicRec2);
+	// Just keep writing more tags to the end of this one record
 
-	CXaraFileRecord atomicRec3(TAG_ATOMICTAGS, TAG_ATOMICTAGS_SIZE);
-	if (ok) ok = atomicRec3.Init();
-	if (ok) ok = atomicRec3.WriteUINT32(TAG_CONTOURCONTROLLER);			// NodeContourController
-	if (ok)	ok = Write(&atomicRec3);
+	// Write the whole record out
+	if (ok)	ok = Write(&atomicRec);
 
-	CXaraFileRecord atomicRec4(TAG_ATOMICTAGS, TAG_ATOMICTAGS_SIZE);
-	if (ok) ok = atomicRec4.Init();
-	if (ok) ok = atomicRec4.WriteUINT32(TAG_CONTOUR);					// NodeContour
-	if (ok)	ok = Write(&atomicRec4);
-
-	CXaraFileRecord atomicRec5(TAG_ATOMICTAGS, TAG_ATOMICTAGS_SIZE);
-	if (ok) ok = atomicRec5.Init();
-	if (ok) ok = atomicRec5.WriteUINT32(TAG_SHADOWCONTROLLER);			// NodeShadowController
-	if (ok)	ok = Write(&atomicRec5);
-
-	CXaraFileRecord atomicRec6(TAG_ATOMICTAGS, TAG_ATOMICTAGS_SIZE);
-	if (ok) ok = atomicRec6.Init();
-	if (ok) ok = atomicRec6.WriteUINT32(TAG_SHADOW);						// NodeShadow
-	if (ok)	ok = Write(&atomicRec6);
-
-	CXaraFileRecord atomicRec7(TAG_ATOMICTAGS, TAG_ATOMICTAGS_SIZE);
-	if (ok) ok = atomicRec7.Init();
-	if (ok) ok = atomicRec7.WriteUINT32(TAG_CLIPVIEWCONTROLLER);			// NodeClipViewController
-	if (ok)	ok = Write(&atomicRec7);
-
-	CXaraFileRecord atomicRec8(TAG_ATOMICTAGS, TAG_ATOMICTAGS_SIZE);
-	if (ok) ok = atomicRec8.Init();
-	if (ok) ok = atomicRec8.WriteUINT32(TAG_CLIPVIEW);					// NodeClipView
-	if (ok)	ok = Write(&atomicRec8);
-
-	CXaraFileRecord atomicRec9(TAG_ATOMICTAGS, TAG_ATOMICTAGS_SIZE);
-	if (ok) ok = atomicRec9.Init();
-	if (ok) ok = atomicRec9.WriteUINT32(TAG_CURRENTATTRIBUTES);			// Current Attributes container/component
-	if (ok)	ok = Write(&atomicRec9);
-
-	
-
 	return (ok);
 }
 
Index: Trunk/XaraLX/Kernel/xpfrgn.cpp
===================================================================
--- Trunk/XaraLX/Kernel/xpfrgn.cpp	(revision 1707)
+++ Trunk/XaraLX/Kernel/xpfrgn.cpp	(revision 1708)
@@ -2262,6 +2262,7 @@
 	BOOL bOldLayerVisibility = FALSE;
 	Layer* pSingleLayer = NULL;
 	Spread* pSingleSpread = NULL;
+	NodeBitmap* pSingleBitmap = NULL;
 	if (pFirstNode == pLastNode)
 	{
 		if (pFirstNode->IsLayer())
@@ -2270,10 +2271,14 @@
 			bOldLayerVisibility = pSingleLayer->GetVisibleFlagState();
 			pSingleLayer->SetVisible(TRUE);
 		}
-		if (pFirstNode->IsSpread())
+		else if (pFirstNode->IsSpread())
 		{
 			pSingleSpread = (Spread*)pFirstNode;
 		}
+		else if (pFirstNode->IsABitmap())
+		{
+			pSingleBitmap = (NodeBitmap*)pFirstNode;
+		}
 	}
 	
 	// Find the bounding rect of the nodes and determine if the background needs
@@ -2443,6 +2448,12 @@
 	
 	KernelBitmap* pRealBmp = KernelBitmap::MakeKernelBitmap(pFullBitmap);
 
+	// If we are converting a single NodeBitmap then maintain the lossy flag
+	if (pSingleBitmap && pSingleBitmap->GetBitmap())
+	{
+		pRealBmp->SetAsLossy(pSingleBitmap->GetBitmap()->IsLossy());
+	}
+
 	// Attach the bitmap to this document or a copy will be created when 
 	// it is used in the NodeBitmap
 	BitmapList* pBmpList = NULL;
Index: Trunk/XaraLX/Kernel/imjpeg.cpp
===================================================================
--- Trunk/XaraLX/Kernel/imjpeg.cpp	(revision 1707)
+++ Trunk/XaraLX/Kernel/imjpeg.cpp	(revision 1708)
@@ -526,6 +526,9 @@
 		return FALSE;
 	}
 
+	// Make sure this bitmap is treated as lossy
+	pOilBitmap->SetAsLossy();
+
 	// Clear any eof signal as CCFile considers this an error
 	if (m_pFile->eof())
 	{
Index: Trunk/XaraLX/Kernel/node.h
===================================================================
--- Trunk/XaraLX/Kernel/node.h	(revision 1707)
+++ Trunk/XaraLX/Kernel/node.h	(revision 1708)
@@ -533,7 +533,13 @@
 
 	// This function returns NULL in the base class.
 	virtual KernelBitmap* EnumerateBitmaps(UINT32 Count);
-		
+
+	// This function returns the special value 1e9 to indicate no effective minimum
+	virtual double GetEffectiveBitmapMinDPI(KernelBitmap* pBitmap) { return(1e9); }
+
+	// Replace a specific bitmap with another one
+	virtual BOOL ReplaceBitmap(KernelBitmap* pOrigBitmap, KernelBitmap* pNewBitmap) { return(FALSE); }
+
 	// Find out the optoken of the default operation for this node. (See EditSelectionOp and Return hotkey.)
 	virtual TCHAR* GetDefaultOpToken() {return NULL;}
 		
Index: Trunk/XaraLX/Kernel/bitmap.cpp
===================================================================
--- Trunk/XaraLX/Kernel/bitmap.cpp	(revision 1707)
+++ Trunk/XaraLX/Kernel/bitmap.cpp	(revision 1708)
@@ -594,6 +594,37 @@
 
 /********************************************************************************************
 
+>	BOOL KernelBitmap::SetAsLossy(BOOL bLossy = TRUE)
+			
+	Author:		Gerry_Iles (Xara Group Ltd) <camelotdev@xxxxxxxx>
+	Created:	20/2/97
+
+	Returns:	FALSE if invalid pointer found
+
+	Purpose:	Set this bitmap' lossy flag
+
+********************************************************************************************/
+
+BOOL KernelBitmap::SetAsLossy(BOOL bLossy)
+{
+	if (ActualBitmap == NULL)
+		return FALSE;
+
+	ERROR3IF_OILBMP_PTR_INVALID(ActualBitmap,
+				"Bitmap Error.  Found a reference to a deleted bitmap.");
+
+	if (ActualBitmap)
+	{
+		ActualBitmap->SetAsLossy(bLossy);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+/********************************************************************************************
+
 >	BOOL KernelBitmap::IsDefaultBitmap()
 					
 	Author:		Neville_Humphrys (Xara Group Ltd) <camelotdev@xxxxxxxx>
@@ -1542,6 +1573,8 @@
 	{
 		// We can't replace the default bitmap so make a new OilBitmap and attach that
 		CWxBitmap	   *pOilBitmap = new CWxBitmap(pInfo, pBits);
+		if (pOilBitmap && IsLossy())
+			pOilBitmap->SetAsLossy();
 		ActualBitmap = OILBitmap::Attach(pOilBitmap);
 	}
 	else
@@ -1748,6 +1781,31 @@
 }
 
 
+/********************************************************************************************
+
+>	BOOL KernelBitmap::IsLossy()
+
+	Author:		Gerry_Iles (Xara Group Ltd) <camelotdev@xxxxxxxx>
+	Created:	15/8/96
+	Purpose:	Finds if this bitmap is lossy or not
+
+********************************************************************************************/
+
+BOOL KernelBitmap::IsLossy()
+{
+	if (ActualBitmap == NULL)
+		return FALSE;
+
+	ERROR3IF_OILBMP_PTR_INVALID(ActualBitmap,
+				"Bitmap Error.  Found a reference to a deleted bitmap.");
+
+	if (HasBeenDeleted())
+		return OILBitmap::Default->IsLossy();
+
+	return ActualBitmap->IsLossy();
+}
+
+
 /*******************************************************************************************
 >	UINT32 KernelBitmap::GetDelay()
 
@@ -2217,6 +2275,7 @@
 
 	m_bIsAFractal = FALSE;
 	m_bIsGreyscale = FALSE;
+	m_bIsLossy = FALSE;
 
 	SetBitmapAnimDelay(10);
 	SetAnimationRestoreType(GDM_LEAVE);
Index: Trunk/XaraLX/wxOil/xpoilflt.cpp
===================================================================
--- Trunk/XaraLX/wxOil/xpoilflt.cpp	(revision 1707)
+++ Trunk/XaraLX/wxOil/xpoilflt.cpp	(revision 1708)
@@ -857,6 +857,7 @@
 	BOOL bAlpha = TRUE;
 	long Compression = 200;
 	String_256 CommonTrans;
+	BOOL bResample = FALSE;
 	wxString str;
 
 	str = CXMLUtils::ConvertToWXString(xmlGetProp(pNode, (xmlChar*)"dpi"));
@@ -883,8 +884,14 @@
 		CommonTrans = str;
 	}
 
-	pCapTree->SetRasterise(DPI, bAlpha, (INT32)Compression, CommonTrans);
+	str = CXMLUtils::ConvertToWXString(xmlGetProp(pNode, (xmlChar*)"resample"));
+	if (!str.IsEmpty())
+	{
+		bResample = (str == _T("true"));
+	}
 
+	pCapTree->SetRasterise(DPI, bAlpha, (INT32)Compression, CommonTrans, bResample);
+
 	return TRUE;
 }
 


Xara