/// <summary>
 /// GIF文件处理线程
 /// </summary>
 private unsafe void write()
 {
     Bitmap lastBitmap = null, currentBitmap = null;
     BitmapData lastBitmapData = null, currentBitmapData = null;
     try
     {
         double currentInterval = -interval;
         while (true)
         {
             Monitor.Enter(bitmapLock);
             try
             {
                 if (bitmaps.Count == 0)
                 {
                     if (timer == null) break;
                     Monitor.Wait(bitmapLock);
                 }
                 if (bitmaps.Count == 0) break;
                 currentBitmap = bitmaps.PopExpand();
             }
             finally { Monitor.Exit(bitmapLock); }
             currentInterval += interval;
             if (currentBitmap != null)
             {
                 int left = 0, top = 0, right = currentBitmap.Width, bottom = currentBitmap.Height;
                 currentBitmapData = currentBitmap.LockBits(new Rectangle(0, 0, right, bottom), System.Drawing.Imaging.ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                 if (lastBitmapData != null)
                 {
                     byte* lastBitmapFixed = (byte*)lastBitmapData.Scan0, currentBitmapFixed = (byte*)currentBitmapData.Scan0;
                     int minHeight = currentBitmap.Height <= lastBitmap.Height ? currentBitmap.Height : lastBitmap.Height;
                     int minWidth = lastBitmap.Width, width3;
                     if (currentBitmap.Width <= lastBitmap.Width)
                     {
                         minWidth = currentBitmap.Width;
                         width3 = (minWidth << 1) + minWidth;
                         for (byte* lastRow = lastBitmapFixed, currentRow = currentBitmapFixed; top != minHeight; ++top)
                         {
                             if (!Laurent.Lee.CLB.Unsafe.TmphMemory.Equal(lastRow, currentRow, width3)) break;
                             lastRow += lastBitmapData.Stride;
                             currentRow += currentBitmapData.Stride;
                         }
                         if (currentBitmap.Height <= lastBitmap.Height && top != minHeight)
                         {
                             ++top;
                             for (byte* lastRow = lastBitmapFixed + lastBitmapData.Stride * minHeight, currentRow = currentBitmapFixed + currentBitmapData.Stride * minHeight; top != bottom; --bottom)
                             {
                                 if (!Laurent.Lee.CLB.Unsafe.TmphMemory.Equal(lastRow -= lastBitmapData.Stride, currentRow -= currentBitmapData.Stride, width3)) break;
                             }
                             --top;
                         }
                     }
                     if (currentBitmap.Height <= lastBitmap.Height && top != minHeight)
                     {
                         width3 = (minWidth << 1) + minWidth;
                         int endRowStride = lastBitmapData.Stride * (bottom - top);
                         byte* lastTopRow = lastBitmapFixed + lastBitmapData.Stride * top, currentTopRow = currentBitmapFixed + currentBitmapData.Stride * top;
                         if ((((int)lastBitmapFixed & (sizeof(ulong) - 1)) | (lastBitmapData.Stride & (sizeof(ulong) - 1)) | ((int)currentBitmapFixed & (sizeof(ulong) - 1)) | (currentBitmapData.Stride & (sizeof(ulong) - 1))) == 0)
                         {
                             byte* lastTopCol = lastTopRow, topColEnd = lastTopRow + width3 - 1;
                             ulong TmphColor = 0;
                             for (byte* currentTopCol = currentTopRow; lastTopCol <= topColEnd; lastTopCol += sizeof(ulong), currentTopCol += sizeof(ulong))
                             {
                                 TmphColor = 0;
                                 for (byte* lastRow = lastTopCol, currentRow = currentTopCol, endRow = lastRow + endRowStride; lastRow != endRow; lastRow += lastBitmapData.Stride, currentRow += currentBitmapData.Stride)
                                 {
                                     TmphColor |= *(ulong*)lastRow ^ *(ulong*)currentRow;
                                 }
                                 if (TmphColor != 0) break;
                             }
                             int length = (int)(lastTopCol - lastTopRow);
                             if (lastTopCol <= topColEnd) length += TmphColor.endBits() >> 3;
                             left += (length /= 3) < minWidth ? length : minWidth;
                             if (currentBitmap.Width <= lastBitmap.Width && left != minWidth)
                             {
                                 int offset = width3 & (sizeof(ulong) - 1);
                                 byte* currentTopCol = currentTopRow + width3;
                                 lastTopCol = lastTopRow + width3;
                                 length = 0;
                                 if (offset != 0)
                                 {
                                     currentTopCol -= offset;
                                     lastTopCol -= offset;
                                     TmphColor = 0;
                                     for (byte* lastRow = lastTopCol, currentRow = currentTopCol, endRow = lastRow + endRowStride; lastRow != endRow; lastRow += lastBitmapData.Stride, currentRow += currentBitmapData.Stride)
                                     {
                                         TmphColor |= *(ulong*)lastRow ^ *(ulong*)currentRow;
                                     }
                                     if ((TmphColor <<= ((sizeof(ulong) - offset) << 3)) == 0) length = offset;
                                     else length = ((sizeof(ulong) << 3) - TmphColor.bits()) >> 3;
                                 }
                                 if (length == offset)
                                 {
                                     topColEnd = lastTopCol;
                                     do
                                     {
                                         TmphColor = 0;
                                         for (byte* lastRow = (lastTopCol -= sizeof(ulong)), currentRow = (currentTopCol -= sizeof(ulong)), endRow = lastRow + endRowStride; lastRow != endRow; lastRow += lastBitmapData.Stride, currentRow += currentBitmapData.Stride)
                                         {
                                             TmphColor |= *(ulong*)lastRow ^ *(ulong*)currentRow;
                                         }
                                     }
                                     while (TmphColor == 0);
                                     length += (int)(topColEnd - lastTopCol) - sizeof(ulong) + ((sizeof(ulong) << 3) - TmphColor.bits()) >> 3;
                                 }
                                 right -= length / 3;
                             }
                         }
                         else
                         {
                             for (byte* lastTopCol = lastTopRow, topColEnd = lastTopRow + width3, currentTopCol = currentTopRow; lastTopCol != topColEnd; lastTopCol += 3, currentTopCol += 3, ++left)
                             {
                                 int TmphColor = 0;
                                 for (byte* lastRow = lastTopCol, currentRow = currentTopCol, endRow = lastRow + endRowStride; lastRow != endRow; lastRow += lastBitmapData.Stride, currentRow += currentBitmapData.Stride)
                                 {
                                     TmphColor |= *(int*)lastRow ^ *(int*)currentRow;
                                 }
                                 if ((TmphColor & 0xffffff) != 0) break;
                             }
                             if (currentBitmap.Width <= lastBitmap.Width && left != minWidth)
                             {
                                 byte* lastTopCol = lastTopRow + width3, currentTopCol = currentTopRow + width3;
                                 do
                                 {
                                     int TmphColor = 0;
                                     for (byte* lastRow = (lastTopCol -= 3), currentRow = (currentTopCol -= 3), endRow = lastRow + endRowStride; lastRow != endRow; lastRow += lastBitmapData.Stride, currentRow += currentBitmapData.Stride)
                                     {
                                         TmphColor |= *(int*)lastRow ^ *(int*)currentRow;
                                     }
                                     if ((TmphColor & 0xffffff) != 0) break;
                                     --right;
                                 }
                                 while (true);
                             }
                         }
                     }
                 }
                 if (top == bottom)
                 {
                     currentBitmap.UnlockBits(currentBitmapData);
                     currentBitmapData = null;
                     currentBitmap.Dispose();
                     currentBitmap = null;
                 }
                 else
                 {
                     int delayTime = (int)(currentInterval / 10);
                     for (currentInterval -= delayTime * 10; delayTime > short.MaxValue; delayTime -= short.MaxValue)
                     {
                         if (!gif.AddGraphicControl(short.MaxValue, Laurent.Lee.CLB.Drawing.GIF.TmphFile.TmphGraphicControl.TmphMethodType.None, true)) break;
                         if (!gif.addImage(lastBitmapData, 0, 0, 0, 0, 1, 1, false, maxPixel)) break;
                     }
                     if (lastBitmap != null)
                     {
                         lastBitmap.UnlockBits(lastBitmapData);
                         lastBitmapData = null;
                         lastBitmap.Dispose();
                         lastBitmap = null;
                     }
                     if (delayTime != 0 && !gif.AddGraphicControl((short)delayTime, Laurent.Lee.CLB.Drawing.GIF.TmphFile.TmphGraphicControl.TmphMethodType.None, true)) break;
                     if (!gif.addImage(currentBitmapData, left, top, left, top, right - left, bottom - top, false, maxPixel)) break;
                     lastBitmapData = currentBitmapData;
                     lastBitmap = currentBitmap;
                     currentBitmapData = null;
                     currentBitmap = null;
                 }
             }
         }
     }
     finally
     {
         TmphCollection<Bitmap> bitmaps = this.bitmaps;
         Monitor.Enter(bitmapLock);
         try
         {
             dispose();
             gif.Dispose();
             gif = null;
             this.bitmaps = null;
             isFinally = true;
             Monitor.Pulse(bitmapLock);
         }
         finally
         {
             Monitor.Exit(bitmapLock);
             if (lastBitmap != null)
             {
                 if (lastBitmapData != null) lastBitmap.UnlockBits(lastBitmapData);
                 lastBitmap.Dispose();
             }
             if (currentBitmap != null)
             {
                 if (currentBitmapData != null) currentBitmap.UnlockBits(currentBitmapData);
                 currentBitmap.Dispose();
             }
             foreach (Bitmap bitmap in bitmaps) bitmap.Dispose();
         }
     }
 }
 /// <summary>
 /// 截屏到GIF文件
 /// </summary>
 /// <param name="filename">GIF文件名</param>
 /// <param name="interval">截屏定时毫秒数</param>
 /// <param name="leftOffset">X方向偏移量</param>
 /// <param name="topOffset">Y方向偏移量</param>
 /// <param name="width">截屏高度</param>
 /// <param name="height">截屏宽度</param>
 /// <param name="screen">截屏设备</param>
 /// <param name="maxPixel">最大色彩深度</param>
 /// <param name="isWaitFinally">释放资源是否等待GIF文件处理结束</param>
 public TmphCopyScreen(string filename, double interval, int leftOffset = 0, int topOffset = 0, int width = 0, int height = 0, Screen screen = null, byte maxPixel = 8, bool isWaitFinally = true)
 {
     this.screen = screen;
     if (screen == null) screen = Screen.PrimaryScreen;
     if (width == 0)
     {
         if (leftOffset < 0) leftOffset = 0;
         width = screen.Bounds.Width;
     }
     else
     {
         if (leftOffset < 0)
         {
             width += leftOffset;
             leftOffset = 0;
         }
     }
     if (height == 0)
     {
         if (topOffset < 0) leftOffset = 0;
         height = screen.Bounds.Height;
     }
     else
     {
         if (topOffset < 0)
         {
             height += topOffset;
             topOffset = 0;
         }
     }
     if (width <= 0 || height <= 0) CLB.TmphLog.Error.Throw(CLB.TmphLog.TmphExceptionType.IndexOutOfRange);
     if (width > short.MaxValue) width = short.MaxValue;
     if (height > short.MaxValue) height = short.MaxValue;
     this.leftOffset = leftOffset;
     this.topOffset = topOffset;
     this.maxPixel = (byte)(maxPixel - 2) < 8 ? maxPixel : (byte)8;
     this.isWaitFinally = isWaitFinally;
     gif = new Laurent.Lee.CLB.Drawing.GIF.TmphFile.TmphWriter(filename, (short)width, (short)height);
     timer = new System.Timers.Timer(this.interval = interval < 40 ? 40 : interval);
     timer.Elapsed += nextScreen;
     bitmaps = new TmphCollection<Bitmap>();
     nextScreen(null, null);
     timer.Start();
     TmphThreadPool.TinyPool.Start(write, null, null);
 }