ASE Home Page Products Download Purchase Support About ASE
ChartDirector Support
Forum HomeForum Home   SearchSearch

Message ListMessage List     Post MessagePost Message

  stretching image on the chart
Posted by Steven on Mar-13-2021 03:17
Hello ASE,

I'm using XY-chart with zoom control to display incoming data on the screen. It works very well.

By the way, I have three questions below for you.

1. I used setBackground() of PlotArea class to put an image on the chart. But I can see a part of the image because the image is bigger than chart size. Is it possible to stretch image in the size of the chart?

2. I'm using zoom control. So, is it possible zoom in or out the background image when I update zoom control of the chart?

3. Some functions - setBackground(), setWallpaper(), and setBgImage() are using only filename of the image. Is there another way for displaying background image instead of passing the filename? (ie. I want to pass memory pointer of the image instead of filename, if possible)

Thank you.
Best regards,
Steven Wonly

  Re: stretching image on the chart
Posted by Peter Kwan on Mar-15-2021 19:11
Hi Steven,

All of your requirements can be achieved with ChartDirector.

Many of our recent sample code no longer use image files on the disk. It loads the image as "resources". The exact details depend on your programming language edition of ChartDirector, your development framework (eg. Windows Forms or WPF or MFC, etc) and whether you are writing a web or desktop application.

In your post, you mentioned "memory pointer". Are you use C++? For C++, it is as follows:

https://www.advsofteng.com/doc/cdcpp.htm#pathspec.htm

For (3), you can register your block of memory as a resource using BaseChart::setResource. You can then use the "@/my_resource_name" syntax as the path to load the image from the resource. Note that the memory must contain a supported image type, such as PNG, JPG and GIF.

For (1), if it is a static image, you can put a textbox at the background. The text can be the CDML tag "<*img=path_name,width=www,height=hhh*>". This allows you to insert an image and resize it.

However, considering you also need to perform (2), it is better to use the more powerful DrawArea object. Basically, you can load the image into a DrawArea object, then resize it with DrawArea.resize.

https://www.advsofteng.com/doc/cdnet.htm#DrawArea.load.htm
https://www.advsofteng.com/doc/cdnet.htm#DrawArea.resize.htm

You can then register the DrawArea as a resource and use it as the background image. See:

To perform (2), you can use the affine transform. See:

https://www.advsofteng.com/doc/cdnet.htm#DrawArea.affineTransform.htm

Affine transform is a coordinate transform that can achieve resizing and shifting (and many other things such as rotation, shear, flipping) in one step. For your case, you would need to determine the affine transform parameters based on the viewport settings.

Regards
Peter Kwan

  Re: stretching image on the chart
Posted by Steven Wonly on Mar-16-2021 02:33
Attachments:
Hi Peter,

I modified the source code along your guidance. But I could not make it.
Let me share some parts of them below. And source code for ChartDirector is attached.
Am I missing something?

Thank you
Steven

--------------------------------------------------------------------------------------------
a) Reading jpeg file & Assigning resource routines are added while initializing ChartDirector.

#define CHART_MAP_ID "mapCanvas.jpg" // resource ID for map file

MemBlock m_imgMap;    // global variable

// read jpeg file and update m_imgMap.len & m_imgMap.data

if (m_imgMap.len) {
// create resource for ChartDirector
Chart::setResource(CHART_MAP_ID, m_imgMap);
//BaseChart::setResource(CHART_MAP_ID, &m_imgMap);
TRACE(_T("defined ChartDirector resourcen"));
}


b) And modified setBackground() function as below code

pPlotArea->setBackground("test.jpg", Chart::BottomLeft);

--> sprintf_s(szCDML, "<*img=@/%s*>", CHART_MAP_ID);
      pPlotArea->setBackground(szCDML, Chart::BottomLeft);
DisplayChart.cpp
// Test_MqttUwbDlg_Chart.cpp : implementation file
//
// --> this file will be included end of Test_MqttUwbDlg.cpp file
//
// following routines for managing Chart
//

#include <sstream>		// std::ostringstream
#include <algorithm>

#include "ColorList.h"
#include "DlgTabView.h"

#define CHART_MAP_ID			"mapCanvas.jpg"	// ChartDirector resource ID for map file


class CTestMqttUwbDlg : public CDialogEx
{
public:
	MemBlock			m_imgMap;			// memory block for map image that will be loaded from the file

	int					m_bgColor;			// background color; Transparent or RGB_LIGHTGRAY
	int					m_bgColorOld;		// default background color for chart

	CChartViewer		m_ChartViewer;		// scatter chart to display position of tags & anchors
	CViewPortControl	m_ZoomControl;		// zoom control - full view of chart & mark zoomed area
};


void CTestMqttUwbDlg::InitChart(void)
{
	// load MAP image file for background of the chart
	// use CDML (ChartDirector Mark up Language) to make resource
	// loading big image file (map file) from the disk is heavy job.
	// So, let's use resouce rather than loading file every time.
	// refer: https://www.chartdir.com/forum/download_thread.php?site=chartdir&bn=chartdir_support&thread=1615576662
	//
	// [ Caution ] 
	// ChartDirector supports only png, jpg, jpeg, gif, and wbmp/wmp (case insensitive).
	//

	// load map image file, if filename is defined
	if( m_Param.canvas.szMapFile[0] ) {
		CFile f;
		int   nRead;
		CFileException e;
		if (!f.Open(A2T(m_Param.canvas.szMapFile), CFile::modeRead, &e)) {
			// FAILED to open file
			m_imgMap.len  = 0;
			m_imgMap.data = NULL;
		}
		else {
			// SUCCEEDED to open file
			m_imgMap.len = f.GetLength();
			m_imgMap.data = new char[m_imgMap.len];
			if (m_imgMap.data == NULL) {
				m_imgMap.len = 0;
			}
			else {
				nRead = f.Read((void*)m_imgMap.data, m_imgMap.len);
				if (nRead != m_imgMap.len) {
					m_imgMap.len = 0;
					if (m_imgMap.data) delete[] m_imgMap.data;
				}
			}
			f.Close();
		}

		if (m_imgMap.len) {
			// create resource for ChartDirector
			Chart::setResource(CHART_MAP_ID, m_imgMap);
			//BaseChart::setResource(CHART_MAP_ID, &m_imgMap);
			TRACE(_T("defined ChartDirector resource\n"));
		}
	} // end of if( exist szMapFile )


	// determine background color for the charts
	// if loaded MAP file, use Transparent color. Otherwise use RGB_LIGHTGRAY
	m_bgColorOld = CanvasGetBackColor();	// default background color
	m_bgColor = (m_imgMap.len == 0) ? RGB_LIGHTGRAY : Transparent;

	// draw initial chart screen
	// Trigger the ViewPortChanged event to draw the chart
	m_ChartViewer.updateViewPort(true, false);
}

void CTestMqttUwbDlg::DrawCanvas(CChartViewer* pCanvas)
{
	//------------------------------------------------------------------------------
	// chart type: scatter chart / all 4 quadrants in the XY plane
	// symbols   : use built-in symbols for position of each anchor and tag
	// future    : 3D scatter chart -> can display x, y, z position
	//------------------------------------------------------------------------------

	// get canvas size
	CRect	  sizeCanvas;
	PlotArea* pPlotArea;

	GetDlgItem(IDC_CANVAS)->GetWindowRect(&sizeCanvas);
	//TRACE(_T("Canvas size: %d x %d\n"), sizeCanvas.Width(), sizeCanvas.Height());

	// Create an XYChart object same as canvas size, with grey (f4f4f4) background,
	// black (000000) border, 1 pixel raised effect, and with a rounded frame.
	XYChart* chart = new XYChart(sizeCanvas.Width(), sizeCanvas.Height(), RGB_GRAY1, RGB_BLACK, 1);

	// Set the plotarea at (40, 20) and of size Width-60 x Height-110 pixels. Use white (0x404040) 
	// background. Enable both horizontal and vertical grids by setting their colors to 
	// gray (cccccc). Set clipping mode to clip the data lines to the plot area.
	pPlotArea = chart->setPlotArea(40,							// x
								   20,							// y
								   sizeCanvas.Width() - 60,		// width
								   sizeCanvas.Height() - 110,	// height
								   m_bgColor,					// background color; Transparent or RGB_LIGHTGRAY
								   -1,							// alt background color - white (0xffffffff)
								   -1,							// edge color - white (0xffffffff)
								   RGB_GRAY2,					// horizontal grid color - gray
								   RGB_GRAY2);					// vertical grid color - gray
	TRACE(_T("plot area: %d x %d, %d, %d\n"), pPlotArea->getLeftX(), pPlotArea->getTopY(), pPlotArea->getWidth(), pPlotArea->getHeight());


	if (m_bgColor == Transparent) {
#if(1)
		// set the background image on the plot area using PlotArea class
		// wallpaper() is an image that will repeat itself to fill the region
		// backgroung() image is an image only appears and will not repeat itself
		//chart->setWallpaper("Layout_167th_ParkingLot_02.jpg");
		//chart->setBgImage("Layout_167th_ParkingLot_02.jpg", Chart::BottomLeft);	// works as setWallpaper()
		//pPlotArea->setBackground("Layout_167th_ParkingLot_02.jpg", Chart::BottomLeft);

		char szCDML[80];
		sprintf_s(szCDML, "<*img=@/%s,width=%d,height=%d*>", CHART_MAP_ID, pPlotArea->getWidth(), pPlotArea->getHeight());
		pPlotArea->setBackground(szCDML, Chart::BottomLeft);
#else 
		// set the background image on the plot area using DrawArea class
		char szCDML[80];
		sprintf_s(szCDML, "<*img=@/%s*>", CHART_MAP_ID);
		
		DrawArea* pDrawArea = chart->getDrawArea();
		//pDrawArea->load(m_Param.canvas.szMapFile);
		pDrawArea->load(szCDML);

		// Re-sampling filter
		//		0: Box filter		using the nearest integer-coordinate pixel
		//		1: Linear Filter	using the linear weighted average of nearby integer-coordinate pixels
		//		2: Quadratic filter		8: Lanczos filter
		//		3: B-spline filter		9: Gaussian filter
		//		4: Hermite filter		10:Hanning filter
		//		5: Catrom filter		11:Hamming filter
		//		6: Mitchell filter		12:Blackman filter
		//		7: Sinc filter			13:Bessel filter
		pDrawArea->resize(pPlotArea->getWidth(), pPlotArea->getHeight(), 1/*filter*/);
#endif
	} // end of if( background color == Transparent )



	chart->setRoundedFrame(m_bgColorOld);

	// Enable clipping mode to clip the part of the data that is outside the plot area.
	chart->setClipping();

	//
	// add a chart title
	// Add a title to the chart using 15 pts Times New Roman Bold Italic font, with a light
	// grey (dddddd) background, black (000000) border, and a glass like raised effect.
	//chart->addTitle("Pester", "timesbi.ttf", 15)->setBackground(0xdddddd, 0x000000, Chart::glassEffect());
	
	//
	// add a legent box
    // Add a legend box at the top of the plot area with 9pts Arial Bold font. We set the 
    // legend box to the same width as the plot area and use grid layout (as opposed to 
    // flow or top/down layout). This distributes the 3 legend icons evenly on top of the 
    // plot area.
	chart->addLegend(20, sizeCanvas.Height() - 60, false, "timesbi.ttf", 10);


	// add a title to the X/Y axis
	chart->xAxis()->setTitle("mm", "arialbi.ttf", 7)->setAlignment(Chart::Right);
	chart->yAxis()->setTitle("mm", "arialbi.ttf", 7)->setAlignment(Chart::Top);

	// Set 4 quadrant mode, with both x and y axes symetrical around the origin
	//chart->setAxisAtOrigin(Chart::XYAxisAtOrigin, Chart::XAxisSymmetric + Chart::YAxisSymmetric);

	// set range of X/Y axis --> use X/Y scales that defined in Zoom Canvas
	// ie. turn off auto-scale
	// and set tick mark every 1000mm on a chart
	//chart->xAxis()->setLinearScale(MIN_CHART_X, MAX_CHART_X, CHART_TICK_SIZE);	//
	//chart->yAxis()->setLinearScale(MIN_CHART_Y, MAX_CHART_Y, CHART_TICK_SIZE);	//
	chart->xAxis()->setLinearScale(m_pView->m_nRangeXMin, m_pView->m_nRangeXMax, CHART_TICK_SIZE);	//
	chart->yAxis()->setLinearScale(m_pView->m_nRangeYMin, m_pView->m_nRangeYMax, CHART_TICK_SIZE);	//

	//---------------------------------------------------------------
	// put in data (ie. position of UWB devices) on the canvas (chart)
	AddData2Canvas(chart, m_pView->m_nMarkSize*2);

	//
	// In this example, we have not explicitly configured the full x and y range. In this case, 
	// the first time syncLinearAxisWithViewPort is called, ChartDirector will auto-scale the axis
	// and assume the resulting range is the full range. In subsequent calls, ChartDirector will 
	// set the axis range based on the view port and the full range.
	//
	// syncLinearAxisWithViewPort()
	// If the data scale has already been defined, this method will compute the visible data scale based on the
	// viewport coordinates using date/time interpolation, and then sets the axis to that date/time scale.
	pCanvas->syncLinearAxisWithViewPort("x", chart->xAxis());
	pCanvas->syncLinearAxisWithViewPort("y", chart->yAxis());

	// We need to update the track line too. If the mouse is moving on the chart (eg. if 
	// the user drags the mouse on the chart to scroll it), the track line will be updated
	// in the MouseMovePlotArea event. Otherwise, we need to update the track line here.
	if ((!pCanvas->isInMouseMoveEvent()) && pCanvas->isMouseOnPlotArea())
		DrawCrossHairCursor(chart, pCanvas->getPlotAreaMouseX(), pCanvas->getPlotAreaMouseY());

    // Set the chart image to the WinChartViewer
	delete pCanvas->getChart();
	pCanvas->setChart(chart);
}

// utility to get default background color
int	CTestMqttUwbDlg::CanvasGetBackColor(void)
{
}

void CTestMqttUwbDlg::CanvasUpdateImageMap(CChartViewer *pViewer)
{
}

// called by both of DrawCanvas() and DrawZoomCanvas()
void CTestMqttUwbDlg::AddData2Canvas(XYChart*& chart, int nSymbolSize)
{
	// draw received input data
	//...
}


// Draw cross hair cursor with axis labels
void CTestMqttUwbDlg::DrawCrossHairCursor(XYChart* pChart, int mouseX, int mouseY)
{
}

// Draw track cursor when mouse is moving over plotarea, and update image map if necessary
void CTestMqttUwbDlg::OnMouseMovePlotArea()
{
}

// clicked a chart
void CTestMqttUwbDlg::OnChartViewer()
{
}

// View port changed event
void CTestMqttUwbDlg::OnViewPortChanged()
{
}

  Re: stretching image on the chart
Posted by Steven Wonly on Mar-16-2021 02:39
Hi Peter,

I'm using VC2019, MFC, and ChartDirector version 6.

Thank you.
Steven

  Re: stretching image on the chart
Posted by Peter Kwan on Mar-16-2021 11:05
Hi Steven,

If your ChartDirector version is of 6.3 or above, it will support resources. (You can right click on the "chartdir60.dll" and select Properties/Details to see its version.)

If you use the DrawArea method, the DrawArea should be a new DrawArea, not the one used by the chart. It should be:

// Include it as a member variable of the dialog
DrawArea* d = 0;


// The resource path
char imagePath[80];
sprintf_s(imagePath, "@/%s", CHART_MAP_ID);

//Load image to drawarea and resize, then set it as a resource
delete d; // delete previous DrawArea
d = new DrawArea();
d->load(imagePath);
d->resize(pPlotArea->getWidth(), pPlotArea->getHeight());

// For debugging, you may ask the Drawarea to output the image to check if the DrawArea can understand the memory block
// d->outPNG("some_temp_path.png");

//The background image
chart->setResource("abcd", d);
pPlotarea->setBackground("@/abcd");


If you use the CDML method, you may try:

// .... code to generate the szCDML ...

// Put the text box under the plot area. No setBackground is necessary.
// **** Note: In setPlotArea, you may want to set the plotarea background color to Chart::Transparent (not m_bgColor)
chart->addText(pPlotArea->getLeftX(), pPlotArea->getTopY(), szCDML)->setZOrder(Chart::PlotAreaZ);


Regards
Peter Kwan

  Re: stretching image on the chart
Posted by Steven Wonly on Mar-17-2021 01:45
Attachments:
Hi Peter,

It works after modifying code. Thank you for your help.

By the way, CPU usage goes up too much when I use affineTransform() and resize(), setBackground() functions. CPU usage for each case is listed below. (cf. the image size that I'm using is 1776 (H) x 987 (V) x 24bit, Jpeg image)

     without background image: 4% ~ 7%
     with resize() + setBackground(): 18% ~ 35%
     with resize() + setBackground() + affineTransform() : 18% ~ 43%

May I ask you another idea for improving performance? The source code of this part is attached.

Thank you.
Steven
DisplayChart.cpp
DrawArea*	m_mapCanvas;	// declared as class member variable


void CTestMqttUwbDlg::DrawCanvas(CChartViewer* pCanvas)
{
	PlotArea* pPlotArea = pCanvas->setPlotArea();

	//..

	if (m_bgColor == Chart::Transparent) {
		// set the background image on the plot area using DrawArea class
		char szResource[30];		

		if (m_mapCanvas) delete m_mapCanvas;
		m_mapCanvas = new DrawArea();
		m_mapCanvas->load(m_Param.canvas.szMapFile);

		// set affine transform parameters before make clone
		//    xDest = a * xSrc + b * ySrc + c
		//	  yDest = d * xSrc + e * ySrc + f
		double sx, sy;
		double fScaleX, fScaleY;

		fScaleX = 1.0 / pCanvas->getViewPortWidth();	// = m_mapCanvas->getWidth()  / ( pCanvas->getViewPortWidth()  * m_mapCanvas->getWidth()  )
		fScaleY = 1.0 / pCanvas->getViewPortHeight();	// = m_mapCanvas->getHeight() / ( pCanvas->getViewPortHeight() * m_mapCanvas->getHeight() )

		sx = pCanvas->getViewPortLeft() * m_mapCanvas->getWidth() * fScaleX;
		sy = pCanvas->getViewPortTop() * m_mapCanvas->getHeight() * fScaleY;

		m_mapCanvas->affineTransform(fScaleX,	0,			-sx,
									 0,			fScaleY,	-sy,
									 Chart::Transparent,	1);
		m_mapCanvas->resize(pPlotArea->getWidth(), pPlotArea->getHeight(), 1/*filter*/);
		
		// update reource
		Chart::setResource(CHART_ID_MAP, m_mapCanvas);

		// update background of canvas with transformed map image
		sprintf_s(szResource, "@/%s", CHART_ID_MAP);
		pPlotArea->setBackground(szResource, Chart::TopLeft);
	} // end of if( background color == Transparent )

	//..

}

  Re: stretching image on the chart
Posted by Steven Wonly on Mar-17-2021 05:36
Attachments:
Hi Peter,

I was able to decrease cpu usage after using StretchBlt() instead of affineTransform() and resize(). The cpu usage goes down to 10%. But I got a flickering screen.

The source code is attached.
Would you please look at the source code and give me some advice?

Thank you.
Steven
DisplayChart.cpp
int			m_bgColor;		// member variable of the class - = Chart::Transparent
DrawArea*	m_mapCanvas=0;	// member variable of the class - 
CImage		m_pMap;			// member variable of the calss - initialized with image file
double		m_nSX=-1, m_nSY=-1, m_nCX=-1, m_nCY=-1;	// member variable of the class

void CTestMqttUwbDlg::DrawCanvas(CChartViewer* pCanvas)
{
	//------------------------------------------------------------------------------
	// chart type: scatter chart / all 4 quadrants in the XY plane
	// symbols   : use built-in symbols for position of each anchor and tag
	// future    : 3D scatter chart -> can display x, y, z position
	//------------------------------------------------------------------------------

	// get canvas size
	CRect	  sizeCanvas;
	PlotArea* pPlotArea;

	GetDlgItem(IDC_CANVAS)->GetWindowRect(&sizeCanvas);
	//TRACE(_T("Canvas size: %d x %d\n"), sizeCanvas.Width(), sizeCanvas.Height());

	// Create an XYChart object same as canvas size, with grey (f4f4f4) background,
	// black (000000) border, 1 pixel raised effect, and with a rounded frame.
	XYChart* chart = new XYChart(sizeCanvas.Width(), sizeCanvas.Height(), RGB_GRAY1, RGB_BLACK, 1);

	// Set the plotarea at (40, 20) and of size Width-60 x Height-110 pixels. Use white (0x404040) 
	// background. Enable both horizontal and vertical grids by setting their colors to 
	// gray (cccccc). Set clipping mode to clip the data lines to the plot area.
	pPlotArea = chart->setPlotArea(40,							// x
								   20,							// y
								   sizeCanvas.Width() - 60,		// width
								   sizeCanvas.Height() - 110,	// height
								   m_bgColor,					// background color; Transparent or RGB_LIGHTGRAY
								   -1,							// alt background color - white (0xffffffff)
								   -1,							// edge color - white (0xffffffff)
								   RGB_GRAY2,					// horizontal grid color - gray
								   RGB_GRAY2);					// vertical grid color - gray
	//TRACE(_T("plot area: %d x %d, %d, %d / %d %d\n"), pPlotArea->getLeftX(), pPlotArea->getTopY(), 
	//													pPlotArea->getWidth(), pPlotArea->getHeight(), 
	//													sizeCanvas.Width(), sizeCanvas.Height());

#if(0)
	if (m_bgColor == Chart::Transparent) {
		// set the background image on the plot area using DrawArea class
		char szResource[30];		

		// make resource with map image file only if updated zoom area
		double sx, sy, cx, cy;
		sx = pCanvas->getViewPortLeft();
		sy = pCanvas->getViewPortTop();
		cx = pCanvas->getViewPortWidth();
		cy = pCanvas->getViewPortHeight();

		if ((m_nSX != sx) || (m_nSY != sy) || (m_nCX != cx) || (m_nCY != cy)) {
			// updated zoom area
			// update resource with map image file for background of canvas
			m_nSX = sx, m_nSY = sy;
			m_nCX = cx, m_nCY = cy;

			if (m_mapCanvas) delete m_mapCanvas;
			m_mapCanvas = new DrawArea();
			m_mapCanvas->load(m_Param.canvas.szMapFile);

			// set affine transform parameters before make clone
			//    xDest = a * xSrc + b * ySrc + c
			//	  yDest = d * xSrc + e * ySrc + f
			double fScaleX, fScaleY;

			fScaleX = 1.0 / cx;	// = m_mapCanvas->getWidth()  / ( pCanvas->getViewPortWidth()  * m_mapCanvas->getWidth()  )
			fScaleY = 1.0 / cy;	// = m_mapCanvas->getHeight() / ( pCanvas->getViewPortHeight() * m_mapCanvas->getHeight() )

			sx = sx * m_mapCanvas->getWidth() * fScaleX;
			sy = sy * m_mapCanvas->getHeight() * fScaleY;

			m_mapCanvas->affineTransform(fScaleX,	0,		-sx,
										 0,			fScaleY,-sy,
										 Chart::Transparent, 1);
			// Re-sampling filter
			//		0: Box filter		using the nearest integer-coordinate pixel
			//		1: Linear Filter	using the linear weighted average of nearby integer-coordinate pixels
			//		2: Quadratic filter		8: Lanczos filter
			//		3: B-spline filter		9: Gaussian filter
			//		4: Hermite filter		10:Hanning filter
			//		5: Catrom filter		11:Hamming filter
			//		6: Mitchell filter		12:Blackman filter
			//		7: Sinc filter			13:Bessel filter
			m_mapCanvas->resize(pPlotArea->getWidth(), pPlotArea->getHeight(), 1/*filter*/);

			// update reource
			Chart::setResource(CHART_ID_MAP, m_mapCanvas);
		} // end of if( updated zoom area )

		// update background of canvas with transformed map image
		sprintf_s(szResource, "@/%s", CHART_ID_MAP);
		pPlotArea->setBackground(szResource, Chart::TopLeft);
	} // end of if( background color == Transparent )
#else
	if (m_bgColor == Chart::Transparent) {
		// put background image on IDC_CANVAS
		// source
		// zoom area:	pCanvas->getViewPortLeft(), pCanvas->getViewPortTop()
		//				pCanvas->getViewPortWidth(), pCanvas->getViewPortHeight()
		//
		// destination
		// canvas area: pPlotArea->getLeftX(), pPlotArea->getTopY()
		//				pPlotArea->getWidth(), pPlotArea->getHeight()
		double sx, sy, cx, cy;
		RECT rectSrc, rectDest;
		CDC* screenDC = GetDlgItem(IDC_CANVAS)->GetDC();

		sx = pCanvas->getViewPortLeft();
		sy = pCanvas->getViewPortTop();
		cx = pCanvas->getViewPortWidth();
		cy = pCanvas->getViewPortHeight();

		rectSrc.left = sx * m_pMap->GetWidth();
		rectSrc.top = sy * m_pMap->GetHeight();
		rectSrc.right = rectSrc.left + cx * m_pMap->GetWidth();
		rectSrc.bottom = rectSrc.top + cy * m_pMap->GetHeight();

		rectDest.left = pPlotArea->getLeftX();
		rectDest.top = pPlotArea->getTopY();
		rectDest.right = rectDest.left + pPlotArea->getWidth();
		rectDest.bottom = rectDest.top + pPlotArea->getHeight();

		//m_pMap->StretchBlt(chart->getPlotArea()->getInternalPtr(), rectDest, rectSrc, SRCCOPY);
		m_pMap->StretchBlt(screenDC->m_hDC, rectDest, rectSrc, SRCCOPY);
		ReleaseDC(screenDC);
	}
#endif

	chart->setRoundedFrame(m_bgColorOld);

	// Enable clipping mode to clip the part of the data that is outside the plot area.
	chart->setClipping();

	//
	// add a chart title
	// Add a title to the chart using 15 pts Times New Roman Bold Italic font, with a light
	// grey (dddddd) background, black (000000) border, and a glass like raised effect.
	//chart->addTitle("Pester", "timesbi.ttf", 15)->setBackground(0xdddddd, 0x000000, Chart::glassEffect());
	
	//
	// add a legent box
    // Add a legend box at the top of the plot area with 9pts Arial Bold font. We set the 
    // legend box to the same width as the plot area and use grid layout (as opposed to 
    // flow or top/down layout). This distributes the 3 legend icons evenly on top of the 
    // plot area.
#if(0)
	LegendBox *legend = chart->addLegend2(60, sizeCanvas.Height()-35, 1/*numOfColumn*/, "arialbd.ttf", 9);
	legend->setBackground(Chart::Transparent, Chart::Transparent);
	legend->setWidth(sizeCanvas.Width()-60);
#else
	chart->addLegend(20, sizeCanvas.Height() - 60, false, "timesbi.ttf", 10);
#endif

	// add a title to the X/Y axis
	chart->xAxis()->setTitle("mm", "arialbi.ttf", 7)->setAlignment(Chart::Right);
	chart->yAxis()->setTitle("mm", "arialbi.ttf", 7)->setAlignment(Chart::Top);

	// Set 4 quadrant mode, with both x and y axes symetrical around the origin
	//chart->setAxisAtOrigin(Chart::XYAxisAtOrigin, Chart::XAxisSymmetric + Chart::YAxisSymmetric);

	// set range of X/Y axis --> use X/Y scales that defined in Zoom Canvas
	// ie. turn off auto-scale
	// and set tick mark every 1000mm on a chart
	//chart->xAxis()->setLinearScale(MIN_CHART_X, MAX_CHART_X, CHART_TICK_SIZE);	//
	//chart->yAxis()->setLinearScale(MIN_CHART_Y, MAX_CHART_Y, CHART_TICK_SIZE);	//
	chart->xAxis()->setLinearScale(m_pView->m_nRangeXMin, m_pView->m_nRangeXMax, CHART_TICK_SIZE);	//
	chart->yAxis()->setLinearScale(m_pView->m_nRangeYMin, m_pView->m_nRangeYMax, CHART_TICK_SIZE);	//

	//---------------------------------------------------------------
	// put in data (ie. position of UWB devices) on the canvas (chart)
	AddData2Canvas(chart, m_pView->m_nMarkSize*2);

	//
	// In this example, we have not explicitly configured the full x and y range. In this case, 
	// the first time syncLinearAxisWithViewPort is called, ChartDirector will auto-scale the axis
	// and assume the resulting range is the full range. In subsequent calls, ChartDirector will 
	// set the axis range based on the view port and the full range.
	//
	// syncLinearAxisWithViewPort()
	// If the data scale has already been defined, this method will compute the visible data scale based on the
	// viewport coordinates using date/time interpolation, and then sets the axis to that date/time scale.
	pCanvas->syncLinearAxisWithViewPort("x", chart->xAxis());
	pCanvas->syncLinearAxisWithViewPort("y", chart->yAxis());

	// We need to update the track line too. If the mouse is moving on the chart (eg. if 
	// the user drags the mouse on the chart to scroll it), the track line will be updated
	// in the MouseMovePlotArea event. Otherwise, we need to update the track line here.
	if ((!pCanvas->isInMouseMoveEvent()) && pCanvas->isMouseOnPlotArea())
		DrawCrossHairCursor(chart, pCanvas->getPlotAreaMouseX(), pCanvas->getPlotAreaMouseY());

    // Set the chart image to the WinChartViewer
	delete pCanvas->getChart();
	pCanvas->setChart(chart);
}

  Re: stretching image on the chart
Posted by Peter Kwan on Mar-17-2021 17:37
Hi Steven,

In your code, you StretchBlt to the screen. That's why the window flicker.

The CChartViewer is designed with double buffering, so it does not flicker by itself. (Some other MFC controls, such as the CTreeCtrl can flicker even by itself.) However, it cannot stop your code from StretchBlt to the screen and overwriting the chart on the screen. Although your code then draws the chart, there is a delay and this causes the flickering. Some GUI frameworks can double-buffer the screen itself to avoid this type of flickering, but MFC does not do this.

For your case, due to the large image size, the affine transform plus resize I suggest may not be the most efficient method. It loads the jpg image every time. Then it applies affine transform to the large image where only a small part is necessary, and then resize it (the resizing is actually another affine transform) to the plot area size. If you think the CPU usage is too big, the followings are two alternatives:

- In your CImage code, instead of StretchBlt to the screen, you can StretchBlt to another CImage and save it as a BMP image in memory. You can then pass the BMP image to ChartDirector as a resource and use it for the plot area background.

or

- You can load the image once into a DrawArea. When you need the background image, you can create another DrawArea, then use DrawArea.merge to copy the rectSrc part of the first DrawArea to the second DrawArea. (The second DrawArea should be set to the size of rectSrc first using DrawArea.setSize.) You can then resize the second rectangle to the plot area size and use it as a resource for the background. With this method, the image is only loaded once (the first DrawArea is not deleted until program ends), and there is only one resize.

If your chart can update without zooming and scrolling (such as it is realtime chart), you can cache the second DrawArea and reuse it if the viewport does not changed.

Regards
Peter Kwan

  Re: stretching image on the chart
Posted by Steven Wonly on Mar-18-2021 03:14
Hi Peter,

Thank you for your help. I can use StretchBlt() without flicker and decrease cpu usage. But background image was not smooth while using StretchBlt().

I tested second option that you gave me before. It is faster than using load() function but still slower than StretchBlt(). I can get better image rather than using StretchBlt(). I think, it make sense to me.

So, let me ask you one more question.
Can I use affineTransform() between CImage object and MemBlock object? If possible, I want to use affineTransform() instead of StretchBlt().

Thank you.
Steven

  Re: stretching image on the chart
Posted by Peter Kwan on Mar-18-2021 11:33
Hi Steven,

There are many methods to resize a chart. They differ on how they handle fractional pixels. For example, if a pixel at (10, 10) (a pixel is a square), after resizing and scrolling, becomes the sqaure region with opposite corners at (18.15, 44.73) tp (21.74, 48.05), how should this be handled? The code can just round the coordinates to integers and copy the pixel, or it can use an algorithm to compute the effect of the fractional coverage. Copying an integer is much much faster than the floating point computation algorithm. That explains the speed and quality difference between StretchBlt and the DrawArea affineTransform or resize.

For the second option, you should only need one "resize", and no "affineTransform". "resize" is an optimized version of affineTransform and can be faster than affineTransform and with the same quality.

The first option I mentioned is effective performing affineTransform between CImage and produce a DrawArea object (which can be used to output MemBlock if you like). I estimate the majority of the overhead is in the affineTransform, it may or may not be faster than the second option, which only uses "resize".

In more detail, the first option is:

(a) Copy the rectSrc of the CImage to another CImage at 1:1 ratio (may use BitBlt). For example, if the rectSrc is from (1000, 1000) to (1300, 1300), the new CImage should be 200 x 200 in size. This step reduces the size of the BMP for further processing. There is no distortion or loss of quality for 1:1 copying, since there is no fractional pixel.

(b) Save the second CImage as a BMP image in memory and then load the BMP image to a new DrawArea object. This transfers the image to the DrawArea.

(c) Resize the DrawArea to the same size as the plot area. As mentioned above, "resize" is an optimized version of affineTransform and has the same quality.

The (a) and (b) above should be fast. The (c) is an optimized affineTransform. It will not be as fast as StrectBlt but provides better quality.

Regards
Peter Kwan

  Re: stretching image on the chart
Posted by Steven Wonly on Mar-22-2021 21:23
Hi Peter,

Thank you so much.

Best regards,
Steven