Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3584 | sourcerer | 1 | /* |
2 | * Copyright 2009 Vincent Sanders |
||
3 | * Copyright 2009 Michael Drake |
||
4 | * |
||
5 | * This file is part of libnsfb, http://www.netsurf-browser.org/ |
||
6 | * Licenced under the MIT License, |
||
7 | * http://www.opensource.org/licenses/mit-license.php |
||
8 | */ |
||
9 | |||
10 | /** \file |
||
11 | * generic plotter functions which are not depth dependant (implementation). |
||
12 | */ |
||
13 | |||
14 | #include |
||
15 | #include |
||
16 | #include |
||
17 | #include |
||
18 | |||
19 | #include "libnsfb.h" |
||
20 | #include "libnsfb_plot.h" |
||
21 | #include "libnsfb_plot_util.h" |
||
22 | |||
23 | #include "nsfb.h" |
||
24 | #include "plot.h" |
||
25 | #include "surface.h" |
||
26 | |||
27 | extern const nsfb_plotter_fns_t _nsfb_1bpp_plotters; |
||
28 | extern const nsfb_plotter_fns_t _nsfb_8bpp_plotters; |
||
29 | extern const nsfb_plotter_fns_t _nsfb_16bpp_plotters; |
||
30 | extern const nsfb_plotter_fns_t _nsfb_24bpp_plotters; |
||
31 | extern const nsfb_plotter_fns_t _nsfb_32bpp_xrgb8888_plotters; |
||
32 | extern const nsfb_plotter_fns_t _nsfb_32bpp_xbgr8888_plotters; |
||
33 | |||
34 | static bool set_clip(nsfb_t *nsfb, nsfb_bbox_t *clip) |
||
35 | { |
||
36 | nsfb_bbox_t fbarea; |
||
37 | |||
38 | /* screen area */ |
||
39 | fbarea.x0 = 0; |
||
40 | fbarea.y0 = 0; |
||
41 | fbarea.x1 = nsfb->width; |
||
42 | fbarea.y1 = nsfb->height; |
||
43 | |||
44 | if (clip == NULL) { |
||
45 | nsfb->clip = fbarea; |
||
46 | } else { |
||
47 | if (!nsfb_plot_clip(&fbarea, clip)) |
||
48 | return false; |
||
49 | |||
50 | nsfb->clip = *clip; |
||
51 | } |
||
52 | return true; |
||
53 | } |
||
54 | |||
55 | static bool get_clip(nsfb_t *nsfb, nsfb_bbox_t *clip) |
||
56 | { |
||
57 | *clip = nsfb->clip; |
||
58 | return true; |
||
59 | } |
||
60 | |||
61 | static bool clg(nsfb_t *nsfb, nsfb_colour_t c) |
||
62 | { |
||
63 | return nsfb->plotter_fns->fill(nsfb, &nsfb->clip, c); |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | * Establish whether there is any value in a line's crossing. |
||
68 | * (Helper function for find_span().) |
||
69 | * |
||
70 | * \param x x coordinate of intersection |
||
71 | * \param y current y level |
||
72 | * \param x0 line start coordinate |
||
73 | * \param y0 line start coordinate |
||
74 | * \param x1 line end coordinate |
||
75 | * \param y1 line end coordinate |
||
76 | * \return true if crossing has value |
||
77 | * |
||
78 | * + | | / |
||
79 | * / | |/ |
||
80 | * y level -- ----/---- ----+---- ----+---- ----+---- |
||
81 | * / / /| |
||
82 | * + / / | |
||
83 | * |
||
84 | * (a) (b) (c) (d) |
||
85 | * |
||
86 | * |
||
87 | * Figure (a) values: 1 = 1 -- Odd -- Valid crossing |
||
88 | * Figure (b) values: 0 + 1 = 1 -- Odd -- Valid crossing |
||
89 | * Figure (c) values: 1 + 1 = 2 -- Even -- Not valid crossing |
||
90 | * Figure (d) values: 0 + 0 = 0 -- Even -- Not valid crossing |
||
91 | * |
||
92 | * Vertices are shared between consecutive lines. This function ensures that |
||
93 | * the vertex point is only counted as a crossing for one of the lines by |
||
94 | * only considering crossings of the top vertex. This is what NetSurf's |
||
95 | * plotter API expects. |
||
96 | * |
||
97 | * It's up to the client to call this function for both lines and check the |
||
98 | * evenness of the total. |
||
99 | */ |
||
100 | static bool establish_crossing_value(int x, int y, int x0, int y0, |
||
101 | int x1, int y1) |
||
102 | { |
||
103 | bool v1 = (x == x0 && y == y0); /* whether we're crossing 1st vertex */ |
||
104 | bool v2 = (x == x1 && y == y1); /* whether we're crossing 2nd vertex */ |
||
105 | |||
106 | if ((v1 && (y0 < y1)) || (v2 && (y1 < y0))) { |
||
107 | /* crossing top vertex */ |
||
108 | return true; |
||
109 | } else if (!v1 && !v2) { |
||
110 | /* Intersection with current y level is not at a vertex. |
||
111 | * Normal crossing. */ |
||
112 | return true; |
||
113 | } |
||
114 | return false; |
||
115 | } |
||
116 | |||
117 | |||
118 | /** |
||
119 | * Find first filled span along horizontal line at given coordinate |
||
120 | * |
||
121 | * \param p array of polygon vertices (x1, y1, x2, y2, ... , xN, yN) |
||
122 | * \param n number of polygon vertex values (N * 2) |
||
123 | * \param x current position along current scan line |
||
124 | * \param y position of current scan line |
||
125 | * \param x0 updated to start of filled area |
||
126 | * \param x1 updated to end of filled area |
||
127 | * \return true if an intersection was found |
||
128 | */ |
||
129 | static bool find_span(const int *p, int n, int x, int y, int *x0, int *x1) |
||
130 | { |
||
131 | int i; |
||
132 | int p_x0, p_y0; |
||
133 | int p_x1, p_y1; |
||
134 | int x0_min, x1_min; |
||
135 | int x_new; |
||
136 | unsigned int x0c, x1c; /* counters for crossings at span end points */ |
||
137 | bool crossing_value; |
||
138 | bool found_span_start = false; |
||
139 | |||
140 | x0_min = x1_min = INT_MIN; |
||
141 | x0c = x1c = 0; |
||
142 | *x0 = *x1 = INT_MAX; |
||
143 | |||
144 | /* search row for next span, returning it if one exists */ |
||
145 | do { |
||
146 | /* reset endpoint info, if valid span endpoints not found */ |
||
147 | if (!found_span_start) |
||
148 | *x0 = INT_MAX; |
||
149 | *x1 = INT_MAX; |
||
150 | |||
151 | /* search all lines in polygon */ |
||
152 | for (i = 0; i < n; i = i + 2) { |
||
153 | /* get line endpoints */ |
||
154 | if (i != n - 2) { |
||
155 | /* not the last line */ |
||
156 | p_x0 = p[i]; p_y0 = p[i + 1]; |
||
157 | p_x1 = p[i + 2]; p_y1 = p[i + 3]; |
||
158 | } else { |
||
159 | /* last line; 2nd endpoint is first vertex */ |
||
160 | p_x0 = p[i]; p_y0 = p[i + 1]; |
||
161 | p_x1 = p[0]; p_y1 = p[1]; |
||
162 | } |
||
163 | /* ignore horizontal lines */ |
||
164 | if (p_y0 == p_y1) |
||
165 | continue; |
||
166 | |||
167 | /* ignore lines that don't cross this y level */ |
||
168 | if ((y < p_y0 && y < p_y1) || (y > p_y0 && y > p_y1)) |
||
169 | continue; |
||
170 | |||
171 | if (p_x0 == p_x1) { |
||
172 | /* vertical line, x is constant */ |
||
173 | x_new = p_x0; |
||
174 | } else { |
||
175 | /* find crossing (intersection of this line and |
||
176 | * current y level) */ |
||
177 | int num = (y - p_y0) * (p_x1 - p_x0); |
||
178 | int den = (p_y1 - p_y0); |
||
179 | |||
180 | /* To round to nearest (rather than down) |
||
181 | * half the denominator is either added to |
||
182 | * or subtracted from the numerator, |
||
183 | * depending on whether the numerator and |
||
184 | * denominator have the same sign. */ |
||
185 | num = ((num < 0) == (den < 0)) ? |
||
186 | num + (den / 2) : |
||
187 | num - (den / 2); |
||
188 | x_new = p_x0 + num / den; |
||
189 | } |
||
190 | |||
191 | /* ignore crossings before current x */ |
||
192 | if (x_new < x || |
||
193 | (!found_span_start && x_new < x0_min) || |
||
194 | (found_span_start && x_new < x1_min)) |
||
195 | continue; |
||
196 | |||
197 | crossing_value = establish_crossing_value(x_new, y, |
||
198 | p_x0, p_y0, p_x1, p_y1); |
||
199 | |||
200 | |||
201 | /* set nearest intersections as filled area endpoints */ |
||
202 | if (!found_span_start && |
||
203 | x_new < *x0 && crossing_value) { |
||
204 | /* nearer than first endpoint */ |
||
205 | *x1 = *x0; |
||
206 | x1c = x0c; |
||
207 | *x0 = x_new; |
||
208 | x0c = 1; |
||
209 | } else if (!found_span_start && |
||
210 | x_new == *x0 && crossing_value) { |
||
211 | /* same as first endpoint */ |
||
212 | x0c++; |
||
213 | } else if (x_new < *x1 && crossing_value) { |
||
214 | /* nearer than second endpoint */ |
||
215 | *x1 = x_new; |
||
216 | x1c = 1; |
||
217 | } else if (x_new == *x1 && crossing_value) { |
||
218 | /* same as second endpoint */ |
||
219 | x1c++; |
||
220 | } |
||
221 | } |
||
222 | /* check whether the span endpoints have been found */ |
||
223 | if (!found_span_start && x0c % 2 == 1) { |
||
224 | /* valid fill start found */ |
||
225 | found_span_start = true; |
||
226 | |||
227 | } |
||
228 | if (x1c % 2 == 1) { |
||
229 | /* valid fill endpoint found */ |
||
230 | if (!found_span_start) { |
||
231 | /* not got a start yet; use this as start */ |
||
232 | found_span_start = true; |
||
233 | x0c = x1c; |
||
234 | *x0 = *x1; |
||
235 | } else { |
||
236 | /* got valid end of span */ |
||
237 | return true; |
||
238 | } |
||
239 | } |
||
240 | /* if current positions aren't valid endpoints, set new |
||
241 | * minimums after current positions */ |
||
242 | if (!found_span_start) |
||
243 | x0_min = *x0 + 1; |
||
244 | x1_min = *x1 + 1; |
||
245 | |||
246 | } while (*x1 != INT_MAX); |
||
247 | |||
248 | /* no spans found */ |
||
249 | return false; |
||
250 | } |
||
251 | |||
252 | |||
253 | /** |
||
254 | * Plot a polygon |
||
255 | * |
||
256 | * \param nsfb framebuffer context |
||
257 | * \param p array of polygon vertices (x1, y1, x2, y2, ... , xN, yN) |
||
258 | * \param n number of polygon vertices (N) |
||
259 | * \param c fill colour |
||
260 | * \return true if no errors |
||
261 | */ |
||
262 | static bool polygon(nsfb_t *nsfb, const int *p, unsigned int n, nsfb_colour_t c) |
||
263 | { |
||
264 | int poly_x0, poly_y0; /* Bounding box top left corner */ |
||
265 | int poly_x1, poly_y1; /* Bounding box bottom right corner */ |
||
266 | int i, j; /* indexes */ |
||
267 | int x0, x1; /* filled span extents */ |
||
268 | int y; /* current y coordinate */ |
||
269 | int y_max; /* bottom of plot area */ |
||
270 | nsfb_bbox_t fline; |
||
271 | nsfb_plot_pen_t pen; |
||
272 | |||
273 | /* find no. of vertex values */ |
||
274 | int v = n * 2; |
||
275 | |||
276 | /* Can't plot polygons with 2 or fewer vertices */ |
||
277 | if (n <= 2) |
||
278 | return true; |
||
279 | |||
280 | pen.stroke_colour = c; |
||
281 | |||
282 | /* Find polygon bounding box */ |
||
283 | poly_x0 = poly_x1 = *p; |
||
284 | poly_y0 = poly_y1 = p[1]; |
||
285 | for (i = 2; i < v; i = i + 2) { |
||
286 | j = i + 1; |
||
287 | if (p[i] < poly_x0) |
||
288 | poly_x0 = p[i]; |
||
289 | else if (p[i] > poly_x1) |
||
290 | poly_x1 = p[i]; |
||
291 | if (p[j] < poly_y0) |
||
292 | poly_y0 = p[j]; |
||
293 | else if (p[j] > poly_y1) |
||
294 | poly_y1 = p[j]; |
||
295 | } |
||
296 | |||
297 | /* Don't try to plot it if it's outside the clip rectangle */ |
||
298 | if (nsfb->clip.y1 < poly_y0 || |
||
299 | nsfb->clip.y0 > poly_y1 || |
||
300 | nsfb->clip.x1 < poly_x0 || |
||
301 | nsfb->clip.x0 > poly_x1) |
||
302 | return true; |
||
303 | |||
304 | /* Find the top of the important area */ |
||
305 | if (poly_y0 > nsfb->clip.y0) |
||
306 | y = poly_y0; |
||
307 | else |
||
308 | y = nsfb->clip.y0; |
||
309 | |||
310 | /* Find the bottom of the important area */ |
||
311 | if (poly_y1 < nsfb->clip.y1) |
||
312 | y_max = poly_y1; |
||
313 | else |
||
314 | y_max = nsfb->clip.y1; |
||
315 | |||
316 | for (; y < y_max; y++) { |
||
317 | x1 = poly_x0 - 1; |
||
318 | /* For each row */ |
||
319 | while (find_span(p, v, x1 + 1, y, &x0, &x1)) { |
||
320 | /* don't draw anything outside clip region */ |
||
321 | if (x1 < nsfb->clip.x0) |
||
322 | continue; |
||
323 | else if (x0 < nsfb->clip.x0) |
||
324 | x0 = nsfb->clip.x0; |
||
325 | if (x0 > nsfb->clip.x1) |
||
326 | break; |
||
327 | else if (x1 > nsfb->clip.x1) |
||
328 | x1 = nsfb->clip.x1; |
||
329 | |||
330 | fline.x0 = x0; |
||
331 | fline.y0 = y; |
||
332 | fline.x1 = x1; |
||
333 | fline.y1 = y; |
||
334 | |||
335 | /* draw this filled span on current row */ |
||
336 | nsfb->plotter_fns->line(nsfb, 1, &fline, &pen); |
||
337 | |||
338 | /* don't look for more spans if already at end of clip |
||
339 | * region or polygon */ |
||
340 | if (x1 == nsfb->clip.x1 || x1 == poly_x1) |
||
341 | break; |
||
342 | } |
||
343 | } |
||
344 | return true; |
||
345 | } |
||
346 | |||
347 | static bool |
||
348 | rectangle(nsfb_t *nsfb, nsfb_bbox_t *rect, |
||
349 | int line_width, nsfb_colour_t c, |
||
350 | bool dotted, bool dashed) |
||
351 | { |
||
352 | nsfb_bbox_t side[4]; |
||
353 | nsfb_plot_pen_t pen; |
||
354 | |||
355 | pen.stroke_colour = c; |
||
356 | pen.stroke_width = line_width; |
||
357 | if (dotted || dashed) { |
||
358 | pen.stroke_type = NFSB_PLOT_OPTYPE_PATTERN; |
||
359 | } else { |
||
360 | pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; |
||
361 | } |
||
362 | |||
363 | side[0] = *rect; |
||
364 | side[1] = *rect; |
||
365 | side[2] = *rect; |
||
366 | side[3] = *rect; |
||
367 | |||
368 | side[0].y1 = side[0].y0; |
||
369 | side[1].y0 = side[1].y1; |
||
370 | side[2].x1 = side[2].x0; |
||
371 | side[3].x0 = side[3].x1; |
||
372 | |||
373 | return nsfb->plotter_fns->line(nsfb, 4, side, &pen); |
||
374 | } |
||
375 | |||
376 | /* plotter routine for ellipse points */ |
||
377 | static void |
||
378 | ellipsepoints(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) |
||
379 | { |
||
380 | nsfb->plotter_fns->point(nsfb, cx + x, cy + y, c); |
||
381 | nsfb->plotter_fns->point(nsfb, cx - x, cy + y, c); |
||
382 | nsfb->plotter_fns->point(nsfb, cx + x, cy - y, c); |
||
383 | nsfb->plotter_fns->point(nsfb, cx - x, cy - y, c); |
||
384 | } |
||
385 | |||
386 | static void |
||
387 | ellipsefill(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) |
||
388 | { |
||
389 | nsfb_bbox_t fline[2]; |
||
390 | nsfb_plot_pen_t pen; |
||
391 | |||
392 | pen.stroke_colour = c; |
||
393 | |||
394 | fline[0].x0 = fline[1].x0 = cx - x; |
||
395 | fline[0].x1 = fline[1].x1 = cx + x; |
||
396 | fline[0].y0 = fline[0].y1 = cy + y; |
||
397 | fline[1].y0 = fline[1].y1 = cy - y; |
||
398 | |||
399 | nsfb->plotter_fns->line(nsfb, 2, fline, &pen); |
||
400 | |||
401 | } |
||
402 | |||
403 | #define ROUND(a) ((int)(a+0.5)) |
||
404 | |||
405 | static bool |
||
406 | ellipse_midpoint(nsfb_t *nsfb, |
||
407 | int cx, |
||
408 | int cy, |
||
409 | int rx, |
||
410 | int ry, |
||
411 | nsfb_colour_t c, |
||
412 | void (ellipsefn)(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c)) |
||
413 | { |
||
414 | int rx2 = rx * rx; |
||
415 | int ry2 = ry * ry; |
||
416 | int tworx2 = 2 * rx2; |
||
417 | int twory2 = 2 * ry2; |
||
418 | int p; |
||
419 | int x = 0; |
||
420 | int y = ry; |
||
421 | int px = 0; |
||
422 | int py = tworx2 * y; |
||
423 | |||
424 | ellipsefn(nsfb, cx, cy, x, y, c); |
||
425 | |||
426 | /* region 1 */ |
||
427 | p = ROUND(ry2 - (rx2 * ry) + (0.25 * rx2)); |
||
428 | while (px < py) { |
||
429 | x++; |
||
430 | px += twory2; |
||
431 | if (p <0) { |
||
432 | p+=ry2 + px; |
||
433 | } else { |
||
434 | y--; |
||
435 | py -= tworx2; |
||
436 | p+=ry2 + px - py; |
||
437 | } |
||
438 | ellipsefn(nsfb, cx, cy, x, y, c); |
||
439 | } |
||
440 | |||
441 | /* region 2 */ |
||
442 | p = ROUND(ry2*(x+0.5)*(x+0.5) + rx2*(y-1)*(y-1) - rx2*ry2); |
||
443 | while (y > 0) { |
||
444 | y--; |
||
445 | py -= tworx2; |
||
446 | if (p > 0) { |
||
447 | p+=rx2 - py; |
||
448 | } else { |
||
449 | x++; |
||
450 | px += twory2; |
||
451 | p+=rx2 - py + px; |
||
452 | } |
||
453 | ellipsefn(nsfb, cx, cy, x, y, c); |
||
454 | } |
||
455 | return true; |
||
456 | } |
||
457 | |||
458 | |||
459 | /* plotter routine for 8way circle symetry */ |
||
460 | static void |
||
461 | circlepoints(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) |
||
462 | { |
||
463 | nsfb->plotter_fns->point(nsfb, cx + x, cy + y, c); |
||
464 | nsfb->plotter_fns->point(nsfb, cx - x, cy + y, c); |
||
465 | nsfb->plotter_fns->point(nsfb, cx + x, cy - y, c); |
||
466 | nsfb->plotter_fns->point(nsfb, cx - x, cy - y, c); |
||
467 | nsfb->plotter_fns->point(nsfb, cx + y, cy + x, c); |
||
468 | nsfb->plotter_fns->point(nsfb, cx - y, cy + x, c); |
||
469 | nsfb->plotter_fns->point(nsfb, cx + y, cy - x, c); |
||
470 | nsfb->plotter_fns->point(nsfb, cx - y, cy - x, c); |
||
471 | } |
||
472 | |||
473 | static void |
||
474 | circlefill(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c) |
||
475 | { |
||
476 | nsfb_bbox_t fline[4]; |
||
477 | nsfb_plot_pen_t pen; |
||
478 | |||
479 | pen.stroke_colour = c; |
||
480 | |||
481 | fline[0].x0 = fline[1].x0 = cx - x; |
||
482 | fline[0].x1 = fline[1].x1 = cx + x; |
||
483 | fline[0].y0 = fline[0].y1 = cy + y; |
||
484 | fline[1].y0 = fline[1].y1 = cy - y; |
||
485 | |||
486 | fline[2].x0 = fline[3].x0 = cx - y; |
||
487 | fline[2].x1 = fline[3].x1 = cx + y; |
||
488 | fline[2].y0 = fline[2].y1 = cy + x; |
||
489 | fline[3].y0 = fline[3].y1 = cy - x; |
||
490 | |||
491 | nsfb->plotter_fns->line(nsfb, 4, fline, &pen); |
||
492 | } |
||
493 | |||
494 | static bool circle_midpoint(nsfb_t *nsfb, |
||
495 | int cx, |
||
496 | int cy, |
||
497 | int r, |
||
498 | nsfb_colour_t c, |
||
499 | void (circfn)(nsfb_t *nsfb, int cx, int cy, int x, int y, nsfb_colour_t c)) |
||
500 | { |
||
501 | int x = 0; |
||
502 | int y = r; |
||
503 | int p = 1 - r; |
||
504 | |||
505 | circfn(nsfb, cx, cy, x, y, c); |
||
506 | while (x < y) { |
||
507 | x++; |
||
508 | if (p < 0) { |
||
509 | p += 2 * x + 1; |
||
510 | } else { |
||
511 | y--; |
||
512 | p += 2 * (x - y) + 1; |
||
513 | } |
||
514 | circfn(nsfb, cx, cy, x, y, c); |
||
515 | } |
||
516 | return true; |
||
517 | } |
||
518 | |||
519 | static bool ellipse(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c) |
||
520 | { |
||
521 | int width = (ellipse->x1 - ellipse->x0)>>1; |
||
522 | int height = (ellipse->y1 - ellipse->y0)>>1; |
||
523 | |||
524 | if (width == height) { |
||
525 | /* circle */ |
||
526 | return circle_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, c, circlepoints); |
||
527 | } else { |
||
528 | return ellipse_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, height, c, ellipsepoints); |
||
529 | } |
||
530 | } |
||
531 | |||
532 | static bool ellipse_fill(nsfb_t *nsfb, nsfb_bbox_t *ellipse, nsfb_colour_t c) |
||
533 | { |
||
534 | int width = (ellipse->x1 - ellipse->x0) >> 1; |
||
535 | int height = (ellipse->y1 - ellipse->y0) >> 1; |
||
536 | |||
537 | if (width == height) { |
||
538 | /* circle */ |
||
539 | return circle_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, c, circlefill); |
||
540 | } else { |
||
541 | return ellipse_midpoint(nsfb, ellipse->x0 + width, ellipse->y0 + height, width, height, c, ellipsefill); |
||
542 | } |
||
543 | } |
||
544 | |||
545 | |||
546 | |||
547 | /* copy an area of surface from one location to another. |
||
548 | * |
||
549 | * @warning This implementation is woefully incomplete! |
||
550 | */ |
||
551 | static bool |
||
552 | copy(nsfb_t *nsfb, nsfb_bbox_t *srcbox, nsfb_bbox_t *dstbox) |
||
553 | { |
||
554 | int srcx = srcbox->x0; |
||
555 | int srcy = srcbox->y0; |
||
556 | int dstx = dstbox->x0; |
||
557 | int dsty = dstbox->y0; |
||
558 | int width = dstbox->x1 - dstbox->x0; |
||
559 | int height = dstbox->y1 - dstbox->y0; |
||
560 | uint8_t *srcptr; |
||
561 | uint8_t *dstptr; |
||
562 | int hloop; |
||
563 | nsfb_bbox_t allbox; |
||
564 | |||
565 | nsfb_plot_add_rect(srcbox, dstbox, &allbox); |
||
566 | |||
567 | nsfb->surface_rtns->claim(nsfb, &allbox); |
||
568 | |||
569 | srcptr = (nsfb->ptr + |
||
570 | (srcy * nsfb->linelen) + |
||
571 | ((srcx * nsfb->bpp) / 8)); |
||
572 | |||
573 | dstptr = (nsfb->ptr + |
||
574 | (dsty * nsfb->linelen) + |
||
575 | ((dstx * nsfb->bpp) / 8)); |
||
576 | |||
577 | |||
578 | if (width == nsfb->width) { |
||
579 | /* take shortcut and use memmove */ |
||
580 | memmove(dstptr, srcptr, (width * height * nsfb->bpp) / 8); |
||
581 | } else { |
||
582 | if (srcy > dsty) { |
||
583 | for (hloop = height; hloop > 0; hloop--) { |
||
584 | memmove(dstptr, srcptr, (width * nsfb->bpp) / 8); |
||
585 | srcptr += nsfb->linelen; |
||
586 | dstptr += nsfb->linelen; |
||
587 | } |
||
588 | } else { |
||
589 | srcptr += height * nsfb->linelen; |
||
590 | dstptr += height * nsfb->linelen; |
||
591 | for (hloop = height; hloop > 0; hloop--) { |
||
592 | srcptr -= nsfb->linelen; |
||
593 | dstptr -= nsfb->linelen; |
||
594 | memmove(dstptr, srcptr, (width * nsfb->bpp) / 8); |
||
595 | } |
||
596 | } |
||
597 | } |
||
598 | |||
599 | nsfb->surface_rtns->update(nsfb, dstbox); |
||
600 | |||
601 | return true; |
||
602 | } |
||
603 | |||
604 | |||
605 | |||
606 | static bool arc(nsfb_t *nsfb, int x, int y, int radius, int angle1, int angle2, nsfb_colour_t c) |
||
607 | { |
||
608 | nsfb=nsfb; |
||
609 | x = x; |
||
610 | y = y; |
||
611 | radius = radius; |
||
612 | c = c; |
||
613 | angle1=angle1; |
||
614 | angle2=angle2; |
||
615 | return true; |
||
616 | } |
||
617 | |||
618 | #define N_SEG 30 |
||
619 | |||
620 | static int |
||
621 | cubic_points(unsigned int pointc, |
||
622 | nsfb_point_t *point, |
||
623 | nsfb_bbox_t *curve, |
||
624 | nsfb_point_t *ctrla, |
||
625 | nsfb_point_t *ctrlb) |
||
626 | { |
||
627 | unsigned int seg_loop; |
||
628 | double t; |
||
629 | double one_minus_t; |
||
630 | double a; |
||
631 | double b; |
||
632 | double c; |
||
633 | double d; |
||
634 | double x; |
||
635 | double y; |
||
636 | int cur_point; |
||
637 | |||
638 | point[0].x = curve->x0; |
||
639 | point[0].y = curve->y0; |
||
640 | cur_point = 1; |
||
641 | pointc--; |
||
642 | |||
643 | for (seg_loop = 1; seg_loop < pointc; ++seg_loop) { |
||
644 | t = (double)seg_loop / (double)pointc; |
||
645 | |||
646 | one_minus_t = 1.0 - t; |
||
647 | |||
648 | a = one_minus_t * one_minus_t * one_minus_t; |
||
649 | b = 3.0 * t * one_minus_t * one_minus_t; |
||
650 | c = 3.0 * t * t * one_minus_t; |
||
651 | d = t * t * t; |
||
652 | |||
653 | x = a * curve->x0 + b * ctrla->x + c * ctrlb->x + d * curve->x1; |
||
654 | y = a * curve->y0 + b * ctrla->y + c * ctrlb->y + d * curve->y1; |
||
655 | |||
656 | point[cur_point].x = x; |
||
657 | point[cur_point].y = y; |
||
658 | if ((point[cur_point].x != point[cur_point - 1].x) || |
||
659 | (point[cur_point].y != point[cur_point - 1].y)) |
||
660 | cur_point++; |
||
661 | } |
||
662 | |||
663 | point[cur_point].x = curve->x1; |
||
664 | point[cur_point].y = curve->y1; |
||
665 | if ((point[cur_point].x != point[cur_point - 1].x) || |
||
666 | (point[cur_point].y != point[cur_point - 1].y)) |
||
667 | cur_point++; |
||
668 | |||
669 | return cur_point; |
||
670 | } |
||
671 | |||
672 | /* calculate a series of points which describe a quadratic bezier spline. |
||
673 | * |
||
674 | * fills an array of points with values describing a quadratic curve. Both the |
||
675 | * start and end points are included as the first and last points |
||
676 | * respectively. Only if the next point on the curve is different from its |
||
677 | * predecessor is the point added which ensures points for the same position |
||
678 | * are not repeated. |
||
679 | */ |
||
680 | static int |
||
681 | quadratic_points(unsigned int pointc, |
||
682 | nsfb_point_t *point, |
||
683 | nsfb_bbox_t *curve, |
||
684 | nsfb_point_t *ctrla) |
||
685 | { |
||
686 | unsigned int seg_loop; |
||
687 | double t; |
||
688 | double one_minus_t; |
||
689 | double a; |
||
690 | double b; |
||
691 | double c; |
||
692 | double x; |
||
693 | double y; |
||
694 | int cur_point; |
||
695 | |||
696 | point[0].x = curve->x0; |
||
697 | point[0].y = curve->y0; |
||
698 | cur_point = 1; |
||
699 | pointc--; /* we have added the start point, one less point in the curve */ |
||
700 | |||
701 | for (seg_loop = 1; seg_loop < pointc; ++seg_loop) { |
||
702 | t = (double)seg_loop / (double)pointc; |
||
703 | |||
704 | one_minus_t = 1.0 - t; |
||
705 | |||
706 | a = one_minus_t * one_minus_t; |
||
707 | b = 2.0 * t * one_minus_t; |
||
708 | c = t * t; |
||
709 | |||
710 | x = a * curve->x0 + b * ctrla->x + c * curve->x1; |
||
711 | y = a * curve->y0 + b * ctrla->y + c * curve->y1; |
||
712 | |||
713 | point[cur_point].x = x; |
||
714 | point[cur_point].y = y; |
||
715 | if ((point[cur_point].x != point[cur_point - 1].x) || |
||
716 | (point[cur_point].y != point[cur_point - 1].y)) |
||
717 | cur_point++; |
||
718 | } |
||
719 | |||
720 | point[cur_point].x = curve->x1; |
||
721 | point[cur_point].y = curve->y1; |
||
722 | if ((point[cur_point].x != point[cur_point - 1].x) || |
||
723 | (point[cur_point].y != point[cur_point - 1].y)) |
||
724 | cur_point++; |
||
725 | |||
726 | return cur_point; |
||
727 | } |
||
728 | |||
729 | static bool |
||
730 | polylines(nsfb_t *nsfb, |
||
731 | int pointc, |
||
732 | const nsfb_point_t *points, |
||
733 | nsfb_plot_pen_t *pen) |
||
734 | { |
||
735 | int point_loop; |
||
736 | nsfb_bbox_t line; |
||
737 | |||
738 | if (pen->stroke_type != NFSB_PLOT_OPTYPE_NONE) { |
||
739 | for (point_loop = 0; point_loop < (pointc - 1); point_loop++) { |
||
740 | line = *(nsfb_bbox_t *)&points[point_loop]; |
||
741 | nsfb->plotter_fns->line(nsfb, 1, &line, pen); |
||
742 | } |
||
743 | } |
||
744 | return true; |
||
745 | } |
||
746 | |||
747 | |||
748 | |||
749 | static bool |
||
750 | quadratic(nsfb_t *nsfb, |
||
751 | nsfb_bbox_t *curve, |
||
752 | nsfb_point_t *ctrla, |
||
753 | nsfb_plot_pen_t *pen) |
||
754 | { |
||
755 | nsfb_point_t points[N_SEG]; |
||
756 | |||
757 | if (pen->stroke_type == NFSB_PLOT_OPTYPE_NONE) |
||
758 | return false; |
||
759 | |||
760 | return polylines(nsfb, quadratic_points(N_SEG, points, curve, ctrla), points, pen); |
||
761 | } |
||
762 | |||
763 | static bool |
||
764 | cubic(nsfb_t *nsfb, |
||
765 | nsfb_bbox_t *curve, |
||
766 | nsfb_point_t *ctrla, |
||
767 | nsfb_point_t *ctrlb, |
||
768 | nsfb_plot_pen_t *pen) |
||
769 | { |
||
770 | nsfb_point_t points[N_SEG]; |
||
771 | |||
772 | if (pen->stroke_type == NFSB_PLOT_OPTYPE_NONE) |
||
773 | return false; |
||
774 | |||
775 | return polylines(nsfb, cubic_points(N_SEG, points, curve, ctrla,ctrlb), points, pen); |
||
776 | } |
||
777 | |||
778 | |||
779 | static bool |
||
780 | path(nsfb_t *nsfb, int pathc, nsfb_plot_pathop_t *pathop, nsfb_plot_pen_t *pen) |
||
781 | { |
||
782 | int path_loop; |
||
783 | nsfb_point_t *pts; |
||
784 | nsfb_point_t *curpt; |
||
785 | int ptc = 0; |
||
786 | nsfb_bbox_t curve; |
||
787 | nsfb_point_t ctrla; |
||
788 | nsfb_point_t ctrlb; |
||
789 | int added_count = 0; |
||
790 | int bpts; |
||
791 | |||
792 | /* count the verticies in the path and add N_SEG extra for curves */ |
||
793 | for (path_loop = 0; path_loop < pathc; path_loop++) { |
||
794 | ptc++; |
||
795 | if ((pathop[path_loop].operation == NFSB_PLOT_PATHOP_QUAD) || |
||
796 | (pathop[path_loop].operation == NFSB_PLOT_PATHOP_CUBIC)) |
||
797 | ptc += N_SEG; |
||
798 | } |
||
799 | |||
800 | /* allocate storage for the vertexes */ |
||
801 | curpt = pts = malloc(ptc * sizeof(nsfb_point_t)); |
||
802 | |||
803 | for (path_loop = 0; path_loop < pathc; path_loop++) { |
||
804 | switch (pathop[path_loop].operation) { |
||
805 | case NFSB_PLOT_PATHOP_QUAD: |
||
806 | curpt-=2; |
||
807 | added_count -= 2; |
||
808 | curve.x0 = pathop[path_loop - 2].point.x; |
||
809 | curve.y0 = pathop[path_loop - 2].point.y; |
||
810 | ctrla.x = pathop[path_loop - 1].point.x; |
||
811 | ctrla.y = pathop[path_loop - 1].point.y; |
||
812 | curve.x1 = pathop[path_loop].point.x; |
||
813 | curve.y1 = pathop[path_loop].point.y; |
||
814 | bpts = quadratic_points(N_SEG, curpt, &curve, &ctrla); |
||
815 | curpt += bpts; |
||
816 | added_count += bpts; |
||
817 | break; |
||
818 | |||
819 | case NFSB_PLOT_PATHOP_CUBIC: |
||
820 | curpt-=3; |
||
821 | added_count -=3; |
||
822 | curve.x0 = pathop[path_loop - 3].point.x; |
||
823 | curve.y0 = pathop[path_loop - 3].point.y; |
||
824 | ctrla.x = pathop[path_loop - 2].point.x; |
||
825 | ctrla.y = pathop[path_loop - 2].point.y; |
||
826 | ctrlb.x = pathop[path_loop - 1].point.x; |
||
827 | ctrlb.y = pathop[path_loop - 1].point.y; |
||
828 | curve.x1 = pathop[path_loop].point.x; |
||
829 | curve.y1 = pathop[path_loop].point.y; |
||
830 | bpts = cubic_points(N_SEG, curpt, &curve, &ctrla, &ctrlb); |
||
831 | curpt += bpts; |
||
832 | added_count += bpts; |
||
833 | break; |
||
834 | |||
835 | default: |
||
836 | *curpt = pathop[path_loop].point; |
||
837 | curpt++; |
||
838 | added_count ++; |
||
839 | break; |
||
840 | } |
||
841 | } |
||
842 | |||
843 | if (pen->fill_type != NFSB_PLOT_OPTYPE_NONE) { |
||
844 | polygon(nsfb, (int *)pts, added_count, pen->fill_colour); |
||
845 | } |
||
846 | |||
847 | if (pen->stroke_type != NFSB_PLOT_OPTYPE_NONE) { |
||
848 | polylines(nsfb, added_count, pts, pen); |
||
849 | } |
||
850 | |||
851 | free(pts); |
||
852 | |||
853 | return true; |
||
854 | } |
||
855 | |||
856 | bool select_plotters(nsfb_t *nsfb) |
||
857 | { |
||
858 | const nsfb_plotter_fns_t *table = NULL; |
||
859 | |||
860 | switch (nsfb->format) { |
||
861 | |||
862 | case NSFB_FMT_XBGR8888: /* 32bpp Unused Blue Green Red */ |
||
863 | case NSFB_FMT_ABGR8888: /* 32bpp Alpha Blue Green Red */ |
||
864 | table = &_nsfb_32bpp_xbgr8888_plotters; |
||
865 | nsfb->bpp = 32; |
||
866 | break; |
||
867 | |||
868 | case NSFB_FMT_XRGB8888: /* 32bpp Unused Red Green Blue */ |
||
869 | case NSFB_FMT_ARGB8888: /* 32bpp Alpha Red Green Blue */ |
||
870 | table = &_nsfb_32bpp_xrgb8888_plotters; |
||
871 | nsfb->bpp = 32; |
||
872 | break; |
||
873 | |||
874 | |||
875 | case NSFB_FMT_RGB888: /* 24 bpp Alpha Red Green Blue */ |
||
876 | #ifdef ENABLE_24_BPP |
||
877 | table = &_nsfb_24bpp_plotters; |
||
878 | nsfb->bpp = 24; |
||
879 | break; |
||
880 | #else |
||
881 | return false; |
||
882 | #endif |
||
883 | |||
884 | case NSFB_FMT_ARGB1555: /* 16 bpp 555 */ |
||
885 | case NSFB_FMT_RGB565: /* 16 bpp 565 */ |
||
886 | table = &_nsfb_16bpp_plotters; |
||
887 | nsfb->bpp = 16; |
||
888 | break; |
||
889 | |||
890 | case NSFB_FMT_I8: /* 8bpp indexed */ |
||
891 | table = &_nsfb_8bpp_plotters; |
||
892 | nsfb->bpp = 8; |
||
893 | break; |
||
894 | |||
895 | case NSFB_FMT_I1: /* black and white */ |
||
896 | #ifdef ENABLE_1_BPP |
||
897 | table = &_nsfb_1bpp_plotters; |
||
898 | nsfb->bpp = 1 |
||
899 | break; |
||
900 | #else |
||
901 | return false; |
||
902 | #endif |
||
903 | |||
904 | case NSFB_FMT_ANY: /* No specific format - use surface default */ |
||
905 | default: |
||
906 | return false; |
||
907 | } |
||
908 | |||
909 | if (nsfb->plotter_fns != NULL) |
||
910 | free(nsfb->plotter_fns); |
||
911 | |||
912 | nsfb->plotter_fns = calloc(1, sizeof(nsfb_plotter_fns_t)); |
||
913 | memcpy(nsfb->plotter_fns, table, sizeof(nsfb_plotter_fns_t)); |
||
914 | |||
915 | /* set the generics */ |
||
916 | nsfb->plotter_fns->clg = clg; |
||
917 | nsfb->plotter_fns->set_clip = set_clip; |
||
918 | nsfb->plotter_fns->get_clip = get_clip; |
||
919 | nsfb->plotter_fns->polygon = polygon; |
||
920 | nsfb->plotter_fns->rectangle = rectangle; |
||
921 | nsfb->plotter_fns->ellipse = ellipse; |
||
922 | nsfb->plotter_fns->ellipse_fill = ellipse_fill; |
||
923 | nsfb->plotter_fns->copy = copy; |
||
924 | nsfb->plotter_fns->arc = arc; |
||
925 | nsfb->plotter_fns->quadratic = quadratic; |
||
926 | nsfb->plotter_fns->cubic = cubic; |
||
927 | nsfb->plotter_fns->path = path; |
||
928 | nsfb->plotter_fns->polylines = polylines; |
||
929 | |||
930 | /* set default clip rectangle to size of framebuffer */ |
||
931 | nsfb->clip.x0 = 0; |
||
932 | nsfb->clip.y0 = 0; |
||
933 | nsfb->clip.x1 = nsfb->width; |
||
934 | nsfb->clip.y1 = nsfb->height; |
||
935 | |||
936 | return true; |
||
937 | } |
||
938 | |||
939 | /* |
||
940 | * Local variables: |
||
941 | * c-basic-offset: 4 |
||
942 | * tab-width: 8 |
||
943 | * End: |
||
944 | */>>>>>>>0)>>>>>>>>>>>=>>>>>>>>>>>>> |