Browse Source

refactoring
renamed ThreadLoading to ThreadLoader
split ResAtlas.h/cpp into multiple files

dmuratshin 9 years ago
parent
commit
44bf912222

+ 43 - 15
examples/Demo/src/TestManageRes.h

@@ -1,10 +1,22 @@
 #pragma once
 #include "test.h"
-#include "Multithreading.h"
+#include "ThreadLoader.h"
+#include "pthread.h"
+
 #ifdef EMSCRIPTEN
 #include <emscripten.h>
 #endif
 
+
+void* myThreadFunc(void* t)
+{
+    Test* test = (Test*)t;
+    resources.load();
+
+    return 0;
+}
+
+
 class ManageResTest: public Test
 {
 public:
@@ -12,9 +24,11 @@ public:
     {
         toggle sw[] = {toggle("unload resources", 1), toggle("load resources", 0)};
         addToggle("switch", sw, 2);
+
 #ifdef EMSCRIPTEN
 #else
-        addButton("mt", "Multithreading loading");
+        addButton("mt1", "load using ThreadLoader");
+        addButton("mt2", "load from own thread");
 #endif
 
 
@@ -77,29 +91,43 @@ public:
         }
     }
 
+    spSprite createLoadingAnimation()
+    {
+        spSprite sp = new Sprite;
+        sp->setName("loading");
+        sp->setResAnim(resourcesUI.getResAnim("loading"));
+        sp->attachTo(ui);
+        sp->setAnchor(0.5f, 0.5f);
+        sp->setPosition(getSize() - sp->getSize() / 4);
+        sp->setScale(0.5f);
+        sp->addTween(Actor::TweenRotation(-(float)MATH_PI * 2), 1500, -1);
+
+        return sp;
+    }
+
     void clicked(string id)
     {
-        if (id == "mt" || id == "mt_slow")
+        if (id == "mt1")
         {
             resources.unload();
 
-            spSprite sp = new Sprite;
-            sp->setName("loading");
-            sp->setResAnim(resourcesUI.getResAnim("loading"));
-            sp->attachTo(ui);
-            sp->setAnchor(Vector2(0.5f, 0.5f));
-            sp->setPosition(getSize() - sp->getSize() / 4);
-            sp->setScale(0.5f);
-            sp->addTween(Actor::TweenRotation(-(float)MATH_PI * 2), 1500, -1);
+            createLoadingAnimation();
 
-            spThreadLoading l = new ThreadLoading;
-            l->addEventListener(ThreadLoading::COMPLETE, CLOSURE(this, &ManageResTest::_loaded));
+            spThreadLoader loading = new ThreadLoader;
 
+            loading->addEventListener(ThreadLoader::COMPLETE, CLOSURE(this, &ManageResTest::_loaded));
             addRef();//protect Test instance from automatic delete if you close it too fast
 
-            l->add(&resources);
+            loading->add(&resources);
+            loading->start();
+        }
+
+        if (id == "mt2")
+        {
+            resources.unload();
 
-            l->start(getStage());
+            pthread_t thread;
+            pthread_create(&thread, 0, myThreadFunc, this);
         }
     }
 };

+ 6 - 2
oxygine/SDL/win32/oxygine.vcxproj

@@ -169,13 +169,15 @@
     <ClCompile Include="..\..\src\minizip\ioapi.c" />
     <ClCompile Include="..\..\src\minizip\ioapi_mem.c" />
     <ClCompile Include="..\..\src\minizip\unzip.c" />
-    <ClCompile Include="..\..\src\Multithreading.cpp" />
+    <ClCompile Include="..\..\src\ThreadLoader.cpp" />
     <ClCompile Include="..\..\src\PointerState.cpp" />
     <ClCompile Include="..\..\src\Polygon.cpp" />
     <ClCompile Include="..\..\src\ProgressBar.cpp" />
     <ClCompile Include="..\..\src\res\CreateResourceContext.cpp" />
     <ClCompile Include="..\..\src\res\ResAnim.cpp" />
     <ClCompile Include="..\..\src\res\ResAtlas.cpp" />
+    <ClCompile Include="..\..\src\res\ResAtlasGeneric.cpp" />
+    <ClCompile Include="..\..\src\res\ResAtlasPrebuilt.cpp" />
     <ClCompile Include="..\..\src\res\ResBuffer.cpp" />
     <ClCompile Include="..\..\src\res\ResFont.cpp" />
     <ClCompile Include="..\..\src\res\ResFontBM.cpp" />
@@ -278,7 +280,7 @@
     <ClInclude Include="..\..\src\minizip\ioapi.h" />
     <ClInclude Include="..\..\src\minizip\ioapi_mem.h" />
     <ClInclude Include="..\..\src\minizip\unzip.h" />
-    <ClInclude Include="..\..\src\Multithreading.h" />
+    <ClInclude Include="..\..\src\ThreadLoader.h" />
     <ClInclude Include="..\..\src\oxygine-framework.h" />
     <ClInclude Include="..\..\src\oxygine_include.h" />
     <ClInclude Include="..\..\src\PointerState.h" />
@@ -289,6 +291,8 @@
     <ClInclude Include="..\..\src\res\CreateResourceContext.h" />
     <ClInclude Include="..\..\src\res\ResAnim.h" />
     <ClInclude Include="..\..\src\res\ResAtlas.h" />
+    <ClInclude Include="..\..\src\res\ResAtlasGeneric.h" />
+    <ClInclude Include="..\..\src\res\ResAtlasPrebuilt.h" />
     <ClInclude Include="..\..\src\res\ResBuffer.h" />
     <ClInclude Include="..\..\src\res\ResFont.h" />
     <ClInclude Include="..\..\src\res\ResFontBM.h" />

+ 21 - 9
oxygine/SDL/win32/oxygine.vcxproj.filters

@@ -21,7 +21,7 @@
     </Filter>
     <Filter Include="src\winnie_alloc">
       <UniqueIdentifier>{fa541539-b6e5-433e-b100-978d94de0a0d}</UniqueIdentifier>
-    </Filter>    
+    </Filter>
     <Filter Include="src\res">
       <UniqueIdentifier>{18bec777-8592-44da-9495-72a4cdb72f88}</UniqueIdentifier>
     </Filter>
@@ -65,7 +65,7 @@
     </ClCompile>
     <ClCompile Include="..\..\src\winnie_alloc\winnie_alloc.cpp">
       <Filter>src\winnie_alloc</Filter>
-    </ClCompile>    
+    </ClCompile>
     <ClCompile Include="..\..\src\res\ResAnim.cpp">
       <Filter>src\res</Filter>
     </ClCompile>
@@ -240,9 +240,6 @@
     <ClCompile Include="..\..\src\MemoryTexture.cpp">
       <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\Multithreading.cpp">
-      <Filter>src</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\src\PointerState.cpp">
       <Filter>src</Filter>
     </ClCompile>
@@ -336,6 +333,15 @@
     <ClCompile Include="..\..\src\Material.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\res\ResAtlasGeneric.cpp">
+      <Filter>src\res</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\res\ResAtlasPrebuilt.cpp">
+      <Filter>src\res</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\ThreadLoader.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\src\closure\closure.h">
@@ -391,7 +397,7 @@
     </ClInclude>
     <ClInclude Include="..\..\src\winnie_alloc\winnie_alloc_config.h">
       <Filter>src\winnie_alloc</Filter>
-    </ClInclude>    
+    </ClInclude>
     <ClInclude Include="..\..\src\res\CreateResourceContext.h">
       <Filter>src\res</Filter>
     </ClInclude>
@@ -596,9 +602,6 @@
     <ClInclude Include="..\..\src\MemoryTexture.h">
       <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\src\Multithreading.h">
-      <Filter>src</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\src\oxygine-framework.h">
       <Filter>src</Filter>
     </ClInclude>
@@ -725,6 +728,15 @@
     <ClInclude Include="..\..\src\Material.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\res\ResAtlasGeneric.h">
+      <Filter>src\res</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\res\ResAtlasPrebuilt.h">
+      <Filter>src\res</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\ThreadLoader.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="ReadMe.txt" />

+ 0 - 159
oxygine/src/Multithreading.cpp

@@ -1,159 +0,0 @@
-#include "Multithreading.h"
-#include "core/Mem2Native.h"
-#include "res/Resources.h"
-#include "res/Resource.h"
-#include "Stage.h"
-#include "res/CreateResourceContext.h"
-
-#ifdef __S3E__
-#include "s3eThread.h"
-#endif
-
-#ifdef OXYGINE_SDL
-#include "SDL_thread.h"
-#endif
-
-namespace oxygine
-{
-
-#if OXYGINE_SDL
-    typedef int s3eThread;
-    void* s3eThreadCreate(void* (*func)(void*), void* data)
-    {
-        return SDL_CreateThread((SDL_ThreadFunction)func, "loading", data);
-    }
-
-    void s3eThreadJoin(void* t)
-    {
-        int s = 0;
-        SDL_WaitThread((SDL_Thread*)t, &s);
-    }
-#elif __S3E__
-#else
-    typedef int s3eThread;
-    void* s3eThreadCreate(void* (*func)(void*), void* data)
-    {
-        log::warning("not implemented");
-        return 0;
-    }
-
-    void s3eThreadJoin(void* t)
-    {
-        log::warning("not implemented");
-    }
-#endif
-
-
-    void ThreadLoading::copyFrom(const ThreadLoading& src, cloneOptions opt)
-    {
-        Actor::copyFrom(src, opt);
-        _thread = pthread_self();
-        _threadDone = false;
-
-        _resources = src._resources;
-        _ress = src._ress;
-    }
-
-    ThreadLoading::ThreadLoading(): _thread(pthread_self()), _threadDone(false)
-    {
-        setTouchEnabled(false);
-    }
-
-    ThreadLoading::~ThreadLoading()
-    {
-        //if (!pthread_equal(_thread, pthread_self()))
-        //  pthread_join(_thread, 0);
-    }
-
-
-    void ThreadLoading::add(Resources* res)
-    {
-        _resources.push_back(res);
-    }
-
-    void ThreadLoading::add(Resource* res)
-    {
-        _ress.push_back(res);
-    }
-
-    bool ThreadLoading::isCompleted()
-    {
-        MutexAutoLock k(_m);
-        if (_threadDone && _m2n.isEmpty())
-            return true;
-
-        return false;
-    }
-
-    void* ThreadLoading::_staticThreadFunc(void* t)
-    {
-        ThreadLoading* This = (ThreadLoading*)t;
-        This->_threadFunc();
-        return 0;
-    }
-
-    void ThreadLoading::load()
-    {
-        _threadFunc();
-    }
-
-    void ThreadLoading::unload()
-    {
-        for (resources::iterator i = _resources.begin(); i != _resources.end(); ++i)
-        {
-            Resources* res = *i;
-            res->unload();
-        }
-
-        for (ress::iterator i = _ress.begin(); i != _ress.end(); ++i)
-        {
-            Resource* res = *i;
-            res->unload();
-        }
-    }
-
-    void ThreadLoading::_threadFunc()
-    {
-        for (resources::iterator i = _resources.begin(); i != _resources.end(); ++i)
-        {
-            Resources* res = *i;
-            res->load();
-        }
-
-        for (ress::iterator i = _ress.begin(); i != _ress.end(); ++i)
-        {
-            Resource* res = *i;
-            //log::messageln("loading res: %s", res->getName().c_str());
-            res->load();
-        }
-
-        MutexAutoLock k(_m);
-        _threadDone = true;
-    }
-
-    void ThreadLoading::doUpdate(const UpdateState& us)
-    {
-        _m2n.update();
-
-        if (isCompleted())
-        {
-            Event ev(COMPLETE, true);
-
-            dispatchEvent(&ev);
-
-            detach();
-        }
-    }
-
-    void ThreadLoading::start(spActor parent)
-    {
-        {
-            MutexAutoLock k(_m);
-            _threadDone = false;
-        }
-
-        parent->addChild(this);
-        pthread_create(&_thread, 0, _staticThreadFunc, this);
-        //_thread = s3eThreadCreate(_staticThreadFunc, this);
-    }
-}

+ 96 - 0
oxygine/src/ThreadLoader.cpp

@@ -0,0 +1,96 @@
+#include "ThreadLoader.h"
+#include "Event.h"
+#include "res/Resources.h"
+#include "res/Resource.h"
+#include "core/oxygine.h"
+
+namespace oxygine
+{
+    ThreadLoader::ThreadLoader(): _thread(pthread_self()), _threadDone(false)
+    {
+    }
+
+    ThreadLoader::~ThreadLoader()
+    {
+        //if (!pthread_equal(_thread, pthread_self()))
+        //  pthread_join(_thread, 0);
+    }
+
+
+    void ThreadLoader::add(Resources* res)
+    {
+        _resources.push_back(res);
+    }
+
+    void ThreadLoader::add(Resource* res)
+    {
+        _ress.push_back(res);
+    }
+
+    bool ThreadLoader::isCompleted()
+    {
+        return _threadDone;
+    }
+
+    void* ThreadLoader::_staticThreadFunc(void* t)
+    {
+        ThreadLoader* This = (ThreadLoader*)t;
+        This->_threadFunc();
+        return 0;
+    }
+
+    void ThreadLoader::load()
+    {
+        _threadFunc();
+    }
+
+    void ThreadLoader::unload()
+    {
+        for (resources::iterator i = _resources.begin(); i != _resources.end(); ++i)
+        {
+            Resources* res = *i;
+            res->unload();
+        }
+
+        for (ress::iterator i = _ress.begin(); i != _ress.end(); ++i)
+        {
+            Resource* res = *i;
+            res->unload();
+        }
+    }
+
+
+    void threadDone(const ThreadMessages::message& msg)
+    {
+        ThreadLoader* tl = (ThreadLoader*)msg.cbData;
+
+        Event ev(ThreadLoader::COMPLETE, true);
+        tl->dispatchEvent(&ev);
+        tl->releaseRef();
+    }
+
+    void ThreadLoader::_threadFunc()
+    {
+        for (resources::iterator i = _resources.begin(); i != _resources.end(); ++i)
+        {
+            Resources* res = *i;
+            res->load();
+        }
+
+        for (ress::iterator i = _ress.begin(); i != _ress.end(); ++i)
+        {
+            Resource* res = *i;
+            //log::messageln("loading res: %s", res->getName().c_str());
+            res->load();
+        }
+
+        core::getMainThreadMessages().postCallback(0, 0, 0, threadDone, this);
+    }
+
+    void ThreadLoader::start()
+    {
+        _threadDone = false;
+        addRef();
+        pthread_create(&_thread, 0, _staticThreadFunc, this);
+    }
+}

+ 8 - 19
oxygine/src/Multithreading.h → oxygine/src/ThreadLoader.h

@@ -1,38 +1,32 @@
 #pragma once
 #include "oxygine_include.h"
-#include "Actor.h"
-#include "core/Mem2Native.h"
-#include "res/Resource.h"
+#include "EventDispatcher.h"
 #include "pthread.h"
+#include "core/ThreadMessages.h"
 
 namespace oxygine
 {
-    DECLARE_SMART(ThreadLoading, spThreadLoading);
+    DECLARE_SMART(ThreadLoader, spThreadLoader);
     class Resources;
     class Resource;
-    class LoadResourcesContextMT;
 
-    class ThreadLoading: public Actor
+    class ThreadLoader: public EventDispatcher
     {
     public:
-        DECLARE_COPYCLONE_NEW(ThreadLoading);
-
         enum
         {
             COMPLETE = sysEventID('T', 'L', 'C'),
         };
 
-        ThreadLoading();
-        ~ThreadLoading();
-
-        //float getProgress() const;
+        ThreadLoader();
+        ~ThreadLoader();
 
         bool isCompleted();
+
         virtual void add(Resources* res);
         virtual void add(Resource* res);
 
-        /**Adds self to 'parent' actor as child and starts loading thread. Removes self automatically from 'parent' when loading is completed*/
-        void start(spActor parent);
+        void start();
         //void stop();
 
         /**Loads resources from current thread WITHOUT creating new (for debug)*/
@@ -42,13 +36,8 @@ namespace oxygine
     private:
         static void* _staticThreadFunc(void* t);
         virtual void _threadFunc();
-        void doUpdate(const UpdateState& us);
-
-        Mem2Native _m2n;
-        Mutex _m;
 
         pthread_t _thread;
-        //void *_thread;
         volatile bool _threadDone;
 
         typedef std::list<Resources*> resources;

+ 1 - 1
oxygine/src/oxygine-framework.h

@@ -23,7 +23,7 @@
 #include "Input.h"
 #include "InputText.h"
 #include "MemoryTexture.h"
-#include "Multithreading.h"
+#include "ThreadLoader.h"
 #include "PointerState.h"
 #include "ProgressBar.h"
 #include "RenderState.h"

+ 3 - 2
oxygine/src/res/CreateResourceContext.h

@@ -30,7 +30,8 @@ namespace oxygine
         bool                getAlphaHitTest() const { return _alphaHitTest; }
         const char*         getType() const {return _root.name();}
 
-        void            checkSetAttributes();
+        void                checkSetAttributes();
+        void                setXmlFolder(const std::string* ptr) { _xmlFolder = ptr; }
 
 
         XmlWalker       next();
@@ -59,7 +60,7 @@ namespace oxygine
 
     class ResourcesLoadOptions;
 
-    class CreateResourceContext//todo rename
+    class CreateResourceContext
     {
     public:
         CreateResourceContext() : resources(0), xml_name(0), prebuilt_folder(0), options(0),

+ 2 - 635
oxygine/src/res/ResAtlas.cpp

@@ -1,22 +1,10 @@
 #include "ResAtlas.h"
-#include "core/pixel.h"
-#include "core/ImageDataOperations.h"
-#include "utils/ImageUtils.h"
 #include "MemoryTexture.h"
-#include "utils/AtlasTool.h"
 #include "Resources.h"
-#include "CreateResourceContext.h"
-#include "core/NativeTexture.h"
-#include <stdio.h>
-#include "core/Mem2Native.h"
 #include "core/VideoDriver.h"
-#include <stdint.h>
-#include "utils/stringUtils.h"
+#include "ResAtlasGeneric.h"
+#include "ResAtlasPrebuilt.h"
 
-extern "C"
-{
-#include "utils/cdecode.h"
-}
 
 #ifdef __S3E__
 #include "IwImage.h"
@@ -27,60 +15,6 @@ extern "C"
 
 namespace oxygine
 {
-    int defaultAtlasWidth = 2048;
-    int defaultAtlasHeight = 2048;
-
-    struct atlas_data
-    {
-        spNativeTexture texture;
-        MemoryTexture mt;
-        Atlas2 atlas;
-    };
-
-
-    void apply_atlas(atlas_data& ad, bool linear, bool clamp2edge)
-    {
-        if (!ad.texture)
-            return;
-
-        spMemoryTexture mt = new MemoryTexture;
-        Rect bounds = ad.atlas.getBounds();
-
-        int w = nextPOT(bounds.getRight());
-        int h = nextPOT(bounds.getBottom());
-
-        mt->init(ad.mt.lock().getRect(Rect(0, 0, w, h)));
-#if 0
-        static int n = 0;
-        n++;
-        char name[255];
-        safe_sprintf(name, "test%d.tga", n);
-        saveImage(image_data, name);
-#endif
-
-		CreateTextureTask task;
-		task.linearFilter = linear;
-		task.clamp2edge = clamp2edge;
-		task.src = mt;
-		task.dest = ad.texture;
-		LoadResourcesContext::get()->createTexture(task);
-    }
-
-    void next_atlas(int w, int h, TextureFormat tf, atlas_data& ad, const char* name)
-    {
-        ad.mt.init(w, h, tf);
-        ad.mt.fill_zero();
-
-        ad.atlas.clean();
-        ad.atlas.init(w, h);
-        //printf("creating next atlas\n");
-        ad.texture = IVideoDriver::instance->createTexture();
-        ad.texture->setName(name);
-        ad.texture->init(0, w, h, TF_UNDEFINED);
-        //ad.texture->init(ad.mt.getWidth(), ad.mt.getHeight(), ad.mt.getFormat());
-    }
-
-
     static load_texture_hook _hook = 0;
     void set_load_texture_hook(load_texture_hook hook)
     {
@@ -259,571 +193,4 @@ namespace oxygine
 
         return ra;
     }
-
-
-    int roundUp(int numToRound, int multiple)
-    {
-        if (multiple == 0)
-            return numToRound;
-
-        int remainder = numToRound % multiple;
-        if (remainder == 0)
-            return numToRound;
-        return numToRound + multiple - remainder;
-    }
-
-
-    int HIT_TEST_DOWNSCALE = 4;
-    const int ALIGN = sizeof(int32_t);
-    const int BITS = ALIGN * 8;
-
-    void makeAlpha(const ImageData& srcImage, Rect& bounds, std::vector<unsigned char>& alpha, HitTestData& adata, bool hittest)
-    {
-        int w = srcImage.w;
-        int h = srcImage.h;
-
-        size_t pos = alpha.size();
-        adata.data = reinterpret_cast<unsigned char*>(pos);
-        adata.w = roundUp(w, HIT_TEST_DOWNSCALE) / HIT_TEST_DOWNSCALE;
-        adata.h = roundUp(h, HIT_TEST_DOWNSCALE) / HIT_TEST_DOWNSCALE;
-
-
-        int lineInts = roundUp(adata.w, BITS) / BITS;
-
-        int destPitch = lineInts * ALIGN;
-
-        int size = adata.h * destPitch;
-
-        alpha.resize(pos + size);
-
-
-        const unsigned char* srcData = srcImage.data;
-        int srcStep = srcImage.bytespp;
-        int srcPitch = srcImage.pitch;
-
-        unsigned char* destData = &alpha[pos];
-
-        adata.pitch = destPitch;
-
-        const unsigned char* srcRow = srcData;
-        unsigned char* destRow = destData;
-
-
-        int minX = w;
-        int minY = h;
-        int maxX = 0;
-        int maxY = 0;
-
-        bool hasAlpha = false;
-
-        for (int y = 0; y != h; y += 1)
-        {
-            const unsigned char* srcLine = srcRow;
-            int32_t* destLine = reinterpret_cast<int32_t*>(destRow);
-
-            bool lineWithAlpha = false;
-
-
-            for (int x = 0; x != w; x += 1)
-            {
-                PixelR8G8B8A8 pd;
-                Pixel p;
-                pd.getPixel(srcLine, p);
-
-
-                if (p.a > 5)
-                {
-                    hasAlpha = true;
-
-                    int dx = x / HIT_TEST_DOWNSCALE;
-                    int n = dx / BITS;
-                    int b = dx % BITS;
-
-                    destLine[n] |= 1 << b;
-
-                    lineWithAlpha = true;
-                    if (x > maxX)
-                        maxX = x;
-                    else if (x < minX)
-                        minX = x;
-                }
-                srcLine += srcStep;
-            }
-
-            if (lineWithAlpha)
-            {
-                if (minY == h)
-                    minY = y;
-                maxY = y;
-            }
-
-            if (y % HIT_TEST_DOWNSCALE == HIT_TEST_DOWNSCALE - 1)
-            {
-                //reset line
-                destRow += destPitch;
-            }
-
-            srcRow += srcPitch;
-        }
-
-        //if image is transparent
-        if (minX == w && maxX == 0)
-        {
-            minX = 0;
-            maxX = 0;
-        }
-
-        if (minY == h && maxY == 0)
-        {
-            minY = 0;
-            maxY = 0;
-        }
-
-        bounds = Rect(minX, minY, maxX - minX + 1, maxY - minY + 1);
-
-        if (!hasAlpha || !hittest)
-        {
-            alpha.resize(pos);
-            adata = HitTestData();
-        }
-    }
-
-
-
-    void ResAtlasGeneric::loadAtlas(CreateResourceContext& context)
-    {
-        pugi::xml_node node = context.walker.getNode();
-
-        int w = node.attribute("width").as_int(defaultAtlasWidth);
-        int h = node.attribute("height").as_int(defaultAtlasHeight);
-
-        const char* format = node.attribute("format").as_string("8888");
-
-        loadBase(node);
-
-        atlas_data ad;
-
-
-        TextureFormat tf = string2TextureFormat(format);
-
-
-        bool compressed = false;
-
-
-        std::vector<ResAnim*> anims;
-
-
-        while (true)
-        {
-            XmlWalker walker = context.walker.next();
-            if (walker.empty())
-                break;
-
-            pugi::xml_node child_node = walker.getNode();
-
-            const char* name = child_node.name();
-            if (strcmp(name, "image"))
-                continue;
-
-
-            std::string id = child_node.attribute("id").value();
-            std::string file = child_node.attribute("file").value();
-
-            if (file.empty())
-            {
-                createEmpty(walker, context);
-                continue;
-            }
-
-
-            bool trim = child_node.attribute("trim").as_bool(true);
-            bool extend = child_node.attribute("extend").as_bool(true);
-
-            Point offset = extend ? Point(2, 2) : Point(0, 0);
-
-            MemoryTexture mt;
-            ImageData im;
-
-            int columns = 0;
-            int rows = 0;
-            int frame_width = 0;
-            int frame_height = 0;
-            float frame_scale = 1.0f;
-            bool loaded = false;
-
-
-            file::buffer bf;
-            file::read(walker.getPath("file").c_str(), bf);
-
-            mt.init(bf, true, tf);
-            im = mt.lock();
-            if (im.w)
-            {
-                rows = child_node.attribute("rows").as_int();
-                frame_width = child_node.attribute("frame_width").as_int();
-                columns = child_node.attribute("cols").as_int();
-                frame_height = child_node.attribute("frame_height").as_int();
-
-                if (!rows)
-                    rows = 1;
-
-                if (!columns)
-                    columns = 1;
-
-                if (frame_width)
-                    columns = im.w / frame_width;
-                else
-                    frame_width = im.w / columns;
-
-
-                if (frame_height)
-                    rows = im.h / frame_height;
-                else
-                    frame_height = im.h / rows;
-            }
-
-
-            if (columns)
-            {
-                animationFrames frames;
-                int frames_count = rows * columns;
-                frames.reserve(frames_count);
-
-                ResAnim* ra = new ResAnim(this);
-
-
-                anims.push_back(ra);
-
-                for (int y = 0; y < rows; ++y)
-                {
-                    for (int x = 0; x < columns; ++x)
-                    {
-                        Rect frameRect;
-                        frameRect.pos = Point(x * frame_width, y * frame_height);
-                        frameRect.size = Point(frame_width, frame_height);
-
-                        ImageData srcImage_ = im.getRect(frameRect);
-
-
-                        HitTestData adata;
-                        ImageData src;
-                        Rect bounds(0, 0, im.w, im.h);
-                        if (trim)
-                            makeAlpha(srcImage_, bounds, _hitTestBuffer, adata, walker.getAlphaHitTest());
-                        src = srcImage_.getRect(bounds);
-
-                        Rect dest(0, 0, 0, 0);
-
-                        if (!ad.texture)
-                        {
-                            std::string atlas_id = getName();
-                            next_atlas(w, h, tf, ad, atlas_id.c_str());
-                        }
-
-                        bool s = ad.atlas.add(&ad.mt, src, dest, offset);
-                        if (s == false)
-                        {
-                            apply_atlas(ad, _linearFilter, _clamp2edge);
-                            next_atlas(w, h, tf, ad, walker.getCurrentFolder().c_str());
-                            s = ad.atlas.add(&ad.mt, src, dest, offset);
-                            OX_ASSERT(s);
-                        }
-
-                        //extend = false;
-                        if (extend)
-                        {
-                            //duplicate image edges
-                            MemoryTexture& mt = ad.mt;
-                            ImageData tmp;
-
-                            if (bounds.getY() == 0)
-                            {
-                                tmp = mt.lock(Rect(dest.pos.x, dest.pos.y - 1, src.w, 1));
-                                operations::copy(src.getRect(Rect(0, 0, src.w, 1)), tmp);
-                            }
-
-                            if (bounds.getHeight() == im.h)
-                            {
-                                tmp = mt.lock(Rect(dest.pos.x, dest.pos.y + src.h, src.w, 1));
-                                operations::copy(src.getRect(Rect(0, src.h - 1, src.w, 1)), tmp);
-                            }
-
-                            if (bounds.getX() == 0)
-                            {
-                                tmp = mt.lock(Rect(dest.pos.x - 1, dest.pos.y, 1, src.h));
-                                operations::copy(src.getRect(Rect(0, 0, 1, src.h)), tmp);
-                            }
-
-                            if (bounds.getWidth() == im.w)
-                            {
-                                tmp = mt.lock(Rect(dest.pos.x + src.w, dest.pos.y, 1, src.h));
-                                operations::copy(src.getRect(Rect(src.w - 1, 0, 1, src.h)), tmp);
-                            }
-                        }
-
-
-                        //operations::copy(src.getRect(Rect(0, 0, 1, 1)), mt.lock(&Rect(dest.pos.x - 1, dest.pos.y - 1, 1, 1)));
-                        //operations::copy(src.getRect(Rect(src.w - 1, 0, 1, 1)), mt.lock(&Rect(dest.pos.x + src.w, dest.pos.y - 1, 1, 1)));
-
-                        //operations::copy(src.getRect(Rect(0, src.h - 1, 1, 1)), mt.lock(&Rect(dest.pos.x - 1, dest.pos.y + src.h, 1, 1)));
-                        //operations::copy(src.getRect(Rect(src.w - 1, src.h - 1, 1, 1)), mt.lock(&Rect(dest.pos.x + src.w, dest.pos.y + src.h, 1, 1)));
-
-
-                        float iw = 1.0f;
-                        float ih = 1.0f;
-
-                        RectF srcRect(dest.pos.x * iw, dest.pos.y * ih, dest.size.x * iw, dest.size.y * ih);
-
-                        Vector2 sizeScaled = Vector2((float)dest.size.x, (float)dest.size.y) * walker.getScaleFactor();
-                        RectF destRect(bounds.pos.cast<Vector2>(), sizeScaled);
-
-                        AnimationFrame frame;
-                        Diffuse df;
-                        df.base = ad.texture;
-                        df.premultiplied = true;//!Renderer::getPremultipliedAlphaRender();
-
-                        Vector2 fsize = Vector2((float)frame_width, (float)frame_height) * walker.getScaleFactor();
-                        frame.init2(ra, x, y, df,
-                                    srcRect, destRect, fsize);
-
-                        frame.setHitTestData(adata);
-
-                        frames.push_back(frame);
-                    }
-                }
-
-                init_resAnim(ra, file, child_node);
-
-                ra->init(frames, columns, walker.getScaleFactor(), 1.0f / walker.getScaleFactor());
-                ra->setParent(this);
-                context.resources->add(ra, context.options->_shortenIDS);
-            }
-
-        }
-
-        apply_atlas(ad, _linearFilter, _clamp2edge);
-
-        for (std::vector<ResAnim*>::iterator i = anims.begin(); i != anims.end(); ++i)
-        {
-            ResAnim* rs = *i;
-            int num = rs->getTotalFrames();
-
-            for (int n = 0; n < num; ++n)
-            {
-                AnimationFrame& frame = const_cast<AnimationFrame&>(rs->getFrame(n));
-
-                float iw = 1.0f / frame.getDiffuse().base->getWidth();
-                float ih = 1.0f / frame.getDiffuse().base->getHeight();
-
-                RectF rect = frame.getSrcRect();
-                rect.pos.x *= iw;
-                rect.pos.y *= ih;
-                rect.size.x *= iw;
-                rect.size.y *= ih;
-                frame.setSrcRect(rect);
-
-                HitTestData ad = frame.getHitTestData();
-                if (ad.pitch)
-                {
-                    ad.data = &_hitTestBuffer[reinterpret_cast<size_t>(ad.data)];
-                    frame.setHitTestData(ad);
-                }
-            }
-        }
-    }
-
-
-    ResAtlasPrebuilt::ResAtlasPrebuilt(CreateResourceContext& context)
-    {
-        pugi::xml_node node = context.walker.getNode();
-        pugi::xml_node meta = context.walker.getMeta();
-
-        const char* format = node.attribute("format").as_string("8888");
-
-        loadBase(node);
-
-
-        TextureFormat tf = string2TextureFormat(format);
-
-        pugi::xml_node meta_image = meta.child("atlas");
-
-        pugi::xml_node meta_alpha = meta.child("ht");
-        if (meta_alpha)
-        {
-            const char* data = meta_alpha.text().as_string();
-            int len = meta_alpha.attribute("len").as_int();
-            base64_decodestate state;
-            base64_init_decodestate(&state);
-            _hitTestBuffer.resize(len * 3 / 4);
-            if (len)
-                base64_decode_block(data, len, (char*)&_hitTestBuffer.front(), &state);
-        }
-
-        bool compressed = false;
-
-        while (meta_image)
-        {
-            const char* file = meta_image.attribute("file").value();
-
-            int w = meta_image.attribute("w").as_int();
-            int h = meta_image.attribute("h").as_int();
-
-            const char* file_format = meta_image.attribute("format").as_string(0);
-            TextureFormat ffmt = TF_UNDEFINED;
-            if (file_format)
-            {
-                ffmt = string2TextureFormat(file_format);
-                compressed = isCompressedFormat(ffmt);
-            }
-
-            std::string alpha_file = meta_image.attribute("alpha").as_string("");
-            if (!alpha_file.empty())
-            {
-                alpha_file = *context.prebuilt_folder + alpha_file;
-            }
-
-            addAtlas(tf, *context.prebuilt_folder + file, alpha_file, w, h);
-
-            meta_image = meta_image.next_sibling("atlas");
-            context.walker.nextMeta();
-        }
-
-        while (true)
-        {
-            XmlWalker walker = context.walker.next();
-            if (walker.empty())
-                break;
-
-            pugi::xml_node child_node = walker.getNode();
-
-            const char* name = child_node.name();
-            if (strcmp(name, "image"))
-                continue;
-
-            pugi::xml_node meta_frames = walker.getMeta();
-
-            std::string id = child_node.attribute("id").value();
-            std::string file = child_node.attribute("file").value();
-
-            if (file.empty())
-            {
-                createEmpty(walker, context);
-                continue;
-            }
-
-
-            OX_ASSERT(meta_frames && "Did you recreate atlasses?");
-
-            int columns = 0;
-            int rows = 0;
-            int frame_width = 0;
-            int frame_height = 0;
-            float frame_scale = 1.0f;
-
-
-            const char* frame_size = meta_frames.attribute("fs").value();
-
-            sscanf(frame_size, "%d,%d,%d,%d,%f", &columns, &rows,
-                   &frame_width, &frame_height,
-                   &frame_scale);
-
-            HitTestData adata;
-            const char* alpha = meta_frames.attribute("ht").as_string(0);
-            if (alpha)
-            {
-                int ad_w, ad_h, ad_pos, ad_size;
-                sscanf(alpha, "%d,%d,%d,%d", &ad_pos, &ad_size, &ad_w, &ad_h);
-                adata.w = ad_w;
-                adata.h = ad_h;
-                adata.pitch = ad_size / ad_h;
-                adata.data = &_hitTestBuffer[ad_pos];
-            }
-
-
-
-            if (columns)
-            {
-                animationFrames frames;
-                int frames_count = rows * columns;
-                frames.reserve(frames_count);
-
-                ResAnim* ra = new ResAnim(this);
-
-
-                OX_ASSERT(meta_frames);
-
-
-                char* frames_data = (char*)meta_frames.first_child().value();
-
-
-                const char* begin = frames_data;
-                while (*frames_data)
-                {
-                    if (*frames_data == ';')
-                    {
-                        *frames_data = 0;
-                        int id = 0;
-                        int x = 0;
-                        int y = 0;
-
-                        int bbox_x = 0;
-                        int bbox_y = 0;
-                        int bbox_w = 0;
-                        int bbox_h = 0;
-
-                        sscanf(begin, "%d,%d,%d,%d,%d,%d,%d", &id, &x, &y, &bbox_x, &bbox_y, &bbox_w, &bbox_h);
-
-                        begin = frames_data + 1;
-
-                        spNativeTexture& texture = _atlasses[id].base;
-                        spNativeTexture& alpha = _atlasses[id].alpha;
-
-                        float iw = 1.0f / texture->getWidth();
-                        float ih = 1.0f / texture->getHeight();
-
-                        RectF srcRect(x * iw, y * ih, bbox_w * iw, bbox_h * ih);
-
-                        float fs = frame_scale;
-                        RectF destRect(
-                            Vector2((float)bbox_x, (float)bbox_y) * fs,
-                            Vector2((float)bbox_w, (float)bbox_h) * fs
-                        );
-
-                        AnimationFrame frame;
-                        Diffuse df;
-                        df.base = texture;
-                        df.alpha = alpha;
-
-                        //compressed data could not be premultiplied
-                        df.premultiplied = !compressed;
-
-                        size_t n = frames.size();
-                        int column = n % columns;
-                        int row = (int)(n / columns);
-
-                        frame.init2(ra, column, row, df,
-                                    srcRect, destRect,
-                                    Vector2((float)frame_width, (float)frame_height));
-                        if (adata.pitch)
-                        {
-                            frame.setHitTestData(adata);
-                            adata.data += adata.h * adata.pitch;
-                        }
-
-                        frames.push_back(frame);
-                        if ((int)frames.size() >= frames_count)
-                            break;
-                    }
-
-                    ++frames_data;
-                }
-
-                init_resAnim(ra, file, child_node);
-
-                ra->init(frames, columns, walker.getScaleFactor(), 1.0f / frame_scale);
-                ra->setParent(this);
-                context.resources->add(ra, context.options->_shortenIDS);
-            }
-        }
-    }
 }

+ 1 - 20
oxygine/src/res/ResAtlas.h

@@ -1,9 +1,8 @@
 #pragma once
 #include "oxygine_include.h"
-#include "core/file.h"
-#include <map>
 #include "Resource.h"
 #include "ResAnim.h"
+#include "CreateResourceContext.h"
 
 namespace oxygine
 {
@@ -58,22 +57,4 @@ namespace oxygine
 
     typedef void(*load_texture_hook)(const std::string& file, spNativeTexture nt, LoadResourcesContext* load_context);
     void set_load_texture_hook(load_texture_hook);
-
-
-    class ResAtlasGeneric : public ResAtlas
-    {
-    public:
-
-        //protected:
-        void loadAtlas(CreateResourceContext& context);
-    };
-
-    class ResAtlasPrebuilt : public ResAtlas
-    {
-    public:
-
-        ResAtlasPrebuilt(CreateResourceContext& context);
-
-    protected:
-    };
 }

+ 469 - 0
oxygine/src/res/ResAtlasGeneric.cpp

@@ -0,0 +1,469 @@
+#include "ResAtlasGeneric.h"
+#include "core/ImageDataOperations.h"
+#include "MemoryTexture.h"
+#include "utils/AtlasTool.h"
+#include "core/VideoDriver.h"
+#include "Resources.h"
+
+namespace oxygine
+{
+
+    int defaultAtlasWidth = 2048;
+    int defaultAtlasHeight = 2048;
+
+
+    struct atlas_data
+    {
+        spNativeTexture texture;
+        MemoryTexture mt;
+        Atlas2 atlas;
+    };
+
+
+
+    int roundUp(int numToRound, int multiple)
+    {
+        if (multiple == 0)
+            return numToRound;
+
+        int remainder = numToRound % multiple;
+        if (remainder == 0)
+            return numToRound;
+        return numToRound + multiple - remainder;
+    }
+
+
+    int HIT_TEST_DOWNSCALE = 4;
+    const int ALIGN = sizeof(int32_t);
+    const int BITS = ALIGN * 8;
+
+    void makeAlpha(const ImageData& srcImage, Rect& bounds, std::vector<unsigned char>& alpha, HitTestData& adata, bool hittest)
+    {
+        int w = srcImage.w;
+        int h = srcImage.h;
+
+        size_t pos = alpha.size();
+        adata.data = reinterpret_cast<unsigned char*>(pos);
+        adata.w = roundUp(w, HIT_TEST_DOWNSCALE) / HIT_TEST_DOWNSCALE;
+        adata.h = roundUp(h, HIT_TEST_DOWNSCALE) / HIT_TEST_DOWNSCALE;
+
+
+        int lineInts = roundUp(adata.w, BITS) / BITS;
+
+        int destPitch = lineInts * ALIGN;
+
+        int size = adata.h * destPitch;
+
+        alpha.resize(pos + size);
+
+
+        const unsigned char* srcData = srcImage.data;
+        int srcStep = srcImage.bytespp;
+        int srcPitch = srcImage.pitch;
+
+        unsigned char* destData = &alpha[pos];
+
+        adata.pitch = destPitch;
+
+        const unsigned char* srcRow = srcData;
+        unsigned char* destRow = destData;
+
+
+        int minX = w;
+        int minY = h;
+        int maxX = 0;
+        int maxY = 0;
+
+        bool hasAlpha = false;
+
+        for (int y = 0; y != h; y += 1)
+        {
+            const unsigned char* srcLine = srcRow;
+            int32_t* destLine = reinterpret_cast<int32_t*>(destRow);
+
+            bool lineWithAlpha = false;
+
+
+            for (int x = 0; x != w; x += 1)
+            {
+                PixelR8G8B8A8 pd;
+                Pixel p;
+                pd.getPixel(srcLine, p);
+
+
+                if (p.a > 5)
+                {
+                    hasAlpha = true;
+
+                    int dx = x / HIT_TEST_DOWNSCALE;
+                    int n = dx / BITS;
+                    int b = dx % BITS;
+
+                    destLine[n] |= 1 << b;
+
+                    lineWithAlpha = true;
+                    if (x > maxX)
+                        maxX = x;
+                    else if (x < minX)
+                        minX = x;
+                }
+                srcLine += srcStep;
+            }
+
+            if (lineWithAlpha)
+            {
+                if (minY == h)
+                    minY = y;
+                maxY = y;
+            }
+
+            if (y % HIT_TEST_DOWNSCALE == HIT_TEST_DOWNSCALE - 1)
+            {
+                //reset line
+                destRow += destPitch;
+            }
+
+            srcRow += srcPitch;
+        }
+
+        //if image is transparent
+        if (minX == w && maxX == 0)
+        {
+            minX = 0;
+            maxX = 0;
+        }
+
+        if (minY == h && maxY == 0)
+        {
+            minY = 0;
+            maxY = 0;
+        }
+
+        bounds = Rect(minX, minY, maxX - minX + 1, maxY - minY + 1);
+
+        if (!hasAlpha || !hittest)
+        {
+            alpha.resize(pos);
+            adata = HitTestData();
+        }
+    }
+
+
+
+
+    void ResAtlasGeneric::applyAtlas(atlas_data& ad, bool linear, bool clamp2edge)
+    {
+        if (!ad.texture)
+            return;
+
+        spMemoryTexture mt = new MemoryTexture;
+        Rect bounds = ad.atlas.getBounds();
+
+        int w = nextPOT(bounds.getRight());
+        int h = nextPOT(bounds.getBottom());
+
+        mt->init(ad.mt.lock().getRect(Rect(0, 0, w, h)));
+#if 0
+        static int n = 0;
+        n++;
+        char name[255];
+        safe_sprintf(name, "test%d.tga", n);
+        saveImage(image_data, name);
+#endif
+
+        CreateTextureTask task;
+        task.linearFilter = linear;
+        task.clamp2edge = clamp2edge;
+        task.src = mt;
+        task.dest = ad.texture;
+        LoadResourcesContext::get()->createTexture(task);
+    }
+
+    void ResAtlasGeneric::nextAtlas(int w, int h, TextureFormat tf, atlas_data& ad, const char* name)
+    {
+        ad.mt.init(w, h, tf);
+        ad.mt.fill_zero();
+
+        ad.atlas.clean();
+        ad.atlas.init(w, h);
+
+
+        if (_atlasses.size() > _current)
+            ad.texture = _atlasses[_current].base;
+        else
+        {
+            ad.texture = IVideoDriver::instance->createTexture();
+
+            atlas atl;
+            atl.base = ad.texture;
+            _atlasses.push_back(atl);
+        }
+        ad.texture->setName(name);
+
+        _current++;
+    }
+
+
+    void ResAtlasGeneric::_unload()
+    {
+    }
+
+    void ResAtlasGeneric::_load(LoadResourcesContext*)
+    {
+        //CreateResourceContext copy2 = _copy;
+        //loadAtlas2(copy2);
+    }
+
+    void ResAtlasGeneric::loadAtlas(CreateResourceContext& context)
+    {
+        //_xmlFolder = context.walker.getXmlFolder();
+        //_copy = context;
+        //_copy.walker.setXmlFolder(&_xmlFolder);
+        loadAtlas2(context);
+    }
+
+    void ResAtlasGeneric::loadAtlas2(CreateResourceContext& context)
+    {
+        _current = 0;
+        pugi::xml_node node = context.walker.getNode();
+
+        int w = node.attribute("width").as_int(defaultAtlasWidth);
+        int h = node.attribute("height").as_int(defaultAtlasHeight);
+
+        const char* format = node.attribute("format").as_string("8888");
+
+        loadBase(node);
+
+        atlas_data ad;
+
+
+        TextureFormat tf = string2TextureFormat(format);
+
+
+        bool compressed = false;
+
+
+        std::vector<ResAnim*> anims;
+
+
+        while (true)
+        {
+            XmlWalker walker = context.walker.next();
+            if (walker.empty())
+                break;
+
+            pugi::xml_node child_node = walker.getNode();
+
+            const char* name = child_node.name();
+            if (strcmp(name, "image"))
+                continue;
+
+
+            std::string id = child_node.attribute("id").value();
+            std::string file = child_node.attribute("file").value();
+
+            if (file.empty())
+            {
+                createEmpty(walker, context);
+                continue;
+            }
+
+
+            bool trim = child_node.attribute("trim").as_bool(true);
+            bool extend = child_node.attribute("extend").as_bool(true);
+
+            Point offset = extend ? Point(2, 2) : Point(0, 0);
+
+            MemoryTexture mt;
+            ImageData im;
+
+            int columns = 0;
+            int rows = 0;
+            int frame_width = 0;
+            int frame_height = 0;
+            float frame_scale = 1.0f;
+            bool loaded = false;
+
+
+            file::buffer bf;
+            file::read(walker.getPath("file").c_str(), bf);
+
+            mt.init(bf, true, tf);
+            im = mt.lock();
+            if (im.w)
+            {
+                rows = child_node.attribute("rows").as_int();
+                frame_width = child_node.attribute("frame_width").as_int();
+                columns = child_node.attribute("cols").as_int();
+                frame_height = child_node.attribute("frame_height").as_int();
+
+                if (!rows)
+                    rows = 1;
+
+                if (!columns)
+                    columns = 1;
+
+                if (frame_width)
+                    columns = im.w / frame_width;
+                else
+                    frame_width = im.w / columns;
+
+
+                if (frame_height)
+                    rows = im.h / frame_height;
+                else
+                    frame_height = im.h / rows;
+            }
+
+
+            if (columns)
+            {
+                animationFrames frames;
+                int frames_count = rows * columns;
+                frames.reserve(frames_count);
+
+                ResAnim* ra = new ResAnim(this);
+
+
+                anims.push_back(ra);
+
+                for (int y = 0; y < rows; ++y)
+                {
+                    for (int x = 0; x < columns; ++x)
+                    {
+                        Rect frameRect;
+                        frameRect.pos = Point(x * frame_width, y * frame_height);
+                        frameRect.size = Point(frame_width, frame_height);
+
+                        ImageData srcImage_ = im.getRect(frameRect);
+
+
+                        HitTestData adata;
+                        ImageData src;
+                        Rect bounds(0, 0, im.w, im.h);
+                        if (trim)
+                            makeAlpha(srcImage_, bounds, _hitTestBuffer, adata, walker.getAlphaHitTest());
+                        src = srcImage_.getRect(bounds);
+
+                        Rect dest(0, 0, 0, 0);
+
+                        if (!ad.texture)
+                        {
+                            std::string atlas_id = getName();
+                            nextAtlas(w, h, tf, ad, atlas_id.c_str());
+                        }
+
+                        bool s = ad.atlas.add(&ad.mt, src, dest, offset);
+                        if (s == false)
+                        {
+                            applyAtlas(ad, _linearFilter, _clamp2edge);
+
+                            nextAtlas(w, h, tf, ad, walker.getCurrentFolder().c_str());
+                            s = ad.atlas.add(&ad.mt, src, dest, offset);
+                            OX_ASSERT(s);
+                        }
+
+                        //extend = false;
+                        if (extend)
+                        {
+                            //duplicate image edges
+                            MemoryTexture& mt = ad.mt;
+                            ImageData tmp;
+
+                            if (bounds.getY() == 0)
+                            {
+                                tmp = mt.lock(Rect(dest.pos.x, dest.pos.y - 1, src.w, 1));
+                                operations::copy(src.getRect(Rect(0, 0, src.w, 1)), tmp);
+                            }
+
+                            if (bounds.getHeight() == im.h)
+                            {
+                                tmp = mt.lock(Rect(dest.pos.x, dest.pos.y + src.h, src.w, 1));
+                                operations::copy(src.getRect(Rect(0, src.h - 1, src.w, 1)), tmp);
+                            }
+
+                            if (bounds.getX() == 0)
+                            {
+                                tmp = mt.lock(Rect(dest.pos.x - 1, dest.pos.y, 1, src.h));
+                                operations::copy(src.getRect(Rect(0, 0, 1, src.h)), tmp);
+                            }
+
+                            if (bounds.getWidth() == im.w)
+                            {
+                                tmp = mt.lock(Rect(dest.pos.x + src.w, dest.pos.y, 1, src.h));
+                                operations::copy(src.getRect(Rect(src.w - 1, 0, 1, src.h)), tmp);
+                            }
+                        }
+
+
+                        //operations::copy(src.getRect(Rect(0, 0, 1, 1)), mt.lock(&Rect(dest.pos.x - 1, dest.pos.y - 1, 1, 1)));
+                        //operations::copy(src.getRect(Rect(src.w - 1, 0, 1, 1)), mt.lock(&Rect(dest.pos.x + src.w, dest.pos.y - 1, 1, 1)));
+
+                        //operations::copy(src.getRect(Rect(0, src.h - 1, 1, 1)), mt.lock(&Rect(dest.pos.x - 1, dest.pos.y + src.h, 1, 1)));
+                        //operations::copy(src.getRect(Rect(src.w - 1, src.h - 1, 1, 1)), mt.lock(&Rect(dest.pos.x + src.w, dest.pos.y + src.h, 1, 1)));
+
+
+                        float iw = 1.0f;
+                        float ih = 1.0f;
+
+                        RectF srcRect(dest.pos.x * iw, dest.pos.y * ih, dest.size.x * iw, dest.size.y * ih);
+
+                        Vector2 sizeScaled = Vector2((float)dest.size.x, (float)dest.size.y) * walker.getScaleFactor();
+                        RectF destRect(bounds.pos.cast<Vector2>(), sizeScaled);
+
+                        AnimationFrame frame;
+                        Diffuse df;
+                        df.base = ad.texture;
+                        df.premultiplied = true;//!Renderer::getPremultipliedAlphaRender();
+
+                        Vector2 fsize = Vector2((float)frame_width, (float)frame_height) * walker.getScaleFactor();
+                        frame.init2(ra, x, y, df,
+                                    srcRect, destRect, fsize);
+
+                        frame.setHitTestData(adata);
+
+                        frames.push_back(frame);
+                    }
+                }
+
+                init_resAnim(ra, file, child_node);
+
+                ra->init(frames, columns, walker.getScaleFactor(), 1.0f / walker.getScaleFactor());
+                ra->setParent(this);
+                context.resources->add(ra, context.options->_shortenIDS);
+            }
+
+        }
+
+        applyAtlas(ad, _linearFilter, _clamp2edge);
+
+        for (std::vector<ResAnim*>::iterator i = anims.begin(); i != anims.end(); ++i)
+        {
+            ResAnim* rs = *i;
+            int num = rs->getTotalFrames();
+
+            for (int n = 0; n < num; ++n)
+            {
+                AnimationFrame& frame = const_cast<AnimationFrame&>(rs->getFrame(n));
+
+                float iw = 1.0f / frame.getDiffuse().base->getWidth();
+                float ih = 1.0f / frame.getDiffuse().base->getHeight();
+
+                RectF rect = frame.getSrcRect();
+                rect.pos.x *= iw;
+                rect.pos.y *= ih;
+                rect.size.x *= iw;
+                rect.size.y *= ih;
+                frame.setSrcRect(rect);
+
+                HitTestData ad = frame.getHitTestData();
+                if (ad.pitch)
+                {
+                    ad.data = &_hitTestBuffer[reinterpret_cast<size_t>(ad.data)];
+                    frame.setHitTestData(ad);
+                }
+            }
+        }
+    }
+
+}

+ 27 - 0
oxygine/src/res/ResAtlasGeneric.h

@@ -0,0 +1,27 @@
+#pragma once
+#include "ResAtlas.h"
+namespace oxygine
+{
+
+    struct atlas_data;
+
+    class ResAtlasGeneric : public ResAtlas
+    {
+    public:
+
+        int _current;
+
+        //CreateResourceContext _copy;
+
+        //std::string _xmlFolder;
+
+        void nextAtlas(int w, int h, TextureFormat tf, atlas_data& ad, const char* name);
+        void applyAtlas(atlas_data& ad, bool linear, bool clamp2edge);
+
+        void loadAtlas(CreateResourceContext& context);
+        void loadAtlas2(CreateResourceContext& context);
+
+        void _load(LoadResourcesContext*) OVERRIDE;
+        void _unload() OVERRIDE;
+    };
+}

+ 205 - 0
oxygine/src/res/ResAtlasPrebuilt.cpp

@@ -0,0 +1,205 @@
+#include "ResAtlasPrebuilt.h"
+#include "Resources.h"
+
+extern "C"
+{
+#include "utils/cdecode.h"
+}
+
+namespace oxygine
+{
+
+    ResAtlasPrebuilt::ResAtlasPrebuilt(CreateResourceContext& context)
+    {
+        pugi::xml_node node = context.walker.getNode();
+        pugi::xml_node meta = context.walker.getMeta();
+
+        const char* format = node.attribute("format").as_string("8888");
+
+        loadBase(node);
+
+
+        TextureFormat tf = string2TextureFormat(format);
+
+        pugi::xml_node meta_image = meta.child("atlas");
+
+        pugi::xml_node meta_alpha = meta.child("ht");
+        if (meta_alpha)
+        {
+            const char* data = meta_alpha.text().as_string();
+            int len = meta_alpha.attribute("len").as_int();
+            base64_decodestate state;
+            base64_init_decodestate(&state);
+            _hitTestBuffer.resize(len * 3 / 4);
+            if (len)
+                base64_decode_block(data, len, (char*)&_hitTestBuffer.front(), &state);
+        }
+
+        bool compressed = false;
+
+        while (meta_image)
+        {
+            const char* file = meta_image.attribute("file").value();
+
+            int w = meta_image.attribute("w").as_int();
+            int h = meta_image.attribute("h").as_int();
+
+            const char* file_format = meta_image.attribute("format").as_string(0);
+            TextureFormat ffmt = TF_UNDEFINED;
+            if (file_format)
+            {
+                ffmt = string2TextureFormat(file_format);
+                compressed = isCompressedFormat(ffmt);
+            }
+
+            std::string alpha_file = meta_image.attribute("alpha").as_string("");
+            if (!alpha_file.empty())
+            {
+                alpha_file = *context.prebuilt_folder + alpha_file;
+            }
+
+            addAtlas(tf, *context.prebuilt_folder + file, alpha_file, w, h);
+
+            meta_image = meta_image.next_sibling("atlas");
+            context.walker.nextMeta();
+        }
+
+        while (true)
+        {
+            XmlWalker walker = context.walker.next();
+            if (walker.empty())
+                break;
+
+            pugi::xml_node child_node = walker.getNode();
+
+            const char* name = child_node.name();
+            if (strcmp(name, "image"))
+                continue;
+
+            pugi::xml_node meta_frames = walker.getMeta();
+
+            std::string id = child_node.attribute("id").value();
+            std::string file = child_node.attribute("file").value();
+
+            if (file.empty())
+            {
+                createEmpty(walker, context);
+                continue;
+            }
+
+
+            OX_ASSERT(meta_frames && "Did you recreate atlasses?");
+
+            int columns = 0;
+            int rows = 0;
+            int frame_width = 0;
+            int frame_height = 0;
+            float frame_scale = 1.0f;
+
+
+            const char* frame_size = meta_frames.attribute("fs").value();
+
+            sscanf(frame_size, "%d,%d,%d,%d,%f", &columns, &rows,
+                   &frame_width, &frame_height,
+                   &frame_scale);
+
+            HitTestData adata;
+            const char* alpha = meta_frames.attribute("ht").as_string(0);
+            if (alpha)
+            {
+                int ad_w, ad_h, ad_pos, ad_size;
+                sscanf(alpha, "%d,%d,%d,%d", &ad_pos, &ad_size, &ad_w, &ad_h);
+                adata.w = ad_w;
+                adata.h = ad_h;
+                adata.pitch = ad_size / ad_h;
+                adata.data = &_hitTestBuffer[ad_pos];
+            }
+
+
+
+            if (columns)
+            {
+                animationFrames frames;
+                int frames_count = rows * columns;
+                frames.reserve(frames_count);
+
+                ResAnim* ra = new ResAnim(this);
+
+
+                OX_ASSERT(meta_frames);
+
+
+                char* frames_data = (char*)meta_frames.first_child().value();
+
+
+                const char* begin = frames_data;
+                while (*frames_data)
+                {
+                    if (*frames_data == ';')
+                    {
+                        *frames_data = 0;
+                        int id = 0;
+                        int x = 0;
+                        int y = 0;
+
+                        int bbox_x = 0;
+                        int bbox_y = 0;
+                        int bbox_w = 0;
+                        int bbox_h = 0;
+
+                        sscanf(begin, "%d,%d,%d,%d,%d,%d,%d", &id, &x, &y, &bbox_x, &bbox_y, &bbox_w, &bbox_h);
+
+                        begin = frames_data + 1;
+
+                        spNativeTexture& texture = _atlasses[id].base;
+                        spNativeTexture& alpha = _atlasses[id].alpha;
+
+                        float iw = 1.0f / texture->getWidth();
+                        float ih = 1.0f / texture->getHeight();
+
+                        RectF srcRect(x * iw, y * ih, bbox_w * iw, bbox_h * ih);
+
+                        float fs = frame_scale;
+                        RectF destRect(
+                            Vector2((float)bbox_x, (float)bbox_y) * fs,
+                            Vector2((float)bbox_w, (float)bbox_h) * fs
+                        );
+
+                        AnimationFrame frame;
+                        Diffuse df;
+                        df.base = texture;
+                        df.alpha = alpha;
+
+                        //compressed data could not be premultiplied
+                        df.premultiplied = !compressed;
+
+                        size_t n = frames.size();
+                        int column = n % columns;
+                        int row = (int)(n / columns);
+
+                        frame.init2(ra, column, row, df,
+                                    srcRect, destRect,
+                                    Vector2((float)frame_width, (float)frame_height));
+                        if (adata.pitch)
+                        {
+                            frame.setHitTestData(adata);
+                            adata.data += adata.h * adata.pitch;
+                        }
+
+                        frames.push_back(frame);
+                        if ((int)frames.size() >= frames_count)
+                            break;
+                    }
+
+                    ++frames_data;
+                }
+
+                init_resAnim(ra, file, child_node);
+
+                ra->init(frames, columns, walker.getScaleFactor(), 1.0f / frame_scale);
+                ra->setParent(this);
+                context.resources->add(ra, context.options->_shortenIDS);
+            }
+        }
+    }
+}

+ 14 - 0
oxygine/src/res/ResAtlasPrebuilt.h

@@ -0,0 +1,14 @@
+#pragma once
+#include "ResAtlas.h"
+namespace oxygine
+{
+
+    class ResAtlasPrebuilt : public ResAtlas
+    {
+    public:
+
+        ResAtlasPrebuilt(CreateResourceContext& context);
+
+    protected:
+    };
+}

+ 4 - 4
oxygine/src/res/Resources.cpp

@@ -166,10 +166,10 @@ namespace oxygine
         }
     };
 
-	void Resources::load(const std::string& xmlFile, const ResourcesLoadOptions& opt)
-	{
-		loadXML2(xmlFile, opt);
-	}
+    void Resources::load(const std::string& xmlFile, const ResourcesLoadOptions& opt)
+    {
+        loadXML2(xmlFile, opt);
+    }
 
     void Resources::loadXML2(const std::string& xmlFile, const ResourcesLoadOptions& opt)
     {

+ 9 - 10
oxygine/src/res/Resources.h

@@ -32,14 +32,14 @@ namespace oxygine
     public:
         ResourcesLoadOptions() : _loadCompletely(true), _useLoadCounter(false), _shortenIDS(false) {};
 
-		//load only Resources definitions. Skips internal heavy data (atlasses/textures/buffers). Could be overridden in xml: <your_res_type ... load = "false"/>
-		ResourcesLoadOptions& dontLoadAll(bool v = false) { _loadCompletely = v; return *this; }
+        //load only Resources definitions. Skips internal heavy data (atlasses/textures/buffers). Could be overridden in xml: <your_res_type ... load = "false"/>
+        ResourcesLoadOptions& dontLoadAll(bool v = false) { _loadCompletely = v; return *this; }
 
-		//use load counter internally
-		ResourcesLoadOptions& useLoadCounter(bool v = true) { _useLoadCounter = v; return *this; }
+        //use load counter internally
+        ResourcesLoadOptions& useLoadCounter(bool v = true) { _useLoadCounter = v; return *this; }
 
-		//use not standard folder with prebuilt resources (atlasses, fonts, etc)
-		ResourcesLoadOptions& prebuiltFolder(const std::string &folder) {_prebuilFolder = folder; return *this; }
+        //use not standard folder with prebuilt resources (atlasses, fonts, etc)
+        ResourcesLoadOptions& prebuiltFolder(const std::string& folder) {_prebuilFolder = folder; return *this; }
 
 
         bool _loadCompletely;
@@ -74,7 +74,6 @@ namespace oxygine
         @param use load counter internally
         @param use not standard folder with prebuilt resources (atlasses, fonts, etc)
         */
-		OXYGINE_DEPRECATED
         void loadXML(const std::string& file, LoadResourcesContext* load_context = 0,
                      bool load_completely = true, bool use_load_counter = false,
                      const std::string& prebuilt_folder = "");
@@ -86,9 +85,9 @@ namespace oxygine
         */
         void loadXML2(const std::string& xmlFile, const ResourcesLoadOptions& opt = ResourcesLoadOptions());
 
-		//use loadXML2, later it will be renamed to loadXML
-		OXYGINE_DEPRECATED
-		void load(const std::string& xmlFile, const ResourcesLoadOptions& opt = ResourcesLoadOptions());
+        //use loadXML2, later it will be renamed to loadXML
+        OXYGINE_DEPRECATED
+        void load(const std::string& xmlFile, const ResourcesLoadOptions& opt = ResourcesLoadOptions());
 
         /**Adds Resource*/
         void add(Resource* r, bool accessByShortenID = false);