| 144 | |
| 145 | template <typename AtlasTextureHandle> |
| 146 | auto TextureAtlasSet<AtlasTextureHandle>::addTexture(Image const& image, bool borderPixels) -> TextureHandle { |
| 147 | if (image.empty()) |
| 148 | throw TextureAtlasException("Empty image given in TextureAtlasSet::addTexture"); |
| 149 | |
| 150 | Image finalImage; |
| 151 | if (borderPixels) { |
| 152 | Vec2U imageSize = image.size(); |
| 153 | Vec2U finalImageSize = imageSize + Vec2U(2, 2); |
| 154 | finalImage = Image(finalImageSize, PixelFormat::RGBA32); |
| 155 | |
| 156 | // Fill 1px border on all sides of the image |
| 157 | for (unsigned y = 0; y < finalImageSize[1]; ++y) { |
| 158 | for (unsigned x = 0; x < finalImageSize[0]; ++x) { |
| 159 | unsigned xind = clamp<unsigned>(x, 1, imageSize[0]) - 1; |
| 160 | unsigned yind = clamp<unsigned>(y, 1, imageSize[1]) - 1; |
| 161 | finalImage.set32(x, y, image.getrgb(xind, yind)); |
| 162 | } |
| 163 | } |
| 164 | } else { |
| 165 | finalImage = image; |
| 166 | } |
| 167 | |
| 168 | auto tryAtlas = [&](TextureAtlas* atlas) -> TextureHandle { |
| 169 | auto placement = addTextureToAtlas(atlas, finalImage, borderPixels); |
| 170 | if (!placement) |
| 171 | return nullptr; |
| 172 | |
| 173 | auto textureEntry = make_shared<TextureEntry>(); |
| 174 | textureEntry->textureImage = std::move(finalImage); |
| 175 | textureEntry->atlasPlacement = *placement; |
| 176 | |
| 177 | m_textures.add(textureEntry); |
| 178 | sortAtlases(); |
| 179 | return textureEntry; |
| 180 | }; |
| 181 | |
| 182 | // Try the first 'm_textureFitTries' atlases to see if we can fit a given |
| 183 | // texture in an existing atlas. Do this from the most full to the least |
| 184 | // full atlas to maximize compression. |
| 185 | size_t startAtlas = m_atlases.size() - min<size_t>(m_atlases.size(), m_textureFitTries); |
| 186 | for (size_t i = startAtlas; i < m_atlases.size(); ++i) { |
| 187 | if (auto texturePtr = tryAtlas(m_atlases[i].get())) |
| 188 | return texturePtr; |
| 189 | } |
| 190 | |
| 191 | // If we have not found an existing atlas to put the texture, need to create |
| 192 | // a new atlas |
| 193 | m_atlases.append(make_shared<TextureAtlas>(TextureAtlas{ |
| 194 | createAtlasTexture(Vec2U::filled(m_atlasCellSize * m_atlasNumCells), PixelFormat::RGBA32), |
| 195 | unique_ptr<bool[]>(new bool[m_atlasNumCells * m_atlasNumCells]()), 0 |
| 196 | })); |
| 197 | |
| 198 | if (auto texturePtr = tryAtlas(m_atlases.last().get())) |
| 199 | return texturePtr; |
| 200 | |
| 201 | // If it can't fit in a brand new empty atlas, it will not fit in any atlas |
| 202 | destroyAtlasTexture(m_atlases.last()->atlasTexture); |
| 203 | m_atlases.removeLast(); |