From 0ecc4edb4338d607faf02530b59fa981e40b04e6 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Wed, 3 Nov 2021 17:24:00 +0900 Subject: [PATCH] Add webp CG support Used in the MangaGamer version of Rance 5D. The logic for the OVER chunk was taken from libsys4. --- .github/workflows/linux.yml | 2 +- .github/workflows/windows.yml | 2 +- CMakeLists.txt | 5 ++ README.md | 6 +- config.h.in | 1 + modules/SACT/sactamask.c | 2 +- modules/lib/cg.c | 23 ++++-- modules/lib/ngraph.h | 2 +- src/CMakeLists.txt | 4 ++ src/cg.h | 15 ++-- src/webp.c | 129 ++++++++++++++++++++++++++++++++++ src/webp.h | 28 ++++++++ 12 files changed, 200 insertions(+), 19 deletions(-) create mode 100644 src/webp.c create mode 100644 src/webp.h diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e324553..610ba57 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -15,7 +15,7 @@ jobs: - name: Install Deps run: | sudo apt update - sudo apt install libgtk-3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libportmidi-dev libcjson-dev + sudo apt install libgtk-3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libwebp-dev libportmidi-dev libcjson-dev - name: Build run: | diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index e4983fc..bd7a30f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -33,7 +33,7 @@ jobs: echo PATH: $PATH - name: Install Deps - run: pacman -S --noconfirm ${{ matrix.package }}-SDL2 ${{ matrix.package }}-SDL2_ttf ${{ matrix.package }}-SDL2_mixer ${{ matrix.package }}-libjpeg-turbo ${{ matrix.package }}-cjson ${{ matrix.package }}-nsis ${{ matrix.package }}-ntldd-git + run: pacman -S --noconfirm ${{ matrix.package }}-SDL2 ${{ matrix.package }}-SDL2_ttf ${{ matrix.package }}-SDL2_mixer ${{ matrix.package }}-libjpeg-turbo ${{ matrix.package }}-libwebp ${{ matrix.package }}-cjson ${{ matrix.package }}-nsis ${{ matrix.package }}-ntldd-git - name: Checkout uses: actions/checkout@v2 diff --git a/CMakeLists.txt b/CMakeLists.txt index f86eb2d..03b8c50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,11 @@ else() set(HAVE_JPEG 1) endif() + optional_pkg_check_modules(WEBP IMPORTED_TARGET libwebp) + if (WEBP_FOUND) + set(HAVE_WEBP 1) + endif() + set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads) if (CMAKE_USE_PTHREADS_INIT) diff --git a/README.md b/README.md index 3a1582c..0d86842 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Prebuilt binaries for Windows and Android can be downloaded from the [Releases]( ## Build ### Linux (Debian / Ubuntu) - $ sudo apt install build-essential cmake libgtk-3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libcjson-dev + $ sudo apt install build-essential cmake libgtk-3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libwebp-dev libcjson-dev $ mkdir -p out/debug $ cd out/debug $ cmake -DCMAKE_BUILD_TYPE=Debug ../../ @@ -18,7 +18,7 @@ Prebuilt binaries for Windows and Android can be downloaded from the [Releases]( [Homebrew](https://brew.sh/index_ja) is needed. - $ brew install cmake pkg-config sdl2 sdl2_mixer sdl2_ttf libjpeg cjson + $ brew install cmake pkg-config sdl2 sdl2_mixer sdl2_ttf libjpeg webp cjson $ mkdir -p out/debug $ cd out/debug $ cmake -DCMAKE_BUILD_TYPE=Debug ../../ @@ -28,7 +28,7 @@ Prebuilt binaries for Windows and Android can be downloaded from the [Releases]( [MSYS2](https://www.msys2.org) is needed. - $ pacman -S cmake mingw-w64-x86_64-cmake mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-cjson + $ pacman -S cmake mingw-w64-x86_64-cmake mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-libwebp mingw-w64-x86_64-cjson $ mkdir -p out/debug $ cd out/debug $ cmake -G"MSYS Makefiles" -DCMAKE_BUILD_TYPE=Debug ../../ diff --git a/config.h.in b/config.h.in index d7c7697..b50fa73 100644 --- a/config.h.in +++ b/config.h.in @@ -32,6 +32,7 @@ #cmakedefine HAVE_SDLJOY @HAVE_SDLJOY@ #cmakedefine HAVE_SIGACTION @HAVE_SIGACTION@ #cmakedefine HAVE_UNAME @HAVE_UNAME@ +#cmakedefine HAVE_WEBP @HAVE_WEBP@ #define DEFAULT_GOTHIC_TTF "@DEFAULT_FONT_PATH@MTLc3m.ttf" #define DEFAULT_MINCHO_TTF "@DEFAULT_FONT_PATH@mincho.otf" diff --git a/modules/SACT/sactamask.c b/modules/SACT/sactamask.c index 830f6c7..ee62862 100644 --- a/modules/SACT/sactamask.c +++ b/modules/SACT/sactamask.c @@ -86,7 +86,7 @@ static surface_t *smask_get(int no) { if (i == am->datanum) return NULL; - return sf_getcg(am->mmap->addr + am->offset[i]); + return sf_getcg(am->mmap->addr + am->offset[i], 0 /*FIXME*/); } // ベースになるマスクの alpha 値を拡大して取り出す diff --git a/modules/lib/cg.c b/modules/lib/cg.c index ac21782..49ec0c8 100644 --- a/modules/lib/cg.c +++ b/modules/lib/cg.c @@ -33,6 +33,9 @@ #include "ags.h" #include "pms.h" #include "qnt.h" +#ifdef HAVE_WEBP +#include "webp.h" +#endif #include "ngraph.h" #include "dri.h" #include "ald_manager.h" @@ -50,6 +53,10 @@ static CG_TYPE check_cgformat(BYTE *data) { return ALCG_PMS8; } else if (pms64k_checkfmt(data)) { return ALCG_PMS16; +#ifdef HAVE_WEBP + } else if (webp_checkfmt(data)) { + return ALCG_WEBP; +#endif } return ALCG_UNKNOWN; } @@ -61,7 +68,7 @@ static CG_TYPE check_cgformat(BYTE *data) { * @return CG が展開された surface * 未知の形式のときは NULL が返る */ -surface_t *sf_getcg(void *b) { +surface_t *sf_getcg(void *b, size_t size) { surface_t *sf = NULL; int type; cgdata *cg = NULL; @@ -77,6 +84,11 @@ surface_t *sf_getcg(void *b) { case ALCG_QNT: cg = qnt_extract(b); break; +#ifdef HAVE_WEBP + case ALCG_WEBP: + cg = webp_extract(b, size); + break; +#endif default: break; } @@ -93,7 +105,7 @@ surface_t *sf_getcg(void *b) { break; case ALCG_PMS16: - if (cg->alpha) { + if (cg->alpha) { sf = sf_create_surface(cg->width, cg->height, sf0->depth); gr_drawimage16(sf, cg, cg->x, cg->y); gr_draw_amap(sf, cg->x, cg->y, cg->alpha, cg->width, cg->height, cg->width); @@ -103,7 +115,8 @@ surface_t *sf_getcg(void *b) { } break; - case ALCG_QNT: + case ALCG_QNT: + case ALCG_WEBP: if (cg->alpha) { sf = sf_create_surface(cg->width, cg->height, sf0->depth); gr_drawimage24(sf, cg, cg->x, cg->y); @@ -112,7 +125,7 @@ surface_t *sf_getcg(void *b) { sf = sf_create_pixel(cg->width, cg->height, sf0->depth); gr_drawimage24(sf, cg, cg->x, cg->y); } - break; + break; } if (cg->pic) free(cg->pic); @@ -137,7 +150,7 @@ surface_t *sf_loadcg_no(int no) { return NULL; } - sf = sf_getcg(dfile->data); + sf = sf_getcg(dfile->data, dfile->size); ald_freedata(dfile); diff --git a/modules/lib/ngraph.h b/modules/lib/ngraph.h index 37a4c85..9c43e78 100644 --- a/modules/lib/ngraph.h +++ b/modules/lib/ngraph.h @@ -83,7 +83,7 @@ extern void gr_drawimage16(surface_t *ds, cgdata *cg, int x, int y); /* defined in cg.c */ -extern surface_t *sf_getcg(void *buf); +extern surface_t *sf_getcg(void *buf, size_t size); extern surface_t *sf_loadcg_no(int no); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 924f6f3..53f5c29 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,6 +46,10 @@ if (HAVE_JPEG) target_link_libraries(xsystem35 PRIVATE JPEG::JPEG) endif() endif() +if (HAVE_WEBP) + target_sources(xsystem35 PRIVATE webp.c) + target_link_libraries(xsystem35 PRIVATE PkgConfig::WEBP) +endif() # Music target_sources(xsystem35 PRIVATE diff --git a/src/cg.h b/src/cg.h index 6acfdf1..8680dea 100644 --- a/src/cg.h +++ b/src/cg.h @@ -32,13 +32,14 @@ */ typedef enum { ALCG_UNKNOWN = 1, - ALCG_VSP = 2, - ALCG_PMS8 = 3, - ALCG_PMS16 = 4, - ALCG_BMP8 = 5, - ALCG_BMP24 = 6, - ALCG_QNT = 7, - ALCG_JPEG = 8, + ALCG_VSP, + ALCG_PMS8, + ALCG_PMS16, + ALCG_BMP8, + ALCG_BMP24, + ALCG_QNT, + ALCG_JPEG, + ALCG_WEBP, } CG_TYPE; /* diff --git a/src/webp.c b/src/webp.c new file mode 100644 index 0000000..139f550 --- /dev/null +++ b/src/webp.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2021 kichikuou + * + * 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 the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ + +#include + +#include "system.h" +#include "webp.h" +#include "dri.h" +#include "ald_manager.h" +#include "LittleEndian.h" + +boolean webp_checkfmt(BYTE *data) { + if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F') + return FALSE; + if (data[8] != 'W' || data[9] != 'E' || data[10] != 'B' || data[11] != 'P') + return FALSE; + return TRUE; +} + +static int get_base_cg(uint8_t *data, size_t size) { + if (size < 12) + return 0; + if (data[size-12] != 'O' || data[size-11] != 'V' || data[size-10] != 'E' || data[size-9] != 'R') + return 0; + int uk = LittleEndian_getDW(data, size-8); // size? + if (uk != 4) + WARNING("WEBP: expected 0x4 preceding base CG number, got %d\n", uk); + return LittleEndian_getDW(data, size-4); +} + +static uint8_t *webp_load(uint8_t *data, size_t size, int *width, int *height, int *has_alpha) { + uint8_t *pixels = WebPDecodeRGBA(data, size, width, height); + if (!pixels) { + return NULL; + } + + WebPBitstreamFeatures features; + WebPGetFeatures(data, size, &features); + *has_alpha = features.has_alpha; + + int base = get_base_cg(data, size); + if (!base) + return pixels; + + dridata *dfile = ald_getdata(DRIFILE_CG, base - 1); + if (!dfile) { + WARNING("Cannot load base CG %d\n", base - 1); + return pixels; + } + + int base_width, base_height, base_has_alpha; + uint8_t *base_pixels = webp_load(dfile->data, dfile->size, &base_width, &base_height, &base_has_alpha); + ald_freedata(dfile); + if (!base_pixels) { + WARNING("Cannot decode base CG %d\n", base - 1); + return pixels; + } + if (*width != base_width || *height != base_height) { + WARNING("webp base CG dimensions don't match: (%d,%d) / (%d,%d)\n", + base_width, base_height, *width, *height); + WebPFree(base_pixels); + return pixels; + } + *has_alpha |= base_has_alpha; + + uint8_t *p = pixels; + uint8_t *bp = base_pixels; + for (int n = base_width * base_height; n > 0; n--) { + if (p[0] == 255 && p[1] == 0 && p[2] == 255) { + p[0] = bp[0]; + p[1] = bp[1]; + p[2] = bp[2]; + p[3] = bp[3]; + } + p += 4; + bp += 4; + } + WebPFree(base_pixels); + return pixels; +} + +static void rgba_to_rgb_and_alpha(uint8_t *rgba, uint8_t *rgb, uint8_t *alpha, int nr_pixels) { + for (int i = 0; i < nr_pixels; i++) { + *rgb++ = *rgba++; + *rgb++ = *rgba++; + *rgb++ = *rgba++; + *alpha++ = *rgba++; + } +} + +cgdata *webp_extract(BYTE *data, size_t size) { + int width, height, has_alpha; + uint8_t *rgba = webp_load(data, size, &width, &height, &has_alpha); + if (!rgba) { + WARNING("webp image decode failed"); + return NULL; + } + + cgdata *cg = calloc(1, sizeof(cgdata)); + cg->type = ALCG_WEBP; + cg->width = width; + cg->height = height; + cg->pic = malloc(cg->width * cg->height * 3); + cg->alpha = malloc(cg->width * cg->height); + rgba_to_rgb_and_alpha(rgba, cg->pic, cg->alpha, cg->width * cg->height); + if (!has_alpha) { + free(cg->alpha); + cg->alpha = NULL; + } + WebPFree(rgba); + + return cg; +} diff --git a/src/webp.h b/src/webp.h new file mode 100644 index 0000000..a0f9824 --- /dev/null +++ b/src/webp.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 kichikuou + * + * 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 the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ + +#ifndef __WEBP_H__ +#define __WEBP_H__ + +#include "cg.h" + +boolean webp_checkfmt(BYTE *data); +cgdata *webp_extract(BYTE *data, size_t size); + +#endif // __WEBP_H__