From 83e6c1ba57e47efcb03573d8fdd17aa0cd0ff6dd Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Wed, 13 Oct 2010 20:29:30 +0000 Subject: fix bitmap plotting svn path=/trunk/netsurf/; revision=10882 --- windows/plot.c | 462 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 299 insertions(+), 163 deletions(-) (limited to 'windows/plot.c') diff --git a/windows/plot.c b/windows/plot.c index f985d0870..78dcb8cfd 100644 --- a/windows/plot.c +++ b/windows/plot.c @@ -53,6 +53,8 @@ static float nsws_plot_scale = 1.0; static RECT localhistory_clip; +static RECT plot_clip; + static bool clip(int x0, int y0, int x1, int y1) { @@ -72,7 +74,13 @@ static bool clip(int x0, int y0, int x1, int y1) clip->top = y0 ; clip->right = x1; clip->bottom = y1; - + + + plot_clip.left = x0; + plot_clip.top = y0; + plot_clip.right = x1 + 1; /* rectangle co-ordinates are exclusive */ + plot_clip.bottom = y1 + 1; /* rectangle co-ordinates are exclusive */ + return true; } @@ -122,16 +130,16 @@ static bool line(int x0, int y0, int x1, int y1, const plot_style_t *style) r.top = y0; r.right = x1; r.bottom = y1; - + SelectClipRgn(hdc, clipregion); - + MoveToEx(hdc, x0, y0, (LPPOINT) NULL); LineTo(hdc, x1, y1); - + SelectClipRgn(hdc, NULL); /* ValidateRect(current_hwnd, &r); - */ + */ pen = SelectObject(hdc, bak); DeleteObject(pen); DeleteObject(clipregion); @@ -140,19 +148,11 @@ static bool line(int x0, int y0, int x1, int y1, const plot_style_t *style) return true; } -static bool rectangle(int x0, int y0, int x1, int y1, const plot_style_t - *style) +static bool rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style) { x1++; y1++; -/* x0 = MAX(x0, 0); - y0 = MAX(y0, 0); - if (!((current_gui == NULL) || (thumbnail))) { - x1 = MIN(x1, gui_window_width(current_gui)); - y1 = MIN(y1, gui_window_height(current_gui)); - } -*/ -#if NSWS_PLOT_DEBUG +#if NSWS_PLOT_DEBUG LOG(("rectangle from %d,%d to %d,%d thumbnail %d", x0, y0, x1, y1, thumbnail)); #endif @@ -160,37 +160,24 @@ static bool rectangle(int x0, int y0, int x1, int y1, const plot_style_t if (hdc == NULL) { return false; } -/* RECT *clipr = gui_window_clip_rect(current_gui); - if (clipr == NULL) - clipr = &localhistory_clip; - HRGN clipregion = CreateRectRgnIndirect(clipr); - if (clipregion == NULL) { - - ReleaseDC(current_hwnd, hdc); - return false; - } -*/ COLORREF pencol = (DWORD)(style->stroke_colour & 0x00FFFFFF); - DWORD penstyle = PS_GEOMETRIC | + 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 : + (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) { -// DeleteObject(clipregion); - ReleaseDC(current_hwnd, hdc); return false; } HGDIOBJ penbak = SelectObject(hdc, (HGDIOBJ) pen); if (penbak == NULL) { -// DeleteObject(clipregion); DeleteObject(pen); ReleaseDC(current_hwnd, hdc); @@ -198,7 +185,6 @@ static bool rectangle(int x0, int y0, int x1, int y1, const plot_style_t } HBRUSH brush = CreateBrushIndirect(&lb1); if (brush == NULL) { -// DeleteObject(clipregion); SelectObject(hdc, penbak); DeleteObject(pen); @@ -207,7 +193,6 @@ static bool rectangle(int x0, int y0, int x1, int y1, const plot_style_t } HGDIOBJ brushbak = SelectObject(hdc, (HGDIOBJ) brush); if (brushbak == NULL) { -// DeleteObject(clipregion); SelectObject(hdc, penbak); DeleteObject(pen); DeleteObject(brush); @@ -215,22 +200,11 @@ static bool rectangle(int x0, int y0, int x1, int y1, const plot_style_t ReleaseDC(current_hwnd, hdc); return false; } - RECT r; - r.left = x0; - r.top = y0; - r.right = x1; - r.bottom = y1; - //SelectClipRgn(hdc, clipregion); - Rectangle(hdc, x0, y0, x1, y1); - //SelectClipRgn(hdc, NULL); -/* ValidateRect(current_hwnd, &r); - */ pen = SelectObject(hdc, penbak); brush = SelectObject(hdc, brushbak); -// DeleteObject(clipregion); DeleteObject(pen); DeleteObject(brush); @@ -303,9 +277,9 @@ static bool polygon(const int *p, unsigned int n, const plot_style_t *style) printf ("%ld,%ld ", points[i].x, points[i].y); #endif } - + SelectClipRgn(hdc, clipregion); - + if (n >= 2) Polygon(hdc, points, n); @@ -325,7 +299,7 @@ static bool polygon(const int *p, unsigned int n, const plot_style_t *style) } -static bool text(int x, int y, const char *text, size_t length, +static bool text(int x, int y, const char *text, size_t length, const plot_font_style_t *style) { #if NSWS_PLOT_DEBUG @@ -343,7 +317,7 @@ static bool text(int x, int y, const char *text, size_t length, ReleaseDC(current_hwnd, hdc); return false; } - + HFONT fontbak, font = get_font(style); if (font == NULL) { DeleteObject(clipregion); @@ -364,9 +338,9 @@ static bool text(int x, int y, const char *text, size_t length, r.bottom = y + s.cy / 4; SelectClipRgn(hdc, clipregion); - + SetTextAlign(hdc, TA_BASELINE | TA_LEFT); - if ((style->background & 0xFF000000) != 0x01000000) + if ((style->background & 0xFF000000) != 0x01000000) /* 100% alpha */ SetBkColor(hdc, (DWORD) (style->background & 0x00FFFFFF)); SetBkMode(hdc, TRANSPARENT); @@ -410,7 +384,7 @@ static bool disc(int x, int y, int radius, const plot_style_t *style) ReleaseDC(current_hwnd, hdc); return false; } - + COLORREF col = (DWORD)((style->fill_colour | style->stroke_colour) & 0x00FFFFFF); HPEN pen = CreatePen(PS_GEOMETRIC | PS_SOLID, 1, col); @@ -454,7 +428,7 @@ static bool disc(int x, int y, int radius, const plot_style_t *style) r.bottom = y + radius; SelectClipRgn(hdc, clipregion); - + if (style->fill_type == PLOT_OP_TYPE_NONE) Arc(hdc, x - radius, y - radius, x + radius, y + radius, x - radius, y - radius, @@ -526,7 +500,7 @@ static bool arc(int x, int y, int radius, int angle1, int angle2, q2 += 4; angle1 = ((angle1 + 45) % 90) - 45; angle2 = ((angle2 + 45) % 90) - 45; - + switch(q1) { case 1: a1 = 1.0; @@ -545,7 +519,7 @@ static bool arc(int x, int y, int radius, int angle1, int angle2, a1 = tan((M_PI / 180) * angle1); break; } - + switch(q2) { case 1: a2 = 1.0; @@ -564,18 +538,18 @@ static bool arc(int x, int y, int radius, int angle1, int angle2, a2 = tan((M_PI / 180) * angle2); break; } - + r.left = x - radius; r.top = y - radius; r.right = x + radius; r.bottom = y + radius; SelectClipRgn(hdc, clipregion); - + Arc(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(hdc, NULL); /* ValidateRect(current_hwnd, &r); */ @@ -587,90 +561,109 @@ static bool arc(int x, int y, int radius, int angle1, int angle2, return true; } - -static bool bitmap(int x, int y, int width, int height, - struct bitmap *bitmap, colour bg, - bitmap_flags_t flags) +static bool +plot_block(COLORREF col, int x, int y, int width, int height) { -#if NSWS_PLOT_DEBUG - LOG(("%p bitmap %d,%d width %d height %d", current_hwnd, x, y, width, - height)); -#endif - if (bitmap == NULL) - return false; - HDC hdc = GetDC(current_hwnd); - if (hdc == NULL) { - return false; - } - RECT *cliprect = gui_window_clip_rect(current_gui); - if (cliprect == NULL) - cliprect = &localhistory_clip; - HRGN clipregion = CreateRectRgnIndirect(cliprect); + HDC hdc; + 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; + } + + clipregion = CreateRectRgnIndirect(&plot_clip); if (clipregion == NULL) { - - ReleaseDC(current_hwnd, hdc); return false; } - HDC Memhdc = CreateCompatibleDC(hdc); - if (Memhdc == NULL) { + + hdc = GetDC(current_hwnd); + if (hdc == NULL) { DeleteObject(clipregion); - ReleaseDC(current_hwnd, hdc); return false; } + + SelectClipRgn(hdc, clipregion); + + /* Saving the original pen object */ + original = SelectObject(hdc,GetStockObject(DC_PEN)); + + SelectObject(hdc, GetStockObject(DC_PEN)); + SelectObject(hdc, GetStockObject(DC_BRUSH)); + SetDCPenColor(hdc, col); + SetDCBrushColor(hdc, col); + Rectangle(hdc,x,y,width,height); + + SelectObject(hdc,original); /* Restoring the original pen object */ + + DeleteObject(clipregion); + ReleaseDC(current_hwnd, hdc); + return true; + +} + +/* 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) +{ + HDC Memhdc; BITMAPINFOHEADER bmih; - RECT r; int v, vv, vi, h, hh, width4, transparency; unsigned char alpha; - bool modifying = false; + bool isscaled = false; /* set if the scaled bitmap requires freeing */ + BITMAP MemBM; + BITMAPINFO *bmi; + HBITMAP MemBMh; - SelectClipRgn(hdc, clipregion); - if ((bitmap->width != width) || (bitmap->height != height)) { +#if NSWS_PLOT_DEBUG + LOG(("%p bitmap %d,%d width %d height %d", bitmap, x, y, width, height)); + LOG(("clipped %ld,%ld to %ld,%ld",plot_clip.left, plot_clip.top, plot_clip.right, plot_clip.bottom)); +#endif + + assert(bitmap != NULL); + + Memhdc = CreateCompatibleDC(hdc); + if (Memhdc == NULL) { + return false; + } + + if ((bitmap->width != width) || + (bitmap->height != height)) { + 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; - modifying = true; - } - - if ((flags & BITMAPF_REPEAT_X) || (flags & BITMAPF_REPEAT_Y)) { - struct bitmap *prebitmap = bitmap_pretile(bitmap, - cliprect->right - x, - cliprect->bottom - y, flags); - if (prebitmap == NULL) - return false; - if (modifying) { - free(bitmap->pixdata); - free(bitmap); - } - modifying = true; - bitmap = prebitmap; + isscaled = true; } - BITMAP MemBM; - BITMAPINFO *bmi = (BITMAPINFO *) malloc(sizeof(BITMAPINFOHEADER) + - (bitmap->width * bitmap->height * 4)); + + bmi = (BITMAPINFO *) malloc(sizeof(BITMAPINFOHEADER) + + (bitmap->width * bitmap->height * 4)); if (bmi == NULL) { - DeleteObject(clipregion); DeleteDC(Memhdc); - - ReleaseDC(current_hwnd, hdc); return false; } - HBITMAP MemBMh = CreateCompatibleBitmap( - hdc, bitmap->width, bitmap->height); + + MemBMh = CreateCompatibleBitmap(hdc, bitmap->width, bitmap->height); if (MemBMh == NULL){ - DeleteObject(clipregion); free(bmi); DeleteDC(Memhdc); - - ReleaseDC(current_hwnd, hdc); return false; } /* save 'background' data for alpha channel work */ SelectObject(Memhdc, MemBMh); - BitBlt(Memhdc, 0, 0, bitmap->width, bitmap->height, hdc, x, y, - SRCCOPY); + BitBlt(Memhdc, 0, 0, bitmap->width, bitmap->height, hdc, x, y, SRCCOPY); GetObject(MemBMh, sizeof(BITMAP), &MemBM); - + bmih.biSize = sizeof(bmih); bmih.biWidth = bitmap->width; bmih.biHeight = bitmap->height; @@ -683,15 +676,15 @@ static bool bitmap(int x, int y, int width, int height, bmih.biClrUsed = 0; bmih.biClrImportant = 0; bmi->bmiHeader = bmih; - + GetDIBits(hdc, MemBMh, 0, bitmap->height, bmi->bmiColors, bmi, DIB_RGB_COLORS); - + /* then load 'foreground' bits from bitmap->pixdata */ - + width4 = bitmap->width * 4; - for (v = 0, vv = 0, vi = (bitmap->height - 1) * width4; - v < bitmap->height; + 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]; @@ -701,13 +694,13 @@ static bool bitmap(int x, int y, int width, int height, bitmap->pixdata[vi + hh + 2]; bmi->bmiColors[vv + h].rgbGreen = bitmap->pixdata[vi + hh + 1]; - bmi->bmiColors[vv + h].rgbRed = + 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 + + * transparency + (bitmap->pixdata[vi + hh + 2]) * alpha) >> 8; bmi->bmiColors[vv + h].rgbGreen = @@ -722,61 +715,204 @@ static bool bitmap(int x, int y, int width, int height, bitmap->pixdata[vi + hh] * alpha) >> 8; } -/* alternative simple 2/3 stage alpha value handling */ -/* if (bitmap->pixdata[vi + hh + 3] > 0xAA) { - 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 (bitmap->pixdata[vi + hh + 3] > 0x70){ - bmi->bmiColors[vv + h].rgbBlue = - (bmi->bmiColors[vv + h].rgbBlue + - bitmap->pixdata[vi + hh + 2]) / 2; - bmi->bmiColors[vv + h].rgbRed = - (bmi->bmiColors[vv + h].rgbRed + - bitmap->pixdata[vi + hh]) / 2; - bmi->bmiColors[vv + h].rgbGreen = - (bmi->bmiColors[vv + h].rgbGreen + - bitmap->pixdata[vi + hh + 1]) / 2; - } else if (bitmap->pixdata[vi + hh + 3] > 0x30){ - bmi->bmiColors[vv + h].rgbBlue = - (bmi->bmiColors[vv + h].rgbBlue * 3 + - bitmap->pixdata[vi + hh + 2]) / 4; - bmi->bmiColors[vv + h].rgbRed = - (bmi->bmiColors[vv + h].rgbRed * 3 + - bitmap->pixdata[vi + hh]) / 4; - bmi->bmiColors[vv + h].rgbGreen = - (bmi->bmiColors[vv + h].rgbGreen * 3 + - bitmap->pixdata[vi + hh + 1]) / 4; - } -*/ } + } } SetDIBitsToDevice(hdc, x, y, bitmap->width, bitmap->height, - 0, 0, 0, bitmap->height, (const void *) bmi->bmiColors, + 0, 0, 0, bitmap->height, + (const void *) bmi->bmiColors, bmi, DIB_RGB_COLORS); - - r.left = x; - r.top = y; - r.right = x + bitmap->width; - r.bottom = y + bitmap->height; - if (modifying && bitmap && bitmap->pixdata) { + + if (isscaled && bitmap && bitmap->pixdata) { free(bitmap->pixdata); free(bitmap); } - -/* ValidateRect(current_hwnd, &r); - */ free(bmi); -/* SelectClipRgn(hdc, NULL); - */ DeleteObject(clipregion); + + free(bmi); DeleteObject(MemBMh); DeleteDC(Memhdc); + return true; +} + + +static bool +plot_bitmap(struct bitmap *bitmap, int x, int y, int width, int height) +{ + int bltres; + HDC hdc; + 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; + } + + clipregion = CreateRectRgnIndirect(&plot_clip); + if (clipregion == NULL) { + return false; + } + + hdc = GetDC(current_hwnd); + if (hdc == NULL) { + DeleteObject(clipregion); + return false; + } + SelectClipRgn(hdc, clipregion); + + if (bitmap->opaque) { + /* opaque bitmap */ + if ((bitmap->width == width) && + (bitmap->height == height)) { + /* unscaled */ + bltres = SetDIBitsToDevice(hdc, + x, y, + width, height, + 0, 0, + 0, + height, + bitmap->pixdata, + (BITMAPINFO *)bitmap->pbmi, + DIB_RGB_COLORS); + } else { + /* scaled */ + SetStretchBltMode(hdc, COLORONCOLOR); + bltres = StretchDIBits(hdc, + x, y, + width, height, + 0, 0, + bitmap->width, bitmap->height, + bitmap->pixdata, + (BITMAPINFO *)bitmap->pbmi, + DIB_RGB_COLORS, + SRCCOPY); + + + } + } else { + /* Bitmap with alpha.*/ +#ifdef WINDOWS_GDI_ALPHA_WORKED + BLENDFUNCTION blnd = { AC_SRC_OVER, 0, 0xff, AC_SRC_ALPHA }; + HDC bmihdc; + bmihdc = CreateCompatibleDC(hdc); + SelectObject(bmihdc, bitmap->windib); + bltres = AlphaBlend(hdc, + x, y, + width, height, + bmihdc, + 0, 0, + bitmap->width, bitmap->height, + blnd); + DeleteDC(bmihdc); +#else + bltres = plot_alpha_bitmap(hdc, bitmap, x, y, width, height); + LOG(("bltres = %d", bltres)); +#endif + + } + + DeleteObject(clipregion); ReleaseDC(current_hwnd, hdc); return true; + } +static bool +windows_plot_bitmap(int x, int y, + int width, int height, + struct bitmap *bitmap, colour bg, + bitmap_flags_t flags) +{ + int xf,yf; + bool repeat_x = (flags & BITMAPF_REPEAT_X); + bool repeat_y = (flags & BITMAPF_REPEAT_Y); + + /* Bail early if we can */ + + /* check if nothing to plot */ + if (width == 0 || height == 0) + return true; + + /* 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 + * bounding box defines the extent of the repeat (which may go in all + * four directions from the initial tile). + */ + + if (!(repeat_x || repeat_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 plot_block(*(COLORREF *)bitmap->pixdata, x, y, x + width, y + height); + + } else { + return plot_bitmap(bitmap, x, y, width, height); + } + } + + /* Optimise tiled plots of 1x1 bitmaps by replacing with a flat fill + * 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, + plot_clip.left, + plot_clip.top, + plot_clip.right, + plot_clip.bottom); + } + } + + /* Optimise tiled plots of bitmaps scaled to 1x1 by replacing with + * a flat fill of the area. Can only be done when image is fully + * opaque. */ + if ((width == 1) && (height == 1)) { + if (bitmap->opaque) { + /** TODO: Currently using top left pixel. Maybe centre + * pixel or average value would be better. */ + return plot_block(*(COLORREF *)bitmap->pixdata, + plot_clip.left, + plot_clip.top, + plot_clip.right, + plot_clip.bottom); + } + } +/* + LOG(("Tiled plotting %d,%d by %d,%d",x,y,width,height)); + 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) + for (; x > plot_clip.left; x -= width); + + /* get top most tile position */ + if (repeat_y) + for (; y > plot_clip.top; y -= height); + +/* + LOG(("repeat from %d,%d to %ld,%ld", x, y, plot_clip.right, plot_clip.bottom)); +*/ + + /* tile down and across to extents */ + for (xf = x; xf < plot_clip.right; xf += width) { + for (yf = y; yf < plot_clip.bottom; yf += height) { + + plot_bitmap(bitmap, xf, yf, width, height); + if (!repeat_y) + break; + } + if (!repeat_x) + break; + } + return true; +} + + static bool flush(void) { #if NSWS_PLOT_DEBUG @@ -812,7 +948,7 @@ struct plotter_table plot = { .text = text, .disc = disc, .arc = arc, - .bitmap = bitmap, + .bitmap = windows_plot_bitmap, .flush = flush, .path = path, .option_knockout = true, -- cgit v1.2.3