Mercurial > projects > dynamin
comparison dynamin/painting/graphics.d @ 0:aa4efef0f0b1
Initial commit of code.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Mon, 15 Jun 2009 22:10:48 -0500 |
parents | |
children | dcaa95190f4b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:aa4efef0f0b1 |
---|---|
1 // Written in the D programming language | |
2 // www.digitalmars.com/d/ | |
3 | |
4 /* | |
5 * The contents of this file are subject to the Mozilla Public License Version | |
6 * 1.1 (the "License"); you may not use this file except in compliance with | |
7 * the License. You may obtain a copy of the License at | |
8 * http://www.mozilla.org/MPL/ | |
9 * | |
10 * Software distributed under the License is distributed on an "AS IS" basis, | |
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
12 * for the specific language governing rights and limitations under the | |
13 * License. | |
14 * | |
15 * The Original Code is the Dynamin library. | |
16 * | |
17 * The Initial Developer of the Original Code is Jordan Miner. | |
18 * Portions created by the Initial Developer are Copyright (C) 2006-2009 | |
19 * the Initial Developer. All Rights Reserved. | |
20 * | |
21 * Contributor(s): | |
22 * Jordan Miner <jminer7@gmail.com> | |
23 * | |
24 */ | |
25 | |
26 module dynamin.painting.graphics; | |
27 | |
28 import dynamin.c.cairo; | |
29 import dynamin.core.string; | |
30 import dynamin.core.math; | |
31 import dynamin.core.file; | |
32 import dynamin.painting.coordinates; | |
33 import dynamin.painting.color; | |
34 import tango.io.Stdout; | |
35 | |
36 /// | |
37 class Font { | |
38 private: | |
39 string _family; | |
40 int _style = 0; | |
41 int _size; | |
42 public: | |
43 this(string family_ = "", int size = 10, bool b = false, bool i = false, bool u = false) { | |
44 this.family = family_; | |
45 this.size = size; | |
46 bold = b; | |
47 italic = i; | |
48 underline = u; | |
49 } | |
50 /** | |
51 * Gets or sets the family name of this font. Common font family names on | |
52 * Windows are "Arial", "Times New Roman", and "Tahoma". | |
53 */ | |
54 string family() { return _family; } | |
55 /// ditto | |
56 void family(string str) { _family = str; } | |
57 /// | |
58 int style() { return _style; } | |
59 /// ditto | |
60 void style(int s) { _style = s; } | |
61 /// Gets or sets whether this font is bold. | |
62 bool bold() { return cast(bool)(_style & 1); } | |
63 /// ditto | |
64 void bold(bool b) { b ? (_style |= 1) : (_style &= ~1); } | |
65 /// Gets or sets whether this font is italic. | |
66 bool italic() { return cast(bool)(_style & 2); } | |
67 /// ditto | |
68 void italic(bool b) { b ? (_style |= 2) : (_style &= ~2); } | |
69 /// Gets or sets whether this font is underline. | |
70 bool underline() { return cast(bool)(_style & 4); } | |
71 /// ditto | |
72 void underline(bool b) { b ? (_style |= 4) : (_style &= ~4); } | |
73 /// Gets or sets whether this font is strikethrough. | |
74 bool strikethrough() { return cast(bool)(_style & 8); } | |
75 /// ditto | |
76 void strikethrough(bool b) { b ? (_style |= 8) : (_style &= ~8); } | |
77 /** | |
78 * Gets or sets the size of this font in user space units, not in points. | |
79 * This size is the ascent plus the descent, not including the leading. | |
80 */ | |
81 int size() { return _size; } | |
82 /// ditto | |
83 void size(int s) { _size = s; } | |
84 } | |
85 | |
86 /// | |
87 struct FontExtents { | |
88 /// | |
89 real ascent; | |
90 /// | |
91 real descent; | |
92 /// | |
93 real leading() { return height - ascent - descent; } | |
94 /// | |
95 real height; | |
96 /// | |
97 real maxAdvance; | |
98 } | |
99 | |
100 //import lodepng = dynamin.lodepng.decode; | |
101 /// An RGBA 32-bit-per-pixel image. | |
102 class Image { | |
103 Color* _data; | |
104 uint _width, _height; | |
105 Color* data() { return _data; } | |
106 uint width() { return _width; } | |
107 uint height() { return _height; } | |
108 protected this() { | |
109 } | |
110 Color* opIndex(int x, int y) { | |
111 return _data + x+y*_width; | |
112 } | |
113 static Image load(string file) { | |
114 static if(false) { | |
115 auto img = new Image; | |
116 lodepng.PngInfo info; | |
117 img._data = cast(Color*)lodepng.decode32(readFileBytes(file), info); | |
118 img._width = info.image.width; | |
119 img._height = info.image.height; | |
120 | |
121 ubyte r; | |
122 for(uint i = 0; i < img.width * img.height; ++i) { | |
123 // lodepng returns data as ABGR instead of the ARGB that cairo, | |
124 // Windows, and I think X use. | |
125 r = img.data[i].R; | |
126 img.data[i].R = img.data[i].B; | |
127 img.data[i].B = r; | |
128 // cairo, Windows, and I think X use pre-multiplied alpha | |
129 img.data[i].R = img.data[i].R * img.data[i].A / 255; | |
130 img.data[i].G = img.data[i].G * img.data[i].A / 255; | |
131 img.data[i].B = img.data[i].B * img.data[i].A / 255; | |
132 } | |
133 | |
134 return img; | |
135 } else { return null; } | |
136 } | |
137 } | |
138 | |
139 /// | |
140 enum GraphicsOperator { | |
141 Clear, /// | |
142 | |
143 Source, /// | |
144 Over, /// | |
145 In, /// | |
146 Out, /// | |
147 Atop, /// | |
148 | |
149 Dest, /// | |
150 DestOver, /// | |
151 DestIn, /// | |
152 DestOut, /// | |
153 DestAtop, /// | |
154 | |
155 Xor, /// | |
156 Add, /// | |
157 Saturate /// | |
158 } | |
159 | |
160 /// | |
161 enum GraphicsFillRule { | |
162 /// | |
163 Winding, | |
164 EvenOdd /// | |
165 } | |
166 /** | |
167 * Example: | |
168 * ----- | |
169 * graphics.source = Color.Gold; | |
170 * graphics.rectangle(40, 10, 100, 120); | |
171 * graphics.fill(); | |
172 * graphics.source = Color.Black; | |
173 * | |
174 * graphics.lineWidth = 20; | |
175 * | |
176 * // GraphicsLineCap.Butt is default | |
177 * graphics.moveTo(40, 30); | |
178 * graphics.lineTo(140, 30); | |
179 * graphics.stroke(); | |
180 * | |
181 * graphics.lineCap = GraphicsLineCap.Round; | |
182 * graphics.moveTo(40, 70); | |
183 * graphics.lineTo(140, 70); | |
184 * graphics.stroke(); | |
185 * | |
186 * graphics.lineCap = GraphicsLineCap.Square; | |
187 * graphics.moveTo(40, 110); | |
188 * graphics.lineTo(140, 110); | |
189 * graphics.stroke(); | |
190 * ----- | |
191 * $(IMAGE ../web/example_line_cap.png) | |
192 */ | |
193 enum GraphicsLineCap { | |
194 /** | |
195 * Uses no ending. The line ends exactly at the end point. | |
196 */ | |
197 Butt, | |
198 /** | |
199 * Uses a rounded ending with the center of the circle at the end point. | |
200 * Therefore, the cap extends past the end point for half the line width. | |
201 */ | |
202 Round, | |
203 /** | |
204 * Uses a square ending with the center of the square at the end point. | |
205 * Therefore, the cap extends past the end point for half the line width. | |
206 */ | |
207 Square | |
208 } | |
209 | |
210 // cairo_copy_clip_rectangles --> Rectangle[] ClipRectangles() | |
211 // cairo_get_dash --> Dashes() | |
212 // cairo_get_color_stop_rgba --> ColorStops() | |
213 /** | |
214 * A Graphics object uses its source to draw on its target. Its target is set | |
215 * when it is created, but its source can be changed whenever desired. For | |
216 * example, for a painting event, the target of a Graphics is the control | |
217 * being painted. In other cases it could be an image. Its source is usually a | |
218 * color, but could be a gradient, an image, or some other pattern. | |
219 * | |
220 * If the documentation here is not sufficient, cairo might have | |
221 * better documentation at $(LINK http://www.cairographics.org/manual/). | |
222 */ | |
223 class Graphics { | |
224 private: | |
225 cairo_t* cr; | |
226 public: | |
227 this(cairo_t* cr) { | |
228 this.cr = cr; | |
229 cairo_reference(cr); | |
230 checkStatus(); | |
231 } | |
232 ~this() { | |
233 cairo_destroy(cr); | |
234 } | |
235 /** | |
236 * Returns: a pointer to the cairo context (cairo_t*) that backs this object | |
237 */ | |
238 cairo_t* handle() { return cr; } | |
239 void checkStatus() { | |
240 cairo_status_t status = cairo_status(cr); | |
241 if(status == CAIRO_STATUS_SUCCESS) | |
242 return; | |
243 | |
244 Stdout("Cairo error: ")(cairo_status_to_string(status)).newline; | |
245 assert(0); | |
246 } | |
247 /// | |
248 void moveTo(real x, real y) { | |
249 cairo_move_to(cr, x, y); | |
250 } | |
251 /// ditto | |
252 void moveTo(Point pt) { | |
253 moveTo(pt.x, pt.y); | |
254 } | |
255 /// | |
256 void lineTo(real x, real y) { | |
257 cairo_line_to(cr, x, y); | |
258 } | |
259 /// ditto | |
260 void lineTo(Point pt) { | |
261 lineTo(pt.x, pt.y); | |
262 } | |
263 /// | |
264 void curveTo(Point pt1, Point pt2, Point pt3) { | |
265 curveTo(pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y); | |
266 } | |
267 /// ditto | |
268 void curveTo(real x1, real y1, real x2, real y2, real x3, real y3) { | |
269 cairo_curve_to(cr, x1, y1, x2, y2, x3, y3); | |
270 } | |
271 /// | |
272 void relMoveTo(real x, real y) { | |
273 cairo_rel_move_to(cr, x, y); | |
274 } | |
275 /// ditto | |
276 void relMoveTo(Point pt) { | |
277 relMoveTo(pt.x, pt.y); | |
278 } | |
279 /// | |
280 void relLineTo(real x, real y) { | |
281 cairo_rel_line_to(cr, x, y); | |
282 } | |
283 /// ditto | |
284 void relLineTo(Point pt) { | |
285 relLineTo(pt.x, pt.y); | |
286 } | |
287 /// | |
288 void relCurveTo(Point pt1, Point pt2, Point pt3) { | |
289 relCurveTo(pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y); | |
290 } | |
291 /// ditto | |
292 void relCurveTo(real x1, real y1, real x2, real y2, real x3, real y3) { | |
293 cairo_rel_curve_to(cr, x1, y1, x2, y2, x3, y3); | |
294 } | |
295 /** | |
296 * Adds an arc to the current path. A line is added connecting the | |
297 * current point to the beginning of the arc. | |
298 * Example: | |
299 * ----- | |
300 * graphics.moveTo(5, 5); | |
301 * graphics.arc(50.5, 80.5, 40, 40, -0.2, PI/2); | |
302 * graphics.stroke(); | |
303 * ----- | |
304 * $(IMAGE ../web/example_arc.png) | |
305 */ | |
306 void arc(Point ptc, real radius, real angle1, real angle2) { | |
307 arc(ptc.x, ptc.y, radius, angle1, angle2); | |
308 } | |
309 /// ditto | |
310 void arc(real xc, real yc, real radius, real angle1, real angle2) { | |
311 cairo_arc(cr, xc, yc, radius, angle1, angle2); | |
312 } | |
313 /// ditto | |
314 void arc(real xc, real yc, real xradius, real yradius, real angle1, real angle2) { | |
315 cairo_save(cr); | |
316 cairo_translate(cr, xc, yc); | |
317 cairo_scale(cr, xradius, yradius); | |
318 cairo_arc(cr, 0, 0, 1, angle1, angle2); | |
319 cairo_restore(cr); | |
320 } | |
321 /** | |
322 * Adds an ellipse as a closed sub-path--a line will not connect it | |
323 * to the current point. | |
324 * Example: | |
325 * ----- | |
326 * graphics.ellipse(70.5, 50.5, 60, 25); | |
327 * graphics.stroke(); | |
328 * ----- | |
329 * $(IMAGE ../web/example_ellipse.png) | |
330 */ | |
331 void ellipse(real xc, real yc, real radius) { | |
332 cairo_new_sub_path(cr); | |
333 cairo_arc(cr, xc, yc, radius, 0, Pi * 2); | |
334 cairo_close_path(cr); | |
335 } | |
336 /// ditto | |
337 void ellipse(real xc, real yc, real xradius, real yradius) { | |
338 cairo_new_sub_path(cr); | |
339 arc(xc, yc, xradius, yradius, 0, Pi * 2); | |
340 cairo_close_path(cr); | |
341 } | |
342 /** | |
343 * Adds a rectangle as a sub-path--a line will not connect it | |
344 * to the current point. | |
345 * Example: | |
346 * ----- | |
347 * graphics.rectangle(5.5, 5.5, 70, 20); | |
348 * graphics.stroke(); | |
349 * ----- | |
350 * $(IMAGE ../web/example_rectangle.png) | |
351 */ | |
352 void rectangle(Rect rect) { | |
353 rectangle(rect.x, rect.y, rect.width, rect.height); | |
354 } | |
355 /// ditto | |
356 void rectangle(real x, real y, real width, real height) { | |
357 cairo_rectangle(cr, x, y, width, height); | |
358 } | |
359 /// | |
360 void closePath() { | |
361 cairo_close_path(cr); | |
362 } | |
363 /** | |
364 * Draws the outline of the current path. | |
365 * Example: | |
366 * ----- | |
367 * graphics.moveTo(12.5, 14.5); | |
368 * graphics.lineTo(123.5, 22.5); | |
369 * graphics.lineTo(139.5, 108.5); | |
370 * graphics.lineTo(49.5, 86.5); | |
371 * graphics.closePath(); | |
372 * graphics.stroke(); | |
373 * ----- | |
374 * $(IMAGE ../web/example_stroke.png) | |
375 */ | |
376 void stroke() { | |
377 cairo_stroke(cr); | |
378 } | |
379 /** | |
380 * Draws the inside of the current path. | |
381 * Example: | |
382 * ----- | |
383 * graphics.MoveTo(12.5, 14.5); | |
384 * graphics.LineTo(123.5, 22.5); | |
385 * graphics.LineTo(139.5, 108.5); | |
386 * graphics.LineTo(49.5, 86.5); | |
387 * graphics.ClosePath(); | |
388 * graphics.Fill(); | |
389 * ----- | |
390 * $(IMAGE ../web/example_fill.png) | |
391 */ | |
392 void fill() { | |
393 cairo_fill(cr); | |
394 } | |
395 /** | |
396 * Paints the current source everywhere within the current clip region. | |
397 * Examples: | |
398 * ----- | |
399 * graphics.source = Color(255, 128, 0); | |
400 * graphics.paint(); | |
401 * ----- | |
402 * $(IMAGE ../web/example_paint.png) | |
403 */ | |
404 void paint() { | |
405 cairo_paint(cr); | |
406 } | |
407 /** | |
408 * Gets or sets the line _width used for stroking. | |
409 * Example: | |
410 * ----- | |
411 * graphics.ellipse(40.5, 30.5, 30, 20); | |
412 * graphics.lineWidth = 1; | |
413 * graphics.stroke(); | |
414 * graphics.ellipse(40.5, 80.5, 30, 20); | |
415 * graphics.lineWidth = 5; | |
416 * graphics.stroke(); | |
417 * ----- | |
418 * $(IMAGE ../web/example_line_width.png) | |
419 */ | |
420 real lineWidth() { | |
421 return cairo_get_line_width(cr); | |
422 } | |
423 /// ditto | |
424 void lineWidth(real width) { | |
425 cairo_set_line_width(cr, width); | |
426 } | |
427 /** | |
428 * Gets or sets the line cap used for stroking. | |
429 * | |
430 * The line cap is only examined when the stroke is performed, not before. | |
431 * Therefore, drawing two lines, each with a different line cap, would | |
432 * require calling stroke twice. | |
433 */ | |
434 GraphicsLineCap lineCap() { | |
435 return cast(GraphicsLineCap)cairo_get_line_cap(cr); | |
436 } | |
437 /// ditto | |
438 void lineCap(GraphicsLineCap cap) { | |
439 cairo_set_line_cap(cr, cap); | |
440 } | |
441 /** | |
442 * Sets the font size to the specified size in user space units, not | |
443 * in points. | |
444 */ | |
445 void fontSize(real size) { | |
446 assert(size != 0); | |
447 cairo_set_font_size(cr, size); | |
448 } | |
449 /** | |
450 * Set the font used to draw text. | |
451 */ | |
452 void font(Font f) { | |
453 assert(f.size != 0); | |
454 cairo_set_font_size(cr, f.size); | |
455 cairo_select_font_face(cr, toCharPtr(f.family), f.italic, f.bold); | |
456 } | |
457 // TODO: if text is all ascii, do fast path with no uniscribe | |
458 void drawText(string text, real x, real y) { | |
459 auto extents = getFontExtents; | |
460 cairo_font_extents_t fextents; | |
461 cairo_font_extents(cr, &fextents); | |
462 cairo_move_to(cr, x, y + fextents.ascent); | |
463 cairo_show_text(cr, toCharPtr(text)); // 99% of time spent in show_text | |
464 checkStatus(); | |
465 } | |
466 /// | |
467 Size getTextExtents(string text) { | |
468 cairo_text_extents_t textents; | |
469 cairo_text_extents(cr, toCharPtr(text), &textents); | |
470 cairo_font_extents_t fextents; | |
471 cairo_font_extents(cr, &fextents); | |
472 return Size(textents.x_advance, fextents.height); | |
473 } | |
474 /// | |
475 FontExtents getFontExtents() { // TODO: make property? | |
476 cairo_font_extents_t fextents; | |
477 cairo_font_extents(cr, &fextents); | |
478 FontExtents extents; | |
479 extents.ascent = fextents.ascent; | |
480 extents.descent = fextents.descent; | |
481 extents.height = fextents.height; | |
482 extents.maxAdvance = fextents.max_x_advance; | |
483 return extents; | |
484 } | |
485 /// | |
486 Rect getClipExtents() { // TODO: make property? | |
487 double x, y, width, height; | |
488 cairo_clip_extents(cr, &x, &y, &width, &height); | |
489 return Rect(x, y, width, height); | |
490 } | |
491 /// | |
492 void save() { | |
493 cairo_save(cr); | |
494 } | |
495 /// | |
496 void restore() { | |
497 cairo_restore(cr); | |
498 checkStatus(); | |
499 } | |
500 /// | |
501 void clip() { | |
502 cairo_clip(cr); | |
503 } | |
504 /// | |
505 void translate(Point pt) { | |
506 translate(pt.x, pt.y); | |
507 } | |
508 /// ditto | |
509 void translate(real x, real y) { | |
510 cairo_translate(cr, x, y); | |
511 } | |
512 /// | |
513 void scale(real x, real y) { | |
514 cairo_scale(cr, x, y); | |
515 } | |
516 /// | |
517 void rotate(real angle) { | |
518 cairo_rotate(cr, angle); | |
519 } | |
520 /// | |
521 GraphicsOperator operator() { | |
522 return cast(GraphicsOperator)cairo_get_operator(cr); | |
523 } | |
524 /// | |
525 void operator(GraphicsOperator op) { | |
526 cairo_set_operator(cr, cast(cairo_operator_t)op); | |
527 } | |
528 /** | |
529 * Sets the dash pattern to be used when lines are drawn. | |
530 */ | |
531 void setDash(real[] dashes, real offset) { | |
532 auto cdashes = new double[dashes.length]; | |
533 foreach(int i, real r; dashes) | |
534 cdashes[i] = r; | |
535 cairo_set_dash(cr, cdashes.ptr, cdashes.length, offset); | |
536 } | |
537 /** | |
538 * Gets or sets the fill rule the current fill rule. | |
539 * The default is GraphicsFillRule.Winding. | |
540 */ | |
541 GraphicsFillRule fillRule() { | |
542 return cast(GraphicsFillRule)cairo_get_fill_rule(cr); | |
543 } | |
544 /// ditto | |
545 void fillRule(GraphicsFillRule rule) { | |
546 cairo_set_fill_rule(cr, cast(cairo_fill_rule_t)rule); | |
547 } | |
548 /** | |
549 * The temporary surface created will be the same size as the current clip. To speed up using this function, call Clip() to the area you will be drawing in. | |
550 */ | |
551 void pushGroup() { | |
552 cairo_push_group(cr); | |
553 } | |
554 //popGroup() { // TODO: returning a pattern | |
555 // cairo_pop_group(cr); | |
556 //} | |
557 /** | |
558 * Terminates the redirection begun by a call to PushGroup() or | |
559 * PushGroupWithContent() and installs the resulting pattern as the | |
560 * source pattern. | |
561 */ | |
562 void popGroupToSource() { | |
563 cairo_pop_group_to_source(cr); | |
564 checkStatus(); | |
565 } | |
566 // TODO: figure out the best way to set the source and get the source | |
567 void source(Color c) { | |
568 cairo_set_source_rgba(cr, c.R/255.0, c.G/255.0, c.B/255.0, c.A/255.0); | |
569 } | |
570 //void source(Pattern s) {} | |
571 //void setSource(Surface s, real x = 0, real y = 0) {} | |
572 // TODO: remove this function and have users do: | |
573 // g.setSource(img, x, y); | |
574 // g.paint(); | |
575 // ??? | |
576 // paintSource(Image, real, real) ? | |
577 /// Draws the specified image unscaled. | |
578 void drawImage(Image image, real x, real y) { | |
579 auto surface= cairo_image_surface_create_for_data(cast(char*)image.data, | |
580 CAIRO_FORMAT_ARGB32, image.width, image.height, image.width*4); | |
581 save(); | |
582 cairo_set_source_surface(cr, surface, x, y); | |
583 cairo_paint(cr); | |
584 restore(); | |
585 cairo_surface_destroy(surface); | |
586 } | |
587 // Draws the specified image scaled to the specified width and height. | |
588 //void drawImage(Image image, real x, real y, real width, real height); | |
589 } |