Compare commits

..

3 Commits

Author SHA1 Message Date
lizzie 424705d193 [opengl] prevent area from crashing nvlogl64.dll
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-23 06:32:39 +02:00
xbzk 5b86242313 [android, ui] remove the wash (white tint) of focused game cards (#4285)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This removes the ugly wash from focused game cards, without removing the interaction's cool ripple effects.
I've stumbled upon a reddit complaining about it, and in fact i always hated it but hadn't realized yet.

It happens to any layout.
Joypad users notice more since navigation consists on focusing cards.
Carousel users notice even more coz cards are bigger and always focused.

Border is more than enough to highlight a card, and we want to cherish colors just like creator artists wanted it to be, right?

No chance of bugs. No chance of disagreement (¬¬).
Approve it already so Youtube videos with carousel thumbs get prettier earlier ^^.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4285
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-23 00:29:59 +02:00
xbzk d24e79c991 [android, ui] refresh game icons when updates are discovered (#4284)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This small impl aims to fix an android old visual bug where it could cache/read a base game icon before its update was mounted, so games with update-specific icons kept showing the base icon like almost always (in a way that some players may never have seen the updated icons).

Fix: when scan detects an update matching an already found base game, reload that game’s metadata and refresh only its visible icon if the icon actually changed.

I believe it's polished to the max. No find/scans. When base is found it is tracked via hashmap, and when update is found and conditions met the refresh is triggered.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4284
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-23 00:25:55 +02:00
7 changed files with 61 additions and 64 deletions
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="?attr/colorControlHighlight" />
<item android:state_focused="true" android:color="@android:color/transparent" />
<item android:state_selected="true" android:color="@android:color/transparent" />
<item android:state_hovered="true" android:color="@android:color/transparent" />
<item android:color="@android:color/transparent" />
</selector>
@@ -10,6 +10,7 @@
android:clipChildren="true"
android:layout_margin="0dp"
app:cardBackgroundColor="@color/eden_card_background"
app:rippleColor="@color/game_card_ripple"
app:strokeWidth="1dp"
app:strokeColor="@color/eden_border">
@@ -11,6 +11,7 @@
app:cardCornerRadius="16dp"
app:cardPreventCornerOverlap="true"
android:clipChildren="true"
app:rippleColor="@color/game_card_ripple"
android:layout_margin="4dp">
<androidx.constraintlayout.widget.ConstraintLayout
@@ -22,6 +22,7 @@
android:focusable="true"
android:transitionName="card_game"
app:cardCornerRadius="16dp"
app:rippleColor="@color/game_card_ripple"
android:foreground="@color/eden_border_gradient_start">
<androidx.constraintlayout.widget.ConstraintLayout
@@ -22,6 +22,7 @@
android:focusable="true"
android:transitionName="card_game_compact"
app:cardCornerRadius="16dp"
app:rippleColor="@color/game_card_ripple"
android:foreground="@color/eden_border_gradient_start">
<androidx.constraintlayout.widget.ConstraintLayout
@@ -12,6 +12,7 @@
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:cardBackgroundColor="@android:color/transparent"
app:rippleColor="@color/game_card_ripple"
app:strokeWidth="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
+48 -64
View File
@@ -5,7 +5,6 @@ layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
#ifdef VULKAN
struct ScreenRectVertex {
vec2 position;
vec2 tex_coord;
@@ -14,94 +13,79 @@ layout (push_constant) uniform PushConstants {
mat4 modelview_matrix;
ScreenRectVertex vertices[4];
};
#else // OpenGL
layout(location = 1) uniform uvec2 screen_size;
#endif
/***** Area Sampling *****/
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
// Effectively a more accurate sharp bilinear filter when upscaling,
// that also works as a mathematically perfect downscale filter.
// https://entropymine.com/imageworsener/pixelmixing/
// https://github.com/obsproject/obs-studio/pull/1715
// https://legacy.imagemagick.org/Usage/filter/
vec4 AreaSampling(sampler2D textureSampler, vec2 texCoords, vec2 source_size, vec2 target_size) {
// Determine the sizes of the source and target images.
vec2 inverted_target_size = vec2(1.0) / target_size;
// Determine the range of the source image that the target pixel will cover.
vec2 range = source_size * inverted_target_size;
vec2 beg = (texCoords.xy * source_size) - (range * 0.5);
vec2 end = beg + range;
vec4 AreaSampling(sampler2D s, vec2 tc, vec4 trans_bounds) {
// Compute the top-left and bottom-right corners of the pixel box.
ivec2 f_beg = ivec2(floor(beg));
ivec2 f_end = ivec2(floor(end));
ivec4 b = ivec4(floor(trans_bounds));
// Compute how much of the start and end pixels are covered horizontally & vertically.
float area_w = 1.0 - fract(beg.x);
float area_n = 1.0 - fract(beg.y);
float area_e = fract(end.x);
float area_s = fract(end.y);
// W,N,E,S
vec4 kb = vec4(1.0f - fract(trans_bounds.xy), fract(trans_bounds.zw));
// Compute the areas of the corner pixels in the pixel box.
float area_nw = area_n * area_w;
float area_ne = area_n * area_e;
float area_sw = area_s * area_w;
float area_se = area_s * area_e;
// Initialize the color accumulator.
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
// Accumulate corner pixels.
avg_color += area_nw * texelFetch(textureSampler, ivec2(f_beg.x, f_beg.y), 0);
avg_color += area_ne * texelFetch(textureSampler, ivec2(f_end.x, f_beg.y), 0);
avg_color += area_sw * texelFetch(textureSampler, ivec2(f_beg.x, f_end.y), 0);
avg_color += area_se * texelFetch(textureSampler, ivec2(f_end.x, f_end.y), 0);
// NW,NE,SW,SE
vec4 kc = kb.yyww * kb.xzxz;
// Accumulate corner pixels by forming a corner matrix.
vec4 r = mat4x4(
texelFetch(s, ivec2(b.xy), 0),
texelFetch(s, ivec2(b.zy), 0),
texelFetch(s, ivec2(b.xw), 0),
texelFetch(s, ivec2(b.zw), 0)
) * kc;
// Determine the size of the pixel box.
int x_range = int(f_end.x - f_beg.x - 0.5);
int y_range = int(f_end.y - f_beg.y - 0.5);
ivec2 q = clamp(ivec2(
int(b.z - b.x - 0.5f),
int(b.w - b.y - 0.5f)
), ivec2(-16), ivec2(16));
vec2 qf = vec2(q);
// Accumulate top and bottom edge pixels.
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
avg_color += area_n * texelFetch(textureSampler, ivec2(x, f_beg.y), 0);
avg_color += area_s * texelFetch(textureSampler, ivec2(x, f_end.y), 0);
for (int x = 0; x < q.x; ++x) {
r += kb.y * texelFetch(s, ivec2(x + b.x + 1, b.y), 0);
r += kb.w * texelFetch(s, ivec2(x + b.x + 1, b.w), 0);
}
// Accumulate left and right edge pixels and all the pixels in between.
for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y) {
avg_color += area_w * texelFetch(textureSampler, ivec2(f_beg.x, y), 0);
avg_color += area_e * texelFetch(textureSampler, ivec2(f_end.x, y), 0);
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
avg_color += texelFetch(textureSampler, ivec2(x, y), 0);
for (int y = 0; y < q.y; ++y) {
r += kb.x * texelFetch(s, ivec2(b.x, y + b.y + 1), 0);
r += kb.z * texelFetch(s, ivec2(b.z, y + b.y + 1), 0);
for (int x = 0; x < q.x; ++x) {
r += texelFetch(s, ivec2(x, y) + b.xy + 1, 0);
}
}
// Compute the area of the pixel box that was sampled.
float area_corners = area_nw + area_ne + area_sw + area_se;
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
float area_center = float(x_range) * float(y_range);
// Return the normalized average color.
return avg_color / (area_corners + area_edges + area_center);
return r / (
kc.x + kc.y + kc.z + kc.w //corners
+ qf.x * (kb.y + kb.w) + qf.y * (kb.x + kb.z) //edges
+ qf.x * qf.y // center
);
}
void main() {
vec2 source_image_size = textureSize(color_texture, 0);
vec2 window_size;
#ifdef VULKAN
vec2 dst_size = vec2(
vertices[1].position.x - vertices[0].position.x,
vertices[2].position.y - vertices[0].position.y
);
#else // OpenGL
vec2 dst_size = screen_size;
#endif
#ifdef VULKAN
window_size.x = vertices[1].position.x - vertices[0].position.x;
window_size.y = vertices[2].position.y - vertices[0].position.y;
#else // OpenGL
window_size = screen_size;
#endif
color = AreaSampling(color_texture, frag_tex_coord, source_image_size, window_size);
vec2 src_size = textureSize(color_texture, 0);
// Determine the range of the source image that the target pixel will cover.
vec2 scale = src_size * (1.0f / dst_size);
color = AreaSampling(color_texture, frag_tex_coord, vec4(
(frag_tex_coord * src_size) - (scale * 0.5f),
// (scale * 0.5f) + scale
// {A / 2 + A} ==> {(A + 2A) / 2} ==> {3A / 2} ==> {A * (3/2)}
(frag_tex_coord * src_size) + scale * 0.5f
));
}