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