我正在尝试创建一个将PNG从URL加载到内存中的函数,然后使用关键字" openbadges"添加一个iTXt块。和一些json数据。除了执行
之外,我的每个部分都有效metadata.SetQuery("/iTXt/openbadges", "");
我得到一个例外:
Value does not fall within the expected range.
这是功能:
private static byte[] CreateOpenBadge(BadgeAssertionEntity assertion)
{
using (var image = LoadImage(new Uri(assertion.Badge.ImageUrl)))
using (var imageStream = new MemoryStream())
{
image.Save(imageStream, ImageFormat.Png);
var pngDecoder = new PngBitmapDecoder(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
using (var badgeStream = new MemoryStream())
{
var pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(pngDecoder.Frames[0]));
var metadata = pngEncoder.Frames[0].Metadata as BitmapMetadata;
if (metadata == null)
throw new ApplicationException();
metadata.SetQuery("/iTXt/openbadges", "");
pngEncoder.Save(badgeStream);
return badgeStream.ToArray();
}
}
}
任何想法我做错了什么?
答案 0 :(得分:3)
诀窍是setquery()" / iTXt / Keyword "和" / iTXt / TextEntry ",但您必须使用 char [] 作为关键字的值。
这是因为内部" / iTXt / Keyword "想要 VT_LPSTR 作为值类型,只有 char [] 才能正确转换为该值。
这是一个使用WIC编写棋盘格的样本,包括一些iTXt元数据。
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace PNGEncoder
{
class Program
{
static void Main(string[] args)
{
var width = 256;
var height = 256;
var pngMetadata = new BitmapMetadata("png");
pngMetadata.SetQuery("/iTXt/Keyword", "keyword0".ToCharArray());
pngMetadata.SetQuery("/iTXt/TextEntry", "textentry0");
pngMetadata.SetQuery("/[1]iTXt/Keyword", "keyword1".ToCharArray());
pngMetadata.SetQuery("/[1]iTXt/TextEntry", "textentry1");
var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray8, null);
var pixels = new byte[width * height];
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
pixels[y * width + x] = (byte)(255 * (((x >> 4) ^ (y >> 4)) & 1));
}
}
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width, 0);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap, null, pngMetadata, null));
using (var stream = File.Create("checkerBoard.png"))
{
encoder.Save(stream);
}
}
}
}
答案 1 :(得分:1)
我放弃了使用PngBitmapEncoder。相反,我将直接修改png字节。我附上了为此目的而制作的课程,其他人认为它很有用。
本课程深受AShelly在Using Chunks in a PNG
的回应启发我还使用http://damieng.com/blog/2006/08/08/calculating_crc32_in_c_and_net作为crc哈希。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Badger.Libraries.Hashing;
namespace Badger.Libraries.Images
{
public class Png
{
private readonly byte[] _header;
private readonly IList<Chunk> _chunks;
public Png(Uri imageUri)
{
_header = new byte[8];
_chunks = new List<Chunk>();
var webResponse = WebRequest.Create(imageUri).GetResponse();
using (var webResponseStream = webResponse.GetResponseStream())
using (var memoryStream = new MemoryStream())
{
if (webResponseStream == null)
throw new ArgumentException("invalid uri");
webResponseStream.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.Read(_header, 0, _header.Length);
while (memoryStream.Position < memoryStream.Length)
_chunks.Add(ChunkFromStream(memoryStream));
memoryStream.Close();
}
}
public void AddInternationalText(string keyword, string text)
{
// 1-79 (keyword)
// 1 (null character)
// 1 (compression flag)
// 1 (compression method)
// 0+ (language)
// 1 (null character)
// 0+ (translated keyword)
// 1 (null character)
// 0+ (text)
var typeBytes = Encoding.UTF8.GetBytes("iTXt");
var keywordBytes = Encoding.UTF8.GetBytes(keyword);
var textBytes = Encoding.UTF8.GetBytes(text);
var nullByte = BitConverter.GetBytes('\0')[0];
var zeroByte = BitConverter.GetBytes(0)[0];
var data = new List<byte>();
data.AddRange(keywordBytes);
data.Add(nullByte);
data.Add(zeroByte);
data.Add(zeroByte);
data.Add(nullByte);
data.Add(nullByte);
data.AddRange(textBytes);
var chunk = new Chunk(typeBytes, data.ToArray());
_chunks.Insert(1, chunk);
}
public byte[] ToBytes()
{
using (var stream = new MemoryStream())
{
stream.Write(_header, 0, _header.Length);
foreach (var chunk in _chunks)
chunk.WriteToStream(stream);
var bytes = stream.ToArray();
stream.Close();
return bytes;
}
}
private static Chunk ChunkFromStream(Stream stream)
{
var length = ReadBytes(stream, 4);
var type = ReadBytes(stream, 4);
var data = ReadBytes(stream, Convert.ToInt32(BitConverter.ToUInt32(length.Reverse().ToArray(), 0)));
stream.Seek(4, SeekOrigin.Current);
return new Chunk(type, data);
}
private static byte[] ReadBytes(Stream stream, int n)
{
var buffer = new byte[n];
stream.Read(buffer, 0, n);
return buffer;
}
private static void WriteBytes(Stream stream, byte[] bytes)
{
stream.Write(bytes, 0, bytes.Length);
}
private class Chunk
{
public Chunk(byte[] type, byte[] data)
{
_type = type;
_data = data;
}
public void WriteToStream(Stream stream)
{
WriteBytes(stream, BitConverter.GetBytes(Convert.ToUInt32(_data.Length)).Reverse().ToArray());
WriteBytes(stream, _type);
WriteBytes(stream, _data);
WriteBytes(stream, CalculateCrc(_type, _data));
}
private static byte[] CalculateCrc(IEnumerable<byte> type, IEnumerable<byte> data)
{
var bytes = new List<byte>();
bytes.AddRange(type);
bytes.AddRange(data);
var hasher = new Crc32();
using (var stream = new MemoryStream(bytes.ToArray()))
return hasher.ComputeHash(stream);
}
private readonly byte[] _type;
private readonly byte[] _data;
}
}
}