Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_AINTERN_H
#define AGS_LIB_ALLEGRO_AINTERN_H
#include "ags/lib/allegro/base.h"
#include "ags/lib/allegro/color.h"
/* default truecolor pixel format */
#define DEFAULT_RGB_R_SHIFT_15 0
#define DEFAULT_RGB_G_SHIFT_15 5
#define DEFAULT_RGB_B_SHIFT_15 10
#define DEFAULT_RGB_R_SHIFT_16 0
#define DEFAULT_RGB_G_SHIFT_16 5
#define DEFAULT_RGB_B_SHIFT_16 11
#define DEFAULT_RGB_R_SHIFT_24 0
#define DEFAULT_RGB_G_SHIFT_24 8
#define DEFAULT_RGB_B_SHIFT_24 16
#define DEFAULT_RGB_R_SHIFT_32 0
#define DEFAULT_RGB_G_SHIFT_32 8
#define DEFAULT_RGB_B_SHIFT_32 16
#define DEFAULT_RGB_A_SHIFT_32 24
namespace AGS3 {
} // namespace AGS3
#endif

View File

@@ -0,0 +1,83 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_ALCONFIG_H
#define AGS_LIB_ALLEGRO_ALCONFIG_H
namespace AGS3 {
/* which color depths to include? */
#define ALLEGRO_COLOR8
#define ALLEGRO_COLOR16
#define ALLEGRO_COLOR24
#define ALLEGRO_COLOR32
#ifndef INLINE
#define INLINE
#endif
#ifndef RET_VOLATILE
#define RET_VOLATILE volatile
#endif
#ifndef ZERO_SIZE_ARRAY
#define ZERO_SIZE_ARRAY(type, name) type name[]
#endif
#ifndef AL_CONST
#define AL_CONST const
#endif
#ifndef AL_VAR
#define AL_VAR(type, name) extern type name
#endif
#ifndef AL_ARRAY
#define AL_ARRAY(type, name) extern type name[]
#endif
#ifndef AL_FUNC
#define AL_FUNC(type, name, args) type name args
#endif
#ifndef AL_PRINTFUNC
#define AL_PRINTFUNC(type, name, args, a, b) AL_FUNC(type, name, args)
#endif
#ifndef AL_METHOD
#define AL_METHOD(type, name, args) type (*name) args
#endif
#ifndef AL_FUNCPTR
#define AL_FUNCPTR(type, name, args) extern type (*name) args
#endif
#ifndef AL_FUNCPTRARRAY
#define AL_FUNCPTRARRAY(type, name, args) extern type (*name[]) args
#endif
#ifndef AL_INLINE
#define AL_INLINE(type, name, args, code) type name args;
#endif
} // namespace AGS3
#endif

View File

@@ -0,0 +1,52 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_BASE_H
#define AGS_LIB_ALLEGRO_BASE_H
#include "common/scummsys.h"
#include "common/algorithm.h"
#include "common/endian.h"
#include "ags/lib/allegro/error.h"
namespace AGS3 {
#define ALLEGRO_VERSION 4
#define ALLEGRO_SUB_VERSION 4
#define ALLEGRO_WIP_VERSION 2
#define ALLEGRO_VERSION_STR "4.4.2"
#define ALLEGRO_DATE_STR "2011"
#define ALLEGRO_DATE 20110519 /* yyyymmdd */
/* Returns the median of x, y, z */
#define MID(x,y,z) ((x) > (y) ? ((y) > (z) ? (y) : ((x) > (z) ? \
(z) : (x))) : ((y) > (z) ? ((z) > (x) ? (z) : \
(x)): (y)))
#define AL_ID MKTAG
#define AL_FUNC(type, name, args) type name args
#define SCUMMVM_ID MKTAG('S', 'C', 'V', 'M')
} // namespace AGS3
#endif

View File

@@ -0,0 +1,974 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/color.h"
#include "ags/lib/allegro/system.h"
#include "ags/lib/allegro/aintern.h"
#include "ags/shared/core/types.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
#include "common/textconsole.h"
#include "common/system.h"
#include "graphics/paletteman.h"
namespace AGS3 {
#define VGA_COLOR_TRANS(x) ((x) * 255 / 63)
void color::readFromFile(AGS::Shared::Stream *file) {
r = file->ReadByte();
g = file->ReadByte();
b = file->ReadByte();
filler = file->ReadByte();
}
void color::writeToFile(AGS::Shared::Stream *file) const {
file->WriteByte(r);
file->WriteByte(g);
file->WriteByte(b);
file->WriteByte(filler);
}
static void convertPalette(const PALETTE src, byte dest[Graphics::PALETTE_SIZE]) {
const color *cSrc = (const color *)src;
for (int i = 0; i < Graphics::PALETTE_COUNT; ++i, cSrc++, dest += 3) {
dest[0] = VGA_COLOR_TRANS(cSrc->r);
dest[1] = VGA_COLOR_TRANS(cSrc->g);
dest[2] = VGA_COLOR_TRANS(cSrc->b);
}
}
static void applyPalette() {
if (g_system->getScreenFormat().bytesPerPixel == 1) {
byte pal[Graphics::PALETTE_SIZE];
convertPalette(_G(current_palette), pal);
g_system->getPaletteManager()->setPalette(pal, 0, Graphics::PALETTE_COUNT);
}
}
void set_palette(const PALETTE p) {
for (int idx = 0; idx < PAL_SIZE; ++idx)
_G(current_palette)[idx] = p[idx];
applyPalette();
}
void set_palette_range(const PALETTE p, int from, int to, int retracesync) {
for (int i = from; i <= to; ++i) {
_G(current_palette)[i] = p[i];
}
applyPalette();
}
int makecol15(int r, int g, int b) {
return (((r >> 3) << _G(_rgb_r_shift_15)) |
((g >> 3) << _G(_rgb_g_shift_15)) |
((b >> 3) << _G(_rgb_b_shift_15)));
}
int makecol16(int r, int g, int b) {
return (((r >> 3) << _G(_rgb_r_shift_16)) |
((g >> 2) << _G(_rgb_g_shift_16)) |
((b >> 3) << _G(_rgb_b_shift_16)));
}
int makecol24(int r, int g, int b) {
return ((r << _G(_rgb_r_shift_24)) |
(g << _G(_rgb_g_shift_24)) |
(b << _G(_rgb_b_shift_24)));
}
int makecol32(int r, int g, int b) {
return ((r << _G(_rgb_r_shift_32)) |
(g << _G(_rgb_g_shift_32)) |
(b << _G(_rgb_b_shift_32)));
}
int makeacol32(int r, int g, int b, int a) {
return ((r << _G(_rgb_r_shift_32)) |
(g << _G(_rgb_g_shift_32)) |
(b << _G(_rgb_b_shift_32)) |
(a << _G(_rgb_a_shift_32)));
}
int getr8(int c) {
return (int)_G(current_palette)[c].r;
}
int getg8(int c) {
return (int)_G(current_palette)[c].g;
}
int getb8(int c) {
return (int)_G(current_palette)[c].b;
}
int getr15(int c) {
return _rgb_scale_5[(c >> _G(_rgb_r_shift_15)) & 0x1F];
}
int getg15(int c) {
return _rgb_scale_5[(c >> _G(_rgb_g_shift_15)) & 0x1F];
}
int getb15(int c) {
return _rgb_scale_5[(c >> _G(_rgb_b_shift_15)) & 0x1F];
}
int getr16(int c) {
return _rgb_scale_5[(c >> _G(_rgb_r_shift_16)) & 0x1F];
}
int getg16(int c) {
return _rgb_scale_6[(c >> _G(_rgb_g_shift_16)) & 0x3F];
}
int getb16(int c) {
return _rgb_scale_5[(c >> _G(_rgb_b_shift_16)) & 0x1F];
}
int getr24(int c) {
return ((c >> _G(_rgb_r_shift_24)) & 0xFF);
}
int getg24(int c) {
return ((c >> _G(_rgb_g_shift_24)) & 0xFF);
}
int getb24(int c) {
return ((c >> _G(_rgb_b_shift_24)) & 0xFF);
}
int getr32(int c) {
return ((c >> _G(_rgb_r_shift_32)) & 0xFF);
}
int getg32(int c) {
return ((c >> _G(_rgb_g_shift_32)) & 0xFF);
}
int getb32(int c) {
return ((c >> _G(_rgb_b_shift_32)) & 0xFF);
}
int geta32(int c) {
return ((c >> _G(_rgb_a_shift_32)) & 0xFF);
}
int makecol(byte r, byte g, byte b) {
return (b) | (g << 8) | (r << 16);
}
int makecol8(byte r, byte g, byte b) {
return (b) | (g << 8) | (r << 16);
}
void get_color(int idx, RGB *p) {
*p = _G(current_palette)[idx];
}
void get_palette_range(PALETTE p, int from, int to) {
Common::copy(&_G(current_palette)[from], &_G(current_palette)[to + 1], &p[from]);
}
void get_palette(PALETTE p) {
get_palette_range(p, 0, PAL_SIZE - 1);
}
void fade_interpolate(AL_CONST PALETTE source, AL_CONST PALETTE dest, PALETTE output, int pos, int from, int to) {
assert(pos >= 0 && pos <= 64);
assert(from >= 0 && from < PAL_SIZE);
assert(to >= 0 && to < PAL_SIZE);
for (int c = from; c <= to; c++) {
output[c].r = ((int)source[c].r * (63 - pos) + (int)dest[c].r * pos) / 64;
output[c].g = ((int)source[c].g * (63 - pos) + (int)dest[c].g * pos) / 64;
output[c].b = ((int)source[c].b * (63 - pos) + (int)dest[c].b * pos) / 64;
}
}
void select_palette(AL_CONST PALETTE p) {
int c;
for (c = 0; c < PAL_SIZE; c++) {
_G(prev_current_palette)[c] = _G(current_palette)[c];
_G(current_palette)[c] = p[c];
}
applyPalette();
}
void unselect_palette(void) {
int c;
for (c = 0; c < PAL_SIZE; c++)
_G(current_palette)[c] = _G(prev_current_palette)[c];
applyPalette();
}
void set_blender_mode(BlenderMode m, int r, int g, int b, int a) {
_G(_blender_mode) = m;
_G(trans_blend_alpha) = a;
_G(trans_blend_red) = r;
_G(trans_blend_green) = g;
_G(trans_blend_blue) = b;
}
void set_alpha_blender(void) {
set_blender_mode(kSourceAlphaBlender, 0, 0, 0, 0);
}
void set_trans_blender(int r, int g, int b, int a) {
set_blender_mode(kRgbToRgbBlender, r, g, b, a);
}
/* makecol_depth:
* Converts R, G, and B values (ranging 0-255) to whatever pixel format
* is required by the specified color depth.
*/
int makecol_depth(int color_depth, int r, int g, int b) {
switch (color_depth) {
case 8:
return makecol8(r, g, b);
case 15:
return makecol15(r, g, b);
case 16:
return makecol16(r, g, b);
case 24:
return makecol24(r, g, b);
case 32:
return makecol32(r, g, b);
}
return 0;
}
/* makeacol_depth:
* Converts R, G, B, and A values (ranging 0-255) to whatever pixel format
* is required by the specified color depth.
*/
int makeacol_depth(int color_depth, int r, int g, int b, int a) {
switch (color_depth) {
case 8:
return makecol8(r, g, b);
case 15:
return makecol15(r, g, b);
case 16:
return makecol16(r, g, b);
case 24:
return makecol24(r, g, b);
case 32:
return makeacol32(r, g, b, a);
}
return 0;
}
/* getr_depth:
* Extracts the red component (ranging 0-255) from a pixel in the format
* being used by the specified color depth.
*/
int getr_depth(int color_depth, int c) {
switch (color_depth) {
case 8:
return getr8(c);
case 15:
return getr15(c);
case 16:
return getr16(c);
case 24:
return getr24(c);
case 32:
return getr32(c);
}
return 0;
}
/* getg_depth:
* Extracts the green component (ranging 0-255) from a pixel in the format
* being used by the specified color depth.
*/
int getg_depth(int color_depth, int c) {
switch (color_depth) {
case 8:
return getg8(c);
case 15:
return getg15(c);
case 16:
return getg16(c);
case 24:
return getg24(c);
case 32:
return getg32(c);
}
return 0;
}
/* getb_depth:
* Extracts the blue component (ranging 0-255) from a pixel in the format
* being used by the specified color depth.
*/
int getb_depth(int color_depth, int c) {
switch (color_depth) {
case 8:
return getb8(c);
case 15:
return getb15(c);
case 16:
return getb16(c);
case 24:
return getb24(c);
case 32:
return getb32(c);
}
return 0;
}
/* geta_depth:
* Extracts the alpha component (ranging 0-255) from a pixel in the format
* being used by the specified color depth.
*/
int geta_depth(int color_depth, int c) {
if (color_depth == 32)
return geta32(c);
return 0;
}
/* getr:
* Extracts the red component (ranging 0-255) from a pixel in the format
* being used by the current video mode.
*/
int getr(int c) {
return getr_depth(_G(_color_depth), c);
}
/* getg:
* Extracts the green component (ranging 0-255) from a pixel in the format
* being used by the current video mode.
*/
int getg(int c) {
return getg_depth(_G(_color_depth), c);
}
/* getb:
* Extracts the blue component (ranging 0-255) from a pixel in the format
* being used by the current video mode.
*/
int getb(int c) {
return getb_depth(_G(_color_depth), c);
}
/* geta:
* Extracts the alpha component (ranging 0-255) from a pixel in the format
* being used by the current video mode.
*/
int geta(int c) {
return geta_depth(_G(_color_depth), c);
}
/* 1.5k lookup table for color matching */
static unsigned int col_diff[3 * 128];
/* bestfit_init:
* Color matching is done with weighted squares, which are much faster
* if we pregenerate a little lookup table...
*/
static void bestfit_init(void) {
int i;
for (i = 1; i < 64; i++) {
int k = i * i;
col_diff[0 + i] = col_diff[0 + 128 - i] = k * (59 * 59);
col_diff[128 + i] = col_diff[128 + 128 - i] = k * (30 * 30);
col_diff[256 + i] = col_diff[256 + 128 - i] = k * (11 * 11);
}
}
/* bestfit_color:
* Searches a palette for the color closest to the requested R, G, B value.
*/
int bestfit_color(AL_CONST PALETTE pal, int r, int g, int b) {
int i, coldiff, lowest, bestfit;
assert(r >= 0 && r <= 63);
assert(g >= 0 && g <= 63);
assert(b >= 0 && b <= 63);
if (col_diff[1] == 0)
bestfit_init();
bestfit = 0;
lowest = INT_MAX;
/* only the transparent (pink) color can be mapped to index 0 */
if ((r == 63) && (g == 0) && (b == 63))
i = 0;
else
i = 1;
while (i < PAL_SIZE) {
AL_CONST RGB *rgb = &pal[i];
coldiff = (col_diff + 0)[(rgb->g - g) & 0x7F];
if (coldiff < lowest) {
coldiff += (col_diff + 128)[(rgb->r - r) & 0x7F];
if (coldiff < lowest) {
coldiff += (col_diff + 256)[(rgb->b - b) & 0x7F];
if (coldiff < lowest) {
bestfit = rgb - pal; /* faster than `bestfit = i;' */
if (coldiff == 0)
return bestfit;
lowest = coldiff;
}
}
}
i++;
}
return bestfit;
}
/* makecol8:
* Converts R, G, and B values (ranging 0-255) to an 8 bit paletted color.
* If the global _G(rgb_map) table is initialised, it uses that, otherwise
* it searches through the current palette to find the best match.
*/
int makecol8(int r, int g, int b) {
if (_G(rgb_map))
return _G(rgb_map)->data[r >> 3][g >> 3][b >> 3];
else
return bestfit_color(_G(current_palette), r >> 2, g >> 2, b >> 2);
}
/* hsv_to_rgb:
* Converts from HSV colorspace to RGB values.
*/
void hsv_to_rgb(float h, float s, float v, int *r, int *g, int *b) {
float f, x, y, z;
int i;
assert(s >= 0 && s <= 1);
assert(v >= 0 && v <= 1);
v *= 255.0f;
if (s == 0.0f) { /* ok since we don't divide by s, and faster */
*r = *g = *b = v + 0.5f;
} else {
h = fmod(h, 360.0f) / 60.0f;
if (h < 0.0f)
h += 6.0f;
i = (int)h;
f = h - i;
x = v * s;
y = x * f;
v += 0.5f; /* round to the nearest integer below */
z = v - x;
switch (i) {
case 6:
case 0:
*r = v;
*g = z + y;
*b = z;
break;
case 1:
*r = v - y;
*g = v;
*b = z;
break;
case 2:
*r = z;
*g = v;
*b = z + y;
break;
case 3:
*r = z;
*g = v - y;
*b = v;
break;
case 4:
*r = z + y;
*g = z;
*b = v;
break;
case 5:
*r = v;
*g = z;
*b = v - y;
break;
}
}
}
/* rgb_to_hsv:
* Converts an RGB value into the HSV colorspace.
*/
void rgb_to_hsv(int r, int g, int b, float *h, float *s, float *v) {
int delta;
assert(r >= 0 && r <= 255);
assert(g >= 0 && g <= 255);
assert(b >= 0 && b <= 255);
if (r > g) {
if (b > r) {
/* b>r>g */
delta = b - g;
*h = 240.0f + ((r - g) * 60) / (float)delta;
*s = (float)delta / (float)b;
*v = (float)b * (1.0f / 255.0f);
} else {
/* r>g and r>b */
delta = r - MIN(g, b);
*h = ((g - b) * 60) / (float)delta;
if (*h < 0.0f)
*h += 360.0f;
*s = (float)delta / (float)r;
*v = (float)r * (1.0f / 255.0f);
}
} else {
if (b > g) {
/* b>g>=r */
delta = b - r;
*h = 240.0f + ((r - g) * 60) / (float)delta;
*s = (float)delta / (float)b;
*v = (float)b * (1.0f / 255.0f);
} else {
/* g>=b and g>=r */
delta = g - MIN(r, b);
if (delta == 0) {
*h = 0.0f;
if (g == 0)
*s = *v = 0.0f;
else {
*s = (float)delta / (float)g;
*v = (float)g * (1.0f / 255.0f);
}
} else {
*h = 120.0f + ((b - r) * 60) / (float)delta;
*s = (float)delta / (float)g;
*v = (float)g * (1.0f / 255.0f);
}
}
}
}
/* create_rgb_table:
* Fills an RGB_MAP lookup table with conversion data for the specified
* palette. This is the faster version by Jan Hubicka.
*
* Uses alg. similar to floodfill - it adds one seed per every color in
* palette to its best position. Then areas around seed are filled by
* same color because it is best approximation for them, and then areas
* about them etc...
*
* It does just about 80000 tests for distances and this is about 100
* times better than normal 256*32000 tests so the calculation time
* is now less than one second at all computers I tested.
*/
void create_rgb_table(RGB_MAP *table, AL_CONST PALETTE pal, void (*callback)(int pos)) {
#define UNUSED 65535
#define LAST 65532
/* macro add adds to single linked list */
#define add(i) (next[(i)] == UNUSED ? (next[(i)] = LAST, \
(first != LAST ? (next[last] = (i)) : (first = (i))), \
(last = (i))) : 0)
/* same but w/o checking for first element */
#define add1(i) (next[(i)] == UNUSED ? (next[(i)] = LAST, \
next[last] = (i), \
(last = (i))) : 0)
/* calculates distance between two colors */
#define dist(a1, a2, a3, b1, b2, b3) \
(col_diff[ ((a2) - (b2)) & 0x7F] + \
(col_diff + 128)[((a1) - (b1)) & 0x7F] + \
(col_diff + 256)[((a3) - (b3)) & 0x7F])
/* converts r,g,b to position in array and back */
#define pos(r, g, b) \
(((r) / 2) * 32 * 32 + ((g) / 2) * 32 + ((b) / 2))
#define depos(pal, r, g, b) \
((b) = ((pal) & 31) * 2, \
(g) = (((pal) >> 5) & 31) * 2, \
(r) = (((pal) >> 10) & 31) * 2)
/* is current color better than pal1? */
#define better(r1, g1, b1, pal1) \
(((int)dist((r1), (g1), (b1), \
(pal1).r, (pal1).g, (pal1).b)) > (int)dist2)
/* checking of position */
#define dopos(rp, gp, bp, ts) \
if ((rp > -1 || r > 0) && (rp < 1 || r < 61) && \
(gp > -1 || g > 0) && (gp < 1 || g < 61) && \
(bp > -1 || b > 0) && (bp < 1 || b < 61)) { \
i = first + rp * 32 * 32 + gp * 32 + bp; \
if (!data[i]) { \
data[i] = val; \
add1(i); \
} \
else if ((ts) && (data[i] != val)) { \
dist2 = (rp ? (col_diff+128)[(r+2*rp-pal[val].r) & 0x7F] : r2) + \
(gp ? (col_diff )[(g+2*gp-pal[val].g) & 0x7F] : g2) + \
(bp ? (col_diff+256)[(b+2*bp-pal[val].b) & 0x7F] : b2); \
if (better((r+2*rp), (g+2*gp), (b+2*bp), pal[data[i]])) { \
data[i] = val; \
add1(i); \
} \
} \
}
int i, curr, r, g, b, val, dist2;
unsigned int r2, g2, b2;
unsigned short next[32 * 32 * 32];
unsigned char *data;
int first = LAST;
int last = LAST;
int count = 0;
int cbcount = 0;
#define AVERAGE_COUNT 18000
if (col_diff[1] == 0)
bestfit_init();
memset(next, 255, sizeof(next));
memset(table->data, 0, sizeof(char) * 32 * 32 * 32);
data = (unsigned char *)table->data;
/* add starting seeds for floodfill */
for (i = 1; i < PAL_SIZE; i++) {
curr = pos(pal[i].r, pal[i].g, pal[i].b);
if (next[curr] == UNUSED) {
data[curr] = i;
add(curr);
}
}
/* main floodfill: two versions of loop for faster growing in blue axis */
while (first != LAST) {
depos(first, r, g, b);
/* calculate distance of current color */
val = data[first];
r2 = (col_diff + 128)[((pal[val].r) - (r)) & 0x7F];
g2 = (col_diff)[((pal[val].g) - (g)) & 0x7F];
b2 = (col_diff + 256)[((pal[val].b) - (b)) & 0x7F];
/* try to grow to all directions */
dopos(0, 0, 1, 1);
dopos(0, 0, -1, 1);
dopos(1, 0, 0, 1);
dopos(-1, 0, 0, 1);
dopos(0, 1, 0, 1);
dopos(0, -1, 0, 1);
/* faster growing of blue direction */
if ((b > 0) && (data[first - 1] == val)) {
b -= 2;
first--;
b2 = (col_diff + 256)[((pal[val].b) - (b)) & 0x7F];
dopos(-1, 0, 0, 0);
dopos(1, 0, 0, 0);
dopos(0, -1, 0, 0);
dopos(0, 1, 0, 0);
first++;
}
/* get next from list */
i = first;
first = next[first];
next[i] = UNUSED;
/* second version of loop */
if (first != LAST) {
depos(first, r, g, b);
val = data[first];
r2 = (col_diff + 128)[((pal[val].r) - (r)) & 0x7F];
g2 = (col_diff)[((pal[val].g) - (g)) & 0x7F];
b2 = (col_diff + 256)[((pal[val].b) - (b)) & 0x7F];
dopos(0, 0, 1, 1);
dopos(0, 0, -1, 1);
dopos(1, 0, 0, 1);
dopos(-1, 0, 0, 1);
dopos(0, 1, 0, 1);
dopos(0, -1, 0, 1);
if ((b < 61) && (data[first + 1] == val)) {
b += 2;
first++;
b2 = (col_diff + 256)[((pal[val].b) - (b)) & 0x7f];
dopos(-1, 0, 0, 0);
dopos(1, 0, 0, 0);
dopos(0, -1, 0, 0);
dopos(0, 1, 0, 0);
first--;
}
i = first;
first = next[first];
next[i] = UNUSED;
}
count++;
if (count == (cbcount + 1) * AVERAGE_COUNT / 256) {
if (cbcount < 256) {
if (callback)
callback(cbcount);
cbcount++;
}
}
}
/* only the transparent (pink) color can be mapped to index 0 */
if ((pal[0].r == 63) && (pal[0].g == 0) && (pal[0].b == 63))
table->data[31][0][31] = 0;
if (callback)
while (cbcount < 256)
callback(cbcount++);
}
/* create_light_table:
* Constructs a lighting color table for the specified palette. At light
* intensity 255 the table will produce the palette colors directly, and
* at level 0 it will produce the specified R, G, B value for all colors
* (this is specified in 0-63 VGA format). If the callback function is
* not NULL, it will be called 256 times during the calculation, allowing
* you to display a progress indicator.
*/
void create_light_table(COLOR_MAP *table, AL_CONST PALETTE pal, int r, int g, int b, void (*callback)(int pos)) {
int r1, g1, b1, r2, g2, b2, x, y;
unsigned int t1, t2;
assert(table);
assert(r >= 0 && r <= 63);
assert(g >= 0 && g <= 63);
assert(b >= 0 && b <= 63);
if (_G(rgb_map)) {
for (x = 0; x < PAL_SIZE - 1; x++) {
t1 = x * 0x010101;
t2 = 0xFFFFFF - t1;
r1 = (1 << 24) + r * t2;
g1 = (1 << 24) + g * t2;
b1 = (1 << 24) + b * t2;
for (y = 0; y < PAL_SIZE; y++) {
r2 = (r1 + pal[y].r * t1) >> 25;
g2 = (g1 + pal[y].g * t1) >> 25;
b2 = (b1 + pal[y].b * t1) >> 25;
table->data[x][y] = _G(rgb_map)->data[r2][g2][b2];
}
}
if (callback)
(*callback)(x);
} else {
for (x = 0; x < PAL_SIZE - 1; x++) {
t1 = x * 0x010101;
t2 = 0xFFFFFF - t1;
r1 = (1 << 23) + r * t2;
g1 = (1 << 23) + g * t2;
b1 = (1 << 23) + b * t2;
for (y = 0; y < PAL_SIZE; y++) {
r2 = (r1 + pal[y].r * t1) >> 24;
g2 = (g1 + pal[y].g * t1) >> 24;
b2 = (b1 + pal[y].b * t1) >> 24;
table->data[x][y] = bestfit_color(pal, r2, g2, b2);
}
}
if (callback)
(*callback)(x);
}
for (y = 0; y < PAL_SIZE; y++)
table->data[255][y] = y;
}
/* create_trans_table:
* Constructs a translucency color table for the specified palette. The
* r, g, and b parameters specifiy the solidity of each color component,
* ranging from 0 (totally transparent) to 255 (totally solid). Source
* color #0 is a special case, and is set to leave the destination
* unchanged, so that masked sprites will draw correctly. If the callback
* function is not NULL, it will be called 256 times during the calculation,
* allowing you to display a progress indicator.
*/
void create_trans_table(COLOR_MAP *table, AL_CONST PALETTE pal, int r, int g, int b, void (*callback)(int pos)) {
int tmp[768], *q;
int x, y, i, j, k;
unsigned char *p;
int tr, tg, tb;
int add;
assert(table);
assert(r >= 0 && r <= 255);
assert(g >= 0 && g <= 255);
assert(b >= 0 && b <= 255);
/* This is a bit ugly, but accounts for the solidity parameters
being in the range 0-255 rather than 0-256. Given that the
precision of r,g,b components is only 6 bits it shouldn't do any
harm. */
if (r > 128)
r++;
if (g > 128)
g++;
if (b > 128)
b++;
if (_G(rgb_map))
add = 255;
else
add = 127;
for (x = 0; x < 256; x++) {
tmp[x * 3] = pal[x].r * (256 - r) + add;
tmp[x * 3 + 1] = pal[x].g * (256 - g) + add;
tmp[x * 3 + 2] = pal[x].b * (256 - b) + add;
}
for (x = 1; x < PAL_SIZE; x++) {
i = pal[x].r * r;
j = pal[x].g * g;
k = pal[x].b * b;
p = table->data[x];
q = tmp;
if (_G(rgb_map)) {
for (y = 0; y < PAL_SIZE; y++) {
tr = (i + * (q++)) >> 9;
tg = (j + * (q++)) >> 9;
tb = (k + * (q++)) >> 9;
p[y] = _G(rgb_map)->data[tr][tg][tb];
}
} else {
for (y = 0; y < PAL_SIZE; y++) {
tr = (i + * (q++)) >> 8;
tg = (j + * (q++)) >> 8;
tb = (k + * (q++)) >> 8;
p[y] = bestfit_color(pal, tr, tg, tb);
}
}
if (callback)
(*callback)(x - 1);
}
for (y = 0; y < PAL_SIZE; y++) {
table->data[0][y] = y;
table->data[y][y] = y;
}
if (callback)
(*callback)(255);
}
} // namespace AGS3

View File

@@ -0,0 +1,155 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_COLOR_H
#define AGS_LIB_ALLEGRO_COLOR_H
#include "common/scummsys.h"
#include "graphics/screen.h"
#include "ags/lib/allegro/base.h"
#include "ags/lib/allegro/alconfig.h"
namespace AGS3 {
#define PAL_SIZE 256
#define MASK_COLOR_8 0
#define MASK_COLOR_15 0x7C1F
#define MASK_COLOR_16 0xF81F
#define MASK_COLOR_24 0xFF00FF
#define MASK_COLOR_32 0xFF00FF
class BITMAP;
namespace AGS {
namespace Shared {
class Stream;
} // namespace Shared
} // namespace AGS
#include "common/pack-start.h" // START STRUCT PACKING
struct color {
byte r, g, b;
byte filler;
void readFromFile(AGS::Shared::Stream *file);
void writeToFile(AGS::Shared::Stream *file) const;
void clear() {
r = g = b = filler = 0;
}
} PACKED_STRUCT;
typedef color RGB;
typedef RGB PALETTE[PAL_SIZE];
#include "common/pack-end.h" // END STRUCT PACKING
struct RGB_MAP {
byte data[32][32][32];
};
struct COLOR_MAP {
byte data[PAL_SIZE][PAL_SIZE];
};
enum BlenderMode {
kSourceAlphaBlender,
kArgbToArgbBlender,
kArgbToRgbBlender,
kRgbToArgbBlender,
kRgbToRgbBlender,
kAlphaPreservedBlenderMode,
kOpaqueBlenderMode,
kAdditiveBlenderMode,
kTintBlenderMode,
kTintLightBlenderMode
};
extern int makecol(byte r, byte g, byte b);
AL_ARRAY(const int, _rgb_scale_5);
AL_ARRAY(const int, _rgb_scale_6);
AL_FUNC(void, set_palette, (AL_CONST PALETTE p));
AL_FUNC(void, set_palette_range, (AL_CONST PALETTE p, int from, int to, int retracesync));
AL_FUNC(void, get_color, (int idx, RGB *p));
AL_FUNC(void, get_palette, (PALETTE p));
AL_FUNC(void, get_palette_range, (PALETTE p, int from, int to));
AL_FUNC(void, fade_interpolate, (AL_CONST PALETTE source, AL_CONST PALETTE dest, PALETTE output, int pos, int from, int to));
AL_FUNC(void, select_palette, (AL_CONST PALETTE p));
AL_FUNC(void, unselect_palette, (void));
AL_FUNC(void, create_rgb_table, (RGB_MAP *table, AL_CONST PALETTE pal, AL_METHOD(void, callback, (int pos))));
AL_FUNC(void, create_light_table, (COLOR_MAP *table, AL_CONST PALETTE pal, int r, int g, int b, AL_METHOD(void, callback, (int pos))));
AL_FUNC(void, create_trans_table, (COLOR_MAP *table, AL_CONST PALETTE pal, int r, int g, int b, AL_METHOD(void, callback, (int pos))));
AL_FUNC(void, set_blender_mode, (BlenderMode, int r, int g, int b, int a));
AL_FUNC(void, set_alpha_blender, (void));
AL_FUNC(void, set_trans_blender, (int r, int g, int b, int a));
AL_FUNC(void, hsv_to_rgb, (float h, float s, float v, int *r, int *g, int *b));
AL_FUNC(void, rgb_to_hsv, (int r, int g, int b, float *h, float *s, float *v));
AL_FUNC(int, bestfit_color, (AL_CONST PALETTE pal, int r, int g, int b));
AL_FUNC(int, makecol8, (int r, int g, int b));
AL_FUNC(int, makecol_depth, (int color_depth, int r, int g, int b));
AL_FUNC(int, makeacol_depth, (int color_depth, int r, int g, int b, int a));
AL_FUNC(int, getr, (int c));
AL_FUNC(int, getg, (int c));
AL_FUNC(int, getb, (int c));
AL_FUNC(int, geta, (int c));
AL_FUNC(int, getr_depth, (int color_depth, int c));
AL_FUNC(int, getg_depth, (int color_depth, int c));
AL_FUNC(int, getb_depth, (int color_depth, int c));
AL_FUNC(int, geta_depth, (int color_depth, int c));
extern int makecol15(int r, int g, int b);
extern int makecol16(int r, int g, int b);
extern int makecol24(int r, int g, int b);
extern int makecol32(int r, int g, int b);
extern int makeacol32(int r, int g, int b, int a);
extern int getr8(int c);
extern int getg8(int c);
extern int getb8(int c);
extern int getr15(int c);
extern int getg15(int c);
extern int getb15(int c);
extern int getr16(int c);
extern int getg16(int c);
extern int getb16(int c);
extern int getr24(int c);
extern int getg24(int c);
extern int getb24(int c);
extern int getr32(int c);
extern int getg32(int c);
extern int getb32(int c);
extern int geta32(int c);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,31 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/config.h"
#include "common/textconsole.h"
namespace AGS3 {
void override_config_data(const char *data, int length) {
// No implementation
}
} // namespace AGS3

View File

@@ -0,0 +1,31 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_CONFIG_H
#define AGS_LIB_ALLEGRO_CONFIG_H
namespace AGS3 {
extern void override_config_data(const char *data, int length);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,115 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/draw.h"
namespace AGS3 {
void drawing_mode(int mode, BITMAP *pattern, int x_anchor, int y_anchor) {
// TODO: Drawing mode
}
void xor_mode(int on) {
drawing_mode(on ? DRAW_MODE_XOR : DRAW_MODE_SOLID, NULL, 0, 0);
}
void solid_mode(void) {
drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
}
void do_line(BITMAP *bmp, int x1, int y1, int x2, int y2, int d, DrawMethod proc) {
int dx = x2 - x1;
int dy = y2 - y1;
int i1, i2;
int x, y;
int dd;
/* worker macro */
#define DO_LINE(pri_sign, pri_c, pri_cond, sec_sign, sec_c, sec_cond) \
{ \
if (d##pri_c == 0) { \
proc(bmp, x1, y1, d); \
return; \
} \
\
i1 = 2 * d##sec_c; \
dd = i1 - (sec_sign (pri_sign d##pri_c)); \
i2 = dd - (sec_sign (pri_sign d##pri_c)); \
\
x = x1; \
y = y1; \
\
while (pri_c pri_cond pri_c##2) { \
proc(bmp, x, y, d); \
\
if (dd sec_cond 0) { \
sec_c = sec_c sec_sign 1; \
dd += i2; \
} \
else \
dd += i1; \
\
pri_c = pri_c pri_sign 1; \
} \
}
if (dx >= 0) {
if (dy >= 0) {
if (dx >= dy) {
/* (x1 <= x2) && (y1 <= y2) && (dx >= dy) */
DO_LINE(+, x, <= , +, y, >=);
} else {
/* (x1 <= x2) && (y1 <= y2) && (dx < dy) */
DO_LINE(+, y, <= , +, x, >=);
}
} else {
if (dx >= -dy) {
/* (x1 <= x2) && (y1 > y2) && (dx >= dy) */
DO_LINE(+, x, <= , -, y, <=);
} else {
/* (x1 <= x2) && (y1 > y2) && (dx < dy) */
DO_LINE(-, y, >= , +, x, >=);
}
}
} else {
if (dy >= 0) {
if (-dx >= dy) {
/* (x1 > x2) && (y1 <= y2) && (dx >= dy) */
DO_LINE(-, x, >= , +, y, >=);
} else {
/* (x1 > x2) && (y1 <= y2) && (dx < dy) */
DO_LINE(+, y, <= , -, x, <=);
}
} else {
if (-dx >= -dy) {
/* (x1 > x2) && (y1 > y2) && (dx >= dy) */
DO_LINE(-, x, >= , -, y, <=);
} else {
/* (x1 > x2) && (y1 > y2) && (dx < dy) */
DO_LINE(-, y, >= , -, x, <=);
}
}
}
#undef DO_LINE
}
} // namespace AGS3

View File

@@ -0,0 +1,48 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/base.h"
#include "ags/lib/allegro/gfx.h"
#include "ags/lib/allegro/surface.h"
#ifndef AGS_LIB_ALLEGRO_DRAW_H
#define AGS_LIB_ALLEGRO_DRAW_H
namespace AGS3 {
#define DRAW_MODE_SOLID 0 /* flags for drawing_mode() */
#define DRAW_MODE_XOR 1
#define DRAW_MODE_COPY_PATTERN 2
#define DRAW_MODE_SOLID_PATTERN 3
#define DRAW_MODE_MASKED_PATTERN 4
#define DRAW_MODE_TRANS 5
AL_FUNC(void, drawing_mode, (int mode, BITMAP *pattern, int x_anchor, int y_anchor));
AL_FUNC(void, xor_mode, (int on));
AL_FUNC(void, solid_mode, (void));
typedef void (*DrawMethod)(BITMAP *, int, int, int);
extern void do_line(BITMAP *bmp, int x1, int y1, int x2, int y2, int d, DrawMethod proc);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,74 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_ERROR_H
#define AGS_LIB_ALLEGRO_ERROR_H
#include "common/scummsys.h"
namespace AGS3 {
// Error codes
enum AllegroError {
AL_NOERROR = 0,
AL_EPERM = 1,
AL_ENOENT = 2,
AL_ESRCH = 3,
AL_EINTR = 4,
AL_EIO = 5,
AL_ENXIO = 6,
AL_E2BIG = 7,
AL_ENOEXEC = 8,
AL_EBADF = 9,
AL_ECHILD = 10,
AL_EAGAIN = 11,
AL_ENOMEM = 12,
AL_EACCES = 13,
AL_EFAULT = 14,
AL_EBUSY = 16,
AL_EEXIST = 17,
AL_EXDEV = 18,
AL_ENODEV = 19,
AL_ENOTDIR = 20,
AL_EISDIR = 21,
AL_EINVAL = 22,
AL_ENFILE = 23,
AL_EMFILE = 24,
AL_ENOTTY = 25,
AL_EFBIG = 27,
AL_ENOSPC = 28,
AL_ESPIPE = 29,
AL_EROFS = 30,
AL_EMLINK = 31,
AL_EPIPE = 32,
AL_EDOM = 33,
AL_ERANGE = 34,
AL_EDEADLK = 36,
AL_ENAMETOOLONG = 38,
AL_ENOLCK = 39,
AL_ENOSYS = 40,
AL_ENOTEMPTY = 41,
AL_EILSEQ = 42
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,261 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/file.h"
#include "common/file.h"
#include "common/str.h"
#include "common/textconsole.h"
namespace AGS3 {
PACKFILE *PACKFILE::pack_fopen_chunk(int pack) {
error("TODO: pack_fopen_chunk is not yet supported");
}
PACKFILE *PACKFILE::pack_fclose_chunk() {
error("TODO: pack_fclose_chunk is not yet supported");
}
int PACKFILE::pack_igetw() {
byte buf[2];
return pack_fread(buf, 2) == 2 ? READ_LE_UINT16(buf) : 0;
}
int32_t PACKFILE::pack_igetl() {
byte buf[4];
return pack_fread(buf, 4) == 4 ? READ_LE_UINT32(buf) : 0;
}
int PACKFILE::pack_iputw(int w) {
byte buf[2];
WRITE_LE_UINT16(buf, w);
pack_fwrite(buf, 2);
return 0;
}
int32_t PACKFILE::pack_iputl(int32_t l) {
byte buf[4];
WRITE_LE_UINT32(buf, l);
pack_fwrite(buf, 4);
return 0;
}
int PACKFILE::pack_mgetw() {
byte buf[2];
return pack_fread(buf, 2) == 2 ? READ_BE_UINT16(buf) : 0;
}
int32_t PACKFILE::pack_mgetl() {
byte buf[4];
return pack_fread(buf, 4) == 4 ? READ_BE_UINT32(buf) : 0;
}
int PACKFILE::pack_mputw(int w) {
byte buf[2];
WRITE_BE_UINT16(buf, 2);
pack_fwrite(buf, 2);
return 0;
}
int32_t PACKFILE::pack_mputl(int32_t l) {
byte buf[4];
WRITE_BE_UINT16(buf, 4);
pack_fwrite(buf, 4);
return 0;
}
char *PACKFILE::pack_fgets(char *p, int max) {
int c;
char *dest = p;
while ((c = pack_getc()) != 0 && !pack_feof() && max-- > 0) {
*dest++ = c;
}
return p;
}
int PACKFILE::pack_fputs(AL_CONST char *p) {
pack_fwrite(p, strlen(p));
pack_putc(0);
return 0;
}
/*------------------------------------------------------------------*/
/* Use strictly UTF-8 encoding for the file paths
*/
#define U_CURRENT U_UTF8
#define ugetc utf8_getc
#define ugetx utf8_getx
#define ugetxc utf8_getx
#define usetc utf8_setc
#define uwidth utf8_width
#define ucwidth utf8_cwidth
#define uisok utf8_isok
void set_filename_encoding(int) {
// No implementation
}
char *fix_filename_case(char *path) {
return path;
}
char *fix_filename_slashes(char *path) {
return path;
}
char *append_filename(char *dest, const char *path, const char *filename, int size) {
strncpy(dest, path, size);
strncat(dest, filename, size);
return dest;
}
char *canonicalize_filename(char *dest, const char *filename, int size) {
strncpy(dest, filename, size);
return dest;
}
char *make_relative_filename(char *dest, const char *path, const char *filename, int size) {
strncpy(dest, filename, size);
return dest;
}
int is_relative_filename(const char *filename) {
// ScummVM doesn't have absolute paths
return true;
}
/*------------------------------------------------------------------*/
void packfile_password(AL_CONST char *password) {
error("TODO: packfile_password");
}
PACKFILE *pack_fopen(AL_CONST char *filename, AL_CONST char *mode) {
assert(!strcmp(mode, "r") || !strcmp(mode, "rb"));
Common::File *f = new Common::File();
if (f->open(filename)) {
return new ScummVMPackFile(f);
} else {
delete f;
return nullptr;
}
}
PACKFILE *pack_fopen_vtable(AL_CONST PACKFILE_VTABLE *vtable, void *userdata) {
return new VTablePackFile(vtable, userdata);
}
int pack_fclose(PACKFILE *f) {
f->close();
delete f;
return 0;
}
int pack_fseek(PACKFILE *f, int offset) {
return f->pack_fseek(offset);
}
PACKFILE *pack_fopen_chunk(PACKFILE *f, int pack) {
return f->pack_fopen_chunk(pack);
}
PACKFILE *pack_fclose_chunk(PACKFILE *f) {
return f->pack_fclose_chunk();
}
int pack_getc(PACKFILE *f) {
return f->pack_getc();
}
int pack_putc(int c, PACKFILE *f) {
return f->pack_putc(c);
}
int pack_feof(PACKFILE *f) {
return f->pack_feof();
}
int pack_ferror(PACKFILE *f) {
return f->pack_ferror();
}
int pack_igetw(PACKFILE *f) {
error("TODO: xxx");
}
int32_t pack_igetl(PACKFILE *f) {
return f->pack_igetl();
}
int pack_iputw(int w, PACKFILE *f) {
return f->pack_iputw(w);
}
int32_t pack_iputl(int32_t l, PACKFILE *f) {
return f->pack_iputl(l);
}
int pack_mgetw(PACKFILE *f) {
return f->pack_mgetw();
}
int32_t pack_mgetl(PACKFILE *f) {
return f->pack_mgetl();
}
int pack_mputw(int w, PACKFILE *f) {
return f->pack_mputw(w);
}
int32_t pack_mputl(int32_t l, PACKFILE *f) {
return f->pack_mputl(l);
}
long pack_fread(void *p, long n, PACKFILE *f) {
return f->pack_fread(p, n);
}
long pack_fwrite(AL_CONST void *p, long n, PACKFILE *f) {
return f->pack_fwrite(p, n);
}
int pack_ungetc(int c, PACKFILE *f) {
return f->pack_ungetc(c);
}
char *pack_fgets(char *p, int max, PACKFILE *f) {
return f->pack_fgets(p, max);
}
int pack_fputs(AL_CONST char *p, PACKFILE *f) {
return f->pack_fputs(p);
}
void *pack_get_userdata(PACKFILE *f) {
return f->pack_get_userdata();
}
} // namespace AGS3

View File

@@ -0,0 +1,227 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_FILE_H
#define AGS_LIB_ALLEGRO_FILE_H
#include "ags/lib/allegro/alconfig.h"
#include "ags/shared/core/types.h"
#include "common/file.h"
namespace AGS3 {
#define F_READ "r"
#define F_WRITE "w"
#define F_BUF_SIZE 4096 /* 4K buffer for caching data */
struct _al_normal_packfile_details {
int hndl; /* DOS file handle */
int flags; /* PACKFILE_FLAG_* constants */
unsigned char *buf_pos; /* position in buffer */
int buf_size; /* number of bytes in the buffer */
long todo; /* number of bytes still on the disk */
struct PACKFILE *parent; /* nested, parent file */
char *filename; /* name of the file */
unsigned char buf[F_BUF_SIZE]; /* the actual data buffer */
};
struct PACKFILE_VTABLE {
AL_METHOD(int, pf_fclose, (void *userdata));
AL_METHOD(int, pf_getc, (void *userdata));
AL_METHOD(int, pf_ungetc, (int c, void *userdata));
AL_METHOD(long, pf_fread, (void *p, long n, void *userdata));
AL_METHOD(int, pf_putc, (int c, void *userdata));
AL_METHOD(long, pf_fwrite, (AL_CONST void *p, long n, void *userdata));
AL_METHOD(int, pf_fseek, (void *userdata, int offset));
AL_METHOD(int, pf_feof, (void *userdata));
AL_METHOD(int, pf_ferror, (void *userdata));
};
/**
* Allegro file class
*/
struct PACKFILE {
virtual ~PACKFILE() {
close();
}
virtual void close() {}
virtual int pack_fseek(int offset) = 0;
virtual int pack_getc() = 0;
virtual int pack_putc(int c) = 0;
virtual int pack_ungetc(int c) = 0;
virtual long pack_fread(void *p, long n) = 0;
virtual long pack_fwrite(AL_CONST void *p, long n) = 0;
virtual int pack_feof() = 0;
virtual int pack_ferror() = 0;
virtual void *pack_get_userdata() const {
return nullptr;
}
PACKFILE *pack_fopen_chunk(int pack);
PACKFILE *pack_fclose_chunk();
int pack_igetw();
int32_t pack_igetl();
int pack_iputw(int w);
int32_t pack_iputl(int32_t l);
int pack_mgetw();
int32_t pack_mgetl();
int pack_mputw(int w);
int32_t pack_mputl(int32_t l);
char *pack_fgets(char *p, int max);
int pack_fputs(AL_CONST char *p);
};
struct ScummVMPackFile : public PACKFILE {
public:
Common::SeekableReadStream *_stream;
ScummVMPackFile(Common::SeekableReadStream *rs) : PACKFILE(), _stream(rs) {
}
virtual ~ScummVMPackFile() {}
void close() override {
delete _stream;
_stream = nullptr;
}
int pack_fseek(int offset) override {
return _stream->seek(offset, SEEK_CUR);
}
int pack_getc() override {
return _stream->readByte();
}
int pack_putc(int c) override {
error("pack_putc is not yet supported");
}
int pack_ungetc(int c) override {
_stream->seek(-1, SEEK_CUR);
return 0;
}
long pack_fread(void *p, long n) override {
return _stream->read(p, n);
}
long pack_fwrite(AL_CONST void *p, long n) override {
error("pack_fwrite is not yet supported");
}
int pack_feof() override {
return _stream->eos();
}
int pack_ferror() override {
return _stream->err();
}
};
struct VTablePackFile : public PACKFILE {
AL_CONST PACKFILE_VTABLE *_vTable;
void *_userData;
VTablePackFile(AL_CONST PACKFILE_VTABLE *vTable, void *userData) :
_vTable(vTable), _userData(userData) {
}
void close() override {
_vTable->pf_fclose(_userData);
}
int pack_fseek(int offset) override {
return _vTable->pf_fseek(_userData, offset);
}
int pack_getc() override {
return _vTable->pf_getc(_userData);
}
int pack_putc(int c) override {
return _vTable->pf_putc(c, _userData);
}
int pack_ungetc(int c) override {
return _vTable->pf_ungetc(c, _userData);
}
long pack_fread(void *p, long n) override {
return _vTable->pf_fread(p, n, _userData);
}
long pack_fwrite(AL_CONST void *p, long n) override {
return _vTable->pf_fwrite(p, n, _userData);
}
int pack_feof() override {
return _vTable->pf_feof(_userData);
}
int pack_ferror() override {
return _vTable->pf_ferror(_userData);
}
void *pack_get_userdata() const override {
return _userData;
}
};
extern void set_filename_encoding(int);
extern char *fix_filename_case(char *path);
extern char *fix_filename_slashes(char *path);
extern char *append_filename(char *dest, const char *path, const char *filename, int size);
extern char *canonicalize_filename(char *dest, const char *filename, int size);
extern char *make_relative_filename(char *dest, const char *path, const char *filename, int size);
extern int is_relative_filename(const char *filename);
AL_FUNC(void, packfile_password, (AL_CONST char *password));
AL_FUNC(PACKFILE *, pack_fopen, (AL_CONST char *filename, AL_CONST char *mode));
AL_FUNC(PACKFILE *, pack_fopen_vtable, (AL_CONST PACKFILE_VTABLE *vtable, void *userdata));
AL_FUNC(int, pack_fclose, (PACKFILE *f));
AL_FUNC(int, pack_fseek, (PACKFILE *f, int offset));
AL_FUNC(PACKFILE *, pack_fopen_chunk, (PACKFILE *f, int pack));
AL_FUNC(PACKFILE *, pack_fclose_chunk, (PACKFILE *f));
AL_FUNC(int, pack_getc, (PACKFILE *f));
AL_FUNC(int, pack_putc, (int c, PACKFILE *f));
AL_FUNC(int, pack_feof, (PACKFILE *f));
AL_FUNC(int, pack_ferror, (PACKFILE *f));
AL_FUNC(int, pack_igetw, (PACKFILE *f));
AL_FUNC(int32_t, pack_igetl, (PACKFILE *f));
AL_FUNC(int, pack_iputw, (int w, PACKFILE *f));
AL_FUNC(int32_t, pack_iputl, (int32_t l, PACKFILE *f));
AL_FUNC(int, pack_mgetw, (PACKFILE *f));
AL_FUNC(int32_t, pack_mgetl, (PACKFILE *f));
AL_FUNC(int, pack_mputw, (int w, PACKFILE *f));
AL_FUNC(int32_t, pack_mputl, (int32_t l, PACKFILE *f));
AL_FUNC(long, pack_fread, (void *p, long n, PACKFILE *f));
AL_FUNC(long, pack_fwrite, (AL_CONST void *p, long n, PACKFILE *f));
AL_FUNC(int, pack_ungetc, (int c, PACKFILE *f));
AL_FUNC(char *, pack_fgets, (char *p, int max, PACKFILE *f));
AL_FUNC(int, pack_fputs, (AL_CONST char *p, PACKFILE *f));
AL_FUNC(void *, pack_get_userdata, (PACKFILE *f));
} // namespace AGS3
#endif

View File

@@ -0,0 +1,174 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/fixed.h"
#include "ags/lib/allegro/error.h"
#include "ags/globals.h"
namespace AGS3 {
fixed ftofix(double x) {
if (x > 32767.0) {
*_G(allegro_errno) = AL_ERANGE;
return 0x7FFFFFFF;
}
if (x < -32767.0) {
*_G(allegro_errno) = AL_ERANGE;
return (fixed) - 0x7FFFFFFF;
}
return (fixed)(x * 65536.0 + (x < 0 ? -0.5 : 0.5));
}
double fixtof(fixed x) {
return (double)x / 65536.0;
}
fixed fixadd(fixed x, fixed y) {
fixed result = x + y;
if (result >= 0) {
if ((x < 0) && (y < 0)) {
*_G(allegro_errno) = AL_ERANGE;
return (fixed) - 0x7FFFFFFF;
} else
return result;
} else {
if ((x > 0) && (y > 0)) {
*_G(allegro_errno) = AL_ERANGE;
return 0x7FFFFFFF;
} else
return result;
}
}
fixed fixsub(fixed x, fixed y) {
fixed result = x - y;
if (result >= 0) {
if ((x < 0) && (y > 0)) {
*_G(allegro_errno) = AL_ERANGE;
return (fixed) - 0x7FFFFFFF;
} else
return result;
} else {
if ((x > 0) && (y < 0)) {
*_G(allegro_errno) = AL_ERANGE;
return 0x7FFFFFFF;
} else
return result;
}
}
fixed fixmul(fixed x, fixed y) {
int64 lx = x;
int64 ly = y;
int64 lres = (lx * ly);
if (lres > 0x7FFFFFFF0000LL) {
*_G(allegro_errno) = AL_ERANGE;
return 0x7FFFFFFF;
} else if (lres < -0x7FFFFFFF0000LL) {
*_G(allegro_errno) = AL_ERANGE;
return 0x80000000;
} else {
int res = lres >> 16;
return res;
}
}
fixed fixdiv(fixed x, fixed y) {
if (y == 0) {
*_G(allegro_errno) = AL_ERANGE;
return (fixed)(x < 0) ? -0x7FFFFFFF : 0x7FFFFFFF;
} else
return ftofix(fixtof(x) / fixtof(y));
}
int fixfloor(fixed x) {
// FIXME: GCC warning "this condition has identical branches [-Wduplicated-branches]" on this code i.e. both branches are functionally identical. Remove?
#if 0
/* (x >> 16) is not portable */
if (x >= 0)
return (x >> 16);
else
return ~((~x) >> 16);
#else
return (x >> 16);
#endif
}
int fixceil(fixed x) {
if (x > 0x7FFF0000) {
*_G(allegro_errno) = AL_ERANGE;
return 0x7FFF;
}
return fixfloor(x + 0xFFFF);
}
fixed itofix(int x) {
return x << 16;
}
int fixtoi(fixed x) {
return fixfloor(x) + ((x & 0x8000) >> 15);
}
fixed fixcos(fixed x) {
return _cos_tbl[((x + 0x4000) >> 15) & 0x1FF];
}
fixed fixsin(fixed x) {
return _cos_tbl[((x - 0x400000 + 0x4000) >> 15) & 0x1FF];
}
fixed fixtan(fixed x) {
return _tan_tbl[((x + 0x4000) >> 15) & 0xFF];
}
fixed fixacos(fixed x) {
if ((x < -65536) || (x > 65536)) {
*_G(allegro_errno) = AL_EDOM;
return 0;
}
return _acos_tbl[(x + 65536 + 127) >> 8];
}
fixed fixasin(fixed x) {
if ((x < -65536) || (x > 65536)) {
*_G(allegro_errno) = AL_EDOM;
return 0;
}
return 0x00400000 - _acos_tbl[(x + 65536 + 127) >> 8];
}
} // namespace AGS3

View File

@@ -0,0 +1,54 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_FIXED_H
#define AGS_LIB_ALLEGRO_FIXED_H
#include "common/scummsys.h"
#include "ags/shared/core/types.h"
namespace AGS3 {
typedef int32_t fixed;
extern const fixed _cos_tbl[];
extern const fixed _tan_tbl[];
extern const fixed _acos_tbl[];
extern fixed ftofix(double x);
extern double fixtof(fixed x);
extern fixed fixadd(fixed x, fixed y);
extern fixed fixsub(fixed x, fixed y);
extern fixed fixmul(fixed x, fixed y);
extern fixed fixdiv(fixed x, fixed y);
extern int fixfloor(fixed x);
extern int fixceil(fixed x);
extern fixed itofix(int x);
extern int fixtoi(fixed x);
extern fixed fixcos(fixed x);
extern fixed fixsin(fixed x);
extern fixed fixtan(fixed x);
extern fixed fixacos(fixed x);
extern fixed fixasin(fixed x);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,233 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/flood.h"
#include "ags/lib/allegro/gfx.h"
#include "ags/shared/core/types.h"
#include "common/array.h"
namespace AGS3 {
struct FLOODED_LINE {
short flags; /* status of the segment */
short lpos, rpos; /* left and right ends of segment */
short y; /* y coordinate of the segment */
int next; /* linked list if several per line */
};
#define FLOOD_IN_USE 1
#define FLOOD_TODO_ABOVE 2
#define FLOOD_TODO_BELOW 4
#define FLOOD_LINE(c) (&scratchMem[c])
template<class SIZE>
static bool scanLine(BITMAP *bmp, int x, int y, int src_color, int &left, int &right) {
// check start pixel
const SIZE *pixel = (const SIZE *)bmp->getBasePtr(x, y);
if (*pixel != src_color)
return false;
// Work left from starting point
const SIZE *pixelLeft = pixel - 1;
for (left = x - 1; left >= bmp->cl; left--, pixelLeft--) {
if (*pixelLeft != src_color)
break;
}
// Work right from starting point
const SIZE *pixelRight = pixel + 1;
for (right = x + 1; right < bmp->cr; right++, pixelRight++) {
if (*pixelRight != src_color)
break;
}
return true;
}
/**
* Fills a horizontal line around the specified position, and adds it
* to the list of drawn segments. Returns the first x coordinate after
* the part of the line which it has dealt with.
*/
static int flooder(BITMAP *bmp, int x, int y, int src_color, int dest_color,
Common::Array<FLOODED_LINE> &scratchMem) {
FLOODED_LINE *p;
int left = 0, right = 0;
int c;
assert(bmp);
bool ret;
switch (bmp->format.bytesPerPixel) {
case 1:
ret = scanLine<byte>(bmp, x, y, src_color, left, right);
break;
case 2:
ret = scanLine<byte>(bmp, x, y, src_color, left, right);
break;
case 4:
ret = scanLine<byte>(bmp, x, y, src_color, left, right);
break;
default:
error("Unknown format");
}
if (!ret)
return x + 1;
left++;
right--;
/* draw the line */
bmp->hLine(left, y, right, dest_color);
/* store it in the list of flooded segments */
c = y;
p = FLOOD_LINE(c);
if (p->flags) {
while (p->next) {
c = p->next;
p = FLOOD_LINE(c);
}
p->next = c = scratchMem.size();
scratchMem.resize(scratchMem.size() + 1);
p = FLOOD_LINE(c);
}
p->flags = FLOOD_IN_USE;
p->lpos = left;
p->rpos = right;
p->y = y;
p->next = 0;
if (y > bmp->ct)
p->flags |= FLOOD_TODO_ABOVE;
if (y + 1 < bmp->cb)
p->flags |= FLOOD_TODO_BELOW;
return right + 2;
}
/**
* Checks a line segment, using the scratch buffer is to store a list of
* segments which have already been drawn in order to minimise the required
* number of tests.
*/
static bool check_flood_line(BITMAP *bmp, int y, int left, int right, int src_color, int dest_color,
Common::Array<FLOODED_LINE> &scratchMem) {
int c;
FLOODED_LINE *p;
bool ret = false;
while (left <= right) {
c = y;
for (;;) {
p = FLOOD_LINE(c);
if ((left >= p->lpos) && (left <= p->rpos)) {
left = p->rpos + 2;
break;
}
c = p->next;
if (!c) {
left = flooder(bmp, left, y, src_color, dest_color, scratchMem);
ret = true;
break;
}
}
}
return ret;
}
void floodfill(BITMAP *bmp, int x, int y, int color) {
int src_color;
int c;
bool done;
FLOODED_LINE *p;
Common::Array<FLOODED_LINE> scratchMem;
/* make sure we have a valid starting point */
if ((x < bmp->cl) || (x >= bmp->cr) || (y < bmp->ct) || (y >= bmp->cb))
return;
/* what color to replace? */
src_color = getpixel(bmp, x, y);
if (src_color == color) {
return;
}
/* set up the list of flooded segments */
scratchMem.resize(bmp->cb);
p = FLOOD_LINE(0);
for (c = 0; c < (int)scratchMem.size(); c++) {
p[c].flags = 0;
p[c].lpos = INT16_MAX;
p[c].rpos = INT16_MIN;
p[c].y = y;
p[c].next = 0;
}
/* start up the flood algorithm */
flooder(bmp, x, y, src_color, color, scratchMem);
/* continue as long as there are some segments still to test */
do {
done = true;
/* for each line on the screen */
for (c = 0; c < (int)scratchMem.size(); c++) {
p = FLOOD_LINE(c);
/* check below the segment? */
if (p->flags & FLOOD_TODO_BELOW) {
p->flags &= ~FLOOD_TODO_BELOW;
if (check_flood_line(bmp, p->y + 1, p->lpos, p->rpos, src_color, color, scratchMem)) {
done = false;
p = FLOOD_LINE(c);
}
}
/* check above the segment? */
if (p->flags & FLOOD_TODO_ABOVE) {
p->flags &= ~FLOOD_TODO_ABOVE;
if (check_flood_line(bmp, p->y - 1, p->lpos, p->rpos, src_color, color, scratchMem)) {
done = false;
/* special case shortcut for going backwards */
if ((c < bmp->cb) && (c > 0))
c -= 2;
}
}
}
} while (!done);
}
} // namespace AGS3

View File

@@ -0,0 +1,36 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_FLOOD_H
#define AGS_LIB_ALLEGRO_FLOOD_H
#include "graphics/managed_surface.h"
#include "ags/lib/allegro/base.h"
namespace AGS3 {
class BITMAP;
extern void floodfill(BITMAP *bmp, int x, int y, int color);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,99 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/fmaths.h"
namespace AGS3 {
fixed fixsqrt(fixed x) {
if (x > 0)
return ftofix(sqrt(fixtof(x)));
if (x < 0)
*allegro_errno = EDOM;
return 0;
}
fixed fixhypot(fixed x, fixed y) {
return ftofix(hypot(fixtof(x), fixtof(y)));
}
fixed fixatan(fixed x) {
int a, b, c; /* for binary search */
fixed d; /* difference value for search */
if (x >= 0) { /* search the first part of tan table */
a = 0;
b = 127;
} else { /* search the second half instead */
a = 128;
b = 255;
}
do {
c = (a + b) >> 1;
d = x - _tan_tbl[c];
if (d > 0)
a = c + 1;
else if (d < 0)
b = c - 1;
} while ((a <= b) && (d));
if (x >= 0)
return ((long)c) << 15;
return (-0x00800000L + (((long)c) << 15));
}
fixed fixatan2(fixed y, fixed x) {
fixed r;
if (x == 0) {
if (y == 0) {
*allegro_errno = EDOM;
return 0L;
} else
return ((y < 0) ? -0x00400000L : 0x00400000L);
}
*allegro_errno = 0;
r = fixdiv(y, x);
if (*allegro_errno) {
*allegro_errno = 0;
return ((y < 0) ? -0x00400000L : 0x00400000L);
}
r = fixatan(r);
if (x >= 0)
return r;
if (y >= 0)
return 0x00800000L + r;
return r - 0x00800000L;
}
} // namespace AGS3

View File

@@ -0,0 +1,40 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_FMATHS_H
#define AGS_LIB_ALLEGRO_FMATHS_H
#include "ags/lib/allegro/fixed.h"
namespace AGS3 {
AL_FUNC(fixed, fixsqrt, (fixed x));
AL_FUNC(fixed, fixhypot, (fixed x, fixed y));
AL_FUNC(fixed, fixatan, (fixed x));
AL_FUNC(fixed, fixatan2, (fixed y, fixed x));
AL_ARRAY(const fixed, _cos_tbl);
AL_ARRAY(const fixed, _tan_tbl);
AL_ARRAY(const fixed, _acos_tbl);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,390 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/gfx.h"
#include "ags/lib/allegro/color.h"
#include "ags/lib/allegro/flood.h"
#include "ags/lib/allegro/rotate.h"
#include "ags/ags.h"
#include "ags/globals.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "graphics/screen.h"
namespace AGS3 {
int color_conversion;
/*-------------------------------------------------------------------*/
void set_color_conversion(int mode) {
color_conversion = mode;
}
int get_color_conversion() {
return color_conversion;
}
int set_gfx_mode(int card, int w, int h, int depth) {
// Graphics shutdown can be ignored
if (card != -1) {
assert(card == SCUMMVM_ID);
::AGS::g_vm->setGraphicsMode(w, h, depth);
}
return 0;
}
void set_clip_rect(BITMAP *bitmap, int x1, int y1, int x2, int y2) {
// The rect passed to the function in inclusive-inclusive, but
// internally the clipping rect in BITMAP is inclusive-exclusive.
bitmap->cl = CLIP(x1, 0, (int)bitmap->w - 1);
bitmap->ct = CLIP(y1, 0, (int)bitmap->h - 1);
bitmap->cr = CLIP(x2 + 1, 0, (int)bitmap->w);
bitmap->cb = CLIP(y2 + 1, 0, (int)bitmap->h);
}
void get_clip_rect(BITMAP *bitmap, int *x1, int *y1, int *x2, int *y2) {
if (x1)
*x1 = bitmap->cl;
if (y1)
*y1 = bitmap->ct;
if (x2)
*x2 = bitmap->cr - 1;
if (y2)
*y2 = bitmap->cb - 1;
}
void acquire_bitmap(BITMAP *bitmap) {
// No implementation needed
}
void release_bitmap(BITMAP *bitmap) {
// No implementation needed
}
void clear_to_color(BITMAP *bitmap, int color) {
Graphics::ManagedSurface &surf = **bitmap;
surf.clear(color);
}
int bitmap_color_depth(BITMAP *bmp) {
Graphics::ManagedSurface &surf = **bmp;
return (surf.format.bytesPerPixel == 1) ? 8 : surf.format.bpp();
}
int bitmap_mask_color(BITMAP *bmp) {
// For paletted sprites this is 0.
// For other color depths this is bright pink (RGB 255, 0, 255).
// The alpha chanel should be 0.
//if (bmp-format.bytesPerPixel == 1)
// return 0;
//return bmp->format.AGRBToColor(0, 255, 0, 255);
return bmp->getTransparentColor();
}
void blit(const BITMAP *src, BITMAP *dest, int src_x, int src_y, int dst_x, int dst_y, int width, int height) {
dest->draw(src, Common::Rect(src_x, src_y, src_x + width, src_y + height),
dst_x, dst_y, false, false, false, -1);
}
void stretch_blit(const BITMAP *src, BITMAP *dest,
int source_x, int source_y, int source_width, int source_height,
int dest_x, int dest_y, int dest_width, int dest_height) {
dest->stretchDraw(src,
Common::Rect(source_x, source_y, source_x + source_width, source_y + source_height),
Common::Rect(dest_x, dest_y, dest_x + dest_width, dest_y + dest_height),
false, -1);
}
void masked_blit(const BITMAP *src, BITMAP *dest, int src_x, int src_y, int dst_x, int dst_y, int width, int height) {
assert(src->format == dest->format);
dest->draw(src, Common::Rect(src_x, src_y, src_x + width, src_y + height),
dst_x, dst_y, false, false, true, -1);
}
void masked_stretch_blit(const BITMAP *src, BITMAP *dest,
int source_x, int source_y, int source_width, int source_height,
int dest_x, int dest_y, int dest_width, int dest_height) {
dest->stretchDraw(src,
Common::Rect(source_x, source_y, source_x + source_width, source_y + source_height),
Common::Rect(dest_x, dest_y, dest_x + dest_width, dest_y + dest_height),
true, -1);
}
void draw_sprite(BITMAP *bmp, const BITMAP *sprite, int x, int y) {
bmp->draw(sprite, Common::Rect(0, 0, sprite->w, sprite->h),
x, y, false, false, true, -1);
}
void stretch_sprite(BITMAP *bmp, const BITMAP *sprite, int x, int y, int w, int h) {
bmp->stretchDraw(sprite, Common::Rect(0, 0, sprite->w, sprite->h),
Common::Rect(x, y, x + w, y + h),
true, -1);
}
void draw_trans_sprite(BITMAP *bmp, const BITMAP *sprite, int x, int y) {
bmp->draw(sprite, Common::Rect(0, 0, sprite->w, sprite->h),
x, y, false, false, true, _G(trans_blend_alpha));
}
void draw_lit_sprite(BITMAP *bmp, const BITMAP *sprite, int x, int y, int color) {
bmp->draw(sprite, Common::Rect(0, 0, sprite->w, sprite->h),
x, y, false, false, true, color,
_G(trans_blend_red), _G(trans_blend_green), _G(trans_blend_blue));
}
void draw_sprite_h_flip(BITMAP *bmp, const BITMAP *sprite, int x, int y) {
bmp->draw(sprite, Common::Rect(0, 0, sprite->w, sprite->h),
x, y, true, false, true, -1);
}
void draw_sprite_v_flip(BITMAP *bmp, const BITMAP *sprite, int x, int y) {
bmp->draw(sprite, Common::Rect(0, 0, sprite->w, sprite->h),
x, y, false, true, true, -1);
}
void draw_sprite_vh_flip(BITMAP *bmp, const BITMAP *sprite, int x, int y) {
bmp->draw(sprite, Common::Rect(0, 0, sprite->w, sprite->h),
x, y, true, true, true, -1);
}
void rotate_sprite(BITMAP *bmp, const BITMAP *sprite, int x, int y, fixed angle) {
pivot_scaled_sprite(bmp, sprite, (x<<16) + (sprite->w * 0x10000) / 2, (y<<16) + (sprite->h * 0x10000) / 2, sprite->w << 15, sprite->h << 15, angle, 0x10000);
}
void pivot_sprite(BITMAP *bmp, const BITMAP *sprite, int x, int y, int cx, int cy, fixed angle) {
pivot_scaled_sprite(bmp, sprite, x<<16, y<<16, cx<<16, cy<<16, angle, 0x10000);
}
bool is_screen_bitmap(BITMAP *bmp) {
return dynamic_cast<Graphics::Screen *>(bmp) != nullptr;
}
bool is_video_bitmap(BITMAP *bmp) {
return dynamic_cast<Graphics::Screen *>(bmp) != nullptr;
}
bool is_planar_bitmap(BITMAP *bmp) {
return false;
}
bool is_linear_bitmap(BITMAP *bmp) {
return true;
}
bool is_same_bitmap(BITMAP *bmp1, BITMAP *bmp2) {
if ((bmp1 == nullptr) || (bmp2 == nullptr))
return false;
if (bmp1 == bmp2)
return true;
// TODO: allegro also returns true if one bmp is a sub of the other,
// i.e. they share the same id
// This (if needed?) would require a different implementation
return false;
}
void bmp_select(BITMAP *bmp) {
// No implementation needed
}
byte *bmp_write_line(BITMAP *bmp, int line) {
return bmp->line[line];
}
void bmp_write8(byte *addr, int color) {
*addr = color;
}
void bmp_write15(byte *addr, int color) {
*((uint16 *)addr) = color;
}
void bmp_write16(byte *addr, int color) {
*((uint16 *)addr) = color;
}
void bmp_write24(byte *addr, int color) {
*addr = (color & 0xff);
*(addr + 1) = ((color >> 8) & 0xff);
*(addr + 2) = ((color >> 16) & 0xff);
}
void bmp_write32(byte *addr, int color) {
*((uint32 *)addr) = color;
}
void memory_putpixel(BITMAP *bmp, int x, int y, int color) {
putpixel(bmp, x, y, color);
}
void putpixel(BITMAP *bmp, int x, int y, int color) {
Graphics::ManagedSurface &surf = **bmp;
if (x < 0 || x >= surf.w || y < 0 || y >= surf.h)
return;
void *p = surf.getBasePtr(x, y);
switch (surf.format.bytesPerPixel) {
case 1:
*((uint8 *)p) = color;
break;
case 2:
*((uint16 *)p) = color;
break;
case 4:
*((uint32 *)p) = color;
break;
default:
break;
}
}
void _putpixel(BITMAP *bmp, int x, int y, int color) {
Graphics::ManagedSurface &surf = **bmp;
if (x < 0 || x >= surf.w || y < 0 || y >= surf.h)
return;
void *p = surf.getBasePtr(x, y);
*((uint8 *)p) = color;
}
void _putpixel15(BITMAP *bmp, int x, int y, int color) {
error("Unsupported bpp");
}
void _putpixel16(BITMAP *bmp, int x, int y, int color) {
Graphics::ManagedSurface &surf = **bmp;
if (x < 0 || x >= surf.w || y < 0 || y >= surf.h)
return;
void *p = surf.getBasePtr(x, y);
*((uint16 *)p) = color;
}
void _putpixel24(BITMAP *bmp, int x, int y, int color) {
error("Unsupported bpp");
}
void _putpixel32(BITMAP *bmp, int x, int y, int color) {
Graphics::ManagedSurface &surf = **bmp;
if (x < 0 || x >= surf.w || y < 0 || y >= surf.h)
return;
void *p = surf.getBasePtr(x, y);
*((uint32 *)p) = color;
}
int getpixel(const BITMAP *bmp, int x, int y) {
Graphics::ManagedSurface &surf = **bmp;
// Allegro returns -1 if the pixel lies outside the bitmap
if (x < 0 || y < 0 || x >= surf.w || y >= surf.h)
return -1;
void *p = surf.getBasePtr(x, y);
switch (surf.format.bytesPerPixel) {
case 1:
return *((uint8 *)p);
case 2:
return *((uint16 *)p);
case 4:
return *((uint32 *)p);
default:
break;
}
error("Unsupported bpp");
}
int _getpixel(const BITMAP *bmp, int x, int y) {
Graphics::ManagedSurface &surf = **bmp;
if (x < 0 || y < 0 || x >= surf.w || y >= surf.h)
return -1;
void *p = surf.getBasePtr(x, y);
return *((uint8 *)p);
}
int _getpixel15(const BITMAP *bmp, int x, int y) {
error("Unsupported bpp");
}
int _getpixel16(const BITMAP *bmp, int x, int y) {
Graphics::ManagedSurface &surf = **bmp;
if (x < 0 || y < 0 || x >= surf.w || y >= surf.h)
return -1;
void *p = surf.getBasePtr(x, y);
return *((uint16 *)p);
}
int _getpixel24(const BITMAP *bmp, int x, int y) {
error("Unsupported bpp");
}
int _getpixel32(const BITMAP *bmp, int x, int y) {
Graphics::ManagedSurface &surf = **bmp;
if (x < 0 || y < 0 || x >= surf.w || y >= surf.h)
return -1;
void *p = surf.getBasePtr(x, y);
return *((uint32 *)p);
}
void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color) {
Graphics::ManagedSurface &surf = **bmp;
surf.drawLine(x1, y1, x2, y2, color);
}
void rect(BITMAP *bmp, int x1, int y1, int x2, int y2, int color) {
Graphics::ManagedSurface &surf = **bmp;
if (x1 > x2)
SWAP(x1, x2);
if (y1 > y2)
SWAP(y1, y2);
surf.frameRect(Common::Rect(x1, y1, x2 + 1, y2 + 1), color);
}
void rectfill(BITMAP *bmp, int x1, int y1, int x2, int y2, int color) {
Graphics::ManagedSurface &surf = **bmp;
if (x1 > x2)
SWAP(x1, x2);
if (y1 > y2)
SWAP(y1, y2);
surf.fillRect(Common::Rect(x1, y1, x2 + 1, y2 + 1), color);
}
void triangle(BITMAP *bmp, int x1, int y1, int x2, int y2, int x3, int y3, int color) {
Graphics::ManagedSurface &surf = **bmp;
surf.drawLine(x1, y1, x2, y2, color);
surf.drawLine(x2, y2, x3, y3, color);
surf.drawLine(x3, y3, x1, y1, color);
}
void circlefill(BITMAP *bmp, int x, int y, int radius, int color) {
bmp->circlefill(x, y, radius, color);
}
void clear_bitmap(BITMAP *bmp) {
bmp->clear();
}
} // namespace AGS3

View File

@@ -0,0 +1,240 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_GRAPHICS_H
#define AGS_LIB_ALLEGRO_GRAPHICS_H
#include "graphics/managed_surface.h"
#include "ags/lib/allegro/base.h"
#include "ags/lib/allegro/fixed.h"
#include "ags/lib/allegro/surface.h"
#include "common/array.h"
namespace AGS3 {
#define GFX_TEXT -1
#define GFX_AUTODETECT 0
#define GFX_AUTODETECT_FULLSCREEN 1
#define GFX_AUTODETECT_WINDOWED 2
#define GFX_SAFE AL_ID('S','A','F','E')
#define GFX_NONE AL_ID('N','O','N','E')
/* Bitfield for relaying graphics driver type information */
#define GFX_TYPE_UNKNOWN 0
#define GFX_TYPE_WINDOWED 1
#define GFX_TYPE_FULLSCREEN 2
#define GFX_TYPE_DEFINITE 4
#define GFX_TYPE_MAGIC 8
/* drawing modes for draw_sprite_ex() */
#define DRAW_SPRITE_NORMAL 0
#define DRAW_SPRITE_LIT 1
#define DRAW_SPRITE_TRANS 2
/* flipping modes for draw_sprite_ex() */
#define DRAW_SPRITE_NO_FLIP 0x0
#define DRAW_SPRITE_H_FLIP 0x1
#define DRAW_SPRITE_V_FLIP 0x2
#define DRAW_SPRITE_VH_FLIP 0x3
/* Blender mode defines, for the gfx_driver->set_blender_mode() function */
#define blender_mode_none 0
#define blender_mode_trans 1
#define blender_mode_add 2
#define blender_mode_burn 3
#define blender_mode_color 4
#define blender_mode_difference 5
#define blender_mode_dissolve 6
#define blender_mode_dodge 7
#define blender_mode_hue 8
#define blender_mode_invert 9
#define blender_mode_luminance 10
#define blender_mode_multiply 11
#define blender_mode_saturation 12
#define blender_mode_screen 13
#define blender_mode_alpha 14
#define SCREEN_W (gfx_driver ? gfx_driver->w : 0)
#define SCREEN_H (gfx_driver ? gfx_driver->h : 0)
#define VIRTUAL_W (screen ? screen->w : 0)
#define VIRTUAL_H (screen ? screen->h : 0)
#define COLORCONV_NONE 0
#define COLORCONV_8_TO_15 1
#define COLORCONV_8_TO_16 2
#define COLORCONV_8_TO_24 4
#define COLORCONV_8_TO_32 8
#define COLORCONV_15_TO_8 0x10
#define COLORCONV_15_TO_16 0x20
#define COLORCONV_15_TO_24 0x40
#define COLORCONV_15_TO_32 0x80
#define COLORCONV_16_TO_8 0x100
#define COLORCONV_16_TO_15 0x200
#define COLORCONV_16_TO_24 0x400
#define COLORCONV_16_TO_32 0x800
#define COLORCONV_24_TO_8 0x1000
#define COLORCONV_24_TO_15 0x2000
#define COLORCONV_24_TO_16 0x4000
#define COLORCONV_24_TO_32 0x8000
#define COLORCONV_32_TO_8 0x10000
#define COLORCONV_32_TO_15 0x20000
#define COLORCONV_32_TO_16 0x40000
#define COLORCONV_32_TO_24 0x80000
#define COLORCONV_32A_TO_8 0x100000
#define COLORCONV_32A_TO_15 0x200000
#define COLORCONV_32A_TO_16 0x400000
#define COLORCONV_32A_TO_24 0x800000
#define COLORCONV_DITHER_PAL 0x1000000
#define COLORCONV_DITHER_HI 0x2000000
#define COLORCONV_KEEP_TRANS 0x4000000
#define COLORCONV_DITHER (COLORCONV_DITHER_PAL | \
COLORCONV_DITHER_HI)
#define COLORCONV_EXPAND_256 (COLORCONV_8_TO_15 | \
COLORCONV_8_TO_16 | \
COLORCONV_8_TO_24 | \
COLORCONV_8_TO_32)
#define COLORCONV_REDUCE_TO_256 (COLORCONV_15_TO_8 | \
COLORCONV_16_TO_8 | \
COLORCONV_24_TO_8 | \
COLORCONV_32_TO_8 | \
COLORCONV_32A_TO_8)
#define COLORCONV_EXPAND_15_TO_16 COLORCONV_15_TO_16
#define COLORCONV_REDUCE_16_TO_15 COLORCONV_16_TO_15
#define COLORCONV_EXPAND_HI_TO_TRUE (COLORCONV_15_TO_24 | \
COLORCONV_15_TO_32 | \
COLORCONV_16_TO_24 | \
COLORCONV_16_TO_32)
#define COLORCONV_REDUCE_TRUE_TO_HI (COLORCONV_24_TO_15 | \
COLORCONV_24_TO_16 | \
COLORCONV_32_TO_15 | \
COLORCONV_32_TO_16)
#define COLORCONV_24_EQUALS_32 (COLORCONV_24_TO_32 | \
COLORCONV_32_TO_24)
#define COLORCONV_TOTAL (COLORCONV_EXPAND_256 | \
COLORCONV_REDUCE_TO_256 | \
COLORCONV_EXPAND_15_TO_16 | \
COLORCONV_REDUCE_16_TO_15 | \
COLORCONV_EXPAND_HI_TO_TRUE | \
COLORCONV_REDUCE_TRUE_TO_HI | \
COLORCONV_24_EQUALS_32 | \
COLORCONV_32A_TO_15 | \
COLORCONV_32A_TO_16 | \
COLORCONV_32A_TO_24)
#define COLORCONV_PARTIAL (COLORCONV_EXPAND_15_TO_16 | \
COLORCONV_REDUCE_16_TO_15 | \
COLORCONV_24_EQUALS_32)
#define COLORCONV_MOST (COLORCONV_EXPAND_15_TO_16 | \
COLORCONV_REDUCE_16_TO_15 | \
COLORCONV_EXPAND_HI_TO_TRUE | \
COLORCONV_REDUCE_TRUE_TO_HI | \
COLORCONV_24_EQUALS_32)
#define COLORCONV_KEEP_ALPHA (COLORCONV_TOTAL \
& ~(COLORCONV_32A_TO_8 | \
COLORCONV_32A_TO_15 | \
COLORCONV_32A_TO_16 | \
COLORCONV_32A_TO_24))
AL_FUNC(void, set_color_conversion, (int mode));
AL_FUNC(int, get_color_conversion, ());
AL_FUNC(int, set_gfx_mode, (int card, int w, int h, int depth));
AL_FUNC(void, set_clip_rect, (BITMAP *bitmap, int x1, int y1, int x2, int y2));
AL_FUNC(void, get_clip_rect, (BITMAP *bitmap, int *x1, int *y1, int *x2, int *y2));
AL_FUNC(void, clear_bitmap, (BITMAP *bitmap));
AL_FUNC(void, acquire_bitmap, (BITMAP *bitmap));
AL_FUNC(void, release_bitmap, (BITMAP *bitmap));
AL_FUNC(void, draw_sprite, (BITMAP *bmp, const BITMAP *sprite, int x, int y));
AL_FUNC(void, stretch_sprite, (BITMAP *bmp, const BITMAP *sprite, int x, int y, int w, int h));
extern void clear_to_color(BITMAP *bitmap, int color);
extern int bitmap_color_depth(BITMAP *bmp);
extern int bitmap_mask_color(BITMAP *bmp);
extern void add_palette_if_needed(Graphics::ManagedSurface &surf);
extern void blit(const BITMAP *src, BITMAP *dest, int src_x, int src_y, int dst_x, int dst_y, int width, int height);
extern void masked_blit(const BITMAP *src, BITMAP *dest, int src_x, int src_y, int dst_x, int dst_y, int width, int height);
extern void stretch_blit(const BITMAP *src, BITMAP *dest, int source_x, int source_y, int source_width, int source_height,
int dest_x, int dest_y, int dest_width, int dest_height);
extern void masked_stretch_blit(const BITMAP *src, BITMAP *dest, int source_x, int source_y, int source_width, int source_height,
int dest_x, int dest_y, int dest_width, int dest_height);
extern void draw_trans_sprite(BITMAP *bmp, const BITMAP *sprite, int x, int y);
extern void draw_lit_sprite(BITMAP *bmp, const BITMAP *sprite, int x, int y, int color);
extern void draw_sprite_h_flip(BITMAP *bmp, const BITMAP *sprite, int x, int y);
extern void draw_sprite_v_flip(BITMAP *bmp, const BITMAP *sprite, int x, int y);
extern void draw_sprite_vh_flip(BITMAP *bmp, const BITMAP *sprite, int x, int y);
extern void rotate_sprite(BITMAP *bmp, const BITMAP *sprite, int x, int y, fixed angle);
extern void pivot_sprite(BITMAP *bmp, const BITMAP *sprite, int x, int y, int cx, int cy, fixed angle);
extern bool is_screen_bitmap(BITMAP *bmp);
extern bool is_video_bitmap(BITMAP *bmp);
extern bool is_linear_bitmap(BITMAP *bmp);
extern bool is_same_bitmap(BITMAP *bmp1, BITMAP *bmp2);
extern bool is_planar_bitmap(BITMAP *bmp);
extern void bmp_select(BITMAP *bmp);
extern byte *bmp_write_line(BITMAP *bmp, int line);
extern void bmp_write8(byte *addr, int color);
extern void bmp_write15(byte *addr, int color);
extern void bmp_write16(byte *addr, int color);
extern void bmp_write24(byte *addr, int color);
extern void bmp_write32(byte *addr, int color);
extern void memory_putpixel(BITMAP *bmp, int x, int y, int color);
extern void putpixel(BITMAP *bmp, int x, int y, int color);
extern void _putpixel(BITMAP *bmp, int x, int y, int color);
extern void _putpixel15(BITMAP *bmp, int x, int y, int color);
extern void _putpixel16(BITMAP *bmp, int x, int y, int color);
extern void _putpixel24(BITMAP *bmp, int x, int y, int color);
extern void _putpixel32(BITMAP *bmp, int x, int y, int color);
extern int getpixel(const BITMAP *bmp, int x, int y);
extern int _getpixel(const BITMAP *bmp, int x, int y);
extern int _getpixel15(const BITMAP *bmp, int x, int y);
extern int _getpixel16(const BITMAP *bmp, int x, int y);
extern int _getpixel24(const BITMAP *bmp, int x, int y);
extern int _getpixel32(const BITMAP *bmp, int x, int y);
extern void line(BITMAP *bmp, int x1, int y_1, int x2, int y2, int color);
extern void rect(BITMAP *bmp, int x1, int y_1, int x2, int y2, int color);
extern void rectfill(BITMAP *bmp, int x1, int y_1, int x2, int y2, int color);
extern void triangle(BITMAP *bmp, int x1, int y_1, int x2, int y2, int x3, int y3, int color);
extern void circlefill(BITMAP *bmp, int x, int y, int radius, int color);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,48 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/gfx.h"
#include "ags/lib/allegro/color.h"
namespace AGS3 {
/* lookup table for scaling 5 bit colors up to 8 bits */
const int _rgb_scale_5[32] = {
0, 8, 16, 24, 33, 41, 49, 57,
66, 74, 82, 90, 99, 107, 115, 123,
132, 140, 148, 156, 165, 173, 181, 189,
198, 206, 214, 222, 231, 239, 247, 255
};
/* lookup table for scaling 6 bit colors up to 8 bits */
const int _rgb_scale_6[64] = {
0, 4, 8, 12, 16, 20, 24, 28,
32, 36, 40, 44, 48, 52, 56, 60,
65, 69, 73, 77, 81, 85, 89, 93,
97, 101, 105, 109, 113, 117, 121, 125,
130, 134, 138, 142, 146, 150, 154, 158,
162, 166, 170, 174, 178, 182, 186, 190,
195, 199, 203, 207, 211, 215, 219, 223,
227, 231, 235, 239, 243, 247, 251, 255
};
} // namespace AGS3

View File

@@ -0,0 +1,277 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_MATH_H
#define AGS_LIB_ALLEGRO_MATH_H
#include "ags/lib/allegro/error.h"
#include "ags/lib/allegro/fixed.h"
#include "ags/globals.h"
namespace AGS3 {
const fixed _cos_tbl[512] = {
/* precalculated fixed point (16.16) cosines for a full circle (0-255) */
65536L, 65531L, 65516L, 65492L, 65457L, 65413L, 65358L, 65294L,
65220L, 65137L, 65043L, 64940L, 64827L, 64704L, 64571L, 64429L,
64277L, 64115L, 63944L, 63763L, 63572L, 63372L, 63162L, 62943L,
62714L, 62476L, 62228L, 61971L, 61705L, 61429L, 61145L, 60851L,
60547L, 60235L, 59914L, 59583L, 59244L, 58896L, 58538L, 58172L,
57798L, 57414L, 57022L, 56621L, 56212L, 55794L, 55368L, 54934L,
54491L, 54040L, 53581L, 53114L, 52639L, 52156L, 51665L, 51166L,
50660L, 50146L, 49624L, 49095L, 48559L, 48015L, 47464L, 46906L,
46341L, 45769L, 45190L, 44604L, 44011L, 43412L, 42806L, 42194L,
41576L, 40951L, 40320L, 39683L, 39040L, 38391L, 37736L, 37076L,
36410L, 35738L, 35062L, 34380L, 33692L, 33000L, 32303L, 31600L,
30893L, 30182L, 29466L, 28745L, 28020L, 27291L, 26558L, 25821L,
25080L, 24335L, 23586L, 22834L, 22078L, 21320L, 20557L, 19792L,
19024L, 18253L, 17479L, 16703L, 15924L, 15143L, 14359L, 13573L,
12785L, 11996L, 11204L, 10411L, 9616L, 8820L, 8022L, 7224L,
6424L, 5623L, 4821L, 4019L, 3216L, 2412L, 1608L, 804L,
0L, -804L, -1608L, -2412L, -3216L, -4019L, -4821L, -5623L,
-6424L, -7224L, -8022L, -8820L, -9616L, -10411L, -11204L, -11996L,
-12785L, -13573L, -14359L, -15143L, -15924L, -16703L, -17479L, -18253L,
-19024L, -19792L, -20557L, -21320L, -22078L, -22834L, -23586L, -24335L,
-25080L, -25821L, -26558L, -27291L, -28020L, -28745L, -29466L, -30182L,
-30893L, -31600L, -32303L, -33000L, -33692L, -34380L, -35062L, -35738L,
-36410L, -37076L, -37736L, -38391L, -39040L, -39683L, -40320L, -40951L,
-41576L, -42194L, -42806L, -43412L, -44011L, -44604L, -45190L, -45769L,
-46341L, -46906L, -47464L, -48015L, -48559L, -49095L, -49624L, -50146L,
-50660L, -51166L, -51665L, -52156L, -52639L, -53114L, -53581L, -54040L,
-54491L, -54934L, -55368L, -55794L, -56212L, -56621L, -57022L, -57414L,
-57798L, -58172L, -58538L, -58896L, -59244L, -59583L, -59914L, -60235L,
-60547L, -60851L, -61145L, -61429L, -61705L, -61971L, -62228L, -62476L,
-62714L, -62943L, -63162L, -63372L, -63572L, -63763L, -63944L, -64115L,
-64277L, -64429L, -64571L, -64704L, -64827L, -64940L, -65043L, -65137L,
-65220L, -65294L, -65358L, -65413L, -65457L, -65492L, -65516L, -65531L,
-65536L, -65531L, -65516L, -65492L, -65457L, -65413L, -65358L, -65294L,
-65220L, -65137L, -65043L, -64940L, -64827L, -64704L, -64571L, -64429L,
-64277L, -64115L, -63944L, -63763L, -63572L, -63372L, -63162L, -62943L,
-62714L, -62476L, -62228L, -61971L, -61705L, -61429L, -61145L, -60851L,
-60547L, -60235L, -59914L, -59583L, -59244L, -58896L, -58538L, -58172L,
-57798L, -57414L, -57022L, -56621L, -56212L, -55794L, -55368L, -54934L,
-54491L, -54040L, -53581L, -53114L, -52639L, -52156L, -51665L, -51166L,
-50660L, -50146L, -49624L, -49095L, -48559L, -48015L, -47464L, -46906L,
-46341L, -45769L, -45190L, -44604L, -44011L, -43412L, -42806L, -42194L,
-41576L, -40951L, -40320L, -39683L, -39040L, -38391L, -37736L, -37076L,
-36410L, -35738L, -35062L, -34380L, -33692L, -33000L, -32303L, -31600L,
-30893L, -30182L, -29466L, -28745L, -28020L, -27291L, -26558L, -25821L,
-25080L, -24335L, -23586L, -22834L, -22078L, -21320L, -20557L, -19792L,
-19024L, -18253L, -17479L, -16703L, -15924L, -15143L, -14359L, -13573L,
-12785L, -11996L, -11204L, -10411L, -9616L, -8820L, -8022L, -7224L,
-6424L, -5623L, -4821L, -4019L, -3216L, -2412L, -1608L, -804L,
0L, 804L, 1608L, 2412L, 3216L, 4019L, 4821L, 5623L,
6424L, 7224L, 8022L, 8820L, 9616L, 10411L, 11204L, 11996L,
12785L, 13573L, 14359L, 15143L, 15924L, 16703L, 17479L, 18253L,
19024L, 19792L, 20557L, 21320L, 22078L, 22834L, 23586L, 24335L,
25080L, 25821L, 26558L, 27291L, 28020L, 28745L, 29466L, 30182L,
30893L, 31600L, 32303L, 33000L, 33692L, 34380L, 35062L, 35738L,
36410L, 37076L, 37736L, 38391L, 39040L, 39683L, 40320L, 40951L,
41576L, 42194L, 42806L, 43412L, 44011L, 44604L, 45190L, 45769L,
46341L, 46906L, 47464L, 48015L, 48559L, 49095L, 49624L, 50146L,
50660L, 51166L, 51665L, 52156L, 52639L, 53114L, 53581L, 54040L,
54491L, 54934L, 55368L, 55794L, 56212L, 56621L, 57022L, 57414L,
57798L, 58172L, 58538L, 58896L, 59244L, 59583L, 59914L, 60235L,
60547L, 60851L, 61145L, 61429L, 61705L, 61971L, 62228L, 62476L,
62714L, 62943L, 63162L, 63372L, 63572L, 63763L, 63944L, 64115L,
64277L, 64429L, 64571L, 64704L, 64827L, 64940L, 65043L, 65137L,
65220L, 65294L, 65358L, 65413L, 65457L, 65492L, 65516L, 65531L
};
const fixed _tan_tbl[256] = {
/* precalculated fixed point (16.16) tangents for a half circle (0-127) */
0L, 804L, 1609L, 2414L, 3220L, 4026L, 4834L, 5644L,
6455L, 7268L, 8083L, 8901L, 9721L, 10545L, 11372L, 12202L,
13036L, 13874L, 14717L, 15564L, 16416L, 17273L, 18136L, 19005L,
19880L, 20762L, 21650L, 22546L, 23449L, 24360L, 25280L, 26208L,
27146L, 28093L, 29050L, 30018L, 30996L, 31986L, 32988L, 34002L,
35030L, 36071L, 37126L, 38196L, 39281L, 40382L, 41500L, 42636L,
43790L, 44963L, 46156L, 47369L, 48605L, 49863L, 51145L, 52451L,
53784L, 55144L, 56532L, 57950L, 59398L, 60880L, 62395L, 63947L,
65536L, 67165L, 68835L, 70548L, 72308L, 74116L, 75974L, 77887L,
79856L, 81885L, 83977L, 86135L, 88365L, 90670L, 93054L, 95523L,
98082L, 100736L, 103493L, 106358L, 109340L, 112447L, 115687L, 119071L,
122609L, 126314L, 130198L, 134276L, 138564L, 143081L, 147847L, 152884L,
158218L, 163878L, 169896L, 176309L, 183161L, 190499L, 198380L, 206870L,
216043L, 225990L, 236817L, 248648L, 261634L, 275959L, 291845L, 309568L,
329472L, 351993L, 377693L, 407305L, 441808L, 482534L, 531352L, 590958L,
665398L, 761030L, 888450L, 1066730L, 1334016L, 1779314L, 2669641L, 5340086L,
-2147483647L, -5340086L, -2669641L, -1779314L, -1334016L, -1066730L, -888450L, -761030L,
-665398L, -590958L, -531352L, -482534L, -441808L, -407305L, -377693L, -351993L,
-329472L, -309568L, -291845L, -275959L, -261634L, -248648L, -236817L, -225990L,
-216043L, -206870L, -198380L, -190499L, -183161L, -176309L, -169896L, -163878L,
-158218L, -152884L, -147847L, -143081L, -138564L, -134276L, -130198L, -126314L,
-122609L, -119071L, -115687L, -112447L, -109340L, -106358L, -103493L, -100736L,
-98082L, -95523L, -93054L, -90670L, -88365L, -86135L, -83977L, -81885L,
-79856L, -77887L, -75974L, -74116L, -72308L, -70548L, -68835L, -67165L,
-65536L, -63947L, -62395L, -60880L, -59398L, -57950L, -56532L, -55144L,
-53784L, -52451L, -51145L, -49863L, -48605L, -47369L, -46156L, -44963L,
-43790L, -42636L, -41500L, -40382L, -39281L, -38196L, -37126L, -36071L,
-35030L, -34002L, -32988L, -31986L, -30996L, -30018L, -29050L, -28093L,
-27146L, -26208L, -25280L, -24360L, -23449L, -22546L, -21650L, -20762L,
-19880L, -19005L, -18136L, -17273L, -16416L, -15564L, -14717L, -13874L,
-13036L, -12202L, -11372L, -10545L, -9721L, -8901L, -8083L, -7268L,
-6455L, -5644L, -4834L, -4026L, -3220L, -2414L, -1609L, -804L
};
const fixed _acos_tbl[513] = {
/* precalculated fixed point (16.16) inverse cosines (-1 to 1) */
0x800000L, 0x7C65C7L, 0x7AE75AL, 0x79C19EL, 0x78C9BEL, 0x77EF25L, 0x772953L, 0x76733AL,
0x75C991L, 0x752A10L, 0x74930CL, 0x740345L, 0x7379C1L, 0x72F5BAL, 0x72768FL, 0x71FBBCL,
0x7184D3L, 0x711174L, 0x70A152L, 0x703426L, 0x6FC9B5L, 0x6F61C9L, 0x6EFC36L, 0x6E98D1L,
0x6E3777L, 0x6DD805L, 0x6D7A5EL, 0x6D1E68L, 0x6CC40BL, 0x6C6B2FL, 0x6C13C1L, 0x6BBDAFL,
0x6B68E6L, 0x6B1558L, 0x6AC2F5L, 0x6A71B1L, 0x6A217EL, 0x69D251L, 0x698420L, 0x6936DFL,
0x68EA85L, 0x689F0AL, 0x685465L, 0x680A8DL, 0x67C17DL, 0x67792CL, 0x673194L, 0x66EAAFL,
0x66A476L, 0x665EE5L, 0x6619F5L, 0x65D5A2L, 0x6591E7L, 0x654EBFL, 0x650C26L, 0x64CA18L,
0x648890L, 0x64478CL, 0x640706L, 0x63C6FCL, 0x63876BL, 0x63484FL, 0x6309A5L, 0x62CB6AL,
0x628D9CL, 0x625037L, 0x621339L, 0x61D69FL, 0x619A68L, 0x615E90L, 0x612316L, 0x60E7F7L,
0x60AD31L, 0x6072C3L, 0x6038A9L, 0x5FFEE3L, 0x5FC56EL, 0x5F8C49L, 0x5F5372L, 0x5F1AE7L,
0x5EE2A7L, 0x5EAAB0L, 0x5E7301L, 0x5E3B98L, 0x5E0473L, 0x5DCD92L, 0x5D96F3L, 0x5D6095L,
0x5D2A76L, 0x5CF496L, 0x5CBEF2L, 0x5C898BL, 0x5C545EL, 0x5C1F6BL, 0x5BEAB0L, 0x5BB62DL,
0x5B81E1L, 0x5B4DCAL, 0x5B19E7L, 0x5AE638L, 0x5AB2BCL, 0x5A7F72L, 0x5A4C59L, 0x5A1970L,
0x59E6B6L, 0x59B42AL, 0x5981CCL, 0x594F9BL, 0x591D96L, 0x58EBBDL, 0x58BA0EL, 0x588889L,
0x58572DL, 0x5825FAL, 0x57F4EEL, 0x57C40AL, 0x57934DL, 0x5762B5L, 0x573243L, 0x5701F5L,
0x56D1CCL, 0x56A1C6L, 0x5671E4L, 0x564224L, 0x561285L, 0x55E309L, 0x55B3ADL, 0x558471L,
0x555555L, 0x552659L, 0x54F77BL, 0x54C8BCL, 0x549A1BL, 0x546B98L, 0x543D31L, 0x540EE7L,
0x53E0B9L, 0x53B2A7L, 0x5384B0L, 0x5356D4L, 0x532912L, 0x52FB6BL, 0x52CDDDL, 0x52A068L,
0x52730CL, 0x5245C9L, 0x52189EL, 0x51EB8BL, 0x51BE8FL, 0x5191AAL, 0x5164DCL, 0x513825L,
0x510B83L, 0x50DEF7L, 0x50B280L, 0x50861FL, 0x5059D2L, 0x502D99L, 0x500175L, 0x4FD564L,
0x4FA967L, 0x4F7D7DL, 0x4F51A6L, 0x4F25E2L, 0x4EFA30L, 0x4ECE90L, 0x4EA301L, 0x4E7784L,
0x4E4C19L, 0x4E20BEL, 0x4DF574L, 0x4DCA3AL, 0x4D9F10L, 0x4D73F6L, 0x4D48ECL, 0x4D1DF1L,
0x4CF305L, 0x4CC829L, 0x4C9D5AL, 0x4C729AL, 0x4C47E9L, 0x4C1D45L, 0x4BF2AEL, 0x4BC826L,
0x4B9DAAL, 0x4B733BL, 0x4B48D9L, 0x4B1E84L, 0x4AF43BL, 0x4AC9FEL, 0x4A9FCDL, 0x4A75A7L,
0x4A4B8DL, 0x4A217EL, 0x49F77AL, 0x49CD81L, 0x49A393L, 0x4979AFL, 0x494FD5L, 0x492605L,
0x48FC3FL, 0x48D282L, 0x48A8CFL, 0x487F25L, 0x485584L, 0x482BECL, 0x48025DL, 0x47D8D6L,
0x47AF57L, 0x4785E0L, 0x475C72L, 0x47330AL, 0x4709ABL, 0x46E052L, 0x46B701L, 0x468DB7L,
0x466474L, 0x463B37L, 0x461201L, 0x45E8D0L, 0x45BFA6L, 0x459682L, 0x456D64L, 0x45444BL,
0x451B37L, 0x44F229L, 0x44C920L, 0x44A01CL, 0x44771CL, 0x444E21L, 0x44252AL, 0x43FC38L,
0x43D349L, 0x43AA5FL, 0x438178L, 0x435894L, 0x432FB4L, 0x4306D8L, 0x42DDFEL, 0x42B527L,
0x428C53L, 0x426381L, 0x423AB2L, 0x4211E5L, 0x41E91AL, 0x41C051L, 0x41978AL, 0x416EC5L,
0x414601L, 0x411D3EL, 0x40F47CL, 0x40CBBBL, 0x40A2FBL, 0x407A3CL, 0x40517DL, 0x4028BEL,
0x400000L, 0x3FD742L, 0x3FAE83L, 0x3F85C4L, 0x3F5D05L, 0x3F3445L, 0x3F0B84L, 0x3EE2C2L,
0x3EB9FFL, 0x3E913BL, 0x3E6876L, 0x3E3FAFL, 0x3E16E6L, 0x3DEE1BL, 0x3DC54EL, 0x3D9C7FL,
0x3D73ADL, 0x3D4AD9L, 0x3D2202L, 0x3CF928L, 0x3CD04CL, 0x3CA76CL, 0x3C7E88L, 0x3C55A1L,
0x3C2CB7L, 0x3C03C8L, 0x3BDAD6L, 0x3BB1DFL, 0x3B88E4L, 0x3B5FE4L, 0x3B36E0L, 0x3B0DD7L,
0x3AE4C9L, 0x3ABBB5L, 0x3A929CL, 0x3A697EL, 0x3A405AL, 0x3A1730L, 0x39EDFFL, 0x39C4C9L,
0x399B8CL, 0x397249L, 0x3948FFL, 0x391FAEL, 0x38F655L, 0x38CCF6L, 0x38A38EL, 0x387A20L,
0x3850A9L, 0x38272AL, 0x37FDA3L, 0x37D414L, 0x37AA7CL, 0x3780DBL, 0x375731L, 0x372D7EL,
0x3703C1L, 0x36D9FBL, 0x36B02BL, 0x368651L, 0x365C6DL, 0x36327FL, 0x360886L, 0x35DE82L,
0x35B473L, 0x358A59L, 0x356033L, 0x353602L, 0x350BC5L, 0x34E17CL, 0x34B727L, 0x348CC5L,
0x346256L, 0x3437DAL, 0x340D52L, 0x33E2BBL, 0x33B817L, 0x338D66L, 0x3362A6L, 0x3337D7L,
0x330CFBL, 0x32E20FL, 0x32B714L, 0x328C0AL, 0x3260F0L, 0x3235C6L, 0x320A8CL, 0x31DF42L,
0x31B3E7L, 0x31887CL, 0x315CFFL, 0x313170L, 0x3105D0L, 0x30DA1EL, 0x30AE5AL, 0x308283L,
0x305699L, 0x302A9CL, 0x2FFE8BL, 0x2FD267L, 0x2FA62EL, 0x2F79E1L, 0x2F4D80L, 0x2F2109L,
0x2EF47DL, 0x2EC7DBL, 0x2E9B24L, 0x2E6E56L, 0x2E4171L, 0x2E1475L, 0x2DE762L, 0x2DBA37L,
0x2D8CF4L, 0x2D5F98L, 0x2D3223L, 0x2D0495L, 0x2CD6EEL, 0x2CA92CL, 0x2C7B50L, 0x2C4D59L,
0x2C1F47L, 0x2BF119L, 0x2BC2CFL, 0x2B9468L, 0x2B65E5L, 0x2B3744L, 0x2B0885L, 0x2AD9A7L,
0x2AAAABL, 0x2A7B8FL, 0x2A4C53L, 0x2A1CF7L, 0x29ED7BL, 0x29BDDCL, 0x298E1CL, 0x295E3AL,
0x292E34L, 0x28FE0BL, 0x28CDBDL, 0x289D4BL, 0x286CB3L, 0x283BF6L, 0x280B12L, 0x27DA06L,
0x27A8D3L, 0x277777L, 0x2745F2L, 0x271443L, 0x26E26AL, 0x26B065L, 0x267E34L, 0x264BD6L,
0x26194AL, 0x25E690L, 0x25B3A7L, 0x25808EL, 0x254D44L, 0x2519C8L, 0x24E619L, 0x24B236L,
0x247E1FL, 0x2449D3L, 0x241550L, 0x23E095L, 0x23ABA2L, 0x237675L, 0x23410EL, 0x230B6AL,
0x22D58AL, 0x229F6BL, 0x22690DL, 0x22326EL, 0x21FB8DL, 0x21C468L, 0x218CFFL, 0x215550L,
0x211D59L, 0x20E519L, 0x20AC8EL, 0x2073B7L, 0x203A92L, 0x20011DL, 0x1FC757L, 0x1F8D3DL,
0x1F52CFL, 0x1F1809L, 0x1EDCEAL, 0x1EA170L, 0x1E6598L, 0x1E2961L, 0x1DECC7L, 0x1DAFC9L,
0x1D7264L, 0x1D3496L, 0x1CF65BL, 0x1CB7B1L, 0x1C7895L, 0x1C3904L, 0x1BF8FAL, 0x1BB874L,
0x1B7770L, 0x1B35E8L, 0x1AF3DAL, 0x1AB141L, 0x1A6E19L, 0x1A2A5EL, 0x19E60BL, 0x19A11BL,
0x195B8AL, 0x191551L, 0x18CE6CL, 0x1886D4L, 0x183E83L, 0x17F573L, 0x17AB9BL, 0x1760F6L,
0x17157BL, 0x16C921L, 0x167BE0L, 0x162DAFL, 0x15DE82L, 0x158E4FL, 0x153D0BL, 0x14EAA8L,
0x14971AL, 0x144251L, 0x13EC3FL, 0x1394D1L, 0x133BF5L, 0x12E198L, 0x1285A2L, 0x1227FBL,
0x11C889L, 0x11672FL, 0x1103CAL, 0x109E37L, 0x10364BL, 0xFCBDAL, 0xF5EAEL, 0xEEE8CL,
0xE7B2DL, 0xE0444L, 0xD8971L, 0xD0A46L, 0xC863FL, 0xBFCBBL, 0xB6CF4L, 0xAD5F0L,
0xA366FL, 0x98CC6L, 0x8D6ADL, 0x810DBL, 0x73642L, 0x63E62L, 0x518A6L, 0x39A39L,
0x0L
};
/* fixatan:
* Fixed point inverse tangent. Does a binary search on the tan table.
*/
fixed fixatan(fixed x) {
int a, b, c; /* for binary search */
fixed d; /* difference value for search */
if (x >= 0) { /* search the first part of tan table */
a = 0;
b = 127;
} else { /* search the second half instead */
a = 128;
b = 255;
}
do {
c = (a + b) >> 1;
d = x - _tan_tbl[c];
if (d > 0)
a = c + 1;
else if (d < 0)
b = c - 1;
} while ((a <= b) && (d));
if (x >= 0)
return ((long)c) << 15;
return (-0x00800000L + (((long)c) << 15));
}
/* fixatan2:
* Like the libc atan2, but for fixed point numbers.
*/
fixed fixatan2(fixed y, fixed x) {
fixed r;
if (x == 0) {
if (y == 0) {
*_G(allegro_errno) = AL_EDOM;
return 0L;
} else
return ((y < 0) ? -0x00400000L : 0x00400000L);
}
*_G(allegro_errno) = AL_NOERROR;
r = fixdiv(y, x);
if (*_G(allegro_errno)) {
*_G(allegro_errno) = AL_NOERROR;
return ((y < 0) ? -0x00400000L : 0x00400000L);
}
r = fixatan(r);
if (x >= 0)
return r;
if (y >= 0)
return 0x00800000L + r;
return r - 0x00800000L;
}
} // namespace AGS3
#endif

View File

@@ -0,0 +1,374 @@
#include "ags/lib/allegro/rotate.h"
#include "ags/lib/allegro/gfx.h"
#include "common/scummsys.h"
namespace AGS3 {
/* rotate_scale_coordinates:
* Calculates the coordinates for the rotated, scaled and flipped sprite,
* and passes them on to the given function.
*/
void rotate_scale_coordinates(fixed w, fixed h,
fixed x, fixed y, fixed cx, fixed cy,
fixed angle,
fixed scale_x, fixed scale_y,
fixed xs[4], fixed ys[4])
{
// Setting angle to the range -180...180 degrees makes sin & cos more numerically stable.
// (Yes, this does have an effect for big angles!)
// Note that using "real" sin() and cos() gives much better precision than fixsin() and fixcos().
angle = angle & 0xffffff;
if (angle >= 0x800000)
angle -= 0x1000000;
double angle_radian = angle * (M_PI / (double)0x800000);
double sin_angle = sin(angle_radian);
double cos_angle = cos(angle_radian);
fixed fix_cos, fix_sin;
if (cos_angle >= 0)
fix_cos = (int)(cos_angle * 0x10000 + 0.5);
else
fix_cos = (int)(cos_angle * 0x10000 - 0.5);
if (sin_angle >= 0)
fix_sin = (int)(sin_angle * 0x10000 + 0.5);
else
fix_sin = (int)(sin_angle * 0x10000 - 0.5);
/* Decide what order to take corners in. */
int tl = 0, tr = 1, bl = 3, br = 2;
/* Calculate new coordinates of all corners. */
w = fixmul(w, scale_x);
h = fixmul(h, scale_y);
cx = fixmul(cx, scale_x);
cy = fixmul(cy, scale_y);
fixed xofs = x - fixmul(cx, fix_cos) + fixmul(cy, fix_sin);
fixed yofs = y - fixmul(cx, fix_sin) - fixmul(cy, fix_cos);
xs[tl] = xofs;
ys[tl] = yofs;
xs[tr] = xofs + fixmul(w, fix_cos);
ys[tr] = yofs + fixmul(w, fix_sin);
xs[bl] = xofs - fixmul(h, fix_sin);
ys[bl] = yofs + fixmul(h, fix_cos);
xs[br] = xs[tr] + xs[bl] - xs[tl];
ys[br] = ys[tr] + ys[bl] - ys[tl];
}
/* parallelogram_map:
* Worker routine for drawing rotated and/or scaled and/or flipped sprites:
* It actually maps the sprite to any parallelogram-shaped area of the
* bitmap. The top left corner is mapped to (xs[0], ys[0]), the top right to
* (xs[1], ys[1]), the bottom right to x (xs[2], ys[2]), and the bottom left
* to (xs[3], ys[3]). The corners are assumed to form a perfect
* parallelogram, i.e. xs[0]+xs[2] = xs[1]+xs[3]. The corners are given in
* fixed point format, so xs[] and ys[] are coordinates of the outer corners
* of corner pixels in clockwise order beginning with top left.
* All coordinates begin with 0 in top left corner of pixel (0, 0). So a
* rotation by 0 degrees of a sprite to the top left of a bitmap can be
* specified with coordinates (0, 0) for the top left pixel in source
* bitmap. With the default scanline drawer, a pixel in the destination
* bitmap is drawn if and only if its center is covered by any pixel in the
* sprite. The color of this covering sprite pixel is used to draw.
*/
void parallelogram_map(BITMAP *bmp, const BITMAP *spr, fixed xs[4], fixed ys[4]) {
// Get index of topmost point.
int top_index = 0;
if (ys[1] < ys[0])
top_index = 1;
if (ys[2] < ys[top_index])
top_index = 2;
if (ys[3] < ys[top_index])
top_index = 3;
// Get direction of points: clockwise or anti-clockwise.
int right_index = (double)(xs[(top_index+1) & 3] - xs[top_index]) *
(double)(ys[(top_index-1) & 3] - ys[top_index]) >
(double)(xs[(top_index-1) & 3] - xs[top_index]) *
(double)(ys[(top_index+1) & 3] - ys[top_index]) ? 1 : -1;
// Get coordinates of the corners.
// Coordinates in bmp and sprite ordered as top-right-bottom-left.
fixed corner_bmp_x[4], corner_bmp_y[4];
fixed corner_spr_x[4], corner_spr_y[4];
int index = top_index;
for (int i = 0; i < 4; i++) {
corner_bmp_x[i] = xs[index];
corner_bmp_y[i] = ys[index];
if (index < 2)
corner_spr_y[i] = 0;
else
// Need `- 1' since otherwise it would be outside sprite.
corner_spr_y[i] = (spr->h << 16) - 1;
if ((index == 0) || (index == 3))
corner_spr_x[i] = 0;
else
corner_spr_x[i] = (spr->w << 16) - 1;
index = (index + right_index) & 3;
}
// Get scanline starts, ends and deltas, and clipping coordinates.
#define top_bmp_y corner_bmp_y[0]
#define right_bmp_y corner_bmp_y[1]
#define bottom_bmp_y corner_bmp_y[2]
#define left_bmp_y corner_bmp_y[3]
#define top_bmp_x corner_bmp_x[0]
#define right_bmp_x corner_bmp_x[1]
#define bottom_bmp_x corner_bmp_x[2]
#define left_bmp_x corner_bmp_x[3]
#define top_spr_y corner_spr_y[0]
#define right_spr_y corner_spr_y[1]
#define bottom_spr_y corner_spr_y[2]
#define left_spr_y corner_spr_y[3]
#define top_spr_x corner_spr_x[0]
#define right_spr_x corner_spr_x[1]
#define bottom_spr_x corner_spr_x[2]
#define left_spr_x corner_spr_x[3]
// Calculate left and right clipping.
fixed clip_left, clip_right;
if (bmp->clip) {
clip_left = bmp->cl << 16;
clip_right = (bmp->cr << 16) - 1;
} else {
clip_left = 0;
clip_right = (bmp->w << 16) - 1;
}
// Stop if we're totally outside.
if ((left_bmp_x > clip_right) && (top_bmp_x > clip_right) && (bottom_bmp_x > clip_right))
return;
if ((right_bmp_x < clip_left) && (top_bmp_x < clip_left) && (bottom_bmp_x < clip_left))
return;
// Bottom clipping.
int clip_bottom_i = (bottom_bmp_y + 0x8000) >> 16;
if (bmp->clip) {
if (clip_bottom_i > bmp->cb)
clip_bottom_i = bmp->cb;
}
// Calculate y coordinate of first scanline.
int bmp_y_i = (top_bmp_y + 0x8000) >> 16;
if (bmp->clip) {
if (bmp_y_i < bmp->ct)
bmp_y_i = bmp->ct;
}
// Sprite is above or below bottom clipping area.
if (bmp_y_i >= clip_bottom_i)
return;
// Vertical gap between top corner and centre of topmost scanline.
fixed extra_scanline_fraction = (bmp_y_i << 16) + 0x8000 - top_bmp_y;
// Calculate x coordinate of beginning of scanline in bmp.
fixed l_bmp_dx = fixdiv(left_bmp_x - top_bmp_x, left_bmp_y - top_bmp_y);
fixed l_bmp_x = top_bmp_x + fixmul(extra_scanline_fraction, l_bmp_dx);
// Calculate x coordinate of beginning of scanline in spr.
// note: all these are rounded down which is probably a Good Thing (tm)
fixed l_spr_dx = fixdiv(left_spr_x - top_spr_x, left_bmp_y - top_bmp_y);
fixed l_spr_x = top_spr_x + fixmul(extra_scanline_fraction, l_spr_dx);
// Calculate y coordinate of beginning of scanline in spr.
fixed l_spr_dy = fixdiv(left_spr_y - top_spr_y, left_bmp_y - top_bmp_y);
fixed l_spr_y = top_spr_y + fixmul(extra_scanline_fraction, l_spr_dy);
// Calculate left loop bound.
int l_bmp_y_bottom_i = (left_bmp_y + 0x8000) >> 16;
if (l_bmp_y_bottom_i > clip_bottom_i)
l_bmp_y_bottom_i = clip_bottom_i;
// Calculate x coordinate of end of scanline in bmp.
fixed r_bmp_dx = fixdiv(right_bmp_x - top_bmp_x, right_bmp_y - top_bmp_y);
fixed r_bmp_x = top_bmp_x + fixmul(extra_scanline_fraction, r_bmp_dx);
// Calculate right loop bound.
int r_bmp_y_bottom_i = (right_bmp_y + 0x8000) >> 16;
// Get dx and dy, the offsets to add to the source coordinates as we move
// one pixel rightwards along a scanline. This formula can be derived by
// considering the 2x2 matrix that transforms the sprite to the
// parallelogram.
// We'd better use double to get this as exact as possible, since any
// errors will be accumulated along the scanline.
fixed spr_dx = (fixed)((ys[3] - ys[0]) * 65536.0 * (65536.0 * spr->w) /
((xs[1] - xs[0]) * (double)(ys[3] - ys[0]) - (xs[3] - xs[0]) * (double)(ys[1] - ys[0])));
fixed spr_dy = (fixed)((ys[1] - ys[0]) * 65536.0 * (65536.0 * spr->h) /
((xs[3] - xs[0]) * (double)(ys[1] - ys[0]) - (xs[1] - xs[0]) * (double)(ys[3] - ys[0])));
bool sameFormat = (spr->format == bmp->format);
uint32 transColor = 0, alphaMask = 0xff;
if (spr->format.bytesPerPixel != 1) {
transColor = spr->format.ARGBToColor(0, 255, 0, 255);
alphaMask = spr->format.ARGBToColor(255, 0, 0, 0);
alphaMask = ~alphaMask;
}
// Loop through scanlines.
while (1) {
// Has beginning of scanline passed a corner?
if (bmp_y_i >= l_bmp_y_bottom_i) {
// Are we done?
if (bmp_y_i >= clip_bottom_i)
break;
// Vertical gap between left corner and centre of scanline.
extra_scanline_fraction = (bmp_y_i << 16) + 0x8000 - left_bmp_y;
// Update x coordinate of beginning of scanline in bmp.
l_bmp_dx = fixdiv(bottom_bmp_x - left_bmp_x, bottom_bmp_y - left_bmp_y);
l_bmp_x = left_bmp_x + fixmul(extra_scanline_fraction, l_bmp_dx);
// Update x coordinate of beginning of scanline in spr.
l_spr_dx = fixdiv(bottom_spr_x - left_spr_x, bottom_bmp_y - left_bmp_y);
l_spr_x = left_spr_x + fixmul(extra_scanline_fraction, l_spr_dx);
// Update y coordinate of beginning of scanline in spr.
l_spr_dy = fixdiv(bottom_spr_y - left_spr_y, bottom_bmp_y - left_bmp_y);
l_spr_y = left_spr_y + fixmul(extra_scanline_fraction, l_spr_dy);
// Update loop bound.
l_bmp_y_bottom_i = (bottom_bmp_y + 0x8000) >> 16;
if (l_bmp_y_bottom_i > clip_bottom_i)
l_bmp_y_bottom_i = clip_bottom_i;
}
// Has end of scanline passed a corner?
if (bmp_y_i >= r_bmp_y_bottom_i) {
// Vertical gap between right corner and centre of scanline.
extra_scanline_fraction = (bmp_y_i << 16) + 0x8000 - right_bmp_y;
// Update x coordinate of end of scanline in bmp.
r_bmp_dx = fixdiv(bottom_bmp_x - right_bmp_x, bottom_bmp_y - right_bmp_y);
r_bmp_x = right_bmp_x + fixmul(extra_scanline_fraction, r_bmp_dx);
// Update loop bound: We aren't supposed to use this any more, so
// just set it to some big enough value.
r_bmp_y_bottom_i = clip_bottom_i;
}
// Make left bmp coordinate be an integer and clip it.
fixed l_bmp_x_rounded;
l_bmp_x_rounded = (l_bmp_x + 0x8000) & ~0xffff;
if (l_bmp_x_rounded < clip_left)
l_bmp_x_rounded = clip_left;
// ... and move starting point in sprite accordingly.
fixed l_spr_x_rounded = l_spr_x + fixmul(l_bmp_x_rounded + 0x7fff - l_bmp_x, spr_dx);
fixed l_spr_y_rounded = l_spr_y + fixmul(l_bmp_x_rounded + 0x7fff - l_bmp_x, spr_dy);
// Make right bmp coordinate be an integer and clip it.
fixed r_bmp_x_rounded = (r_bmp_x - 0x8000) & ~0xffff;
if (r_bmp_x_rounded > clip_right)
r_bmp_x_rounded = clip_right;
// Draw!
if (l_bmp_x_rounded <= r_bmp_x_rounded) {
/* The bodies of these ifs are only reached extremely seldom,
it's an ugly hack to avoid reading outside the sprite when
the rounding errors are accumulated the wrong way. It would
be nicer if we could ensure that this never happens by making
all multiplications and divisions be rounded up or down at
the correct places.
I did try another approach: recalculate the edges of the
scanline from scratch each scanline rather than incrementally.
Drawing a sprite with that routine took about 25% longer time
though.
*/
if ((unsigned)(l_spr_x_rounded >> 16) >= (unsigned)spr->w) {
if (((l_spr_x_rounded < 0) && (spr_dx <= 0)) || ((l_spr_x_rounded > 0) && (spr_dx >= 0))) {
// This can happen.
goto skip_draw;
} else {
// I don't think this can happen, but I can't prove it.
do {
l_spr_x_rounded += spr_dx;
l_bmp_x_rounded += 65536;
if (l_bmp_x_rounded > r_bmp_x_rounded)
goto skip_draw;
} while ((unsigned)(l_spr_x_rounded >> 16) >= (unsigned)spr->w);
}
}
int right_edge_test = l_spr_x_rounded + ((r_bmp_x_rounded - l_bmp_x_rounded) >> 16) * spr_dx;
if ((unsigned)(right_edge_test >> 16) >= (unsigned)spr->w) {
if (((right_edge_test < 0) && (spr_dx <= 0)) || ((right_edge_test > 0) && (spr_dx >= 0))) {
// This can happen.
do {
r_bmp_x_rounded -= 65536;
right_edge_test -= spr_dx;
if (l_bmp_x_rounded > r_bmp_x_rounded)
goto skip_draw;
} while ((unsigned)(right_edge_test >> 16) >= (unsigned)spr->w);
} else {
// I don't think this can happen, but I can't prove it.
goto skip_draw;
}
}
if ((unsigned)(l_spr_y_rounded >> 16) >= (unsigned)spr->h) {
if (((l_spr_y_rounded < 0) && (spr_dy <= 0)) || ((l_spr_y_rounded > 0) && (spr_dy >= 0))) {
// This can happen.
goto skip_draw;
} else {
// I don't think this can happen, but I can't prove it.
do {
l_spr_y_rounded += spr_dy;
l_bmp_x_rounded += 65536;
if (l_bmp_x_rounded > r_bmp_x_rounded)
goto skip_draw;
} while (((unsigned)l_spr_y_rounded >> 16) >= (unsigned)spr->h);
}
}
right_edge_test = l_spr_y_rounded + ((r_bmp_x_rounded - l_bmp_x_rounded) >> 16) * spr_dy;
if ((unsigned)(right_edge_test >> 16) >= (unsigned)spr->h) {
if (((right_edge_test < 0) && (spr_dy <= 0)) || ((right_edge_test > 0) && (spr_dy >= 0))) {
// This can happen.
do {
r_bmp_x_rounded -= 65536;
right_edge_test -= spr_dy;
if (l_bmp_x_rounded > r_bmp_x_rounded)
goto skip_draw;
} while ((unsigned)(right_edge_test >> 16) >= (unsigned)spr->h);
} else {
// I don't think this can happen, but I can't prove it.
goto skip_draw;
}
}
// draw scanline
int r_bmp_x_i = (r_bmp_x_rounded >> 16);
int l_bmp_x_i = (l_bmp_x_rounded >> 16);
for (; l_bmp_x_i <= r_bmp_x_i; ++l_bmp_x_i) {
uint32 c = (uint32)getpixel(spr, l_spr_x_rounded >> 16, l_spr_y_rounded >> 16);
if ((c & alphaMask) != transColor) {
if (!sameFormat) {
uint8 a, r, g, b;
spr->format.colorToARGB(c, a, r, g, b);
c = bmp->format.ARGBToColor(a, r, g, b);
}
putpixel(bmp, l_bmp_x_i, bmp_y_i, c);
}
l_spr_x_rounded += spr_dx;
l_spr_y_rounded += spr_dy;
}
}
// I'm not going to apoligize for this label and its gotos.
// to get rid of it would just make the code look worse.
skip_draw:
// Jump to next scanline.
bmp_y_i++;
// Update beginning of scanline.
l_bmp_x += l_bmp_dx;
l_spr_x += l_spr_dx;
l_spr_y += l_spr_dy;
// Update end of scanline.
r_bmp_x += r_bmp_dx;
}
}
void pivot_scaled_sprite(BITMAP *bmp, const BITMAP *sprite, fixed x, fixed y, fixed cx, fixed cy, fixed angle, fixed scale) {
fixed xs[4], ys[4];
rotate_scale_coordinates(sprite->w << 16, sprite->h << 16,
x, y, cx, cy, angle, scale, scale, xs, ys);
parallelogram_map(bmp, sprite, xs, ys);
}
} // namespace AGS3

View File

@@ -0,0 +1,35 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_ROTATE_H
#define AGS_LIB_ALLEGRO_ROTATE_H
#include "ags/lib/allegro/surface.h"
#include "ags/lib/allegro/fixed.h"
namespace AGS3 {
extern void pivot_scaled_sprite(BITMAP *bmp, const BITMAP *sprite, fixed x, fixed y, fixed cx, fixed cy, fixed angle, fixed scale);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,398 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/gfx.h"
#include "ags/lib/allegro/color.h"
#include "ags/lib/allegro/flood.h"
#include "ags/ags.h"
#include "ags/globals.h"
#include "common/textconsole.h"
#include "graphics/screen.h"
namespace AGS3 {
BITMAP::BITMAP(Graphics::ManagedSurface *owner) : _owner(owner),
w(owner->w), h(owner->h), pitch(owner->pitch), format(owner->format),
clip(true), ct(0), cl(0), cr(owner->w), cb(owner->h) {
line.resize(h);
for (int y = 0; y < h; ++y)
line[y] = (byte *)_owner->getBasePtr(0, y);
}
int BITMAP::getpixel(int x, int y) const {
if (x < 0 || y < 0 || x >= w || y >= h)
return -1;
const byte *pixel = (const byte *)getBasePtr(x, y);
if (format.bytesPerPixel == 1)
return *pixel;
else if (format.bytesPerPixel == 2)
return *(const uint16 *)pixel;
else
return *(const uint32 *)pixel;
}
void BITMAP::makeOpaque() {
if (format.aBits() == 0)
return;
assert(format.bytesPerPixel == 4);
uint32 alphaMask = format.ARGBToColor(0xff, 0, 0, 0);
unsigned char *pixels = getPixels();
for (int y = 0 ; y < h ; ++y, pixels += pitch) {
uint32 *data = (uint32 *)pixels;
for (int x = 0 ; x < w ; ++x, ++data)
(*data) |= alphaMask;
}
}
void BITMAP::circlefill(int x, int y, int radius, int color) {
int cx = 0;
int cy = radius;
int df = 1 - radius;
int d_e = 3;
int d_se = -2 * radius + 5;
do {
_owner->hLine(x - cy, y - cx, x + cy, color);
if (cx)
_owner->hLine(x - cy, y + cx, x + cy, color);
if (df < 0) {
df += d_e;
d_e += 2;
d_se += 2;
} else {
if (cx != cy) {
_owner->hLine(x - cx, y - cy, x + cx, color);
if (cy)
_owner->hLine(x - cx, y + cy, x + cx, color);
}
df += d_se;
d_e += 2;
d_se += 4;
cy--;
}
cx++;
} while (cx <= cy);
}
void BITMAP::floodfill(int x, int y, int color) {
AGS3::floodfill(this, x, y, color);
}
#define VGA_COLOR_TRANS(x) ((x) * 255 / 63)
BITMAP::DrawInnerArgs::DrawInnerArgs(BITMAP *_dstBitmap, const BITMAP *srcBitmap,
const Common::Rect &srcRect, const Common::Rect &_dstRect, bool _skipTrans,
int _srcAlpha, bool _horizFlip, bool _vertFlip, int _tintRed,
int _tintGreen, int _tintBlue, bool doScale) : skipTrans(_skipTrans),
srcAlpha(_srcAlpha), horizFlip(_horizFlip), vertFlip(_vertFlip),
tintRed(_tintRed), tintGreen(_tintGreen), tintBlue(_tintBlue),
src(**srcBitmap), shouldDraw(false), dstBitmap(*_dstBitmap),
useTint(_tintRed >= 0 && _tintGreen >= 0 && _tintBlue >= 0),
blenderMode(_G(_blender_mode)), dstRect(_dstRect) {
// Allegro disables draw when the clipping rect has negative width/height.
// Common::Rect instead asserts, which we don't want.
if (dstBitmap.cr <= dstBitmap.cl || dstBitmap.cb <= dstBitmap.ct)
return;
// Figure out the dest area that will be updated
srcArea = srcRect;
srcArea.clip(Common::Rect(0, 0, srcBitmap->w, srcBitmap->h));
if (srcArea.isEmpty())
return;
if (!doScale) {
// Ensure the src rect is constrained to the source bitmap
dstRect.setWidth(srcArea.width());
dstRect.setHeight(srcArea.height());
}
Common::Rect destRect = dstRect.findIntersectingRect(
Common::Rect(dstBitmap.cl, dstBitmap.ct, dstBitmap.cr, dstBitmap.cb));
if (destRect.isEmpty())
// Area is entirely outside the clipping area, so nothing to draw
return;
// Get source and dest surface. Note that for the destination we create
// a temporary sub-surface based on the allowed clipping area
Graphics::ManagedSurface &dest = *dstBitmap._owner;
destArea = dest.getSubArea(destRect);
// Define scaling and other stuff used by the drawing loops
scaleX = SCALE_THRESHOLD * srcRect.width() / dstRect.width();
scaleY = SCALE_THRESHOLD * srcRect.height() / dstRect.height();
sameFormat = (src.format == dstBitmap.format);
if (src.format.bytesPerPixel == 1 && dstBitmap.format.bytesPerPixel != 1) {
for (int i = 0; i < PAL_SIZE; ++i) {
palette[i].r = VGA_COLOR_TRANS(_G(current_palette)[i].r);
palette[i].g = VGA_COLOR_TRANS(_G(current_palette)[i].g);
palette[i].b = VGA_COLOR_TRANS(_G(current_palette)[i].b);
}
}
transColor = 0, alphaMask = 0xff;
if (skipTrans && src.format.bytesPerPixel != 1) {
transColor = src.format.ARGBToColor(0, 255, 0, 255);
alphaMask = src.format.ARGBToColor(255, 0, 0, 0);
alphaMask = ~alphaMask;
}
xStart = (dstRect.left < destRect.left) ? dstRect.left - destRect.left : 0;
yStart = (dstRect.top < destRect.top) ? dstRect.top - destRect.top : 0;
shouldDraw = true;
}
void BITMAP::draw(const BITMAP *srcBitmap, const Common::Rect &srcRect,
int dstX, int dstY, bool horizFlip, bool vertFlip,
bool skipTrans, int srcAlpha, int tintRed, int tintGreen,
int tintBlue) {
// A restricted number of 8bit games (e.g. Snow Problem) contain (leftover?) 32bit resources.
// We can ignore these to prevent conversion on load (and triggering the assertion)
if (format.bytesPerPixel == 1 && srcBitmap->format.bytesPerPixel != 1) {
warning("Attempt to draw >1BPP surface onto 1BPP surface, ignoring");
return;
}
assert(format.bytesPerPixel == 2 || format.bytesPerPixel == 4 ||
(format.bytesPerPixel == 1 && srcBitmap->format.bytesPerPixel == 1));
Graphics::ManagedSurface flipped;
if (horizFlip || vertFlip) {
// Horizontal flipping produces errors in the optimized paths, while vertical
// may result in crashes. For now, we pre-flip to a temporary surface
Graphics::ManagedSurface cropped(const_cast<BITMAP *>(srcBitmap)->getSurface(), srcRect);
flipped.copyFrom(cropped);
if (horizFlip) {
flipped.surfacePtr()->flipHorizontal(flipped.getBounds());
}
if (vertFlip) {
flipped.surfacePtr()->flipVertical(flipped.getBounds());
}
}
BITMAP temp(&flipped);
auto args = DrawInnerArgs(this, (horizFlip || vertFlip) ? &temp : srcBitmap, (horizFlip || vertFlip) ? flipped.getBounds() : srcRect, Common::Rect(dstX, dstY, dstX + 1, dstY + 1), skipTrans, srcAlpha, false, false, tintRed, tintGreen, tintBlue, false);
if (!args.shouldDraw) return;
if (!args.sameFormat && args.src.format.bytesPerPixel == 1) {
if (format.bytesPerPixel == 4)
drawInnerGeneric<4, 1, false>(args);
else
drawInnerGeneric<2, 1, false>(args);
return;
}
#ifdef SCUMMVM_NEON
if (_G(simd_flags) & AGS3::Globals::SIMD_NEON) {
drawNEON<false>(args);
return;
}
#endif
#ifdef SCUMMVM_AVX2
if (_G(simd_flags) & AGS3::Globals::SIMD_AVX2) {
drawAVX2<false>(args);
return;
}
#endif
#ifdef SCUMMVM_SSE2
if (_G(simd_flags) & AGS3::Globals::SIMD_SSE2) {
drawSSE2<false>(args);
return;
}
#endif
drawGeneric<false>(args);
}
void BITMAP::stretchDraw(const BITMAP *srcBitmap, const Common::Rect &srcRect,
const Common::Rect &dstRect, bool skipTrans, int srcAlpha) {
assert(format.bytesPerPixel == 2 || format.bytesPerPixel == 4 ||
(format.bytesPerPixel == 1 && srcBitmap->format.bytesPerPixel == 1));
auto args = DrawInnerArgs(this, srcBitmap, srcRect, dstRect, skipTrans, srcAlpha, false, false, -1, -1, -1, true);
if (!args.shouldDraw) return;
if (!args.sameFormat && args.src.format.bytesPerPixel == 1) {
if (format.bytesPerPixel == 4)
drawInnerGeneric<4, 1, true>(args);
else
drawInnerGeneric<2, 1, true>(args);
return;
}
// Stretching at the same time as blitting produces errors when
// using the optimized paths; for now, we pre-stretch to a temporary surface
Graphics::ManagedSurface cropped(const_cast<BITMAP *>(srcBitmap)->getSurface(), srcRect);
// We need to use Surface::scale, since ManagedSurface _always_ respects the source alpha, and thus skips transparent pixels
Graphics::ManagedSurface *stretched = cropped.scale(dstRect.width(), dstRect.height());
BITMAP temp(stretched);
auto optimizedArgs = DrawInnerArgs(this, &temp, stretched->getBounds(), dstRect, skipTrans, srcAlpha, false, false, -1, -1, -1, false);
#ifdef SCUMMVM_NEON
if (_G(simd_flags) & AGS3::Globals::SIMD_NEON) {
drawNEON<false>(optimizedArgs);
} else
#endif
#ifdef SCUMMVM_AVX2
if (_G(simd_flags) & AGS3::Globals::SIMD_AVX2) {
drawAVX2<false>(optimizedArgs);
} else
#endif
#ifdef SCUMMVM_SSE2
if (_G(simd_flags) & AGS3::Globals::SIMD_SSE2) {
drawSSE2<false>(optimizedArgs);
} else
#endif
{
drawGeneric<true>(optimizedArgs);
}
delete stretched;
}
void BITMAP::blendPixel(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha, bool useTint, byte *destVal) const {
switch (_G(_blender_mode)) {
case kSourceAlphaBlender:
if (!useTint) format.colorToARGB(getColor(destVal, format.bytesPerPixel), aDest, rDest, gDest, bDest);
blendSourceAlpha(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, alpha);
break;
case kArgbToArgbBlender:
if (!useTint) format.colorToARGB(getColor(destVal, format.bytesPerPixel), aDest, rDest, gDest, bDest);
blendArgbToArgb(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, alpha);
break;
case kArgbToRgbBlender:
if (!useTint) format.colorToARGB(getColor(destVal, format.bytesPerPixel), aDest, rDest, gDest, bDest);
blendArgbToRgb(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, alpha);
break;
case kRgbToArgbBlender:
if (!useTint) format.colorToARGB(getColor(destVal, format.bytesPerPixel), aDest, rDest, gDest, bDest);
blendRgbToArgb(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, alpha);
break;
case kRgbToRgbBlender:
if (!useTint) format.colorToARGB(getColor(destVal, format.bytesPerPixel), aDest, rDest, gDest, bDest);
blendRgbToRgb(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, alpha);
break;
case kAlphaPreservedBlenderMode:
if (!useTint) format.colorToARGB(getColor(destVal, format.bytesPerPixel), aDest, rDest, gDest, bDest);
blendPreserveAlpha(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, alpha);
break;
case kOpaqueBlenderMode:
blendOpaque(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, alpha);
break;
case kAdditiveBlenderMode:
if (!useTint) format.colorToARGB(getColor(destVal, format.bytesPerPixel), aDest, rDest, gDest, bDest);
blendAdditiveAlpha(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, alpha);
break;
case kTintBlenderMode:
blendTintSprite(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, alpha, false);
break;
case kTintLightBlenderMode:
blendTintSprite(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, alpha, true);
break;
}
}
void BITMAP::blendTintSprite(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha, bool light) const {
// Used from draw_lit_sprite after set_blender_mode(kTintBlenderMode or kTintLightBlenderMode)
// Original blender function: _myblender_color32 and _myblender_color32_light
float xh, xs, xv;
float yh, ys, yv;
int r, g, b;
rgb_to_hsv(rSrc, gSrc, bSrc, &xh, &xs, &xv);
rgb_to_hsv(rDest, gDest, bDest, &yh, &ys, &yv);
if (light) {
// adjust luminance
// (I think the writer meant value, since they are using hsV)
yv -= (1.0 - ((float)alpha / 250.0));
if (yv < 0.0)
yv = 0.0;
}
hsv_to_rgb(xh, xs, yv, &r, &g, &b);
rDest = static_cast<uint8>(r & 0xff);
gDest = static_cast<uint8>(g & 0xff);
bDest = static_cast<uint8>(b & 0xff);
// Preserve value in aDest
}
/*-------------------------------------------------------------------*/
/**
* Dervied screen surface
*/
class Screen : public Graphics::Screen, public BITMAP {
public:
Screen() : Graphics::Screen(), BITMAP(this) {}
Screen(int width, int height) : Graphics::Screen(width, height), BITMAP(this) {}
Screen(int width, int height, const Graphics::PixelFormat &pixelFormat) :
Graphics::Screen(width, height, pixelFormat), BITMAP(this) {}
~Screen() override {}
};
/*-------------------------------------------------------------------*/
BITMAP *create_bitmap(int width, int height) {
return create_bitmap_ex(get_color_depth(), width, height);
}
BITMAP *create_bitmap_ex(int color_depth, int width, int height) {
Graphics::PixelFormat format;
switch (color_depth) {
case 8:
format = Graphics::PixelFormat::createFormatCLUT8();
break;
case 16:
format = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
break;
case 32:
format = Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24);
break;
default:
error("Invalid color depth");
}
width = MAX(width, 0);
height = MAX(height, 0);
BITMAP *bitmap = new Surface(width, height, format);
return bitmap;
}
BITMAP *create_sub_bitmap(BITMAP *parent, int x, int y, int width, int height) {
Graphics::ManagedSurface &surf = **parent;
return new Surface(surf, Common::Rect(x, y, x + width, y + height));
}
BITMAP *create_video_bitmap(int width, int height) {
return new Screen(width, height);
}
BITMAP *create_system_bitmap(int width, int height) {
return create_bitmap(width, height);
}
void destroy_bitmap(BITMAP *bitmap) {
delete bitmap;
}
} // namespace AGS3

View File

@@ -0,0 +1,356 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_SURFACE_H
#define AGS_LIB_ALLEGRO_SURFACE_H
#include "graphics/managed_surface.h"
#include "ags/lib/allegro/base.h"
#include "ags/lib/allegro/color.h"
#include "common/array.h"
namespace AGS3 {
class BITMAP {
private:
Graphics::ManagedSurface *_owner;
public:
int16 &w, &h;
int32 &pitch;
Graphics::PixelFormat &format;
bool clip;
int ct, cb, cl, cr;
Common::Array<byte *> line;
public:
BITMAP(Graphics::ManagedSurface *owner);
virtual ~BITMAP() {
}
Graphics::ManagedSurface &operator*() const {
return *_owner;
}
Graphics::ManagedSurface &getSurface() {
return *_owner;
}
const Graphics::ManagedSurface &getSurface() const {
return *_owner;
}
unsigned char *getPixels() const {
return (unsigned char *)_owner->getPixels();
}
unsigned char *getBasePtr(uint16 x, uint16 y) const {
return (unsigned char *)_owner->getBasePtr(x, y);
}
uint getTransparentColor() const {
// See allegro bitmap_mask_color
// For paletted sprites this is 0.
// For other color depths this is bright pink (RGB 255, 0, 255) with alpha set to 0.
if (format.bytesPerPixel == 1)
return 0;
return format.ARGBToColor(0, 255, 0, 255);
}
inline const Common::Point getOffsetFromOwner() const {
return _owner->getOffsetFromOwner();
}
int getpixel(int x, int y) const;
void clear() {
_owner->clear();
}
void makeOpaque();
/**
* Draws a solid filled in circle
*/
void circlefill(int x, int y, int radius, int color);
/**
* Fills an enclosed area starting at a given point
*/
void floodfill(int x, int y, int color);
/**
* Draw a horizontal line
*/
void hLine(int x, int y, int x2, uint32 color) {
_owner->hLine(x, y, x2, color);
}
/**
* Draw a vertical line.
*/
void vLine(int x, int y, int y2, uint32 color) {
_owner->vLine(x, y, y2, color);
}
/**
* Draws the passed surface onto this one
*/
void draw(const BITMAP *srcBitmap, const Common::Rect &srcRect,
int dstX, int dstY, bool horizFlip, bool vertFlip,
bool skipTrans, int srcAlpha, int tintRed = -1, int tintGreen = -1,
int tintBlue = -1);
/**
* Stretches and draws the passed surface onto this one
*/
void stretchDraw(const BITMAP *srcBitmap, const Common::Rect &srcRect,
const Common::Rect &destRect, bool skipTrans, int srcAlpha);
inline bool isSubBitmap() const {
return _owner->disposeAfterUse() == DisposeAfterUse::NO;
}
private:
// True color blender functions
// In Allegro all the blender functions are of the form
// unsigned int blender_func(unsigned long x, unsigned long y, unsigned long n)
// when x is the sprite color, y the destination color, and n an alpha value
void blendPixel(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha, bool useTint, byte *destVal) const;
inline void rgbBlend(uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Note: the original's handling varies slightly for R & B vs G.
// We need to exactly replicate it to ensure Lamplight City's
// calendar puzzle works correctly
if (alpha)
alpha++;
uint32 x = ((uint32)rSrc << 16) | ((uint32)gSrc << 8) | (uint32)bSrc;
uint32 y = ((uint32)rDest << 16) | ((uint32)gDest << 8) | (uint32)bDest;
uint32 res = ((x & 0xFF00FF) - (y & 0xFF00FF)) * alpha / 256 + y;
y &= 0xFF00;
x &= 0xFF00;
uint32 g = (x - y) * alpha / 256 + y;
rDest = (res >> 16) & 0xff;
gDest = (g >> 8) & 0xff;
bDest = res & 0xff;
}
inline void argbBlend(uint32 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest) const {
// Original logic has uint32 src and dst colors as ARGB8888
// ++src_alpha;
// uint32 dst_alpha = geta32(dst);
// if (dst_alpha)
// ++dst_alpha;
// uint32 dst_g = (dst & 0x00FF00) * dst_alpha / 256;
// dst = (dst & 0xFF00FF) * dst_alpha / 256;
// dst_g = (((src & 0x00FF00) - (dst_g & 0x00FF00)) * src_alpha / 256 + dst_g) & 0x00FF00;
// dst = (((src & 0xFF00FF) - (dst & 0xFF00FF)) * src_alpha / 256 + dst) & 0xFF00FF;
// dst_alpha = 256 - (256 - src_alpha) * (256 - dst_alpha) / 256;
// src_alpha = /* 256 * 256 == */ 0x10000 / dst_alpha;
// dst_g = (dst_g * src_alpha / 256) & 0x00FF00;
// dst = (dst * src_alpha / 256) & 0xFF00FF;
// return dst | dst_g | (--dst_alpha << 24);
double sAlpha = (double)(aSrc & 0xff) / 255.0;
double dAlpha = (double)aDest / 255.0;
dAlpha *= (1.0 - sAlpha);
rDest = static_cast<uint8>((rSrc * sAlpha + rDest * dAlpha) / (sAlpha + dAlpha));
gDest = static_cast<uint8>((gSrc * sAlpha + gDest * dAlpha) / (sAlpha + dAlpha));
bDest = static_cast<uint8>((bSrc * sAlpha + bDest * dAlpha) / (sAlpha + dAlpha));
aDest = static_cast<uint8>(255. * (sAlpha + dAlpha));
}
// kRgbToRgbBlender
inline void blendRgbToRgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Default mode for set_trans_blender
rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, alpha);
// Original doesn't set alpha (so it is 0), but the function is not meant to be used
// on bitmap with transparency. Should we set alpha to 0xff?
aDest = 0;
}
// kAlphaPreservedBlenderMode
inline void blendPreserveAlpha(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender function: _myblender_alpha_trans24
// Like blendRgbToRgb, but result as the same alpha as destColor
rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, alpha);
// Preserve value in aDest
}
// kArgbToArgbBlender
inline void blendArgbToArgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender functions: _argb2argb_blender
if (alpha == 0)
alpha = aSrc;
else
alpha = aSrc * ((alpha & 0xff) + 1) / 256;
if (alpha != 0)
argbBlend(alpha, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest);
}
// kRgbToArgbBlender
inline void blendRgbToArgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender function: _rgb2argb_blenders
if (alpha == 0 || alpha == 0xff) {
aDest = 0xff;
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
} else
argbBlend(alpha, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest);
}
// kArgbToRgbBlender
inline void blendArgbToRgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender function: _argb2rgb_blender
if (alpha == 0)
alpha = aSrc;
else
alpha = aSrc * ((alpha & 0xff) + 1) / 256;
rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, alpha);
// Original doesn't set alpha (so it is 0), but the function is not meant to be used
// on bitmap with transparency. Should we set alpha to 0xff?
aDest = 0;
}
// kOpaqueBlenderMode
inline void blendOpaque(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender function: _opaque_alpha_blender
aDest = 0xff;
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
}
// kSourceAlphaBlender
inline void blendSourceAlpha(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Used after set_alpha_blender
// Uses alpha from source. Result is fully opaque
rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, aSrc);
// Original doesn't set alpha (so it is 0), but the function is not meant to be used
// on bitmap with transparency. Should we set alpha to 0xff?
aDest = 0;
}
// kAdditiveBlenderMode
inline void blendAdditiveAlpha(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
// Original blender function: _additive_alpha_copysrc_blender
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
uint32 a = (uint32)aSrc + (uint32)aDest;
if (a > 0xff)
aDest = 0xff;
else
aDest = static_cast<uint8>(a);
}
// kTintBlenderMode and kTintLightBlenderMode
void blendTintSprite(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha, bool light) const;
friend class DrawInnerImpl_AVX2;
friend class DrawInnerImpl_SSE2;
friend class DrawInnerImpl_NEON;
constexpr static int SCALE_THRESHOLD_BITS = 8;
constexpr static int SCALE_THRESHOLD = 1 << SCALE_THRESHOLD_BITS;
struct DrawInnerArgs {
const bool useTint, horizFlip, vertFlip, skipTrans;
bool sameFormat, shouldDraw;
int xStart, yStart, srcAlpha, tintRed, tintGreen, tintBlue, scaleX, scaleY;
uint32 transColor, alphaMask;
PALETTE palette;
BlenderMode blenderMode;
Common::Rect dstRect, srcArea;
BITMAP &dstBitmap;
const ::Graphics::ManagedSurface &src;
::Graphics::Surface destArea;
DrawInnerArgs(BITMAP *dstBitmap, const BITMAP *srcBitmap,
const Common::Rect &srcRect, const Common::Rect &dstRect,
bool skipTrans, int srcAlpha, bool horizFlip,
bool vertFlip, int tintRed, int tintGreen, int tintBlue,
bool doScale);
};
template<bool Scale>
void drawGeneric(DrawInnerArgs &args);
#ifdef SCUMMVM_NEON
template<bool Scale>
void drawNEON(DrawInnerArgs &args);
#endif
#ifdef SCUMMVM_SSE2
template<bool Scale>
void drawSSE2(DrawInnerArgs &args);
#endif
#ifdef SCUMMVM_AVX2
template<bool Scale>
void drawAVX2(DrawInnerArgs &args);
#endif
template<int DestBytesPerPixel, int SrcBytesPerPixel, bool Scale>
void drawInnerGeneric(DrawInnerArgs &args);
inline uint32 getColor(const byte *data, byte bpp) const {
switch (bpp) {
case 1:
return *data;
case 2:
return *(const uint16 *)data;
case 4:
return *(const uint32 *)data;
default:
error("Unsupported format in BITMAP::getColor");
}
}
};
/**
* Derived surface class
*/
class Surface : public Graphics::ManagedSurface, public BITMAP {
public:
Surface(int width, int height, const Graphics::PixelFormat &pixelFormat) :
Graphics::ManagedSurface(width, height, pixelFormat), BITMAP(this) {
// Allegro uses 255, 0, 255 RGB as the transparent color
if (pixelFormat.bytesPerPixel == 2 || pixelFormat.bytesPerPixel == 4)
setTransparentColor(pixelFormat.RGBToColor(255, 0, 255));
}
Surface(Graphics::ManagedSurface &surf, const Common::Rect &bounds) :
Graphics::ManagedSurface(surf, bounds), BITMAP(this) {
// Allegro uses 255, 0, 255 RGB as the transparent color
if (surf.format.bytesPerPixel == 2 || surf.format.bytesPerPixel == 4)
setTransparentColor(surf.format.RGBToColor(255, 0, 255));
}
~Surface() override {
}
};
BITMAP *create_bitmap(int width, int height);
BITMAP *create_bitmap_ex(int color_depth, int width, int height);
BITMAP *create_sub_bitmap(BITMAP *parent, int x, int y, int width, int height);
BITMAP *create_video_bitmap(int width, int height);
BITMAP *create_system_bitmap(int width, int height);
void destroy_bitmap(BITMAP *bitmap);
} // namespace AGS3
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,212 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/color.h"
#include "ags/lib/allegro/surface.h"
#include "ags/globals.h"
namespace AGS3 {
template<int DestBytesPerPixel, int SrcBytesPerPixel, bool Scale>
void BITMAP::drawInnerGeneric(DrawInnerArgs &args) {
const int xDir = args.horizFlip ? -1 : 1;
byte rSrc, gSrc, bSrc, aSrc;
byte rDest = 0, gDest = 0, bDest = 0, aDest = 0;
// Instead of skipping pixels outside our boundary here, we just clip
// our area instead.
int xCtrStart = 0, xCtrBppStart = 0, xCtrWidth = args.dstRect.width();
if (args.xStart + xCtrWidth > args.destArea.w) { // Clip the right
xCtrWidth = args.destArea.w - args.xStart;
}
if (args.xStart < 0) { // Clip the left
xCtrStart = -args.xStart;
xCtrBppStart = xCtrStart * SrcBytesPerPixel;
args.xStart = 0;
}
int destY = args.yStart, yCtr = 0, srcYCtr = 0, scaleYCtr = 0, yCtrHeight = args.dstRect.height();
if (args.yStart < 0) { // Clip the top
yCtr = -args.yStart;
destY = 0;
if (Scale) {
scaleYCtr = yCtr * args.scaleY;
srcYCtr = scaleYCtr / BITMAP::SCALE_THRESHOLD;
}
}
if (args.yStart + yCtrHeight > args.destArea.h) { // Clip the bottom
yCtrHeight = args.destArea.h - args.yStart;
}
byte *destP = (byte *)args.destArea.getBasePtr(0, destY);
const byte *srcP = (const byte *)args.src.getBasePtr(
args.horizFlip ? args.srcArea.right - 1 : args.srcArea.left,
args.vertFlip ? args.srcArea.bottom - 1 - yCtr :
args.srcArea.top + yCtr);
for (; yCtr < yCtrHeight; ++destY, ++yCtr, scaleYCtr += args.scaleY) {
if (Scale) {
int newSrcYCtr = scaleYCtr / BITMAP::SCALE_THRESHOLD;
if (srcYCtr != newSrcYCtr) {
int diffSrcYCtr = newSrcYCtr - srcYCtr;
srcP += args.src.pitch * diffSrcYCtr;
srcYCtr = newSrcYCtr;
}
}
// Loop through the pixels of the row
for (int destX = args.xStart, xCtr = xCtrStart, xCtrBpp = xCtrBppStart, scaleXCtr = xCtr * args.scaleX; xCtr < xCtrWidth; ++destX, ++xCtr, xCtrBpp += SrcBytesPerPixel, scaleXCtr += args.scaleX) {
const byte *srcVal = srcP + xDir * xCtrBpp;
if (Scale) {
srcVal = srcP + (scaleXCtr / BITMAP::SCALE_THRESHOLD) * SrcBytesPerPixel;
}
uint32 srcCol = getColor(srcVal, SrcBytesPerPixel);
// Check if this is a transparent color we should skip
if (args.skipTrans && ((srcCol & args.alphaMask) == args.transColor))
continue;
byte *destVal = (byte *)&destP[destX * DestBytesPerPixel];
// When blitting to the same format we can just copy the color
if (DestBytesPerPixel == 1) {
*destVal = srcCol;
continue;
} else if ((DestBytesPerPixel == SrcBytesPerPixel) && args.srcAlpha == -1) {
if (DestBytesPerPixel == 4)
*(uint32 *)destVal = srcCol;
else
*(uint16 *)destVal = srcCol;
continue;
}
// We need the rgb values to do blending and/or convert between formats
if (SrcBytesPerPixel == 1) {
const RGB &rgb = args.palette[srcCol];
aSrc = 0xff;
rSrc = rgb.r;
gSrc = rgb.g;
bSrc = rgb.b;
} else {
if (SrcBytesPerPixel == 4) {
aSrc = srcCol >> 24;
rSrc = (srcCol >> 16) & 0xff;
gSrc = (srcCol >> 8) & 0xff;
bSrc = srcCol & 0xff;
} else { // SrcBytesPerPixel == 2
aSrc = 0xff;
rSrc = (srcCol >> 11) & 0x1f;
rSrc = (rSrc << 3) | (rSrc >> 2);
gSrc = (srcCol >> 5) & 0x3f;
gSrc = (gSrc << 2) | (gSrc >> 4);
bSrc = srcCol & 0x1f;
bSrc = (bSrc << 3) | (bSrc >> 2);
}
//src.format.colorToARGB(srcCol, aSrc, rSrc, gSrc, bSrc);
}
if (args.srcAlpha == -1) {
// This means we don't use blending.
aDest = aSrc;
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
} else {
if (args.useTint) {
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
aDest = aSrc;
rSrc = args.tintRed;
gSrc = args.tintGreen;
bSrc = args.tintBlue;
aSrc = args.srcAlpha;
} else {
uint32 destCol = getColor(destVal, DestBytesPerPixel);
if (DestBytesPerPixel == 1) {
const RGB &rgb = args.palette[destCol];
aDest = 0xff;
rDest = rgb.r;
gDest = rgb.g;
bDest = rgb.b;
} else {
if (DestBytesPerPixel == 4) {
aDest = destCol >> 24;
rDest = (destCol >> 16) & 0xff;
gDest = (destCol >> 8) & 0xff;
bDest = destCol & 0xff;
} else { // DestBytesPerPixel == 2
aDest = 0xff;
rDest = (destCol >> 11) & 0x1f;
rDest = (rDest << 3) | (rDest >> 2);
gDest = (destCol >> 5) & 0x3f;
gDest = (gDest << 2) | (gDest >> 4);
bDest = destCol & 0x1f;
bDest = (bDest << 3) | (bDest >> 2);
}
//src.format.colorToARGB(srcCol, aSrc, rSrc, gSrc, bSrc);
}
}
blendPixel(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, args.srcAlpha, args.useTint, destVal);
}
uint32 pixel;// = format.ARGBToColor(aDest, rDest, gDest, bDest);
if (DestBytesPerPixel == 4) {
pixel = (aDest << 24) | (rDest << 16) | (gDest << 8) | (bDest);
*(uint32 *)destVal = pixel;
}
else {
pixel = ((rDest >> 3) << 11) | ((gDest >> 2) << 5) | (bDest >> 3);
*(uint16 *)destVal = pixel;
}
}
destP += args.destArea.pitch;
if (!Scale) srcP += args.vertFlip ? -args.src.pitch : args.src.pitch;
}
}
template<bool Scale>
void BITMAP::drawGeneric(DrawInnerArgs &args) {
if (args.sameFormat) {
switch (format.bytesPerPixel) {
case 1: drawInnerGeneric<1, 1, Scale>(args); break;
case 2: drawInnerGeneric<2, 2, Scale>(args); break;
case 4: drawInnerGeneric<4, 4, Scale>(args); break;
}
} else if (format.bytesPerPixel == 4 && args.src.format.bytesPerPixel == 2) {
drawInnerGeneric<4, 2, Scale>(args);
} else if (format.bytesPerPixel == 2 && args.src.format.bytesPerPixel == 4) {
drawInnerGeneric<2, 4, Scale>(args);
}
}
template void BITMAP::drawGeneric<false>(DrawInnerArgs &);
template void BITMAP::drawGeneric<true>(DrawInnerArgs &);
template void BITMAP::drawInnerGeneric<4, 4, false>(DrawInnerArgs &);
template void BITMAP::drawInnerGeneric<4, 4, true>(DrawInnerArgs &);
template void BITMAP::drawInnerGeneric<4, 2, false>(DrawInnerArgs &);
template void BITMAP::drawInnerGeneric<4, 2, true>(DrawInnerArgs &);
template void BITMAP::drawInnerGeneric<2, 4, false>(DrawInnerArgs &);
template void BITMAP::drawInnerGeneric<2, 4, true>(DrawInnerArgs &);
template void BITMAP::drawInnerGeneric<4, 1, false>(DrawInnerArgs &);
template void BITMAP::drawInnerGeneric<4, 1, true>(DrawInnerArgs &);
template void BITMAP::drawInnerGeneric<2, 1, false>(DrawInnerArgs &);
template void BITMAP::drawInnerGeneric<2, 1, true>(DrawInnerArgs &);
} // end of namespace AGS3

View File

@@ -0,0 +1,989 @@
/* 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/>.
*
*/
#include "ags/ags.h"
// Without this ifdef the iOS backend breaks, please do not remove
#ifdef SCUMMVM_NEON
#include "ags/globals.h"
#include "ags/lib/allegro/color.h"
#include "ags/lib/allegro/flood.h"
#include "ags/lib/allegro/gfx.h"
#include "common/textconsole.h"
#include "graphics/screen.h"
#include <arm_neon.h>
#if !defined(__aarch64__) && !defined(__ARM_NEON)
#if defined(__clang__)
#pragma clang attribute push (__attribute__((target("neon"))), apply_to=function)
#elif defined(__GNUC__)
#pragma GCC push_options
#pragma GCC target("fpu=neon")
#endif
#endif // !defined(__aarch64__) && !defined(__ARM_NEON)
namespace AGS3 {
class DrawInnerImpl_NEON {
static inline uint32x4_t simd2BppTo4Bpp(uint16x4_t pixels) {
uint32x4_t x = vmovl_u16(pixels);
// c is the extracted 5/6 bit color from the image
uint32x4_t c = vshrq_n_u32(x, 11);
// We convert it back to normal by shifting it thrice over, naturally, and then using the 2 most
// sinificant bits in the original color for the least significant bits in the new one
uint32x4_t r = vshlq_n_u32(vorrq_u32(vshlq_n_u32(c, 3), vshrq_n_u32(c, 2)), 16);
c = vshrq_n_u32(vandq_u32(x, vmovq_n_u32(0x07e0)), 5);
uint32x4_t g = vshlq_n_u32(vorrq_u32(vshlq_n_u32(c, 2), vshrq_n_u32(c, 4)), 8);
c = vandq_u32(x, vmovq_n_u32(0x001f));
uint32x4_t b = vorrq_u32(vshlq_n_u32(c, 3), vshrq_n_u32(c, 2));
// By default 2bpp to 4bpp makes the alpha channel 255
return vorrq_u32(vorrq_u32(vorrq_u32(r, g), b), vmovq_n_u32(0xff000000));
}
static inline uint16x4_t simd4BppTo2Bpp(uint32x4_t pixels) {
// x is the final 16 bit rgb pixel
uint32x4_t x = vshrq_n_u32(vandq_u32(pixels, vmovq_n_u32(0x000000ff)), 3);
x = vorrq_u32(x, vshlq_n_u32(vshrq_n_u32(vandq_u32(pixels, vmovq_n_u32(0x0000ff00)), 8+2), 5));
x = vorrq_u32(x, vshlq_n_u32(vshrq_n_u32(vandq_u32(pixels, vmovq_n_u32(0x00ff0000)), 16+3), 11));
return vmovn_u32(x);
}
static inline uint16x8_t rgbBlendSIMD2Bpp(uint16x8_t srcCols, uint16x8_t destCols, uint16x8_t alphas) {
// Here we add 1 to alphas if its 0. This is what the original blender function did
alphas = vaddq_u16(alphas, vandq_u16(vceqq_u16(alphas, vmovq_n_u16(0)), vmovq_n_u16(1)));
// Split the components into rgb
uint16x8_t srcComps[] = {
vandq_u16(srcCols, vmovq_n_u16(0x1f)), // B
vandq_u16(vshrq_n_u16(srcCols, 5), vmovq_n_u16(0x3f)), // G
vshrq_n_u16(srcCols, 11), // R
}, destComps[] = {
vandq_u16(destCols, vmovq_n_u16(0x1f)), // B
vandq_u16(vshrq_n_u16(destCols, 5), vmovq_n_u16(0x3f)), // G
vshrq_n_u16(destCols, 11), // R
};
// At some point I made it so that it would put them into their 8bit depth format
// to keep the function as 1-1 with the original, but it didn't seem to help much
//srcComps[0] = vorrq_u16(vshlq_n_u16(srcComps[0], 3), vshrq_n_u16(srcComps[0], 2));
//srcComps[1] = vorrq_u16(vshlq_n_u16(srcComps[1], 2), vshrq_n_u16(srcComps[1], 4));
//srcComps[2] = vorrq_u16(vshlq_n_u16(srcComps[2], 3), vshrq_n_u16(srcComps[2], 2));
//destComps[0] = vorrq_u16(vshlq_n_u16(destComps[0], 3), vshrq_n_u16(destComps[0], 2));
//destComps[1] = vorrq_u16(vshlq_n_u16(destComps[1], 2), vshrq_n_u16(destComps[1], 4));
//destComps[2] = vorrq_u16(vshlq_n_u16(destComps[2], 3), vshrq_n_u16(destComps[2], 2));
// Calculate the differences between the colors
uint16x8_t diffs[] = {
vsubq_u16(srcComps[0], destComps[0]), // B
vsubq_u16(srcComps[1], destComps[1]), // G
vsubq_u16(srcComps[2], destComps[2]), // R
};
// Multiply by alpha and shift depth bits to the right
// pretty much the same as (int)(((float)component / 255.0f) * ((float)alpha / 255.0f) * 255.0f)
alphas = vshrq_n_u16(alphas, 2);
diffs[1] = vshrq_n_u16(vmulq_u16(diffs[1], alphas), 6);
alphas = vshrq_n_u16(alphas, 1);
diffs[0] = vshrq_n_u16(vmulq_u16(diffs[0], alphas), 5);
diffs[2] = vshrq_n_u16(vmulq_u16(diffs[2], alphas), 5);
// Originally, I converted it back to normal here from the 8bpp form, but don't need to do that anymore
//diffs[0] = vandq_u16(vshrq_n_u16(vaddq_u16(diffs[0], destComps[0]), 3), vmovq_n_u16(0x1f));
//diffs[1] = vandq_u16(vshrq_n_u16(vaddq_u16(diffs[1], destComps[1]), 2), vmovq_n_u16(0x3f));
//diffs[2] = vandq_u16(vshrq_n_u16(vaddq_u16(diffs[2], destComps[2]), 3), vmovq_n_u16(0x1f));
// Here we add the difference between the 2 colors times alpha onto the destination
diffs[0] = vandq_u16(vaddq_u16(diffs[0], destComps[0]), vmovq_n_u16(0x1f));
diffs[1] = vandq_u16(vaddq_u16(diffs[1], destComps[1]), vmovq_n_u16(0x3f));
diffs[2] = vandq_u16(vaddq_u16(diffs[2], destComps[2]), vmovq_n_u16(0x1f));
// We compile all the colors into diffs[0] as a 16 bit rgb pixel
diffs[0] = vorrq_u16(diffs[0], vshlq_n_u16(diffs[1], 5));
return vorrq_u16(diffs[0], vshlq_n_u16(diffs[2], 11));
}
// preserveAlpha:
// false => set destCols's alpha to 0
// true => keep destCols's alpha
static inline uint32x4_t rgbBlendSIMD(uint32x4_t srcCols, uint32x4_t destCols, uint32x4_t alphas, bool preserveAlpha) {
// Here we add 1 to alphas if its 0. This is what the original blender function did
alphas = vaddq_u32(alphas, vandq_u32(vcgtq_u32(alphas, vmovq_n_u32(0)), vmovq_n_u32(1)));
// Get the alpha from the destination
uint32x4_t alpha = vandq_u32(destCols, vmovq_n_u32(0xff000000));
// Get red and blue components
uint32x4_t srcColsCopy = srcCols;
srcColsCopy = vandq_u32(srcColsCopy, vmovq_n_u32(0xff00ff));
uint32x4_t destColsCopy = destCols;
destColsCopy = vandq_u32(destColsCopy, vmovq_n_u32(0xff00ff));
// compute the difference, then multiply by alpha and divide by 255
srcColsCopy = vsubq_u32(srcColsCopy, destColsCopy);
srcColsCopy = vmulq_u32(srcColsCopy, alphas);
srcColsCopy = vshrq_n_u32(srcColsCopy, 8);
srcColsCopy = vaddq_u32(srcColsCopy, destCols); // Add the new red/blue to the old ones
// do the same for the green component
srcCols = vandq_u32(srcCols, vmovq_n_u32(0xff00));
destCols = vandq_u32(destCols, vmovq_n_u32(0xff00));
srcCols = vsubq_u32(srcCols, destCols);
srcCols = vmulq_u32(srcCols, alphas);
srcCols = vshrq_n_u32(srcCols, 8);
srcCols = vaddq_u32(srcCols, destCols); // Add the new green to the old green
// keep values in 8bit range and glue red/blue and green together
srcColsCopy = vandq_u32(srcColsCopy, vmovq_n_u32(0xff00ff));
srcCols = vandq_u32(srcCols, vmovq_n_u32(0xff00));
srcCols = vorrq_u32(srcCols, srcColsCopy);
// Remember that alpha is not alphas, but rather the alpha of destCols
if (preserveAlpha) {
srcCols = vandq_u32(srcCols, vmovq_n_u32(0x00ffffff));
srcCols = vorrq_u32(srcCols, alpha);
}
return srcCols;
}
// uses the alpha from srcCols and destCols
static inline uint32x4_t argbBlendSIMD(uint32x4_t srcCols, uint32x4_t destCols) {
float32x4_t srcA = vcvtq_f32_u32(vshrq_n_u32(srcCols, 24));
srcA = vmulq_n_f32(srcA, 1.0f / 255.0f);
float32x4_t srcR = vcvtq_f32_u32(vandq_u32(vshrq_n_u32(srcCols, 16), vmovq_n_u32(0xff)));
float32x4_t srcG = vcvtq_f32_u32(vandq_u32(vshrq_n_u32(srcCols, 8), vmovq_n_u32(0xff)));
float32x4_t srcB = vcvtq_f32_u32(vandq_u32(srcCols, vmovq_n_u32(0xff)));
float32x4_t destA = vcvtq_f32_u32(vshrq_n_u32(destCols, 24));
destA = vmulq_n_f32(destA, 1.0f / 255.0f);
float32x4_t destR = vcvtq_f32_u32(vandq_u32(vshrq_n_u32(destCols, 16), vmovq_n_u32(0xff)));
float32x4_t destG = vcvtq_f32_u32(vandq_u32(vshrq_n_u32(destCols, 8), vmovq_n_u32(0xff)));
float32x4_t destB = vcvtq_f32_u32(vandq_u32(destCols, vmovq_n_u32(0xff)));
// the destination alpha gets multiplied by 255 - source alpha
destA = vmulq_f32(destA, vsubq_f32(vmovq_n_f32(1.0f), srcA));
// ((src * sAlpha) + (dest * dAlpha)) / (sAlpha + dAlpha)
float32x4_t combA = vaddq_f32(srcA, destA);
float32x4_t combArcp = vrecpeq_f32(combA);
destR = vmulq_f32(vaddq_f32(vmulq_f32(srcR, srcA), vmulq_f32(destR, destA)), combArcp);
destG = vmulq_f32(vaddq_f32(vmulq_f32(srcG, srcA), vmulq_f32(destG, destA)), combArcp);
destB = vmulq_f32(vaddq_f32(vmulq_f32(srcB, srcA), vmulq_f32(destB, destA)), combArcp);
combA = vmulq_n_f32(combA, 255.0);
// Now put it back together
return vorrq_u32(vshlq_n_u32(vcvtq_u32_f32(combA), 24),
vorrq_u32(vshlq_n_u32(vcvtq_u32_f32(destR), 16),
vorrq_u32(vshlq_n_u32(vcvtq_u32_f32(destG), 8),
vcvtq_u32_f32(destB))));
}
static inline uint32x4_t blendTintSpriteSIMD(uint32x4_t srcCols, uint32x4_t destCols, uint32x4_t alphas, bool light) {
// This function is NOT 1 to 1 with the original... It just approximates it
// It gets the value of the HSV of the dest color
// Then it gets the HSV of the srcCols
// how the values are transformed
// from 1 uint32x4_t srcCols with each lane being ARGB uint32
// srcCols[0] = A | R | G | B
// srcCols[1] = A | R | G | B
// srcCols[2] = A | R | G | B
// srcCols[3] = A | R | G | B
// ->
// to 4 float32x4_t's each being a separate channel with each lane
// corresponding to their respective srcCols lane
// dda = { A[0], A[1], A[2], A[3] }
// ddr = { R[0], R[1], R[2], R[3] }
// ddg = { G[0], G[1], G[2], G[3] }
// ddb = { B[0], B[1], B[2], B[3] }
// do the transformation (we don't actually need alpha at all)
float32x4_t ddr, ddg, ddb;
ddr = vmulq_n_f32(vcvtq_f32_u32(vandq_u32(vshrq_n_u32(destCols, 16), vmovq_n_u32(0xff))), 1.0 / 255.0);
ddg = vmulq_n_f32(vcvtq_f32_u32(vandq_u32(vshrq_n_u32(destCols, 8), vmovq_n_u32(0xff))), 1.0 / 255.0);
ddb = vmulq_n_f32(vcvtq_f32_u32(vandq_u32(destCols, vmovq_n_u32(0xff))), 1.0 / 255.0);
float32x4_t ssr, ssg, ssb;
ssr = vmulq_n_f32(vcvtq_f32_u32(vandq_u32(vshrq_n_u32(srcCols, 16), vmovq_n_u32(0xff))), 1.0 / 255.0);
ssg = vmulq_n_f32(vcvtq_f32_u32(vandq_u32(vshrq_n_u32(srcCols, 8), vmovq_n_u32(0xff))), 1.0 / 255.0);
ssb = vmulq_n_f32(vcvtq_f32_u32(vandq_u32(srcCols, vmovq_n_u32(0xff))), 1.0 / 255.0);
// Get the maxes and mins (needed for HSV->RGB and vice-versa)
float32x4_t dmaxes = vmaxq_f32(ddr, vmaxq_f32(ddg, ddb));
float32x4_t smaxes = vmaxq_f32(ssr, vmaxq_f32(ssg, ssb));
float32x4_t smins = vminq_f32(ssr, vminq_f32(ssg, ssb));
// This is here to stop from dividing by 0
const float32x4_t eplison0 = vmovq_n_f32(0.0000001);
float32x4_t chroma = vmaxq_f32(vsubq_f32(smaxes, smins), eplison0);
// RGB to HSV is a piecewise function, so we compute each part of the function first...
float32x4_t hr, hg, hb, hue, chromaReq;
chromaReq = vrecpeq_f32(chroma);
hr = vmulq_f32(vsubq_f32(ssg, ssb), chromaReq);
float32x4_t hrDiv6 = vmulq_n_f32(hr, 1.0 / 6.0);
hrDiv6 = vsubq_f32(hrDiv6, vcvtq_f32_u32(vandq_u32(vcltq_f32(hrDiv6, vmovq_n_f32(0.0)), vmovq_n_u32(1))));
hr = vsubq_f32(hr, vmulq_n_f32(vcvtq_f32_s32(vcvtq_s32_f32(hrDiv6)), 6.0));
hg = vaddq_f32(vmulq_f32(vsubq_f32(ssb, ssr), chromaReq), vmovq_n_f32(2.0));
hb = vaddq_f32(vmulq_f32(vsubq_f32(ssr, ssg), chromaReq), vmovq_n_f32(4.0));
// And then compute which one will be used based on criteria
float32x4_t hrfactors = vcvtq_f32_u32(vandq_u32(vandq_u32(vceqq_f32(ssr, smaxes), vmvnq_u32(vceqq_f32(ssr, ssb))), vmovq_n_u32(1)));
float32x4_t hgfactors = vcvtq_f32_u32(vandq_u32(vandq_u32(vceqq_f32(ssg, smaxes), vmvnq_u32(vceqq_f32(ssg, ssr))), vmovq_n_u32(1)));
float32x4_t hbfactors = vcvtq_f32_u32(vandq_u32(vandq_u32(vceqq_f32(ssb, smaxes), vmvnq_u32(vceqq_f32(ssb, ssg))), vmovq_n_u32(1)));
hue = vmulq_f32(hr, hrfactors);
hue = vaddq_f32(hue, vmulq_f32(hg, hgfactors));
hue = vaddq_f32(hue, vmulq_f32(hb, hbfactors));
// Mess with the light like the original function
float32x4_t val = dmaxes;
if (light) {
val = vsubq_f32(val, vsubq_f32(vmovq_n_f32(1.0), vmulq_n_f32(vcvtq_f32_u32(alphas), 1.0 / 250.0)));
val = vmaxq_f32(val, vmovq_n_f32(0.0));
}
// then it stitches the HSV back together
// the hue and saturation come from the source (tint) color, and the value comes from
// the destination (real source) color
chroma = vmulq_f32(val, vmulq_f32(vsubq_f32(smaxes, smins), vrecpeq_f32(vaddq_f32(smaxes, eplison0))));
float32x4_t hprime_mod2 = vmulq_n_f32(hue, 1.0 / 2.0);
hprime_mod2 = vmulq_n_f32(vsubq_f32(hprime_mod2, vcvtq_f32_s32(vcvtq_s32_f32(hprime_mod2))), 2.0);
float32x4_t x = vmulq_f32(chroma, vsubq_f32(vmovq_n_f32(1.0), vabsq_f32(vsubq_f32(hprime_mod2, vmovq_n_f32(1.0)))));
uint32x4_t hprime_rounded = vcvtq_u32_f32(hue);
uint32x4_t x_int = vcvtq_u32_f32(vmulq_n_f32(x, 255.0));
uint32x4_t c_int = vcvtq_u32_f32(vmulq_n_f32(chroma, 255.0));
// Again HSV->RGB is also a piecewise function
uint32x4_t val0 = vorrq_u32(vshlq_n_u32(x_int, 8), vshlq_n_u32(c_int, 16));
val0 = vandq_u32(val0, vorrq_u32(vceqq_u32(hprime_rounded, vmovq_n_u32(0)), vceqq_u32(hprime_rounded, vmovq_n_u32(6))));
uint32x4_t val1 = vorrq_u32(vshlq_n_u32(c_int, 8), vshlq_n_u32(x_int, 16));
val1 = vandq_u32(val1, vceqq_u32(hprime_rounded, vmovq_n_u32(1)));
uint32x4_t val2 = vorrq_u32(vshlq_n_u32(c_int, 8), x_int);
val2 = vandq_u32(val2, vceqq_u32(hprime_rounded, vmovq_n_u32(2)));
uint32x4_t val3 = vorrq_u32(vshlq_n_u32(x_int, 8), c_int);
val3 = vandq_u32(val3, vceqq_u32(hprime_rounded, vmovq_n_u32(3)));
uint32x4_t val4 = vorrq_u32(vshlq_n_u32(x_int, 16), c_int);
val4 = vandq_u32(val4, vceqq_u32(hprime_rounded, vmovq_n_u32(4)));
uint32x4_t val5 = vorrq_u32(vshlq_n_u32(c_int, 16), x_int);
val5 = vandq_u32(val5, vceqq_u32(hprime_rounded, vmovq_n_u32(5)));
// or the values together
uint32x4_t final = vorrq_u32(val0, vorrq_u32(val1, vorrq_u32(val2, vorrq_u32(val3, vorrq_u32(val4, val5)))));
// add the minimums back in
uint32x4_t val_add = vcvtq_u32_f32(vmulq_n_f32(vsubq_f32(val, chroma), 255.0));
val_add = vorrq_u32(val_add, vorrq_u32(vshlq_n_u32(val_add, 8), vorrq_u32(vshlq_n_u32(val_add, 16), vandq_u32(destCols, vmovq_n_u32(0xff000000)))));
final = vaddq_u32(final, val_add);
return final;
}
static inline uint32x4_t blendPixelSIMD(uint32x4_t srcCols, uint32x4_t destCols, uint32x4_t alphas) {
uint32x4_t srcAlphas, difAlphas, mask, ch1, ch2;
auto setupArgbAlphas = [&]() {
// This acts the same as this in the normal blender functions
// if (alpha == 0)
// alpha = aSrc;
// else
// alpha = aSrc * ((alpha & 0xff) + 1) / 256;
// where alpha is the alpha byte of the srcCols
srcAlphas = vshrq_n_u32(srcCols, 24);
difAlphas = vaddq_u32(vandq_u32(alphas, vmovq_n_u32(0xff)), vmovq_n_u32(1));
difAlphas = vshrq_n_u32(vmulq_u32(srcAlphas, difAlphas), 8);
difAlphas = vshlq_n_u32(difAlphas, 24);
srcAlphas = vshlq_n_u32(srcAlphas, 24);
mask = vceqq_u32(alphas, vmovq_n_u32(0));
srcAlphas = vandq_u32(srcAlphas, mask);
difAlphas = vandq_u32(difAlphas, vmvnq_u32(mask));
srcCols = vandq_u32(srcCols, vmovq_n_u32(0x00ffffff));
srcCols = vorrq_u32(srcCols, vorrq_u32(srcAlphas, difAlphas));
};
switch (_G(_blender_mode)) {
case kSourceAlphaBlender: // see BITMAP member function blendSourceAlpha
alphas = vshrq_n_u32(srcCols, 24);
return rgbBlendSIMD(srcCols, destCols, alphas, false);
case kArgbToArgbBlender: // see BITMAP member function blendArgbToArgb
setupArgbAlphas();
// only blend if alpha isn't 0, otherwise use destCols
mask = vcgtq_u32(vshrq_n_u32(srcCols, 24), vmovq_n_u32(0));
ch1 = vandq_u32(argbBlendSIMD(srcCols, destCols), mask);
ch2 = vandq_u32(destCols, vmvnq_u32(mask));
return vorrq_u32(ch1, ch2);
case kArgbToRgbBlender: // see BITMAP member function blendArgbToRgb
setupArgbAlphas();
return rgbBlendSIMD(srcCols, destCols, vshrq_n_u32(srcCols, 24), false);
case kRgbToArgbBlender: // see BITMAP member function blendRgbToArgb
// if alpha is NOT 0 or 255
ch2 = vandq_u32(srcCols, vmovq_n_u32(0x00ffffff));
ch2 = vorrq_u32(ch2, vshlq_n_u32(alphas, 24));
ch2 = argbBlendSIMD(ch2, destCols);
// if alpha is 0 or 255
ch1 = vorrq_u32(srcCols, vmovq_n_u32(0xff000000));
// mask and or them together
mask = vorrq_u32(vceqq_u32(alphas, vmovq_n_u32(0)), vceqq_u32(alphas, vmovq_n_u32(0xff)));
ch1 = vandq_u32(ch1, mask);
ch2 = vandq_u32(ch2, vmvnq_u32(mask));
return vorrq_u32(ch1, ch2);
case kRgbToRgbBlender: // see BITMAP member function blendRgbToRgb
return rgbBlendSIMD(srcCols, destCols, alphas, false);
case kAlphaPreservedBlenderMode: // see BITMAP member function blendPreserveAlpha
return rgbBlendSIMD(srcCols, destCols, alphas, true);
case kOpaqueBlenderMode: // see BITMAP member function blendOpaque
return vorrq_u32(srcCols, vmovq_n_u32(0xff000000));
case kAdditiveBlenderMode: // see BITMAP member function blendAdditiveAlpha
srcAlphas = vaddq_u32(vshrq_n_u32(srcCols, 24), vshrq_n_u32(destCols, 24));
srcAlphas = vminq_u32(srcAlphas, vmovq_n_u32(0xff));
srcCols = vandq_u32(srcCols, vmovq_n_u32(0x00ffffff));
return vorrq_u32(srcCols, vshlq_n_u32(srcAlphas, 24));
case kTintBlenderMode: // see BITMAP member function blendTintSprite
return blendTintSpriteSIMD(srcCols, destCols, alphas, false);
case kTintLightBlenderMode: // see BITMAP member function blendTintSprite
return blendTintSpriteSIMD(srcCols, destCols, alphas, true);
}
return srcCols;
}
static inline uint16x8_t blendPixelSIMD2Bpp(uint16x8_t srcCols, uint16x8_t destCols, uint16x8_t alphas) {
uint16x8_t mask, ch1, ch2;
switch (_G(_blender_mode)) {
case kSourceAlphaBlender:
case kOpaqueBlenderMode:
case kAdditiveBlenderMode:
return srcCols;
case kArgbToArgbBlender:
case kArgbToRgbBlender:
ch1 = vandq_u16(vmovq_n_u16(0xff), vceqq_u16(alphas, vmovq_n_u16(0)));
ch2 = vandq_u16(alphas, vcgtq_u16(alphas, vmovq_n_u16(0)));
alphas = vorrq_u16(ch1, ch2);
// fall through
case kRgbToRgbBlender:
case kAlphaPreservedBlenderMode:
return rgbBlendSIMD2Bpp(srcCols, destCols, alphas);
case kRgbToArgbBlender:
mask = vorrq_u16(vceqq_u16(alphas, vmovq_n_u16(0)), vceqq_u16(alphas, vmovq_n_u16(255)));
ch1 = vandq_u16(srcCols, mask);
ch2 = vandq_u16(rgbBlendSIMD2Bpp(srcCols, destCols, alphas), vmvnq_u16(mask));
return vorrq_u16(ch1, ch2);
case kTintBlenderMode:
case kTintLightBlenderMode:
uint32x4_t srcColsLo = simd2BppTo4Bpp(vget_low_u16(srcCols));
uint32x4_t srcColsHi = simd2BppTo4Bpp(vget_high_u16(srcCols));
uint32x4_t destColsLo = simd2BppTo4Bpp(vget_low_u16(destCols));
uint32x4_t destColsHi = simd2BppTo4Bpp(vget_high_u16(destCols));
uint32x4_t alphasLo = vmovl_u16(vget_low_u16(alphas));
uint32x4_t alphasHi = vmovl_u16(vget_high_u16(alphas));
uint16x4_t lo = simd4BppTo2Bpp(blendTintSpriteSIMD(srcColsLo, destColsLo, alphasLo, _G(_blender_mode) == kTintLightBlenderMode));
uint16x4_t hi = simd4BppTo2Bpp(blendTintSpriteSIMD(srcColsHi, destColsHi, alphasHi, _G(_blender_mode) == kTintLightBlenderMode));
return vcombine_u16(lo, hi);
}
return srcCols;
}
template<int DestBytesPerPixel, int SrcBytesPerPixel>
static inline void drawPixelSIMD(byte *destPtr, const byte *srcP2, uint32x4_t tint, uint32x4_t alphas, uint32x4_t maskedAlphas, uint32x4_t transColors, int xDir, int xCtrBpp, int srcAlpha, int skipTrans, bool horizFlip, bool useTint, uint32x4_t skipMask) {
uint32x4_t srcCols, destCol;
if (DestBytesPerPixel == 4)
destCol = vld1q_u32((uint32 *)destPtr);
else
destCol = simd2BppTo4Bpp(vld1_u16((uint16 *)destPtr));
if (SrcBytesPerPixel == 4)
srcCols = vld1q_u32((const uint32 *)(srcP2 + xDir * xCtrBpp));
else
srcCols = simd2BppTo4Bpp(vld1_u16((const uint16 *)(srcP2 + xDir * xCtrBpp)));
// we do this here because we need to check if we should skip the pixel before we blend it
uint32x4_t mask1 = skipTrans ? vceqq_u32(vandq_u32(srcCols, maskedAlphas), transColors) : vmovq_n_u32(0);
mask1 = vorrq_u32(mask1, skipMask);
if (srcAlpha != -1) {
// take into account for useTint
if (useTint) {
srcCols = blendPixelSIMD(tint, srcCols, alphas);
} else {
srcCols = blendPixelSIMD(srcCols, destCol, alphas);
}
}
uint32x4_t destCols2 = vandq_u32(destCol, mask1);
uint32x4_t srcCols2 = vandq_u32(srcCols, vmvnq_u32(mask1));
uint32x4_t final = vorrq_u32(destCols2, srcCols2);
if (horizFlip) {
final = vrev64q_u32(final);
final = vcombine_u32(vget_high_u32(final), vget_low_u32(final));
}
if (DestBytesPerPixel == 4) {
vst1q_u32((uint32 *)destPtr, final);
} else {
vst1_u16((uint16 *)destPtr, simd4BppTo2Bpp(final));
}
}
static inline void drawPixelSIMD2Bpp(byte *destPtr, const byte *srcP2, uint16x8_t tint, uint16x8_t alphas, uint16x8_t transColors, int xDir, int xCtrBpp, int srcAlpha, int skipTrans, bool horizFlip, bool useTint, uint16x8_t skipMask) {
uint16x8_t destCol = vld1q_u16((uint16 *)destPtr);
uint16x8_t srcCols = vld1q_u16((const uint16 *)(srcP2 + xDir * xCtrBpp));
uint16x8_t mask1 = skipTrans ? vceqq_u16(srcCols, transColors) : vmovq_n_u16(0);
mask1 = vorrq_u16(mask1, skipMask);
if (srcAlpha != -1) {
// take into account for useTint
if (useTint) {
srcCols = blendPixelSIMD2Bpp(tint, srcCols, alphas);
} else {
srcCols = blendPixelSIMD2Bpp(srcCols, destCol, alphas);
}
}
uint16x8_t destCols2 = vandq_u16(destCol, mask1);
uint16x8_t srcCols2 = vandq_u16(srcCols, vmvnq_u16(mask1));
uint16x8_t final = vorrq_u16(destCols2, srcCols2);
if (horizFlip) {
final = vrev64q_u16(final);
final = vcombine_u16(vget_high_u16(final), vget_low_u16(final));
}
vst1q_u16((uint16 *)destPtr, final);
}
public:
// This template handles 2bpp and 4bpp, the other specializations handle 1bpp and format conversion blits
template<int DestBytesPerPixel, int SrcBytesPerPixel, bool Scale>
static void drawInner4BppWithConv(BITMAP::DrawInnerArgs &args) {
const int xDir = args.horizFlip ? -1 : 1;
byte rSrc, gSrc, bSrc, aSrc;
byte rDest = 0, gDest = 0, bDest = 0, aDest = 0;
uint32x4_t tint = vshlq_n_u32(vdupq_n_u32(args.srcAlpha), 24);
tint = vorrq_u32(tint, vshlq_n_u32(vdupq_n_u32(args.tintRed), 16));
tint = vorrq_u32(tint, vshlq_n_u32(vdupq_n_u32(args.tintGreen), 8));
tint = vorrq_u32(tint, vdupq_n_u32(args.tintBlue));
uint32x4_t maskedAlphas = vmovq_n_u32(args.alphaMask);
uint32x4_t transColors = vmovq_n_u32(args.transColor);
uint32x4_t alphas = vmovq_n_u32(args.srcAlpha);
// This is so that we can calculate what pixels to crop off in a vectorized way
const uint32x4_t addIndexesNormal = {0, 1, 2, 3};
const uint32x4_t addIndexesFlipped = {3, 2, 1, 0};
uint32x4_t addIndexes = args.horizFlip ? addIndexesFlipped : addIndexesNormal;
// This is so that we can calculate in parallel the pixel indexes for scaled drawing
uint32x4_t scaleAdds = {0, (uint32)args.scaleX, (uint32)args.scaleX*2, (uint32)args.scaleX*3};
// Clip the bounds ahead of time (so we don't waste time checking if we are in bounds when
// we are in the inner loop)
int xCtrStart = 0, xCtrBppStart = 0, xCtrWidth = args.dstRect.width();
if (args.xStart + xCtrWidth > args.destArea.w) {
xCtrWidth = args.destArea.w - args.xStart;
}
if (args.xStart < 0) {
xCtrStart = -args.xStart;
xCtrBppStart = xCtrStart * SrcBytesPerPixel;
args.xStart = 0;
}
int destY = args.yStart, srcYCtr = 0, yCtr = 0, scaleYCtr = 0, yCtrHeight = args.dstRect.height();
if (Scale) yCtrHeight = args.dstRect.height();
if (args.yStart < 0) {
yCtr = -args.yStart;
destY = 0;
if (Scale) {
scaleYCtr = yCtr * args.scaleY;
srcYCtr = scaleYCtr / BITMAP::SCALE_THRESHOLD;
}
}
if (args.yStart + yCtrHeight > args.destArea.h) {
yCtrHeight = args.destArea.h - args.yStart;
}
/*if (!Scale && xCtrWidth % 4 != 0) {
--yCtrHeight;
}*/
const int secondToLast = xCtrWidth - 4;
byte *destP = (byte *)args.destArea.getBasePtr(0, destY);
const byte *srcP = (const byte *)args.src.getBasePtr(
args.horizFlip ? args.srcArea.right - 4 : args.srcArea.left,
args.vertFlip ? args.srcArea.bottom - 1 - yCtr : args.srcArea.top + yCtr);
for (; yCtr < yCtrHeight; ++destY, ++yCtr, scaleYCtr += args.scaleY) {
uint32x4_t xCtrWidthSIMD = vdupq_n_u32(xCtrWidth); // This is the width of the row
if (!Scale) {
int xCtr = xCtrStart, xCtrBpp = xCtrBppStart, destX = args.xStart;
for (; xCtr < secondToLast; destX += 4, xCtr += 4, xCtrBpp += SrcBytesPerPixel*4) {
byte *destPtr = &destP[destX * DestBytesPerPixel];
drawPixelSIMD<DestBytesPerPixel, SrcBytesPerPixel>(destPtr, srcP, tint, alphas, maskedAlphas, transColors, xDir, xCtrBpp, args.srcAlpha, args.skipTrans, args.horizFlip, args.useTint, vmovq_n_u32(0));
}
byte *destPtr = &destP[destX * DestBytesPerPixel];
uint32x4_t srcCols = vmovq_n_u32(0);
uint32x4_t destCols = vmovq_n_u32(0);
memcpy(&srcCols, srcP + xDir * xCtrBpp, (xCtrWidth - xCtr) * SrcBytesPerPixel);
memcpy(&destCols, destPtr, (xCtrWidth - xCtr) * DestBytesPerPixel);
// Skip pixels that are beyond the row
// uint32x4_t skipMask = vcgeq_u32(vaddq_u32(vdupq_n_u32(xCtr), addIndexes), xCtrWidthSIMD);
drawPixelSIMD<DestBytesPerPixel, SrcBytesPerPixel>((byte *)&destCols, (byte *)&srcCols, tint, alphas, maskedAlphas, transColors, xDir, 0, args.srcAlpha, args.skipTrans, args.horizFlip, args.useTint, vmovq_n_u32(0));
memcpy(destPtr, &destCols, (xCtrWidth - xCtr) * DestBytesPerPixel);
// Goto next row in source and destination image
destP += args.destArea.pitch;
srcP += args.vertFlip ? -args.src.pitch : args.src.pitch;
} else {
// Here we are scaling the image
int newSrcYCtr = scaleYCtr / BITMAP::SCALE_THRESHOLD;
// Since the source yctr might not update every row of the destination, we have
// to see if we are on a new row...
if (srcYCtr != newSrcYCtr) {
int diffSrcYCtr = newSrcYCtr - srcYCtr; // Have we moved yet
srcP += args.src.pitch * diffSrcYCtr;
srcYCtr = newSrcYCtr;
}
// Now also since we might skip a pixel or 2 or duplicate one to reach the desired
// scaling size, we create a small dummy buffer that we copy the pixels into and then
// call the drawPixelsSIMD function
byte srcBuffer[4*4];
for (int xCtr = xCtrStart, xCtrBpp = xCtrBppStart, destX = args.xStart, scaleXCtr = xCtrStart * args.scaleX; xCtr < xCtrWidth; destX += 4, xCtr += 4, xCtrBpp += SrcBytesPerPixel*4) {
if (yCtr + 1 == yCtrHeight && xCtr + 4 > xCtrWidth) break; // Don't go past the last 4 pixels
uint32x4_t indexes = vdupq_n_u32(scaleXCtr);
// Calculate in parallel the indexes of the pixels
indexes = vmulq_n_u32(vshrq_n_u32(vaddq_u32(indexes, scaleAdds), BITMAP::SCALE_THRESHOLD_BITS), SrcBytesPerPixel);
// Simply memcpy them in. memcpy has no real performance overhead here
memcpy(&srcBuffer[0*(uintptr_t)SrcBytesPerPixel], srcP + vgetq_lane_u32(indexes, 0), SrcBytesPerPixel);
memcpy(&srcBuffer[1*(uintptr_t)SrcBytesPerPixel], srcP + vgetq_lane_u32(indexes, 1), SrcBytesPerPixel);
memcpy(&srcBuffer[2*(uintptr_t)SrcBytesPerPixel], srcP + vgetq_lane_u32(indexes, 2), SrcBytesPerPixel);
memcpy(&srcBuffer[3*(uintptr_t)SrcBytesPerPixel], srcP + vgetq_lane_u32(indexes, 3), SrcBytesPerPixel);
scaleXCtr += args.scaleX*4;
// Now this is pretty much the same as before with non-scaled code, except that we use
// our dummy source buffer instead of the actual source bitmap
byte *destPtr = &destP[destX * (uintptr_t)DestBytesPerPixel];
uint32x4_t skipMask = vcgeq_u32(vaddq_u32(vdupq_n_u32(xCtr), addIndexes), xCtrWidthSIMD);
drawPixelSIMD<DestBytesPerPixel, SrcBytesPerPixel>(destPtr, (const byte *)srcBuffer, tint, alphas, maskedAlphas, transColors, 1, 0, args.srcAlpha, args.skipTrans, args.horizFlip, args.useTint, skipMask);
}
// We calculate every row here except the last (because then we need to
// check for if we fall off the edge of the row)
// The only exception here is scaling drawing this is because:
// 1) if statements are costly, and the less we do the faster this loop is
// 2) with this, the only branch in the normal drawing loop is the width check
// 3) the scaling code will actually draw until the last 4 pixels of the image
// and do the extra if checks because the scaling code is already much slower
// than the normal drawing loop, and the less duplicate code helps here.
if (yCtr + 1 != yCtrHeight) destP += args.destArea.pitch;
}
}
// Get the last x values of the last row
int xCtr = xCtrStart, xCtrBpp = xCtrBppStart, destX = args.xStart;
// We have a picture that is a multiple of 4, so no extra pixels to draw
/*if (xCtrWidth % 4 == 0)*/ return;
// Drawing the last few not scaled pixels here.
// Same as the loop above but now we check if we are going to overflow,
// and thus we don't need to mask out pixels that go over the row.
if (!Scale) {
for (; xCtr + 4 < xCtrWidth; destX += 4, xCtr += 4, xCtrBpp += SrcBytesPerPixel*4) {
byte *destPtr = &destP[(ptrdiff_t)destX * DestBytesPerPixel];
drawPixelSIMD<DestBytesPerPixel, SrcBytesPerPixel>(destPtr, srcP, tint, alphas, maskedAlphas, transColors, xDir, xCtrBpp, args.srcAlpha, args.skipTrans, args.horizFlip, args.useTint, vmovq_n_u32(0));
}
// Because we move in 4 pixel units, and horizFlip moves in 1, we have to move
// 1 pixel past the last pixel we did not blit, meaning going forward 3 pixels.
if (args.horizFlip) srcP += SrcBytesPerPixel * 3;
} else {
// So if we are scaling, set up the xCtr to what it was before (AKA the last 4 or so pixels of the image)
xCtr = xCtrWidth - xCtrWidth % 4;
xCtrBpp = xCtr * SrcBytesPerPixel;
destX = args.xStart+xCtr;
}
// For the last 4 pixels, we just do them in serial, nothing special
for (; xCtr < xCtrWidth; ++destX, ++xCtr, xCtrBpp += SrcBytesPerPixel) {
const byte *srcColPtr = (const byte *)(srcP + xDir * xCtrBpp);
if (Scale) {
srcColPtr = (const byte *)(srcP + (xCtr * args.scaleX) / BITMAP::SCALE_THRESHOLD * SrcBytesPerPixel);
}
byte *destVal = (byte *)&destP[destX * DestBytesPerPixel];
uint32 srcCol = args.dstBitmap.getColor(srcColPtr, SrcBytesPerPixel);
// Check if this is a transparent color we should skip
if (args.skipTrans && ((srcCol & args.alphaMask) == args.transColor))
continue;
args.src.format.colorToARGB(srcCol, aSrc, rSrc, gSrc, bSrc);
if (args.srcAlpha != -1) {
if (args.useTint) {
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
aDest = aSrc;
rSrc = args.tintRed;
gSrc = args.tintGreen;
bSrc = args.tintBlue;
aSrc = args.srcAlpha;
}
args.dstBitmap.blendPixel(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, args.srcAlpha, args.useTint, destVal);
srcCol = args.dstBitmap.format.ARGBToColor(aDest, rDest, gDest, bDest);
} else {
srcCol = args.dstBitmap.format.ARGBToColor(aSrc, rSrc, gSrc, bSrc);
}
if (DestBytesPerPixel == 4)
*(uint32 *)destVal = srcCol;
else
*(uint16 *)destVal = srcCol;
}
}
template<bool Scale>
static void drawInner2Bpp(BITMAP::DrawInnerArgs &args) {
const int xDir = args.horizFlip ? -1 : 1;
byte rSrc, gSrc, bSrc, aSrc;
byte rDest = 0, gDest = 0, bDest = 0, aDest = 0;
uint16x8_t tint = vdupq_n_u16(args.src.format.ARGBToColor(args.srcAlpha, args.tintRed, args.tintGreen, args.tintBlue));
uint16x8_t transColors = vdupq_n_u16(args.transColor);
uint16x8_t alphas = vdupq_n_u16(args.srcAlpha);
// This is so that we can calculate what pixels to crop off in a vectorized way
uint16x8_t addIndexesNormal = {0, 1, 2, 3, 4, 5, 6, 7};
uint16x8_t addIndexesFlipped = {7, 6, 5, 4, 3, 2, 1, 0};
uint16x8_t addIndexes = args.horizFlip ? addIndexesFlipped : addIndexesNormal;
// This is so that we can calculate in parallel the pixel indices for scaled drawing
uint32x4_t scaleAdds = {0, (uint32)args.scaleX, (uint32)args.scaleX*2, (uint32)args.scaleX*3};
uint32x4_t scaleAdds2 = {(uint32)args.scaleX*4, (uint32)args.scaleX*5, (uint32)args.scaleX*6, (uint32)args.scaleX*7};
// Clip the bounds ahead of time (so we don't waste time checking if we are in bounds when
// we are in the inner loop)
int xCtrStart = 0, xCtrBppStart = 0, xCtrWidth = args.dstRect.width();
if (args.xStart + xCtrWidth > args.destArea.w) {
xCtrWidth = args.destArea.w - args.xStart;
}
if (args.xStart < 0) {
xCtrStart = -args.xStart;
xCtrBppStart = xCtrStart * 2;
args.xStart = 0;
}
int destY = args.yStart, yCtr = 0, srcYCtr = 0, scaleYCtr = 0, yCtrHeight = args.dstRect.height();
if (Scale) yCtrHeight = args.dstRect.height();
if (args.yStart < 0) {
yCtr = -args.yStart;
destY = 0;
if (Scale) {
scaleYCtr = yCtr * args.scaleY;
srcYCtr = scaleYCtr / BITMAP::SCALE_THRESHOLD;
}
}
if (args.yStart + yCtrHeight > args.destArea.h) {
yCtrHeight = args.destArea.h - args.yStart;
}
/*if (!Scale && xCtrWidth % 8 != 0) {
--yCtrHeight;
}*/
const int secondToLast = xCtrWidth - 8;
byte *destP = (byte *)args.destArea.getBasePtr(0, destY);
const byte *srcP = (const byte *)args.src.getBasePtr(
args.horizFlip ? args.srcArea.right - 8 : args.srcArea.left,
args.vertFlip ? args.srcArea.bottom - 1 - yCtr : args.srcArea.top + yCtr);
for (; yCtr < yCtrHeight; ++destY, ++yCtr, scaleYCtr += args.scaleY) {
uint16x8_t xCtrWidthSIMD = vmovq_n_u16(xCtrWidth); // This is the width of the row
if (!Scale) {
// If we are not scaling the image
int xCtr = xCtrStart, xCtrBpp = xCtrBppStart, destX = args.xStart;
for (; xCtr < secondToLast; destX += 8, xCtr += 8, xCtrBpp += 16) {
byte *destPtr = &destP[destX * 2];
drawPixelSIMD2Bpp(destPtr, srcP, tint, alphas, transColors, xDir, xCtrBpp, args.srcAlpha, args.skipTrans, args.horizFlip, args.useTint, vmovq_n_u16(0));
}
byte *destPtr = &destP[destX * 2];
uint16x8_t srcCols = vmovq_n_u16(0);
uint16x8_t destCols = vmovq_n_u16(0);
const int copySize = (xCtrWidth - xCtr) * 2;
memcpy(&srcCols, srcP + xDir * xCtrBpp, copySize);
memcpy(&destCols, destPtr, copySize);
// Skip pixels that are beyond the row
// uint16x8_t skipMask = vcgeq_u16(vaddq_u16(vdupq_n_u16(xCtr), addIndexes), xCtrWidthSIMD);
drawPixelSIMD2Bpp((byte *)&destCols, (byte *)&srcCols, tint, alphas, transColors, xDir, 0, args.srcAlpha, args.skipTrans, args.horizFlip, args.useTint, vmovq_n_u16(0));
memcpy(destPtr, &destCols, copySize);
// Goto next row in source and destination image
destP += args.destArea.pitch;
srcP += args.vertFlip ? -args.src.pitch : args.src.pitch;
} else {
// Here we are scaling the image
int newSrcYCtr = scaleYCtr / BITMAP::SCALE_THRESHOLD;
// Since the source yctr might not update every row of the destination, we have
// to see if we are on a new row...
if (srcYCtr != newSrcYCtr) {
int diffSrcYCtr = newSrcYCtr - srcYCtr;
srcP += args.src.pitch * diffSrcYCtr;
srcYCtr = newSrcYCtr;
}
// Now also since we might skip a pixel or 2 or duplicate one to reach the desired
// scaling size, we create a small dummy buffer that we copy the pixels into and then
// call the drawPixelsSIMD function
uint16 srcBuffer[8];
for (int xCtr = xCtrStart, xCtrBpp = xCtrBppStart, destX = args.xStart, scaleXCtr = xCtrStart * args.scaleX; xCtr < xCtrWidth; destX += 8, xCtr += 8, xCtrBpp += 16) {
if (yCtr + 1 == yCtrHeight && xCtr + 8 > xCtrWidth) break;
uint32x4_t indexes = vdupq_n_u32(scaleXCtr), indexes2 = vdupq_n_u32(scaleXCtr);
// Calculate in parallel the indices of the pixels
indexes = vmulq_n_u32(vshrq_n_u32(vaddq_u32(indexes, scaleAdds), BITMAP::SCALE_THRESHOLD_BITS), 2);
indexes2 = vmulq_n_u32(vshrq_n_u32(vaddq_u32(indexes2, scaleAdds2), BITMAP::SCALE_THRESHOLD_BITS), 2);
// Simply memcpy them in. memcpy has no real performance overhead here
srcBuffer[0] = *(const uint16 *)(srcP + vgetq_lane_u32(indexes, 0));
srcBuffer[1] = *(const uint16 *)(srcP + vgetq_lane_u32(indexes, 1));
srcBuffer[2] = *(const uint16 *)(srcP + vgetq_lane_u32(indexes, 2));
srcBuffer[3] = *(const uint16 *)(srcP + vgetq_lane_u32(indexes, 3));
srcBuffer[4] = *(const uint16 *)(srcP + vgetq_lane_u32(indexes2, 0));
srcBuffer[5] = *(const uint16 *)(srcP + vgetq_lane_u32(indexes2, 1));
srcBuffer[6] = *(const uint16 *)(srcP + vgetq_lane_u32(indexes2, 2));
srcBuffer[7] = *(const uint16 *)(srcP + vgetq_lane_u32(indexes2, 3));
scaleXCtr += args.scaleX*8;
// Now this is pretty much the same as before with non-scaled code, except that we use
// our dummy source buffer instead of the actual source bitmap
byte *destPtr = &destP[destX * 2];
uint16x8_t skipMask = vcgeq_u16(vaddq_u16(vdupq_n_u16(xCtr), addIndexes), xCtrWidthSIMD);
drawPixelSIMD2Bpp(destPtr, (const byte *)srcBuffer, tint, alphas, transColors, 1, 0, args.srcAlpha, args.skipTrans, args.horizFlip, args.useTint, skipMask);
}
// We calculate every row here except the last (because then we need to
// check for if we fall off the edge of the row)
// The only exception here is scaling drawing this is because:
// 1) if statements are costly, and the less we do the faster this loop is
// 2) with this, the only branch in the normal drawing loop is the width check
// 3) the scaling code will actually draw until the last 4 pixels of the image
// and do the extra if checks because the scaling code is already much slower
// than the normal drawing loop, and the less duplicate code helps here.
if (yCtr + 1 != yCtrHeight) destP += args.destArea.pitch;
}
}
// We have a picture that is a multiple of 8, so no extra pixels to draw
/*if (xCtrWidth % 8 == 0)*/ return;
// Get the last x values of the last row
int xCtr = xCtrStart, xCtrBpp = xCtrBppStart, destX = args.xStart;
// Drawing the last few not scaled pixels here.
// Same as the loop above but now we check if we are going to overflow,
// and thus we don't need to mask out pixels that go over the row.
if (!Scale) {
for (; xCtr + 8 < xCtrWidth; destX += 8, xCtr += 8, xCtrBpp += 16) {
byte *destPtr = &destP[destX * 2];
drawPixelSIMD2Bpp(destPtr, srcP, tint, alphas, transColors, xDir, xCtrBpp, args.srcAlpha, args.skipTrans, args.horizFlip, args.useTint, vmovq_n_u16(0));
}
// Because we move in 8 pixel units, and horizFlip moves in 1, we have to move
// 1 pixel past the last pixel we did not blit, meaning going forward 7 pixels.
if (args.horizFlip) srcP += 2 * 7;
} else {
// So if we are scaling, set up the xCtr to what it was before (AKA the last 8 or so pixels of the image)
xCtr = xCtrWidth - xCtrWidth % 8;
xCtrBpp = xCtr * 2;
destX = args.xStart+xCtr;
}
// For the last 4 pixels, we just do them in serial, nothing special
for (; xCtr < xCtrWidth; ++destX, ++xCtr, xCtrBpp += 2) {
const byte *srcColPtr = (const byte *)(srcP + xDir * xCtrBpp);
if (Scale) {
srcColPtr = (const byte *)(srcP + (xCtr * args.scaleX) / BITMAP::SCALE_THRESHOLD * 2);
}
byte *destVal = (byte *)&destP[destX * 2];
uint32 srcCol = (uint32)(*(const uint16 *)srcColPtr);
// Check if this is a transparent color we should skip
if (args.skipTrans && srcCol == args.transColor)
continue;
args.src.format.colorToARGB(srcCol, aSrc, rSrc, gSrc, bSrc);
if (args.srcAlpha != -1) {
if (args.useTint) {
rDest = rSrc;
gDest = gSrc;
bDest = bSrc;
aDest = aSrc;
rSrc = args.tintRed;
gSrc = args.tintGreen;
bSrc = args.tintBlue;
aSrc = args.srcAlpha;
}/* else {
format.colorToARGB((uint32)(*(uint16 *)destVal), aDest, rDest, gDest, bDest);
}*/
args.dstBitmap.blendPixel(aSrc, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest, args.srcAlpha, args.useTint, destVal);
srcCol = args.dstBitmap.format.ARGBToColor(aDest, rDest, gDest, bDest);
} else {
srcCol = args.dstBitmap.format.ARGBToColor(aSrc, rSrc, gSrc, bSrc);
}
*(uint16 *)destVal = srcCol;
}
}
template<bool Scale>
static void drawInner1Bpp(BITMAP::DrawInnerArgs &args) {
const int xDir = args.horizFlip ? -1 : 1;
uint8x16_t transColors = vmovq_n_u8(args.transColor);
// This is so that we can calculate in parallel the pixel indices for scaled drawing
uint32x4_t scaleAdds1 = {0, (uint32)args.scaleX, (uint32)args.scaleX*2, (uint32)args.scaleX*3};
uint32x4_t scaleAdds2 = {(uint32)args.scaleX*4, (uint32)args.scaleX*5, (uint32)args.scaleX*6, (uint32)args.scaleX*7};
uint32x4_t scaleAdds3 = {(uint32)args.scaleX*8, (uint32)args.scaleX*9, (uint32)args.scaleX*10, (uint32)args.scaleX*11};
uint32x4_t scaleAdds4 = {(uint32)args.scaleX*12, (uint32)args.scaleX*13, (uint32)args.scaleX*14, (uint32)args.scaleX*15};
// Clip the bounds ahead of time (so we don't waste time checking if we are in bounds when
// we are in the inner loop)
int xCtrStart = 0, xCtrWidth = args.dstRect.width();
if (args.xStart + xCtrWidth > args.destArea.w) {
xCtrWidth = args.destArea.w - args.xStart;
}
if (args.xStart < 0) {
xCtrStart = -args.xStart;
args.xStart = 0;
}
int destY = args.yStart, yCtr = 0, srcYCtr = 0, scaleYCtr = 0, yCtrHeight = args.dstRect.height();
if (Scale) yCtrHeight = args.dstRect.height();
if (args.yStart < 0) {
yCtr = -args.yStart;
destY = 0;
if (Scale) {
scaleYCtr = yCtr * args.scaleY;
srcYCtr = scaleYCtr / BITMAP::SCALE_THRESHOLD;
}
}
if (args.yStart + yCtrHeight > args.destArea.h) {
yCtrHeight = args.destArea.h - args.yStart;
}
byte *destP = (byte *)args.destArea.getBasePtr(0, destY);
const byte *srcP = (const byte *)args.src.getBasePtr(
args.horizFlip ? args.srcArea.right - 16 : args.srcArea.left,
args.vertFlip ? args.srcArea.bottom - 1 - yCtr : args.srcArea.top + yCtr);
for (; yCtr < yCtrHeight; ++destY, ++yCtr, scaleYCtr += args.scaleY) {
if (Scale) {
// So here we update the srcYCtr differently due to this being for
// scaling
int newSrcYCtr = scaleYCtr / BITMAP::SCALE_THRESHOLD;
if (srcYCtr != newSrcYCtr) {
// Since the source yctr might not update every row of the destination, we have
// to see if we are on a new row...
int diffSrcYCtr = newSrcYCtr - srcYCtr;
srcP += args.src.pitch * diffSrcYCtr;
srcYCtr = newSrcYCtr;
}
}
int xCtr = xCtrStart, destX = args.xStart, scaleXCtr = xCtrStart * args.scaleX;
for (; xCtr + 16 < xCtrWidth; destX += 16, xCtr += 16) {
byte *destPtr = &destP[destX];
// Here we don't use the drawPixelSIMD function because 1bpp bitmaps in allegro
// can't have any blending applied to them
uint8x16_t destCols = vld1q_u8(destPtr);
uint8x16_t srcCols = vld1q_u8(srcP + xDir * xCtr);
if (Scale) {
// If we are scaling, we have to set each pixel individually
uint32x4_t indexes1 = vdupq_n_u32(scaleXCtr), indexes2 = vdupq_n_u32(scaleXCtr);
uint32x4_t indexes3 = vdupq_n_u32(scaleXCtr), indexes4 = vdupq_n_u32(scaleXCtr);
indexes1 = vshrq_n_u32(vaddq_u32(indexes1, scaleAdds1), BITMAP::SCALE_THRESHOLD_BITS);
indexes2 = vshrq_n_u32(vaddq_u32(indexes2, scaleAdds2), BITMAP::SCALE_THRESHOLD_BITS);
indexes3 = vshrq_n_u32(vaddq_u32(indexes3, scaleAdds3), BITMAP::SCALE_THRESHOLD_BITS);
indexes4 = vshrq_n_u32(vaddq_u32(indexes4, scaleAdds4), BITMAP::SCALE_THRESHOLD_BITS);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes1, 0)], srcCols, 0);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes1, 1)], srcCols, 1);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes1, 2)], srcCols, 2);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes1, 3)], srcCols, 3);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes2, 0)], srcCols, 4);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes2, 1)], srcCols, 5);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes2, 2)], srcCols, 6);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes2, 3)], srcCols, 7);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes3, 0)], srcCols, 8);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes3, 1)], srcCols, 9);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes3, 2)], srcCols, 10);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes3, 3)], srcCols, 11);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes4, 0)], srcCols, 12);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes4, 1)], srcCols, 13);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes4, 2)], srcCols, 14);
srcCols = vsetq_lane_u8(srcP[vgetq_lane_u32(indexes4, 3)], srcCols, 15);
scaleXCtr += args.scaleX*16;
}
// Mask out transparent pixels
uint8x16_t mask1 = args.skipTrans ? vceqq_u8(srcCols, transColors) : vmovq_n_u8(0);
uint8x16_t final = vorrq_u8(vandq_u8(srcCols, vmvnq_u8(mask1)), vandq_u8(destCols, mask1));
if (args.horizFlip) {
final = vrev64q_u8(final);
final = vcombine_u8(vget_high_u8(final), vget_low_u8(final));
}
vst1q_u8(destPtr, final);
}
// Get the last x values
// Because we move in 16 pixel units, and horizFlip moves in 1, we have to move
// 1 pixel past the last pixel we did not blit, meaning going forward 15 pixels.
if (args.horizFlip) srcP += 15;
for (; xCtr < xCtrWidth; ++destX, ++xCtr, scaleXCtr += args.scaleX) {
const byte *srcCol = (const byte *)(srcP + xDir * xCtr);
if (Scale) {
srcCol = (const byte *)(srcP + scaleXCtr / BITMAP::SCALE_THRESHOLD);
}
// Check if this is a transparent color we should skip
if (args.skipTrans && *srcCol == args.transColor)
continue;
byte *destVal = (byte *)&destP[destX];
*destVal = *srcCol;
}
if (args.horizFlip) srcP -= 15; // Undo what we did up there
destP += args.destArea.pitch; // Go to next row
// Only advance the src row by 1 every time like this if we don't scale
if (!Scale) srcP += args.vertFlip ? -args.src.pitch : args.src.pitch;
}
}
}; // end of class DrawInnerImpl_NEON
template<bool Scale>
void BITMAP::drawNEON(DrawInnerArgs &args) {
if (args.sameFormat) {
switch (format.bytesPerPixel) {
case 1: DrawInnerImpl_NEON::drawInner1Bpp<Scale>(args); break;
case 2: DrawInnerImpl_NEON::drawInner2Bpp<Scale>(args); break;
case 4: DrawInnerImpl_NEON::drawInner4BppWithConv<4, 4, Scale>(args); break;
}
} else if (format.bytesPerPixel == 4 && args.src.format.bytesPerPixel == 2) {
DrawInnerImpl_NEON::drawInner4BppWithConv<4, 2, Scale>(args);
} else if (format.bytesPerPixel == 2 && args.src.format.bytesPerPixel == 4) {
DrawInnerImpl_NEON::drawInner4BppWithConv<2, 4, Scale>(args);
}
}
template void BITMAP::drawNEON<false>(DrawInnerArgs &);
template void BITMAP::drawNEON<true>(DrawInnerArgs &);
} // namespace AGS3
#if !defined(__aarch64__) && !defined(__ARM_NEON)
#if defined(__clang__)
#pragma clang attribute pop
#elif defined(__GNUC__)
#pragma GCC pop_options
#endif
#endif // !defined(__aarch64__) && !defined(__ARM_NEON)
#endif // SCUMMVM_NEON

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
/* 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/>.
*
*/
#include "ags/lib/allegro/system.h"
#include "ags/lib/allegro/aintern.h"
#include "ags/globals.h"
#include "common/system.h"
namespace AGS3 {
GFX_MODE_LIST *get_gfx_mode_list(int card) {
GFX_MODE_LIST *list = new GFX_MODE_LIST();
list->num_modes = 1;
list->mode = new GFX_MODE[1];
GFX_MODE &gm = list->mode[0];
gm.width = 320;
gm.height = 200;
gm.bpp = 32;
return list;
}
void destroy_gfx_mode_list(GFX_MODE_LIST *list) {
delete[] list->mode;
delete list;
}
void set_color_depth(int depth) {
_G(_color_depth) = depth;
}
int get_color_depth() {
return _G(_color_depth);
}
int get_desktop_resolution(int32_t *width, int32_t *height) {
// TODO: ScummVM has a hardcoded dummy desktop resolution. See if there's any
// need to change the values, given we're hardcoded for pretend full-screen
if (width)
*width = 640;
if (height)
*height = 480;
return 0;
}
void request_refresh_rate(int rate) {
// No implementation
}
void set_close_button_callback(void(*proc)()) {
// No implementation
}
} // namespace AGS3

View File

@@ -0,0 +1,151 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_SYSTEM_H
#define AGS_LIB_ALLEGRO_SYSTEM_H
#include "ags/lib/allegro/base.h"
#include "ags/lib/allegro/color.h"
#include "ags/lib/allegro/gfx.h"
#include "ags/shared/core/types.h"
namespace AGS3 {
#ifndef AL_METHOD
#define AL_METHOD(type, name, args) type (*name) args
#endif
#define SYSTEM_AUTODETECT 0
#define SYSTEM_SCUMMVM AL_ID('S','C','V','M')
#define SYSTEM_NONE AL_ID('N','O','N','E')
#define GFX_SCUMMVM AL_ID('S', 'C', 'V', 'M')
#define SWITCH_NONE 0
#define SWITCH_PAUSE 1
#define SWITCH_AMNESIA 2
#define SWITCH_BACKGROUND 3
#define SWITCH_BACKAMNESIA 4
#define SWITCH_IN 0
#define SWITCH_OUT 1
struct GFX_MODE {
int width, height, bpp;
};
struct GFX_MODE_LIST {
int num_modes; /* number of gfx modes */
GFX_MODE *mode; /* pointer to the actual mode list array */
};
struct SYSTEM_DRIVER {
int id;
const char *name;
const char *desc;
const char *ascii_name;
AL_METHOD(int, init, (void));
AL_METHOD(void, exit, (void));
AL_METHOD(void, get_executable_name, (char *output, int size));
AL_METHOD(int, find_resource, (char *dest, const char *resource, int size));
AL_METHOD(void, set_window_title, (const char *name));
AL_METHOD(int, set_close_button_callback, (AL_METHOD(void, proc, (void))));
AL_METHOD(void, message, (const char *msg));
AL_METHOD(void, assert, (const char *msg));
AL_METHOD(void, save_console_state, (void));
AL_METHOD(void, restore_console_state, (void));
AL_METHOD(BITMAP *, create_bitmap, (int color_depth, int width, int height));
AL_METHOD(void, created_bitmap, (BITMAP *bmp));
AL_METHOD(BITMAP *, create_sub_bitmap, (BITMAP *parent, int x, int y, int width, int height));
AL_METHOD(void, created_sub_bitmap, (BITMAP *bmp, BITMAP *parent));
AL_METHOD(int, destroy_bitmap, (BITMAP *bitmap));
AL_METHOD(void, read_hardware_palette, (void));
AL_METHOD(void, set_palette_range, (const RGB *p, int from, int to, int retracesync));
AL_METHOD(struct GFX_VTABLE *, get_vtable, (int color_depth));
AL_METHOD(int, set_display_switch_mode, (int mode));
AL_METHOD(void, display_switch_lock, (int lock, int foreground));
AL_METHOD(int, desktop_color_depth, (void));
AL_METHOD(int, get_desktop_resolution, (int *width, int *height));
AL_METHOD(void, get_gfx_safe_mode, (int *driver, struct GFX_MODE *mode));
AL_METHOD(void, yield_timeslice, (void));
AL_METHOD(void *, create_mutex, (void));
AL_METHOD(void, destroy_mutex, (void *handle));
AL_METHOD(void, lock_mutex, (void *handle));
AL_METHOD(void, unlock_mutex, (void *handle));
};
/* creates and manages the screen bitmap */
struct GFX_DRIVER {
int id;
AL_CONST char *name;
AL_CONST char *desc;
AL_CONST char *ascii_name;
AL_METHOD(BITMAP *, init, (int w, int h, int v_w, int v_h, int color_depth));
AL_METHOD(void, exit, (BITMAP *b));
AL_METHOD(int, scroll, (int x, int y));
AL_METHOD(void, vsync, (void));
AL_METHOD(void, set_palette, (AL_CONST RGB *p, int from, int to, int retracesync));
AL_METHOD(int, request_scroll, (int x, int y));
AL_METHOD(int, poll_scroll, (void));
AL_METHOD(void, enable_triple_buffer, (void));
AL_METHOD(BITMAP *, create_video_bitmap, (int width, int height));
AL_METHOD(void, destroy_video_bitmap, (BITMAP *bitmap));
AL_METHOD(int, show_video_bitmap, (BITMAP *bitmap));
AL_METHOD(int, request_video_bitmap, (BITMAP *bitmap));
AL_METHOD(BITMAP *, create_system_bitmap, (int width, int height));
AL_METHOD(void, destroy_system_bitmap, (BITMAP *bitmap));
AL_METHOD(int, set_mouse_sprite, (BITMAP *sprite, int xfocus, int yfocus));
AL_METHOD(int, show_mouse, (BITMAP *bmp, int x, int y));
AL_METHOD(void, hide_mouse, (void));
AL_METHOD(void, move_mouse, (int x, int y));
AL_METHOD(void, save_video_state, (void));
AL_METHOD(void, restore_video_state, (void));
AL_METHOD(void, set_blender_mode, (int mode, int r, int g, int b, int a));
AL_METHOD(GFX_MODE_LIST *, fetch_mode_list, (void));
int w, h; /* physical (not virtual!) screen size */
int linear; /* true if video memory is linear */
long bank_size; /* bank size, in bytes */
long bank_gran; /* bank granularity, in bytes */
long vid_mem; /* video memory size, in bytes */
long vid_phys_base; /* physical address of video memory */
int windowed; /* true if driver runs windowed */
};
extern void set_color_depth(int depth);
extern int get_color_depth();
extern int get_desktop_resolution(int32_t *width, int32_t *height);
extern void request_refresh_rate(int rate);
extern void set_close_button_callback(void(*proc)());
extern GFX_MODE_LIST *get_gfx_mode_list(int card);
extern void destroy_gfx_mode_list(GFX_MODE_LIST *list);
inline void vsync() {}
inline int set_display_switch_callback(int dir, AL_METHOD(void, cb, (void))) {
return 0;
}
inline int set_display_switch_mode(int v) {
return -1;
}
} // namespace AGS3
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,151 @@
/* 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/>.
*
*/
#ifndef AGS_LIB_ALLEGRO_UNICODE_H
#define AGS_LIB_ALLEGRO_UNICODE_H
#include "ags/lib/allegro/base.h"
namespace AGS3 {
#define U_ASCII AL_ID('A','S','C','8')
#define U_ASCII_CP AL_ID('A','S','C','P')
#define U_UNICODE AL_ID('U','N','I','C')
#define U_UTF8 AL_ID('U','T','F','8')
#define U_CURRENT AL_ID('c','u','r','.')
/* UTF-8 support functions
*/
extern int utf8_getc(const char *s);
extern int utf8_getx(char **s);
extern int utf8_setc(char *s, int c);
extern int utf8_width(const char *s);
extern int utf8_cwidth(int c);
extern int utf8_isok(int c);
/**
* ASCII support functions
*/
extern int ascii_getc(const char *s);
extern int ascii_getx(char **s);
extern int ascii_setc(char *s, int c);
extern int ascii_width(const char *s);
extern int ascii_cwidth(int c);
extern int ascii_isok(int c);
/* ugetc: */
extern int (*ugetc)(const char *s);
/* ugetxc: */
extern int (*ugetx)(char **s);
/* ugetxc: */
extern int (*ugetxc)(const char * const *s);
/* usetc: */
extern int (*usetc)(char *s, int c);
/* uwidth: */
extern int (*uwidth)(const char *s);
/* ucwidth: */
extern int (*ucwidth)(int c);
/* uisok: */
extern int (*uisok)(int c);
/* set_uformat:
* Selects a new text encoding format.
*/
extern void set_uformat(int type);
enum { LC_CTYPE };
extern const char *setlocale(int type, const char *language);
/* get_uformat:
* Returns the current text encoding format.
*/
extern int get_uformat();
extern size_t ustrsize(const char *s);
/* &nicode string length
*/
extern int ustrlen(const char *s);
/* utolower:
* Unicode-aware version of the ANSI tolower() function.
*/
extern int utolower(int c);
/* utoupper:
* Unicode-aware version of the ANSI toupper() function.
*/
extern int utoupper(int c);
/* Unicode string compare
*/
extern int ustrcmp(const char *s1, const char *s2);
/* ustricmp:
* Unicode-aware version of the DJGPP stricmp() function.
*/
extern int ustricmp(const char *s1, const char *s2);
/* ustrncmp:
* Unicode-aware version of the ANSI strncmp() function.
*/
extern int ustrncmp(const char *s1, const char *s2, int n);
/* ustrnicmp:
* Unicode-aware version of the DJGPP strnicmp() function.
*/
extern int ustrnicmp(const char *s1, const char *s2, int n);
/* uoffset:
* Returns the offset in bytes from the start of the string to the
* character at the specified index. If the index is negative, counts
* backward from the end of the string (-1 returns an offset to the
* last character).
*/
extern int uoffset(const char *s, int index);
/* ugetat:
* Returns the character from the specified index within the string.
*/
extern int ugetat(const char *s, int idx);
/* ustrlwr:
* Unicode-aware version of the ANSI strlwr() function.
*/
extern char *ustrlwr(char *s);
/* ustrupr:
* Unicode-aware version of the ANSI strupr() function.
*/
extern char *ustrupr(char *s);
/* ustrstr:
* Unicode-aware version of the ANSI strstr() function.
*/
extern const char *ustrstr(const char *s1, const char *s2);
/* usetat:
* Modifies the character at the specified index within the string,
* handling adjustments for variable width data. Returns how far the
* rest of the string was moved.
*/
int usetat(char *s, int index, int c);
/* ustrsizez:
* Returns the size of the specified string in bytes, including the
* trailing zero.
*/
extern int ustrsizez(const char *s);
extern int need_uconvert(const char *s, int type, int newtype);
extern int uvszprintf(char *buf, int size, const char *format, va_list args);
} // namespace AGS3
#endif