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