This is an automated email from the git hooks/post-receive script.

git pushed a commit to reference refs/pull/114/head
in repository efl.

View the commit online.

commit 0b686b9fdbc43ac26b576cbd7ad8f8b233f497b5
Author: [email protected] <[email protected]>
AuthorDate: Mon Aug 10 17:18:40 2026 -0600

    fix(evas_ector_gl): evaluate gradients in span-local space
    
    The span shader was rendering every gradient offset from where the
    software backend puts it.  Expedite's vector tests showed the whole ramp
    slid across the shape: on VG Basic Gradient 12.5% of the pixels were more
    than 10 dE76 away from the software rendering, far above the ~2.3 dE
    just-noticeable threshold.
    
    _span_gradient_linear_coeffs() and _span_gradient_radial_coeffs() both took
    offx/offy and both discarded them as EINA_UNUSED.  That is wrong: the two
    backends evaluate the gradient parameter in different spaces.  The software
    rasterizer feeds fetch_linear_gradient() the span-local raster coordinates
    and applies the ector surface origin to the destination pointer only, so the
    origin never reaches the gradient parameter.  The shader instead evaluates at
    px/py, which are surface-space gl_FragCoord values and do include the origin.
    
    Fold the translation into the constant term so the shader evaluates at
    span-local coordinates like the software does.  Measured offx/offy of -3 on
    the expedite tests, which at a = b = 0.0125 is a ramp offset of 0.075 -
    matching the 0.076 offset measured from the rendered pixels.
    
    Linear and radial were both affected, and both are fixed here.
    
    Vector tests against the software backend, worst per-channel delta out of
    255, before -> after:
    
      VG Basic Gradient                 42 -> 4
      VG Basic Radial Gradient          75 -> 5
      VG Scaled                         41 -> 6
      VG Gradient Multi-Shape Transform 26 -> 5
    
    That is now the same noise floor as the non-gradient vector tests, which
    sit at 3 to 5.  Reproduce with:
    
      expedite-parity -e opengl_x11 -c 20 119 120 125 126
---
 src/modules/evas/engines/gl_generic/evas_engine.c | 39 +++++++++++++++++------
 1 file changed, 29 insertions(+), 10 deletions(-)

diff --git a/src/modules/evas/engines/gl_generic/evas_engine.c b/src/modules/evas/engines/gl_generic/evas_engine.c
index 08c2412856..404232ba64 100644
--- a/src/modules/evas/engines/gl_generic/evas_engine.c
+++ b/src/modules/evas/engines/gl_generic/evas_engine.c
@@ -2845,7 +2845,7 @@ eng_ector_begin(void *engine, void *surface,
 static void
 _span_gradient_linear_coeffs(Ector_Renderer_Software_Gradient_Data *gd,
                               const Eina_Matrix3 *inv,
-                              int offx EINA_UNUSED, int offy EINA_UNUSED,
+                              int offx, int offy,
                               float atlas_off_x EINA_UNUSED,
                               float atlas_off_y EINA_UNUSED,
                               float *out_a, float *out_b, float *out_c)
@@ -2853,21 +2853,34 @@ _span_gradient_linear_coeffs(Ector_Renderer_Software_Gradient_Data *gd,
    double dx  = gd->linear.dx;
    double dy  = gd->linear.dy;
    double off = gd->linear.off;
+   double a, b;
 
    /* The software computes t = dx*rx + dy*ry + off where:
     *   rx = inv.xx*(x+0.5) + inv.xy*(y+0.5) + inv.xz
     *   ry = inv.yx*(x+0.5) + inv.yy*(y+0.5) + inv.yz
     *
+    * That (x, y) is the *span-local* raster coordinate.  The ector surface
+    * origin (offx, offy) is applied to the destination pointer only
+    * (ector_software_rasterizer.c) and never reaches the gradient
+    * parameter.  The shader instead evaluates at px/py in surface space,
+    * which does include that origin, so the translation has to be undone
+    * here — otherwise the gradient slides across the shape by
+    * (a*offx + b*offy) in ramp units.
+    *
     * The shader receives px = gl_FragCoord.x - fbo_offset which already
-    * equals (x + 0.5) per the OpenGL spec (FragCoord centers at half-integer).
-    * So t = a*px + b*py + c decomposes as:
+    * equals (x + 0.5) per the OpenGL spec (FragCoord centers at half-integer),
+    * so span-local (x + 0.5) is px - offx.  t = a*px + b*py + c decomposes as:
     *   a = dx*inv.xx + dy*inv.yx
     *   b = dx*inv.xy + dy*inv.yy
-    *   c = dx*inv.xz + dy*inv.yz + off   (no extra half-pixel term needed)
+    *   c = dx*inv.xz + dy*inv.yz + off - a*offx - b*offy
     */
-   *out_a = (float)(dx * inv->xx + dy * inv->yx);
-   *out_b = (float)(dx * inv->xy + dy * inv->yy);
-   *out_c = (float)(dx * inv->xz + dy * inv->yz + off);
+   a = dx * inv->xx + dy * inv->yx;
+   b = dx * inv->xy + dy * inv->yy;
+
+   *out_a = (float)a;
+   *out_b = (float)b;
+   *out_c = (float)(dx * inv->xz + dy * inv->yz + off
+                    - a * (double)offx - b * (double)offy);
 }
 
 /**
@@ -2888,7 +2901,7 @@ _span_gradient_linear_coeffs(Ector_Renderer_Software_Gradient_Data *gd,
 static void
 _span_gradient_radial_coeffs(Ector_Renderer_Software_Gradient_Data *gd,
                               const Eina_Matrix3 *inv,
-                              int offx EINA_UNUSED, int offy EINA_UNUSED,
+                              int offx, int offy,
                               float atlas_off_x EINA_UNUSED,
                               float atlas_off_y EINA_UNUSED,
                               float *out_a, float *out_b, float *out_c,
@@ -2899,15 +2912,21 @@ _span_gradient_radial_coeffs(Ector_Renderer_Software_Gradient_Data *gd,
     *   rx = inv.xx*(x+0.5) + inv.xy*(y+0.5) + inv.xz - fx
     *   ry = inv.yx*(x+0.5) + inv.yy*(y+0.5) + inv.yz - fy
     *
+    * As in the linear case that (x, y) is span-local, so the ector surface
+    * origin (offx, offy) must be subtracted from the shader's surface-space
+    * px/py.  Fold it into the constant terms.
+    *
     * The shader's px/py already include the +0.5 (gl_FragCoord centering),
     * so the constant terms are just inv.xz - fx and inv.yz - fy. */
    *out_a = (float)inv->xx;
    *out_b = (float)inv->xy;
-   *out_c = (float)(inv->xz - gd->radial.fx);
+   *out_c = (float)(inv->xz - gd->radial.fx
+                    - inv->xx * (double)offx - inv->xy * (double)offy);
 
    *out_d = (float)inv->yx;
    *out_e = (float)inv->yy;
-   *out_f = (float)(inv->yz - gd->radial.fy);
+   *out_f = (float)(inv->yz - gd->radial.fy
+                    - inv->yx * (double)offx - inv->yy * (double)offy);
 
    /* Quadratic parameters — pass inv2a instead of a to avoid
     * per-fragment division in the shader. */

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to