2011-06-02 2 views
4

Привет всем, Итак, я сейчас пытаюсь реализовать устройство APNG Specification, но у меня возникают проблемы с рендерингом рамки. Моя функцияВыполнение функции рендеринга APNG

private void UpdateUI() 
    { 
     foreach (PictureBox pb in pics) 
     { 
      APNGBox box = (APNGBox)pb.Tag; 
      APNGLib.APNG png = box.png; 
      if (box.buffer == null) 
      { 
       box.buffer = new Bitmap((int)png.Width, (int)png.Height); 
      } 
      APNGLib.Frame f = png.GetFrame(box.frameNum); 
      using (Graphics g = Graphics.FromImage(box.buffer)) 
      { 
       switch (f.DisposeOp) 
       { 
        case APNGLib.Frame.DisposeOperation.NONE: 
         break; 
        case APNGLib.Frame.DisposeOperation.BACKGROUND: 
         g.Clear(Color.Transparent); 
         break; 
        case APNGLib.Frame.DisposeOperation.PREVIOUS: 
         if (box.prevBuffer != null) 
         { 
          g.DrawImage(box.prevBuffer, Point.Empty); 
         } 
         else 
         { 
          g.Clear(Color.Transparent); 
         } 
         break; 
        default: 
         break; 
       } 
       Bitmap read = png.ToBitmap(box.frameNum++); 
       switch (f.BlendOp) 
       { 
        case APNGLib.Frame.BlendOperation.OVER: 
         g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver; 
         break; 
        case APNGLib.Frame.BlendOperation.SOURCE: 
         g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; 
         break; 
        default: 
         break; 
       } 
       g.DrawImage(read, new Point((int)f.XOffset, (int)f.YOffset)); 
      } 

      box.prevBuffer = box.buffer; 
      pb.Image = box.buffer; 

      if (box.frameNum >= box.png.FrameCount) 
      { 
       box.frameNum = 0; 
       box.buffer = null; 
       box.prevBuffer = null; 
      } 
     } 
    } 

APNGBox является

internal class APNGBox 
    { 
     public int frameNum = 0; 
     public APNGLib.APNG png; 
     public Bitmap buffer; 
     public Bitmap prevBuffer; 
    } 

Я думаю, что это в основном правильно, как я запустить его против всех изображений в APNG Gallery. Однако некоторые из них неверно отображаются (This имеет проблемы с артефактом, а this не сохраняет красную полосу слева последовательно). Обратите внимание: у вас будет просмотр страницы в Firefox 3 или выше для просмотра анимации.

Я убежден, что проблема связана с тем, как я обрабатываю DISPOSE_OP_PREVIOUS, но я не могу понять, что я делаю неправильно. Может ли кто-нибудь предположить, что я могу потерять?

ответ

5

Итак, я смог разобраться в этом, и опубликую здесь, если кто-то в будущем столкнется с подобной проблемой. Оказывается, проблема была в основном по трем направлениям:

  1. Не следует изменить «предыдущий буфер», если тип кадра «Назад»
  2. Следует использовать dispose_op предыдущего фрейма, а не dispose_op текущего кадра, чтобы определить, как она располагает
  3. нужно обязательно располагать только область старого фрейма, а не весь кадр

новый код:

internal class APNGBox 
    { 
     public int frameNum { get; set; } 
     public APNGLib.APNG apng { get; set; } 
     public Bitmap buffer { get; set; } 
     public Bitmap prevBuffer { get; set; } 

     public APNGBox(APNGLib.APNG png) 
     { 
      frameNum = 0; 
      apng = png; 
      buffer = apng.ToBitmap(0); 
      prevBuffer = null; 
     } 
    } 

    private void UpdateUI() 
    { 
     foreach (PictureBox pb in pics) 
     { 
      APNGBox box = (APNGBox)pb.Tag; 
      APNGLib.APNG png = box.apng; 
      if (!png.IsAnimated) 
      { 
       if (pb.Image == null) 
       { 
        pb.Image = png.ToBitmap(); 
       } 
      } 
      else 
      { 
       if (box.frameNum != png.FrameCount - 1) 
       { 
        Bitmap prev = box.prevBuffer == null ? null : new Bitmap(box.prevBuffer); 
        APNGLib.Frame f1 = png.GetFrame(box.frameNum); 
        if (f1.DisposeOp != APNGLib.Frame.DisposeOperation.PREVIOUS) 
        { 
         box.prevBuffer = new Bitmap(box.buffer); 
        } 
        DisposeBuffer(box.buffer, new Rectangle((int)f1.XOffset, (int)f1.YOffset, (int)f1.Width, (int)f1.Height), f1.DisposeOp, prev); 
        box.frameNum++; 
        APNGLib.Frame f2 = png.GetFrame(box.frameNum); 
        RenderNextFrame(box.buffer, new Point((int)f2.XOffset, (int)f2.YOffset), png.ToBitmap(box.frameNum), f2.BlendOp); 
       } 
       else 
       { 
        box.frameNum = 0; 
        box.prevBuffer = null; 
        ClearFrame(box.buffer); 
        RenderNextFrame(box.buffer, Point.Empty, png.ToBitmap(box.frameNum), APNGLib.Frame.BlendOperation.SOURCE); 
       } 
       pb.Invalidate(); 
      } 
     } 
    } 

    private void DisposeBuffer(Bitmap buffer, Rectangle region, APNGLib.Frame.DisposeOperation dispose, Bitmap prevBuffer) 
    { 
     using (Graphics g = Graphics.FromImage(buffer)) 
     { 
      g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; 

      Brush b = new SolidBrush(Color.Transparent); 
      switch (dispose) 
      { 
       case APNGLib.Frame.DisposeOperation.NONE: 
        break; 
       case APNGLib.Frame.DisposeOperation.BACKGROUND: 
        g.FillRectangle(b, region); 
        break; 
       case APNGLib.Frame.DisposeOperation.PREVIOUS: 
        if(prevBuffer != null) 
        { 
         g.FillRectangle(b, region); 
         g.DrawImage(prevBuffer, region, region, GraphicsUnit.Pixel); 
        } 
        break; 
       default: 
        break; 
      } 
     } 
    } 

    private void RenderNextFrame(Bitmap buffer, Point point, Bitmap nextFrame, APNGLib.Frame.BlendOperation blend) 
    { 
     using(Graphics g = Graphics.FromImage(buffer)) 
     { 
      switch(blend) 
      { 
       case APNGLib.Frame.BlendOperation.OVER: 
        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver; 
        break; 
       case APNGLib.Frame.BlendOperation.SOURCE: 
        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; 
        break; 
       default: 
        break; 
      } 
      g.DrawImage(nextFrame, point); 
     } 
    } 

    private void ClearFrame(Bitmap buffer) 
    { 
     using(Graphics g = Graphics.FromImage(buffer)) 
     { 
      g.Clear(Color.Transparent); 
     } 
    } 
+0

Привет, у вас есть полный рабочий исходный код для обеспечения, пожалуйста? Я хотел бы отобразить apng в моем winforms приложении. Благодарю. – Dede

+0

Прошло некоторое время с тех пор, как я в последний раз работал над ними, но я записал некоторые демо: https://github.com/murrple-1/APNGManagement. Кроме того, я уверен, что в то время я был новичком в .net/C#, поэтому для зависимостей нет NuGet (я должен это делать когда-нибудь) –

+0

@dede Добавил поддержку NuGet только сейчас. Приложение APNGViewer должно быть полезно для вас –

Смежные вопросы