<|start_filename|>deps/packers/TexturePacker.h<|end_filename|>
#pragma once

/*!
**
** Copyright (c) 2009 by <NAME> mailto:<EMAIL>
**
** The MIT license:
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is furnished
** to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in all
** copies or substantial portions of the Software.

** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/


// This code snippet will efficiently pack a collection of rectangles into a single larger enclosing rectangle.
// Typically used for packing many textures into a single texture.
//

// Here is how you use it.

// First create an instance of the TexturePacker interface.

// TEXTURE_PACKER::TexturePacker *tp = TEXTURE_PACKER::createTexturePacker();
//
// Next inform the system how many textures you want to pack.
//
// tp->setTextureCount(10);
//
// Now, add each texture's width and height in sequence 1-10
//
//  tp->addTexture(wid,hit)
//
// Next you pack them .
//
// int unused_area = tp->packTextures(width,height,true,true)
//
// The return code is the unused surface area.  It also returns the width and height of the rectangle needed to contain all textures.
// If you want to force your output texture to be a power of two, set that option to true.
// If you want a one pixel border around your textures set that parameter to true.
//
// Finally, to retrieve the results, for each texture 0-(n-1) call 'getTextureLocation'.
//
// This will return the x,y position of the texture in the texture atlas, and the width and height (should be the same as the original)
// Now, this is very important, if the getTextureLocation returns true, it means that the texture was rotated 90 degrees to make it
// better fit into the available space.  You should rotate your U/V co-ordinates accordingly.
//
// The algorithm for packing textures as as follows:
//
// Step #1 : Find the longest edge and total area of all source textures.
// Step #2 : Create a single large rectangle big enough to fit all textures; round up to the nearest power of two if necessary.
// Step #3 : Place each texture, first by finding the texture with the largest area and longest edge that has not yet been placed.
// Step #4 :  .. Look through the free nodes list and if a free node is exactly the same size, then use it.
//               Otherwise, find a free node that is furthest down and to the left of the co-ordinate space (growing up and too the right)
//               Always insert the node so that the long edge lays down, preventing as much as possible the texture-atlas from growing vertically.
//               If a placed node has been rotated 90 degrees (width/height swapped) flag it as so.
// Step #5 : If the texture we are inserting shares an edge with the free node, then simply shrink the free node down to make up the difference.
// Step #6 : If the texture doesn't share any edge then split the rectangle in two, allocating a new free node and adding to the node list.
// Step #7 : See if any nodes can be combined back into one; do this until no more rectangles can be collapsed.
// Step #8 : Repeat until all textures have been inserted.
// Step #9 : Find the maximum height we ended up using and return that as the actual height.  Clamp the height to the nearest power of two if needed.
// Step #10 : Iterate through the results and copy your textures to a single large texture-atlas.
//              This code does not do the image copying, that is done by your own application.

namespace TEXTURE_PACKER
{

class TexturePacker
{
public:
  virtual void  setTextureCount(int tcount) = 0; // number of textures to consider..
  virtual void  addTexture(int wid,int hit) = 0; // add textures 0 - n

  virtual int packTextures(int &width,int &height,bool forcePowerOfTwo,bool onePixelBorder) = 0;  // pack the textures, the return code is the amount of wasted/unused area.

  virtual bool  getTextureLocation(int index,int &x,int &y,int &wid,int &hit) = 0; // returns true if the texture has been rotated 90 degrees

};


TexturePacker * createTexturePacker(void);
void            releaseTexturePacker(TexturePacker *tp);

}; // end the texture packer namespace



<|start_filename|>deps/v1/make.bat<|end_filename|>
pushd .
cd ..\dot\redist
call make
popd
cl *.cpp -I .. ..\dot\dot*.c* /Ox /Oy /DNDEBUG /MT /link setargv.obj 
del kk.dds
atlas -r -o kk.dds images
kk.dds


<|start_filename|>attila.cc<|end_filename|>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
#include <deque>

#define ATTILA_VERSION "1.1.0" /* (2018/11/17): fix uv0/uv1 coords; add c mode; update libspot
#define ATTILA_VERSION "1.0.9" // (2016/02/24): fix extra unused area bug; fix minimum width bug
#define ATTILA_VERSION "1.0.8" // (2016/02/08): pump up libspot
#define ATTILA_VERSION "1.0.7" // (2015/09/28): faster image pasting
#define ATTILA_VERSION "1.0.6" // (2015/07/30): minimum width option
#define ATTILA_VERSION "1.0.5" // (2015/05/11): pump up libspot
#define ATTILA_VERSION "1.0.4" // (2015/04/09): enable mipmap generation
#define ATTILA_VERSION "1.0.3" // (2015/02/12): bugfixed error while handling @filelists
#define ATTILA_VERSION "1.0.2" // (2015/02/11): upgraded to latest spot lib
#define ATTILA_VERSION "1.0.1" // (2015/02/09): options, including image cropping and padding
#define ATTILA_VERSION "1.0.0" // (2015/02/06): initial version */

#include "deps/spot/spot.hpp"
#include "deps/packers/TexturePacker.h"
#include "deps/packers/MaxRectsBinPack.h"

#ifdef ATTILA_USE_OPENMP
#   include <omp.h>
    static float now() {
        return float( omp_get_wtime() );
    }
#else
#   include <chrono>
    static float now() {
        static auto epoch = std::chrono::steady_clock::now();
        return float( std::chrono::duration_cast< std::chrono::milliseconds >( std::chrono::steady_clock::now() - epoch ).count() / 1000.0 );
    }
#endif

namespace options { 
    bool        ENABLE_CROPPING      = false;
    bool        ENABLE_POT           = false;
    bool        ENABLE_EDGE          = false;
    bool        ENABLE_BLEEDING      = false;
    bool        ENABLE_MIPMAPS       = false;
    bool        ENABLE_C_OUTPUT      = false;
    unsigned    ENABLE_MINIMUM_WIDTH = 0;
};

spot::image crop( const spot::image &src, unsigned *left = 0, unsigned *right = 0, unsigned *top = 0, unsigned *bottom = 0 ) {
    unsigned width = src.w;
    unsigned height = src.h;
    unsigned x0 = ~0, y0 = ~0, x1 = 0, y1 = 0;
    for( unsigned y = 0; y < height; y++ ) {
        for( unsigned x = 0; x < width; x++ ) {
            if( src.at(x,y).a != 0) {
                if( x < x0 ) x0 = x;
                if( x > x1 ) x1 = x;
                if( y < y0 ) y0 = y;
                if( y > y1 ) y1 = y;
            }
        }
    }
    if( left   ) *left = x0;
    if( top    ) *top = y0;
    if( right  ) *right = width - 1 - x1;
    if( bottom ) *bottom = height - 1 - y1;
    return src.copy(x0, y0, x1-x0, y1-y0);
}

spot::image &halve( spot::image &img, unsigned factor = 2 ) {
    if( factor >= 2 ) {
        for( unsigned y = 0, h = img.h / factor; y < h; ++y ) {
            for( unsigned x = 0, w = img.w / factor; x < w; ++x ) {
                img[x + y * w] = img[x * factor + y * factor * img.w];
            }
        }
        img.w /= factor;
        img.h /= factor;
        img.resize( img.w * img.h );
    }
    return img;
}

spot::image build_mipmaps( spot::image img, unsigned maxlevels = 0 ) {
    auto edge = img.w, bottom = img.h, left = edge - edge, top = bottom - bottom;
    spot::image cpy( edge + edge / 2, bottom );
    cpy = cpy.paste( left, top, img );
    do {
        img = halve( img );
        cpy = cpy.paste( edge, top, img );
        top += img.h;
    } while( img.w > 1 && img.h > 1 );
    return cpy;
}

std::string normalize( std::string filename ) {
    for( auto &ch : filename ) {
        if( ch >= 'A' && ch <= 'Z' ) {
            ch = ch - 'A' + 'a';
        }
        else
        if( ch == '\\' ) {
            ch = '/';
        }
    }
    return filename;
}

struct texture {
    std::string src, dst;

    float w, h, x, y, u0, v0, u1, v1;
    float left, right, top, bottom;
    unsigned rotate;
    spot::image img;

    texture() :
    w(0), h(0), x(0), y(0), 
    u0(0), v0(0), u1(1), v1(1),
    left(0), right(0), top(0), bottom(0),
    rotate(0)
    {}

    bool load( const std::string &pathfile ) {
        //reset
        *this = texture();

        img.load(pathfile);
        if( img.empty() )
            return false;

        src = pathfile;
        w = img.w;
        h = img.h;

        if( options::ENABLE_CROPPING ) {
            // padding enabled. crop input image: shrink to fit blank pixels
            unsigned l, r, t, b;
            spot::image cropped = crop( img, &l, &r, &t, &b );
            if( !cropped.empty() ) {
                left = l;
                right = r;
                top = t;
                bottom = b;
                img = cropped;
                w = img.w;
                h = img.h;
            }
        }

        return true;
    }

    std::string json( const std::string &tab = std::string() ) const {
        std::stringstream ss;
        ss << tab << "\"" << normalize(src) << "\": {" << std::endl;
        ss << tab << tab << "\"src\": \"" << normalize(src) << "\", \"dst\": \"" << normalize(dst) << "\"," << std::endl;
        ss << tab << tab << "\"x\": " << x << ", \"y\": " << y << ", \"w\": " << w << ", \"h\": " << h << "," << std::endl;
        ss << tab << tab << "\"u0\": " << u0 << ", \"v0\": " << v0 << ", \"u1\": " << u1 << ", \"v1\": " << v1 << "," << std::endl;
        if( options::ENABLE_CROPPING ) {
        ss << tab << tab << "\"crop-left\": " << left << ", \"crop-right\": " << right << ", \"crop-top\": " << top << ", \"crop-bottom\": " << bottom << "," << std::endl;            
        }
        ss << tab << tab << "\"rotate\": " << rotate << ", \"hash\": " << std::hash<std::string>()(normalize(src)) << std::endl;
        ss << tab << "}";
        return ss.str();
    }

    std::string c( const std::string &tab = std::string() ) const {
        std::stringstream ss;
        ss << tab << "{";
        ss << "\"" << normalize(src) << "\", ";
        ss << x << "," << y << ", " << w << "," << h << ", ";
        ss << u0 << "," << v0 << ", " << u1 << "," << v1 << ", ";
        ss << rotate << ", " << std::hash<std::string>()(normalize(src));
        if( options::ENABLE_CROPPING ) {
        ss << ", " << left << ", " << right << ", " << top << ", " << bottom;
        }
        ss << "}," << std::endl;
        return ss.str();
    }
};

// taken from https://github.com/r-lyeh/wire
std::deque<std::string> split( std::istream &is, const std::string &delimiters ) {
    std::string map( 256, '\0' );
    for( const unsigned char &ch : delimiters )
        map[ ch ] = '\1';
    std::deque<std::string> tokens(1);
    std::stringstream ss;
    ss << is.rdbuf();
    for( const unsigned char &ch : ss.str() ) {
        /**/ if( !map.at(ch)          ) tokens.back().push_back( char(ch) );
        else if( tokens.back().size() ) tokens.push_back( std::string() );
    }
    while( tokens.size() && !tokens.back().size() ) tokens.pop_back();
    return tokens;
}


int help( const char **argv ) {
    std::cerr << std::string() + argv[0] + " v" ATTILA_VERSION " (libspot v" SPOT_VERSION ") - lightweight atlas texture-packer - https://github.com/r-lyeh/attila\n\n";
    std::cerr << std::string() + "Usage:\n";
    std::cerr << std::string() + "\t" + argv[0] + " [options] output.img input.img [input2.img [...]]\n";
    std::cerr << std::string() + "\t" + argv[0] + " [options] output.img @imagelist.txt [@imagelist2.txt [...]]\n\n";
    std::cerr << std::string() + "Options:\n";
    std::cerr << std::string() + "\t--help                 prints help\n";
    std::cerr << std::string() + "\t--enable-bleeding      enables output alpha bleeding (default: disabled)\n";
    std::cerr << std::string() + "\t--enable-cropping      enables input alpha cropping (default: disabled)\n";
    std::cerr << std::string() + "\t--enable-edge          enables output blank pixel separator (default: disabled)\n";
    std::cerr << std::string() + "\t--enable-pot           enables output power-of-two texture (default: disabled)\n";
    std::cerr << std::string() + "\t--enable-mipmaps       enables mipmaps (default: disabled)\n";
    std::cerr << std::string() + "\t--enable-c             enables flat c output instead of json\n";
    std::cerr << std::string() + "\t--enable-width WIDTH   enables minimum fixed width (in pixels) (default: 0)\n\n";
    std::cerr << "Notes:\n\tA JSON table of contents (toc) is written to stdout. Redirect it to a file if needed.\n\n";

    std::string sep1 = "\tInput image formats: ";
    for( auto &fmt : spot::list_supported_inputs() ) {
        std::cerr << sep1 << fmt;
        sep1 = ", ";
    }
    std::cerr << std::endl;

    std::string sep2 = "\tOutput image formats: ";
    for( auto &fmt : spot::list_supported_outputs() ) {
        std::cerr << sep2 << fmt;
        sep2 = ", ";
    }
    std::cerr << std::endl;

    return -1;    
}

std::deque<std::string> list;
std::deque<texture> textures;

int attila( int argc, const char **argv ) {
    auto start = now();

    while( --argc > 0 ) {
        list.push_back(argv[argc]);
    }

    for( unsigned i = 0; i < list.size(); ) {
        const std::string filename = list[i];

        /**/ if( filename == "--enable-cropping") {
            list.erase( list.begin() + i );
            options::ENABLE_CROPPING = true;
        }
        else if( filename == "--enable-edge") {
            list.erase( list.begin() + i );
            options::ENABLE_EDGE = true;
        }
        else if( filename == "--enable-pot") {
            list.erase( list.begin() + i );
            options::ENABLE_POT = true;
        }
        else if( filename == "--enable-bleeding") {
            list.erase( list.begin() + i );
            options::ENABLE_BLEEDING = true;
        }
        else if( filename == "--enable-mipmaps") {
            list.erase( list.begin() + i );
            options::ENABLE_MIPMAPS = true;
        }
        else if( filename == "--enable-c") {
            list.erase( list.begin() + i );
            options::ENABLE_C_OUTPUT = true;
        }
        else if( filename == "--enable-width") {
            list.erase( list.begin() + i );
            if( i-1 >= 0 && i-1 < list.size() ) {
                options::ENABLE_MINIMUM_WIDTH = std::strtoul( (list.begin() + --i)->c_str(), 0, 0 );
                list.erase( list.begin() + i );
            }
        }
        else if( filename == "--help" ) {
            return help( argv );
        }
        else if( filename[0] == '@' ) {
            std::ifstream ifs( &filename[1] );
            list.erase( list.begin() + i );
            if( ifs.good() ) {
                std::deque<std::string> lines = split(ifs,"\t\f\v\r\n");
                for( unsigned i = 0; i < lines.size(); ++i ) {
                    list.push_front( lines[i] );
                }
            }
        }
        else {
            ++i;
        }    
    }

    if( list.size() <= 1 ) {
        return help( argv );
    }

    std::string output = list.back();
    list.pop_back();

    for( const auto &filename : list ) {
        textures.push_back( texture() );
        if( !textures.back().load(filename) ) {
            std::cerr << "[FAIL] Attila - cannot load: " << filename << std::endl;
            textures.pop_back();
        }
    }

    if( textures.empty() ) {
        return -1;
    }

    std::sort( textures.begin(), textures.end(), []( texture &a, texture &b ) { return a.src < b.src; } );

    //using namespace rbp;
    //MaxRectsBinPack mr;
    //std::vector<Rect> rects;

    if( options::ENABLE_MINIMUM_WIDTH ) {
        textures.push_back( texture() );
        textures.back().w = options::ENABLE_MINIMUM_WIDTH;
        textures.back().h = 1;
    }

    TEXTURE_PACKER::TexturePacker *tp = TEXTURE_PACKER::createTexturePacker();
    tp->setTextureCount(textures.size());
    for( int i = 0; i < textures.size(); ++i ) {
        tp->addTexture(textures[i].w, textures[i].h);
    }

    if( options::ENABLE_MINIMUM_WIDTH ) {
        textures.pop_back();
    }

    int width;
    int height;
    int unused_area = tp->packTextures(width,height,options::ENABLE_POT != 0,options::ENABLE_EDGE != 0);

    /* if( options::ENABLE_MINIMUM_WIDTH ) {
        if (width > options::ENABLE_MINIMUM_WIDTH)
        width -= options::ENABLE_MINIMUM_WIDTH;
    } */

    //mr.Init( width, height );

    spot::image mega( width, height );
    //std::cerr << "[ OK ] Attila - Built " << width << 'x' << height << " texture...\n";

    std::string ss, sep;

    for( int i = 0; i < textures.size(); ++i ) {
        //rects.push_back( mr.Insert( textures[i].w, textures[i].h, MaxRectsBinPack::RectBestShortSideFit ) );

        texture &t = textures[i];

        int x, y, w, h;
        t.rotate = tp->getTextureLocation(i,x,y,w,h) ? 90 : 0;
        t.x = x; t.y = y;
        t.u0 = float(  x) / width, t.v0 = float(  y) / height;
        t.u1 = float(w+x) / width, t.v1 = float(h+y) / height;
        t.dst = output;
        
        if( options::ENABLE_C_OUTPUT ) {
            ss += sep + textures[i].c("\t");
        } else {
            ss += sep + textures[i].json("\t");
            sep = ",\n";
        }

#if 0
        spot::image img;
        if( !img.load(t.src) ) {
            std::cerr << "[FAIL] Attila - cannot load: " << t.src << std::endl;            
            return -1;
        }
        if( options::ENABLE_CROPPING ) {
            img = crop(img);
        }
#else
        spot::image &img = t.img;
#endif
        mega.paste( mega, x, y, t.rotate ? img.rotate_left() : img );
    }

    if( options::ENABLE_C_OUTPUT ) {
        std::string members = "const char *name; int w, h, x, y; double u0,v0, u1,v1; int rotate, hash; ";
        std::string optional = options::ENABLE_CROPPING ? "int crop_left, crop_right, crop_top, crop_bottom; " : "";
        std::cout << "// struct atlas_entry {" + members + optional + "} const atlas[] =" << std::endl;
        std::cout << "{\n" << ss << "}\n" << std::endl; 
    } else {
        std::cout << "{\n" << ss << "\n}\n" << std::endl; 
    }

    //std::cout << "mr = " << mr.Occupancy() << std::endl;

    auto check_ext = []( std::string file, std::string ext ) -> bool {
        file = normalize( file );
        ext = normalize( ext );
        return file.size() < ext.size() ? false : file.substr( file.size() - ext.size() ) == ext;
    };

    if( options::ENABLE_MIPMAPS ) {
        mega = build_mipmaps( mega );
    }

    if( options::ENABLE_BLEEDING ) {
        mega = mega.bleed();
    }

    /**/ if( check_ext( output, ".bmp" ) ) {
        mega.save_as_bmp( output );
    }
    else if( check_ext( output, ".dds" ) ) {
        mega.save_as_dds( output );
    }
    else if( check_ext( output, ".jpg" ) ) {
        mega.save_as_jpg( output, 70 );
    }
    else if( check_ext( output, ".png" ) ) {
        mega.save_as_png( output );
    }
    else if( check_ext( output, ".tga" ) ) {
        mega.save_as_tga( output );
    }
    else if( check_ext( output, ".webp" ) ) {
        mega.save_as_webp( output, 70 );
    }
    else if( check_ext( output, ".pug" ) ) {
        mega.save_as_pug( output, 70 );
    }
    else {
        mega.save_as_png( output );
    }

    TEXTURE_PACKER::releaseTexturePacker( tp );

    auto end = now();

    std::cerr << "[ OK ] Attila - Texture (" << width << 'x' << height << ") area: " << (100.0*unused_area/(width*height)) << "% free in " << (end - start) << " secs." << std::endl;
    return 0;
}

int main( int argc, const char **argv ) try {
    return attila( argc, argv );
}
catch( std::exception &e ) {
    std::cerr << "[FAIL] Attila - " << e.what() << std::endl;
}
catch( const char *error ) {
    std::cerr << "[FAIL] Attila - " << error << std::endl;
}
catch( ... ) {
    std::cerr << "[FAIL] Attila - unknown exception" << std::endl;
}


#include "deps/packers/TexturePacker.cpp"


<|start_filename|>deps/v1/TexturePacker.cpp<|end_filename|>
#include "TexturePacker.h"
#include <assert.h>

#pragma warning(disable:4100 4244)

namespace TEXTURE_PACKER
{


class Rect
{
public:

  void set(int x1,int y1,int x2,int y2)
  {
    mX1 = x1;
    mY1 = y1;
    mX2 = x2;
    mY2 = y2;
  }

  bool intersects(const Rect &r) const
  {
    bool intersect = true;

    if ( mX2 < r.mX1 ||
         mX1 > r.mX2 ||
         mY2 < r.mY1 ||
         mY1 > r.mY2 )
    {
      intersect  = false;
    }
    return intersect;
  }
  int mX1;
  int mY1;
  int mX2;
  int mY2;
};

class Texture
{
public:
  void set(int wid,int hit)
  {
    mWidth = wid;
    mHeight = hit;
    mX = 0;
    mY = 0;
    mFlipped = false;
    mPlaced = false;
    mArea = wid*hit;
    if ( wid >= hit )
      mLongestEdge = wid;
    else
      mLongestEdge = hit;
  }

  bool isPlaced(void) const { return mPlaced; };

  void place(int x,int y,bool flipped)
  {
    mX = x;
    mY = y;
    mFlipped = flipped;
    mPlaced = true;
  }

  int mWidth;
  int mHeight;
  int mX;
  int mY;
  int mLongestEdge;
  int mArea;

  bool  mFlipped:1;       // true if the texture was rotated to make it fit better.
  bool  mPlaced:1;
};

class Node
{
public:
  Node(int x,int y,int wid,int hit)
  {
    mX = x;
    mY = y;
    mWidth = wid;
    mHeight = hit;
    mNext = 0;
  }

  Node *getNext(void) const { return mNext; };

  bool fits(int wid,int hit,int &edgeCount) const
  {
    bool ret = false;

    edgeCount = 0;

    if ( wid == mWidth  || hit == mHeight || wid == mHeight || hit == mWidth )
    {
      if ( wid == mWidth )
      {
        edgeCount++;
        if ( hit == mHeight )
          edgeCount++;
      }
      else if ( wid == mHeight )
      {
        edgeCount++;
        if ( hit == mWidth )
        {
          edgeCount++;
        }
      }
      else if ( hit == mWidth )
      {
        edgeCount++;
      }
      else if ( hit == mHeight )
      {
        edgeCount++;
      }
    }

    if ( wid <= mWidth && hit <= mHeight )
    {
      ret = true;
    }
    else if ( hit <= mWidth && wid <= mHeight )
    {
      ret = true;
    }

    return ret;
  }

  void getRect(Rect &r) const
  {
    r.set(mX,mY,mX+mWidth-1,mY+mHeight-1);
  }

  void validate(Node *n)
  {
    Rect r1;
    Rect r2;
    getRect(r1);
    n->getRect(r2);
    assert( !r1.intersects(r2) );
  }

  bool merge(const Node &n)
  {
    bool ret = false;

    Rect r1;
    Rect r2;

    getRect(r1);
    n.getRect(r2);
    r1.mX2++;
    r1.mY2++;
    r2.mX2++;
    r2.mY2++;

    // if we share the top edge then..
    if ( r1.mX1 == r2.mX1 && r1.mX2 == r2.mX2 && r1.mY1 == r2.mY2 )
    {
      mY = n.mY;
      mHeight+=n.mHeight;
      ret = true;
    }
    else if ( r1.mX1 == r2.mX1 && r1.mX2 == r2.mX2 && r1.mY2 == r2.mY1 ) // if we share the bottom edge
    {
      mHeight+=n.mHeight;
      ret = true;
    }
    else if ( r1.mY1 == r2.mY1 && r1.mY2 == r2.mY1 && r1.mX1 == r2.mX2 ) // if we share the left edge
    {
      mX = n.mX;
      mWidth+=n.mWidth;
      ret = true;
    }
    else if ( r1.mY1 == r2.mY1 && r1.mY2 == r2.mY1 && r1.mX2 == r2.mX1 ) // if we share the left edge
    {
      mWidth+=n.mWidth;
      ret = true;
    }

    return ret;
  }


  Node *mNext;
  int mX;
  int mY;
  int mWidth;
  int mHeight;
};

class MyTexturePacker : public TexturePacker
{
public:
  MyTexturePacker(void)
  {
    mTextureCount = 0;
    mTextures = 0;
    mLongestEdge = 0;
    mTotalArea = 0;
    mTextureIndex = 0;
    mFreeList = 0;
  }
  ~MyTexturePacker(void)
  {
    reset();
  }

  void reset(void)
  {
    mTextureCount = 0;
    delete []mTextures;
    mTextures = 0;
    mLongestEdge = 0;
    mTotalArea = 0;
    mTextureIndex = 0;
    if ( mFreeList )
    {
      Node *next = mFreeList;
      while ( next )
      {
        Node *kill = next;
        next = next->getNext();
        delete kill;
      }
    }
  }

  virtual void  setTextureCount(int tcount) // number of textures to consider..
  {
    reset();
    mTextureCount = tcount;
    mTextures = new Texture[tcount];
  }

  virtual void  addTexture(int wid,int hit)  // add textures 0 - n
  {
    assert( mTextureIndex < mTextureCount );
    if ( mTextureIndex < mTextureCount )
    {
      mTextures[mTextureIndex].set(wid,hit);
      mTextureIndex++;
      if ( wid > mLongestEdge )  mLongestEdge = wid;
      if ( hit > mLongestEdge )  mLongestEdge = hit;
      mTotalArea+=(wid*hit);
    }
  }

  virtual void newFree(int x,int y,int wid,int hit)
  {
    Node *node = new Node(x,y,wid,hit);
    node->mNext = mFreeList;
    mFreeList = node;
  }

  int nextPow2(int v) const
  {
    int p = 1;
    while ( p < v )
    {
      p = p*2;
    }
    return p;
  }

  virtual int packTextures(int &width,int &height,bool forcePowerOfTwo,bool onePixelBorder)  // pack the textures, the return code is the amount of wasted/unused area.
  {
    width = 0;
    height = 0;

    if ( onePixelBorder )
    {
      for (int i=0; i<mTextureCount; i++)
      {
        Texture &t = mTextures[i];
        t.mWidth+=2;
        t.mHeight+=2;
      }
      mLongestEdge+=2;
    }

    if ( forcePowerOfTwo )
    {
      mLongestEdge = nextPow2(mLongestEdge);
    }

    width  = mLongestEdge;              // The width is no more than the longest edge of any rectangle passed in
    int count = mTotalArea / (mLongestEdge*mLongestEdge);
    height = (count+2)*mLongestEdge;            // We guess that the height is no more than twice the longest edge.  On exit, this will get shrunk down to the actual tallest height.

    mDebugCount = 0;
    newFree(0,0,width,height);

    // We must place each texture
    for (int i=0; i<mTextureCount; i++)
    {

      int index = 0;
      int longestEdge = 0;
      int mostArea = 0;

      // We first search for the texture with the longest edge, placing it first.
      // And the most area...
      for (int j=0; j<mTextureCount; j++)
      {

        Texture &t = mTextures[j];

        if ( !t.isPlaced() ) // if this texture has not already been placed.
        {
          if ( t.mLongestEdge > longestEdge )
          {
            mostArea = t.mArea;
            longestEdge = t.mLongestEdge;
            index = j;
          }
          else if ( t.mLongestEdge == longestEdge ) // if the same length, keep the one with the most area.
          {
            if ( t.mArea > mostArea )
            {
              mostArea = t.mArea;
              index = j;
            }
          }
        }
      }

      // For the texture with the longest edge we place it according to this criteria.
      //   (1) If it is a perfect match, we always accept it as it causes the least amount of fragmentation.
      //   (2) A match of one edge with the minimum area left over after the split.
      //   (3) No edges match, so look for the node which leaves the least amount of area left over after the split.
      Texture &t = mTextures[index];

      int leastY = 0x7FFFFFFF;
      int leastX = 0x7FFFFFFF;

      Node *previousBestFit = 0;
      Node *bestFit = 0;
      Node *previous = 0;
      Node *search = mFreeList;
      int edgeCount = 0;

      // Walk the singly linked list of free nodes
      // see if it will fit into any currently free space
      while ( search )
      {
        int ec;
        if ( search->fits(t.mWidth,t.mHeight,ec) ) // see if the texture will fit into this slot, and if so how many edges does it share.
        {
          if ( ec == 2 )
          {
            previousBestFit = previous;     // record the pointer previous to this one (used to patch the linked list)
            bestFit = search; // record the best fit.
            edgeCount = ec;
            break;
          }
          if ( search->mY < leastY )
          {
            leastY = search->mY;
            leastX = search->mX;
            previousBestFit = previous;
            bestFit = search;
            edgeCount = ec;
          }
          else if ( search->mY == leastY && search->mX < leastX )
          {
            leastX = search->mX;
            previousBestFit = previous;
            bestFit = search;
            edgeCount = ec;
          }
        }
        previous = search;
        search = search->mNext;
      }

      assert( bestFit ); // we should always find a fit location!

      if ( bestFit )
      {
        validate();

        switch ( edgeCount )
        {
          case 0:
            if ( t.mLongestEdge <= bestFit->mWidth )
            {
              bool flipped = false;

              int wid = t.mWidth;
              int hit = t.mHeight;

              if ( hit > wid )
              {
                wid = t.mHeight;
                hit = t.mWidth;
                flipped = true;
              }

              t.place(bestFit->mX,bestFit->mY,flipped); // place it.

              newFree(bestFit->mX,bestFit->mY+hit,bestFit->mWidth,bestFit->mHeight-hit);

              bestFit->mX+=wid;
              bestFit->mWidth-=wid;
              bestFit->mHeight = hit;
              validate();
            }
            else
            {

              assert( t.mLongestEdge <= bestFit->mHeight );

              bool flipped = false;

              int wid = t.mWidth;
              int hit = t.mHeight;

              if ( hit < wid )
              {
                wid = t.mHeight;
                hit = t.mWidth;
                flipped = true;
              }

              t.place(bestFit->mX,bestFit->mY,flipped); // place it.

              newFree(bestFit->mX,bestFit->mY+hit,bestFit->mWidth,bestFit->mHeight-hit);

              bestFit->mX+=wid;
              bestFit->mWidth-=wid;
              bestFit->mHeight = hit;
              validate();
            }
            break;
          case 1:
            {
              if ( t.mWidth == bestFit->mWidth )
              {
                t.place(bestFit->mX,bestFit->mY,false);
                bestFit->mY+=t.mHeight;
                bestFit->mHeight-=t.mHeight;
                validate();
              }
              else if ( t.mHeight == bestFit->mHeight )
              {
                t.place(bestFit->mX,bestFit->mY,false);
                bestFit->mX+=t.mWidth;
                bestFit->mWidth-=t.mWidth;
                validate();
              }
              else if ( t.mWidth == bestFit->mHeight )
              {
                t.place(bestFit->mX,bestFit->mY,true);
                bestFit->mX+=t.mHeight;
                bestFit->mWidth-=t.mHeight;
                validate();
              }
              else if ( t.mHeight == bestFit->mWidth )
              {
                t.place(bestFit->mX,bestFit->mY,true);
                bestFit->mY+=t.mWidth;
                bestFit->mHeight-=t.mWidth;
                validate();
              }
            }
            break;
          case 2:
            {
              bool flipped = t.mWidth != bestFit->mWidth || t.mHeight != bestFit->mHeight;
              t.place(bestFit->mX,bestFit->mY,flipped);
              if ( previousBestFit )
              {
                previousBestFit->mNext = bestFit->mNext;
              }
              else
              {
                mFreeList = bestFit->mNext;
              }
              delete bestFit;
              validate();
            }
            break;
        }
        while ( mergeNodes() ); // keep merging nodes as much as we can...
      }
    }

    height = 0;
    for (int i=0; i<mTextureCount; i++)
    {
      Texture &t = mTextures[i];
      if ( onePixelBorder )
      {
        t.mWidth-=2;
        t.mHeight-=2;
        t.mX++;
        t.mY++;
      }
      int y = t.mY+(t.mFlipped ? t.mWidth : t.mHeight);
      if ( y > height )
        height = y;
    }

    if ( forcePowerOfTwo )
    {
      height = nextPow2(height);
    }

    return (width*height)-mTotalArea;
  }

  bool mergeNodes(void)
  {
    Node *f = mFreeList;
    while ( f )
    {
      Node *prev = 0;
      Node *c = mFreeList;
      while ( c )
      {
        if ( f != c )
        {
          if ( f->merge(*c) )
          {
            assert(prev);
            prev->mNext = c->mNext;
            delete c;
            return true;
          }
        }
        prev = c;
        c = c->mNext;
      }
      f = f->mNext;
    }
    return false;
  }

  void validate(void)
  {
    #ifdef _DEBUG
    Node *f = mFreeList;
    while ( f )
    {
      Node *c = mFreeList;
      while ( c )
      {
        if ( f != c )
        {
          f->validate(c);
        }
        c = c->mNext;
      }
      f = f->mNext;
    }
    #endif
  }

  virtual bool  getTextureLocation(int index,int &x,int &y,int &wid,int &hit)
  {
    bool ret = false;

    x = y = wid = hit = 0;

    assert( index < mTextureCount );
    if ( index < mTextureCount )
    {
      Texture &t = mTextures[index];
      x = t.mX;
      y = t.mY;
      if ( t.mFlipped )
      {
        wid = t.mHeight;
        hit = t.mWidth;
      }
      else
      {
        wid = t.mWidth;
        hit = t.mHeight;
      }
      ret = t.mFlipped;
    }

    return ret;
  }


private:
  int    mDebugCount;
  Node    *mFreeList;
  int    mTextureIndex;
  int    mTextureCount;
  Texture *mTextures;
  int    mLongestEdge;
  int    mTotalArea;
};

#if 0

class MyTexturePacker2 : public TexturePacker
{
	struct rect
	{
		int x,y,w,h;
		bool rotated;

		rect() : x(0),y(0),w(0),h(0),rotated(false)
		{}
	};

	std::map< std::pair<int,int> /*dimension*/, std::vector<rect> /*images*/ > columns;

	public:

		MyTexturePacker2()
		{}

		~MyTexturePacker2()
		{}

		// number of textures to consider..
		virtual void setTextureCount( int count )
		{}

		// add textures 0 - n
		virtual void addTexture( int w, int h )
		{
			bool rotated = false;

			if( w >= h )
			{
				std::swap( w, h );
				rotated = true;
			}

			columns[ std::make_pair<w,h> ].push_back( rect() );
			columns[ std::make_pair<w,h> ].back().w = w;
			columns[ std::make_pair<w,h> ].back().h = h;
			columns[ std::make_pair<w,h> ].back().rotated = rotated;
		}

		// pack the textures, the return code is the amount of wasted/unused area.
		virtual int packTextures(int &width,int &height,bool forcePowerOfTwo,bool onePixelBorder) 
		{
			(void)forcePowerOfTwo;
			(void)onePixelBorder;

			return 0;
		}

		virtual bool  getTextureLocation(int index,int &x,int &y,int &wid,int &hit) = 0; // returns true if the texture has been rotated 90 degrees
};

#endif

TexturePacker * createTexturePacker(void)
{
  MyTexturePacker *m = new MyTexturePacker;
  return static_cast< TexturePacker *>(m);
}

void            releaseTexturePacker(TexturePacker *tp)
{
  MyTexturePacker *m = static_cast< MyTexturePacker *>(tp);
  delete m;
}

}; // end of namepsace

<|start_filename|>deps/v1/atlas.cpp<|end_filename|>
// 	std::transform(filename.begin(), filename.end(), filename.begin(), std::ptr_fun(std::tolower));


// to do : remove all initial characters which are duplicated in *all* entries
// to do : change \ -> / in filenames
// to do : png writing support

// C Headers

#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <cstdarg>

// C++ Headers

#include <string>
#include <vector>
#include <fstream>		//fout
#include <sstream>
#include <locale>		//tolower
#include <algorithm>	//transform, replace, replace_if, replace_copy, ...

// Custom headers

#include <dot/dot.hpp>			// custom image library
#include "TexturePacker.h"		// custom texture atlas alg

// Config

// PS3 version need atlas image to be split in 4096x4096 .dds texture files
//#define PS3_VERSION

#define VERSION_INFO \
		"Texture Atlas v1.0 by rlyeh (c) 2010, MIT license.\n" \
		"Using TexturePacker library by <NAME>.\n" \
		"Using stb_image 1.18 library by <NAME>.\n" \
		"Using stb_image DDS extension library by <NAME>.\n"

namespace options
{
	bool
		bSubdirs = false,
		bQuiet = false,
		bShowHelp = false;
}

// Program

#define printf( ... ) do if( !options::bQuiet ) printf( __VA_ARGS__ ); while( 0 )

template <typename Elem, typename Traits, typename Ax>
std::basic_string<Elem, Traits, Ax> &tolower(std::basic_string<Elem, Traits, Ax> *str)
{
	std::use_facet<std::ctype<Elem> >(std::locale()).tolower(&*str->begin(), &*str->end());
	return *str;
}

// Copy conversions
template <typename Elem, typename Traits, typename Ax>
std::basic_string<Elem, Traits, Ax> tolower(std::basic_string<Elem, Traits, Ax> str)
{
	std::use_facet<std::ctype<Elem> >(std::locale()).tolower(&*str.begin(), &*str.end());
	return str;
}

//! std::string s;
//! tolower(s.begin(), s.end());
template < typename ForwardIterator >
ForwardIterator makelower(ForwardIterator first, ForwardIterator last,
const std::locale& locale_ref = std::locale())
{
for(; first!=last; ++first)
*first = std::tolower(*first, locale_ref);
return first;
}

//wstring
struct string : public std::string //std::basic_string<char>
{
	//http://msdn.microsoft.com/en-us/library/56e442dc(VS.71).aspx

	string tolower()
	{
		string s = (*this);

		makelower( s.begin(), s.end() );

		return s;
	}

	/*
	string &tolower()
	{
		std::transform( begin(), end(), begin(), (int(*)(int))std::tolower);

		return (*this);
	}
	*/

	int find( const string &target ) const
	{
		int amount = 0, pos = 0;

		while( (pos = (int)( std::string::find( target, pos ) + 1)) != (int)(std::string::npos + 1))
			amount++;

		return amount;
	}

	string replace( const string &target, const string &replacement ) const
	{
		string s = *this;

		size_t found;

		while( ( found = s.std::string::find( target ) ) != string::npos )
		{
			s.std::string::replace( found, target.length(), replacement );
		}

		return s; //.substr( 0, s.length() - 3 );
	}

	//--- Path !

	//std::vector<std::pair<std::string, std::string>> path()
	//{} return vect tokenize("\\/").[] -> file ext

	string shortname() const
	{
		// branch according to OS

		size_t begin, end;

		if( true )
			begin = find_last_of("\\"), end = find_last_of(".");
		else
			begin = find_last_of("//"), end = find_last_of(".");

		if( begin == std::string::npos )
			begin = 0;

		return substr( begin, end );
	}

	string extension() const
	{
		return substr( find_last_of(".") + 1 );
	}

	string remove_extension() const
	{
		return substr( 0, find_last_of(".") );
	}

	string sanitize() const
	{
		// branch according to OS

		if( true )
			return replace( "\\", "/" );
		else
			return replace( "/", "\\" );
	}

	//

	string() : std::string()
	{}

	string( const string &s )
	{
		operator=( s );
	}

	string( string *s )
	{
		operator=( *s );
	}

	string( const std::string &s )
	{
		std::string::operator=( s );
	}

	string( const char *fmt, ... )
	{
		va_list args;
		char buffer[ 2048 ];

		va_start( args, fmt );
		std::vsprintf( buffer, fmt, args );
		va_end( args );

		std::string::operator=( buffer );
	}

	string( const char *fmt, va_list args )
	{
		char buffer[ 2048 ];

		vsprintf( buffer, fmt, args );

		std::string::operator=( buffer );
	}

	template <class T>
	string( T t )
	{
	  std::ostringstream oss;
	  oss << std::dec << t;
	  operator=( oss.str() );
	}

	~string()
	{}


	void operator =( const string &s )
	{
		std::string::operator=( s.str() );
	}
/*
	void operator =( const std::string &s )
	{
		std::string::operator=( s );
	}
*/
	std::string str() const
	{
		return std::string( c_str() );
	}
};

void progress( const char *str )
{
	static string last;
	static int counter = 0;

	if( str && last != str )
	{
		printf("- %s\n", str );
		last = str;
	}

	return;

	// Print

	char progress_indicator[] = "\\|/-";

	string current = string("%c %s...%s", progress_indicator[ (counter++) % 4 ], str ? str : last.c_str(), str ? "" : " done!" );

	printf( "%s%*s%s", current.c_str(), last.size() > current.size() ? last.size() - current.size() : 0, str ? "\r" : "\r-\n");

	// Changed?

	last = current;
}

int gRenderSet = 0;
int gCommonCharacters = 0x4000;
string gLastFile;

struct image
{
	enum eSaveType
	{
		PPM,
		BMP,
		TGA,
		DDS
	};

	string name;

	int x, y, w, h, ow, oh, rotation;

	dot::image RGBA;

	image()	: x(0),y(0),w(0),h(0),ow(0),oh(0),rotation(0)//,RGBA( NULL )
	{}

	image( const image &i ) : x(0),y(0),w(0),h(0),ow(0),oh(0),rotation(0)
	{
		operator=( i );
	}

	image( image *pi ) : x(0),y(0),w(0),h(0),ow(0),oh(0),rotation(0)
	{
		operator=( *pi );
	}

	~image()
	{}

	image & operator=( const image &i )
	{
		//assert( false );

		name = i.name;
		x = i.x;
		y = i.y;
		w = i.w;
		h = i.h;
		ow = i.ow;
		oh = i.oh;
		rotation = i.rotation;
		RGBA = i.RGBA;

		//(const_cast<image &>(i)).RGBA = NULL;

		return *this;
	}

	bool write( const char *filePath, eSaveType type )
	{
		printf("- Writing texture atlas '%s' (%d x %d) ... ", filePath, w, h);

		assert( RGBA.size() );

#ifdef PS3_VERSION

		// force 4096 height

		if( h > 4096 )
			h = 4096;

		for( ; h < 4096 ; h++ )
			for( int x = 0; x < w; x++)
				RGBA.push_back( 0x80808080 );

#endif
		RGBA.w = w;
		RGBA.h = h;

		if( 1 )
		{
			if( type == PPM )
			{
				FILE *f = fopen(filePath, "w");

				if( f )
				{
					auto &rgba = RGBA.rgba_data();
					unsigned char *color = rgba.data();

					fprintf(f, "P3\n%d %d\n%d\n", w, h, 255);

					for (size_t i=0; i< w*h; i++, color += 4)
						fprintf(f,"%d %d %d ", (color[0]>>0) & 255, (color[1]>>0) & 255, (color[2]>>0) & 255);

					fclose( f );

					printf("done!\n");
					return true;
				}
				else
				{
					printf("failed!\n");
					return false;
				}
			}

			if( type == TGA && RGBA.save_as_tga( filePath ) )
			{
				printf("done!\n");
				return true;
			}

/*
			if( type == BMP && RGBA.save_as_bmp( filePath ) )
			{
				printf("done!\n");
				return true;
			}
*/

			if( type == DDS && RGBA.save_as_dds( filePath ) )
			{
				printf("done!\n");
				return true;
			}
		}

		printf("failed! Error: cant write '%s' file\n", filePath);
		return false;
	}
};

class atlas
{
	private:

		int m_Width, m_Height;
		size_t m_ProcessedFiles;
		float m_FreeSpace;

		std::vector< std::string > files, inputs, outputs;
		std::vector< image > images;

		TEXTURE_PACKER::TexturePacker *tp;

	public:

		atlas() //: m_ProcessedFiles( 0 )
		{
		}

		~atlas()
		{
		}

		private:

		void scan_file( std::string pathfile, bool subdirs )
		{
			printf("- Parsing directory...");

			{
				char command[2048];
				sprintf( command, "dir \"%s\" %s /b /o:n > $$temp$$ 2> nul", pathfile.c_str(), subdirs ? "/s" : "" );
				system( command );
			}

			printf(" done!\n");

			FILE *in = fopen("$$temp$$", "rb");

			if( in )
			{
				char buffer[ 2048 ];

				while( fgets( buffer, 2048, in ) )
				{
					char *pos = &buffer[ strlen(buffer) - 1 ];

					while( *pos == '\n' || *pos == '\r' )
						*pos-- = '\0';

					if( string(buffer) != "$$temp$$" )
					{
						files.push_back( string( buffer ).tolower() );

						if( files.size() == 1 )
						{
							gLastFile = files.back();
						}

						int lCommonCharacters = strspn( files.back().c_str(), gLastFile.c_str() );
						gCommonCharacters = ( lCommonCharacters < gCommonCharacters ? lCommonCharacters : gCommonCharacters );
						gLastFile = files.back();
					}
				}

				fclose( in );
				remove("$$temp$$");
			}

			for( int i = 0; i < files.size(); i++ )
			{
				const char *buffer = files[i].c_str();

				static int c = 0;
				progress( string("Reading file %d... %s", ++c, buffer).c_str() );

				int w, h, bpp;

				//optimize me
				dot::image img;

				if( img.load(buffer) )
				{
					image i;

					i.name = buffer;
					i.ow = img.w;
					i.oh = img.h;

					images.push_back( i );
				}

				m_ProcessedFiles++;
			}

			files.clear();

			progress( NULL );
		}

		bool save_subset( string filename, image &im, std::vector< image > &images )
		{
			if( !images.size() )
				return false;

			string extension = string( filename ).extension().tolower();

			if( gRenderSet )
			{
				filename = string( "%s_%d.%s", filename.remove_extension().c_str(), gRenderSet, extension.c_str() );
			}

			string shortname = string( filename ).shortname().tolower();

			bool isDDS = ( extension == "dds" );
			bool isPPM = ( extension == "ppm" );
			bool isBMP = ( extension == "bmp" );
			bool isTGA = ( extension == "tga" );
			bool isXML = ( extension == "xml" );
			bool isTXT = ( extension == "txt" );
			bool isHPP = ( extension == "hpp" || extension == "h" );

			if( isDDS || isPPM || isBMP || isTGA )
			{
				image::eSaveType type[] = { image::DDS, image::DDS, image::PPM, image::BMP, image::TGA };

				return im.write( filename.c_str(), type[ isDDS * 1 | isPPM * 2 | isBMP * 3 | isTGA * 4 ] );
			}

			if( isXML );

			if( isTXT )
			{
				printf("- Writing texture info '%s' ... ", filename.c_str() );

				std::ofstream fout;

				fout.open( filename.c_str(), std::ios::app );    // open file for appending

				if( fout.fail() )
				{
					printf("failed\n");
					return false;
				}

				fout << string(	"; Generated by TextureAtlas. Data per line: \"id\" set x1 y1 x2 y2 w h rotation\n"
								"; \n"
								"; \n"
								"; Atlas size %d x %d, %03.2f%% unused space\n"
								"; \n", m_Width, m_Height, m_FreeSpace);

				for( int i = 0; i < images.size(); i++ )
				{
					string name;

					if( gCommonCharacters < images[i].name.size() )
					{
						name = images[i].name.substr( gCommonCharacters, images[i].name.size() ).c_str();
					}
					else
					{
						name = images[i].name;
					}

					fout << string("\"%s\" %d %d %d %d %d %d %d %d ; %d \n", name.sanitize().c_str(), gRenderSet, images[i].x, images[i].y, images[i].x+images[i].w, images[i].y+images[i].h, images[i].w, images[i].h, images[i].rotation, i);
				}

				fout.close();

				if( fout.fail() )
				{
					printf("failed\n");
					return false;
				}

				printf("done!\n");
			}

			if( isHPP )
			{
				printf("- Writing texture info '%s' ... ", filename.c_str() );

				std::ofstream fout;

				fout.open( filename.c_str(), std::ios::app );    // open file for appending

				if( fout.fail() )
				{
					printf("failed\n");
					return false;
				}

				fout << string( "#ifndef __TEXTURE_ATLAS_COMMON_H__\n"
								"#define __TEXTURE_ATLAS_COMMON_H__\n"
								"\n"
								"struct TextureAtlasEntry { const char *id; size_t set,x1,y1,x2,y2,w,h,rotation; float u0,v0,u1,v1; };\n"
								"\n"
								"#endif /*__TEXTURE_ATLAS_COMMON_H__*/\n"
								"\n"
								"TextureAtlasEntry %s_h[] = {\n"
								"/* Atlas size %d x %d, %03.2f%% unused space */\n", shortname.c_str(), m_Width, m_Height, m_FreeSpace );

				for( int i = 0; i < images.size(); i++ )
				{
					string name;

					if( gCommonCharacters < images[i].name.size() )
					{
						name = images[i].name.substr( gCommonCharacters, images[i].name.size() ).c_str();
					}
					else
					{
						name = images[i].name;
					}

					fout << string("/* %12d */ {\"%s\",%d,%d,%d,%d,%d,%d,%d,%d,%d.f/%d.f,%d.f/%d.f,%d.f/%d.f,%d.f/%d.f}%s",
						i, name.sanitize().c_str(),
						gRenderSet, images[i].x, images[i].y, images[i].x+images[i].w, images[i].y+images[i].h, images[i].w, images[i].h, images[i].rotation,
						images[i].x, m_Width, images[i].y, m_Height, images[i].x+images[i].w, m_Width, images[i].y+images[i].h, m_Height,
						i == images.size() - 1 ? "\n};\n\n" : ",\n" );
				}

				fout.close();

				if( fout.fail() )
				{
					printf("failed\n");
					return false;
				}

				printf("done!\n");
			}

			return false;
		}

		void render_subset( image &target, std::vector< image > &images )
		{
			if( images.size() )
			{
				target.w = m_Width;
				target.h = m_Height;

				target.RGBA = dot::image( m_Width, m_Height );
				target.RGBA.resize(0);

				for( int i = 0; i < m_Width * m_Height; ++i )
					target.RGBA.push_back( dot::pixel::RGBA(0x80808080) );

				for( int i = 0; i < images.size(); i++)
				{
					progress("Rendering subset");

#ifdef PS3_VERSION

					if( images[i].name == "$$temp$$" )
						continue;

#endif

					int w, h, bpp;
					dot::image img;

					bool loaded = img.load( images[i].name.c_str() );
					assert( loaded );

					int iw = images[i].w, ih = images[i].h, ix = images[i].x, iy = images[i].y, ir = images[i].rotation;

					if( !ir ) {
						target.RGBA = target.RGBA.paste( ix, iy, img );
					}
					else {
						target.RGBA = target.RGBA.paste( ix, iy, img.rotate_left() );
					}
				}
			}

			progress( NULL );
		}

		bool pack_subset( std::vector< image > &images )
		{
			if( images.size() )
			{
				tp = TEXTURE_PACKER::createTexturePacker();

				assert( tp );

#ifdef PS3_VERSION

				// force 4096 width

				images.push_back( image() );
				images.back().ow = 4096;
				images.back().oh = 1;
				images.back().name = "$$temp$$";

#endif

				tp->setTextureCount( images.size() );

				for( int i = 0; i < images.size(); i++ )
				{
					tp->addTexture( images[i].ow, images[i].oh );
				}

				// it seems .DDS file specs do need bForcePowerOfTwo to be true in all cases
				bool bForcePowerOfTwo = true, bAddOnePixelBorder = false;

				m_FreeSpace = tp->packTextures( m_Width, m_Height, bForcePowerOfTwo, bAddOnePixelBorder );
				m_FreeSpace = (100.f * m_FreeSpace ) / (m_Width * m_Height);

				for( int i = 0; i < images.size(); i++ )
				{
					images[i].rotation = 90 * tp->getTextureLocation( i, images[i].x, images[i].y, images[i].w, images[i].h ); // returns true if the texture has been rotated 90 degrees
				}

				releaseTexturePacker(tp);

				printf("%dx%d texture atlas (%d images, %03.2f%% surface free) ... ", m_Width, m_Height, images.size(), m_FreeSpace );

#ifdef PS3_VERSION

				// remove 4096 width hack

				images.erase( --images.end() );

				// and validate

				return m_Width == 4096 && m_Height <= 4096;

#endif
			}

			return true;
		}

		bool pack_procedurally( std::vector< image > &subset, int idx = 0 )
		{
			printf("- Packing subset from %d to %d files: ", idx, idx + subset.size() );

			if( !pack_subset( subset ) )
			{
				printf("failed\n");

				std::vector< image > subsetL;
				std::vector< image > subsetR;

				for( int i = 0; i < subset.size(); i++ )
				{
					if( i < subset.size() * 0.5f )
						subsetL.push_back( subset[i] );
					else
						subsetR.push_back( subset[i] );
				}

				return pack_procedurally( subsetL, idx ) && pack_procedurally( subsetR, idx + subsetL.size() + 1 );
			}

			printf("done!\n");

			image im;

			render_subset( im, subset );

#ifdef PS3_VERSION

			gRenderSet++;

#endif

			for( int i = 0; i < outputs.size(); i++)
				save_subset( outputs[i], im, subset );

			return true;
		}

		public:

		void input( std::string s )
		{
			inputs.push_back( s );
		}

		void output( std::string s )
		{
			outputs.push_back( s );
		}

		void process( bool bSubdirs )
		{
			if( !inputs.size() )
			{
				printf("Please specify at least one input source\n");
				return;
			}

			for( int i = 0; i < inputs.size(); i++ )
			{
				scan_file( inputs[i], bSubdirs );
			}

			pack_procedurally( images );
		}
};


int main( int argc, char **argv )
{
	atlas a;

	for( int argn = 1; argn < argc; argn++ )
	{
		char *arg = argv[argn], *arg_next = argv[argn + 1];

		if( arg[0] == '-' )
		{
			if(!strcmpi(arg, "-q"))
				options::bQuiet = true;

			if(!strcmpi(arg, "-h"))
				options::bShowHelp = true;

			if(!strcmpi(arg, "-s"))
				options::bSubdirs = true;

			if(!strcmpi(arg, "-r"))
				options::bSubdirs = true;

			if(!strcmpi(arg, "-o"))
				if( arg_next )
					a.output( arg_next );
		}
		else
		{
			a.input( arg );
		}
	}

	if( options::bShowHelp )
	{
		options::bQuiet = false;

		printf("%s", VERSION_INFO );
		printf("\n");
		printf("This tool packs collections of textures into a single larger enclosing image.\n");
		printf("Following input file formats are supported: JPG, PNG, BMP, TGA, PSD, DDS and HDR.\n");
		printf("\n");
		printf("Usage:\n");
		printf("\n");
		printf(" * Specify as many input files/paths as you need. Use -r for recursive search.\n");
		printf(" * Specify as many outputs as you need (dds/bmp/ppm/cpp/xml/txt), or none for texture atlas evaluation.\n");
		printf("\n");
		printf("Syntax:\n");
		printf("\n");
		printf("  %s [-h][-q][-r][-v][-o filePath] filePath1 [filePath2] [...]\n", argv[0] );
		printf("\n");
		printf("Parameters:\n");
		printf("\n");
		printf("  -h : Show this help page\n");
		printf("  -q : Quiet mode\n");
		printf("  -r : Recurse subdirectories\n");
		printf("  -v : Show version\n");
//		printf("\n");
//		printf("Examples:\n");
//		printf("\n");
//		printf("Search for images in current folder and subfolders and do nothing:\n");
//		printf("  %s *.*\n", argv[0]);
//		printf("\n");
//		printf("Search for images in current folder and write atlas texture/info in test.bmp/test.xml:\n");
//		printf("  %s -bmp -test.bmp test.xml\n", argv[0]);
//		printf("\n");
//		printf("Search for images in given folders and write atlas texture/info in test.dds/test.xml\n");
//		printf("  %s -f:*.tga -f:png\\*.png -f:misc\\*.* test.dds test.xml\n", argv[0]);
	}
	else
	{
		a.process( options::bSubdirs );
	}

	return 0;
}
