From 320c0e1a75106a74280410ce23db8a308f44193b Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Fri, 10 Feb 2017 09:38:31 +0000 Subject: update windows plotters to new API --- frontends/windows/plot.c | 1176 ++++++++++++++++++++++++++-------------------- 1 file changed, 668 insertions(+), 508 deletions(-) (limited to 'frontends/windows') diff --git a/frontends/windows/plot.c b/frontends/windows/plot.c index fd2961957..5b7648ef1 100644 --- a/frontends/windows/plot.c +++ b/frontends/windows/plot.c @@ -50,341 +50,331 @@ HDC plot_hdc; static RECT plot_clip; /* currently set clipping rectangle */ -static bool clip(const struct rect *clip) -{ - PLOT_LOG("clip %d,%d to %d,%d", clip->x0, clip->y0, clip->x1, clip->y1); - - plot_clip.left = clip->x0; - plot_clip.top = clip->y0; - plot_clip.right = clip->x1 + 1; /* co-ordinates are exclusive */ - plot_clip.bottom = clip->y1 + 1; /* co-ordinates are exclusive */ - - return true; -} -static bool line(int x0, int y0, int x1, int y1, const plot_style_t *style) +/** + * bitmap helper to plot a solid block of colour + * + * \param col colour to plot with + * \param x the x coordinate to plot at + * \param y the y coordinate to plot at + * \param width the width of block to plot + * \param height the height to plot + * \return NSERROR_OK on sucess else error code. + */ +static nserror +plot_block(COLORREF col, int x, int y, int width, int height) { - PLOT_LOG("from %d,%d to %d,%d", x0, y0, x1, y1); + HRGN clipregion; + HGDIOBJ original = NULL; + + /* Bail early if we can */ + if ((x >= plot_clip.right) || + ((x + width) < plot_clip.left) || + (y >= plot_clip.bottom) || + ((y + height) < plot_clip.top)) { + /* Image completely outside clip region */ + return NSERROR_OK; + } /* ensure the plot HDC is set */ if (plot_hdc == NULL) { LOG("HDC not set on call to plotters"); - return false; + return NSERROR_INVALID; } - HRGN clipregion = CreateRectRgnIndirect(&plot_clip); + clipregion = CreateRectRgnIndirect(&plot_clip); if (clipregion == NULL) { - return false; + return NSERROR_INVALID; } - COLORREF col = (DWORD)(style->stroke_colour & 0x00FFFFFF); - /* windows 0x00bbggrr */ - DWORD penstyle = PS_GEOMETRIC | ((style->stroke_type == - PLOT_OP_TYPE_DOT) ? PS_DOT : - (style->stroke_type == PLOT_OP_TYPE_DASH) ? PS_DASH: - 0); - LOGBRUSH lb = {BS_SOLID, col, 0}; - HPEN pen = ExtCreatePen(penstyle, style->stroke_width, &lb, 0, NULL); - if (pen == NULL) { - DeleteObject(clipregion); - return false; - } - HGDIOBJ bak = SelectObject(plot_hdc, (HGDIOBJ) pen); - if (bak == NULL) { - DeleteObject(pen); - DeleteObject(clipregion); - return false; - } -/* - RECT r; - r.left = x0; - r.top = y0; - r.right = x1; - r.bottom = y1; -*/ SelectClipRgn(plot_hdc, clipregion); - MoveToEx(plot_hdc, x0, y0, (LPPOINT) NULL); + /* Saving the original pen object */ + original = SelectObject(plot_hdc,GetStockObject(DC_PEN)); - LineTo(plot_hdc, x1, y1); + SelectObject(plot_hdc, GetStockObject(DC_PEN)); + SelectObject(plot_hdc, GetStockObject(DC_BRUSH)); + SetDCPenColor(plot_hdc, col); + SetDCBrushColor(plot_hdc, col); + Rectangle(plot_hdc, x, y, width, height); - SelectClipRgn(plot_hdc, NULL); - pen = SelectObject(plot_hdc, bak); + SelectObject(plot_hdc,original); /* Restoring the original pen object */ - DeleteObject(pen); DeleteObject(clipregion); - return true; + return NSERROR_OK; + } -static bool rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style) -{ - PLOT_LOG("rectangle from %d,%d to %d,%d", x0, y0, x1, y1); - /* ensure the plot HDC is set */ - if (plot_hdc == NULL) { - LOG("HDC not set on call to plotters"); - return false; - } - - HRGN clipregion = CreateRectRgnIndirect(&plot_clip); - if (clipregion == NULL) { - return false; +/** + * plot an alpha blended bitmap + * + * blunt force truma way of achiving alpha blended plotting + */ +static nserror +plot_alpha_bitmap(HDC hdc, + struct bitmap *bitmap, + int x, int y, + int width, int height) +{ +#ifdef WINDOWS_GDI_ALPHA_WORKED + BLENDFUNCTION blnd = { AC_SRC_OVER, 0, 0xff, AC_SRC_ALPHA }; + HDC bmihdc; + bool bltres; + bmihdc = CreateCompatibleDC(hdc); + SelectObject(bmihdc, bitmap->windib); + bltres = AlphaBlend(hdc, + x, y, + width, height, + bmihdc, + 0, 0, + bitmap->width, bitmap->height, + blnd); + DeleteDC(bmihdc); + if (!bltres) { + return NSERROR_INVALID; } +#else + HDC Memhdc; + BITMAPINFOHEADER bmih; + int v, vv, vi, h, hh, width4, transparency; + unsigned char alpha; + bool isscaled = false; /* set if the scaled bitmap requires freeing */ + BITMAP MemBM; + BITMAPINFO *bmi; + HBITMAP MemBMh; - x1++; - y1++; - - COLORREF pencol = (DWORD)(style->stroke_colour & 0x00FFFFFF); - DWORD penstyle = PS_GEOMETRIC | - (style->stroke_type == PLOT_OP_TYPE_DOT ? PS_DOT : - (style->stroke_type == PLOT_OP_TYPE_DASH ? PS_DASH : - (style->stroke_type == PLOT_OP_TYPE_NONE ? PS_NULL : - 0))); - LOGBRUSH lb = {BS_SOLID, pencol, 0}; - LOGBRUSH lb1 = {BS_SOLID, style->fill_colour, 0}; - if (style->fill_type == PLOT_OP_TYPE_NONE) - lb1.lbStyle = BS_HOLLOW; + PLOT_LOG("%p bitmap %d,%d width %d height %d", bitmap, x, y, width, height); + PLOT_LOG("clipped %ld,%ld to %ld,%ld",plot_clip.left, plot_clip.top, plot_clip.right, plot_clip.bottom); - HPEN pen = ExtCreatePen(penstyle, style->stroke_width, &lb, 0, NULL); - if (pen == NULL) { - return false; - } - HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen); - if (penbak == NULL) { - DeleteObject(pen); - return false; - } - HBRUSH brush = CreateBrushIndirect(&lb1); - if (brush == NULL) { - SelectObject(plot_hdc, penbak); - DeleteObject(pen); - return false; - } - HGDIOBJ brushbak = SelectObject(plot_hdc, (HGDIOBJ) brush); - if (brushbak == NULL) { - SelectObject(plot_hdc, penbak); - DeleteObject(pen); - DeleteObject(brush); - return false; + Memhdc = CreateCompatibleDC(hdc); + if (Memhdc == NULL) { + return NSERROR_INVALID; } - SelectClipRgn(plot_hdc, clipregion); - - Rectangle(plot_hdc, x0, y0, x1, y1); + if ((bitmap->width != width) || + (bitmap->height != height)) { + PLOT_LOG("scaling from %d,%d to %d,%d", + bitmap->width, bitmap->height, width, height); + bitmap = bitmap_scale(bitmap, width, height); + if (bitmap == NULL) { + return NSERROR_INVALID; + } + isscaled = true; + } - pen = SelectObject(plot_hdc, penbak); - brush = SelectObject(plot_hdc, brushbak); - SelectClipRgn(plot_hdc, NULL); - DeleteObject(pen); - DeleteObject(brush); - DeleteObject(clipregion); + bmi = (BITMAPINFO *) malloc(sizeof(BITMAPINFOHEADER) + + (bitmap->width * bitmap->height * 4)); + if (bmi == NULL) { + DeleteDC(Memhdc); + return NSERROR_INVALID; + } - return true; -} + MemBMh = CreateCompatibleBitmap(hdc, bitmap->width, bitmap->height); + if (MemBMh == NULL){ + free(bmi); + DeleteDC(Memhdc); + return NSERROR_INVALID; + } + /* save 'background' data for alpha channel work */ + SelectObject(Memhdc, MemBMh); + BitBlt(Memhdc, 0, 0, bitmap->width, bitmap->height, hdc, x, y, SRCCOPY); + GetObject(MemBMh, sizeof(BITMAP), &MemBM); -static bool polygon(const int *p, unsigned int n, const plot_style_t *style) -{ - PLOT_LOG("polygon %d points", n); + bmih.biSize = sizeof(bmih); + bmih.biWidth = bitmap->width; + bmih.biHeight = bitmap->height; + bmih.biPlanes = 1; + bmih.biBitCount = 32; + bmih.biCompression = BI_RGB; + bmih.biSizeImage = 4 * bitmap->height * bitmap->width; + bmih.biXPelsPerMeter = 3600; /* 100 dpi */ + bmih.biYPelsPerMeter = 3600; + bmih.biClrUsed = 0; + bmih.biClrImportant = 0; + bmi->bmiHeader = bmih; - /* ensure the plot HDC is set */ - if (plot_hdc == NULL) { - LOG("HDC not set on call to plotters"); - return false; - } + GetDIBits(hdc, MemBMh, 0, bitmap->height, bmi->bmiColors, bmi, + DIB_RGB_COLORS); - POINT points[n]; - unsigned int i; - HRGN clipregion = CreateRectRgnIndirect(&plot_clip); - if (clipregion == NULL) { - return false; - } + /* then load 'foreground' bits from bitmap->pixdata */ - COLORREF pencol = (DWORD)(style->fill_colour & 0x00FFFFFF); - COLORREF brushcol = (DWORD)(style->fill_colour & 0x00FFFFFF); - HPEN pen = CreatePen(PS_GEOMETRIC | PS_NULL, 1, pencol); - if (pen == NULL) { - DeleteObject(clipregion); - return false; - } - HPEN penbak = SelectObject(plot_hdc, pen); - if (penbak == NULL) { - DeleteObject(clipregion); - DeleteObject(pen); - return false; - } - HBRUSH brush = CreateSolidBrush(brushcol); - if (brush == NULL) { - DeleteObject(clipregion); - SelectObject(plot_hdc, penbak); - DeleteObject(pen); - return false; - } - HBRUSH brushbak = SelectObject(plot_hdc, brush); - if (brushbak == NULL) { - DeleteObject(clipregion); - SelectObject(plot_hdc, penbak); - DeleteObject(pen); - DeleteObject(brush); - return false; + width4 = bitmap->width * 4; + for (v = 0, vv = 0, vi = (bitmap->height - 1) * width4; + v < bitmap->height; + v++, vv += bitmap->width, vi -= width4) { + for (h = 0, hh = 0; h < bitmap->width; h++, hh += 4) { + alpha = bitmap->pixdata[vi + hh + 3]; +/* multiplication of alpha value; subject to profiling could be optional */ + if (alpha == 0xFF) { + bmi->bmiColors[vv + h].rgbBlue = + bitmap->pixdata[vi + hh + 2]; + bmi->bmiColors[vv + h].rgbGreen = + bitmap->pixdata[vi + hh + 1]; + bmi->bmiColors[vv + h].rgbRed = + bitmap->pixdata[vi + hh]; + } else if (alpha > 0) { + transparency = 0x100 - alpha; + bmi->bmiColors[vv + h].rgbBlue = + (bmi->bmiColors[vv + h].rgbBlue + * transparency + + (bitmap->pixdata[vi + hh + 2]) * + alpha) >> 8; + bmi->bmiColors[vv + h].rgbGreen = + (bmi->bmiColors[vv + h]. + rgbGreen + * transparency + + (bitmap->pixdata[vi + hh + 1]) * + alpha) >> 8; + bmi->bmiColors[vv + h].rgbRed = + (bmi->bmiColors[vv + h].rgbRed + * transparency + + bitmap->pixdata[vi + hh] + * alpha) >> 8; + } + } } - SetPolyFillMode(plot_hdc, WINDING); - for (i = 0; i < n; i++) { - points[i].x = (long) p[2 * i]; - points[i].y = (long) p[2 * i + 1]; + SetDIBitsToDevice(hdc, x, y, bitmap->width, bitmap->height, + 0, 0, 0, bitmap->height, + (const void *) bmi->bmiColors, + bmi, DIB_RGB_COLORS); - PLOT_LOG("%ld,%ld ", points[i].x, points[i].y); + if (isscaled && bitmap && bitmap->pixdata) { + free(bitmap->pixdata); + free(bitmap); } - SelectClipRgn(plot_hdc, clipregion); - - if (n >= 2) - Polygon(plot_hdc, points, n); - - SelectClipRgn(plot_hdc, NULL); - - pen = SelectObject(plot_hdc, penbak); - brush = SelectObject(plot_hdc, brushbak); - DeleteObject(clipregion); - DeleteObject(pen); - DeleteObject(brush); + free(bmi); + DeleteObject(MemBMh); + DeleteDC(Memhdc); +#endif - return true; + return NSERROR_OK; } -static bool text(int x, int y, const char *text, size_t length, - const plot_font_style_t *style) +/** + */ +static nserror +plot_bitmap(struct bitmap *bitmap, int x, int y, int width, int height) { - PLOT_LOG("words %s at %d,%d", text, x, y); + HRGN clipregion; + nserror res = NSERROR_OK; + + /* Bail early if we can */ + if ((x >= plot_clip.right) || + ((x + width) < plot_clip.left) || + (y >= plot_clip.bottom) || + ((y + height) < plot_clip.top)) { + /* Image completely outside clip region */ + return NSERROR_OK; + } /* ensure the plot HDC is set */ if (plot_hdc == NULL) { LOG("HDC not set on call to plotters"); - return false; + return NSERROR_INVALID; } - HRGN clipregion = CreateRectRgnIndirect(&plot_clip); + clipregion = CreateRectRgnIndirect(&plot_clip); if (clipregion == NULL) { - return false; - } - - HFONT fontbak, font = get_font(style); - if (font == NULL) { - DeleteObject(clipregion); - return false; + return NSERROR_INVALID; } - int wlen; - SIZE s; - LPWSTR wstring; - fontbak = (HFONT) SelectObject(plot_hdc, font); - GetTextExtentPoint(plot_hdc, text, length, &s); -/* - RECT r; - r.left = x; - r.top = y - (3 * s.cy) / 4; - r.right = x + s.cx; - r.bottom = y + s.cy / 4; -*/ SelectClipRgn(plot_hdc, clipregion); - SetTextAlign(plot_hdc, TA_BASELINE | TA_LEFT); - if ((style->background & 0xFF000000) != 0x01000000) - /* 100% alpha */ - SetBkColor(plot_hdc, (DWORD) (style->background & 0x00FFFFFF)); - SetBkMode(plot_hdc, TRANSPARENT); - SetTextColor(plot_hdc, (DWORD) (style->foreground & 0x00FFFFFF)); + if (bitmap->opaque) { + int bltres; + /* opaque bitmap */ + if ((bitmap->width == width) && + (bitmap->height == height)) { + /* unscaled */ + bltres = SetDIBitsToDevice(plot_hdc, + x, y, + width, height, + 0, 0, + 0, + height, + bitmap->pixdata, + (BITMAPINFO *)bitmap->pbmi, + DIB_RGB_COLORS); + } else { + /* scaled */ + SetStretchBltMode(plot_hdc, COLORONCOLOR); + bltres = StretchDIBits(plot_hdc, + x, y, + width, height, + 0, 0, + bitmap->width, bitmap->height, + bitmap->pixdata, + (BITMAPINFO *)bitmap->pbmi, + DIB_RGB_COLORS, + SRCCOPY); - wlen = MultiByteToWideChar(CP_UTF8, 0, text, length, NULL, 0); - wstring = malloc(2 * (wlen + 1)); - if (wstring == NULL) { - return false; + + } + /* check to see if GDI operation failed */ + if (bltres == 0) { + res = NSERROR_INVALID; + } + PLOT_LOG("bltres = %d", bltres); + } else { + /* Bitmap with alpha.*/ + res = plot_alpha_bitmap(plot_hdc, bitmap, x, y, width, height); } - MultiByteToWideChar(CP_UTF8, 0, text, length, wstring, wlen); - TextOutW(plot_hdc, x, y, wstring, wlen); - SelectClipRgn(plot_hdc, NULL); - free(wstring); - font = SelectObject(plot_hdc, fontbak); DeleteObject(clipregion); - DeleteObject(font); - return true; + return res; } -static bool disc(int x, int y, int radius, const plot_style_t *style) -{ - PLOT_LOG("disc at %d,%d radius %d", x, y, radius); - /* ensure the plot HDC is set */ - if (plot_hdc == NULL) { - LOG("HDC not set on call to plotters"); - return false; - } - HRGN clipregion = CreateRectRgnIndirect(&plot_clip); - if (clipregion == NULL) { - return false; - } - COLORREF col = (DWORD)((style->fill_colour | style->stroke_colour) - & 0x00FFFFFF); - HPEN pen = CreatePen(PS_GEOMETRIC | PS_SOLID, 1, col); - if (pen == NULL) { - DeleteObject(clipregion); - return false; - } - HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen); - if (penbak == NULL) { - DeleteObject(clipregion); - DeleteObject(pen); - return false; - } - HBRUSH brush = CreateSolidBrush(col); - if (brush == NULL) { - DeleteObject(clipregion); - SelectObject(plot_hdc, penbak); - DeleteObject(pen); - return false; - } - HGDIOBJ brushbak = SelectObject(plot_hdc, (HGDIOBJ) brush); - if (brushbak == NULL) { - DeleteObject(clipregion); - SelectObject(plot_hdc, penbak); - DeleteObject(pen); - DeleteObject(brush); - return false; - } -/* - RECT r; - r.left = x - radius; - r.top = y - radius; - r.right = x + radius; - r.bottom = y + radius; -*/ - SelectClipRgn(plot_hdc, clipregion); - - if (style->fill_type == PLOT_OP_TYPE_NONE) - Arc(plot_hdc, x - radius, y - radius, x + radius, y + radius, - x - radius, y - radius, - x - radius, y - radius); - else - Ellipse(plot_hdc, x - radius, y - radius, x + radius, y + radius); +/** + * \brief Sets a clip rectangle for subsequent plot operations. + * + * \param ctx The current redraw context. + * \param clip The rectangle to limit all subsequent plot + * operations within. + * \return NSERROR_OK on success else error code. + */ +static nserror clip(const struct redraw_context *ctx, const struct rect *clip) +{ + PLOT_LOG("clip %d,%d to %d,%d", clip->x0, clip->y0, clip->x1, clip->y1); - SelectClipRgn(plot_hdc, NULL); - pen = SelectObject(plot_hdc, penbak); - brush = SelectObject(plot_hdc, brushbak); - DeleteObject(clipregion); - DeleteObject(pen); - DeleteObject(brush); + plot_clip.left = clip->x0; + plot_clip.top = clip->y0; + plot_clip.right = clip->x1 + 1; /* co-ordinates are exclusive */ + plot_clip.bottom = clip->y1 + 1; /* co-ordinates are exclusive */ - return true; + return NSERROR_OK; } -static bool arc(int x, int y, int radius, int angle1, int angle2, - const plot_style_t *style) + +/** + * Plots an arc + * + * plot an arc segment around (x,y), anticlockwise from angle1 + * to angle2. Angles are measured anticlockwise from + * horizontal, in degrees. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the arc plot. + * \param x The x coordinate of the arc. + * \param y The y coordinate of the arc. + * \param radius The radius of the arc. + * \param angle1 The start angle of the arc. + * \param angle2 The finish angle of the arc. + * \return NSERROR_OK on success else error code. + */ +static nserror +arc(const struct redraw_context *ctx, + const plot_style_t *style, + int x, int y, + int radius, int angle1, int angle2) { PLOT_LOG("arc centre %d,%d radius %d from %d to %d", x, y, radius, angle1, angle2); @@ -392,25 +382,25 @@ static bool arc(int x, int y, int radius, int angle1, int angle2, /* ensure the plot HDC is set */ if (plot_hdc == NULL) { LOG("HDC not set on call to plotters"); - return false; + return NSERROR_INVALID; } HRGN clipregion = CreateRectRgnIndirect(&plot_clip); if (clipregion == NULL) { - return false; + return NSERROR_INVALID; } COLORREF col = (DWORD)(style->stroke_colour & 0x00FFFFFF); HPEN pen = CreatePen(PS_GEOMETRIC | PS_SOLID, 1, col); if (pen == NULL) { DeleteObject(clipregion); - return false; + return NSERROR_INVALID; } HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen); if (penbak == NULL) { DeleteObject(clipregion); DeleteObject(pen); - return false; + return NSERROR_INVALID; } int q1, q2; @@ -466,13 +456,6 @@ static bool arc(int x, int y, int radius, int angle1, int angle2, break; } -/* - RECT r; - r.left = x - radius; - r.top = y - radius; - r.right = x + radius; - r.bottom = y + radius; -*/ SelectClipRgn(plot_hdc, clipregion); Arc(plot_hdc, x - radius, y - radius, x + radius, y + radius, @@ -484,269 +467,384 @@ static bool arc(int x, int y, int radius, int angle1, int angle2, DeleteObject(clipregion); DeleteObject(pen); - return true; + return NSERROR_OK; } -static bool -plot_block(COLORREF col, int x, int y, int width, int height) -{ - HRGN clipregion; - HGDIOBJ original = NULL; - /* Bail early if we can */ - if ((x >= plot_clip.right) || - ((x + width) < plot_clip.left) || - (y >= plot_clip.bottom) || - ((y + height) < plot_clip.top)) { - /* Image completely outside clip region */ - return true; - } +/** + * Plots a circle + * + * Plot a circle centered on (x,y), which is optionally filled. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the circle plot. + * \param x x coordinate of circle centre. + * \param y y coordinate of circle centre. + * \param radius circle radius. + * \return NSERROR_OK on success else error code. + */ +static nserror +disc(const struct redraw_context *ctx, + const plot_style_t *style, + int x, int y, int radius) +{ + PLOT_LOG("disc at %d,%d radius %d", x, y, radius); /* ensure the plot HDC is set */ if (plot_hdc == NULL) { LOG("HDC not set on call to plotters"); - return false; + return NSERROR_INVALID; } - clipregion = CreateRectRgnIndirect(&plot_clip); + HRGN clipregion = CreateRectRgnIndirect(&plot_clip); if (clipregion == NULL) { - return false; + return NSERROR_INVALID; } - SelectClipRgn(plot_hdc, clipregion); - - /* Saving the original pen object */ - original = SelectObject(plot_hdc,GetStockObject(DC_PEN)); + COLORREF col = (DWORD)((style->fill_colour | style->stroke_colour) + & 0x00FFFFFF); + HPEN pen = CreatePen(PS_GEOMETRIC | PS_SOLID, 1, col); + if (pen == NULL) { + DeleteObject(clipregion); + return NSERROR_INVALID; + } + HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen); + if (penbak == NULL) { + DeleteObject(clipregion); + DeleteObject(pen); + return NSERROR_INVALID; + } + HBRUSH brush = CreateSolidBrush(col); + if (brush == NULL) { + DeleteObject(clipregion); + SelectObject(plot_hdc, penbak); + DeleteObject(pen); + return NSERROR_INVALID; + } + HGDIOBJ brushbak = SelectObject(plot_hdc, (HGDIOBJ) brush); + if (brushbak == NULL) { + DeleteObject(clipregion); + SelectObject(plot_hdc, penbak); + DeleteObject(pen); + DeleteObject(brush); + return NSERROR_INVALID; + } - SelectObject(plot_hdc, GetStockObject(DC_PEN)); - SelectObject(plot_hdc, GetStockObject(DC_BRUSH)); - SetDCPenColor(plot_hdc, col); - SetDCBrushColor(plot_hdc, col); - Rectangle(plot_hdc, x, y, width, height); + SelectClipRgn(plot_hdc, clipregion); - SelectObject(plot_hdc,original); /* Restoring the original pen object */ + if (style->fill_type == PLOT_OP_TYPE_NONE) { + Arc(plot_hdc, x - radius, y - radius, x + radius, y + radius, + x - radius, y - radius, + x - radius, y - radius); + } else { + Ellipse(plot_hdc, x - radius, y - radius, x + radius, y + radius); + } + SelectClipRgn(plot_hdc, NULL); + pen = SelectObject(plot_hdc, penbak); + brush = SelectObject(plot_hdc, brushbak); DeleteObject(clipregion); + DeleteObject(pen); + DeleteObject(brush); - return true; - + return NSERROR_OK; } -/* blunt force truma way of achiving alpha blended plotting */ -static bool -plot_alpha_bitmap(HDC hdc, - struct bitmap *bitmap, - int x, int y, - int width, int height) -{ -#ifdef WINDOWS_GDI_ALPHA_WORKED - BLENDFUNCTION blnd = { AC_SRC_OVER, 0, 0xff, AC_SRC_ALPHA }; - HDC bmihdc; - bool bltres; - bmihdc = CreateCompatibleDC(hdc); - SelectObject(bmihdc, bitmap->windib); - bltres = AlphaBlend(hdc, - x, y, - width, height, - bmihdc, - 0, 0, - bitmap->width, bitmap->height, - blnd); - DeleteDC(bmihdc); - return bltres; -#else - HDC Memhdc; - BITMAPINFOHEADER bmih; - int v, vv, vi, h, hh, width4, transparency; - unsigned char alpha; - bool isscaled = false; /* set if the scaled bitmap requires freeing */ - BITMAP MemBM; - BITMAPINFO *bmi; - HBITMAP MemBMh; - PLOT_LOG("%p bitmap %d,%d width %d height %d", bitmap, x, y, width, height); - PLOT_LOG("clipped %ld,%ld to %ld,%ld",plot_clip.left, plot_clip.top, plot_clip.right, plot_clip.bottom); +/** + * Plots a line + * + * plot a line from (x0,y0) to (x1,y1). Coordinates are at + * centre of line width/thickness. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the line plot. + * \param line A rectangle defining the line to be drawn + * \return NSERROR_OK on success else error code. + */ +static nserror +line(const struct redraw_context *ctx, + const plot_style_t *style, + const struct rect *line) +{ + PLOT_LOG("from %d,%d to %d,%d", x0, y0, x1, y1); - Memhdc = CreateCompatibleDC(hdc); - if (Memhdc == NULL) { - return false; + /* ensure the plot HDC is set */ + if (plot_hdc == NULL) { + LOG("HDC not set on call to plotters"); + return NSERROR_INVALID; } - if ((bitmap->width != width) || - (bitmap->height != height)) { - PLOT_LOG("scaling from %d,%d to %d,%d", - bitmap->width, bitmap->height, width, height); - bitmap = bitmap_scale(bitmap, width, height); - if (bitmap == NULL) - return false; - isscaled = true; + HRGN clipregion = CreateRectRgnIndirect(&plot_clip); + if (clipregion == NULL) { + return NSERROR_INVALID; } - bmi = (BITMAPINFO *) malloc(sizeof(BITMAPINFOHEADER) + - (bitmap->width * bitmap->height * 4)); - if (bmi == NULL) { - DeleteDC(Memhdc); - return false; + COLORREF col = (DWORD)(style->stroke_colour & 0x00FFFFFF); + /* windows 0x00bbggrr */ + DWORD penstyle = PS_GEOMETRIC | ((style->stroke_type == + PLOT_OP_TYPE_DOT) ? PS_DOT : + (style->stroke_type == PLOT_OP_TYPE_DASH) ? PS_DASH: + 0); + LOGBRUSH lb = {BS_SOLID, col, 0}; + HPEN pen = ExtCreatePen(penstyle, style->stroke_width, &lb, 0, NULL); + if (pen == NULL) { + DeleteObject(clipregion); + return NSERROR_INVALID; + } + HGDIOBJ bak = SelectObject(plot_hdc, (HGDIOBJ) pen); + if (bak == NULL) { + DeleteObject(pen); + DeleteObject(clipregion); + return NSERROR_INVALID; } - MemBMh = CreateCompatibleBitmap(hdc, bitmap->width, bitmap->height); - if (MemBMh == NULL){ - free(bmi); - DeleteDC(Memhdc); - return false; - } + SelectClipRgn(plot_hdc, clipregion); + + MoveToEx(plot_hdc, line->x0, line->y0, (LPPOINT) NULL); + + LineTo(plot_hdc, line->x1, line->y1); + + SelectClipRgn(plot_hdc, NULL); + pen = SelectObject(plot_hdc, bak); + + DeleteObject(pen); + DeleteObject(clipregion); - /* save 'background' data for alpha channel work */ - SelectObject(Memhdc, MemBMh); - BitBlt(Memhdc, 0, 0, bitmap->width, bitmap->height, hdc, x, y, SRCCOPY); - GetObject(MemBMh, sizeof(BITMAP), &MemBM); + return NSERROR_OK; +} - bmih.biSize = sizeof(bmih); - bmih.biWidth = bitmap->width; - bmih.biHeight = bitmap->height; - bmih.biPlanes = 1; - bmih.biBitCount = 32; - bmih.biCompression = BI_RGB; - bmih.biSizeImage = 4 * bitmap->height * bitmap->width; - bmih.biXPelsPerMeter = 3600; /* 100 dpi */ - bmih.biYPelsPerMeter = 3600; - bmih.biClrUsed = 0; - bmih.biClrImportant = 0; - bmi->bmiHeader = bmih; - GetDIBits(hdc, MemBMh, 0, bitmap->height, bmi->bmiColors, bmi, - DIB_RGB_COLORS); +/** + * Plots a rectangle. + * + * The rectangle can be filled an outline or both controlled + * by the plot style The line can be solid, dotted or + * dashed. Top left corner at (x0,y0) and rectangle has given + * width and height. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the rectangle plot. + * \param rect A rectangle defining the line to be drawn + * \return NSERROR_OK on success else error code. + */ +static nserror +rectangle(const struct redraw_context *ctx, + const plot_style_t *style, + const struct rect *rect) +{ + PLOT_LOG("rectangle from %d,%d to %d,%d", + rect->x0, rect->y0, rect->x1, rect->y1); - /* then load 'foreground' bits from bitmap->pixdata */ + /* ensure the plot HDC is set */ + if (plot_hdc == NULL) { + LOG("HDC not set on call to plotters"); + return NSERROR_INVALID; + } - width4 = bitmap->width * 4; - for (v = 0, vv = 0, vi = (bitmap->height - 1) * width4; - v < bitmap->height; - v++, vv += bitmap->width, vi -= width4) { - for (h = 0, hh = 0; h < bitmap->width; h++, hh += 4) { - alpha = bitmap->pixdata[vi + hh + 3]; -/* multiplication of alpha value; subject to profiling could be optional */ - if (alpha == 0xFF) { - bmi->bmiColors[vv + h].rgbBlue = - bitmap->pixdata[vi + hh + 2]; - bmi->bmiColors[vv + h].rgbGreen = - bitmap->pixdata[vi + hh + 1]; - bmi->bmiColors[vv + h].rgbRed = - bitmap->pixdata[vi + hh]; - } else if (alpha > 0) { - transparency = 0x100 - alpha; - bmi->bmiColors[vv + h].rgbBlue = - (bmi->bmiColors[vv + h].rgbBlue - * transparency + - (bitmap->pixdata[vi + hh + 2]) * - alpha) >> 8; - bmi->bmiColors[vv + h].rgbGreen = - (bmi->bmiColors[vv + h]. - rgbGreen - * transparency + - (bitmap->pixdata[vi + hh + 1]) * - alpha) >> 8; - bmi->bmiColors[vv + h].rgbRed = - (bmi->bmiColors[vv + h].rgbRed - * transparency + - bitmap->pixdata[vi + hh] - * alpha) >> 8; - } - } + HRGN clipregion = CreateRectRgnIndirect(&plot_clip); + if (clipregion == NULL) { + return NSERROR_INVALID; } - SetDIBitsToDevice(hdc, x, y, bitmap->width, bitmap->height, - 0, 0, 0, bitmap->height, - (const void *) bmi->bmiColors, - bmi, DIB_RGB_COLORS); - if (isscaled && bitmap && bitmap->pixdata) { - free(bitmap->pixdata); - free(bitmap); + COLORREF pencol = (DWORD)(style->stroke_colour & 0x00FFFFFF); + DWORD penstyle = PS_GEOMETRIC | + (style->stroke_type == PLOT_OP_TYPE_DOT ? PS_DOT : + (style->stroke_type == PLOT_OP_TYPE_DASH ? PS_DASH : + (style->stroke_type == PLOT_OP_TYPE_NONE ? PS_NULL : + 0))); + LOGBRUSH lb = {BS_SOLID, pencol, 0}; + LOGBRUSH lb1 = {BS_SOLID, style->fill_colour, 0}; + if (style->fill_type == PLOT_OP_TYPE_NONE) + lb1.lbStyle = BS_HOLLOW; + + HPEN pen = ExtCreatePen(penstyle, style->stroke_width, &lb, 0, NULL); + if (pen == NULL) { + return NSERROR_INVALID; + } + HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen); + if (penbak == NULL) { + DeleteObject(pen); + return NSERROR_INVALID; + } + HBRUSH brush = CreateBrushIndirect(&lb1); + if (brush == NULL) { + SelectObject(plot_hdc, penbak); + DeleteObject(pen); + return NSERROR_INVALID; + } + HGDIOBJ brushbak = SelectObject(plot_hdc, (HGDIOBJ) brush); + if (brushbak == NULL) { + SelectObject(plot_hdc, penbak); + DeleteObject(pen); + DeleteObject(brush); + return NSERROR_INVALID; } - free(bmi); - DeleteObject(MemBMh); - DeleteDC(Memhdc); - return true; -#endif + SelectClipRgn(plot_hdc, clipregion); + + /* windows GDI call coordinates are inclusive */ + Rectangle(plot_hdc, rect->x0, rect->y0, rect->x1 + 1, rect->y1 + 1); + + pen = SelectObject(plot_hdc, penbak); + brush = SelectObject(plot_hdc, brushbak); + SelectClipRgn(plot_hdc, NULL); + DeleteObject(pen); + DeleteObject(brush); + DeleteObject(clipregion); + + return NSERROR_OK; } -static bool -plot_bitmap(struct bitmap *bitmap, int x, int y, int width, int height) +/** + * Plot a polygon + * + * Plots a filled polygon with straight lines between + * points. The lines around the edge of the ploygon are not + * plotted. The polygon is filled with the non-zero winding + * rule. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the polygon plot. + * \param p verticies of polygon + * \param n number of verticies. + * \return NSERROR_OK on success else error code. + */ +static nserror +polygon(const struct redraw_context *ctx, + const plot_style_t *style, + const int *p, + unsigned int n) { - int bltres; - HRGN clipregion; - - /* Bail early if we can */ - if ((x >= plot_clip.right) || - ((x + width) < plot_clip.left) || - (y >= plot_clip.bottom) || - ((y + height) < plot_clip.top)) { - /* Image completely outside clip region */ - return true; - } + PLOT_LOG("polygon %d points", n); /* ensure the plot HDC is set */ if (plot_hdc == NULL) { LOG("HDC not set on call to plotters"); - return false; + return NSERROR_INVALID; } - clipregion = CreateRectRgnIndirect(&plot_clip); + POINT points[n]; + unsigned int i; + HRGN clipregion = CreateRectRgnIndirect(&plot_clip); if (clipregion == NULL) { - return false; + return NSERROR_INVALID; } - SelectClipRgn(plot_hdc, clipregion); + COLORREF pencol = (DWORD)(style->fill_colour & 0x00FFFFFF); + COLORREF brushcol = (DWORD)(style->fill_colour & 0x00FFFFFF); + HPEN pen = CreatePen(PS_GEOMETRIC | PS_NULL, 1, pencol); + if (pen == NULL) { + DeleteObject(clipregion); + return NSERROR_INVALID; + } + HPEN penbak = SelectObject(plot_hdc, pen); + if (penbak == NULL) { + DeleteObject(clipregion); + DeleteObject(pen); + return NSERROR_INVALID; + } + HBRUSH brush = CreateSolidBrush(brushcol); + if (brush == NULL) { + DeleteObject(clipregion); + SelectObject(plot_hdc, penbak); + DeleteObject(pen); + return NSERROR_INVALID; + } + HBRUSH brushbak = SelectObject(plot_hdc, brush); + if (brushbak == NULL) { + DeleteObject(clipregion); + SelectObject(plot_hdc, penbak); + DeleteObject(pen); + DeleteObject(brush); + return NSERROR_INVALID; + } + SetPolyFillMode(plot_hdc, WINDING); + for (i = 0; i < n; i++) { + points[i].x = (long) p[2 * i]; + points[i].y = (long) p[2 * i + 1]; - if (bitmap->opaque) { - /* opaque bitmap */ - if ((bitmap->width == width) && - (bitmap->height == height)) { - /* unscaled */ - bltres = SetDIBitsToDevice(plot_hdc, - x, y, - width, height, - 0, 0, - 0, - height, - bitmap->pixdata, - (BITMAPINFO *)bitmap->pbmi, - DIB_RGB_COLORS); - } else { - /* scaled */ - SetStretchBltMode(plot_hdc, COLORONCOLOR); - bltres = StretchDIBits(plot_hdc, - x, y, - width, height, - 0, 0, - bitmap->width, bitmap->height, - bitmap->pixdata, - (BITMAPINFO *)bitmap->pbmi, - DIB_RGB_COLORS, - SRCCOPY); + PLOT_LOG("%ld,%ld ", points[i].x, points[i].y); + } + SelectClipRgn(plot_hdc, clipregion); - } - } else { - /* Bitmap with alpha.*/ - bltres = plot_alpha_bitmap(plot_hdc, bitmap, x, y, width, height); + if (n >= 2) { + Polygon(plot_hdc, points, n); } - PLOT_LOG("bltres = %d", bltres); + SelectClipRgn(plot_hdc, NULL); + pen = SelectObject(plot_hdc, penbak); + brush = SelectObject(plot_hdc, brushbak); DeleteObject(clipregion); + DeleteObject(pen); + DeleteObject(brush); + + return NSERROR_OK; +} - return true; +/** + * Plots a path. + * + * Path plot consisting of cubic Bezier curves. Line and fill colour is + * controlled by the plot style. + * + * \param ctx The current redraw context. + * \param pstyle Style controlling the path plot. + * \param p elements of path + * \param n nunber of elements on path + * \param width The width of the path + * \param transform A transform to apply to the path. + * \return NSERROR_OK on success else error code. + */ +static nserror +path(const struct redraw_context *ctx, + const plot_style_t *pstyle, + const float *p, + unsigned int n, + float width, + const float transform[6]) +{ + PLOT_LOG("path unimplemented"); + return NSERROR_OK; } -static bool -windows_plot_bitmap(int x, int y, - int width, int height, - struct bitmap *bitmap, colour bg, - bitmap_flags_t flags) + +/** + * Plot a bitmap + * + * Tiled plot of a bitmap image. (x,y) gives the top left + * coordinate of an explicitly placed tile. From this tile the + * image can repeat in all four directions -- up, down, left + * and right -- to the extents given by the current clip + * rectangle. + * + * The bitmap_flags say whether to tile in the x and y + * directions. If not tiling in x or y directions, the single + * image is plotted. The width and height give the dimensions + * the image is to be scaled to. + * + * \param ctx The current redraw context. + * \param bitmap The bitmap to plot + * \param x The x coordinate to plot the bitmap + * \param y The y coordiante to plot the bitmap + * \param width The width of area to plot the bitmap into + * \param height The height of area to plot the bitmap into + * \param bg the background colour to alpha blend into + * \param flags the flags controlling the type of plot operation + * \return NSERROR_OK on success else error code. + */ +static nserror +bitmap(const struct redraw_context *ctx, + struct bitmap *bitmap, + int x, int y, + int width, + int height, + colour bg, + bitmap_flags_t flags) { int xf,yf; bool repeat_x = (flags & BITMAPF_REPEAT_X); @@ -758,12 +856,12 @@ windows_plot_bitmap(int x, int y, if (bitmap == NULL) { LOG("Passed null bitmap!"); - return true; + return NSERROR_OK; } /* check if nothing to plot */ if (width == 0 || height == 0) - return true; + return NSERROR_OK; /* x and y define coordinate of top left of of the initial explicitly * placed tile. The width and height are the image scaling and the @@ -775,7 +873,7 @@ windows_plot_bitmap(int x, int y, /* Not repeating at all, so just plot it */ if ((bitmap->width == 1) && (bitmap->height == 1)) { if ((*(bitmap->pixdata + 3) & 0xff) == 0) { - return true; + return NSERROR_OK; } return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff, x, y, x + width, y + height); @@ -788,10 +886,10 @@ windows_plot_bitmap(int x, int y, * of the area. Can only be done when image is fully opaque. */ if ((bitmap->width == 1) && (bitmap->height == 1)) { if ((*(COLORREF *)bitmap->pixdata & 0xff000000) != 0) { - return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff, - plot_clip.left, - plot_clip.top, - plot_clip.right, + return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff, + plot_clip.left, + plot_clip.top, + plot_clip.right, plot_clip.bottom); } } @@ -803,10 +901,10 @@ windows_plot_bitmap(int x, int y, if (bitmap->opaque) { /** TODO: Currently using top left pixel. Maybe centre * pixel or average value would be better. */ - return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff, - plot_clip.left, - plot_clip.top, - plot_clip.right, + return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff, + plot_clip.left, + plot_clip.top, + plot_clip.right, plot_clip.bottom); } } @@ -815,12 +913,14 @@ windows_plot_bitmap(int x, int y, PLOT_LOG("clipped %ld,%ld to %ld,%ld",plot_clip.left, plot_clip.top, plot_clip.right, plot_clip.bottom); /* get left most tile position */ - if (repeat_x) + if (repeat_x) { for (; x > plot_clip.left; x -= width); + } /* get top most tile position */ - if (repeat_y) + if (repeat_y) { for (; y > plot_clip.top; y -= height); + } PLOT_LOG("repeat from %d,%d to %ld,%ld", x, y, plot_clip.right, plot_clip.bottom); @@ -833,25 +933,86 @@ windows_plot_bitmap(int x, int y, break; } if (!repeat_x) - break; + break; } - return true; + return NSERROR_OK; } -static bool flush(void) +/** + * Text plotting. + * + * \param ctx The current redraw context. + * \param fstyle plot style for this text + * \param x x coordinate + * \param y y coordinate + * \param text UTF-8 string to plot + * \param length length of string, in bytes + * \return NSERROR_OK on success else error code. + */ +static nserror +text(const struct redraw_context *ctx, + const struct plot_font_style *fstyle, + int x, + int y, + const char *text, + size_t length) { - PLOT_LOG("flush unimplemented"); - return true; -} + PLOT_LOG("words %s at %d,%d", text, x, y); -static bool path(const float *p, unsigned int n, colour fill, float width, - colour c, const float transform[6]) -{ - PLOT_LOG("path unimplemented"); - return true; + /* ensure the plot HDC is set */ + if (plot_hdc == NULL) { + LOG("HDC not set on call to plotters"); + return NSERROR_INVALID; + } + + HRGN clipregion = CreateRectRgnIndirect(&plot_clip); + if (clipregion == NULL) { + return NSERROR_INVALID; + } + + HFONT fontbak, font = get_font(fstyle); + if (font == NULL) { + DeleteObject(clipregion); + return NSERROR_INVALID; + } + int wlen; + SIZE s; + LPWSTR wstring; + fontbak = (HFONT) SelectObject(plot_hdc, font); + GetTextExtentPoint(plot_hdc, text, length, &s); + + SelectClipRgn(plot_hdc, clipregion); + + SetTextAlign(plot_hdc, TA_BASELINE | TA_LEFT); + if ((fstyle->background & 0xFF000000) != 0x01000000) { + /* 100% alpha */ + SetBkColor(plot_hdc, (DWORD) (fstyle->background & 0x00FFFFFF)); + } + SetBkMode(plot_hdc, TRANSPARENT); + SetTextColor(plot_hdc, (DWORD) (fstyle->foreground & 0x00FFFFFF)); + + wlen = MultiByteToWideChar(CP_UTF8, 0, text, length, NULL, 0); + wstring = malloc(2 * (wlen + 1)); + if (wstring == NULL) { + return NSERROR_INVALID; + } + MultiByteToWideChar(CP_UTF8, 0, text, length, wstring, wlen); + TextOutW(plot_hdc, x, y, wstring, wlen); + + SelectClipRgn(plot_hdc, NULL); + free(wstring); + font = SelectObject(plot_hdc, fontbak); + DeleteObject(clipregion); + DeleteObject(font); + + return NSERROR_OK; } + +/** + * win32 API plot operation table + */ const struct plotter_table win_plotters = { .rectangle = rectangle, .line = line, @@ -860,8 +1021,7 @@ const struct plotter_table win_plotters = { .text = text, .disc = disc, .arc = arc, - .bitmap = windows_plot_bitmap, - .flush = flush, + .bitmap = bitmap, .path = path, .option_knockout = true, }; -- cgit v1.2.3