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,444 +50,18 @@ 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) -{ - PLOT_LOG("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; - } - - 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); - - LineTo(plot_hdc, x1, y1); - - SelectClipRgn(plot_hdc, NULL); - pen = SelectObject(plot_hdc, bak); - - DeleteObject(pen); - DeleteObject(clipregion); - - return true; -} - -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; - } - - 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; - - 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; - } - - SelectClipRgn(plot_hdc, clipregion); - - Rectangle(plot_hdc, x0, y0, x1, y1); - - pen = SelectObject(plot_hdc, penbak); - brush = SelectObject(plot_hdc, brushbak); - SelectClipRgn(plot_hdc, NULL); - DeleteObject(pen); - DeleteObject(brush); - DeleteObject(clipregion); - - return true; -} - - -static bool polygon(const int *p, unsigned int n, const plot_style_t *style) -{ - 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; - } - - POINT points[n]; - unsigned int i; - HRGN clipregion = CreateRectRgnIndirect(&plot_clip); - if (clipregion == NULL) { - return false; - } - - 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; - } - 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]; - - PLOT_LOG("%ld,%ld ", points[i].x, points[i].y); - } - - 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); - - return true; -} - - -static bool text(int x, int y, const char *text, size_t length, - const plot_font_style_t *style) -{ - PLOT_LOG("words %s at %d,%d", text, x, y); - - /* 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; - } - - HFONT fontbak, font = get_font(style); - if (font == NULL) { - DeleteObject(clipregion); - return false; - } - 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)); - - wlen = MultiByteToWideChar(CP_UTF8, 0, text, length, NULL, 0); - wstring = malloc(2 * (wlen + 1)); - if (wstring == NULL) { - return false; - } - 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; -} - -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); - - SelectClipRgn(plot_hdc, NULL); - pen = SelectObject(plot_hdc, penbak); - brush = SelectObject(plot_hdc, brushbak); - DeleteObject(clipregion); - DeleteObject(pen); - DeleteObject(brush); - - return true; -} - -static bool arc(int x, int y, int radius, int angle1, int angle2, - const plot_style_t *style) -{ - PLOT_LOG("arc centre %d,%d radius %d from %d to %d", x, y, radius, - angle1, angle2); - - /* 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->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; - } - - int q1, q2; - double a1=1.0, a2=1.0, b1=1.0, b2=1.0; - q1 = (int) ((angle1 + 45) / 90) - 45; - q2 = (int) ((angle2 + 45) / 90) - 45; - while (q1 > 4) - q1 -= 4; - while (q2 > 4) - q2 -= 4; - while (q1 <= 0) - q1 += 4; - while (q2 <= 0) - q2 += 4; - angle1 = ((angle1 + 45) % 90) - 45; - angle2 = ((angle2 + 45) % 90) - 45; - - switch(q1) { - case 1: - a1 = 1.0; - b1 = -tan((M_PI / 180) * angle1); - break; - case 2: - b1 = -1.0; - a1 = -tan((M_PI / 180) * angle1); - break; - case 3: - a1 = -1.0; - b1 = tan((M_PI / 180) * angle1); - break; - case 4: - b1 = 1.0; - a1 = tan((M_PI / 180) * angle1); - break; - } - - switch(q2) { - case 1: - a2 = 1.0; - b2 = -tan((M_PI / 180) * angle2); - break; - case 2: - b2 = -1.0; - a2 = -tan((M_PI / 180) * angle2); - break; - case 3: - a2 = -1.0; - b2 = tan((M_PI / 180) * angle2); - break; - case 4: - b2 = 1.0; - a2 = tan((M_PI / 180) * 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, - x + (int)(a1 * radius), y + (int)(b1 * radius), - x + (int)(a2 * radius), y + (int)(b2 * radius)); - - SelectClipRgn(plot_hdc, NULL); - pen = SelectObject(plot_hdc, penbak); - DeleteObject(clipregion); - DeleteObject(pen); - - return true; -} - -static bool +/** + * 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) { HRGN clipregion; @@ -499,24 +73,24 @@ plot_block(COLORREF col, int x, int y, int width, int height) (y >= plot_clip.bottom) || ((y + height) < plot_clip.top)) { /* Image completely outside clip region */ - return true; - } + 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; } 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)); + original = SelectObject(plot_hdc,GetStockObject(DC_PEN)); SelectObject(plot_hdc, GetStockObject(DC_PEN)); SelectObject(plot_hdc, GetStockObject(DC_BRUSH)); @@ -528,15 +102,20 @@ plot_block(COLORREF col, int x, int y, int width, int height) DeleteObject(clipregion); - 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, + +/** + * 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 @@ -545,15 +124,17 @@ plot_alpha_bitmap(HDC hdc, bool bltres; bmihdc = CreateCompatibleDC(hdc); SelectObject(bmihdc, bitmap->windib); - bltres = AlphaBlend(hdc, - x, y, + bltres = AlphaBlend(hdc, + x, y, width, height, bmihdc, - 0, 0, + 0, 0, bitmap->width, bitmap->height, blnd); DeleteDC(bmihdc); - return bltres; + if (!bltres) { + return NSERROR_INVALID; + } #else HDC Memhdc; BITMAPINFOHEADER bmih; @@ -569,16 +150,17 @@ plot_alpha_bitmap(HDC hdc, Memhdc = CreateCompatibleDC(hdc); if (Memhdc == NULL) { - return false; + return NSERROR_INVALID; } - if ((bitmap->width != width) || + if ((bitmap->width != width) || (bitmap->height != height)) { - PLOT_LOG("scaling from %d,%d to %d,%d", + 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; + if (bitmap == NULL) { + return NSERROR_INVALID; + } isscaled = true; } @@ -586,14 +168,14 @@ plot_alpha_bitmap(HDC hdc, (bitmap->width * bitmap->height * 4)); if (bmi == NULL) { DeleteDC(Memhdc); - return false; + return NSERROR_INVALID; } MemBMh = CreateCompatibleBitmap(hdc, bitmap->width, bitmap->height); if (MemBMh == NULL){ free(bmi); DeleteDC(Memhdc); - return false; + return NSERROR_INVALID; } /* save 'background' data for alpha channel work */ @@ -655,7 +237,7 @@ plot_alpha_bitmap(HDC hdc, } } SetDIBitsToDevice(hdc, x, y, bitmap->width, bitmap->height, - 0, 0, 0, bitmap->height, + 0, 0, 0, bitmap->height, (const void *) bmi->bmiColors, bmi, DIB_RGB_COLORS); @@ -667,16 +249,19 @@ plot_alpha_bitmap(HDC hdc, free(bmi); DeleteObject(MemBMh); DeleteDC(Memhdc); - return true; #endif + + return NSERROR_OK; } -static bool +/** + */ +static nserror plot_bitmap(struct bitmap *bitmap, int x, int y, int width, int height) { - int bltres; HRGN clipregion; + nserror res = NSERROR_OK; /* Bail early if we can */ if ((x >= plot_clip.right) || @@ -684,25 +269,26 @@ plot_bitmap(struct bitmap *bitmap, int x, int y, int width, int height) (y >= plot_clip.bottom) || ((y + height) < plot_clip.top)) { /* Image completely outside clip region */ - return true; - } + 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; } clipregion = CreateRectRgnIndirect(&plot_clip); if (clipregion == NULL) { - return false; + return NSERROR_INVALID; } SelectClipRgn(plot_hdc, clipregion); if (bitmap->opaque) { + int bltres; /* opaque bitmap */ - if ((bitmap->width == width) && + if ((bitmap->width == width) && (bitmap->height == height)) { /* unscaled */ bltres = SetDIBitsToDevice(plot_hdc, @@ -717,36 +303,548 @@ plot_bitmap(struct bitmap *bitmap, int x, int y, int width, int height) } else { /* scaled */ SetStretchBltMode(plot_hdc, COLORONCOLOR); - bltres = StretchDIBits(plot_hdc, - x, y, + bltres = StretchDIBits(plot_hdc, + x, y, width, height, - 0, 0, + 0, 0, bitmap->width, bitmap->height, - bitmap->pixdata, - (BITMAPINFO *)bitmap->pbmi, - DIB_RGB_COLORS, + bitmap->pixdata, + (BITMAPINFO *)bitmap->pbmi, + DIB_RGB_COLORS, SRCCOPY); } + /* check to see if GDI operation failed */ + if (bltres == 0) { + res = NSERROR_INVALID; + } + PLOT_LOG("bltres = %d", bltres); } else { /* Bitmap with alpha.*/ - bltres = plot_alpha_bitmap(plot_hdc, bitmap, x, y, width, height); + res = plot_alpha_bitmap(plot_hdc, bitmap, x, y, width, height); } - PLOT_LOG("bltres = %d", bltres); - DeleteObject(clipregion); - return true; - + return res; } -static bool -windows_plot_bitmap(int x, int y, - int width, int height, - struct bitmap *bitmap, colour bg, - bitmap_flags_t flags) + + + +/** + * \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); + + 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 NSERROR_OK; +} + + +/** + * 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); + + /* 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; + } + + COLORREF col = (DWORD)(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; + } + + int q1, q2; + double a1=1.0, a2=1.0, b1=1.0, b2=1.0; + q1 = (int) ((angle1 + 45) / 90) - 45; + q2 = (int) ((angle2 + 45) / 90) - 45; + while (q1 > 4) + q1 -= 4; + while (q2 > 4) + q2 -= 4; + while (q1 <= 0) + q1 += 4; + while (q2 <= 0) + q2 += 4; + angle1 = ((angle1 + 45) % 90) - 45; + angle2 = ((angle2 + 45) % 90) - 45; + + switch(q1) { + case 1: + a1 = 1.0; + b1 = -tan((M_PI / 180) * angle1); + break; + case 2: + b1 = -1.0; + a1 = -tan((M_PI / 180) * angle1); + break; + case 3: + a1 = -1.0; + b1 = tan((M_PI / 180) * angle1); + break; + case 4: + b1 = 1.0; + a1 = tan((M_PI / 180) * angle1); + break; + } + + switch(q2) { + case 1: + a2 = 1.0; + b2 = -tan((M_PI / 180) * angle2); + break; + case 2: + b2 = -1.0; + a2 = -tan((M_PI / 180) * angle2); + break; + case 3: + a2 = -1.0; + b2 = tan((M_PI / 180) * angle2); + break; + case 4: + b2 = 1.0; + a2 = tan((M_PI / 180) * angle2); + break; + } + + SelectClipRgn(plot_hdc, clipregion); + + Arc(plot_hdc, x - radius, y - radius, x + radius, y + radius, + x + (int)(a1 * radius), y + (int)(b1 * radius), + x + (int)(a2 * radius), y + (int)(b2 * radius)); + + SelectClipRgn(plot_hdc, NULL); + pen = SelectObject(plot_hdc, penbak); + DeleteObject(clipregion); + DeleteObject(pen); + + return NSERROR_OK; +} + + +/** + * 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 NSERROR_INVALID; + } + + HRGN clipregion = CreateRectRgnIndirect(&plot_clip); + if (clipregion == NULL) { + return NSERROR_INVALID; + } + + 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; + } + + 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); + } + + SelectClipRgn(plot_hdc, NULL); + pen = SelectObject(plot_hdc, penbak); + brush = SelectObject(plot_hdc, brushbak); + DeleteObject(clipregion); + DeleteObject(pen); + DeleteObject(brush); + + return NSERROR_OK; +} + + +/** + * 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); + + /* 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; + } + + 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; + } + + 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); + + return NSERROR_OK; +} + + +/** + * 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); + + /* 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; + } + + 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; + } + + 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; +} + + +/** + * 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) +{ + 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 NSERROR_INVALID; + } + + POINT points[n]; + unsigned int i; + HRGN clipregion = CreateRectRgnIndirect(&plot_clip); + if (clipregion == NULL) { + return NSERROR_INVALID; + } + + 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]; + + PLOT_LOG("%ld,%ld ", points[i].x, points[i].y); + } + + 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); + + return NSERROR_OK; +} + + +/** + * 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; +} + + +/** + * 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); + + /* 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; } -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; -} +/** + * 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, };