Windows2003-3790/windows/advcore/gdiplus/test/polytest/wndstuff.cpp
2020-09-30 16:53:55 +02:00

2066 lines
57 KiB
C++

/******************************Module*Header*******************************\
* Module Name: wndstuff.cpp
*
* This file contains all the code necessary for a simple GDI+ primitive
* test.
*
* Author: J. Andrew Goossen [andrewgo]
*
* Copyright (c) 1991-2000 Microsoft Corporation
*
\**************************************************************************/
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <float.h>
#include <windows.h>
#include <objbase.h>
#include <mmsystem.h>
#include <gdiplus.h>
using namespace Gdiplus;
#include "wndstuff.h"
#include "../gpinit.inc"
#define HIT_DISTANCE 16
#define ABS(x) (((x) >= 0) ? (x) : -(x))
#define ROUND(x) ((INT) floor(x + 0.5f))
// We set the GDI transform to a 16x scaling transform, and the mode to
// GM_ADVANCED, so that we can get full fractional accuracy on the points
// that we feed to GDI:
#define GDI_FIXEDPOINT_SCALE 16
// State for tracking the primitive vertices and transform:
const INT PrimitivePointsMax = 64;
INT PrimitivePointsCount = 4;
PointF PrimitivePoints[PrimitivePointsMax] = { PointF(100, 100),
PointF(100, 400),
PointF(400, 400),
PointF(400, 100) };
Matrix *PrimitiveTransform;
Matrix *PrimitiveInverseTransform;
INT PrimitiveDragVertex = -1;
BOOL IsAddingPoints = FALSE;
// State for tracking the location of the transform overlay:
const REAL OverlayDimension = 100;
PointF OverlayPoints[3]; // 3 device-space representing overlay,
// where [1] is the elbow
PointF OverlayOffset; // World-space coordinate of overlay elbow
INT OverlayDragVertex = -1;
// Miscellaneous state:
INT WindowWidth;
INT WindowHeight;
// Settings:
BOOL DoFill = FALSE;
BOOL DoDraw = TRUE;
BOOL DoAntialias = FALSE;
BOOL DoGammaCorrect = FALSE;
BOOL DoGdi = FALSE;
BOOL DoClipGrid = FALSE;
BOOL DoRandomTest = FALSE;
BOOL DoWindingFill = FALSE;
BOOL DoAnchors = TRUE;
BOOL DoSpine = FALSE;
BOOL DoWiden = FALSE;
BOOL DoCompound = FALSE;
BOOL DoTransformOverlay = FALSE;
BOOL DoScalingOnly = FALSE;
BOOL DoShape = FALSE;
BOOL DoBrushRect = FALSE;
REAL RenderMiterLimit = 10;
INT RenderWidth = 1;
INT RenderAlpha = 255;
LinearGradientBrush *LinearBrush;
GraphicsPath *PathGradientPath;
Brush *RenderBrush;
Pen *RenderPen;
Region *RenderRegion;
HRGN RenderHrgn;
HBRUSH RenderHbrush;
HPEN RenderHpen;
WORD MmWrapMode = MM_WRAP_TILE;
WORD MmBrushType = MM_BRUSH_SOLID;
WORD MmEndCap = MM_CAP_FLAT;
WORD MmJoin = MM_JOIN_MITER;
WORD MmDashStyle = MM_STYLE_SOLID;
WORD MmAlignment = MM_ALIGNMENT_CENTER;
WORD MmPrimitive = MM_POLYGON;
// Other useful globals:
HINSTANCE ghInstance;
HWND ghwndMain;
HBRUSH ghbrWhite;
//FARPROC glpfnEnterWidth;
//FARPROC glpfnEnterAlpha;
//FARPROC glpfnEnterPoints;
//FARPROC glpfnEnterTransform;
/***************************************************************************\
* Creates the GDI+ brush to be used.
*
\***************************************************************************/
VOID
CreateBrush_Gdiplus()
{
WrapMode wrapMode;
INT i;
// Delete the old brush:
delete RenderBrush;
LinearBrush = NULL;
RenderBrush = NULL;
// Create the new one:
Bitmap bitmap(L"winnt256.bmp");
switch (MmWrapMode)
{
case MM_WRAP_TILE: wrapMode = WrapModeTile; break;
case MM_WRAP_CLAMP: wrapMode = WrapModeClamp; break;
case MM_WRAP_FLIPX: wrapMode = WrapModeTileFlipX; break;
case MM_WRAP_FLIPY: wrapMode = WrapModeTileFlipY; break;
case MM_WRAP_FLIPXY: wrapMode = WrapModeTileFlipXY; break;
}
switch (MmBrushType)
{
case MM_BRUSH_SOLID:
RenderBrush = new SolidBrush(Color(128, 128, 128));
break;
case MM_BRUSH_TEXTURE:
RenderBrush = new TextureBrush(&bitmap, wrapMode);
break;
case MM_BRUSH_TEXTURE_32x32:
{
Bitmap texture(32, 32, PixelFormat32bppARGB);
Graphics g(&texture);
g.DrawImage(&bitmap, Rect(0, 0, 32, 32));
TextureBrush *brush = new TextureBrush(&texture, wrapMode);
// Set a translate:
Matrix matrix(1, 0, 0, 1, 100, 100);
brush->SetTransform(&matrix);
RenderBrush = brush;
}
break;
case MM_BRUSH_TEXTURE_1x1:
{
SolidBrush solidBrush(Color::Green);
Bitmap texture(1, 1, PixelFormat32bppARGB);
Graphics g(&texture);
g.FillRectangle(&solidBrush, 0, 0, 1, 1);
TextureBrush *brush = new TextureBrush(&texture, wrapMode);
// Set a translate:
Matrix matrix(1, 0, 0, 1, 100, 100);
brush->SetTransform(&matrix);
RenderBrush = brush;
}
break;
case MM_BRUSH_LINEAR:
{
LinearGradientBrush *brush = new LinearGradientBrush(
Point(100, 100),
Point(100, 300),
Color::Red,
Color::Black
);
brush->SetWrapMode(wrapMode);
RenderBrush = brush;
LinearBrush = brush;
}
break;
case MM_BRUSH_PATHGRADIENT:
{
PathGradientBrush *brush;
INT count;
if (PathGradientPath != NULL)
{
brush = new PathGradientBrush(PathGradientPath);
count = PathGradientPath->GetPointCount();
}
else
{
// Substitute a default path for now:
PointF points[] = { PointF(100, 100), PointF(100, 300),
PointF(300, 300), PointF(30, 100) };
brush = new PathGradientBrush(points, 4);
count = 4;
}
Color *colors = new Color[count];
for (i = 0; i < count; i += 2)
{
colors[i] = Color::Green;
colors[i+1] = Color::Red;
}
brush->SetSurroundColors(colors, &count);
delete [] colors;
brush->SetCenterPoint(OverlayOffset);
brush->SetCenterColor(Color::Black);
RenderBrush = brush;
}
break;
}
}
/***************************************************************************\
* Creates the GDI brush to be used.
*
\***************************************************************************/
VOID
CreateBrush_Gdi()
{
DeleteObject(RenderHbrush);
RenderHbrush = CreateSolidBrush(RGB(128, 128, 128));
}
/***************************************************************************\
* Creates the GDI and GDI+ brushes to be used.
*
\***************************************************************************/
VOID
CreateBrushes()
{
CreateBrush_Gdiplus();
CreateBrush_Gdi();
}
/***************************************************************************\
* Creates the GDI+ pen to be used.
*
\***************************************************************************/
VOID
CreatePen_Gdiplus()
{
DashCap dashCap;
LineCap lineCap;
LineJoin lineJoin;
DashStyle dashStyle;
PenAlignment alignment;
delete RenderPen;
RenderPen = new Pen(Color((BYTE) RenderAlpha, 255, 0, 0), (REAL) RenderWidth);
switch (MmEndCap)
{
case MM_CAP_ROUND: lineCap = LineCapRound; dashCap = DashCapRound; break;
case MM_CAP_SQUARE: lineCap = LineCapSquare; dashCap = DashCapFlat; break;
case MM_CAP_FLAT: lineCap = LineCapFlat; dashCap = DashCapFlat; break;
case MM_CAP_TRIANGLE: lineCap = LineCapTriangle; dashCap = DashCapTriangle; break;
}
RenderPen->SetEndCap(lineCap);
RenderPen->SetStartCap(lineCap);
RenderPen->SetDashCap(dashCap);
switch (MmJoin)
{
case MM_JOIN_ROUND: lineJoin = LineJoinRound; break;
case MM_JOIN_BEVEL: lineJoin = LineJoinBevel; break;
case MM_JOIN_MITER: lineJoin = LineJoinMiter; break;
}
RenderPen->SetLineJoin(lineJoin);
switch (MmDashStyle)
{
case MM_STYLE_SOLID: dashStyle = DashStyleSolid; break;
case MM_STYLE_DASH: dashStyle = DashStyleDash; break;
case MM_STYLE_DOT: dashStyle = DashStyleDot; break;
case MM_STYLE_DASHDOT: dashStyle = DashStyleDashDot; break;
case MM_STYLE_DASHDOTDOT: dashStyle = DashStyleDashDotDot; break;
}
RenderPen->SetDashStyle(dashStyle);
switch (MmAlignment)
{
case MM_ALIGNMENT_CENTER: alignment = PenAlignmentCenter; break;
case MM_ALIGNMENT_INSET: alignment = PenAlignmentInset; break;
}
RenderPen->SetAlignment(alignment);
RenderPen->SetMiterLimit(RenderMiterLimit);
// We should add a 'compound array' UI to make this more flexible.
// But for now, we only ever create one type of compound line:
if (DoCompound)
{
REAL compoundArray[] = { 0.0f, 0.2f, 0.8f, 1.0f };
RenderPen->SetCompoundArray(compoundArray, 4);
}
}
/***************************************************************************\
* Creates the GDI pen to be used.
*
\***************************************************************************/
VOID
CreatePen_Gdi()
{
DWORD lineCap;
DWORD lineJoin;
DWORD dashStyle;
LOGBRUSH logBrush;
DeleteObject(RenderHpen);
switch (MmEndCap)
{
case MM_CAP_ROUND: lineCap = PS_ENDCAP_ROUND; break;
case MM_CAP_SQUARE: lineCap = PS_ENDCAP_SQUARE; break;
case MM_CAP_FLAT: lineCap = PS_ENDCAP_FLAT; break;
case MM_CAP_TRIANGLE: lineCap = PS_ENDCAP_SQUARE; break; // No equivalent
}
switch (MmJoin)
{
case MM_JOIN_ROUND: lineJoin = PS_JOIN_ROUND; break;
case MM_JOIN_BEVEL: lineJoin = PS_JOIN_BEVEL; break;
case MM_JOIN_MITER: lineJoin = PS_JOIN_MITER; break;
}
switch (MmDashStyle)
{
case MM_STYLE_SOLID: dashStyle = PS_SOLID; break;
case MM_STYLE_DASH: dashStyle = PS_DASH; break;
case MM_STYLE_DOT: dashStyle = PS_DOT; break;
case MM_STYLE_DASHDOT: dashStyle = PS_DASHDOT; break;
case MM_STYLE_DASHDOTDOT: dashStyle = PS_DASHDOTDOT; break;
}
logBrush.lbStyle = BS_SOLID;
logBrush.lbColor = RGB(255, 0, 0);
logBrush.lbHatch = 0;
RenderHpen = ExtCreatePen(lineCap | lineJoin | dashStyle | PS_GEOMETRIC,
GDI_FIXEDPOINT_SCALE * RenderWidth,
&logBrush,
0,
NULL);
}
/***************************************************************************\
* Creates the GDI+ and GDI pens to be used.
*
\***************************************************************************/
VOID
CreatePens()
{
CreatePen_Gdiplus();
CreatePen_Gdi();
}
/***************************************************************************\
* Creates the GDI+ clip region to be used.
*
\***************************************************************************/
VOID
CreateRegion_Gdiplus()
{
INT x;
INT y;
delete RenderRegion;
RenderRegion = new Region();
for (x = 0; x < WindowWidth; x += 128)
{
for (y = 0; y < WindowHeight; y += 128)
{
RenderRegion->Exclude(Rect(x + 64, y, 64, 64));
RenderRegion->Exclude(Rect(x, y + 64, 64, 64));
}
}
}
/***************************************************************************\
* Creates the GDI clip region to be used.
*
\***************************************************************************/
VOID
CreateRegion_Gdi()
{
INT x;
INT y;
HRGN hrgn;
DeleteObject(RenderHrgn);
RenderHrgn = CreateRectRgn(0, 0, WindowWidth, WindowHeight);
hrgn = CreateRectRgn(0, 0, 0, 0);
for (x = 0; x < WindowWidth; x += 128)
{
for (y = 0; y < WindowHeight; y += 128)
{
SetRectRgn(hrgn, x + 64, y, x + 128, y + 64);
CombineRgn(RenderHrgn, RenderHrgn, hrgn, RGN_DIFF);
SetRectRgn(hrgn, x, y + 64, x + 64, y + 128);
CombineRgn(RenderHrgn, RenderHrgn, hrgn, RGN_DIFF);
}
}
DeleteObject(hrgn);
}
/***************************************************************************\
* Free all our global objects.
*
\***************************************************************************/
VOID
DeleteObjects_Gdiplus()
{
delete RenderRegion;
delete RenderBrush;
delete RenderPen;
delete PrimitiveTransform;
delete PrimitiveInverseTransform;
}
/***************************************************************************\
* Free all our global objects.
*
\***************************************************************************/
VOID
DeleteObjects_Gdi()
{
DeleteObject(RenderHrgn);
DeleteObject(RenderHbrush);
DeleteObject(RenderHpen);
}
/***************************************************************************\
* Draw the control points
*
\***************************************************************************/
VOID
DrawAnchors(
Graphics *g,
PointF* points,
INT count
)
{
SolidBrush blueBrush(Color(150, 128, 128, 128));
for (; count != 0; count--, points++)
{
PointF point(*points);
PrimitiveTransform->TransformPoints(&point, 1);
g->FillRectangle(&blueBrush, RectF(point.X - 2, point.Y - 2, 5, 5));
}
}
/***************************************************************************\
* DrawTransformOverlay
*
\***************************************************************************/
VOID
DrawTransformOverlay(
Graphics *g
)
{
Pen pen(Color::Purple, 1);
SolidBrush brush(Color::Purple);
g->DrawLine(&pen, OverlayPoints[1].X, OverlayPoints[1].Y,
OverlayPoints[0].X, OverlayPoints[0].Y);
g->DrawLine(&pen, OverlayPoints[1].X, OverlayPoints[1].Y,
OverlayPoints[2].X, OverlayPoints[2].Y);
g->FillRectangle(&brush, RectF(OverlayPoints[0].X - 2,
OverlayPoints[0].Y - 2,
5,
5));
g->FillRectangle(&brush, RectF(OverlayPoints[2].X - 2,
OverlayPoints[2].Y - 2,
5,
5));
}
/***************************************************************************\
* Render_Gdiplus
*
\***************************************************************************/
INT
Render_Gdiplus(
Graphics *g,
PointF *points,
INT count
)
{
INT i;
INT pointsUsed;
// if (DoBrushRect)
// {
// LinearBrush->SetLinearPoints(points[0], points[1]);
// }
RectF rect(points[0].X, points[0].Y,
points[1].X - points[0].X, points[1].Y - points[0].Y);
Pen spinePen(Color(0, 128, 0), 0);
GraphicsPath shapePath;
switch (MmPrimitive)
{
case MM_POLYGON:
{
shapePath.AddPolygon(points, count);
if (!DoShape)
{
if (DoFill)
g->FillPolygon(RenderBrush, points, count, (DoWindingFill)
? FillModeWinding
: FillModeAlternate);
if (DoDraw)
g->DrawPolygon(RenderPen, points, count);
if (DoSpine)
g->DrawPolygon(&spinePen, points, count);
}
pointsUsed = count;
break;
}
case MM_LINES:
{
shapePath.AddLines(points, count);
if (!DoShape)
{
if (DoDraw)
g->DrawLines(RenderPen, points, count);
if (DoSpine)
g->DrawLines(&spinePen, points, count);
}
pointsUsed = count;
break;
}
case MM_BEZIER:
{
GraphicsPath path;
path.AddBeziers(points, count);
shapePath.AddPath(&path, FALSE);
if (!DoShape)
{
if (DoFill)
g->FillPath(RenderBrush, &path);
if (DoDraw)
g->DrawPath(RenderPen, &path);
if (DoSpine)
g->DrawPath(&spinePen, &path);
}
pointsUsed = count;
break;
}
case MM_RECTANGLE:
{
shapePath.AddRectangle(rect);
if (!DoShape)
{
if (DoFill)
g->FillRectangle(RenderBrush, rect);
if (DoDraw)
g->DrawRectangle(RenderPen, rect);
if (DoSpine)
g->DrawRectangle(&spinePen, rect);
}
pointsUsed = 2;
break;
}
case MM_ELLIPSE:
{
shapePath.AddEllipse(rect);
if (!DoShape)
{
if (DoFill)
g->FillEllipse(RenderBrush, rect);
if (DoDraw)
g->DrawEllipse(RenderPen, rect);
if (DoSpine)
g->DrawEllipse(&spinePen, rect);
}
pointsUsed = 2;
break;
}
case MM_TEXTPATH:
{
WCHAR string[] = L"GDI+ Rules!";
GraphicsPath path((DoWindingFill) ? FillModeWinding
: FillModeAlternate);
FontFamily family(L"Times New Roman");
PointF origin(points[0].X, points[0].Y);
path.AddString(
string,
wcslen(string),
&family,
0,
200,
origin,
NULL
);
shapePath.AddPath(&path, FALSE);
if (!DoShape)
{
if (DoFill)
g->FillPath(RenderBrush, &path);
if (DoDraw)
g->DrawPath(RenderPen, &path);
if (DoSpine)
g->DrawPath(&spinePen, &path);
}
pointsUsed = 1;
break;
}
}
if (DoShape)
{
// Recreate the path to be use for the path-gradient brush,
// using the new shape data:
delete PathGradientPath;
PathGradientPath = shapePath.Clone();
// Recreate the brush and do a complete fill of the window using
// the specified brush:
CreateBrushes();
if (DoFill)
g->FillRectangle(RenderBrush, -262144, -262144, 524288, 524288);
if (DoDraw)
g->DrawPath(RenderPen, &shapePath);
if (DoSpine)
g->DrawPath(&spinePen, &shapePath);
}
if (DoWiden)
{
Pen widenPen(Color::Black, 0);
shapePath.Widen(RenderPen, NULL);
g->DrawPath(&widenPen, &shapePath);
}
return(pointsUsed);
}
/***************************************************************************\
* Render_Gdi
*
\***************************************************************************/
INT
Render_Gdi(
HDC hdc,
PointF *primitivePoints,
INT count
)
{
BOOL drawSpine;
HGDIOBJ oldPen;
HGDIOBJ oldBrush;
HPEN hpenSpine;
INT pointsUsed;
POINT points[PrimitivePointsMax];
INT i;
// Convert to integer, the preferred GDI format. Remember that we've
// set the transform to scale down by 16, so we have to multiply by
// 16 here. We've done this so that we can specify 28.4 directly to
// GDI:
for (i = 0; i < count; i++)
{
points[i].x = ROUND(GDI_FIXEDPOINT_SCALE * primitivePoints[i].X);
points[i].y = ROUND(GDI_FIXEDPOINT_SCALE * primitivePoints[i].Y);
}
hpenSpine = CreatePen(PS_SOLID, 0, RGB(0, 128, 0));
for (drawSpine = FALSE; drawSpine != TRUE; drawSpine = TRUE)
{
if (drawSpine)
{
oldPen = SelectObject(hdc, hpenSpine);
oldBrush = SelectObject(hdc, GetStockObject(NULL_BRUSH));
}
else
{
oldPen = SelectObject(hdc,
(DoDraw) ? RenderHpen : GetStockObject(NULL_PEN));
oldBrush = SelectObject(hdc,
(DoFill) ? RenderHbrush : GetStockObject(NULL_BRUSH));
}
switch (MmPrimitive)
{
case MM_POLYGON:
{
Polygon(hdc, points, count);
pointsUsed = count;
break;
}
case MM_LINES:
{
Polyline(hdc, points, count);
pointsUsed = count;
break;
}
case MM_BEZIER:
{
// Don't use StrokeAndFillPath because GDI would close the
// stroke:
BeginPath(hdc);
PolyBezier(hdc, points, count);
EndPath(hdc);
FillPath(hdc);
BeginPath(hdc);
PolyBezier(hdc, points, count);
EndPath(hdc);
StrokePath(hdc);
pointsUsed = count;
break;
}
case MM_RECTANGLE:
{
Rectangle(hdc, points[0].x, points[0].y, points[1].x, points[1].y);
pointsUsed = 2;
break;
}
case MM_ELLIPSE:
{
Ellipse(hdc, points[0].x, points[0].y, points[1].x, points[1].y);
pointsUsed = 2;
break;
}
case MM_TEXTPATH:
{
LOGFONT logFont;
memset(&logFont, 0, sizeof(logFont));
// Don't forget to multiply the height by 16, because
// we're using a scaling transform with GDI so that we
// can spit out 28.4 coordinates:
logFont.lfHeight = - GDI_FIXEDPOINT_SCALE * 200;
strcpy(logFont.lfFaceName, "Times New Roman");
SetBkMode(hdc, TRANSPARENT);
HFONT font = CreateFontIndirect(&logFont);
HGDIOBJ oldFont = SelectObject(hdc, font);
WCHAR string[] = L"GDI+ Rules!";
BeginPath(hdc);
ExtTextOutW(hdc, points[0].x, points[0].y, 0, NULL, string,
wcslen(string), NULL);
EndPath(hdc);
StrokeAndFillPath(hdc);
SelectObject(hdc, oldFont);
DeleteObject(font);
pointsUsed = 1;
}
}
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
}
DeleteObject(hpenSpine);
return(pointsUsed);
}
/***************************************************************************\
* PrepareContext_Gdiplus
*
\***************************************************************************/
VOID
PrepareContext_Gdiplus(
Graphics *g
)
{
g->SetSmoothingMode((DoAntialias) ? SmoothingModeAntiAlias
: SmoothingModeNone);
g->SetCompositingQuality((DoGammaCorrect) ?
CompositingQualityGammaCorrected : CompositingQualityAssumeLinear);
if (DoClipGrid)
{
g->SetClip(RenderRegion);
}
g->SetTransform(PrimitiveTransform);
}
/***************************************************************************\
* PrepareContext_Gdi
*
\***************************************************************************/
VOID
PrepareContext_Gdi(
HDC hdc
)
{
REAL m[6];
XFORM xform;
SetMiterLimit(hdc, RenderMiterLimit, NULL);
SetPolyFillMode(hdc, (DoWindingFill) ? WINDING : ALTERNATE);
if (DoClipGrid)
{
SelectClipRgn(hdc, RenderHrgn);
}
// Setup the transform:
PrimitiveTransform->GetElements(m);
// Scale the transform down by 16 so that we can give GDI 28.4
// coordinates directly as integers:
xform.eM11 = m[0] / GDI_FIXEDPOINT_SCALE;
xform.eM12 = m[1] / GDI_FIXEDPOINT_SCALE;
xform.eM21 = m[2] / GDI_FIXEDPOINT_SCALE;
xform.eM22 = m[3] / GDI_FIXEDPOINT_SCALE;
xform.eDx = m[4];
xform.eDy = m[5];
SetGraphicsMode(hdc, GM_ADVANCED);
SetWorldTransform(hdc, &xform);
}
/***************************************************************************\
* GenerateRandomPoints
*
\***************************************************************************/
INT
GenerateRandomPoints(
PointF *randomPoints,
INT maxPoints
)
{
INT randomPointsCount;
INT i;
// Make 1 in 32 have lotsa randomPoints:
if ((rand() & 31) == 0)
{
randomPointsCount = rand() & 511;
}
else
{
randomPointsCount = (rand() & 7) + 1;
}
randomPointsCount = min(randomPointsCount, maxPoints);
// !!! Need to randomize
switch (rand() & 3)
{
case 0: // Trivially clipped
for (i = 0; i < randomPointsCount; i++)
{
randomPoints[i].X = (rand() % (16 * WindowWidth * 16)) / 16.0f;
randomPoints[i].Y = (rand() % (16 * WindowHeight)) / 16.0f;
}
break;
case 1: // Really small
for (i = 0; i < randomPointsCount; i++)
{
randomPoints[i].X = (rand() & 127) / 16.0f + 32;
randomPoints[i].Y = (rand() & 127) / 16.0f + 32;
}
break;
default: // Big space, with at least one point inside window:
randomPoints[0].X = (rand() % (16 * WindowWidth)) / 16.0f;
randomPoints[0].Y = (rand() % (16 * WindowHeight)) / 16.0f;
if (0)
{
for (i = 1; i < randomPointsCount; i++)
{
// Once in a while, make the points REALLY REALLY big:
randomPoints[i].X = (REAL) (rand() * rand() * rand());
randomPoints[i].Y = (REAL) (rand() * rand() * rand());
}
}
else
{
for (i = 1; i < randomPointsCount; i++)
{
randomPoints[i].X = (rand() % 1000000 - 500000) / 16.0f;
randomPoints[i].Y = (rand() % 1000000 - 500000) / 16.0f;
}
}
break;
}
return(randomPointsCount);
}
/***************************************************************************\
* Draw
*
\***************************************************************************/
VOID
Draw(
HDC hdc,
BOOL doTime = FALSE
)
{
CHAR stringBuffer[200];
LONGLONG startCounter;
LONGLONG endCounter;
LONGLONG counterFrequency;
INT pointsUsed;
INT repetitions = (doTime) ? 10 : 1;
INT i;
// Clear the window:
HGDIOBJ hbrush = GetStockObject(WHITE_BRUSH);
HGDIOBJ holdBrush = SelectObject(hdc, hbrush);
PatBlt(hdc, -10000, -10000, 20000, 20000, PATCOPY);
SelectObject(hdc, holdBrush);
DeleteObject(hbrush);
QueryPerformanceCounter((LARGE_INTEGER*) &startCounter);
// Draw the stuff:
if (DoGdi)
{
SaveDC(hdc);
PrepareContext_Gdi(hdc);
for (i = 0; i < repetitions; i++)
{
pointsUsed = Render_Gdi(hdc, PrimitivePoints, PrimitivePointsCount);
}
RestoreDC(hdc, -1);
}
else
{
Graphics g(hdc);
PrepareContext_Gdiplus(&g);
if (!DoRandomTest)
{
for (i = 0; i < repetitions; i++)
{
pointsUsed = Render_Gdiplus(&g, PrimitivePoints, PrimitivePointsCount);
}
}
else
{
PointF points[512];
INT count;
// To get faster 'test' rendering (by avoiding clears between
// successive tests), always draw in a batch of '20':
for (i = 0; i < 20; i++)
{
count = GenerateRandomPoints(points, 512);
pointsUsed = Render_Gdiplus(&g, points, count);
}
}
}
// Display the time:
QueryPerformanceCounter((LARGE_INTEGER*) &endCounter);
QueryPerformanceFrequency((LARGE_INTEGER*) &counterFrequency);
float seconds = (float)(endCounter - startCounter) / counterFrequency;
INT milliseconds = (INT) (seconds * 1000 + 0.5f);
if (doTime)
{
sprintf(stringBuffer, "%li repetitions: %li ms", repetitions, milliseconds);
}
else
{
sprintf(stringBuffer, "Rasterization time: %li ms", milliseconds);
}
SetBkMode(hdc, TRANSPARENT);
ExtTextOut(hdc, 0, 0, 0, NULL, stringBuffer, strlen(stringBuffer), NULL);
if (!DoRandomTest)
{
// Now that we're out of the timing loop, draw our control points:
Graphics g(hdc);
if (DoAnchors)
{
DrawAnchors(&g, PrimitivePoints, pointsUsed);
}
if (DoTransformOverlay)
{
DrawTransformOverlay(&g);
}
}
}
/***************************************************************************\
* EnterWidth
*
* Dialog for entering pen width.
\***************************************************************************/
INT_PTR EnterWidth(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
BOOL bTrans;
switch (message)
{
case WM_INITDIALOG:
SetDlgItemInt(hDlg, IDD_WIDTH, RenderWidth, TRUE);
return(TRUE);
case WM_COMMAND:
if (wParam == IDD_OK)
{
RenderWidth = GetDlgItemInt(hDlg, IDD_WIDTH, &bTrans, TRUE);
EndDialog(hDlg, wParam);
InvalidateRect(ghwndMain, NULL, TRUE);
}
break;
case WM_SETFOCUS:
SetFocus(GetDlgItem(hDlg, IDD_WIDTH));
return(FALSE);
default:
return(FALSE);
}
return(TRUE);
}
/***************************************************************************\
* EnterAlpha
*
* Dialog for entering pen alpha.
\***************************************************************************/
INT_PTR EnterAlpha(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
BOOL bTrans;
switch (message)
{
case WM_INITDIALOG:
SetDlgItemInt(hDlg, IDD_ALPHA, RenderAlpha, TRUE);
return(TRUE);
case WM_COMMAND:
if (wParam == IDD_OK)
{
RenderAlpha = GetDlgItemInt(hDlg, IDD_ALPHA, &bTrans, TRUE);
EndDialog(hDlg, wParam);
InvalidateRect(ghwndMain, NULL, TRUE);
}
break;
case WM_SETFOCUS:
SetFocus(GetDlgItem(hDlg, IDD_ALPHA));
return(FALSE);
default:
return(FALSE);
}
return(TRUE);
}
/***************************************************************************\
* EnterPoints
*
* Dialog for entering points.
\***************************************************************************/
INT_PTR EnterPoints(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
BOOL bTrans;
INT i;
switch (message)
{
case WM_INITDIALOG:
SetDlgItemInt(hDlg, IDD_POINT1X, ROUND(PrimitivePoints[0].X), TRUE);
SetDlgItemInt(hDlg, IDD_POINT1Y, ROUND(PrimitivePoints[0].Y), TRUE);
SetDlgItemInt(hDlg, IDD_POINT2X, ROUND(PrimitivePoints[1].X), TRUE);
SetDlgItemInt(hDlg, IDD_POINT2Y, ROUND(PrimitivePoints[1].Y), TRUE);
SetDlgItemInt(hDlg, IDD_POINT3X, ROUND(PrimitivePoints[2].X), TRUE);
SetDlgItemInt(hDlg, IDD_POINT3Y, ROUND(PrimitivePoints[2].Y), TRUE);
SetDlgItemInt(hDlg, IDD_POINT4X, ROUND(PrimitivePoints[3].X), TRUE);
SetDlgItemInt(hDlg, IDD_POINT4Y, ROUND(PrimitivePoints[3].Y), TRUE);
return(TRUE);
case WM_COMMAND:
if (wParam == IDD_OK)
{
PrimitivePoints[0].X = (REAL) (INT) GetDlgItemInt(hDlg, IDD_POINT1X, &bTrans, TRUE);
PrimitivePoints[0].Y = (REAL) (INT) GetDlgItemInt(hDlg, IDD_POINT1Y, &bTrans, TRUE);
PrimitivePoints[1].X = (REAL) (INT) GetDlgItemInt(hDlg, IDD_POINT2X, &bTrans, TRUE);
PrimitivePoints[1].Y = (REAL) (INT) GetDlgItemInt(hDlg, IDD_POINT2Y, &bTrans, TRUE);
PrimitivePoints[2].X = (REAL) (INT) GetDlgItemInt(hDlg, IDD_POINT3X, &bTrans, TRUE);
PrimitivePoints[2].Y = (REAL) (INT) GetDlgItemInt(hDlg, IDD_POINT3Y, &bTrans, TRUE);
PrimitivePoints[3].X = (REAL) (INT) GetDlgItemInt(hDlg, IDD_POINT4X, &bTrans, TRUE);
PrimitivePoints[3].Y = (REAL) (INT) GetDlgItemInt(hDlg, IDD_POINT4Y, &bTrans, TRUE);
EndDialog(hDlg, wParam);
InvalidateRect(ghwndMain, NULL, TRUE);
}
break;
case WM_SETFOCUS:
SetFocus(GetDlgItem(hDlg, IDD_POINT1X));
return(FALSE);
default:
return(FALSE);
}
return(TRUE);
}
/***************************************************************************\
* EnterTransform
*
* Dialog for entering arbitrary transforms.
\***************************************************************************/
INT_PTR EnterTransform(
HWND hDlg,
WORD message,
WPARAM wParam,
LONG lParam)
{
BOOL bTrans;
REAL m[6];
PrimitiveTransform->GetElements(m);
switch (message)
{
case WM_INITDIALOG:
SetDlgItemInt(hDlg, IDD_M11, ROUND(m[0] * 1000.0f), TRUE);
SetDlgItemInt(hDlg, IDD_M12, ROUND(m[1] * 1000.0f), TRUE);
SetDlgItemInt(hDlg, IDD_M21, ROUND(m[2] * 1000.0f), TRUE);
SetDlgItemInt(hDlg, IDD_M22, ROUND(m[3] * 1000.0f), TRUE);
SetDlgItemInt(hDlg, IDD_M31, ROUND(m[4] * 1000.0f), TRUE);
SetDlgItemInt(hDlg, IDD_M32, ROUND(m[5] * 1000.0f), TRUE);
return(TRUE);
case WM_COMMAND:
if (wParam == IDD_OK)
{
m[0] = ((INT) GetDlgItemInt(hDlg, IDD_M11, &bTrans, TRUE)) / 1000.0f;
m[1] = ((INT) GetDlgItemInt(hDlg, IDD_M12, &bTrans, TRUE)) / 1000.0f;
m[2] = ((INT) GetDlgItemInt(hDlg, IDD_M21, &bTrans, TRUE)) / 1000.0f;
m[3] = ((INT) GetDlgItemInt(hDlg, IDD_M22, &bTrans, TRUE)) / 1000.0f;
m[4] = ((INT) GetDlgItemInt(hDlg, IDD_M31, &bTrans, TRUE)) / 1000.0f;
m[5] = ((INT) GetDlgItemInt(hDlg, IDD_M32, &bTrans, TRUE)) / 1000.0f;
PrimitiveTransform->SetElements(m[0], m[1], m[2], m[3], m[4], m[5]);
PrimitiveInverseTransform->SetElements(m[0], m[1], m[2], m[3], m[4], m[5]);
PrimitiveInverseTransform->Invert();
// Calculate the new world-space elbow location:
OverlayOffset.X = OverlayPoints[1].X;
OverlayOffset.Y = OverlayPoints[1].Y;
PrimitiveInverseTransform->TransformPoints(&OverlayOffset);
// Now calculate the new device-space end-points, by initializing
// in world-space and then converting back to device-space:
OverlayPoints[0].X = OverlayOffset.X + OverlayDimension;
OverlayPoints[0].Y = OverlayOffset.Y;
OverlayPoints[2].X = OverlayOffset.X;
OverlayPoints[2].Y = OverlayOffset.Y - OverlayDimension;
PrimitiveTransform->TransformPoints(&OverlayPoints[0]);
PrimitiveTransform->TransformPoints(&OverlayPoints[2]);
// We're done; force a redraw:
EndDialog(hDlg, wParam);
InvalidateRect(ghwndMain, NULL, TRUE);
}
break;
case WM_SETFOCUS:
SetFocus(GetDlgItem(hDlg, IDD_OK));
return(FALSE);
default:
return(FALSE);
}
return(TRUE);
}
/***************************************************************************\
* ComputeOverlayTransformFromPoints
*
\***************************************************************************/
VOID
ComputeOverlayTransformFromPoints()
{
REAL dx1 = OverlayPoints[0].X - OverlayPoints[1].X;
REAL dy1 = OverlayPoints[0].Y - OverlayPoints[1].Y;
REAL dx2 = OverlayPoints[1].X - OverlayPoints[2].X;
REAL dy2 = OverlayPoints[1].Y - OverlayPoints[2].Y;
REAL xMid = (REAL) (WindowWidth >> 1);
REAL yMid = (REAL) (WindowHeight >> 1);
RectF srcRect(OverlayOffset.X, OverlayOffset.Y, OverlayDimension, OverlayDimension);
// The order is upper-left, upper-right, lower-left corner:
PointF dstPoints[] = { PointF(xMid, yMid),
PointF(xMid + dx1, yMid + dy1),
PointF(xMid + dx2, yMid + dy2) };
delete PrimitiveTransform;
PrimitiveTransform = new Matrix(srcRect, dstPoints);
delete PrimitiveInverseTransform;
PrimitiveInverseTransform = PrimitiveTransform->Clone();
PrimitiveInverseTransform->Invert();
}
/***************************************************************************\
* CreateOverlayTransform
*
\***************************************************************************/
VOID
CreateOverlayTransform()
{
REAL xMid = (REAL) (WindowWidth >> 1);
REAL yMid = (REAL) (WindowHeight >> 1);
OverlayPoints[0].X = xMid + OverlayDimension;
OverlayPoints[0].Y = yMid;
OverlayPoints[1].X = xMid;
OverlayPoints[1].Y = yMid;
OverlayPoints[2].X = xMid;
OverlayPoints[2].Y = yMid - OverlayDimension;
OverlayOffset.X = xMid;
OverlayOffset.Y = yMid;
ComputeOverlayTransformFromPoints();
}
/***************************************************************************\
* UpdateOverlay
*
\***************************************************************************/
VOID
UpdateOverlay(
REAL x,
REAL y
)
{
if (OverlayDragVertex == 1)
{
// The root of the overlay is being moved, so we move the overlay
// as a whole:
REAL dx = x - OverlayPoints[1].X;
REAL dy = y - OverlayPoints[1].Y;
OverlayPoints[0].X += dx;
OverlayPoints[0].Y += dy;
OverlayPoints[1].X = x;
OverlayPoints[1].Y = y;
OverlayPoints[2].X += dx;
OverlayPoints[2].Y += dy;
}
else
{
OverlayPoints[OverlayDragVertex].X = x;
OverlayPoints[OverlayDragVertex].Y = y;
ComputeOverlayTransformFromPoints();
}
}
/***************************************************************************\
* RecenterOverlay
*
\***************************************************************************/
VOID
RecenterOverlay()
{
REAL xMid = (REAL) (WindowWidth >> 1);
REAL yMid = (REAL) (WindowHeight >> 1);
REAL dx = xMid - OverlayPoints[1].X;
REAL dy = yMid - OverlayPoints[1].Y;
// Center the transform around the new world-space focus point:
OverlayOffset.X = OverlayPoints[1].X;
OverlayOffset.Y = OverlayPoints[1].Y;
if (PrimitiveInverseTransform != NULL)
{
PrimitiveInverseTransform->TransformPoints(&OverlayOffset, 1);
}
// Bring the overlay control back to the middle of the screen:
OverlayPoints[0].X += dx;
OverlayPoints[0].Y += dy;
OverlayPoints[1].X = xMid;
OverlayPoints[1].Y = yMid;
OverlayPoints[2].X += dx;
OverlayPoints[2].Y += dy;
ComputeOverlayTransformFromPoints();
}
/***************************************************************************\
* FindNearest
*
\***************************************************************************/
INT
FindNearest(
REAL x, // Device space
REAL y,
const PointF *points, // World space
INT count,
const Matrix *matrix = NULL // World-to-device transform
)
{
INT i;
REAL d;
REAL minDistance;
INT vertex;
PointF inputPoint(x, y);
// Find the nearest vertex, using a simple Manhattan metric.
minDistance = 100000;
for (i = 0; i < count; i++)
{
PointF point(points[i]);
// For the distance metric, we want to be doing our calculations
// in device space:
if (matrix)
{
matrix->TransformPoints(&point, 1);
}
d = ABS(x - point.X) + ABS(y - point.Y);
if (d < minDistance)
{
minDistance = d;
vertex = i;
}
}
return((minDistance < HIT_DISTANCE) ? vertex : -1);
}
/***************************************************************************\
* MainWindowProc(hwnd, message, wParam, lParam)
*
* Processes all messages for the main window.
*
* History:
* 04-07-91 -by- KentD
* Wrote it.
\***************************************************************************/
LRESULT
MainWindowProc(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
PointF point;
LONG i;
LONG d;
LONG minDistance;
LONG vertex;
HDC hdc;
PAINTSTRUCT ps;
HMENU hmenu = GetMenu(hwnd);
WORD mmCommand = LOWORD(wParam);
switch (message)
{
case WM_CREATE:
// NOTICE-DavePr@2002/05/28
// Missing FreeProcInstance for these anyway.
//glpfnEnterWidth = (FARPROC) MakeProcInstance(EnterWidth, ghwndMain);
//glpfnEnterAlpha = (FARPROC) MakeProcInstance(EnterAlpha, ghwndMain);
//glpfnEnterPoints = (FARPROC) MakeProcInstance(EnterPoints, ghwndMain);
//glpfnEnterTransform = (FARPROC) MakeProcInstance(EnterTransform, ghwndMain);
SetTimer(hwnd, 1, 80, NULL);
break;
case WM_COMMAND:
switch(mmCommand)
{
case MM_RANDOMTEST:
DoRandomTest = !DoRandomTest;
if (!DoRandomTest)
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_WIDTH:
//DialogBox(ghInstance, "Width", ghwndMain, glpfnEnterWidth);
DialogBox(ghInstance, "Width", ghwndMain, EnterWidth);
CreatePens();
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_ALPHA:
//DialogBox(ghInstance, "Alpha", ghwndMain, glpfnEnterAlpha);
DialogBox(ghInstance, "Alpha", ghwndMain, EnterAlpha);
CreatePens();
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_POINTS:
//DialogBox(ghInstance, "Points", ghwndMain, glpfnEnterPoints);
DialogBox(ghInstance, "Points", ghwndMain, EnterPoints);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_EDITTRANSFORM:
//DialogBox(ghInstance, "Transform", ghwndMain, glpfnEnterTransform);
DialogBox(ghInstance, "Transform", ghwndMain, EnterTransform);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_REDRAW:
hdc = GetDC(hwnd);
Draw(hdc);
ReleaseDC(hwnd, hdc);
break;
case MM_TIME:
hdc = GetDC(hwnd);
Draw(hdc, TRUE);
ReleaseDC(hwnd, hdc);
break;
case MM_CLIPGRID:
DoClipGrid = !DoClipGrid;
CheckMenuItem(hmenu, mmCommand, DoClipGrid ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_ANTIALIAS:
DoAntialias = !DoAntialias;
CheckMenuItem(hmenu, mmCommand, DoAntialias ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_GAMMACORRECT:
DoGammaCorrect = !DoGammaCorrect;
CheckMenuItem(hmenu, mmCommand, DoGammaCorrect ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_WINDING:
DoWindingFill = !DoWindingFill;
CheckMenuItem(hmenu, mmCommand, DoWindingFill ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_SPINE:
DoSpine = !DoSpine;
CheckMenuItem(hmenu, mmCommand, DoSpine ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_WIDENPATH:
DoWiden = !DoWiden;
CheckMenuItem(hmenu, mmCommand, DoWiden ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_ANCHORS:
DoAnchors = !DoAnchors;
CheckMenuItem(hmenu, mmCommand, DoAnchors ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_TRANSFORMOVERLAY:
DoTransformOverlay = !DoTransformOverlay;
CheckMenuItem(hmenu, mmCommand, DoTransformOverlay ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_SCALINGONLY:
DoScalingOnly = !DoScalingOnly;
CheckMenuItem(hmenu, mmCommand, DoScalingOnly ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_RESETTRANSFORM:
CreateOverlayTransform();
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_FILL:
DoFill = !DoFill;
CheckMenuItem(hmenu, mmCommand, DoFill ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_DRAW:
DoDraw = !DoDraw;
CheckMenuItem(hmenu, mmCommand, DoDraw ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_POLYGON:
case MM_LINES:
case MM_BEZIER:
case MM_RECTANGLE:
case MM_ELLIPSE:
case MM_TEXTPATH:
CheckMenuItem(hmenu, MmPrimitive, MF_UNCHECKED);
MmPrimitive = mmCommand;
CheckMenuItem(hmenu, MmPrimitive, MF_CHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_COMPOUND:
DoCompound = !DoCompound;
CheckMenuItem(hmenu, mmCommand, DoCompound ? MF_CHECKED : MF_UNCHECKED);
CreatePens();
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_STYLE_SOLID:
case MM_STYLE_DASH:
case MM_STYLE_DOT:
case MM_STYLE_DASHDOT:
case MM_STYLE_DASHDOTDOT:
CheckMenuItem(hmenu, MmDashStyle, MF_UNCHECKED);
MmDashStyle = mmCommand;
CheckMenuItem(hmenu, MmDashStyle, MF_CHECKED);
CreatePens();
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_CAP_FLAT:
case MM_CAP_SQUARE:
case MM_CAP_ROUND:
case MM_CAP_TRIANGLE:
CheckMenuItem(hmenu, MmEndCap, MF_UNCHECKED);
MmEndCap = mmCommand;
CheckMenuItem(hmenu, MmEndCap, MF_CHECKED);
CreatePens();
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_JOIN_ROUND:
case MM_JOIN_MITER:
case MM_JOIN_BEVEL:
CheckMenuItem(hmenu, MmJoin, MF_UNCHECKED);
MmJoin = mmCommand;
CheckMenuItem(hmenu, MmJoin, MF_CHECKED);
CreatePens();
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_ALIGNMENT_CENTER:
case MM_ALIGNMENT_INSET:
CheckMenuItem(hmenu, MmAlignment, MF_UNCHECKED);
MmAlignment = mmCommand;
CheckMenuItem(hmenu, MmAlignment, MF_CHECKED);
CreatePens();
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_WRAP_TILE:
case MM_WRAP_CLAMP:
case MM_WRAP_FLIPX:
case MM_WRAP_FLIPY:
case MM_WRAP_FLIPXY:
CheckMenuItem(hmenu, MmWrapMode, MF_UNCHECKED);
MmWrapMode = mmCommand;
CheckMenuItem(hmenu, MmWrapMode, MF_CHECKED);
CreateBrushes();
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_BRUSH_SOLID:
case MM_BRUSH_TEXTURE:
case MM_BRUSH_TEXTURE_32x32:
case MM_BRUSH_TEXTURE_1x1:
case MM_BRUSH_LINEAR:
case MM_BRUSH_PATHGRADIENT:
CheckMenuItem(hmenu, MmBrushType, MF_UNCHECKED);
MmBrushType = mmCommand;
CheckMenuItem(hmenu, MmBrushType, MF_CHECKED);
CreateBrushes();
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_DYNAMICBRUSHRECTANGLE:
DoBrushRect = !DoBrushRect;
CheckMenuItem(hmenu, mmCommand, DoBrushRect ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_EDITBRUSHSHAPE:
DoShape = !DoShape;
CheckMenuItem(hmenu, mmCommand, DoShape ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
case MM_GDI:
DoGdi = !DoGdi;
CheckMenuItem(hmenu, mmCommand, DoGdi ? MF_CHECKED : MF_UNCHECKED);
InvalidateRect(hwnd, NULL, TRUE);
break;
default:
break;
}
break;
case WM_SIZE:
WindowWidth = (short)LOWORD(lParam);
WindowHeight = (short)HIWORD(lParam);
CreateRegion_Gdiplus();
CreateRegion_Gdi();
RecenterOverlay();
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_LBUTTONDOWN:
point.X = (REAL)(short)LOWORD(lParam);
point.Y = (REAL)(short)HIWORD(lParam);
// First, try to find a hit with the overlay. Then try a hit with
// the primitive points, in world space:
OverlayDragVertex = -1;
PrimitiveDragVertex = -1;
if (DoTransformOverlay)
{
OverlayDragVertex = FindNearest(point.X, point.Y, OverlayPoints, 3);
}
if (OverlayDragVertex == -1)
{
PrimitiveDragVertex = FindNearest(point.X, point.Y, PrimitivePoints,
PrimitivePointsCount, PrimitiveTransform);
}
// The first left-click disables 'adding points' mode:
IsAddingPoints = FALSE;
break;
case WM_RBUTTONDOWN:
point.X = (REAL)(short)LOWORD(lParam);
point.Y = (REAL)(short)HIWORD(lParam);
PrimitiveInverseTransform->TransformPoints(&point, 1);
// If we were in 'adding points' mode (which occurs when we're
// right-clicking in succession), simply add the point to the
// list.
//
// If we're not in 'adding points' mode, reset the point list
// and switch us to 'adding points' mode:
if (!IsAddingPoints)
{
IsAddingPoints = TRUE;
PrimitivePointsCount = 0;
}
// Add this point to the list:
if (PrimitivePointsCount < PrimitivePointsMax)
{
PrimitivePoints[PrimitivePointsCount] = point;
PrimitivePointsCount++;
// If this was the first point, make all the points the same
// (in part to make 'ellipse' and 'rectangle' properly empty):
if (PrimitivePointsCount == 1)
{
for (i = 1; i < PrimitivePointsMax; i++)
{
PrimitivePoints[i] = PrimitivePoints[0];
}
}
hdc = GetDC(hwnd);
Draw(hdc);
ReleaseDC(hwnd, hdc);
}
break;
case WM_MOUSEMOVE:
point.X = (REAL)(short)LOWORD(lParam);
point.Y = (REAL)(short)HIWORD(lParam);
// Overlay hit-testing works in screen space:
if (OverlayDragVertex != -1)
{
// To prevent extraneous redraws, redraw only if the new point
// is different:
if ((OverlayPoints[OverlayDragVertex].X != point.X) ||
(OverlayPoints[OverlayDragVertex].Y != point.Y))
{
UpdateOverlay(point.X, point.Y);
hdc = GetDC(hwnd);
Draw(hdc);
ReleaseDC(hwnd, hdc);
}
}
// Primitive hit-testing works in world space:
PrimitiveInverseTransform->TransformPoints(&point, 1);
if (PrimitiveDragVertex != -1)
{
// To prevent extraneous redraws, redraw only if the new point
// is different:
if ((PrimitivePoints[PrimitiveDragVertex].X != point.X) ||
(PrimitivePoints[PrimitiveDragVertex].Y != point.Y))
{
PrimitivePoints[PrimitiveDragVertex] = point;
hdc = GetDC(hwnd);
Draw(hdc);
ReleaseDC(hwnd, hdc);
}
}
break;
case WM_LBUTTONUP:
PrimitiveDragVertex = -1;
OverlayDragVertex = -1;
RecenterOverlay();
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
Draw(hdc);
ReleaseDC(hwnd, hdc);
break;
case WM_TIMER:
if (DoRandomTest)
{
hdc = GetDC(hwnd);
Draw(hdc);
ReleaseDC(hwnd, hdc);
}
break;
case WM_DESTROY:
DeleteObjects_Gdiplus();
DeleteObjects_Gdi();
DeleteObject(ghbrWhite);
PostQuitMessage(0);
return(DefWindowProc(hwnd, message, wParam, lParam));
default:
return(DefWindowProc(hwnd, message, wParam, lParam));
}
return(0);
}
/***************************************************************************\
* InitializeApplication()
*
* Initializes app.
*
* History:
* 04-07-91 -by- KentD
* Wrote it.
\***************************************************************************/
BOOL InitializeApplication(VOID)
{
WNDCLASS wc;
ghbrWhite = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.style = 0;
wc.lpfnWndProc = MainWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = ghInstance;
wc.hIcon = LoadIcon(ghInstance, MAKEINTRESOURCE(POLYTESTICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = ghbrWhite;
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "TestClass";
if (!RegisterClass(&wc))
{
return(FALSE);
}
ghwndMain =
CreateWindowEx(
0,
"TestClass",
"PolyTest",
(
WS_OVERLAPPED |
WS_CAPTION |
WS_BORDER |
WS_THICKFRAME |
WS_MAXIMIZEBOX |
WS_MINIMIZEBOX |
WS_CLIPCHILDREN |
WS_VISIBLE |
WS_SYSMENU
),
80,
70,
500,
500,
NULL,
NULL,
ghInstance,
NULL
);
if (ghwndMain == NULL)
{
return(FALSE);
}
SetFocus(ghwndMain);
// Create our initialize stuff:
CreateBrushes();
CreatePens();
CreateOverlayTransform();
return(TRUE);
}
/***************************************************************************\
* main(argc, argv[])
*
* Sets up the message loop.
*
* History:
* 04-07-91 -by- KentD
* Wrote it.
\***************************************************************************/
_cdecl
main(
INT argc,
PCHAR argv[])
{
MSG msg;
HACCEL haccel;
CHAR* pSrc;
CHAR* pDst;
if (!gGdiplusInitHelper.IsValid())
{
return 0;
}
ghInstance = GetModuleHandle(NULL);
if (!InitializeApplication())
{
return(0);
}
haccel = LoadAccelerators(ghInstance, MAKEINTRESOURCE(ACCELS));
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, haccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return(1);
}