/// <summary> /// Overlay one stroke on the image. /// </summary> /// <param name="img"></param> /// <param name="s"></param> /// <param name="size"></param> /// PRI2: I believe we can use the DibGraphicsBuffer (see opaque ink handling above) to improve the /// look of the transparency. This currently has the following shortcoming: The colors produced by the exported gif /// from CP's highlight pens have some whiteness which causes a little fog over the dark colors /// below, for example the yellow highlight over a black background results in a lighter dark color, /// no longer quite black. In contrast the native ink mixing gives us nearly the original black. It is as if /// the colors are on colored but clear mylar that overlay one another. It's probably possible to get this effect /// with DrawImage somehow?? private void addTransparentStroke(Image img, Microsoft.Ink.Strokes s, double size) { Microsoft.Ink.Ink tmpInk = new Microsoft.Ink.Ink(); tmpInk.AddStrokesAtRectangle(s, s.GetBoundingBox()); //Make a GIF Image from the Stroke. Note that this image is assumed to be in a 500x500 pixel space. byte[] ba = tmpInk.Save(Microsoft.Ink.PersistenceFormat.Gif); Image inkImg = Image.FromStream(new MemoryStream(ba)); Graphics g = Graphics.FromImage(img); //Get the origin from the ink Bounding Box (in ink space) Rectangle inkBB = tmpInk.GetBoundingBox(); //Convert the origin of the ink rectangle to pixel space (500x500) Microsoft.Ink.Renderer r = new Microsoft.Ink.Renderer(); Point inkOrigin = inkBB.Location; r.InkSpaceToPixel(g, ref inkOrigin); //Convert the transparency coefficient from 0-255 with 0==opaque to the range of 0-1 with 1==opaque. int t1 = Math.Abs(tmpInk.Strokes[0].DrawingAttributes.Transparency - 255); float t2 = (float)t1 / 255f; //Setting transparency with a ColorMatrix float[][] ptsArray = { new float[] { 1, 0, 0, 0, 0 }, //r new float[] { 0, 1, 0, 0, 0 }, //g new float[] { 0, 0, 1, 0, 0 }, //b new float[] { 0, 0, 0, t2, 0 }, //alpha new float[] { 0, 0, 0, 0, 1 } }; ColorMatrix clrMatrix = new ColorMatrix(ptsArray); ImageAttributes imgAttributes = new ImageAttributes(); imgAttributes.SetColorMatrix(clrMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); //Adjust Y origin to account for horizontal scroll. (scrollPos becomes more positive as ink goes upward.) float scrolledYInkOrigin = (float)inkOrigin.Y - (500 * (float)scrollPos); //Still in 500x500 space //Scale and locate the destination rectangle of the ink within the slide image: RectangleF destRect = new RectangleF( (float)inkOrigin.X * ((float)img.Width / 500f) * (float)size, scrolledYInkOrigin * ((float)img.Height / 500f) * (float)size, (float)inkImg.Width * ((float)img.Width / 500f) * (float)size, (float)inkImg.Height * ((float)img.Height / 500f) * (float)size); Rectangle destRect2 = new Rectangle((int)destRect.X, (int)destRect.Y, (int)destRect.Width, (int)destRect.Height); //Draw the overlay: g.DrawImage(inkImg, destRect2, 0, 0, inkImg.Width, inkImg.Height, GraphicsUnit.Pixel, imgAttributes); g.Dispose(); }
private void AddOpaqueInkOverlayOld(double size, Image img) { //add the opaque ink overlay if ((opaqueInk != null) && (opaqueInk.Strokes.Count > 0)) { //Make a GIF Image from the ink. Note that this image is assumed to be in a 500x500 pixel space. byte[] ba = opaqueInk.Save(Microsoft.Ink.PersistenceFormat.Gif); Image inkImg = Image.FromStream(new MemoryStream(ba)); Graphics g = Graphics.FromImage(img); GraphicsUnit gu = GraphicsUnit.Pixel; //Get the origin from the ink Bounding Box (in ink space) Rectangle inkBB = opaqueInk.GetBoundingBox(); //Convert the origin of the ink rectangle to pixel space (500x500) Microsoft.Ink.Renderer r = new Microsoft.Ink.Renderer(); Point inkOrigin = inkBB.Location; r.InkSpaceToPixel(g, ref inkOrigin); //Adjust Y origin to account for horizontal scroll. float scrolledYInkOrigin = (float)inkOrigin.Y - (500 * (float)scrollPos); //Scale and locate the destination rectangle of the ink within the slide image: RectangleF destRect = new RectangleF( (float)inkOrigin.X * ((float)img.Width / 500f) * (float)size, scrolledYInkOrigin * ((float)img.Height / 500f) * (float)size, (float)inkImg.Width * ((float)img.Width / 500f) * (float)size, (float)inkImg.Height * ((float)img.Height / 500f) * (float)size); //Draw the overlay: g.DrawImage(inkImg, destRect, inkImg.GetBounds(ref gu), GraphicsUnit.Pixel); g.Dispose(); } }
/// <summary> /// Plays back ink data to a specified control /// </summary> /// <param name="destinationControl"> /// The control to play the Ink Data to.</param> /// <param name="destinationRenderer"> /// The Ink Renderer used to convert ink data to display coordinates</param> /// <param name="keepDestinationAspectRatio"> /// Specified whether to keep original aspect ratio of ink when scaling</param> /// <param name="playbackRate"> /// The rate at which to play back the Ink Data</param> protected void PlaybackRecordedData( Control destinationControl, bool keepDestinationAspectRatio, PacketPlaybackRate playbackRate, System.Drawing.Drawing2D.Matrix iTrans) { if( null != PlaybackInk ) { System.Drawing.Graphics g = destinationControl.CreateGraphics(); Microsoft.Ink.Renderer destinationRenderer = new Microsoft.Ink.Renderer(); destinationRenderer.SetViewTransform( iTrans ); // Set whether or not to keep ink aspect ratio in the display window bool keepAspectRatio = keepDestinationAspectRatio; // Declare scaling factors float scaleFactorX = 1.0f; float scaleFactorY = 1.0f; // Get the size of the display window System.Drawing.Size displayWindowSize = destinationControl.ClientSize; // Get ink bounding box in ink space; convert the box's size to pixel; System.Drawing.Rectangle inkBoundingBox = PlaybackInk.GetBoundingBox(); // Set the size and offset of the destination input System.Drawing.Point inkBoundingBoxSize = new System.Drawing.Point(inkBoundingBox.Width, inkBoundingBox.Height); // Convert the inkspace coordinate to pixels destinationRenderer.InkSpaceToPixel(g, ref inkBoundingBoxSize); // Get the offset of ink System.Drawing.Point inkOffset = inkBoundingBox.Location; // Determine what the scaling factor of the destination control is so // we know how to correctly resize the ink data getDisplayWindowScalingFactor(new System.Drawing.Size(inkBoundingBoxSize), displayWindowSize, keepAspectRatio, ref scaleFactorX, ref scaleFactorY); // Iterate through all ink strokes and extract the packet data foreach ( Microsoft.Ink.Stroke currentStroke in PlaybackInk.Strokes ) { // Convert the stroke's packet data to INPUT structs INPUT[] inputs = SendInputInterop.StrokeToInputs( currentStroke, destinationRenderer, g, scaleFactorX, scaleFactorY, destinationControl, inkOffset ); if ( null != inputs ) { // Iterate through all the extracted INPUT data in order to send to destination control for ( int i = 0; i < inputs.Length; i++ ) { // Use the Win32 SendInput API to send the ink data point to the control // Note that all playback will use the upper left of the destination control // as the origin SendInputInterop.SendInput( 1, new INPUT[] { inputs[i] }, System.Runtime.InteropServices.Marshal.SizeOf( inputs[i] ) ); // Determine the delay between packets (within a stroke) switch( playbackRate ) { case PacketPlaybackRate.Default: default: System.Threading.Thread.Sleep(5); break; case PacketPlaybackRate.Slow: System.Threading.Thread.Sleep(100); break; case PacketPlaybackRate.Fast: break; } } } // Reset the focus to the destination control in case it has been changed if( destinationControl.InvokeRequired ) { GenericVoidCallback func = new GenericVoidCallback( delegate { destinationControl.Focus(); } ); destinationControl.Invoke( func ); } else destinationControl.Focus(); // Create a delay between each stroke if ( 0 != InterStrokeDelay) { System.Threading.Thread.Sleep((int)(InterStrokeDelay)); } } // dispose the graphics object g.Dispose(); } }
/// <summary> /// Plays back ink data to a specified control /// </summary> /// <param name="destinationControl"> /// The control to play the Ink Data to.</param> /// <param name="destinationRenderer"> /// The Ink Renderer used to convert ink data to display coordinates</param> /// <param name="keepDestinationAspectRatio"> /// Specified whether to keep original aspect ratio of ink when scaling</param> /// <param name="playbackRate"> /// The rate at which to play back the Ink Data</param> protected void PlaybackRecordedData(Control destinationControl, bool keepDestinationAspectRatio, PacketPlaybackRate playbackRate, System.Drawing.Drawing2D.Matrix iTrans) { if (null != PlaybackInk) { System.Drawing.Graphics g = destinationControl.CreateGraphics(); Microsoft.Ink.Renderer destinationRenderer = new Microsoft.Ink.Renderer(); destinationRenderer.SetViewTransform(iTrans); // Set whether or not to keep ink aspect ratio in the display window bool keepAspectRatio = keepDestinationAspectRatio; // Declare scaling factors float scaleFactorX = 1.0f; float scaleFactorY = 1.0f; // Get the size of the display window System.Drawing.Size displayWindowSize = destinationControl.ClientSize; // Get ink bounding box in ink space; convert the box's size to pixel; System.Drawing.Rectangle inkBoundingBox = PlaybackInk.GetBoundingBox(); // Set the size and offset of the destination input System.Drawing.Point inkBoundingBoxSize = new System.Drawing.Point(inkBoundingBox.Width, inkBoundingBox.Height); // Convert the inkspace coordinate to pixels destinationRenderer.InkSpaceToPixel(g, ref inkBoundingBoxSize); // Get the offset of ink System.Drawing.Point inkOffset = inkBoundingBox.Location; // Determine what the scaling factor of the destination control is so // we know how to correctly resize the ink data getDisplayWindowScalingFactor(new System.Drawing.Size(inkBoundingBoxSize), displayWindowSize, keepAspectRatio, ref scaleFactorX, ref scaleFactorY); // Iterate through all ink strokes and extract the packet data foreach (Microsoft.Ink.Stroke currentStroke in PlaybackInk.Strokes) { // Convert the stroke's packet data to INPUT structs INPUT[] inputs = SendInputInterop.StrokeToInputs(currentStroke, destinationRenderer, g, scaleFactorX, scaleFactorY, destinationControl, inkOffset); if (null != inputs) { // Iterate through all the extracted INPUT data in order to send to destination control for (int i = 0; i < inputs.Length; i++) { // Use the Win32 SendInput API to send the ink data point to the control // Note that all playback will use the upper left of the destination control // as the origin SendInputInterop.SendInput(1, new INPUT[] { inputs[i] }, System.Runtime.InteropServices.Marshal.SizeOf(inputs[i])); // Determine the delay between packets (within a stroke) switch (playbackRate) { case PacketPlaybackRate.Default: default: System.Threading.Thread.Sleep(5); break; case PacketPlaybackRate.Slow: System.Threading.Thread.Sleep(100); break; case PacketPlaybackRate.Fast: break; } } } // Reset the focus to the destination control in case it has been changed if (destinationControl.InvokeRequired) { GenericVoidCallback func = new GenericVoidCallback(delegate { destinationControl.Focus(); }); destinationControl.Invoke(func); } else { destinationControl.Focus(); } // Create a delay between each stroke if (0 != InterStrokeDelay) { System.Threading.Thread.Sleep((int)(InterStrokeDelay)); } } // dispose the graphics object g.Dispose(); } }