module harmonia.gx.images; import harmonia.gx.geometry; import harmonia.gx.graphics; private import harmonia.ui.application; import harmonia.io.mmfile; version (Windows) { private import harmonia.ui.native.win32graphics; } else { static assert(0); // Windows only for a while, need somebody to take care } class Image: NativeImage //, ISurface - NativeImage is a surface { private: this() {} public: this( size sz , bool alpha ) { super(sz, alpha); } static Image create( ubyte[] bytes /*png,jpg encoded bytes*/) { Image ni = new Image(); if(ni.nativeDecode( bytes)) return ni; delete ni; return null; } static Image load( string path ) { MMFile f = MMFile.open(path); if( f is null ) return null; Image im = create(f[]); delete f; return im; } static Image loadFromURL(string url) { wchar* p = url; if( url.like("res:*") ) return loadFromResource( url[4..url.length] ); if( url.like("file:*") ) return load( url[5..url.length] ); if( url.like("file://*") ) return load( url[7..url.length] ); if( url.like("file:///*") ) // netscape treasures return load( url[8..url.length] ); return load( url ); } static Image loadFromResource( string name ) { ubyte[] bytes = Application.loadResource(name); if( bytes.length > 0 ) { Image im = create(bytes[]); return im; } return null; } size dimension() { return nativeDimension(); } uint bytesPerPixel( ) { return nativeBytesPerPixel(); } } /** * Expandable image * * margin[0] margin[2] * * +---+-----------+---+ * | | | | margin[1] * | | | | * +---+-----------+---+ * | | | | * | | | | * | | | | * +---+-----------+---+ * | | | | margin[3] * | | | | * +---+-----------+---+ * */ class ImageEx: Image { ushort[] _margins; bit[] _expands; static ImageEx create(ubyte[] bytes /*png,jpg encoded bytes*/, ushort[4] margins, bit[5] expands ) { ImageEx ni = new ImageEx(); if(ni.nativeDecode( bytes)) { size d = ni.dimension; assert( d.x >= (margins[0] + margins[2]) && d.y >= (margins[1] + margins[3]) ); ni._margins = margins; ni._expands = expands; return ni; } delete ni; return null; } void draw(Graphics g, rect dst) { int wl = _margins[0]; int wr = _margins[2]; int w = wl + wr; int ht = _margins[1]; int hb = _margins[3]; int h = ht + hb; // constraints if( dst.width < w ) { if( wl == 0 ) wr = dst.width; else if( wr == 0 ) wl = dst.width; else wr = dst.width - wl; if(wr < 0) { wr = 0; wl = dst.width; } w = wl + wr; } if( dst.height < h ) { if( ht == 0 ) hb = dst.height; else if( hb == 0 ) ht = dst.height; else hb = dst.height - ht; if(hb < 0) { hb = 0; ht = dst.height; } h = ht + hb; } int intWidth = dimension.x - w; // orig img interior width int intHeight = dimension.y - h; // orig img interior height int rightX = dimension.x - wr; // orig img right org int bottomY = dimension.y - hb; // orig img bottom org int dstIntWidth = dst.width - w; // destination interior width int dstIntHeight = dst.height - h; // destination interior height int dstRightX = dst.right - wr + 1; // destination right org int dstBottomY = dst.bottom - hb + 1; // destination bottom org auto Graphics src = new Graphics(this); if(ht > 0) { // left top corner if(wl > 0) g.copyRect(dst.pos, rect(point(0,0),size(wl,ht)), src); // top middle part if(dstIntWidth > 0) { rect srcr = rect( point(wl,0), size(intWidth,ht) ); rect dstr = rect( point(dst.pos.x + wl, dst.pos.y), size(dstIntWidth, ht) ); if( _expands[1] ) g.stretchRect( dstr, srcr, src ); else g.tileRect( dstr, srcr, src ); } // right top corner if(wr > 0) g.copyRect( point(dstRightX, dst.pos.y), rect(point(rightX,0),size(wr,ht)), src ); } if(intHeight > 0 && dstIntHeight > 0) { // interior left side if(wl > 0) { rect srcr = rect( point(0,ht), size(wl,intHeight) ); rect dstr = rect( point(dst.pos.x, dst.pos.y + ht), size(wl, dstIntHeight) ); if( _expands[0] ) g.stretchRect( dstr, srcr, src ); else g.tileRect( dstr, srcr, src ); } // interior middle middle if(dstIntWidth > 0) { rect srcr = rect( point(wl,ht), size(intWidth,intHeight) ); rect dstr = rect( point(dst.pos.x + wl, dst.pos.y + ht), size(dstIntWidth, dstIntHeight) ); if( _expands[4] ) g.stretchRect( dstr, srcr, src ); else g.tileRect( dstr, srcr, src ); } // interior right side if(wr > 0) { rect srcr = rect( point(rightX,ht), size(wr,intHeight) ); rect dstr = rect( point(dstRightX, dst.pos.y + ht), size(wr, dstIntHeight) ); if( _expands[2] ) g.stretchRect( dstr, srcr, src ); else g.tileRect( dstr, srcr, src ); } } if(hb > 0) { // bottom left side if(wl > 0) g.copyRect(point(dst.pos.x, dstBottomY), rect( point(0,bottomY), size(wl,hb)), src); // bottom middle middle if(dstIntWidth > 0) { rect srcr = rect( point(wl,bottomY), size(intWidth, hb) ); rect dstr = rect( point(dst.pos.x + wl, dstBottomY), size(dstIntWidth, hb) ); if( _expands[3] ) g.stretchRect( dstr, srcr, src ); else g.tileRect( dstr, srcr, src ); } // bottom right side if(wr > 0) g.copyRect(point(dstRightX, dstBottomY), rect(point(rightX,bottomY),size(wr,hb)), src ); } } } //| //| ImageList is an Image having set of icons defined. //| It has following layout: //| //| 0 1 2 2 4 <- icon indexesed //| +---+---+---+---+---+ //| | | | | | | images for state0 //| | | | | | | //| +---+---+---+---+---+ //| | | | | | | images for state1 //| | | | | | | //| +---+---+---+---+---+ //| | | | | | | images for state2 //| | | | | | | //| +---+---+---+---+---+ //| //| class ImageList:Image { protected: uint nIcons; uint nStates; public: this( uint numberOfIcons, uint numberOfStates ) { nIcons = numberOfIcons; nStates = numberOfStates; } void draw(Graphics g, point dst, uint nIcon, uint nState) { rect src = iconSrcRect(nIcon,nState); if(!src.isEmpty) { auto Graphics srcg = new Graphics(this); g.copyRect( dst, src, srcg ); } } rect iconSrcRect(uint nIcon, uint nState) { size sz = size( dimension.x / nIcons, dimension.y / nStates ); if( nIcon < nIcons && nState < nStates ) return rect( point( nIcon * sz.x, nState * sz.x ), sz ); return rect.empty; } uint icons() { return nIcons; } uint states() { return nStates; } size iconDimension() { return size( dimension.x / nIcons, dimension.y / nStates ); } static ImageList create( uint numberOfIcons, uint numberOfStates, ubyte[] bytes /*png,jpg encoded bytes*/) { ImageList ni = new ImageList(numberOfIcons,numberOfStates); if(ni.nativeDecode( bytes)) return ni; delete ni; return null; } } //| //| Icon - picture with states //| class Icon { protected: ImageList il; uint nIcon; public: this( ImageList il, uint nIcon ) { this.il = il; this.nIcon = nIcon; } void draw(Graphics g, point dst, uint nState) { il.draw(g, dst, nIcon, nState); } uint states() { return il.states(); } size dimension() { return il.iconDimension(); } }