home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 275 / DPCS0111DVD.ISO / Toolkit / Audio-Visual / VirtualDub / Source / VirtualDub-1.9.10-src.7z / src / Kasumi / source / region.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2009-09-14  |  31.8 KB  |  1,335 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Graphics support library
  3. //    Copyright (C) 1998-2007 Avery Lee
  4. //
  5. //    This program is free software; you can redistribute it and/or modify
  6. //    it under the terms of the GNU General Public License as published by
  7. //    the Free Software Foundation; either version 2 of the License, or
  8. //    (at your option) any later version.
  9. //
  10. //    This program is distributed in the hope that it will be useful,
  11. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. //    GNU General Public License for more details.
  14. //
  15. //    You should have received a copy of the GNU General Public License
  16. //    along with this program; if not, write to the Free Software
  17. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19. #include <vd2/Kasumi/pixmap.h>
  20. #include <vd2/Kasumi/pixmaputils.h>
  21. #include <vd2/Kasumi/region.h>
  22. #include <vd2/system/math.h>
  23. #include <vd2/system/vdstl.h>
  24.  
  25. void VDPixmapRegion::swap(VDPixmapRegion& x) {
  26.     mSpans.swap(x.mSpans);
  27.     std::swap(mBounds, x.mBounds);
  28. }
  29.  
  30. VDPixmapPathRasterizer::VDPixmapPathRasterizer()
  31.     : mpEdgeBlocks(NULL)
  32.     , mpFreeEdgeBlocks(NULL)
  33.     , mEdgeBlockIdx(kEdgeBlockMax)
  34.     , mpScanBuffer(NULL)
  35. {
  36.     ClearScanBuffer();
  37. }
  38.  
  39. VDPixmapPathRasterizer::VDPixmapPathRasterizer(const VDPixmapPathRasterizer&)
  40.     : mpEdgeBlocks(NULL)
  41.     , mpFreeEdgeBlocks(NULL)
  42.     , mEdgeBlockIdx(kEdgeBlockMax)
  43.     , mpScanBuffer(NULL)
  44. {
  45.     ClearScanBuffer();
  46. }
  47.  
  48. VDPixmapPathRasterizer::~VDPixmapPathRasterizer() {
  49.     Clear();
  50.     FreeEdgeLists();
  51. }
  52.  
  53. VDPixmapPathRasterizer& VDPixmapPathRasterizer::operator=(const VDPixmapPathRasterizer&) {
  54.     return *this;
  55. }
  56.  
  57. void VDPixmapPathRasterizer::Clear() {
  58.     ClearEdgeList();
  59.     ClearScanBuffer();
  60. }
  61.  
  62. void VDPixmapPathRasterizer::QuadraticBezier(const vdint2 *pts) {
  63.     int x0 = pts[0].x;
  64.     int x1 = pts[1].x;
  65.     int x2 = pts[2].x;
  66.     int y0 = pts[0].y;
  67.     int y1 = pts[1].y;
  68.     int y2 = pts[2].y;
  69.  
  70.     // P = (1-t)^2*P0 + 2t(1-t)*P1 + t^2*P2
  71.     // P = (1-2t+t^2)P0 + 2(t-t^2)P1 + t^2*P2
  72.     // P = (P0-2P1+P2)t^2 + 2(P1-P0)t + P0
  73.  
  74.     int cx2 =    x0-2*x1+x2;
  75.     int cx1 = -2*x0+2*x1;
  76.     int cx0 =    x0;
  77.  
  78.     int cy2 =    y0-2*y1+y2;
  79.     int cy1 = -2*y0+2*y1;
  80.     int cy0 =    y0;
  81.  
  82.     // This equation is from Graphics Gems I.
  83.     //
  84.     // The idea is that since we're approximating a cubic curve with lines,
  85.     // any error we incur is due to the curvature of the line, which we can
  86.     // estimate by calculating the maximum acceleration of the curve.  For
  87.     // a cubic, the acceleration (second derivative) is a line, meaning that
  88.     // the absolute maximum acceleration must occur at either the beginning
  89.     // (|c2|) or the end (|c2+c3|).  Our bounds here are a little more
  90.     // conservative than that, but that's okay.
  91.     //
  92.     // If the acceleration of the parametric formula is zero (c2 = c3 = 0),
  93.     // that component of the curve is linear and does not incur any error.
  94.     // If a=0 for both X and Y, the curve is a line segment and we can
  95.     // use a step size of 1.
  96.  
  97.     int maxaccel1 = abs(cy2);
  98.     int maxaccel2 = abs(cx2);
  99.  
  100.     int maxaccel = maxaccel1 > maxaccel2 ? maxaccel1 : maxaccel2;
  101.     int h = 1;
  102.  
  103.     while(maxaccel > 8 && h < 1024) {
  104.         maxaccel >>= 2;
  105.         h += h;
  106.     }
  107.  
  108.     int lastx = x0;
  109.     int lasty = y0;
  110.  
  111.     // compute forward differences
  112.     sint64 h1 = (sint64)(0x40000000 / h) << 2;
  113.     sint64 h2 = h1/h;
  114.  
  115.     sint64 ax0 = (sint64)cx0 << 32;
  116.     sint64 ax1 =   h1*(sint64)cx1 +   h2*(sint64)cx2;
  117.     sint64 ax2 = 2*h2*(sint64)cx2;
  118.  
  119.     sint64 ay0 = (sint64)cy0 << 32;
  120.     sint64 ay1 =   h1*(sint64)cy1 +   h2*(sint64)cy2;
  121.     sint64 ay2 = 2*h2*(sint64)cy2;
  122.  
  123.     // round, not truncate
  124.     ax0 += 0x80000000;
  125.     ay0 += 0x80000000;
  126.  
  127.     do {
  128.         ax0 += ax1;
  129.         ax1 += ax2;
  130.         ay0 += ay1;
  131.         ay1 += ay2;
  132.  
  133.         int xi = (int)((uint64)ax0 >> 32);
  134.         int yi = (int)((uint64)ay0 >> 32);
  135.  
  136.         FastLine(lastx, lasty, xi, yi);
  137.         lastx = xi;
  138.         lasty = yi;
  139.     } while(--h);
  140. }
  141.  
  142. void VDPixmapPathRasterizer::CubicBezier(const vdint2 *pts) {
  143.     int x0 = pts[0].x;
  144.     int x1 = pts[1].x;
  145.     int x2 = pts[2].x;
  146.     int x3 = pts[3].x;
  147.     int y0 = pts[0].y;
  148.     int y1 = pts[1].y;
  149.     int y2 = pts[2].y;
  150.     int y3 = pts[3].y;
  151.  
  152.     int cx3 = -  x0+3*x1-3*x2+x3;
  153.     int cx2 =  3*x0-6*x1+3*x2;
  154.     int cx1 = -3*x0+3*x1;
  155.     int cx0 =    x0;
  156.  
  157.     int cy3 = -  y0+3*y1-3*y2+y3;
  158.     int cy2 =  3*y0-6*y1+3*y2;
  159.     int cy1 = -3*y0+3*y1;
  160.     int cy0 =    y0;
  161.  
  162.     // This equation is from Graphics Gems I.
  163.     //
  164.     // The idea is that since we're approximating a cubic curve with lines,
  165.     // any error we incur is due to the curvature of the line, which we can
  166.     // estimate by calculating the maximum acceleration of the curve.  For
  167.     // a cubic, the acceleration (second derivative) is a line, meaning that
  168.     // the absolute maximum acceleration must occur at either the beginning
  169.     // (|c2|) or the end (|c2+c3|).  Our bounds here are a little more
  170.     // conservative than that, but that's okay.
  171.     //
  172.     // If the acceleration of the parametric formula is zero (c2 = c3 = 0),
  173.     // that component of the curve is linear and does not incur any error.
  174.     // If a=0 for both X and Y, the curve is a line segment and we can
  175.     // use a step size of 1.
  176.  
  177.     int maxaccel1 = abs(2*cy2) + abs(6*cy3);
  178.     int maxaccel2 = abs(2*cx2) + abs(6*cx3);
  179.  
  180.     int maxaccel = maxaccel1 > maxaccel2 ? maxaccel1 : maxaccel2;
  181.     int h = 1;
  182.  
  183.     while(maxaccel > 8 && h < 1024) {
  184.         maxaccel >>= 2;
  185.         h += h;
  186.     }
  187.  
  188.     int lastx = x0;
  189.     int lasty = y0;
  190.  
  191.     // compute forward differences
  192.     sint64 h1 = (sint64)(0x40000000 / h) << 2;
  193.     sint64 h2 = h1/h;
  194.     sint64 h3 = h2/h;
  195.  
  196.     sint64 ax0 = (sint64)cx0 << 32;
  197.     sint64 ax1 =   h1*(sint64)cx1 +   h2*(sint64)cx2 + h3*(sint64)cx3;
  198.     sint64 ax2 = 2*h2*(sint64)cx2 + 6*h3*(sint64)cx3;
  199.     sint64 ax3 = 6*h3*(sint64)cx3;
  200.  
  201.     sint64 ay0 = (sint64)cy0 << 32;
  202.     sint64 ay1 =   h1*(sint64)cy1 +   h2*(sint64)cy2 + h3*(sint64)cy3;
  203.     sint64 ay2 = 2*h2*(sint64)cy2 + 6*h3*(sint64)cy3;
  204.     sint64 ay3 = 6*h3*(sint64)cy3;
  205.  
  206.     // round, not truncate
  207.     ax0 += 0x80000000;
  208.     ay0 += 0x80000000;
  209.  
  210.     do {
  211.         ax0 += ax1;
  212.         ax1 += ax2;
  213.         ax2 += ax3;
  214.         ay0 += ay1;
  215.         ay1 += ay2;
  216.         ay2 += ay3;
  217.  
  218.         int xi = (int)((uint64)ax0 >> 32);
  219.         int yi = (int)((uint64)ay0 >> 32);
  220.  
  221.         FastLine(lastx, lasty, xi, yi);
  222.         lastx = xi;
  223.         lasty = yi;
  224.     } while(--h);
  225. }
  226.  
  227. void VDPixmapPathRasterizer::Line(const vdint2& pt1, const vdint2& pt2) {
  228.     FastLine(pt1.x, pt1.y, pt2.x, pt2.y);
  229. }
  230.  
  231. void VDPixmapPathRasterizer::FastLine(int x0, int y0, int x1, int y1) {
  232.     int flag = 1;
  233.  
  234.     if (y1 == y0)
  235.         return;
  236.  
  237.     if (y1 < y0) {
  238.         int t;
  239.  
  240.         t=x0; x0=x1; x1=t;
  241.         t=y0; y0=y1; y1=t;
  242.         flag = 0;
  243.     }
  244.  
  245.     int dy = y1-y0;
  246.     int xacc = x0<<13;
  247.  
  248.     // prestep y0 down
  249.     int iy0 = (y0+3) >> 3;
  250.     int iy1 = (y1+3) >> 3;
  251.  
  252.     if (iy0 < iy1) {
  253.         int invslope = (x1-x0)*65536/dy;
  254.  
  255.         int prestep = (4-y0) & 7;
  256.         xacc += (invslope * prestep)>>3;
  257.  
  258.         if (iy0 < mScanYMin || iy1 > mScanYMax) {
  259.             ReallocateScanBuffer(iy0, iy1);
  260.             VDASSERT(iy0 >= mScanYMin && iy1 <= mScanYMax);
  261.         }
  262.  
  263.         while(iy0 < iy1) {
  264.             int ix = (xacc+32767)>>16;
  265.  
  266.             if (mEdgeBlockIdx >= kEdgeBlockMax) {
  267.                 if (mpFreeEdgeBlocks) {
  268.                     EdgeBlock *newBlock = mpFreeEdgeBlocks;
  269.                     mpFreeEdgeBlocks = mpFreeEdgeBlocks->next;
  270.                     newBlock->next = mpEdgeBlocks;
  271.                     mpEdgeBlocks = newBlock;
  272.                 } else {
  273.                     mpEdgeBlocks = new EdgeBlock(mpEdgeBlocks);
  274.                 }
  275.  
  276.                 mEdgeBlockIdx = 0;
  277.             }
  278.  
  279.             Edge& e = mpEdgeBlocks->edges[mEdgeBlockIdx];
  280.             Scan& s = mpScanBufferBiased[iy0];
  281.             VDASSERT(iy0 >= mScanYMin && iy0 < mScanYMax);
  282.             ++mEdgeBlockIdx;
  283.  
  284.             e.posandflag = ix*2+flag;
  285.             e.next = s.chain;
  286.             s.chain = &e;
  287.             ++s.count;
  288.  
  289.             ++iy0;
  290.             xacc += invslope;
  291.         }
  292.     }
  293. }
  294.  
  295. void VDPixmapPathRasterizer::ScanConvert(VDPixmapRegion& region) {
  296.     // Convert the edges to spans.  We couldn't do this before because some of
  297.     // the regions may have winding numbers >+1 and it would have been a pain
  298.     // to try to adjust the spans on the fly.  We use one heap to detangle
  299.     // a scanline's worth of edges from the singly-linked lists, and another
  300.     // to collect the actual scans.
  301.     vdfastvector<int> heap;
  302.  
  303.     region.mSpans.clear();
  304.     int xmin = INT_MAX;
  305.     int xmax = INT_MIN;
  306.     int ymin = INT_MAX;
  307.     int ymax = INT_MIN;
  308.  
  309.     for(int y=mScanYMin; y<mScanYMax; ++y) {
  310.         uint32 flipcount = mpScanBufferBiased[y].count;
  311.  
  312.         if (!flipcount)
  313.             continue;
  314.  
  315.         // Keep the edge heap from doing lots of stupid little reallocates.
  316.         if (heap.capacity() < flipcount)
  317.             heap.resize((flipcount + 63)&~63);
  318.  
  319.         // Detangle scanline into edge heap.
  320.         int *heap0 = heap.data();
  321.         int *heap1 = heap0;
  322.         for(const Edge *ptr = mpScanBufferBiased[y].chain; ptr; ptr = ptr->next)
  323.             *heap1++ = ptr->posandflag;
  324.  
  325.         VDASSERT(heap1 - heap0 == flipcount);
  326.  
  327.         // Sort edge heap.  Note that we conveniently made the opening edges
  328.         // one more than closing edges at the same spot, so we won't have any
  329.         // problems with abutting spans.
  330.  
  331.         std::sort(heap0, heap1);
  332.  
  333. #if 0
  334.         while(heap0 != heap1) {
  335.             int x = *heap0++ >> 1;
  336.             region.mSpans.push_back((y<<16) + x + 0x80008000);
  337.             region.mSpans.push_back((y<<16) + x + 0x80008001);
  338.         }
  339.         continue;
  340. #endif
  341.  
  342.         // Trim any odd edges off, since we can never close on one.
  343.         if (flipcount & 1)
  344.             --heap1;
  345.  
  346.         // Process edges and add spans.  Since we only check for a non-zero
  347.         // winding number, it doesn't matter which way the outlines go. Also, since
  348.         // the parity always flips after each edge regardless of direction, we can
  349.         // process the edges in pairs.
  350.  
  351.         size_t spanstart = region.mSpans.size();
  352.  
  353.         int x_left;
  354.         int count = 0;
  355.         while(heap0 != heap1) {
  356.             int x = *heap0++;
  357.  
  358.             if (!count)
  359.                 x_left = (x>>1);
  360.  
  361.             count += (x&1);
  362.  
  363.             x = *heap0++;
  364.  
  365.             count += (x&1);
  366.  
  367.             if (!--count) {
  368.                 int x_right = (x>>1);
  369.  
  370.                 if (x_right > x_left) {
  371.                     region.mSpans.push_back((y<<16) + x_left  + 0x80008000);
  372.                     region.mSpans.push_back((y<<16) + x_right + 0x80008000);
  373.  
  374.                 }
  375.             }
  376.         }
  377.  
  378.         size_t spanend = region.mSpans.size();
  379.  
  380.         if (spanend > spanstart) {
  381.             if (ymin > y)
  382.                 ymin = y;
  383.  
  384.             if (ymax < y)
  385.                 ymax = y;
  386.  
  387.             int x1 = (region.mSpans[spanstart] & 0xffff) - 0x8000;
  388.             int x2 = (region.mSpans[spanend-1] & 0xffff) - 0x8000;
  389.  
  390.             if (xmin > x1)
  391.                 xmin = x1;
  392.  
  393.             if (xmax < x2)
  394.                 xmax = x2;
  395.         }
  396.     }
  397.  
  398.     if (xmax > xmin) {
  399.         region.mBounds.set(xmin, ymin, xmax, ymax);
  400.     } else {
  401.         region.mBounds.set(0, 0, 0, 0);
  402.     }
  403.  
  404.     // Dump the edge and scan buffers, since we no longer need them.
  405.     ClearEdgeList();
  406.     ClearScanBuffer();
  407. }
  408.  
  409. void VDPixmapPathRasterizer::ClearEdgeList() {
  410.     if (mpEdgeBlocks) {
  411.         EdgeBlock *block = mpEdgeBlocks;
  412.         
  413.         while(EdgeBlock *next = block->next)
  414.             block = next;
  415.  
  416.         block->next = mpFreeEdgeBlocks;
  417.         mpFreeEdgeBlocks = mpEdgeBlocks;
  418.         mpEdgeBlocks = NULL;
  419.     }
  420.  
  421.     mEdgeBlockIdx = kEdgeBlockMax;
  422. }
  423.  
  424. void VDPixmapPathRasterizer::FreeEdgeLists() {
  425.     ClearEdgeList();
  426.  
  427.     while(EdgeBlock *block = mpFreeEdgeBlocks) {
  428.         mpFreeEdgeBlocks = block->next;
  429.  
  430.         delete block;
  431.     }
  432. }
  433.  
  434. void VDPixmapPathRasterizer::ClearScanBuffer() {
  435.     delete[] mpScanBuffer;
  436.     mpScanBuffer = mpScanBufferBiased = NULL;
  437.     mScanYMin = 0;
  438.     mScanYMax = 0;
  439. }
  440.  
  441. void VDPixmapPathRasterizer::ReallocateScanBuffer(int ymin, int ymax) {
  442.     // 
  443.     // check if there actually is a scan buffer to avoid unintentionally pinning at zero
  444.     if (mpScanBuffer) {
  445.         int nicedelta = (mScanYMax - mScanYMin);
  446.  
  447.         if (ymin < mScanYMin) {
  448.             int yminnice = mScanYMin - nicedelta;
  449.             if (ymin > yminnice)
  450.                 ymin = yminnice;
  451.  
  452.             ymin &= ~31;
  453.         } else
  454.             ymin = mScanYMin;
  455.  
  456.         if (ymax > mScanYMax) {
  457.             int ymaxnice = mScanYMax + nicedelta;
  458.             if (ymax < ymaxnice)
  459.                 ymax = ymaxnice;
  460.  
  461.             ymax = (ymax + 31) & ~31;
  462.         } else
  463.             ymax = mScanYMax;
  464.  
  465.         VDASSERT(ymin <= mScanYMin && ymax >= mScanYMax);
  466.     }
  467.  
  468.     // reallocate scan buffer
  469.     Scan *pNewBuffer = new Scan[ymax - ymin];
  470.     Scan *pNewBufferBiased = pNewBuffer - ymin;
  471.  
  472.     if (mpScanBuffer) {
  473.         memcpy(pNewBufferBiased + mScanYMin, mpScanBufferBiased + mScanYMin, (mScanYMax - mScanYMin) * sizeof(Scan));
  474.         delete[] mpScanBuffer;
  475.  
  476.         // zero new areas of scan buffer
  477.         for(int y=ymin; y<mScanYMin; ++y) {
  478.             pNewBufferBiased[y].chain = NULL;
  479.             pNewBufferBiased[y].count = 0;
  480.         }
  481.  
  482.         for(int y=mScanYMax; y<ymax; ++y) {
  483.             pNewBufferBiased[y].chain = NULL;
  484.             pNewBufferBiased[y].count = 0;
  485.         }
  486.     } else {
  487.         for(int y=ymin; y<ymax; ++y) {
  488.             pNewBufferBiased[y].chain = NULL;
  489.             pNewBufferBiased[y].count = 0;
  490.         }
  491.     }
  492.  
  493.     mpScanBuffer = pNewBuffer;
  494.     mpScanBufferBiased = pNewBufferBiased;
  495.     mScanYMin = ymin;
  496.     mScanYMax = ymax;
  497. }
  498.  
  499. bool VDPixmapFillRegion(const VDPixmap& dst, const VDPixmapRegion& region, int x, int y, uint32 color) {
  500.     if (dst.format != nsVDPixmap::kPixFormat_XRGB8888)
  501.         return false;
  502.  
  503.     // fast out
  504.     if (region.mSpans.empty())
  505.         return true;
  506.  
  507.     // check if vertical clipping is required
  508.     const size_t n = region.mSpans.size();
  509.     uint32 start = 0;
  510.     uint32 end = n;
  511.  
  512.     uint32 spanmin = (-x) + ((-y) << 16) + 0x80008000;
  513.  
  514.     if (region.mSpans.front() < spanmin) {
  515.         uint32 lo = 0, hi = n;
  516.  
  517.         // compute top clip
  518.         while(lo < hi) {
  519.             int mid = ((lo + hi) >> 1) & ~1;
  520.  
  521.             if (region.mSpans[mid + 1] < spanmin)
  522.                 lo = mid + 2;
  523.             else
  524.                 hi = mid;
  525.         }
  526.  
  527.         start = lo;
  528.  
  529.         // check for total top clip
  530.         if (start >= n)
  531.             return true;
  532.     }
  533.  
  534.     uint32 spanlimit = (dst.w - x) + ((dst.h - y - 1) << 16) + 0x80008000;
  535.  
  536.     if (region.mSpans.back() > spanlimit) {
  537.         // compute bottom clip
  538.         int lo = start;
  539.         int hi = n;
  540.  
  541.         while(lo < hi) {
  542.             int mid = ((lo + hi) >> 1) & ~1;
  543.  
  544.             if (region.mSpans[mid] >= spanlimit)
  545.                 hi = mid;
  546.             else
  547.                 lo = mid+2;
  548.         }
  549.  
  550.         end = lo;
  551.  
  552.         // check for total bottom clip
  553.         if (start >= end)
  554.             return true;
  555.     }
  556.  
  557.     // fill region
  558.     const uint32 *pSpan = ®ion.mSpans[start];
  559.     const uint32 *pEnd  = ®ion.mSpans[0] + end;
  560.     int lasty = -1;
  561.     uint32 *dstp;
  562.  
  563.     for(; pSpan != pEnd; pSpan += 2) {
  564.         uint32 span0 = pSpan[0];
  565.         uint32 span1 = pSpan[1];
  566.  
  567.         uint32 py = (span0 >> 16) - 0x8000 + y;
  568.         uint32 px = (span0 & 0xffff) - 0x8000 + x;
  569.         uint32 w = span1-span0;
  570.  
  571.         VDASSERT(py < (uint32)dst.h);
  572.         VDASSERT(px < (uint32)dst.w);
  573.         VDASSERT(dst.w - (int)px >= (int)w);
  574.  
  575.         if (lasty != py)
  576.             dstp = (uint32 *)vdptroffset(dst.data, dst.pitch * py);
  577.  
  578.         uint32 *p = dstp + px;
  579.         do {
  580.             *p++ = color;
  581.         } while(--w);
  582.     }
  583.  
  584.     return true;
  585. }
  586.  
  587. namespace {
  588.     void RenderABuffer32(const VDPixmap& dst, int y, const uint8 *alpha, uint32 w, uint32 color) {
  589.         if (!w)
  590.             return;
  591.  
  592.         // update dest pointer
  593.         uint32 *dstp = (uint32 *)vdptroffset(dst.data, dst.pitch * y);
  594.  
  595.         const uint32 color_rb = color & 0x00FF00FF;
  596.         const uint32 color_g  = color & 0x0000FF00;
  597.         do {
  598.             const uint32 px = *dstp;
  599.             const uint32 px_rb = px & 0x00FF00FF;
  600.             const uint32 px_g  = px & 0x0000FF00;
  601.             const sint32 a     = *alpha++;
  602.  
  603.             const uint32 result_rb = (((px_rb << 6) + ((sint32)(color_rb - px_rb)*a + 0x00200020)) & 0x3FC03FC0);
  604.             const uint32 result_g  = (((px_g  << 6) + ((sint32)(color_g  - px_g )*a + 0x00002000)) & 0x003FC000);
  605.  
  606.             *dstp++ = (result_rb + result_g) >> 6;
  607.         } while(--w);
  608.     }
  609.  
  610.     void RenderABuffer8(const VDPixmap& dst, int y, const uint8 *alpha, uint32 w, uint32 color) {
  611.         if (!w)
  612.             return;
  613.  
  614.         // update dest pointer
  615.         uint8 *dstp = (uint8 *)vdptroffset(dst.data, dst.pitch * y);
  616.  
  617.         do {
  618.             const uint8 px = *dstp;
  619.             const sint8 a = *alpha++;
  620.  
  621.             *dstp++ = px + (((sint32)(color - px) * a + 32) >> 6);
  622.         } while(--w);
  623.     }
  624.  
  625.     void RenderABuffer8_128(const VDPixmap& dst, int y, const uint8 *alpha, uint32 w, uint32 color) {
  626.         if (!w)
  627.             return;
  628.  
  629.         // update dest pointer
  630.         uint8 *dstp = (uint8 *)vdptroffset(dst.data, dst.pitch * y);
  631.  
  632.         do {
  633.             const uint8 px = *dstp;
  634.             const sint16 a = *alpha++;
  635.  
  636.             *dstp++ = px + (((sint32)(color - px) * a + 64) >> 7);
  637.         } while(--w);
  638.     }
  639.  
  640.     void RenderABuffer8_256(const VDPixmap& dst, int y, const uint16 *alpha, uint32 w, uint32 color) {
  641.         if (!w)
  642.             return;
  643.  
  644.         // update dest pointer
  645.         uint8 *dstp = (uint8 *)vdptroffset(dst.data, dst.pitch * y);
  646.  
  647.         do {
  648.             const uint8 px = *dstp;
  649.             const sint32 a = *alpha++;
  650.  
  651.             *dstp++ = px + (((sint32)(color - px) * a + 128) >> 8);
  652.         } while(--w);
  653.     }
  654.  
  655.     void RenderABuffer8_1024(const VDPixmap& dst, int y, const uint16 *alpha, uint32 w, uint32 color) {
  656.         if (!w)
  657.             return;
  658.  
  659.         // update dest pointer
  660.         uint8 *dstp = (uint8 *)vdptroffset(dst.data, dst.pitch * y);
  661.  
  662.         do {
  663.             const uint8 px = *dstp;
  664.             const sint32 a = *alpha++;
  665.  
  666.             *dstp++ = px + (((sint32)(color - px) * a + 512) >> 10);
  667.         } while(--w);
  668.     }
  669. }
  670.  
  671. bool VDPixmapFillRegionAntialiased_32x_32x(const VDPixmap& dst, const VDPixmapRegion& region, int x, int y, uint32 color) {
  672.     if (dst.format != nsVDPixmap::kPixFormat_Y8)
  673.         return false;
  674.  
  675.     // fast out
  676.     if (region.mSpans.empty())
  677.         return true;
  678.  
  679.     // check if vertical clipping is required
  680.     const size_t n = region.mSpans.size();
  681.     uint32 start = 0;
  682.     uint32 end = n;
  683.  
  684.     uint32 spanmin = -x + (-y << 16) + 0x80008000;
  685.  
  686.     if (region.mSpans.front() < spanmin) {
  687.         // find first span : x2 > spanmin
  688.         start = std::upper_bound(region.mSpans.begin(), region.mSpans.end(), spanmin) - region.mSpans.begin();
  689.         start &= ~1;
  690.  
  691.         // check for total top clip
  692.         if (start >= n)
  693.             return true;
  694.     }
  695.  
  696.     uint32 spanlimit = (dst.w*32 - x) + (((dst.h*32 - y) - 1) << 16) + 0x80008000;
  697.  
  698.     if (region.mSpans.back() > spanlimit) {
  699.         // find last span : x1 < spanlimit
  700.         end = std::lower_bound(region.mSpans.begin(), region.mSpans.end(), spanlimit) - region.mSpans.begin();
  701.  
  702.         end = (end + 1) & ~1;
  703.  
  704.         // check for total bottom clip
  705.         if (start >= end)
  706.             return true;
  707.     }
  708.  
  709.     // allocate A-buffer
  710.     vdfastvector<uint16> abuffer(dst.w, 0);
  711.  
  712.     // fill region
  713.     const uint32 *pSpan = ®ion.mSpans[start];
  714.     const uint32 *pEnd  = ®ion.mSpans[0] + end;
  715.     int lasty = -1;
  716.  
  717.     sint32 dstw32 = dst.w * 32;
  718.     sint32 dsth32 = dst.h * 32;
  719.  
  720.     for(; pSpan != pEnd; pSpan += 2) {
  721.         uint32 span0 = pSpan[0];
  722.         uint32 span1 = pSpan[1];
  723.  
  724.         sint32 py = (span0 >> 16) - 0x8000 + y;
  725.  
  726.         if ((uint32)py >= (uint32)dsth32)
  727.             continue;
  728.  
  729.         sint32 px1 = (span0 & 0xffff) - 0x8000 + x;
  730.         sint32 px2 = (span1 & 0xffff) - 0x8000 + x;
  731.         sint32 w = span1-span0;
  732.  
  733.         if (lasty != py) {
  734.             if (((lasty ^ py) & 0xFFFFFFE0)) {
  735.                 if (lasty >= 0) {
  736.                     // flush scanline
  737.  
  738.                     RenderABuffer8_1024(dst, lasty >> 5, abuffer.data(), dst.w, color);
  739.                 }
  740.  
  741.                 memset(abuffer.data(), 0, abuffer.size() * sizeof(abuffer[0]));
  742.             }
  743.             lasty = py;
  744.         }
  745.  
  746.         if (px1 < 0)
  747.             px1 = 0;
  748.         if (px2 > dstw32)
  749.             px2 = dstw32;
  750.  
  751.         if (px1 >= px2)
  752.             continue;
  753.  
  754.         uint32 ix1 = px1 >> 5;
  755.         uint32 ix2 = px2 >> 5;
  756.         uint16 *p1 = abuffer.data() + ix1;
  757.         uint16 *p2 = abuffer.data() + ix2;
  758.  
  759.         if (p1 == p2) {
  760.             p1[0] += (px2 - px1);
  761.         } else {
  762.             if (px1 & 31) {
  763.                 p1[0] += 32 - (px1 & 31);
  764.                 ++p1;
  765.             }
  766.  
  767.             while(p1 != p2) {
  768.                 p1[0] += 32;
  769.                 ++p1;
  770.             }
  771.  
  772.             if (px2 & 31)
  773.                 p1[0] += px2 & 32;
  774.         }
  775.     }
  776.  
  777.     if (lasty >= 0)
  778.         RenderABuffer8_1024(dst, lasty >> 5, abuffer.data(), dst.w, color);
  779.  
  780.     return true;
  781. }
  782.  
  783. bool VDPixmapFillRegionAntialiased_16x_16x(const VDPixmap& dst, const VDPixmapRegion& region, int x, int y, uint32 color) {
  784.     if (dst.format != nsVDPixmap::kPixFormat_Y8)
  785.         return false;
  786.  
  787.     // fast out
  788.     if (region.mSpans.empty())
  789.         return true;
  790.  
  791.     // check if vertical clipping is required
  792.     const size_t n = region.mSpans.size();
  793.     uint32 start = 0;
  794.     uint32 end = n;
  795.  
  796.     uint32 spanmin = -x + (-y << 16) + 0x80008000;
  797.  
  798.     if (region.mSpans.front() < spanmin) {
  799.         // find first span : x2 > spanmin
  800.         start = std::upper_bound(region.mSpans.begin(), region.mSpans.end(), spanmin) - region.mSpans.begin();
  801.         start &= ~1;
  802.  
  803.         // check for total top clip
  804.         if (start >= n)
  805.             return true;
  806.     }
  807.  
  808.     uint32 spanlimit = (dst.w*16 - x) + (((dst.h*16 - y) - 1) << 16) + 0x80008000;
  809.  
  810.     if (region.mSpans.back() > spanlimit) {
  811.         // find last span : x1 < spanlimit
  812.         end = std::lower_bound(region.mSpans.begin(), region.mSpans.end(), spanlimit) - region.mSpans.begin();
  813.  
  814.         end = (end + 1) & ~1;
  815.  
  816.         // check for total bottom clip
  817.         if (start >= end)
  818.             return true;
  819.     }
  820.  
  821.     // allocate A-buffer
  822.     vdfastvector<uint16> abuffer(dst.w, 0);
  823.  
  824.     // fill region
  825.     const uint32 *pSpan = ®ion.mSpans[start];
  826.     const uint32 *pEnd  = ®ion.mSpans[0] + end;
  827.     int lasty = -1;
  828.  
  829.     sint32 dstw16 = dst.w * 16;
  830.     sint32 dsth16 = dst.h * 16;
  831.  
  832.     for(; pSpan != pEnd; pSpan += 2) {
  833.         uint32 span0 = pSpan[0];
  834.         uint32 span1 = pSpan[1];
  835.  
  836.         sint32 py = (span0 >> 16) - 0x8000 + y;
  837.  
  838.         if ((uint32)py >= (uint32)dsth16)
  839.             continue;
  840.  
  841.         sint32 px1 = (span0 & 0xffff) - 0x8000 + x;
  842.         sint32 px2 = (span1 & 0xffff) - 0x8000 + x;
  843.         sint32 w = span1-span0;
  844.  
  845.         if (lasty != py) {
  846.             if (((lasty ^ py) & 0xFFFFFFF0)) {
  847.                 if (lasty >= 0) {
  848.                     // flush scanline
  849.  
  850.                     RenderABuffer8_256(dst, lasty >> 4, abuffer.data(), dst.w, color);
  851.                 }
  852.  
  853.                 memset(abuffer.data(), 0, abuffer.size() * sizeof(abuffer[0]));
  854.             }
  855.             lasty = py;
  856.         }
  857.  
  858.         if (px1 < 0)
  859.             px1 = 0;
  860.         if (px2 > dstw16)
  861.             px2 = dstw16;
  862.  
  863.         if (px1 >= px2)
  864.             continue;
  865.  
  866.         uint32 ix1 = px1 >> 4;
  867.         uint32 ix2 = px2 >> 4;
  868.         uint16 *p1 = abuffer.data() + ix1;
  869.         uint16 *p2 = abuffer.data() + ix2;
  870.  
  871.         if (p1 == p2) {
  872.             p1[0] += (px2 - px1);
  873.         } else {
  874.             if (px1 & 15) {
  875.                 p1[0] += 16 - (px1 & 15);
  876.                 ++p1;
  877.             }
  878.  
  879.             while(p1 != p2) {
  880.                 p1[0] += 16;
  881.                 ++p1;
  882.             }
  883.  
  884.             if (px2 & 15)
  885.                 p1[0] += px2 & 15;
  886.         }
  887.     }
  888.  
  889.     if (lasty >= 0)
  890.         RenderABuffer8_256(dst, lasty >> 4, abuffer.data(), dst.w, color);
  891.  
  892.     return true;
  893. }
  894.  
  895. bool VDPixmapFillRegionAntialiased_16x_8x(const VDPixmap& dst, const VDPixmapRegion& region, int x, int y, uint32 color) {
  896.     if (dst.format != nsVDPixmap::kPixFormat_XRGB8888 && dst.format != nsVDPixmap::kPixFormat_Y8)
  897.         return false;
  898.  
  899.     // fast out
  900.     if (region.mSpans.empty())
  901.         return true;
  902.  
  903.     // check if vertical clipping is required
  904.     const size_t n = region.mSpans.size();
  905.     uint32 start = 0;
  906.     uint32 end = n;
  907.  
  908.     uint32 spanmin = -x + (-y << 16) + 0x80008000;
  909.  
  910.     if (region.mSpans.front() < spanmin) {
  911.         // find first span : x2 > spanmin
  912.         start = std::upper_bound(region.mSpans.begin(), region.mSpans.end(), spanmin) - region.mSpans.begin();
  913.         start &= ~1;
  914.  
  915.         // check for total top clip
  916.         if (start >= n)
  917.             return true;
  918.     }
  919.  
  920.     uint32 spanlimit = (dst.w*16 - x) + (((dst.h*8 - y) - 1) << 16) + 0x80008000;
  921.  
  922.     if (region.mSpans.back() > spanlimit) {
  923.         // find last span : x1 < spanlimit
  924.         end = std::lower_bound(region.mSpans.begin(), region.mSpans.end(), spanlimit) - region.mSpans.begin();
  925.  
  926.         end = (end + 1) & ~1;
  927.  
  928.         // check for total bottom clip
  929.         if (start >= end)
  930.             return true;
  931.     }
  932.  
  933.     // allocate A-buffer
  934.     vdfastvector<uint8> abuffer(dst.w, 0);
  935.  
  936.     // fill region
  937.     const uint32 *pSpan = ®ion.mSpans[start];
  938.     const uint32 *pEnd  = ®ion.mSpans[0] + end;
  939.     int lasty = -1;
  940.  
  941.     sint32 dstw16 = dst.w * 16;
  942.     sint32 dsth8 = dst.h * 8;
  943.  
  944.     for(; pSpan != pEnd; pSpan += 2) {
  945.         uint32 span0 = pSpan[0];
  946.         uint32 span1 = pSpan[1];
  947.  
  948.         sint32 py = (span0 >> 16) - 0x8000 + y;
  949.  
  950.         if ((uint32)py >= (uint32)dsth8)
  951.             continue;
  952.  
  953.         sint32 px1 = (span0 & 0xffff) - 0x8000 + x;
  954.         sint32 px2 = (span1 & 0xffff) - 0x8000 + x;
  955.         sint32 w = span1-span0;
  956.  
  957.         if (lasty != py) {
  958.             if (((lasty ^ py) & 0xFFFFFFF8)) {
  959.                 if (lasty >= 0) {
  960.                     // flush scanline
  961.  
  962.                     RenderABuffer8_128(dst, lasty >> 3, abuffer.data(), dst.w, color);
  963.                 }
  964.  
  965.                 memset(abuffer.data(), 0, abuffer.size());
  966.             }
  967.             lasty = py;
  968.         }
  969.  
  970.         if (px1 < 0)
  971.             px1 = 0;
  972.         if (px2 > dstw16)
  973.             px2 = dstw16;
  974.  
  975.         if (px1 >= px2)
  976.             continue;
  977.  
  978.         uint32 ix1 = px1 >> 4;
  979.         uint32 ix2 = px2 >> 4;
  980.         uint8 *p1 = abuffer.data() + ix1;
  981.         uint8 *p2 = abuffer.data() + ix2;
  982.  
  983.         if (p1 == p2) {
  984.             p1[0] += (px2 - px1);
  985.         } else {
  986.             if (px1 & 15) {
  987.                 p1[0] += 16 - (px1 & 15);
  988.                 ++p1;
  989.             }
  990.  
  991.             while(p1 != p2) {
  992.                 p1[0] += 16;
  993.                 ++p1;
  994.             }
  995.  
  996.             if (px2 & 15)
  997.                 p1[0] += px2 & 15;
  998.         }
  999.     }
  1000.  
  1001.     if (lasty >= 0)
  1002.         RenderABuffer8_128(dst, lasty >> 3, abuffer.data(), dst.w, color);
  1003.  
  1004.     return true;
  1005. }
  1006.  
  1007. bool VDPixmapFillRegionAntialiased8x(const VDPixmap& dst, const VDPixmapRegion& region, int x, int y, uint32 color) {
  1008.     if (dst.format == nsVDPixmap::kPixFormat_YUV444_Planar ||
  1009.         dst.format == nsVDPixmap::kPixFormat_YUV422_Planar ||
  1010.         dst.format == nsVDPixmap::kPixFormat_YUV420_Planar ||
  1011.         dst.format == nsVDPixmap::kPixFormat_YUV410_Planar) {
  1012.         VDPixmap pxY;
  1013.         VDPixmap pxCb;
  1014.         VDPixmap pxCr;
  1015.  
  1016.         pxY.format = nsVDPixmap::kPixFormat_Y8;
  1017.         pxY.data = dst.data;
  1018.         pxY.pitch = dst.pitch;
  1019.         pxY.w = dst.w;
  1020.         pxY.h = dst.h;
  1021.  
  1022.         pxCb.format = nsVDPixmap::kPixFormat_Y8;
  1023.         pxCb.data = dst.data2;
  1024.         pxCb.pitch = dst.pitch2;
  1025.         pxCb.w = dst.w;
  1026.         pxCb.h = dst.h;
  1027.  
  1028.         pxCr.format = nsVDPixmap::kPixFormat_Y8;
  1029.         pxCr.data = dst.data3;
  1030.         pxCr.pitch = dst.pitch3;
  1031.         pxCr.w = dst.w;
  1032.         pxCr.h = dst.h;
  1033.  
  1034.         uint32 colorY = (color >> 8) & 0xff;
  1035.         uint32 colorCb = (color >> 0) & 0xff;
  1036.         uint32 colorCr = (color >> 16) & 0xff;
  1037.  
  1038.         VDPixmapFillRegionAntialiased8x(pxY, region, x, y, colorY);
  1039.  
  1040.         switch(dst.format) {
  1041.         case nsVDPixmap::kPixFormat_YUV410_Planar:
  1042.             pxCr.w = pxCb.w = dst.w >> 2;
  1043.             pxCr.h = pxCb.h = dst.h >> 2;
  1044.             x >>= 2;
  1045.             y >>= 2;
  1046.             VDPixmapFillRegionAntialiased_32x_32x(pxCb, region, x, y, colorCb);
  1047.             VDPixmapFillRegionAntialiased_32x_32x(pxCr, region, x, y, colorCr);
  1048.             return true;
  1049.         case nsVDPixmap::kPixFormat_YUV420_Planar:
  1050.             pxCr.w = pxCb.w = dst.w >> 1;
  1051.             pxCr.h = pxCb.h = dst.h >> 1;
  1052.             x >>= 1;
  1053.             y >>= 1;
  1054.             x += 2;
  1055.             VDPixmapFillRegionAntialiased_16x_16x(pxCb, region, x, y, colorCb);
  1056.             VDPixmapFillRegionAntialiased_16x_16x(pxCr, region, x, y, colorCr);
  1057.             return true;
  1058.         case nsVDPixmap::kPixFormat_YUV422_Planar:
  1059.             pxCr.w = pxCb.w = dst.w >> 1;
  1060.             x >>= 1;
  1061.             x += 2;
  1062.             VDPixmapFillRegionAntialiased_16x_8x(pxCb, region, x, y, colorCb);
  1063.             VDPixmapFillRegionAntialiased_16x_8x(pxCr, region, x, y, colorCr);
  1064.             return true;
  1065.         case nsVDPixmap::kPixFormat_YUV444_Planar:
  1066.             VDPixmapFillRegionAntialiased8x(pxCb, region, x, y, colorCb);
  1067.             VDPixmapFillRegionAntialiased8x(pxCr, region, x, y, colorCr);
  1068.             return true;
  1069.         }
  1070.     }
  1071.  
  1072.     if (dst.format != nsVDPixmap::kPixFormat_XRGB8888 && dst.format != nsVDPixmap::kPixFormat_Y8)
  1073.         return false;
  1074.  
  1075.     // fast out
  1076.     if (region.mSpans.empty())
  1077.         return true;
  1078.  
  1079.     // check if vertical clipping is required
  1080.     const size_t n = region.mSpans.size();
  1081.     uint32 start = 0;
  1082.     uint32 end = n;
  1083.  
  1084.     uint32 spanmin = -x + (-y << 16) + 0x80008000;
  1085.  
  1086.     if (region.mSpans.front() < spanmin) {
  1087.         // find first span : x2 > spanmin
  1088.         start = std::upper_bound(region.mSpans.begin(), region.mSpans.end(), spanmin) - region.mSpans.begin();
  1089.         start &= ~1;
  1090.  
  1091.         // check for total top clip
  1092.         if (start >= n)
  1093.             return true;
  1094.     }
  1095.  
  1096.     uint32 spanlimit = (dst.w*8 - x) + (((dst.h*8 - y) - 1) << 16) + 0x80008000;
  1097.  
  1098.     if (region.mSpans.back() > spanlimit) {
  1099.         // find last span : x1 < spanlimit
  1100.         end = std::lower_bound(region.mSpans.begin(), region.mSpans.end(), spanlimit) - region.mSpans.begin();
  1101.  
  1102.         end = (end + 1) & ~1;
  1103.  
  1104.         // check for total bottom clip
  1105.         if (start >= end)
  1106.             return true;
  1107.     }
  1108.  
  1109.     // allocate A-buffer
  1110.     vdfastvector<uint8> abuffer(dst.w, 0);
  1111.  
  1112.     // fill region
  1113.     const uint32 *pSpan = ®ion.mSpans[start];
  1114.     const uint32 *pEnd  = ®ion.mSpans[0] + end;
  1115.     int lasty = -1;
  1116.  
  1117.     sint32 dstw8 = dst.w * 8;
  1118.     sint32 dsth8 = dst.h * 8;
  1119.  
  1120.     for(; pSpan != pEnd; pSpan += 2) {
  1121.         uint32 span0 = pSpan[0];
  1122.         uint32 span1 = pSpan[1];
  1123.  
  1124.         sint32 py = (span0 >> 16) - 0x8000 + y;
  1125.  
  1126.         if ((uint32)py >= (uint32)dsth8)
  1127.             continue;
  1128.  
  1129.         sint32 px1 = (span0 & 0xffff) - 0x8000 + x;
  1130.         sint32 px2 = (span1 & 0xffff) - 0x8000 + x;
  1131.         sint32 w = span1-span0;
  1132.  
  1133.         if (lasty != py) {
  1134.             if (((lasty ^ py) & 0xFFFFFFF8)) {
  1135.                 if (lasty >= 0) {
  1136.                     // flush scanline
  1137.  
  1138.                     if (dst.format == nsVDPixmap::kPixFormat_XRGB8888)
  1139.                         RenderABuffer32(dst, lasty >> 3, abuffer.data(), dst.w, color);
  1140.                     else
  1141.                         RenderABuffer8(dst, lasty >> 3, abuffer.data(), dst.w, color);
  1142.                 }
  1143.  
  1144.                 memset(abuffer.data(), 0, abuffer.size());
  1145.             }
  1146.             lasty = py;
  1147.         }
  1148.  
  1149.         if (px1 < 0)
  1150.             px1 = 0;
  1151.         if (px2 > dstw8)
  1152.             px2 = dstw8;
  1153.  
  1154.         if (px1 >= px2)
  1155.             continue;
  1156.  
  1157.         uint32 ix1 = px1 >> 3;
  1158.         uint32 ix2 = px2 >> 3;
  1159.         uint8 *p1 = abuffer.data() + ix1;
  1160.         uint8 *p2 = abuffer.data() + ix2;
  1161.  
  1162.         if (p1 == p2) {
  1163.             p1[0] += (px2 - px1);
  1164.         } else {
  1165.             if (px1 & 7) {
  1166.                 p1[0] += 8 - (px1 & 7);
  1167.                 ++p1;
  1168.             }
  1169.  
  1170.             while(p1 != p2) {
  1171.                 p1[0] += 8;
  1172.                 ++p1;
  1173.             }
  1174.  
  1175.             if (px2 & 7)
  1176.                 p1[0] += px2 & 7;
  1177.         }
  1178.     }
  1179.  
  1180.     if (lasty >= 0) {
  1181.         if (dst.format == nsVDPixmap::kPixFormat_XRGB8888)
  1182.             RenderABuffer32(dst, lasty >> 3, abuffer.data(), dst.w, color);
  1183.         else
  1184.             RenderABuffer8(dst, lasty >> 3, abuffer.data(), dst.w, color);
  1185.     }
  1186.  
  1187.     return true;
  1188. }
  1189.  
  1190. void VDPixmapCreateRoundRegion(VDPixmapRegion& dst, float r) {
  1191.     int ir = VDCeilToInt(r);
  1192.     float r2 = r*r;
  1193.  
  1194.     dst.mSpans.clear();
  1195.     dst.mBounds.set(-ir, 0, ir+1, 0);
  1196.  
  1197.     for(int y = -ir; y <= ir; ++y) {
  1198.         int dx = VDCeilToInt(sqrtf(r2 - y*y));
  1199.  
  1200.         if (dx > 0) {
  1201.             dst.mSpans.push_back(0x80008000 + (y << 16) - dx);
  1202.             dst.mSpans.push_back(0x80008001 + (y << 16) + dx);
  1203.             if (dst.mBounds.top > y)
  1204.                 dst.mBounds.top = y;
  1205.             if (dst.mBounds.bottom < y)
  1206.                 dst.mBounds.bottom = y;
  1207.         }
  1208.     }
  1209. }
  1210.  
  1211. void VDPixmapConvolveRegion(VDPixmapRegion& dst, const VDPixmapRegion& r1, const VDPixmapRegion& r2, int dx1, int dx2, int dy) {
  1212.     dst.mSpans.clear();
  1213.     dst.mSpans.resize(r1.mSpans.size()+r2.mSpans.size());
  1214.  
  1215.     const uint32 *itA    = r1.mSpans.data();
  1216.     const uint32 *itAE    = itA + r1.mSpans.size();
  1217.     const uint32 *itB    = r2.mSpans.data();
  1218.     const uint32 *itBE    = itB + r2.mSpans.size();
  1219.     uint32 *dstp0 = dst.mSpans.data();
  1220.     uint32 *dstp = dst.mSpans.data();
  1221.  
  1222.     uint32 offset1 = (dy<<16) + dx1;
  1223.     uint32 offset2 = (dy<<16) + dx2;
  1224.  
  1225.     while(itA != itAE && itB != itBE) {
  1226.         uint32 x1;
  1227.         uint32 x2;
  1228.  
  1229.         if (itB[0] + offset1 < itA[0]) {
  1230.             // B span is earlier.  Use it.
  1231.             x1 = itB[0] + offset1;
  1232.             x2 = itB[1] + offset2;
  1233.             itB += 2;
  1234.  
  1235.             // B spans *can* overlap, due to the widening.
  1236.             while(itB != itBE && itB[0]+offset1 <= x2) {
  1237.                 uint32 bx2 = itB[1] + offset2;
  1238.                 if (x2 < bx2)
  1239.                     x2 = bx2;
  1240.  
  1241.                 itB += 2;
  1242.             }
  1243.  
  1244.             goto a_start;
  1245.         } else {
  1246.             // A span is earlier.  Use it.
  1247.             x1 = itA[0];
  1248.             x2 = itA[1];
  1249.             itA += 2;
  1250.  
  1251.             // A spans don't overlap, so begin merge loop with B first.
  1252.         }
  1253.  
  1254.         for(;;) {
  1255.             // If we run out of B spans or the B span doesn't overlap,
  1256.             // then the next A span can't either (because A spans don't
  1257.             // overlap) and we exit.
  1258.  
  1259.             if (itB == itBE || itB[0]+offset1 > x2)
  1260.                 break;
  1261.  
  1262.             do {
  1263.                 uint32 bx2 = itB[1] + offset2;
  1264.                 if (x2 < bx2)
  1265.                     x2 = bx2;
  1266.  
  1267.                 itB += 2;
  1268.             } while(itB != itBE && itB[0]+offset1 <= x2);
  1269.  
  1270.             // If we run out of A spans or the A span doesn't overlap,
  1271.             // then the next B span can't either, because we would have
  1272.             // consumed all overlapping B spans in the above loop.
  1273. a_start:
  1274.             if (itA == itAE || itA[0] > x2)
  1275.                 break;
  1276.  
  1277.             do {
  1278.                 uint32 ax2 = itA[1];
  1279.                 if (x2 < ax2)
  1280.                     x2 = ax2;
  1281.  
  1282.                 itA += 2;
  1283.             } while(itA != itAE && itA[0] <= x2);
  1284.         }
  1285.  
  1286.         // Flush span.
  1287.         dstp[0] = x1;
  1288.         dstp[1] = x2;
  1289.         dstp += 2;
  1290.     }
  1291.  
  1292.     // Copy over leftover spans.
  1293.     memcpy(dstp, itA, sizeof(uint32)*(itAE - itA));
  1294.     dstp += itAE - itA;
  1295.  
  1296.     while(itB != itBE) {
  1297.         // B span is earlier.  Use it.
  1298.         uint32 x1 = itB[0] + offset1;
  1299.         uint32 x2 = itB[1] + offset2;
  1300.         itB += 2;
  1301.  
  1302.         // B spans *can* overlap, due to the widening.
  1303.         while(itB != itBE && itB[0]+offset1 <= x2) {
  1304.             uint32 bx2 = itB[1] + offset2;
  1305.             if (x2 < bx2)
  1306.                 x2 = bx2;
  1307.  
  1308.             itB += 2;
  1309.         }
  1310.  
  1311.         dstp[0] = x1;
  1312.         dstp[1] = x2;
  1313.         dstp += 2;
  1314.     }
  1315.  
  1316.     dst.mSpans.resize(dstp - dst.mSpans.data());
  1317. }
  1318.  
  1319. void VDPixmapConvolveRegion(VDPixmapRegion& dst, const VDPixmapRegion& r1, const VDPixmapRegion& r2) {
  1320.     VDPixmapRegion temp;
  1321.  
  1322.     const uint32 *src1 = r2.mSpans.data();
  1323.     const uint32 *src2 = src1 + r2.mSpans.size();
  1324.  
  1325.     dst.mSpans.clear();
  1326.     while(src1 != src2) {
  1327.         uint32 p1 = src1[0];
  1328.         uint32 p2 = src1[1];
  1329.         src1 += 2;
  1330.  
  1331.         temp.mSpans.swap(dst.mSpans);
  1332.         VDPixmapConvolveRegion(dst, temp, r1, (p1 & 0xffff) - 0x8000, (p2 & 0xffff) - 0x8000, (p1 >> 16) - 0x8000);
  1333.     }
  1334. }
  1335.