Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion arcade/draw/rect.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,20 @@ def draw_texture_rect(
ctx.disable(ctx.BLEND)

atlas = atlas or ctx.default_atlas
program = ctx.sprite_program_single

texture_id, _ = atlas.add(texture)
if pixelated:
atlas.texture.filter = gl.NEAREST, gl.NEAREST
program.set_uniform_safe("uv_offset_bias", 0.0)
else:
atlas.texture.filter = gl.LINEAR, gl.LINEAR
program.set_uniform_safe("uv_offset_bias", 1.0)

atlas.texture.use(unit=0)
atlas.use_uv_texture(unit=1)

geometry = ctx.geometry_empty
program = ctx.sprite_program_single
program["pos"] = rect.center_x, rect.center_y, 0
program["color"] = color.normalized
program["size"] = rect.width, rect.height
Expand Down
6 changes: 0 additions & 6 deletions arcade/resources/system/shaders/gui/nine_patch_gs.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,6 @@ void main() {
vec2 uv0, uv1, uv2, uv3;
vec2 atlas_size = vec2(textureSize(sprite_texture, 0));
getSpriteUVs(uv_texture, int(texture_id), uv0, uv1, uv2, uv3);
// TODO: Do center pixel interpolation. Revert by 0.5 pixels for now
vec2 half_px = 0.5 / atlas_size;
uv0 -= half_px;
uv1 += vec2(half_px.x, -half_px.y);
uv2 += vec2(-half_px.x, half_px.y);
uv3 += half_px;

// Local corner offsets in pixels
float left = start.x;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ uniform WindowBlock {
mat4 view;
} window;

// Texture atlas
uniform sampler2D sprite_texture;
// Texture containing UVs for the entire atlas
uniform sampler2D uv_texture;
// How much half-pixel offset to apply to the UVs.
// 0.0 is no offset, 1.0 is half a pixel offset
uniform float uv_offset_bias;

in float v_angle[];
in vec4 v_color[];
Expand Down Expand Up @@ -49,11 +55,26 @@ void main() {
vec2 uv0, uv1, uv2, uv3;
getSpriteUVs(uv_texture, int(v_texture[0]), uv0, uv1, uv2, uv3);

// Apply half pixel offset modified by bias.
// What bias to set depends on the texture filtering mode.
// Linear can have 1.0 bias while nearest should have 0.0 (unless scale is 1:1)
// uvs (
// 0.0, 0.0,
// 1.0, 0.0,
// 0.0, 1.0,
// 1.0, 1.0
// )
vec2 hp = 0.5 / textureSize(sprite_texture, 0) * uv_offset_bias;
uv0 += hp;
uv1 += vec2(-hp.x, hp.y);
uv2 += vec2(hp.x, -hp.y);
uv3 += -hp;

// Set the out color for all vertices
gs_color = v_color[0];
// Upper left
gl_Position = mvp * vec4(rot * vec2(-hsize.x, hsize.y) + center.xy, center.z, 1.0);
gs_uv = uv0;
gs_uv = uv0;
EmitVertex();

// lower left
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#version 330

// Texture atlas
uniform sampler2D sprite_texture;
// Global color set on the sprite list
uniform vec4 spritelist_color;

in vec2 gs_uv;
Expand All @@ -11,6 +13,7 @@ out vec4 f_color;
void main() {
vec4 basecolor = texture(sprite_texture, gs_uv);
basecolor *= gs_color * spritelist_color;
// Alpha test
if (basecolor.a == 0.0) {
discard;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ uniform WindowBlock {
mat4 view;
} window;

// Texture atlas
uniform sampler2D sprite_texture;
// Texture containing UVs for the entire atlas
uniform sampler2D uv_texture;
// How much half-pixel offset to apply to the UVs.
// 0.0 is no offset, 1.0 is half a pixel offset
uniform float uv_offset_bias;

in float v_angle[];
in vec4 v_color[];
Expand All @@ -36,6 +42,21 @@ void main() {
vec2 uv0, uv1, uv2, uv3;
getSpriteUVs(uv_texture, int(v_texture[0]), uv0, uv1, uv2, uv3);

// Apply half pixel offset modified by bias.
// What bias to set depends on the texture filtering mode.
// Linear can have 1.0 bias while nearest should have 0.0 (unless scale is 1:1)
// uvs (
// 0.0, 0.0,
// 1.0, 0.0,
// 0.0, 1.0,
// 1.0, 1.0
// )
vec2 hp = 0.5 / textureSize(sprite_texture, 0) * uv_offset_bias;
uv0 += hp;
uv1 += vec2(-hp.x, hp.y);
uv2 += vec2(hp.x, -hp.y);
uv3 += -hp;

// Set the out color for all vertices
gs_color = v_color[0];
// Upper left
Expand Down
8 changes: 8 additions & 0 deletions arcade/sprite_list/sprite_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,14 @@ def draw(

self.program["spritelist_color"] = self._color

# Control center pixel interpolation:
# 0.0 = raw interpolation using texture corners
# 1.0 = center pixel interpolation
if self.ctx.NEAREST in atlas_texture.filter:
self.program.set_uniform_safe("uv_offset_bias", 0.0)
else:
self.program.set_uniform_safe("uv_offset_bias", 1.0)

atlas_texture.use(0)
atlas.use_uv_texture(1)
if not self._geometry:
Expand Down
19 changes: 9 additions & 10 deletions arcade/texture_atlas/region.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,26 +89,25 @@ def __init__(
# Width and height
_width = self.width / atlas.width
_height = self.height / atlas.height
# Half pixel correction
hp_x, hp_y = 0.5 / atlas.width, 0.5 / atlas.height

# Upper left corner. Note that are mapping the texture upside down and corners
# are named as from the vertex position point of view.
ul_x, ul_y = self.x / atlas.width, self.y / atlas.height

# upper_left, upper_right, lower_left, lower_right
self.texture_coordinates = (
# upper_left
ul_x + hp_x,
ul_y + hp_y,
ul_x,
ul_y,
# upper_right
ul_x + _width - hp_x,
ul_y + hp_y,
ul_x + _width,
ul_y,
# lower_left
ul_x + hp_x,
ul_y + _height - hp_y,
ul_x,
ul_y + _height,
# lower_right
ul_x + _width - hp_x,
ul_y + _height - hp_y,
ul_x + _width,
ul_y + _height,
)

def verify_image_size(self, image_data: ImageData):
Expand Down
25 changes: 14 additions & 11 deletions tests/unit/atlas/test_region.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,37 @@
from arcade.texture_atlas.atlas_default import DefaultTextureAtlas


def test_region_coordinates(ctx):
"""Test region class."""
def test_region_coordinates_simple(ctx):
"""Basic region test."""
atlas = DefaultTextureAtlas(size=(8, 8), border=0, auto_resize=False)
region = AtlasRegion(atlas=atlas, x=0, y=0, width=8, height=8)
assert region.x == 0
assert region.y == 0
assert region.width == 8
assert region.height == 8
# Simulate the half pixel location
a, b = 0.5 / 8, 1 - 0.5 / 8
a, b = 0, 1.0
assert region.texture_coordinates == (
a, a,
b, a,
a, b,
b, b,
)
# Above raw values:
# (
# 0.0625, 0.0625,
# 0.9375, 0.0625,
# 0.0625, 0.9375,
# 0.9375, 0.9375)
# )


def test_region_coordinates_complex(ctx):
"""A more complex region test."""
atlas = DefaultTextureAtlas(size=(16, 16), border=0, auto_resize=False)
region = AtlasRegion(atlas=atlas, x=1, y=2, width=8, height=6)
assert region.x == 1
assert region.y == 2
assert region.width == 8
assert region.height == 6
assert region.texture_coordinates == (0.0625, 0.125, 0.5625, 0.125, 0.0625, 0.5, 0.5625, 0.5)


def test_verify_size(ctx):
im_data = ImageData(PIL.Image.new("RGBA", (8, 8)))
texture = Texture(im_data)
region = AtlasRegion(atlas=ctx.default_atlas, x=0, y=0, width=8, height=8)

region.verify_image_size(im_data)
Expand Down
Loading