lottie/render: add neon path srcOver compostion mode for solid src.
[platform/core/uifw/lottie-player.git] / src / vector / vdrawhelper.cpp
1 #include "vdrawhelper.h"
2 #include <climits>
3 #include <cstring>
4 #include <mutex>
5 #include <unordered_map>
6
7 class VGradientCache {
8 public:
9     struct CacheInfo : public VSpanData::Pinnable {
10         inline CacheInfo(VGradientStops s) : stops(std::move(s)) {}
11         uint32_t       buffer32[VGradient::colorTableSize];
12         VGradientStops stops;
13         bool           alpha;
14     };
15
16     typedef std::unordered_multimap<uint64_t, std::shared_ptr<const CacheInfo>>
17          VGradientColorTableHash;
18     bool generateGradientColorTable(const VGradientStops &stops,
19                                     uint32_t *colorTable, int size);
20     inline const std::shared_ptr<const CacheInfo> getBuffer(
21         const VGradient &gradient)
22     {
23         uint64_t                         hash_val = 0;
24         std::shared_ptr<const CacheInfo> info;
25
26         const VGradientStops &stops = gradient.mStops;
27         for (uint i = 0; i < stops.size() && i <= 2; i++)
28             hash_val += stops[i].second.premulARGB();
29
30         cacheAccess.lock();
31
32         int count = cache.count(hash_val);
33         if (!count) {
34             // key is not present in the hash
35             info = addCacheElement(hash_val, gradient);
36         } else if (count == 1) {
37             VGradientColorTableHash::const_iterator it = cache.find(hash_val);
38             if (it->second->stops == stops) {
39                 info = it->second;
40             } else {
41                 // didn't find an exact match
42                 info = addCacheElement(hash_val, gradient);
43             }
44         } else {
45             // we have a multiple data with same key
46             auto range = cache.equal_range(hash_val);
47             for (auto i = range.first; i != range.second; ++i) {
48                 if (i->second->stops == stops) {
49                     info = i->second;
50                     break;
51                 }
52             }
53             if (!info) {
54                 // didn't find an exact match
55                 info = addCacheElement(hash_val, gradient);
56             }
57         }
58         cacheAccess.unlock();
59         return info;
60     }
61
62 protected:
63     inline uint                            maxCacheSize() const { return 60; }
64     const std::shared_ptr<const CacheInfo> addCacheElement(
65         uint64_t hash_val, const VGradient &gradient)
66     {
67         if (cache.size() == maxCacheSize()) {
68             int count = rand() % maxCacheSize();
69             while (count--) {
70                 cache.erase(cache.begin());
71             }
72         }
73         auto cache_entry = std::make_shared<CacheInfo>(gradient.mStops);
74         cache_entry->alpha = generateGradientColorTable(
75             gradient.mStops, cache_entry->buffer32, VGradient::colorTableSize);
76         cache.insert(std::make_pair(hash_val, cache_entry));
77         return cache_entry;
78     }
79
80     VGradientColorTableHash cache;
81     std::mutex              cacheAccess;
82 };
83
84 bool VGradientCache::generateGradientColorTable(const VGradientStops &stops,
85                                                 uint32_t *colorTable, int size)
86 {
87     int                  dist, idist, pos = 0, i;
88     bool                 alpha = false;
89     int                  stopCount = stops.size();
90     const VGradientStop *curr, *next, *start;
91     uint32_t             curColor, nextColor;
92     float                delta, t, incr, fpos;
93
94     start = stops.data();
95     curr = start;
96     if (!curr->second.isOpaque()) alpha = true;
97     curColor = curr->second.premulARGB();
98     incr = 1.0 / (float)size;
99     fpos = 1.5 * incr;
100
101     colorTable[pos++] = curColor;
102
103     while (fpos <= curr->first) {
104         colorTable[pos] = colorTable[pos - 1];
105         pos++;
106         fpos += incr;
107     }
108
109     for (i = 0; i < stopCount - 1; ++i) {
110         curr = (start + i);
111         next = (start + i + 1);
112         delta = 1 / (next->first - curr->first);
113         if (!next->second.isOpaque()) alpha = true;
114         nextColor = next->second.premulARGB();
115         while (fpos < next->first && pos < size) {
116             t = (fpos - curr->first) * delta;
117             dist = (int)(255 * t);
118             idist = 255 - dist;
119             colorTable[pos] =
120                 INTERPOLATE_PIXEL_255(curColor, idist, nextColor, dist);
121             ++pos;
122             fpos += incr;
123         }
124         curColor = nextColor;
125     }
126
127     for (; pos < size; ++pos) colorTable[pos] = curColor;
128
129     // Make sure the last color stop is represented at the end of the table
130     colorTable[size - 1] = curColor;
131     return alpha;
132 }
133
134 static VGradientCache VGradientCacheInstance;
135
136 void VRasterBuffer::init()
137 {
138     mBuffer = nullptr;
139     mWidth = 0;
140     mHeight = 0;
141     mCompositionMode = VPainter::CompModeSrcOver;
142 }
143
144 void VRasterBuffer::clear()
145 {
146     memset(mBuffer, 0, mHeight * mBytesPerLine);
147 }
148
149 VBitmap::Format VRasterBuffer::prepare(VBitmap *image)
150 {
151     mBuffer = (uchar *)image->bits();
152     mWidth = image->width();
153     mHeight = image->height();
154     mBytesPerPixel = 4;
155     mBytesPerLine = image->stride();
156
157     mFormat = image->format();
158     // drawHelper = qDrawHelper + format;
159     return mFormat;
160 }
161
162 void VSpanData::init(VRasterBuffer *image)
163 {
164     mRasterBuffer = image;
165     mSystemClip = VRect(0, 0, image->width(), image->height());
166     mType = VSpanData::Type::None;
167     mBlendFunc = nullptr;
168     mUnclippedBlendFunc = nullptr;
169 }
170
171 extern CompositionFunction             COMP_functionForMode_C[];
172 extern CompositionFunctionSolid        COMP_functionForModeSolid_C[];
173 static const CompositionFunction *     functionForMode = COMP_functionForMode_C;
174 static const CompositionFunctionSolid *functionForModeSolid =
175     COMP_functionForModeSolid_C;
176
177 /*
178  *  Gradient Draw routines
179  *
180  */
181
182 #define FIXPT_BITS 8
183 #define FIXPT_SIZE (1 << FIXPT_BITS)
184 static inline void getLinearGradientValues(LinearGradientValues *v,
185                                            const VSpanData *     data)
186 {
187     const VGradientData *grad = &data->mGradient;
188     v->dx = grad->linear.x2 - grad->linear.x1;
189     v->dy = grad->linear.y2 - grad->linear.y1;
190     v->l = v->dx * v->dx + v->dy * v->dy;
191     v->off = 0;
192     if (v->l != 0) {
193         v->dx /= v->l;
194         v->dy /= v->l;
195         v->off = -v->dx * grad->linear.x1 - v->dy * grad->linear.y1;
196     }
197 }
198
199 static inline void getRadialGradientValues(RadialGradientValues *v,
200                                            const VSpanData *     data)
201 {
202     const VGradientData &gradient = data->mGradient;
203     v->dx = gradient.radial.cx - gradient.radial.fx;
204     v->dy = gradient.radial.cy - gradient.radial.fy;
205
206     v->dr = gradient.radial.cradius - gradient.radial.fradius;
207     v->sqrfr = gradient.radial.fradius * gradient.radial.fradius;
208
209     v->a = v->dr * v->dr - v->dx * v->dx - v->dy * v->dy;
210     v->inv2a = 1 / (2 * v->a);
211
212     v->extended = !vIsZero(gradient.radial.fradius) || v->a <= 0;
213 }
214
215 static inline int gradientClamp(const VGradientData *grad, int ipos)
216 {
217     int limit;
218
219     if (grad->mSpread == VGradient::Spread::Repeat) {
220         ipos = ipos % VGradient::colorTableSize;
221         ipos = ipos < 0 ? VGradient::colorTableSize + ipos : ipos;
222     } else if (grad->mSpread == VGradient::Spread::Reflect) {
223         limit = VGradient::colorTableSize * 2;
224         ipos = ipos % limit;
225         ipos = ipos < 0 ? limit + ipos : ipos;
226         ipos = ipos >= VGradient::colorTableSize ? limit - 1 - ipos : ipos;
227     } else {
228         if (ipos < 0)
229             ipos = 0;
230         else if (ipos >= VGradient::colorTableSize)
231             ipos = VGradient::colorTableSize - 1;
232     }
233     return ipos;
234 }
235
236 static uint32_t gradientPixelFixed(const VGradientData *grad, int fixed_pos)
237 {
238     int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
239
240     return grad->mColorTable[gradientClamp(grad, ipos)];
241 }
242
243 static inline uint32_t gradientPixel(const VGradientData *grad, float pos)
244 {
245     int ipos = (int)(pos * (VGradient::colorTableSize - 1) + (float)(0.5));
246
247     return grad->mColorTable[gradientClamp(grad, ipos)];
248 }
249
250 void fetch_linear_gradient(uint32_t *buffer, const Operator *op,
251                            const VSpanData *data, int y, int x, int length)
252 {
253     float                t, inc;
254     const VGradientData *gradient = &data->mGradient;
255
256     bool  affine = true;
257     float rx = 0, ry = 0;
258     if (op->linear.l == 0) {
259         t = inc = 0;
260     } else {
261         rx = data->m21 * (y + float(0.5)) + data->m11 * (x + float(0.5)) +
262              data->dx;
263         ry = data->m22 * (y + float(0.5)) + data->m12 * (x + float(0.5)) +
264              data->dy;
265         t = op->linear.dx * rx + op->linear.dy * ry + op->linear.off;
266         inc = op->linear.dx * data->m11 + op->linear.dy * data->m12;
267         affine = !data->m13 && !data->m23;
268
269         if (affine) {
270             t *= (VGradient::colorTableSize - 1);
271             inc *= (VGradient::colorTableSize - 1);
272         }
273     }
274
275     const uint32_t *end = buffer + length;
276     if (affine) {
277         if (inc > float(-1e-5) && inc < float(1e-5)) {
278             memfill32(buffer, gradientPixelFixed(gradient, int(t * FIXPT_SIZE)),
279                       length);
280         } else {
281             if (t + inc * length < float(INT_MAX >> (FIXPT_BITS + 1)) &&
282                 t + inc * length > float(INT_MIN >> (FIXPT_BITS + 1))) {
283                 // we can use fixed point math
284                 int t_fixed = int(t * FIXPT_SIZE);
285                 int inc_fixed = int(inc * FIXPT_SIZE);
286                 while (buffer < end) {
287                     *buffer = gradientPixelFixed(gradient, t_fixed);
288                     t_fixed += inc_fixed;
289                     ++buffer;
290                 }
291             } else {
292                 // we have to fall back to float math
293                 while (buffer < end) {
294                     *buffer =
295                         gradientPixel(gradient, t / VGradient::colorTableSize);
296                     t += inc;
297                     ++buffer;
298                 }
299             }
300         }
301     } else {  // fall back to float math here as well
302         float rw = data->m23 * (y + float(0.5)) + data->m13 * (x + float(0.5)) +
303                    data->m33;
304         while (buffer < end) {
305             float x = rx / rw;
306             float y = ry / rw;
307             t = (op->linear.dx * x + op->linear.dy * y) + op->linear.off;
308
309             *buffer = gradientPixel(gradient, t);
310             rx += data->m11;
311             ry += data->m12;
312             rw += data->m13;
313             if (!rw) {
314                 rw += data->m13;
315             }
316             ++buffer;
317         }
318     }
319 }
320
321 static inline float radialDeterminant(float a, float b, float c)
322 {
323     return (b * b) - (4 * a * c);
324 }
325
326 static void fetch(uint32_t *buffer, uint32_t *end, const Operator *op,
327                   const VSpanData *data, float det, float delta_det,
328                   float delta_delta_det, float b, float delta_b)
329 {
330     if (op->radial.extended) {
331         while (buffer < end) {
332             uint32_t result = 0;
333             if (det >= 0) {
334                 float w = std::sqrt(det) - b;
335                 if (data->mGradient.radial.fradius + op->radial.dr * w >= 0)
336                     result = gradientPixel(&data->mGradient, w);
337             }
338
339             *buffer = result;
340
341             det += delta_det;
342             delta_det += delta_delta_det;
343             b += delta_b;
344
345             ++buffer;
346         }
347     } else {
348         while (buffer < end) {
349             *buffer++ = gradientPixel(&data->mGradient, std::sqrt(det) - b);
350
351             det += delta_det;
352             delta_det += delta_delta_det;
353             b += delta_b;
354         }
355     }
356 }
357
358 void fetch_radial_gradient(uint32_t *buffer, const Operator *op,
359                            const VSpanData *data, int y, int x, int length)
360 {
361     // avoid division by zero
362     if (vIsZero(op->radial.a)) {
363         memfill32(buffer, 0, length);
364         return;
365     }
366
367     float rx =
368         data->m21 * (y + float(0.5)) + data->dx + data->m11 * (x + float(0.5));
369     float ry =
370         data->m22 * (y + float(0.5)) + data->dy + data->m12 * (x + float(0.5));
371     bool affine = !data->m13 && !data->m23;
372
373     uint32_t *end = buffer + length;
374     if (affine) {
375         rx -= data->mGradient.radial.fx;
376         ry -= data->mGradient.radial.fy;
377
378         float inv_a = 1 / float(2 * op->radial.a);
379
380         const float delta_rx = data->m11;
381         const float delta_ry = data->m12;
382
383         float b = 2 * (op->radial.dr * data->mGradient.radial.fradius +
384                        rx * op->radial.dx + ry * op->radial.dy);
385         float delta_b =
386             2 * (delta_rx * op->radial.dx + delta_ry * op->radial.dy);
387         const float b_delta_b = 2 * b * delta_b;
388         const float delta_b_delta_b = 2 * delta_b * delta_b;
389
390         const float bb = b * b;
391         const float delta_bb = delta_b * delta_b;
392
393         b *= inv_a;
394         delta_b *= inv_a;
395
396         const float rxrxryry = rx * rx + ry * ry;
397         const float delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
398         const float rx_plus_ry = 2 * (rx * delta_rx + ry * delta_ry);
399         const float delta_rx_plus_ry = 2 * delta_rxrxryry;
400
401         inv_a *= inv_a;
402
403         float det =
404             (bb - 4 * op->radial.a * (op->radial.sqrfr - rxrxryry)) * inv_a;
405         float delta_det = (b_delta_b + delta_bb +
406                            4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) *
407                           inv_a;
408         const float delta_delta_det =
409             (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
410
411         fetch(buffer, end, op, data, det, delta_det, delta_delta_det, b,
412               delta_b);
413     } else {
414         float rw = data->m23 * (y + float(0.5)) + data->m33 +
415                    data->m13 * (x + float(0.5));
416
417         while (buffer < end) {
418             if (rw == 0) {
419                 *buffer = 0;
420             } else {
421                 float invRw = 1 / rw;
422                 float gx = rx * invRw - data->mGradient.radial.fx;
423                 float gy = ry * invRw - data->mGradient.radial.fy;
424                 float b = 2 * (op->radial.dr * data->mGradient.radial.fradius +
425                                gx * op->radial.dx + gy * op->radial.dy);
426                 float det = radialDeterminant(
427                     op->radial.a, b, op->radial.sqrfr - (gx * gx + gy * gy));
428
429                 uint32_t result = 0;
430                 if (det >= 0) {
431                     float detSqrt = std::sqrt(det);
432
433                     float s0 = (-b - detSqrt) * op->radial.inv2a;
434                     float s1 = (-b + detSqrt) * op->radial.inv2a;
435
436                     float s = vMax(s0, s1);
437
438                     if (data->mGradient.radial.fradius + op->radial.dr * s >= 0)
439                         result = gradientPixel(&data->mGradient, s);
440                 }
441
442                 *buffer = result;
443             }
444
445             rx += data->m11;
446             ry += data->m12;
447             rw += data->m13;
448
449             ++buffer;
450         }
451     }
452 }
453
454 static inline Operator getOperator(const VSpanData * data,
455                                    const VRle::Span *, int)
456 {
457     Operator op;
458     bool     solidSource = false;
459
460     switch (data->mType) {
461     case VSpanData::Type::Solid:
462         solidSource = (vAlpha(data->mSolid) == 255);
463         op.srcFetch = nullptr;
464         break;
465     case VSpanData::Type::LinearGradient:
466         solidSource = false;
467         getLinearGradientValues(&op.linear, data);
468         op.srcFetch = &fetch_linear_gradient;
469         break;
470     case VSpanData::Type::RadialGradient:
471         solidSource = false;
472         getRadialGradientValues(&op.radial, data);
473         op.srcFetch = &fetch_radial_gradient;
474         break;
475     default:
476         op.srcFetch = nullptr;
477         break;
478     }
479
480     op.mode = data->mRasterBuffer->mCompositionMode;
481     if (op.mode == VPainter::CompModeSrcOver && solidSource)
482         op.mode = VPainter::CompModeSrc;
483
484     op.funcSolid = functionForModeSolid[op.mode];
485     op.func = functionForMode[op.mode];
486
487     return op;
488 }
489
490 static void blendColorARGB(int count, const VRle::Span *spans, void *userData)
491 {
492     VSpanData *data = (VSpanData *)(userData);
493     Operator   op = getOperator(data, spans, count);
494     const uint color = data->mSolid;
495
496     if (op.mode == VPainter::CompModeSrc) {
497         // inline for performance
498         while (count--) {
499             uint *target = data->buffer(spans->x, spans->y);
500             if (spans->coverage == 255) {
501                 memfill32(target, color, spans->len);
502             } else {
503                 uint c = BYTE_MUL(color, spans->coverage);
504                 int  ialpha = 255 - spans->coverage;
505                 for (int i = 0; i < spans->len; ++i)
506                     target[i] = c + BYTE_MUL(target[i], ialpha);
507             }
508             ++spans;
509         }
510         return;
511     }
512
513     while (count--) {
514         uint *target = data->buffer(spans->x, spans->y);
515         op.funcSolid(target, spans->len, color, spans->coverage);
516         ++spans;
517     }
518 }
519
520 #define BLEND_GRADIENT_BUFFER_SIZE 2048
521 static void blendGradientARGB(int count, const VRle::Span *spans,
522                               void *userData)
523 {
524     VSpanData *data = (VSpanData *)(userData);
525     Operator   op = getOperator(data, spans, count);
526
527     unsigned int buffer[BLEND_GRADIENT_BUFFER_SIZE];
528
529     if (!op.srcFetch) return;
530
531     while (count--) {
532         uint *target = data->buffer(spans->x, spans->y);
533         int   length = spans->len;
534         while (length) {
535             int l = std::min(length, BLEND_GRADIENT_BUFFER_SIZE);
536             op.srcFetch(buffer, &op, data, spans->y, spans->x, l);
537             op.func(target, buffer, l, spans->coverage);
538             target += l;
539             length -= l;
540         }
541         ++spans;
542     }
543 }
544
545 void VSpanData::setup(const VBrush &brush, VPainter::CompositionMode /*mode*/,
546                       int /*alpha*/)
547 {
548     switch (brush.type()) {
549     case VBrush::Type::NoBrush:
550         mType = VSpanData::Type::None;
551         break;
552     case VBrush::Type::Solid:
553         mType = VSpanData::Type::Solid;
554         mSolid = brush.mColor.premulARGB();
555         break;
556     case VBrush::Type::LinearGradient: {
557         mType = VSpanData::Type::LinearGradient;
558         auto cacheInfo = VGradientCacheInstance.getBuffer(*brush.mGradient);
559         mGradient.mColorTable = cacheInfo->buffer32;
560         mGradient.mColorTableAlpha = cacheInfo->alpha;
561         mGradient.linear.x1 = brush.mGradient->linear.x1;
562         mGradient.linear.y1 = brush.mGradient->linear.y1;
563         mGradient.linear.x2 = brush.mGradient->linear.x2;
564         mGradient.linear.y2 = brush.mGradient->linear.y2;
565         mGradient.mSpread = brush.mGradient->mSpread;
566         setupMatrix(brush.mGradient->mMatrix);
567         break;
568     }
569     case VBrush::Type::RadialGradient: {
570         mType = VSpanData::Type::RadialGradient;
571         auto cacheInfo = VGradientCacheInstance.getBuffer(*brush.mGradient);
572         mGradient.mColorTable = cacheInfo->buffer32;
573         mGradient.mColorTableAlpha = cacheInfo->alpha;
574         mGradient.radial.cx = brush.mGradient->radial.cx;
575         mGradient.radial.cy = brush.mGradient->radial.cy;
576         mGradient.radial.fx = brush.mGradient->radial.fx;
577         mGradient.radial.fy = brush.mGradient->radial.fy;
578         mGradient.radial.cradius = brush.mGradient->radial.cradius;
579         mGradient.radial.fradius = brush.mGradient->radial.fradius;
580         mGradient.mSpread = brush.mGradient->mSpread;
581         setupMatrix(brush.mGradient->mMatrix);
582         break;
583     }
584     default:
585         break;
586     }
587     updateSpanFunc();
588 }
589
590 void VSpanData::setupMatrix(const VMatrix &matrix)
591 {
592     VMatrix inv = matrix.inverted();
593     m11 = inv.m11;
594     m12 = inv.m12;
595     m13 = inv.m13;
596     m21 = inv.m21;
597     m22 = inv.m22;
598     m23 = inv.m23;
599     m33 = inv.m33;
600     dx = inv.mtx;
601     dy = inv.mty;
602
603     // const bool affine = inv.isAffine();
604     //    fast_matrix = affine
605     //        && m11 * m11 + m21 * m21 < 1e4
606     //        && m12 * m12 + m22 * m22 < 1e4
607     //        && fabs(dx) < 1e4
608     //        && fabs(dy) < 1e4;
609 }
610
611 void VSpanData::updateSpanFunc()
612 {
613     switch (mType) {
614     case VSpanData::Type::None:
615         mUnclippedBlendFunc = nullptr;
616         break;
617     case VSpanData::Type::Solid:
618         mUnclippedBlendFunc = &blendColorARGB;
619         break;
620     case VSpanData::Type::LinearGradient:
621     case VSpanData::Type::RadialGradient: {
622         mUnclippedBlendFunc = &blendGradientARGB;
623         break;
624     }
625     default:
626         break;
627     }
628 }
629
630 #if !defined(__SSE2__) && !defined(__ARM_NEON__)
631 void memfill32(uint32_t *dest, uint32_t value, int length)
632 {
633     int n;
634
635     if (length <= 0) return;
636
637     // Cute hack to align future memcopy operation
638     // and do unroll the loop a bit. Not sure it is
639     // the most efficient, but will do for now.
640     n = (length + 7) / 8;
641     switch (length & 0x07) {
642     case 0:
643         do {
644             *dest++ = value;
645             VECTOR_FALLTHROUGH;
646         case 7:
647             *dest++ = value;
648             VECTOR_FALLTHROUGH;
649         case 6:
650             *dest++ = value;
651             VECTOR_FALLTHROUGH;
652         case 5:
653             *dest++ = value;
654             VECTOR_FALLTHROUGH;
655         case 4:
656             *dest++ = value;
657             VECTOR_FALLTHROUGH;
658         case 3:
659             *dest++ = value;
660             VECTOR_FALLTHROUGH;
661         case 2:
662             *dest++ = value;
663             VECTOR_FALLTHROUGH;
664         case 1:
665             *dest++ = value;
666         } while (--n > 0);
667     }
668 }
669 #endif
670
671 void vInitDrawhelperFunctions()
672 {
673     vInitBlendFunctions();
674
675 #if defined(__ARM_NEON__)
676     // update fast path for NEON
677     extern void comp_func_solid_SourceOver_neon(
678         uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
679
680     COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] =
681         comp_func_solid_SourceOver_neon;
682 #endif
683
684 #if defined(__SSE2__)
685     // update fast path for SSE2
686     extern void comp_func_solid_SourceOver_sse2(
687         uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
688     extern void comp_func_solid_Source_sse2(
689         uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
690     extern void comp_func_Source_sse2(uint32_t * dest, const uint32_t *src,
691                                       int length, uint32_t const_alpha);
692     extern void comp_func_SourceOver_sse2(uint32_t * dest, const uint32_t *src,
693                                           int length, uint32_t const_alpha);
694
695     COMP_functionForModeSolid_C[VPainter::CompModeSrc] =
696         comp_func_solid_Source_sse2;
697     COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] =
698         comp_func_solid_SourceOver_sse2;
699
700     COMP_functionForMode_C[VPainter::CompModeSrc] = comp_func_Source_sse2;
701     // COMP_functionForMode_C[VPainter::CompModeSrcOver] =
702     // comp_func_SourceOver_sse2;
703 #endif
704 }
705
706 V_CONSTRUCTOR_FUNCTION(vInitDrawhelperFunctions)