/// <summary> /// Returns the Ink stroke corresponding to the given /// Sketch substroke Id. Returns null if no corresponding /// Ink stroke exists. /// </summary> /// <param name="substr">The Id of the Substroke to look up</param> /// <returns>The corresponding Ink stroke</returns> public Microsoft.Ink.Stroke GetInkStrokeBySubstrokeId(Guid?substrId) { if (!sketchStr2ink.ContainsKey(substrId)) { return(null); } Microsoft.Ink.Stroke iStroke; using (Microsoft.Ink.Strokes strokes = mInk.Strokes) { int index = sketchStr2ink[substrId]; try { iStroke = strokes[index - 1]; } catch (ArgumentException) { return(null); // We should never in practice get here, // but if we do, it means an Ink stroke // we reference is missing from mInk } } return(iStroke); }
/// <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 addTransparentInkOverlay(Image img, double size) { //Ink transparency is in the range 0-255 with 0==opaque and 255==invisible. //In practice CP sets transparency to 160, but we should be prepared for //arbitrary values. //We have no assurance that individual strokes won't have different transparencies, so we'll //do each stroke as a separate overlay. if ((transparentInk != null) && (transparentInk.Strokes.Count > 0)) { foreach (Microsoft.Ink.Stroke s in transparentInk.Strokes) { Microsoft.Ink.Strokes strokes = transparentInk.CreateStrokes(new int[] { s.Id }); addTransparentStroke(img, strokes, size); } } }
/// <summary> /// Adding multiple strokes (which can't be erased individually because there are no Guids) /// Specifically, this is for student submission overlays. /// There are also some CP3 scenarios such as when the instructor opens a CP3 file that has pre-existing ink. /// </summary> /// <param name="ink"></param> public void AddInk(Microsoft.Ink.Ink newInk) { if ((newInk != null) && (newInk.Strokes.Count > 0)) { //separate transparent and opaque strokes List <int> transparentStrokes = new List <int>(); List <int> opaqueStrokes = new List <int>(); //iterate over strokes, adding the ink ids to the correct list foreach (Microsoft.Ink.Stroke s in newInk.Strokes) { if ((s.DrawingAttributes.Transparency != 0) && (s.DrawingAttributes.Transparency != 255)) { transparentStrokes.Add(s.Id); } else { opaqueStrokes.Add(s.Id); } } //Add transparent strokes to transparentInk. if (transparentStrokes.Count > 0) { Microsoft.Ink.Strokes tStrokes = newInk.CreateStrokes((int[])transparentStrokes.ToArray()); if (transparentInk == null) { transparentInk = new Microsoft.Ink.Ink(); } transparentInk.AddStrokesAtRectangle(tStrokes, tStrokes.GetBoundingBox()); dirtyBit = true; } //Add opaque strokes to opaqueInk. if (opaqueStrokes.Count > 0) { Microsoft.Ink.Strokes oStrokes = newInk.CreateStrokes((int[])opaqueStrokes.ToArray()); if (opaqueInk == null) { opaqueInk = new Microsoft.Ink.Ink(); } opaqueInk.AddStrokesAtRectangle(oStrokes, oStrokes.GetBoundingBox()); dirtyBit = true; } } }
/// <summary> /// Constructor /// </summary> /// <param name="sketch">Sketch to add a label to</param> /// <param name="substrokes">Substrokes to label</param> /// <param name="inkStrokes">InkOverlay strokes</param> /// <param name="label">Label to apply</param> /// <param name="labelColor">Color of the applied label</param> /// <param name="domainInfo">DomainInfo for our Labeler</param> public ApplyLabelCmd(Sketch.Sketch sketch, List <Substroke> substrokes, Microsoft.Ink.Strokes inkStrokes, string label, System.Drawing.Color labelColor, DomainInfo domainInfo) { this.sketch = sketch; this.substrokes = substrokes; this.label = label; this.labelColor = labelColor; this.inkStrokes = inkStrokes; this.domainInfo = domainInfo; // Initialize the original colors of the inkStrokes this.origColors = new System.Drawing.Color[inkStrokes.Count]; int count = 0; foreach (Microsoft.Ink.Stroke stroke in inkStrokes) { origColors[count++] = stroke.DrawingAttributes.Color; } }
/// <summary> /// Constructor /// </summary> /// <param name="sketch">Sketch to remove a label from</param> /// <param name="substrokes">Substrokes that contain the label</param> /// <param name="inkStrokes">InkOverlay strokes</param> /// <param name="mIdToSubstroke">Hashtable mapping Microsoft.Ink.Stroke Ids to Sketch.Substrokes</param> /// <param name="label">Label to remove</param> /// <param name="domainInfo">DomainInfo for our Labeler</param> public RemoveLabelCmd(Sketch.Sketch sketch, List <Substroke> substrokes, Microsoft.Ink.Strokes inkStrokes, Dictionary <int, Guid?> mIdToSubstroke, string label, DomainInfo domainInfo) { this.sketch = sketch; this.substrokes = substrokes; this.inkStrokes = inkStrokes; this.mIdToSubstroke = mIdToSubstroke; this.label = label; this.domainInfo = domainInfo; // Initialize the original colors of the inkStrokes this.origColors = new System.Drawing.Color[inkStrokes.Count]; int count = 0; foreach (Microsoft.Ink.Stroke stroke in inkStrokes) { origColors[count++] = stroke.DrawingAttributes.Color; } }
/// <summary> /// Loads an Ink instance and synchronizes Sketch /// </summary> /// <param name="ink">The Ink to load</param> public void LoadInk(Microsoft.Ink.Ink ink) { unsubscribeToInk(); this.Clear(); mInk = ink; // Load strokes using (Microsoft.Ink.Strokes iStrokes = ink.Strokes) { foreach (Microsoft.Ink.Stroke iStroke in iStrokes) { AddInkStroke(iStroke); } } subscribeToInk(); }
/// <summary> /// Adds an Ink stroke contained within the assocated /// Ink object and and updates the Sketch. Checks to ensure /// that the given Ink stroke is contained within Ink. /// </summary> /// <param name="id">The id of the Ink Stroke to add</param> public void AddInkStroke(int id) { if (ink2sketchStr.ContainsKey(id)) { return; } // Try to fetch the corresponding Ink stroke Microsoft.Ink.Stroke iStroke; try { using (Microsoft.Ink.Strokes strokes = mInk.Strokes) { if (strokes.Count == id) { iStroke = strokes[id - 1]; } else { iStroke = strokes[strokes.Count - 1]; } } } catch (ArgumentException) { return; // We should never in practice get here, // but if we do, it means the Ink stroke // referenced is missing from mInk // !!!!!!!!! Eric 6-14-2009: // You will get here anytime a stroke has // been deleted. Added a little catch that may // or may not work (check if strokes.Count == id) } AddInkStroke(iStroke); }