Add hue/saturation/lightness shift support to AFP renderer to fix neo generator seven boss animation rendering.
This commit is contained in:
parent
6937473d0a
commit
3b10423955
@ -3,7 +3,7 @@ import signal
|
||||
from PIL import Image # type: ignore
|
||||
from typing import Any, Callable, List, Optional, Sequence, Union
|
||||
|
||||
from ..types import Color, Matrix, Point, AAMode
|
||||
from ..types import Color, HSL, Matrix, Point, AAMode
|
||||
from .perspective import perspective_calculate
|
||||
|
||||
|
||||
@ -186,6 +186,7 @@ def blend_mask_combine(
|
||||
def blend_point(
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
hsl_shift: HSL,
|
||||
# This should be a sequence of exactly 4 values, either bytes or a tuple.
|
||||
src_color: Sequence[int],
|
||||
# This should be a sequence of exactly 4 values, either bytes or a tuple.
|
||||
@ -200,6 +201,23 @@ def blend_point(
|
||||
clamp((src_color[3] * mult_color.a) + (255 * add_color.a)),
|
||||
)
|
||||
|
||||
# Only add in HSL shift effects if they exist, since its expensive to
|
||||
# convert and shift. Also I'm not sure if this should be done before or
|
||||
# after the add and multiply.
|
||||
if not hsl_shift.is_identity:
|
||||
hslcolor = Color(
|
||||
src_color[0] / 255, src_color[1] / 255, src_color[2] / 255, 1.0
|
||||
).as_hsl()
|
||||
hslcolor = hslcolor.add(hsl_shift)
|
||||
newcolor = hslcolor.as_rgb()
|
||||
|
||||
src_color = (
|
||||
clamp(newcolor.r * 255),
|
||||
clamp(newcolor.g * 255),
|
||||
clamp(newcolor.b * 255),
|
||||
src_color[3],
|
||||
)
|
||||
|
||||
if blendfunc == 3:
|
||||
return blend_multiply(dest_color, src_color)
|
||||
# TODO: blend mode 4, which is "screen" blending according to SWF references. I've only seen this
|
||||
@ -240,6 +258,7 @@ def pixel_renderer(
|
||||
callback: Callable[[Point], Optional[Point]],
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
hsl_shift: HSL,
|
||||
blendfunc: int,
|
||||
imgbytes: Union[bytes, bytearray],
|
||||
texbytes: Union[bytes, bytearray],
|
||||
@ -422,7 +441,12 @@ def pixel_renderer(
|
||||
|
||||
# Finally, blend it with the destination.
|
||||
return blend_point(
|
||||
add_color, mult_color, average, imgbytes[imgoff : (imgoff + 4)], blendfunc
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
average,
|
||||
imgbytes[imgoff : (imgoff + 4)],
|
||||
blendfunc,
|
||||
)
|
||||
else:
|
||||
# Calculate what texture pixel data goes here.
|
||||
@ -441,6 +465,7 @@ def pixel_renderer(
|
||||
return blend_point(
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
texbytes[texoff : (texoff + 4)],
|
||||
imgbytes[imgoff : (imgoff + 4)],
|
||||
blendfunc,
|
||||
@ -459,6 +484,7 @@ def affine_line_renderer(
|
||||
inverse: Matrix,
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
hsl_shift: HSL,
|
||||
blendfunc: int,
|
||||
imgbytes: Union[bytes, bytearray],
|
||||
texbytes: Union[bytes, bytearray],
|
||||
@ -491,6 +517,7 @@ def affine_line_renderer(
|
||||
lambda point: inverse.multiply_point(point),
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
blendfunc,
|
||||
imgbytes,
|
||||
texbytes,
|
||||
@ -505,6 +532,7 @@ def affine_composite(
|
||||
img: Image.Image,
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
hsl_shift: HSL,
|
||||
transform: Matrix,
|
||||
mask: Optional[Image.Image],
|
||||
blendfunc: int,
|
||||
@ -577,6 +605,7 @@ def affine_composite(
|
||||
lambda point: inverse.multiply_point(point),
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
blendfunc,
|
||||
imgbytes,
|
||||
texbytes,
|
||||
@ -623,6 +652,7 @@ def affine_composite(
|
||||
inverse,
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
blendfunc,
|
||||
imgbytes,
|
||||
texbytes,
|
||||
@ -676,6 +706,7 @@ def perspective_line_renderer(
|
||||
inverse: Matrix,
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
hsl_shift: HSL,
|
||||
blendfunc: int,
|
||||
imgbytes: Union[bytes, bytearray],
|
||||
texbytes: Union[bytes, bytearray],
|
||||
@ -716,6 +747,7 @@ def perspective_line_renderer(
|
||||
perspective_inverse,
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
blendfunc,
|
||||
imgbytes,
|
||||
texbytes,
|
||||
@ -730,6 +762,7 @@ def perspective_composite(
|
||||
img: Image.Image,
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
hsl_shift: HSL,
|
||||
transform: Matrix,
|
||||
camera: Point,
|
||||
focal_length: float,
|
||||
@ -804,6 +837,7 @@ def perspective_composite(
|
||||
perspective_inverse,
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
blendfunc,
|
||||
imgbytes,
|
||||
texbytes,
|
||||
@ -852,6 +886,7 @@ def perspective_composite(
|
||||
inverse_matrix,
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
blendfunc,
|
||||
imgbytes,
|
||||
texbytes,
|
||||
|
@ -1,13 +1,14 @@
|
||||
from PIL import Image # type: ignore
|
||||
from typing import Optional
|
||||
|
||||
from ..types import Color, Point, Matrix
|
||||
from ..types import Color, HSL, Point, Matrix
|
||||
|
||||
|
||||
def affine_composite(
|
||||
img: Image.Image,
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
hsl_shift: HSL,
|
||||
transform: Matrix,
|
||||
mask: Optional[Image.Image],
|
||||
blendfunc: int,
|
||||
@ -22,6 +23,7 @@ def perspective_composite(
|
||||
img: Image.Image,
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
hsl_shift: HSL,
|
||||
transform: Matrix,
|
||||
camera: Point,
|
||||
focal_length: float,
|
||||
|
@ -2,7 +2,7 @@ import multiprocessing
|
||||
from PIL import Image # type: ignore
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from ..types import Color, Matrix, Point, AAMode
|
||||
from ..types import Color, HSL, Matrix, Point, AAMode
|
||||
from .perspective import perspective_calculate
|
||||
|
||||
cdef extern struct floatcolor_t:
|
||||
@ -11,6 +11,11 @@ cdef extern struct floatcolor_t:
|
||||
double b;
|
||||
double a;
|
||||
|
||||
cdef extern struct hslcolor_t:
|
||||
double h;
|
||||
double s;
|
||||
double l;
|
||||
|
||||
cdef extern struct matrix_t:
|
||||
double a11;
|
||||
double a12;
|
||||
@ -36,6 +41,7 @@ cdef extern int composite_fast(
|
||||
unsigned int maxy,
|
||||
floatcolor_t add_color,
|
||||
floatcolor_t mult_color,
|
||||
hslcolor_t hsl_shift,
|
||||
double xscale,
|
||||
double yscale,
|
||||
matrix_t inverse,
|
||||
@ -52,6 +58,7 @@ def affine_composite(
|
||||
img: Image.Image,
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
hsl_shift: HSL,
|
||||
transform: Matrix,
|
||||
mask: Optional[Image.Image],
|
||||
blendfunc: int,
|
||||
@ -112,6 +119,7 @@ def affine_composite(
|
||||
# Convert classes to C structs.
|
||||
cdef floatcolor_t c_addcolor = floatcolor_t(r=add_color.r, g=add_color.g, b=add_color.b, a=add_color.a)
|
||||
cdef floatcolor_t c_multcolor = floatcolor_t(r=mult_color.r, g=mult_color.g, b=mult_color.b, a=mult_color.a)
|
||||
cdef hslcolor_t c_hslcolor = hslcolor_t(h=hsl_shift.h, s=hsl_shift.s, l=hsl_shift.l)
|
||||
cdef matrix_t c_inverse = matrix_t(
|
||||
a11=inverse.a11, a12=inverse.a12, a13=inverse.a13,
|
||||
a21=inverse.a21, a22=inverse.a22, a23=inverse.a23,
|
||||
@ -132,6 +140,7 @@ def affine_composite(
|
||||
maxy,
|
||||
c_addcolor,
|
||||
c_multcolor,
|
||||
c_hslcolor,
|
||||
transform.xscale,
|
||||
transform.yscale,
|
||||
c_inverse,
|
||||
@ -157,6 +166,7 @@ def perspective_composite(
|
||||
img: Image.Image,
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
hsl_shift: HSL,
|
||||
transform: Matrix,
|
||||
camera: Point,
|
||||
focal_length: float,
|
||||
@ -200,6 +210,7 @@ def perspective_composite(
|
||||
# Convert classes to C structs.
|
||||
cdef floatcolor_t c_addcolor = floatcolor_t(r=add_color.r, g=add_color.g, b=add_color.b, a=add_color.a)
|
||||
cdef floatcolor_t c_multcolor = floatcolor_t(r=mult_color.r, g=mult_color.g, b=mult_color.b, a=mult_color.a)
|
||||
cdef hslcolor_t c_hslcolor = hslcolor_t(h=hsl_shift.h, s=hsl_shift.s, l=hsl_shift.l)
|
||||
cdef matrix_t c_inverse = matrix_t(
|
||||
a11=inverse_matrix.a11, a12=inverse_matrix.a12, a13=inverse_matrix.a13,
|
||||
a21=inverse_matrix.a21, a22=inverse_matrix.a22, a23=inverse_matrix.a23,
|
||||
@ -220,6 +231,7 @@ def perspective_composite(
|
||||
maxy,
|
||||
c_addcolor,
|
||||
c_multcolor,
|
||||
c_hslcolor,
|
||||
transform.xscale,
|
||||
transform.yscale,
|
||||
c_inverse,
|
||||
|
@ -26,6 +26,12 @@ extern "C"
|
||||
double a;
|
||||
} floatcolor_t;
|
||||
|
||||
typedef struct hslcolor {
|
||||
double h;
|
||||
double s;
|
||||
double l;
|
||||
} hslcolor_t;
|
||||
|
||||
typedef struct point {
|
||||
double x;
|
||||
double y;
|
||||
@ -81,6 +87,7 @@ extern "C"
|
||||
int use_perspective;
|
||||
floatcolor_t add_color;
|
||||
floatcolor_t mult_color;
|
||||
hslcolor_t hsl_shift;
|
||||
int blendfunc;
|
||||
pthread_t *thread;
|
||||
int aa_mode;
|
||||
@ -90,6 +97,101 @@ extern "C"
|
||||
return fmin(fmax(0.0, roundf(color)), 255.0);
|
||||
}
|
||||
|
||||
inline unsigned int min(unsigned int x, unsigned int y) {
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
inline unsigned int max(unsigned int x, unsigned int y) {
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
void rgb_to_hsl(int r, int g, int b, double *h, double *s, double *l) {
|
||||
int cmax = max(max(r, g), b);
|
||||
int cmin = min(min(r, g), b);
|
||||
double sum = (double)(cmin + cmax);
|
||||
|
||||
// First, calculate luminance, which is the sum divided by 2. We
|
||||
// also need to scale down by 255 since RGB values are integers!
|
||||
*l = sum / (2.0 * 255.0);
|
||||
if (cmax == cmin) {
|
||||
// No point in calculating anything else, its just luminance.
|
||||
*h = 0.0;
|
||||
*s = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Second, calculate saturation.
|
||||
double delta = (double)(cmax - cmin);
|
||||
if (*l <= 0.5) {
|
||||
// 255 scaling appears on both sides, so no need to handle it.
|
||||
*s = delta / sum;
|
||||
} else {
|
||||
// We need to remember to scale by 255 here, so let's factor it out.
|
||||
*s = delta / ((2.0 * 255) - sum);
|
||||
}
|
||||
|
||||
// Finaly, calculate hue. This can theoretically go above 1.0 or below
|
||||
// 0.0 and most equations show it being clamped, but we need to clamp
|
||||
// again when converting back so don't bother wasting time.
|
||||
if (r == cmax) {
|
||||
*h = ((double)(g - b) / 6.0) / delta;
|
||||
} else if (g == cmax) {
|
||||
*h = (1.0 / 3.0) + ((double)(b - r) / 6.0) / delta;
|
||||
} else {
|
||||
*h = (2.0 / 3.0) + ((double)(r - g) / 6.0) / delta;
|
||||
}
|
||||
}
|
||||
|
||||
inline double hue_to_rgb(double v1, double v2, double vh) {
|
||||
// Clamp hue value to 0.0/1.0, respecting the fact that 361 degrees is
|
||||
// equivalent to 1 degree, and negative 1 degree is equivalent to 359.
|
||||
if (vh < 0.0) {
|
||||
vh += 1.0;
|
||||
}
|
||||
if (vh >= 1.0) {
|
||||
vh -= 1.0;
|
||||
}
|
||||
|
||||
// Split back into 3 quadrants since RGB isn't linear with in these,
|
||||
// there's a step function where at some point the slope goes from positive
|
||||
// to negative non-continuously.
|
||||
if ((6.0 * vh) < 1.0) {
|
||||
return v1 + ((v2 - v1) * 6.0 * vh);
|
||||
}
|
||||
if ((2.0 * vh) < 1.0) {
|
||||
return v2;
|
||||
}
|
||||
if ((3.0 * vh) < 2.0) {
|
||||
return v1 + ((v2 - v1) * ((2.0 / 3.0) - vh) * 6.0);
|
||||
}
|
||||
|
||||
return v1;
|
||||
}
|
||||
|
||||
void hsl_to_rgb(double h, double s, double l, unsigned char *r, unsigned char *g, unsigned char *b) {
|
||||
// Clamp hue value to 0.0/1.0, respecting the fact that 361 degrees is
|
||||
// equivalent to 1 degree, and negative 1 degree is equivalent to 359.
|
||||
while (h < 0.0) {
|
||||
h += 1.0;
|
||||
}
|
||||
while (h >= 1.0) {
|
||||
h -= 1.0;
|
||||
}
|
||||
s = fmin(fmax(s, 0.0), 1.0);
|
||||
l = fmin(fmax(l, 0.0), 1.0);
|
||||
|
||||
if (s == 0.0) {
|
||||
*r = *g = *b = (int)(l * 255.0);
|
||||
} else {
|
||||
double v2 = (l < 0.5) ? (l * (1.0 + s)) : ((l + s) - (l * s));
|
||||
double v1 = (2.0 * l) - v2;
|
||||
|
||||
*r = (unsigned char)(255.0 * hue_to_rgb(v1, v2, h + (1.0 / 3.0)));
|
||||
*g = (unsigned char)(255.0 * hue_to_rgb(v1, v2, h));
|
||||
*b = (unsigned char)(255.0 * hue_to_rgb(v1, v2, h - (1.0 / 3.0)));
|
||||
}
|
||||
}
|
||||
|
||||
intcolor_t blend_normal(
|
||||
intcolor_t dest,
|
||||
intcolor_t src
|
||||
@ -242,6 +344,7 @@ extern "C"
|
||||
intcolor_t blend_point(
|
||||
floatcolor_t add_color,
|
||||
floatcolor_t mult_color,
|
||||
hslcolor_t hsl_shift,
|
||||
intcolor_t src_color,
|
||||
intcolor_t dest_color,
|
||||
int blendfunc
|
||||
@ -254,6 +357,32 @@ extern "C"
|
||||
clamp((src_color.a * mult_color.a) + (255 * add_color.a)),
|
||||
};
|
||||
|
||||
// Add in hsl shift if there is anything to do.
|
||||
if (hsl_shift.h != 0.0 || hsl_shift.s != 0.0 || hsl_shift.l != 0.0) {
|
||||
hslcolor_t hslcolor;
|
||||
rgb_to_hsl(
|
||||
src_color.r,
|
||||
src_color.g,
|
||||
src_color.b,
|
||||
&hslcolor.h,
|
||||
&hslcolor.s,
|
||||
&hslcolor.l
|
||||
);
|
||||
|
||||
hslcolor.h += hsl_shift.h;
|
||||
hslcolor.s += hsl_shift.s;
|
||||
hslcolor.l += hsl_shift.l;
|
||||
|
||||
hsl_to_rgb(
|
||||
hslcolor.h,
|
||||
hslcolor.s,
|
||||
hslcolor.l,
|
||||
&src_color.r,
|
||||
&src_color.g,
|
||||
&src_color.b
|
||||
);
|
||||
}
|
||||
|
||||
if (blendfunc == 3) {
|
||||
return blend_multiply(dest_color, src_color);
|
||||
}
|
||||
@ -485,7 +614,7 @@ extern "C"
|
||||
}
|
||||
|
||||
// Blend it.
|
||||
work->imgdata[imgoff] = blend_point(work->add_color, work->mult_color, average, work->imgdata[imgoff], work->blendfunc);
|
||||
work->imgdata[imgoff] = blend_point(work->add_color, work->mult_color, work->hsl_shift, average, work->imgdata[imgoff], work->blendfunc);
|
||||
} else {
|
||||
// Grab the center of the pixel to get the color.
|
||||
int texx = -1;
|
||||
@ -510,7 +639,7 @@ extern "C"
|
||||
|
||||
// Blend it.
|
||||
unsigned int texoff = texx + (texy * work->texwidth);
|
||||
work->imgdata[imgoff] = blend_point(work->add_color, work->mult_color, work->texdata[texoff], work->imgdata[imgoff], work->blendfunc);
|
||||
work->imgdata[imgoff] = blend_point(work->add_color, work->mult_color, work->hsl_shift, work->texdata[texoff], work->imgdata[imgoff], work->blendfunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -533,6 +662,7 @@ extern "C"
|
||||
unsigned int maxy,
|
||||
floatcolor_t add_color,
|
||||
floatcolor_t mult_color,
|
||||
hslcolor_t hsl_shift,
|
||||
double xscale,
|
||||
double yscale,
|
||||
matrix_t inverse,
|
||||
@ -567,6 +697,7 @@ extern "C"
|
||||
work.inverse = inverse;
|
||||
work.add_color = add_color;
|
||||
work.mult_color = mult_color;
|
||||
work.hsl_shift = hsl_shift;
|
||||
work.blendfunc = blendfunc;
|
||||
work.aa_mode = aa_mode;
|
||||
work.use_perspective = use_perspective;
|
||||
@ -614,6 +745,7 @@ extern "C"
|
||||
work->inverse = inverse;
|
||||
work->add_color = add_color;
|
||||
work->mult_color = mult_color;
|
||||
work->hsl_shift = hsl_shift;
|
||||
work->blendfunc = blendfunc;
|
||||
work->thread = thread;
|
||||
work->aa_mode = aa_mode;
|
||||
|
@ -20,6 +20,7 @@ from .swf import (
|
||||
from .decompile import ByteCode
|
||||
from .types import (
|
||||
Color,
|
||||
HSL,
|
||||
Matrix,
|
||||
Point,
|
||||
Rectangle,
|
||||
@ -120,6 +121,7 @@ class PlacedObject:
|
||||
projection: int,
|
||||
mult_color: Color,
|
||||
add_color: Color,
|
||||
hsl_shift: HSL,
|
||||
blend: int,
|
||||
mask: Optional[Mask],
|
||||
) -> None:
|
||||
@ -130,6 +132,7 @@ class PlacedObject:
|
||||
self.projection = projection
|
||||
self.mult_color = mult_color
|
||||
self.add_color = add_color
|
||||
self.hsl_shift = hsl_shift
|
||||
self.blend = blend
|
||||
self.mask = mask
|
||||
self.visible: bool = True
|
||||
@ -164,6 +167,7 @@ class PlacedShape(PlacedObject):
|
||||
projection: int,
|
||||
mult_color: Color,
|
||||
add_color: Color,
|
||||
hsl_shift: HSL,
|
||||
blend: int,
|
||||
mask: Optional[Mask],
|
||||
source: RegisteredShape,
|
||||
@ -176,6 +180,7 @@ class PlacedShape(PlacedObject):
|
||||
projection,
|
||||
mult_color,
|
||||
add_color,
|
||||
hsl_shift,
|
||||
blend,
|
||||
mask,
|
||||
)
|
||||
@ -201,6 +206,7 @@ class PlacedClip(PlacedObject):
|
||||
projection: int,
|
||||
mult_color: Color,
|
||||
add_color: Color,
|
||||
hsl_shift: HSL,
|
||||
blend: int,
|
||||
mask: Optional[Mask],
|
||||
source: RegisteredClip,
|
||||
@ -213,6 +219,7 @@ class PlacedClip(PlacedObject):
|
||||
projection,
|
||||
mult_color,
|
||||
add_color,
|
||||
hsl_shift,
|
||||
blend,
|
||||
mask,
|
||||
)
|
||||
@ -375,6 +382,7 @@ class PlacedImage(PlacedObject):
|
||||
projection: int,
|
||||
mult_color: Color,
|
||||
add_color: Color,
|
||||
hsl_shift: HSL,
|
||||
blend: int,
|
||||
mask: Optional[Mask],
|
||||
source: RegisteredImage,
|
||||
@ -387,6 +395,7 @@ class PlacedImage(PlacedObject):
|
||||
projection,
|
||||
mult_color,
|
||||
add_color,
|
||||
hsl_shift,
|
||||
blend,
|
||||
mask,
|
||||
)
|
||||
@ -411,6 +420,7 @@ class PlacedDummy(PlacedObject):
|
||||
projection: int,
|
||||
mult_color: Color,
|
||||
add_color: Color,
|
||||
hsl_shift: HSL,
|
||||
blend: int,
|
||||
mask: Optional[Mask],
|
||||
source: RegisteredDummy,
|
||||
@ -423,6 +433,7 @@ class PlacedDummy(PlacedObject):
|
||||
projection,
|
||||
mult_color,
|
||||
add_color,
|
||||
hsl_shift,
|
||||
blend,
|
||||
mask,
|
||||
)
|
||||
@ -1059,6 +1070,7 @@ class AFPRenderer(VerboseOutput):
|
||||
if obj.object_id == tag.object_id and obj.depth == tag.depth:
|
||||
new_mult_color = tag.mult_color or obj.mult_color
|
||||
new_add_color = tag.add_color or obj.add_color
|
||||
new_hsl_shift = tag.hsl_shift or obj.hsl_shift
|
||||
new_transform = (
|
||||
obj.transform.update(tag.transform)
|
||||
if tag.transform is not None
|
||||
@ -1092,6 +1104,7 @@ class AFPRenderer(VerboseOutput):
|
||||
new_projection,
|
||||
new_mult_color,
|
||||
new_add_color,
|
||||
new_hsl_shift,
|
||||
new_blend,
|
||||
obj.mask,
|
||||
newobj,
|
||||
@ -1108,6 +1121,7 @@ class AFPRenderer(VerboseOutput):
|
||||
new_projection,
|
||||
new_mult_color,
|
||||
new_add_color,
|
||||
new_hsl_shift,
|
||||
new_blend,
|
||||
obj.mask,
|
||||
newobj,
|
||||
@ -1124,6 +1138,7 @@ class AFPRenderer(VerboseOutput):
|
||||
new_projection,
|
||||
new_mult_color,
|
||||
new_add_color,
|
||||
new_hsl_shift,
|
||||
new_blend,
|
||||
obj.mask,
|
||||
newobj,
|
||||
@ -1141,6 +1156,7 @@ class AFPRenderer(VerboseOutput):
|
||||
new_projection,
|
||||
new_mult_color,
|
||||
new_add_color,
|
||||
new_hsl_shift,
|
||||
new_blend,
|
||||
obj.mask,
|
||||
newobj,
|
||||
@ -1160,6 +1176,7 @@ class AFPRenderer(VerboseOutput):
|
||||
)
|
||||
obj.mult_color = new_mult_color
|
||||
obj.add_color = new_add_color
|
||||
obj.hsl_shift = new_hsl_shift
|
||||
obj.transform = new_transform
|
||||
obj.rotation_origin = new_rotation_origin
|
||||
obj.projection = new_projection
|
||||
@ -1194,6 +1211,7 @@ class AFPRenderer(VerboseOutput):
|
||||
tag.projection,
|
||||
tag.mult_color or Color(1.0, 1.0, 1.0, 1.0),
|
||||
tag.add_color or Color(0.0, 0.0, 0.0, 0.0),
|
||||
tag.hsl_shift or HSL(0.0, 0.0, 0.0),
|
||||
tag.blend or 0,
|
||||
None,
|
||||
newobj,
|
||||
@ -1212,6 +1230,7 @@ class AFPRenderer(VerboseOutput):
|
||||
tag.projection,
|
||||
tag.mult_color or Color(1.0, 1.0, 1.0, 1.0),
|
||||
tag.add_color or Color(0.0, 0.0, 0.0, 0.0),
|
||||
tag.hsl_shift or HSL(0.0, 0.0, 0.0),
|
||||
tag.blend or 0,
|
||||
None,
|
||||
newobj,
|
||||
@ -1229,6 +1248,7 @@ class AFPRenderer(VerboseOutput):
|
||||
tag.projection,
|
||||
tag.mult_color or Color(1.0, 1.0, 1.0, 1.0),
|
||||
tag.add_color or Color(0.0, 0.0, 0.0, 0.0),
|
||||
tag.hsl_shift or HSL(0.0, 0.0, 0.0),
|
||||
tag.blend or 0,
|
||||
None,
|
||||
newobj,
|
||||
@ -1258,6 +1278,7 @@ class AFPRenderer(VerboseOutput):
|
||||
tag.projection,
|
||||
tag.mult_color or Color(1.0, 1.0, 1.0, 1.0),
|
||||
tag.add_color or Color(0.0, 0.0, 0.0, 0.0),
|
||||
tag.hsl_shift or HSL(0.0, 0.0, 0.0),
|
||||
tag.blend or 0,
|
||||
None,
|
||||
newobj,
|
||||
@ -1386,6 +1407,7 @@ class AFPRenderer(VerboseOutput):
|
||||
),
|
||||
Color(0.0, 0.0, 0.0, 0.0),
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
HSL(0.0, 0.0, 0.0),
|
||||
Matrix.identity().translate(Point(mask.bounds.left, mask.bounds.top)),
|
||||
None,
|
||||
0,
|
||||
@ -1406,6 +1428,7 @@ class AFPRenderer(VerboseOutput):
|
||||
),
|
||||
Color(0.0, 0.0, 0.0, 0.0),
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
HSL(0.0, 0.0, 0.0),
|
||||
transform,
|
||||
None,
|
||||
257,
|
||||
@ -1424,6 +1447,7 @@ class AFPRenderer(VerboseOutput):
|
||||
),
|
||||
Color(0.0, 0.0, 0.0, 0.0),
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
HSL(0.0, 0.0, 0.0),
|
||||
transform,
|
||||
None,
|
||||
257,
|
||||
@ -1438,6 +1462,7 @@ class AFPRenderer(VerboseOutput):
|
||||
),
|
||||
Color(0.0, 0.0, 0.0, 0.0),
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
HSL(0.0, 0.0, 0.0),
|
||||
transform,
|
||||
self.__camera.center,
|
||||
self.__camera.focal_length,
|
||||
@ -1453,6 +1478,7 @@ class AFPRenderer(VerboseOutput):
|
||||
parent_mask.copy(),
|
||||
Color(0.0, 0.0, 0.0, 0.0),
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
HSL(0.0, 0.0, 0.0),
|
||||
Matrix.identity(),
|
||||
None,
|
||||
256,
|
||||
@ -1470,6 +1496,7 @@ class AFPRenderer(VerboseOutput):
|
||||
parent_mask: Image.Image,
|
||||
parent_mult_color: Color,
|
||||
parent_add_color: Color,
|
||||
parent_hsl_shift: HSL,
|
||||
parent_blend: int,
|
||||
only_depths: Optional[List[int]] = None,
|
||||
prefix: str = "",
|
||||
@ -1505,6 +1532,7 @@ class AFPRenderer(VerboseOutput):
|
||||
.multiply(parent_mult_color)
|
||||
.add(parent_add_color)
|
||||
)
|
||||
hsl_shift = renderable.hsl_shift or HSL(0.0, 0.0, 0.0).add(parent_hsl_shift)
|
||||
blend = renderable.blend or 0
|
||||
if parent_blend not in {0, 1, 2} and blend in {0, 1, 2}:
|
||||
blend = parent_blend
|
||||
@ -1541,6 +1569,7 @@ class AFPRenderer(VerboseOutput):
|
||||
mask,
|
||||
mult_color,
|
||||
add_color,
|
||||
hsl_shift,
|
||||
blend,
|
||||
only_depths=new_only_depths,
|
||||
prefix=prefix + " ",
|
||||
@ -1631,6 +1660,7 @@ class AFPRenderer(VerboseOutput):
|
||||
img,
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
transform,
|
||||
mask,
|
||||
blend,
|
||||
@ -1656,6 +1686,7 @@ class AFPRenderer(VerboseOutput):
|
||||
img,
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
transform,
|
||||
mask,
|
||||
blend,
|
||||
@ -1677,6 +1708,7 @@ class AFPRenderer(VerboseOutput):
|
||||
img,
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
transform,
|
||||
self.__camera.center,
|
||||
self.__camera.focal_length,
|
||||
@ -1699,6 +1731,7 @@ class AFPRenderer(VerboseOutput):
|
||||
img,
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
transform,
|
||||
mask,
|
||||
blend,
|
||||
@ -1717,6 +1750,7 @@ class AFPRenderer(VerboseOutput):
|
||||
img,
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
transform,
|
||||
mask,
|
||||
blend,
|
||||
@ -1731,6 +1765,7 @@ class AFPRenderer(VerboseOutput):
|
||||
img,
|
||||
add_color,
|
||||
mult_color,
|
||||
hsl_shift,
|
||||
transform,
|
||||
self.__camera.center,
|
||||
self.__camera.focal_length,
|
||||
@ -2051,6 +2086,7 @@ class AFPRenderer(VerboseOutput):
|
||||
AP2PlaceObjectTag.PROJECTION_AFFINE,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Color(0.0, 0.0, 0.0, 0.0),
|
||||
HSL(0.0, 0.0, 0.0),
|
||||
0,
|
||||
None,
|
||||
RegisteredClip(
|
||||
@ -2106,6 +2142,7 @@ class AFPRenderer(VerboseOutput):
|
||||
AP2PlaceObjectTag.PROJECTION_AFFINE,
|
||||
Color(1.0, 1.0, 1.0, 1.0),
|
||||
Color(0.0, 0.0, 0.0, 0.0),
|
||||
HSL(0.0, 0.0, 0.0),
|
||||
0,
|
||||
None,
|
||||
background_object,
|
||||
@ -2120,6 +2157,7 @@ class AFPRenderer(VerboseOutput):
|
||||
# These could possibly be overwritten from an external source of we wanted.
|
||||
actual_mult_color = Color(1.0, 1.0, 1.0, 1.0)
|
||||
actual_add_color = Color(0.0, 0.0, 0.0, 0.0)
|
||||
actual_hsl_shift = HSL(0.0, 0.0, 0.0)
|
||||
actual_blend = 0
|
||||
|
||||
max_frame: Optional[int] = None
|
||||
@ -2206,6 +2244,7 @@ class AFPRenderer(VerboseOutput):
|
||||
movie_mask,
|
||||
actual_mult_color,
|
||||
actual_add_color,
|
||||
actual_hsl_shift,
|
||||
actual_blend,
|
||||
only_depths=only_depths,
|
||||
)
|
||||
|
@ -8,6 +8,7 @@ from .decompile import ByteCode
|
||||
from .types import (
|
||||
Matrix,
|
||||
Color,
|
||||
HSL,
|
||||
Point,
|
||||
Rectangle,
|
||||
AP2Action,
|
||||
@ -323,6 +324,7 @@ class AP2PlaceObjectTag(Tag):
|
||||
projection: int,
|
||||
mult_color: Optional[Color],
|
||||
add_color: Optional[Color],
|
||||
hsl_shift: Optional[HSL],
|
||||
triggers: Dict[int, List[ByteCode]],
|
||||
unrecognized_options: bool,
|
||||
) -> None:
|
||||
@ -364,6 +366,9 @@ class AP2PlaceObjectTag(Tag):
|
||||
# If there is a color to add with the sprite/shape when drawing.
|
||||
self.add_color = add_color
|
||||
|
||||
# If there is a hue/saturation/lightness shift effect when drawing.
|
||||
self.hsl_shift = hsl_shift
|
||||
|
||||
# List of triggers for this object, and their respective bytecodes to execute when the trigger
|
||||
# fires.
|
||||
self.triggers = triggers
|
||||
@ -398,6 +403,9 @@ class AP2PlaceObjectTag(Tag):
|
||||
"add_color": self.add_color.as_dict(*args, **kwargs)
|
||||
if self.add_color is not None
|
||||
else None,
|
||||
"hsl_shift": self.hsl_shift.as_dict(*args, **kwargs)
|
||||
if self.hsl_shift
|
||||
else None,
|
||||
"triggers": {
|
||||
i: [b.as_dict(*args, **kwargs) for b in t]
|
||||
for (i, t) in self.triggers.items()
|
||||
@ -2007,18 +2015,29 @@ class SWF(VerboseOutput, TrackedCoverage):
|
||||
component="tags",
|
||||
)
|
||||
|
||||
# HSL shift data.
|
||||
hue: Optional[int] = None
|
||||
saturation: Optional[int] = None
|
||||
lightness: Optional[int] = None
|
||||
|
||||
if flags & 0x20000000:
|
||||
# TODO: Again, absolutely no idea, gets passed into a function and I don't see how its used.
|
||||
# Looks like Hue/Lightness/Saturation shift, matching after effects in the limits.
|
||||
# First value is degrees to shift the hue, second and third values I'm not sure if
|
||||
# its saturation then lightness or lightness then saturation but both have limits of
|
||||
# -100 to 100 in after effects and the file that I found with this option chooses
|
||||
# 0 for each.
|
||||
unhandled_flags &= ~0x20000000
|
||||
unk_a, unk_b, unk_c = struct.unpack(
|
||||
hue, saturation, lightness = struct.unpack(
|
||||
"<hbb", datachunk[running_pointer : (running_pointer + 4)]
|
||||
)
|
||||
self.add_coverage(dataoffset + running_pointer, 4)
|
||||
running_pointer += 4
|
||||
unrecognized_options = True
|
||||
|
||||
# TODO: Need to confirm whether 2 and 3 options are saturation and lightness or
|
||||
# lightness and saturation. Should be easy if we ever find an animation using either
|
||||
# of these values.
|
||||
self.vprint(
|
||||
f"{prefix} Unknown Data: {unk_a}, {unk_b}, {unk_c}",
|
||||
f"{prefix} HSL Shift: {hue}, {saturation}, {lightness}",
|
||||
component="tags",
|
||||
)
|
||||
|
||||
@ -2186,6 +2205,9 @@ class SWF(VerboseOutput, TrackedCoverage):
|
||||
projection=projection,
|
||||
mult_color=multcolor if color_information else None,
|
||||
add_color=addcolor if color_information else None,
|
||||
hsl_shift=HSL(hue / 360.0, saturation / 100.0, lightness / 100.0)
|
||||
if hue is not None
|
||||
else None,
|
||||
triggers=bytecodes,
|
||||
unrecognized_options=unrecognized_options,
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
from .generic import Matrix, Color, Point, Rectangle
|
||||
from .generic import Matrix, Color, HSL, Point, Rectangle
|
||||
from .ap2 import (
|
||||
AP2Tag,
|
||||
AP2Action,
|
||||
@ -93,6 +93,7 @@ from .aa import AAMode
|
||||
__all__ = [
|
||||
"Matrix",
|
||||
"Color",
|
||||
"HSL",
|
||||
"Point",
|
||||
"Rectangle",
|
||||
"AP2Tag",
|
||||
|
@ -1,3 +1,4 @@
|
||||
import colorsys
|
||||
import math
|
||||
|
||||
from typing import Any, Dict, List, Tuple
|
||||
@ -37,6 +38,10 @@ class Color:
|
||||
a=self.a + other.a,
|
||||
)
|
||||
|
||||
def as_hsl(self) -> "HSL":
|
||||
h, l, s = colorsys.rgb_to_hls(self.r, self.g, self.b)
|
||||
return HSL(h, s, l)
|
||||
|
||||
def as_tuple(self) -> Tuple[int, int, int, int]:
|
||||
return (
|
||||
int(self.r * 255),
|
||||
@ -49,6 +54,61 @@ class Color:
|
||||
return f"r: {round(self.r, 5)}, g: {round(self.g, 5)}, b: {round(self.b, 5)}, a: {round(self.a, 5)}"
|
||||
|
||||
|
||||
class HSL:
|
||||
# A hue/saturation/lightness color shift, represented as a series of floats between
|
||||
# -1.0 and 1.0. The hue represents a percentage along the polar coordinates,
|
||||
# 0.0 being 0 degrees, -1.0 being -360 degrees and 1.0 being 360 degrees. The
|
||||
# saturation and lightness values representing actual normalized percentages where
|
||||
# a lightness of 100 would be written as 1.0.
|
||||
def __init__(self, h: float, s: float, l: float) -> None:
|
||||
self.h = h
|
||||
self.s = s
|
||||
self.l = l
|
||||
|
||||
@property
|
||||
def is_identity(self) -> bool:
|
||||
return self.h == 0.0 and self.s == 0.0 and self.l == 0.0
|
||||
|
||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||
return {
|
||||
"h": self.h,
|
||||
"s": self.s,
|
||||
"l": self.l,
|
||||
}
|
||||
|
||||
def add(self, other: "HSL") -> "HSL":
|
||||
# Not entirely sure this is correct, but we don't have any animations to compare to.
|
||||
# Basically, not sure if HSL colorspace is linear in this way, but as long as no
|
||||
# animations try to stack multiple HSL shift effects this shouldn't matter.
|
||||
return HSL(h=self.h + other.h, s=self.s + other.s, l=self.l + other.l)
|
||||
|
||||
def as_rgb(self) -> "Color":
|
||||
h = self.h
|
||||
while h < 0.0:
|
||||
h += 1.0
|
||||
while h > 1.0:
|
||||
h -= 1.0
|
||||
|
||||
s = min(max(self.s, 0.0), 1.0)
|
||||
l = min(max(self.l, 0.0), 1.0)
|
||||
r, g, b = colorsys.hls_to_rgb(h, l, s)
|
||||
return Color(r, g, b, 1.0)
|
||||
|
||||
def as_tuple(self) -> Tuple[int, int, int]:
|
||||
h = int(self.h * 360)
|
||||
while h < 0:
|
||||
h += 360
|
||||
while h > 360:
|
||||
h -= 360
|
||||
|
||||
s = min(max(int(self.s), -100), 100)
|
||||
l = min(max(int(self.l), -100), 100)
|
||||
return (h, s, l)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"h: {round(self.h, 5)}, s: {round(self.s, 5)}, l: {round(self.l, 5)}"
|
||||
|
||||
|
||||
class Point:
|
||||
# A simple 3D point. For ease of construction, the Z can be left out
|
||||
# at which point it is assumed to be zero.
|
||||
|
@ -1,3 +1,3 @@
|
||||
#! /bin/bash
|
||||
|
||||
flake8 bemani/ --ignore E203,E501,E252,W503,W504,B006,B008,B009 | grep -v "migrations\/"
|
||||
flake8 bemani/ --ignore E203,E501,E252,E741,W503,W504,B006,B008,B009 | grep -v "migrations\/"
|
||||
|
Loading…
x
Reference in New Issue
Block a user