[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/gnuplot/gnuplot/5.5/src/win/wgdiplus.cpp [Back]  [Original]

/*
Copyright (c) 2011-2017 Bastian Maerkisch. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// include iostream / cstdio _before_ syscfg.h in order
// to avoid re-definition by wtext.h/winmain.c routines
#include 
#include 
extern "C" {
# include "syscfg.h"
}
#include 
#include 
#define GDIPVER 0x0110
#include 
#include 
#include 
#ifdef __WATCOMC__
// swprintf_s is missing from 
# define swprintf_s(s, c, f, ...) swprintf(s, c, f, __VA_ARGS__)
#endif

#include "wgdiplus.h"
#include "wgnuplib.h"
#include "winmain.h"
#include "wcommon.h"
using namespace Gdiplus;
// do not use namespace std: otherwise MSVC complains about
// ambiguous symbol bool
//using namespace std;

static bool gdiplusInitialized = false;
static ULONG_PTR gdiplusToken;

#define MINMAX(a,val,b) (((val) SetDashPattern(dashstyles[style - 1], dashstyle_len[style - 1]);
}


/* ****************  GDI+ only functions ********************************** */


static void
gdiplusPolyline(Graphics &graphics, Pen &pen, Point *points, int polyi)
{
	// Dash patterns get scaled with line width, in contrast to GDI.
	// Avoid smearing out caused by antialiasing for small line widths.
	SmoothingMode mode = graphics.GetSmoothingMode();

	if ((points[0].X != points[polyi - 1].X) ||
		(points[0].Y != points[polyi - 1].Y))
		graphics.DrawLines(&pen, points, polyi);
	else
		graphics.DrawPolygon(&pen, points, polyi - 1);

	/* restore */
	if (mode != SmoothingModeNone)
		graphics.SetSmoothingMode(mode);
}


static void
gdiplusPolyline(Graphics &graphics, Pen &pen, PointF *points, int polyi)
{
	// Dash patterns get scaled with line width, in contrast to GDI.
	// Avoid smearing out caused by antialiasing for small line widths.
	SmoothingMode mode = graphics.GetSmoothingMode();

	bool all_vert_or_horz = true;
	for (int i = 1; i < polyi; i++)
		if (!((points[i - 1].X == points[i].X) ||
		      (points[i - 1].Y == points[i].Y)))
			all_vert_or_horz = false;

	// if all lines are horizontal or vertical we snap to nearest pixel
	// to avoid "blurry" lines
	if (all_vert_or_horz) {
		for (int i = 0; i < polyi; i++) {
			points[i].X = INT(points[i].X + 0.5);
			points[i].Y = INT(points[i].Y + 0.5);
		}
	}

	if ((points[0].X != points[polyi - 1].X) ||
	    (points[0].Y != points[polyi - 1].Y))
		graphics.DrawLines(&pen, points, polyi);
	else
		graphics.DrawPolygon(&pen, points, polyi - 1);

	/* restore */
	if (mode != SmoothingModeNone)
		graphics.SetSmoothingMode(mode);
}


Brush *
gdiplusPatternBrush(int style, COLORREF color, double alpha, COLORREF backcolor, BOOL transparent)
{
	Color gdipColor = gdiplusCreateColor(color, alpha);
	Color gdipBackColor = gdiplusCreateColor(backcolor, transparent ? 0 : 1.);
	Brush * brush;
	style %= pattern_num;
	const HatchStyle styles[] = { HatchStyleTotal, HatchStyleDiagonalCross,
		HatchStyleZigZag, HatchStyleTotal,
		HatchStyleForwardDiagonal, HatchStyleBackwardDiagonal,
		HatchStyleLightDownwardDiagonal, HatchStyleDarkUpwardDiagonal };
	switch (style) {
		case 0:
			brush = new SolidBrush(gdipBackColor);
			break;
		case 3:
			brush = new SolidBrush(gdipColor);
			break;
		default:
			brush = new HatchBrush(styles[style], gdipColor, gdipBackColor);
	}
	return brush;
}


static void
gdiplusDot(Graphics &graphics, Brush &brush, int x, int y)
{
	/* no antialiasing in order to avoid blurred pixel */
	SmoothingMode mode = graphics.GetSmoothingMode();
	graphics.SetSmoothingMode(SmoothingModeNone);
	graphics.FillRectangle(&brush, x, y, 1, 1);
	graphics.SetSmoothingMode(mode);
}


static Font *
SetFont_gdiplus(Graphics &graphics, LPRECT rect, LPGW lpgw, LPTSTR fontname, int size)
{
	if ((fontname == NULL) || (*fontname == 0))
		fontname = lpgw->deffontname;
	if (size == 0)
		size = lpgw->deffontsize;

	/* make a local copy */
	fontname = _tcsdup(fontname);

	/* save current font */
	_tcscpy(lpgw->fontname, fontname);
	lpgw->fontsize = size;

	// apply fontscale
	size *= lpgw->fontscale;

	/* set up font style */
	INT fontStyle = FontStyleRegular;
	LPTSTR italic, bold, underline, strikeout;
	if ((italic = _tcsstr(fontname, TEXT(" Italic"))) != NULL)
		fontStyle |= FontStyleItalic;
	else if ((italic = _tcsstr(fontname, TEXT(":Italic"))) != NULL)
		fontStyle |= FontStyleItalic;
	if ((bold = _tcsstr(fontname, TEXT(" Bold"))) != NULL)
		fontStyle |= FontStyleBold;
	else if ((bold = _tcsstr(fontname, TEXT(":Bold"))) != NULL)
		fontStyle |= FontStyleBold;
	if ((underline = _tcsstr(fontname, TEXT(" Underline"))) != NULL)
		fontStyle |= FontStyleUnderline;
	if ((strikeout = _tcsstr(fontname, TEXT(" Strikeout"))) != NULL)
		fontStyle |= FontStyleStrikeout;
	if (italic) *italic = 0;
	if (bold) *bold = 0;
	if (underline) *underline = 0;
	if (strikeout) *strikeout = 0;

#ifdef UNICODE
	const FontFamily * fontFamily = new FontFamily(fontname);
#else
	LPWSTR family = UnicodeText(fontname, lpgw->encoding);
	const FontFamily * fontFamily = new FontFamily(family);
	free(family);
#endif
	free(fontname);
	Font * font;
	int fontHeight;
	bool deleteFontFamily = true;
	if (fontFamily->GetLastStatus() != Ok) {
		delete fontFamily;
#if (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR))
		// MinGW 4.8.1 does not have this
		fontFamily = FontFamily::GenericSansSerif();
		deleteFontFamily = false;
#else
#ifdef UNICODE
		fontFamily = new FontFamily(GraphDefaultFont());
#else
		family = UnicodeText(GraphDefaultFont(), S_ENC_DEFAULT); // should always be available
		fontFamily = new FontFamily(family);
		free(family);
#endif
#endif
	}
	font = new Font(fontFamily, size, fontStyle, UnitPoint);
	double scale = font->GetSize() / fontFamily->GetEmHeight(fontStyle) * graphics.GetDpiY() / 72.;
	/* store text metrics for later use */
	lpgw->tmHeight = fontHeight = scale * (fontFamily->GetCellAscent(fontStyle) + fontFamily->GetCellDescent(fontStyle));
	lpgw->tmAscent = scale * fontFamily->GetCellAscent(fontStyle);
	lpgw->tmDescent = scale * fontFamily->GetCellDescent(fontStyle);
	if (deleteFontFamily)
		delete fontFamily;

	RectF box;
	graphics.MeasureString(L"0123456789", -1, font, PointF(0, 0), StringFormat::GenericTypographic(), &box);
	lpgw->vchar = MulDiv(fontHeight, lpgw->ymax, rect->bottom - rect->top);
	lpgw->hchar = MulDiv(box.Width, lpgw->xmax, 10 * (rect->right - rect->left));
	lpgw->htic = MulDiv(lpgw->hchar, 2, 5);
	unsigned cy = MulDiv(box.Width, 2 * graphics.GetDpiY(), 50 * graphics.GetDpiX());
	lpgw->vtic = MulDiv(cy, lpgw->ymax, rect->bottom - rect->top);

	// Can always rotate text.
	lpgw->rotate = TRUE;

	return font;
}


void
InitFont_gdiplus(LPGW lpgw, HDC hdc, LPRECT rect)
{
	gdiplusInit();
	Graphics graphics(hdc);
	// call for the side effects:  set vchar/hchar and text metrics
	Font * font = SetFont_gdiplus(graphics, rect, lpgw, lpgw->fontname, lpgw->fontsize);
	// TODO:  save font object for later use
	delete font;
}


static void
EnhancedSetFont()
{
	if ((enhstate.lpgw->fontsize != enhstate.fontsize) ||
	    (_tcscmp(enhstate.lpgw->fontname, enhstate.fontname) != 0)) {
		if (enhstate_gdiplus.font)
			delete enhstate_gdiplus.font;
		enhstate_gdiplus.font = SetFont_gdiplus(*enhstate_gdiplus.graphics, enhstate.rect, enhstate.lpgw, enhstate.fontname, enhstate.fontsize);
	}
}


static unsigned
EnhancedTextLength(char * text)
{
	LPWSTR textw = UnicodeTextWithEscapes(enhanced_text, enhstate.lpgw->encoding);
	RectF box;
	enhstate_gdiplus.graphics->MeasureString(textw, -1, enhstate_gdiplus.font, PointF(0, 0), enhstate_gdiplus.stringformat, &box);
	free(textw);
	return ceil(box.Width);
}


static void
EnhancedPutText(int x, int y, char * text)
{
	LPWSTR textw = UnicodeTextWithEscapes(text, enhstate.lpgw->encoding);
	Graphics *g = enhstate_gdiplus.graphics;
	if (enhstate.lpgw->angle == 0) {
		PointF pointF(x, y + enhstate.lpgw->tmDescent);
		g->DrawString(textw, -1, enhstate_gdiplus.font, pointF, enhstate_gdiplus.stringformat, enhstate_gdiplus.brush);
	} else {
		/* shift rotated text correctly */
		g->TranslateTransform(x, y);
		g->RotateTransform(-enhstate.lpgw->angle);
		g->DrawString(textw, -1, enhstate_gdiplus.font, PointF(0, enhstate.lpgw->tmDescent), enhstate_gdiplus.stringformat, enhstate_gdiplus.brush);
		g->ResetTransform();
	}
	free(textw);
}


static void
EnhancedCleanup()
{
	delete enhstate_gdiplus.font;
	delete enhstate_gdiplus.stringformat;
}


static void
draw_enhanced_init(LPGW lpgw, Graphics &graphics, SolidBrush &brush, LPRECT rect)
{
	enhstate.set_font = &EnhancedSetFont;
	enhstate.text_length = &EnhancedTextLength;
	enhstate.put_text = &EnhancedPutText;
	enhstate.cleanup = &EnhancedCleanup;

	enhstate_gdiplus.graphics = &graphics;
	enhstate_gdiplus.font = SetFont_gdiplus(graphics, rect, lpgw, lpgw->fontname, lpgw->fontsize);
	enhstate_gdiplus.brush = &brush;
	enhstate.res_scale = graphics.GetDpiY() / 96.;

	enhstate_gdiplus.stringformat = new StringFormat(StringFormat::GenericTypographic());
	enhstate_gdiplus.stringformat->SetAlignment(StringAlignmentNear);
	enhstate_gdiplus.stringformat->SetLineAlignment(StringAlignmentFar);
	INT flags = enhstate_gdiplus.stringformat->GetFormatFlags();
	flags |= StringFormatFlagsMeasureTrailingSpaces;
	enhstate_gdiplus.stringformat->SetFormatFlags(flags);
}


void
drawgraph_gdiplus(LPGW lpgw, HDC hdc, LPRECT rect)
{
	gdiplusInit();
	Graphics graphics(hdc);
	do_draw_gdiplus(lpgw, graphics, rect, DRAW_SCREEN);
}


void
metafile_gdiplus(LPGW lpgw, HDC hdc, LPRECT lprect, LPWSTR name)
{
	gdiplusInit();
	Rect rect(lprect->left, lprect->top, lprect->right - lprect->left, lprect->bottom - lprect->top);
	Metafile metafile(name, hdc, rect, MetafileFrameUnitPixel, EmfTypeEmfPlusDual, NULL);
	Graphics graphics(&metafile);
	do_draw_gdiplus(lpgw, graphics, lprect, DRAW_METAFILE);
}


HENHMETAFILE
clipboard_gdiplus(LPGW lpgw, HDC hdc, LPRECT lprect)
{
	gdiplusInit();
	Rect rect(lprect->left, lprect->top, lprect->right - lprect->left, lprect->bottom - lprect->top);
	Metafile metafile(hdc, rect, MetafileFrameUnitPixel, EmfTypeEmfPlusDual, NULL);
	// Note: We can only get a valid handle once the graphics object has released
	// the metafile. Creating the graphics object on the heap seems the only way to
	// achieve that.
	Graphics * graphics = Graphics::FromImage(&metafile);
	do_draw_gdiplus(lpgw, *graphics, lprect, DRAW_METAFILE);
	delete graphics;
	return metafile.GetHENHMETAFILE();
}


void
print_gdiplus(LPGW lpgw, HDC hdc, HANDLE printer, LPRECT rect)
{
	gdiplusInit();

	// temporarily turn of antialiasing
	BOOL aa = lpgw->antialiasing;
	lpgw->antialiasing = FALSE;

	Graphics graphics(hdc, printer);
	graphics.SetPageUnit(UnitPixel);
	do_draw_gdiplus(lpgw, graphics, rect, DRAW_PRINTER);

	// restore settings
	lpgw->antialiasing = aa;
}


static void
do_draw_gdiplus(LPGW lpgw, Graphics &graphics, LPRECT rect, enum draw_target target)
{
	/* draw ops */
	unsigned int ngwop = 0;
	struct GWOP *curptr;
	struct GWOPBLK *blkptr;

	/* layers and hypertext */
	unsigned plotno = 0;
	bool gridline = false;
	bool skipplot = false;
	bool keysample = false;
	bool interactive;
	LPWSTR hypertext = NULL;
	int hypertype = 0;

	/* colors */
	bool isColor = true;		/* use colors? */
	COLORREF last_color = 0;	/* currently selected color */
	double alpha_c = 1.;		/* alpha for transparency */

	/* text */
	Font * font;

	/* lines */
	double line_width = lpgw->linewidth;	/* current line width */
	double lw_scale = 1.;
	LOGPEN cur_penstruct;		/* current pen settings */

	/* polylines and polygons */
	int polymax = 200;			/* size of ppt */
	int polyi = 0;				/* number of points in ppt */
	std::vector ppt(polymax);	/* storage of polyline/polygon-points */
	int last_polyi = 0;			/* number of points in last_poly */
	std::vector last_poly(0);	/* storage of last filled polygon */
	unsigned int lastop = -1;	/* used for plotting last point on a line */

	/* filled polygons and boxes */
	int last_fillstyle = -1;
	COLORREF last_fillcolor = 0;
	double last_fill_alpha = 1.;
	bool transparent = false;	/* transparent fill? */
	Brush * pattern_brush = NULL;
	Brush * fill_brush = NULL;
	Bitmap * poly_bitmap = NULL;
	Graphics * poly_graphics = NULL;
	float poly_scale = 2.f;

	/* images */
	POINT corners[4];			/* image corners */
	int color_mode = 0;			/* image color mode */

	struct s_boxedtext {
		TBOOLEAN boxing;
		t_textbox_options option;
		POINT margin;
		POINT start;
		RECT  box;
		int   angle;
	} boxedtext;

	/* point symbols */
	enum win_pointtypes last_symbol = W_invalid_pointtype;
	CachedBitmap *cb = NULL;
	POINT cb_ofs;
	bool ps_caching = false;

	/* coordinates and lengths */
	float xdash, ydash;			/* the transformed coordinates */
	int rr, rl, rt, rb;			/* coordinates of drawing area */
	int htic, vtic;				/* tic sizes */
	float hshift, vshift;			/* correction of text position */

	/* indices */
	int seq = 0;				/* sequence counter for W_image and W_boxedtext */

	if (lpgw->locked) return;

	/* clear hypertexts only in display sessions */
	interactive = (target == DRAW_SCREEN);
	if (interactive)
		clear_tooltips(lpgw);

	/* Need to scale line widths for raster printers so they are the same
	   as on screen */
	if (target == DRAW_PRINTER) {
		HDC hdc = graphics.GetHDC();
		HDC hdc_screen = GetDC(NULL);
		lw_scale = (double) GetDeviceCaps(hdc, LOGPIXELSX) /
		           (double) GetDeviceCaps(hdc_screen, LOGPIXELSY);
		line_width *= lw_scale;
		ReleaseDC(NULL, hdc_screen);
		graphics.ReleaseHDC(hdc);
	}

	// only cache point symbols when drawing to a screen
	ps_caching = (target == DRAW_SCREEN);

	rr = rect->right;
	rl = rect->left;
	rt = rect->top;
	rb = rect->bottom;

	if (lpgw->antialiasing) {
		graphics.SetSmoothingMode(SmoothingModeAntiAlias8x8);
		graphics.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
	}
	graphics.SetInterpolationMode(InterpolationModeNearestNeighbor);

	/* background fill */
	if (isColor)
		graphics.Clear(gdiplusCreateColor(lpgw->background, 1.));
	else
		graphics.Clear(Color(255, 255, 255));

	/* Init brush and pens: need to be created after the Graphics object. */
	SolidBrush solid_brush(gdiplusCreateColor(lpgw->background, 1.));
	SolidBrush solid_fill_brush(gdiplusCreateColor(lpgw->background, 1.));
	cur_penstruct = (lpgw->color && isColor) ?  lpgw->colorpen[2] : lpgw->monopen[2];
	last_color = cur_penstruct.lopnColor;

	Pen pen(Color(0, 0, 0));
	Pen solid_pen(Color(0, 0, 0));
	pen.SetColor(gdiplusCreateColor(last_color, 1.));
	pen.SetLineCap(lpgw->rounded ? LineCapRound : LineCapSquare,
					lpgw->rounded ? LineCapRound : LineCapSquare,
					DashCapFlat);
	pen.SetLineJoin(lpgw->rounded ? LineJoinRound : LineJoinMiter);
	solid_pen.SetColor(gdiplusCreateColor(last_color, 1.));
	solid_pen.SetLineCap(lpgw->rounded ? LineCapRound : LineCapSquare,
					lpgw->rounded ? LineCapRound : LineCapSquare,
					DashCapFlat);
	solid_pen.SetLineJoin(lpgw->rounded ? LineJoinRound : LineJoinMiter);

	htic = (lpgw->org_pointsize * MulDiv(lpgw->htic, rr - rl, lpgw->xmax) + 1);
	vtic = (lpgw->org_pointsize * MulDiv(lpgw->vtic, rb - rt, lpgw->ymax) + 1);

	lpgw->angle = 0;
	lpgw->justify = LEFT;
	StringFormat stringFormat(StringFormat::GenericTypographic());
	stringFormat.SetAlignment(StringAlignmentNear);
	stringFormat.SetLineAlignment(StringAlignmentNear);
	INT flags = stringFormat.GetFormatFlags();
	flags |= StringFormatFlagsMeasureTrailingSpaces;
	stringFormat.SetFormatFlags(flags);
	font = SetFont_gdiplus(graphics, rect, lpgw, NULL, 0);

	/* calculate text shifting for horizontal text */
	hshift = 0.0;
	vshift = - lpgw->tmHeight / 2;

	/* init layer variables */
	lpgw->numplots = 0;
	lpgw->hasgrid = FALSE;
	for (unsigned i = 0; i < lpgw->maxkeyboxes; i++) {
		lpgw->keyboxes[i].left = INT_MAX;
		lpgw->keyboxes[i].right = 0;
		lpgw->keyboxes[i].bottom = INT_MAX;
		lpgw->keyboxes[i].top = 0;
	}

	boxedtext.boxing = FALSE;

	/* do the drawing */
	blkptr = lpgw->gwopblk_head;
	curptr = NULL;
	if (blkptr != NULL)
		curptr = blkptr->gwop;
	if (curptr == NULL)
		return;

	while (ngwop < lpgw->nGWOP) {
		// transform the coordinates
		if (lpgw->oversample) {
			xdash = float(curptr->x) * (rr - rl - 1) / float(lpgw->xmax) + rl;
			ydash = float(rb) - float(curptr->y) * (rb - rt - 1) / float(lpgw->ymax) + rt - 1;
		} else {
			xdash = MulDiv(curptr->x, rr - rl - 1, lpgw->xmax) + rl;
			ydash = rb - MulDiv(curptr->y, rb - rt - 1, lpgw->ymax) + rt - 1;
		}

		/* finish last filled polygon */
		if ((!last_poly.empty()) &&
			(((lastop == W_filled_polygon_draw) && (curptr->op != W_fillstyle)) ||
			 ((curptr->op == W_fillstyle) && (curptr->x != unsigned(last_fillstyle))))) {
			if (poly_graphics == NULL) {
				// changing smoothing mode is necessary in case of new/unknown code paths
				SmoothingMode mode = graphics.GetSmoothingMode();
				if (lpgw->antialiasing && !lpgw->polyaa)
					graphics.SetSmoothingMode(SmoothingModeNone);
				graphics.FillPolygon(fill_brush, last_poly.data(), last_polyi);
				graphics.SetSmoothingMode(mode);
			} else {
				poly_graphics->FillPolygon(fill_brush, last_poly.data(), last_polyi);
			}
			last_polyi = 0;
			last_poly.clear();
		}

		/* handle layer commands first */
		if (curptr->op == W_layer) {
			t_termlayer layer = (t_termlayer) curptr->x;
			switch (layer) {
				case TERM_LAYER_BEFORE_PLOT:
					plotno++;
					lpgw->numplots = plotno;
					if (plotno >= lpgw->maxhideplots) {
						unsigned int idx;
						lpgw->maxhideplots += 10;
						lpgw->hideplot = (BOOL *) realloc(lpgw->hideplot, lpgw->maxhideplots * sizeof(BOOL));
						for (idx = plotno; idx < lpgw->maxhideplots; idx++)
							lpgw->hideplot[idx] = FALSE;
					}
					if (plotno maxhideplots)
						skipplot = (lpgw->hideplot[plotno - 1] > 0);
					break;
				case TERM_LAYER_AFTER_PLOT:
					skipplot = false;
					break;
#if 0
				case TERM_LAYER_BACKTEXT:
				case TERM_LAYER_FRONTTEXT
				case TERM_LAYER_END_TEXT:
					break;
#endif
				case TERM_LAYER_BEGIN_GRID:
					gridline = true;
					lpgw->hasgrid = TRUE;
					break;
				case TERM_LAYER_END_GRID:
					gridline = false;
					break;
				case TERM_LAYER_BEGIN_KEYSAMPLE:
					keysample = true;
					break;
				case TERM_LAYER_END_KEYSAMPLE:
					/* grey out keysample if graph is hidden */
					if ((plotno maxhideplots) && lpgw->hideplot[plotno - 1]) {
						ARGB argb = Color::MakeARGB(128, 192, 192, 192);
						Color transparentgrey(argb);
						SolidBrush greybrush(transparentgrey);
						LPRECT bb = lpgw->keyboxes + plotno - 1;
						graphics.FillRectangle(&greybrush, INT(bb->left), INT(bb->bottom),
						                       bb->right - bb->left, bb->top - bb->bottom);
					}
					keysample = false;
					break;
				case TERM_LAYER_RESET:
				case TERM_LAYER_RESET_PLOTNO:
					plotno = 0;
					break;
				case TERM_LAYER_BEGIN_PM3D_MAP:
				case TERM_LAYER_BEGIN_PM3D_FLUSH:
					// Antialiasing of pm3d polygons is obtained by drawing to a
					// bitmap four times as large and copying it back with interpolation
					if (lpgw->antialiasing && lpgw->polyaa) {
						poly_bitmap = new Bitmap(poly_scale * (rr - rl), poly_scale * (rb - rt), &graphics);
						poly_graphics = Graphics::FromImage(poly_bitmap);
						poly_graphics->SetSmoothingMode(SmoothingModeNone);
						Matrix transform(poly_scale, 0.0f, 0.0f, poly_scale, 0.0f, 0.0f);
						poly_graphics->SetTransform(&transform);
					}
					break;
				case TERM_LAYER_END_PM3D_MAP:
				case TERM_LAYER_END_PM3D_FLUSH:
					if (poly_graphics != NULL) {
						delete poly_graphics;
						poly_graphics = NULL;
						graphics.SetInterpolationMode(InterpolationModeHighQualityBilinear);
						graphics.SetPixelOffsetMode(PixelOffsetModeHighQuality);
						graphics.DrawImage(poly_bitmap, 0, 0, rr - rl, rb - rt);
						graphics.SetInterpolationMode(InterpolationModeNearestNeighbor);
						graphics.SetPixelOffsetMode(PixelOffsetModeNone);
						delete poly_bitmap;
					}
					break;
				case TERM_LAYER_BEGIN_IMAGE:
				case TERM_LAYER_BEGIN_COLORBOX:
					// Turn of antialiasing for failsafe/pixel images and color boxes
					// to avoid seams.
					if (lpgw->antialiasing)
						graphics.SetSmoothingMode(SmoothingModeNone);
					break;
				case TERM_LAYER_END_IMAGE:
				case TERM_LAYER_END_COLORBOX:
					if (lpgw->antialiasing)
						graphics.SetSmoothingMode(SmoothingModeAntiAlias8x8);
					break;
				default:
					break;
			};
		}

		/* Hide this layer? Do not skip commands which could affect key samples: */
		if (!(skipplot || (gridline && lpgw->hidegrid)) ||
			keysample || (curptr->op == W_line_type) || (curptr->op == W_setcolor)
			          || (curptr->op == W_pointsize) || (curptr->op == W_line_width)
			          || (curptr->op == W_dash_type) || (curptr->op == W_setcolorrgb)) {

		/* special case hypertexts */
		if ((hypertext != NULL) && (hypertype == TERM_HYPERTEXT_TOOLTIP)) {
			/* point symbols */
			if ((curptr->op >= W_dot) && (curptr->op op) {
		case 0:	/* have run past last in this block */
			break;

		case W_layer: /* already handled above */
			break;

		case W_polyline: {
			POINTL * poly = (POINTL *) curptr->pdata;
			if (poly == NULL) break; // memory allocation failed
			polyi = curptr->x;
			PointF * points = new PointF[polyi];
			for (int i = 0; i < polyi; i++) {
				// transform the coordinates
				if (lpgw->oversample) {
					points[i].X = float(poly[i].x) * (rr - rl - 1) / float(lpgw->xmax) + rl;
					points[i].Y = float(rb) - float(poly[i].y) * (rb - rt - 1) / float(lpgw->ymax) + rt - 1;
				} else {
					points[i].X = MulDiv(poly[i].x, rr - rl - 1, lpgw->xmax) + rl;
					points[i].Y = rb - MulDiv(poly[i].y, rb - rt - 1, lpgw->ymax) + rt - 1;
				}
			}
			if (poly_graphics == NULL)
				gdiplusPolyline(graphics, pen, points, polyi);
			else
				gdiplusPolyline(*poly_graphics, pen, points, polyi);
			if (keysample) {
				draw_update_keybox(lpgw, plotno, points[0].X, points[0].Y);
				draw_update_keybox(lpgw, plotno, points[polyi - 1].X, points[polyi - 1].Y);
			}
			delete [] points;
			break;
		}

		case W_line_type: {
			int cur_pen = (int)curptr->x % WGNUMPENS;

			/* create new pens */
			if (cur_pen > LT_NODRAW) {
				cur_pen += 2;
				cur_penstruct.lopnWidth =
					(lpgw->color && isColor) ? lpgw->colorpen[cur_pen].lopnWidth : lpgw->monopen[cur_pen].lopnWidth;
				cur_penstruct.lopnColor = lpgw->colorpen[cur_pen].lopnColor;
				if (!lpgw->color || !isColor) {
					COLORREF color = cur_penstruct.lopnColor;
					unsigned luma = luma_from_color(GetRValue(color), GetGValue(color), GetBValue(color));
					cur_penstruct.lopnColor = RGB(luma, luma, luma);
				}
				cur_penstruct.lopnStyle =
					lpgw->dashed ? lpgw->monopen[cur_pen].lopnStyle : lpgw->colorpen[cur_pen].lopnStyle;
			} else if (cur_pen == LT_NODRAW) {
				cur_pen = WGNUMPENS;
				cur_penstruct.lopnStyle = PS_NULL;
				cur_penstruct.lopnColor = 0;
				cur_penstruct.lopnWidth.x = 1;
			} else { /* background;
				cur_penstruct.lopnWidth.x = 1;
			}
			cur_penstruct.lopnWidth.x *= line_width;

			Color color = gdiplusCreateColor(cur_penstruct.lopnColor, 1.);
			solid_brush.SetColor(color);

			solid_pen.SetColor(color);
			solid_pen.SetWidth(cur_penstruct.lopnWidth.x);

			pen.SetColor(color);
			pen.SetWidth(cur_penstruct.lopnWidth.x);
			if (cur_penstruct.lopnStyle x);

			if (dt >= 0) {
				dt %= WGNUMPENS;
				dt += 2;
				cur_penstruct.lopnStyle = lpgw->monopen[dt].lopnStyle;
				gdiplusSetDashStyle(&pen, static_cast(cur_penstruct.lopnStyle));
			} else if (dt == DASHTYPE_SOLID) {
				cur_penstruct.lopnStyle = PS_SOLID;
				gdiplusSetDashStyle(&pen, static_cast(cur_penstruct.lopnStyle));
			} else if (dt == DASHTYPE_AXIS) {
				dt = 1;
				cur_penstruct.lopnStyle =
					lpgw->dashed ? lpgw->monopen[dt].lopnStyle : lpgw->colorpen[dt].lopnStyle;
				gdiplusSetDashStyle(&pen, static_cast(cur_penstruct.lopnStyle));
			} else if (dt == DASHTYPE_CUSTOM) {
				t_dashtype * dash = static_cast(curptr->pdata);
				if (dash == NULL) break; // memory allocation failed
				INT count = 0;
				while ((dash->pattern[count] != 0.) && (count < DASHPATTERN_LENGTH)) count++;
				pen.SetDashPattern(dash->pattern, count);
			}
			break;
		}

		case W_text_encoding:
			lpgw->encoding = (set_encoding_id) curptr->x;
			break;

		case W_put_text: {
			char * str;
			str = (char *) curptr->pdata;
			if (str) {
				LPWSTR textw = UnicodeText(str, lpgw->encoding);
				if (textw) {
					if (lpgw->angle == 0) {
						PointF pointF(xdash + hshift, ydash + vshift);
						graphics.DrawString(textw, -1, font, pointF, &stringFormat, &solid_brush);
					} else {
						/* shift rotated text correctly */
						graphics.TranslateTransform(xdash + hshift, ydash + vshift);
						graphics.RotateTransform(-lpgw->angle);
						graphics.DrawString(textw, -1, font, PointF(0,0), &stringFormat, &solid_brush);
						graphics.ResetTransform();
					}
					RectF size;
					int dxl, dxr;
					if (keysample || boxedtext.boxing) {
						graphics.MeasureString(textw, -1, font, PointF(0,0), &stringFormat, &size);
						if (lpgw->justify == LEFT) {
							dxl = 0;
							dxr = size.Width + 0.5;
						} else if (lpgw->justify == CENTRE) {
							dxl = dxr = size.Width / 2;
						} else {
							dxl = size.Width + 0.5;
							dxr = 0;
						}
					}
					if (keysample) {
						draw_update_keybox(lpgw, plotno, xdash - dxl, ydash - size.Height / 2);
						draw_update_keybox(lpgw, plotno, xdash + dxr, ydash + size.Height / 2);
					}
					if (boxedtext.boxing) {
						if (boxedtext.box.left > (xdash - boxedtext.start.x - dxl))
							boxedtext.box.left = xdash - boxedtext.start.x - dxl;
						if (boxedtext.box.right < (xdash - boxedtext.start.x + dxr))
							boxedtext.box.right = xdash - boxedtext.start.x + dxr;
						if (boxedtext.box.top > (ydash - boxedtext.start.y - size.Height / 2))
							boxedtext.box.top = ydash - boxedtext.start.y - size.Height / 2;
						if (boxedtext.box.bottom < (ydash - boxedtext.start.y + size.Height / 2))
							boxedtext.box.bottom = ydash - boxedtext.start.y + size.Height / 2;
						/* We have to remember the text angle as well. */
						boxedtext.angle = lpgw->angle;
					}
					free(textw);
				}
			}
			break;
		}

		case W_enhanced_text: {
			char * str = (char *) curptr->pdata;
			if (str) {
				RECT extend;
				draw_enhanced_init(lpgw, graphics, solid_brush, rect);
				draw_enhanced_text(lpgw, rect, xdash, ydash, str);
				draw_get_enhanced_text_extend(&extend);

				if (keysample) {
					draw_update_keybox(lpgw, plotno, xdash - extend.left, ydash - extend.top);
					draw_update_keybox(lpgw, plotno, xdash + extend.right, ydash + extend.bottom);
				}
				if (boxedtext.boxing) {
					if (boxedtext.box.left > (boxedtext.start.x - xdash - extend.left))
						boxedtext.box.left = boxedtext.start.x - xdash - extend.left;
					if (boxedtext.box.right < (boxedtext.start.x - xdash + extend.right))
						boxedtext.box.right = boxedtext.start.x - xdash + extend.right;
					if (boxedtext.box.top > (boxedtext.start.y - ydash - extend.top))
						boxedtext.box.top = boxedtext.start.y - ydash - extend.top;
					if (boxedtext.box.bottom < (boxedtext.start.y - ydash + extend.bottom))
						boxedtext.box.bottom = boxedtext.start.y - ydash + extend.bottom;
					/* We have to store the text angle as well. */
					boxedtext.angle = lpgw->angle;
				}
			}
			break;
		}

		case W_hypertext:
			if (interactive) {
				/* Make a copy for future reference */
				char * str = (char *) curptr->pdata;
				free(hypertext);
				hypertext = UnicodeText(str, lpgw->encoding);
				hypertype = curptr->x;
			}
			break;

		case W_boxedtext:
			if (seq == 0) {
				boxedtext.option = (t_textbox_options) curptr->x;
				seq++;
				break;
			}
			seq = 0;
			switch (boxedtext.option) {
			case TEXTBOX_INIT:
				/* initialise bounding box */
				boxedtext.box.left   = boxedtext.box.right = 0;
				boxedtext.box.bottom = boxedtext.box.top   = 0;
				boxedtext.start.x = xdash;
				boxedtext.start.y = ydash;
				/* Note: initialising the text angle here would be best IMHO,
				   but current core code does not set this until the actual
				   print-out is done. */
				boxedtext.angle = lpgw->angle;
				boxedtext.boxing = TRUE;
				break;
			case TEXTBOX_OUTLINE:
			case TEXTBOX_BACKGROUNDFILL: {
				/* draw rectangle */
				int dx = boxedtext.margin.x;
				int dy = boxedtext.margin.y;
				if ((boxedtext.angle % 90) == 0) {
					Rect rect;

					switch (boxedtext.angle) {
					case 0:
						rect.X      = + boxedtext.box.left;
						rect.Y      = + boxedtext.box.top;
						rect.Width  = boxedtext.box.right - boxedtext.box.left;
						rect.Height = boxedtext.box.bottom - boxedtext.box.top;
						rect.Inflate(dx, dy);
						break;
					case 90:
						rect.X      = + boxedtext.box.top;
						rect.Y      = - boxedtext.box.right;
						rect.Height = boxedtext.box.right - boxedtext.box.left;
						rect.Width  = boxedtext.box.bottom - boxedtext.box.top;
						rect.Inflate(dy, dx);
						break;
					case 180:
						rect.X      = - boxedtext.box.right;
						rect.Y      = - boxedtext.box.bottom;
						rect.Width  = boxedtext.box.right - boxedtext.box.left;
						rect.Height = boxedtext.box.bottom - boxedtext.box.top;
						rect.Inflate(dx, dy);
						break;
					case 270:
						rect.X      = - boxedtext.box.bottom;
						rect.Y      = + boxedtext.box.left;
						rect.Height = boxedtext.box.right - boxedtext.box.left;
						rect.Width  = boxedtext.box.bottom - boxedtext.box.top;
						rect.Inflate(dy, dx);
						break;
					}
					rect.Offset(boxedtext.start.x, boxedtext.start.y);
					if (boxedtext.option == TEXTBOX_OUTLINE) {
						graphics.DrawRectangle(&pen, rect);
					} else {
						/* Fill bounding box with current color. */
						graphics.FillRectangle(&solid_brush, rect);
					}
				} else {
					float theta = boxedtext.angle * M_PI / 180.;
					float sin_theta = sin(theta);
					float cos_theta = cos(theta);
					PointF rect[5];

					rect[0].X =  (boxedtext.box.left   - dx) * cos_theta +
								 (boxedtext.box.top    - dy) * sin_theta;
					rect[0].Y = -(boxedtext.box.left   - dx) * sin_theta +
								 (boxedtext.box.top    - dy) * cos_theta;
					rect[1].X =  (boxedtext.box.left   - dx) * cos_theta +
								 (boxedtext.box.bottom + dy) * sin_theta;
					rect[1].Y = -(boxedtext.box.left   - dx) * sin_theta +
								 (boxedtext.box.bottom + dy) * cos_theta;
					rect[2].X =  (boxedtext.box.right  + dx) * cos_theta +
								 (boxedtext.box.bottom + dy) * sin_theta;
					rect[2].Y = -(boxedtext.box.right  + dx) * sin_theta +
								 (boxedtext.box.bottom + dy) * cos_theta;
					rect[3].X =  (boxedtext.box.right  + dx) * cos_theta +
								 (boxedtext.box.top    - dy) * sin_theta;
					rect[3].Y = -(boxedtext.box.right  + dx) * sin_theta +
								 (boxedtext.box.top    - dy) * cos_theta;
					for (int i = 0; i < 4; i++) {
						rect[i].X += boxedtext.start.x;
						rect[i].Y += boxedtext.start.y;
					}
					if (boxedtext.option == TEXTBOX_OUTLINE) {
						rect[4].X = rect[0].X;
						rect[4].Y = rect[0].Y;
						gdiplusPolyline(graphics, pen, rect, 5);
					} else {
						graphics.FillPolygon(fill_brush, rect, 4);
					}
				}
				boxedtext.boxing = FALSE;
				break;
			}
			case TEXTBOX_MARGINS:
				/* Adjust size of whitespace around text: default is 1/2 char height + 2 char widths. */
				boxedtext.margin.x = MulDiv(curptr->x * lpgw->hchar, rr - rl, 1000 * lpgw->xmax);
				boxedtext.margin.y = MulDiv(curptr->y * lpgw->hchar, rr - rl, 1000 * lpgw->xmax);
				break;
			default:
				break;
			}
			break;

		case W_fillstyle: {
			/* HBB 20010916: new entry, needed to squeeze the many
			 * parameters of a filled box call through the bottleneck
			 * of the fixed number of parameters in GraphOp() and
			 * struct GWOP, respectively. */
			/* FIXME: resetting polyi here should not be necessary */
			polyi = 0; /* start new sequence */
			int fillstyle = curptr->x;

			/* Eliminate duplicate fillstyle requests. */
			if ((fillstyle == last_fillstyle) &&
				(last_color == last_fillcolor) &&
				(last_fill_alpha == alpha_c))
				break;

			transparent = false;
			switch (fillstyle & 0x0f) {
				case FS_TRANSPARENT_SOLID: {
					double alpha = (fillstyle >> 4) / 100.;
					solid_fill_brush.SetColor(gdiplusCreateColor(last_color, alpha));
					fill_brush = &solid_fill_brush;
					break;
				}
				case FS_SOLID: {
					if (alpha_c < 1.) {
						solid_fill_brush.SetColor(gdiplusCreateColor(last_color, alpha_c));
					} else if ((int)(fillstyle >> 4) == 100) {
						/* special case this common choice */
						solid_fill_brush.SetColor(gdiplusCreateColor(last_color, 1.));
					} else {
						double density = MINMAX(0, (int)(fillstyle >> 4), 100) * 0.01;
						COLORREF color =
							RGB(255 - density * (255 - GetRValue(last_color)),
								255 - density * (255 - GetGValue(last_color)),
								255 - density * (255 - GetBValue(last_color)));
						solid_fill_brush.SetColor(gdiplusCreateColor(color, 1.));
					}
					fill_brush = &solid_fill_brush;
					break;
				}
				case FS_TRANSPARENT_PATTERN:
					transparent = true;
					/* intentionally fall through */
				case FS_PATTERN: {
					/* style == 2 --> use fill pattern according to
							 * fillpattern. Pattern number is enumerated */
					int pattern = GPMAX(fillstyle >> 4, 0) % pattern_num;
					if (pattern_brush)
						delete pattern_brush;
					pattern_brush = gdiplusPatternBrush(pattern,
									last_color, 1., lpgw->background, transparent);
					fill_brush = pattern_brush;
					break;
				}
				case FS_EMPTY:
					/* FIXME: Instead of filling with background color, we should not fill at all in this case! */
					/* fill with background color */
					solid_fill_brush.SetColor(gdiplusCreateColor(lpgw->background, 1.));
					fill_brush = &solid_fill_brush;
					break;
				case FS_DEFAULT:
				default:
					/* Leave the current brush and color in place */
					solid_fill_brush.SetColor(gdiplusCreateColor(last_color, 1.));
					fill_brush = &solid_fill_brush;
					break;
			}
			last_fillstyle = fillstyle;
			last_fillcolor = last_color;
			last_fill_alpha = alpha_c;
			break;
		}

		case W_move:
			// This is used for filled boxes only.
			ppt[0].X = xdash;
			ppt[0].Y = ydash;
			break;

		case W_boxfill: {
			/* NOTE: the x and y passed with this call are the coordinates of the
			 * lower right corner of the box. The upper left corner was stored into
			 * ppt[0] by a preceding W_move, and the style was set
			 * by a W_fillstyle call. */
			// snap to pixel grid
			Point p1(xdash + 0.5, ydash + 0.5);
			Point p2(ppt[0].X + 0.5, ppt[0].Y + 0.5);
			Point p;
			int height, width;

			p.X = GPMIN(p1.X, p2.X);
			p.Y = GPMIN(p1.Y, p2.Y);
			width = abs(p2.X - p1.X);
			height = abs(p1.Y - p2.Y);

			SmoothingMode mode = graphics.GetSmoothingMode();
			graphics.SetSmoothingMode(SmoothingModeNone);
			graphics.FillRectangle(fill_brush, p.X, p.Y, width, height);
			graphics.SetSmoothingMode(mode);
			if (keysample)
				draw_update_keybox(lpgw, plotno, xdash + 1, ydash);
			polyi = 0;
			break;
		}

		case W_text_angle:
			if (lpgw->angle != (int)curptr->x) {
				lpgw->angle = (int)curptr->x;
				/* recalculate shifting of rotated text */
				hshift = - sin(M_PI / 180. * lpgw->angle) * lpgw->tmHeight / 2.;
				vshift = - cos(M_PI / 180. * lpgw->angle) * lpgw->tmHeight / 2.;
				if (lpgw->antialiasing) {
					// Cleartype is only applied to non-rotated text
					if ((lpgw->angle % 180) != 0)
						graphics.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);
					else
						graphics.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
				}
			}
			break;

		case W_justify:
			switch (curptr->x) {
				case LEFT:
					stringFormat.SetAlignment(StringAlignmentNear);
					break;
				case RIGHT:
					stringFormat.SetAlignment(StringAlignmentFar);
					break;
				case CENTRE:
					stringFormat.SetAlignment(StringAlignmentCenter);
					break;
			}
			lpgw->justify = curptr->x;
			break;

		case W_font: {
			int size = curptr->x;
			char * fontname = (char *) curptr->pdata;
			delete font;
#ifdef UNICODE
			/* FIXME: Maybe this should be in win.trm instead. */
			LPWSTR wfontname = UnicodeText(fontname, lpgw->encoding);
			font = SetFont_gdiplus(graphics, rect, lpgw, wfontname, size);
			free(wfontname);
#else
			font = SetFont_gdiplus(graphics, rect, lpgw, fontname, size);
#endif
			/* recalculate shifting of rotated text */
			hshift = - sin(M_PI / 180. * lpgw->angle) * lpgw->tmHeight / 2.;
			vshift = - cos(M_PI / 180. * lpgw->angle) * lpgw->tmHeight / 2.;
			break;
		}

		case W_pointsize:
			if (curptr->x > 0) {
				double pointsize = curptr->x / 100.0;
				htic = MulDiv(pointsize * lpgw->pointscale * lpgw->htic, rr - rl, lpgw->xmax) + 1;
				vtic = MulDiv(pointsize * lpgw->pointscale * lpgw->vtic, rb - rt, lpgw->ymax) + 1;
			} else {
				htic = vtic = 0;
			}
			/* invalidate point symbol cache */
			last_symbol = W_invalid_pointtype;
			break;

		case W_line_width:
			/* HBB 20000813: this may look strange, but it ensures
			 * that linewidth is exactly 1 iff it's in default
			 * state */
			line_width = curptr->x == 100 ? 1 : (curptr->x / 100.0);
			line_width *= lpgw->linewidth * lw_scale;
			if (poly_graphics != NULL)
				line_width *= poly_scale;
			solid_pen.SetWidth(line_width);
			pen.SetWidth(line_width);
			/* invalidate point symbol cache */
			last_symbol = W_invalid_pointtype;
			break;

		case W_setcolor:
		case W_setcolorrgb: {
			COLORREF color;

			/* distinguish gray values and RGB colors */
			if (curptr->op == W_setcolor) {	/* TC_LT */
				int pen = (int)curptr->x % WGNUMPENS;
				color = (pen background : lpgw->colorpen[pen + 2].lopnColor;
				if (!lpgw->color || !isColor) {
					unsigned luma = luma_from_color(GetRValue(color), GetGValue(color), GetBValue(color));
					color = RGB(luma, luma, luma);
				}
				alpha_c = 1.;
			} else {					/* TC_RGB */
				rgb255_color rgb255;
				rgb255.r = (curptr->y & 0xff);
				rgb255.g = (curptr->x >> 8);
				rgb255.b = (curptr->x & 0xff);
				alpha_c = 1. - ((curptr->y >> 8) & 0xff) / 255.;

				if (lpgw->color || ((rgb255.r == rgb255.g) && (rgb255.r == rgb255.b))) {
					/* Use colors or this is already gray scale */
					color = RGB(rgb255.r, rgb255.g, rgb255.b);
				} else {
					/* convert to gray */
					unsigned luma = luma_from_color(rgb255.r, rgb255.g, rgb255.b);
					color = RGB(luma, luma, luma);
				}
			}

			/* update brushes and pens */
			Color pcolor = gdiplusCreateColor(color, alpha_c);
			solid_brush.SetColor(pcolor);
			pen.SetColor(pcolor);
			solid_pen.SetColor(pcolor);

			/* invalidate point symbol cache */
			if (last_color != color)
				last_symbol = W_invalid_pointtype;

			/* remember this color */
			cur_penstruct.lopnColor = color;
			last_color = color;
			break;
		}

		case W_filled_polygon_pt: {
			/* a point of the polygon is coming */
			if (polyi >= polymax) {
				polymax += 200;
				ppt.reserve(polymax + 1);
			}
			ppt[polyi].X = xdash;
			ppt[polyi].Y = ydash;
			polyi++;
			break;
		}

		case W_filled_polygon_draw: {
			bool found = false;
			int i, k;
			//bool same_rot = true;

			// Test if successive polygons share a common edge:
			if ((!last_poly.empty()) && (polyi > 2)) {
				// Check for a common edge with previous filled polygon.
				for (i = 0; (i < polyi) && !found; i++) {
					for (k = 0; (k < last_polyi) && !found; k++) {
						if ((ppt[i].X == last_poly[k].X) && (ppt[i].Y == last_poly[k].Y)) {
							if ((ppt[(i + 1) % polyi].X == last_poly[(k + 1) % last_polyi].X) &&
							    (ppt[(i + 1) % polyi].Y == last_poly[(k + 1) % last_polyi].Y)) {
								//found = true;
								//same_rot = true;
							}
							// This is the dominant case for filling between curves,
							// see fillbetween.dem and polar.dem.
							if ((ppt[(i + 1) % polyi].X == last_poly[(k + last_polyi - 1) % last_polyi].X) &&
							    (ppt[(i + 1) % polyi].Y == last_poly[(k + last_polyi - 1) % last_polyi].Y)) {
								found = true;
								//same_rot = false;
							}
						}
					}
				}
			}

			if (found) { // merge polygons
				// rewind
				i--; k--;

				int extra = polyi - 2;
				// extend buffer to make room for extra points
				last_poly.reserve(last_polyi + extra + 1);
				/* TODO: should use memmove instead */
				for (int n = last_polyi - 1; n >= k; n--) {
					last_poly[n + extra].X = last_poly[n].X;
					last_poly[n + extra].Y = last_poly[n].Y;
				}
				// copy new points
				for (int n = 0; n < extra; n++) {
					last_poly[k + n].X = ppt[(i + 2 + n) % polyi].X;
					last_poly[k + n].Y = ppt[(i + 2 + n) % polyi].Y;
				}
				last_polyi += extra;
			} else {
				if (!last_poly.empty()) {
					if (poly_graphics == NULL) {
						// changing smoothing mode is still necessary in case of new/unknown code paths
						SmoothingMode mode = graphics.GetSmoothingMode();
						if (lpgw->antialiasing && !lpgw->polyaa)
							graphics.SetSmoothingMode(SmoothingModeNone);
						graphics.FillPolygon(fill_brush, last_poly.data(), last_polyi);
						graphics.SetSmoothingMode(mode);
					} else {
						poly_graphics->FillPolygon(fill_brush, last_poly.data(), last_polyi);
					}
				}
				// save the current polygon
				last_poly = ppt;
				last_polyi = polyi;
			}

			polyi = 0;
			break;
		}

		case W_image:	{
			/* Due to the structure of gwop 6 entries are needed in total. */
			if (seq == 0) {
				/* First OP contains only the color mode */
				color_mode = curptr->x;
			} else if (seq < 5) {
				/* Next four OPs contain the `corner` array */
				corners[seq - 1].x = xdash + 0.5;
				corners[seq - 1].y = ydash + 0.5;
			} else {
				/* The last OP contains the image and it's size */
				char * image = (char *) curptr->pdata;
				if (image == NULL) break; // memory allocation failed
				unsigned int width = curptr->x;
				unsigned int height = curptr->y;
				if (image) {
					Bitmap * bitmap;

					graphics.SetPixelOffsetMode(PixelOffsetModeHighQuality);

					/* create clip region */
					Rect clipRect(
						(INT) GPMIN(corners[2].x, corners[3].x), (INT) GPMIN(corners[2].y, corners[3].y),
						abs(corners[2].x - corners[3].x), abs(corners[2].y - corners[3].y));
					graphics.SetClip(clipRect);

					if (color_mode != IC_RGBA) {
						int pad_bytes = (4 - (3 * width) % 4) % 4; /* scan lines start on ULONG boundaries */
						int stride = width * 3 + pad_bytes;
						bitmap = new Bitmap(width, height, stride, PixelFormat24bppRGB, (BYTE *) image);
					} else {
						int stride = width * 4;
						bitmap = new Bitmap(width, height, stride, PixelFormat32bppARGB, (BYTE *) image);
					}

					if (bitmap) {
						/* image is upside-down */
						bitmap->RotateFlip(RotateNoneFlipY);
						if (lpgw->color) {
							graphics.DrawImage(bitmap,
								(INT) GPMIN(corners[0].x, corners[1].x),
								(INT) GPMIN(corners[0].y, corners[1].y),
								abs(corners[1].x - corners[0].x),
								abs(corners[1].y - corners[0].y));
						} else {
							/* convert to grayscale */
							ColorMatrix cm = {{{0.30f, 0.30f, 0.30f, 0, 0},
											   {0.59f, 0.59f, 0.59f, 0, 0},
											   {0.11f, 0.11f, 0.11f, 0, 0},
											   {0, 0, 0, 1, 0},
											   {0, 0, 0, 0, 1}
											 }};
							ImageAttributes ia;
							ia.SetColorMatrix(&cm, ColorMatrixFlagsDefault, ColorAdjustTypeBitmap);
							graphics.DrawImage(bitmap,
								RectF((INT) GPMIN(corners[0].x, corners[1].x),
									(INT) GPMIN(corners[0].y, corners[1].y),
									abs(corners[1].x - corners[0].x),
									abs(corners[1].y - corners[0].y)),
								0, 0, width, height,
								UnitPixel, &ia);
						}
						delete bitmap;
					}
					graphics.ResetClip();
					graphics.SetPixelOffsetMode(PixelOffsetModeNone);
				}
			}
			seq = (seq + 1) % 6;
			break;
		}

		default: {
			enum win_pointtypes symbol = (enum win_pointtypes) curptr->op;

			/* This covers only point symbols. All other codes should be
			   handled in the switch statement. */
			if ((symbol < W_dot) || (symbol > W_last_pointtype))
				break;

			// draw cached point symbol
			if (ps_caching && (last_symbol == symbol) && (cb != NULL)) {
				// always draw point symbols on integer (pixel) positions
				if (lpgw->oversample)
					graphics.DrawCachedBitmap(cb, INT(xdash + 0.5) - cb_ofs.x, INT(ydash + 0.5) - cb_ofs.y);
				else
					graphics.DrawCachedBitmap(cb, xdash - cb_ofs.x, ydash - cb_ofs.y);
				break;
			} else {
				if (cb != NULL) {
					delete cb;
					cb = NULL;
				}
			}

			Bitmap *b = 0;
			Graphics *g = 0;
			int xofs;
			int yofs;

			// Switch between cached and direct drawing
			if (ps_caching) {
				// Create a compatible bitmap
				b = new Bitmap(2 * htic + 3, 2 * vtic + 3, &graphics);
				g = Graphics::FromImage(b);
				if (lpgw->antialiasing)
					g->SetSmoothingMode(SmoothingModeAntiAlias8x8);
				cb_ofs.x = xofs = htic + 1;
				cb_ofs.y = yofs = vtic + 1;
				last_symbol = symbol;
			} else {
				g = &graphics;
				// snap to pixel
				if (lpgw->oversample) {
					xofs = xdash + 0.5;
					yofs = ydash + 0.5;
				} else {
					xofs = xdash;
					yofs = ydash;
				}
			}

			switch (symbol) {
			case W_dot:
				gdiplusDot(*g, solid_brush, xofs, yofs);
				break;
			case W_plus: /* do plus */
			case W_star: /* do star: first plus, then cross */
				g->DrawLine(&solid_pen, xofs - htic, yofs, xofs + htic, yofs);
				g->DrawLine(&solid_pen, xofs, yofs - vtic, xofs, yofs + vtic);
				if (symbol == W_plus)
					break;
			case W_cross: /* do X */
				g->DrawLine(&solid_pen, xofs - htic, yofs - vtic, xofs + htic - 1, yofs + vtic);
				g->DrawLine(&solid_pen, xofs - htic, yofs + vtic, xofs + htic - 1, yofs - vtic);
				break;
			case W_circle: /* do open circle */
				g->DrawEllipse(&solid_pen, xofs - htic, yofs - htic, 2 * htic, 2 * htic);
				break;
			case W_fcircle: /* do filled circle */
				g->FillEllipse(&solid_brush, xofs - htic, yofs - htic, 2 * htic, 2 * htic);
				break;
			default: {	/* potentially closed figure */
				Point p[6];
				int i;
				int shape = 0;
				int filled = 0;
				int index = 0;
				const float pointshapes[6][10] = {
					{-1, -1, +1, -1, +1, +1, -1, +1, 0, 0}, /* box */
					{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* dummy, circle */
					{ 0, -4./3, -4./3, 2./3,
						4./3,  2./3, 0, 0}, /* triangle */
					{ 0, 4./3, -4./3, -2./3,
						4./3,  -2./3, 0, 0}, /* inverted triangle */
					{ 0, +1, -1,  0,  0, -1, +1,  0, 0, 0}, /* diamond */
					{ 0, 1, 0.95106, 0.30902, 0.58779, -0.80902,
						-0.58779, -0.80902, -0.95106, 0.30902} /* pentagon */
				};

				// This should never happen since all other codes should be
				// handled in the switch statement.
				if ((symbol < W_box) || (symbol > W_last_pointtype))
					break;

				// Calculate index, instead of an ugly long switch statement;
				// Depends on definition of commands in wgnuplib.h.
				index = (symbol - W_box);
				shape = index / 2;
				filled = (index % 2) > 0;

				for (i = 0; i < 5; ++i) {
					if (pointshapes[shape][i * 2 + 1] == 0
						&& pointshapes[shape][i * 2] == 0)
						break;
					p[i].X = xofs + htic * pointshapes[shape][i * 2] + 0.5;
					p[i].Y = yofs + vtic * pointshapes[shape][i * 2 + 1] + 0.5;
				}
				if (filled) {
					/* filled polygon with border */
					g->FillPolygon(&solid_brush, p, i);
				} else {
					/* Outline polygon */
					p[i].X = p[0].X;
					p[i].Y = p[0].Y;
					gdiplusPolyline(*g, solid_pen, p, i + 1);
					gdiplusDot(*g, solid_brush, xofs, yofs);
				}
			} /* default case */
			} /* switch (point symbol) */

			if (b != NULL) {
				// create a cached bitmap for faster redrawing
				cb = new CachedBitmap(b, &graphics);
				// display point symbol snapped to pixel
				if (lpgw->oversample)
					graphics.DrawCachedBitmap(cb, INT(xdash + 0.5) - xofs, INT(ydash + 0.5) - yofs);
				else
					graphics.DrawCachedBitmap(cb, xdash - xofs, ydash - yofs);
				delete b;
				delete g;
			}

			if (keysample) {
				draw_update_keybox(lpgw, plotno, xdash + htic, ydash + vtic);
				draw_update_keybox(lpgw, plotno, xdash - htic, ydash - vtic);
			}
			break;
			} /* default case */
		} /* switch(opcode) */
		} /* hide layer? */

		lastop = curptr->op;
		ngwop++;
		curptr++;
		if ((unsigned)(curptr - blkptr->gwop) >= GWOPMAX) {
			if ((blkptr = blkptr->next) == NULL)
				/* If exact multiple of GWOPMAX entries are queued,
				 * next will be NULL. Only the next GraphOp() call would
				 * have allocated a new block */
				break;
			if (blkptr->gwop == NULL)
				break;
			curptr = blkptr->gwop;
		}
	} /* while (ngwop < lpgw->nGWOP) */

	/* clean-up */
	if (pattern_brush)
		delete pattern_brush;
	if (cb)
		delete cb;
	if (font)
		delete font;
	ppt.clear();
}


UINT nImageCodecs = 0; // number of image encoders
ImageCodecInfo * pImageCodecInfo = NULL;  // list of image encoders


static int
gdiplusGetEncoders()
{
	UINT num = 0;
	UINT size = 0;

	GetImageEncodersSize(&nImageCodecs, &size);
	if (size == 0)
		return -1;
	pImageCodecInfo = (ImageCodecInfo *) malloc(size);
	if (pImageCodecInfo == NULL)
		return -1;
	GetImageEncoders(nImageCodecs, size, pImageCodecInfo);
	return num;
}


void
SaveAsBitmap(LPGW lpgw)
{
	static OPENFILENAMEW Ofn;
	static WCHAR lpstrCustomFilter[256] = { '\0' };
	static WCHAR lpstrFileName[MAX_PATH] = { '\0' };
	static WCHAR lpstrFileTitle[MAX_PATH] = { '\0' };
	UINT i;

	// make sure GDI+ is initialized
	gdiplusInit();

	// ask GDI+ about supported encoders
	if (pImageCodecInfo == NULL)
		if (gdiplusGetEncoders() < 0)
			std::cerr 

Web Proxy Viewer  |  New URL  |  Original Page