gfx/skia/patches/archive/0016-Bug-718849-Radial-gradients.patch

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/patches/archive/0016-Bug-718849-Radial-gradients.patch	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,400 @@
     1.4 +# HG changeset patch
     1.5 +# User Matt Woodrow <mwoodrow@mozilla.com>
     1.6 +# Date 1339988782 -43200
     1.7 +# Node ID 1e9dae659ee6c992f719fd4136efbcc5410ded37
     1.8 +# Parent 946750f6d95febd199fb7b748e9d2c48fd01c8a6
     1.9 +[mq]: skia-windows-gradients
    1.10 +
    1.11 +diff --git a/gfx/skia/src/effects/SkGradientShader.cpp b/gfx/skia/src/effects/SkGradientShader.cpp
    1.12 +--- a/gfx/skia/src/effects/SkGradientShader.cpp
    1.13 ++++ b/gfx/skia/src/effects/SkGradientShader.cpp
    1.14 +@@ -847,16 +847,19 @@ bool Linear_Gradient::setContext(const S
    1.15 +         fFlags |= SkShader::kConstInY32_Flag;
    1.16 +         if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
    1.17 +             // only claim this if we do have a 16bit mode (i.e. none of our
    1.18 +             // colors have alpha), and if we are not dithering (which obviously
    1.19 +             // is not const in Y).
    1.20 +             fFlags |= SkShader::kConstInY16_Flag;
    1.21 +         }
    1.22 +     }
    1.23 ++    if (fStart == fEnd) {
    1.24 ++        fFlags &= ~kOpaqueAlpha_Flag;
    1.25 ++    }
    1.26 +     return true;
    1.27 + }
    1.28 + 
    1.29 + #define NO_CHECK_ITER               \
    1.30 +     do {                            \
    1.31 +     unsigned fi = fx >> Gradient_Shader::kCache32Shift; \
    1.32 +     SkASSERT(fi <= 0xFF);           \
    1.33 +     fx += dx;                       \
    1.34 +@@ -976,16 +979,21 @@ void Linear_Gradient::shadeSpan(int x, i
    1.35 +     TileProc            proc = fTileProc;
    1.36 +     const SkPMColor* SK_RESTRICT cache = this->getCache32();
    1.37 + #ifdef USE_DITHER_32BIT_GRADIENT
    1.38 +     int                 toggle = ((x ^ y) & 1) * kDitherStride32;
    1.39 + #else
    1.40 +     int toggle = 0;
    1.41 + #endif
    1.42 + 
    1.43 ++    if (fStart == fEnd) {
    1.44 ++        sk_bzero(dstC, count * sizeof(*dstC));
    1.45 ++        return;
    1.46 ++    }
    1.47 ++
    1.48 +     if (fDstToIndexClass != kPerspective_MatrixClass) {
    1.49 +         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    1.50 +                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    1.51 +         SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
    1.52 + 
    1.53 +         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    1.54 +             SkFixed dxStorage[1];
    1.55 +             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
    1.56 +@@ -1169,16 +1177,21 @@ void Linear_Gradient::shadeSpan16(int x,
    1.57 +     SkASSERT(count > 0);
    1.58 + 
    1.59 +     SkPoint             srcPt;
    1.60 +     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    1.61 +     TileProc            proc = fTileProc;
    1.62 +     const uint16_t* SK_RESTRICT cache = this->getCache16();
    1.63 +     int                 toggle = ((x ^ y) & 1) * kDitherStride16;
    1.64 + 
    1.65 ++    if (fStart == fEnd) {
    1.66 ++        sk_bzero(dstC, count * sizeof(*dstC));
    1.67 ++        return;
    1.68 ++    }
    1.69 ++
    1.70 +     if (fDstToIndexClass != kPerspective_MatrixClass) {
    1.71 +         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    1.72 +                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    1.73 +         SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
    1.74 + 
    1.75 +         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    1.76 +             SkFixed dxStorage[1];
    1.77 +             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
    1.78 +@@ -1739,21 +1752,25 @@ void Radial_Gradient::shadeSpan(int x, i
    1.79 +    possible circles on which the point may fall.  Solving for t yields
    1.80 +    the gradient value to use.
    1.81 + 
    1.82 +    If a<0, the start circle is entirely contained in the
    1.83 +    end circle, and one of the roots will be <0 or >1 (off the line
    1.84 +    segment).  If a>0, the start circle falls at least partially
    1.85 +    outside the end circle (or vice versa), and the gradient
    1.86 +    defines a "tube" where a point may be on one circle (on the
    1.87 +-   inside of the tube) or the other (outside of the tube).  We choose
    1.88 +-   one arbitrarily.
    1.89 ++   inside of the tube) or the other (outside of the tube). We choose
    1.90 ++   the one with the highest t value, as long as the radius that it 
    1.91 ++   corresponds to is >=0. In the case where neither root has a positive
    1.92 ++   radius, we don't draw anything.
    1.93 + 
    1.94 ++   XXXmattwoodrow: I've removed this for now since it breaks
    1.95 ++   down when Dr == 0. Is there something else we can do instead?
    1.96 +    In order to keep the math to within the limits of fixed point,
    1.97 +-   we divide the entire quadratic by Dr^2, and replace
    1.98 ++   we divide the entire quadratic by Dr, and replace
    1.99 +    (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
   1.100 + 
   1.101 +    [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
   1.102 +    + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
   1.103 +    + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
   1.104 + 
   1.105 +    (x' and y' are computed by appending the subtract and scale to the
   1.106 +    fDstToIndex matrix in the constructor).
   1.107 +@@ -1763,99 +1780,122 @@ void Radial_Gradient::shadeSpan(int x, i
   1.108 +    x' and y', if x and y are linear in the span, 'B' can be computed
   1.109 +    incrementally with a simple delta (db below).  If it is not (e.g.,
   1.110 +    a perspective projection), it must be computed in the loop.
   1.111 + 
   1.112 + */
   1.113 + 
   1.114 + namespace {
   1.115 + 
   1.116 +-inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
   1.117 +-                                SkScalar sr2d2, SkScalar foura,
   1.118 +-                                SkScalar oneOverTwoA, bool posRoot) {
   1.119 ++inline bool two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
   1.120 ++                             SkScalar sr2d2, SkScalar foura,
   1.121 ++                             SkScalar oneOverTwoA, SkScalar diffRadius, 
   1.122 ++                             SkScalar startRadius, SkFixed& t) {
   1.123 +     SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
   1.124 +     if (0 == foura) {
   1.125 +-        return SkScalarToFixed(SkScalarDiv(-c, b));
   1.126 ++        SkScalar result = SkScalarDiv(-c, b);
   1.127 ++        if (result * diffRadius + startRadius >= 0) {
   1.128 ++            t = SkScalarToFixed(result);
   1.129 ++            return true;
   1.130 ++        }
   1.131 ++        return false;
   1.132 +     }
   1.133 + 
   1.134 +     SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
   1.135 +     if (discrim < 0) {
   1.136 +-        discrim = -discrim;
   1.137 ++        return false;
   1.138 +     }
   1.139 +     SkScalar rootDiscrim = SkScalarSqrt(discrim);
   1.140 +-    SkScalar result;
   1.141 +-    if (posRoot) {
   1.142 +-        result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
   1.143 +-    } else {
   1.144 +-        result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
   1.145 ++
   1.146 ++    // Make sure the results corresponds to a positive radius.
   1.147 ++    SkScalar result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
   1.148 ++    if (result * diffRadius + startRadius >= 0) {
   1.149 ++        t = SkScalarToFixed(result);
   1.150 ++        return true;
   1.151 +     }
   1.152 +-    return SkScalarToFixed(result);
   1.153 ++    result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
   1.154 ++    if (result * diffRadius + startRadius >= 0) {
   1.155 ++        t = SkScalarToFixed(result);
   1.156 ++        return true;
   1.157 ++    }
   1.158 ++
   1.159 ++    return false;
   1.160 + }
   1.161 + 
   1.162 + typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
   1.163 +         SkScalar fy, SkScalar dy,
   1.164 +         SkScalar b, SkScalar db,
   1.165 +-        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
   1.166 ++        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA,
   1.167 ++        SkScalar fDiffRadius, SkScalar fRadius1,
   1.168 +         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
   1.169 +         int count);
   1.170 + 
   1.171 + void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
   1.172 +         SkScalar fy, SkScalar dy,
   1.173 +         SkScalar b, SkScalar db,
   1.174 +-        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
   1.175 ++        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA,
   1.176 ++        SkScalar fDiffRadius, SkScalar fRadius1,
   1.177 +         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
   1.178 +         int count) {
   1.179 +     for (; count > 0; --count) {
   1.180 +-        SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
   1.181 +-                                     fOneOverTwoA, posRoot);
   1.182 +-
   1.183 +-        if (t < 0) {
   1.184 ++        SkFixed t;
   1.185 ++        if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) {
   1.186 ++          *(dstC++) = 0;
   1.187 ++        } else if (t < 0) {
   1.188 +             *dstC++ = cache[-1];
   1.189 +         } else if (t > 0xFFFF) {
   1.190 +             *dstC++ = cache[Gradient_Shader::kCache32Count * 2];
   1.191 +         } else {
   1.192 +             SkASSERT(t <= 0xFFFF);
   1.193 +             *dstC++ = cache[t >> Gradient_Shader::kCache32Shift];
   1.194 +         }
   1.195 + 
   1.196 +         fx += dx;
   1.197 +         fy += dy;
   1.198 +         b += db;
   1.199 +     }
   1.200 + }
   1.201 + void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
   1.202 +         SkScalar fy, SkScalar dy,
   1.203 +         SkScalar b, SkScalar db,
   1.204 +-        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
   1.205 ++        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA,
   1.206 ++        SkScalar fDiffRadius, SkScalar fRadius1,
   1.207 +         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
   1.208 +         int count) {
   1.209 +     for (; count > 0; --count) {
   1.210 +-        SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
   1.211 +-                                     fOneOverTwoA, posRoot);
   1.212 +-        SkFixed index = mirror_tileproc(t);
   1.213 +-        SkASSERT(index <= 0xFFFF);
   1.214 +-        *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
   1.215 ++        SkFixed t;
   1.216 ++        if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) {
   1.217 ++          *(dstC++) = 0;
   1.218 ++        } else {
   1.219 ++          SkFixed index = mirror_tileproc(t);
   1.220 ++          SkASSERT(index <= 0xFFFF);
   1.221 ++          *dstC++ = cache[index >> (16 - Gradient_Shader::kCache32Shift)];
   1.222 ++        }
   1.223 +         fx += dx;
   1.224 +         fy += dy;
   1.225 +         b += db;
   1.226 +     }
   1.227 + }
   1.228 + 
   1.229 + void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
   1.230 +         SkScalar fy, SkScalar dy,
   1.231 +         SkScalar b, SkScalar db,
   1.232 +-        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
   1.233 ++        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, 
   1.234 ++        SkScalar fDiffRadius, SkScalar fRadius1,
   1.235 +         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
   1.236 +         int count) {
   1.237 +     for (; count > 0; --count) {
   1.238 +-        SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
   1.239 +-                                     fOneOverTwoA, posRoot);
   1.240 +-        SkFixed index = repeat_tileproc(t);
   1.241 +-        SkASSERT(index <= 0xFFFF);
   1.242 +-        *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
   1.243 ++        SkFixed t;
   1.244 ++        if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) {
   1.245 ++          *(dstC++) = 0;
   1.246 ++        } else {
   1.247 ++          SkFixed index = repeat_tileproc(t);
   1.248 ++          SkASSERT(index <= 0xFFFF);
   1.249 ++          *dstC++ = cache[index >> (16 - Gradient_Shader::kCache32Shift)];
   1.250 ++        }
   1.251 +         fx += dx;
   1.252 +         fy += dy;
   1.253 +         b += db;
   1.254 +     }
   1.255 + }
   1.256 + 
   1.257 + 
   1.258 + 
   1.259 +@@ -1935,17 +1975,16 @@ public:
   1.260 +           sk_bzero(dstC, count * sizeof(*dstC));
   1.261 +           return;
   1.262 +         }
   1.263 +         SkMatrix::MapXYProc dstProc = fDstToIndexProc;
   1.264 +         TileProc            proc = fTileProc;
   1.265 +         const SkPMColor* SK_RESTRICT cache = this->getCache32();
   1.266 + 
   1.267 +         SkScalar foura = fA * 4;
   1.268 +-        bool posRoot = fDiffRadius < 0;
   1.269 +         if (fDstToIndexClass != kPerspective_MatrixClass) {
   1.270 +             SkPoint srcPt;
   1.271 +             dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
   1.272 +                                  SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
   1.273 +             SkScalar dx, fx = srcPt.fX;
   1.274 +             SkScalar dy, fy = srcPt.fY;
   1.275 + 
   1.276 +             if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
   1.277 +@@ -1954,60 +1993,69 @@ public:
   1.278 +                 dx = SkFixedToScalar(fixedX);
   1.279 +                 dy = SkFixedToScalar(fixedY);
   1.280 +             } else {
   1.281 +                 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
   1.282 +                 dx = fDstToIndex.getScaleX();
   1.283 +                 dy = fDstToIndex.getSkewY();
   1.284 +             }
   1.285 +             SkScalar b = (SkScalarMul(fDiff.fX, fx) +
   1.286 +-                         SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
   1.287 ++                          SkScalarMul(fDiff.fY, fy) - fStartRadius * fDiffRadius) * 2;
   1.288 +             SkScalar db = (SkScalarMul(fDiff.fX, dx) +
   1.289 +                           SkScalarMul(fDiff.fY, dy)) * 2;
   1.290 + 
   1.291 +             TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
   1.292 +             if (proc == clamp_tileproc) {
   1.293 +                 shadeProc = shadeSpan_twopoint_clamp;
   1.294 +             } else if (proc == mirror_tileproc) {
   1.295 +                 shadeProc = shadeSpan_twopoint_mirror;
   1.296 +             } else {
   1.297 +                 SkASSERT(proc == repeat_tileproc);
   1.298 +             }
   1.299 +             (*shadeProc)(fx, dx, fy, dy, b, db,
   1.300 +-                         fSr2D2, foura, fOneOverTwoA, posRoot,
   1.301 ++                         fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1,
   1.302 +                          dstC, cache, count);
   1.303 +         } else {    // perspective case
   1.304 +             SkScalar dstX = SkIntToScalar(x);
   1.305 +             SkScalar dstY = SkIntToScalar(y);
   1.306 +             for (; count > 0; --count) {
   1.307 +                 SkPoint             srcPt;
   1.308 +                 dstProc(fDstToIndex, dstX, dstY, &srcPt);
   1.309 +                 SkScalar fx = srcPt.fX;
   1.310 +                 SkScalar fy = srcPt.fY;
   1.311 +                 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
   1.312 +                              SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
   1.313 +-                SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
   1.314 +-                                             fOneOverTwoA, posRoot);
   1.315 +-                SkFixed index = proc(t);
   1.316 +-                SkASSERT(index <= 0xFFFF);
   1.317 +-                *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
   1.318 ++                SkFixed t;
   1.319 ++                if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) {
   1.320 ++                  *(dstC++) = 0;
   1.321 ++                } else {
   1.322 ++                  SkFixed index = proc(t);
   1.323 ++                  SkASSERT(index <= 0xFFFF);
   1.324 ++                  *dstC++ = cache[index >> (16 - kCache32Bits)];
   1.325 ++                }
   1.326 +                 dstX += SK_Scalar1;
   1.327 +             }
   1.328 +         }
   1.329 +     }
   1.330 + 
   1.331 +     virtual bool setContext(const SkBitmap& device,
   1.332 +                             const SkPaint& paint,
   1.333 +                             const SkMatrix& matrix) SK_OVERRIDE {
   1.334 +         if (!this->INHERITED::setContext(device, paint, matrix)) {
   1.335 +             return false;
   1.336 +         }
   1.337 + 
   1.338 +         // we don't have a span16 proc
   1.339 +         fFlags &= ~kHasSpan16_Flag;
   1.340 ++
   1.341 ++        // If we might end up wanting to draw nothing as part of the gradient
   1.342 ++        // then we should mark ourselves as not being opaque.
   1.343 ++        if (fA >= 0 || (fDiffRadius == 0 && fCenter1 == fCenter2)) {
   1.344 ++            fFlags &= ~kOpaqueAlpha_Flag;
   1.345 ++        }
   1.346 +         return true;
   1.347 +     }
   1.348 + 
   1.349 +     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Two_Point_Radial_Gradient)
   1.350 + 
   1.351 + protected:
   1.352 +     Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
   1.353 +             : INHERITED(buffer),
   1.354 +@@ -2033,26 +2081,22 @@ private:
   1.355 +     const SkScalar fRadius1;
   1.356 +     const SkScalar fRadius2;
   1.357 +     SkPoint fDiff;
   1.358 +     SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
   1.359 + 
   1.360 +     void init() {
   1.361 +         fDiff = fCenter1 - fCenter2;
   1.362 +         fDiffRadius = fRadius2 - fRadius1;
   1.363 +-        SkScalar inv = SkScalarInvert(fDiffRadius);
   1.364 +-        fDiff.fX = SkScalarMul(fDiff.fX, inv);
   1.365 +-        fDiff.fY = SkScalarMul(fDiff.fY, inv);
   1.366 +-        fStartRadius = SkScalarMul(fRadius1, inv);
   1.367 ++        fStartRadius = fRadius1;
   1.368 +         fSr2D2 = SkScalarSquare(fStartRadius);
   1.369 +-        fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
   1.370 ++        fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SkScalarSquare(fDiffRadius);
   1.371 +         fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
   1.372 + 
   1.373 +         fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
   1.374 +-        fPtsToUnit.postScale(inv, inv);
   1.375 +     }
   1.376 + };
   1.377 + 
   1.378 + ///////////////////////////////////////////////////////////////////////////////
   1.379 + 
   1.380 + class Sweep_Gradient : public Gradient_Shader {
   1.381 + public:
   1.382 +     Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
   1.383 +@@ -2488,16 +2532,20 @@ SkShader* SkGradientShader::CreateTwoPoi
   1.384 +                                                  int colorCount,
   1.385 +                                                  SkShader::TileMode mode,
   1.386 +                                                  SkUnitMapper* mapper) {
   1.387 +     if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
   1.388 +         return NULL;
   1.389 +     }
   1.390 +     EXPAND_1_COLOR(colorCount);
   1.391 + 
   1.392 ++    if (start == end && startRadius == 0) {
   1.393 ++        return CreateRadial(start, endRadius, colors, pos, colorCount, mode, mapper);
   1.394 ++    }
   1.395 ++
   1.396 +     return SkNEW_ARGS(Two_Point_Radial_Gradient,
   1.397 +                       (start, startRadius, end, endRadius, colors, pos,
   1.398 +                        colorCount, mode, mapper));
   1.399 + }
   1.400 + 
   1.401 + SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
   1.402 +                                         const SkColor colors[],
   1.403 +                                         const SkScalar pos[],

mercurial