lottie/render: fixed rendering backend issue for arm.
[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(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 *spans, int spanCount)
456 {
457     Operator op;
458     bool     solidSource = false;
459
460     switch (data->mType) {
461     case VSpanData::Type::Solid:
462         solidSource = vAlpha(data->mSolid) & 0xFF;
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 =
500                 ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
501             if (spans->coverage == 255) {
502                 memfill32(target, color, spans->len);
503             } else {
504                 uint c = BYTE_MUL(color, spans->coverage);
505                 int  ialpha = 255 - spans->coverage;
506                 for (int i = 0; i < spans->len; ++i)
507                     target[i] = c + BYTE_MUL(target[i], ialpha);
508             }
509             ++spans;
510         }
511         return;
512     }
513
514     while (count--) {
515         uint *target =
516             ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
517         op.funcSolid(target, spans->len, color, spans->coverage);
518         ++spans;
519     }
520 }
521
522 #define BLEND_GRADIENT_BUFFER_SIZE 2048
523 static void blendGradientARGB(int count, const VRle::Span *spans,
524                               void *userData)
525 {
526     VSpanData *data = (VSpanData *)(userData);
527     Operator   op = getOperator(data, spans, count);
528
529     unsigned int buffer[BLEND_GRADIENT_BUFFER_SIZE];
530
531     if (!op.srcFetch) return;
532
533     while (count--) {
534         uint *target =
535             ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
536         int length = spans->len;
537         while (length) {
538             int l = std::min(length, BLEND_GRADIENT_BUFFER_SIZE);
539             op.srcFetch(buffer, &op, data, spans->y, spans->x, l);
540             op.func(target, buffer, l, spans->coverage);
541             target += l;
542             length -= l;
543         }
544         ++spans;
545     }
546 }
547
548 void VSpanData::setup(const VBrush &brush, VPainter::CompositionMode mode,
549                       int alpha)
550 {
551     switch (brush.type()) {
552     case VBrush::Type::NoBrush:
553         mType = VSpanData::Type::None;
554         break;
555     case VBrush::Type::Solid:
556         mType = VSpanData::Type::Solid;
557         mSolid = brush.mColor.premulARGB();
558         break;
559     case VBrush::Type::LinearGradient: {
560         mType = VSpanData::Type::LinearGradient;
561         auto cacheInfo = VGradientCacheInstance.getBuffer(*brush.mGradient);
562         mGradient.mColorTable = cacheInfo->buffer32;
563         mGradient.mColorTableAlpha = cacheInfo->alpha;
564         mGradient.linear.x1 = brush.mGradient->linear.x1;
565         mGradient.linear.y1 = brush.mGradient->linear.y1;
566         mGradient.linear.x2 = brush.mGradient->linear.x2;
567         mGradient.linear.y2 = brush.mGradient->linear.y2;
568         mGradient.mSpread = brush.mGradient->mSpread;
569         setupMatrix(brush.mGradient->mMatrix);
570         break;
571     }
572     case VBrush::Type::RadialGradient: {
573         mType = VSpanData::Type::RadialGradient;
574         auto cacheInfo = VGradientCacheInstance.getBuffer(*brush.mGradient);
575         mGradient.mColorTable = cacheInfo->buffer32;
576         mGradient.mColorTableAlpha = cacheInfo->alpha;
577         mGradient.radial.cx = brush.mGradient->radial.cx;
578         mGradient.radial.cy = brush.mGradient->radial.cy;
579         mGradient.radial.fx = brush.mGradient->radial.fx;
580         mGradient.radial.fy = brush.mGradient->radial.fy;
581         mGradient.radial.cradius = brush.mGradient->radial.cradius;
582         mGradient.radial.fradius = brush.mGradient->radial.fradius;
583         mGradient.mSpread = brush.mGradient->mSpread;
584         setupMatrix(brush.mGradient->mMatrix);
585         break;
586     }
587     default:
588         break;
589     }
590     updateSpanFunc();
591 }
592
593 void VSpanData::setupMatrix(const VMatrix &matrix)
594 {
595     VMatrix inv = matrix.inverted();
596     m11 = inv.m11;
597     m12 = inv.m12;
598     m13 = inv.m13;
599     m21 = inv.m21;
600     m22 = inv.m22;
601     m23 = inv.m23;
602     m33 = inv.m33;
603     dx = inv.mtx;
604     dy = inv.mty;
605
606     // const bool affine = inv.isAffine();
607     //    fast_matrix = affine
608     //        && m11 * m11 + m21 * m21 < 1e4
609     //        && m12 * m12 + m22 * m22 < 1e4
610     //        && fabs(dx) < 1e4
611     //        && fabs(dy) < 1e4;
612 }
613
614 void VSpanData::updateSpanFunc()
615 {
616     switch (mType) {
617     case VSpanData::Type::None:
618         mUnclippedBlendFunc = nullptr;
619         break;
620     case VSpanData::Type::Solid:
621         mUnclippedBlendFunc = &blendColorARGB;
622         break;
623     case VSpanData::Type::LinearGradient:
624     case VSpanData::Type::RadialGradient: {
625         mUnclippedBlendFunc = &blendGradientARGB;
626         break;
627     }
628     default:
629         break;
630     }
631 }
632
633 #if !defined(__SSE2__)
634 void memfill32(uint32_t *dest, uint32_t value, int length)
635 {
636     int n;
637
638     if (length <= 0) return;
639
640     // Cute hack to align future memcopy operation
641     // and do unroll the loop a bit. Not sure it is
642     // the most efficient, but will do for now.
643     n = (length + 7) / 8;
644     switch (length & 0x07) {
645     case 0:
646         do {
647             *dest++ = value;
648             VECTOR_FALLTHROUGH;
649         case 7:
650             *dest++ = value;
651             VECTOR_FALLTHROUGH;
652         case 6:
653             *dest++ = value;
654             VECTOR_FALLTHROUGH;
655         case 5:
656             *dest++ = value;
657             VECTOR_FALLTHROUGH;
658         case 4:
659             *dest++ = value;
660             VECTOR_FALLTHROUGH;
661         case 3:
662             *dest++ = value;
663             VECTOR_FALLTHROUGH;
664         case 2:
665             *dest++ = value;
666             VECTOR_FALLTHROUGH;
667         case 1:
668             *dest++ = value;
669         } while (--n > 0);
670     }
671 }
672 #endif
673
674 void vInitDrawhelperFunctions()
675 {
676     vInitBlendFunctions();
677
678 #if defined(__ARM_NEON__)
679     // update fast path for NEON
680 //    extern void comp_func_solid_SourceOver_neon(
681 //        uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
682 //    extern void comp_func_solid_Source_neon(
683 //        uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
684
685 //    COMP_functionForModeSolid_C[VPainter::CompModeSrc] =
686 //        comp_func_solid_Source_neon;
687 //    COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] =
688 //        comp_func_solid_SourceOver_neon;
689 #endif
690
691 #if defined(__SSE2__)
692     // update fast path for SSE2
693     extern void comp_func_solid_SourceOver_sse2(
694         uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
695     extern void comp_func_solid_Source_sse2(
696         uint32_t * dest, int length, uint32_t color, uint32_t const_alpha);
697     extern void comp_func_Source_sse2(uint32_t * dest, const uint32_t *src,
698                                       int length, uint32_t const_alpha);
699     extern void comp_func_SourceOver_sse2(uint32_t * dest, const uint32_t *src,
700                                           int length, uint32_t const_alpha);
701
702     COMP_functionForModeSolid_C[VPainter::CompModeSrc] =
703         comp_func_solid_Source_sse2;
704     COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] =
705         comp_func_solid_SourceOver_sse2;
706
707     COMP_functionForMode_C[VPainter::CompModeSrc] = comp_func_Source_sse2;
708     // COMP_functionForMode_C[VPainter::CompModeSrcOver] =
709     // comp_func_SourceOver_sse2;
710 #endif
711 }
712
713 V_CONSTRUCTOR_FUNCTION(vInitDrawhelperFunctions)