471 lines
12 KiB
C++
471 lines
12 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* 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 3 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://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* This code contains portions of Libart_LGPL - library of basic graphic primitives
|
|
*
|
|
* Copyright (c) 1998 Raph Levien
|
|
*
|
|
* Licensed under GNU LGPL v2
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* This code contains portions of Swfdec
|
|
*
|
|
* Copyright (c) 2004-2006 David Schleef <ds@schleef.org>
|
|
*
|
|
* Licensed under GNU GPL v2
|
|
*
|
|
*/
|
|
|
|
#include "sword25/gfx/image/art.h"
|
|
#include "sword25/gfx/image/vectorimage.h"
|
|
|
|
namespace Sword25 {
|
|
|
|
void art_rgb_fill_run1(byte *buf, byte r, byte g, byte b, int n) {
|
|
int i;
|
|
|
|
if (r == g && g == b && r == 255) {
|
|
memset(buf, g, n + n + n + n);
|
|
} else {
|
|
uint32 *alt = (uint32 *)buf;
|
|
uint32 color = MS_RGB(r, g, b);
|
|
|
|
for (i = 0; i < n; i++)
|
|
*alt++ = color;
|
|
}
|
|
}
|
|
|
|
void art_rgb_run_alpha1(byte *buf, byte r, byte g, byte b, int alpha, int n) {
|
|
int i;
|
|
int v;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
#if defined(SCUMM_LITTLE_ENDIAN)
|
|
v = *buf;
|
|
*buf++ = MIN(v + alpha, 0xff);
|
|
v = *buf;
|
|
*buf++ = v + (((b - v) * alpha + 0x80) >> 8);
|
|
v = *buf;
|
|
*buf++ = v + (((g - v) * alpha + 0x80) >> 8);
|
|
v = *buf;
|
|
*buf++ = v + (((r - v) * alpha + 0x80) >> 8);
|
|
#else
|
|
v = *buf;
|
|
*buf++ = v + (((r - v) * alpha + 0x80) >> 8);
|
|
v = *buf;
|
|
*buf++ = v + (((g - v) * alpha + 0x80) >> 8);
|
|
v = *buf;
|
|
*buf++ = v + (((b - v) * alpha + 0x80) >> 8);
|
|
v = *buf;
|
|
*buf++ = MIN(v + alpha, 0xff);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
|
|
|
|
struct _ArtRgbSVPAlphaData {
|
|
int alphatab[256];
|
|
byte r, g, b, alpha;
|
|
byte *buf;
|
|
int rowstride;
|
|
int x0, x1;
|
|
};
|
|
|
|
static void art_rgb_svp_alpha_callback1(void *callback_data, int y,
|
|
int start, ArtSVPRenderAAStep *steps, int n_steps) {
|
|
ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
|
|
byte *linebuf;
|
|
int run_x0, run_x1;
|
|
uint32 running_sum = start;
|
|
int x0, x1;
|
|
int k;
|
|
byte r, g, b;
|
|
int *alphatab;
|
|
int alpha;
|
|
|
|
linebuf = data->buf;
|
|
x0 = data->x0;
|
|
x1 = data->x1;
|
|
|
|
r = data->r;
|
|
g = data->g;
|
|
b = data->b;
|
|
alphatab = data->alphatab;
|
|
|
|
if (n_steps > 0) {
|
|
run_x1 = steps[0].x;
|
|
if (run_x1 > x0) {
|
|
alpha = (running_sum >> 16) & 0xff;
|
|
if (alpha)
|
|
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
|
|
}
|
|
|
|
for (k = 0; k < n_steps - 1; k++) {
|
|
running_sum += steps[k].delta;
|
|
run_x0 = run_x1;
|
|
run_x1 = steps[k + 1].x;
|
|
if (run_x1 > run_x0) {
|
|
alpha = (running_sum >> 16) & 0xff;
|
|
if (alpha)
|
|
art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
|
|
}
|
|
}
|
|
running_sum += steps[k].delta;
|
|
if (x1 > run_x1) {
|
|
alpha = (running_sum >> 16) & 0xff;
|
|
if (alpha)
|
|
art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
|
|
}
|
|
} else {
|
|
alpha = (running_sum >> 16) & 0xff;
|
|
if (alpha)
|
|
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
|
|
}
|
|
|
|
data->buf += data->rowstride;
|
|
}
|
|
|
|
static void art_rgb_svp_alpha_opaque_callback1(void *callback_data, int y,
|
|
int start,
|
|
ArtSVPRenderAAStep *steps, int n_steps) {
|
|
ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
|
|
byte *linebuf;
|
|
int run_x0, run_x1;
|
|
uint32 running_sum = start;
|
|
int x0, x1;
|
|
int k;
|
|
byte r, g, b;
|
|
int *alphatab;
|
|
int alpha;
|
|
|
|
linebuf = data->buf;
|
|
x0 = data->x0;
|
|
x1 = data->x1;
|
|
|
|
r = data->r;
|
|
g = data->g;
|
|
b = data->b;
|
|
alphatab = data->alphatab;
|
|
|
|
if (n_steps > 0) {
|
|
run_x1 = steps[0].x;
|
|
if (run_x1 > x0) {
|
|
alpha = running_sum >> 16;
|
|
if (alpha) {
|
|
if (alpha >= 255)
|
|
art_rgb_fill_run1(linebuf, r, g, b, run_x1 - x0);
|
|
else
|
|
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
|
|
}
|
|
}
|
|
|
|
for (k = 0; k < n_steps - 1; k++) {
|
|
running_sum += steps[k].delta;
|
|
run_x0 = run_x1;
|
|
run_x1 = steps[k + 1].x;
|
|
if (run_x1 > run_x0) {
|
|
alpha = running_sum >> 16;
|
|
if (alpha) {
|
|
if (alpha >= 255)
|
|
art_rgb_fill_run1(linebuf + (run_x0 - x0) * 4, r, g, b, run_x1 - run_x0);
|
|
else
|
|
art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
|
|
}
|
|
}
|
|
}
|
|
running_sum += steps[k].delta;
|
|
if (x1 > run_x1) {
|
|
alpha = running_sum >> 16;
|
|
if (alpha) {
|
|
if (alpha >= 255)
|
|
art_rgb_fill_run1(linebuf + (run_x1 - x0) * 4, r, g, b, x1 - run_x1);
|
|
else
|
|
art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
|
|
}
|
|
}
|
|
} else {
|
|
alpha = running_sum >> 16;
|
|
if (alpha) {
|
|
if (alpha >= 255)
|
|
art_rgb_fill_run1(linebuf, r, g, b, x1 - x0);
|
|
else
|
|
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
|
|
}
|
|
}
|
|
|
|
data->buf += data->rowstride;
|
|
}
|
|
|
|
void art_rgb_svp_alpha1(const ArtSVP *svp,
|
|
int x0, int y0, int x1, int y1,
|
|
uint32 color,
|
|
byte *buf, int rowstride) {
|
|
ArtRgbSVPAlphaData data;
|
|
int i;
|
|
int a, da;
|
|
|
|
data.r = (color >> 16) & 0xFF;
|
|
data.g = (color >> 8) & 0xFF;
|
|
data.b = (color >> 0) & 0xFF;
|
|
data.alpha = (color >> 24) & 0xFF;
|
|
|
|
a = 0x8000;
|
|
da = (data.alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
data.alphatab[i] = a >> 16;
|
|
a += da;
|
|
}
|
|
|
|
data.buf = buf;
|
|
data.rowstride = rowstride;
|
|
data.x0 = x0;
|
|
data.x1 = x1;
|
|
if (data.alpha == 255)
|
|
art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback1, &data);
|
|
else
|
|
art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback1, &data);
|
|
}
|
|
|
|
static int art_vpath_len(ArtVpath *a) {
|
|
int i = 0;
|
|
while (a[i].code != ART_END)
|
|
i++;
|
|
return i;
|
|
}
|
|
|
|
ArtVpath *art_vpath_cat(ArtVpath *a, ArtVpath *b) {
|
|
ArtVpath *dest;
|
|
ArtVpath *p;
|
|
int len_a, len_b;
|
|
|
|
len_a = art_vpath_len(a);
|
|
len_b = art_vpath_len(b);
|
|
dest = art_new(ArtVpath, len_a + len_b + 1);
|
|
if (!dest)
|
|
error("[art_vpath_cat] Cannot allocate memory");
|
|
|
|
p = dest;
|
|
|
|
for (int i = 0; i < len_a; i++)
|
|
*p++ = *a++;
|
|
for (int i = 0; i <= len_b; i++)
|
|
*p++ = *b++;
|
|
|
|
return dest;
|
|
}
|
|
|
|
void art_svp_make_convex(ArtSVP *svp) {
|
|
int i;
|
|
|
|
if (svp->n_segs > 0 && svp->segs[0].dir == 0) {
|
|
for (i = 0; i < svp->n_segs; i++) {
|
|
svp->segs[i].dir = !svp->segs[i].dir;
|
|
}
|
|
}
|
|
}
|
|
|
|
ArtVpath *art_vpath_reverse(ArtVpath *a) {
|
|
ArtVpath *dest;
|
|
ArtVpath it;
|
|
int len;
|
|
int state = 0;
|
|
int i;
|
|
|
|
len = art_vpath_len(a);
|
|
dest = art_new(ArtVpath, len + 1);
|
|
if (!dest)
|
|
error("[art_vpath_reverse] Cannot allocate memory");
|
|
|
|
for (i = 0; i < len; i++) {
|
|
it = a[len - i - 1];
|
|
if (state) {
|
|
it.code = ART_LINETO;
|
|
} else {
|
|
it.code = ART_MOVETO_OPEN;
|
|
state = 1;
|
|
}
|
|
if (a[len - i - 1].code == ART_MOVETO ||
|
|
a[len - i - 1].code == ART_MOVETO_OPEN) {
|
|
state = 0;
|
|
}
|
|
dest[i] = it;
|
|
}
|
|
dest[len] = a[len];
|
|
|
|
return dest;
|
|
}
|
|
|
|
ArtVpath *art_vpath_reverse_free(ArtVpath *a) {
|
|
ArtVpath *dest;
|
|
|
|
dest = art_vpath_reverse(a);
|
|
free(a);
|
|
|
|
return dest;
|
|
}
|
|
|
|
void drawBez(ArtBpath *bez1, ArtBpath *bez2, byte *buffer, int width, int height, int deltaX, int deltaY, double scaleX, double scaleY, double penWidth, unsigned int color) {
|
|
ArtVpath *vec = NULL;
|
|
ArtVpath *vec1 = NULL;
|
|
ArtVpath *vec2 = NULL;
|
|
ArtSVP *svp = NULL;
|
|
|
|
#if 0
|
|
const char *codes[] = {"ART_MOVETO", "ART_MOVETO_OPEN", "ART_CURVETO", "ART_LINETO", "ART_END"};
|
|
for (int i = 0;; i++) {
|
|
debugN(" bez[%d].code = %s;\n", i, codes[bez[i].code]);
|
|
if (bez[i].code == ART_END)
|
|
break;
|
|
if (bez[i].code == ART_CURVETO) {
|
|
debugN(" bez[%d].x1 = %f; bez[%d].y1 = %f;\n", i, bez[i].x1, i, bez[i].y1);
|
|
debugN(" bez[%d].x2 = %f; bez[%d].y2 = %f;\n", i, bez[i].x2, i, bez[i].y2);
|
|
}
|
|
debugN(" bez[%d].x3 = %f; bez[%d].y3 = %f;\n", i, bez[i].x3, i, bez[i].y3);
|
|
}
|
|
|
|
debugN(" drawBez(bez, buffer, 1.0, 1.0, %f, 0x%08x);\n", penWidth, color);
|
|
#endif
|
|
|
|
// HACK: Some frames have green bounding boxes drawn.
|
|
// Perhaps they were used by original game artist Umriss
|
|
// We skip them just like the original
|
|
if (bez2 == 0 && color == BS_RGB(0x00, 0xff, 0x00)) {
|
|
return;
|
|
}
|
|
|
|
vec1 = art_bez_path_to_vec(bez1, 0.5);
|
|
if (bez2 != 0) {
|
|
vec2 = art_bez_path_to_vec(bez2, 0.5);
|
|
vec2 = art_vpath_reverse_free(vec2);
|
|
vec = art_vpath_cat(vec1, vec2);
|
|
|
|
free(vec1);
|
|
free(vec2);
|
|
} else {
|
|
vec = vec1;
|
|
}
|
|
|
|
int size = art_vpath_len(vec);
|
|
ArtVpath *vect = art_new(ArtVpath, size + 1);
|
|
if (!vect)
|
|
error("[drawBez] Cannot allocate memory");
|
|
|
|
int k;
|
|
for (k = 0; k < size; k++) {
|
|
vect[k].code = vec[k].code;
|
|
vect[k].x = (vec[k].x - deltaX) * scaleX;
|
|
vect[k].y = (vec[k].y - deltaY) * scaleY;
|
|
}
|
|
vect[k].code = ART_END;
|
|
|
|
if (bez2 == 0) { // Line drawing
|
|
svp = art_svp_vpath_stroke(vect, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_CAP_ROUND, penWidth, 1.0, 0.5);
|
|
} else {
|
|
svp = art_svp_from_vpath(vect);
|
|
art_svp_make_convex(svp);
|
|
}
|
|
|
|
art_rgb_svp_alpha1(svp, 0, 0, width, height, color, buffer, width * 4);
|
|
|
|
free(vect);
|
|
art_svp_free(svp);
|
|
free(vec);
|
|
}
|
|
|
|
void VectorImage::render(int width, int height) {
|
|
double scaleX = (width == - 1) ? 1 : static_cast<double>(width) / static_cast<double>(getWidth());
|
|
double scaleY = (height == - 1) ? 1 : static_cast<double>(height) / static_cast<double>(getHeight());
|
|
|
|
debug(3, "VectorImage::render(%d, %d) %s", width, height, _fname.c_str());
|
|
|
|
if (_pixelData)
|
|
free(_pixelData);
|
|
|
|
_pixelData = (byte *)malloc(width * height * 4);
|
|
memset(_pixelData, 0, width * height * 4);
|
|
|
|
for (uint e = 0; e < _elements.size(); e++) {
|
|
|
|
//// Draw shapes
|
|
for (uint s = 0; s < _elements[e].getFillStyleCount(); s++) {
|
|
int fill0len = 0;
|
|
int fill1len = 0;
|
|
|
|
// Count vector sizes in order to minimize memory
|
|
// fragmentation
|
|
for (uint p = 0; p < _elements[e].getPathCount(); p++) {
|
|
if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1)
|
|
fill0len += _elements[e].getPathInfo(p).getVecLen();
|
|
|
|
if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1)
|
|
fill1len += _elements[e].getPathInfo(p).getVecLen();
|
|
}
|
|
|
|
// Now lump together vectors
|
|
ArtBpath *fill1 = art_new(ArtBpath, fill1len + 1);
|
|
ArtBpath *fill0 = art_new(ArtBpath, fill0len + 1);
|
|
ArtBpath *fill1pos = fill1;
|
|
ArtBpath *fill0pos = fill0;
|
|
|
|
for (uint p = 0; p < _elements[e].getPathCount(); p++) {
|
|
if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1) {
|
|
for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
|
|
*fill0pos++ = _elements[e].getPathInfo(p).getVec()[i];
|
|
}
|
|
|
|
if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1) {
|
|
for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
|
|
*fill1pos++ = _elements[e].getPathInfo(p).getVec()[i];
|
|
}
|
|
}
|
|
|
|
// Close vectors
|
|
(*fill0pos).code = ART_END;
|
|
(*fill1pos).code = ART_END;
|
|
|
|
drawBez(fill1, fill0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, -1, _elements[e].getFillStyleColor(s));
|
|
|
|
free(fill0);
|
|
free(fill1);
|
|
}
|
|
|
|
//// Draw strokes
|
|
for (uint s = 0; s < _elements[e].getLineStyleCount(); s++) {
|
|
double penWidth = _elements[e].getLineStyleWidth(s);
|
|
penWidth *= sqrt(fabs(scaleX * scaleY));
|
|
|
|
for (uint p = 0; p < _elements[e].getPathCount(); p++) {
|
|
if (_elements[e].getPathInfo(p).getLineStyle() == s + 1) {
|
|
drawBez(_elements[e].getPathInfo(p).getVec(), 0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, penWidth, _elements[e].getLineStyleColor(s));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} // End of namespace Sword25
|