mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-10-09 07:10:05 +03:00
AJP alpha channel support
Add PMS decoder from xsystem35 for AJP alpha mask.
This commit is contained in:
+12
-1
@@ -31,10 +31,20 @@ enum cg_type {
|
||||
ALCG_UNKNOWN = 1,
|
||||
ALCG_QNT = 2,
|
||||
ALCG_AJP = 3,
|
||||
ALCG_PNG = 4
|
||||
ALCG_PNG = 4,
|
||||
ALCG_PMS8 = 5,
|
||||
ALCG_PMS16 = 6,
|
||||
};
|
||||
|
||||
struct cg_palette {
|
||||
uint8_t red[256];
|
||||
uint8_t green[256];
|
||||
uint8_t blue[256];
|
||||
};
|
||||
|
||||
struct cg_metrics {
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
int bpp;
|
||||
@@ -50,6 +60,7 @@ struct cg_metrics {
|
||||
struct cg {
|
||||
enum cg_type type; // cg format type
|
||||
struct cg_metrics metrics;
|
||||
struct cg_palette *pal;
|
||||
void *pixels;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* 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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_PMS_H
|
||||
#define SYSTEM4_PMS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct cg;
|
||||
|
||||
bool pms_checkfmt(const uint8_t *data);
|
||||
bool pms8_checkfmt(const uint8_t *data);
|
||||
bool pms16_checkfmt(const uint8_t *data);
|
||||
void pms_extract(const uint8_t *data, size_t size, struct cg *cg);
|
||||
uint8_t *pms_extract_mask(const uint8_t *data, size_t size);
|
||||
|
||||
#endif /* SYSTEM4_PMS_H */
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "little_endian.h"
|
||||
#include "system4.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/pms.h"
|
||||
|
||||
bool ajp_checkfmt(const uint8_t *data)
|
||||
{
|
||||
@@ -31,7 +32,7 @@ bool ajp_checkfmt(const uint8_t *data)
|
||||
|
||||
static const uint8_t ajp_key[] = {
|
||||
0x5d, 0x91, 0xae, 0x87,
|
||||
0x4a, 0x56, 0x51, 0xcd,
|
||||
0x4a, 0x56, 0x41, 0xcd,
|
||||
0x83, 0xec, 0x4c, 0x92,
|
||||
0xb5, 0xcb, 0x16, 0x34
|
||||
};
|
||||
@@ -98,10 +99,6 @@ void ajp_extract(uint8_t *data, size_t size, struct cg *cg)
|
||||
ajp_decrypt(jpeg_data, ajp.jpeg_size);
|
||||
ajp_decrypt(mask_data, ajp.mask_size);
|
||||
|
||||
if (ajp.mask_size) {
|
||||
WARNING("AJP mask not implemented");
|
||||
}
|
||||
|
||||
tjhandle decompressor = tjInitDecompress();
|
||||
tjDecompressHeader2(decompressor, jpeg_data, ajp.jpeg_size, &width, &height, &subsamp);
|
||||
if ((uint32_t)width != ajp.width)
|
||||
@@ -116,15 +113,25 @@ void ajp_extract(uint8_t *data, size_t size, struct cg *cg)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// XXX: add solid alpha mask
|
||||
uint8_t *mask;
|
||||
if (ajp.mask_size && pms8_checkfmt(mask_data)) {
|
||||
mask = pms_extract_mask(mask_data, ajp.mask_size);
|
||||
} else {
|
||||
if (ajp.mask_size)
|
||||
WARNING("Unsupported AJP mask format");
|
||||
mask = xmalloc(width * height);
|
||||
memset(mask, 0xFF, width * height);
|
||||
}
|
||||
|
||||
uint8_t *tmp = xmalloc(width * height * 4);
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
tmp[i*4+0] = buf[i*3+0];
|
||||
tmp[i*4+1] = buf[i*3+1];
|
||||
tmp[i*4+2] = buf[i*3+2];
|
||||
tmp[i*4+3] = 0xFF;
|
||||
tmp[i*4+3] = mask[i];
|
||||
}
|
||||
free(buf);
|
||||
free(mask);
|
||||
buf = tmp;
|
||||
|
||||
cg->type = ALCG_AJP;
|
||||
|
||||
+2
-1
@@ -7,8 +7,9 @@ system4 = ['afa.c',
|
||||
'buffer.c',
|
||||
'cg.c',
|
||||
'ex.c',
|
||||
'file.c',
|
||||
'file.c',
|
||||
'instructions.c',
|
||||
'pms.c',
|
||||
'qnt.c',
|
||||
'string.c',
|
||||
'system.c',
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* Based on code from xsystem35
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
*
|
||||
* 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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "little_endian.h"
|
||||
#include "system4.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/pms.h"
|
||||
|
||||
struct pms_header {
|
||||
int version; // PMS data version
|
||||
int header_size; // size of header
|
||||
int bpp; // PMS data depth, 8 or 16
|
||||
int bpps; // shadow data depth, if exists
|
||||
int sf; // sprite flag (not used?)
|
||||
int bf; // palette bank
|
||||
int x; // display location x
|
||||
int y; // display location y
|
||||
int width; // image width
|
||||
int height; // image height
|
||||
int dp; // data pointer
|
||||
int pp; // pointer to palette or alpha
|
||||
int cp; // pointer to comment
|
||||
};
|
||||
|
||||
bool pms_checkfmt(const uint8_t *data)
|
||||
{
|
||||
int x, y, w, h;
|
||||
|
||||
if (data[0] != 'P' || data[1] != 'M')
|
||||
return false;
|
||||
|
||||
x = LittleEndian_getDW(data, 16);
|
||||
y = LittleEndian_getDW(data, 20);
|
||||
w = LittleEndian_getDW(data, 24);
|
||||
h = LittleEndian_getDW(data, 28);
|
||||
|
||||
if (x < 0 || y < 0 || w < 0 || h < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pms8_checkfmt(const uint8_t *data)
|
||||
{
|
||||
return pms_checkfmt(data) && data[6] == 8;
|
||||
}
|
||||
|
||||
bool pms16_checkfmt(const uint8_t *data)
|
||||
{
|
||||
return pms_checkfmt(data) && data[7] == 16;
|
||||
}
|
||||
|
||||
static struct cg_palette *pms_get_palette(const uint8_t *b)
|
||||
{
|
||||
struct cg_palette *pal = xmalloc(sizeof(struct cg_palette));
|
||||
for (int i = 0; i < 256; i++) {
|
||||
pal->red[i] = *b++;
|
||||
pal->green[i] = *b++;
|
||||
pal->blue[i] = *b++;
|
||||
}
|
||||
return pal;
|
||||
}
|
||||
|
||||
/* Convert PMS8 image data to bitmap. */
|
||||
static uint8_t *pms8_extract(struct pms_header *pms, const uint8_t *b)
|
||||
{
|
||||
int n, c0, c1;
|
||||
const int scanline = pms->width;
|
||||
uint8_t *pic = xmalloc((pms->width+10)*(pms->height+10));
|
||||
|
||||
// for each line...
|
||||
for (int y = 0; y < pms->height; y ++) {
|
||||
// for each pixel...
|
||||
for (int x = 0; x < pms->width; ) {
|
||||
int loc = y * scanline + x;
|
||||
c0 = *b++;
|
||||
// non-command byte: read 1 pixel into buffer
|
||||
if (c0 <= 0xf7) {
|
||||
pic[loc] = c0;
|
||||
x++;
|
||||
}
|
||||
// copy n+3 pixels from previous line
|
||||
else if (c0 == 0xff) {
|
||||
n = (*b++) + 3;
|
||||
x += n;
|
||||
memcpy(pic + loc, pic + loc - scanline, n);
|
||||
}
|
||||
// copy n+3 pixels from 2 lines previous
|
||||
else if (c0 == 0xfe) {
|
||||
n = (*b++) + 3;
|
||||
x += n;
|
||||
memcpy(pic + loc, pic + loc - scanline * 2, n);
|
||||
}
|
||||
// repeat 1 pixel n+4 times (1-byte RLE)
|
||||
else if (c0 == 0xfd) {
|
||||
n = (*b++) + 4;
|
||||
x += n;
|
||||
c0 = *b++;
|
||||
memset(pic + loc, c0, n);
|
||||
}
|
||||
// repeast a sequence of 2 pixels n+3 times (2-byte RLE)
|
||||
else if (c0 == 0xfc) {
|
||||
n = ((*b++) + 3) * 2;
|
||||
x += n;
|
||||
c0 = *b++;
|
||||
c1 = *b++;
|
||||
for (int i = 0; i < n; i+=2) {
|
||||
pic[loc+i] = c0;
|
||||
pic[loc+i+1] = c1;
|
||||
}
|
||||
}
|
||||
// not sure why this exists, probably padding
|
||||
else {
|
||||
pic[loc] = *b++;
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pic;
|
||||
}
|
||||
|
||||
/* Convert PMS16 image data to bitmap. */
|
||||
static uint16_t *pms16_extract(struct pms_header *pms, const uint8_t *b)
|
||||
{
|
||||
int n, c0, c1, pc0, pc1;
|
||||
const int scanline = pms->width;
|
||||
uint16_t *pic = xmalloc(sizeof(uint16_t) * (pms->width+10) * (pms->height+10));
|
||||
|
||||
for (int y = 0; y < pms->height; y++) {
|
||||
for (int x = 0; x < pms->width;) {
|
||||
int loc = y * scanline + x;
|
||||
c0 = *b++;
|
||||
// non-command byte: read 1 pixel into buffer
|
||||
if (c0 <= 0xf7) {
|
||||
c1 = *b++;
|
||||
x++;
|
||||
pic[loc] = c0 | (c1 << 8);
|
||||
}
|
||||
// copy n+2 pixels from previous line into buffer
|
||||
else if (c0 == 0xff) {
|
||||
n = (*b++) + 2;
|
||||
x += n;
|
||||
for (int i = 0; i < n; i++) {
|
||||
pic[loc + i] = pic[loc + i - scanline];
|
||||
}
|
||||
}
|
||||
// copy n+2 pixels from 2 lines previous
|
||||
else if (c0 == 0xfe) {
|
||||
n = (*b++) + 2;
|
||||
x += n;
|
||||
for (int i = 0; i < n; i++) {
|
||||
pic[loc + i] = pic[loc + i - scanline * 2];
|
||||
}
|
||||
}
|
||||
// repeat a pixel n+3 times (1-byte RLE)
|
||||
else if (c0 == 0xfd) {
|
||||
n = (*b++) + 3;
|
||||
x += n;
|
||||
c0 = *b++;
|
||||
c1 = *b++;
|
||||
pc0 = c0 | (c1 << 8);
|
||||
for (int i = 0; i < n; i++) {
|
||||
pic[loc + i] = pc0;
|
||||
}
|
||||
}
|
||||
// repeat a sequence of 2 pixels n+2 times (2-byte RLE)
|
||||
else if (c0 == 0xfc) {
|
||||
n = ((*b++) + 2) * 2;
|
||||
x += n;
|
||||
c0 = *b++;
|
||||
c1 = *b++;
|
||||
pc0 = c0 | (c1 << 8);
|
||||
c0 = *b++;
|
||||
c1 = *b++;
|
||||
pc1 = c0 | (c1 << 8);
|
||||
for (int i = 0; i < n; i+=2) {
|
||||
pic[loc + i ] = pc0;
|
||||
pic[loc + i + 1] = pc1;
|
||||
}
|
||||
}
|
||||
// copy 1 pixel from previous line (left diagonal)
|
||||
else if (c0 == 0xfb) {
|
||||
x++;
|
||||
pic[loc] = pic[loc - scanline - 1];
|
||||
}
|
||||
// copy 1 pixel from previous line (right diagonal)
|
||||
else if (c0 == 0xfa) {
|
||||
x++;
|
||||
pic[loc] = pic[loc - scanline + 1];
|
||||
}
|
||||
// this one's a bit tricky, but basically what this does is it
|
||||
// combines one byte with the next n bytes
|
||||
else if (c0 == 0xf9) {
|
||||
n = (*b++) + 1;
|
||||
x += n;
|
||||
c0 = *b++;
|
||||
c1 = *b++;
|
||||
pc0 = ((c0 & 0xe0)<<8) + ((c0 & 0x18)<<6) + ((c0 & 0x07)<<2);
|
||||
pc1 = ((c1 & 0xc0)<<5) + ((c1 & 0x3c)<<3) + (c1 & 0x03);
|
||||
pic[loc] = pc0 + pc1;
|
||||
for (int i = 1; i < n; i++) {
|
||||
c1 = *b++;
|
||||
pc1 = ((c1 & 0xc0)<<5) + ((c1 & 0x3c)<<3) + (c1 & 0x03);
|
||||
pic[loc + i] = pc0 | pc1;
|
||||
}
|
||||
}
|
||||
// not sure why this exists, probably padding
|
||||
else {
|
||||
c0 = *b++;
|
||||
c1 = *b++;
|
||||
x++;
|
||||
pic[loc] = c0 | (c1 << 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pic;
|
||||
}
|
||||
|
||||
static void pms_read_header(struct pms_header *hdr, const uint8_t *data)
|
||||
{
|
||||
hdr->version = LittleEndian_getW(data, 2);
|
||||
hdr->header_size = LittleEndian_getW(data, 4);
|
||||
hdr->bpp = data[6];
|
||||
hdr->bpps = data[7];
|
||||
hdr->sf = data[8];
|
||||
hdr->bf = LittleEndian_getW(data, 10);
|
||||
hdr->x = LittleEndian_getDW(data, 16);
|
||||
hdr->y = LittleEndian_getDW(data, 20);
|
||||
hdr->width = LittleEndian_getDW(data, 24);
|
||||
hdr->height = LittleEndian_getDW(data, 28);
|
||||
hdr->dp = LittleEndian_getDW(data, 32);
|
||||
hdr->pp = LittleEndian_getDW(data, 36);
|
||||
hdr->cp = LittleEndian_getDW(data, 40);
|
||||
}
|
||||
|
||||
static void pms_init_metrics(struct pms_header *pms, struct cg_metrics *dst)
|
||||
{
|
||||
dst->x = pms->x;
|
||||
dst->y = pms->y;
|
||||
dst->w = pms->width;
|
||||
dst->h = pms->height;
|
||||
dst->bpp = pms->bpp;
|
||||
dst->has_pixel = pms->dp;
|
||||
dst->has_alpha = pms->pp;
|
||||
dst->pixel_pitch = pms->width * (pms->bpp / 8);
|
||||
dst->alpha_pitch = 1;
|
||||
}
|
||||
|
||||
/* Load a PMS8 CG. */
|
||||
static void pms8_load(const uint8_t *data, struct pms_header *pms, struct cg *cg)
|
||||
{
|
||||
cg->pal = pms_get_palette(data + pms->pp);
|
||||
|
||||
cg->type = ALCG_PMS8;
|
||||
cg->pixels = pms8_extract(pms, data + pms->dp);
|
||||
// TODO: convert to RGBA
|
||||
}
|
||||
|
||||
static void pms16_load(const uint8_t *data, struct pms_header *pms, struct cg *cg)
|
||||
{
|
||||
cg->type = ALCG_PMS16;
|
||||
cg->pixels = pms16_extract(pms, data + pms->dp);
|
||||
|
||||
if (pms->pp) {
|
||||
// TODO: alpha channel
|
||||
//uint8_t *alpha = malloc((pms->width + 10) * (pms->height + 10));
|
||||
//pms8_extract(pms, alpha, data + pms->pp);
|
||||
}
|
||||
// TODO: convert to RGBA
|
||||
}
|
||||
|
||||
void pms_extract(const uint8_t *data, size_t size, struct cg *cg)
|
||||
{
|
||||
struct pms_header pms;
|
||||
pms_read_header(&pms, data);
|
||||
pms_init_metrics(&pms, &cg->metrics);
|
||||
|
||||
if ((size_t)pms.dp > size)
|
||||
ERROR("PMS pixel offset out of bounds");
|
||||
if ((size_t)pms.pp > size)
|
||||
ERROR("PMS palette/alpha offset out of bounds");
|
||||
|
||||
if (pms.bpp == 8)
|
||||
pms8_load(data, &pms, cg);
|
||||
else if (pms.bpp == 16)
|
||||
pms16_load(data, &pms, cg);
|
||||
else
|
||||
WARNING("Unsupported PMS bpp: %d", pms.bpp);
|
||||
}
|
||||
|
||||
uint8_t *pms_extract_mask(const uint8_t *data, size_t size)
|
||||
{
|
||||
struct pms_header pms;
|
||||
pms_read_header(&pms, data);
|
||||
|
||||
if ((size_t)pms.dp > size)
|
||||
ERROR("PMS pixel offset out of bounds");
|
||||
if ((size_t)pms.pp > size)
|
||||
ERROR("PMS palette/alpha offset out of bounds");
|
||||
|
||||
if (pms.bpp != 8)
|
||||
ERROR("PMS mask is not 8bpp");
|
||||
|
||||
return pms8_extract(&pms, data + pms.dp);
|
||||
}
|
||||
Reference in New Issue
Block a user