// // libavg - Media Playback Engine. // Copyright (C) 2003-2014 Ulrich von Zadow // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // Current versions can be found at www.libavg.de // #include "Image.h" #include "../base/Logger.h" #include "../base/Exception.h" #include "../base/ObjectCounter.h" #include "../graphics/Filterfliprgb.h" #include "../graphics/TextureMover.h" #include "../graphics/BitmapLoader.h" #include "OGLSurface.h" #include "OffscreenCanvas.h" #include #include using namespace std; namespace avg { Image::Image(OGLSurface * pSurface, const MaterialInfo& material) : m_sFilename(""), m_pSurface(pSurface), m_State(CPU), m_Source(NONE), m_Material(material) { ObjectCounter::get()->incRef(&typeid(*this)); assertValid(); } Image::~Image() { if (m_State == GPU && m_Source != NONE) { m_pSurface->destroy(); } ObjectCounter::get()->decRef(&typeid(*this)); } void Image::moveToGPU() { assertValid(); if (m_State == CPU) { switch (m_Source) { case FILE: case BITMAP: setupSurface(); break; case SCENE: m_pSurface->create(B8G8R8X8, m_pCanvas->getTex()); break; case NONE: break; default: AVG_ASSERT(false); } m_State = GPU; } assertValid(); } void Image::moveToCPU() { assertValid(); if (m_State == GPU) { switch (m_Source) { case FILE: case BITMAP: m_pBmp = m_pSurface->getTex()->moveTextureToBmp(); break; case SCENE: break; case NONE: break; default: AVG_ASSERT(false); return; } m_State = CPU; m_pSurface->destroy(); } assertValid(); } void Image::discard() { assertValid(); setEmpty(); m_State = CPU; assertValid(); } void Image::setEmpty() { assertValid(); if (m_State == GPU) { m_pSurface->destroy(); } changeSource(NONE); assertValid(); } void Image::setFilename(const std::string& sFilename, TextureCompression comp) { assertValid(); AVG_TRACE(Logger::category::MEMORY, Logger::severity::INFO, "Loading " << sFilename); BitmapPtr pBmp = loadBitmap(sFilename); if (comp == TEXTURECOMPRESSION_B5G6R5 && pBmp->hasAlpha()) { throw Exception(AVG_ERR_UNSUPPORTED, "B5G6R5-compressed textures with an alpha channel are not supported."); } changeSource(FILE); m_pBmp = pBmp; m_sFilename = sFilename; switch (comp) { case TEXTURECOMPRESSION_B5G6R5: m_pBmp = BitmapPtr(new Bitmap(pBmp->getSize(), B5G6R5, sFilename)); if (!BitmapLoader::get()->isBlueFirst()) { FilterFlipRGB().applyInPlace(pBmp); } m_pBmp->copyPixels(*pBmp); break; case TEXTURECOMPRESSION_NONE: break; default: assert(false); } if (m_State == GPU) { m_pSurface->destroy(); setupSurface(); } assertValid(); } void Image::setBitmap(BitmapPtr pBmp, TextureCompression comp) { assertValid(); if (!pBmp) { throw Exception(AVG_ERR_UNSUPPORTED, "setBitmap(): bitmap must not be None!"); } if (comp == TEXTURECOMPRESSION_B5G6R5 && pBmp->hasAlpha()) { throw Exception(AVG_ERR_UNSUPPORTED, "B5G6R5-compressed textures with an alpha channel are not supported."); } bool bSourceChanged = changeSource(BITMAP); PixelFormat pf; switch (comp) { case TEXTURECOMPRESSION_NONE: pf = pBmp->getPixelFormat(); break; case TEXTURECOMPRESSION_B5G6R5: pf = B5G6R5; if (!BitmapLoader::get()->isBlueFirst()) { FilterFlipRGB().applyInPlace(pBmp); } break; default: assert(false); } if (m_State == GPU) { GLTexturePtr pTex = m_pSurface->getTex(); if (bSourceChanged || m_pSurface->getSize() != pBmp->getSize() || m_pSurface->getPixelFormat() != pf) { pTex = GLTexturePtr(new GLTexture(pBmp->getSize(), pf, m_Material.getUseMipmaps(), 0, m_Material.getWrapSMode(), m_Material.getWrapTMode())); m_pSurface->create(pf, pTex); } TextureMoverPtr pMover = TextureMover::create(pBmp->getSize(), pf, GL_STATIC_DRAW); BitmapPtr pMoverBmp = pMover->lock(); pMoverBmp->copyPixels(*pBmp); pMover->unlock(); pMover->moveToTexture(*pTex); m_pBmp = BitmapPtr(); } else { m_pBmp = BitmapPtr(new Bitmap(pBmp->getSize(), pf, "")); m_pBmp->copyPixels(*pBmp); } assertValid(); } void Image::setCanvas(OffscreenCanvasPtr pCanvas) { assertValid(); if (m_Source == SCENE && pCanvas == m_pCanvas) { return; } changeSource(SCENE); m_pCanvas = pCanvas; if (m_State == GPU) { m_pSurface->create(B8G8R8X8, m_pCanvas->getTex()); } assertValid(); } OffscreenCanvasPtr Image::getCanvas() const { return m_pCanvas; } const string& Image::getFilename() const { return m_sFilename; } BitmapPtr Image::getBitmap() { if (m_Source == NONE) { return BitmapPtr(); } else { switch (m_State) { case CPU: if (m_Source == SCENE) { return BitmapPtr(); } else { return BitmapPtr(new Bitmap(*m_pBmp)); } case GPU: return m_pSurface->getTex()->moveTextureToBmp(); default: AVG_ASSERT(false); return BitmapPtr(); } } } IntPoint Image::getSize() { if (m_Source == NONE) { return IntPoint(0,0); } else { switch (m_State) { case CPU: if (m_Source == SCENE) { return m_pCanvas->getSize(); } else { return m_pBmp->getSize(); } case GPU: return m_pSurface->getSize(); default: AVG_ASSERT(false); return IntPoint(0,0); } } } PixelFormat Image::getPixelFormat() { PixelFormat pf; if (BitmapLoader::get()->isBlueFirst()) { pf = B8G8R8X8; } else { pf = R8G8B8X8; } if (m_Source != NONE) { switch (m_State) { case CPU: if (m_Source != SCENE) { pf = m_pBmp->getPixelFormat(); } case GPU: pf = m_pSurface->getPixelFormat(); default: AVG_ASSERT(false); } } return pf; } OGLSurface* Image::getSurface() { AVG_ASSERT(m_State == GPU); return m_pSurface; } Image::State Image::getState() { return m_State; } Image::Source Image::getSource() { return m_Source; } Image::TextureCompression Image::string2compression(const string& s) { if (s == "none") { return Image::TEXTURECOMPRESSION_NONE; } else if (s == "B5G6R5") { return Image::TEXTURECOMPRESSION_B5G6R5; } else { throw(Exception(AVG_ERR_UNSUPPORTED, "Image compression "+s+" not supported.")); } } string Image::compression2String(TextureCompression compression) { switch(compression) { case Image::TEXTURECOMPRESSION_NONE: return "none"; case Image::TEXTURECOMPRESSION_B5G6R5: return "B5G6R5"; default: AVG_ASSERT(false); return 0; } } void Image::setupSurface() { PixelFormat pf = m_pBmp->getPixelFormat(); // cerr << "setupSurface: " << pf << endl; GLTexturePtr pTex(new GLTexture(m_pBmp->getSize(), pf, m_Material.getUseMipmaps(), 0, m_Material.getWrapSMode(), m_Material.getWrapTMode())); m_pSurface->create(pf, pTex); TextureMoverPtr pMover = TextureMover::create(m_pBmp->getSize(), pf, GL_STATIC_DRAW); pMover->moveBmpToTexture(m_pBmp, *pTex); m_pBmp = BitmapPtr(); } bool Image::changeSource(Source newSource) { if (newSource != m_Source) { switch (m_Source) { case NONE: break; case FILE: case BITMAP: if (m_State == CPU) { m_pBmp = BitmapPtr(); } m_sFilename = ""; break; case SCENE: m_pCanvas = OffscreenCanvasPtr(); break; default: AVG_ASSERT(false); } m_Source = newSource; return true; } else { return false; } } void Image::assertValid() const { AVG_ASSERT(m_pSurface); AVG_ASSERT((m_Source == FILE) == (m_sFilename != "")); AVG_ASSERT((m_Source == SCENE) == bool(m_pCanvas)); switch (m_State) { case CPU: AVG_ASSERT((m_Source == FILE || m_Source == BITMAP) == bool(m_pBmp)); AVG_ASSERT(!(m_pSurface->isCreated())); break; case GPU: AVG_ASSERT(!m_pBmp); if (m_Source != NONE) { AVG_ASSERT(m_pSurface->isCreated()); } else { AVG_ASSERT(!m_pSurface->isCreated()); } break; default: AVG_ASSERT(false); } } }