6

我正在尝试创建一个使用新的 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. 在第一种情况下,控件的背景也存在问题(变黑,即使我已将主网格的背景画笔设置为 ImageBrush 链接到主 Appx 包中的本地文件。

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

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

4

3 回答 3

4

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

于 2013-01-02T16:37:05.733 回答
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 事件。

于 2012-12-30T00:53:34.327 回答
0

您可以生成一个图像,但它需要相当多的工作。正如您所指出的 - 您不能只保存整个图像。因此,我更改了保存逻辑以循环遍历图像并一次保存 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);
        }
    }
于 2015-05-25T23:47:03.957 回答