Restore support for animated png files

* Rework resource manager
* Add missing files
* Improve some graphics classes
This commit is contained in:
Eduardo Bart
2013-01-08 19:31:41 -02:00
parent fdcad184f9
commit 0120b7554c
47 changed files with 807 additions and 161 deletions

View File

@@ -24,53 +24,60 @@
#include "graphics.h"
#include <framework/core/eventdispatcher.h>
/*
AnimatedTexture::AnimatedTexture(int width, int height, int channels, int numFrames, uchar *framesPixels, int *framesDelay) :
Texture(),
m_numFrames(numFrames)
AnimatedTexture::AnimatedTexture(const Size& size, std::vector<ImagePtr> frames, std::vector<int> framesDelay, bool buildMipmaps, bool compress)
{
m_size.resize(width, height);
if(!setupSize(size, buildMipmaps))
return;
m_framesTextureId.resize(numFrames);
m_framesDelay.resize(numFrames);
for(int i=0;i<numFrames;++i) {
uchar *framePixels = framesPixels + (i*height*width* channels);
uint id = internalLoadGLTexture(framePixels, channels, width, height);
m_framesTextureId[i] = id;
m_framesDelay[i] = framesDelay[i];
for(uint i=0;i<frames.size();++i) {
m_frames.push_back(new Texture(frames[i], buildMipmaps, compress));
}
m_currentFrame = -1;
g_dispatcher.scheduleEvent(std::bind(&AnimatedTexture::processAnimation, this), 0);
m_framesDelay = framesDelay;
m_hasMipmaps = buildMipmaps;
m_id = m_frames[0]->getId();
m_currentFrame = 0;
m_animTimer.restart();
}
AnimatedTexture::~AnimatedTexture()
{
glDeleteTextures(m_numFrames, &m_framesTextureId[0]);
m_id = 0;
}
void AnimatedTexture::enableBilinearFilter()
bool AnimatedTexture::buildHardwareMipmaps()
{
for(int i=0;i<m_numFrames;++i) {
glBindTexture(GL_TEXTURE_2D, m_framesTextureId[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
if(!g_graphics.canUseHardwareMipmaps())
return false;
for(const TexturePtr& frame : m_frames)
frame->buildHardwareMipmaps();
m_hasMipmaps = true;
return true;
}
void AnimatedTexture::processAnimation()
void AnimatedTexture::setSmooth(bool smooth)
{
for(const TexturePtr& frame : m_frames)
frame->setSmooth(smooth);
m_smooth = smooth;
}
void AnimatedTexture::setRepeat(bool repeat)
{
for(const TexturePtr& frame : m_frames)
frame->setRepeat(repeat);
m_repeat = repeat;
}
void AnimatedTexture::updateAnimation()
{
if(m_animTimer.ticksElapsed() < m_framesDelay[m_currentFrame])
return;
m_animTimer.restart();
m_currentFrame++;
if(m_currentFrame >= m_numFrames)
if(m_currentFrame >= m_frames.size())
m_currentFrame = 0;
m_id = m_framesTextureId[m_currentFrame];
AnimatedTexturePtr self = asAnimatedTexture();
// continue to animate only if something still referencing this texture
if(self->ref_count() > 1)
g_dispatcher.scheduleEvent(std::bind(&AnimatedTexture::processAnimation, self), m_framesDelay[m_currentFrame]);
m_id = m_frames[m_currentFrame]->getId();
}
*/

View File

@@ -24,24 +24,28 @@
#define ANIMATEDTEXTURE_H
#include "texture.h"
/*
#include <framework/core/timer.h>
class AnimatedTexture : public Texture
{
public:
AnimatedTexture(int width, int height, int channels, int numFrames, uchar *framesPixels, int *framesDelay);
AnimatedTexture(const Size& size, std::vector<ImagePtr> frames, std::vector<int> framesDelay, bool buildMipmaps = false, bool compress = false);
virtual ~AnimatedTexture();
void enableBilinearFilter();
void processAnimation();
virtual bool buildHardwareMipmaps();
AnimatedTexturePtr asAnimatedTexture() { return static_self_cast<AnimatedTexture>(); }
virtual void setSmooth(bool smooth);
virtual void setRepeat(bool repeat);
void updateAnimation();
virtual bool isAnimatedTexture() { return true; }
private:
std::vector<uint> m_framesTextureId;
std::vector<TexturePtr> m_frames;
std::vector<int> m_framesDelay;
int m_numFrames;
int m_currentFrame;
ticks_t m_lastAnimCheckTicks;
uint m_currentFrame;
Timer m_animTimer;
};
*/
#endif

View File

@@ -52,9 +52,6 @@ void BitmapFont::load(const OTMLNodePtr& fontNode)
m_glyphsSize[32].setWidth(spaceWidth);
m_glyphsSize[160].setWidth(spaceWidth);
// use 127 as spacer [Width: 1]
m_glyphsSize[127].setWidth(1);
// new line actually has a size that will be useful in multiline algorithm
m_glyphsSize[(uchar)'\n'] = Size(1, m_glyphHeight);
@@ -262,6 +259,9 @@ Size BitmapFont::calculateTextRectSize(const std::string& text)
void BitmapFont::calculateGlyphsWidthsAutomatically(const ImagePtr& image, const Size& glyphSize)
{
if(!image)
return;
int numHorizontalGlyphs = image->getSize().width() / glyphSize.width();
auto texturePixels = image->getPixels();
@@ -338,4 +338,4 @@ std::string BitmapFont::wrapText(const std::string& text, int maxWidth)
outText = outText.substr(0, outText.length()-1);
return outText;
}
}

View File

@@ -39,8 +39,8 @@ public:
void setAlign(Fw::AlignmentFlag align) { m_align = align; update(); }
Size getTextSize() { return m_textSize; }
std::string getText() { return m_text; }
BitmapFontPtr getFont() { return m_font; }
std::string getText() const { return m_text; }
BitmapFontPtr getFont() const { return m_font; }
Fw::AlignmentFlag getAlign() { return m_align; }
private:

View File

@@ -59,6 +59,11 @@ public:
m_textureCoordArray.addQuad(src);
m_hardwareCached = false;
}
void addUpsideDownQuad(const Rect& dest, const Rect& src) {
m_vertexArray.addUpsideDownQuad(dest);
m_textureCoordArray.addQuad(src);
m_hardwareCached = false;
}
void addBoudingRect(const Rect& dest, int innerLineWidth);
void addRepeatedRects(const Rect& dest, const Rect& src);

View File

@@ -45,13 +45,12 @@ void FontManager::clearFonts()
m_defaultFont = BitmapFontPtr(new BitmapFont("emptyfont"));
}
bool FontManager::importFont(std::string fontFile)
bool FontManager::importFont(std::string file)
{
try {
if(!stdext::ends_with(fontFile, ".otfont"))
fontFile += ".otfont";
file = g_resources.guessFileType(file, "otfont");
OTMLDocumentPtr doc = OTMLDocument::parse(fontFile);
OTMLDocumentPtr doc = OTMLDocument::parse(file);
OTMLNodePtr fontNode = doc->at("Font");
std::string name = fontNode->valueAt("name");
@@ -69,12 +68,12 @@ bool FontManager::importFont(std::string fontFile)
m_fonts.push_back(font);
// set as default if needed
if(!m_defaultFont)
if(!m_defaultFont || fontNode->valueAt<bool>("default", false))
m_defaultFont = font;
return true;
} catch(stdext::exception& e) {
g_logger.error(stdext::format("Unable to load font from file '%s': %s", fontFile, e.what()));
g_logger.error(stdext::format("Unable to load font from file '%s': %s", file, e.what()));
return false;
}
}
@@ -100,4 +99,3 @@ BitmapFontPtr FontManager::getFont(const std::string& fontName)
g_logger.error(stdext::format("font '%s' not found", fontName));
return getDefaultFont();
}

View File

@@ -34,7 +34,7 @@ public:
void terminate();
void clearFonts();
bool importFont(std::string fontFile);
bool importFont(std::string file);
bool fontExists(const std::string& fontName);
BitmapFontPtr getFont(const std::string& fontName);

View File

@@ -173,6 +173,8 @@ bool Graphics::selectPainterEngine(PainterEngine painterEngine)
{
Painter *painter = nullptr;
Painter *fallbackPainter = nullptr;
PainterEngine fallbackPainterEngine = Painter_Any;
#ifdef PAINTER_OGL2
// always prefer OpenGL 2 over OpenGL 1
if(g_painterOGL2) {
@@ -181,6 +183,7 @@ bool Graphics::selectPainterEngine(PainterEngine painterEngine)
painter = g_painterOGL2;
}
fallbackPainter = g_painterOGL2;
fallbackPainterEngine = Painter_OpenGL2;
}
#endif
@@ -192,11 +195,14 @@ bool Graphics::selectPainterEngine(PainterEngine painterEngine)
painter = g_painterOGL1;
}
fallbackPainter = g_painterOGL1;
fallbackPainterEngine = Painter_OpenGL1;
}
#endif
if(!painter)
if(!painter) {
painter = fallbackPainter;
m_selectedPainterEngine = fallbackPainterEngine;
}
// switch painters GL state
if(painter) {

View File

@@ -59,6 +59,8 @@ public:
std::string getVersion() { return (const char*)glGetString(GL_VERSION); }
std::string getExtensions() { return (const char*)glGetString(GL_EXTENSIONS); }
void setShouldUseShaders(bool enable) { m_shouldUseShaders = enable; }
bool ok() { return m_ok; }
bool canUseDrawArrays();
bool canUseShaders();
@@ -71,6 +73,7 @@ public:
bool canUseClampToEdge();
bool canUseBlendFuncSeparate();
bool canCacheBackbuffer();
bool shouldUseShaders() { return m_shouldUseShaders; }
bool hasScissorBug();
private:
@@ -87,6 +90,7 @@ private:
stdext::boolean<true> m_useMipmaps;
stdext::boolean<true> m_useHardwareMipmaps;
stdext::boolean<true> m_useClampToEdge;
stdext::boolean<true> m_shouldUseShaders;
stdext::boolean<true> m_cacheBackbuffer;
PainterEngine m_prefferedPainterEngine;
PainterEngine m_selectedPainterEngine;
@@ -94,4 +98,4 @@ private:
extern Graphics g_graphics;
#endif
#endif

View File

@@ -35,13 +35,11 @@ Image::Image(const Size& size, int bpp, uint8 *pixels)
memcpy(&m_pixels[0], pixels, m_pixels.size());
}
ImagePtr Image::load(const std::string& file)
ImagePtr Image::load(std::string file)
{
ImagePtr image;
try {
// currently only png images are supported
if(!stdext::ends_with(file, ".png"))
stdext::throw_exception("image file format no supported");
file = g_resources.guessFileType(file, "png");
// load image file data
image = loadPNG(file);
@@ -54,7 +52,7 @@ ImagePtr Image::load(const std::string& file)
ImagePtr Image::loadPNG(const std::string& file)
{
std::stringstream fin;
g_resources.loadFile(file, fin);
g_resources.readFileStream(file, fin);
ImagePtr image;
apng_data apng;
if(load_apng(fin, &apng) == 0) {

View File

@@ -31,7 +31,7 @@ class Image : public stdext::shared_object
public:
Image(const Size& size, int bpp = 4, uint8 *pixels = nullptr);
static ImagePtr load(const std::string& file);
static ImagePtr load(std::string file);
static ImagePtr loadPNG(const std::string& file);
void overwriteMask(const Color& maskedColor, const Color& insideColor = Color::white, const Color& outsideColor = Color::alpha);

View File

@@ -75,6 +75,7 @@ public:
virtual void drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles) = 0;
virtual void drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture) = 0;
virtual void drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) = 0;
virtual void drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) = 0;
void drawTexturedRect(const Rect& dest, const TexturePtr& texture) { drawTexturedRect(dest, texture, Rect(Point(0,0), texture->getSize())); }
virtual void drawRepeatedTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) = 0;
virtual void drawFilledRect(const Rect& dest) = 0;

View File

@@ -149,6 +149,18 @@ void PainterOGL1::drawTexturedRect(const Rect& dest, const TexturePtr& texture,
drawCoords(m_coordsBuffer, TriangleStrip);
}
void PainterOGL1::drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src)
{
if(dest.isEmpty() || src.isEmpty() || texture->isEmpty())
return;
setTexture(texture.get());
m_coordsBuffer.clear();
m_coordsBuffer.addUpsideDownQuad(dest, src);
drawCoords(m_coordsBuffer, TriangleStrip);
}
void PainterOGL1::drawRepeatedTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src)
{
if(dest.isEmpty() || src.isEmpty() || texture->isEmpty())

View File

@@ -51,6 +51,7 @@ public:
void drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles);
void drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture);
void drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src);
void drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src);
void drawRepeatedTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src);
void drawFilledRect(const Rect& dest);
void drawFilledTriangle(const Point& a, const Point& b, const Point& c);

View File

@@ -141,6 +141,19 @@ void PainterOGL2::drawTexturedRect(const Rect& dest, const TexturePtr& texture,
drawCoords(m_coordsBuffer, TriangleStrip);
}
void PainterOGL2::drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src)
{
if(dest.isEmpty() || src.isEmpty() || texture->isEmpty())
return;
setDrawProgram(m_shaderProgram ? m_shaderProgram : m_drawTexturedProgram.get());
setTexture(texture);
m_coordsBuffer.clear();
m_coordsBuffer.addUpsideDownQuad(dest, src);
drawCoords(m_coordsBuffer, TriangleStrip);
}
void PainterOGL2::drawRepeatedTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src)
{
if(dest.isEmpty() || src.isEmpty() || texture->isEmpty())

View File

@@ -43,6 +43,7 @@ public:
void drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles);
void drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture);
void drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src);
void drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src);
void drawRepeatedTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src);
void drawFilledRect(const Rect& dest);
void drawFilledTriangle(const Point& a, const Point& b, const Point& c);

View File

@@ -45,6 +45,8 @@ void PainterShaderProgram::setupUniforms()
bindUniformLocation(TIME_UNIFORM, "u_Time");
bindUniformLocation(TEX0_UNIFORM, "u_Tex0");
bindUniformLocation(TEX1_UNIFORM, "u_Tex1");
bindUniformLocation(TEX2_UNIFORM, "u_Tex2");
bindUniformLocation(TEX3_UNIFORM, "u_Tex3");
bindUniformLocation(RESOLUTION_UNIFORM, "u_Resolution");
setUniformValue(PROJECTION_MATRIX_UNIFORM, m_projectionMatrix);
@@ -54,6 +56,8 @@ void PainterShaderProgram::setupUniforms()
setUniformValue(TIME_UNIFORM, m_time);
setUniformValue(TEX0_UNIFORM, 0);
setUniformValue(TEX1_UNIFORM, 1);
setUniformValue(TEX2_UNIFORM, 2);
setUniformValue(TEX3_UNIFORM, 3);
setUniformValue(RESOLUTION_UNIFORM, (float)m_resolution.width(), (float)m_resolution.height());
}
@@ -154,7 +158,7 @@ void PainterShaderProgram::bindMultiTextures()
int i=1;
for(const TexturePtr& tex : m_multiTextures) {
glActiveTexture(GL_TEXTURE0 + 1);
glActiveTexture(GL_TEXTURE0 + i++);
glBindTexture(GL_TEXTURE_2D, tex->getId());
}

View File

@@ -20,8 +20,8 @@
* THE SOFTWARE.
*/
#ifndef PAINTERSHADER_H
#define PAINTERSHADER_H
#ifndef PAINTERSHADERPROGRAM_H
#define PAINTERSHADERPROGRAM_H
#include "shaderprogram.h"
#include "coordsbuffer.h"
@@ -40,7 +40,9 @@ protected:
TIME_UNIFORM = 4,
TEX0_UNIFORM = 5,
TEX1_UNIFORM = 6,
RESOLUTION_UNIFORM = 7,
TEX2_UNIFORM = 7,
TEX3_UNIFORM = 8,
RESOLUTION_UNIFORM = 9,
};
friend class PainterOGL2;

View File

@@ -26,9 +26,11 @@
ParticleManager g_particles;
bool ParticleManager::importParticle(const std::string& file)
bool ParticleManager::importParticle(std::string file)
{
try {
file = g_resources.guessFileType(file, "otps");
OTMLDocumentPtr doc = OTMLDocument::parse(file);
for(const OTMLNodePtr& node : doc->children()) {
if(node->tag() == "Effect") {

View File

@@ -29,7 +29,7 @@
class ParticleManager
{
public:
bool importParticle(const std::string& file);
bool importParticle(std::string file);
ParticleEffectPtr createEffect(const std::string& name);
void terminate();

View File

@@ -80,7 +80,7 @@ bool Shader::compileSourceCode(const std::string& sourceCode)
bool Shader::compileSourceFile(const std::string& sourceFile)
{
try {
std::string sourceCode = g_resources.loadFile(sourceFile);
std::string sourceCode = g_resources.readFileContents(sourceFile);
return compileSourceCode(sourceCode);
} catch(stdext::exception& e) {
g_logger.error(stdext::format("unable to load shader source form file: %s", sourceFile));

View File

@@ -35,10 +35,10 @@ public:
void bind();
void copyFromScreen(const Rect& screenRect);
bool buildHardwareMipmaps();
virtual bool buildHardwareMipmaps();
void setSmooth(bool smooth);
void setRepeat(bool repeat);
virtual void setSmooth(bool smooth);
virtual void setRepeat(bool repeat);
void setUpsideDown(bool upsideDown);
uint getId() { return m_id; }
@@ -50,6 +50,7 @@ public:
bool isEmpty() { return m_id == 0; }
bool hasRepeat() { return m_repeat; }
bool hasMipmaps() { return m_hasMipmaps; }
virtual bool isAnimatedTexture() { return false; }
protected:
void createTexture();

View File

@@ -26,6 +26,7 @@
#include "image.h"
#include <framework/core/resourcemanager.h>
#include <framework/core/clock.h>
#include <framework/graphics/apngloader.h>
TextureManager g_textures;
@@ -38,11 +39,26 @@ void TextureManager::init()
void TextureManager::terminate()
{
m_textures.clear();
m_animatedTextures.clear();
m_emptyTexture = nullptr;
}
void TextureManager::poll()
{
// update only every 16msec, this allows upto 60 fps for animated textures
static ticks_t lastUpdate = 0;
ticks_t now = g_clock.millis();
if(now - lastUpdate < 16)
return;
lastUpdate = now;
for(const AnimatedTexturePtr& animatedTexture : m_animatedTextures)
animatedTexture->updateAnimation();
}
void TextureManager::clearTexturesCache()
{
m_animatedTextures.clear();
m_textures.clear();
}
@@ -62,13 +78,11 @@ TexturePtr TextureManager::getTexture(const std::string& fileName)
// texture not found, load it
if(!texture) {
try {
// currently only png textures are supported
if(!stdext::ends_with(filePath, ".png"))
stdext::throw_exception("texture file format not supported");
std::string filePathEx = g_resources.guessFileType(filePath, "png");
// load texture file data
std::stringstream fin;
g_resources.loadFile(filePath, fin);
g_resources.readFileStream(filePathEx, fin);
texture = loadPNG(fin);
} catch(stdext::exception& e) {
g_logger.error(stdext::format("Unable to load texture '%s': %s", fileName, e.what()));
@@ -88,12 +102,22 @@ TexturePtr TextureManager::loadPNG(std::stringstream& file)
apng_data apng;
if(load_apng(file, &apng) == 0) {
Size imageSize(apng.width, apng.height);
if(apng.num_frames > 1) { // animated texture
//uchar *framesdata = apng.pdata + (apng.first_frame * apng.width * apng.height * apng.bpp);
//texture = TexturePtr(new AnimatedTexture(apng.width, apng.height, apng.bpp, apng.num_frames, framesdata, (int*)apng.frames_delay));
g_logger.error("animated textures is disabled for a while");
std::vector<ImagePtr> frames;
std::vector<int> framesDelay;
for(uint i=0;i<apng.num_frames;++i) {
uchar *frameData = apng.pdata + ((apng.first_frame+i) * imageSize.area() * apng.bpp);
int frameDelay = apng.frames_delay[i];
framesDelay.push_back(frameDelay);
frames.push_back(ImagePtr(new Image(imageSize, apng.bpp, frameData)));
}
AnimatedTexturePtr animatedTexture = new AnimatedTexture(imageSize, frames, framesDelay);
m_animatedTextures.push_back(animatedTexture);
texture = animatedTexture;
} else {
ImagePtr image = ImagePtr(new Image(Size(apng.width, apng.height), apng.bpp, apng.pdata));
ImagePtr image = ImagePtr(new Image(imageSize, apng.bpp, apng.pdata));
texture = TexturePtr(new Texture(image));
}
free_apng(&apng);

View File

@@ -30,6 +30,7 @@ class TextureManager
public:
void init();
void terminate();
void poll();
void clearTexturesCache();
TexturePtr getTexture(const std::string& fileName);
@@ -39,6 +40,7 @@ private:
TexturePtr loadPNG(std::stringstream& file);
std::unordered_map<std::string, TexturePtr> m_textures;
std::vector<AnimatedTexturePtr> m_animatedTextures;
TexturePtr m_emptyTexture;
};

View File

@@ -61,6 +61,18 @@ public:
addVertex(right, bottom);
}
inline void addUpsideDownQuad(const Rect& rect) {
float top = rect.top();
float right = rect.right()+1;
float bottom = rect.bottom()+1;
float left = rect.left();
addVertex(left, bottom);
addVertex(right, bottom);
addVertex(left, top);
addVertex(right, top);
}
void clear() { m_buffer.reset(); }
float *vertices() const { return m_buffer.data(); }
int vertexCount() const { return m_buffer.size() / 2; }