From 444c36345e34df8a1565dd9913778faacb1d4363 Mon Sep 17 00:00:00 2001
From: Rudolf Polzer <divverent@xonotic.org>
Date: Wed, 29 May 2013 19:02:33 +0200
Subject: [PATCH] initial WebP support by graphitemaster

---
 cl_screen.c   |  18 ++++++-
 cl_screen.h   |   2 +
 gl_textures.c |   5 ++
 image.c       |  30 ++++++-----
 image_webp.c  | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++
 image_webp.h  |  40 +++++++++++++++
 makefile.inc  |   1 +
 7 files changed, 221 insertions(+), 15 deletions(-)
 create mode 100644 image_webp.c
 create mode 100644 image_webp.h

diff --git a/cl_screen.c b/cl_screen.c
index 2a7af7dd..5f99d57d 100644
--- a/cl_screen.c
+++ b/cl_screen.c
@@ -51,6 +51,8 @@ cvar_t vid_pixelheight = {CVAR_SAVE, "vid_pixelheight", "1", "adjusts vertical f
 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","1", "save jpeg instead of targa"};
 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9", "image quality of saved jpeg"};
 cvar_t scr_screenshot_png = {CVAR_SAVE, "scr_screenshot_png","0", "save png instead of targa"};
+cvar_t scr_screenshot_webp = {CVAR_SAVE, "scr_screenshot_webp", "0", "save webp instead of targa"};
+cvar_t scr_screenshot_webp_quality = {CVAR_SAVE, "scr_screenshot_webp_quality", "0.9", "image quality of saved webp"};
 cvar_t scr_screenshot_gammaboost = {CVAR_SAVE, "scr_screenshot_gammaboost","1", "gamma correction on saved screenshots and videos, 1.0 saves unmodified images"};
 cvar_t scr_screenshot_hwgamma = {CVAR_SAVE, "scr_screenshot_hwgamma","1", "apply the video gamma ramp to saved screenshots and videos"};
 cvar_t scr_screenshot_alpha = {CVAR_SAVE, "scr_screenshot_alpha","0", "try to write an alpha channel to screenshots (debugging feature)"};
@@ -1332,6 +1334,8 @@ void CL_Screen_Init(void)
 	Cvar_RegisterVariable (&scr_screenshot_jpeg);
 	Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
 	Cvar_RegisterVariable (&scr_screenshot_png);
+	Cvar_RegisterVariable (&scr_screenshot_webp);
+	Cvar_RegisterVariable (&scr_screenshot_webp_quality);
 	Cvar_RegisterVariable (&scr_screenshot_gammaboost);
 	Cvar_RegisterVariable (&scr_screenshot_hwgamma);
 	Cvar_RegisterVariable (&scr_screenshot_name_in_mapdir);
@@ -1404,6 +1408,7 @@ void SCR_ScreenShot_f (void)
 	unsigned char *buffer2;
 	qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
 	qboolean png = (scr_screenshot_png.integer != 0) && !jpeg;
+	qboolean webp = (scr_screenshot_webp.integer != 0) && !png;
 	char vabuf[1024];
 
 	if (Cmd_Argc() == 2)
@@ -1415,16 +1420,25 @@ void SCR_ScreenShot_f (void)
 		{
 			jpeg = true;
 			png = false;
+			webp = false;
 		}
 		else if (!strcasecmp(ext, "tga"))
 		{
 			jpeg = false;
 			png = false;
+			webp = false;
 		}
 		else if (!strcasecmp(ext, "png"))
 		{
 			jpeg = false;
 			png = true;
+			webp = false;
+		}
+		else if (!strcasecmp(ext, "webp"))
+		{
+			jpeg = false;
+			png = false;
+			webp = true;
 		}
 		else
 		{
@@ -1454,7 +1468,7 @@ void SCR_ScreenShot_f (void)
 			return;
 		}
 
-		dpsnprintf(filename, sizeof(filename), "screenshots/%s-%02d.%s", prefix_name, shotnumber100, jpeg ? "jpg" : png ? "png" : "tga");
+		dpsnprintf(filename, sizeof(filename), "screenshots/%s-%02d.%s", prefix_name, shotnumber100, jpeg ? "jpg" : png ? "png" : webp ? "webp" : "tga");
 	}
 	else
 	{
@@ -1485,7 +1499,7 @@ void SCR_ScreenShot_f (void)
 			return;
 		}
 
-		dpsnprintf(filename, sizeof(filename), "screenshots/%s%06d.%s", prefix_name, shotnumber, jpeg ? "jpg" : png ? "png" : "tga");
+		dpsnprintf(filename, sizeof(filename), "screenshots/%s%06d.%s", prefix_name, shotnumber, jpeg ? "jpg" : png ? "png" : webp ? "webp" : "tga");
 
 		shotnumber++;
 	}
diff --git a/cl_screen.h b/cl_screen.h
index faf906b2..8d7a165c 100644
--- a/cl_screen.h
+++ b/cl_screen.h
@@ -12,6 +12,8 @@ extern cvar_t vid_pixelheight;
 extern cvar_t scr_screenshot_jpeg;
 extern cvar_t scr_screenshot_jpeg_quality;
 extern cvar_t scr_screenshot_png;
+extern cvar_t scr_screenshot_webp;
+extern cvar_t scr_screenshot_webp_quality;
 extern cvar_t scr_screenshot_gammaboost;
 extern cvar_t scr_screenshot_name;
 
diff --git a/gl_textures.c b/gl_textures.c
index 2dd11ebf..18ee3720 100644
--- a/gl_textures.c
+++ b/gl_textures.c
@@ -7,6 +7,7 @@ extern LPDIRECT3DDEVICE9 vid_d3d9dev;
 #include "image.h"
 #include "jpeg.h"
 #include "image_png.h"
+#include "image_webp.h"
 #include "intoverflow.h"
 #include "dpsoftrast.h"
 
@@ -795,6 +796,8 @@ static void r_textures_start(void)
 		Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
 	if (! PNG_OpenLibrary ())
 		Cvar_SetValueQuick (&scr_screenshot_png, 0);
+	if (! WEBP_OpenLibrary ())
+		Cvar_SetValueQuick (&scr_screenshot_webp, 0);
 }
 
 static void r_textures_shutdown(void)
@@ -802,6 +805,8 @@ static void r_textures_shutdown(void)
 	rtexturepool_t *temp;
 
 	JPEG_CloseLibrary ();
+	PNG_CloseLibrary ();
+	WEBP_CloseLibrary ();
 
 	while(gltexturepoolchain)
 	{
diff --git a/image.c b/image.c
index 366e80dd..14704620 100644
--- a/image.c
+++ b/image.c
@@ -3,6 +3,7 @@
 #include "image.h"
 #include "jpeg.h"
 #include "image_png.h"
+#include "image_webp.h"
 #include "r_shadow.h"
 
 int		image_width;
@@ -881,29 +882,32 @@ imageformat_t imageformats_dq[] =
 
 imageformat_t imageformats_textures[] =
 {
-	{"%s.tga", LoadTGA_BGRA},
-	{"%s.png", PNG_LoadImage_BGRA},
-	{"%s.jpg", JPEG_LoadImage_BGRA},
-	{"%s.pcx", LoadPCX_BGRA},
-	{"%s.wal", LoadWAL_BGRA},
+	{"%s.tga",  LoadTGA_BGRA},
+	{"%s.png",  PNG_LoadImage_BGRA},
+	{"%s.jpg",  JPEG_LoadImage_BGRA},
+	{"%s.pcx",  LoadPCX_BGRA},
+	{"%s.wal",  LoadWAL_BGRA},
+	{"%s.webp", WEBP_LoadImage_BGRA},
 	{NULL, NULL}
 };
 
 imageformat_t imageformats_gfx[] =
 {
-	{"%s.tga", LoadTGA_BGRA},
-	{"%s.png", PNG_LoadImage_BGRA},
-	{"%s.jpg", JPEG_LoadImage_BGRA},
-	{"%s.pcx", LoadPCX_BGRA},
+	{"%s.tga",  LoadTGA_BGRA},
+	{"%s.png",  PNG_LoadImage_BGRA},
+	{"%s.jpg",  JPEG_LoadImage_BGRA},
+	{"%s.pcx",  LoadPCX_BGRA},
+	{"%s.webp", WEBP_LoadImage_BGRA},
 	{NULL, NULL}
 };
 
 imageformat_t imageformats_other[] =
 {
-	{"%s.tga", LoadTGA_BGRA},
-	{"%s.png", PNG_LoadImage_BGRA},
-	{"%s.jpg", JPEG_LoadImage_BGRA},
-	{"%s.pcx", LoadPCX_BGRA},
+	{"%s.tga",  LoadTGA_BGRA},
+	{"%s.png",  PNG_LoadImage_BGRA},
+	{"%s.jpg",  JPEG_LoadImage_BGRA},
+	{"%s.pcx",  LoadPCX_BGRA},
+	{"%s.webp", WEBP_LoadImage_BGRA},
 	{NULL, NULL}
 };
 
diff --git a/image_webp.c b/image_webp.c
new file mode 100644
index 00000000..bd86ceb3
--- /dev/null
+++ b/image_webp.c
@@ -0,0 +1,140 @@
+/*
+	Copyright (C) 2013 Dale "graphitemaster" Weiler
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program 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 General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to:
+
+		Free Software Foundation, Inc.
+		59 Temple Place - Suite 330
+		Boston, MA  02111-1307, USA
+
+	(comment)
+	* I Dale Weiler allow this code to be relicensed if required.
+	* The GPL is just for compatability with darkplaces. If in the
+	* distant future this code needs to be relicensed for what ever
+	* reason, I herby allow it, no questions asked. Consider this
+	* public domain.
+	(endcomment)
+*/
+
+#include "quakedef.h"
+#include "image.h"
+#include "image_webp.h"
+
+static int            (*qwebp_get_info)          (const unsigned char *, size_t, int *, int *);
+static unsigned char *(*qwebp_decode_bgra_into)  (const unsigned char *, size_t, unsigned char *, size_t, int);
+static size_t         (*qwebp_encode_rgb)        (const unsigned char *, int, int, int, float, unsigned char **);
+static size_t         (*qwebp_encode_rgba)       (const unsigned char *, int, int, int, float, unsigned char **);
+
+static dllfunction_t webpfuncs[] =
+{
+	{"WebPGetInfo",         (void **) &qwebp_get_info},
+	{"WebPDecodeBGRAInto",  (void **) &qwebp_decode_bgra_into},
+	{"WebPEncodeRGB",       (void **) &qwebp_encode_rgb},
+	{"WebPEncodeRGBA",      (void **) &qwebp_encode_rgba},
+	{NULL, NULL}
+};
+
+dllhandle_t webp_dll = NULL;
+
+qboolean WEBP_OpenLibrary (void)
+{
+
+	// anything older than 2 doesn't have WebPGetInfo
+	const char* dllnames [] =
+	{
+#if WIN32
+		"libwebp-4.dll", // always search newest version
+		"libwebp-3.dll",
+		"libwebp-2.dll", // this one ships with SDL2
+		"libwebp_a.dll", // this one only ships with old SDL releases
+#elif defined(MACOSX)
+		"libwebp.dylib", // no versions for OSX
+#else
+		"libwebp.so.4",
+		"libwebp.so.3",
+		"libwebp.so.2",
+		"libwebp.so.0",
+		"libwebp.so",
+#endif
+		NULL
+	};
+
+	if (webp_dll)
+		return true;
+
+	if(!Sys_LoadLibrary (dllnames, &webp_dll, webpfuncs))
+		return false;
+
+	return true;
+}
+
+void WEBP_CloseLibrary (void)
+{
+	Sys_UnloadLibrary (&webp_dll);
+}
+
+unsigned char *WEBP_LoadImage_BGRA (const unsigned char *raw, int filesize, int *miplevel)
+{
+	unsigned char *data = NULL;
+
+	if (!webp_dll)
+		return NULL;
+
+	if (!qwebp_get_info(raw, filesize, &image_width, &image_height))
+		return NULL;
+
+	if ((data = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4)))
+	{
+		if (!qwebp_decode_bgra_into(raw, filesize, data, 4 * image_height, 4))
+		{
+			Con_Printf("WEBP_LoadImage : failed decode\n");
+			Mem_Free(data);
+			return NULL;
+		}
+		return data;
+	}
+	
+	Con_Printf("WEBP_LoadImage : not enough memory\n");
+	return NULL;
+}
+
+
+qboolean WEBP_SaveImage_preflipped (const char *filename, int width, int height, qboolean has_alpha, unsigned char *data)
+{
+	qfile_t       *file   = NULL;
+	unsigned char *memory = NULL;
+	unsigned int   wrote  = 0;
+	
+	size_t (*encode)(const unsigned char *, int, int, int, float, unsigned char **) = (has_alpha)
+		? qwebp_encode_rgba
+		: qwebp_encode_rgb;
+		
+
+	if (!(wrote = encode(data, width, height, 4, scr_screenshot_webp_quality.value * 100, &memory)))
+		return false;
+
+
+	if (!(file = FS_OpenRealFile(filename, "wb", true)))
+	{
+		free(memory);
+		return false;
+	}
+
+	FS_Write(file, memory, wrote);
+	FS_Close(file);
+
+	free(memory);
+	return true;
+}
diff --git a/image_webp.h b/image_webp.h
new file mode 100644
index 00000000..7b17d52c
--- /dev/null
+++ b/image_webp.h
@@ -0,0 +1,40 @@
+/*
+	Copyright (C) 2013  Dale "graphitemaster" Weiler
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program 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 General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to:
+
+		Free Software Foundation, Inc.
+		59 Temple Place - Suite 330
+		Boston, MA  02111-1307, USA
+
+	(comment)
+	* I Dale Weiler allow this code to be relicensed if required.
+	* The GPL is just for compatability with darkplaces. If in the
+	* distant future this code needs to be relicensed for what ever
+	* reason, I herby allow it, no questions asked. Consider this
+	* public domain.
+	(endcomment)
+*/
+
+#ifndef WEBP_H
+#define WEBP_H
+
+qboolean WEBP_OpenLibrary (void);
+void WEBP_CloseLibrary (void);
+unsigned char* WEBP_LoadImage_BGRA (const unsigned char *f, int filesize, int *miplevel);
+qboolean WEBP_SaveImage_preflipped (const char *filename, int width, int height, qboolean has_alpha, unsigned char *data);
+
+#endif
+
diff --git a/makefile.inc b/makefile.inc
index fe1eabe1..7ad855e2 100644
--- a/makefile.inc
+++ b/makefile.inc
@@ -133,6 +133,7 @@ OBJ_COMMON= \
 	host_cmd.o \
 	image.o \
 	image_png.o \
+	image_webp.o \
 	jpeg.o \
 	keys.o \
 	lhnet.o \
-- 
2.39.5