Mercurial > projects > dynamin
view dynamin/painting/graphics.d @ 104:5c8c1c2e12c0
Change from real to double.
double is not dependant on the platform, and it uses less space.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Fri, 06 Jul 2012 18:39:45 -0500 |
parents | 73060bc3f004 |
children | acdbb30fee7e |
line wrap: on
line source
/* * Copyright Jordan Miner * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * */ module dynamin.painting.graphics; import dynamin.c.cairo; import dynamin.core.string; import dynamin.core.math; import dynamin.core.file; import dynamin.painting.coordinates; import dynamin.painting.color; import tango.io.Stdout; /// class Font { private: string _family; int _style = 0; int _size; public: this(string family_ = "", int size = 10, bool b = false, bool i = false, bool u = false) { this.family = family_; this.size = size; bold = b; italic = i; underline = u; } /** * Gets or sets the family name of this font. Common font family names on * Windows are "Arial", "Times New Roman", and "Tahoma". */ string family() { return _family; } /// ditto void family(string str) { _family = str; } /// int style() { return _style; } /// ditto void style(int s) { _style = s; } /// Gets or sets whether this font is bold. bool bold() { return cast(bool)(_style & 1); } /// ditto void bold(bool b) { b ? (_style |= 1) : (_style &= ~1); } /// Gets or sets whether this font is italic. bool italic() { return cast(bool)(_style & 2); } /// ditto void italic(bool b) { b ? (_style |= 2) : (_style &= ~2); } /// Gets or sets whether this font is underline. bool underline() { return cast(bool)(_style & 4); } /// ditto void underline(bool b) { b ? (_style |= 4) : (_style &= ~4); } /// Gets or sets whether this font is strikethrough. bool strikethrough() { return cast(bool)(_style & 8); } /// ditto void strikethrough(bool b) { b ? (_style |= 8) : (_style &= ~8); } /** * Gets or sets the size of this font in user space units, not in points. * This size is the ascent plus the descent, not including the leading. */ int size() { return _size; } /// ditto void size(int s) { _size = s; } } /// struct FontExtents { /// double ascent; /// double descent; /// double leading() { return height - ascent - descent; } /// double height; /// double maxAdvance; } //import lodepng = dynamin.lodepng.decode; /// An RGBA 32-bit-per-pixel image. class Image { Color* _data; uint _width, _height; Color* data() { return _data; } uint width() { return _width; } uint height() { return _height; } protected this() { } Color* opIndex(int x, int y) { return _data + x+y*_width; } static Image load(string file) { static if(false) { auto img = new Image; lodepng.PngInfo info; img._data = cast(Color*)lodepng.decode32(readFileBytes(file), info); img._width = info.image.width; img._height = info.image.height; ubyte r; for(uint i = 0; i < img.width * img.height; ++i) { // lodepng returns data as ABGR instead of the ARGB that cairo, // Windows, and I think X use. r = img.data[i].R; img.data[i].R = img.data[i].B; img.data[i].B = r; // cairo, Windows, and I think X use pre-multiplied alpha img.data[i].R = img.data[i].R * img.data[i].A / 255; img.data[i].G = img.data[i].G * img.data[i].A / 255; img.data[i].B = img.data[i].B * img.data[i].A / 255; } return img; } else { return null; } } } /// enum GraphicsOperator { Clear, /// Source, /// Over, /// In, /// Out, /// Atop, /// Dest, /// DestOver, /// DestIn, /// DestOut, /// DestAtop, /// Xor, /// Add, /// Saturate /// } /// enum GraphicsFillRule { /// Winding, EvenOdd /// } /** * Example: * ----- * graphics.source = Color.Gold; * graphics.rectangle(40, 10, 100, 120); * graphics.fill(); * graphics.source = Color.Black; * * graphics.lineWidth = 20; * * // GraphicsLineCap.Butt is default * graphics.moveTo(40, 30); * graphics.lineTo(140, 30); * graphics.stroke(); * * graphics.lineCap = GraphicsLineCap.Round; * graphics.moveTo(40, 70); * graphics.lineTo(140, 70); * graphics.stroke(); * * graphics.lineCap = GraphicsLineCap.Square; * graphics.moveTo(40, 110); * graphics.lineTo(140, 110); * graphics.stroke(); * ----- * $(IMAGE ../web/example_line_cap.png) */ enum GraphicsLineCap { /** * Uses no ending. The line ends exactly at the end point. */ Butt, /** * Uses a rounded ending with the center of the circle at the end point. * Therefore, the cap extends past the end point for half the line width. */ Round, /** * Uses a square ending with the center of the square at the end point. * Therefore, the cap extends past the end point for half the line width. */ Square } // cairo_copy_clip_rectangles --> Rectangle[] ClipRectangles() // cairo_get_dash --> Dashes() // cairo_get_color_stop_rgba --> ColorStops() /** * A Graphics object uses its source to draw on its target. Its target is set * when it is created, but its source can be changed whenever desired. For * example, for a painting event, the target of a Graphics is the control * being painted. In other cases it could be an image. Its source is usually a * color, but could be a gradient, an image, or some other pattern. * * If the documentation here is not sufficient, cairo might have * better documentation at $(LINK http://www.cairographics.org/manual/). */ class Graphics { private: cairo_t* cr; public: this(cairo_t* cr) { this.cr = cr; cairo_reference(cr); checkStatus(); } ~this() { cairo_destroy(cr); } /** * Returns: a pointer to the cairo context (cairo_t*) that backs this object */ cairo_t* handle() { return cr; } void checkStatus() { cairo_status_t status = cairo_status(cr); if(status == CAIRO_STATUS_SUCCESS) return; Stdout("Cairo error: ")(cairo_status_to_string(status)).newline; assert(0); } /// void moveTo(double x, double y) { cairo_move_to(cr, x, y); } /// ditto void moveTo(Point pt) { moveTo(pt.x, pt.y); } /// void lineTo(double x, double y) { cairo_line_to(cr, x, y); } /// ditto void lineTo(Point pt) { lineTo(pt.x, pt.y); } /// void curveTo(Point pt1, Point pt2, Point pt3) { curveTo(pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y); } /// ditto void curveTo(double x1, double y1, double x2, double y2, double x3, double y3) { cairo_curve_to(cr, x1, y1, x2, y2, x3, y3); } /// void relMoveTo(double x, double y) { cairo_rel_move_to(cr, x, y); } /// ditto void relMoveTo(Point pt) { relMoveTo(pt.x, pt.y); } /// void relLineTo(double x, double y) { cairo_rel_line_to(cr, x, y); } /// ditto void relLineTo(Point pt) { relLineTo(pt.x, pt.y); } /// void relCurveTo(Point pt1, Point pt2, Point pt3) { relCurveTo(pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y); } /// ditto void relCurveTo(double x1, double y1, double x2, double y2, double x3, double y3) { cairo_rel_curve_to(cr, x1, y1, x2, y2, x3, y3); } /** * Adds an arc to the current path. A line is added connecting the * current point to the beginning of the arc. * Example: * ----- * graphics.moveTo(5, 5); * graphics.arc(50.5, 80.5, 40, 40, -0.2, PI/2); * graphics.stroke(); * ----- * $(IMAGE ../web/example_arc.png) */ void arc(Point ptc, double radius, double angle1, double angle2) { arc(ptc.x, ptc.y, radius, angle1, angle2); } /// ditto void arc(double xc, double yc, double radius, double angle1, double angle2) { cairo_arc(cr, xc, yc, radius, angle1, angle2); } /// ditto void arc(double xc, double yc, double xradius, double yradius, double angle1, double angle2) { cairo_save(cr); cairo_translate(cr, xc, yc); cairo_scale(cr, xradius, yradius); cairo_arc(cr, 0, 0, 1, angle1, angle2); cairo_restore(cr); } /** * Adds an ellipse as a closed sub-path--a line will not connect it * to the current point. * Example: * ----- * graphics.ellipse(70.5, 50.5, 60, 25); * graphics.stroke(); * ----- * $(IMAGE ../web/example_ellipse.png) */ void ellipse(double xc, double yc, double radius) { cairo_new_sub_path(cr); cairo_arc(cr, xc, yc, radius, 0, Pi * 2); cairo_close_path(cr); } /// ditto void ellipse(double xc, double yc, double xradius, double yradius) { cairo_new_sub_path(cr); arc(xc, yc, xradius, yradius, 0, Pi * 2); cairo_close_path(cr); } /** * Adds a rectangle as a sub-path--a line will not connect it * to the current point. * Example: * ----- * graphics.rectangle(5.5, 5.5, 70, 20); * graphics.stroke(); * ----- * $(IMAGE ../web/example_rectangle.png) */ void rectangle(Rect rect) { rectangle(rect.x, rect.y, rect.width, rect.height); } /// ditto void rectangle(double x, double y, double width, double height) { cairo_rectangle(cr, x, y, width, height); } /** * Adds a rectangle with rounded corners as a sub-path--a line will * not connect it to the current point. */ void roundedRectangle(Rect rect, double radius) { roundedRectangle(rect.x, rect.y, rect.width, rect.height, radius); } /// ditto void roundedRectangle(double x, double y, double width, double height, double radius) { alias radius r; cairo_new_sub_path(cr); arc(x+r, y+r, r, Pi, 3*Pi/2); arc(x+width-r, y+r, r, 3*Pi/2, 0); arc(x+width-r, y+height-r, r, 0, Pi/2); arc(x+r, y+height-r, r, Pi/2, Pi); closePath(); } /// void closePath() { cairo_close_path(cr); } /** * Draws the outline of the current path. * Example: * ----- * graphics.moveTo(12.5, 14.5); * graphics.lineTo(123.5, 22.5); * graphics.lineTo(139.5, 108.5); * graphics.lineTo(49.5, 86.5); * graphics.closePath(); * graphics.stroke(); * ----- * $(IMAGE ../web/example_stroke.png) */ void stroke() { cairo_stroke(cr); } /** * Draws the inside of the current path. * Example: * ----- * graphics.MoveTo(12.5, 14.5); * graphics.LineTo(123.5, 22.5); * graphics.LineTo(139.5, 108.5); * graphics.LineTo(49.5, 86.5); * graphics.ClosePath(); * graphics.Fill(); * ----- * $(IMAGE ../web/example_fill.png) */ void fill() { cairo_fill(cr); } /** * Paints the current source everywhere within the current clip region. * Examples: * ----- * graphics.source = Color(255, 128, 0); * graphics.paint(); * ----- * $(IMAGE ../web/example_paint.png) */ void paint() { cairo_paint(cr); } /** * Gets or sets the line _width used for stroking. * Example: * ----- * graphics.ellipse(40.5, 30.5, 30, 20); * graphics.lineWidth = 1; * graphics.stroke(); * graphics.ellipse(40.5, 80.5, 30, 20); * graphics.lineWidth = 5; * graphics.stroke(); * ----- * $(IMAGE ../web/example_line_width.png) */ double lineWidth() { return cairo_get_line_width(cr); } /// ditto void lineWidth(double width) { cairo_set_line_width(cr, width); } /** * Gets or sets the line cap used for stroking. * * The line cap is only examined when the stroke is performed, not before. * Therefore, drawing two lines, each with a different line cap, would * require calling stroke twice. */ GraphicsLineCap lineCap() { return cast(GraphicsLineCap)cairo_get_line_cap(cr); } /// ditto void lineCap(GraphicsLineCap cap) { cairo_set_line_cap(cr, cap); } /** * Sets the font size to the specified size in user space units, not * in points. */ void fontSize(double size) { assert(size != 0); cairo_set_font_size(cr, size); } /** * Set the font used to draw text. */ void font(Font f) { assert(f.size != 0); cairo_set_font_size(cr, f.size); cairo_select_font_face(cr, toCharPtr(f.family), f.italic, f.bold); } // TODO: if text is all ascii, do fast path with no uniscribe void drawText(string text, double x, double y) { auto extents = getFontExtents; cairo_font_extents_t fextents; cairo_font_extents(cr, &fextents); cairo_move_to(cr, x, y + fextents.ascent); cairo_show_text(cr, toCharPtr(text)); // 99% of time spent in show_text checkStatus(); } /// Size getTextExtents(string text) { cairo_text_extents_t textents; cairo_text_extents(cr, toCharPtr(text), &textents); cairo_font_extents_t fextents; cairo_font_extents(cr, &fextents); return Size(textents.x_advance, fextents.height); } /// FontExtents getFontExtents() { // TODO: make property? cairo_font_extents_t fextents; cairo_font_extents(cr, &fextents); FontExtents extents; extents.ascent = fextents.ascent; extents.descent = fextents.descent; extents.height = fextents.height; extents.maxAdvance = fextents.max_x_advance; return extents; } /// Rect getClipExtents() { // TODO: make property? double x, y, width, height; cairo_clip_extents(cr, &x, &y, &width, &height); return Rect(x, y, width, height); } /// void save() { cairo_save(cr); } /// void restore() { cairo_restore(cr); checkStatus(); } /// void clip() { cairo_clip(cr); } /// void translate(Point pt) { translate(pt.x, pt.y); } /// ditto void translate(double x, double y) { cairo_translate(cr, x, y); } /// void scale(double x, double y) { cairo_scale(cr, x, y); } /// void rotate(double angle) { cairo_rotate(cr, angle); } /// GraphicsOperator operator() { return cast(GraphicsOperator)cairo_get_operator(cr); } /// void operator(GraphicsOperator op) { cairo_set_operator(cr, cast(cairo_operator_t)op); } /** * Sets the dash pattern to be used when lines are drawn. */ void setDash(double[] dashes, double offset) { cairo_set_dash(cr, dashes.ptr, dashes.length, offset); } /** * Gets or sets the fill rule the current fill rule. * The default is GraphicsFillRule.Winding. */ GraphicsFillRule fillRule() { return cast(GraphicsFillRule)cairo_get_fill_rule(cr); } /// ditto void fillRule(GraphicsFillRule rule) { cairo_set_fill_rule(cr, cast(cairo_fill_rule_t)rule); } /** * 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. */ void pushGroup() { cairo_push_group(cr); } //popGroup() { // TODO: returning a pattern // cairo_pop_group(cr); //} /** * Terminates the redirection begun by a call to PushGroup() or * PushGroupWithContent() and installs the resulting pattern as the * source pattern. */ void popGroupToSource() { cairo_pop_group_to_source(cr); checkStatus(); } // TODO: figure out the best way to set the source and get the source void source(Color c) { cairo_set_source_rgba(cr, c.R/255.0, c.G/255.0, c.B/255.0, c.A/255.0); } //void source(Pattern s) {} //void setSource(Surface s, double x = 0, double y = 0) {} // TODO: remove this function and have users do: // g.setSource(img, x, y); // g.paint(); // ??? // paintSource(Image, double, double) ? /// Draws the specified image unscaled. void drawImage(Image image, double x, double y) { auto surface= cairo_image_surface_create_for_data(cast(char*)image.data, CAIRO_FORMAT_ARGB32, image.width, image.height, image.width*4); save(); cairo_set_source_surface(cr, surface, x, y); cairo_paint(cr); restore(); cairo_surface_destroy(surface); } // Draws the specified image scaled to the specified width and height. //void drawImage(Image image, double x, double y, double width, double height); }