vector: port radial drawing implementation from ector library. 47/184147/4
authorsubhransu sekhar mohanty <smohantty@subhransus-MacBook-Air.local>
Sat, 14 Jul 2018 06:51:47 +0000 (15:51 +0900)
committerHermet Park <chuneon.park@samsung.com>
Mon, 16 Jul 2018 12:17:28 +0000 (12:17 +0000)
Change-Id: I0885d2493a19f71bc82ecf9d03268539a2c98bec

src/vector/vdrawhelper.cpp

index 641d3d6..a2c8932 100644 (file)
@@ -196,6 +196,22 @@ getLinearGradientValues(LinearGradientValues *v, const VSpanData *data)
     }
 }
 
+static inline void
+getRadialGradientValues(RadialGradientValues *v, const VSpanData *data)
+{
+    const VGradientData &gradient = data->mGradient;
+    v->dx = gradient.radial.cx - gradient.radial.fx;
+    v->dy = gradient.radial.cy - gradient.radial.fy;
+
+    v->dr = gradient.radial.cradius - gradient.radial.fradius;
+    v->sqrfr = gradient.radial.fradius * gradient.radial.fradius;
+
+    v->a = v->dr * v->dr - v->dx*v->dx - v->dy*v->dy;
+    v->inv2a = 1 / (2 * v->a);
+
+    v->extended = !vIsNull(gradient.radial.fradius) || v->a <= 0;
+}
+
 static inline int
 gradientClamp(const VGradientData *grad, int ipos)
 {
@@ -304,6 +320,129 @@ fetch_linear_gradient(uint32_t *buffer, const Operator *op, const VSpanData *dat
     }
 }
 
+static inline float radialDeterminant(float a, float b, float c)
+{
+    return (b * b) - (4 * a * c);
+}
+
+static void fetch(uint32_t *buffer, uint32_t *end,
+                  const Operator *op, const VSpanData *data, float det,
+                  float delta_det, float delta_delta_det, float b, float delta_b)
+{
+    if (op->radial.extended) {
+        while (buffer < end) {
+            uint32_t result = 0;
+            if (det >= 0) {
+                float w = std::sqrt(det) - b;
+                if (data->mGradient.radial.fradius + op->radial.dr * w >= 0)
+                    result = gradientPixel(&data->mGradient, w);
+            }
+
+            *buffer = result;
+
+            det += delta_det;
+            delta_det += delta_delta_det;
+            b += delta_b;
+
+            ++buffer;
+        }
+    } else {
+        while (buffer < end) {
+            *buffer++ = gradientPixel(&data->mGradient, std::sqrt(det) - b);
+
+            det += delta_det;
+            delta_det += delta_delta_det;
+            b += delta_b;
+        }
+    }
+}
+
+void fetch_radial_gradient(uint32_t *buffer, const Operator *op, const VSpanData *data, int y, int x, int length)
+{
+    // avoid division by zero
+    if (vIsNull(op->radial.a)) {
+        memfill32(buffer, 0, length);
+        return;
+    }
+
+    float rx = data->m21 * (y + float(0.5))
+               + data->dx + data->m11 * (x + float(0.5));
+    float ry = data->m22 * (y + float(0.5))
+               + data->dy + data->m12 * (x + float(0.5));
+    bool affine = !data->m13 && !data->m23;
+
+    uint32_t *end = buffer + length;
+    if (affine) {
+        rx -= data->mGradient.radial.fx;
+        ry -= data->mGradient.radial.fy;
+
+        float inv_a = 1 / float(2 * op->radial.a);
+
+        const float delta_rx = data->m11;
+        const float delta_ry = data->m12;
+
+        float b = 2*(op->radial.dr*data->mGradient.radial.fradius + rx * op->radial.dx + ry * op->radial.dy);
+        float delta_b = 2*(delta_rx * op->radial.dx + delta_ry * op->radial.dy);
+        const float b_delta_b = 2 * b * delta_b;
+        const float delta_b_delta_b = 2 * delta_b * delta_b;
+
+        const float bb = b * b;
+        const float delta_bb = delta_b * delta_b;
+
+        b *= inv_a;
+        delta_b *= inv_a;
+
+        const float rxrxryry = rx * rx + ry * ry;
+        const float delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
+        const float rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry);
+        const float delta_rx_plus_ry = 2 * delta_rxrxryry;
+
+        inv_a *= inv_a;
+
+        float det = (bb - 4 * op->radial.a * (op->radial.sqrfr - rxrxryry)) * inv_a;
+        float delta_det = (b_delta_b + delta_bb + 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a;
+        const float delta_delta_det = (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
+
+        fetch(buffer, end, op, data, det, delta_det, delta_delta_det, b, delta_b);
+    } else {
+        float rw = data->m23 * (y + float(0.5))
+                   + data->m33 + data->m13 * (x + float(0.5));
+
+        while (buffer < end) {
+            if (rw == 0) {
+                *buffer = 0;
+            } else {
+                float invRw = 1 / rw;
+                float gx = rx * invRw - data->mGradient.radial.fx;
+                float gy = ry * invRw - data->mGradient.radial.fy;
+                float b  = 2*(op->radial.dr*data->mGradient.radial.fradius + gx*op->radial.dx + gy*op->radial.dy);
+                float det = radialDeterminant(op->radial.a, b, op->radial.sqrfr - (gx*gx + gy*gy));
+
+                uint32_t result = 0;
+                if (det >= 0) {
+                    float detSqrt = std::sqrt(det);
+
+                    float s0 = (-b - detSqrt) * op->radial.inv2a;
+                    float s1 = (-b + detSqrt) * op->radial.inv2a;
+
+                    float s = vMax(s0, s1);
+
+                    if (data->mGradient.radial.fradius + op->radial.dr * s >= 0)
+                        result = gradientPixel(&data->mGradient, s);
+                }
+
+                *buffer = result;
+            }
+
+            rx += data->m11;
+            ry += data->m12;
+            rw += data->m13;
+
+            ++buffer;
+        }
+    }
+}
+
 
 static inline Operator getOperator(const VSpanData *data, const VRle::Span *spans, int spanCount)
 {
@@ -320,6 +459,11 @@ static inline Operator getOperator(const VSpanData *data, const VRle::Span *span
         getLinearGradientValues(&op.linear, data);
         op.srcFetch = &fetch_linear_gradient;
         break;
+    case VSpanData::Type::RadialGradient:
+        solidSource = false;
+        getRadialGradientValues(&op.radial, data);
+        op.srcFetch = &fetch_radial_gradient;
+        break;
     default:
         break;
     }