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