00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "util/base/exception.h"
00027
00028 #include "atlasbook.h"
00029
00030 namespace FIFE {
00031 AtlasBlock AtlasBlock::intersects(AtlasBlock const& rect) const {
00032 AtlasBlock ret;
00033
00034 ret.left = std::max(rect.left, left);
00035 ret.right = std::min(rect.right, right);
00036 ret.top = std::max(rect.top, top);
00037 ret.bottom = std::min(rect.bottom, bottom);
00038
00039 if(ret.left > ret.right || ret.top > ret.bottom) {
00040 ret.setTrivial();
00041 }
00042
00043 return ret;
00044 }
00045
00046 void AtlasBlock::merge(AtlasBlock const& rect) {
00047 left = std::min(rect.left, left);
00048 right = std::max(rect.right, right);
00049 top = std::min(rect.top, top);
00050 bottom = std::max(rect.bottom, bottom);
00051 }
00052
00053 AtlasBlock* AtlasPage::getBlock(uint32_t width, uint32_t height) {
00054 if(static_cast<int32_t>(width*height*pixelSize) > freePixels) {
00055 return 0;
00056 }
00057
00058 blocks.push_back(AtlasBlock(Rect(), 0));
00059 AtlasBlock* newBlock = &blocks[blocks.size() - 1];
00060
00061 for(uint32_t v = 0; (v+1)*height <= this->height; ++v) {
00062 newBlock->top = v * height;
00063 newBlock->bottom = (v+1) * height;
00064
00065 for(uint32_t u = 0; (u+1)*width <= this->width; ++u) {
00066
00067 newBlock->left = u * width;
00068 newBlock->right = (u+1) * width;
00069
00070 AtlasBlock const* intersection = intersects(newBlock);
00071 if(!intersection) {
00072 freePixels -= width*height*pixelSize;
00073 assert(freePixels >= 0);
00074
00075
00076 if(newBlock->left > 0) {
00077 AtlasBlock squeezed(*newBlock);
00078
00079 --squeezed.left;
00080 --squeezed.right;
00081
00082 if(!(intersection = intersects(&squeezed))) {
00083 ++squeezed.left;
00084 ++squeezed.right;
00085 int blockWidth = newBlock->getWidth();
00086
00087
00088 for(int i = 0, div = 2; i < 4; ++i) {
00089 squeezed.left -= blockWidth / div;
00090 squeezed.right -= blockWidth / div;
00091
00092 if((intersection = intersects(&squeezed))) {
00093 squeezed.left += blockWidth / div;
00094 squeezed.right += blockWidth / div;
00095 }
00096 div <<= 1;
00097 }
00098
00099
00100 while(!(intersection = intersects(&squeezed)) && squeezed.left > 0) {
00101 --squeezed.left;
00102 --squeezed.right;
00103 }
00104
00105 newBlock->left = squeezed.left + 1;
00106 newBlock->right = squeezed.right + 1;
00107 }
00108 }
00109
00110
00111 if(newBlock->top > 0) {
00112 AtlasBlock squeezed(*newBlock);
00113
00114 --squeezed.top;
00115 --squeezed.bottom;
00116
00117 if(!(intersection = intersects(&squeezed))) {
00118 ++squeezed.top;
00119 ++squeezed.bottom;
00120 int blockHeight = newBlock->getHeight();
00121
00122
00123 for(int i = 0, div = 2; i < 4; ++i) {
00124 squeezed.top -= blockHeight / div;
00125 squeezed.bottom -= blockHeight / div;
00126
00127 if((intersection = intersects(&squeezed))) {
00128 squeezed.top += blockHeight / div;
00129 squeezed.bottom += blockHeight / div;
00130 }
00131 div <<= 1;
00132 }
00133
00134
00135 while(!(intersection = intersects(&squeezed)) && squeezed.top > 0) {
00136 --squeezed.top;
00137 --squeezed.bottom;
00138 }
00139
00140 newBlock->top = squeezed.top + 1;
00141 newBlock->bottom = squeezed.bottom + 1;
00142 }
00143 }
00144
00145 newBlock->page = this->page;
00146 return newBlock;
00147 }
00148 }
00149 }
00150
00151 blocks.pop_back();
00152 return 0;
00153 }
00154
00155 void AtlasPage::shrink(bool pot) {
00156 AtlasBlock boundaryBox(Rect(), 0);
00157 for(Blocks::iterator block = blocks.begin(); block != blocks.end(); ++block) {
00158 boundaryBox.merge(*block);
00159 }
00160
00161 assert(boundaryBox.left == 0);
00162 assert(boundaryBox.top == 0);
00163
00164 if(pot) {
00165 uint32_t bwidth = boundaryBox.getWidth();
00166 uint32_t bheight = boundaryBox.getHeight();
00167
00168 if(bwidth < width) {
00169
00170 uint32_t powof2 = 1;
00171 while(powof2 < bwidth) powof2 <<= 1;
00172 width = std::min(powof2, width);
00173 }
00174
00175 if(bheight < height) {
00176
00177 uint32_t powof2 = 1;
00178 while(powof2 < bheight) powof2 <<= 1;
00179 height = std::min(powof2, height);
00180 }
00181
00182 } else {
00183 width = boundaryBox.getWidth();
00184 height = boundaryBox.getHeight();
00185 }
00186 }
00187
00188 AtlasBlock const* AtlasPage::intersects(AtlasBlock const* block) const {
00189 for(size_t b = 0; b < blocks.size() - 1; ++b) {
00190 if(!blocks[b].intersects(*block).isTrivial())
00191 return block;
00192 }
00193
00194 return 0;
00195 }
00196
00197 AtlasBlock* AtlasBook::getBlock(uint32_t width, uint32_t height) {
00198 for(Pages::iterator page = pages.begin(); page != pages.end(); ++page) {
00199 AtlasBlock* block = page->getBlock(width, height);
00200 if(block) {
00201 return block;
00202 }
00203 }
00204 return extendCache(width, height)->getBlock(width, height);
00205 }
00206
00207 AtlasPage* AtlasBook::extendCache(uint32_t minPageWidth, uint32_t minPageHeight) {
00208 if(minPageWidth > pageWidth ||
00209 minPageHeight > pageHeight) {
00210 throw Exception("Texture is too big for this atlas.");
00211 }
00212
00213 assert(minPageWidth <= pageWidth);
00214 assert(minPageHeight <= pageHeight);
00215
00216 pages.push_back(AtlasPage(pageWidth, pageHeight, pixelSize, pages.size()));
00217 return &pages[pages.size()-1];
00218 }
00219
00220 void AtlasBook::shrink(bool pot) {
00221 for(Pages::iterator page = pages.begin(); page != pages.end(); ++page) {
00222 page->shrink(pot);
00223 }
00224 }
00225 }