protected void GetStylusParams(StylusPointDescription sd) { if (sd.HasProperty(StylusPointProperties.XTiltOrientation) && sd.HasProperty(StylusPointProperties.YTiltOrientation)) { StylusPointPropertyInfo tiltXInfo = sd.GetPropertyInfo(StylusPointProperties.XTiltOrientation); StylusPointPropertyInfo tiltYInfo = sd.GetPropertyInfo(StylusPointProperties.YTiltOrientation); mInputMinTiltX = tiltXInfo.Minimum; mInputMaxTiltX = tiltXInfo.Maximum; mInputMinTiltY = tiltYInfo.Minimum; mInputMaxTiltY = tiltYInfo.Maximum; mScaleTiltX = (mTargetMaxTiltX - mTargetMinTiltX) / (mInputMaxTiltX - mInputMinTiltX); mScaleTiltY = (mTargetMaxTiltY - mTargetMinTiltY) / (mInputMaxTiltY - mInputMinTiltY); } else { mScaleTiltX = 0; mScaleTiltY = 0; } }
/// <summary> /// Get the width or height of the stylus point's bounding box. /// </summary> /// <param name="stylusPoint">The point for which the width or height is being calculated</param> /// <param name="isWidth">True if this should calculate width, false for height</param> /// <returns>The width or height of the stylus poing</returns> /// <remarks> /// Note that this is not DPI aware. This implementation has never been aware of DPI changes and /// changing that now could cause issues with people who depended on this to be based on 96 DPI. /// </remarks> protected override double GetStylusPointWidthOrHeight(StylusPoint stylusPoint, bool isWidth) { double pixelsPerInch = DpiUtil.DefaultPixelsPerInch; StylusPointProperty property = (isWidth ? StylusPointProperties.Width : StylusPointProperties.Height); double value = 0d; if (stylusPoint.HasProperty(property)) { // Get the property value in the corresponding units value = (double)stylusPoint.GetPropertyValue(property); StylusPointPropertyInfo propertyInfo = stylusPoint.Description.GetPropertyInfo(property); if (!DoubleUtil.AreClose(propertyInfo.Resolution, 0d)) { value /= propertyInfo.Resolution; } else { value = 0; } // Convert the value to Inches if (propertyInfo.Unit == StylusPointPropertyUnit.Centimeters) { value /= CentimetersPerInch; } // Convert the value to pixels value *= pixelsPerInch; } return(value); }
/// <summary> /// Saves the packets into a stream of bytes /// </summary> /// <param name="stroke">Stroke to save</param> /// <param name="stream">null to calculate size only</param> /// <param name="compressor"></param> /// <param name="strokeLookupEntry"></param> /// <returns></returns> #else /// <summary> /// Saves the packets into a stream of bytes /// </summary> /// <param name="stroke">Stroke to save</param> /// <param name="stream">null to calculate size only</param> /// <param name="strokeLookupEntry"></param> #endif static uint SavePackets( Stroke stroke, Stream stream, #if OLD_ISF Compressor compressor, #endif StrokeCollectionSerializer.StrokeLookupEntry strokeLookupEntry) { // First write or calculate how many points are there uint pointCount = (uint)stroke.StylusPoints.Count; uint localBytesWritten = (stream != null) ? SerializationHelper.Encode(stream, pointCount) : SerializationHelper.VarSize(pointCount); byte compressionAlgorithm; int[][] outputArrays = strokeLookupEntry.ISFReadyStrokeData; //We don't serialize button data //int valuesPerPoint = stroke.StylusPoints.Description.GetOutputArrayLengthPerPoint(); //int buttonCount = stroke.StylusPoints.Description.ButtonCount; ReadOnlyCollection <StylusPointPropertyInfo> propertyInfos = stroke.StylusPoints.Description.GetStylusPointProperties(); int i = 0; for (; i < propertyInfos.Count; i++) { StylusPointPropertyInfo propertyInfo = propertyInfos[i]; if (i == 2 && !strokeLookupEntry.StorePressure) { // // only store pressure if we need to // continue; } if (propertyInfo.IsButton) { // // we're at the buttons, handle this below // break; } compressionAlgorithm = strokeLookupEntry.CompressionData; localBytesWritten += SavePacketPropertyData(outputArrays[i], stream, #if OLD_ISF compressor, #endif propertyInfo.Id, ref compressionAlgorithm); } /* * We don't serialize button data * // Now write all button data. Button data is stored as if it is another packet property * // with size (cbuttoncount + 7)/8 bytes and corresponding guids are stored in the packet * // description. Button data is only stored if buttons are present in the description and there * // are packets in the stroke * if (buttonCount > 0 && pointCount > 0) * { * Debug.Assert(i == valuesPerPoint - 1); * BitStreamWriter bitWriter = new BitStreamWriter(); * // * // Get the array of button data (i is still pointing at it) * // * int[] buttonData = outputArrays[i]; * * for (int x = 0; x < pointCount; x++) * { * // * // each int in the button data array contains buttonCount number * // of bits that need to be written to the BitStreamWriter * // the BitStreamWriter takes bytes at a time. We always write the most * // signifigant bits first * // * int uncompactedButtonDataForPoint = buttonData[x]; * * // calculate the number of full bytes used for buttons per packet * // Example: 10 buttons would require 1 full byte * // but 8 would require * int fullBytesForButtonsPerPacket = buttonCount / Native.BitsPerByte; * * // calculate the number of bits that spill beyond the full byte boundary * // Example: 10 buttons would require 2 extra bits (8 fit in a full byte) * int bitsToWrite = buttonCount % Native.BitsPerByte; * * for (; fullBytesForButtonsPerPacket >= 0; fullBytesForButtonsPerPacket--) * { * byte byteOfButtonData = * Convert.ToByte(uncompactedButtonDataForPoint >> (fullBytesForButtonsPerPacket * Native.BitsPerByte)); * // * // write 8 or less bytes to the bitwriter * // checking for 0 handles the case where we're writing 8, 16 or 24 bytes * // and bitsToWrite is initialize to zero * // * if (bitsToWrite > 0) * { * bitWriter.Write(byteOfButtonData, bitsToWrite); * } * if (fullBytesForButtonsPerPacket > 0) * { * bitsToWrite = Native.BitsPerByte; * } * } * } * * // retrieve the button bytes * byte[] packedButtonData = bitWriter.ToBytes(); * * if (packedButtonData.Length != * ((buttonCount * pointCount + 7) / Native.BitsPerByte)) * { * throw new ArgumentException(StrokeCollectionSerializer.ISFDebugMessage("Packed button length not equal to expected length")); * } * * // write out the packed button data to the output stream * stream.Write(packedButtonData, 0, packedButtonData.Length); * localBytesWritten += (uint)packedButtonData.Length; * } */ return(localBytesWritten); }
/// <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> /// Retrieve the packet description, packets data and XFORM which is the information the native recognizer needs. /// The method is called from AddStrokes. /// </summary> private void GetPacketData ( Stroke stroke, out MS.Win32.Recognizer.PACKET_DESCRIPTION packetDescription, out int countOfBytes, out IntPtr packets, out NativeMethods.XFORM xForm ) { int i; countOfBytes = 0; packets = IntPtr.Zero; packetDescription = new MS.Win32.Recognizer.PACKET_DESCRIPTION(); Matrix matrix = Matrix.Identity; xForm = new NativeMethods.XFORM((float)(matrix.M11), (float)(matrix.M12), (float)(matrix.M21), (float)(matrix.M22), (float)(matrix.OffsetX), (float)(matrix.OffsetY)); StylusPointCollection stylusPoints = stroke.StylusPoints; if (stylusPoints.Count == 0) { return; //we'll fail when the calling routine sees that packets is IntPtr.Zer } if (stylusPoints.Description.PropertyCount > StylusPointDescription.RequiredCountOfProperties) { // // reformat to X, Y, P // StylusPointDescription reformatDescription = new StylusPointDescription( new StylusPointPropertyInfo[] { new StylusPointPropertyInfo(StylusPointProperties.X), new StylusPointPropertyInfo(StylusPointProperties.Y), stylusPoints.Description.GetPropertyInfo(StylusPointProperties.NormalPressure) }); stylusPoints = stylusPoints.Reformat(reformatDescription); } // // now make sure we only take a finite amount of data for the stroke // if (stylusPoints.Count > MaxStylusPoints) { stylusPoints = stylusPoints.Clone(MaxStylusPoints); } Guid[] propertyGuids = new Guid[] { StylusPointPropertyIds.X, //required index for SPD StylusPointPropertyIds.Y, //required index for SPD StylusPointPropertyIds.NormalPressure }; //required index for SPD Debug.Assert(stylusPoints != null); Debug.Assert(propertyGuids.Length == StylusPointDescription.RequiredCountOfProperties); // Get the packet description packetDescription.cbPacketSize = (uint)(propertyGuids.Length * Marshal.SizeOf(typeof(Int32))); packetDescription.cPacketProperties = (uint)propertyGuids.Length; // // use X, Y defaults for metrics, sometimes mouse metrics can be bogus // always use NormalPressure metrics, though. // StylusPointPropertyInfo[] infosToUse = new StylusPointPropertyInfo[StylusPointDescription.RequiredCountOfProperties]; infosToUse[StylusPointDescription.RequiredXIndex] = StylusPointPropertyInfoDefaults.X; infosToUse[StylusPointDescription.RequiredYIndex] = StylusPointPropertyInfoDefaults.Y; infosToUse[StylusPointDescription.RequiredPressureIndex] = stylusPoints.Description.GetPropertyInfo(StylusPointProperties.NormalPressure); MS.Win32.Recognizer.PACKET_PROPERTY[] packetProperties = new MS.Win32.Recognizer.PACKET_PROPERTY[packetDescription.cPacketProperties]; StylusPointPropertyInfo propertyInfo; for (i = 0; i < packetDescription.cPacketProperties; i++) { packetProperties[i].guid = propertyGuids[i]; propertyInfo = infosToUse[i]; MS.Win32.Recognizer.PROPERTY_METRICS propertyMetrics = new MS.Win32.Recognizer.PROPERTY_METRICS( ); propertyMetrics.nLogicalMin = propertyInfo.Minimum; propertyMetrics.nLogicalMax = propertyInfo.Maximum; propertyMetrics.Units = (int)(propertyInfo.Unit); propertyMetrics.fResolution = propertyInfo.Resolution; packetProperties[i].PropertyMetrics = propertyMetrics; } unsafe { int allocationSize = (int)(Marshal.SizeOf(typeof(MS.Win32.Recognizer.PACKET_PROPERTY)) * packetDescription.cPacketProperties); packetDescription.pPacketProperties = Marshal.AllocCoTaskMem(allocationSize); MS.Win32.Recognizer.PACKET_PROPERTY *pPacketProperty = (MS.Win32.Recognizer.PACKET_PROPERTY *)(packetDescription.pPacketProperties.ToPointer()); MS.Win32.Recognizer.PACKET_PROPERTY *pElement = pPacketProperty; for (i = 0; i < packetDescription.cPacketProperties; i++) { Marshal.StructureToPtr(packetProperties[i], new IntPtr(pElement), false); pElement++; } } // Get packet data int[] rawPackets = stylusPoints.ToHiMetricArray(); int packetCount = rawPackets.Length; if (packetCount != 0) { countOfBytes = packetCount * Marshal.SizeOf(typeof(Int32)); packets = Marshal.AllocCoTaskMem(countOfBytes); Marshal.Copy(rawPackets, 0, packets, packetCount); } }
/// <summary> /// Query all supported properties from WM_POINTER stack and convert to WPF equivalents. /// /// This maintains a set of properties from WM_POINTER that are supported and the equivalent /// properties in WPF. This way, the WM_POINTER properties can be used to directly query /// raw data from the stack and associate it 1 to 1 with raw stylus data in WPF. /// </summary> /// <returns> /// True if initialization succeeds, false otherwise. /// </returns> /// <remarks> /// A note on the pressure property. If the device does not support pressure as a property, /// we still insert pressure on the WPF side. This is a requirement in many places in the /// stylus code. However, the raw data queried from WM_POINTER will NOT have pressure data. /// This is ok because StylusPointCollection pivots on the ContainsTruePressure property in /// the StylusPointDescription. If this is true, every point will have X, Y, Pressure and then /// additional raw data from index 3..N where N is the number of properties per StylusPoint. /// If this is false, it will add a default pressure so WPF ends up with X, Y, DefaultPressure /// and then 2..N per StylusPoint. This can be slightly confusing at first glance, since pressure /// of one kind or another is always required in the StylusPointDescription, but it allows for /// backfilling of pressure data further up (as in towards the public boundary) and the deeper /// portions of the stack just worry about querying device capabilities. /// </remarks> private bool TryInitializeSupportedStylusPointProperties() { bool success = false; uint propCount = 0; // Initialize to having not seen a real pressure property PressureIndex = -1; UsingFakePressure = true; // Retrieve all properties from the WM_POINTER stack success = UnsafeNativeMethods.GetPointerDeviceProperties(Device, ref propCount, null); if (success) { SupportedPointerProperties = new UnsafeNativeMethods.POINTER_DEVICE_PROPERTY[propCount]; success = UnsafeNativeMethods.GetPointerDeviceProperties(Device, ref propCount, SupportedPointerProperties); if (success) { // Prepare a location for X, Y, and Pressure List <StylusPointProperty> properties = new List <StylusPointProperty>() { StylusPointPropertyInfoDefaults.X, StylusPointPropertyInfoDefaults.Y, StylusPointPropertyInfoDefaults.NormalPressure, }; List <StylusPointProperty> buttonProperties = new List <StylusPointProperty>(); // Prepare a location for X and Y. Pressure does not need a location as it will be added later if applicable. List <UnsafeNativeMethods.POINTER_DEVICE_PROPERTY> supportedProperties = new List <UnsafeNativeMethods.POINTER_DEVICE_PROPERTY>() { new UnsafeNativeMethods.POINTER_DEVICE_PROPERTY(), new UnsafeNativeMethods.POINTER_DEVICE_PROPERTY(), }; List <UnsafeNativeMethods.POINTER_DEVICE_PROPERTY> supportedButtonProperties = new List <UnsafeNativeMethods.POINTER_DEVICE_PROPERTY>(); bool seenX = false, seenY = false, seenPressure = false; foreach (var prop in SupportedPointerProperties) { StylusPointPropertyInfo propInfo = PointerStylusPointPropertyInfoHelper.CreatePropertyInfo(prop); if (propInfo != null) { // If seeing a required property, just overwrite the default placeholder // otherwise tack it onto the end of the appropriate list. if (propInfo.Id == StylusPointPropertyIds.NormalPressure) { seenPressure = true; properties[StylusPointDescription.RequiredPressureIndex] = propInfo; // Pressure is not in the pointer properties by default so we must insert it. supportedProperties.Insert(StylusPointDescription.RequiredPressureIndex, prop); } else if (propInfo.Id == StylusPointPropertyIds.X) { seenX = true; properties[StylusPointDescription.RequiredXIndex] = propInfo; supportedProperties[StylusPointDescription.RequiredXIndex] = prop; } else if (propInfo.Id == StylusPointPropertyIds.Y) { seenY = true; properties[StylusPointDescription.RequiredYIndex] = propInfo; supportedProperties[StylusPointDescription.RequiredYIndex] = prop; } else if (propInfo.IsButton) { buttonProperties.Add(propInfo); supportedButtonProperties.Add(prop); } else { properties.Add(propInfo); supportedProperties.Add(prop); } } } // If we saw a real pressure property, we should mark that down if (seenPressure) { PressureIndex = StylusPointDescription.RequiredPressureIndex; UsingFakePressure = false; HardwareCapabilities |= TabletHardwareCapabilities.SupportsPressure; } Debug.Assert(properties[StylusPointDescription.RequiredXIndex /*0*/].Id == StylusPointPropertyIds.X || !seenX, "X isn't where we expect it! Fix pointer stack to ask for X at index 0"); Debug.Assert(properties[StylusPointDescription.RequiredYIndex /*1*/].Id == StylusPointPropertyIds.Y || !seenY, "Y isn't where we expect it! Fix pointer stack to ask for Y at index 1"); Debug.Assert(properties[StylusPointDescription.RequiredPressureIndex /*1*/].Id == StylusPointPropertyIds.NormalPressure /*2*/, "Fix pointer stack to ask for NormalPressure at index 2!"); // Append buttons to the end of normal properties properties.AddRange(buttonProperties); SupportedButtonPropertyIndex = supportedProperties.Count; supportedProperties.AddRange(supportedButtonProperties); // Reset the properties to only what we support, this way we can generate raw data directly from them StylusPointProperties = new ReadOnlyCollection <StylusPointProperty>(properties); SupportedPointerProperties = supportedProperties.ToArray(); } } return(success); }
/// <summary> /// This function checks if this packet property results in a valid metric entry. This will be a valid entry if /// 1. it is a custom property, 2. Does not belong to the global list of gaMetricEntry_Never, 3. Belongs to the /// global list of gaMetricEntry_Must and 4. Belongs to global list of gaMetricEntry_Optional and at least one of /// its metric values is different from the corresponding default. /// </summary> /// <param name="propertyInfo"></param> /// <param name="tag"></param> /// <param name="metricEntryType"></param> /// <param name="index"></param> /// <returns></returns> static bool IsValidMetricEntry(StylusPointPropertyInfo propertyInfo, KnownTagCache.KnownTagIndex tag, out MetricEntryType metricEntryType, out uint index) { index = 0; // If this is a custom property, check if all the Metric values are null or not. If they are then this is not a // valid metric entry if (tag >= (KnownTagCache.KnownTagIndex)KnownIdCache.CustomGuidBaseIndex) { metricEntryType = MetricEntryType.Custom; if (Int32.MinValue == propertyInfo.Minimum && Int32.MaxValue == propertyInfo.Maximum && StylusPointPropertyUnit.None == propertyInfo.Unit && DoubleUtil.AreClose(1.0, propertyInfo.Resolution)) { return(false); } else { return(true); } } else { int ul; // First find the property in the gaMetricEntry_Never. If it belongs to this list, // we will never write the metric table for this prop. So return FALSE; for (ul = 0; ul < MetricEntry_Never.Length; ul++) { if (MetricEntry_Never[ul] == tag) { metricEntryType = MetricEntryType.Never; return(false); } } // Then search the property in the gaMetricEntry_Must list. If it belongs to this list, // we must always write the metric table for this prop. So return TRUE; for (ul = 0; ul < MetricEntry_Must.Length; ul++) { if (MetricEntry_Must[ul] == tag) { metricEntryType = MetricEntryType.Must; if (propertyInfo.Minimum == DefaultPropertyMetrics.Minimum && propertyInfo.Maximum == DefaultPropertyMetrics.Maximum && propertyInfo.Unit == DefaultPropertyMetrics.Unit && DoubleUtil.AreClose(propertyInfo.Resolution, DefaultPropertyMetrics.Resolution)) { return(false); } else { return(true); } } } // Now seach it in the gaMetricEntry_Optional list. If it is there, check the metric values // agianst the default values and if there is any non default value, return TRUE; for (ul = 0; ul < MetricEntry_Optional.Length; ul++) { if (((MetricEntryList)MetricEntry_Optional[ul]).Tag == tag) { metricEntryType = MetricEntryType.Optional; if (propertyInfo.Minimum == MetricEntry_Optional[ul].PropertyMetrics.Minimum && propertyInfo.Maximum == MetricEntry_Optional[ul].PropertyMetrics.Maximum && propertyInfo.Unit == MetricEntry_Optional[ul].PropertyMetrics.Unit && DoubleUtil.AreClose(propertyInfo.Resolution, MetricEntry_Optional[ul].PropertyMetrics.Resolution)) { return(false); } else { index = (uint)ul; return(true); } } } // it is not found in any of the list. Force to write all metric entries for the property. metricEntryType = MetricEntryType.Must; return(true); } }
public MetricEntryList(KnownTagCache.KnownTagIndex tag, StylusPointPropertyInfo prop) { Tag = tag; PropertyMetrics = prop; }