【问题标题】:How can I use animated PNG in UWP?如何在 UWP 中使用动画 PNG?
【发布时间】:2021-03-11 04:18:39
【问题描述】:

我有这个动画png from this api,我想把它放在某人的个人资料照片上,但它只显示为 1 帧照片。

我知道有BitmapDecoder,但我不知道如何在 XAML 中设置它。

谁能帮帮我?

到目前为止,我已经尝试过这个并给了我一个冻结的 img:

<Image   Source="https://steamcdn-a.akamaihd.net/steamcommunity/public/images/items/1263950/ebe6b674deca163b28423e3b925bd36b0f0f357b.png" />

这给了我一个糟糕的黑白图像:

<BitmapIcon   UriSource="https://steamcdn-a.akamaihd.net/steamcommunity/public/images/items/1263950/ebe6b674deca163b28423e3b925bd36b0f0f357b.png" />

【问题讨论】:

  • UWP 中没有支持动画 PNG 图像的 API。我们建议您使用 GIF 图片作为 animated iamge。您可以参考sample获取有关动画GIF图像的更多信息。
  • @YanGu-MSFT 问题是 API 响应只是 A png ,所以当我将 img 转换为 gif 时,我得到一个糟糕的模糊 img。
  • 非常感谢您抽出宝贵时间分享您的想法。但是,到目前为止,还没有 API 支持为您的网站提供的动画 png 图像设置动画。

标签: c# vb.net xaml uwp


【解决方案1】:

当我发布此内容时,我只是注意到这是一篇旧帖子,但也许将来有人会使用它。

实际上,您可以使用一些东西:

抱歉,我刚刚注意到这是一个 UWP 问题,不确定 WPF 如何转换为 UWP,但我的答案在 WPF 中有效,但您可以使用 box 类,只需要修改转换器。

我已经修改了这个库 https://github.com/murrple-1/APNGManagement 并且它可以工作。

它是用 C# 编写的,它以 winforms .NET framework 4.xx 为目标。但我在 VB.NET 和 .NET5.0 上使用它没有任何问题。

我所做的是克隆存储库并将引用 APNGLib 添加到我的项目(您从编译/构建中获得的 .dll 文件)。

然后创建一个用户控件(WPF)来保存图像:

XAML

<UserControl x:Class="ApngImg"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ToolSet"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Image x:Name="APNGcontrolIMG" Width="200" Height="200"/>
    </Grid>
</UserControl>

在 xaml 后面的代码中:


Imports System.IO
Imports APNGLib

Public Class ApngImg

    
    Sub New()
        DataContext = Me
        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Dim pngFilePath As String = "C:\ebe6b674deca163b28423e3b925bd36b0f0f357b.png"
        Dim png As New APNG 'picturebox
        Using s As Stream = File.OpenRead(pngFilePath)
            png.Load(s)
        End Using
        Dim boxy As New APNGBox(png, APNGcontrolIMG)

    End Sub

然后编辑了原来的APNGViewer_WinForms.APNGBox(从C#翻译成VB.NET)

我所做的只是将container 添加为System.Windows.Controls.Image 到构造函数,为容器添加私有属性并添加一个函数,将System.Drawing.Bitmap 转换为System.Windows.Media.Imaging.BitmapImage,以便图像可以显示在wpf 图像控件中。

    Public Function Convert(ByVal src As Bitmap) As BitmapImage
        Dim ms As MemoryStream = New MemoryStream()
        src.Save(ms, System.Drawing.Imaging.ImageFormat.Png)
        Dim image As BitmapImage = New BitmapImage()
        image.BeginInit()
        ms.Seek(0, SeekOrigin.Begin)
        image.StreamSource = ms
        image.EndInit()
        IMGcontainer.Source = image
        Return image
    End Function

请注意,在我的情况下不需要返回,因为我无法弄清楚在进行转换时如何更新属性以便图像控件会注意到。相反,我只是设置了容器 Source 属性。

这很老套,但很有效。

这是完整的 APNGBox 类:

Imports System
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Windows.Forms
Imports APNGLib
'addition made by dumi
Imports System.IO
Imports System.Windows.Media.Imaging
Imports System.ComponentModel
'---------------------------------------------------------
Public Class APNGBox
    Inherits PictureBox
    'addition made by dumi
    Implements INotifyPropertyChanged
    Public Event PropertyChanged As PropertyChangedEventHandler _
        Implements INotifyPropertyChanged.PropertyChanged

    Public Sub NotifyPropertyChanged(ByVal info As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
    End Sub
    '---------------------------------------------------------
    Public Property CurrentFrameNumber As Integer
    Public Property Images As IList(Of Bitmap)
    Private APNGFile As APNG
    'addition made by dumi
    Private IMGcontainer As System.Windows.Controls.Image
    '---------------------------------------------------------
    Private timer As Timer
    Private playthroughs As UInteger
    Private _ImageBitmap As BitmapImage

    'addition made by dumi
    Public Property ImageBitmap As BitmapImage
        Get
            Return _ImageBitmap
        End Get
        Set
            _ImageBitmap = Value
            NotifyPropertyChanged(NameOf(ImageBitmap))
        End Set
    End Property
    '---------------------------------------------------------

    Public Sub New(ByVal png As APNG, Container As System.Windows.Controls.Image)
        APNGFile = png
        'addition made by dumi
        IMGcontainer = Container
        '---------------------------------------------------------
        CurrentFrameNumber = 0
        Images = New List(Of Bitmap)()
        InitImages()
        'addition made by dumi
        ImageBitmap = Convert(Images(0))
        '---------------------------------------------------------
        Image = Images(0)
        Size = Image.Size
        playthroughs = 0

        If APNGFile.IsAnimated Then
            timer = New Timer()
            timer.Interval = APNGFile.GetFrame(0).Milliseconds
            AddHandler timer.Tick, New EventHandler(AddressOf timer_Tick)
        End If
    End Sub

    'addition made by dumi
    Public Function Convert(ByVal src As Bitmap) As BitmapImage
        Dim ms As MemoryStream = New MemoryStream()
        src.Save(ms, System.Drawing.Imaging.ImageFormat.Png)
        Dim image As BitmapImage = New BitmapImage()
        image.BeginInit()
        ms.Seek(0, SeekOrigin.Begin)
        image.StreamSource = ms
        image.EndInit()
        IMGcontainer.Source = image
        Return image
    End Function
    '---------------------------------------------------------
    Private Sub timer_Tick(ByVal sender As Object, ByVal e As EventArgs)
        If APNGFile.MaxPlays = 0 OrElse playthroughs < APNGFile.MaxPlays Then
            NextImage()
            Dim f As Frame = APNGFile.GetFrame(CurrentFrameNumber)
            timer.Interval = f.Milliseconds
        End If
    End Sub

    Private Sub InitImages()
        If APNGFile.IsAnimated Then
            Dim current As Bitmap = New Bitmap(CInt(APNGFile.Width), CInt(APNGFile.Height))
            Dim previous As Bitmap = Nothing
            ImageRender.RenderNextFrame(current, Point.Empty, APNGFile.ToBitmap(0), Frame.BlendOperation.SOURCE)
            Images.Add(New Bitmap(current))

            For i As Integer = 1 To APNGFile.FrameCount - 1
                Dim oldFrame As APNGLib.Frame = APNGFile.GetFrame(i - 1)
                Dim prev As Bitmap = If(previous Is Nothing, Nothing, New Bitmap(previous))

                If oldFrame.DisposeOp <> APNGLib.Frame.DisposeOperation.PREVIOUS Then
                    previous = New Bitmap(current)
                End If

                ImageRender.DisposeBuffer(current, New Rectangle(CInt(oldFrame.XOffset), CInt(oldFrame.YOffset), CInt(oldFrame.Width), CInt(oldFrame.Height)), oldFrame.DisposeOp, prev)
                Dim currFrame As APNGLib.Frame = APNGFile.GetFrame(i)
                ImageRender.RenderNextFrame(current, New Point(CInt(currFrame.XOffset), CInt(currFrame.YOffset)), APNGFile.ToBitmap(i), currFrame.BlendOp)
                Images.Add(New Bitmap(current))
            Next
        Else
            Images.Add(APNGFile.ToBitmap())
        End If
    End Sub

    Public Sub NextImage()
        CurrentFrameNumber += 1

        If CurrentFrameNumber >= APNGFile.FrameCount Then
            playthroughs += 1
            CurrentFrameNumber = 0
        End If
        'addition made by dumi
        ImageBitmap = Convert(Images(CurrentFrameNumber))
        '---------------------------------------------------------
        Image = Images(CurrentFrameNumber)
    End Sub

    Public Sub ToImage(ByVal index As Integer)
        'addition made by dumi
        ImageBitmap = Convert(Images(index))
        '---------------------------------------------------------
        Image = Images(index)
        CurrentFrameNumber = index
    End Sub

    Public Sub Start()
        If timer IsNot Nothing Then
            timer.Start()
        End If
    End Sub

    Public Sub [Stop]()
        If timer IsNot Nothing Then
            timer.[Stop]()
        End If
    End Sub
End Class

这是使用 winforms 添加到原始 github 代码中的图像:

这是我在 wpf 中的结果:

【讨论】:

    猜你喜欢
    • 2012-12-10
    • 2013-05-02
    • 2014-06-03
    • 2016-11-02
    • 1970-01-01
    • 2014-06-04
    • 1970-01-01
    • 1970-01-01
    • 2020-01-02
    相关资源
    最近更新 更多