1 // [Blend2D]
2 // 2D Vector Graphics Powered by a JIT Compiler.
3 //
4 // [License]
5 // Zlib - See LICENSE.md file in the package.
6 
7 #include "./api-build_p.h"
8 #include "./context.h"
9 #include "./math_p.h"
10 #include "./matrix_p.h"
11 #include "./pipedefs_p.h"
12 #include "./support_p.h"
13 
14 // ============================================================================
15 // [BLPipeFetchData - Init Pattern]
16 // ============================================================================
17 
blExtendXFromExtendMode(uint32_t extendMode)18 static BL_INLINE uint32_t blExtendXFromExtendMode(uint32_t extendMode) noexcept {
19   BL_ASSERT(extendMode < BL_EXTEND_MODE_COMPLEX_COUNT);
20 
21   constexpr uint32_t kTable = (BL_EXTEND_MODE_PAD     <<  0) | // [pad-x     pad-y    ]
22                               (BL_EXTEND_MODE_REPEAT  <<  2) | // [repeat-x  repeat-y ]
23                               (BL_EXTEND_MODE_REFLECT <<  4) | // [reflect-x reflect-y]
24                               (BL_EXTEND_MODE_PAD     <<  6) | // [pad-x     repeat-y ]
25                               (BL_EXTEND_MODE_PAD     <<  8) | // [pad-x     reflect-y]
26                               (BL_EXTEND_MODE_REPEAT  << 10) | // [repeat-x  pad-y    ]
27                               (BL_EXTEND_MODE_REPEAT  << 12) | // [repeat-x  reflect-y]
28                               (BL_EXTEND_MODE_REFLECT << 14) | // [reflect-x pad-y    ]
29                               (BL_EXTEND_MODE_REFLECT << 16) ; // [reflect-x repeat-y ]
30   return (kTable >> (extendMode * 2u)) & 0x3u;
31 }
32 
blExtendYFromExtendMode(uint32_t extendMode)33 static BL_INLINE uint32_t blExtendYFromExtendMode(uint32_t extendMode) noexcept {
34   BL_ASSERT(extendMode < BL_EXTEND_MODE_COMPLEX_COUNT);
35 
36   constexpr uint32_t kTable = (BL_EXTEND_MODE_PAD     <<  0) | // [pad-x     pad-y    ]
37                               (BL_EXTEND_MODE_REPEAT  <<  2) | // [repeat-x  repeat-y ]
38                               (BL_EXTEND_MODE_REFLECT <<  4) | // [reflect-x reflect-y]
39                               (BL_EXTEND_MODE_REPEAT  <<  6) | // [pad-x     repeat-y ]
40                               (BL_EXTEND_MODE_REFLECT <<  8) | // [pad-x     reflect-y]
41                               (BL_EXTEND_MODE_PAD     << 10) | // [repeat-x  pad-y    ]
42                               (BL_EXTEND_MODE_REFLECT << 12) | // [repeat-x  reflect-y]
43                               (BL_EXTEND_MODE_PAD     << 14) | // [reflect-x pad-y    ]
44                               (BL_EXTEND_MODE_REPEAT  << 16) ; // [reflect-x repeat-y ]
45   return (kTable >> (extendMode * 2u)) & 0x3u;
46 }
47 
blPipeFetchDataInitPatternTxTy(BLPipeFetchData * fetchData,uint32_t fetchBase,uint32_t extendMode,int tx,int ty,bool isFractional)48 static BL_INLINE uint32_t blPipeFetchDataInitPatternTxTy(BLPipeFetchData* fetchData, uint32_t fetchBase, uint32_t extendMode, int tx, int ty, bool isFractional) noexcept {
49   BLPipeFetchData::Pattern& d = fetchData->pattern;
50   uint32_t extendX = blExtendXFromExtendMode(extendMode);
51   uint32_t extendY = blExtendYFromExtendMode(extendMode);
52   uint32_t ixIndex = 17;
53 
54   int rx = 0;
55   int ry = 0;
56 
57   // If the pattern width/height is 1 all extend modes produce the same output.
58   // However, it's safer to just set it to PAD as FetchPatternPart requires
59   // `width` to be equal or greater than 2 if the extend mode is REPEAT or
60   // REFLECT.
61   if (d.src.size.w <= 1) extendX = BL_EXTEND_MODE_PAD;
62   if (d.src.size.h <= 1) extendY = BL_EXTEND_MODE_PAD;
63 
64   if (extendX >= BL_EXTEND_MODE_REPEAT) {
65     bool isReflect = extendX == BL_EXTEND_MODE_REFLECT;
66 
67     rx = int(d.src.size.w) << uint32_t(isReflect);
68     if (unsigned(tx) >= unsigned(rx))
69       tx %= rx;
70     if (tx < 0)
71       tx += rx;
72 
73     // In extreme cases, when `rx` is very small, fetch4()/fetch8() functions
74     // may overflow `x` if they increment more than they can fix by subtracting
75     // `rw` in case of overflow (and overflow happens as it's used to start
76     // over). To fix this and simplify the compiled code we simply precalculate
77     // these constants so they are always safe.
78     ixIndex = blMin<uint32_t>(uint32_t(rx), 17);
79 
80     // Don't specialize `Repeat vs Reflect` when we are not pixel aligned.
81     if (isFractional)
82       extendX = 1; // TODO: Naming...
83   }
84 
85   if (extendY >= BL_EXTEND_MODE_REPEAT) {
86     ry = int(d.src.size.h) << uint32_t(extendY == BL_EXTEND_MODE_REFLECT);
87     if (unsigned(ty) >= unsigned(ry))
88       ty %= ry;
89     if (ty < 0)
90       ty += ry;
91   }
92 
93   d.simple.tx = tx;
94   d.simple.ty = ty;
95   d.simple.rx = rx;
96   d.simple.ry = ry;
97   d.simple.ix = blModuloTable[ixIndex];
98 
99   return fetchBase + extendX;
100 }
101 
initPatternAxAy(uint32_t extendMode,int x,int y)102 uint32_t BLPipeFetchData::initPatternAxAy(uint32_t extendMode, int x, int y) noexcept {
103   return blPipeFetchDataInitPatternTxTy(this, BL_PIPE_FETCH_TYPE_PATTERN_AA_PAD, extendMode, -x, -y, false);
104 }
105 
initPatternFxFy(uint32_t extendMode,uint32_t filter,uint32_t bytesPerPixel,int64_t tx64,int64_t ty64)106 uint32_t BLPipeFetchData::initPatternFxFy(uint32_t extendMode, uint32_t filter, uint32_t bytesPerPixel, int64_t tx64, int64_t ty64) noexcept {
107   BL_UNUSED(bytesPerPixel);
108 
109   BLPipeFetchData::Pattern& d = this->pattern;
110 
111   uint32_t fetchBase = BL_PIPE_FETCH_TYPE_PATTERN_AA_PAD;
112   uint32_t wx = uint32_t(tx64 & 0xFF);
113   uint32_t wy = uint32_t(ty64 & 0xFF);
114 
115   int tx = -int(((tx64)) >> 8);
116   int ty = -int(((ty64)) >> 8);
117 
118   // If one or both `wx` or `why` are non-zero it means that the translation
119   // is fractional. In that case we must calculate weights of [x0 y0], [x1 y0],
120   // [x0 y1], and [x1 y1] pixels.
121   bool isFractional = (wx | wy) != 0;
122   if (isFractional) {
123     if (filter == BL_PATTERN_QUALITY_NEAREST) {
124       tx -= (wx >= 128);
125       ty -= (wy >= 128);
126       isFractional = false;
127     }
128     else {
129       d.simple.wa = ((      wy) * (      wx)      ) >> 8; // [x0 y0]
130       d.simple.wb = ((      wy) * (256 - wx) + 255) >> 8; // [x1 y0]
131       d.simple.wc = ((256 - wy) * (      wx)      ) >> 8; // [x0 y1]
132       d.simple.wd = ((256 - wy) * (256 - wx) + 255) >> 8; // [x1 y1]
133 
134       // The FxFy fetcher must work even when one or both `wx` or `wy` are
135       // zero, so we always decrement `tx` and `ty` based on the fetch type.
136       if (wy == 0) {
137         tx--;
138         fetchBase = BL_PIPE_FETCH_TYPE_PATTERN_FX_PAD;
139       }
140       else if (wx == 0) {
141         ty--;
142         fetchBase = BL_PIPE_FETCH_TYPE_PATTERN_FY_PAD;
143       }
144       else {
145         tx--;
146         ty--;
147         fetchBase = BL_PIPE_FETCH_TYPE_PATTERN_FX_FY_PAD;
148       }
149     }
150   }
151 
152   return blPipeFetchDataInitPatternTxTy(this, fetchBase, extendMode, tx, ty, isFractional);
153 }
154 
initPatternAffine(uint32_t extendMode,uint32_t filter,uint32_t bytesPerPixel,const BLMatrix2D & m)155 uint32_t BLPipeFetchData::initPatternAffine(uint32_t extendMode, uint32_t filter, uint32_t bytesPerPixel, const BLMatrix2D& m) noexcept {
156   BLPipeFetchData::Pattern& d = this->pattern;
157 
158   // Inverted transformation matrix.
159   BLMatrix2D mInv;
160   if (BLMatrix2D::invert(mInv, m) != BL_SUCCESS)
161     return BL_PIPE_FETCH_TYPE_FAILURE;
162 
163   double xx = mInv.m00;
164   double xy = mInv.m01;
165   double yx = mInv.m10;
166   double yy = mInv.m11;
167 
168   if (isNearOne(xx) && isNearZero(xy) && isNearZero(yx) && isNearOne(yy)) {
169     return initPatternFxFy(
170       extendMode,
171       filter,
172       bytesPerPixel,
173       blFloorToInt64(-mInv.m20 * 256.0),
174       blFloorToInt64(-mInv.m21 * 256.0));
175   }
176 
177   uint32_t fetchType =
178     filter == BL_PATTERN_QUALITY_NEAREST
179       ? BL_PIPE_FETCH_TYPE_PATTERN_AFFINE_NN_ANY
180       : BL_PIPE_FETCH_TYPE_PATTERN_AFFINE_BI_ANY;
181 
182   // Pattern bounds.
183   int tw = int(d.src.size.w);
184   int th = int(d.src.size.h);
185 
186   uint32_t opt = blMax(tw, th) < 32767 &&
187                  d.src.stride >= 0 &&
188                  d.src.stride <= intptr_t(blMaxValue<int16_t>());
189 
190   // TODO: [PIPEGEN] Not implemented for bilinear yet.
191   if (filter == BL_PATTERN_QUALITY_BILINEAR)
192     opt = 0;
193 
194   fetchType += opt;
195 
196   // Pattern X/Y extends.
197   uint32_t extendX = blExtendXFromExtendMode(extendMode);
198   uint32_t extendY = blExtendYFromExtendMode(extendMode);
199 
200   // Translation.
201   double tx = mInv.m20;
202   double ty = mInv.m21;
203 
204   tx += 0.5 * (xx + yx);
205   ty += 0.5 * (xy + yy);
206 
207   // 32x32 fixed point scale as double, equals to `pow(2, 32)`.
208   double fpScale = 4294967296.0;
209 
210   // Overflow check of X/Y. When this check passes we decrement rx/ry from
211   // the overflown values.
212   int ox = blMaxValue<int32_t>();
213   int oy = blMaxValue<int32_t>();
214 
215   // Normalization of X/Y. These values are added to the current `px` and `py`
216   // when they overflow the repeat|reflect bounds.
217   int rx = 0;
218   int ry = 0;
219 
220   d.affine.minX = 0;
221   d.affine.minY = 0;
222 
223   d.affine.maxX = int32_t(tw - 1);
224   d.affine.maxY = int32_t(th - 1);
225 
226   d.affine.corX = int32_t(tw - 1);
227   d.affine.corY = int32_t(th - 1);
228 
229   if (extendX != BL_EXTEND_MODE_PAD) {
230     d.affine.minX = blMinValue<int32_t>();
231     if (extendX == BL_EXTEND_MODE_REPEAT)
232       d.affine.corX = 0;
233 
234     ox = tw;
235     if (extendX == BL_EXTEND_MODE_REFLECT)
236       tw *= 2;
237 
238     if (xx < 0.0) {
239       xx = -xx;
240       yx = -yx;
241       tx = double(tw) - tx;
242 
243       if (extendX == BL_EXTEND_MODE_REPEAT) {
244         ox = 0;
245         d.affine.corX = d.affine.maxX;
246         d.affine.maxX = -1;
247       }
248     }
249     ox--;
250   }
251 
252   if (extendY != BL_EXTEND_MODE_PAD) {
253     d.affine.minY = blMinValue<int32_t>();
254     if (extendY == BL_EXTEND_MODE_REPEAT)
255       d.affine.corY = 0;
256 
257     oy = th;
258     if (extendY == BL_EXTEND_MODE_REFLECT)
259       th *= 2;
260 
261     if (xy < 0.0) {
262       xy = -xy;
263       yy = -yy;
264       ty = double(th) - ty;
265 
266       if (extendY == BL_EXTEND_MODE_REPEAT) {
267         oy = 0;
268         d.affine.corY = d.affine.maxY;
269         d.affine.maxY = -1;
270       }
271     }
272     oy--;
273   }
274 
275   // Keep the center of the pixel at [0.5, 0.5] if the filter is NEAREST so
276   // it can properly round to the nearest pixel during the fetch phase.
277   // However, if the filter is not NEAREST the `tx` and `ty` have to be
278   // translated by -0.5 so the position starts at the beginning of the pixel.
279   if (filter != BL_PATTERN_QUALITY_NEAREST) {
280     tx -= 0.5;
281     ty -= 0.5;
282   }
283 
284   // Pattern boundaries converted to `double`.
285   double tw_d = double(tw);
286   double th_d = double(th);
287 
288   // Normalize the matrix in a way that it won't overflow the pattern more
289   // than once per a single iteration. Happens when scaling part is very
290   // small. Only useful for repeated / reflected cases.
291   if (extendX == BL_EXTEND_MODE_PAD) {
292     tw_d = 4294967296.0;
293   }
294   else {
295     tx = fmod(tx, tw_d);
296     rx = tw;
297     if (xx >= tw_d) xx = fmod(xx, tw_d);
298   }
299 
300   if (extendY == BL_EXTEND_MODE_PAD) {
301     th_d = 4294967296.0;
302   }
303   else {
304     ty = fmod(ty, th_d);
305     ry = th;
306     if (xy >= th_d) xy = fmod(xy, th_d);
307   }
308 
309   d.affine.xx.i64 = blFloorToInt64(xx * fpScale);
310   d.affine.xy.i64 = blFloorToInt64(xy * fpScale);
311   d.affine.yx.i64 = blFloorToInt64(yx * fpScale);
312   d.affine.yy.i64 = blFloorToInt64(yy * fpScale);
313 
314   d.affine.tx.i64 = blFloorToInt64(tx * fpScale);
315   d.affine.ty.i64 = blFloorToInt64(ty * fpScale);
316   d.affine.rx.i64 = blBitShl(int64_t(rx), 32);
317   d.affine.ry.i64 = blBitShl(int64_t(ry), 32);
318 
319   d.affine.ox.i32Hi = ox;
320   d.affine.ox.i32Lo = blMaxValue<int32_t>();
321   d.affine.oy.i32Hi = oy;
322   d.affine.oy.i32Lo = blMaxValue<int32_t>();
323 
324   d.affine.tw = tw_d;
325   d.affine.th = th_d;
326 
327   d.affine.xx2.u64 = d.affine.xx.u64 << 1u;
328   d.affine.xy2.u64 = d.affine.xy.u64 << 1u;
329 
330   if (extendX >= BL_EXTEND_MODE_REPEAT && d.affine.xx2.u32Hi >= uint32_t(tw)) d.affine.xx2.u32Hi %= uint32_t(tw);
331   if (extendY >= BL_EXTEND_MODE_REPEAT && d.affine.xy2.u32Hi >= uint32_t(th)) d.affine.xy2.u32Hi %= uint32_t(th);
332 
333   if (opt) {
334     d.affine.addrMul[0] = bytesPerPixel;
335     d.affine.addrMul[1] = int16_t(d.src.stride);
336   }
337   else {
338     d.affine.addrMul[0] = 0;
339     d.affine.addrMul[1] = 0;
340   }
341 
342   return fetchType;
343 }
344 
345 // ============================================================================
346 // [BLPipeFetchData - Init Gradient]
347 // ============================================================================
348 
blPipeFetchDataInitLinearGradient(BLPipeFetchData * fetchData,const BLLinearGradientValues & values,uint32_t extendMode,const BLMatrix2D & m)349 static BL_INLINE uint32_t blPipeFetchDataInitLinearGradient(BLPipeFetchData* fetchData, const BLLinearGradientValues& values, uint32_t extendMode, const BLMatrix2D& m) noexcept {
350   BLPipeFetchData::Gradient& d = fetchData->gradient;
351 
352   // Inverted transformation matrix.
353   BLMatrix2D mInv;
354   if (BLMatrix2D::invert(mInv, m) != BL_SUCCESS)
355     return BL_PIPE_FETCH_TYPE_FAILURE;
356 
357   BLPoint p0(values.x0, values.y0);
358   BLPoint p1(values.x1, values.y1);
359 
360   uint32_t lutSize = d.lut.size;
361   BL_ASSERT(lutSize > 0);
362 
363   bool isPad     = extendMode == BL_EXTEND_MODE_PAD;
364   bool isReflect = extendMode == BL_EXTEND_MODE_REFLECT;
365 
366   // Distance between [x0, y0] and [x1, y1], before transform.
367   double ax = p1.x - p0.x;
368   double ay = p1.y - p0.y;
369   double dist = ax * ax + ay * ay;
370 
371   // Invert origin and move it to the center of the pixel.
372   BLPoint o = BLPoint(0.5, 0.5) - m.mapPoint(p0);
373 
374   double dt = ax * mInv.m00 + ay * mInv.m01;
375   double dy = ax * mInv.m10 + ay * mInv.m11;
376 
377   double scale = double(int64_t(uint64_t(lutSize) << 32)) / dist;
378   double offset = o.x * dt + o.y * dy;
379 
380   dt *= scale;
381   dy *= scale;
382   offset *= scale;
383 
384   d.linear.dy.i64 = blFloorToInt64(dy);
385   d.linear.dt.i64 = blFloorToInt64(dt);
386   d.linear.dt2.u64 = d.linear.dt.u64 << 1;
387   d.linear.pt[0].i64 = blFloorToInt64(offset);
388   d.linear.pt[1].u64 = d.linear.pt[0].u64 + d.linear.dt.u64;
389 
390   uint32_t rorSize = isReflect ? lutSize * 2u : lutSize;
391   d.linear.rep.u32Hi = isPad ? uint32_t(0xFFFFFFFFu) : uint32_t(rorSize - 1u);
392   d.linear.rep.u32Lo = 0xFFFFFFFFu;
393   d.linear.msk.u     = isPad ? (lutSize - 1u) * 0x00010001u : (lutSize * 2u - 1u) * 0x00010001u;
394 
395   return isPad ? BL_PIPE_FETCH_TYPE_GRADIENT_LINEAR_PAD : BL_PIPE_FETCH_TYPE_GRADIENT_LINEAR_ROR;
396 }
397 
398 // The radial gradient uses the following equation:
399 //
400 //    b = x * fx + y * fy
401 //    d = x^2 * (r^2 - fy^2) + y^2 * (r^2 - fx^2) + x*y * (2*fx*fy)
402 //
403 //    pos = ((b + sqrt(d))) * scale)
404 //
405 // Simplified to:
406 //
407 //    C1 = r^2 - fy^2
408 //    C2 = r^2 - fx^2
409 //    C3 = 2 * fx * fy
410 //
411 //    b = x*fx + y*fy
412 //    d = x^2 * C1 + y^2 * C2 + x*y * C3
413 //
414 //    pos = ((b + sqrt(d))) * scale)
415 //
416 // Radial gradient function can be defined as follows:
417 //
418 //    D = C1*(x^2) + C2*(y^2) + C3*(x*y)
419 //
420 // Which could be rewritten as:
421 //
422 //    D = D1 + D2 + D3
423 //
424 //    Where: D1 = C1*(x^2)
425 //           D2 = C2*(y^2)
426 //           D3 = C3*(x*y)
427 //
428 // The variables `x` and `y` increase linearly, thus we can use multiple
429 // differentiation to get delta (d) and delta-of-delta (dd).
430 //
431 // Deltas for `C*(x^2)` at `t`:
432 //
433 //   C*x*x: 1st delta `d`  at step `t`: C*(t^2) + 2*C*x
434 //   C*x*x: 2nd delta `dd` at step `t`: 2*C *t^2
435 //
436 //   ( Hint, use Mathematica DifferenceDelta[x*x*C, {x, 1, t}] )
437 //
438 // Deltas for `C*(x*y)` at `t`:
439 //
440 //   C*x*y: 1st delta `d`  at step `tx/ty`: C*x*ty + C*y*tx + C*tx*ty
441 //   C*x*y: 2nd delta `dd` at step `tx/ty`: 2*C * tx*ty
blPipeFetchDataInitRadialGradient(BLPipeFetchData * fetchData,const BLRadialGradientValues & values,uint32_t extendMode,const BLMatrix2D & m)442 static BL_INLINE uint32_t blPipeFetchDataInitRadialGradient(BLPipeFetchData* fetchData, const BLRadialGradientValues& values, uint32_t extendMode, const BLMatrix2D& m) noexcept {
443   BLPipeFetchData::Gradient& d = fetchData->gradient;
444 
445   // Inverted transformation matrix.
446   BLMatrix2D mInv;
447   if (BLMatrix2D::invert(mInv, m) != BL_SUCCESS)
448     return BL_PIPE_FETCH_TYPE_FAILURE;
449 
450   BLPoint c(values.x0, values.y0);
451   BLPoint f(values.x1, values.y1);
452 
453   double r = values.r0;
454   uint32_t lutSize = d.lut.size;
455 
456   BL_ASSERT(lutSize != 0);
457   BL_ASSERT(extendMode < BL_EXTEND_MODE_SIMPLE_COUNT);
458 
459   BLPoint fOrig = f;
460   f -= c;
461 
462   double fxfx = f.x * f.x;
463   double fyfy = f.y * f.y;
464 
465   double rr = r * r;
466   double dd = rr - fxfx - fyfy;
467 
468   // If the focal point is near the border we move it slightly to prevent
469   // division by zero. This idea comes from AntiGrain library.
470   if (isNearZero(dd)) {
471     if (!isNearZero(f.x)) f.x += (f.x < 0.0) ? 0.5 : -0.5;
472     if (!isNearZero(f.y)) f.y += (f.y < 0.0) ? 0.5 : -0.5;
473 
474     fxfx = f.x * f.x;
475     fyfy = f.y * f.y;
476     dd = rr - fxfx - fyfy;
477   }
478 
479   double scale = double(int(lutSize)) / dd;
480   double ax = rr - fyfy;
481   double ay = rr - fxfx;
482 
483   d.radial.ax = ax;
484   d.radial.ay = ay;
485   d.radial.fx = f.x;
486   d.radial.fy = f.y;
487 
488   double xx = mInv.m00;
489   double xy = mInv.m01;
490   double yx = mInv.m10;
491   double yy = mInv.m11;
492 
493   d.radial.xx = xx;
494   d.radial.xy = xy;
495   d.radial.yx = yx;
496   d.radial.yy = yy;
497   d.radial.ox = (mInv.m20 - fOrig.x) + 0.5 * (xx + yx);
498   d.radial.oy = (mInv.m21 - fOrig.y) + 0.5 * (xy + yy);
499 
500   double ax_xx = ax * xx;
501   double ay_xy = ay * xy;
502   double fx_xx = f.x * xx;
503   double fy_xy = f.y * xy;
504 
505   d.radial.dd = ax_xx * xx + ay_xy * xy + 2.0 * (fx_xx * fy_xy);
506   d.radial.bd = fx_xx + fy_xy;
507 
508   d.radial.ddx = 2.0 * (ax_xx + fy_xy * f.x);
509   d.radial.ddy = 2.0 * (ay_xy + fx_xx * f.y);
510 
511   d.radial.ddd = 2.0 * d.radial.dd;
512   d.radial.scale = scale;
513   d.radial.maxi = (extendMode == BL_EXTEND_MODE_REFLECT) ? int(lutSize * 2 - 1) : int(lutSize - 1);
514 
515   return BL_PIPE_FETCH_TYPE_GRADIENT_RADIAL_PAD + extendMode;
516 }
517 
blPipeFetchDataInitConicalGradient(BLPipeFetchData * fetchData,const BLConicalGradientValues & values,uint32_t extendMode,const BLMatrix2D & m)518 static BL_INLINE uint32_t blPipeFetchDataInitConicalGradient(BLPipeFetchData* fetchData, const BLConicalGradientValues& values, uint32_t extendMode, const BLMatrix2D& m) noexcept {
519   BLPipeFetchData::Gradient& d = fetchData->gradient;
520 
521   BLPoint c(values.x0, values.y0);
522   double angle = values.angle;
523 
524   uint32_t lutSize = d.lut.size;
525   uint32_t tableId = blBitCtz(lutSize) - 8;
526   BL_ASSERT(tableId < BLCommonTable::kTableCount);
527 
528   BLMatrix2D mNew(m);
529   mNew.rotate(angle, c);
530 
531   // Invert the origin and move it to the center of the pixel.
532   c = BLPoint(0.5, 0.5) - mNew.mapPoint(c);
533 
534   BLMatrix2D mInv;
535   if (BLMatrix2D::invert(mInv, mNew) != BL_SUCCESS)
536     return BL_PIPE_FETCH_TYPE_FAILURE;
537 
538   d.conical.xx = mInv.m00;
539   d.conical.xy = mInv.m01;
540   d.conical.yx = mInv.m10;
541   d.conical.yy = mInv.m11;
542   d.conical.ox = c.x * mInv.m00 + c.y * mInv.m10;
543   d.conical.oy = c.x * mInv.m01 + c.y * mInv.m11;
544 
545   d.conical.consts = &blCommonTable.xmm_f_con[tableId];
546   d.conical.maxi = int(lutSize - 1);
547 
548   return BL_PIPE_FETCH_TYPE_GRADIENT_CONICAL;
549 }
550 
initGradient(uint32_t gradientType,const void * values,uint32_t extendMode,const BLGradientLUT * lut,const BLMatrix2D & m)551 uint32_t BLPipeFetchData::initGradient(uint32_t gradientType, const void* values, uint32_t extendMode, const BLGradientLUT* lut, const BLMatrix2D& m) noexcept {
552   // Initialize LUT.
553   this->gradient.lut.data = lut->data();
554   this->gradient.lut.size = uint32_t(lut->size);
555 
556   // Initialize gradient by type.
557   switch (gradientType) {
558     case BL_GRADIENT_TYPE_LINEAR:
559       return blPipeFetchDataInitLinearGradient(this, *static_cast<const BLLinearGradientValues*>(values), extendMode, m);
560 
561     case BL_GRADIENT_TYPE_RADIAL:
562       return blPipeFetchDataInitRadialGradient(this, *static_cast<const BLRadialGradientValues*>(values), extendMode, m);
563 
564     case BL_GRADIENT_TYPE_CONICAL:
565       return blPipeFetchDataInitConicalGradient(this, *static_cast<const BLConicalGradientValues*>(values), extendMode, m);
566 
567     default:
568       // Should not happen, but be defensive.
569       return BL_PIPE_FETCH_TYPE_FAILURE;
570   }
571 }
572