Fix anti-aliasing of rectangles so that they are always crisp.
This commit is contained in:
parent
ec3453ae54
commit
73a36e17c2
@ -1,9 +1,9 @@
|
||||
import multiprocessing
|
||||
import signal
|
||||
from PIL import Image # type: ignore
|
||||
from typing import Any, Callable, List, Optional, Sequence, Tuple, Union
|
||||
from typing import Any, Callable, List, Optional, Sequence, Union
|
||||
|
||||
from ..types import Color, Matrix, Point
|
||||
from ..types import Color, Matrix, Point, AAMode
|
||||
from .perspective import perspective_calculate
|
||||
|
||||
|
||||
@ -201,14 +201,14 @@ def pixel_renderer(
|
||||
texheight: int,
|
||||
xscale: float,
|
||||
yscale: float,
|
||||
callback: Callable[[Point], Tuple[Optional[Point], bool]],
|
||||
callback: Callable[[Point], Optional[Point]],
|
||||
add_color: Color,
|
||||
mult_color: Color,
|
||||
blendfunc: int,
|
||||
imgbytes: Union[bytes, bytearray],
|
||||
texbytes: Union[bytes, bytearray],
|
||||
maskbytes: Optional[Union[bytes, bytearray]],
|
||||
enable_aa: bool,
|
||||
aa_mode: int,
|
||||
) -> Sequence[int]:
|
||||
# Determine offset
|
||||
maskoff = imgx + (imgy * imgwidth)
|
||||
@ -218,7 +218,7 @@ def pixel_renderer(
|
||||
# This pixel is masked off!
|
||||
return imgbytes[imgoff:(imgoff + 4)]
|
||||
|
||||
if enable_aa:
|
||||
if aa_mode != AAMode.NONE:
|
||||
r = 0
|
||||
g = 0
|
||||
b = 0
|
||||
@ -229,17 +229,21 @@ def pixel_renderer(
|
||||
# Essentially what we're doing here is calculating the scale, clamping it at 1.0 as the
|
||||
# minimum and then setting the AA sample swing accordingly. This has the effect of anti-aliasing
|
||||
# scaled up images a bit softer than would otherwise be achieved.
|
||||
xswing = 0.5 * max(1.0, xscale)
|
||||
yswing = 0.5 * max(1.0, yscale)
|
||||
if aa_mode == AAMode.UNSCALED_SSAA_ONLY:
|
||||
xswing = 0.5
|
||||
yswing = 0.5
|
||||
else:
|
||||
xswing = 0.5 * max(1.0, xscale)
|
||||
yswing = 0.5 * max(1.0, yscale)
|
||||
|
||||
xpoints = [0.5 - xswing, 0.5 - (xswing / 2.0), 0.5, 0.5 + (xswing / 2.0), 0.5 + xswing]
|
||||
ypoints = [0.5 - yswing, 0.5 - (yswing / 2.0), 0.5, 0.5 + (yswing / 2.0), 0.5 + yswing]
|
||||
|
||||
# First, figure out if we can use bilinear resampling.
|
||||
bilinear = False
|
||||
if xscale >= 1.0 and yscale >= 1.0:
|
||||
aaloc, enable_bilinear = callback(Point(imgx + 0.5, imgy + 0.5))
|
||||
if aaloc is not None and enable_bilinear:
|
||||
if aa_mode == AAMode.SSAA_OR_BILINEAR and xscale >= 1.0 and yscale >= 1.0:
|
||||
aaloc = callback(Point(imgx + 0.5, imgy + 0.5))
|
||||
if aaloc is not None:
|
||||
aax, aay, _ = aaloc.as_tuple()
|
||||
if not (aax <= 0 or aay <= 0 or aax >= (texwidth - 1) or aay >= (texheight - 1)):
|
||||
bilinear = True
|
||||
@ -247,7 +251,7 @@ def pixel_renderer(
|
||||
# Now perform the desired AA operation.
|
||||
if bilinear:
|
||||
# Calculate the pixel we're after, and what percentage into the pixel we are.
|
||||
texloc, _ = callback(Point(imgx + 0.5, imgy + 0.5))
|
||||
texloc = callback(Point(imgx + 0.5, imgy + 0.5))
|
||||
if texloc is None:
|
||||
raise Exception("Logic error!")
|
||||
aax, aay, _ = texloc.as_tuple()
|
||||
@ -293,7 +297,7 @@ def pixel_renderer(
|
||||
else:
|
||||
for addy in ypoints:
|
||||
for addx in xpoints:
|
||||
texloc, _ = callback(Point(imgx + addx, imgy + addy))
|
||||
texloc = callback(Point(imgx + addx, imgy + addy))
|
||||
denom += 1
|
||||
|
||||
if texloc is None:
|
||||
@ -340,7 +344,7 @@ def pixel_renderer(
|
||||
return blend_point(add_color, mult_color, average, imgbytes[imgoff:(imgoff + 4)], blendfunc)
|
||||
else:
|
||||
# Calculate what texture pixel data goes here.
|
||||
texloc, _ = callback(Point(imgx + 0.5, imgy + 0.5))
|
||||
texloc = callback(Point(imgx + 0.5, imgy + 0.5))
|
||||
if texloc is None:
|
||||
return imgbytes[imgoff:(imgoff + 4)]
|
||||
|
||||
@ -370,7 +374,7 @@ def affine_line_renderer(
|
||||
imgbytes: Union[bytes, bytearray],
|
||||
texbytes: Union[bytes, bytearray],
|
||||
maskbytes: Optional[Union[bytes, bytearray]],
|
||||
enable_aa: bool,
|
||||
aa_mode: int,
|
||||
) -> None:
|
||||
while True:
|
||||
imgy = work.get()
|
||||
@ -392,14 +396,14 @@ def affine_line_renderer(
|
||||
texheight,
|
||||
1.0 / inverse.xscale,
|
||||
1.0 / inverse.yscale,
|
||||
lambda point: (inverse.multiply_point(point), True),
|
||||
lambda point: inverse.multiply_point(point),
|
||||
add_color,
|
||||
mult_color,
|
||||
blendfunc,
|
||||
imgbytes,
|
||||
texbytes,
|
||||
maskbytes,
|
||||
enable_aa,
|
||||
aa_mode,
|
||||
)
|
||||
|
||||
results.put((imgy, bytes(rowbytes)))
|
||||
@ -414,7 +418,7 @@ def affine_composite(
|
||||
blendfunc: int,
|
||||
texture: Image.Image,
|
||||
single_threaded: bool = False,
|
||||
enable_aa: bool = True,
|
||||
aa_mode: int = AAMode.SSAA_OR_BILINEAR,
|
||||
) -> Image.Image:
|
||||
# Calculate the inverse so we can map canvas space back to texture space.
|
||||
try:
|
||||
@ -477,14 +481,14 @@ def affine_composite(
|
||||
texheight,
|
||||
1.0 / inverse.xscale,
|
||||
1.0 / inverse.yscale,
|
||||
lambda point: (inverse.multiply_point(point), True),
|
||||
lambda point: inverse.multiply_point(point),
|
||||
add_color,
|
||||
mult_color,
|
||||
blendfunc,
|
||||
imgbytes,
|
||||
texbytes,
|
||||
maskbytes,
|
||||
enable_aa,
|
||||
aa_mode,
|
||||
)
|
||||
|
||||
img = Image.frombytes('RGBA', (imgwidth, imgheight), bytes(imgbytes))
|
||||
@ -529,7 +533,7 @@ def affine_composite(
|
||||
imgbytes,
|
||||
texbytes,
|
||||
maskbytes,
|
||||
enable_aa,
|
||||
aa_mode,
|
||||
),
|
||||
)
|
||||
procs.append(proc)
|
||||
@ -581,15 +585,15 @@ def perspective_line_renderer(
|
||||
imgbytes: Union[bytes, bytearray],
|
||||
texbytes: Union[bytes, bytearray],
|
||||
maskbytes: Optional[Union[bytes, bytearray]],
|
||||
enable_aa: bool,
|
||||
aa_mode: int,
|
||||
) -> None:
|
||||
def perspective_inverse(imgpoint: Point) -> Tuple[Optional[Point], bool]:
|
||||
def perspective_inverse(imgpoint: Point) -> Optional[Point]:
|
||||
# Calculate the texture coordinate with our perspective interpolation.
|
||||
texdiv = inverse.multiply_point(imgpoint)
|
||||
if texdiv.z <= 0.0:
|
||||
return None, False
|
||||
return None
|
||||
|
||||
return Point(texdiv.x / texdiv.z, texdiv.y / texdiv.z), False
|
||||
return Point(texdiv.x / texdiv.z, texdiv.y / texdiv.z)
|
||||
|
||||
while True:
|
||||
imgy = work.get()
|
||||
@ -618,7 +622,7 @@ def perspective_line_renderer(
|
||||
imgbytes,
|
||||
texbytes,
|
||||
maskbytes,
|
||||
enable_aa,
|
||||
aa_mode,
|
||||
)
|
||||
|
||||
results.put((imgy, bytes(rowbytes)))
|
||||
@ -635,7 +639,7 @@ def perspective_composite(
|
||||
blendfunc: int,
|
||||
texture: Image.Image,
|
||||
single_threaded: bool = False,
|
||||
enable_aa: bool = True,
|
||||
aa_mode: int = AAMode.SSAA_ONLY,
|
||||
) -> Image.Image:
|
||||
# Warn if we have an unsupported blend.
|
||||
if blendfunc not in {0, 1, 2, 3, 8, 9, 70, 256, 257}:
|
||||
@ -664,13 +668,13 @@ def perspective_composite(
|
||||
else:
|
||||
maskbytes = None
|
||||
|
||||
def perspective_inverse(imgpoint: Point) -> Tuple[Optional[Point], bool]:
|
||||
def perspective_inverse(imgpoint: Point) -> Optional[Point]:
|
||||
# Calculate the texture coordinate with our perspective interpolation.
|
||||
texdiv = inverse_matrix.multiply_point(imgpoint)
|
||||
if texdiv.z <= 0.0:
|
||||
return None, False
|
||||
return None
|
||||
|
||||
return Point(texdiv.x / texdiv.z, texdiv.y / texdiv.z), False
|
||||
return Point(texdiv.x / texdiv.z, texdiv.y / texdiv.z)
|
||||
|
||||
cores = multiprocessing.cpu_count()
|
||||
if single_threaded or cores < 2:
|
||||
@ -703,7 +707,7 @@ def perspective_composite(
|
||||
imgbytes,
|
||||
texbytes,
|
||||
maskbytes,
|
||||
enable_aa,
|
||||
aa_mode,
|
||||
)
|
||||
|
||||
img = Image.frombytes('RGBA', (imgwidth, imgheight), bytes(imgbytes))
|
||||
@ -750,7 +754,7 @@ def perspective_composite(
|
||||
imgbytes,
|
||||
texbytes,
|
||||
maskbytes,
|
||||
enable_aa,
|
||||
aa_mode,
|
||||
),
|
||||
)
|
||||
procs.append(proc)
|
||||
|
@ -13,7 +13,7 @@ def affine_composite(
|
||||
blendfunc: int,
|
||||
texture: Image.Image,
|
||||
single_threaded: bool = ...,
|
||||
enable_aa: bool = ...,
|
||||
aa_mode: int = ...
|
||||
) -> Image.Image:
|
||||
...
|
||||
|
||||
@ -29,6 +29,6 @@ def perspective_composite(
|
||||
blendfunc: int,
|
||||
texture: Image.Image,
|
||||
single_threaded: bool = ...,
|
||||
enable_aa: bool = ...,
|
||||
aa_mode: int = ...
|
||||
) -> Image.Image:
|
||||
...
|
||||
|
@ -2,7 +2,7 @@ import multiprocessing
|
||||
from PIL import Image # type: ignore
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from ..types import Color, Matrix, Point
|
||||
from ..types import Color, Matrix, Point, AAMode
|
||||
from .perspective import perspective_calculate
|
||||
|
||||
cdef extern struct floatcolor_t:
|
||||
@ -45,7 +45,7 @@ cdef extern int composite_fast(
|
||||
unsigned int texwidth,
|
||||
unsigned int texheight,
|
||||
unsigned int threads,
|
||||
unsigned int enable_aa
|
||||
unsigned int aa_mode
|
||||
)
|
||||
|
||||
def affine_composite(
|
||||
@ -57,7 +57,7 @@ def affine_composite(
|
||||
blendfunc: int,
|
||||
texture: Image.Image,
|
||||
single_threaded: bool = False,
|
||||
enable_aa: bool = True,
|
||||
aa_mode: int = AAMode.SSAA_OR_BILINEAR,
|
||||
) -> Image.Image:
|
||||
# Calculate the inverse so we can map canvas space back to texture space.
|
||||
try:
|
||||
@ -141,7 +141,7 @@ def affine_composite(
|
||||
texwidth,
|
||||
texheight,
|
||||
threads,
|
||||
1 if enable_aa else 0,
|
||||
aa_mode,
|
||||
)
|
||||
if errors != 0:
|
||||
raise Exception("Error raised in C++!")
|
||||
@ -164,7 +164,7 @@ def perspective_composite(
|
||||
blendfunc: int,
|
||||
texture: Image.Image,
|
||||
single_threaded: bool = False,
|
||||
enable_aa: bool = True,
|
||||
aa_mode: int = AAMode.SSAA_ONLY,
|
||||
) -> Image.Image:
|
||||
if blendfunc not in {0, 1, 2, 3, 8, 9, 70, 256, 257}:
|
||||
print(f"WARNING: Unsupported blend {blendfunc}")
|
||||
@ -229,7 +229,7 @@ def perspective_composite(
|
||||
texwidth,
|
||||
texheight,
|
||||
threads,
|
||||
1 if enable_aa else 0,
|
||||
aa_mode,
|
||||
)
|
||||
if errors != 0:
|
||||
raise Exception("Error raised in C++!")
|
||||
|
@ -5,6 +5,11 @@
|
||||
|
||||
#define MIN_THREAD_WORK 10
|
||||
|
||||
#define AA_MODE_NONE 0
|
||||
#define AA_MODE_UNSCALED_SSAA_ONLY 1
|
||||
#define AA_MODE_SSAA_ONLY 2
|
||||
#define AA_MODE_SSAA_OR_BILINEAR 3
|
||||
|
||||
extern "C"
|
||||
{
|
||||
typedef struct intcolor {
|
||||
@ -77,7 +82,7 @@ extern "C"
|
||||
floatcolor_t mult_color;
|
||||
int blendfunc;
|
||||
pthread_t *thread;
|
||||
int enable_aa;
|
||||
int aa_mode;
|
||||
} work_t;
|
||||
|
||||
inline unsigned char clamp(float color) {
|
||||
@ -265,8 +270,16 @@ extern "C"
|
||||
// costs us almost nothing. Essentially what we're doing here is calculating the scale, clamping it at 1.0 as the
|
||||
// minimum and then setting the AA sample swing accordingly. This has the effect of anti-aliasing scaled up images
|
||||
// a bit softer than would otherwise be achieved.
|
||||
float xswing = 0.5 * fmax(1.0, work->xscale);
|
||||
float yswing = 0.5 * fmax(1.0, work->yscale);
|
||||
float xswing;
|
||||
float yswing;
|
||||
|
||||
if (work->aa_mode == AA_MODE_UNSCALED_SSAA_ONLY) {
|
||||
xswing = 0.5;
|
||||
yswing = 0.5;
|
||||
} else {
|
||||
xswing = 0.5 * fmax(1.0, work->xscale);
|
||||
yswing = 0.5 * fmax(1.0, work->yscale);
|
||||
}
|
||||
|
||||
for (unsigned int imgy = work->miny; imgy < work->maxy; imgy++) {
|
||||
for (unsigned int imgx = work->minx; imgx < work->maxx; imgx++) {
|
||||
@ -280,7 +293,7 @@ extern "C"
|
||||
}
|
||||
|
||||
// Blend for simple anti-aliasing.
|
||||
if (work->enable_aa) {
|
||||
if (work->aa_mode != AA_MODE_NONE) {
|
||||
// Calculate what texture pixel data goes here.
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
@ -292,7 +305,7 @@ extern "C"
|
||||
// First, figure out if we can use bilinear resampling. Bilinear seems to look
|
||||
// awful on perspective transforms, so disable it for all of them.
|
||||
int bilinear = 0;
|
||||
if (!work->use_perspective && work->xscale >= 1.0 && work->yscale >= 1.0) {
|
||||
if (work->aa_mode == AA_MODE_SSAA_OR_BILINEAR && work->xscale >= 1.0 && work->yscale >= 1.0) {
|
||||
point_t aaloc = work->inverse.multiply_point((point_t){(float)(imgx + 0.5), (float)(imgy + 0.5)});
|
||||
int aax = aaloc.x;
|
||||
int aay = aaloc.y;
|
||||
@ -503,7 +516,7 @@ extern "C"
|
||||
unsigned int texwidth,
|
||||
unsigned int texheight,
|
||||
unsigned int threads,
|
||||
unsigned int enable_aa
|
||||
unsigned int aa_mode
|
||||
) {
|
||||
// Cast to a usable type.
|
||||
intcolor_t *imgdata = (intcolor_t *)imgbytes;
|
||||
@ -528,7 +541,7 @@ extern "C"
|
||||
work.add_color = add_color;
|
||||
work.mult_color = mult_color;
|
||||
work.blendfunc = blendfunc;
|
||||
work.enable_aa = enable_aa;
|
||||
work.aa_mode = aa_mode;
|
||||
work.use_perspective = use_perspective;
|
||||
|
||||
chunk_composite_fast(&work);
|
||||
@ -575,7 +588,7 @@ extern "C"
|
||||
work->mult_color = mult_color;
|
||||
work->blendfunc = blendfunc;
|
||||
work->thread = thread;
|
||||
work->enable_aa = enable_aa;
|
||||
work->aa_mode = aa_mode;
|
||||
work->use_perspective = use_perspective;
|
||||
|
||||
if (me)
|
||||
|
@ -22,6 +22,7 @@ from .types import (
|
||||
Matrix,
|
||||
Point,
|
||||
Rectangle,
|
||||
AAMode,
|
||||
AP2Trigger,
|
||||
AP2Action,
|
||||
PushAction,
|
||||
@ -1046,7 +1047,7 @@ class AFPRenderer(VerboseOutput):
|
||||
257,
|
||||
mask.rectangle,
|
||||
single_threaded=self.__single_threaded,
|
||||
enable_aa=False,
|
||||
aa_mode=AAMode.NONE,
|
||||
)
|
||||
elif projection == AP2PlaceObjectTag.PROJECTION_PERSPECTIVE:
|
||||
if self.__camera is None:
|
||||
@ -1060,7 +1061,7 @@ class AFPRenderer(VerboseOutput):
|
||||
257,
|
||||
mask.rectangle,
|
||||
single_threaded=self.__single_threaded,
|
||||
enable_aa=False,
|
||||
aa_mode=AAMode.NONE,
|
||||
)
|
||||
else:
|
||||
calculated_mask = perspective_composite(
|
||||
@ -1074,7 +1075,7 @@ class AFPRenderer(VerboseOutput):
|
||||
257,
|
||||
mask.rectangle,
|
||||
single_threaded=self.__single_threaded,
|
||||
enable_aa=False,
|
||||
aa_mode=AAMode.NONE,
|
||||
)
|
||||
|
||||
# Composite it onto the current mask.
|
||||
@ -1087,7 +1088,7 @@ class AFPRenderer(VerboseOutput):
|
||||
256,
|
||||
calculated_mask,
|
||||
single_threaded=self.__single_threaded,
|
||||
enable_aa=False,
|
||||
aa_mode=AAMode.NONE,
|
||||
)
|
||||
|
||||
def __render_object(
|
||||
@ -1162,6 +1163,7 @@ class AFPRenderer(VerboseOutput):
|
||||
print("WARNING: Unhandled UV coordinate color!")
|
||||
|
||||
texture = None
|
||||
rectangle = False
|
||||
if params.flags & 0x2:
|
||||
# We need to look up the texture for this.
|
||||
if params.region not in self.textures:
|
||||
@ -1203,9 +1205,15 @@ class AFPRenderer(VerboseOutput):
|
||||
|
||||
shape.rectangle = Image.new('RGBA', (int(right - left), int(bottom - top)), (params.blend.as_tuple()))
|
||||
texture = shape.rectangle
|
||||
rectangle = True
|
||||
|
||||
if texture is not None:
|
||||
if projection == AP2PlaceObjectTag.PROJECTION_AFFINE:
|
||||
if self.__enable_aa:
|
||||
aamode = AAMode.UNSCALED_SSAA_ONLY if rectangle else AAMode.SSAA_OR_BILINEAR
|
||||
else:
|
||||
aamode = AAMode.NONE
|
||||
|
||||
img = affine_composite(
|
||||
img,
|
||||
add_color,
|
||||
@ -1215,10 +1223,15 @@ class AFPRenderer(VerboseOutput):
|
||||
blend,
|
||||
texture,
|
||||
single_threaded=self.__single_threaded,
|
||||
enable_aa=self.__enable_aa,
|
||||
aa_mode=aamode,
|
||||
)
|
||||
elif projection == AP2PlaceObjectTag.PROJECTION_PERSPECTIVE:
|
||||
if self.__camera is None:
|
||||
if self.__enable_aa:
|
||||
aamode = AAMode.UNSCALED_SSAA_ONLY if rectangle else AAMode.SSAA_OR_BILINEAR
|
||||
else:
|
||||
aamode = AAMode.NONE
|
||||
|
||||
print("WARNING: Element requests perspective projection but no camera exists!")
|
||||
img = affine_composite(
|
||||
img,
|
||||
@ -1229,9 +1242,14 @@ class AFPRenderer(VerboseOutput):
|
||||
blend,
|
||||
texture,
|
||||
single_threaded=self.__single_threaded,
|
||||
enable_aa=self.__enable_aa,
|
||||
aa_mode=aamode,
|
||||
)
|
||||
else:
|
||||
if self.__enable_aa:
|
||||
aamode = AAMode.UNSCALED_SSAA_ONLY if rectangle else AAMode.SSAA_ONLY
|
||||
else:
|
||||
aamode = AAMode.NONE
|
||||
|
||||
img = perspective_composite(
|
||||
img,
|
||||
add_color,
|
||||
@ -1243,7 +1261,7 @@ class AFPRenderer(VerboseOutput):
|
||||
blend,
|
||||
texture,
|
||||
single_threaded=self.__single_threaded,
|
||||
enable_aa=self.__enable_aa,
|
||||
aa_mode=aamode,
|
||||
)
|
||||
|
||||
elif isinstance(renderable, PlacedImage):
|
||||
@ -1263,7 +1281,7 @@ class AFPRenderer(VerboseOutput):
|
||||
blend,
|
||||
texture,
|
||||
single_threaded=self.__single_threaded,
|
||||
enable_aa=self.__enable_aa,
|
||||
aa_mode=AAMode.SSAA_OR_BILINEAR if self.__enable_aa else AAMode.NONE,
|
||||
)
|
||||
elif projection == AP2PlaceObjectTag.PROJECTION_PERSPECTIVE:
|
||||
if self.__camera is None:
|
||||
@ -1277,7 +1295,7 @@ class AFPRenderer(VerboseOutput):
|
||||
blend,
|
||||
texture,
|
||||
single_threaded=self.__single_threaded,
|
||||
enable_aa=self.__enable_aa,
|
||||
aa_mode=AAMode.SSAA_OR_BILINEAR if self.__enable_aa else AAMode.NONE,
|
||||
)
|
||||
else:
|
||||
img = perspective_composite(
|
||||
@ -1291,7 +1309,7 @@ class AFPRenderer(VerboseOutput):
|
||||
blend,
|
||||
texture,
|
||||
single_threaded=self.__single_threaded,
|
||||
enable_aa=self.__enable_aa,
|
||||
aa_mode=AAMode.SSAA_ONLY if self.__enable_aa else AAMode.NONE,
|
||||
)
|
||||
elif isinstance(renderable, PlacedDummy):
|
||||
# Nothing to do!
|
||||
|
@ -87,6 +87,7 @@ from .statement import (
|
||||
AndIf,
|
||||
OrIf,
|
||||
)
|
||||
from .aa import AAMode
|
||||
|
||||
|
||||
__all__ = [
|
||||
@ -176,4 +177,5 @@ __all__ = [
|
||||
'GetURL2Action',
|
||||
'StartDragAction',
|
||||
'DefineFunction2Action',
|
||||
'AAMode',
|
||||
]
|
||||
|
8
bemani/format/afp/types/aa.py
Normal file
8
bemani/format/afp/types/aa.py
Normal file
@ -0,0 +1,8 @@
|
||||
from typing_extensions import Final
|
||||
|
||||
|
||||
class AAMode:
|
||||
NONE: Final[int] = 0
|
||||
UNSCALED_SSAA_ONLY: Final[int] = 1
|
||||
SSAA_ONLY: Final[int] = 2
|
||||
SSAA_OR_BILINEAR: Final[int] = 3
|
Loading…
Reference in New Issue
Block a user