/* Copyright (c) 2021 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #ifndef __TexturePacker_H__ #define __TexturePacker_H__ #ifdef __cplusplus extern "C" { #endif typedef struct TexturePacker TexturePacker; #include "3dmodelio/TextureAtlasData.h" #include "bitmapimage/BitmapImage.h" #include "gamemath/TextureAtlas.h" #include "stemobject/StemObject.h" #define TexturePacker_superclass StemObject struct TexturePacker_atlasEntry { BitmapImage * image; // Always stored unflipped bool imageOwned; char * name; uint32_t identifier; unsigned int width; unsigned int height; unsigned int margin; int internalMargin; unsigned int x; unsigned int y; }; #define TexturePacker_ivars \ StemObject_ivars \ \ enum BitmapPixelFormat pixelFormat; \ unsigned int entryCount; \ struct TexturePacker_atlasEntry * entries; \ unsigned int packedWidth; \ unsigned int packedHeight; #define TexturePacker_vtable(self_type) \ StemObject_vtable(self_type) stemobject_declare(TexturePacker) TexturePacker * TexturePacker_create(enum BitmapPixelFormat pixelFormat); bool TexturePacker_init(TexturePacker * self, enum BitmapPixelFormat pixelFormat); void TexturePacker_dispose(TexturePacker * self); // Adds an image to the list of entries to be included in a future call to TexturePacker_pack(). If an entry with the // same name has already been added, it will be replaced. // If ownImage is true, takes ownership of image and may mutate it. If an unowned image needs to be mutated, an owned // copy may be created. // flipped specifies whether the first row of pixels in image is the top (false) or the bottom (true). // margin specifies a number of blank pixels on all sides that will be added around image during packing. // internalMargin specifies a number of pixels by which the resulting atlas entry's bounds will be offset inward from // the borders of image. The pixels outside the internal margin are included in the atlas, but will be outside its // entry's bounds. // image must match TexturePacker's pixelFormat. abort() will be called if it doesn't. // Return value: True if there was a name collision and an entry was overwritten; false if a new entry was created bool TexturePacker_addImage(TexturePacker * self, BitmapImage * image, bool ownImage, const char * name, bool flipped, unsigned int margin, int internalMargin); // Similar to TexturePacker_addImage(), but operates on an abstract rectangle with a numeric identifier rather than a // BitmapImage. Calls to addImage() and addRect() generally should not be mixed in the same TexturePacker instance; // in the resulting TexturePacker_atlasEntry struct, for addImage() or addAllEntriesFromAtlas() calls, identifier is // UINT32_MAX, and for addRectangle() calls, image and name are both NULL. If rasterize() is called on a TexturePacker // that contains non-image rectangle entries, each rectangle region will be left blank in the resulting BitmapImage. // Since all entries in TextureAtlasData must have name strings, any entries added by this function will be omitted // from the object returned from TexturePacker_createTextureAtlasData(). However, TexturePacker_createTextureAtlas() // will include all rectangle entries as uint32 keys. // Any entry with the same identifier will be replaced, and the function returns true if a replacement was made. // Note that adding a rectangle with an identifier of UINT32_MAX is an error, and will cause an assertion failure. bool TexturePacker_addRectangle(TexturePacker * self, uint32_t identifier, unsigned int width, unsigned int height, unsigned int margin, int internalMargin); // Extracts a rectangular region of atlasImage specified by bounds, and returns a new bitmap containing its contents. // inputFlipped specifies whether the first row of pixels in atlasImage is the top (false) or the bottom (true). // outputFlipped specifies whether to create output with the first row of pixels at the top (false) or the bottom (true). // coordinatesFlipped specifies whether bounds is measured from the top of atlasImage (false) or the bottom (true). // coordinatesRelative specifies whether bounds is in pixel units (false) or atlas units from 0 to 1 (true). // Normally, TextureAtlas_lookup() uses flipped relative coordinates, and TextureAtlasData_getEntryWithName() uses // unflipped pixel coordinates. BitmapImage * TexturePacker_extractEntryImage(Rect4f bounds, BitmapImage * atlasImage, bool inputFlipped, bool outputFlipped, bool coordinatesFlipped, bool coordinatesRelative); // Extracts all images from atlasData and adds them to the list of entries to be included in a future call to TexturePacker_pack(). // imageFlipped specifies whether the first row of pixels in atlasImage is the top (false) or the bottom (true). // All added entries will receive the margin specified by margin, with no internal margin. // atlasImage must match TexturePacker's pixelFormat. abort() will be called if it doesn't. // Returns true if any name collisions occurred (overwriting old with new), and false if there were no collisions. bool TexturePacker_addAllEntriesFromAtlas(TexturePacker * self, TextureAtlasData * atlasData, BitmapImage * atlasImage, bool imageFlipped, unsigned int margin); // Calculates a compacted arrangement of all images that were previously added with addImage(), addRectangle(), // or addAllEntriesFromAtlas(). At least one entry must be added before calling pack(). After packing, call rasterize() // to get the resulting packed image, and call createTextureAtlasData() to get the resulting list of packed boundaries. // If powersOfTwoOnly is true, TexturePacker_pack() will only consider packing dimensions where both dimensions are a // power of two. This is significantly faster, but may result in less compact usage of space. void TexturePacker_pack(TexturePacker * self, bool powersOfTwoOnly); // Returns a new image containing all packed atlas entries. TexturePacker_pack() must be called prior to rasterize(). // flip specifies whether to output and image with the first row of pixels at the top (false) or at the bottom (true). BitmapImage * TexturePacker_rasterize(TexturePacker * self, bool flip); // Returns a new TextureAtlasData object with containing all image entries. TexturePacker_pack() must be called prior // to calling createTextureAtlasData(). Non-image rectangle entries are omitted from the result. TextureAtlasData * TexturePacker_createTextureAtlasData(TexturePacker * self, const char * textureName); // Returns a new TextureAtlas object with a list of all image and rectangle entries. TexturePacker_pack() must be // called prior to calling createTextureAtlas(). Image entries use their name string as the hash table key in the // result; rectangles use their identifier as a uint32 key. TextureAtlas * TexturePacker_createTextureAtlas(TexturePacker * self); // Calculates and returns the overall utilization of the available space in the packed image, from 0 (no space used) to // 1 (all space used). float TexturePacker_getDensity(TexturePacker * self); #ifdef __cplusplus } #endif #endif