Permalink
Browse files

C client: Optimise screenshot saving with OpenGL

  • Loading branch information...
UnknownShadow200 committed Oct 10, 2018
1 parent 8782d25 commit c8c22257e7ff469e20829f42c68385dac1922011
Showing with 33 additions and 46 deletions.
  1. +1 −1 src/Animations.c
  2. +8 −6 src/Bitmap.c
  3. +4 −3 src/Bitmap.h
  4. +1 −1 src/Entity.c
  5. +3 −3 src/Game.c
  6. +6 −20 src/Graphics.c
  7. +6 −8 src/Graphics.h
  8. +2 −2 src/MapRenderer.c
  9. +2 −2 src/TexturePack.c
View
@@ -324,7 +324,7 @@ static void Animations_PackChanged(void* obj) {
static void Animations_FileChanged(void* obj, struct Stream* stream, const String* name) {
if (String_CaselessEqualsConst(name, "animations.png")) {
ReturnCode res = Bitmap_DecodePng(&anims_bmp, stream);
ReturnCode res = Png_Decode(&anims_bmp, stream);
if (!res) return;
Chat_LogError2(res, "decoding", name);
View
@@ -58,7 +58,7 @@ enum PNG_FILTER {
typedef void (*Png_RowExpander)(int width, uint32_t* palette, uint8_t* src, uint32_t* dst);
uint8_t png_sig[PNG_SIG_SIZE] = { 137, 80, 78, 71, 13, 10, 26, 10 };
bool Bitmap_DetectPng(uint8_t* data, uint32_t len) {
bool Png_Detect(uint8_t* data, uint32_t len) {
if (len < PNG_SIG_SIZE) return false;
int i;
@@ -324,14 +324,14 @@ static void Png_ComputeTransparency(Bitmap* bmp, uint32_t transparentCol) {
/* Most bits per sample is 16. Most samples per pixel is 4. Add 1 for filter byte. */
#define PNG_BUFFER_SIZE ((PNG_MAX_DIMS * 2 * 4 + 1) * 2)
/* TODO: Test a lot of .png files and ensure output is right */
ReturnCode Bitmap_DecodePng(Bitmap* bmp, struct Stream* stream) {
ReturnCode Png_Decode(Bitmap* bmp, struct Stream* stream) {
Bitmap_Create(bmp, 0, 0, NULL);
uint8_t tmp[PNG_PALETTE * 3];
ReturnCode res;
res = Stream_Read(stream, tmp, PNG_SIG_SIZE);
if (res) return res;
if (!Bitmap_DetectPng(tmp, PNG_SIG_SIZE)) return PNG_ERR_INVALID_SIG;
if (!Png_Detect(tmp, PNG_SIG_SIZE)) return PNG_ERR_INVALID_SIG;
uint32_t transparentCol = PackedCol_ARGB(0, 0, 0, 255);
uint8_t col, bitsPerSample, bytesPerPixel;
@@ -607,7 +607,7 @@ static void Png_EncodeRow(uint8_t* src, uint8_t* cur, uint8_t* prior, uint8_t* b
best[0] = bestFilter;
}
ReturnCode Bitmap_EncodePng(Bitmap* bmp, struct Stream* stream) {
ReturnCode Png_Encode(Bitmap* bmp, struct Stream* stream, Png_RowSelector selectRow) {
ReturnCode res;
uint8_t tmp[32];
if ((res = Stream_Write(stream, png_sig, PNG_SIG_SIZE))) return res;
@@ -649,9 +649,11 @@ ReturnCode Bitmap_EncodePng(Bitmap* bmp, struct Stream* stream) {
ZLib_MakeStream(&zlStream, &zlState, stream);
for (y = 0; y < bmp->Height; y++) {
uint8_t* src = (uint8_t*)Bitmap_GetRow(bmp, y);
int row = selectRow(bmp, y);
uint8_t* src = (uint8_t*)Bitmap_GetRow(bmp, row);
uint8_t* prev = (y & 1) == 0 ? prevLine : curLine;
uint8_t* cur = (y & 1) == 0 ? curLine : prevLine;
uint8_t* cur = (y & 1) == 0 ? curLine : prevLine;
Png_EncodeRow(src, cur, prev, bestLine, lineSize);
/* +1 for filter byte */
if ((res = Stream_Write(&zlStream, bestLine, lineSize + 1))) return res;
View
@@ -19,12 +19,13 @@ void Bitmap_Allocate(Bitmap* bmp, int width, int height);
/* Allocates a power-of-2 sized bitmap larger or equal to to the given size, and clears it to 0. You are responsible for freeing its memory! */
void Bitmap_AllocateClearedPow2(Bitmap* bmp, int width, int height);
bool Bitmap_DetectPng(uint8_t* data, uint32_t len);
bool Png_Detect(uint8_t* data, uint32_t len);
typedef int (*Png_RowSelector)(Bitmap* bmp, int row);
/*
Partially based off information from
https://handmade.network/forums/wip/t/2363-implementing_a_basic_png_reader_the_handmade_way
https://github.com/nothings/stb/blob/master/stb_image.h
*/
ReturnCode Bitmap_DecodePng(Bitmap* bmp, struct Stream* stream);
ReturnCode Bitmap_EncodePng(Bitmap* bmp, struct Stream* stream);
ReturnCode Png_Decode(Bitmap* bmp, struct Stream* stream);
ReturnCode Png_Encode(Bitmap* bmp, struct Stream* stream, Png_RowSelector selectRow);
#endif
View
@@ -697,7 +697,7 @@ static void Player_CheckSkin(struct Player* p) {
struct Stream mem; Bitmap bmp;
Stream_ReadonlyMemory(&mem, item.ResultData, item.ResultSize);
ReturnCode res = Bitmap_DecodePng(&bmp, &mem);
ReturnCode res = Png_Decode(&bmp, &mem);
if (res) {
Chat_LogError2(res, "decoding", &url);
Mem_Free(bmp.Scan0); return;
View
@@ -209,7 +209,7 @@ bool Game_CanPick(BlockID block) {
bool Game_UpdateTexture(GfxResourceID* texId, struct Stream* src, const String* file, uint8_t* skinType) {
Bitmap bmp;
ReturnCode res = Bitmap_DecodePng(&bmp, src);
ReturnCode res = Png_Decode(&bmp, src);
if (res) { Chat_LogError2(res, "decoding", file); }
bool success = !res && Game_ValidateBitmap(file, &bmp);
@@ -287,7 +287,7 @@ static void Game_OnNewMapLoadedCore(void* obj) {
static void Game_TextureChangedCore(void* obj, struct Stream* src, const String* name) {
Bitmap bmp;
if (String_CaselessEqualsConst(name, "terrain.png")) {
ReturnCode res = Bitmap_DecodePng(&bmp, src);
ReturnCode res = Png_Decode(&bmp, src);
if (res) {
Chat_LogError2(res, "decoding", name);
@@ -296,7 +296,7 @@ static void Game_TextureChangedCore(void* obj, struct Stream* src, const String*
Mem_Free(bmp.Scan0);
}
} else if (String_CaselessEqualsConst(name, "default.png")) {
ReturnCode res = Bitmap_DecodePng(&bmp, src);
ReturnCode res = Png_Decode(&bmp, src);
if (res) {
Chat_LogError2(res, "decoding", name);
View
@@ -393,7 +393,7 @@ void Gfx_SetAlphaArgBlend(bool enabled) {
}
void Gfx_ClearCol(PackedCol col) { d3d9_clearCol = col.Packed; }
void Gfx_SetColourWriteMask(bool r, bool g, bool b, bool a) {
void Gfx_SetColWriteMask(bool r, bool g, bool b, bool a) {
DWORD channels = (r ? 1u : 0u) | (g ? 2u : 0u) | (b ? 4u : 0u) | (a ? 8u : 0u);
D3D9_SetRenderState(D3DRS_COLORWRITEENABLE, channels, "D3D9_SetColourWrite");
}
@@ -608,6 +608,7 @@ void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zNear, float zFar,
/*########################################################################################################################*
*-----------------------------------------------------------Misc----------------------------------------------------------*
*#########################################################################################################################*/
static int D3D9_SelectRow(Bitmap* bmp, int y) { return y; }
ReturnCode Gfx_TakeScreenshot(struct Stream* output, int width, int height) {
IDirect3DSurface9* backbuffer = NULL;
IDirect3DSurface9* temp = NULL;
@@ -625,7 +626,7 @@ ReturnCode Gfx_TakeScreenshot(struct Stream* output, int width, int height) {
if (res) goto finished;
{
Bitmap bmp; Bitmap_Create(&bmp, width, height, rect.pBits);
res = Bitmap_EncodePng(&bmp, output);
res = Png_Encode(&bmp, output, D3D9_SelectRow);
if (res) { IDirect3DSurface9_UnlockRect(temp); goto finished; }
}
res = IDirect3DSurface9_UnlockRect(temp);
@@ -954,7 +955,7 @@ void Gfx_ClearCol(PackedCol col) {
gl_lastClearCol = col;
}
void Gfx_SetColourWriteMask(bool r, bool g, bool b, bool a) {
void Gfx_SetColWriteMask(bool r, bool g, bool b, bool a) {
glColorMask(r, g, b, a);
}
@@ -1219,27 +1220,12 @@ void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zNear, float zFar,
/*########################################################################################################################*
*-----------------------------------------------------------Misc----------------------------------------------------------*
*#########################################################################################################################*/
static int GL_SelectRow(Bitmap* bmp, int y) { return (bmp->Height - 1) - y; }
ReturnCode Gfx_TakeScreenshot(struct Stream* output, int width, int height) {
Bitmap bmp; Bitmap_Allocate(&bmp, width, height);
glReadPixels(0, 0, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, bmp.Scan0);
uint8_t tmp[PNG_MAX_DIMS * BITMAP_SIZEOF_PIXEL];
/* flip vertically around y */
int x, y;
uint32_t stride = (uint32_t)(bmp.Width) * BITMAP_SIZEOF_PIXEL;
for (y = 0; y < height / 2; y++) {
uint32_t* src = Bitmap_GetRow(&bmp, y);
uint32_t* dst = Bitmap_GetRow(&bmp, (height - 1) - y);
Mem_Copy(tmp, src, stride);
Mem_Copy(src, dst, stride);
Mem_Copy(dst, tmp, stride);
/*for (x = 0; x < bmp.Width; x++) {
uint32_t temp = dst[x]; dst[x] = src[x]; src[x] = temp;
}*/
}
ReturnCode res = Bitmap_EncodePng(&bmp, output);
ReturnCode res = Png_Encode(&bmp, output, GL_SelectRow);
Mem_Free(bmp.Scan0);
return res;
}
View
@@ -9,10 +9,6 @@
*/
struct Stream;
#define ICOUNT(verticesCount) (((verticesCount) >> 2) * 6)
#define VERTEX_FORMAT_P3FC4B 0
#define VERTEX_FORMAT_P3FT2FC4B 1
enum COMPARE_FUNC {
COMPARE_FUNC_ALWAYS, COMPARE_FUNC_NOTEQUAL, COMPARE_FUNC_NEVER,
COMPARE_FUNC_LESS, COMPARE_FUNC_LESSEQUAL, COMPARE_FUNC_EQUAL,
@@ -23,8 +19,9 @@ enum BLEND_FUNC {
BLEND_FUNC_INV_SRC_ALPHA, BLEND_FUNC_DST_ALPHA, BLEND_FUNC_INV_DST_ALPHA,
};
enum FOG_FUNC { FOG_LINEAR, FOG_EXP, FOG_EXP2, };
enum MATRIX_TYPE { MATRIX_TYPE_PROJECTION, MATRIX_TYPE_VIEW, MATRIX_TYPE_TEXTURE };
enum VERTEX_FORMAT { VERTEX_FORMAT_P3FC4B, VERTEX_FORMAT_P3FT2FC4B };
enum FOG_FUNC { FOG_LINEAR, FOG_EXP, FOG_EXP2 };
enum MATRIX_TYPE { MATRIX_TYPE_PROJECTION, MATRIX_TYPE_VIEW, MATRIX_TYPE_TEXTURE };
void Gfx_Init(void);
void Gfx_Free(void);
@@ -36,10 +33,11 @@ bool Gfx_Mipmaps;
bool Gfx_CustomMipmapsLevels;
struct Matrix Gfx_View, Gfx_Projection;
#define ICOUNT(verticesCount) (((verticesCount) >> 2) * 6)
#define GFX_MAX_INDICES (65536 / 4 * 6)
#define GFX_MAX_VERTICES 65536
/* Callback invoked when the current context is lost, and is repeatedly invoked until the context can be retrieved. */
/* Callback invoked when the context is lost. Repeatedly invoked until a context can be retrieved. */
ScheduledTaskCallback Gfx_LostContextFunction;
GfxResourceID Gfx_CreateTexture(Bitmap* bmp, bool managedPool, bool mipmaps);
@@ -69,7 +67,7 @@ void Gfx_Clear(void);
void Gfx_ClearCol(PackedCol col);
void Gfx_SetDepthTest(bool enabled);
void Gfx_SetDepthTestFunc(int compareFunc);
void Gfx_SetColourWriteMask(bool r, bool g, bool b, bool a);
void Gfx_SetColWriteMask(bool r, bool g, bool b, bool a);
void Gfx_SetDepthWrite(bool enabled);
GfxResourceID Gfx_CreateDynamicVb(int vertexFormat, int maxVertices);
View
@@ -220,7 +220,7 @@ void MapRenderer_RenderTranslucent(double delta) {
Gfx_SetBatchFormat(VERTEX_FORMAT_P3FT2FC4B);
Gfx_SetTexturing(false);
Gfx_SetAlphaBlending(false);
Gfx_SetColourWriteMask(false, false, false, false);
Gfx_SetColWriteMask(false, false, false, false);
int batch;
for (batch = 0; batch < MapRenderer_1DUsedCount; batch++) {
@@ -235,7 +235,7 @@ void MapRenderer_RenderTranslucent(double delta) {
/* Then actually draw the transluscent blocks */
Gfx_SetAlphaBlending(true);
Gfx_SetTexturing(true);
Gfx_SetColourWriteMask(true, true, true, true);
Gfx_SetColWriteMask(true, true, true, true);
Gfx_SetDepthWrite(false); /* we already calculated depth values in depth pass */
Gfx_EnableMipmaps();
View
@@ -440,7 +440,7 @@ void TexturePack_ExtractZip_File(const String* filename) {
ReturnCode TexturePack_ExtractTerrainPng(struct Stream* stream) {
Bitmap bmp;
ReturnCode res = Bitmap_DecodePng(&bmp, stream);
ReturnCode res = Png_Decode(&bmp, stream);
if (!res) {
Event_RaiseVoid(&TextureEvents_PackChanged);
@@ -499,7 +499,7 @@ void TexturePack_Extract_Req(struct AsyncRequest* item) {
TextureCache_AddLastModified(&url, &item->LastModified);
struct Stream mem; Stream_ReadonlyMemory(&mem, data, len);
bool png = Bitmap_DetectPng(data, len);
bool png = Png_Detect(data, len);
ReturnCode res = png ? TexturePack_ExtractTerrainPng(&mem)
: TexturePack_ExtractZip(&mem);
const char* operation = png ? "decoding" : "extracting";

0 comments on commit c8c2225

Please sign in to comment.