mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-18 12:05:14 -04:00
C client: Optimise screenshot saving with OpenGL
This commit is contained in:
parent
8782d254ac
commit
c8c22257e7
@ -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);
|
||||
|
14
src/Bitmap.c
14
src/Bitmap.c
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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";
|
||||
|
Loading…
x
Reference in New Issue
Block a user