NetSurf
plot.c
Go to the documentation of this file.
1/*
2 * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
3 * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
6 *
7 * NetSurf is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * NetSurf is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/**
21 * \file
22 * win32 plotter implementation.
23 */
24
25#include "utils/config.h"
26#include <sys/types.h>
27#include <stdint.h>
28#include <string.h>
29#include <limits.h>
30#include <math.h>
31#include <windows.h>
32
33#include "utils/log.h"
34#include "utils/utf8.h"
35#include "netsurf/mouse.h"
36#include "netsurf/window.h"
37#include "netsurf/plotters.h"
38
39#include "windows/bitmap.h"
40#include "windows/font.h"
41#include "windows/gui.h"
42#include "windows/plot.h"
43
45
46/** currently set clipping rectangle */
47static RECT plot_clip;
48
49
50/**
51 * bitmap helper to plot a solid block of colour
52 *
53 * \param col colour to plot with
54 * \param x the x coordinate to plot at
55 * \param y the y coordinate to plot at
56 * \param width the width of block to plot
57 * \param height the height to plot
58 * \return NSERROR_OK on sucess else error code.
59 */
60static nserror
61plot_block(COLORREF col, int x, int y, int width, int height)
62{
63 HRGN clipregion;
64 HGDIOBJ original = NULL;
65
66 /* Bail early if we can */
67 if ((x >= plot_clip.right) ||
68 ((x + width) < plot_clip.left) ||
69 (y >= plot_clip.bottom) ||
70 ((y + height) < plot_clip.top)) {
71 /* Image completely outside clip region */
72 return NSERROR_OK;
73 }
74
75 /* ensure the plot HDC is set */
76 if (plot_hdc == NULL) {
77 NSLOG(netsurf, INFO, "HDC not set on call to plotters");
78 return NSERROR_INVALID;
79 }
80
81 clipregion = CreateRectRgnIndirect(&plot_clip);
82 if (clipregion == NULL) {
83 return NSERROR_INVALID;
84 }
85
86 SelectClipRgn(plot_hdc, clipregion);
87
88 /* Saving the original pen object */
89 original = SelectObject(plot_hdc,GetStockObject(DC_PEN));
90
91 SelectObject(plot_hdc, GetStockObject(DC_PEN));
92 SelectObject(plot_hdc, GetStockObject(DC_BRUSH));
93 SetDCPenColor(plot_hdc, col);
94 SetDCBrushColor(plot_hdc, col);
95 Rectangle(plot_hdc, x, y, width, height);
96
97 SelectObject(plot_hdc,original); /* Restoring the original pen object */
98
99 DeleteObject(clipregion);
100
101 return NSERROR_OK;
102
103}
104
105
106/**
107 * plot an alpha blended bitmap
108 *
109 * blunt force truma way of achiving alpha blended plotting
110 *
111 * \param hdc drawing cotext
112 * \param bitmap bitmap to render
113 * \param x x coordinate to plot at
114 * \param y y coordinate to plot at
115 * \param width The width to plot the bitmap into
116 * \param height The height to plot the bitmap into
117 * \return NSERROR_OK on success else appropriate error code.
118 */
119static nserror
121 struct bitmap *bitmap,
122 int x, int y,
123 int width, int height)
124{
125#ifdef WINDOWS_GDI_ALPHA_WORKED
126 BLENDFUNCTION blnd = { AC_SRC_OVER, 0, 0xff, AC_SRC_ALPHA };
127 HDC bmihdc;
128 bool bltres;
129 bmihdc = CreateCompatibleDC(hdc);
130 SelectObject(bmihdc, bitmap->windib);
131 bltres = AlphaBlend(hdc,
132 x, y,
133 width, height,
134 bmihdc,
135 0, 0,
137 blnd);
138 DeleteDC(bmihdc);
139 if (!bltres) {
140 return NSERROR_INVALID;
141 }
142#else
143 HDC Memhdc;
144 BITMAPINFOHEADER bmih;
145 int v, vv, vi, h, hh, width4, transparency;
146 unsigned char alpha;
147 bool isscaled = false; /* set if the scaled bitmap requires freeing */
148 BITMAP MemBM;
149 BITMAPINFO *bmi;
150 HBITMAP MemBMh;
151
152 NSLOG(plot, DEEPDEBUG, "%p bitmap %d,%d width %d height %d",
153 bitmap, x, y, width, height);
154 NSLOG(plot, DEEPDEBUG, "clipped %ld,%ld to %ld,%ld",
155 plot_clip.left, plot_clip.top,
156 plot_clip.right, plot_clip.bottom);
157
158 Memhdc = CreateCompatibleDC(hdc);
159 if (Memhdc == NULL) {
160 return NSERROR_INVALID;
161 }
162
163 if ((bitmap->width != width) ||
164 (bitmap->height != height)) {
165 NSLOG(plot, DEEPDEBUG, "scaling from %d,%d to %d,%d",
168 if (bitmap == NULL) {
169 return NSERROR_INVALID;
170 }
171 isscaled = true;
172 }
173
174 bmi = (BITMAPINFO *) malloc(sizeof(BITMAPINFOHEADER) +
175 (bitmap->width * bitmap->height * 4));
176 if (bmi == NULL) {
177 DeleteDC(Memhdc);
178 return NSERROR_INVALID;
179 }
180
181 MemBMh = CreateCompatibleBitmap(hdc, bitmap->width, bitmap->height);
182 if (MemBMh == NULL){
183 free(bmi);
184 DeleteDC(Memhdc);
185 return NSERROR_INVALID;
186 }
187
188 /* save 'background' data for alpha channel work */
189 SelectObject(Memhdc, MemBMh);
190 BitBlt(Memhdc, 0, 0, bitmap->width, bitmap->height, hdc, x, y, SRCCOPY);
191 GetObject(MemBMh, sizeof(BITMAP), &MemBM);
192
193 bmih.biSize = sizeof(bmih);
194 bmih.biWidth = bitmap->width;
195 bmih.biHeight = bitmap->height;
196 bmih.biPlanes = 1;
197 bmih.biBitCount = 32;
198 bmih.biCompression = BI_RGB;
199 bmih.biSizeImage = 4 * bitmap->height * bitmap->width;
200 bmih.biXPelsPerMeter = 3600; /* 100 dpi */
201 bmih.biYPelsPerMeter = 3600;
202 bmih.biClrUsed = 0;
203 bmih.biClrImportant = 0;
204 bmi->bmiHeader = bmih;
205
206 GetDIBits(hdc, MemBMh, 0, bitmap->height, bmi->bmiColors, bmi,
207 DIB_RGB_COLORS);
208
209 /* then load 'foreground' bits from bitmap->pixdata */
210
211 width4 = bitmap->width * 4;
212 for (v = 0, vv = 0, vi = (bitmap->height - 1) * width4;
213 v < bitmap->height;
214 v++, vv += bitmap->width, vi -= width4) {
215 for (h = 0, hh = 0; h < bitmap->width; h++, hh += 4) {
216 alpha = bitmap->pixdata[vi + hh + 3];
217/* multiplication of alpha value; subject to profiling could be optional */
218 if (alpha == 0xFF) {
219 bmi->bmiColors[vv + h].rgbBlue =
220 bitmap->pixdata[vi + hh + 2];
221 bmi->bmiColors[vv + h].rgbGreen =
222 bitmap->pixdata[vi + hh + 1];
223 bmi->bmiColors[vv + h].rgbRed =
224 bitmap->pixdata[vi + hh];
225 } else if (alpha > 0) {
226 transparency = 0x100 - alpha;
227 bmi->bmiColors[vv + h].rgbBlue =
228 (bmi->bmiColors[vv + h].rgbBlue
229 * transparency +
230 (bitmap->pixdata[vi + hh + 2]) *
231 alpha) >> 8;
232 bmi->bmiColors[vv + h].rgbGreen =
233 (bmi->bmiColors[vv + h].
234 rgbGreen
235 * transparency +
236 (bitmap->pixdata[vi + hh + 1]) *
237 alpha) >> 8;
238 bmi->bmiColors[vv + h].rgbRed =
239 (bmi->bmiColors[vv + h].rgbRed
240 * transparency +
241 bitmap->pixdata[vi + hh]
242 * alpha) >> 8;
243 }
244 }
245 }
246 SetDIBitsToDevice(hdc, x, y, bitmap->width, bitmap->height,
247 0, 0, 0, bitmap->height,
248 (const void *) bmi->bmiColors,
249 bmi, DIB_RGB_COLORS);
250
251 if (isscaled && bitmap && bitmap->pixdata) {
252 free(bitmap->pixdata);
253 free(bitmap);
254 }
255
256 free(bmi);
257 DeleteObject(MemBMh);
258 DeleteDC(Memhdc);
259#endif
260
261 return NSERROR_OK;
262}
263
264
265/**
266 * Internal bitmap plotting
267 *
268 * \param bitmap The bitmap to plot
269 * \param x x coordinate to plot at
270 * \param y y coordinate to plot at
271 * \param width The width to plot the bitmap into
272 * \param height The height to plot the bitmap into
273 * \return NSERROR_OK on success else appropriate error code.
274 */
275static nserror
276plot_bitmap(struct bitmap *bitmap, int x, int y, int width, int height)
277{
278 HRGN clipregion;
279 nserror res = NSERROR_OK;
280
281 /* Bail early if we can */
282 if ((x >= plot_clip.right) ||
283 ((x + width) < plot_clip.left) ||
284 (y >= plot_clip.bottom) ||
285 ((y + height) < plot_clip.top)) {
286 /* Image completely outside clip region */
287 return NSERROR_OK;
288 }
289
290 /* ensure the plot HDC is set */
291 if (plot_hdc == NULL) {
292 NSLOG(netsurf, INFO, "HDC not set on call to plotters");
293 return NSERROR_INVALID;
294 }
295
296 clipregion = CreateRectRgnIndirect(&plot_clip);
297 if (clipregion == NULL) {
298 return NSERROR_INVALID;
299 }
300
301 SelectClipRgn(plot_hdc, clipregion);
302
303 if (bitmap->opaque) {
304 int bltres;
305 /* opaque bitmap */
306 if ((bitmap->width == width) &&
307 (bitmap->height == height)) {
308 /* unscaled */
309 bltres = SetDIBitsToDevice(plot_hdc,
310 x, y,
311 width, height,
312 0, 0,
313 0,
314 height,
316 (BITMAPINFO *)bitmap->pbmi,
317 DIB_RGB_COLORS);
318 } else {
319 /* scaled */
320 SetStretchBltMode(plot_hdc, COLORONCOLOR);
321 bltres = StretchDIBits(plot_hdc,
322 x, y,
323 width, height,
324 0, 0,
327 (BITMAPINFO *)bitmap->pbmi,
328 DIB_RGB_COLORS,
329 SRCCOPY);
330
331
332 }
333 /* check to see if GDI operation failed */
334 if (bltres == 0) {
335 res = NSERROR_INVALID;
336 }
337 NSLOG(plot, DEEPDEBUG, "bltres = %d", bltres);
338 } else {
339 /* Bitmap with alpha.*/
341 }
342
343 DeleteObject(clipregion);
344
345 return res;
346}
347
348
349/**
350 * \brief Sets a clip rectangle for subsequent plot operations.
351 *
352 * \param ctx The current redraw context.
353 * \param clip The rectangle to limit all subsequent plot
354 * operations within.
355 * \return NSERROR_OK on success else error code.
356 */
357static nserror clip(const struct redraw_context *ctx, const struct rect *clip)
358{
359 NSLOG(plot, DEEPDEBUG, "clip %d,%d to %d,%d", clip->x0, clip->y0, clip->x1, clip->y1);
360
361 plot_clip.left = clip->x0;
362 plot_clip.top = clip->y0;
363 plot_clip.right = clip->x1 + 1; /* co-ordinates are exclusive */
364 plot_clip.bottom = clip->y1 + 1; /* co-ordinates are exclusive */
365
366 return NSERROR_OK;
367}
368
369
370/**
371 * Plots an arc
372 *
373 * plot an arc segment around (x,y), anticlockwise from angle1
374 * to angle2. Angles are measured anticlockwise from
375 * horizontal, in degrees.
376 *
377 * \param ctx The current redraw context.
378 * \param style Style controlling the arc plot.
379 * \param x The x coordinate of the arc.
380 * \param y The y coordinate of the arc.
381 * \param radius The radius of the arc.
382 * \param angle1 The start angle of the arc.
383 * \param angle2 The finish angle of the arc.
384 * \return NSERROR_OK on success else error code.
385 */
386static nserror
387arc(const struct redraw_context *ctx,
388 const plot_style_t *style,
389 int x, int y,
390 int radius, int angle1, int angle2)
391{
392 NSLOG(plot, DEEPDEBUG, "arc centre %d,%d radius %d from %d to %d", x, y, radius,
393 angle1, angle2);
394
395 /* ensure the plot HDC is set */
396 if (plot_hdc == NULL) {
397 NSLOG(netsurf, INFO, "HDC not set on call to plotters");
398 return NSERROR_INVALID;
399 }
400
401 HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
402 if (clipregion == NULL) {
403 return NSERROR_INVALID;
404 }
405
406 COLORREF col = (DWORD)(style->stroke_colour & 0x00FFFFFF);
407 HPEN pen = CreatePen(PS_GEOMETRIC | PS_SOLID, 1, col);
408 if (pen == NULL) {
409 DeleteObject(clipregion);
410 return NSERROR_INVALID;
411 }
412 HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen);
413 if (penbak == NULL) {
414 DeleteObject(clipregion);
415 DeleteObject(pen);
416 return NSERROR_INVALID;
417 }
418
419 int q1, q2;
420 double a1=1.0, a2=1.0, b1=1.0, b2=1.0;
421 q1 = (int) ((angle1 + 45) / 90) - 45;
422 q2 = (int) ((angle2 + 45) / 90) - 45;
423 while (q1 > 4)
424 q1 -= 4;
425 while (q2 > 4)
426 q2 -= 4;
427 while (q1 <= 0)
428 q1 += 4;
429 while (q2 <= 0)
430 q2 += 4;
431 angle1 = ((angle1 + 45) % 90) - 45;
432 angle2 = ((angle2 + 45) % 90) - 45;
433
434 switch(q1) {
435 case 1:
436 a1 = 1.0;
437 b1 = -tan((M_PI / 180) * angle1);
438 break;
439 case 2:
440 b1 = -1.0;
441 a1 = -tan((M_PI / 180) * angle1);
442 break;
443 case 3:
444 a1 = -1.0;
445 b1 = tan((M_PI / 180) * angle1);
446 break;
447 case 4:
448 b1 = 1.0;
449 a1 = tan((M_PI / 180) * angle1);
450 break;
451 }
452
453 switch(q2) {
454 case 1:
455 a2 = 1.0;
456 b2 = -tan((M_PI / 180) * angle2);
457 break;
458 case 2:
459 b2 = -1.0;
460 a2 = -tan((M_PI / 180) * angle2);
461 break;
462 case 3:
463 a2 = -1.0;
464 b2 = tan((M_PI / 180) * angle2);
465 break;
466 case 4:
467 b2 = 1.0;
468 a2 = tan((M_PI / 180) * angle2);
469 break;
470 }
471
472 SelectClipRgn(plot_hdc, clipregion);
473
474 Arc(plot_hdc, x - radius, y - radius, x + radius, y + radius,
475 x + (int)(a1 * radius), y + (int)(b1 * radius),
476 x + (int)(a2 * radius), y + (int)(b2 * radius));
477
478 SelectClipRgn(plot_hdc, NULL);
479 pen = SelectObject(plot_hdc, penbak);
480 DeleteObject(clipregion);
481 DeleteObject(pen);
482
483 return NSERROR_OK;
484}
485
486
487/**
488 * Plots a circle
489 *
490 * Plot a circle centered on (x,y), which is optionally filled.
491 *
492 * \param ctx The current redraw context.
493 * \param style Style controlling the circle plot.
494 * \param x x coordinate of circle centre.
495 * \param y y coordinate of circle centre.
496 * \param radius circle radius.
497 * \return NSERROR_OK on success else error code.
498 */
499static nserror
500disc(const struct redraw_context *ctx,
501 const plot_style_t *style,
502 int x, int y, int radius)
503{
504 NSLOG(plot, DEEPDEBUG, "disc at %d,%d radius %d", x, y, radius);
505
506 /* ensure the plot HDC is set */
507 if (plot_hdc == NULL) {
508 NSLOG(netsurf, INFO, "HDC not set on call to plotters");
509 return NSERROR_INVALID;
510 }
511
512 HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
513 if (clipregion == NULL) {
514 return NSERROR_INVALID;
515 }
516
517 COLORREF col = (DWORD)((style->fill_colour | style->stroke_colour)
518 & 0x00FFFFFF);
519 HPEN pen = CreatePen(PS_GEOMETRIC | PS_SOLID, 1, col);
520 if (pen == NULL) {
521 DeleteObject(clipregion);
522 return NSERROR_INVALID;
523 }
524 HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen);
525 if (penbak == NULL) {
526 DeleteObject(clipregion);
527 DeleteObject(pen);
528 return NSERROR_INVALID;
529 }
530 HBRUSH brush = CreateSolidBrush(col);
531 if (brush == NULL) {
532 DeleteObject(clipregion);
533 SelectObject(plot_hdc, penbak);
534 DeleteObject(pen);
535 return NSERROR_INVALID;
536 }
537 HGDIOBJ brushbak = SelectObject(plot_hdc, (HGDIOBJ) brush);
538 if (brushbak == NULL) {
539 DeleteObject(clipregion);
540 SelectObject(plot_hdc, penbak);
541 DeleteObject(pen);
542 DeleteObject(brush);
543 return NSERROR_INVALID;
544 }
545
546 SelectClipRgn(plot_hdc, clipregion);
547
548 if (style->fill_type == PLOT_OP_TYPE_NONE) {
549 Arc(plot_hdc, x - radius, y - radius, x + radius, y + radius,
550 x - radius, y - radius,
551 x - radius, y - radius);
552 } else {
553 Ellipse(plot_hdc, x - radius, y - radius, x + radius, y + radius);
554 }
555
556 SelectClipRgn(plot_hdc, NULL);
557 pen = SelectObject(plot_hdc, penbak);
558 brush = SelectObject(plot_hdc, brushbak);
559 DeleteObject(clipregion);
560 DeleteObject(pen);
561 DeleteObject(brush);
562
563 return NSERROR_OK;
564}
565
566
567/**
568 * Plots a line
569 *
570 * plot a line from (x0,y0) to (x1,y1). Coordinates are at
571 * centre of line width/thickness.
572 *
573 * \param ctx The current redraw context.
574 * \param style Style controlling the line plot.
575 * \param line A rectangle defining the line to be drawn
576 * \return NSERROR_OK on success else error code.
577 */
578static nserror
579line(const struct redraw_context *ctx,
580 const plot_style_t *style,
581 const struct rect *line)
582{
583 NSLOG(plot, DEEPDEBUG, "from %d,%d to %d,%d",
584 line->x0, line->y0, line->x1, line->y1);
585
586 /* ensure the plot HDC is set */
587 if (plot_hdc == NULL) {
588 NSLOG(netsurf, INFO, "HDC not set on call to plotters");
589 return NSERROR_INVALID;
590 }
591
592 HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
593 if (clipregion == NULL) {
594 return NSERROR_INVALID;
595 }
596
597 COLORREF col = (DWORD)(style->stroke_colour & 0x00FFFFFF);
598 /* windows 0x00bbggrr */
599 DWORD penstyle = PS_GEOMETRIC |
600 ((style->stroke_type == PLOT_OP_TYPE_DOT) ? PS_DOT :
601 (style->stroke_type == PLOT_OP_TYPE_DASH) ? PS_DASH:
602 0);
603 LOGBRUSH lb = {BS_SOLID, col, 0};
604 HPEN pen = ExtCreatePen(penstyle,
606 &lb, 0, NULL);
607 if (pen == NULL) {
608 DeleteObject(clipregion);
609 return NSERROR_INVALID;
610 }
611 HGDIOBJ bak = SelectObject(plot_hdc, (HGDIOBJ) pen);
612 if (bak == NULL) {
613 DeleteObject(pen);
614 DeleteObject(clipregion);
615 return NSERROR_INVALID;
616 }
617
618 SelectClipRgn(plot_hdc, clipregion);
619
620 MoveToEx(plot_hdc, line->x0, line->y0, (LPPOINT) NULL);
621
622 LineTo(plot_hdc, line->x1, line->y1);
623
624 SelectClipRgn(plot_hdc, NULL);
625 pen = SelectObject(plot_hdc, bak);
626
627 DeleteObject(pen);
628 DeleteObject(clipregion);
629
630 return NSERROR_OK;
631}
632
633
634/**
635 * Plots a rectangle.
636 *
637 * The rectangle can be filled an outline or both controlled
638 * by the plot style The line can be solid, dotted or
639 * dashed. Top left corner at (x0,y0) and rectangle has given
640 * width and height.
641 *
642 * \param ctx The current redraw context.
643 * \param style Style controlling the rectangle plot.
644 * \param rect A rectangle defining the line to be drawn
645 * \return NSERROR_OK on success else error code.
646 */
647static nserror
648rectangle(const struct redraw_context *ctx,
649 const plot_style_t *style,
650 const struct rect *rect)
651{
652 NSLOG(plot, DEEPDEBUG, "rectangle from %d,%d to %d,%d",
653 rect->x0, rect->y0, rect->x1, rect->y1);
654
655 /* ensure the plot HDC is set */
656 if (plot_hdc == NULL) {
657 NSLOG(netsurf, INFO, "HDC not set on call to plotters");
658 return NSERROR_INVALID;
659 }
660
661 HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
662 if (clipregion == NULL) {
663 return NSERROR_INVALID;
664 }
665
666 COLORREF pencol = (DWORD)(style->stroke_colour & 0x00FFFFFF);
667 DWORD penstyle = PS_GEOMETRIC |
668 (style->stroke_type == PLOT_OP_TYPE_DOT ? PS_DOT :
669 (style->stroke_type == PLOT_OP_TYPE_DASH ? PS_DASH :
670 (style->stroke_type == PLOT_OP_TYPE_NONE ? PS_NULL :
671 0)));
672 LOGBRUSH lb = {BS_SOLID, pencol, 0};
673 LOGBRUSH lb1 = {BS_SOLID, style->fill_colour, 0};
674 if (style->fill_type == PLOT_OP_TYPE_NONE)
675 lb1.lbStyle = BS_HOLLOW;
676
677 HPEN pen = ExtCreatePen(penstyle,
679 &lb, 0, NULL);
680 if (pen == NULL) {
681 return NSERROR_INVALID;
682 }
683 HGDIOBJ penbak = SelectObject(plot_hdc, (HGDIOBJ) pen);
684 if (penbak == NULL) {
685 DeleteObject(pen);
686 return NSERROR_INVALID;
687 }
688 HBRUSH brush = CreateBrushIndirect(&lb1);
689 if (brush == NULL) {
690 SelectObject(plot_hdc, penbak);
691 DeleteObject(pen);
692 return NSERROR_INVALID;
693 }
694 HGDIOBJ brushbak = SelectObject(plot_hdc, (HGDIOBJ) brush);
695 if (brushbak == NULL) {
696 SelectObject(plot_hdc, penbak);
697 DeleteObject(pen);
698 DeleteObject(brush);
699 return NSERROR_INVALID;
700 }
701
702 SelectClipRgn(plot_hdc, clipregion);
703
704 /* windows GDI call coordinates are inclusive */
705 Rectangle(plot_hdc, rect->x0, rect->y0, rect->x1 + 1, rect->y1 + 1);
706
707 pen = SelectObject(plot_hdc, penbak);
708 brush = SelectObject(plot_hdc, brushbak);
709 SelectClipRgn(plot_hdc, NULL);
710 DeleteObject(pen);
711 DeleteObject(brush);
712 DeleteObject(clipregion);
713
714 return NSERROR_OK;
715}
716
717
718/**
719 * Plot a polygon
720 *
721 * Plots a filled polygon with straight lines between
722 * points. The lines around the edge of the ploygon are not
723 * plotted. The polygon is filled with the non-zero winding
724 * rule.
725 *
726 * \param ctx The current redraw context.
727 * \param style Style controlling the polygon plot.
728 * \param p verticies of polygon
729 * \param n number of verticies.
730 * \return NSERROR_OK on success else error code.
731 */
732static nserror
733polygon(const struct redraw_context *ctx,
734 const plot_style_t *style,
735 const int *p,
736 unsigned int n)
737{
738 NSLOG(plot, DEEPDEBUG, "polygon %d points", n);
739
740 /* ensure the plot HDC is set */
741 if (plot_hdc == NULL) {
742 NSLOG(netsurf, INFO, "HDC not set on call to plotters");
743 return NSERROR_INVALID;
744 }
745
746 POINT points[n];
747 unsigned int i;
748 HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
749 if (clipregion == NULL) {
750 return NSERROR_INVALID;
751 }
752
753 COLORREF pencol = (DWORD)(style->fill_colour & 0x00FFFFFF);
754 COLORREF brushcol = (DWORD)(style->fill_colour & 0x00FFFFFF);
755 HPEN pen = CreatePen(PS_GEOMETRIC | PS_NULL, 1, pencol);
756 if (pen == NULL) {
757 DeleteObject(clipregion);
758 return NSERROR_INVALID;
759 }
760 HPEN penbak = SelectObject(plot_hdc, pen);
761 if (penbak == NULL) {
762 DeleteObject(clipregion);
763 DeleteObject(pen);
764 return NSERROR_INVALID;
765 }
766 HBRUSH brush = CreateSolidBrush(brushcol);
767 if (brush == NULL) {
768 DeleteObject(clipregion);
769 SelectObject(plot_hdc, penbak);
770 DeleteObject(pen);
771 return NSERROR_INVALID;
772 }
773 HBRUSH brushbak = SelectObject(plot_hdc, brush);
774 if (brushbak == NULL) {
775 DeleteObject(clipregion);
776 SelectObject(plot_hdc, penbak);
777 DeleteObject(pen);
778 DeleteObject(brush);
779 return NSERROR_INVALID;
780 }
781 SetPolyFillMode(plot_hdc, WINDING);
782 for (i = 0; i < n; i++) {
783 points[i].x = (long) p[2 * i];
784 points[i].y = (long) p[2 * i + 1];
785
786 NSLOG(plot, DEEPDEBUG, "%ld,%ld ", points[i].x, points[i].y);
787 }
788
789 SelectClipRgn(plot_hdc, clipregion);
790
791 if (n >= 2) {
792 Polygon(plot_hdc, points, n);
793 }
794
795 SelectClipRgn(plot_hdc, NULL);
796
797 pen = SelectObject(plot_hdc, penbak);
798 brush = SelectObject(plot_hdc, brushbak);
799 DeleteObject(clipregion);
800 DeleteObject(pen);
801 DeleteObject(brush);
802
803 return NSERROR_OK;
804}
805
806
807/**
808 * Plots a path.
809 *
810 * Path plot consisting of cubic Bezier curves. Line and fill colour is
811 * controlled by the plot style.
812 *
813 * \param ctx The current redraw context.
814 * \param pstyle Style controlling the path plot.
815 * \param p elements of path
816 * \param n nunber of elements on path
817 * \param transform A transform to apply to the path.
818 * \return NSERROR_OK on success else error code.
819 */
820static nserror
821path(const struct redraw_context *ctx,
822 const plot_style_t *pstyle,
823 const float *p,
824 unsigned int n,
825 const float transform[6])
826{
827 NSLOG(plot, DEEPDEBUG, "path unimplemented");
828 return NSERROR_OK;
829}
830
831
832/**
833 * Plot a bitmap
834 *
835 * Tiled plot of a bitmap image. (x,y) gives the top left
836 * coordinate of an explicitly placed tile. From this tile the
837 * image can repeat in all four directions -- up, down, left
838 * and right -- to the extents given by the current clip
839 * rectangle.
840 *
841 * The bitmap_flags say whether to tile in the x and y
842 * directions. If not tiling in x or y directions, the single
843 * image is plotted. The width and height give the dimensions
844 * the image is to be scaled to.
845 *
846 * \param ctx The current redraw context.
847 * \param bitmap The bitmap to plot
848 * \param x The x coordinate to plot the bitmap
849 * \param y The y coordiante to plot the bitmap
850 * \param width The width of area to plot the bitmap into
851 * \param height The height of area to plot the bitmap into
852 * \param bg the background colour to alpha blend into
853 * \param flags the flags controlling the type of plot operation
854 * \return NSERROR_OK on success else error code.
855 */
856static nserror
857bitmap(const struct redraw_context *ctx,
858 struct bitmap *bitmap,
859 int x, int y,
860 int width,
861 int height,
862 colour bg,
863 bitmap_flags_t flags)
864{
865 int xf,yf;
866 bool repeat_x = (flags & BITMAPF_REPEAT_X);
867 bool repeat_y = (flags & BITMAPF_REPEAT_Y);
868
869 /* Bail early if we can */
870
871 NSLOG(plot, DEEPDEBUG, "Plotting %p at %d,%d by %d,%d",bitmap, x,y,width,height);
872
873 if (bitmap == NULL) {
874 NSLOG(netsurf, INFO, "Passed null bitmap!");
875 return NSERROR_OK;
876 }
877
878 /* check if nothing to plot */
879 if (width == 0 || height == 0)
880 return NSERROR_OK;
881
882 /* x and y define coordinate of top left of of the initial explicitly
883 * placed tile. The width and height are the image scaling and the
884 * bounding box defines the extent of the repeat (which may go in all
885 * four directions from the initial tile).
886 */
887
888 if (!(repeat_x || repeat_y)) {
889 /* Not repeating at all, so just plot it */
890 if ((bitmap->width == 1) && (bitmap->height == 1)) {
891 if ((*(bitmap->pixdata + 3) & 0xff) == 0) {
892 return NSERROR_OK;
893 }
894 return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff,
895 x,
896 y,
897 x + width,
898 y + height);
899
900 } else {
901 return plot_bitmap(bitmap, x, y, width, height);
902 }
903 }
904
905 /* Optimise tiled plots of 1x1 bitmaps by replacing with a flat fill
906 * of the area. Can only be done when image is fully opaque. */
907 if ((bitmap->width == 1) && (bitmap->height == 1)) {
908 if ((*(COLORREF *)bitmap->pixdata & 0xff000000) != 0) {
909 return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff,
910 plot_clip.left,
911 plot_clip.top,
912 plot_clip.right,
913 plot_clip.bottom);
914 }
915 }
916
917 /* Optimise tiled plots of bitmaps scaled to 1x1 by replacing with
918 * a flat fill of the area. Can only be done when image is fully
919 * opaque.
920 */
921 if ((width == 1) && (height == 1)) {
922 if (bitmap->opaque) {
923 /** TODO: Currently using top left pixel. Maybe centre
924 * pixel or average value would be better. */
925 return plot_block((*(COLORREF *)bitmap->pixdata) & 0xffffff,
926 plot_clip.left,
927 plot_clip.top,
928 plot_clip.right,
929 plot_clip.bottom);
930 }
931 }
932
933 NSLOG(plot, DEEPDEBUG, "Tiled plotting %d,%d by %d,%d", x, y, width, height);
934 NSLOG(plot, DEEPDEBUG, "clipped %ld,%ld to %ld,%ld",
935 plot_clip.left, plot_clip.top,
936 plot_clip.right, plot_clip.bottom);
937
938 /* get left most tile position */
939 if (repeat_x) {
940 for (; x > plot_clip.left; x -= width);
941 }
942
943 /* get top most tile position */
944 if (repeat_y) {
945 for (; y > plot_clip.top; y -= height);
946 }
947
948 NSLOG(plot, DEEPDEBUG, "repeat from %d,%d to %ld,%ld",
949 x, y, plot_clip.right, plot_clip.bottom);
950
951 /* tile down and across to extents */
952 for (xf = x; xf < plot_clip.right; xf += width) {
953 for (yf = y; yf < plot_clip.bottom; yf += height) {
954
955 plot_bitmap(bitmap, xf, yf, width, height);
956 if (!repeat_y)
957 break;
958 }
959 if (!repeat_x)
960 break;
961 }
962 return NSERROR_OK;
963}
964
965
966/**
967 * Text plotting.
968 *
969 * \param ctx The current redraw context.
970 * \param fstyle plot style for this text
971 * \param x x coordinate
972 * \param y y coordinate
973 * \param text UTF-8 string to plot
974 * \param length length of string, in bytes
975 * \return NSERROR_OK on success else error code.
976 */
977static nserror
978text(const struct redraw_context *ctx,
979 const struct plot_font_style *fstyle,
980 int x,
981 int y,
982 const char *text,
983 size_t length)
984{
985 NSLOG(plot, DEEPDEBUG, "words %s at %d,%d", text, x, y);
986
987 /* ensure the plot HDC is set */
988 if (plot_hdc == NULL) {
989 NSLOG(netsurf, INFO, "HDC not set on call to plotters");
990 return NSERROR_INVALID;
991 }
992
993 HRGN clipregion = CreateRectRgnIndirect(&plot_clip);
994 if (clipregion == NULL) {
995 return NSERROR_INVALID;
996 }
997
998 HFONT fontbak, font = get_font(fstyle);
999 if (font == NULL) {
1000 DeleteObject(clipregion);
1001 return NSERROR_INVALID;
1002 }
1003 int wlen;
1004 SIZE s;
1005 LPWSTR wstring;
1006 fontbak = (HFONT) SelectObject(plot_hdc, font);
1007 GetTextExtentPoint(plot_hdc, text, length, &s);
1008
1009 SelectClipRgn(plot_hdc, clipregion);
1010
1011 SetTextAlign(plot_hdc, TA_BASELINE | TA_LEFT);
1012 if ((fstyle->background & 0xFF000000) != 0x01000000) {
1013 /* 100% alpha */
1014 SetBkColor(plot_hdc, (DWORD) (fstyle->background & 0x00FFFFFF));
1015 }
1016 SetBkMode(plot_hdc, TRANSPARENT);
1017 SetTextColor(plot_hdc, (DWORD) (fstyle->foreground & 0x00FFFFFF));
1018
1019 wlen = MultiByteToWideChar(CP_UTF8, 0, text, length, NULL, 0);
1020 wstring = malloc(2 * (wlen + 1));
1021 if (wstring == NULL) {
1022 return NSERROR_INVALID;
1023 }
1024 MultiByteToWideChar(CP_UTF8, 0, text, length, wstring, wlen);
1025 TextOutW(plot_hdc, x, y, wstring, wlen);
1026
1027 SelectClipRgn(plot_hdc, NULL);
1028 free(wstring);
1029 font = SelectObject(plot_hdc, fontbak);
1030 DeleteObject(clipregion);
1031 DeleteObject(font);
1032
1033 return NSERROR_OK;
1034}
1035
1036
1037/**
1038 * win32 API plot operation table
1039 */
1042 .line = line,
1043 .polygon = polygon,
1044 .clip = clip,
1045 .text = text,
1046 .disc = disc,
1047 .arc = arc,
1048 .bitmap = bitmap,
1049 .path = path,
1050 .option_knockout = true,
1051};
#define M_PI
Definition: plotters.c:101
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_INVALID
Invalid data.
Definition: errors.h:49
@ NSERROR_OK
No error.
Definition: errors.h:30
struct bitmap * bitmap_scale(struct bitmap *prescale, int width, int height)
Definition: bitmap.c:236
HFONT get_font(const plot_font_style_t *style)
Definition: font.c:92
The interface to the win32 font and utf8 handling.
Core mouse and pointer states.
Target independent plotting interface.
#define BITMAPF_REPEAT_X
Definition: plotters.h:38
#define BITMAPF_REPEAT_Y
Definition: plotters.h:39
unsigned long bitmap_flags_t
Definition: plotters.h:36
Interface to platform-specific graphical user interface window operations.
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
#define plot_style_fixed_to_int(v)
Definition: plot_style.h:54
@ PLOT_OP_TYPE_NONE
No operation.
Definition: plot_style.h:66
@ PLOT_OP_TYPE_DASH
Dashed plot.
Definition: plot_style.h:69
@ PLOT_OP_TYPE_DOT
Dotted plot.
Definition: plot_style.h:68
int width
Definition: gui.c:159
int height
Definition: gui.c:160
Interface to utility string handling.
RISC OS wimp toolkit bitmap.
Definition: bitmap.c:68
int width
width of bitmap
Definition: bitmap.c:69
BITMAPV5HEADER * pbmi
Definition: bitmap.h:27
int height
height of bitmap
Definition: bitmap.c:70
bool opaque
Whether the bitmap is opaque.
Definition: bitmap.c:74
UBYTE * pixdata
Definition: bitmap.c:71
HBITMAP windib
Definition: bitmap.h:26
Font style for plotting.
Definition: plot_style.h:111
colour foreground
Colour of text.
Definition: plot_style.h:123
colour background
Background colour to blend to, if appropriate.
Definition: plot_style.h:122
Plot style for stroke/fill plotters.
Definition: plot_style.h:76
colour fill_colour
Colour of fill.
Definition: plot_style.h:81
plot_style_fixed stroke_width
Width of stroke, in pixels.
Definition: plot_style.h:78
plot_operation_type_t fill_type
Fill plot type.
Definition: plot_style.h:80
colour stroke_colour
Colour of stroke.
Definition: plot_style.h:79
plot_operation_type_t stroke_type
Stroke plot type.
Definition: plot_style.h:77
Plotter operations table.
Definition: plotters.h:102
nserror(* rectangle)(const struct redraw_context *ctx, const plot_style_t *pstyle, const struct rect *rectangle)
Plots a rectangle.
Definition: plotters.h:188
Definition: gui.h:44
int y
Definition: gui.h:46
int x
Definition: gui.h:45
Rectangle coordinates.
Definition: types.h:40
int x0
Definition: types.h:41
int y0
Top left.
Definition: types.h:41
int x1
Definition: types.h:42
int y1
Bottom right.
Definition: types.h:42
Redraw context.
Definition: plotters.h:51
uint32_t colour
Colour type: XBGR.
Definition: types.h:35
UTF-8 manipulation functions (interface).
static nserror path(const struct redraw_context *ctx, const plot_style_t *pstyle, const float *p, unsigned int n, const float transform[6])
Plots a path.
Definition: plot.c:821
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)
Plot a bitmap.
Definition: plot.c:857
static nserror line(const struct redraw_context *ctx, const plot_style_t *style, const struct rect *line)
Plots a line.
Definition: plot.c:579
static nserror plot_bitmap(struct bitmap *bitmap, int x, int y, int width, int height)
Internal bitmap plotting.
Definition: plot.c:276
static nserror text(const struct redraw_context *ctx, const struct plot_font_style *fstyle, int x, int y, const char *text, size_t length)
Text plotting.
Definition: plot.c:978
static nserror plot_alpha_bitmap(HDC hdc, struct bitmap *bitmap, int x, int y, int width, int height)
plot an alpha blended bitmap
Definition: plot.c:120
static nserror plot_block(COLORREF col, int x, int y, int width, int height)
bitmap helper to plot a solid block of colour
Definition: plot.c:61
static nserror arc(const struct redraw_context *ctx, const plot_style_t *style, int x, int y, int radius, int angle1, int angle2)
Plots an arc.
Definition: plot.c:387
HDC plot_hdc
Definition: plot.c:44
const struct plotter_table win_plotters
win32 API plot operation table
Definition: plot.c:1040
static RECT plot_clip
currently set clipping rectangle
Definition: plot.c:47
static nserror disc(const struct redraw_context *ctx, const plot_style_t *style, int x, int y, int radius)
Plots a circle.
Definition: plot.c:500
static nserror rectangle(const struct redraw_context *ctx, const plot_style_t *style, const struct rect *rect)
Plots a rectangle.
Definition: plot.c:648
static nserror clip(const struct redraw_context *ctx, const struct rect *clip)
Sets a clip rectangle for subsequent plot operations.
Definition: plot.c:357
static nserror polygon(const struct redraw_context *ctx, const plot_style_t *style, const int *p, unsigned int n)
Plot a polygon.
Definition: plot.c:733