/** \file mswdraw.c * Draw basic geometric shapes */ /* XTrackCAD - Model Railroad CAD * Copyright (C) 2005 Dave Bullis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _WIN32_WINNT 0x0600 /* for wheel mouse supposrt */ #include #include #include #include #include #include #define wFont_t tagLOGFONTA #include "mswint.h" #include wBool_t wDrawDoTempDraw = TRUE; /* ***************************************************************************** * * Draw * ***************************************************************************** */ #define M_PI 3.14159265358979323846 static wBool_t initted = FALSE; static FARPROC oldDrawProc; static long tmpOp = 0x990066; static long setOp = 0x8800c6; static long clrOp = 0xbb0226; #define CENTERMARK_LENGTH 4 static bool bDrawMainBM = 0; typedef struct { double x, y; } coOrd; #ifdef SLOW static wDrawPix_t XWINPIX2DRAWPIX( wDraw_p d, wWinPix_t ix ) { return (wDrawPix_t)ix; } static wDrawPix_t YWINPIX2DRAWPIX( wDraw_p d, wWinPix_t iy ) { wWinPix_t y; y = (wDrawPix_t)(d->h-2-iy); return y; } static wWinPix_t XDRAWPIX2WINPIX( wDraw_p d, wDrawPix_t xx ) { wWinPix_t ix; ix = (wWinPix_t)(xx); return ix; } static wWinPix_t YDRAWPIX2WINPIX( wDraw_p d, wDrawPix_t y ) { wWinPix_t iy; iy = (d->h)-2 - (wWinPix_t)(y); return iy; } #else #define XWINPIX2DRAWPIX( d, ix ) \ ((wDrawPix_t)ix) #define YWINPIX2DRAWPIX( d, iy ) \ ((wDrawPix_t)(d->h-2-iy)) #define XDRAWPIX2WINPIX( d, xx ) \ ((wWinPix_t)(xx)) #define YDRAWPIX2WINPIX( d, y ) \ (d->h - 2 - (wWinPix_t)(y)) #endif /* ***************************************************************************** * * Basic Line Draw * ***************************************************************************** */ void wDrawDelayUpdate( wDraw_p d, wBool_t delay ) { } wBool_t wDrawSetTempMode( wDraw_p bd, wBool_t bTemp ) { wBool_t rc = bd->bTempMode; bd->bTempMode = bTemp; if (rc == FALSE && bTemp == TRUE) { // Main to Temp drawing // Copy mainBM to tempBM wDrawClearTemp( bd ); if (bDrawMainBM) { return rc; } HDC hDcOld = CreateCompatibleDC(bd->hDc); HBITMAP hBmOld = SelectObject(hDcOld, bd->hBmMain); SelectObject(bd->hDc, bd->hBmTemp); BitBlt(bd->hDc, 0, 0, bd->w, bd->h, hDcOld, 0, 0, SRCCOPY); SelectObject(hDcOld, hBmOld); DeleteDC(hDcOld); bd->bCopiedMain = TRUE; } return rc; } /** * Sets the proper pen and composition for the next drawing operation * * * \param d IN drawing context * \param dw IN line width * \param lt IN line type (dashed, solid, ...) * \param dc IN color * \param dopt IN drawing options */ static void setDrawMode( wDraw_p d, wDrawWidth dw, wDrawLineType_e lt, wDrawColor dc, wDrawOpts dopt ) { long centerPen[] = {40,10,20,10}; long phantomPen[] = {40,10,20,10,20,10}; HPEN hOldPen; static wDraw_p d0; static wDrawWidth dw0 = -1; static wDrawLineType_e lt0 = (wDrawLineType_e)-1; static wDrawColor dc0 = -1; static LOGBRUSH logBrush = { 0, 0, 0 }; DWORD penStyle; if ( wDrawDoTempDraw && (dopt & wDrawOptTemp) ) { SelectObject(d->hDc, d->hBmTemp); } else { SelectObject(d->hDc, d->hBmMain); } if ( d->hasPalette ) { int winPaletteClock = mswGetPaletteClock(); if ( d->paletteClock < winPaletteClock ) { RealizePalette( d->hDc ); d->paletteClock = winPaletteClock; } } SetROP2( d->hDc, R2_COPYPEN ); if ( d == d0 && dw0 == dw && lt == lt0 && dc == dc0 ) { return; } // make sure that the line width is at least 1! if( !dw ) { dw++; } d0 = d; dw0 = dw; lt0 = lt; dc0 = dc; void * penarray = NULL; int penarray_size = 0; logBrush.lbColor = mswGetColor(d->hasPalette,dc); if ( lt==wDrawLineSolid ) { penStyle = PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT; } else if (lt == wDrawLineDot) { penStyle = PS_GEOMETRIC | PS_DOT; } else if (lt == wDrawLineDash) { penStyle = PS_GEOMETRIC | PS_DASH; } else if (lt == wDrawLineDashDot) { penStyle = PS_GEOMETRIC | PS_DASHDOT; } else if ( lt == wDrawLineDashDotDot) { penStyle = PS_GEOMETRIC | PS_DASHDOTDOT; } else if ( lt == wDrawLineCenter) { penStyle = PS_GEOMETRIC | PS_USERSTYLE; penarray = ¢erPen; penarray_size = sizeof(centerPen)/sizeof(long); } else if ( lt == wDrawLinePhantom) { penStyle = PS_GEOMETRIC | PS_USERSTYLE; penarray = &phantomPen; penarray_size = sizeof(phantomPen) / sizeof(long); } else { penStyle = PS_GEOMETRIC | PS_SOLID; } d->hPen = ExtCreatePen( penStyle, dw, &logBrush, penarray_size, penarray ); hOldPen = SelectObject( d->hDc, d->hPen ); DeleteObject( hOldPen ); } static void setDrawBrush( wDraw_p d, wDrawColor dc, wDrawOpts dopt ) { HBRUSH hOldBrush; static wDraw_p d0; static wDrawColor dc0 = -1; setDrawMode( d, 0, wDrawLineSolid, dc, dopt ); if ( d == d0 && dc == dc0 ) { return; } d0 = d; dc0 = dc; d->hBrush = CreateSolidBrush( mswGetColor(d->hasPalette,dc) ); hOldBrush = SelectObject( d->hDc, d->hBrush ); DeleteObject( hOldBrush ); } static void myInvalidateRect( wDraw_p d, RECT * prect ) { if ( prect->top < 0 ) { prect->top = 0; } if ( prect->left < 0 ) { prect->left = 0; } if ( prect->bottom > d->h ) { prect->bottom = d->h; } if ( prect->right > d->w ) { prect->right = d->w; } InvalidateRect( d->hWnd, prect, FALSE ); } static int clip0( POINT * p0, POINT * p1, wDraw_p d ) { long int x0=p0->x, y0=p0->y, x1=p1->x, y1=p1->y; long int dx, dy; if ( x0<0 && x1<0 ) { return 0; } if ( y0<0 && y1<0 ) { return 0; } dx=x1-x0; dy=y1-y0; if ( x0 < 0 ) { y0 -= x0*dy/dx; x0 = 0; } if ( y0 < 0 ) { if ( (x0 -= y0*dx/dy) < 0 ) { return 0; } y0 = 0; } if ( x1 < 0 ) { y1 -= x1*dy/dx; x1 = 0; } if ( y1 < 0 ) { if ( (x1 -= y1*dx/dy) < 0 ) { return 0; } y1 = 0; } p0->x = (int)x0; p0->y = (int)y0; p1->x = (int)x1; p1->y = (int)y1; return 1; } void wDrawLine( wDraw_p d, wDrawPix_t p0x, wDrawPix_t p0y, wDrawPix_t p1x, wDrawPix_t p1y, wDrawWidth dw, wDrawLineType_e lt, wDrawColor dc, wDrawOpts dopt ) { POINT p0, p1; RECT rect; setDrawMode( d, dw, lt, dc, dopt ); p0.x = XDRAWPIX2WINPIX(d,p0x); p0.y = YDRAWPIX2WINPIX(d,p0y); p1.x = XDRAWPIX2WINPIX(d,p1x); p1.y = YDRAWPIX2WINPIX(d,p1y); MoveTo( d->hDc, p0.x, p0.y ); LineTo( d->hDc, p1.x, p1.y ); if (d->hWnd) { if (dw==0) { dw = 1; } dw++; if (p0.y= 360.0) { angle -= 360.0; } angle *= (M_PI*2.0)/360.0; return sin( angle ); } static double mswcos( double angle ) { while (angle < 0.0) { angle += 360.0; } while (angle >= 360.0) { angle -= 360.0; } angle *= (M_PI*2.0)/360.0; return cos( angle ); } static double mswasin( double x, double h ) { double angle; angle = asin( x/h ); angle /= (M_PI*2.0)/360.0; return angle; } static double mswNormalizeAngle( double a ) { while (a<0.0) { a += 360.0; } while (a>=360.0) { a -= 360.0; } return a; } /** * Draw an arc around a specified center * * \param d IN ? * \param px, py IN center of arc * \param r IN radius * \param a0, a1 IN start and end angle * \param drawCenter draw marking for center * \param dw line width * \param lt line type * \param dc color * \param dopt ? */ void wDrawArc( wDraw_p d, wDrawPix_t px, wDrawPix_t py, wDrawPix_t r, double a0, double a1, int sizeCenter, wDrawWidth dw, wDrawLineType_e lt, wDrawColor dc, wDrawOpts dopt ) { int i, cnt; POINT p0, p1, ps, pe, pp0, pp1, pp2, pc; wDrawPix_t psx, psy, pex, pey; double aa, ai; RECT rect; int needMoveTo; wBool_t fakeArc = FALSE; // calculate the center coordinates pc.x = XDRAWPIX2WINPIX( d, px ); pc.y = YDRAWPIX2WINPIX( d, py ); p0.x = XDRAWPIX2WINPIX(d,px-r); p0.y = YDRAWPIX2WINPIX(d,py+r); p1.x = XDRAWPIX2WINPIX(d,px+r); p1.y = YDRAWPIX2WINPIX(d,py-r); pex = px + r * mswsin(a0); pey = py + r * mswcos(a0); psx = px + r * mswsin(a0+a1); psy = py + r * mswcos(a0+a1); /*pointOnCircle( &pe, p, r, a0 ); pointOnCircle( &ps, p, r, a0+a1 );*/ ps.x = XDRAWPIX2WINPIX(d,psx); ps.y = YDRAWPIX2WINPIX(d,psy); pe.x = XDRAWPIX2WINPIX(d,pex); pe.y = YDRAWPIX2WINPIX(d,pey); setDrawMode( d, dw, lt, dc, dopt ); if (dw == 0) { dw = 1; } if ( r > 30000 || a1 < 1.0 ) { /* The book says 32K but experience says otherwise */ fakeArc = TRUE; } // Starting point psx = px + r * mswsin(a0); psy = py + r * mswcos(a0); pp0.x = XDRAWPIX2WINPIX( d, psx ); pp0.y = YDRAWPIX2WINPIX( d, psy ); if ( fakeArc ) { cnt = (int)(a1 / 2); if ( cnt <= 0 ) { cnt = 1; } if ( cnt > 180 ) { cnt = 180; } ai = d2r(a1) / cnt; aa = d2r(a0); needMoveTo = TRUE; for ( i=0; ihDc, pp0.x, pp0.y ); needMoveTo = FALSE; } LineTo( d->hDc, pp1.x, pp1.y ); } else { needMoveTo = TRUE; } pp0.x = pp2.x; pp0.y = pp2.y; } } else { DWORD rr = XDRAWPIX2WINPIX( d, r ); SetArcDirection( d->hDc,AD_CLOCKWISE ); // Draw two arcs from the center to eliminate the odd pie-shaped end artifact if ( dw > 2.0 ) { double a2 = a1 / 2.0; pp2.x = XDRAWPIX2WINPIX( d, px + r * mswsin(a0+a2) ); pp2.y = YDRAWPIX2WINPIX( d, py + r * mswcos(a0+a2) ); MoveTo( d->hDc, pp2.x, pp2.y ); AngleArc( d->hDc, pc.x, pc.y, rr, (float)mswNormalizeAngle(90 - (a0+a2)), (float)(-a2) ); MoveTo( d->hDc, pp2.x, pp2.y ); AngleArc( d->hDc, pc.x, pc.y, rr, (float)mswNormalizeAngle(90 - (a0+a2)), (float)(a2) ); } else { MoveTo( d->hDc, pp0.x, pp0.y ); AngleArc( d->hDc, pc.x, pc.y, rr, (float)mswNormalizeAngle(90 - a0), (float)(-a1) ); } } // should the center of the arc be drawn? if( sizeCenter ) { // now draw the crosshair MoveTo( d->hDc, pc.x - CENTERMARK_LENGTH*sizeCenter, pc.y ); LineTo( d->hDc, pc.x + CENTERMARK_LENGTH*sizeCenter, pc.y ); MoveTo( d->hDc, pc.x, pc.y - CENTERMARK_LENGTH*sizeCenter ); LineTo( d->hDc, pc.x, pc.y + CENTERMARK_LENGTH*sizeCenter ); // invalidate the area of the crosshair rect.top = pc.y - CENTERMARK_LENGTH*sizeCenter - 1; rect.bottom = pc.y + CENTERMARK_LENGTH*sizeCenter + 1; rect.left = pc.x - CENTERMARK_LENGTH*sizeCenter - 1; rect.right = pc.x + CENTERMARK_LENGTH*sizeCenter + 1; myInvalidateRect( d, &rect ); } if (d->hWnd) { dw++; a1 += a0; if (a1>360.0) { rect.top = p0.y; } else { rect.top = min(pe.y,ps.y); } if (a1>(a0>180?360.0:0.0)+180) { rect.bottom = p1.y; } else { rect.bottom = max(pe.y,ps.y); } if (a1>(a0>270?360.0:0.0)+270) { rect.left = p0.x; } else { rect.left = min(pe.x,ps.x); } if (a1>(a0>90?360.0:0.0)+90) { rect.right = p1.x; } else { rect.right = max(pe.x,ps.x); } rect.top -= dw; rect.bottom += dw; rect.left -= dw; rect.right += dw; myInvalidateRect( d, &rect ); } } void wDrawPoint( wDraw_p d, wDrawPix_t px, wDrawPix_t py, wDrawColor dc, wDrawOpts dopt ) { POINT p0; RECT rect; p0.x = XDRAWPIX2WINPIX(d,px); p0.y = YDRAWPIX2WINPIX(d,py); if ( p0.x < 0 || p0.y < 0 ) { return; } if ( p0.x >= d->w || p0.y >= d->h ) { return; } setDrawMode( d, 0, wDrawLineSolid, dc, dopt ); SetPixel( d->hDc, p0.x, p0.y, mswGetColor(d->hasPalette, dc) /*colorPalette.palPalEntry[dc]*/ ); if (d->hWnd) { rect.top = p0.y-1; rect.bottom = p0.y+1; rect.left = p0.x-1; rect.right = p0.x+1; myInvalidateRect( d, &rect ); } } /* ***************************************************************************** * * Fonts * ***************************************************************************** */ static LOGFONT logFont = { /* Initial default values */ -24, 0, /* H, W */ 0, /* A */ 0, FW_REGULAR, 0, 0, 0,/* I, U, SO */ ANSI_CHARSET, 0, /* OP */ 0, /* CP */ 0, /* Q */ 0, /* P&F */ "Arial" }; static LOGFONT timesFont[2][2] = { { { /* Initial default values */ 0, 0, /* H, W */ 0, /* A */ 0, FW_REGULAR, 0, 0, 0,/* I, U, SO */ ANSI_CHARSET, 0, /* OP */ 0, /* CP */ 0, /* Q */ 0, /* P&F */ "Times" }, { /* Initial default values */ 0, 0, /* H, W */ 0, /* A */ 0, FW_REGULAR, 1, 0, 0,/* I, U, SO */ ANSI_CHARSET, 0, /* OP */ 0, /* CP */ 0, /* Q */ 0, /* P&F */ "Times" } }, { { /* Initial default values */ 0, 0, /* H, W */ 0, /* A */ 0, FW_BOLD, 0, 0, 0,/* I, U, SO */ ANSI_CHARSET, 0, /* OP */ 0, /* CP */ 0, /* Q */ 0, /* P&F */ "Times" }, { /* Initial default values */ 0, 0, /* H, W */ 0, /* A */ 0, FW_BOLD, 1, 0, 0,/* I, U, SO */ ANSI_CHARSET, 0, /* OP */ 0, /* CP */ 0, /* Q */ 0, /* P&F */ "Times" } } }; static LOGFONT helvFont[2][2] = { { { /* Initial default values */ 0, 0, /* H, W */ 0, /* A */ 0, FW_REGULAR, 0, 0, 0,/* I, U, SO */ ANSI_CHARSET, 0, /* OP */ 0, /* CP */ 0, /* Q */ 0, /* P&F */ "Arial" }, { /* Initial default values */ 0, 0, /* H, W */ 0, /* A */ 0, FW_REGULAR, 1, 0, 0,/* I, U, SO */ ANSI_CHARSET, 0, /* OP */ 0, /* CP */ 0, /* Q */ 0, /* P&F */ "Arial" } }, { { /* Initial default values */ 0, 0, /* H, W */ 0, /* A */ 0, FW_BOLD, 0, 0, 0,/* I, U, SO */ ANSI_CHARSET, 0, /* OP */ 0, /* CP */ 0, /* Q */ 0, /* P&F */ "Arial" }, { /* Initial default values */ 0, 0, /* H, W */ 0, /* A */ 0, FW_BOLD, 1, 0, 0,/* I, U, SO */ ANSI_CHARSET, 0, /* OP */ 0, /* CP */ 0, /* Q */ 0, /* P&F */ "Hevletica" } } }; void mswFontInit( void ) { const char * face; long size; /** @prefs [msw window font] face=FontName */ face = wPrefGetString( "msw window font", "face" ); /** @prefs [msw window font] size=-24 */ wPrefGetInteger( "msw window font", "size", &size, -24 ); if (face) { strncpy( logFont.lfFaceName, face, LF_FACESIZE ); } logFont.lfHeight = (int)size; } static CHOOSEFONT chooseFont; static wFontSize_t fontSize = 18; static double fontFactor = 1.0; static void doChooseFont( void ) { int rc; memset( &chooseFont, 0, sizeof chooseFont ); chooseFont.lStructSize = sizeof chooseFont; chooseFont.hwndOwner = mswHWnd; chooseFont.lpLogFont = &logFont; chooseFont.Flags = CF_SCREENFONTS|CF_SCALABLEONLY|CF_INITTOLOGFONTSTRUCT; chooseFont.nFontType = SCREEN_FONTTYPE; rc = ChooseFont( &chooseFont ); if (rc) { fontSize = (wFontSize_t)(-logFont.lfHeight * 72) / 96.0 / fontFactor; if (fontSize < 1) { fontSize = 1; } wPrefSetString( "msw window font", "face", logFont.lfFaceName ); wPrefSetInteger( "msw window font", "size", logFont.lfHeight ); } } static int computeFontSize( wDraw_p d, double siz ) { int ret; siz = (siz * d->DPI) / 72.0; ret = (int)(siz * fontFactor); if (ret < 1) { ret = 1; } return -ret; } void wDrawGetTextSize( wDrawPix_t *w, wDrawPix_t *h, wDrawPix_t *d, wDrawPix_t *a, wDraw_p bd, const char * text, wFont_p fp, double siz ) { wWinPix_t x, y; HFONT newFont, prevFont; DWORD extent; int oldLfHeight; TEXTMETRIC textMetric; if (fp == NULL) { fp = &logFont; } fp->lfEscapement = 0; oldLfHeight = fp->lfHeight; fp->lfHeight = computeFontSize( bd, siz ); fp->lfWidth = 0; newFont = CreateFontIndirect( fp ); prevFont = SelectObject( bd->hDc, newFont ); extent = GetTextExtent( bd->hDc, CAST_AWAY_CONST text, (int)(strlen(text)) ); GetTextMetrics(bd->hDc, &textMetric); x = LOWORD(extent); y = HIWORD(extent); *w = (wDrawPix_t)x; *h = (wDrawPix_t)y; *d = (wDrawPix_t)textMetric.tmDescent; *a = (wDrawPix_t)textMetric.tmAscent; SelectObject( bd->hDc, prevFont ); DeleteObject( newFont ); fp->lfHeight = oldLfHeight; } /** * Draw text * * \param d device context * \param px position x * \param py position y * \param angle drawing angle * \param text text to print * \param fp font * \param siz font size * \param dc color * \param dopts drawing options */ void wDrawString( wDraw_p d, wDrawPix_t px, wDrawPix_t py, double angle, const char * text, wFont_p fp, double siz, wDrawColor dc, wDrawOpts dopts) { int x, y; HFONT newFont, prevFont; DWORD extent; int w, h; RECT rect; int oldLfHeight; if (fp == NULL) { fp = &logFont; } oldLfHeight = fp->lfHeight; fp->lfEscapement = (int)(angle*10.0); fp->lfHeight = computeFontSize(d, siz); fp->lfWidth = 0; newFont = CreateFontIndirect(fp); x = XDRAWPIX2WINPIX(d,px) + (int)(mswsin(angle)*fp->lfHeight-0.5); y = YDRAWPIX2WINPIX(d,py) + (int)(mswcos(angle)*fp->lfHeight-0.5); setDrawMode( d, 0, wDrawLineSolid, dc, dopts ); prevFont = SelectObject(d->hDc, newFont); SetBkMode(d->hDc, TRANSPARENT); if (dopts & wDrawOutlineFont) { HPEN oldPen; BeginPath(d->hDc); TextOut(d->hDc, x, y, text, (int)strlen(text)); EndPath(d->hDc); // Now draw outline text oldPen = SelectObject(d->hDc, CreatePen(PS_SOLID, 1, mswGetColor(d->hasPalette, dc))); StrokePath(d->hDc); SelectObject(d->hDc, oldPen); } else { COLORREF old; old = SetTextColor(d->hDc, mswGetColor(d->hasPalette, dc)); TextOut(d->hDc, x, y, text, (int)(strlen(text))); SetTextColor(d->hDc, old); } extent = GetTextExtent(d->hDc, CAST_AWAY_CONST text, (int)(strlen(text))); SelectObject(d->hDc, prevFont); w = LOWORD(extent); h = HIWORD(extent); if (d->hWnd) { rect.top = y - (w + h + 1); rect.bottom = y + (w + h + 1); rect.left = x - (w + h + 1); rect.right = x + (w + h + 1); myInvalidateRect(d, &rect); } DeleteObject(newFont); fp->lfHeight = oldLfHeight; } static const char * wCurFont( void ) { return logFont.lfFaceName; } void wInitializeFonts() { } wFont_p wStandardFont( int family, wBool_t bold, wBool_t italic ) { if (family == F_TIMES) { return ×Font[bold][italic]; } else if (family == F_HELV) { return &helvFont[bold][italic]; } else { return NULL; } } void wSelectFont( const char * title ) { doChooseFont(); } wFontSize_t wSelectedFontSize( void ) { return fontSize; } void wSetSelectedFontSize(wFontSize_t size) { fontSize = size; } /* ***************************************************************************** * * Misc * ***************************************************************************** */ void wDrawFilledRectangle( wDraw_p d, wDrawPix_t px, wDrawPix_t py, wDrawPix_t sx, wDrawPix_t sy, wDrawColor color, wDrawOpts opts ) { int mode; RECT rect; if (d == NULL) { return; } setDrawBrush( d, color, opts ); if (opts & wDrawOptTransparent) { mode = R2_NOTXORPEN; } else { mode = R2_COPYPEN; } SetROP2(d->hDc, mode); rect.left = XDRAWPIX2WINPIX(d,px); rect.right = XDRAWPIX2WINPIX(d,px+sx); rect.top = YDRAWPIX2WINPIX(d,py+sy); rect.bottom = YDRAWPIX2WINPIX(d,py); if ( rect.right < 0 || rect.bottom < 0 ) { return; } if ( rect.left < 0 ) { rect.left = 0; } if ( rect.top < 0 ) { rect.top = 0; } if ( rect.left > d->w || rect.top > d->h ) { return; } if ( rect.right > d->w ) { rect.right = d->w; } if ( rect.bottom > d->h ) { rect.bottom = d->h; } Rectangle( d->hDc, rect.left, rect.top, rect.right, rect.bottom ); if (d->hWnd) { rect.top--; rect.left--; rect.bottom++; rect.right++; myInvalidateRect( d, &rect ); } } #ifdef DRAWFILLPOLYLOG static FILE * logF; #endif static dynArr_t wFillPoints_da; static dynArr_t wFillType_da; #define POINTTYPE(N) DYNARR_N( BYTE, wFillType_da, (N) ) #define POINTPOS(N) DYNARR_N( POINT, wFillPoints_da, (N) ) /** * Add a point definition to the list. The clipping rectangle is recalculated to * include the new point. * * \param d IN drawing context * \param pk IN index of new point * \param pp IN pointer to the point's coordinates * \param type IN line type * \param pr IN/OUT clipping rectangle */ static void addPoint( wDraw_p d, int pk, coOrd * pp, BYTE type, RECT * pr) { POINT p; p.x = XDRAWPIX2WINPIX(d, pp->x); p.y = YDRAWPIX2WINPIX(d, pp->y); #ifdef DRAWFILLPOLYLOG fprintf(logF, " q[%d] = {%d,%d}\n", pk, p.x, p.y); #endif DYNARR_N(POINT, wFillPoints_da, pk) = p; DYNARR_N(BYTE, wFillType_da, pk) = type; if (p.x < pr->left) { pr->left = p.x; } if (p.x > pr->right) { pr->right = p.x; } if (p.y < pr->top) { pr->top = p.y; } if (p.y > pr->bottom) { pr->bottom = p.y; } } /** * Draw a polyline consisting of straights with smoothed or rounded corners. * Optionally the area can be filled. * * \param d IN drawing context * \param node IN 2 dimensional array of coordinates * \param type IN type of corener (vertex, smooth or round) * \param cnt IN number of points * \param color IN color * \param dw IN line width * \param lt IN line type * \param opts IN drawing options * \param fill IN area will be filled if true * \param open IN do not close area */ void wDrawPolygon( wDraw_p d, wDrawPix_t node[][2], wPolyLine_e type[], wIndex_t cnt, wDrawColor color, wDrawWidth dw, wDrawLineType_e lt, wDrawOpts opts, int fill, int open) { RECT rect; int i, prevNode, nextNode; int pointCount = 0; coOrd endPoint0, endPoint1, controlPoint0, controlPoint1; coOrd point, startingPoint; BOOL rc; int closed = 0; if (d == NULL) { return; } // make sure the array for the points is large enough // worst case are rounded corners that require 4 points DYNARR_RESET(POINT,wFillPoints_da); DYNARR_SET(POINT,wFillPoints_da,(cnt + 1) * 4); DYNARR_RESET(BYTE,wFillType_da); DYNARR_SET(POINT,wFillType_da, (cnt + 1) * 4); BeginPath(d->hDc); if (fill) { int mode; setDrawBrush(d, color, opts); if (opts & wDrawOptTransparent) { mode = R2_NOTXORPEN; } else { mode = R2_COPYPEN; } SetROP2(d->hDc, mode); } else { setDrawMode(d, dw, lt, color, opts); } rect.left = rect.right = XDRAWPIX2WINPIX(d,node[cnt-1][0]-1); rect.top = rect.bottom = YDRAWPIX2WINPIX(d,node[cnt-1][1]+1); #ifdef DRAWFILLPOLYLOG logF = fopen("log.txt", "a"); fprintf(logF, "\np[%d] = {%d,%d}\n", cnt-1, node[0][0], node[0][1]); #endif for (i=0; i 0) && (distNext > 0)) { double ratio = sqrt(distPrev / distNext); if (distPrev < distNext) { endPoint1.x = ((nextXDistance*ratio) / 2) + node[i][0]; endPoint1.y = ((nextYDistance*ratio) / 2) + node[i][1]; } else { endPoint0.x = node[i][0] - (prevXDistance / (2 * ratio)); endPoint0.y = node[i][1] - (prevYDistance / (2 * ratio)); } } // experience says that the best look is achieved if the // control points are in the middle between end point and node controlPoint0.x = (node[i][0] - endPoint0.x) / 2 + endPoint0.x; controlPoint0.y = (node[i][1] - endPoint0.y) / 2 + endPoint0.y; controlPoint1.x = (endPoint1.x - node[i][0]) / 2 + node[i][0]; controlPoint1.y = (endPoint1.y - node[i][1]) / 2 + node[i][1]; } else { controlPoint0 = point; controlPoint1 = point; } } if (i==0) { if (type1 == wPolyLineStraight || open) { // for straight lines or open shapes use the starting point as passed addPoint(d, pointCount++, &point, PT_MOVETO, &rect); startingPoint = point; } else { // for Bezier begin with the calculated starting point addPoint(d, pointCount++, &endPoint0, PT_MOVETO, &rect); addPoint(d, pointCount++, &controlPoint0, PT_BEZIERTO, &rect); addPoint(d, pointCount++, &controlPoint1, PT_BEZIERTO, &rect); addPoint(d, pointCount++, &endPoint1, PT_BEZIERTO, &rect); startingPoint = endPoint0; } } else { if (type1 == wPolyLineStraight || (open && (i==cnt-1))) { addPoint(d, pointCount++, &point, PT_LINETO, &rect); } else { if (i==cnt-1 && !open) { closed = TRUE; } addPoint(d, pointCount++, &endPoint0, PT_LINETO, &rect); addPoint(d, pointCount++, &controlPoint0, PT_BEZIERTO, &rect); addPoint(d, pointCount++, &controlPoint1, PT_BEZIERTO, &rect); addPoint(d, pointCount++, &endPoint1, PT_BEZIERTO | (closed ? PT_CLOSEFIGURE : 0), &rect); } } } if (!open && !closed) { addPoint(d, pointCount++, &startingPoint, PT_LINETO, &rect); } rc = PolyDraw(d->hDc, wFillPoints_da.ptr, wFillType_da.ptr, pointCount); EndPath(d->hDc); if (fill && !open) { FillPath(d->hDc); } else { StrokePath(d->hDc); } if (d->hWnd) { rect.top--; rect.left--; rect.bottom++; rect.right++; myInvalidateRect(d, &rect); } } #define MAX_FILLCIRCLE_POINTS (30) void wDrawFilledCircle( wDraw_p d, wDrawPix_t x, wDrawPix_t y, wDrawPix_t r, wDrawColor color, wDrawOpts opts ) { POINT p0, p1; RECT rect; static wDrawPix_t circlePts[MAX_FILLCIRCLE_POINTS][2]; p0.x = XDRAWPIX2WINPIX(d,x-r); p0.y = YDRAWPIX2WINPIX(d,y+r); p1.x = XDRAWPIX2WINPIX(d,x+r); p1.y = YDRAWPIX2WINPIX(d,y-r); setDrawBrush( d, color, opts ); Ellipse( d->hDc, p0.x, p0.y, p1.x, p1.y ); if (d->hWnd) { rect.top = p0.y; rect.bottom = p1.y; rect.left = p0.x; rect.right = p1.x; myInvalidateRect( d, &rect ); } } /* ***************************************************************************** * * Misc * ***************************************************************************** */ void wDrawSaveImage( wDraw_p bd ) { if ( bd->hBmBackup ) { SelectObject( bd->hDcBackup, bd->hBmBackupOld ); DeleteObject( bd->hBmBackup ); bd->hBmBackup = (HBITMAP)0; } if ( bd->hDcBackup == (HDC)0 ) { bd->hDcBackup = CreateCompatibleDC( bd->hDc ); } bd->hBmBackup = CreateCompatibleBitmap( bd->hDc, bd->w, bd->h ); bd->hBmBackupOld = SelectObject( bd->hDcBackup, bd->hBmBackup ); BitBlt( bd->hDcBackup, 0, 0, bd->w, bd->h, bd->hDc, 0, 0, SRCCOPY ); } void wDrawRestoreImage( wDraw_p bd ) { if ( bd->hBmBackup == (HBITMAP)0 ) { mswFail( "wDrawRestoreImage: hBmBackup == 0" ); return; } BitBlt( bd->hDc, 0, 0, bd->w, bd->h, bd->hDcBackup, 0, 0, SRCCOPY ); InvalidateRect( bd->hWnd, NULL, FALSE ); } void wDrawClearTemp( wDraw_p d ) { RECT rect; SelectObject( d->hDc, d->hBmTemp ); BitBlt(d->hDc, 0, 0, d->w, d->h, d->hDc, 0, 0, WHITENESS); if (d->hWnd) { rect.top = 0; rect.bottom = d->h; rect.left = 0; rect.right = d->w; InvalidateRect( d->hWnd, &rect, FALSE ); } } void wDrawClear( wDraw_p d ) { SelectObject( d->hDc, d->hBmMain ); // BitBlt is faster than Rectangle BitBlt(d->hDc, 0, 0, d->w, d->h, d->hDc, 0, 0, WHITENESS); wDrawClearTemp(d); } void wDrawSetSize( wDraw_p d, wWinPix_t width, wWinPix_t height, void * redraw) { d->w = width; d->h = height; if (!SetWindowPos( d->hWnd, HWND_TOP, 0, 0, d->w, d->h, SWP_NOMOVE|SWP_NOZORDER)) { mswFail("wDrawSetSize: SetWindowPos"); } /*wRedraw( d );*/ } void wDrawGetSize( wDraw_p d, wWinPix_t * width, wWinPix_t * height ) { *width = d->w-2; *height = d->h-2; } void * wDrawGetContext( wDraw_p d ) { return d->data; } double wDrawGetDPI( wDraw_p d ) { return d->DPI; } double wDrawGetMaxRadius( wDraw_p d ) { return 4096.0; } void wDrawClip( wDraw_p d, wDrawPix_t x, wDrawPix_t y, wDrawPix_t w, wDrawPix_t h ) { wWinPix_t ix0, iy0, ix1, iy1; HRGN hRgnClip; ix0 = XDRAWPIX2WINPIX(d,x); iy0 = YDRAWPIX2WINPIX(d,y); ix1 = XDRAWPIX2WINPIX(d,x+w); iy1 = YDRAWPIX2WINPIX(d,y+h); /* Note: Ydim is upside down so iy1hDc, hRgnClip ); DeleteObject( hRgnClip ); } void wRedraw( wDraw_p d ) { wDrawClear( d ); if (d->drawRepaint) { d->drawRepaint( d, d->data, 0, 0 ); } } /* ***************************************************************************** * * Cursor handling * ***************************************************************************** */ extern long dontHideCursor; static wCursor_t curCursor = wCursorNormal; void DoSetCursor() { switch (curCursor) { case wCursorNormal: default: SetCursor(LoadCursor(NULL, IDC_ARROW)); break; case wCursorWait: SetCursor(LoadCursor(NULL, IDC_WAIT)); break; case wCursorCross: SetCursor(LoadCursor(NULL, IDC_CROSS)); break; case wCursorIBeam: SetCursor(LoadCursor(NULL, IDC_IBEAM)); break; case wCursorQuestion: SetCursor(LoadCursor(NULL, IDC_HELP)); break; case wCursorHand: SetCursor(LoadCursor(NULL, IDC_HAND)); break; case wCursorNo: SetCursor(LoadCursor(NULL, IDC_NO)); break; case wCursorSizeAll: SetCursor(LoadCursor(NULL, IDC_SIZEALL)); break; case wCursorSizeNESW: SetCursor(LoadCursor(NULL, IDC_SIZENESW)); break; case wCursorSizeNWSE: SetCursor(LoadCursor(NULL, IDC_SIZENWSE)); break; case wCursorSizeNS: SetCursor(LoadCursor(NULL, IDC_SIZENS)); break; case wCursorSizeWE: SetCursor(LoadCursor(NULL, IDC_SIZEWE)); break; case wCursorAppStart: SetCursor(LoadCursor(NULL, IDC_APPSTARTING)); break; case wCursorNone: if (!dontHideCursor) { SetCursor(NULL); } break; } } void wSetCursor(wDraw_p win, wCursor_t cursor) { curCursor = cursor; DoSetCursor(); } /* ***************************************************************************** * * BitMap * ***************************************************************************** */ struct wDrawBitMap_t { wDrawBitMap_p next; wDrawPix_t x; wDrawPix_t y; wDrawPix_t w; wDrawPix_t h; char * bmx; wDrawColor color; HBITMAP bm; }; static wDrawBitMap_p bmRoot = NULL; extern wDrawColor drawColorWhite; extern wDrawColor drawColorBlack; void wDrawBitMap( wDraw_p d, wDrawBitMap_p bm, wDrawPix_t px, wDrawPix_t py, wDrawColor dc, wDrawOpts dopt ) { HDC bmDc; HBITMAP oldBm; DWORD mode; int x0, y0; RECT rect; x0 = XDRAWPIX2WINPIX(d,px-bm->x); y0 = YDRAWPIX2WINPIX(d,py-bm->y+bm->h); #ifdef LATER if ( noNegDrawArgs > 0 && ( x0 < 0 || y0 < 0 ) ) { return; } #endif if (dc == drawColorWhite) { mode = clrOp; dc = drawColorBlack; } else { mode = setOp; } if ( bm->color != dc ) { if ( bm->bm ) { DeleteObject( bm->bm ); } bm->bm = mswCreateBitMap( mswGetColor(d->hasPalette, dc) /*colorPalette.palPalEntry[dc]*/, RGB( 255, 255, 255 ), RGB( 255, 255, 255 ), (wWinPix_t)bm->w, (wWinPix_t)bm->h, bm->bmx ); bm->color = dc; } bmDc = CreateCompatibleDC( d->hDc ); setDrawMode( d, 0, wDrawLineSolid, dc, dopt ); oldBm = SelectObject( bmDc, bm->bm ); BitBlt( d->hDc, x0, y0, (wWinPix_t)bm->w, (wWinPix_t)bm->h, bmDc, 0, 0, mode ); SelectObject( bmDc, oldBm ); DeleteDC( bmDc ); if (d->hWnd) { rect.top = y0-1; rect.bottom = rect.top+ (wWinPix_t)bm->h+1; rect.left = x0-1; rect.right = rect.left+ (wWinPix_t)bm->w+1; myInvalidateRect( d, &rect ); } } wDrawBitMap_p wDrawBitMapCreate( wDraw_p d, int w, int h, int x, int y, const unsigned char * bits ) { wDrawBitMap_p bm; int bmSize = ((w+7)/8) * h; bm = (wDrawBitMap_p)malloc( sizeof *bm ); if (bmRoot == NULL) { bmRoot = bm; bm->next = NULL; } else { bm->next = bmRoot; bmRoot = bm; } bm->x = x; bm->y = y; bm->w = w; bm->h = h; bm->bmx = malloc( bmSize ); bm->bm = (HBITMAP)0; bm->color = -1; memcpy( bm->bmx, bits, bmSize ); /*bm->bm = mswCreateBitMap( GetSysColor(COLOR_BTNTEXT), RGB( 255, 255, 255 ), w, h, bits );*/ return bm; } /* ***************************************************************************** * * Create * ***************************************************************************** */ static int doSetFocus = 1; LRESULT FAR PASCAL XEXPORT mswDrawPush( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { wIndex_t inx = (wIndex_t)GetWindowLongPtr( hWnd, GWL_ID ); wDraw_p b; wWinPix_t ix, iy; wDrawPix_t x, y; HDC hDc; PAINTSTRUCT ps; wAction_t action; RECT rect; HWND activeWnd; HWND focusWnd; wAccelKey_e extChar; switch( message ) { case WM_CREATE: b = (wDraw_p)mswMapIndex( inx ); hDc = GetDC(hWnd); if ( b->option & BD_DIRECT ) { b->hDc = hDc; b->hBmMain = 0; b->hBmTemp = 0; b->hBmOld = 0; } else { b->hDc = CreateCompatibleDC( hDc ); b->hBmMain = CreateCompatibleBitmap( hDc, b->w, b->h ); b->hBmTemp = CreateCompatibleBitmap( hDc, b->w, b->h ); b->hBmOld = SelectObject( b->hDc, b->hBmMain ); } if (mswPalette) { SelectPalette( b->hDc, mswPalette, 0 ); RealizePalette( b->hDc ); } b->wFactor = (double)GetDeviceCaps( b->hDc, LOGPIXELSX ); b->hFactor = (double)GetDeviceCaps( b->hDc, LOGPIXELSY ); double dpi; /** @prefs [Preference] ScreenDPI=96.0 Sets DPI of screen */ wPrefGetFloat(PREFSECTION, DPISET, &dpi, 96.0); b->DPI = dpi; b->hWnd = hWnd; SetROP2( b->hDc, R2_WHITE ); Rectangle( b->hDc, 0, 0, b->w, b->h ); if ( (b->option & BD_DIRECT) == 0 ) { SetROP2( hDc, R2_WHITE ); Rectangle( hDc, 0, 0, b->w, b->h ); ReleaseDC( hWnd, hDc ); } break; case WM_SIZE: b = (wDraw_p)mswMapIndex( inx ); ix = LOWORD( lParam ); iy = HIWORD( lParam ); b->w = ix+2; b->h = iy+2; if (b->hWnd) { if ( b->option & BD_DIRECT ) { } else { hDc = GetDC( b->hWnd ); //- DeleteObject( b->hBmOld ); DeleteObject( b->hBmMain ); DeleteObject( b->hBmTemp ); b->hBmMain = CreateCompatibleBitmap( hDc, b->w, b->h ); b->hBmTemp = CreateCompatibleBitmap( hDc, b->w, b->h ); //- b->hBmOld = SelectObject( b->hDc, b->hBmMain ); ReleaseDC( b->hWnd, hDc ); SetROP2( b->hDc, R2_WHITE ); Rectangle( b->hDc, 0, 0, b->w, b->h ); } } /*if (b->drawResize) b->drawResize( b, b->size );*/ if (b->drawRepaint) { b->drawRepaint( b, b->data, 0, 0 ); } return (LRESULT)0; case WM_MOUSEMOVE: activeWnd = GetActiveWindow(); focusWnd = GetFocus(); if (focusWnd != hWnd) { b = (wDraw_p)mswMapIndex( inx ); if (!b) { break; } if ( !((wControl_p)b->parent) ) { break; } if ( ((wControl_p)b->parent)->hWnd != activeWnd ) { break; } } case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_LBUTTONDBLCLK: case WM_MBUTTONUP: case WM_MBUTTONDOWN: if (message == WM_LBUTTONDOWN) { action = wActionLDown; } else if (message == WM_RBUTTONDOWN) { action = wActionRDown; } else if (message == WM_LBUTTONUP) { action = wActionLUp; } else if (message == WM_RBUTTONUP) { action = wActionRUp; } else if (message == WM_MBUTTONUP) { action = wActionMUp; } else if (message == WM_MBUTTONDOWN) { action = wActionMDown; } else if (message == WM_LBUTTONDBLCLK) { action = wActionLDownDouble; } else { if ( (wParam & MK_LBUTTON) != 0) { action = wActionLDrag; } else if ( (wParam & MK_RBUTTON) != 0) { action = wActionRDrag; } else if ( (wParam & MK_MBUTTON) != 0) { action = wActionMDrag; } else { action = wActionMove; } } b = (wDraw_p)mswMapIndex( inx ); if (!b) { break; } if (doSetFocus && message != WM_MOUSEMOVE) { SetFocus( ((wControl_p)b->parent)->hWnd ); } if ( (b->option&BD_NOCAPTURE) == 0 ) { if (message == WM_LBUTTONDOWN || message == WM_RBUTTONDOWN) { SetCapture( b->hWnd ); } else if (message == WM_LBUTTONUP || message == WM_RBUTTONUP) { ReleaseCapture(); } } ix = LOWORD( lParam ); iy = HIWORD( lParam ); x = XWINPIX2DRAWPIX( b, ix ); y = YWINPIX2DRAWPIX( b, iy ); b->lastX = x; b->lastY = y; if (b->action) { b->action( b, b->data, action, x, y ); } if (b->hWnd) { UpdateWindow(b->hWnd); } return (LRESULT)0; case WM_CHAR: b = (wDraw_p)mswMapIndex( inx ); extChar = wAccelKey_None; if (lParam & 0x01000000L) switch( wParam ) { case VK_DELETE: extChar = wAccelKey_Del; break; case VK_INSERT: extChar = wAccelKey_Ins; break; case VK_HOME: extChar = wAccelKey_Home; break; case VK_END: extChar = wAccelKey_End; break; case VK_PRIOR: extChar = wAccelKey_Pgup; break; case VK_NEXT: extChar = wAccelKey_Pgdn; break; case VK_UP: extChar = wAccelKey_Up; break; case VK_DOWN: extChar = wAccelKey_Down; break; case VK_RIGHT: extChar = wAccelKey_Right; break; case VK_LEFT: extChar = wAccelKey_Left; break; case VK_BACK: extChar = wAccelKey_Back; break; case VK_F1: extChar = wAccelKey_F1; break; case VK_F2: extChar = wAccelKey_F2; break; case VK_F3: extChar = wAccelKey_F3; break; case VK_F4: extChar = wAccelKey_F4; break; case VK_F5: extChar = wAccelKey_F5; break; case VK_F6: extChar = wAccelKey_F6; break; case VK_F7: extChar = wAccelKey_F7; break; case VK_F8: extChar = wAccelKey_F8; break; case VK_F9: extChar = wAccelKey_F9; break; case VK_F10: extChar = wAccelKey_F10; break; case VK_F11: extChar = wAccelKey_F11; break; case VK_F12: extChar = wAccelKey_F12; break; } if (b && b->action) { if (extChar != wAccelKey_None) { b->action( b, b->data, wActionExtKey + ( (int)extChar << 8 ), b->lastX, b->lastY ); } else { b->action( b, b->data, wActionText + ( (int)wParam << 8 ), b->lastX, b->lastY ); } } return (LRESULT)0; case WM_PAINT: b = (wDraw_p)mswMapIndex( inx ); if (b && b->type == B_DRAW) { if (GetUpdateRect( b->hWnd, &rect, FALSE )) { hDc = BeginPaint( hWnd, &ps ); if ( b->hasPalette ) { int winPaletteClock = mswGetPaletteClock(); if ( b->paletteClock < winPaletteClock ) { RealizePalette( hDc ); b->paletteClock = winPaletteClock; } } HBITMAP hBmOld = SelectObject( b->hDc, b->hBmMain ); if (bDrawMainBM) { BitBlt(hDc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, b->hDc, rect.left, rect.top, SRCCOPY); } SelectObject( b->hDc, b->bCopiedMain?b->hBmTemp:b->hBmMain ); BitBlt( hDc, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, b->hDc, rect.left, rect.top, bDrawMainBM?SRCAND:SRCCOPY); SelectObject( b->hDc, hBmOld ); EndPaint( hWnd, &ps ); b->bCopiedMain = FALSE; } } break; case WM_DESTROY: b = (wDraw_p)mswMapIndex( inx ); if (b && b->type == B_DRAW) { if (b->hDc) { DeleteDC( b->hDc ); b->hDc = (HDC)0; } if (b->hDcBackup) { DeleteDC( b->hDcBackup ); b->hDcBackup = (HDC)0; } } break; case WM_SETCURSOR: // Set cursor based on wSetCursor DoSetCursor(); // return TRUE to suppress my parent from overriding me return TRUE; default: break; } return DefWindowProc( hWnd, message, wParam, lParam ); } static LRESULT drawMsgProc( wDraw_p b, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { wAction_t action; switch( message ) { case WM_MOUSEWHEEL: /* handle mouse wheel events */ if (GET_KEYSTATE_WPARAM(wParam) & (MK_SHIFT|MK_MBUTTON) ) { if (GET_KEYSTATE_WPARAM(wParam) & MK_CONTROL ) { if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { action = wActionScrollLeft; } else { action = wActionScrollRight; } } else { if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { action = wActionScrollUp; } else { action = wActionScrollDown; } } } else { if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { action = wActionWheelUp; } else { action = wActionWheelDown; } } if (b->action) { b->action( b, b->data, action, b->lastX, b->lastY ); } return (LRESULT)0; case WM_MOUSEHWHEEL: if ( GET_KEYSTATE_WPARAM(wParam) & (MK_SHIFT|MK_MBUTTON)) { if ( GET_WHEEL_DELTA_WPARAM(wParam) > 0 ) { action = wActionScrollRight; } else { action = wActionScrollLeft; } } if (b->action) { b->action( b, b->data, action, b->lastX, b->lastY ); } return (LRESULT)0; } return DefWindowProc( hWnd, message, wParam, lParam ); } static void drawDoneProc( wControl_p b ) { wDraw_p d = (wDraw_p)b; if (d->hBmMain) { SelectObject( d->hDc, d->hBmOld ); DeleteObject( d->hBmMain ); d->hBmMain = (HBITMAP)0; DeleteObject( d->hBmTemp ); d->hBmTemp = (HBITMAP)0; } if (d->hPen) { SelectObject( d->hDc, GetStockObject( BLACK_PEN ) ); DeleteObject( d->hPen ); d->hPen = (HPEN)0; } if (d->hBrush) { SelectObject( d->hDc, GetStockObject( BLACK_BRUSH) ); DeleteObject( d->hBrush ); d->hBrush = (HBRUSH)0; } if (d->hDc) { DeleteDC( d->hDc ); d->hDc = (HDC)0; } if ( d->hDcBackup ) { DeleteDC( d->hDcBackup ); d->hDcBackup = (HDC)0; } while (bmRoot) { if (bmRoot->bm) { DeleteObject( bmRoot->bm ); } bmRoot = bmRoot->next; } } static callBacks_t drawCallBacks = { NULL, drawDoneProc, (messageCallback_p)drawMsgProc }; static wDraw_p drawList = NULL; void mswRedrawAll( void ) { wDraw_p p; for ( p=drawList; p; p=p->drawNext ) { if (p->drawRepaint) { p->drawRepaint( p, p->data, 0, 0 ); } } } void mswRepaintAll( void ) { wDraw_p b; HDC hDc; RECT rect; PAINTSTRUCT ps; for ( b=drawList; b; b=b->drawNext ) { if (GetUpdateRect( b->hWnd, &rect, FALSE )) { hDc = BeginPaint( b->hWnd, &ps ); HBITMAP hBmOld = SelectObject( b->hDc, b->hBmMain ); BitBlt( hDc, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, b->hDc, rect.left, rect.top, SRCCOPY ); SelectObject( b->hDc, b->hBmTemp ); BitBlt( hDc, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, b->hDc, rect.left, rect.top, SRCAND ); SelectObject( b->hDc, hBmOld ); EndPaint( b->hWnd, &ps ); } } } wDraw_p wDrawCreate( wWin_p parent, wWinPix_t x, wWinPix_t y, const char * helpStr, long option, wWinPix_t w, wWinPix_t h, void * data, wDrawRedrawCallBack_p redrawProc, wDrawActionCallBack_p action ) { wDraw_p d; RECT rect; int index; HDC hDc; d = mswAlloc( parent, B_DRAW, NULL, sizeof *d, data, &index ); mswComputePos( (wControl_p)d, x, y ); d->w = w; d->h = h; d->drawRepaint = NULL; d->action = action; d->option = option; d->hWnd = CreateWindow( mswDrawWindowClassName, NULL, WS_CHILDWINDOW|WS_VISIBLE|WS_BORDER, d->x, d->y, w, h, ((wControl_p)parent)->hWnd, (HMENU)(UINT_PTR)index, mswHInst, NULL ); if (d->hWnd == (HWND)0) { mswFail( "CreateWindow(DRAW)" ); return d; } GetWindowRect( d->hWnd, &rect ); d->w = rect.right - rect.left; d->h = rect.bottom - rect.top; d->drawRepaint = redrawProc; /*if (d->drawRepaint) d->drawRepaint( d, d->data, 0.0, 0.0 );*/ mswAddButton( (wControl_p)d, FALSE, helpStr ); mswCallBacks[B_DRAW] = &drawCallBacks; d->drawNext = drawList; drawList = d; if (mswPalette) { hDc = GetDC( d->hWnd ); d->hasPalette = TRUE; SelectPalette( hDc, mswPalette, 0 ); ReleaseDC( d->hWnd, hDc ); } d->bCopiedMain = FALSE; return d; } /* ***************************************************************************** * * Bitmaps * ***************************************************************************** */ wDraw_p wBitMapCreate( wWinPix_t w, wWinPix_t h, int planes ) { wDraw_p d; HDC hDc; d = (wDraw_p)calloc(1,sizeof *d); d->type = B_DRAW; d->shown = TRUE; d->x = 0; d->y = 0; d->w = w; d->h = h; d->drawRepaint = NULL; d->action = NULL; d->option = 0; hDc = GetDC(mswHWnd); d->hDc = CreateCompatibleDC( hDc ); if ( d->hDc == (HDC)0 ) { wNoticeEx( NT_ERROR, "CreateBitMap: CreateDC fails", "Ok", NULL ); return FALSE; } d->hBmMain = CreateCompatibleBitmap( hDc, d->w, d->h ); if ( d->hBmMain == (HBITMAP)0 ) { wNoticeEx( NT_ERROR, "CreateBitMap: CreateBM Main fails", "Ok", NULL ); ReleaseDC(mswHWnd, hDc); return FALSE; } d->hBmTemp = CreateCompatibleBitmap( hDc, d->w, d->h ); if ( d->hBmTemp == (HBITMAP)0 ) { wNoticeEx( NT_ERROR, "CreateBitMap: CreateBM Temp fails", "Ok", NULL ); ReleaseDC(mswHWnd, hDc); return FALSE; } d->hasPalette = (GetDeviceCaps(hDc,RASTERCAPS ) & RC_PALETTE) != 0; ReleaseDC( mswHWnd, hDc ); d->hBmOld = SelectObject( d->hDc, d->hBmMain ); if (mswPalette) { SelectPalette( d->hDc, mswPalette, 0 ); RealizePalette( d->hDc ); } d->wFactor = (double)GetDeviceCaps( d->hDc, LOGPIXELSX ); d->hFactor = (double)GetDeviceCaps( d->hDc, LOGPIXELSY ); d->DPI = 96.0; /*min( d->wFactor, d->hFactor );*/ d->hWnd = 0; wDrawClear(d); //- SetROP2( d->hDc, R2_WHITE ); //- Rectangle( d->hDc, 0, 0, d->w, d->h ); return d; } wBool_t wBitMapDelete( wDraw_p d ) { if (d->hPen) { SelectObject( d->hDc, GetStockObject( BLACK_PEN ) ); DeleteObject( d->hPen ); d->hPen = (HPEN)0; } if (d->hBmMain) { SelectObject( d->hDc, d->hBmOld ); DeleteObject( d->hBmMain ); d->hBmMain = (HBITMAP)0; DeleteObject( d->hBmTemp ); d->hBmTemp = (HBITMAP)0; } if (d->hDc) { DeleteDC( d->hDc ); d->hDc = (HDC)0; } free(d); return TRUE; } /** * write bitmap file. The bitmap in d must contain a valid HBITMAP * * \param d A wDraw_p to process. * \param fileName Filename of the file. * * \returns A wBool_t. TRUE on success */ wBool_t wBitMapWriteFile(wDraw_p d, const char * fileName) { FIBITMAP *dib = NULL; FIBITMAP *dib2 = NULL; FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; BOOL bSuccess = FALSE; if (d->hBmMain) { BITMAP bm; GetObject(d->hBmMain, sizeof(BITMAP), (LPSTR)&bm); dib = FreeImage_Allocate(bm.bmWidth, bm.bmHeight, bm.bmBitsPixel, 0, 0, 0); // The GetDIBits function clears the biClrUsed and biClrImportant BITMAPINFO members (dont't know why) // So we save these infos below. This is needed for palettized images only. int nColors = FreeImage_GetColorsUsed(dib); HDC dc = GetDC(NULL); GetDIBits(dc, d->hBmMain, 0, FreeImage_GetHeight(dib), FreeImage_GetBits(dib), FreeImage_GetInfo(dib), DIB_RGB_COLORS); ReleaseDC(NULL, dc); // restore BITMAPINFO members FreeImage_GetInfoHeader(dib)->biClrUsed = nColors; FreeImage_GetInfoHeader(dib)->biClrImportant = nColors; // we will get a 32 bit bitmap on Windows systems with invalid alpha // so it needs to be converted to 24 bits. // (see: https://sourceforge.net/p/freeimage/discussion/36110/thread/0699ce8e/ ) dib2 = FreeImage_ConvertTo24Bits(dib); FreeImage_Unload(dib); } // Try to guess the file format from the file extension fif = FreeImage_GetFIFFromFilename(fileName); if (fif != FIF_UNKNOWN) { // Check that the dib can be saved in this format BOOL bCanSave; FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib2); if (image_type == FIT_BITMAP) { // standard bitmap type WORD bpp = FreeImage_GetBPP(dib2); bCanSave = (FreeImage_FIFSupportsWriting(fif) && FreeImage_FIFSupportsExportBPP(fif, bpp)); } else { // special bitmap type bCanSave = FreeImage_FIFSupportsExportType(fif, image_type); } if (bCanSave) { int flags; switch (fif) { case FIF_JPEG: flags = JPEG_QUALITYNORMAL; break; case FIF_PNG: flags = PNG_DEFAULT; break; default: flags = 0; // whatver the default is for the file format } bSuccess = FreeImage_Save(fif, dib2, fileName, flags); } } FreeImage_Unload(dib2); return bSuccess; }