Bitmap#get_pixel: Use cached client side copy of pixels

A very simplistic optimization for games that create a
Bitmap and then only ever query pixels from it.
This commit is contained in:
Jonas Kulla 2014-07-11 03:25:28 +02:00
parent caae9c5689
commit d1bad9b45f
1 changed files with 65 additions and 23 deletions

View File

@ -25,6 +25,7 @@
#include <SDL_image.h> #include <SDL_image.h>
#include <SDL_ttf.h> #include <SDL_ttf.h>
#include <SDL_rect.h> #include <SDL_rect.h>
#include <SDL_surface.h>
#include <pixman.h> #include <pixman.h>
@ -53,6 +54,8 @@
struct BitmapPrivate struct BitmapPrivate
{ {
Bitmap *self;
TEXFBO gl; TEXFBO gl;
Font *font; Font *font;
@ -63,6 +66,12 @@ struct BitmapPrivate
* any context other than as Tilesets */ * any context other than as Tilesets */
SDL_Surface *megaSurface; SDL_Surface *megaSurface;
/* A cached version of the bitmap in client memory, for
* getPixel calls. Is invalidated any time the bitmap
* is modified */
SDL_Surface *surface;
SDL_PixelFormat *format;
/* The 'tainted' area describes which parts of the /* The 'tainted' area describes which parts of the
* bitmap are not cleared, ie. don't have 0 opacity. * bitmap are not cleared, ie. don't have 0 opacity.
* If we're blitting / drawing text to a cleared part * If we're blitting / drawing text to a cleared part
@ -71,9 +80,13 @@ struct BitmapPrivate
* ourselves the expensive blending calculation */ * ourselves the expensive blending calculation */
pixman_region16_t tainted; pixman_region16_t tainted;
BitmapPrivate() BitmapPrivate(Bitmap *self)
: megaSurface(0) : self(self),
megaSurface(0),
surface(0)
{ {
format = SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888);
font = &shState->defaultFont(); font = &shState->defaultFont();
pixman_region_init(&tainted); pixman_region_init(&tainted);
} }
@ -83,6 +96,13 @@ struct BitmapPrivate
pixman_region_fini(&tainted); pixman_region_fini(&tainted);
} }
void allocSurface()
{
surface = SDL_CreateRGBSurface(0, gl.width, gl.height, format->BitsPerPixel,
format->Rmask, format->Gmask,
format->Bmask, format->Amask);
}
void clearTaintedArea() void clearTaintedArea()
{ {
pixman_region_clear(&tainted); pixman_region_clear(&tainted);
@ -175,6 +195,17 @@ struct BitmapPrivate
SDL_FreeSurface(surf); SDL_FreeSurface(surf);
surf = surfConv; surf = surfConv;
} }
void onModified()
{
if (surface)
{
SDL_FreeSurface(surface);
surface = 0;
}
self->modified();
}
}; };
Bitmap::Bitmap(const char *filename) Bitmap::Bitmap(const char *filename)
@ -193,7 +224,7 @@ Bitmap::Bitmap(const char *filename)
if (imgSurf->w > glState.caps.maxTexSize || imgSurf->h > glState.caps.maxTexSize) if (imgSurf->w > glState.caps.maxTexSize || imgSurf->h > glState.caps.maxTexSize)
{ {
/* Mega surface */ /* Mega surface */
p = new BitmapPrivate; p = new BitmapPrivate(this);
p->megaSurface = imgSurf; p->megaSurface = imgSurf;
} }
else else
@ -211,7 +242,7 @@ Bitmap::Bitmap(const char *filename)
throw e; throw e;
} }
p = new BitmapPrivate; p = new BitmapPrivate(this);
p->gl = tex; p->gl = tex;
TEX::bind(p->gl.tex); TEX::bind(p->gl.tex);
@ -230,7 +261,7 @@ Bitmap::Bitmap(int width, int height)
TEXFBO tex = shState->texPool().request(width, height); TEXFBO tex = shState->texPool().request(width, height);
p = new BitmapPrivate; p = new BitmapPrivate(this);
p->gl = tex; p->gl = tex;
clear(); clear();
@ -240,7 +271,7 @@ Bitmap::Bitmap(const Bitmap &other)
{ {
other.ensureNonMega(); other.ensureNonMega();
p = new BitmapPrivate; p = new BitmapPrivate(this);
p->gl = shState->texPool().request(other.width(), other.height()); p->gl = shState->texPool().request(other.width(), other.height());
@ -345,7 +376,7 @@ void Bitmap::stretchBlt(const IntRect &destRect,
SDL_FreeSurface(blitTemp); SDL_FreeSurface(blitTemp);
modified(); p->onModified();
return; return;
} }
@ -395,7 +426,7 @@ void Bitmap::stretchBlt(const IntRect &destRect,
p->addTaintedArea(destRect); p->addTaintedArea(destRect);
modified(); p->onModified();
} }
void Bitmap::fillRect(int x, int y, void Bitmap::fillRect(int x, int y,
@ -420,7 +451,7 @@ void Bitmap::fillRect(const IntRect &rect, const Vec4 &color)
/* Fill op */ /* Fill op */
p->addTaintedArea(rect); p->addTaintedArea(rect);
modified(); p->onModified();
} }
#ifdef RGSS2 #ifdef RGSS2
@ -473,7 +504,7 @@ void Bitmap::gradientFillRect(const IntRect &rect,
p->addTaintedArea(rect); p->addTaintedArea(rect);
modified(); p->onModified();
} }
void Bitmap::clearRect(int x, int y, int width, int height) void Bitmap::clearRect(int x, int y, int width, int height)
@ -489,7 +520,7 @@ void Bitmap::clearRect(const IntRect &rect)
p->fillRect(rect, Vec4()); p->fillRect(rect, Vec4());
modified(); p->onModified();
} }
void Bitmap::blur() void Bitmap::blur()
@ -534,7 +565,7 @@ void Bitmap::blur()
shState->texPool().release(auxTex); shState->texPool().release(auxTex);
modified(); p->onModified();
} }
void Bitmap::radialBlur(int angle, int divisions) void Bitmap::radialBlur(int angle, int divisions)
@ -629,7 +660,7 @@ void Bitmap::radialBlur(int angle, int divisions)
shState->texPool().release(p->gl); shState->texPool().release(p->gl);
p->gl = newTex; p->gl = newTex;
modified(); p->onModified();
} }
#endif #endif
@ -650,7 +681,7 @@ void Bitmap::clear()
p->clearTaintedArea(); p->clearTaintedArea();
modified(); p->onModified();
} }
Color Bitmap::getPixel(int x, int y) const Color Bitmap::getPixel(int x, int y) const
@ -662,16 +693,27 @@ Color Bitmap::getPixel(int x, int y) const
if (x < 0 || y < 0 || x >= width() || y >= height()) if (x < 0 || y < 0 || x >= width() || y >= height())
return Vec4(); return Vec4();
FBO::bind(p->gl.fbo, FBO::Read); if (!p->surface)
{
p->allocSurface();
glState.viewport.pushSet(IntRect(0, 0, width(), height())); FBO::bind(p->gl.fbo, FBO::Read);
uint8_t pixel[4]; glState.viewport.pushSet(IntRect(0, 0, width(), height()));
gl.ReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
glState.viewport.pop(); gl.ReadPixels(0, 0, width(), height(), GL_RGBA, GL_UNSIGNED_BYTE, p->surface->pixels);
return Color(pixel[0], pixel[1], pixel[2], pixel[3]); glState.viewport.pop();
}
size_t offset = x*p->format->BytesPerPixel + y*p->surface->pitch;
uint8_t *bytes = (uint8_t*) p->surface->pixels + offset;
uint32_t pixel = *((uint32_t*) bytes);
return Color((pixel >> p->format->Rshift) & 0xFF,
(pixel >> p->format->Gshift) & 0xFF,
(pixel >> p->format->Bshift) & 0xFF,
(pixel >> p->format->Ashift) & 0xFF);
} }
void Bitmap::setPixel(int x, int y, const Color &color) void Bitmap::setPixel(int x, int y, const Color &color)
@ -693,7 +735,7 @@ void Bitmap::setPixel(int x, int y, const Color &color)
p->addTaintedArea(IntRect(x, y, 1, 1)); p->addTaintedArea(IntRect(x, y, 1, 1));
modified(); p->onModified();
} }
void Bitmap::hueChange(int hue) void Bitmap::hueChange(int hue)
@ -734,7 +776,7 @@ void Bitmap::hueChange(int hue)
shState->texPool().release(p->gl); shState->texPool().release(p->gl);
p->gl = newTex; p->gl = newTex;
modified(); p->onModified();
} }
void Bitmap::drawText(int x, int y, void Bitmap::drawText(int x, int y,
@ -914,7 +956,7 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align)
SDL_FreeSurface(txtSurf); SDL_FreeSurface(txtSurf);
p->addTaintedArea(posRect); p->addTaintedArea(posRect);
modified(); p->onModified();
} }
/* http://www.lemoda.net/c/utf8-to-ucs2/index.html */ /* http://www.lemoda.net/c/utf8-to-ucs2/index.html */