| #include <memory> |
| |
| #include "ext/meshtexturizer.h" |
| |
| |
| #include "tgl.h" // OpenGL includes |
| |
| |
| #include <QReadWriteLock> |
| #include <QReadLocker> |
| #include <QWriteLocker> |
| |
| |
| #include "tcg/tcg_macros.h" |
| #include "tcg/tcg_list.h" |
| #include "tcg/tcg_misc.h" |
| |
| #define COPIED_BORDER 1 // Amount of tile border from the original image |
| #define TRANSP_BORDER 1 // Amount of transparent tile border |
| #define NONPREM_BORDER 1 // Amount of nonpremultiplied copied transparent border |
| #define TOTAL_BORDER (COPIED_BORDER + TRANSP_BORDER) // Overall border to texture tiles above |
| #define TOTAL_BORDER_2 (2 * TOTAL_BORDER) // Twice the above |
| |
| TCG_STATIC_ASSERT(COPIED_BORDER > 0); |
| TCG_STATIC_ASSERT(TRANSP_BORDER > 0); |
| TCG_STATIC_ASSERT(NONPREM_BORDER <= TRANSP_BORDER); |
| |
| |
| |
| |
| |
| class MeshTexturizer::Imp |
| { |
| typedef MeshTexturizer::TextureData TextureData; |
| |
| public: |
| QReadWriteLock m_lock; |
| tcg::list<std::shared_ptr<TextureData>> m_textureDatas; |
| |
| public: |
| Imp() : m_lock(QReadWriteLock::Recursive) {} |
| |
| bool testTextureAlloc(int lx, int ly); |
| GLuint textureAlloc(const TRaster32P &ras, const TRaster32P &aux, |
| int x, int y, int textureLx, int textureLy, |
| bool premultiplied); |
| |
| void allocateTextures(int groupIdx, const TRaster32P &ras, const TRaster32P &aux, |
| int x, int y, int textureLx, int textureLy, |
| bool premultiplied); |
| |
| TextureData *getTextureData(int groupIdx); |
| }; |
| |
| |
| |
| bool MeshTexturizer::Imp::testTextureAlloc(int lx, int ly) |
| { |
| lx += TOTAL_BORDER_2, ly += TOTAL_BORDER_2; |
| |
| glTexImage2D(GL_PROXY_TEXTURE_2D, |
| 0, |
| GL_RGBA, |
| lx, |
| ly, |
| 0, |
| TGL_FMT, |
| GL_UNSIGNED_BYTE, |
| 0); |
| |
| int outLx; |
| glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &outLx); |
| |
| return (lx == outLx); |
| } |
| |
| |
| |
| GLuint MeshTexturizer::Imp::textureAlloc(const TRaster32P &ras, const TRaster32P &aux, |
| int x, int y, int textureLx, int textureLy, |
| bool premultiplied) |
| { |
| struct locals { |
| static void clearMatte(const TRaster32P &ras, int xBegin, int yBegin, int xEnd, int yEnd) |
| { |
| for (int y = yBegin; y != yEnd; ++y) { |
| TPixel32 *line = ras->pixels(y), *pixEnd = line + xEnd; |
| |
| for (TPixel32 *pix = line + xBegin; pix != pixEnd; ++pix) |
| pix->m = 0; |
| } |
| } |
| |
| static void clearMatte_border(const TRaster32P &ras, int border0, int border1) |
| { |
| assert(border0 < border1); |
| |
| |
| clearMatte(ras, border0, border0, ras->getLx() - border0, border1); |
| clearMatte(ras, border0, ras->getLy() - border1, ras->getLx() - border0, ras->getLy() - border0); |
| |
| |
| clearMatte(ras, border0, border1, border1, ras->getLy() - border1); |
| clearMatte(ras, ras->getLx() - border1, border1, ras->getLx() - border0, ras->getLy() - border1); |
| } |
| }; |
| |
| |
| assert(aux->getLx() >= textureLx + TOTAL_BORDER_2 && aux->getLy() >= textureLy + TOTAL_BORDER_2); |
| |
| TRect rasRect(x, y, x + textureLx - 1, y + textureLy - 1); |
| rasRect = rasRect.enlarge(premultiplied ? COPIED_BORDER : COPIED_BORDER + NONPREM_BORDER); |
| rasRect = rasRect * ras->getBounds(); |
| |
| TRect auxRect(rasRect - TPoint(x - TOTAL_BORDER, y - TOTAL_BORDER)); |
| |
| |
| TRaster32P tex(aux->extract(0, 0, textureLx + TOTAL_BORDER_2 - 1, textureLy + TOTAL_BORDER_2 - 1)); |
| tex->clear(); |
| aux->extract(auxRect)->copy(ras->extract(rasRect)); |
| |
| if (!premultiplied && NONPREM_BORDER > 0) |
| locals::clearMatte_border(aux, TRANSP_BORDER - NONPREM_BORDER, TRANSP_BORDER); |
| |
| |
| GLuint texId; |
| glGenTextures(1, &texId); |
| glBindTexture(GL_TEXTURE_2D, texId); |
| |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| |
| glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->getWrap()); |
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
| |
| glTexImage2D(GL_TEXTURE_2D, |
| 0, |
| GL_RGBA, |
| tex->getLx(), |
| tex->getLy(), |
| 0, |
| TGL_FMT, |
| GL_UNSIGNED_BYTE, |
| (GLvoid *)tex->getRawData()); |
| |
| return texId; |
| } |
| |
| |
| |
| void MeshTexturizer::Imp::allocateTextures(int groupIdx, const TRaster32P &ras, const TRaster32P &aux, |
| int x, int y, int textureLx, int textureLy, |
| bool premultiplied) |
| { |
| TextureData *data = m_textureDatas[groupIdx].get(); |
| |
| |
| if (testTextureAlloc(textureLx, textureLy)) { |
| TPointD scale(data->m_geom.getLx() / (double)ras->getLx(), |
| data->m_geom.getLy() / (double)ras->getLy()); |
| TRectD tileGeom( |
| TRectD( |
| scale.x * (x - TOTAL_BORDER), scale.y * (y - TOTAL_BORDER), |
| scale.x * (x + textureLx + TOTAL_BORDER), scale.y * (y + textureLy + TOTAL_BORDER)) + |
| data->m_geom.getP00()); |
| |
| GLuint texId = textureAlloc(ras, aux, x, y, textureLx, textureLy, premultiplied); |
| |
| TextureData::TileData td = {texId, tileGeom}; |
| data->m_tileDatas.push_back(td); |
| |
| return; |
| } |
| |
| if (textureLx <= 1 && textureLy <= 1) |
| return; |
| |
| |
| if (textureLx > textureLy) { |
| int textureLx_2 = textureLx >> 1; |
| allocateTextures(groupIdx, ras, aux, x, y, textureLx_2, textureLy, premultiplied); |
| allocateTextures(groupIdx, ras, aux, x + textureLx_2, y, textureLx_2, textureLy, premultiplied); |
| } else { |
| int textureLy_2 = textureLy >> 1; |
| allocateTextures(groupIdx, ras, aux, x, y, textureLx, textureLy_2, premultiplied); |
| allocateTextures(groupIdx, ras, aux, x, y + textureLy_2, textureLx, textureLy_2, premultiplied); |
| } |
| } |
| |
| |
| |
| MeshTexturizer::TextureData *MeshTexturizer::Imp::getTextureData(int groupIdx) |
| { |
| typedef MeshTexturizer::TextureData TextureData; |
| |
| |
| return m_textureDatas[groupIdx].get(); |
| } |
| |
| |
| |
| |
| |
| MeshTexturizer::MeshTexturizer() |
| : m_imp(new Imp) |
| { |
| } |
| |
| |
| |
| MeshTexturizer::~MeshTexturizer() |
| { |
| } |
| |
| |
| |
| int MeshTexturizer::bindTexture(const TRaster32P &ras, const TRectD &geom, |
| PremultMode premultiplyMode) |
| { |
| QWriteLocker locker(&m_imp->m_lock); |
| |
| |
| int row_length, alignment; |
| glGetIntegerv(GL_UNPACK_ROW_LENGTH, &row_length); |
| glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment); |
| |
| |
| int dataIdx = m_imp->m_textureDatas.push_back(std::make_shared<TextureData>(geom)); |
| |
| |
| |
| int textureLx = tcg::numeric_ops::GE_2Power((unsigned int)ras->getLx() + TOTAL_BORDER_2); |
| int textureLy = tcg::numeric_ops::GE_2Power((unsigned int)ras->getLy() + TOTAL_BORDER_2); |
| |
| |
| textureLx = std::min(textureLx, 1 << 9); |
| textureLy = std::min(textureLy, 1 << 9); |
| |
| |
| |
| TRaster32P tex(textureLx, textureLy); |
| |
| |
| |
| |
| int lx = ras->getLx(), ly = ras->getLy(); |
| int tileLx = textureLx - TOTAL_BORDER_2, tileLy = textureLy - TOTAL_BORDER_2; |
| |
| int xEntireCells = (lx - 1) / tileLx, yEntireCells = (ly - 1) / tileLy; |
| |
| int lastTexLx = tcg::numeric_ops::GE_2Power((unsigned int)(lx - xEntireCells * tileLx + TOTAL_BORDER_2)); |
| int lastTexLy = tcg::numeric_ops::GE_2Power((unsigned int)(ly - yEntireCells * tileLy + TOTAL_BORDER_2)); |
| |
| int lastTileLx = lastTexLx - TOTAL_BORDER_2, lastTileLy = lastTexLy - TOTAL_BORDER_2; |
| |
| bool premultiplied = (premultiplyMode == PREMULTIPLIED); |
| |
| int i, j; |
| for (i = 0; i < yEntireCells; ++i) { |
| for (j = 0; j < xEntireCells; ++j) |
| |
| m_imp->allocateTextures(dataIdx, ras, tex, j * tileLx, i * tileLy, tileLx, tileLy, premultiplied); |
| |
| m_imp->allocateTextures(dataIdx, ras, tex, j * tileLx, i * tileLy, lastTileLx, tileLy, premultiplied); |
| } |
| |
| for (j = 0; j < xEntireCells; ++j) |
| m_imp->allocateTextures(dataIdx, ras, tex, j * tileLx, i * tileLy, tileLx, lastTileLy, premultiplied); |
| |
| m_imp->allocateTextures(dataIdx, ras, tex, j * tileLx, i * tileLy, lastTileLx, lastTileLy, premultiplied); |
| |
| |
| glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length); |
| glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); |
| |
| return dataIdx; |
| } |
| |
| |
| |
| void MeshTexturizer::rebindTexture(int texId, const TRaster32P &ras, const TRectD &geom, |
| PremultMode premultiplyMode) |
| { |
| QWriteLocker locker(&m_imp->m_lock); |
| |
| unbindTexture(texId); |
| int newTexId = bindTexture(ras, geom, premultiplyMode); |
| |
| assert(texId == newTexId); |
| } |
| |
| |
| |
| void MeshTexturizer::unbindTexture(int texId) |
| { |
| QWriteLocker locker(&m_imp->m_lock); |
| m_imp->m_textureDatas.erase(texId); |
| } |
| |
| |
| |
| MeshTexturizer::TextureData *MeshTexturizer::getTextureData(int textureId) |
| { |
| QReadLocker locker(&m_imp->m_lock); |
| return m_imp->getTextureData(textureId); |
| } |