diff --git a/src/dlangui/core/collections.d b/src/dlangui/core/collections.d index e4fd800f..7b379555 100644 --- a/src/dlangui/core/collections.d +++ b/src/dlangui/core/collections.d @@ -41,7 +41,7 @@ import std.algorithm; /// array based collection of items /// retains item order when during add/remove operations -struct Collection(T) { +struct Collection(T, bool ownItems = false) { private T[] _items; private size_t _len; /// returns true if there are no items in collection @@ -62,8 +62,11 @@ struct Collection(T) { // shrink static if (is(T == class) || is(T == struct)) { // clear items - for (size_t i = newSize; i < _len; i++) - _items[i] = T.init; + for (size_t i = newSize; i < _len; i++) { + static if (ownItems) + destroy(_items[i]); + _items[i] = T.init; + } } } else if (newSize > _len) { // expand @@ -130,7 +133,9 @@ struct Collection(T) { size_t index = indexOf(value); if (index == size_t.max) return false; - remove(index); + T res = remove(index); + static if (ownItems) + destroy(res); return true; } /// support of foreach with reference @@ -147,8 +152,11 @@ struct Collection(T) { void clear() { static if (is(T == class) || is(T == struct)) { /// clear references - for(size_t i = 0; i < _len; i++) - _items[i] = T.init; + for(size_t i = 0; i < _len; i++) { + static if (ownItems) + destroy(_items[i]); + _items[i] = T.init; + } } _len = 0; _items = null; diff --git a/src/dlangui/graphics/drawbuf.d b/src/dlangui/graphics/drawbuf.d index 7f93b1bb..30cddd82 100644 --- a/src/dlangui/graphics/drawbuf.d +++ b/src/dlangui/graphics/drawbuf.d @@ -138,7 +138,16 @@ class DrawBuf : RefCountedObject { version (USE_OPENGL) { _id = drawBufIdGenerator++; } + debug(resalloc) _instanceCount++; } + + debug(resalloc) private static int _instanceCount; + debug(resalloc) @property static int instanceCount() { return _instanceCount; } + ~this() { + debug(resalloc) _instanceCount--; + clear(); + } + protected void function(uint) _onDestroyCallback; @property void onDestroyCallback(void function(uint) callback) { _onDestroyCallback = callback; } @property void function(uint) onDestroyCallback() { return _onDestroyCallback; } @@ -355,7 +364,6 @@ class DrawBuf : RefCountedObject { } void clear() {} - ~this() { clear(); } } alias DrawBufRef = Ref!DrawBuf; diff --git a/src/dlangui/graphics/ftfonts.d b/src/dlangui/graphics/ftfonts.d index f5d1121b..55fc7d49 100644 --- a/src/dlangui/graphics/ftfonts.d +++ b/src/dlangui/graphics/ftfonts.d @@ -5,6 +5,7 @@ import dlangui.graphics.fonts; import derelict.freetype.ft; private import dlangui.core.logger; +private import dlangui.core.collections; private import std.algorithm; private import std.file; private import std.string; @@ -99,7 +100,7 @@ private class FreeTypeFontFile { @property int weight() { return _weight; } @property bool italic() { return _italic; } - //private static int _instanceCount; + debug private static int _instanceCount; this(FT_Library library, string filename) { _library = library; _filename = filename; @@ -107,12 +108,12 @@ private class FreeTypeFontFile { _matrix.yy = 0x10000; _matrix.xy = 0; _matrix.yx = 0; - //Log.d("Created FreeTypeFontFile, count=", ++_instanceCount); + debug Log.d("Created FreeTypeFontFile, count=", ++_instanceCount); } ~this() { clear(); - //Log.d("Destroyed FreeTypeFontFile, count=", --_instanceCount); + debug Log.d("Destroyed FreeTypeFontFile, count=", --_instanceCount); } private static string familyName(FT_Face face) @@ -292,21 +293,21 @@ private class FreeTypeFontFile { */ class FreeTypeFont : Font { private FontFileItem _fontItem; - private FreeTypeFontFile[] _files; + private Collection!(FreeTypeFontFile, true) _files; - static int _instanceCount; + debug(resalloc) static int _instanceCount; /// need to call create() after construction to initialize font this(FontFileItem item, int size) { _fontItem = item; _size = size; _height = size; - debug Log.d("Created font, count=", ++_instanceCount); + debug(resalloc) Log.d("Created font, count=", ++_instanceCount); } /// do cleanup ~this() { clear(); - debug Log.d("Destroyed font, count=", --_instanceCount); + debug(resalloc) Log.d("Destroyed font, count=", --_instanceCount); } private int _size; @@ -317,10 +318,6 @@ class FreeTypeFont : Font { /// cleanup resources override void clear() { - foreach(ref FreeTypeFontFile file; _files) { - destroy(file); - file = null; - } _files.clear(); } @@ -375,8 +372,10 @@ class FreeTypeFont : Font { foreach (string filename; _fontItem.filenames) { FreeTypeFontFile file = new FreeTypeFontFile(_fontItem.library, filename); if (file.open(_size, 0)) { - _files ~= file; - } + _files.add(file); + } else { + destroy(file); + } } return _files.length > 0; } @@ -503,6 +502,7 @@ class FreeTypeFontManager : FontManager { weight = font.weight; Log.d("Using properties from font file: face=", face, " weight=", weight, " italic=", italic); } + destroy(font); FontDef def = FontDef(family, face, italic, weight); FontFileItem item = findFileItem(def); diff --git a/src/dlangui/graphics/resources.d b/src/dlangui/graphics/resources.d index 4415d104..9f95dadd 100644 --- a/src/dlangui/graphics/resources.d +++ b/src/dlangui/graphics/resources.d @@ -24,8 +24,8 @@ Copyright: Vadim Lopatin, 2014 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(WEB coolreader.org, Vadim Lopatin) */ -module dlangui.graphics.resources; - +module dlangui.graphics.resources; + import dlangui.graphics.images; import dlangui.graphics.drawbuf; import dlangui.core.logger; @@ -34,8 +34,8 @@ import std.algorithm; import std.xml; import std.algorithm; import std.conv; - - + + class Drawable : RefCountedObject { //private static int _instanceCount; this() { @@ -169,17 +169,28 @@ static Drawable createColorDrawable(string s) { class ImageDrawable : Drawable { protected DrawBufRef _image; protected bool _tiled; - //private int _instanceCount; + + debug(resalloc) private static int _instanceCount; + this(ref DrawBufRef image, bool tiled = false, bool ninePatch = false) { _image = image; _tiled = tiled; if (ninePatch) _image.detectNinePatch(); - //Log.d("Created ImageDrawable, count=", ++_instanceCount); + debug(resalloc) { + _instanceCount++; + Log.d("Created ImageDrawable, count=", _instanceCount); + } } + debug(resalloc) { + @property static int instanceCount() { return _instanceCount; } + } ~this() { _image.clear(); - //Log.d("Destroyed ImageDrawable, count=", --_instanceCount); + debug(resalloc) { + _instanceCount--; + Log.d("Destroyed ImageDrawable, count=", _instanceCount); + } } @property override int width() { if (_image.isNull) @@ -328,29 +339,29 @@ void extractStateFlags(ref string[string] attr, ref uint stateMask, ref uint sta sample: (prefix android: is optional) - - - + + + -*/ +*/ /// Drawable which is drawn depending on state (see http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList) class StateDrawable : Drawable { - static struct StateItem { + static class StateItem { uint stateMask; uint stateValue; ColorTransform transform; @@ -366,8 +377,14 @@ class StateDrawable : Drawable { // max drawable size for all states protected Point _size; + ~this() { + foreach(ref item; _stateList) + destroy(item); + _stateList = null; + } + void addState(uint stateMask, uint stateValue, string resourceId, ref ColorTransform transform) { - StateItem item; + StateItem item = new StateItem(); item.stateMask = stateMask; item.stateValue = stateValue; item.drawable = drawableCache.get(resourceId, transform); @@ -375,14 +392,14 @@ class StateDrawable : Drawable { } void addState(uint stateMask, uint stateValue, DrawableRef drawable) { - StateItem item; + StateItem item = new StateItem(); item.stateMask = stateMask; item.stateValue = stateValue; item.drawable = drawable; itemAdded(item); } - private void itemAdded(ref StateItem item) { + private void itemAdded(StateItem item) { _stateList ~= item; if (!item.drawable.isNull) { if (_size.x < item.drawable.width) @@ -471,12 +488,12 @@ class StateDrawable : Drawable { import std.string; try { - string s = cast(string)std.file.read(filename); - - // Check for well-formedness - //check(s); - - // Make a DOM tree + string s = cast(string)std.file.read(filename); + + // Check for well-formedness + //check(s); + + // Make a DOM tree auto doc = new Document(s); return load(doc); @@ -513,8 +530,8 @@ class StateDrawable : Drawable { alias DrawableRef = Ref!Drawable; - - + + /// decoded raster images cache (png, jpeg) -- access by filenames class ImageCache { @@ -654,23 +671,29 @@ class DrawableCache { DrawableRef _drawable; DrawableRef[ColorTransform] _transformed; - //private int _instanceCount; + debug(resalloc) private static int _instanceCount; this(string id, string filename, bool tiled) { _id = id; _filename = filename; _tiled = tiled; _error = filename is null; - //Log.d("Created DrawableCacheItem, count=", ++_instanceCount); + debug(resalloc) Log.d("Created DrawableCacheItem, count=", ++_instanceCount); } ~this() { _drawable.clear(); - //Log.d("Destroyed DrawableCacheItem, count=", --_instanceCount); + foreach(ref t; _transformed) + t.clear(); + _transformed.clear(); + debug(resalloc) Log.d("Destroyed DrawableCacheItem, count=", --_instanceCount); } /// remove from memory, will cause reload on next access void compact() { if (!_drawable.isNull) _drawable.clear(); - } + foreach(t; _transformed) + t.clear(); + _transformed.clear(); + } /// mark as not used void checkpoint() { _used = false; @@ -864,12 +887,15 @@ class DrawableCache { debug Log.i("Creating DrawableCache"); } ~this() { - debug Log.i("Destroying DrawableCache"); + debug(resalloc) Log.e("Drawable instace count before destroying of DrawableCache: ", ImageDrawable.instanceCount); + + Log.i("Destroying DrawableCache _idToDrawableMap.length=", _idToDrawableMap.length); foreach (ref item; _idToDrawableMap) { destroy(item); item = null; } _idToDrawableMap.clear(); + debug(resalloc) Log.e("Drawable instace count after destroying of DrawableCache: ", ImageDrawable.instanceCount); } } diff --git a/src/dlangui/platforms/sdl/sdlapp.d b/src/dlangui/platforms/sdl/sdlapp.d index 64f258a0..5ac1a6bb 100644 --- a/src/dlangui/platforms/sdl/sdlapp.d +++ b/src/dlangui/platforms/sdl/sdlapp.d @@ -54,6 +54,8 @@ version(USE_SDL) { } if (_win) SDL_DestroyWindow(_win); + if (_drawbuf) + destroy(_drawbuf); } version(USE_OPENGL) { @@ -798,7 +800,7 @@ version(USE_SDL) { SDL_DisplayMode displayMode; if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS) != 0) { - Log.e("Cannot init SDL2"); + Log.e("Cannot init SDL2"); return 2; } scope(exit)SDL_Quit(); @@ -828,7 +830,7 @@ version(USE_SDL) { Platform.setInstance(null); // - debug { + debug(resalloc) { Widget.shuttingDown(); } @@ -836,10 +838,19 @@ version(USE_SDL) { drawableCache = null; imageCache = null; FontManager.instance = null; - debug { - if (Widget.instanceCount() > 0) { - Log.e("Non-zero Widget instance count when exiting: ", Widget.instanceCount()); + debug(resalloc) { + if (DrawBuf.instanceCount > 0) { + Log.e("Non-zero DrawBuf instance count when exiting: ", DrawBuf.instanceCount); + } + if (Style.instanceCount > 0) { + Log.e("Non-zero Style instance count when exiting: ", Style.instanceCount); + } + if (Widget.instanceCount() > 0) { + Log.e("Non-zero Widget instance count when exiting: ", Widget.instanceCount); } + if (ImageDrawable.instanceCount > 0) { + Log.e("Non-zero ImageDrawable instance count when exiting: ", ImageDrawable.instanceCount); + } } Log.d("Exiting main"); diff --git a/src/dlangui/widgets/controls.d b/src/dlangui/widgets/controls.d index 730dd198..941ead09 100644 --- a/src/dlangui/widgets/controls.d +++ b/src/dlangui/widgets/controls.d @@ -123,6 +123,10 @@ class ImageWidget : Widget { _drawableId = drawableId; } + ~this() { + _drawable.clear(); + } + /// get drawable image id @property string drawableId() { return _drawableId; } /// set drawable image id diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d index aac61dc9..62acf8bb 100644 --- a/src/dlangui/widgets/styles.d +++ b/src/dlangui/widgets/styles.d @@ -451,14 +451,18 @@ class Style { return this; } - private static int _instanceCount; + debug(resalloc) private static int _instanceCount; + debug(resalloc) @property static int instanceCount() { return _instanceCount; } + this(Theme theme, string id) { _theme = theme; _parentStyle = theme; _id = id; + debug(resalloc) _instanceCount++; //Log.d("Created style ", _id, ", count=", ++_instanceCount); } + ~this() { foreach(ref Style item; _substates) { //Log.d("Destroying substate"); @@ -473,6 +477,7 @@ class Style { _children.clear(); _backgroundDrawable.clear(); _font.clear(); + debug(resalloc) _instanceCount--; //Log.d("Destroyed style ", _id, ", parentId=", _parentId, ", state=", _stateMask, ", count=", --_instanceCount); } diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 6475e7d0..6a9e4298 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -121,7 +121,7 @@ class Widget { /// set new trackHover flag value (when true, widget will change Hover state while mouse is moving) @property Widget trackHover(bool v) { _trackHover = v; return this; } - debug { + debug(resalloc) { private static int _instanceCount = 0; private static bool _appShuttingDown = false; } @@ -129,13 +129,12 @@ class Widget { this(string ID = null) { _id = ID; _state = State.Enabled; - debug { - _instanceCount++; - } + debug(resalloc) _instanceCount++; //Log.d("Created widget, count = ", ++_instanceCount); } + ~this() { - debug { + debug(resalloc) { //Log.v("destroying widget ", _id); if (_appShuttingDown) Log.e("Destroying widget ", _id, " after app shutdown: probably, resource leak"); @@ -146,7 +145,8 @@ class Widget { _ownStyle = null; //Log.d("Destroyed widget, count = ", --_instanceCount); } - debug { + + debug(resalloc) { /// for debug purposes - number of created widget objects, not yet destroyed static @property int instanceCount() { return _instanceCount; } /// for debug purposes - sets shutdown flag to log widgets not destroyed in time.