MJW Posted February 3, 2017 Share Posted February 3, 2017 This is a CodeLab plugin that provides the code to scale, rotate, and offset a clipboard image. As a plugin, it's not too useful, but it might provide a starting point for writing plugins that use the clipboard. The DLL (in case you want to see what it does): Basic Clipboard Transformations.zip The code: Spoiler // Name: Basic Clipboard Transformations // Submenu: Test // Author: MJW // Title: Basic Clipboard Transformations // Submenu: Test // Version: 1.0.* // Desc: Basic transformations of the clipboard image as a starting pooint for other plugins. // Keywords: clipboard transformations // URL: // Help: #region UICode DoubleSliderControl Amount1 = 1; // [0,10] Clipboard Image Size DoubleSliderControl Amount2 = 0; // [-1,1] Clipboard Image XY Proportion PanSliderControl Amount3 = Pair.Create(0.000,0.000); // Clipboard Image Offset DoubleSliderControl Amount4 = 1; // [0,2] Clipboard Image Offset Range AngleControl Amount5 = 0; // [-180,180] Clipboard Image Rotation CheckboxControl Amount6 = false; // [0,1] Offset Relative to Selection #endregion //----------- // Constants. //----------- const double degToRad = Math.PI / 180.0; const double MaxXYScale = 12.0f; // Maximum XY Proportion scale factor. readonly float XYPropConst = (float)((Math.Sqrt(MaxXYScale) + 1.0) / (Math.Sqrt(MaxXYScale) - 1.0)); //------------------ // Global variables. //------------------ float Mxx, Mxy, Mxw, Myx, Myy, Myw; bool showedCbErrorMsg = false; bool noClipboardImg = true; //***************************************************************************************** //---------------------------------------------------------------------------- // Setup routine to initalize variable based on the control values. // Called from OnSetRenderInfo after ProcessControlValues has been called. // // Because this is done before the render threads run, the "global" variables // can be freely changed, unlike when the setup is done in the render threads. //----------------------------------------------------------------------------- void SetupRendering(Surface dst, Surface src) { int width = dst.Width, height = dst.Height; //------------------------------------------------------------------------------------- // Assign meaningful variable names. // The Image Offset Range, if needed would often be a hardcoded value. // When the value is one, the clipboard image just disappears out of the canvas or the // the selection when the offsets are their maximum or minimum values. If a wider range // of motion is needed, the Image Offset Range value can be increased. //-------------------------------------------------------------------------------------- double ImgScale = Amount1; // Scale factor for image. double ImgXYProp = Amount2; // XY proportion -- stretches image in X or Y. Pair<double, double> ImgOffset = Amount3; // Offset from center of canvas or selection double ImgOffsetRange = Amount4; // Offset range. Normally not needed. double ImgRotation = Amount5; // Counter-clockwise rotation. bool ImgOffsetToSel = Amount6; // If true, image will be centered in selection. noClipboardImg = false; if (Img == null) { if (!showedCbErrorMsg) { MessageBox.Show("This plugin requires a Clipboard image.", "Basic Clipboard Transformations", MessageBoxButtons.OK, MessageBoxIcon.Error); showedCbErrorMsg = true; } // Maybe I should just render using white, but this (sort of) lets the user know // there's no clipboard image. noClipboardImg = true; return; } // Get the scale and offset for use when accessing the clipboard. // Scale for center of the selection. // Maximim shifts will move image just out of the selection (or window). float recipScale = (float)ImgScale; if (recipScale < 0.0005f) // Don't let scaling be zero. recipScale = 0.0005f; float scale = 1.0f / recipScale; float imgXYProp = (float)ImgXYProp; float xPropScale = (XYPropConst - imgXYProp) / (XYPropConst + imgXYProp); float yPropScale = 1.0f / xPropScale; scale *= (float)Math.Sqrt(0.5 * (xPropScale * xPropScale + yPropScale * yPropScale)); float xScale = scale * xPropScale; float yScale = scale * yPropScale; float shiftX = (float)(ImgOffsetRange * ImgOffset.First); float shiftY = (float)(ImgOffsetRange * ImgOffset.Second); // Set up to offset relative to window or selection. int winLeft, winRight, winTop, winBottom; if (ImgOffsetToSel) { // Selection. Rectangle selection = EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt(); winLeft = selection.Left; winRight = selection.Right; winTop = selection.Top; winBottom = selection.Bottom; } else { // Window. winLeft = 0; winRight = width; winTop = 0; winBottom = height; } float winHalfWidth = 0.5f * (float)(winRight - winLeft); float winHalfHeight = 0.5f * (float)(winBottom - winTop); float winCenterX = 0.5f * (float)(winRight + winLeft); float winCenterY = 0.5f * (float)(winBottom + winTop); float imgHalfWidth = 0.5f * (float)Img.Width; float imgHalfHeight = 0.5f * (float)Img.Height; float bbHalfWidth = imgHalfWidth / xScale; float bbHalfHeight = imgHalfHeight / yScale; // Rotation if (ImgRotation != 0.0) { float cos, sin; double rotation = -degToRad * ImgRotation; cos = (float)Math.Cos(rotation); sin = (float)Math.Sin(rotation); // Compute the x, y scaling factors. Mxx = xScale * cos; Myy = yScale * cos; Mxy = xScale * sin; Myx = -yScale * sin; // Get the bounding rectangle, which determines the shift range. float absCos = (float)Math.Abs(cos), absSin = (float)Math.Abs(sin); float tempHalfWidth = bbHalfWidth; bbHalfWidth = absCos * bbHalfWidth + absSin * bbHalfHeight; bbHalfHeight = absCos * bbHalfHeight + absSin * tempHalfWidth; } else { // No rotation, so do it the easy way. Mxx = xScale; Myy = yScale; Mxy = Myx = 0.0f; } float tempX = winCenterX + shiftX * (bbHalfWidth + winHalfWidth); float tempY = winCenterY + shiftY * (bbHalfHeight + winHalfHeight); Mxw = imgHalfWidth - Mxx * tempX - Mxy * tempY; Myw = imgHalfHeight - Myx * tempX - Myy * tempY; } //***************************************************************************************** //---------------------- // The rendering loops. //---------------------- void Render(Surface dst, Surface src, Rectangle rect) { SetupRendering(dst, src); int left = rect.Left, right = rect.Right, top = rect.Top, bottom = rect.Bottom; if (noClipboardImg) { for (int y = top; y < bottom; y++) { if (IsCancelRequested) return; for (int x = left; x < right; x++) { dst[x, y] = src[x, y]; } } } else { for (int y = top; y < bottom; y++) { if (IsCancelRequested) return; for (int x = left; x < right; x++) { float fx = x, fy = y; float tx = Mxx * fx + Mxy * fy + Mxw; float ty = Myx * fx + Myy * fy + Myw; dst[x, y] = Img.GetBilinearSample(tx, ty); } } } } // Setup for getting an image from the clipboard protected Surface Img { get { if (_img != null) return _img; else { Thread t = new Thread(new ThreadStart(GetImageFromClipboard)); t.SetApartmentState(ApartmentState.STA); t.Start(); t.Join(); return _img; } } } private Surface _img = null; private void GetImageFromClipboard() { Bitmap aimg = null; IDataObject clippy; try { clippy = Clipboard.GetDataObject(); if (clippy != null) { if (Clipboard.ContainsData("PNG")) { // Handle bitmaps with transparency Object png_object = Clipboard.GetData("PNG"); if (png_object is MemoryStream) { MemoryStream png_stream = png_object as MemoryStream; aimg = (Bitmap)Image.FromStream(png_stream); } } else if (clippy.GetDataPresent(DataFormats.Bitmap)) { // If that didn't work, try bitmaps without transparency aimg = (Bitmap)clippy.GetData(typeof(Bitmap)); } } } catch (Exception) { } if (aimg != null) { _img = Surface.CopyFromBitmap(aimg); } else { _img = null; } } 3 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.