6

新しいWindowsPhone8 LiveLockscreenAPIを使用するWindowsPhone8アプリ(現在公開されている「TheQuote」のアップデート)を作成しようとしています。基本的に、アプリパッケージから画像の背景をランダムに選択し、ランダムな引用符でテキストブロックを配置して、ロック画面の画像を作成します。バックグラウンドの定期的なタスクでそれをどのように達成できますか?確かにそれを行う方法はあります(さまざまな天気やニュースアプリを含む多くの現在のアプリは、バックグラウンドでローカルにライブロックスクリーンを作成します)が、私は方法を見つけることができないようです、そして今のところインターネット検索は私に何も得ませんでした使える。

どんな助けでも大歓迎です!

どうもありがとうございます!

編集:

コンテンツを使用して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();

このアプローチにより、3つの異なる問題が発生しました。

  1. コンストラクターでWriteableBitmapのサイズを設定しなかった場合、誤って選択され、ロック画面は役に立ちませんでした。

  2. 上記のコードを実行すると、OutOfMemoryエラーがスローされます

  3. 1つのケースでは、コントロールの背景に問題もありました(メインのグリッドの背景ブラシをメインのAppxパッケージからローカルファイルにリンクするImageBrushに設定したにもかかわらず、黒になりました。

これは完全に間違っていますか?より良い(実用的な)方法はありますか?

どうもありがとうございました、私はあなたの助けに感謝します。

4

3 に答える 3

4

バックグラウンド エージェントのメモリ上限に達している可能性が高く、WP8 では 11 MB です。サーバー/Azure で画像をレンダリングしてバックグラウンド エージェントにダウンロードし、電話に保存してロック画面に表示するか、レンダリングにリソース集約型タスクを使用することをお勧めします。
アプリの 1 つでタイル レンダリングを使用していますが、336x336 + 159x159px のサイズのタイル画像を 2 つだけレンダリングしようとしたときにメモリの上限に達しました。

于 2013-01-02T16:37:05.733 に答える
2

コンストラクターで WriteableBitmap のサイズを設定しなかった場合、正しく選択されず、ロック画面が役に立たなくなりました。

生成されたロック画面イメージのサイズとして Application.Current.Host.ActualHeight と ActualWidth を使用しようとしましたか? OSで現在使用されているサイズにロック画面の画像を本質的に適合させますか? Application.Current はバックグラウンドで null になる可能性があると確信しているため、メイン アプリから ApplicationSettings にキャッシュし、その情報をバックグラウンド エージェントで使用する必要があります。

上記のコードを実行すると、OutOfMemory エラーがスローされます

ええ、それは SaveJpeg 呼び出しで ImageQuality=100 を使用しているためです。WP8 で実行されているバックグラウンド エージェントには、11MB のワーキング セットのメモリ制限があることに注意してください。ImageQuality を 70 ~ 80 に下げれば問題ありません。

1 つのケースでは、コントロールの背景にも問題がありました (メインのグリッドの背景ブラシを、メインの Appx パッケージからローカル ファイルにリンクする ImageBrush に設定したにもかかわらず、黒くなりました。

画像の読み込みに少し時間がかかる場合があります。まず、ファイルに保存する前に WriteableBitmap.Invalidate() を呼び出してみてください。それが機能しない場合 (イメージがリモート ソースからのものであるため)、キャプチャしようとしているイメージの BitmapImage.ImageOpened イベントまで待つ必要があります。

于 2012-12-30T00:53:34.327 に答える
0

You can generate an image but it requires quite a bit of more work. As you pointed out - you can't just save the entire image. So I've changed saving logic to loop through the image and save 60 lines at a time. Notice that I call GC.Collect 3 times to go over all 3 GC generations and get rid of all memory allocations.

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 is coming from ImageTools library. And once I get the enumerable back I call PNG compression (which I tweaked to persist using image fragments):

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);

Finally, SaveToPngImage is using PngEncoder from ImageTools, which I've modified:

    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 に答える