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 }