module harmonia.gx.graphics; import harmonia.gx.geometry; import harmonia.gx.images; import harmonia.tools; version (Windows) { private import harmonia.ui.native.win32graphics; } else { static assert(0); // Windows only for a while, need somebody to take care } /** * Indicator of drawable entity * Window, Image and Printer are ISurfaces */ interface ISurface { void initialize(Graphics g); void finalize(Graphics g); } /** * color - ARGB */ enum : uint // system colors { TRANSPARENT = 0xFFFFFFFF, NO_COLOR = 0xFF000000, COLOR_EDIT = 0xFF000001, COLOR_EDIT_TEXT = 0xFF000002, COLOR_EDIT_BORDER = 0xFF000003, COLOR_DIALOG_BRIGHT = 0xFF000004, COLOR_DIALOG_LIGHT = 0xFF000005, COLOR_DIALOG = 0xFF000006, COLOR_DIALOG_SHADOW = 0xFF000007, COLOR_DIALOG_DKSHADOW = 0xFF000008, COLOR_DIALOG_TEXT = 0xFF000009, COLOR_SELECTION = 0xFF00000A, COLOR_SELECTION_TEXT = 0xFF00000B, COLOR_WORKSPACE = 0xFF00000C, COLOR_CAPTION = 0xFF00000D, COLOR_CAPTION_TEXT = 0xFF00000F, COLOR_ACTIVE_CAPTION = 0xFF000010, COLOR_ACTIVE_CAPTION_TEXT = 0xFF000011, COLOR_INFO = 0xFF000012, COLOR_INFO_TEXT = 0xFF000013, COLOR_SCROLLBAR = 0xFF000014, } typedef uint color = NO_COLOR; bool isSysColor(color c) { return (c & 0xFF000000) == 0xFF000000; } /** * Graphic - drawing primitives. */ class Graphics: public NativeGraphics { package: ISurface _surface; Font _font; static Font _defaultFont; color _textColor; bool _underline; public: this( ISurface surface ) { _surface = surface; _surface.initialize(this); nativeSetFont(_font = Font.system); } ~this( ) { if(_surface) _surface.finalize(this); _surface = null;} ISurface surface() { return _surface; } /** * The constant for a draw operation that draws the source over * the destination. */ enum DRAW_MODE { OVER, AND, OR, XOR } /** Copies a rectangular area from a surface to the given coordinates on the current surface. The copy operation is performed by combining pixels according to the setting of the current drawing operation. The same surface can be used as a source and destination to implement quick scrolling. @param surface the surface to copy from @param src the source rectangle @param dst the destination point (origin of where to copy) @see #setDrawOp */ void copyRect(point dst, rect src, Graphics gsrc = null) { if( gsrc is null ) gsrc = this; nativeCopyRect( gsrc, src, dst ); } void tileRect(rect dst, rect src, Graphics gsrc = null) { if( gsrc is null ) gsrc = this; int maxX = dst.right; int maxY = dst.bottom; int srcH = src.height; int srcW = src.width; point srcp = src.pos; size s; point p; if(gsrc is null) gsrc = this; s.y = srcH; for(p.y = dst.top; p.y <= maxY; p.y += srcH) { if(p.y + s.y > maxY) s.y = maxY - p.y + 1; s.x = srcW; for(p.x = dst.left; p.x <= maxX; p.x += srcW) { if(p.x + s.x > maxX) s.x = maxX - p.x + 1; nativeCopyRect( gsrc, rect(srcp,s), p ); } } } void stretchRect(rect dst, rect src, Graphics gsrc = null) { if( gsrc is null ) gsrc = this; nativeStretchRect( gsrc, src, dst ); } /** * draw rect using current brush (if any) and pen (if any) for borders */ void drawRect(rect rc) { nativeDrawRect(rc); } void drawPolygon( point[] points ) { nativeDrawPolygon(points); } // solid fill, will not change current brush void fillRect(rect rc, color b) { nativeFillRect(rc, b); } // gradient drawing void fillRect(rect rc, color topLeft, color topRight, color bottomRight, color bottomLeft) { nativeFillRect(rc,topLeft,topRight,bottomRight,bottomLeft); } void drawLine(point p1, point p2) { nativeDrawLine(p1,p2); } enum DRAW: uint // drawChars flags { LEFT = 0x0, CENTER = 0x1, RIGHT = 0x2, TOP = 0x00, MIDDLE = 0x10, BOTTOM = 0x20, END_ELLIPSIS = 0x100, PATH_ELLIPSIS = 0x200, WORD_ELLIPSIS = 0x300, } // simple draw text at position 'where' void drawChars(wchar[] chars, point where) { //rect rc = rect(where.x,where.y,0,0); nativeDrawChars(chars, where); if( _underline ) { int w = measureChars(chars).x; int y = where.y + _font.ascent + 1; fillRect(rect( where.x, y, where.x + w - 1, y ), _textColor); } } // draw text aligned inside rect 'where' void drawChars(wchar[] chars, rect where, uint flags = DRAW.LEFT | DRAW.MIDDLE) { nativeDrawChars(chars, where, flags); } // return dimensions of the string // on the graphics for the current font size measureChars(wchar[] chars) { return nativeMeasureChars(chars); } void drawImage(Image src, point dst) { auto Graphics ig = new Graphics(src); rect srcr = rect(src.dimension); copyRect(dst,srcr,ig); } // draw monochrome bitmap void drawPixmap(color c, point dst, size pixmapSize, ubyte[] pixmap) { nativeDrawBits(c,dst,pixmapSize,pixmap); } // determines if rc needs to be drawn. bool isDirty(rect rc) { return nativeIsDirty(rc); } enum PenStyle : uint { SOLID_INSIDE, SOLID, DASH, DOT, DASHDOT, DASHDOTDOT } void setPen(color c, int width, PenStyle style = PenStyle.SOLID_INSIDE) { nativeSetPen(c, width, cast(uint)style ); } void setNoPen() { nativeSetPen(TRANSPARENT, 0, 0 ); } enum BrushStyle : uint { SOLID, HORIZONTAL, // ----- VERTICAL, // ||||| FDIAGONAL, // \\\\\ BDIAGONAL, // ///// CROSS, // +++++ DIAGCROSS // xxxxx } void setBrush(color c, BrushStyle style = BrushStyle.SOLID) { nativeSetBrush(c, cast(uint)style ); } void setNoBrush() { nativeSetBrush(TRANSPARENT, cast(BrushStyle)0); } void font(Font f) { if(f is null) nativeSetFont(_font = _defaultFont); else nativeSetFont(_font = f); } Font font() { return _font; } // set text color (!) void textColor( color c ) { _textColor = c; nativeSetColor(c); } color textColor() { return _textColor; } bool underline() { return _underline; } bool underline(bool v) { return _underline = v; } // set origin of coordinate system void origin(point p) { nativeSetOrigin(p); } // push clip - sets clipping area, returns true if resultant area is not empty bool pushClip(rect rc) { return nativePushClip(rc); } // pop clip - restores clipping area, // ATTN: call this only if previous pushClip returned 'true' void popClip() { return nativePopClip(); } static uint pixelsPerInch(bool reset = false) { static uint pixelsInInch = 0; if(pixelsInInch == 0 || reset ) pixelsInInch = nativePixelsPerInchOnScreen(); return pixelsInInch; } } class Font: NativeFont { private: struct FontDef { char[] name; int size; // in pixels. bool bold; bool italic; } FontDef def; this() {} static Dictionary!(FontDef,Font) fonts; static this() { fonts = new Dictionary!(FontDef,Font); // create default system font, its index is 1 create("", 0, false, false); } // called by Dictionary!(FontDef,Font) static Font createByDef(FontDef def) { Font f = new Font(); f.def = def; f.nativeCreate( def.name, def.size, def.bold, def.italic ); return f; } public: static Font create(char[] name, int sizePixels, bool bold = false, bool italic = false) { FontDef def; def.name = name; def.size = sizePixels; def.bold = bold; def.italic = italic; return fonts.intern(def, &createByDef ); } static Font create(char[] name, double sizePoints, bool bold = false, bool italic = false) { FontDef def; def.name = name; def.size = - cast(int)(sizePoints * 10.0); def.bold = bold; def.italic = italic; return fonts.intern(def, &createByDef ); } static Font system() // default system font { return fonts(1); } //this( ) char[] name() { return def.name; } int size() { return def.size; } bool bold() { return def.bold; } bool italic() { return def.italic; } int ascent() { nativeInitMetrics(null); return _ascent; } int height() { nativeInitMetrics(null); return _height; } } color rgb( ubyte r, ubyte g, ubyte b ) { return (cast(color)b << 16) | (cast(color)g << 8) | cast(color)r; } color argb( ubyte a, ubyte r, ubyte g, ubyte b ) { return (cast(color)a << 24) | (cast(color)b << 16) | (cast(color)g << 8) | cast(color)r; } uint red( color c ) { return c & 0xFF; } uint green( color c ) { return (c >> 8) & 0xFF; } uint blue( color c ) { return (c >> 16) & 0xFF; } color mediumColor( color c1, color c2 ) { return rgb( (red(c1) + red(c2)) / 2, (green(c1) + green(c2)) / 2, (blue(c1) + blue(c2)) / 2 ); } enum LU: ubyte //LengthUnit { PX, // pixels MM, // millimeters CM, // centimeters IN, // inches PT, // points PR, // percents } uint pixels(double d, LU lu) { switch( lu ) { case LU.PX: return cast(uint)d; case LU.MM: return cast(uint)(cast(double)(Graphics.pixelsPerInch() * d) / 25.4); case LU.CM: return cast(uint)(cast(double)(Graphics.pixelsPerInch() * d) / 2.54); case LU.IN: return cast(uint)(cast(double)(Graphics.pixelsPerInch() * d)); case LU.PT: return cast(uint)(cast(double)(Graphics.pixelsPerInch() * d) / 72); default: break; } return 0; }