示例#1
0
		///<summary>Kills ImageApplicationThread.  Disposes of both currentImages and ImageRenderingNow.  Does not actually trigger a refresh of the Picturebox, though.</summary>
		private void EraseCurrentImages() {
			KillThreadImageUpdate();//Stop any current access to the current image and render image so we can dispose them.
			pictureBoxMain.Image=null;
			ImageSettingFlagsInvalidated=ImageSettingFlags.NONE;
			if(ImagesCur!=null) {
				for(int i=0;i<ImagesCur.Length;i++) {
					if(ImagesCur[i]!=null) {
						ImagesCur[i].Dispose();
						ImagesCur[i]=null;
					}
				}
			}
			if(ImageRenderingNow!=null) {
				ImageRenderingNow.Dispose();
				ImageRenderingNow=null;
			}
			System.GC.Collect();
		}
示例#2
0
		///<summary>Invalidates some or all of the image settings.  This will cause those settings to be recalculated, either immediately, or when the current ApplySettings thread is finished.  If supplied settings is ApplySettings.NONE, then that part will be skipped.</summary>
		private void InvalidateSettings(ImageSettingFlags settings,bool reloadZoomTransCrop,bool[] mountIdxsToUpdate) {
			if(this.InvokeRequired) {
				InvalidatesettingsCallback c=new InvalidatesettingsCallback(InvalidateSettings);
				Invoke(c,new object[] { settings,reloadZoomTransCrop });
				return;
			}
			//Do not allow image rendering when the paint tools are disabled. This will disable the display image when a folder or non-image document is selected, or when no document is currently selected. The ToolBarPaint.Enabled boolean is controlled in SelectTreeNode() and is set to true only if a valid image is currently being displayed.
			if(treeDocuments.SelectedNode==null || treeDocuments.SelectedNode.Tag==null) {
				EraseCurrentImages();
				return;
			}
			ImageNodeId nodeId=(ImageNodeId)treeDocuments.SelectedNode.Tag;
			if(nodeId.NodeType==ImageNodeType.None || nodeId.NodeType==ImageNodeType.Category) {
				EraseCurrentImages();
				return;
			}
			if(nodeId.NodeType==ImageNodeType.Doc) {
				if(!ToolBarPaint.Enabled) {
					EraseCurrentImages();
					return;
				}
			}
			if(nodeId.NodeType==ImageNodeType.Doc || nodeId.NodeType==ImageNodeType.Eob || nodeId.NodeType==ImageNodeType.Amd) {
				if(reloadZoomTransCrop) {
					//Reloading the image settings only happens when a new image is selected, pasted, scanned, etc...
					//Therefore, the is no need for any current image processing anymore (it would be on a stale image).
					KillThreadImageUpdate();
					ReloadZoomTransCrop(WidthsImagesCur[0],HeightsImagesCur[0],DocSelected,
						new Rectangle(0,0,pictureBoxMain.Width,pictureBoxMain.Height),
						out ZoomImage,out ZoomLevel,out ZoomOverall,out PointTranslation);
					RectCrop=new Rectangle(0,0,-1,-1);
				}
			}
			ImageSettingFlagsInvalidated |= settings;
			//DocSelected is an individual document instance. Assigning a new document to DocForSettings here does not 
			//negatively effect our image application thread, because the thread either will keep its current 
			//reference to the old document, or will apply the settings with this newly assigned document. In either
			//case, the output is either what we expected originally, or is a more accurate image for more recent 
			//settings. We lock here so that we are sure that the resulting document and setting tuple represent
			//a single point in time.
			lock(EventWaitHandleSettings) {//Does not actually lock the EventWaitHandleSettings object, but rather locks the variables in the block.
				MountIdxsFlaggedForUpdate=(bool[])mountIdxsToUpdate.Clone();
				ImageSettingFlagsForSettings=ImageSettingFlagsInvalidated;
				NodeTypeForSettings=((ImageNodeId)treeDocuments.SelectedNode.Tag).NodeType;
				if(NodeTypeForSettings==ImageNodeType.Doc) {
					DocForSettings=DocSelected.Copy();
				}
			}
			//Tell the thread to start processing (as soon as the thread is created, or as soon as otherwise 
			//possible). Set() has no effect if the handle is already signaled.
			EventWaitHandleSettings.Set();
			if(ThreadImageUpdate==null) {//Create the thread if it has not been created, or if it was killed for some reason.
				ThreadImageUpdate=new Thread((ThreadStart)(delegate { Worker(); }));
				ThreadImageUpdate.IsBackground=true;
				ThreadImageUpdate.Start();
			}
			ImageSettingFlagsInvalidated=ImageSettingFlags.NONE;
		}
示例#3
0
        ///<summary>Applies the document specified cropping, flip, rotation, brightness and contrast transformations to the image and returns the resulting image. Zoom and translation must be handled by the calling code. The returned image is always a new image that can be modified without affecting the original image. The change in the image's center point is returned into deltaCenter, so that rotation offsets can be properly calculated when displaying the returned image.</summary>
        public static Bitmap ApplyDocumentSettingsToImage(Document doc, Bitmap image, ImageSettingFlags settings)
        {
            if (image == null)           //Any operation on a non-existant image produces a non-existant image.
            {
                return(null);
            }
            if (doc == null)           //No doc implies no operations, implies that the image should be returned "unaltered".
            //return (Bitmap)image.Clone();//this would keep the original resolution, which causes problems.
            {
                return(new Bitmap(image));               //resets the resolution to 96, just like it does for docs 20 lines down.
            }
            //CROP - Implies that the croping rectangle must be saved in raw-image-space coordinates,
            //with an origin of that equal to the upper left hand portion of the image.
            Rectangle cropResult;

            if ((settings & ImageSettingFlags.CROP) != 0 &&                                                   //Crop not requested.
                doc.CropW > 0 && doc.CropH > 0)                                                               //No clip area yet defined, so no clipping is performed.
            {
                float[] cropDims = ODMathLib.IntersectRectangles(0, 0, image.Width, image.Height,             //Intersect image rectangle with
                                                                 doc.CropX, doc.CropY, doc.CropW, doc.CropH); //document crop rectangle.
                if (cropDims.Length == 0)                                                                     //The entire image has been cropped away.
                {
                    return(null);
                }
                //Rounds dims up, so that data is not lost, but possibly not removing all of what was expected.
                cropResult = new Rectangle((int)cropDims[0], (int)cropDims[1],
                                           (int)Math.Ceiling(cropDims[2]), (int)Math.Ceiling(cropDims[3]));
            }
            else
            {
                cropResult = new Rectangle(0, 0, image.Width, image.Height);                //No cropping.
            }
            //Always use 32-bit images in memory. We could use 24-bit images here (it works in Windows, but MONO produces
            //output using 32-bit data on a 24-bit image in that case, providing horrible output). Perhaps we can update
            //this when MONO is more fully featured.
            Bitmap    cropped     = new Bitmap(cropResult.Width, cropResult.Height, PixelFormat.Format32bppArgb);
            Graphics  g           = Graphics.FromImage(cropped);
            Rectangle croppedDims = new Rectangle(0, 0, cropped.Width, cropped.Height);

            g.DrawImage(image, croppedDims, cropResult, GraphicsUnit.Pixel);
            g.Dispose();
            //FLIP AND ROTATE - must match the operations in GetDocumentFlippedRotatedMatrix().
            if ((settings & ImageSettingFlags.FLIP) != 0)
            {
                if (doc.IsFlipped)
                {
                    cropped.RotateFlip(RotateFlipType.RotateNoneFlipX);
                }
            }
            if ((settings & ImageSettingFlags.ROTATE) != 0)
            {
                if (doc.DegreesRotated % 360 == 90)
                {
                    cropped.RotateFlip(RotateFlipType.Rotate90FlipNone);
                }
                else if (doc.DegreesRotated % 360 == 180)
                {
                    cropped.RotateFlip(RotateFlipType.Rotate180FlipNone);
                }
                else if (doc.DegreesRotated % 360 == 270)
                {
                    cropped.RotateFlip(RotateFlipType.Rotate270FlipNone);
                }
            }
            //APPLY BRIGHTNESS AND CONTRAST -
            //TODO: should be updated later for more general functions
            //(create inputValues and outputValues from stored db function/table).
            if ((settings & ImageSettingFlags.COLORFUNCTION) != 0 &&
                doc.WindowingMax != 0 &&                             //Do not apply color function if brightness/contrast have never been set (assume normal settings).
                !(doc.WindowingMax == 255 && doc.WindowingMin == 0)) //Don't apply if brightness/contrast settings are normal.
            {
                float[] inputValues = new float[] {
                    doc.WindowingMin / 255f,
                    doc.WindowingMax / 255f,
                };
                float[] outputValues = new float[] {
                    0,
                    1,
                };
                BitmapData croppedData = null;
                try {
                    croppedData = cropped.LockBits(new Rectangle(0, 0, cropped.Width, cropped.Height),
                                                   ImageLockMode.ReadWrite, cropped.PixelFormat);
                    unsafe {
                        byte *pBits;
                        if (croppedData.Stride < 0)
                        {
                            pBits = (byte *)croppedData.Scan0.ToPointer() + croppedData.Stride * (croppedData.Height - 1);
                        }
                        else
                        {
                            pBits = (byte *)croppedData.Scan0.ToPointer();
                        }
                        //The following loop goes through each byte of each 32-bit value and applies the color function to it.
                        //Thus, the same transformation is performed to all 4 color components equivalently for each pixel.
                        for (int i = 0; i < croppedData.Stride * croppedData.Height; i++)
                        {
                            float colorComponent = pBits[i] / 255f;
                            float rangedOutput;
                            if (colorComponent <= inputValues[0])
                            {
                                rangedOutput = outputValues[0];
                            }
                            else if (colorComponent >= inputValues[inputValues.Length - 1])
                            {
                                rangedOutput = outputValues[outputValues.Length - 1];
                            }
                            else
                            {
                                int j = 0;
                                while (!(inputValues[j] <= colorComponent && colorComponent < inputValues[j + 1]))
                                {
                                    j++;
                                }
                                rangedOutput = ((colorComponent - inputValues[j]) * (outputValues[j + 1] - outputValues[j]))
                                               / (inputValues[j + 1] - inputValues[j]);
                            }
                            pBits[i] = (byte)Math.Round(255 * rangedOutput);
                        }
                    }
                }
                catch {
                }
                finally {
                    try {
                        cropped.UnlockBits(croppedData);
                    }
                    catch {
                    }
                }
            }
            return(cropped);
        }
示例#4
0
		///<summary>Invalidates some or all of the image settings.  This will cause those settings to be recalculated, either immediately, or when the current ApplySettings thread is finished.  If supplied settings is ApplySettings.NONE, then that part will be skipped.</summary>
		private void InvalidateSettings(ImageSettingFlags settings,bool reloadZoomTransCrop) {
			bool[] mountIdxsToUpdate=new bool[this.ImagesCur.Length];
			if(ImagesCur.Length==1) {//An image is currently showing.
				mountIdxsToUpdate[0]=true;//Mark the document to be updated.
			}
			else if(ImagesCur.Length==4) {//4 bite-wing mount is currently selected.
				if(IdxSelectedInMount>=0) {
					//The current active document will be updated.
					mountIdxsToUpdate[IdxSelectedInMount]=true;
				}
			}
			InvalidateSettings(settings,reloadZoomTransCrop,mountIdxsToUpdate);
		}
示例#5
0
		///<summary>Applies the document specified cropping, flip, rotation, brightness and contrast transformations to the image and returns the resulting image. Zoom and translation must be handled by the calling code. The returned image is always a new image that can be modified without affecting the original image. The change in the image's center point is returned into deltaCenter, so that rotation offsets can be properly calculated when displaying the returned image.</summary>
		public static Bitmap ApplyDocumentSettingsToImage(Document doc, Bitmap image, ImageSettingFlags settings) {
			if(image==null) {//Any operation on a non-existant image produces a non-existant image.
				return null;
			}
			if(doc==null) {//No doc implies no operations, implies that the image should be returned "unaltered".
				//return (Bitmap)image.Clone();//this would keep the original resolution, which causes problems.
				return new Bitmap(image);//resets the resolution to 96, just like it does for docs 20 lines down.
			}
			//CROP - Implies that the croping rectangle must be saved in raw-image-space coordinates, 
			//with an origin of that equal to the upper left hand portion of the image.
			Rectangle cropResult;
			if((settings & ImageSettingFlags.CROP) != 0 &&	//Crop not requested.
				doc.CropW > 0 && doc.CropH > 0)//No clip area yet defined, so no clipping is performed.
			{
				float[] cropDims = ODMathLib.IntersectRectangles(0, 0, image.Width, image.Height,//Intersect image rectangle with
					doc.CropX, doc.CropY, doc.CropW, doc.CropH);//document crop rectangle.
				if (cropDims.Length == 0) {//The entire image has been cropped away.
					return null;
				}
				//Rounds dims up, so that data is not lost, but possibly not removing all of what was expected.
				cropResult = new Rectangle((int)cropDims[0], (int)cropDims[1],
					(int)Math.Ceiling(cropDims[2]), (int)Math.Ceiling(cropDims[3]));
			}
			else {
				cropResult = new Rectangle(0, 0, image.Width, image.Height);//No cropping.
			}
			//Always use 32-bit images in memory. We could use 24-bit images here (it works in Windows, but MONO produces
			//output using 32-bit data on a 24-bit image in that case, providing horrible output). Perhaps we can update
			//this when MONO is more fully featured.
			Bitmap cropped = new Bitmap(cropResult.Width, cropResult.Height, PixelFormat.Format32bppArgb);
			Graphics g = Graphics.FromImage(cropped);
			Rectangle croppedDims = new Rectangle(0, 0, cropped.Width, cropped.Height);
			g.DrawImage(image, croppedDims, cropResult, GraphicsUnit.Pixel);
			g.Dispose();
			//FLIP AND ROTATE - must match the operations in GetDocumentFlippedRotatedMatrix().
			if((settings & ImageSettingFlags.FLIP) != 0) {
				if (doc.IsFlipped) {
					cropped.RotateFlip(RotateFlipType.RotateNoneFlipX);
				}
			}
			if((settings & ImageSettingFlags.ROTATE) != 0) {
				if (doc.DegreesRotated % 360 == 90) {
					cropped.RotateFlip(RotateFlipType.Rotate90FlipNone);
				}
				else if (doc.DegreesRotated % 360 == 180) {
					cropped.RotateFlip(RotateFlipType.Rotate180FlipNone);
				}
				else if (doc.DegreesRotated % 360 == 270) {
					cropped.RotateFlip(RotateFlipType.Rotate270FlipNone);
				}
			}
			//APPLY BRIGHTNESS AND CONTRAST - 
			//TODO: should be updated later for more general functions 
			//(create inputValues and outputValues from stored db function/table).
			if((settings & ImageSettingFlags.COLORFUNCTION) != 0 &&
				doc.WindowingMax != 0 && //Do not apply color function if brightness/contrast have never been set (assume normal settings).
				!(doc.WindowingMax == 255 && doc.WindowingMin == 0)) {//Don't apply if brightness/contrast settings are normal.
				float[] inputValues = new float[] {
					doc.WindowingMin/255f,
					doc.WindowingMax/255f,
				};
				float[] outputValues = new float[]{
					0,
					1,
				};
				BitmapData croppedData = null;
				try {
					croppedData = cropped.LockBits(new Rectangle(0, 0, cropped.Width, cropped.Height),
						ImageLockMode.ReadWrite, cropped.PixelFormat);
					unsafe {
						byte* pBits;
						if (croppedData.Stride < 0) {
							pBits = (byte*)croppedData.Scan0.ToPointer() + croppedData.Stride * (croppedData.Height - 1);
						}
						else {
							pBits = (byte*)croppedData.Scan0.ToPointer();
						}
						//The following loop goes through each byte of each 32-bit value and applies the color function to it.
						//Thus, the same transformation is performed to all 4 color components equivalently for each pixel.
						for (int i = 0; i < croppedData.Stride * croppedData.Height; i++) {
							float colorComponent = pBits[i] / 255f;
							float rangedOutput;
							if (colorComponent <= inputValues[0]) {
								rangedOutput = outputValues[0];
							}
							else if (colorComponent >= inputValues[inputValues.Length - 1]) {
								rangedOutput = outputValues[outputValues.Length - 1];
							}
							else {
								int j = 0;
								while (!(inputValues[j] <= colorComponent && colorComponent < inputValues[j + 1])) {
									j++;
								}
								rangedOutput = ((colorComponent - inputValues[j]) * (outputValues[j + 1] - outputValues[j]))
									/ (inputValues[j + 1] - inputValues[j]);
							}
							pBits[i] = (byte)Math.Round(255 * rangedOutput);
						}
					}
				}
				catch {
				}
				finally {
					try {
						cropped.UnlockBits(croppedData);
					}
					catch {
					}
				}
			}
			return cropped;
		}