【问题标题】:Windows Phone 8 - Generating Lockscreen Image in the BackgroundWindows Phone 8 - 在后台生成锁屏图像
【发布时间】:2012-12-24 04:22:24
【问题描述】:

我正在尝试创建一个使用新的 Windows Phone 8 Live Lockscreen API 的 Windows Phone 8 应用程序(我当前发布的“The Quote”的更新)。我基本上想从应用程序包中随机选择一个图像背景,并在其上放置一个带有随机引用的文本块来创建锁屏图像。我怎样才能在后台定期任务中做到这一点?肯定有办法做到这一点(许多当前的应用程序,包括不同的天气和新闻应用程序在后台本地创建实时锁屏),但我似乎无法找出如何以及到目前为止没有互联网搜索让我得到任何东西有用。

非常感谢任何帮助!

非常感谢!

编辑:

我能够找到一种方法来使用我的内容创建一个 UserControl 并以这种方式对其进行截图:

var bmp = new WriteableBitmap(768, 1280);
bmp.Render(LayoutRoot, null);

String tempJPEG = "TempJPEG.jpg";

var myStore = IsolatedStorageFile.GetUserStoreForApplication();
if (myStore.FileExists(tempJPEG))
{
    myStore.DeleteFile(tempJPEG);
}
IsolatedStorageFileStream myFileStream = myStore.CreateFile(tempJPEG);

WriteableBitmap wb = new WriteableBitmap(bmp);

wb.SaveJpeg(myFileStream, wb.PixelWidth, wb.PixelHeight, 0, 100);
myFileStream.Close();

这种方法给我带来了三个不同的问题:

  1. 如果我没有在构造函数中设置WriteableBitmap的大小,会选择错误,锁屏也没用。

  2. 如果我运行上面的代码,它会抛出 OutOfMemory 错误

  3. 在第一种情况下,控件的背景也存在问题(变黑,即使我已将主 Grid 的背景画笔设置为 ImageBrush 链接到主 Appx 包中的本地文件。

这是完全错误的吗?有更好的(工作)方法吗?

非常感谢大家,感谢您的帮助。

【问题讨论】:

    标签: image background windows-phone-8 lockscreen


    【解决方案1】:

    您很可能在后台代理中遇到内存上限限制,在 WP8 上为 11 MB。我建议您在服务器/Azure 上渲染图像,然后将其下载到后台代理中,将其保存到手机中并在锁屏上显示,或者使用 Resource Intesive Task 进行渲染?
    我在我的一个应用程序中使用平铺渲染,当我尝试仅渲染 2 个大小为 336x336 + 159x159 像素的平铺图像时遇到了内存上限,因此您可以想象渲染 768x1280 图像也可以轻松达到此上限。

    【讨论】:

    【解决方案2】:

    如果我没有在构造函数中设置 WriteableBitmap 的大小,它会选择它 错误,锁屏也没用。

    您是否尝试使用 Application.Current.Host.ActualHeight 和 ActualWidth 作为您生成的锁屏图像的大小?基本上使锁屏图像适应操作系统当前使用的大小?我很确定 Application.Current 在后台可能为空,因此您必须将其缓存在主应用程序的 ApplicationSettings 中,并在后台代理中使用该信息。

    如果我运行上面的代码,它会抛出 OutOfMemory 错误

    是的,那是因为您在 SaveJpeg 调用中使用了 ImageQuality=100。请记住,在 WP8 上运行的后台代理的工作集内存限制为 11MB。将 ImageQuality 降低到 70-80,你应该没问题。

    在第一种情况下,控件的背景也有问题 (变黑了,即使我设置了主网格的背景画笔 到 ImageBrush 链接到主 Appx 包中的本地文件。

    图片可能需要更长的时间才能加载。首先,在将其保存到文件之前尝试调用 WriteableBitmap.Invalidate()。如果这不起作用(因为图像来自远程源),您将不得不等到您尝试捕获的图像的 BitmapImage.ImageOpened 事件。

    【讨论】:

    • 在JPEG保存前一行发生OutOfMemory异常(WriteableBitmap wb = new WriteableBitmap(bmp);)。我怎样才能在那里防止它?
    • 经过一点点挖掘,我发现了另一个不寻常的事情——当我创建一个全新的干净项目并使用我在第一篇文章中编写的代码正常生成图像时,不同的是没有在构造函数中指定分辨率(创建新的 WriteableBitmap( LayoutRoot, null ) ),生成的图像始终为 480x800 像素,即使我在 Lumia 920 (768x1280) 上测试应用程序。
    • 现在,当我在后台代理中使用这个更改的代码时,我得到一个新的异常 - SaveJpeg 调用中的 ArgumentException。
    【解决方案3】:

    您可以生成图像,但需要做很多工作。正如您所指出的 - 您不能只保存整个图像。因此,我更改了保存逻辑以循环遍历图像并一次保存 60 行。请注意,我调用 GC.Collect 3 次以遍历所有 3 代 GC 并消除所有内存分配。

    static IEnumerable<ExtendedImage> GetImageSegments(LockScreenTile tileControl)
    {
        const int segmentMaxHight = 60;
        var aggregatePixelHeight = (int)tileControl.ActualHeight;
        tileControl.LayoutRoot.Height = aggregatePixelHeight;
        for (int row = 0; row < aggregatePixelHeight; row += segmentMaxHight)
        {
            tileControl.LayoutRoot.Margin = new Thickness(0, -row, 0, 0);
            tileControl.Height = Math.Min(segmentMaxHight, aggregatePixelHeight - row);
            yield return tileControl.ToExtendedImage();
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
     }
    

    ExtendedImage 来自 ImageTools 库。一旦我得到可枚举的返回值,我就会调用 PNG 压缩(我对其进行了调整以使用图像片段进行持久化):

    var segments = GetImageSegments(tileControl);
    double pixelRatio = (template.PixelWidth) / tileControl.ActualWidth;
    segments.SaveToPngImage(
        aggregatePixelHeight: (int)(savedActualHeight * pixelRatio),
        pixelWidth: template.PixelWidth,
        densityX: template.DensityX,
        densityY: template.DensityY, 
        directoryName: tileDirectoryName, 
        filePath: filePath);
    

    最后,SaveToPngImage 使用的是 ImageTools 中的 PngEncoder,我已对其进行了修改:

        private void WriteDataChunks(IEnumerable<ExtendedImage> verticalImageSegments)
        {
            byte[] compressedBytes;
            MemoryStream underlyingMemoryStream = null;
            DeflaterOutputStream compressionStream = null;
            try
            {
                underlyingMemoryStream = new MemoryStream();
                compressionStream = new DeflaterOutputStream(underlyingMemoryStream);
    
                foreach (var verticalImageSegment in verticalImageSegments)
                {
                    byte[] pixels = verticalImageSegment.Pixels;
                    var data = new byte[verticalImageSegment.PixelWidth*verticalImageSegment.PixelHeight*4 + verticalImageSegment.PixelHeight];
                    int rowLength = verticalImageSegment.PixelWidth*4 + 1;
    
                    for (int y = 0; y < verticalImageSegment.PixelHeight; y++)
                    {
                        byte compression = 0;
                        if (y > 0)
                        {
                            compression = 2;
                        }
                        data[y*rowLength] = compression;
    
                        for (int x = 0; x < verticalImageSegment.PixelWidth; x++)
                        {
                            // Calculate the offset for the new array.
                            int dataOffset = y*rowLength + x*4 + 1;
    
                            // Calculate the offset for the original pixel array.
                            int pixelOffset = (y*verticalImageSegment.PixelWidth + x)*4;
    
                            data[dataOffset + 0] = pixels[pixelOffset + 0];
                            data[dataOffset + 1] = pixels[pixelOffset + 1];
                            data[dataOffset + 2] = pixels[pixelOffset + 2];
                            data[dataOffset + 3] = pixels[pixelOffset + 3];
    
                            if (y > 0)
                            {
                                int lastOffset = ((y - 1)*verticalImageSegment.PixelWidth + x)*4;
    
                                data[dataOffset + 0] -= pixels[lastOffset + 0];
                                data[dataOffset + 1] -= pixels[lastOffset + 1];
                                data[dataOffset + 2] -= pixels[lastOffset + 2];
                                data[dataOffset + 3] -= pixels[lastOffset + 3];
                            }
                        }
                    }
    
                    compressionStream.Write(data, 0, data.Length);
                }
                compressionStream.Flush();
                compressionStream.Finish();
    
                compressedBytes = underlyingMemoryStream.GetBuffer();
            }
            finally
            {
                if (compressionStream != null)
                {
                    compressionStream.Dispose();
                    underlyingMemoryStream = null;
                }
                if (underlyingMemoryStream != null)
                {
                    underlyingMemoryStream.Dispose();
                }
            }
    
            int numChunks = compressedBytes.Length/ MaxBlockSize;
            if (compressedBytes.Length % MaxBlockSize != 0)
            {
                numChunks++;
            }
    
            for (int i = 0; i < numChunks; i++)
            {
                int length = compressedBytes.Length - i * MaxBlockSize;
    
                if (length > MaxBlockSize)
                {
                    length = MaxBlockSize;
                }
    
                WriteChunk(PngChunkTypes.Data, compressedBytes, i*MaxBlockSize, length);
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多