/// <Summary> /// Encodes the StylusTip in the ISF stream. /// </Summary> #else /// <Summary> /// Encodes the StylusTip in the ISF stream. /// </Summary> #endif private static void PersistStylusTip(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw) { // // persist the StylusTip // if (da.ContainsPropertyData(KnownIds.StylusTip)) { System.Diagnostics.Debug.Assert(da.StylusTip != StylusTip.Ellipse, "StylusTip was put in the EPC for the default value!"); // // persist PenTip.Rectangle for V1 ISF // Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.PenTip, true)); cbData += SerializationHelper.Encode(stream, (uint)PenTip.Rectangle); using (MemoryStream localStream = new MemoryStream(6)) //reasonable default { Int32 stylusTip = Convert.ToInt32(da.StylusTip, System.Globalization.CultureInfo.InvariantCulture); System.Runtime.InteropServices.VarEnum type = SerializationHelper.ConvertToVarEnum(PersistenceTypes.StylusTip, true); ExtendedPropertySerializer.EncodeAttribute(KnownIds.StylusTip, stylusTip, type, localStream); cbData += ExtendedPropertySerializer.EncodeAsISF(KnownIds.StylusTip, localStream.ToArray(), stream, guidList, 0, true); } } }
/// <summary> /// Retrieve the guids for the custom attributes that are not known by /// the v1 ISF decoder /// </summary> /// <param name="attributes"></param> /// <param name="count">count of guids returned (can be less than return.Length</param> /// <returns></returns> internal static Guid[] GetUnknownGuids(ExtendedPropertyCollection attributes, out int count) { Guid[] guids = new Guid[attributes.Count]; count = 0; for (int x = 0; x < attributes.Count; x++) { ExtendedProperty attribute = attributes[x]; if (0 == GuidList.FindKnownTag(attribute.Id)) { guids[count++] = attribute.Id; } } return(guids); }
private static void PersistDrawingFlags(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw) { // // always serialize DrawingFlags, even when it is the default of AntiAliased. V1 loaders // expect it. // Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.DrawingFlags, true)); cbData += SerializationHelper.Encode(stream, (uint)(int)da.DrawingFlags); if (da.ContainsPropertyData(KnownIds.CurveFittingError)) { Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.CurveFittingError, true)); cbData += SerializationHelper.Encode(stream, (uint)(int)da.GetPropertyData(KnownIds.CurveFittingError)); } }
/// <summary> /// Loads a stroke from the stream based on Stroke Descriptor, StylusPointDescription, Drawing Attributes, Stroke IDs, transform and GuidList /// </summary> /// <param name="stream"></param> /// <param name="size"></param> /// <param name="guidList"></param> /// <param name="strokeDescriptor"></param> /// <param name="stylusPointDescription"></param> /// <param name="drawingAttributes"></param> /// <param name="transform"></param> /// <param name="compressor">Compression module</param> /// <param name="stroke">Newly decoded stroke</param> /// <returns></returns> #else /// <summary> /// Loads a stroke from the stream based on Stroke Descriptor, StylusPointDescription, Drawing Attributes, Stroke IDs, transform and GuidList /// </summary> /// <param name="stream"></param> /// <param name="size"></param> /// <param name="guidList"></param> /// <param name="strokeDescriptor"></param> /// <param name="stylusPointDescription"></param> /// <param name="drawingAttributes"></param> /// <param name="transform"></param> /// <param name="stroke">Newly decoded stroke</param> #endif internal static uint DecodeStroke(Stream stream, uint size, GuidList guidList, StrokeDescriptor strokeDescriptor, StylusPointDescription stylusPointDescription, DrawingAttributes drawingAttributes, Matrix transform, #if OLD_ISF Compressor compressor, #endif out Stroke stroke) { ExtendedPropertyCollection extendedProperties; StylusPointCollection stylusPoints; uint cb = DecodeISFIntoStroke( #if OLD_ISF compressor, #endif stream, size, guidList, strokeDescriptor, stylusPointDescription, transform, out stylusPoints, out extendedProperties); if (cb != size) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Stroke size (" + cb.ToString(System.Globalization.CultureInfo.InvariantCulture) + ") != expected (" + size.ToString(System.Globalization.CultureInfo.InvariantCulture) + ")")); } stroke = new Stroke(stylusPoints, drawingAttributes, extendedProperties); return(cb); }
/// <summary> /// Loads a stroke from the stream based on Stroke Descriptor, StylusPointDescription, Drawing Attributes, Stroke IDs, transform and GuidList /// </summary> /// <param name="stream"></param> /// <param name="size"></param> /// <param name="guidList"></param> /// <param name="strokeDescriptor"></param> /// <param name="stylusPointDescription"></param> /// <param name="drawingAttributes"></param> /// <param name="transform"></param> /// <param name="stroke">Newly decoded stroke</param> #endif internal static uint DecodeStroke(Stream stream, uint size, GuidList guidList, StrokeDescriptor strokeDescriptor, StylusPointDescription stylusPointDescription, DrawingAttributes drawingAttributes, Matrix transform, #if OLD_ISF Compressor compressor, #endif out Stroke stroke) { ExtendedPropertyCollection extendedProperties; StylusPointCollection stylusPoints; uint cb = DecodeISFIntoStroke( #if OLD_ISF compressor, #endif stream, size, guidList, strokeDescriptor, stylusPointDescription, transform, out stylusPoints, out extendedProperties); if (cb != size) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Stroke size (" + cb.ToString(System.Globalization.CultureInfo.InvariantCulture) + ") != expected (" + size.ToString(System.Globalization.CultureInfo.InvariantCulture) + ")")); } stroke = new Stroke(stylusPoints, drawingAttributes, extendedProperties); return cb; }
private static void PersistRasterOperation(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw) { // write any non-default RasterOp value that we might have picked up from // V1 interop or by setting IsHighlighter. if (da.RasterOperation != DrawingAttributeSerializer.RasterOperationDefaultV1) { uint ropSize = GuidList.GetDataSizeIfKnownGuid(KnownIds.RasterOperation); if (ropSize == 0) { throw new InvalidOperationException(StrokeCollectionSerializer.ISFDebugMessage("ROP data size was not found")); } Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.RasterOperation, true)); long currentPosition = stream.Position; bw.Write(da.RasterOperation); if ((uint)(stream.Position - currentPosition) != ropSize) { throw new InvalidOperationException(StrokeCollectionSerializer.ISFDebugMessage("ROP data was incorrectly serialized")); } cbData += ropSize; } }
/// <summary> /// Returns an array of bytes of the saved stroke /// </summary> /// <param name="stroke">Stroke to save</param> /// <param name="stream">null to calculate only the size</param> /// <param name="compressor"></param> /// <param name="compressionAlgorithm"></param> /// <param name="guidList"></param> /// <param name="strokeLookupEntry"></param> #else /// <summary> /// Returns an array of bytes of the saved stroke /// </summary> /// <param name="stroke">Stroke to save</param> /// <param name="stream">null to calculate only the size</param> /// <param name="compressionAlgorithm"></param> /// <param name="guidList"></param> /// <param name="strokeLookupEntry"></param> #endif internal static uint EncodeStroke( Stroke stroke, Stream stream, #if OLD_ISF Compressor compressor, #endif byte compressionAlgorithm, GuidList guidList, StrokeCollectionSerializer.StrokeLookupEntry strokeLookupEntry) { uint cbWrite = SavePackets(stroke, stream, #if OLD_ISF compressor, #endif strokeLookupEntry); if (stroke.ExtendedProperties.Count > 0) { cbWrite += ExtendedPropertySerializer.EncodeAsISF(stroke.ExtendedProperties, stream, guidList, compressionAlgorithm, false); } return(cbWrite); }
/// <Summary> /// Encodes a DrawingAttriubtesin the ISF stream. /// </Summary> #else /// <Summary> /// Encodes a DrawingAttriubtesin the ISF stream. /// </Summary> #endif internal static uint EncodeAsISF(DrawingAttributes da, Stream stream, GuidList guidList, byte compressionAlgorithm, bool fTag) { #if DEBUG System.Diagnostics.Debug.Assert(compressionAlgorithm == 0); System.Diagnostics.Debug.Assert(fTag == true); #endif Debug.Assert(stream != null); uint cbData = 0; BinaryWriter bw = new BinaryWriter(stream); PersistDrawingFlags(da, stream, guidList, ref cbData, ref bw); PersistColorAndTransparency(da, stream, guidList, ref cbData, ref bw); PersistRasterOperation(da, stream, guidList, ref cbData, ref bw); PersistWidthHeight(da, stream, guidList, ref cbData, ref bw); PersistStylusTip(da, stream, guidList, ref cbData, ref bw); PersistExtendedProperties(da, stream, guidList, ref cbData, ref bw, compressionAlgorithm, fTag); return(cbData); }
/// <summary> /// This functions loads a stroke from a memory stream based on the descriptor and GuidList. It returns /// the no of bytes it has read from the stream to correctly load the stream, which should be same as /// the value of the size parameter. If they are unequal throws ArgumentException. Stroke descriptor is /// used to load the packetproperty as well as ExtendedPropertyCollection on this stroke. Compressor is used /// to decompress the data. /// </summary> /// <param name="compressor"></param> /// <param name="stream"></param> /// <param name="totalBytesInStrokeBlockOfIsfStream"></param> /// <param name="guidList"></param> /// <param name="strokeDescriptor"></param> /// <param name="stylusPointDescription"></param> /// <param name="transform"></param> /// <param name="stylusPoints"></param> /// <param name="extendedProperties"></param> /// <returns></returns> #else /// <summary> /// This functions loads a stroke from a memory stream based on the descriptor and GuidList. It returns /// the no of bytes it has read from the stream to correctly load the stream, which should be same as /// the value of the size parameter. If they are unequal throws ArgumentException. Stroke descriptor is /// used to load the packetproperty as well as ExtendedPropertyCollection on this stroke. Compressor is used /// to decompress the data. /// </summary> /// <param name="stream"></param> /// <param name="totalBytesInStrokeBlockOfIsfStream"></param> /// <param name="guidList"></param> /// <param name="strokeDescriptor"></param> /// <param name="stylusPointDescription"></param> /// <param name="transform"></param> /// <param name="stylusPoints"></param> /// <param name="extendedProperties"></param> #endif static uint DecodeISFIntoStroke( #if OLD_ISF Compressor compressor, #endif Stream stream, uint totalBytesInStrokeBlockOfIsfStream, GuidList guidList, StrokeDescriptor strokeDescriptor, StylusPointDescription stylusPointDescription, Matrix transform, out StylusPointCollection stylusPoints, out ExtendedPropertyCollection extendedProperties) { stylusPoints = null; extendedProperties = null; // We do allow a stroke with no packet data if (0 == totalBytesInStrokeBlockOfIsfStream) { return(0); } uint locallyDecodedBytes; uint remainingBytesInStrokeBlock = totalBytesInStrokeBlockOfIsfStream; // First try to load any packet data locallyDecodedBytes = LoadPackets(stream, remainingBytesInStrokeBlock, #if OLD_ISF compressor, #endif stylusPointDescription, transform, out stylusPoints); if (locallyDecodedBytes > remainingBytesInStrokeBlock) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Packet buffer overflowed the ISF stream")); } remainingBytesInStrokeBlock -= locallyDecodedBytes; if (0 == remainingBytesInStrokeBlock) { return(locallyDecodedBytes); } // Now read the extended propertes for (int iTag = 1; iTag < strokeDescriptor.Template.Count && remainingBytesInStrokeBlock > 0; iTag++) { KnownTagCache.KnownTagIndex tag = strokeDescriptor.Template[iTag - 1]; switch (tag) { case MS.Internal.Ink.InkSerializedFormat.KnownTagCache.KnownTagIndex.StrokePropertyList: { // we've found the stroke extended properties. Load them now. while (iTag < strokeDescriptor.Template.Count && remainingBytesInStrokeBlock > 0) { tag = strokeDescriptor.Template[iTag]; object data; Guid guid = guidList.FindGuid(tag); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Stroke Custom Attribute tag embedded in ISF stream does not match guid table")); } // load the extended property data from the stream (and decode the type) locallyDecodedBytes = ExtendedPropertySerializer.DecodeAsISF(stream, remainingBytesInStrokeBlock, guidList, tag, ref guid, out data); // add the guid/data pair into the property collection (don't redecode the type) if (extendedProperties == null) { extendedProperties = new ExtendedPropertyCollection(); } extendedProperties[guid] = data; if (locallyDecodedBytes > remainingBytesInStrokeBlock) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); } remainingBytesInStrokeBlock -= locallyDecodedBytes; iTag++; } } break; case MS.Internal.Ink.InkSerializedFormat.KnownTagCache.KnownTagIndex.Buttons: { // Next tag is count of buttons and the tags for the button guids iTag += (int)((uint)strokeDescriptor.Template[iTag]) + 1; } break; // ignore any tags embedded in the Stroke block that this // version of the ISF decoder doesn't understand default: { System.Diagnostics.Trace.WriteLine("Ignoring unhandled stroke tag in ISF stroke descriptor"); } break; } } // Now try to load any tagged property data or point property data while (remainingBytesInStrokeBlock > 0) { // Read the tag first KnownTagCache.KnownTagIndex tag; uint uiTag; locallyDecodedBytes = SerializationHelper.Decode(stream, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (locallyDecodedBytes > remainingBytesInStrokeBlock) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); } remainingBytesInStrokeBlock -= locallyDecodedBytes; // if it is a point property block switch (tag) { case MS.Internal.Ink.InkSerializedFormat.KnownTagCache.KnownTagIndex.PointProperty: { // First load the totalBytesInStrokeBlockOfIsfStream of the point property block uint cbsize; locallyDecodedBytes = SerializationHelper.Decode(stream, out cbsize); if (locallyDecodedBytes > remainingBytesInStrokeBlock) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); } remainingBytesInStrokeBlock -= locallyDecodedBytes; while (remainingBytesInStrokeBlock > 0) { // First read the tag corresponding to the property locallyDecodedBytes = SerializationHelper.Decode(stream, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (locallyDecodedBytes > remainingBytesInStrokeBlock) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); } remainingBytesInStrokeBlock -= locallyDecodedBytes; // Now read the packet index for which the property will apply uint propindex; locallyDecodedBytes = SerializationHelper.Decode(stream, out propindex); if (locallyDecodedBytes > remainingBytesInStrokeBlock) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); } remainingBytesInStrokeBlock -= locallyDecodedBytes; uint propsize; locallyDecodedBytes = SerializationHelper.Decode(stream, out propsize); if (locallyDecodedBytes > remainingBytesInStrokeBlock) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); } remainingBytesInStrokeBlock -= locallyDecodedBytes; // Compressed data totalBytesInStrokeBlockOfIsfStream propsize += 1; // Make sure we have enough data to read if (propsize > remainingBytesInStrokeBlock) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); } byte[] in_buffer = new byte[propsize]; uint bytesRead = StrokeCollectionSerializer.ReliableRead(stream, in_buffer, propsize); if (propsize != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected")); } byte[] out_buffer = Compressor.DecompressPropertyData(in_buffer); System.Diagnostics.Debug.Assert(false, "ExtendedProperties for points are not supported"); // skip the bytes in both success & failure cases // Note: Point ExtendedProperties are discarded remainingBytesInStrokeBlock -= propsize; } } break; default: { object data; Guid guid = guidList.FindGuid(tag); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Stroke Custom Attribute tag embedded in ISF stream does not match guid table")); } // load the extended property data from the stream (and decode the type) locallyDecodedBytes = ExtendedPropertySerializer.DecodeAsISF(stream, remainingBytesInStrokeBlock, guidList, tag, ref guid, out data); // add the guid/data pair into the property collection (don't redecode the type) if (extendedProperties == null) { extendedProperties = new ExtendedPropertyCollection(); } extendedProperties[guid] = data; if (locallyDecodedBytes > remainingBytesInStrokeBlock) { throw new InvalidOperationException(StrokeCollectionSerializer.ISFDebugMessage("ExtendedProperty decoded totalBytesInStrokeBlockOfIsfStream exceeded ISF stream totalBytesInStrokeBlockOfIsfStream")); } remainingBytesInStrokeBlock -= locallyDecodedBytes; } break; } } if (0 != remainingBytesInStrokeBlock) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); } return(totalBytesInStrokeBlockOfIsfStream); }
private static void PersistColorAndTransparency(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw) { // if the color is non-default (e.g. not black), then store it // the v1 encoder throws away the default color (Black) so it isn't valuable // to save. if (da.ContainsPropertyData(KnownIds.Color)) { Color daColor = da.Color; System.Diagnostics.Debug.Assert(da.Color != (Color)DrawingAttributes.GetDefaultDrawingAttributeValue(KnownIds.Color), "Color was put in the EPC for the default value!"); //Note: we don't store the alpha value of the color (we don't use it) uint r = (uint)daColor.R, g = (uint)daColor.G, b = (uint)(daColor.B); uint colorVal = r + (g << Native.BitsPerByte) + (b << (Native.BitsPerByte * 2)); Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.Color, true)); cbData += SerializationHelper.Encode(stream, colorVal); } //set transparency if Color.A is set byte alphaChannel = da.Color.A; if (alphaChannel != 255) { //note: Color.A is set to 255 by default, which means fully opaque //transparency is just the opposite - 0 means fully opaque so //we need to flip the values int transparency = MathHelper.AbsNoThrow(( (int)alphaChannel ) - 255); Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.Transparency, true)); cbData += SerializationHelper.Encode(stream, Convert.ToUInt32(transparency)); } }
/// <Summary> /// Encodes a DrawingAttriubtesin the ISF stream. /// </Summary> #endif internal static uint EncodeAsISF(DrawingAttributes da, Stream stream, GuidList guidList, byte compressionAlgorithm, bool fTag) { #if DEBUG System.Diagnostics.Debug.Assert(compressionAlgorithm == 0); System.Diagnostics.Debug.Assert(fTag == true); #endif Debug.Assert(stream != null); uint cbData = 0; BinaryWriter bw = new BinaryWriter(stream); PersistDrawingFlags(da, stream, guidList, ref cbData, ref bw); PersistColorAndTransparency(da, stream, guidList, ref cbData, ref bw); PersistRasterOperation(da, stream, guidList, ref cbData, ref bw); PersistWidthHeight(da, stream, guidList, ref cbData, ref bw); PersistStylusTip(da, stream, guidList, ref cbData, ref bw); PersistExtendedProperties(da, stream, guidList, ref cbData, ref bw, compressionAlgorithm, fTag); return cbData; }
/// <summary> /// Loads a DrawingAttributes Table from the stream and adds individual drawing attributes to the drawattr /// list passed /// </summary> #endif private uint LoadDrawAttrsTable(Stream strm, GuidList guidList, uint cbSize) { _drawingAttributesTable.Clear(); // First, allocate a temporary buffer and read the stream into it. // These will be compressed DRAW_ATTR structures. uint cbTotal = cbSize; // OK, now we count the number of DRAW_ATTRS compressed into this block uint cbDA = 0; while (cbTotal > 0) { // First read the size of the first drawing attributes block uint cb = SerializationHelper.Decode(strm, out cbDA); if (cbSize < cb) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cb; if (cbTotal < cbDA) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); // Create a new drawing attribute DrawingAttributes attributes = new DrawingAttributes(); // pull off our defaults onthe drawing attribute as we need to // respect what the ISF has. attributes.DrawingFlags = 0; cb = DrawingAttributeSerializer.DecodeAsISF(strm, guidList, cbDA, attributes); // Load the stream into this attribute if (cbSize < cbDA) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); cbTotal -= cbDA; // Add this attribute to the global list _drawingAttributesTable.Add(attributes); } if (0 != cbTotal) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"),"strm"); return cbSize; }
/// <summary> /// This function builds list of all unique Tables, ie Stroke Descriptor Table, Metric Descriptor Table, Transform Descriptor Table /// and Drawing Attributes Table based on all the strokes. Each entry in the Table is unique with respect to the table. /// </summary> /// <param name="guidList"></param> private void BuildTables(GuidList guidList) { _transformTable.Clear(); _strokeDescriptorTable.Clear(); _metricTable.Clear(); _drawingAttributesTable.Clear(); int count = 0; for (count = 0; count < _coreStrokes.Count; count++) { Stroke stroke = _coreStrokes[count]; // First get the updated descriptor from the stroke StrokeDescriptor strokeDescriptor; MetricBlock metricBlock; StrokeSerializer.BuildStrokeDescriptor(stroke, guidList, _strokeLookupTable[stroke], out strokeDescriptor, out metricBlock); bool fMatch = false; // Compare this with all the global stroke descriptor for a match for (int descriptorIndex = 0; descriptorIndex < _strokeDescriptorTable.Count; descriptorIndex++) { if (strokeDescriptor.IsEqual(_strokeDescriptorTable[descriptorIndex])) { fMatch = true; _strokeLookupTable[stroke].StrokeDescriptorTableIndex = (uint)descriptorIndex; break; } } if (false == fMatch) { _strokeDescriptorTable.Add(strokeDescriptor); _strokeLookupTable[stroke].StrokeDescriptorTableIndex = (uint)_strokeDescriptorTable.Count - 1; } // If there is at least one entry in the metric block, check if the current Block is equvalent to // any of the existing one. fMatch = false; for (int tmp = 0; tmp < _metricTable.Count; tmp++) { MetricBlock block = _metricTable[tmp]; SetType type = SetType.SubSet; if (block.CompareMetricBlock(metricBlock, ref type)) { // This entry exists in the list. If it is a subset of the element, do nothing. // Otherwise, replace the entry with this one if (type == SetType.SuperSet) { _metricTable[tmp] = metricBlock; } fMatch = true; _strokeLookupTable[stroke].MetricDescriptorTableIndex = (uint)tmp; break; } } if (false == fMatch) { _metricTable.Add(metricBlock); _strokeLookupTable[stroke].MetricDescriptorTableIndex = (uint)(_metricTable.Count - 1); } // Now build the Transform Table fMatch = false; // // always identity // TransformDescriptor xform = StrokeCollectionSerializer.IdentityTransformDescriptor; // First check to see if this matches with any existing Transform Blocks for (int i = 0; i < _transformTable.Count; i++) { if (true == xform.Compare(_transformTable[i])) { fMatch = true; _strokeLookupTable[stroke].TransformTableIndex = (uint)i; break; } } if (false == fMatch) { _transformTable.Add(xform); _strokeLookupTable[stroke].TransformTableIndex = (uint)(_transformTable.Count - 1); } // Now build the drawing attributes table fMatch = false; DrawingAttributes drattrs = _coreStrokes[count].DrawingAttributes; // First check to see if this matches with any existing transform blocks for (int i = 0; i < _drawingAttributesTable.Count; i++) { if (true == drattrs.Equals(_drawingAttributesTable[i])) { fMatch = true; _strokeLookupTable[stroke].DrawingAttributesTableIndex = (uint)i; break; } } if (false == fMatch) { _drawingAttributesTable.Add(drattrs); _strokeLookupTable[stroke].DrawingAttributesTableIndex = (uint)_drawingAttributesTable.Count - 1; } } }
private static void PersistColorAndTransparency(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw) { // if the color is non-default (e.g. not black), then store it // the v1 encoder throws away the default color (Black) so it isn't valuable // to save. if (da.ContainsPropertyData(KnownIds.Color)) { Color daColor = da.Color; System.Diagnostics.Debug.Assert(da.Color != (Color)DrawingAttributes.GetDefaultDrawingAttributeValue(KnownIds.Color), "Color was put in the EPC for the default value!"); //Note: we don't store the alpha value of the color (we don't use it) uint r = (uint)daColor.R, g = (uint)daColor.G, b = (uint)(daColor.B); uint colorVal = r + (g << Native.BitsPerByte) + (b << (Native.BitsPerByte * 2)); Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.Color, true)); cbData += SerializationHelper.Encode(stream, colorVal); } //set transparency if Color.A is set byte alphaChannel = da.Color.A; if (alphaChannel != 255) { //note: Color.A is set to 255 by default, which means fully opaque //transparency is just the opposite - 0 means fully opaque so //we need to flip the values int transparency = MathHelper.AbsNoThrow(((int)alphaChannel) - 255); Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.Transparency, true)); cbData += SerializationHelper.Encode(stream, Convert.ToUInt32(transparency)); } }
/// <summary> /// Saves all elements in this list in the stream passed with the tags being generated based on the GuidList /// by the caller and using compressionAlgorithm as the preferred algorith identifier. For ExtendedPropertyCollection associated /// with Ink, drawing attributes and Point properties, we need to write the tag while saving them and hence /// fTag param is true. For strokes, the Tag is stored in the stroke descriptor and hence we don't store the /// tag /// </summary> /// <param name="attributes">Custom attributes to encode</param> /// <param name="stream">If stream is null, then size is calculated only.</param> /// <param name="guidList"></param> /// <param name="compressionAlgorithm"></param> /// <param name="fTag"></param> #endif internal static uint EncodeAsISF(ExtendedPropertyCollection attributes, Stream stream, GuidList guidList, byte compressionAlgorithm, bool fTag) { uint cbWrite = 0; for (int i = 0; i < attributes.Count; i++) { ExtendedProperty prop = attributes[i]; using (MemoryStream localStream = new MemoryStream(10)) //reasonable default { ExtendedPropertySerializer.EncodeToStream(prop, localStream); byte[] data = localStream.ToArray(); cbWrite += ExtendedPropertySerializer.EncodeAsISF(prop.Id, data, stream, guidList, compressionAlgorithm, fTag); } } return(cbWrite); }
/// <summary> /// Loads drawing attributes from a memory buffer. /// </summary> /// <param name="stream">Memory buffer to read from</param> /// <param name="guidList">Guid tags if extended properties are used</param> /// <param name="maximumStreamSize">Maximum size of buffer to read through</param> /// <param name="da">The drawing attributes collection to decode into</param> /// <returns>Number of bytes read</returns> #else /// <summary> /// Loads drawing attributes from a memory buffer. /// </summary> /// <param name="stream">Memory buffer to read from</param> /// <param name="guidList">Guid tags if extended properties are used</param> /// <param name="maximumStreamSize">Maximum size of buffer to read through</param> /// <param name="da">The drawing attributes collection to decode into</param> /// <returns>Number of bytes read</returns> #endif internal static uint DecodeAsISF(Stream stream, GuidList guidList, uint maximumStreamSize, DrawingAttributes da) { PenTip penTip = PenTip.Default; PenStyle penStyle = PenStyle.Default; double stylusWidth = DrawingAttributeSerializer.V1PenWidthWhenWidthIsMissing; double stylusHeight = DrawingAttributeSerializer.V1PenHeightWhenHeightIsMissing; uint rasterOperation = DrawingAttributeSerializer.RasterOperationDefaultV1; int transparency = DrawingAttributeSerializer.TransparencyDefaultV1; bool widthIsSetInISF = false; //did we find KnownIds.Width? bool heightIsSetInISF = false; //did we find KnownIds.Height? uint cbTotal = maximumStreamSize; while (maximumStreamSize > 0) { KnownTagCache.KnownTagIndex tag; uint uiTag; // First read the tag uint cb = SerializationHelper.Decode(stream, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (maximumStreamSize < cb) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("ISF size is larger than maximum stream size")); } maximumStreamSize -= cb; // Get the guid based on the tag Guid guid = guidList.FindGuid(tag); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Drawing Attribute tag embedded in ISF stream does not match guid table")); } uint dw = 0; if (KnownIds.PenTip == guid) { cb = SerializationHelper.Decode(stream, out dw); penTip = (PenTip)dw; if (!PenTipHelper.IsDefined(penTip)) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid PenTip value found in ISF stream")); } maximumStreamSize -= cb; } else if (KnownIds.PenStyle == guid) { cb = SerializationHelper.Decode(stream, out dw); penStyle = (PenStyle)dw; maximumStreamSize -= cb; } else if (KnownIds.DrawingFlags == guid) { // Encode the drawing flags with considerations for v2 model cb = SerializationHelper.Decode(stream, out dw); DrawingFlags flags = (DrawingFlags)dw; da.DrawingFlags = flags; maximumStreamSize -= cb; } else if (KnownIds.RasterOperation == guid) { uint ropSize = GuidList.GetDataSizeIfKnownGuid(KnownIds.RasterOperation); if (ropSize == 0) { throw new InvalidOperationException(StrokeCollectionSerializer.ISFDebugMessage("ROP data size was not found")); } byte[] data = new byte[ropSize]; stream.Read(data, 0, (int)ropSize); if (data != null && data.Length > 0) { //data[0] holds the allowable values of 0-255 rasterOperation = Convert.ToUInt32(data[0]); } maximumStreamSize -= ropSize; } else if (KnownIds.CurveFittingError == guid) { cb = SerializationHelper.Decode(stream, out dw); da.FittingError = (int)dw; maximumStreamSize -= cb; } else if (KnownIds.StylusHeight == guid || KnownIds.StylusWidth == guid) { double _size; cb = SerializationHelper.Decode(stream, out dw); _size = (double)dw; maximumStreamSize -= cb; if (maximumStreamSize > 0) { cb = SerializationHelper.Decode(stream, out dw); maximumStreamSize -= cb; if (KnownTagCache.KnownTagIndex.Mantissa == (KnownTagCache.KnownTagIndex)dw) { uint cbInSize; // First thing that is in there is maximumStreamSize of the data cb = SerializationHelper.Decode(stream, out cbInSize); maximumStreamSize -= cb; // in maximumStreamSize is one more than the decoded no cbInSize++; if (cbInSize > maximumStreamSize) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("ISF size if greater then maximum stream size")); } byte[] in_data = new byte[cbInSize]; uint bytesRead = (uint)stream.Read(in_data, 0, (int)cbInSize); if (cbInSize != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected")); } byte[] out_buffer = Compressor.DecompressPropertyData(in_data); using (MemoryStream localStream = new MemoryStream(out_buffer)) using (BinaryReader rdr = new BinaryReader(localStream)) { short sFraction = rdr.ReadInt16(); _size += (double)(sFraction / DrawingAttributes.StylusPrecision); maximumStreamSize -= cbInSize; } } else { // Seek it back by cb stream.Seek(-cb, SeekOrigin.Current); maximumStreamSize += cb; } } if (KnownIds.StylusWidth == guid) { widthIsSetInISF = true; stylusWidth = _size; } else { heightIsSetInISF = true; stylusHeight = _size; } } else if (KnownIds.Transparency == guid) { cb = SerializationHelper.Decode(stream, out dw); transparency = (int)dw; maximumStreamSize -= cb; } else if (KnownIds.Color == guid) { cb = SerializationHelper.Decode(stream, out dw); Color color = Color.FromRgb((byte)(dw & 0xff), (byte)((dw & 0xff00) >> Native.BitsPerByte), (byte)((dw & 0xff0000) >> (Native.BitsPerByte * 2))); da.Color = color; maximumStreamSize -= cb; } else if (KnownIds.StylusTipTransform == guid) { try { object data; cb = ExtendedPropertySerializer.DecodeAsISF(stream, maximumStreamSize, guidList, tag, ref guid, out data); Matrix matrix = Matrix.Parse((string)data); da.StylusTipTransform = matrix; } catch (InvalidOperationException) // Matrix.Parse failed. { System.Diagnostics.Debug.Assert(false, "Corrupt Matrix in the ExtendedPropertyCollection!"); } finally { maximumStreamSize -= cb; } } else { object data; cb = ExtendedPropertySerializer.DecodeAsISF(stream, maximumStreamSize, guidList, tag, ref guid, out data); maximumStreamSize -= cb; da.AddPropertyData(guid, data); } } if (0 != maximumStreamSize) { throw new ArgumentException(); } // // time to create our drawing attributes. // // 1) First we need to evaluate PenTip / StylusTip // Here is the V1 - V2 mapping // // PenTip.Circle == StylusTip.Ellipse // PenTip.Rectangle == StylusTip.Rectangle // PenTip.Rectangle == StylusTip.Diamond if (penTip == PenTip.Default) { //Since StylusTip is stored in the EPC at this point (if set), we can compare against it here. if (da.StylusTip != StylusTip.Ellipse) { // // StylusTip was set to something other than Ellipse // when we last serialized (or else StylusTip would be Ellipse, the default) // when StylusTip is != Ellipse and we serialize, we set PenTip to Rectangle // which is not the default. Therefore, if PenTip is back to Circle, // that means someone set it in V1 and we should respect that by // changing StylusTip back to Ellipse // da.StylusTip = StylusTip.Ellipse; } //else da.StylusTip is already set } else { System.Diagnostics.Debug.Assert(penTip == PenTip.Rectangle); if (da.StylusTip == StylusTip.Ellipse) { // // PenTip is Rectangle and StylusTip was either not set // before or was set to Ellipse and PenTip was changed // in a V1 ink object. Either way, we need to change StylusTip to Rectangle da.StylusTip = StylusTip.Rectangle; } //else da.StylusTip is already set } // // 2) next we need to set hight and width // if (da.StylusTip == StylusTip.Ellipse && widthIsSetInISF && !heightIsSetInISF) { // // special case: V1 PenTip of Circle only used Width to compute the circle size // and so it only serializes Width of 53 // but since our default is Ellipse, if Height is unset and we use the default // height of 30, then your ink that looked like 53,53 in V1 will look // like 30,53 here. // // stylusHeight = stylusWidth; da.HeightChangedForCompatabity = true; } // need to convert width/height into Avalon, since they are stored in HIMETRIC in ISF stylusHeight *= StrokeCollectionSerializer.HimetricToAvalonMultiplier; stylusWidth *= StrokeCollectionSerializer.HimetricToAvalonMultiplier; // Map 0.0 width to DrawingAttributes.DefaultXXXXXX (V1 53 equivalent) double height = DoubleUtil.IsZero(stylusHeight) ? (Double)DrawingAttributes.GetDefaultDrawingAttributeValue(KnownIds.StylusHeight) : stylusHeight; double width = DoubleUtil.IsZero(stylusWidth) ? (Double)DrawingAttributes.GetDefaultDrawingAttributeValue(KnownIds.StylusWidth) : stylusWidth; da.Height = GetCappedHeightOrWidth(height); da.Width = GetCappedHeightOrWidth(width); // // 3) next we need to set IsHighlighter (by looking for RasterOperation.MaskPen) // // // always store raster op // da.RasterOperation = rasterOperation; if (rasterOperation == DrawingAttributeSerializer.RasterOperationDefaultV1) { // // if rasterop is default, make sure IsHighlighter isn't in the EPC // if (da.ContainsPropertyData(KnownIds.IsHighlighter)) { da.RemovePropertyData(KnownIds.IsHighlighter); } } else { if (rasterOperation == DrawingAttributeSerializer.RasterOperationMaskPen) { da.IsHighlighter = true; } } //else, IsHighlighter will be set to false by default, no need to set it // // 4) see if there is a transparency we need to add to color // if (transparency > DrawingAttributeSerializer.TransparencyDefaultV1) { //note: Color.A is set to 255 by default, which means fully opaque //transparency is just the opposite - 0 means fully opaque so //we need to flip the values int alpha = MathHelper.AbsNoThrow(transparency - 255); Color color = da.Color; color.A = Convert.ToByte(alpha); da.Color = color; } return(cbTotal); }
/// <summary> /// Builds the Stroke Descriptor for this stroke based on Packet Layout and Extended Properties /// For details on how this is strored please refer to the spec. /// </summary> internal static void BuildStrokeDescriptor( Stroke stroke, GuidList guidList, StrokeCollectionSerializer.StrokeLookupEntry strokeLookupEntry, out StrokeDescriptor strokeDescriptor, out MetricBlock metricBlock) { // Initialize the metric block for this stroke metricBlock = new MetricBlock(); // Clear any existing template strokeDescriptor = new StrokeDescriptor(); // Uninitialized variable passed in AddMetricEntry MetricEntryType metricEntryType; StylusPointDescription stylusPointDescription = stroke.StylusPoints.Description; KnownTagCache.KnownTagIndex tag = guidList.FindTag(KnownIds.X, true); metricEntryType = metricBlock.AddMetricEntry(stylusPointDescription.GetPropertyInfo(StylusPointProperties.X), tag); tag = guidList.FindTag(KnownIds.Y, true); metricEntryType = metricBlock.AddMetricEntry(stylusPointDescription.GetPropertyInfo(StylusPointProperties.Y), tag); ReadOnlyCollection<StylusPointPropertyInfo> propertyInfos = stylusPointDescription.GetStylusPointProperties(); int i = 0; //i is defined out of the for loop so we can use it later for buttons for (i = 2/*past x,y*/; i < propertyInfos.Count; i++) { if (i == StylusPointDescription.RequiredPressureIndex/*2*/ && !strokeLookupEntry.StorePressure) { // // don't store pressure information // continue; } StylusPointPropertyInfo propertyInfo = propertyInfos[i]; if (propertyInfo.IsButton) { //we don't serialize buttons break; } tag = guidList.FindTag(propertyInfo.Id, true); strokeDescriptor.Template.Add(tag); strokeDescriptor.Size += SerializationHelper.VarSize((uint)tag); // Create the MetricEntry for this property if necessary metricEntryType = metricBlock.AddMetricEntry(propertyInfo, tag); } /* we drop button data on the floor. See Windows OS Bugs 1413460 for details int buttonCount = stylusPointDescription.ButtonCount; // Now write the button tags in the Template if (buttonCount > 0) { // First write the TAG_BUTTONS strokeDescriptor.Template.Add(KnownTagCache.KnownTagIndex.Buttons); strokeDescriptor.Size += SerializationHelper.VarSize((uint)KnownTagCache.KnownTagIndex.Buttons); // Next write the i of buttons strokeDescriptor.Template.Add((KnownTagCache.KnownTagIndex)buttonCount); strokeDescriptor.Size += SerializationHelper.VarSize((uint)buttonCount); //we broke above on i when it was a button, it still //points to the first button for (; i < propertyInfos.Count; i++) { StylusPointPropertyInfo propertyInfo = propertyInfos[i]; tag = guidList.FindTag(propertyInfo.Id, false); strokeDescriptor.Template.Add(tag); strokeDescriptor.Size += SerializationHelper.VarSize((uint)tag); } } */ // Now write the extended properties in the template if (stroke.ExtendedProperties.Count > 0) { strokeDescriptor.Template.Add(KnownTagCache.KnownTagIndex.StrokePropertyList); strokeDescriptor.Size += SerializationHelper.VarSize((uint)KnownTagCache.KnownTagIndex.StrokePropertyList); // Now write the tags corresponding to each extended properties of the stroke for (int x = 0; x < stroke.ExtendedProperties.Count; x++) { tag = guidList.FindTag(stroke.ExtendedProperties[(int)x].Id, false); strokeDescriptor.Template.Add(tag); strokeDescriptor.Size += SerializationHelper.VarSize((uint)tag); } } }
/// <summary> /// Returns an array of bytes of the saved stroke /// </summary> /// <param name="stroke">Stroke to save</param> /// <param name="stream">null to calculate only the size</param> /// <param name="compressionAlgorithm"></param> /// <param name="guidList"></param> /// <param name="strokeLookupEntry"></param> #endif internal static uint EncodeStroke( Stroke stroke, Stream stream, #if OLD_ISF Compressor compressor, #endif byte compressionAlgorithm, GuidList guidList, StrokeCollectionSerializer.StrokeLookupEntry strokeLookupEntry) { uint cbWrite = SavePackets( stroke, stream, #if OLD_ISF compressor, #endif strokeLookupEntry); if (stroke.ExtendedProperties.Count > 0) cbWrite += ExtendedPropertySerializer.EncodeAsISF(stroke.ExtendedProperties, stream, guidList, compressionAlgorithm, false); return cbWrite; }
/// <summary> /// Loads a single ExtendedProperty from the stream and add that to the list. Tag may be passed as in /// the case of Stroke ExtendedPropertyCollection where tag is stored in the stroke descriptor or 0 when tag /// is embeded in the stream /// </summary> /// <param name="stream">Memory buffer to load from</param> /// <param name="cbSize">Maximum length of buffer to read</param> /// <param name="guidList">Guid cache to read from</param> /// <param name="tag">Guid tag to lookup</param> /// <param name="guid">Guid of property</param> /// <param name="data">Data of property</param> /// <returns>Length of buffer read</returns> #endif internal static uint DecodeAsISF(Stream stream, uint cbSize, GuidList guidList, KnownTagCache.KnownTagIndex tag, ref Guid guid, out object data) { uint cb, cbRead = 0; uint cbTotal = cbSize; if (0 == cbSize) { throw new InvalidOperationException(SR.Get(SRID.EmptyDataToLoad)); } if (0 == tag) // no tag is passed, it must be embedded in the data { uint uiTag; cb = SerializationHelper.Decode(stream, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (cb > cbTotal) { throw new ArgumentException(SR.Get(SRID.InvalidSizeSpecified), "cbSize"); } cbTotal -= cb; cbRead += cb; System.Diagnostics.Debug.Assert(guid == Guid.Empty); guid = guidList.FindGuid(tag); } if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Custom Attribute tag embedded in ISF stream does not match guid table"), "tag"); } // Try and find the size uint size = GuidList.GetDataSizeIfKnownGuid(guid); if (size > cbTotal) { throw new ArgumentException(SR.Get(SRID.InvalidSizeSpecified), "cbSize"); } // if the size is 0 if (0 == size) { // Size must be embedded in the stream. Find out the compressed data size cb = SerializationHelper.Decode(stream, out size); uint cbInsize = size + 1; cbRead += cb; cbTotal -= cb; if (cbInsize > cbTotal) { throw new ArgumentException(); } byte[] bytes = new byte[cbInsize]; uint bytesRead = (uint)stream.Read(bytes, 0, (int)cbInsize); if (cbInsize != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected"), "cbSize"); } cbRead += cbInsize; cbTotal -= cbInsize; //Find out the Decompressed buffer size using (MemoryStream decompressedStream = new MemoryStream(Compressor.DecompressPropertyData(bytes))) { // Add the property data = ExtendedPropertySerializer.DecodeAttribute(guid, decompressedStream); } } else { // For known size data, we just read the data directly from the stream byte[] bytes = new byte[size]; uint bytesRead = (uint)stream.Read(bytes, 0, (int)size); if (size != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected"), "cbSize"); } using (MemoryStream subStream = new MemoryStream(bytes)) { data = ExtendedPropertySerializer.DecodeAttribute(guid, subStream); } cbTotal -= size; cbRead += size; } return(cbRead); }
/// <summary> /// This function serializes Drawing Attributes Table in the stream. For information on how they are serialized, please refer to the spec. /// </summary> /// <param name="stream"></param> /// <param name="guidList"></param> #endif private uint SerializeDrawingAttrsTable(Stream stream, GuidList guidList) { uint totalSizeOfSerializedBytes = 0; uint sizeOfHeaderInBytes = 0; if (1 == _drawingAttributesTable.Count) { //we always serialize a single DA, even if it has default values so we will write width back to the stream DrawingAttributes drawingAttributes = _drawingAttributesTable[0]; // There is single drawing attribute. Save it along with the size totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, (uint)KnownTagCache.KnownTagIndex.DrawingAttributesBlock); // Get the size of the saved bytes using (MemoryStream drawingAttributeStream = new MemoryStream(16)) //reasonable default based onn profiling { sizeOfHeaderInBytes = DrawingAttributeSerializer.EncodeAsISF(drawingAttributes, drawingAttributeStream, guidList, 0, true); // Write the size first totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, sizeOfHeaderInBytes); // write the data uint bytesWritten = Convert.ToUInt32(drawingAttributeStream.Position); totalSizeOfSerializedBytes += bytesWritten; Debug.Assert(sizeOfHeaderInBytes == bytesWritten); stream.Write( drawingAttributeStream.GetBuffer(), //returns a direct ref, no copied 0, Convert.ToInt32(bytesWritten)); drawingAttributeStream.Dispose(); } } else { // Temporarily declare an array to hold the size of the saved drawing attributes uint[] sizes = new uint[_drawingAttributesTable.Count]; MemoryStream[] drawingAttributeStreams = new MemoryStream[_drawingAttributesTable.Count]; // First calculate the size of each attribute for (int i = 0; i < _drawingAttributesTable.Count; i++) { DrawingAttributes drawingAttributes = _drawingAttributesTable[i]; drawingAttributeStreams[i] = new MemoryStream(16); //reasonable default based on profiling sizes[i] = DrawingAttributeSerializer.EncodeAsISF(drawingAttributes, drawingAttributeStreams[i], guidList, 0, true); sizeOfHeaderInBytes += SerializationHelper.VarSize(sizes[i]) + sizes[i]; } // Now write the KnownTagCache.KnownTagIndex.DrawingAttributesTable first, then sizeOfHeaderInBytes and then individual Drawing Attributes totalSizeOfSerializedBytes = SerializationHelper.Encode(stream, (uint)KnownTagCache.KnownTagIndex.DrawingAttributesTable); totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, sizeOfHeaderInBytes); for (int i = 0; i < _drawingAttributesTable.Count; i++) { DrawingAttributes drawingAttributes = _drawingAttributesTable[i]; // write the size of the block totalSizeOfSerializedBytes += SerializationHelper.Encode(stream, sizes[i]); // write the saved data uint bytesWritten = Convert.ToUInt32(drawingAttributeStreams[i].Position); totalSizeOfSerializedBytes += bytesWritten; Debug.Assert(sizes[i] == bytesWritten); stream.Write( drawingAttributeStreams[i].GetBuffer(), //returns a direct ref, no copies 0, Convert.ToInt32(bytesWritten)); drawingAttributeStreams[i].Dispose(); } } return totalSizeOfSerializedBytes; }
/// <Summary> /// Encodes the ExtendedProperties in the ISF stream. /// </Summary> #else /// <Summary> /// Encodes the ExtendedProperties in the ISF stream. /// </Summary> #endif private static void PersistExtendedProperties(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw, byte compressionAlgorithm, bool fTag) { // Now save the extended properties ExtendedPropertyCollection epcClone = da.CopyPropertyData(); //walk from the back removing EPs that are uses for DrawingAttributes for (int x = epcClone.Count - 1; x >= 0; x--) { // // look for StylusTipTransform while we're at it and turn it into a string // for serialization // if (epcClone[x].Id == KnownIds.StylusTipTransform) { Matrix matrix = (Matrix)epcClone[x].Value; string matrixString = matrix.ToString(System.Globalization.CultureInfo.InvariantCulture); epcClone[x].Value = matrixString; continue; } if (DrawingAttributes.RemoveIdFromExtendedProperties(epcClone[x].Id)) { epcClone.Remove(epcClone[x].Id); } } cbData += ExtendedPropertySerializer.EncodeAsISF(epcClone, stream, guidList, compressionAlgorithm, fTag); }
/// <summary> /// Takes an ISF Stream and populates the StrokeCollection /// attached to this StrokeCollectionSerializer. /// </summary> /// <param name="inputStream">a Stream the raw isf to decode</param> #endif private void DecodeRawISF(Stream inputStream) { Debug.Assert(inputStream != null); KnownTagCache.KnownTagIndex isfTag; uint remainingBytesInStream; uint bytesDecodedInCurrentTag = 0; bool strokeDescriptorBlockDecoded = false; bool drawingAttributesBlockDecoded = false; bool metricBlockDecoded = false; bool transformDecoded = false; uint strokeDescriptorTableIndex = 0; uint oldStrokeDescriptorTableIndex = 0xFFFFFFFF; uint drawingAttributesTableIndex = 0; uint oldDrawingAttributesTableIndex = 0xFFFFFFFF; uint metricDescriptorTableIndex = 0; uint oldMetricDescriptorTableIndex = 0xFFFFFFFF; uint transformTableIndex = 0; uint oldTransformTableIndex = 0xFFFFFFFF; GuidList guidList = new GuidList(); int strokeIndex = 0; StylusPointDescription currentStylusPointDescription = null; Matrix currentTabletToInkTransform = Matrix.Identity; _strokeDescriptorTable = new System.Collections.Generic.List<StrokeDescriptor>(); _drawingAttributesTable = new System.Collections.Generic.List<DrawingAttributes>(); _transformTable = new System.Collections.Generic.List<TransformDescriptor>(); _metricTable = new System.Collections.Generic.List<MetricBlock>(); // First make sure this ink is empty if (0 != _coreStrokes.Count || _coreStrokes.ExtendedProperties.Count != 0) { throw new InvalidOperationException(ISFDebugMessage("ISF decoder cannot operate on non-empty ink container")); } #if OLD_ISF // // store a compressor reference at this scope, if it is needed (if there is a compresson header) and // therefore instanced during this routine, we will dispose of it // in the finally block // Compressor compressor = null; try { #endif // First read the isfTag uint uiTag; uint localBytesDecoded = SerializationHelper.Decode(inputStream, out uiTag); if (0x00 != uiTag) throw new ArgumentException(SR.Get(SRID.InvalidStream)); // Now read the size of the stream localBytesDecoded = SerializationHelper.Decode(inputStream, out remainingBytesInStream); ISFDebugTrace("Decoded Stream Size in Bytes: " + remainingBytesInStream.ToString()); if (0 == remainingBytesInStream) return; while (0 < remainingBytesInStream) { bytesDecodedInCurrentTag = 0; // First read the isfTag localBytesDecoded = SerializationHelper.Decode(inputStream, out uiTag); isfTag = (KnownTagCache.KnownTagIndex)uiTag; if (remainingBytesInStream >= localBytesDecoded) remainingBytesInStream -= localBytesDecoded; else { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } ISFDebugTrace("Decoding Tag: " + ((KnownTagCache.KnownTagIndex)isfTag).ToString()); switch (isfTag) { case KnownTagCache.KnownTagIndex.GuidTable: case KnownTagCache.KnownTagIndex.DrawingAttributesTable: case KnownTagCache.KnownTagIndex.DrawingAttributesBlock: case KnownTagCache.KnownTagIndex.StrokeDescriptorTable: case KnownTagCache.KnownTagIndex.StrokeDescriptorBlock: case KnownTagCache.KnownTagIndex.MetricTable: case KnownTagCache.KnownTagIndex.MetricBlock: case KnownTagCache.KnownTagIndex.TransformTable: case KnownTagCache.KnownTagIndex.ExtendedTransformTable: case KnownTagCache.KnownTagIndex.Stroke: case KnownTagCache.KnownTagIndex.CompressionHeader: case KnownTagCache.KnownTagIndex.PersistenceFormat: case KnownTagCache.KnownTagIndex.HimetricSize: case KnownTagCache.KnownTagIndex.StrokeIds: { localBytesDecoded = SerializationHelper.Decode(inputStream, out bytesDecodedInCurrentTag); if (remainingBytesInStream < (localBytesDecoded + bytesDecodedInCurrentTag)) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "inputStream"); } remainingBytesInStream -= localBytesDecoded; // Based on the isfTag figure out what information we're loading switch (isfTag) { case KnownTagCache.KnownTagIndex.GuidTable: { // Load guid Table localBytesDecoded = guidList.Load(inputStream, bytesDecodedInCurrentTag); break; } case KnownTagCache.KnownTagIndex.DrawingAttributesTable: { // Load drawing attributes table localBytesDecoded = LoadDrawAttrsTable(inputStream, guidList, bytesDecodedInCurrentTag); drawingAttributesBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.DrawingAttributesBlock: { //initialize to V1 defaults, we do it this way as opposed //to dr.DrawingFlags = 0 because this was a perf hot spot //and instancing the epc first mitigates it ExtendedPropertyCollection epc = new ExtendedPropertyCollection(); epc.Add(KnownIds.DrawingFlags, DrawingFlags.Polyline); DrawingAttributes dr = new DrawingAttributes(epc); localBytesDecoded = DrawingAttributeSerializer.DecodeAsISF(inputStream, guidList, bytesDecodedInCurrentTag, dr); _drawingAttributesTable.Add(dr); drawingAttributesBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.StrokeDescriptorTable: { // Load stroke descriptor table localBytesDecoded = DecodeStrokeDescriptorTable(inputStream, bytesDecodedInCurrentTag); strokeDescriptorBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.StrokeDescriptorBlock: { // Load a single stroke descriptor localBytesDecoded = DecodeStrokeDescriptorBlock(inputStream, bytesDecodedInCurrentTag); strokeDescriptorBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.MetricTable: { // Load Metric Table localBytesDecoded = DecodeMetricTable(inputStream, bytesDecodedInCurrentTag); metricBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.MetricBlock: { // Load a single Metric Block MetricBlock blk; localBytesDecoded = DecodeMetricBlock(inputStream, bytesDecodedInCurrentTag, out blk); _metricTable.Clear(); _metricTable.Add(blk); metricBlockDecoded = true; break; } case KnownTagCache.KnownTagIndex.TransformTable: { // Load Transform Table localBytesDecoded = DecodeTransformTable(inputStream, bytesDecodedInCurrentTag, false); transformDecoded = true; break; } case KnownTagCache.KnownTagIndex.ExtendedTransformTable: { // non-double transform table should have already been loaded if (!transformDecoded) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } // Load double-sized Transform Table localBytesDecoded = DecodeTransformTable(inputStream, bytesDecodedInCurrentTag, true); break; } case KnownTagCache.KnownTagIndex.PersistenceFormat: { uint fmt; localBytesDecoded = SerializationHelper.Decode(inputStream, out fmt); // Set the appropriate persistence information if (0 == fmt) { CurrentPersistenceFormat = PersistenceFormat.InkSerializedFormat; } else if (0x00000001 == fmt) { CurrentPersistenceFormat = PersistenceFormat.Gif; } break; } case KnownTagCache.KnownTagIndex.HimetricSize: { // Loads the Hi Metric Size for Fortified GIFs int sz; localBytesDecoded = SerializationHelper.SignDecode(inputStream, out sz); if (localBytesDecoded > remainingBytesInStream) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); _himetricSize.X = (double)sz; localBytesDecoded += SerializationHelper.SignDecode(inputStream, out sz); _himetricSize.Y = (double)sz; break; } case KnownTagCache.KnownTagIndex.CompressionHeader: { #if OLD_ISF byte[] data = new byte[bytesDecodedInCurrentTag]; // read the header from the stream uint bytesRead = StrokeCollectionSerializer.ReliableRead(inputStream, data, bytesDecodedInCurrentTag); if (bytesDecodedInCurrentTag != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected"), "isfStream"); } uint size = bytesDecodedInCurrentTag; compressor = new Compressor(data, ref size); // in case the actual number of bytes read by the compressor // is less than the encoder had expected (e.g. compression // header was encoded as 10 bytes, but only 7 bytes were read) // then we don't want to adjust the stream position because // there are likely other following tags that are encoded // after the compression tag. This should never happen, // so just fail if the compressor is broken or the ISF is // corrupted. if (size != bytesDecodedInCurrentTag) { throw new InvalidOperationException(ISFDebugMessage("Compressor intialization reported inconsistent size")); } #else //just advance the inputstream position, we don't need //no compression header in the new isf decoding inputStream.Seek(bytesDecodedInCurrentTag, SeekOrigin.Current); #endif localBytesDecoded = bytesDecodedInCurrentTag; break; } case KnownTagCache.KnownTagIndex.StrokeIds: { localBytesDecoded = LoadStrokeIds(inputStream, bytesDecodedInCurrentTag); break; } case KnownTagCache.KnownTagIndex.Stroke: { ISFDebugTrace(" Decoding Stroke Id#(" + (strokeIndex + 1).ToString() + ")"); StrokeDescriptor strokeDescriptor = null; // Load the stroke descriptor based on the index from the list of unique // stroke descriptors if (strokeDescriptorBlockDecoded) { if (oldStrokeDescriptorTableIndex != strokeDescriptorTableIndex) { if (_strokeDescriptorTable.Count <= strokeDescriptorTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } strokeDescriptor = _strokeDescriptorTable[(int)strokeDescriptorTableIndex]; } // use new transform if the last transform is uninit'd or has changed if (oldTransformTableIndex != transformTableIndex) { // if transform was specified in the ISF stream if (transformDecoded) { if (_transformTable.Count <= transformTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); // Load the transform descriptor based on the index from the list of unique // transforn descriptors currentTabletToInkTransform = LoadTransform(_transformTable[(int)transformTableIndex]); } oldTransformTableIndex = transformTableIndex; // cache the transform by remembering the index // since ISF is stored in HIMETRIC, and we want to expose packet data // as Avalon units, we'll update the convert the transform before loading the stroke currentTabletToInkTransform.Scale(StrokeCollectionSerializer.HimetricToAvalonMultiplier, StrokeCollectionSerializer.HimetricToAvalonMultiplier); } MetricBlock metricBlock = null; // Load the metric block based on the index from the list of unique metric blocks if (metricBlockDecoded) { if (oldMetricDescriptorTableIndex != metricDescriptorTableIndex) { if (_metricTable.Count <= metricDescriptorTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } metricBlock = _metricTable[(int)metricDescriptorTableIndex]; } DrawingAttributes activeDrawingAttributes = null; // Load the drawing attributes based on the index from the list of unique drawing attributes if (drawingAttributesBlockDecoded) { if (oldDrawingAttributesTableIndex != drawingAttributesTableIndex) { if (_drawingAttributesTable.Count <= drawingAttributesTableIndex) throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); oldDrawingAttributesTableIndex = drawingAttributesTableIndex; } DrawingAttributes currDA = (DrawingAttributes)_drawingAttributesTable[(int)drawingAttributesTableIndex]; //we always clone so we don't get strokes that share DAs, which can lead //to all sorts of unpredictable behavior (ex: see Windows OS Bugs 1450047) activeDrawingAttributes = currDA.Clone(); } // if we didn't find an existing da to use, instance a new one if (activeDrawingAttributes == null) { activeDrawingAttributes = new DrawingAttributes(); } // Now create the StylusPacketDescription from the stroke descriptor and metric block if (oldMetricDescriptorTableIndex != metricDescriptorTableIndex || oldStrokeDescriptorTableIndex != strokeDescriptorTableIndex) { currentStylusPointDescription = BuildStylusPointDescription(strokeDescriptor, metricBlock, guidList); oldStrokeDescriptorTableIndex = strokeDescriptorTableIndex; oldMetricDescriptorTableIndex = metricDescriptorTableIndex; } // Load the stroke Stroke localStroke; #if OLD_ISF localBytesDecoded = StrokeSerializer.DecodeStroke(inputStream, bytesDecodedInCurrentTag, guidList, strokeDescriptor, currentStylusPointDescription, activeDrawingAttributes, currentTabletToInkTransform, compressor, out localStroke); #else localBytesDecoded = StrokeSerializer.DecodeStroke(inputStream, bytesDecodedInCurrentTag, guidList, strokeDescriptor, currentStylusPointDescription, activeDrawingAttributes, currentTabletToInkTransform, out localStroke); #endif if (localStroke != null) { _coreStrokes.AddWithoutEvent(localStroke); strokeIndex++; } break; } default: { throw new InvalidOperationException(ISFDebugMessage("Invalid ISF tag logic")); } } // if this isfTag's decoded size != expected size, then error out if (localBytesDecoded != bytesDecodedInCurrentTag) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } break; } case KnownTagCache.KnownTagIndex.Transform: case KnownTagCache.KnownTagIndex.TransformIsotropicScale: case KnownTagCache.KnownTagIndex.TransformAnisotropicScale: case KnownTagCache.KnownTagIndex.TransformRotate: case KnownTagCache.KnownTagIndex.TransformTranslate: case KnownTagCache.KnownTagIndex.TransformScaleAndTranslate: { // Load a single Transform Block TransformDescriptor xform; bytesDecodedInCurrentTag = DecodeTransformBlock(inputStream, isfTag, remainingBytesInStream, false, out xform); transformDecoded = true; _transformTable.Clear(); _transformTable.Add(xform); break; } case KnownTagCache.KnownTagIndex.TransformTableIndex: { // Load the Index into the Transform Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out transformTableIndex); break; } case KnownTagCache.KnownTagIndex.MetricTableIndex: { // Load the Index into the Metric Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out metricDescriptorTableIndex); break; } case KnownTagCache.KnownTagIndex.DrawingAttributesTableIndex: { // Load the Index into the Drawing Attributes Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out drawingAttributesTableIndex); break; } case KnownTagCache.KnownTagIndex.InkSpaceRectangle: { // Loads the Ink Space Rectangle information bytesDecodedInCurrentTag = DecodeInkSpaceRectangle(inputStream, remainingBytesInStream); break; } case KnownTagCache.KnownTagIndex.StrokeDescriptorTableIndex: { // Load the Index into the Stroke Descriptor Table which will be used by the stroke following this till // a next different Index is found bytesDecodedInCurrentTag = SerializationHelper.Decode(inputStream, out strokeDescriptorTableIndex); break; } default: { if ((uint)isfTag >= KnownIdCache.CustomGuidBaseIndex || ((uint)isfTag >= KnownTagCache.KnownTagCount && ((uint)isfTag < (KnownTagCache.KnownTagCount + KnownIdCache.OriginalISFIdTable.Length)))) { ISFDebugTrace(" CUSTOM_GUID=" + guidList.FindGuid(isfTag).ToString()); // Loads any custom property data bytesDecodedInCurrentTag = remainingBytesInStream; Guid guid = guidList.FindGuid(isfTag); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Global Custom Attribute tag embedded in ISF stream does not match guid table"), "inkdata"); } object data; // load the custom property data from the stream (and decode the type) localBytesDecoded = ExtendedPropertySerializer.DecodeAsISF(inputStream, bytesDecodedInCurrentTag, guidList, isfTag, ref guid, out data); if (localBytesDecoded > bytesDecodedInCurrentTag) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "inkdata"); } // add the guid/data pair into the property collection (don't redecode the type) _coreStrokes.ExtendedProperties[guid] = data; } else { // Skip objects that this library doesn't know about // First read the size associated with this unknown isfTag localBytesDecoded = SerializationHelper.Decode(inputStream, out bytesDecodedInCurrentTag); if (remainingBytesInStream < (localBytesDecoded + bytesDecodedInCurrentTag)) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } else { inputStream.Seek(bytesDecodedInCurrentTag + localBytesDecoded, SeekOrigin.Current); } } bytesDecodedInCurrentTag = localBytesDecoded; break; } } ISFDebugTrace(" Size = " + bytesDecodedInCurrentTag.ToString()); if (bytesDecodedInCurrentTag > remainingBytesInStream) { throw new ArgumentException(ISFDebugMessage("Invalid ISF data")); } // update remaining ISF buffer length with decoded so far remainingBytesInStream -= bytesDecodedInCurrentTag; } #if OLD_ISF } finally { if (null != compressor) { compressor.Dispose(); compressor = null; } } #endif if (0 != remainingBytesInStream) throw new ArgumentException(ISFDebugMessage("Invalid ISF data"), "inkdata"); }
/// <Summary> /// Encodes the ExtendedProperties in the ISF stream. /// </Summary> #endif private static void PersistExtendedProperties(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw, byte compressionAlgorithm, bool fTag) { // Now save the extended properties ExtendedPropertyCollection epcClone = da.CopyPropertyData(); //walk from the back removing EPs that are uses for DrawingAttributes for (int x = epcClone.Count - 1; x >= 0; x--) { // // look for StylusTipTransform while we're at it and turn it into a string // for serialization // if (epcClone[x].Id == KnownIds.StylusTipTransform) { Matrix matrix = (Matrix)epcClone[x].Value; string matrixString = matrix.ToString(System.Globalization.CultureInfo.InvariantCulture); epcClone[x].Value = matrixString; continue; } if (DrawingAttributes.RemoveIdFromExtendedProperties(epcClone[x].Id)) { epcClone.Remove(epcClone[x].Id); } } cbData += ExtendedPropertySerializer.EncodeAsISF(epcClone, stream, guidList, compressionAlgorithm, fTag); }
/// <summary> /// Loads drawing attributes from a memory buffer. /// </summary> /// <param name="stream">Memory buffer to read from</param> /// <param name="guidList">Guid tags if extended properties are used</param> /// <param name="maximumStreamSize">Maximum size of buffer to read through</param> /// <param name="da">The drawing attributes collection to decode into</param> /// <returns>Number of bytes read</returns> #endif internal static uint DecodeAsISF(Stream stream, GuidList guidList, uint maximumStreamSize, DrawingAttributes da) { PenTip penTip = PenTip.Default; PenStyle penStyle = PenStyle.Default; double stylusWidth = DrawingAttributeSerializer.V1PenWidthWhenWidthIsMissing; double stylusHeight = DrawingAttributeSerializer.V1PenHeightWhenHeightIsMissing; uint rasterOperation = DrawingAttributeSerializer.RasterOperationDefaultV1; int transparency = DrawingAttributeSerializer.TransparencyDefaultV1; bool widthIsSetInISF = false; //did we find KnownIds.Width? bool heightIsSetInISF = false; //did we find KnownIds.Height? uint cbTotal = maximumStreamSize; while (maximumStreamSize > 0) { KnownTagCache.KnownTagIndex tag; uint uiTag; // First read the tag uint cb = SerializationHelper.Decode (stream, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (maximumStreamSize < cb) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("ISF size is larger than maximum stream size")); } maximumStreamSize -= cb; // Get the guid based on the tag Guid guid = guidList.FindGuid (tag); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Drawing Attribute tag embedded in ISF stream does not match guid table")); } uint dw = 0; if (KnownIds.PenTip == guid) { cb = SerializationHelper.Decode (stream, out dw); penTip = (PenTip)dw; if (!PenTipHelper.IsDefined(penTip)) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid PenTip value found in ISF stream")); } maximumStreamSize -= cb; } else if (KnownIds.PenStyle == guid) { cb = SerializationHelper.Decode(stream, out dw); penStyle = (PenStyle)dw; maximumStreamSize -= cb; } else if (KnownIds.DrawingFlags == guid) { // Encode the drawing flags with considerations for v2 model cb = SerializationHelper.Decode (stream, out dw); DrawingFlags flags = (DrawingFlags)dw; da.DrawingFlags = flags; maximumStreamSize -= cb; } else if (KnownIds.RasterOperation == guid) { uint ropSize = GuidList.GetDataSizeIfKnownGuid(KnownIds.RasterOperation); if (ropSize == 0) { throw new InvalidOperationException(StrokeCollectionSerializer. ISFDebugMessage("ROP data size was not found")); } byte[] data = new byte[ropSize]; stream.Read (data, 0, (int)ropSize); if (data != null && data.Length > 0) { //data[0] holds the allowable values of 0-255 rasterOperation = Convert.ToUInt32(data[0]); } maximumStreamSize -= ropSize; } else if (KnownIds.CurveFittingError == guid) { cb = SerializationHelper.Decode (stream, out dw); da.FittingError = (int)dw; maximumStreamSize -= cb; } else if (KnownIds.StylusHeight == guid || KnownIds.StylusWidth == guid) { double _size; cb = SerializationHelper.Decode (stream, out dw); _size = (double)dw; maximumStreamSize -= cb; if (maximumStreamSize > 0) { cb = SerializationHelper.Decode (stream, out dw); maximumStreamSize -= cb; if (KnownTagCache.KnownTagIndex.Mantissa == (KnownTagCache.KnownTagIndex)dw) { uint cbInSize; // First thing that is in there is maximumStreamSize of the data cb = SerializationHelper.Decode (stream, out cbInSize); maximumStreamSize -= cb; // in maximumStreamSize is one more than the decoded no cbInSize++; if (cbInSize > maximumStreamSize) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("ISF size if greater then maximum stream size")); } byte[] in_data = new byte[cbInSize]; uint bytesRead = (uint) stream.Read (in_data, 0, (int)cbInSize); if (cbInSize != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected")); } byte[] out_buffer = Compressor.DecompressPropertyData (in_data); using (MemoryStream localStream = new MemoryStream(out_buffer)) using (BinaryReader rdr = new BinaryReader(localStream)) { short sFraction = rdr.ReadInt16(); _size += (double)(sFraction / DrawingAttributes.StylusPrecision); maximumStreamSize -= cbInSize; } } else { // Seek it back by cb stream.Seek (-cb, SeekOrigin.Current); maximumStreamSize += cb; } } if (KnownIds.StylusWidth == guid) { widthIsSetInISF = true; stylusWidth = _size; } else { heightIsSetInISF = true; stylusHeight = _size; } } else if (KnownIds.Transparency == guid) { cb = SerializationHelper.Decode(stream, out dw); transparency = (int)dw; maximumStreamSize -= cb; } else if (KnownIds.Color == guid) { cb = SerializationHelper.Decode(stream, out dw); Color color = Color.FromRgb((byte)(dw & 0xff), (byte)((dw & 0xff00) >> Native.BitsPerByte), (byte)((dw & 0xff0000) >> (Native.BitsPerByte * 2))); da.Color = color; maximumStreamSize -= cb; } else if (KnownIds.StylusTipTransform == guid) { try { object data; cb = ExtendedPropertySerializer.DecodeAsISF(stream, maximumStreamSize, guidList, tag, ref guid, out data); Matrix matrix = Matrix.Parse((string)data); da.StylusTipTransform = matrix; } catch (InvalidOperationException) // Matrix.Parse failed. { System.Diagnostics.Debug.Assert(false, "Corrupt Matrix in the ExtendedPropertyCollection!"); } finally { maximumStreamSize -= cb; } } else { object data; cb = ExtendedPropertySerializer.DecodeAsISF(stream, maximumStreamSize, guidList, tag, ref guid, out data); maximumStreamSize -= cb; da.AddPropertyData(guid,data); } } if (0 != maximumStreamSize) { throw new ArgumentException (); } // // time to create our drawing attributes. // // 1) First we need to evaluate PenTip / StylusTip // Here is the V1 - V2 mapping // // PenTip.Circle == StylusTip.Ellipse // PenTip.Rectangle == StylusTip.Rectangle // PenTip.Rectangle == StylusTip.Diamond if (penTip == PenTip.Default) { //Since StylusTip is stored in the EPC at this point (if set), we can compare against it here. if (da.StylusTip != StylusTip.Ellipse) { // // StylusTip was set to something other than Ellipse // when we last serialized (or else StylusTip would be Ellipse, the default) // when StylusTip is != Ellipse and we serialize, we set PenTip to Rectangle // which is not the default. Therefore, if PenTip is back to Circle, // that means someone set it in V1 and we should respect that by // changing StylusTip back to Ellipse // da.StylusTip = StylusTip.Ellipse; } //else da.StylusTip is already set } else { System.Diagnostics.Debug.Assert(penTip == PenTip.Rectangle); if (da.StylusTip == StylusTip.Ellipse) { // // PenTip is Rectangle and StylusTip was either not set // before or was set to Ellipse and PenTip was changed // in a V1 ink object. Either way, we need to change StylusTip to Rectangle da.StylusTip = StylusTip.Rectangle; } //else da.StylusTip is already set } // // 2) next we need to set hight and width // if (da.StylusTip == StylusTip.Ellipse && widthIsSetInISF && !heightIsSetInISF) { // // special case: V1 PenTip of Circle only used Width to compute the circle size // and so it only serializes Width of 53 // but since our default is Ellipse, if Height is unset and we use the default // height of 30, then your ink that looked like 53,53 in V1 will look // like 30,53 here. // // stylusHeight = stylusWidth; da.HeightChangedForCompatabity = true; } // need to convert width/height into Avalon, since they are stored in HIMETRIC in ISF stylusHeight *= StrokeCollectionSerializer.HimetricToAvalonMultiplier; stylusWidth *= StrokeCollectionSerializer.HimetricToAvalonMultiplier; // Map 0.0 width to DrawingAttributes.DefaultXXXXXX (V1 53 equivalent) double height = DoubleUtil.IsZero(stylusHeight) ? (Double)DrawingAttributes.GetDefaultDrawingAttributeValue(KnownIds.StylusHeight) : stylusHeight; double width = DoubleUtil.IsZero(stylusWidth) ? (Double)DrawingAttributes.GetDefaultDrawingAttributeValue(KnownIds.StylusWidth) : stylusWidth; da.Height = GetCappedHeightOrWidth(height); da.Width = GetCappedHeightOrWidth(width); // // 3) next we need to set IsHighlighter (by looking for RasterOperation.MaskPen) // // // always store raster op // da.RasterOperation = rasterOperation; if (rasterOperation == DrawingAttributeSerializer.RasterOperationDefaultV1) { // // if rasterop is default, make sure IsHighlighter isn't in the EPC // if (da.ContainsPropertyData(KnownIds.IsHighlighter)) { da.RemovePropertyData(KnownIds.IsHighlighter); } } else { if (rasterOperation == DrawingAttributeSerializer.RasterOperationMaskPen) { da.IsHighlighter = true; } } //else, IsHighlighter will be set to false by default, no need to set it // // 4) see if there is a transparency we need to add to color // if (transparency > DrawingAttributeSerializer.TransparencyDefaultV1) { //note: Color.A is set to 255 by default, which means fully opaque //transparency is just the opposite - 0 means fully opaque so //we need to flip the values int alpha = MathHelper.AbsNoThrow(transparency - 255); Color color = da.Color; color.A = Convert.ToByte(alpha); da.Color = color; } return cbTotal; }
private static void PersistWidthHeight(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw) { //persist the height and width // For v1 loaders we persist height and width in StylusHeight and StylusWidth double stylusWidth = da.Width; double stylusHeight = da.Height; // Save the pen tip's width and height. for (int i = 0; i < 2; i++) { Guid guid = (i == 0) ? KnownIds.StylusWidth : KnownIds.StylusHeight; double size = (0 == i) ? stylusWidth : stylusHeight; // // the size is now in Avalon units, we need to convert to HIMETRIC // size *= StrokeCollectionSerializer.AvalonToHimetricMultiplier; double sizeWhenMissing = (0 == i) ? V1PenWidthWhenWidthIsMissing : V1PenHeightWhenHeightIsMissing; // // only persist height / width if they are equal to the height / width // when missing in the isf stream OR for compatibility with V1 // bool skipPersisting = DoubleUtil.AreClose(size, sizeWhenMissing); if ( stylusWidth == stylusHeight && da.StylusTip == StylusTip.Ellipse && guid == KnownIds.StylusHeight && da.HeightChangedForCompatabity) { //we need to put height in the ISF stream for compat skipPersisting = true; } if (!skipPersisting) { uint uIntegral = (uint)(size + 0.5f); Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(guid, true)); cbData += SerializationHelper.Encode(stream, uIntegral); short sFraction = (size > uIntegral) ? (short)(DrawingAttributes.StylusPrecision * (size - uIntegral) + 0.5f) : (short)(DrawingAttributes.StylusPrecision * (size - uIntegral) - 0.5); // If the fractional values is non zero, we store this value along with TAG_MANTISSA and size with a precisson of 1000 if (0 != sFraction) { uint cb = Native.SizeOfUShort; // For header NO_COMPRESS Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)MS.Internal.Ink.InkSerializedFormat.KnownTagCache.KnownTagIndex.Mantissa); cbData += SerializationHelper.Encode(stream, cb); bw.Write((byte)0x00); bw.Write((short)sFraction); cbData += cb + 1; // include size of encoded 0 and encoded fraction value } } } }
private static void PersistDrawingFlags(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw) { // // always serialize DrawingFlags, even when it is the default of AntiAliased. V1 loaders // expect it. // Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.DrawingFlags, true)); cbData += SerializationHelper.Encode(stream, (uint)(int)da.DrawingFlags); if (da.ContainsPropertyData(KnownIds.CurveFittingError)) { Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.CurveFittingError, true)); cbData += SerializationHelper.Encode(stream, (uint)(int)da.GetPropertyData(KnownIds.CurveFittingError)); } }
/// <summary> /// Loads a single ExtendedProperty from the stream and add that to the list. Tag may be passed as in /// the case of Stroke ExtendedPropertyCollection where tag is stored in the stroke descriptor or 0 when tag /// is embeded in the stream /// </summary> /// <param name="stream">Memory buffer to load from</param> /// <param name="cbSize">Maximum length of buffer to read</param> /// <param name="guidList">Guid cache to read from</param> /// <param name="tag">Guid tag to lookup</param> /// <param name="guid">Guid of property</param> /// <param name="data">Data of property</param> /// <returns>Length of buffer read</returns> #endif internal static uint DecodeAsISF(Stream stream, uint cbSize, GuidList guidList, KnownTagCache.KnownTagIndex tag, ref Guid guid, out object data) { uint cb, cbRead = 0; uint cbTotal = cbSize; if (0 == cbSize) { throw new InvalidOperationException(SR.Get(SRID.EmptyDataToLoad)); } if (0 == tag) // no tag is passed, it must be embedded in the data { uint uiTag; cb = SerializationHelper.Decode(stream, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (cb > cbTotal) throw new ArgumentException(SR.Get(SRID.InvalidSizeSpecified), "cbSize"); cbTotal -= cb; cbRead += cb; System.Diagnostics.Debug.Assert(guid == Guid.Empty); guid = guidList.FindGuid(tag); } if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Custom Attribute tag embedded in ISF stream does not match guid table"), "tag"); } // Try and find the size uint size = GuidList.GetDataSizeIfKnownGuid(guid); if (size > cbTotal) throw new ArgumentException(SR.Get(SRID.InvalidSizeSpecified), "cbSize"); // if the size is 0 if (0 == size) { // Size must be embedded in the stream. Find out the compressed data size cb = SerializationHelper.Decode(stream, out size); uint cbInsize = size + 1; cbRead += cb; cbTotal -= cb; if (cbInsize > cbTotal) throw new ArgumentException(); byte[] bytes = new byte[cbInsize]; uint bytesRead = (uint) stream.Read(bytes, 0, (int)cbInsize); if (cbInsize != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected"), "cbSize"); } cbRead += cbInsize; cbTotal -= cbInsize; //Find out the Decompressed buffer size using (MemoryStream decompressedStream = new MemoryStream(Compressor.DecompressPropertyData(bytes))) { // Add the property data = ExtendedPropertySerializer.DecodeAttribute(guid, decompressedStream); } } else { // For known size data, we just read the data directly from the stream byte[] bytes = new byte[size]; uint bytesRead = (uint) stream.Read(bytes, 0, (int)size); if (size != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected"), "cbSize"); } using (MemoryStream subStream = new MemoryStream(bytes)) { data = ExtendedPropertySerializer.DecodeAttribute(guid, subStream); } cbTotal -= size; cbRead += size; } return cbRead; }
private static void PersistRasterOperation(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw) { // write any non-default RasterOp value that we might have picked up from // V1 interop or by setting IsHighlighter. if (da.RasterOperation != DrawingAttributeSerializer.RasterOperationDefaultV1) { uint ropSize = GuidList.GetDataSizeIfKnownGuid(KnownIds.RasterOperation); if (ropSize == 0) { throw new InvalidOperationException(StrokeCollectionSerializer.ISFDebugMessage("ROP data size was not found")); } Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.RasterOperation, true)); long currentPosition = stream.Position; bw.Write(da.RasterOperation); if ((uint)(stream.Position - currentPosition) != ropSize) { throw new InvalidOperationException(StrokeCollectionSerializer.ISFDebugMessage("ROP data was incorrectly serialized")); } cbData += ropSize; } }
/// <summary> /// This functions loads a stroke from a memory stream based on the descriptor and GuidList. It returns /// the no of bytes it has read from the stream to correctly load the stream, which should be same as /// the value of the size parameter. If they are unequal throws ArgumentException. Stroke descriptor is /// used to load the packetproperty as well as ExtendedPropertyCollection on this stroke. Compressor is used /// to decompress the data. /// </summary> /// <param name="stream"></param> /// <param name="totalBytesInStrokeBlockOfIsfStream"></param> /// <param name="guidList"></param> /// <param name="strokeDescriptor"></param> /// <param name="stylusPointDescription"></param> /// <param name="transform"></param> /// <param name="stylusPoints"></param> /// <param name="extendedProperties"></param> #endif static uint DecodeISFIntoStroke( #if OLD_ISF Compressor compressor, #endif Stream stream, uint totalBytesInStrokeBlockOfIsfStream, GuidList guidList, StrokeDescriptor strokeDescriptor, StylusPointDescription stylusPointDescription, Matrix transform, out StylusPointCollection stylusPoints, out ExtendedPropertyCollection extendedProperties) { stylusPoints = null; extendedProperties = null; // We do allow a stroke with no packet data if (0 == totalBytesInStrokeBlockOfIsfStream) { return 0; } uint locallyDecodedBytes; uint remainingBytesInStrokeBlock = totalBytesInStrokeBlockOfIsfStream; // First try to load any packet data locallyDecodedBytes = LoadPackets( stream, remainingBytesInStrokeBlock, #if OLD_ISF compressor, #endif stylusPointDescription, transform, out stylusPoints); if (locallyDecodedBytes > remainingBytesInStrokeBlock) throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Packet buffer overflowed the ISF stream")); remainingBytesInStrokeBlock -= locallyDecodedBytes; if (0 == remainingBytesInStrokeBlock) { return locallyDecodedBytes; } // Now read the extended propertes for (int iTag = 1; iTag < strokeDescriptor.Template.Count && remainingBytesInStrokeBlock > 0; iTag++) { KnownTagCache.KnownTagIndex tag = strokeDescriptor.Template[iTag - 1]; switch (tag) { case MS.Internal.Ink.InkSerializedFormat.KnownTagCache.KnownTagIndex.StrokePropertyList: { // we've found the stroke extended properties. Load them now. while (iTag < strokeDescriptor.Template.Count && remainingBytesInStrokeBlock > 0) { tag = strokeDescriptor.Template[iTag]; object data; Guid guid = guidList.FindGuid(tag); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Stroke Custom Attribute tag embedded in ISF stream does not match guid table")); } // load the extended property data from the stream (and decode the type) locallyDecodedBytes = ExtendedPropertySerializer.DecodeAsISF(stream, remainingBytesInStrokeBlock, guidList, tag, ref guid, out data); // add the guid/data pair into the property collection (don't redecode the type) if (extendedProperties == null) { extendedProperties = new ExtendedPropertyCollection(); } extendedProperties[guid] = data; if (locallyDecodedBytes > remainingBytesInStrokeBlock) throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); remainingBytesInStrokeBlock -= locallyDecodedBytes; iTag++; } } break; case MS.Internal.Ink.InkSerializedFormat.KnownTagCache.KnownTagIndex.Buttons: { // Next tag is count of buttons and the tags for the button guids iTag += (int)((uint)strokeDescriptor.Template[iTag]) + 1; } break; // ignore any tags embedded in the Stroke block that this // version of the ISF decoder doesn't understand default: { System.Diagnostics.Trace.WriteLine("Ignoring unhandled stroke tag in ISF stroke descriptor"); } break; } } // Now try to load any tagged property data or point property data while (remainingBytesInStrokeBlock > 0) { // Read the tag first KnownTagCache.KnownTagIndex tag; uint uiTag; locallyDecodedBytes = SerializationHelper.Decode(stream, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (locallyDecodedBytes > remainingBytesInStrokeBlock) throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); remainingBytesInStrokeBlock -= locallyDecodedBytes; // if it is a point property block switch (tag) { case MS.Internal.Ink.InkSerializedFormat.KnownTagCache.KnownTagIndex.PointProperty: { // First load the totalBytesInStrokeBlockOfIsfStream of the point property block uint cbsize; locallyDecodedBytes = SerializationHelper.Decode(stream, out cbsize); if (locallyDecodedBytes > remainingBytesInStrokeBlock) throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); remainingBytesInStrokeBlock -= locallyDecodedBytes; while (remainingBytesInStrokeBlock > 0) { // First read the tag corresponding to the property locallyDecodedBytes = SerializationHelper.Decode(stream, out uiTag); tag = (KnownTagCache.KnownTagIndex)uiTag; if (locallyDecodedBytes > remainingBytesInStrokeBlock) throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); remainingBytesInStrokeBlock -= locallyDecodedBytes; // Now read the packet index for which the property will apply uint propindex; locallyDecodedBytes = SerializationHelper.Decode(stream, out propindex); if (locallyDecodedBytes > remainingBytesInStrokeBlock) throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); remainingBytesInStrokeBlock -= locallyDecodedBytes; uint propsize; locallyDecodedBytes = SerializationHelper.Decode(stream, out propsize); if (locallyDecodedBytes > remainingBytesInStrokeBlock) throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); remainingBytesInStrokeBlock -= locallyDecodedBytes; // Compressed data totalBytesInStrokeBlockOfIsfStream propsize += 1; // Make sure we have enough data to read if (propsize > remainingBytesInStrokeBlock) throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); byte[] in_buffer = new byte[propsize]; uint bytesRead = StrokeCollectionSerializer.ReliableRead(stream, in_buffer, propsize); if (propsize != bytesRead) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Read different size from stream then expected")); } byte[] out_buffer = Compressor.DecompressPropertyData(in_buffer); System.Diagnostics.Debug.Assert(false, "ExtendedProperties for points are not supported"); // skip the bytes in both success & failure cases // Note: Point ExtendedProperties are discarded remainingBytesInStrokeBlock -= propsize; } } break; default: { object data; Guid guid = guidList.FindGuid(tag); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Stroke Custom Attribute tag embedded in ISF stream does not match guid table")); } // load the extended property data from the stream (and decode the type) locallyDecodedBytes = ExtendedPropertySerializer.DecodeAsISF(stream, remainingBytesInStrokeBlock, guidList, tag, ref guid, out data); // add the guid/data pair into the property collection (don't redecode the type) if (extendedProperties == null) { extendedProperties = new ExtendedPropertyCollection(); } extendedProperties[guid] = data; if (locallyDecodedBytes > remainingBytesInStrokeBlock) { throw new InvalidOperationException(StrokeCollectionSerializer.ISFDebugMessage("ExtendedProperty decoded totalBytesInStrokeBlockOfIsfStream exceeded ISF stream totalBytesInStrokeBlockOfIsfStream")); } remainingBytesInStrokeBlock -= locallyDecodedBytes; } break; } } if (0 != remainingBytesInStrokeBlock) throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF data")); return totalBytesInStrokeBlockOfIsfStream; }
/// <Summary> /// Encodes the StylusTip in the ISF stream. /// </Summary> #endif private static void PersistStylusTip(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw) { // // persist the StylusTip // if (da.ContainsPropertyData(KnownIds.StylusTip)) { System.Diagnostics.Debug.Assert(da.StylusTip != StylusTip.Ellipse, "StylusTip was put in the EPC for the default value!"); // // persist PenTip.Rectangle for V1 ISF // Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.PenTip, true)); cbData += SerializationHelper.Encode(stream, (uint)PenTip.Rectangle); using (MemoryStream localStream = new MemoryStream(6)) //reasonable default { Int32 stylusTip = Convert.ToInt32(da.StylusTip, System.Globalization.CultureInfo.InvariantCulture); System.Runtime.InteropServices.VarEnum type = SerializationHelper.ConvertToVarEnum(PersistenceTypes.StylusTip, true); ExtendedPropertySerializer.EncodeAttribute(KnownIds.StylusTip, stylusTip, type, localStream); cbData += ExtendedPropertySerializer.EncodeAsISF(KnownIds.StylusTip, localStream.ToArray(), stream, guidList, 0, true); } } }
private static void PersistWidthHeight(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw) { //persist the height and width // For v1 loaders we persist height and width in StylusHeight and StylusWidth double stylusWidth = da.Width; double stylusHeight = da.Height; // Save the pen tip's width and height. for (int i = 0; i < 2; i++) { Guid guid = (i == 0) ? KnownIds.StylusWidth : KnownIds.StylusHeight; double size = (0 == i) ? stylusWidth : stylusHeight; // // the size is now in Avalon units, we need to convert to HIMETRIC // size *= StrokeCollectionSerializer.AvalonToHimetricMultiplier; double sizeWhenMissing = (0 == i) ? V1PenWidthWhenWidthIsMissing : V1PenHeightWhenHeightIsMissing; // // only persist height / width if they are equal to the height / width // when missing in the isf stream OR for compatibility with V1 // bool skipPersisting = DoubleUtil.AreClose(size, sizeWhenMissing); if (stylusWidth == stylusHeight && da.StylusTip == StylusTip.Ellipse && guid == KnownIds.StylusHeight && da.HeightChangedForCompatabity) { //we need to put height in the ISF stream for compat skipPersisting = true; } if (!skipPersisting) { uint uIntegral = (uint)(size + 0.5f); Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(guid, true)); cbData += SerializationHelper.Encode(stream, uIntegral); short sFraction = (size > uIntegral) ? (short)(DrawingAttributes.StylusPrecision * (size - uIntegral) + 0.5f) : (short)(DrawingAttributes.StylusPrecision * (size - uIntegral) - 0.5); // If the fractional values is non zero, we store this value along with TAG_MANTISSA and size with a precisson of 1000 if (0 != sFraction) { uint cb = Native.SizeOfUShort; // For header NO_COMPRESS Debug.Assert(bw != null); cbData += SerializationHelper.Encode(stream, (uint)MS.Internal.Ink.InkSerializedFormat.KnownTagCache.KnownTagIndex.Mantissa); cbData += SerializationHelper.Encode(stream, cb); bw.Write((byte)0x00); bw.Write((short)sFraction); cbData += cb + 1; // include size of encoded 0 and encoded fraction value } } } }
/// <summary> /// Encodes a custom attribute to the ISF stream /// </summary> #endif internal static uint EncodeAsISF(Guid id, byte[] data, Stream strm, GuidList guidList, byte compressionAlgorithm, bool fTag) { uint cbWrite = 0; uint cbSize = GuidList.GetDataSizeIfKnownGuid(id); Debug.Assert(strm != null); if (fTag) { uint uTag = (uint)guidList.FindTag(id, true); cbWrite += SerializationHelper.Encode(strm, uTag); } // If cbSize is 0, it is either a custom property or a known property with 0 // size. In either case, we need to write the size of the individual object if (0 == cbSize) { // Now we need to write the actual data for the property cbSize = (uint)data.Length; byte[] compresseddata = Compressor.CompressPropertyData(data, compressionAlgorithm); #if OLD_ISF byte nAlgo = compressionAlgorithm; uint cbOut = 0; Compressor.CompressPropertyData(data, ref nAlgo, ref cbOut, null); // Allocate a buffer big enough to hold the compressed data byte[] compresseddata2 = new byte[cbOut]; // NativeCompressor the data Compressor.CompressPropertyData(data, ref nAlgo, ref cbOut, compresseddata2); if (compresseddata.Length != compresseddata2.Length) { throw new InvalidOperationException("MAGIC EXCEPTION: Property bytes length when compressed didn't match with new compression"); } for (int i = 0; i < compresseddata.Length; i++) { if (compresseddata[i] != compresseddata2[i]) { throw new InvalidOperationException("MAGIC EXCEPTION: Property data didn't match with new property compression at index " + i.ToString()); } } #endif // write the encoded compressed size minus the algo byte cbWrite += SerializationHelper.Encode(strm, (uint)(compresseddata.Length - 1)); // Write the raw data strm.Write(compresseddata, 0, (int)compresseddata.Length); cbWrite += (uint)compresseddata.Length; } else { // // note that we used to write the nocompression byte, but that // was incorrect. We must not write it because loaders do not // expect it for known guids // // write the algo byte //strm.WriteByte(Compressor.NoCompression); //cbWrite++; // write the raw data without compression strm.Write(data, 0, (int)data.Length); cbWrite += (uint)data.Length; } return cbWrite; }
/// <summary> /// Builds StylusPointDescription based on StrokeDescriptor and Metric Descriptor Block. Sometime Metric Descriptor block may contain /// metric information for properties which are not part of the stroke descriptor. They are simply ignored. /// </summary> /// <param name="strd"></param> /// <param name="block"></param> /// <param name="guidList"></param> /// <returns></returns> private StylusPointDescription BuildStylusPointDescription(StrokeDescriptor strd, MetricBlock block, GuidList guidList) { int cTags = 0; int packetPropertyCount = 0; uint buttonCount = 0; Guid[] buttonguids = null; System.Collections.Generic.List<KnownTagCache.KnownTagIndex> tags = null; // if strd is null, it means there is only default descriptor with X & Y if (null != strd) { tags = new System.Collections.Generic.List<KnownTagCache.KnownTagIndex>(); while (cTags < strd.Template.Count) { KnownTagCache.KnownTagIndex tag = (KnownTagCache.KnownTagIndex)strd.Template[cTags]; if (KnownTagCache.KnownTagIndex.Buttons == tag) { cTags++; // The next item in the array is no of buttongs. buttonCount = (uint)strd.Template[cTags]; cTags++; // Currently we skip the the no of buttons as buttons is not implimented yet buttonguids = new Guid[buttonCount]; for (uint u = 0; u < buttonCount; u++) { Guid guid = guidList.FindGuid(strd.Template[cTags]); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Button guid tag embedded in ISF stream does not match guid table"),"strd"); } buttonguids[(int)u] = guid; cTags++; } } else if (KnownTagCache.KnownTagIndex.StrokePropertyList == tag) { break; // since no more Packet properties can be stored } else { if (KnownTagCache.KnownTagIndex.NoX == tag || KnownTagCache.KnownTagIndex.NoY == tag) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Invalid ISF with NoX or NoY specified"), "strd"); } tags.Add(strd.Template[cTags]); packetPropertyCount++; cTags++; } } } List<StylusPointPropertyInfo> stylusPointPropertyInfos = new List<StylusPointPropertyInfo>(); stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(KnownIds.X, (KnownTagCache.KnownTagIndex)((uint)KnownIdCache.KnownGuidBaseIndex + (uint)KnownIdCache.OriginalISFIdIndex.X), block)); stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(KnownIds.Y, (KnownTagCache.KnownTagIndex)((uint)KnownIdCache.KnownGuidBaseIndex + (uint)KnownIdCache.OriginalISFIdIndex.Y), block)); stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(KnownIds.NormalPressure, (KnownTagCache.KnownTagIndex)((uint)KnownIdCache.KnownGuidBaseIndex + (uint)KnownIdCache.OriginalISFIdIndex.NormalPressure), block)); int pressureIndex = -1; if (tags != null) { for (int i = 0; i < tags.Count; i++) { Guid guid = guidList.FindGuid(tags[i]); if (guid == Guid.Empty) { throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Packet Description Property tag embedded in ISF stream does not match guid table"), "strd"); } if (pressureIndex == -1 && guid == StylusPointPropertyIds.NormalPressure) { pressureIndex = i + 2; //x,y have already been accounted for continue; //we've already added pressure (above) } stylusPointPropertyInfos.Add(GetStylusPointPropertyInfo(guid, tags[i], block)); } if (null != buttonguids) { // // add the buttons to the end of the description if they exist // for (int i = 0; i < buttonguids.Length; i++) { StylusPointProperty buttonProperty = new StylusPointProperty(buttonguids[i], true); StylusPointPropertyInfo buttonInfo = new StylusPointPropertyInfo(buttonProperty); stylusPointPropertyInfos.Add(buttonInfo); } } } return new StylusPointDescription(stylusPointPropertyInfos, pressureIndex); }
/// <summary> /// Saves all elements in this list in the stream passed with the tags being generated based on the GuidList /// by the caller and using compressionAlgorithm as the preferred algorith identifier. For ExtendedPropertyCollection associated /// with Ink, drawing attributes and Point properties, we need to write the tag while saving them and hence /// fTag param is true. For strokes, the Tag is stored in the stroke descriptor and hence we don't store the /// tag /// </summary> /// <param name="attributes">Custom attributes to encode</param> /// <param name="stream">If stream is null, then size is calculated only.</param> /// <param name="guidList"></param> /// <param name="compressionAlgorithm"></param> /// <param name="fTag"></param> #endif internal static uint EncodeAsISF(ExtendedPropertyCollection attributes, Stream stream, GuidList guidList, byte compressionAlgorithm, bool fTag) { uint cbWrite = 0; for (int i = 0; i < attributes.Count; i++) { ExtendedProperty prop = attributes[i]; using (MemoryStream localStream = new MemoryStream(10)) //reasonable default { ExtendedPropertySerializer.EncodeToStream(prop, localStream); byte[] data = localStream.ToArray(); cbWrite += ExtendedPropertySerializer.EncodeAsISF(prop.Id, data, stream, guidList, compressionAlgorithm, fTag); } } return cbWrite; }
/// <Summary> /// Encodes all of the strokes in a strokecollection to ISF /// </Summary> #endif private void StoreStrokeData(Stream localStream, GuidList guidList, ref uint cumulativeEncodedSize, ref uint localEncodedSize) { // Now we will save the stroke data uint currentDrawingAttributesTableIndex = 0; uint currentStrokeDescriptorTableIndex = 0; uint uCurrMetricDescriptorTableIndex = 0; uint currentTransformTableIndex = 0; int[] strokeIds = StrokeIdGenerator.GetStrokeIds(_coreStrokes); for (int i = 0; i < _coreStrokes.Count; i++) { Stroke s = _coreStrokes[i]; uint cbStroke = 0; ISFDebugTrace("Encoding Stroke Id#" + strokeIds[i]); // if the drawing attribute index is different from the current one, write it if (currentDrawingAttributesTableIndex != _strokeLookupTable[s].DrawingAttributesTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.DrawingAttributesTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].DrawingAttributesTableIndex); currentDrawingAttributesTableIndex = _strokeLookupTable[s].DrawingAttributesTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded DrawingAttribute Table Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // if the stroke descriptor index is different from the current one, write it if (currentStrokeDescriptorTableIndex != _strokeLookupTable[s].StrokeDescriptorTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.StrokeDescriptorTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].StrokeDescriptorTableIndex); currentStrokeDescriptorTableIndex = _strokeLookupTable[s].StrokeDescriptorTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded Stroke Descriptor Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // if the metric table index is different from the current one, write it if (uCurrMetricDescriptorTableIndex != _strokeLookupTable[s].MetricDescriptorTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.MetricTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].MetricDescriptorTableIndex); uCurrMetricDescriptorTableIndex = _strokeLookupTable[s].MetricDescriptorTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded Metric Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // if the Transform index is different from the current one, write it if (currentTransformTableIndex != _strokeLookupTable[s].TransformTableIndex) { localEncodedSize = cumulativeEncodedSize; cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.TransformTableIndex); cumulativeEncodedSize += SerializationHelper.Encode(localStream, _strokeLookupTable[s].TransformTableIndex); currentTransformTableIndex = _strokeLookupTable[s].TransformTableIndex; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace(" Encoded Transform Index: size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } // now create a separate Memory Stream object which will be used for storing the saved stroke data temporarily using (MemoryStream tempstrm = new MemoryStream(s.StylusPoints.Count * 5)) //good approximation based on profiling isf files { localEncodedSize = cumulativeEncodedSize; #if OLD_ISF // Now save the stroke in the temp stream cbStroke = StrokeSerializer.EncodeStroke(s, tempstrm, null/*we never use CompressionMode.Max)*/, GetCompressionAlgorithm(), guidList, _strokeLookupTable[s]); #else cbStroke = StrokeSerializer.EncodeStroke(s, tempstrm, GetCompressionAlgorithm(), guidList, _strokeLookupTable[s]); #endif if (cbStroke != tempstrm.Length) { throw new InvalidOperationException(ISFDebugMessage("Encoded stroke size != reported size")); } // Now write the tag KnownTagCache.KnownTagIndex.Stroke cumulativeEncodedSize += SerializationHelper.Encode(localStream, (uint)KnownTagCache.KnownTagIndex.Stroke); ISFDebugTrace("Stroke size=" + tempstrm.Length); // Now write the size of the stroke cumulativeEncodedSize += SerializationHelper.Encode(localStream, cbStroke); // Finally write the stroke data localStream.Write(tempstrm.GetBuffer(), 0, (int)cbStroke); cumulativeEncodedSize += cbStroke; localEncodedSize = cumulativeEncodedSize - localEncodedSize; if (localEncodedSize != 0) ISFDebugTrace("Encoding Stroke Id#" + strokeIds[i] + " size=" + localEncodedSize); if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } if (cumulativeEncodedSize != localStream.Length) throw new InvalidOperationException(ISFDebugMessage("Calculated ISF stream size != actual stream size")); } }
/// <summary> /// Builds the GuidList based on ExtendedPropeties and StrokeCollection /// </summary> /// <returns></returns> private GuidList BuildGuidList() { GuidList guidList = new GuidList(); int i = 0; // First go through the list of ink properties ExtendedPropertyCollection attributes = _coreStrokes.ExtendedProperties; for (i = 0; i < attributes.Count; i++) { guidList.Add(attributes[i].Id); } // Next go through all the strokes for (int j = 0; j < _coreStrokes.Count; j++) { BuildStrokeGuidList(_coreStrokes[j], guidList); } return guidList; }
/// <summary> /// Builds the list of Custom Guids that were used by this particular stroke, either in the packet layout /// or in the drawing attributes, or in the buttons or in Extended properties or in the point properties /// and updates the guidlist with that information /// </summary> /// <param name="stroke"></param> /// <param name="guidList"></param> private void BuildStrokeGuidList(Stroke stroke, GuidList guidList) { int i = 0; // First drawing attributes // Ignore the default Guids/attributes in the DrawingAttributes int count; Guid[] guids = ExtendedPropertySerializer.GetUnknownGuids(stroke.DrawingAttributes.ExtendedProperties, out count); for (i = 0; i < count; i++) { guidList.Add(guids[i]); } Guid[] descriptionGuids = stroke.StylusPoints.Description.GetStylusPointPropertyIds(); for (i = 0; i < descriptionGuids.Length; i++) { guidList.Add(descriptionGuids[i]); } if (stroke.ExtendedProperties.Count > 0) { // Add the ExtendedProperty guids in the list for (i = 0; i < stroke.ExtendedProperties.Count; i++) { guidList.Add(stroke.ExtendedProperties[i].Id); } } }
/// <summary> /// Builds the Stroke Descriptor for this stroke based on Packet Layout and Extended Properties /// For details on how this is strored please refer to the spec. /// </summary> internal static void BuildStrokeDescriptor( Stroke stroke, GuidList guidList, StrokeCollectionSerializer.StrokeLookupEntry strokeLookupEntry, out StrokeDescriptor strokeDescriptor, out MetricBlock metricBlock) { // Initialize the metric block for this stroke metricBlock = new MetricBlock(); // Clear any existing template strokeDescriptor = new StrokeDescriptor(); // Uninitialized variable passed in AddMetricEntry MetricEntryType metricEntryType; StylusPointDescription stylusPointDescription = stroke.StylusPoints.Description; KnownTagCache.KnownTagIndex tag = guidList.FindTag(KnownIds.X, true); metricEntryType = metricBlock.AddMetricEntry(stylusPointDescription.GetPropertyInfo(StylusPointProperties.X), tag); tag = guidList.FindTag(KnownIds.Y, true); metricEntryType = metricBlock.AddMetricEntry(stylusPointDescription.GetPropertyInfo(StylusPointProperties.Y), tag); ReadOnlyCollection <StylusPointPropertyInfo> propertyInfos = stylusPointDescription.GetStylusPointProperties(); int i = 0; //i is defined out of the for loop so we can use it later for buttons for (i = 2 /*past x,y*/; i < propertyInfos.Count; i++) { if (i == /*StylusPointDescription.RequiredPressureIndex //修复构建的代码 */ 2 && !strokeLookupEntry.StorePressure) { // // don't store pressure information // continue; } StylusPointPropertyInfo propertyInfo = propertyInfos[i]; if (propertyInfo.IsButton) { //we don't serialize buttons break; } tag = guidList.FindTag(propertyInfo.Id, true); strokeDescriptor.Template.Add(tag); strokeDescriptor.Size += SerializationHelper.VarSize((uint)tag); // Create the MetricEntry for this property if necessary metricEntryType = metricBlock.AddMetricEntry(propertyInfo, tag); } /* * we drop button data on the floor. * int buttonCount = stylusPointDescription.ButtonCount; * // Now write the button tags in the Template * if (buttonCount > 0) * { * // First write the TAG_BUTTONS * strokeDescriptor.Template.Add(KnownTagCache.KnownTagIndex.Buttons); * strokeDescriptor.Size += SerializationHelper.VarSize((uint)KnownTagCache.KnownTagIndex.Buttons); * * // Next write the i of buttons * strokeDescriptor.Template.Add((KnownTagCache.KnownTagIndex)buttonCount); * strokeDescriptor.Size += SerializationHelper.VarSize((uint)buttonCount); * * //we broke above on i when it was a button, it still * //points to the first button * for (; i < propertyInfos.Count; i++) * { * StylusPointPropertyInfo propertyInfo = propertyInfos[i]; * tag = guidList.FindTag(propertyInfo.Id, false); * * strokeDescriptor.Template.Add(tag); * strokeDescriptor.Size += SerializationHelper.VarSize((uint)tag); * } * } */ // Now write the extended properties in the template if (stroke.ExtendedProperties.Count > 0) { strokeDescriptor.Template.Add(KnownTagCache.KnownTagIndex.StrokePropertyList); strokeDescriptor.Size += SerializationHelper.VarSize((uint)KnownTagCache.KnownTagIndex.StrokePropertyList); // Now write the tags corresponding to each extended properties of the stroke for (int x = 0; x < stroke.ExtendedProperties.Count; x++) { tag = guidList.FindTag(stroke.ExtendedProperties[(int)x].Id, false); strokeDescriptor.Template.Add(tag); strokeDescriptor.Size += SerializationHelper.VarSize((uint)tag); } } }
/// <summary> /// Encodes a custom attribute to the ISF stream /// </summary> #endif internal static uint EncodeAsISF(Guid id, byte[] data, Stream strm, GuidList guidList, byte compressionAlgorithm, bool fTag) { uint cbWrite = 0; uint cbSize = GuidList.GetDataSizeIfKnownGuid(id); Debug.Assert(strm != null); if (fTag) { uint uTag = (uint)guidList.FindTag(id, true); cbWrite += SerializationHelper.Encode(strm, uTag); } // If cbSize is 0, it is either a custom property or a known property with 0 // size. In either case, we need to write the size of the individual object if (0 == cbSize) { // Now we need to write the actual data for the property cbSize = (uint)data.Length; byte[] compresseddata = Compressor.CompressPropertyData(data, compressionAlgorithm); #if OLD_ISF byte nAlgo = compressionAlgorithm; uint cbOut = 0; Compressor.CompressPropertyData(data, ref nAlgo, ref cbOut, null); // Allocate a buffer big enough to hold the compressed data byte[] compresseddata2 = new byte[cbOut]; // NativeCompressor the data Compressor.CompressPropertyData(data, ref nAlgo, ref cbOut, compresseddata2); if (compresseddata.Length != compresseddata2.Length) { throw new InvalidOperationException("MAGIC EXCEPTION: Property bytes length when compressed didn't match with new compression"); } for (int i = 0; i < compresseddata.Length; i++) { if (compresseddata[i] != compresseddata2[i]) { throw new InvalidOperationException("MAGIC EXCEPTION: Property data didn't match with new property compression at index " + i.ToString()); } } #endif // write the encoded compressed size minus the algo byte cbWrite += SerializationHelper.Encode(strm, (uint)(compresseddata.Length - 1)); // Write the raw data strm.Write(compresseddata, 0, (int)compresseddata.Length); cbWrite += (uint)compresseddata.Length; } else { // // note that we used to write the nocompression byte, but that // was incorrect. We must not write it because loaders do not // expect it for known guids // // write the algo byte //strm.WriteByte(Compressor.NoCompression); //cbWrite++; // write the raw data without compression strm.Write(data, 0, (int)data.Length); cbWrite += (uint)data.Length; } return(cbWrite); }