/* * 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(uint8_t *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) { // Skip 'NULL' chunk if any. if (size >= 12 && data[size-12] == 'N' && data[size-11] == 'U' && data[size-10] == 'L' && data[size-9] == 'L' && LittleEndian_getDW(data, size - 8) == 4) { size -= 12; } if (size >= 12 && data[size-12] == 'O' && data[size-11] == 'V' && data[size-10] == 'E' && data[size-9] == 'R' && LittleEndian_getDW(data, size - 8) == 4) { return LittleEndian_getDW(data, size - 4); } return 0; } 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", 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", base - 1); return pixels; } if (*width != base_width || *height != base_height) { WARNING("webp base CG dimensions don't match: (%d,%d) / (%d,%d)", 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(uint8_t *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->depth = 24; 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; }