BSOne.SFC/EM.Lib/ImageEn_SRC/Source/imageenview.pas

23703 lines
764 KiB
Plaintext
Raw Blame History

(* ImageEn Build 7.0.0.06.2637 @ 7-4-17 14:58:42.679 *)
(*
Copyright (c) 1998-2017 by Carlotta Calandra. All rights reserved.
Copyright (c) 2011-2017 by Xequte Software.
This software comes without express or implied warranty.
In no case shall the author be liable for any damage or unwanted behavior of any
computer hardware and/or software.
Author grants you the right to include the component
in your application, whether COMMERCIAL, SHAREWARE, or FREEWARE.
ImageEn, IEvolution and ImageEn ActiveX may not be included in any
commercial, shareware or freeware libraries or components.
www.ImageEn.com
*)
(*
File version 1056
Doc revision 1004
*)
unit imageenview;
{$IFDEF FPC}
{$MODE DELPHI}
{$ENDIF}
{$R-}
{$Q-}
interface
{$I ie.inc}
uses
Windows, Messages, Graphics, Controls, Forms, Classes, SysUtils, StdCtrls, ExtCtrls, Contnrs, hyiedefs, iexBitmaps, imageenio,
imageenproc, ieview, iexTransitions, hyieutils, iexRulers, iexLayers, ietextc
{$ifdef FPC}
,lmessages
{$endif}
;
type
{!!
<FS>TIEGrip
<FM>Declaration<FC>
}
TIEGrip = (ieNone, ieTopLeft, ieTopRight, ieBottomRight, ieBottomLeft, ieLeftSide, ieRightSide, ieTopSide, ieBottomSide, ieRotationCenter);
{!!}
{!!
<FS>TIEFastDrawing
<FM>Declaration<FC>
TIEFastDrawing = (iefFast, iefDelayed, iefNormal);
<FM>Description<FN>
<TABLE>
<R> <H>Item</H> <H>Description</H> </R>
<R> <C><FC>iefFast<FN></C> <C>Layer draw quality is reduced to speed up display (only affects drawing)</C> </R>
<R> <C><FC>iefDelayed<FN></C> <C>Fast drawing (iefFast) is used while rotating, moving or resizing layers. After a delay, layers are then drawn at normal quality (iefNormal)</C> </R>
<R> <C><FC>iefNormal<FN></C> <C>Layers are drawn at normal quality</C> </R>
</TABLE>
!!}
TIEFastDrawing = (iefFast, iefDelayed, iefNormal);
{!!
<FS>TIEGridKind
<FM>Declaration<FC>
TIEGridKind = (iedgNone, iedgPixelGrid, iedgGuideLines);
<FM>Description<FN>
<TABLE>
<R> <H>Item</H> <H>Description</H> </R>
<R> <C><FC>iedgNone<FN></C> <C>No guide lines are shown</C> </R>
<R> <C><FC>iedgPixelGrid<FN></C> <C>A grid is shown marking each pixel when the image is zoomed in (e.g. for pixel editing in an image editor)</C> </R>
<R> <C><FC>iedgGuideLines<FN></C> <C>Guide lines are shown horizontally and vertically over the image (e.g. to help align objects when rotating)</C> </R>
</TABLE>
!!}
TIEGridKind = (iedgNone, iedgPixelGrid, iedgGuideLines);
{!!
<FS>TIEAlignLayers
<FM>Declaration<FC>
TIEAlignLayers = (ilaAlignToLeft, ilaAlignToRight, ilaAlignToTop, ilaAlignToBottom, ilaAlignToHorizontalCenter, ilaAlignToVerticalCenter,
ilaAlignLeftEdges, ilaAlignRightEdges, ilaAlignTopEdges, ilaAlignBottomEdges, ilaAlignHorizontalCenters, ilaAlignVerticalCenters,
ilaMatchWidth, ilaMatchHeight);
<FM>Description<FN>
<TABLE>
<R> <H>Item</H> <H>Description</H> </R>
<R> <C><FC>ilaAlignToLeft<FN></C> <C>Align selected layers to the left side of the image</C> </R>
<R> <C><FC>ilaAlignToRight<FN></C> <C>Align selected layers to the right side of the image</C> </R>
<R> <C><FC>ilaAlignToTop<FN></C> <C>Align selected layers to the top of the image</C> </R>
<R> <C><FC>ilaAlignToBottom<FN></C> <C>Align selected layers to the bottom of the image</C> </R>
<R> <C><FC>ilaAlignToHorizontalCenter<FN></C> <C>Align selected layers along the horizon of the image</C> </R>
<R> <C><FC>ilaAlignToVerticalCenter<FN></C> <C>Align selected layers to the vertical center of the image</C> </R>
<R> <C><FC>ilaAlignLeftEdges<FN></C> <C>Align selected layers to the edge of the left-most layer</C> </R>
<R> <C><FC>ilaAlignRightEdges<FN></C> <C>Align selected layers to the edge of the right-most layer</C> </R>
<R> <C><FC>ilaAlignTopEdges<FN></C> <C>Align selected layers to the edge of the top-most layer</C> </R>
<R> <C><FC>ilaAlignBottomEdges<FN></C> <C>Align selected layers to the edge of the bottom-most layer</C> </R>
<R> <C><FC>ilaAlignHorizontalCenters<FN></C> <C>Align selected layers to have the same horizontal center</C> </R>
<R> <C><FC>ilaAlignVerticalCenters<FN></C> <C>Align selected layers to have the same vertical center</C> </R>
<R> <C><FC>ilaMatchWidth<FN></C> <C>Resize all selected layers to the width of the widest layer</C> </R>
<R> <C><FC>ilaMatchHeight<FN></C> <C>Resize all selected layers to the height of the tallest layer</C> </R>
</TABLE>
!!}
TIEAlignLayers = (ilaAlignToLeft, ilaAlignToRight, ilaAlignToTop, ilaAlignToBottom, ilaAlignToHorizontalCenter, ilaAlignToVerticalCenter,
ilaAlignLeftEdges, ilaAlignRightEdges, ilaAlignTopEdges, ilaAlignBottomEdges, ilaAlignHorizontalCenters, ilaAlignVerticalCenters,
ilaMatchWidth, ilaMatchHeight);
{!!
<FS>TIELayerOptions
<FM>Declaration<FC>
TIELayerOptions = set of (loAllowMultiSelect, loAutoSelectMask, loAutoUndoChangesByUser, loAutoUndoChangesByCode, loAutoPromptForImage, loFitToLayersWhenZooming, loAutoFixBorders, loAutoFixRotation, loDynamicCanvas);
<FM>Description<FN>
Options to control layer behavior:
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>loAllowMultiSelect</C> <C>If enabled, users can select multiple layers by holding down the Shift key. Moving, resizing, rotation, and other methods will apply to all selected layers</C> </R>
<R> <C>loAutoSelectMask</C> <C>If you have enabled a <A TIEImageLayer.IsMask>mask</L> for a layer, then when selecting the layer the mask will also be selected (so that any resizing actions apply to the mask layer too)</C> </R>
<R> <C>loAutoUndoChangesByUser</C> <C>If you have enabled <A TImageEnProc.AutoUndo>, then changes to layers made by users (using <L TImageEnView.MouseInteract>layer interactions<L> or <L TImageEnView Actions>layer TActions<L>) will be saved to the undo stack</C> </R>
<R> <C>loAutoUndoChangesByCode</C> <C>If you have enabled <A TImageEnProc.AutoUndo>, then programmatic changes to layers will be saved to the undo stack. This applies to the methods: <A TImageEnView.LayersAdd>, <A TImageEnView.LayersInsert>, <A TImageEnView.LayersCreateFromSelection>, <A TImageEnView.LayersCreateFromFile>, <A TImageEnView.LayersCreateFromEdge>, <A TImageEnView.LayersCreateFromAlpha>, <A TImageEnView.LayersRemove>, <A TImageEnView.LayersMerge>, <A TImageEnView.LayersMergeAll>, <A TImageEnView.LayersAlign>, <A TImageEnView.LayersMove>, <A TImageEnView.LayersRotateAll>, <A TImageEnView.LayersGroup>, <A TImageEnView.LayersUngroup>, <A TImageEnView.LayersCropBackground> </C> </R>
<R> <C>loAutoPromptForImage</C> <C>When setting <A TImageEnView.MouseInteract> to <FC>miCreateImageLayers<FN>, the user can drag select to create an <A TIEImageLayer> If <FC>loAutoPromptForImage<FN> is enabled, then the user will be prompted to browse for an image file when after completing the selection. If the user cancels, the image layer is not added.</C> </R>
<R> <C>loAutoFixBorders</C> <C>If enabled, <A TImageEnView.LayersFixBorders> will be called prior to rotation to removes any transparency around the edges of the image.</C> </R>
<R> <C>loAutoFixRotation</C> <C>If enabled, <A TImageEnView.LayersFixRotations> will be called after rotation to lock in the rotation angle of image layers.</C> </R>
<R> <C>loDynamicCanvas</C> <C>If enabled, ImageEn will align the view to the bounds of all layers (i.e. by <A TImageEnView.LayersRect>). You will be able to scroll to access layers that have been pushed beyond the bounds of Layer 0. However this may cause the view to scroll when moving layers around (so is best paired with <A TImageEnView.Center>=False). When not enabled, the view is aligned with Layer 0, and any layers pushed outside of the bounds of layer 0 may be inaccessible if they are off-screen.</C> </R>
</TABLE>
<FM>Examples<FC>
// Enable automatic mask selection
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAutoSelectMask ];
// Enable automatic undo for all image changes including layer changes
ImageEnView1.Proc.AutoUndo := True;
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAutoUndoChangesByUser ];
// Disable multiple selection of layers
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions - [ loAutoSelectMask ];
// Allow users to create image layers. Prompt for an image file after selection
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAutoPromptForImage ];
ImageEnView1.MouseInteract := [ miCreateImageLayers ];
!!}
TIELayerOptions = set of (
loAllowMultiSelect, // Multiple layers can be selected
loAutoSelectMask, // When a layer is selected any mask associated with it is also selected
loAutoUndoChangesByUser, // Add layer changes by user to undo stack if Auto-Undo is enabled for TImageEnView
loAutoUndoChangesByCode, // Add layer changes by layer methods to undo stack if Auto-Undo is enabled for TImageEnView
loAutoPromptForImage, // Prompt for an image file when creating an image layer
loAutoFixBorders, // Automatically call LayersFixBorders
loAutoFixRotation, // Automatically call LayersFixRotations
loDynamicCanvas // Fix view to layer 0
);
{!!
<FS>TViewChangeEvent
<FM>Declaration<FC>
TViewChangeEvent = procedure(Sender: TObject; Change: integer) of object;
<FM>Description<FN>
If Change = 0 the event is <A TImageEnView.ViewX> or <A TImageEnView.ViewY> modification.
If Change = 1 the event is <A TImageEnView.Zoom> modification.
Note: Zoom could also modify ViewX/Y.
!!}
TViewChangeEvent = procedure(Sender: TObject; Change: integer) of object;
{!!
<FS>TIEMediaFoundationNotifyEvent
<FM>Declaration<FC>
TIEMediaFoundationNotifyEvent = procedure(Sender: TObject; MediaFoundationObject: TObject; NotifyType: <A TIEMediaFountationNotifyType>) of object;
<FM>Description<FN>
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>Sender<FN></C> <C>Sender object. For <A TImageEnView.OnMediaFoundationNotify> this is a <A TImageEnView> object</C> </R>
<R> <C><FC>MediaFoundationObject<FN></C> <C>Media foundation object. This could be a <A TIEMediaFoundationSourceReader> object</C> </R>
<R> <C><FC>NotifyType<FN></C> <C>Notification type</C> </R>
</TABLE>
!!}
TIEMediaFoundationNotifyEvent = procedure(Sender: TObject; MediaFoundationObject: TObject; NotifyType: TIEMediaFountationNotifyType) of object;
{!!
<FS>TIETextEditorEvent
<FM>Declaration<FC>
TIETextEditorEvent = procedure(Sender: TObject; Layer: integer; Editor: TObject) of object;
<FM>Description<FN>
<FC>Layer<FN> is the index of the layer being edited.
<FC>Editor<FN> is a <A TIEEdit>, which handles the editing operation for a <A TIETextLayer> or <A TIELineLayer>.
!!}
TIETextEditorEvent = procedure(Sender: TObject; Layer: integer; Editor: TObject) of object;
{!!
<FS>TIESmoothTask
<FM>Declaration<FC>
}
TIESmoothTask = (iestScroll, iestZoom);
{!!}
{!!
<FS>TIEFinishSmoothTaskEvent
<FM>Declaration<FC>
TIEFinishSmoothTaskEvent = procedure(Sender: TObject; Task: <A TIESmoothTask>) of object;
<FM>Description<FN>
Event called when a smooth task has finished (smooth scroll, smooth zoom, etc...).
!!}
TIEFinishSmoothTaskEvent = procedure(Sender: TObject; Task: TIESmoothTask) of object;
{!!
<FS>TViewChangingEvent
<FM>Declaration<FC>
type TViewChangingEvent = procedure(Sender: TObject; Change: integer; newValue: Double) of object;
<FM>Description<FN>
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>Change<FN> = 0<FN></C> <C><A TImageEnView.ViewX> modification.</C> </R>
<R> <C><FC>Change<FN> = 1<FN></C> <C><A TImageEnView.ViewY> modification.</C> </R>
<R> <C><FC>Change<FN> = 2<FN></C> <C><A TImageEnView.Zoom> modification.</C> </R>
</TABLE>
<FC>newValue<FN> contains the new value that will be set.
Note: <FC>Zoom<FN> could also modify <FC>ViewX/Y<FN>.
!!}
TViewChangingEvent = procedure(Sender: TObject; Change: integer; newValue: double) of object;
{!!
<FS>TIEMouseInResizingGripEvent
<FM>Declaration<FC>
TIEMouseInResizingGripEvent = procedure(Sender: TObject; Grip: <A TIEGrip>) of object;
!!}
TIEMouseInResizingGripEvent = procedure(Sender: TObject; Grip: TIEGrip) of object;
{!!
<FS>TIEZoomEvent
<FM>Declaration<FC>
}
TIEZoomEvent = procedure(Sender: TObject; var NewZoom: double) of object;
{!!}
{!!
<FS>TIESpecialKeyEvent
<FM>Declaration<FC>
}
TIESpecialKeyEvent = procedure(Sender: TObject; CharCode: word; Shift: TShiftState; var Handled: Boolean) of object;
{!!}
TIEScrollCommand = (iescPosition, iescBottom, iescTop, iescLineDown, iescLineUp, iescPageDown, iescPageUp);
{!!
<FS>TIEOnDrawCanvas
<FM>Declaration<FC>
}
TIEOnDrawCanvas = procedure(Sender: TObject; ACanvas: TCanvas; ARect: TRect) of object;
{!!}
{!!
<FS>TIEOnDrawBackground
<FM>Declaration<FC>
}
TIEOnDrawBackground = procedure(Sender: TObject; ACanvas: TCanvas; ARect: TRect; var Handled: boolean) of object;
{!!}
{!!
<FS>TIEOnDrawPolygon
<FM>Declaration<FC>
}
TIEOnDrawPolygon = procedure(Sender: TObject; polygon: Integer; point: Integer; Canvas: TCanvas; x, y: Integer) of object;
{!!}
{!!
<FS>TIEWallpaperStyle
<FM>Declaration<FC>
TIEWallpaperStyle = (iewoNormal, iewoStretch, iewoTile);
<FM>Description<FN>
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>iewoNormal</C> <C>Draw the wallpaper image at the top left</C> </R>
<R> <C>iewoStretch</C> <C>Stretch the wallpaper to the ImageEnView size</C> </R>
<R> <C>iewoTile</C> <C>Tile the wallpaper over the background</C> </R>
</TABLE>
<FM>Example<FC>
// Tile a bitmap over the background
ImageEnView1.WallpaperStyle := iewoTile;
ImageEnView1.Wallpaper.LoadFromFile('D:\MyWallpaper.bmp');
!!}
TIEWallpaperStyle = (iewoNormal, iewoStretch, iewoTile);
{!!
<FS>TIEMouseInteractItems
<FM>Declaration<FC>
TIEMouseInteractItems = (miZoom, miScroll, miSelect, miSelectPolygon, miSelectCircle, miSelectZoom, miSelectMagicWand, miSelectLasso, miMoveLayers, miResizeLayers, miRotateLayers, miMovingScroll, miCropTool, miCreateImageLayers, miCreateShapeLayers, miCreateLineLayers, miCreatePolylineLayers, miCreateTextLayers);
<FM>Description<FN>
What actions does the mouse perform:
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>miZoom</C> <C>A left mouse click zooms into the image. A right click zooms out</C> </R>
<R> <C>miScroll</C> <C>The image can be navigated by clicking with the left mouse button and dragging</C> </R>
<R> <C>miSelect</C> <C>A rectangular area can be selected. Click and move the mouse to select the rectangle. miSelect excludes miScroll and miSelectXXX. Simultaneously holding down the Shift key allows the selection of multiple regions. The Alt Key forces the selection to maintain its aspect ratio</C> </R>
<R> <C>miSelectZoom</C> <C>If the user selects an area, it will be automatically zoomed into (and become the new display view). Click and move the mouse to select the zoom rectangle</C> </R>
<R> <C>miSelectPolygon</C> <C>A polygonal area can be selected. Click (and release) the left mouse button at each point of the polygon, or click and hold the left button and move to specify continuous irregular lines. Simultaneously holding down the Shift key allows the selection of multiple regions. Methods to terminate selection can be specified in <A TImageEnView.SelectionOptions></C> </R>
<R> <C>miSelectCircle</C> <C>A circular/elliptical area can be selected. Click the left button and move the mouse (holding the Alt key will force a circular selection). Simultaneously holding down the Shift key allows the selection of multiple regions</C> </R>
<R> <C>miSelectMagicWand</C> <C>Selects an irregular region of similar colors. Click the left mouse button upon a pixel and similar colors surrounding it are selected (see <A TImageEnView.MagicWandTolerance>). Simultaneously holding down the Shift key allows the selection of multiple regions</C> </R>
<R> <C>miSelectLasso</C> <C>A polygonal area can be selected by continous movement. Click the left button and move the mouse to select a continuous irregular region</C> </R>
<R> <C>miMoveLayers</C> <C>User can move layers</C> </R>
<R> <C>miResizeLayers</C> <C>User can resize layers using grips</C> </R>
<R> <C>miRotateLayers</C> <C>User can rotate layers using grips</C> </R>
<R> <C>miMovingScroll</C> <C>Moving the mouse over the image (without requiring any clicking) will cause the image to automatically scroll (pan) (see <A TImageEnView.SmoothScrollValue>)</C> </R>
<R> <C>miCropTool</C> <C> User can select a particular area of an image and discards the portions outside the chosen section (See <A TImageEnView.CropToolInteraction>)</C> </R>
<R> <C>miCreateImageLayers</C> <C>Click and move the mouse to create a <A TIEImageLayer> * </C> </R>
<R> <C>miCreateShapeLayers</C> <C>Click and move the mouse to create a <A TIEShapeLayer> * </C> </R>
<R> <C>miCreateLineLayers</C> <C>Click and move the mouse to create a <A TIELineLayer> * </C> </R>
<R> <C>miCreatePolylineLayers</C> <C>Click and move the mouse to create a <A TIEPolylineLayer> * </C> </R>
<R> <C>miCreateTextLayers</C> <C>Click and move the mouse to create a <A TIETextLayer> * </C> </R>
</TABLE>
* Note: If you are unable to create layers on the background, ensure that ImageEnView1.Layers[0].<A TIELayer.Selectable> = False.
<FM>Examples<FC>
// Single left click zoom-in image, single right click zoom-out image, click and
// drag scroll the image (if it is bigger than client area).
ImageEnView1.MouseInteract := [ miZoom, miScroll ];
// Allow users to create image layers. Prompt for an image file after selection
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAutoPromptForImage ];
ImageEnView1.MouseInteract := [ miCreateImageLayers ];
// Allow user to move and resize layers (allow multiple layer selection and ensure masks are moved with layers)
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAllowMultiSelect, loAutoSelectMask ];
ImageEnView1.MouseInteract := [ miMoveLayers, miResizeLayers ];
A selected text layer with resize grips:
<IMG help_images\text_Selected.gif>
// Allow user to rotate layers (allow multiple layer selection and ensure masks are moved with layers)
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAllowMultiSelect, loAutoSelectMask ];
ImageEnView1.MouseInteract := [ miRotateLayers ];
// Allow circular selections
ImageEnView1.MouseInteract := [ miSelectCircle ];
<IMG help_images\Selection.gif>
// Allow the user to create, size and select red arrows
ImageEnView1.MouseInteract := [ miCreateLineLayers, miMoveLayers, miResizeLayers ];
ImageEnView1.LayerDefaults.Clear();
ImageEnView1.LayerDefaults.Add( IELP_LineColor +'=clRed' );
ImageEnView1.LayerDefaults.Add( IELP_LineWidth +'=6' );
ImageEnView1.LayerDefaults.Add( IELP_LineShapeSize +'=20' );
ImageEnView1.LayerDefaults.Add( IELP_LineStartShape +'=1' );
ImageEnView1.LayerDefaults.Add( IELP_Rotate +'=235' );
<FM>See Also<FN>
- <A TImageEnView.SelectionOptions>
- <A TImageEnView.LayerDefaults>
!!}
TIEMouseInteractItems = (
miZoom, // Click-sx zoom-in / click-dx zoom-out
miScroll, // Hand navigation
miSelect, // Rectangular selection
miSelectPolygon, // Polygonal selection
miSelectCircle, // Circular selection
miSelectZoom, // Zoom in rectangle
miSelectMagicWand, // Magic Wand selection
miSelectLasso, // Polygonal selection
miMoveLayers, // Move layers
miResizeLayers, // Resize layers
miRotateLayers, // Rotate layers
miMovingScroll, // Smooth moving scroll
miCropTool, // Crop tool
miCreateImageLayers, // Create TIEImageLayer
miCreateShapeLayers, // Create TIEShapeLayer
miCreateLineLayers , // Create TIELineLayer
miCreatePolylineLayers, // Create TIEPolylineLayer
miCreateTextLayers // Create TIETextLayer
);
{!!
<FS>TIEMouseInteract
<FM>Declaration<FC>
type TIEMouseInteract = set of <A TIEMouseInteractItems>;
!!}
TIEMouseInteract = set of TIEMouseInteractItems;
{!!
<FS>TIEGestureOptions
<FM>Description<FN>
Options for ImageEn gesture support.
<FM>Methods and Properties<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Enabled></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Inertia></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Multiplier></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.SnapValues></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.SnapDelta></C> </R>
</TABLE>
!!}
TIEGestureOptions = class
private
fDisables: array of TIEGestureOptions;
fEnabled: boolean;
fInertia: boolean;
fMultiplier: double;
fSnapValues: boolean;
fSnapDelta: double;
procedure SetEnabled(Value: boolean);
procedure AddDisables(Value: TIEGestureOptions);
public
{!!
<FS>TIEGestureOptions.Enabled
<FM>Declaration<FC>
property Enabled: boolean;
<FM>Description<FN>
Enables/disables the gesture.
!!}
property Enabled: boolean read fEnabled write SetEnabled;
{!!
<FS>TIEGestureOptions.Inertia
<FM>Declaration<FC>
property Inertia: boolean;
<FM>Description<FN>
Whether the gesture supports "inertia". When inertia is enabled and a gesture is performed with sufficient speed then it will continue for a time even after the actual movement ends.
!!}
property Inertia: boolean read fInertia write fInertia;
{!!
<FS>TIEGestureOptions.Multiplier
<FM>Declaration<FC>
property Multiplier: double;
<FM>Description<FN>
Specifies a multiplier for the controlled viewer property.
!!}
property Multiplier: double read fMultiplier write fMultiplier;
{!!
<FS>TIEGestureOptions.SnapValues
<FM>Declaration<FC>
property SnapValues: boolean;
<FM>Description<FN>
If enabled, then values will change only in amounts specified by the <A TIEGestureOptions.SnapDelta>. For example, when performing a small rotation of less than a <A TIEGestureOptions.SnapDelta> of 90 degrees, it will "snap" to 90.
!!}
property SnapValues: boolean read fSnapValues write fSnapValues;
{!!
<FS>TIEGestureOptions.SnapDelta
<FM>Declaration<FC>
property SnapDelta: double;
<FM>Description<FN>
Specifies the amount to snap values by when <A TIEGestureOptions.SnapValues> is enabled.
!!}
property SnapDelta: double read fSnapDelta write fSnapDelta;
end;
{!!
<FS>TIEGesturePanOptions
<FM>Description<FN>
Specify options for the Zoom gesture.
Note: Inherites from the <A TIEGestureOptions> class.
<FM>Methods and Properties<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TIEGesturePanOptions.BoundingBox> <IMG help_images\Star.gif></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Enabled></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Inertia></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Multiplier></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGesturePanOptions.PanWithSingleFingerHorizontally></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGesturePanOptions.PanWithSingleFingerVertically></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.SnapValues></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.SnapDelta></C> </R>
</TABLE>
!!}
TIEGesturePanOptions = class(TIEGestureOptions)
private
fBoundingBox: TRect;
fPanWithSingleFingerVertically: Boolean;
fPanWithSingleFingerHorizontally: Boolean;
public
{!!
<FS>TIEGesturePanOptions.BoundingBox
<FM>Declaration<FC>
property BoundingBox: TRect;
<FM>Description<FN>
Contains the minimum/maximum values for the left, top and right and bottom.
!!}
property BoundingBox: TRect read fBoundingBox write fBoundingBox;
{!!
<FS>TIEGesturePanOptions.PanWithSingleFingerVertically
<FM>Declaration<FC>
PanWithSingleFingerVertically: Boolean;
<FM>Description<FN>
Enables/disables vertical pans with one finger.
Default: True
!!}
property PanWithSingleFingerVertically: Boolean read fPanWithSingleFingerVertically write fPanWithSingleFingerVertically;
{!!
<FS>TIEGesturePanOptions.PanWithSingleFingerHorizontally
<FM>Declaration<FC>
property PanWithSingleFingerHorizontally: Boolean;
<FM>Description<FN>
Enables/disables horizontal pans with one finger.
Default: True
!!}
property PanWithSingleFingerHorizontally: Boolean read fPanWithSingleFingerHorizontally write fPanWithSingleFingerHorizontally;
end;
{!!
<FS>TIEGestureZoomOptions
<FM>Description<FN>
Specifies options for the Zoom gesture.
Note: Inherites from the <A TIEGestureOptions> class.
<FM>Methods and Properties<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TIEGestureZoomOptions.Max> <IMG help_images\Star.gif></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureZoomOptions.Min> <IMG help_images\Star.gif></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Enabled></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Inertia></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Multiplier></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.SnapValues></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.SnapDelta></C> </R>
</TABLE>
!!}
TIEGestureZoomOptions = class(TIEGestureOptions)
private
fMax: double;
fMin: double;
public
{!!
<FS>TIEGestureZoomOptions.Max
<FM>Declaration<FC>
property Max: double;
<FM>Description<FN>
Specify the maximum allowed Zoom.
!!}
property Max: double read fMax write fMax;
{!!
<FS>TIEGestureZoomOptions.Min
<FM>Declaration<FC>
property Min: double;
<FM>Description<FN>
Specify the minimum allowed Zoom.
!!}
property Min: double read fMin write fMin;
end;
{!!
<FS>TIEGestureLayerMoveOptions
<FM>Description<FN>
Specifies options for the layer movement gesture.
Note: Inherites from the <A TIEGestureOptions> class.
<FM>Methods and Properties<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TIEGestureLayerMoveOptions.BoundingBox> <IMG help_images\Star.gif></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Enabled></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Inertia></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Multiplier></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.SnapValues></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.SnapDelta></C> </R>
</TABLE>
!!}
TIEGestureLayerMoveOptions = class(TIEGestureOptions)
private
fBoundingBox: TRect;
public
{!!
<FS>TIEGestureLayerMoveOptions.BoundingBox
<FM>Declaration<FC>
property BoundingBox: TRect;
<FM>Description<FN>
Contains the minimum/maximum values for the left, top and right and bottom.
!!}
property BoundingBox: TRect read fBoundingBox write fBoundingBox;
end;
{!!
<FS>TIEGestureLayerRotateOptions
<FM>Description<FN>
Specifies options for the layer rotation gesture.
Note: Inherites from the <A TIEGestureOptions> class.
<FM>Methods and Properties<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TIEGestureLayerRotateOptions.Min> <IMG help_images\Star.gif></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureLayerRotateOptions.Max> <IMG help_images\Star.gif></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Enabled></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Inertia></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.Multiplier></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.SnapValues></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEGestureOptions.SnapDelta></C> </R>
</TABLE>
!!}
TIEGestureLayerRotateOptions = class(TIEGestureOptions)
private
fMax: double;
fMin: double;
public
{!!
<FS>TIEGestureLayerRotateOptions.Max
<FM>Declaration<FC>
property Max: double;
<FM>Description<FN>
Specify the maximum allowed Zoom.
!!}
property Max: double read fMax write fMax;
{!!
<FS>TIEGestureLayerRotateOptions.Min
<FM>Declaration<FC>
property Min: double;
<FM>Description<FN>
Specify the minimum allowed Zoom.
!!}
property Min: double read fMin write fMin;
end;
{!!
<FS>TIEViewerGestures
<FM>Description<FN>
TImageEnView supports native Windows gestures, allowing pan, zoom, layer rotation and movement.
This class contains the properties to configure the gestures.
<FM>Methods and Properties<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TIEViewerGestures.Enabled></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEViewerGestures.LayerMove></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEViewerGestures.LayerRotate></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEViewerGestures.Pan></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIEViewerGestures.Zoom></C> </R>
</TABLE>
!!}
TIEViewerGestures = class
private
fPan: TIEGesturePanOptions;
fZoom: TIEGestureZoomOptions;
fLayerRotate: TIEGestureLayerRotateOptions;
fLayerMove: TIEGestureLayerMoveOptions;
function GetEnabled(): boolean;
public
constructor Create();
destructor Destroy(); override;
{!!
<FS>TIEViewerGestures.Enabled
<FM>Declaration<FC>
property Enabled: boolean; (Read-only)
<FM>Description<FN>
Returns true whenever at least one gesture is enabled.
!!}
property Enabled: boolean read GetEnabled;
{!!
<FS>TIEViewerGestures.Pan
<FM>Declaration<FC>
property Pan: <A TIEGesturePanOptions>;
<FM>Description<FN>
Allows enabling and configuration of the Pan (image scrolling) gesture. The Windows gesture to "Pan with inertia" is performed by "dragging 1 or 2 fingers".
Note: The <L TIEViewerGestures.Pan>Pan</L> and <L TIEViewerGestures.LayerMove>layer movement</L> gestures are mutually exclusive (i.e. you cannot use both).
!!}
property Pan: TIEGesturePanOptions read fPan;
{!!
<FS>TIEViewerGestures.Zoom
<FM>Declaration<FC>
property Zoom: <A TIEGestureZoomOptions>;
<FM>Description<FN>
Allows enabling and configuration of the Zoom gesture. The Windows gesture of "Zoom" is performed by "moving two fingers apart/towards each other".
!!}
property Zoom: TIEGestureZoomOptions read fZoom;
{!!
<FS>TIEViewerGestures.LayerRotate
<FM>Declaration<FC>
property LayerRotate: <A TIEGestureLayerRotateOptions>;
<FM>Description<FN>
Allows enabling and configuration of the Layer Rotation gesture. The Windows gesture of "Rotate" is performed by "moving two fingers in opposing directions or using one finger to pivot around another".
!!}
property LayerRotate: TIEGestureLayerRotateOptions read fLayerRotate;
{!!
<FS>TIEViewerGestures.LayerMove
<FM>Declaration<FC>
property LayerMove: <A TIEGestureLayerMoveOptions>;
<FM>Description<FN>
Allows enabling and configuration of the Layer Movement gesture. The Windows gesture of "Pan with inertia" is performed by "dragging 1 or 2 fingers".
Note: The <L TIEViewerGestures.Pan>Pan</L> and <L TIEViewerGestures.LayerMove>layer movement</L> gestures are mutually exclusive (i.e. you cannot use both).
!!}
property LayerMove: TIEGestureLayerMoveOptions read fLayerMove;
end;
{!!
<FS>TIENavigatorOptions
<FM>Declaration<FC>
TIENavigatorOptions = set of (ienoMouseWheelZoom, ienoMarkOuter, ienoDontAssignNavBitmap, ienoDontPaintSrcBitmap, ienoDontRefreshSrcIfNavNotFocused);
<FM>Description<FN>
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C><FC>ienoMouseWheelZoom<FN></C> <C>Zoom is possible with mouse-wheel</C> </R>
<R> <C><FC>ienoMarkOuter<FN></C> <C>Marks as grayed non visible area</C> </R>
<R> <C><FC>ienoDontAssignNavBitmap<FN></C> <C>Doesn't assign automatically navigator bitmap</C> </R>
<R> <C><FC>ienoDontPaintSrcBitmap<FN></C> <C>Doesn't repaint source image on moving nav rect</C> </R>
<R> <C><FC>ienoDontRefreshSrcIfNavNotFocused<FN></C> <C>Doesn't refresh main viewer when navigator isn't focused</C> </R>
</TABLE>
!!}
TIENavigatorOptions = set of (ienoMouseWheelZoom, ienoMarkOuter, ienoDontAssignNavBitmap, ienoDontPaintSrcBitmap, ienoDontRefreshSrcIfNavNotFocused);
{!!
<FS>TIESelectionBase
<FM>Declaration<FC>
}
TIESelectionBase = (
iesbClientArea, // Select coordinates over client area (as they appear in the current view)
iesbBitmap // Select coordinates over bitmap (as they relate to the bitmap regardless of view or zoom)
);
{!!}
{!!
<FS>TIESelectionOptions
<FM>Declaration<FC>
TIESelectionOptions = set of (
iesoAnimated,
iesoSizeable,
iesoMoveable,
iesoFilled,
iesoCutBorders,
iesoMarkOuter,
iesoCanScroll,
iesoSelectTranspLayers,
iesoRightButtonSelectLayers,
iesoRightButtonTerminatePolySelect,
iesoDisableOneClickDeselect,
iesoDisableNewSelection,
iesoShowCenter,
iesoAutoTerminatePolySelect,
iesoAllowMoveByKeyboard);
<FM>Description<FN>
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>iesoAnimated</C> <C>Enable animation of the selection</C> </R>
<R> <C>iesoSizeable</C> <C>Allow the user to resize the selection (with grips)</C> </R>
<R> <C>iesoMoveable</C> <C>Allow the user to move the selection (with grips)</C> </R>
<R> <C>iesoFilled</C> <C>The image within the selection is displayed with inverted colors</C> </R>
<R> <C>iesoCutBorders</C> <C>The user is allowed to drag the selection outside the borders (the selection borders will be cut)</C> </R>
<R> <C>iesoMarkOuter</C> <C>Areas of the image outside the selection are shown as grayed or with alpha blending (see <A TImageEnView.SetSelectionMarkOuterStyle>)</C> </R>
<R> <C>iesoCanScroll</C> <C>Automatically scroll the image when selecting outside the visible area</C> </R>
<R> <C>iesoSelectTranspLayers</C> <C>Allow transparent areas of a layer to be selectable</C> </R>
<R> <C>iesoRightButtonSelectLayers</C> <C>Allow selection of layers with the right button (other than left button)</C> </R>
<R> <C>iesoRightButtonTerminatePolySelect</C> <C>Allow <L TImageEnView.MouseInteract>polygon selection</L> to be cancelled with the right mouse button</C> </R>
<R> <C>iesoDisableOneClickDeselect</C> <C>Prevent the selection from being cleared when clicking outside the selection</C> </R>
<R> <C>iesoDisableNewSelection</C> <C>Once a selection has been made (programatically using <A TImageEnView.Select> or by the user using <A TImageEnView.MouseInteract>) then the user cannot make a new selection (though they can still move or resize the selection if iesoSizeable or iesoMoveable is specified)</C> </R>
<R> <C>iesoShowCenter</C> <C>A cross shows the center of the rectangle, circle or polygon while making a selection</C> </R>
<R> <C>iesoAutoTerminatePolySelect</C> <C><L TImageEnView.MouseInteract>Polygon selection</L> will be automatically cancelled when the user clicks close to the start of the selection (i.e. closes the polygon)</C> </R>
<R> <C>iesoAllowMoveByKeyboard</C> <C>Allow rectangular selections and layers to be moved by the cursor keys (Hold Ctrl key to resize selections. Hold Shift to speed up movement)</C> </R>
</TABLE>
!!}
TIESelectionOptions = set of (
iesoAnimated,
iesoSizeable,
iesoMoveable,
iesoFilled,
iesoCutBorders,
iesoMarkOuter,
iesoCanScroll,
iesoSelectTranspLayers,
iesoRightButtonSelectLayers,
iesoRightButtonTerminatePolySelect,
iesoDisableOneClickDeselect,
iesoDisableNewSelection,
iesoShowCenter,
iesoAutoTerminatePolySelect,
iesoAllowMoveByKeyboard
);
{!!
<FS>TIESelOp
<FM>Declaration<FC>
TIESelOp = (iespReplace, iespAdd);
<FM>Description<FN>
iespReplace: replaces all previous selections.
iespAdd: adds a new selection.
!!}
TIESelOp = (iespReplace, iespAdd);
{!!
<FS>TIEMagicWandMode
<FM>Declaration<FC>
TIEMagicWandMode=(iewInclusive, iewExclusive, iewGlobal);
<FM>Description<FN>
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>iewInclusive</C> <C>the selection is a closed polygon</C> </R>
<R> <C>iewExclusive</C> <C>the selection includes only points that match the initial pixel, similar to a flood fill</C> </R>
<R> <C>iewGlobal</C> <C>the selection includes all points that match the initial pixel, examining all pixels of the image</C> </R>
</TABLE>
!!}
TIEMagicWandMode = (iewInclusive, iewExclusive, iewGlobal);
{!!
<FS>TIELayerEvent
<FM>Declaration<FC>
TIELayerEvent = (ielSelected, ielMoved, ielResized, ielMoving, ielResizing, ielRotating, ielRotated, ielBeginResizing, ielBeginMoving, ielDeselected, ielEdited, ielBeginCreating, ielCreating, ielCreated, ielAction);
<FM>Description<FN>
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>ielSelected</C> <C>User selected the layer</C> </R>
<R> <C>ielMoved</C> <C>User moved the layer</C> </R>
<R> <C>ielResized</C> <C>User resized the layer</C> </R>
<R> <C>ielMoving</C> <C>User is moving the layer</C> </R>
<R> <C>ielResizing</C> <C>User is resizing the layer</C> </R>
<R> <C>ielRotating</C> <C>User is rotating the layer</C> </R>
<R> <C>ielRotated</C> <C>User rotated the layer</C> </R>
<R> <C>ielBeginResizing</C> <C>User began to resize the layer (click down)</C> </R>
<R> <C>ielBeginMoving</C> <C>User began to move the layer (click down)</C> </R>
<R> <C>ielDeselected</C> <C>User deselected the layer</C> </R>
<R> <C>ielEdited</C> <C>User edited the text of a layer</C> </R>
<R> <C>ielBeginMoving</C> <C>User began creating a layer (click down)</C> </R>
<R> <C>ielCreating</C> <C>User is creating a layer</C> </R>
<R> <C>ielCreated</C> <C>User had created a new layer</C> </R>
<R> <C>ielAction</C> <C>User has used an <L TImageEnView Actions>ImageEn Action</L> that has changed the layers</C> </R>
</TABLE>
!!}
TIELayerEvent = (ielSelected, ielMoved, ielResized, ielMoving, ielResizing, ielRotating, ielRotated, ielBeginResizing, ielBeginMoving, ielDeselected, ielEdited, ielBeginCreating, ielCreating, ielCreated, ielAction);
{!!
<FS>TIELayerNotify
<FM>Declaration<FC>
TIELayerNotify = procedure(Sender: TObject; layer: integer; event: <A TIELayerEvent>) of object;
<FM>Description<FN>
<FC>layer<FN> is the layer index that is selected, moved or resized.
<FC>event<FN> specifies the event type.
Notes:
- This event only occurs once for each change, even if <L TImageEnView.LayerOptions>multiple layers are selected</L>. You should review the list of selected layers to determine which changed
- To avoid incompatibility, events of ielBeginCreating and ielCreating do NOT occur unless IEIncludeDeprecatedInV6 has been undefined in ie.inc. If available, <FC>layer<FN> will return -1
<FM>Example<FC>
// Log all resizing of layers
procedure TfrmMain.IEView1LayerNotify(Sender: TObject; layer: integer; event: TIELayerEvent);
var
ALayer: TIELayer;
i: Integer;
sChangedLayers: string;
begin
if event = ielResized then
begin
sChangedLayers := '';
for i := 0 to IEView1.LayersCount - 1 do
begin
ALayer := IEView1.Layers[ I ];
if ( ALayer.Locked = False ) and ALayer.Selected then
sChangedLayers := sChangedLayers + IntToStr( i ) +',';
end;
if sChangedLayers <> '' then
begin
SetLength( sChangedLayers, Length( sChangedLayers ) - 1 ); // Remove final comma
memLog.Lines.Add( 'Layers Resized: ' + sChangedLayers );
end;
end;
end;
!!}
TIELayerNotify = procedure(Sender: TObject; layer: integer; event: TIELayerEvent) of object;
{!!
<FS>TIENewLayerEvent
<FM>Declaration<FC>
TIENewLayerEvent = procedure(Sender: TObject; LayerIdx: integer; LayerKind: <A TIELayerKind>) of object;
<FM>Description<FN>
Occurs whenever a layer is added. It is useful to assign default properties to the layer.
Both programmatic methods (e.g. using <A TImageEnView.LayersAdd>) and user methods (e.g. <L TImageEnView.MouseInteract>using miCreateShapeLayers</L>) trigger this event.
<FC>LayerIdx<FN> is the index of the new layer.
<FC>LayerKind<FN> is the kind of layer that was added.
Note: <A TImageEnView.CurrentLayer> will represent the new layer
<FM>Examples<FC>
procedure Tfmain.ImageEnView1NewLayer(Sender: TObject; LayerIdx: Integer; LayerKind: TIELayerKind);
begin
// Assign default properties for new objects
case LayerKind of
ielkPolyline : TIEPolylineLayer( ImageEnView1.CurrentLayer ).SetPoints( iesExplosion, True );
ielkText : TIETextLayer( ImageEnView1.CurrentLayer ).Text := 'Double-click to edit text';
end;
end;
// Make new shape layer a red star
procedure TfrmMain.ImageEnView1NewLayer(Sender: TObject; LayerIdx: integer; LayerKind: TIELayerKind);
begin
if LayerKind = ielkShape then
begin
TIEShapeLayer( ImageEnView1.CurrentLayer ).Shape := iesStar5;
TIEShapeLayer( ImageEnView1.CurrentLayer ).FillColor := clRed;
end;
end;
<FM>See Also<FN>
- <A TImageEnView.OnNewLayer>
!!}
TIENewLayerEvent = procedure(Sender: TObject; LayerIdx: integer; LayerKind: TIELayerKind) of object;
{!!
<FS>TIELayerMoveSizeEvent
<FM>Declaration<FC>
TIELayerMoveSizeEvent = procedure(Sender: TObject; layer: integer; event: <A TIELayerEvent>; var PosX, PosY, Width, Height: Double) of object;
<FM>Description<FN>
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>layer</C> <C>The layer index that is moved or resized (it will be -1 for ielCreating).</C> </R>
<R> <C>event</C> <C>The event type (will be ielMoving, ielResizing or ielCreating).</C> </R>
<R> <C>PosX, PosY</C> <C>The new left and top position of the layer</C> </R>
<R> <C>Width, Height</C> <C>The new width and height of the layer</C> </R>
</TABLE>
<FM>Example<FC>
// Force all layers to be created at 200 x 200
procedure TfrmMain.ImageEnView1MoveSizeLayer(Sender: TObject; layer: integer; event: TIELayerEvent; var PosX, PosY, Width, Height: Double);
begin
if event = ielCreating then
begin
Width := 200;
Height := 200;
end;
end;
// Force all layers to be centered on the image horizon
procedure TfMain.ImageEnView1MoveSizeLayer(Sender: TObject; layer: integer; event: TIELayerEvent; var PosX, PosY, Width, Height: Double);
begin
PosY := ( ImageEnView1.Layers[0].Height - Height ) / 2;
end;
!!}
TIELayerMoveSizeEvent = procedure(Sender: TObject; layer: integer; event: TIELayerEvent; var PosX, PosY, Width, Height: Double) of object;
{!!
<FS>TIEVirtualKeyEvent
<FM>Declaration<FC>
TIEVirtualKeyEvent = procedure(Sender: TObject; VirtualKey: Dword; KeyData: Dword) of object;
<FM>Description<FN>
Event type for <A TImageEnView.OnVirtualKey> event.
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>VirtualKey</C> <C>Specifies the virtual key code (the same as wParam of WM_KEYWODN, WM_KEYUP, WM_SYSKEYDOWN and WM_SYSKEYUP)</C> </R>
<R> <C>KeyData</C> <C>Specifies additional info about the key (the same as lParam of WM_KEYWODN, WM_KEYUP, WM_SYSKEYDOWN and WM_SYSKEYUP)</C> </R>
<R> <C>KeyDown</C> <C>This is true on keydown, false on keyup</C> </R>
</TABLE>
!!}
TIEVirtualKeyEvent = procedure(Sender: TObject; VirtualKey: Dword; KeyData: Dword; KeyDown: Boolean) of object;
{!!
<FS>TIESetCursorEvent
<FM>Declaration<FC>
TIESetCursorEvent = procedure(Sender: TObject; var Cursor: TCursor) of object;
<FM>Description<FN>
Used by the <A TImageEnView.OnSetCursor> event.
!!}
TIESetCursorEvent = procedure(Sender: TObject; var Cursor: TCursor) of object;
{!!
<FS>TIEGestureFlags
<FM>Declaration<FC>
}
TIEGestureFlags = set of (iegfBegin, iegfInertia, iegfEnd);
{!!}
{!!
<FS>TIEGestureID
<FM>Declaration<FC>
}
TIEGestureID = (iegiBegin, iegiEnd, iegiZoom, iegiPan, iegiRotate, iegiTwoFingerTap, iegiPressAndTap);
{!!}
{!!
<FS>TIEImageEnGestureEvent
<FM>Declaration<FC>
TIEImageEnGestureEvent = procedure(Sender: TObject; Flags: <A TIEGestureFlags>; ID: <A TIEGestureID>; Location: TSmallPoint; Value: integer; InertiaX: integer; InertiaY: integer; PressAndTapDistance: integer; var Handled: boolean) of object;
<FM>Description<FN>
Used by the <A TImageEnView.OnImageEnGesture> event.
!!}
TIEImageEnGestureEvent = procedure(Sender: TObject; Flags: TIEGestureFlags; ID: TIEGestureID; Location: TSmallPoint; Value: integer; InertiaX: integer; InertiaY: integer; PressAndTapDistance: integer; var Handled: boolean) of object;
{!!
<FS>TIERSOptions
<FM>Declaration<FC>
TIERSOptions = (iersNone, iersMoveToAdapt, iersSyncLayers);
<FM>Description<FN>
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>iersNone</C> <C>no adaption</C> </R>
<R> <C>iersMoveToAdapt</C> <C>move the selection to fit inside the new size</C> </R>
<R> <C>iersSyncLayers</C> <C>used to maintain the selection in the same position among layers</C> </R>
</TABLE>
!!}
TIERSOptions = (iersNone, iersMoveToAdapt, iersSyncLayers);
{!!
<FS>TIESoftCropMode
<FM>Declaration<FC>
TIESoftCropMode = (iesfNone, iesfAlphaBlend, iesfGrid, iesfAdd);
<FM>Description<FN>
Portion of layers that are outside the background layer are shown as:
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>iesfNone</C> <C>Normal</C> </R>
<R> <C>iesfAlphaBlend</C> <C>Partially transparent (to the level specified by <A TImageEnView.SoftCropValue>)</C> </R>
<R> <C>iesfGrid</C> <C>Grayed (Grid matrix pattern)</C> </R>
<R> <C>iesfAdd</C> <C>Color shifted/washed out (to the level specified by <A TImageEnView.SoftCropValue>)</C> </R>
</TABLE>
!!}
TIESoftCropMode = (iesfNone, iesfAlphaBlend, iesfGrid, iesfAdd);
{!!
<FS>TIEGripShape
<FM>Declaration<FC>
}
TIEGripShape = (iegsBox, iegsCircle);
{!!}
{!!
<FS>TIEUpdateReason
<FM>Declaration<FC>
}
TIEUpdateReason = (ieurDefault, ieurScrolled, ieurZoomed, ieurSelectionChanged, ieurComponentStuffChanged);
{!!}
{!!
<FS>TIELayersResizeAspectRatio
<FM>Declaration<FC>
TIELayersResizeAspectRatio = (iearDisabled, iearALTKey, iearAlways, iearAlwaysOnCornerGrip, iearLayerDefaultOnCornerGrip);
<FM>Description<FN>
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>iearDisabled</C> <C>Doesn't maintain the aspect ratio even when pressing ALT or setting <A TImageEnView.ForceALTKey>)</C> </R>
<R> <C>iearALTKey</C> <C>Maintains the aspect ratio only when pressing ALT or setting <A TImageEnView.ForceALTKey></C> </R>
<R> <C>iearAlways</C> <C>The aspect ratio is always maintained (using any grips)</C> </R>
<R> <C>iearAlwaysOnCornerGrip</C> <C>The aspect ratio is always maintained if the corner grips are used</C> </R>
<R> <C>iearLayerDefaultOnCornerGrip</C> <C>If the layer has a <L TIELayer.PreferredAspectRatio>preferred aspect ratio</L> then dragging the corner grip will maintain the aspect ratio, for side grips and other layer types the aspect ratio is not maintained</C> </R>
</TABLE>
!!}
TIELayersResizeAspectRatio = (iearDisabled, iearALTKey, iearAlways, iearAlwaysOnCornerGrip, iearLayerDefaultOnCornerGrip);
TIECropToolInteraction = class; // forward declaration
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TImageEnView
{!!
<FS>TImageEnView
<FM>Declaration<FC>
TImageEnView = class(<A TIEView>);
<FM>Description<FN>
TImageEnView is an image container, viewer and editor. It supports zooming, scrolling, selections and layers.
TImageEnView encapsulates a <A TImageEnIO> (<A TImageEnView.IO> property) for loading/saving/acquisition and a <A TImageEnProc> (<A TImageEnView.Proc> property) component for image editing/processing (you do not need to add extra TImageEnIO and TImageEnProc components to your form).
For rapid UI development a full set of <L TImageEnView Actions>actions</L> is also available.
<IMG help_images\IEView_Component.gif>
<FM>Example<FC>
// Setup
with ImageEnView1 do
begin
LegacyBitmap := False; // Support bigger files and multithreading
AutoShrink := True; // Make the image fit within the window
BorderStyle := bsNone; // Normally don't require a 3D border
ZoomFilter := rfFastLinear; // Use better quality drawing when the image is not 100%, Use rfFastLinear for best speed or rfLanczos3 for best quality
MouseInteract := [miSelect]; // What action should the mouse do?
end;
// Display an image
ImageEnView1.IO.LoadFromFile('C:\MyImage.jpg');
// Rotate the image
ImageEnView1.Proc.Rotate(270, True);
<FM>Methods and Properties<FN>
<FI>Display<FN>
<TABLE2>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.AutoShrink></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.AutoStretch></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.Center></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.CenterImage></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.DisplayGridKind></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.DisplayGridLyr></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.DelayTimer></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.DelayZoomFilter></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.DelayZoomTime></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ExtentX></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ExtentY></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.Fit></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.FitToHeight></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.FitToWidth></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.GetIdealZoom></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.GetMaxViewXY></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.GetRenderRectangles></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.IdealComponentHeight></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.IdealComponentWidth></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.IdealImageHeight></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.IdealImageWidth></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ImageHorizAlignment></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ImageVertAlignment></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LockPaint></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LockPaintCount></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LockUpdate></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LockUpdateCount></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.OffScreenPaint></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.OffsetX></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.OffsetY></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.PaintRect></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.RulerParams></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetViewXY></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetViewXYSmooth></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetZoomSmooth></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.ShowRulers></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SmoothScrollValue></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SmoothZoomValue></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.Stretch></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.UnLockPaint></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.UnLockUpdate></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.UnLockUpdateEx></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.Update></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.UpdateNoPaint></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.UpdateReason></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.UpdateRect></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ViewX></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ViewY></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.VisibleBitmapRect></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.XBmp2Scr></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.XScr2Bmp></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.YBmp2Scr></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.YScr2Bmp></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.Zoom></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.ZoomAt></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.ZoomIn></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.ZoomFilter></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.ZoomOut></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.ZoomSelection></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ZoomX></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ZoomY></C> </R>
</TABLE>
<FI>Background<FN>
<TABLE2>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.BackgroundStyle></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.Background></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.GradientEndColor></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetChessboardStyle></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.WallpaperStyle></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.Wallpaper></C> </R>
</TABLE>
<FI>Scrollbars<FN>
<TABLE2>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.FlatScrollBars></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.HScrollBarParams></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ScrollBarsAlwaysVisible></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.ScrollBars></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.VScrollBarParams></C> </R>
</TABLE>
<FI>Image Processing<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.Proc></C> </R>
</TABLE>
<FI>Input/Output<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.IO></C> </R>
</TABLE>
<FI>Bitmap<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.Bitmap></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.Blank></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.ChangeResolution></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.Clear></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.ClearAll></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.CopyFromPolygon></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.CopyToPolygon></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.DrawTo></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.IEBitmap></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.IsEmpty></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.IsEmpty2></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.LegacyBitmap></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetExternalBitmap></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetSelectedPixelsColor></C> </R>
</TABLE>
<FI>User Actions<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.CropToolInteraction></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.EnableInteractionHints></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ForceALTkey> (Lock Aspect Ratio)</C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.Gestures></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.MagicWandMaxFilter></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.MagicWandMode></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.MagicWandTolerance></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.MouseInteract></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.MouseScrollRate></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.MouseWheelParams></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetInteractionHint></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.ZoomSelectionAspectRatio></C> </R>
</TABLE>
<FI>Selections<FN>
<TABLE2>
<R> <C_IMG_METHOD> <C><A TImageEnView.AddSelBreak></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.AddSelPoint></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.ApplyBitmapToSelection></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.AssignSelTo></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.CopySelectionToBitmap></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.DelayDisplaySelection></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.DelLastSelPoint></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.Deselect></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.DiscardSavedSelection></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.EnableShiftKey> (Multiple selections)</C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.EndSelect></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.GetSelectionGripStyle></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.InvertSelection></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.IsPointInsideSelection></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LoadSelectionFromFile></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LoadSelectionFromStream></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.MakeSelectionFeather></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.MergeSelectionFromFile></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.MergeSelectionFromStream></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.MoveSelection></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.PolySel></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.PolySelCount></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.PolySelPoints></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.RestoreSelection></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SavedSelectionsCount></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SaveSelection></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SaveSelectionToFile></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SaveSelectionToStream></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelColor1></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelColor2></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.Select></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SelectColors></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SelectCustom></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SelectNonAlpha></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.Selected></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelectedRect></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SelectEllipse></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelectionAbsHeight></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelectionAbsWidth></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelectionAspectRatio></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.SelectionBase></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelectionGridHeight></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelectionGridSize></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelectionGridWidth></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelectionIntensity></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelectionMask></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelectionMaskDepth></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.SelectionOptions></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SelectMagicWand></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SelectRoundRect></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelX1></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelX2></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelY1></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SelY2></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetSelectionGripStyle></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetSelectionMarkOuterStyle></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.VisibleSelection></C> </R>
</TABLE>
<FI>Alpha Channel (Transparency)<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.AlphaChannel></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.CopyToBitmapWithAlpha></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.EnableAlphaChannel></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.HasAlphaChannel></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.RemoveAlphaChannel></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetAlphaRangePixelsColor></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetSelectedAreaAlpha></C> </R>
</TABLE>
<FI>Layers<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.CurrentLayer></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.FindLayerAt></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayerDefaults></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayerOptions></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.Layers></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersAdd></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersAlign></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersClear></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersCaching></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersCancelEditor></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersCopyToAlpha></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersConvertToImageLayers></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersCount></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersCreateFromAlpha></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersCreateFromClipboard></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersCreateFromEdge></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersCreateFromFile></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersCreateFromSelection></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersCropBackground></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersCropped></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersCurrent></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersDeselectAll></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersDrawBox></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersDrawTo></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersFastDrawing></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersFixBorders></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersFixRotations></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersFixSizes></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersGroup></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersImport></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersInsert></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersMerge></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersMergeAll></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersMergeFilter></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersMergeTo></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersMove> (Arrange)</C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersRect></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersRepositionAll></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersRemove></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersResizeAspectRatio></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersRotateAll></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersRotateStep></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersRotationAntialias></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersRotationFilter></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersRotationUseFilterOnPreview></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersSaveMergedTo></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersSelCount></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersSelectAll></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.LayersSelectConstrains></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersSetProperties></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersSizeAll></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LayersUngroup></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.MaxLayerHeight></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.MaxLayerWidth></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetLayersBoxStyle></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetLayersGripStyle></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SoftCrop></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.SoftCropValue></C> </R>
</TABLE>
Also: <L TImageEnIO.LoadFromFileIEN>Layer Loading</L> and <L TImageEnIO.SaveToFileIEN>Layer Saving</L>
<FI>Animations and Transitions<FN>
<TABLE2>
<R> <C_IMG_METHOD> <C><A TImageEnView.AbortTransition></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.PrepareTransition></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.RunTransition></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.TransitionRunning></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.TransitionTiming></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.Playing></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.PlayLoop></C> </R>
</TABLE>
<FI>Navigator<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.IsNavigator></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SetNavigator></C> </R>
</TABLE>
<FI>Animated Polygons<FN>
<TABLE2>
<R> <C_IMG_METHOD> <C><A TImageEnView.AnimPolygonAddPt></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.AnimPolygonClear></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.AnimPolygonDel></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.AnimPolygonNew></C> </R>
</TABLE>
<FI>Other<FN>
<TABLE2>
<R> <C_IMG_METHOD> <C><A TImageEnView.Assign></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.AutoCursors></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.Cursor></C> </R>
<R> <C_IMG_METHOD> <C><A TIEView.GetCanvas></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.BackBuffer></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.BeginPostFrames></C> </R>
<R> <C_IMG_PUBLISHED> <C><A TImageEnView.DrawVersion></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.EndPostFrames></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.GetGripAt></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.HighlightedPixel></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.LoadState></C> </R>
<R> <C_IMG_PROPERTY> <C><A TImageEnView.Modified></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.MoveContentTo></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.ResetState></C> </R>
<R> <C_IMG_METHOD> <C><A TImageEnView.SaveState></C> </R>
</TABLE>
<FM>Events<FN>
<TABLE2>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnActivateTextEditor></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnAcquireBitmap></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnBeforeSelectionChange></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnDeactivateTextEditor></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnDrawBackBuffer></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnDrawBackground></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnDrawCanvas></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnDrawLayer></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnDrawLayerBox></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnDrawLayerGrip></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnDrawPolygon></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnDShowEvent></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnDShowNewFrame></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnFinishSmoothTask></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnFinishWork></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnImageChange></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnImageEnGesture></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnLayerNotify></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnLayerMoveSize></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnLayerSelectionChange></C> </R>
<R> <C_IMG_EVENT> <C><A TIEView.OnMouseEnter></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnMediaFoundationNotify></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnMouseInResizingGrip></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnMouseInSel></C> </R>
<R> <C_IMG_EVENT> <C><A TIEView.OnMouseLeave></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnNewLayer></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnPaint></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnProgress></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnRulerClick></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnRulerGripClick></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnRulerGripDblClick></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnRulerGripPosChange></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnSaveUndo></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnSelectionChange></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnSelectionChanging></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnSetCursor></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnSpecialKey></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnTextEditorKeyDown></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnTransitionPaint></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnTransitionStep></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnTransitionStop></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnViewChange></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnViewChanging></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnVirtualKey></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnZoomIn></C> </R>
<R> <C_IMG_EVENT> <C><A TImageEnView.OnZoomOut></C> </R>
</TABLE>
!!}
{$ifdef IEHASPLATFORMATTRIBUTE}
[ComponentPlatformsAttribute(pidWin32 or pidWin64)]
{$endif}
TImageEnView = class(TIEView)
private
fLegacyBitmap: boolean; // false=use TIEBitmap, true=use TBitmap
fUseDrawDibDraw: boolean; // for same behavior of old versions set it to True
fLCursor: TCursor; // this is default cursor
fCursor: TCursor; // this is current cursor
fWasScreenCursor : TCursor; // Store the screen's cursor for special handling on mouse down
fOnImageChange: TNotifyEvent;
fOnViewChange: TViewChangeEvent;
fOnImageEnGesture: TIEImageEnGestureEvent;
fOnFinishSmoothTask: TIEFinishSmoothTaskEvent;
fOnViewChanging: TViewChangingEvent;
fImageHorizAlignment: TIEHAlign;
fImageVertAlignment: TIEVAlign;
fZoomFilter: TResampleFilter;
fActualZoomFilter: TResampleFilter; // ZoomFilter to use to display the current content
fDelayZoomFilter: boolean; // if true enables fStable, delay zoom filter
fBackgroundStyle: TIEBackgroundStyle;
fMagicWandMaxFilter: boolean; // if true apply a max filter to the selection
fMagicWandTolerance: integer; // 0..255 tolerance
fMagicWandMode: TIEMagicWandMode;
fRXScroll, fRYScroll: double;
fDisplayGridKind: TIEGridKind;
fDisplayGridLyr: integer; // layer where to display the grid (-1 = current, >= 0 specific layer)
fHighlightedPixel: TPoint;
fVScrollBarVisible, fHScrollBarVisible: boolean;
fAniCounter: integer;
fOnPaint: TNotifyEvent;
fOnMouseInResizingGrip: TIEMouseInResizingGripEvent;
fOnZoomIn, fOnZoomOut: TIEZoomEvent;
fDelayDisplaySelection: boolean;
fOnProgress: TIEProgressEvent;
fOnFinishWork: TNotifyEvent;
fOnAcquireBitmap: TIEAcquireBitmapEvent;
fFullUpdateRequest: boolean; // true when Paint requires to repaint full image without cliprect, Paint event set to False
fBlockPaint: boolean;
fRectMoving: boolean; // true if we are moving a rectangle
fDrawVersion: boolean;
fOnDrawBackBuffer: TNotifyEvent;
fOnLayerNotify: TIELayerNotify;
fOnLayerMoveSize: TIELayerMoveSizeEvent;
fOnLayerSelectionChange: TNotifyEvent;
fOnNewLayer: TIENewLayerEvent;
fSelectionAspectRatio: double; // -1 auto aspect , 0 = absolute (fSelectionAbsWidth, fSelectionAbsHeight), >0 specify aspect
fSelectionAbsWidth: integer;
fSelectionAbsHeight: integer;
fOnSpecialKey: TIESpecialKeyEvent;
fNavigator: TImageEnView;
fNavigatorOptions: TIENavigatorOptions;
fNavigatorInside: Boolean;
fIsNavigator: Boolean;
fNavigatorBackBuffer: TIEBitmap;
fNavigatorActualRect: TRect;
fUpdateInsideUpdate: Boolean; // true when Update call itself
fInsideUpdate: Boolean; // true when it is inside Update
fDelayTimer: Integer;
fLyrResizingClientAreaBox: TRect; // ClientAreaBox when we begon resizing a layer
fLyrResizingRotatedBox: array [0..3] of TPoint; // box coordinates when we begon resizing a rotated layer
fZoomSelectionAspectRatio: Boolean;
fMouseScrollRate: Double;
fOnDrawPolygon: TIEOnDrawPolygon;
fLayersRotationFilter: TIEAntialiasMode;
fLayersRotationAntialias: Boolean;
fLayersRotationUseFilterOnPreview: Boolean;
fUpdate_MaskCache: Integer; // -1=update all, >-1 update only specified layer
fLayersSelectConstrains: Boolean;
fLayersResizeAspectRatio: TIELayersResizeAspectRatio;
fLayersResizingAR: Double; // used when resizing layers
fLayersFastDrawing: TIEFastDrawing;
fLayersFastOutput: Boolean; // True if merge and other output operations should ignore anti-alias and other quality settings for layers
fLayersCropped: boolean;
fSelectionGridWidth: Integer;
fSelectionGridHeight: Integer;
fMarkOuterAlpha: Integer;
fMarkOuterColor: TColor;
fOnSetCursor: TIESetCursorEvent;
//
fLutLastZoomX: double; // last zoom used by CreateCoordConvLUT
fLutLastZoomY: double; // last zoom used by CreateCoordConvLUT
fLutLastFRX: integer; // last frx used by CreateCoordConvLUT
fLutLastFRY: integer; // last fry used by CreateCoordConvLUT
fLutLastMaxLayerWidth: integer; // fMaxLayerWidth used by CreateCoordConvLUT
fLutLastMaxLayerHeight: integer; // fMaxLayerHeight used by CreateCoordConvLUT
// handscroll
fHSVX1, fHSVY1: integer; // view in mouse down
//
fo1x, fo1y, fo2x, fo2y: integer;
frx, fry: integer;
//
fBitmapWidth, fBitmapHeight: integer; // monitoring bitmap size
// transition effects
fTransition: TIETransitionEffects; // effect engine
fTransitionEffect: TIETransitionType; // transition type
fTransitionDuration: integer; // transition duration ms
fMinBitmapSize: Integer; // min bitmap size to make it displyed
//
{$IFDEF IEINCLUDEDIRECTSHOW}
fOnDShowNewFrame: TNotifyEvent;
fOnDShowEvent: TNotifyEvent;
{$ENDIF}
{$IFDEF IEINCLUDEMEDIAFOUNDATION}
fOnMediaFoundationNotify: TIEMediaFoundationNotifyEvent;
{$ENDIF}
fFlatScrollBars: Boolean;
fOnDrawBackground: TIEOnDrawBackground;
fOnDrawCanvas: TIEOnDrawCanvas;
fOldHandle: THandle;
fOnDrawLayer: TIEDrawLayerEvent;
fOnDrawLayerBox: TIEDrawLayerBoxEvent;
fOnDrawLayerGrip: TIEDrawLayerGrip;
fVisibleSelection: Boolean;
fOffscreenPaint: Boolean;
fFirstTimeSetCursor: Boolean;
fLayersRotateStep: Integer;
fLayersMergeFilter: TResamplefilter;
// Wallpaper
fWallpaper: TPicture;
fWallpaperStyle: TIEWallpaperStyle;
// setpixel replacement (due the Vista setpixel bug)
fDrawPixelBitmap: TBitmap;
fDrawPixelPtr: PRGB;
// softcrop
fSoftCrop: TIESoftCropMode;
fSoftCropValue: Integer;
// hints
fEnableInteractionHints: Boolean;
fInteractionHint: String;
fInteractionHintX, fInteractionHintY: Integer;
fInteractionHintMinText: String;
// PostFrames support fields
fPostFrames: TList; // a list of TIEPostFramesTarget objects
// Play
fPlaying: boolean; // true=play actived
fPlayTimer: integer; // timer for playback of animated files (0=not allocated)
fPlayLoop: boolean; // when True executes in loop
fNeedUpdateLiveBackground : Boolean; // True when fLiveBackground needs to be filled
fLiveBackground : TIEBitmap; // Show a background/wallpaper based on the current image
// User interactions
fUserInteractions: TObjectList;
// Rulers
fShowRulers: TRulerDirs;
fRulerParams: TIEViewRulerParams;
// Layer editing
fEditingLayer: Integer; // the current layer for which a text editor is active
fTextEditor: TIEEdit; // Component for editing the text of a TIETextLayer or TIELineLayer
fOnTextEditorKeyDown: TKeyEvent;
fOnActivateTextEditor: TIETextEditorEvent;
fOnDeactivateTextEditor: TIETextEditorEvent;
{$IFDEF UNITTESTING}
fDebugUpdatedCount: Integer; // test for unexpected calls to Update();
{$ENDIF}
fNextLayerPosition: Integer; // The next PosX and PosY for an added layer
fModified: Boolean; // Returns true if the image or layers have changed since loading
procedure RemovePostFrames(index: Integer);
//
procedure AniPolyFunc(Sender: TObject; UseLockPaint: boolean);
procedure TimerEvent(Sender: TObject);
procedure SetViewX(v: integer);
procedure SetViewY(v: integer);
procedure SetZoom(v: double);
procedure SetZoomX(v: double);
procedure SetZoomY(v: double);
function GetSelX1: integer;
function GetSelY1: integer;
function GetSelX2: integer;
function GetSelY2: integer;
procedure SetCenter(value: boolean);
function GetCenter(): Boolean;
procedure SetImageHorizAlignment(value: TIEHAlign);
procedure SetImageVertAlignment(value: TIEVAlign);
function GetMouseInteract: TIEMouseInteract;
function GetCropToolInteraction: TIECropToolInteraction;
procedure SetBackgroundStyle(v: TIEBackgroundStyle);
procedure DoSelectionChange;
procedure DoSelectionChanging;
procedure DoBeforeSelectionChange;
procedure DoMouseInResizingGrip(Grip: TIEGrip);
procedure DoMouseInSel;
procedure DoZoomIn(var NewZoom: double);
procedure DoZoomOut(var NewZoom: double);
procedure SetSelectionOptions(v: TIESelectionOptions);
function GetSelectedRect(): TIERectangle;
function GetResizingGrip(x, y: integer; Shift: TShiftState): TIEGrip;
function GetMovingGrip(x, y: integer; Shift: TShiftState): integer;
function SelectResizeEx(grip: TIEGrip; newx, newy: integer; aspectratio: boolean): TIEGrip;
function SelectMoveEx(fMoving, ox, oy: integer; cutBorders: Boolean): integer;
function GetScrollBarsAlwaysVisible: boolean;
procedure SetScrollBarsAlwaysVisible(v: boolean);
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 6.2.2 (8/2/2016)
function GetDisplayGrid(): boolean;
procedure SetDisplayGrid(v: boolean);
{$endif}
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 7.0.0 (21/2/2017)
function GetAutoFixRotationBorders(): boolean;
procedure SetAutoFixRotationBorders(v: boolean);
{$endif}
procedure SetDisplayGridKind(v: TIEGridKind);
procedure SetDisplayGridLyr(v: integer);
function GetTransitionRunning: boolean;
function GetLayersCount: integer;
procedure SetLayersCaching(v: integer);
function GetLayer(idx: integer): TIELayer;
procedure PaintSelection(OutBitmap: TBitmap);
procedure PaintPixelGrid(OutBitmap: TBitmap);
procedure PaintGuideLines(Canvas: TCanvas);
procedure SetCursor(Value: TCursor);
procedure SetDelayTimer(Value: integer);
function GetDelayTimer: integer;
procedure SetGradientEndColor(Value: TColor);
procedure DoDrawVersion;
procedure SetDrawVersion(v: boolean);
procedure SetSelectionMaskDepth(value: integer);
procedure MouseResizeLayer(clientlx, cliently: integer; AltPressed: Boolean);
{$IFDEF IEINCLUDEDIRECTSHOW}
procedure DShowNewFrame(var Message: TMessage); message IEM_NEWFRAME;
procedure DShowEvent(var Message: TMessage); message IEM_EVENT;
{$ENDIF}
{$IFDEF IEINCLUDEMEDIAFOUNDATION}
procedure MediaFoundationNotify(var Message: TMessage); message IEM_MEDIAFOUNDATION;
{$ENDIF}
procedure MouseSelectCircle(x, y: integer; Shift: TShiftState);
procedure MouseSelectRectangle(x, y: integer; Shift: TShiftState);
procedure SetSelectionAspectRatio(value: Double);
function GetSavedSelectionsCount: Integer;
{$ifdef IEINCLUDEFLATSB}
procedure SetFlatScrollBars(Value: Boolean);
{$endif}
procedure SwapSelectionBase;
procedure NavigatorSelectionChanging(Sender: TObject);
procedure NavigatorMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
procedure SetNavigatorRect;
function GetImageEnVersion: String;
procedure SetImageEnVersion(Value: String);
{$ifdef IEDOTNETVERSION}
procedure WMContextMenu(var Message: TWMContextMenu); message WM_CONTEXTMENU;
{$endif}
procedure WMGestureNotify(var Msg: TIEWMGestureNotify); message IEWM_GESTURENOTIFY;
procedure WMGesture(var Msg: TMessage); message IEWM_GESTURE;
procedure SetOnTransitionStop(value: TNotifyEvent);
function GetOnTransitionStop: TNotifyEvent;
procedure SetOnTransitionStep(value: TIETransitionStepEvent);
function GetOnTransitionStep: TIETransitionStepEvent;
procedure SetTransitionTiming(value: TIETransitionTiming);
function GetTransitionTiming: TIETransitionTiming;
procedure SetWallpaper(Value: TPicture);
procedure SetWallpaperStyle(Value: TIEWallpaperStyle);
function AdjustRectToAspectRatio(ARect: TRect; TransitionActive: Boolean): TRect;
procedure DoOnDrawPolygon(polygon: Integer; point: Integer; Canvas: TCanvas; x, y: Integer);
function GetBackBuffer: TBitmap;
procedure SetOnSaveUndo(value: TIESaveUndoEvent);
function GetOnSaveUndo: TIESaveUndoEvent;
procedure ZoomSelectionEx(AspectRatio: Boolean; FireDoZoomIn: Boolean);
procedure SetSoftCrop(v: TIESoftCropMode);
procedure SetSoftCropValue(v: Integer);
procedure SetHighlightedPixel(v: TPoint);
procedure SetOnTransitionPaint(const Value: TIEOnTransitionPaint);
function GetOnTransitionPaint: TIEOnTransitionPaint;
procedure SetSmoothScrollValue(v: Integer);
procedure SetSmoothZoomValue(v: Integer);
procedure SetupAniPolyTimer;
procedure SetupTransition;
procedure SetupDrawPixelBitmap;
procedure TerminatePolySelection(shift: Boolean; removeLastPoint: Boolean);
procedure DrawMarkOutNavigator(const rc: TRect);
procedure SetHighlightedPixelColor(c: TColor);
function GetSelectionGridSize(): Integer;
procedure SetSelectionGridSize(value: Integer);
procedure SetPlaying(const v: boolean);
procedure StartPlayTimer;
procedure StopPlayTimer;
procedure DoImageEnGestureEvent(const GInfo: TIEGESTUREINFO; var Handled: boolean); virtual;
function PerformRotateSnap(Value: double): double;
function PerformLayerMoveSnap(Value: double): double;
function PerformZoomSnap(Value: double): double;
procedure SetLayerOptions(const Value: TIELayerOptions);
function GetLayerDefaults() : TStringList;
procedure SetLayersCropped(const Value: Boolean);
function MultiSelectedLayersCount(): Integer;
function MultiSelectedLayersList(): TIEArrayOfInteger;
procedure SetDisplayStable(value: Boolean);
procedure UpdateLiveBackground(Src: TIEBitmap);
procedure SetShowRulers(const v: TRulerDirs);
function GetOnRulerClick: TRulerClickEvent;
function GetOnRulerGripClick: TRulerGripClickEvent;
function GetOnRulerGripDblClick: TRulerGripClickEvent;
function GetOnRulerGripPosChange: TRulerGripPosChangeEvent;
procedure SetOnRulerClick(const Value: TRulerClickEvent);
procedure SetOnRulerGripClick(const Value: TRulerGripClickEvent);
procedure SetOnRulerGripDblClick(const Value: TRulerGripClickEvent);
procedure SetOnRulerGripPosChange(const Value: TRulerGripPosChangeEvent);
function fIEBitmap_Width(): Integer;
function fIEBitmap_Height(): Integer;
function GetModified(): Boolean;
procedure SetModified(value: Boolean);
protected
// view and selections stabilization
fDelayZoomTicks: integer; // initial fStable value (also available as public property)
fDelayAniSelTicks: integer; // initial fStable2 value
fStable: integer; // stability counter (0=stable) , decremented each AniPolyFunc
fStable2: integer; // secondary stablity counter (for selections)
//
fUpdateReason: TIEUpdateReason;
fUpdateBackBuffer: boolean; // if true draw fBackBuffer
fBackBuffer: TIEBitmap; // back buffer
fZoomX: double; // zoom percentage (starting at 1)
fZoomY: double; // zoom percentage (starting at 1)
fZoomD100X: double; // fZoomX/100
f100DZoomX: double; // 100/fZoomX
fZoomD100Y: double; // fZoomX/100
f100DZoomY: double; // 100/fZoomX
fHDrawDib: HDRAWDIB; // direct draw on the screen
fScrollBars: TIEScrollStyle;
fLockPaint: integer; // 0=paint locked
fOffX, fOffY: integer; // start of view (for centered images)
fExtX, fExtY: integer; // view extent
fZZWW, fZZHH: integer; // size of zoomed bitmap
fViewX, fViewY: integer;
fUpdateLocked: integer; // 0=unlocked
fBitmapInfoHeader256: TBitmapInfoHeader256; // used by DrawTo and PaintRect
fAutoFit: boolean;
fUpdateInvalidate: boolean; // if true (default) Update will call Invalidate
fAutoStretch: boolean;
fAutoShrink: boolean;
// selections
fSel: boolean; // True when the selection is active
fPolySelecting: boolean; // True on polygonal selection
fLassoSelecting: boolean; // True on polygonal and lasso selection
fRectSelecting: boolean; // True on rectangular selection
fCircSelecting: boolean; // True on circular selection
fRectResizing: TIEGrip; // if <>ieNone we are resizing rectangular selection
fSelectMoving: integer; // if >-1 selection moving
fMMoveX, fMMoveY: integer; // used on fPolySelecting
fHSX0, fHSY0: integer; // mouse down coordinates (clipped at bakcground layer)
fHSX1, fHSY1: integer; // mouse down coordinates (clipped at current layer)
fPredX, fPredY: integer; // last call to MouseMove coordinates
fPredLX, fPredLY: integer; // last call to MouseMove coordinates (real coordinates)
fMouseDownX, fMouseDownY: integer; // non clipped mouse down coordinates
fHPolySel: pointer; // pointer to the current animated polygon
fSelectionBase: TIESelectionBase; // coordinate system for selection (bitmap or client area)
fSavedSelectionBase: TIESelectionBase;
fOnSelectionChanging: TNotifyEvent;
fOnSelectionChange: TNotifyEvent;
fOnBeforeSelectionChange: TNotifyEvent;
fOnMouseInSel: TNotifyEvent;
fSelectionOptions: TIESelectionOptions;
fStartingPolyCount: integer; // polycount at MouseDown time
fSelectionMask: TIEMask; // selection mask
fSelectionMaskDepth: integer; // selection mask depth
fSelectionIntensity: integer; // must be 1 if fSelectionMaskDepth is 1, otherwise a value from 0 to 255
fSelColor1, fSelColor2: TColor; // selection colors
fGripColor1, fGripColor2: TColor; // grip color
fGripBrushStyle: TBrushStyle;
fGripSize: integer;
fGripShape: TIEGripShape;
fExtendedGrips: boolean;
fLyrGripColor1, fLyrGripColor2: TColor;
fLyrGripBrushStyle: TBrushStyle;
fLyrGripSize: integer;
fLyrGripShape: TIEGripShape;
fChessboardSize: integer;
fChessboardBrushStyle: TBrushStyle;
fChessboardColor2Customized: Boolean;
fForceALTkey: boolean;
fMouseMoveScrolling: boolean; // true if we are inside MouseMoveScroll
fSavedSelection: TList; // list of saved selections
fEnableShiftKey: boolean; // Enable/disable shift key to add multi selections
fGradientEndColor: TColor;
// animated polygons
fAnimPoly: TList; // animated polygons list (TIEAnimPoly)
fAnimPolyTimer: TTimer; // animation timer
// smooth scroll
fSmoothScrollTimer: TTimer;
fSmoothScrollDestX, fSmoothScrollDestY: Integer;
fSmoothScrollValue: Integer;
// smooth zoom
fSmoothZoomTimer: TTimer;
fSmoothZoomDest: Double;
fSmoothZoomValue: Integer;
//
fMouseInteract: TIEMouseInteract;
fScrollBarsAlwaysVisible: boolean; // true if the scrollbars are always visible
fVScrollBarParams: TIEScrollBarParams;
fHScrollBarParams: TIEScrollBarParams;
fMouseWheelParams: TIEMouseWheelParams;
flx1, fly1, flx2, fly2: integer;
// alpha channel
fEnableAlphaChannel: boolean;
//
// encapsulated components
fImageEnIO: TImageEnIO;
fImageEnProc: TImageEnProc;
// layers
fLayers: TList; // list of TIELayer objects
fLayersCurrent: integer; // 0=first layer
fLayersRect: TIERectangle; // all layers coverage
fLayerOptions: TIELayerOptions;
fLayerDefaults: TStringList;
//
fLayersDrawBox: boolean;
fLayersCaching: Integer; // 0 is no caching. -1 is cache all layers
fMovingLayer: integer; // -1 no layers moving, >= 0 moving a layer
fLayerMoved: Boolean;
fRotatingLayer: integer; // -1 no layers rotating, >= 0 rotating layer
fRotatingLayerValue: Double;
fMovingRotationCenter: integer; // -1 no layers moving rotation center
fLayerResizing: TIEGrip; // ieNone no layer grip
fMovResRotLayerStarted: Boolean;
// conversion LUT
fGXScr2Bmp, fGYScr2Bmp: pintegerarray; // LUT used to convert screen based coordinate to bitmap
fGXScr2BmpSize, fGYScr2BmpSize: Integer; // size of fGXScr2Bmp and fGYScr2Bmp LUTs
fXScr2Bmp, fYScr2Bmp: pintegerarray; // LUT used to convert screen based coordinate to bitmap
fXScr2BmpSize, fYScr2BmpSize: integer; // size of fXScr2Bmp and fYScr2Bmp LUTs
fXBmp2Scr, fYBmp2Scr: pintegerarray; // LUT used to convert bitmap based coordinate to screen
fGXBmp2Scr, fGYBmp2Scr: pintegerarray; // LUT used to convert bitmap based coordinate to screen
fXBmp2ScrSize, fYBmp2ScrSize: integer; // size of fXBmp2Scr, fYBmp2Scr LUTs
//
fAutoCursors: Boolean; // If true (default) ImageEn handles mouse cursors
//
fOnSaveUndo: TIESaveUndoEvent;
fOnVirtualKey: TIEVirtualKeyEvent;
fLayerBoxPen: TPen;
fHighlightedPixelColor: TColor;
fImageSet : Boolean; // Becomes true once we set or assign an image
// Gestures
fGestures: TIEViewerGestures;
fGestureStartValue: integer;
fGestureStartX: integer;
fGestureStartY: integer;
fGestureBaseZoom: double;
fGestureBaseViewX: integer;
fGestureBaseViewY: integer;
fGestureBaseRotate: double;
fGestureBasePosX: integer;
fGestureBasePosY: integer;
//
procedure CalcEdges(var LeftEdge, TopEdge, RightEdge, BottomEdge: integer; NoVertSB, NoHorizSB: boolean);
procedure SetBackground(cl: TColor); override;
procedure PolyDraw1;
procedure SetScrollBars(v: TIEScrollStyle); virtual;
procedure CreateHandle(); override;
function GetOptimalZoom(CanStretch, CanShrink: Boolean): double;
// cross platform events
procedure DoSize;
procedure DoVertScroll(command: TIEScrollCommand; Position: integer);
procedure DoHorizScroll(command: TIEScrollCommand; Position: integer);
procedure DoMouseWheelScroll(Delta: integer; mouseX, mouseY: integer; CtrlPressed: boolean);
procedure DoDoubleClickEx(Shift: boolean);
// platform specific events
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMEraseBkgnd(var Message: TMessage); message WM_ERASEBKGND;
procedure WMVScroll(var Message: TMessage); message WM_VSCROLL;
procedure WMHScroll(var Message: TMessage); message WM_HSCROLL;
procedure WMMouseWheel(var Message: TMessage); message WM_MOUSEWHEEL;
procedure WMMouseHWheel(var Message: TMessage); message IEWM_MOUSEHWHEEL;
function _CreatePalette: HPalette;
function PaletteChanged(Foreground: Boolean): Boolean; {$ifndef FPC} override; {$endif}
procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
procedure WMSetFocus(var Msg: TWMSetFocus); message WM_SETFOCUS;
procedure CMWantSpecialKey(var Msg: TCMWantSpecialKey); message CM_WANTSPECIALKEY;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
procedure IEMUpdate(var Message: TMessage); message IEM_UPDATE;
procedure IEMProgress(var Message: TMessage); message IEM_PROGRESS;
procedure IEMFinishWork(var Message: TMessage); message IEM_FINISHWORK;
procedure CNKEYDOWN(var Message: TMessage); message CN_KEYDOWN; // we handle CN_ instead of WM_ to be sure key events arrive here
procedure CNKEYUP(var Message: TMessage); message CN_KEYUP; // we handle CN_ instead of WM_ to be sure key events arrive here
procedure WMSYSKEYDOWN(var Message: TMessage); message WM_SYSKEYDOWN;
procedure WMSYSKEYUP(var Message: TMessage); message WM_SYSKEYUP;
procedure WMTimer(var Message: TWMTimer); message WM_TIMER;
//
procedure RestoreCursor;
procedure ShowSelectionEx(d: boolean);
procedure HideSelectionEx(dd: boolean);
function GetIdealComponentWidth: integer;
function GetIdealComponentHeight: integer;
function GetIdealImageWidth: integer;
function GetIdealImageHeight: integer;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
function GetPolySel(idx: integer): TPoint;
function GetPolySelCount: integer;
procedure SetMouseInteract(v: TIEMouseInteract); virtual;
procedure SetMouseWheelParams(v: TIEMouseWheelParams);
procedure AnimPolygonAddPtEx(ap: pointer; x, y: integer);
procedure AnimPolygonAddBreak(ap: pointer);
procedure AnimPolygonRemoveLast(ap: pointer);
procedure AnimPolygonRemoveLastPoint(ap: pointer);
procedure AnimPolygonMove(ap: pointer; ox, oy: integer; max_x, max_y: integer; CutSel: boolean);
procedure ViewChange(c: integer); virtual;
procedure ViewChanging(c: integer; newValue: Double); virtual;
procedure SetZoomFilter(v: TResampleFilter);
procedure SubMouseMoveScroll(scx, scy: integer); virtual;
procedure MouseMoveScroll();
function GetFBitmap: TBitmap; override;
function GetIEBitmap: TIEBitmap; override;
procedure SetVisibleSelection(vv: boolean);
function GetVisibleSelection: boolean;
function GetPolySelArray: PPointArray;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
procedure KeyPress(var Key: Char); override;
procedure AddSelPointEx(x, y: integer);
procedure AddSelBreakEx;
procedure SelectEx(x1, y1, x2, y2: integer; Op: TIESelOp; aspectratio: boolean; adjRect: Boolean);
function GetClientWidth: integer; virtual;
function GetClientWidthExRulers: integer;
function GetClientHeight: integer; virtual;
function GetClientHeightExRulers: integer;
function HasParentWindow: boolean; virtual;
procedure SetSelColor1(v: TColor);
procedure SetSelColor2(v: TColor);
function GetImageEnIO: TImageEnIO;
function GetImageEnProc: TImageEnProc;
procedure SetEnableAlphaChannel(v: boolean);
procedure SetOnProgress(v: TIEProgressEvent); virtual;
function GetOnFinishWork: TNotifyEvent; virtual;
procedure SetOnFinishWork(v: TNotifyEvent); virtual;
procedure SetOnAcquireBitmap(v: TIEAcquireBitmapEvent); virtual;
function GetOnAcquireBitmap: TIEAcquireBitmapEvent; virtual;
function GetOnProgress: TIEProgressEvent; virtual;
function GetHasAlphaChannel: boolean; override;
function GetAlphaChannel: TIEBitmap; override;
function GetIsEmpty: boolean; virtual;
function GetIsEmpty2: boolean; virtual;
function RequiresScrollBars: boolean;
procedure SetLayersCurrent(Value: integer); virtual;
function SetLayersCurrentEx(Value: integer; bUserAction, bDeselectAll: Boolean; DoUpdate: Boolean = True): Boolean;
procedure SelectMaskOfLayer(iLayerIndex: Integer; bSelect: Boolean; bUserAction: Boolean);
procedure SyncBitmapToCurrentLayer();
function SyncLayers(): Boolean;
function MouseChangingLayers(): Boolean;
procedure SetLegacyBitmap(Value: boolean); virtual;
procedure CreateCoordConvLUT; virtual;
procedure CalcPaintCoords; virtual;
procedure CalcPaintCoordsEx(var XSrc, YSrc, SrcWidth, SrcHeight: integer; var DstWidth, DstHeight: integer; tViewX, tViewY: integer);
procedure UpdateLimits; virtual;
function QuantizeViewX(vx: integer): integer;
function QuantizeViewY(vy: integer): integer;
procedure HideHorizScrollBar;
procedure HideVertScrollBar;
procedure SetSelectionIntensity(value: integer);
procedure SetLayersDrawBox(value: boolean);
procedure DoLayerNotify(layer: integer; event: TIELayerEvent); virtual;
procedure SetTempCursor(Value: TCursor; bOnMouseDown : Boolean = False);
function GetCurrentLayer: TIELayer;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure GetFitValueXY(var x, y: Double);
function IsPointInsideLayer(x, y: Integer; layer: Integer): Boolean;
procedure OnSmoothSetView(Sender: TObject);
procedure StopSmoothScroll();
procedure OnSmoothZoom(Sender: TObject);
procedure StopSmoothZoom();
procedure DrawSelectionGrid(x1, y1, x2, y2: Integer); virtual;
procedure AfterDrawLayer(layerIndex: Integer; DestBitmap: TIEBitmap; const DestRect: TRect); virtual;
procedure SetVisibleBitmapRect(ARect: TRect);
function GetVisibleBitmapRect : TRect;
function LayersAllowMultiSelect: Boolean;
procedure MoveSelByKey(VKey: Word; ss: TShiftState);
// user interactions
function UserInteractions_MouseDownExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean;
procedure UserInteractions_MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
function UserInteractions_MouseMoveExclusive(Shift: TShiftState; X, Y: Integer; Captured: boolean): boolean;
procedure UserInteractions_MouseMove(Shift: TShiftState; X, Y: Integer; Captured: boolean);
function UserInteractions_MouseUpExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean;
procedure UserInteractions_MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure UserInteractions_VirtualKey(VKey: Dword; KeyData: Dword; KeyDown: Boolean);
procedure UserInteractions_Paint(const UpdateRect: TRect);
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 6.2.2 (2015-12-15)
function GetDPIX() : integer;
function GetDPIY() : integer;
procedure SetDPIX(dpiX: integer); virtual;
procedure SetDPIY(dpiY: integer); virtual;
{$endif}
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 7.0.0 (8/11/2016)
function GetLayersRotationDelayFilterOnPreview(): boolean;
procedure SetLayersRotationDelayFilterOnPreview(value: Boolean);
{$endif}
procedure UpdateTextEditor();
procedure TextEditorKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure TextEditorOnChange(Sender: TObject);
public
fIEBitmapValid: Boolean; // Selected layer is an image (thus fIEBitmap can be used)
fIEBitmap: TIEBitmap; // contains also the alpha channel
fBitmap: TBitmap;
constructor Create(Owner: TComponent); override;
destructor Destroy; override;
procedure Paint; override;
procedure PaintToEx(ABitmap: TIEBitmap; UpdRect: PRect; drawBackground: Boolean; drawGadgets: Boolean); virtual;
procedure GetRenderRectangles(var xDst, yDst, dxDst, dyDst: integer; var xSrc, ySrc, dxSrc, dySrc: integer);
procedure GetMinViewXY(var mx, my: integer);
procedure GetMaxViewXY(var mx, my: integer);
procedure SetZoomNoUpdate(v: double); virtual;
procedure SetZoomXNoUpdate(v: double); virtual;
procedure SetZoomYNoUpdate(v: double); virtual;
function GetFitValue(): double;
procedure DrawBackgroundToCanvas(ACanvas: TCanvas; iWidth: Integer = -1; iHeight: Integer = -1);
procedure MoveContentTo(Destination: TImageEnView);
procedure SaveState(const FileName: String); overload;
procedure SaveState(Stream: TStream); overload;
procedure LoadState(const FileName: String); overload;
procedure LoadState(Stream: TStream); overload;
procedure ResetState();
// Selection
procedure Select(x1, y1, x2, y2: integer; Op: TIESelOp = iespReplace); virtual;
procedure SelectMagicWand(x, y: integer; Op: TIESelOp = iespReplace);
procedure SelectColors(StartColor, FinalColor: TRGB; Op: TIESelOp = iespReplace); overload;
procedure SelectColors(Color: TRGB; Op: TIESelOp = iespReplace); overload;
procedure SelectColors(ColorIndex: integer; Op: TIESelOp = iespReplace); overload;
procedure SelectNonAlpha(Op: TIESelOp = iespReplace);
procedure SelectEllipse(CenterX, CenterY, Width, Height: integer; Op: TIESelOp = iespReplace);
procedure SelectRoundRect(Left, Top, Right, Bottom, RoundWidth, RoundHeight: integer; Op: TIESelOp = iespReplace);
procedure SelectCustom;
procedure MoveSelection(MoveX, MoveY: integer; Sizing: Boolean = False);
property SelectionAspectRatio: double read fSelectionAspectRatio write SetSelectionAspectRatio;
procedure SetSelectionMarkOuterStyle(Alpha: Integer; Color: TColor);
procedure SelectByGroupIndex(iGroupIndex: Integer; bSelect: Boolean; bUserAction: Boolean);
{!!
<FS>TImageEnView.SelectionAbsWidth
<FM>Declaration<FC>
property SelectionAbsWidth: Integer;
<FM>Description<FN>
Locks the selection to a specific width, when <A TImageEnView.SelectionAspectRatio> is 0.
<FM>Example<FC>
// we want a fixed selection of 100 x 100 pixels
ImageEnView.SelectionAbsWidth := 100;
ImageEnView.SelectionAbsHeight := 100;
ImageEnView.SelectionAspectRatio := 0;
<FM>See Also<FN>
- <A TImageEnView.SelectionAbsHeight>
- <A TImageEnView.SelectionAspectRatio>
- <A TIECropToolInteraction.LockWidth>
!!}
property SelectionAbsWidth: integer read fSelectionAbsWidth write fSelectionAbsWidth;
{!!
<FS>TImageEnView.SelectionAbsHeight
<FM>Declaration<FC>
property SelectionAbsHeight: Integer;
<FM>Description<FN>
Locks the selection to a specific height, when <A TImageEnView.SelectionAspectRatio> is 0.
<FM>Example<FC>
// we want a fixed selection of 100 x 100 pixels
ImageEnView.SelectionAbsWidth := 100;
ImageEnView.SelectionAbsHeight := 100;
ImageEnView.SelectionAspectRatio := 0;
<FM>See Also<FN>
- <A TImageEnView.SelectionAbsWidth>
- <A TImageEnView.SelectionAspectRatio>
- <A TIECropToolInteraction.LockHeight>
!!}
property SelectionAbsHeight: integer read fSelectionAbsHeight write fSelectionAbsHeight;
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 7.0.0 (21/2/2017)
// Note: deprecated 'Use loAutoFixBorders of LayerOptions'
property AutoFixRotationBorders: Boolean read GetAutoFixRotationBorders write SetAutoFixRotationBorders;
{$ENDIF}
procedure Deselect;
property SelX1: integer read GetSelx1;
property SelY1: integer read GetSely1;
property SelX2: integer read GetSelx2;
property SelY2: integer read GetSely2;
procedure AddSelPoint(x, y: integer); virtual;
procedure AddSelBreak;
procedure DelLastSelPoint();
property PolySel[idx: integer]: TPoint read GetPolySel;
property PolySelCount: integer read GetPolySelCount;
property PolySelPoints: PPointArray read GetPolySelArray;
property VisibleBitmapRect : TRect read GetVisibleBitmapRect write SetVisibleBitmapRect;
property SmoothScrollValue: Integer read fSmoothScrollValue write SetSmoothScrollValue;
property SmoothZoomValue: Integer read fSmoothZoomValue write SetSmoothZoomValue;
{!!
<FS>TImageEnView.Selected
<FM>Declaration<FC>
property Selected: boolean;
<FM>Description<FN>
Returns True if any portion of the image is selected. Selected is True after you have called the Select method. Selected is False after you have called <A TImageEnView.Deselect> method.
If Selected is True, the properties <A TImageEnView.SelX1>, <A TImageEnView.SelY1>, <A TImageEnView.SelX2>, <A TImageEnView.SelY2> are valid.
Read-only
!!}
property Selected: boolean read fSel;
{!!
<FS>TImageEnView.SelectedRect
<FM>Declaration<FC>
property SelectedRect: <A TIERectangle>;
<FM>Description<FN>
Returns current selected rectangle. If no selected exists returns the whole image rectangle.
The coordinate has the same dimensions of the bitmap (i.e. it is not affected by <A TImageenView.Zoom> and <A TImageenView.ViewX>, <A TImageenView.ViewY> values).
You can programatically make a selection using the <A TImageEnView.Select> method.
Read-only
!!}
property SelectedRect: TIERectangle read GetSelectedRect;
property VisibleSelection: boolean read GetVisibleSelection write SetVisibleSelection;
{!!
<FS>TImageEnView.MagicWandMaxFilter
<FM>Declaration<FC>
property MagicWandMaxFilter: Boolean;
<FM>Description<FN>
Set to True to apply a maximum filter to the magic wand selection (remove the "black hole").
Default: False
<FM>See Also<FN>
- <A TImageEnView.MagicWandMode>
- <A TImageEnView.MagicWandTolerance>
- <A TImageEnView.MouseInteract>
!!}
property MagicWandMaxFilter: boolean read fMagicWandMaxFilter write fMagicWandMaxFilter;
{!!
<FS>TImageEnView.MagicWandTolerance
<FM>Declaration<FC>
property MagicWandTolerance: Integer;
<FM>Description<FN>
Specifies the color difference of original point and region point when using <L TIEMouseInteractItems>miSelectMagicWand</L>. A low number selects colors of a very close match, whereas a higher value allows much broader color differences.
Default: 15
<FM>See Also<FN>
- <A TImageEnView.MagicWandMaxFilter>
- <A TImageEnView.MagicWandMode>
- <A TImageEnView.MouseInteract>
!!}
property MagicWandTolerance: integer read fMagicWandTolerance write fMagicWandTolerance;
{!!
<FS>TImageEnView.MagicWandMode
<FM>Declaration<FC>
property MagicWandMode: <A TIEMagicWandMode>;
<FM>Description<FN>
Specifies how magic wand selection <L TIEMagicWandMode>works</L>.
Default: iewInclusive
<FM>See Also<FN>
- <A TImageEnView.MagicWandMaxFilter>
- <A TImageEnView.MagicWandTolerance>
- <A TImageEnView.MouseInteract>
!!}
property MagicWandMode: TIEMagicWandMode read fMagicWandMode write fMagicWandMode;
function IsPointInsideSelection(x, y: integer): boolean;
{!!
<FS>TImageEnView.SelectionMask
<FM>Declaration<FC>
property SelectionMask: <A TIEMask>;
<FM>Description<FN>
SelectionMask contains the current selection as a bit mask of 1 bit per pixel (zero is unselected, 1 is selected).
<FM>Example<FC>
// Excludes pixel 100,100 from selection
ImageEnView1.SelectionMask.SetPixel(100, 100, 0);
// To create a new selection using only SelectionMask (this select point 50,50 and 55,55)
ImageEnView1.SelectionMask.SetPixel(50, 50, 1);
ImageEnView1.SelectionMask.SetPixel(55, 55, 1);
ImageEnView1.SelectCustom();
<FM>See Also<FN>
- <A TImageEnView.SelectCustom>
!!}
property SelectionMask: TIEMask read fSelectionMask;
procedure InvertSelection;
procedure SetSelectedAreaAlpha(Alpha: integer);
procedure SetSelectionGripStyle(GripColor1: TColor=clBlack; GripColor2: TColor=clWhite; GripBrushStyle: TBrushStyle=bsSolid; GripSize: integer=5; ExtendedGrips: boolean=true; Shape: TIEGripShape = iegsCircle);
procedure GetSelectionGripStyle(var GripColor1: TColor; var GripColor2: TColor; var GripBrushStyle: TBrushStyle; var GripSize: integer; var ExtendedGrips: boolean; var Shape: TIEGripShape);
procedure SetLayersGripStyle(GripColor1: TColor=clBlack; GripColor2: TColor=clWhite; GripBrushStyle: TBrushStyle=bsSolid; GripSize: integer=5; Shape: TIEGripShape = iegsCircle);
procedure SetLayersBoxStyle(PenStyle: TPenStyle=psDot; PenMode: TPenMode=pmNot; PenWidth: Integer = 1; PenColor: TColor=clBlack);
procedure ApplyBitmapToSelection(SrcBitmap: TBitmap; MaintainAspectRatio: Boolean = True; CanStretch: Boolean = False); overload;
procedure ApplyBitmapToSelection(SrcBitmap: TIEBitmap; MergeAlpha: Boolean = True; MaintainAspectRatio: Boolean = True; CanStretch: Boolean = False); overload;
procedure CopySelectionToBitmap(DestBitmap: TBitmap; FillBackground: Boolean = True); overload;
procedure CopySelectionToBitmap(DestBitmap: TIEBitmap; FillBackground: Boolean = True); overload;
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 6.2.0
procedure CopySelectionToIEBitmap(DestBitmap: TIEBitmap; FillBackground: Boolean=true); {$ifdef IEWarningForDeprecated} deprecated {$ifdef IESupportDeprecatedDescription} 'Use CopySelectionToBitmap instead - http://imageen.com/help/Compatibility.html' {$endif}; {$endif}
{$endif}
procedure EndSelect; virtual;
procedure SetSelectedPixelsColor(color: TRGB);
procedure SetAlphaRangePixelsColor(alphaMin, alphaMax: integer; color: TRGB);
procedure SaveSelectionToStream(Stream: TStream);
function LoadSelectionFromStream(Stream: TStream; Options: TIERSOptions=iersMoveToAdapt): boolean;
function MergeSelectionFromStream(Stream: TStream): boolean;
procedure SaveSelectionToFile(const FileName: String);
function LoadSelectionFromFile(const FileName: String; Options: TIERSOptions = iersMoveToAdapt): boolean;
function MergeSelectionFromFile(const FileName: String): Boolean;
function RestoreSelection(Remove: Boolean=true; Options: TIERSOptions = iersMoveToAdapt): boolean;
procedure SaveSelection;
function DiscardSavedSelection: boolean;
property SavedSelectionsCount: Integer read GetSavedSelectionsCount;
{!!
<FS>TImageEnView.EnableShiftKey
<FM>Declaration<FC>
property EnableShiftKey: Boolean;
<FM>Description<FN>
Enables Shift key functionality (holding down the Shift key to insert multiple selections).
Default: true
!!}
property EnableShiftKey: boolean read fEnableShiftKey write fEnableShiftKey;
procedure LayersSizeAll(HorzSizing, VertSizing: Double; bSelectedOnly: Boolean = False; bFixSizes: Boolean = False; ScalePosition: Boolean = False);
procedure LayersRotateAll(Value: Double; bSelectedOnly: Boolean = False; bFixRotations: Boolean = False);
procedure LayersRepositionAll(MoveX, MoveY: Integer; SelectedOnly: Boolean = False; Sizing: Boolean = False);
{!!
<FS>TImageEnView.LayersRotationFilter
<FM>Declaration<FC>
property LayersRotationFilter: <A TIEAntialiasMode>;
<FM>Description<FN>
Specifies the rotation filter to use when layers rotation has completed. This is important to optimize the quality of rotated image layers.
Note: You also need to set <A TImageEnView.LayersRotationAntialias> := True;
Default: ierFast
<FM>Example<FC>
// Rotate the current layer 45 degrees clockwise at highest quality
ImageEnView1.CurrentLayer.Rotate := -45;
ImageEnView1.LayersRotationAntialias := True;
ImageEnView1.LayersRotationFilter := ierBilinear;
ImageEnView1.LayersRotationUseFilterOnPreview := true;
ImageEnView1.LayersFastDrawing := iefDelayed;
// Allow the user to free rotate layers at highest quality
ImageEnView1.MouseInteract := [miRotateLayers];
ImageEnView1.LayersRotationAntialias := True;
ImageEnView1.LayersRotationFilter := ierBilinear;
ImageEnView1.LayersRotationUseFilterOnPreview := true;
ImageEnView1.LayersFastDrawing := iefDelayed;
<FM>See Also<FN>
- <A TImageEnView.LayersRotationAntialias>
- <A TImageEnView.LayersRotationUseFilterOnPreview>
- <A TImageEnView.LayersFastDrawing>
!!}
property LayersRotationFilter: TIEAntialiasMode read fLayersRotationFilter write fLayersRotationFilter;
{!!
<FS>TImageEnView.LayersRotationAntialias
<FM>Declaration<FC>
property LayersRotationAntialias: Boolean;
<FM>Description<FN>
Enables rotation anti-aliasing when layer rotation has completed (to improve their quality). Use <A TImageEnView.LayersRotationFilter> to specify the anti-aliasing algorithm.
Default: False
<FM>Example<FC>
// Rotate the current layer 45 degrees clockwise at highest quality
ImageEnView1.CurrentLayer.Rotate := -45;
ImageEnView1.LayersRotationAntialias := True;
ImageEnView1.LayersRotationFilter := ierBilinear;
ImageEnView1.LayersRotationUseFilterOnPreview := true;
ImageEnView1.LayersFastDrawing := iefDelayed;
// Allow the user to free rotate layers at highest quality
ImageEnView1.MouseInteract := [miRotateLayers];
ImageEnView1.LayersRotationAntialias := True;
ImageEnView1.LayersRotationFilter := ierBilinear;
ImageEnView1.LayersRotationUseFilterOnPreview := true;
ImageEnView1.LayersFastDrawing := iefDelayed;
<FM>See Also<FN>
- <A TImageEnView.LayersRotationFilter>
- <A TImageEnView.LayersRotationUseFilterOnPreview>
- <A TImageEnView.LayersFastDrawing>
!!}
property LayersRotationAntialias: Boolean read fLayersRotationAntialias write fLayersRotationAntialias;
{!!
<FS>TImageEnView.LayersRotationUseFilterOnPreview
<FM>Declaration<FC>
property LayersRotationUseFilterOnPreview: Boolean;
<FM>Description<FN>
Displays the user's rotation with the selected <L TImageEnView.LayersRotationFilter>anti-alias effect</L> immediately (i.e. before calling <A TImageEnView.LayersFixRotations>).
Note: Preview of large rotated images at high quality can be slow, so it is recommended that you also use <A TImageEnView.LayersFastDrawing>.
Default: False
<FM>Example<FC>
// Rotate the current layer 45 degrees clockwise at highest quality
ImageEnView1.CurrentLayer.Rotate := -45;
ImageEnView1.LayersRotationAntialias := True;
ImageEnView1.LayersRotationFilter := ierBilinear;
ImageEnView1.LayersRotationUseFilterOnPreview := true;
ImageEnView1.LayersFastDrawing := iefDelayed;
// Allow the user to free rotate layers at highest quality
ImageEnView1.MouseInteract := [miRotateLayers];
ImageEnView1.LayersRotationAntialias := True;
ImageEnView1.LayersRotationFilter := ierBilinear;
ImageEnView1.LayersRotationUseFilterOnPreview := true;
ImageEnView1.LayersFastDrawing := iefDelayed;
<FM>See Also<FN>
- <A TImageEnView.LayersRotationFilter>
- <A TImageEnView.LayersRotationAntialias>
- <A TImageEnView.LayersFastDrawing>
!!}
property LayersRotationUseFilterOnPreview: boolean read fLayersRotationUseFilterOnPreview write fLayersRotationUseFilterOnPreview;
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 7.0.0 (8/11/2016)
// Note: deprecated 'Use LayersFastDrawing'
property LayersRotationDelayFilterOnPreview: boolean read GetLayersRotationDelayFilterOnPreview write SetLayersRotationDelayFilterOnPreview;
{$endif}
property SelectionMaskDepth: integer read fSelectionMaskDepth write SetSelectionMaskDepth;
property SelectionIntensity: integer read fSelectionIntensity write SetSelectionIntensity;
procedure MakeSelectionFeather(radius: double);
// Animated polygons
function AnimPolygonNew(VColor1, VColor2: TColor; VAnimated: boolean; VSizeable: boolean): pointer;
procedure AnimPolygonDel(ap: pointer);
procedure AnimPolygonAddPt(ap: pointer; x, y: integer);
procedure AnimPolygonClear(ap: pointer);
procedure AnimPolygonEnabled(ap: pointer; Value: Boolean);
// Polygon process
procedure CopyFromPolygon(Source: TBitmap; const Polygon: array of TPoint; PolygonLen: integer; const Position: TPoint);
procedure CopyToPolygon(Dest: TBitmap; const Polygon: array of TPoint; PolygonLen: integer; const Position: TPoint);
// Display
procedure Update; override;
{!!
<FS>TImageEnView.UpdateReason
<FM>Declaration<FC>
property UpdateReason: <A TIEUpdateReason>;
<FM>Description<FN>
Advises the reason for the next <A TImageEnView.Update>. This is used to optimize the Update() task.
This property is restored to ieurDefault after Update() has executed.
!!}
property UpdateReason: TIEUpdateReason read fUpdateReason write fUpdateReason;
procedure UpdateNoPaint;
{!!
<FS>TImageEnView.OffScreenPaint
<FM>Declaration<FC>
property OffScreenPaint: Boolean;
<FM>Description<FN>
When the component is not visible (i.e. Visible=False) the back buffer <A TImageEnView.BackBuffer> is not painted.
If you need the back buffer to be paint correctly set OffScreenPaint to true.
!!}
property OffScreenPaint: Boolean read fOffscreenPaint write fOffscreenPaint;
{!!
<FS>TImageEnView.SelectionGridSize
<FM>Declaration<FC>
property SelectionGridSize: Integer;
<FM>Description<FN>
When this is not 1, a grid is drawn over rectangular selections with the column and row count of SelectionGridSize (e.g. if SelectionGridSize=3 then there will be three columns and rows within the selection box). The color of the grid is specified by <A TIEImageEnGlobalSettings.SelectionGridColor>.
This property can be used in place of <A TImageEnView.SelectionGridWidth> and <A TImageEnView.SelectionGridHeight>.
Default: 1
!!}
property SelectionGridSize: Integer read GetSelectionGridSize write SetSelectionGridSize;
{!!
<FS>TImageEnView.SelectionGridWidth
<FM>Declaration<FC>
property SelectionGridWidth: Integer;
<FM>Description<FN>
When this is not 1, a grid is drawn over rectangular selections with the column count of SelectionGridWidth (e.g. if SelectionGridWidth=3 then there will be three columns within the selection box). The color of the grid is specified by <A TIEImageEnGlobalSettings.SelectionGridColor>.
This property can be used in place of <A TImageEnView.SelectionGridSize>.
Default: 1
See also: <A TImageEnView.SelectionGridHeight>
!!}
property SelectionGridWidth: Integer read fSelectionGridWidth write fSelectionGridWidth;
{!!
<FS>TImageEnView.SelectionGridHeight
<FM>Declaration<FC>
property SelectionGridHeight: Integer;
<FM>Description<FN>
When this is not 1, a grid is drawn over rectangular selections with the row count of SelectionGridHeight (e.g. if SelectionGridHeight=3 then there will be three rows within the selection box). The color of the grid is specified by <A TIEImageEnGlobalSettings.SelectionGridColor>.
This property can be used in place of <A TImageEnView.SelectionGridSize>.
Default: 1
See also: <A TImageEnView.SelectionGridWidth>
!!}
property SelectionGridHeight: Integer read fSelectionGridHeight write fSelectionGridHeight;
procedure Fit(StretchSmall : Boolean = True);
procedure FitToWidth;
procedure FitToHeight;
procedure Stretch;
function GetIdealZoom: double;
property ViewX: integer read fViewX write SetViewX;
property ViewY: integer read fViewY write SetViewY;
procedure CenterImage;
procedure SetViewXY(x, y: integer);
procedure SetViewXYSmooth(x, y: Integer);
procedure SetZoomSmooth(Zoom: Double);
property Zoom: double read fZoomX write SetZoom;
property ZoomX: Double read fZoomX write SetZoomX;
property ZoomY: Double read fZoomY write SetZoomY;
procedure ZoomSelection(AspectRatio: Boolean=true);
procedure ZoomAt(x, y: integer; ZoomVal: double; Center: Boolean=true);
procedure ZoomIn;
procedure ZoomOut;
property ClientWidth read GetClientWidth;
property ClientHeight read GetClientHeight;
property IdealComponentWidth: integer read GetIdealComponentWidth;
property IdealComponentHeight: integer read GetIdealComponentHeight;
property IdealImageWidth: integer read GetIdealImageWidth;
property IdealImageHeight: integer read GetIdealImageHeight;
procedure LockPaint; override;
function UnLockPaint: integer; override;
function NPUnLockPaint: integer; override;
{!!
<FS>TImageEnView.LockPaintCount
<FM>Declaration<FC>
property LockPaintCount: Integer; (Read-only)
<FM>Description<FN>
Returns the lock painting state. 0=no lock, > 0 locking (the image will not be painted).
<A TImageEnView.LockPaint> increases LockPaintCount, <A TImageEnView.UnLockPaint> decreases it.
!!}
property LockPaintCount: integer read fLockPaint;
{!!
<FS>TImageEnView.LockUpdateCount
<FM>Declaration<FC>
property LockUpdateCount: Integer; (Read-only)
<FM>Description<FN>
Returns the lock updating state. 0 = no lock, > 0 locking (the image will not be updated).
<A TImageEnView.LockUpdate> increases LockUpdateCount, <A TImageEnView.UnLockUpdate> decreases it.
!!}
property LockUpdateCount: integer read fUpdateLocked;
procedure DrawTo(Canvas: TCanvas);
{!!
<FS>TImageEnView.OffsetX
<FM>Declaration<FC>
property OffsetX: integer; (Read-only)
<FM>Description<FN>
Returns the horizontal position where the image has been drawn. If <A TImageEnView.ImageHorizAlignment> is iehLeft, <FC>OffsetX<FN> will be zero, whereas for <FC>iehCenter<FN> it will be (Control Width - Image Width) div 2.
Note: This value only indicates the initial draw position for an image, it is not related to scrolling (unlike <A TImageEnView.ViewX>).
<FM>See Also<FN>
- <A TImageEnView.OffsetY>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.VisibleBitmapRect>
!!}
property OffsetX: integer read fOffX;
{!!
<FS>TImageEnView.OffsetY
<FM>Declaration<FC>
property OffsetY: integer; (Read-only)
<FM>Description<FN>
Returns the vertical position where the image has been drawn. If <A TImageEnView.ImageVertAlignment> is ievTop, <FC>OffsetY<FN> will be zero, whereas for <FC>ievCenter<FN> it will be (Control Height - Image Height) div 2.
Note: This value only indicates the initial draw position for an image, it is not related to scrolling (unlike <A TImageEnView.ViewY>).
<FM>See Also<FN>
- <A TImageEnView.OffsetX>
- <A TImageEnView.YScr2Bmp>
- <A TImageEnView.VisibleBitmapRect>
!!}
property OffsetY: integer read fOffY;
{!!
<FS>TImageEnView.ExtentX
<FM>Declaration<FC>
property ExtentX: integer; (Read-only)
<FM>Description<FN>
Returns the width of the area used to show the current image (i.e. the width of the image as it appears onscreen).
<FM>Example<FC>
// Copy the actual view of ImageEnView1 as the current image
ImageEnView2.Bitmap.Width := ImageEnView1.ExtentX;
ImageEnView2.Bitmap.Height := ImageEnView1.ExtentY;
ImageEnView1.DrawTo(ImageEnView2.Bitmap.Canvas);
<FM>See Also<FN>
- <A TImageEnView.ExtentY>
- <A TImageEnView.ViewX>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.VisibleBitmapRect>
!!}
property ExtentX: integer read fExtX;
{!!
<FS>TImageEnView.ExtentY
<FM>Declaration<FC>
property ExtentY: integer; (Read-only)
<FM>Description<FN>
Returns the height of the area used to show the current image (i.e. the height of the image as it appears onscreen).
<FM>Example<FC>
// Copy the actual view of ImageEnView1 as the current image of ImageEnView2
ImageEnView2.Bitmap.Width := ImageEnView1.ExtentX;
ImageEnView2.Bitmap.Height := ImageEnView1.ExtentY;
ImageEnView1.DrawTo(ImageEnView2.Bitmap.Canvas);
<FM>See Also<FN>
- <A TImageEnView.ExtentX>
- <A TImageEnView.ViewY>
- <A TImageEnView.YScr2Bmp>
- <A TImageEnView.VisibleBitmapRect>
!!}
property ExtentY: integer read fExtY;
{!!
<FS>TImageEnView.ScrollBarsAlwaysVisible
<FM>Declaration<FC>
property ScrollBarsAlwaysVisible: Boolean;
<FM>Description<FN>
When the ScrollBarsAlwaysVisible property is True, the scrollbars specified in <A TImageEnView.ScrollBars> property will be displayed, even if when not required.
!!}
property ScrollBarsAlwaysVisible: boolean read GetScrollBarsAlwaysVisible write SetScrollBarsAlwaysVisible default false;
{!!
<FS>TImageEnView.VScrollBarParams
<FM>Declaration<FC>
property VScrollBarParams: <A TIEScrollBarParams>;
<FM>Description<FN>
The VScrollBarParams property allows an application to customize the vertical scrollbar behavior, including tracking (display refresh on mouse dragging), up/down buttons pixel scrolling, pagedown/up pixel scrolling.
<FM>Example<FC>
// disable tracking
ImageEnView1.VScrollBarParams.Tracking := False;
!!}
property VScrollBarParams: TIEScrollBarParams read fVScrollBarParams;
{!!
<FS>TImageEnView.HScrollBarParams
<FM>Declaration<FC>
property HScrollBarParams: <A TIEScrollBarParams>;
<FM>Description<FN>
The HScrollBarParams property allows an application to customize the horizontal scroll bar behavior, including tracking (display refresh on mouse dragging), left/right buttons pixel scrolling, pageleft/pageright pixel scrolling.
<FM>Example<FC>
// disable tracking
ImageEnView1.HScrollBarParams.Tracking := False;
!!}
property HScrollBarParams: TIEScrollBarParams read fHScrollBarParams;
procedure UpdateRect(rclip: TRect);
procedure PaintRect(const rc: TRect);
procedure LockUpdate;
procedure UnLockUpdate;
procedure UnLockUpdateEx;
property BackBuffer: TBitmap read GetBackBuffer;
procedure SetNavigator(nav: TImageEnView; options: TIENavigatorOptions = []);
{!!
<FS>TImageEnView.IsNavigator
<FM>Declaration<FC>
property IsNavigator: Boolean;
<FM>Description<FN>
Returns <FC>true<FN> if this object is used as a navigator of another TImageEnView object. A navigator shows a selection that controls the zoom and scroll of the main control.
See also: <A TImageEnView.SetNavigator>.
!!}
property IsNavigator: Boolean read fIsNavigator;
property MinBitmapSize: Integer read fMinBitmapSize write fMinBitmapSize;
procedure ChangeResolution(NewDPI: Integer; ResampleFilter: TResampleFilter=rfNone);
// layers
property LayersCount: integer read GetLayersCount;
property LayersCurrent: integer read fLayersCurrent write SetLayersCurrent;
function LayersAddEx(Kind: TIELayerKind;
PosX: Integer = -1; PosY: Integer = -1;
Width: Integer = 0; Height: Integer = 0;
SrcBitmap: TIEBitmap = nil; DoCopyBitmap: Boolean = true;
DoSaveUndo: Boolean = False; AssignDefaults: Boolean = False;
SelLayer: Boolean = True
): Integer;
function LayersAdd(Kind: TIELayerKind = ielkImage): integer; overload;
function LayersAdd(Width: Integer; Height: Integer; PixelFormat: TIEPixelFormat = ie24RGB; PosX: Integer = -1; PosY: Integer = -1): Integer; overload;
function LayersAdd(Bitmap: TIEBitmap; DoCopy: Boolean = true): integer; overload;
function LayersAdd(FileName: WideString; PosX: Integer = -1; PosY: Integer = -1): Integer; overload;
function LayersAdd(Shape: TIEShape; PosX: Integer = -1; PosY: Integer = -1; Width: Integer = 0; Height: Integer = 0): Integer; overload;
function LayersAdd(const Text: String;
FontSize : Integer;
FontColor : TColor;
const FontName : string;
FontStyle : TFontStyles = [];
PosX: Integer = -1; PosY: Integer = -1): Integer; overload;
function LayersInsertEx(Position: Integer;
Kind: TIELayerKind;
PosX: Integer = -1; PosY: Integer = -1;
Width: Integer = 0; Height: Integer = 0;
SrcBitmap: TIEBitmap = nil; DoCopyBitmap: Boolean = true;
DoSaveUndo: Boolean = False; AssignDefaults: Boolean = False;
SelLayer: Boolean = True
): Integer;
procedure LayersInsert(Position: Integer; Kind: TIELayerKind = ielkImage); overload;
procedure LayersInsert(Position: Integer; Width: Integer; Height: Integer; PixelFormat: TIEPixelFormat = ie24RGB; PosX: Integer = -1; PosY: Integer = -1); overload;
procedure LayersInsert(Position: Integer; Bitmap: TIEBitmap; DoCopy: Boolean = true); overload;
procedure LayersInsert(Position: Integer; FileName: WideString; PosX: Integer = -1; PosY: Integer = -1); overload;
procedure LayersInsert(Position: Integer; Shape: TIEShape; PosX: Integer = -1; PosY: Integer = -1; Width: Integer = 0; Height: Integer = 0); overload;
procedure LayersInsert(Position: Integer;
const Text: String;
FontSize : Integer;
FontColor : TColor;
const FontName : string;
FontStyle : TFontStyles = [];
PosX: Integer = -1; PosY: Integer = -1); overload;
procedure LayersRemoveEx(LyrIndex: Integer = -2; SaveUndo: Boolean = False);
procedure LayersRemove(LyrIndex: Integer = -2); virtual;
procedure LayersClear;
procedure LayersMove(CurIndex, NewIndex: integer);
procedure LayersMerge(Layer1, Layer2: integer; RemoveUpperLayer: Boolean = true); overload;
procedure LayersMerge(LayerList: array of integer); overload;
procedure LayersMerge(LayerList: TIEArrayOfInteger); overload;
procedure LayersMerge(); overload;
procedure LayersMergeTo(Layer1, Layer2: integer; Destination: TIEBitmap);
procedure LayersDrawTo(Destination: TIEBitmap; AdjustBitmap: Boolean = True);
procedure LayersSaveMergedTo(Destination: TIEBitmap; FastOutput: Boolean = False); overload;
procedure LayersSaveMergedTo(const Filename: string; FastOutput: Boolean = False); overload;
procedure LayersSaveMergedTo(const Stream: TStream; FileType: TIOFileType; FastOutput: Boolean = False); overload;
procedure LayersMergeAll(AlphaCompositing: Boolean = False);
procedure LayersCopyToAlpha(DestLayer: Integer);
procedure LayersCropBackground(SelectedOnly: Boolean = False; FillAlpha: Integer = 255; AllowReduce: Boolean = True; AllowEnlarge : Boolean = True);
procedure LayersActivateTextEditor(LayerIdx: integer);
procedure LayersCancelEditor(SaveChanges: Boolean = True);
function LayersCreateFromAlpha: Integer;
function LayersCreateFromEdge(ScreenX, ScreenY: integer; Tolerance: integer; MaxFilter: boolean = False): integer;
procedure LayersSetPropertiesEx(LayerIndex: integer; Props: TStrings; const PropName, Value: string; SaveUndo: Boolean; UndoMsg: string);
procedure LayersSetProperties(LayerIndex: integer; Props: TStrings); overload;
procedure LayersSetProperties(LayerIndex: integer; const PropName, Value: Variant); overload;
property LayersCaching: integer read fLayersCaching write SetLayersCaching;
property SoftCrop: TIESoftCropMode read fSoftCrop write SetSoftCrop;
property SoftCropValue: Integer read fSoftCropValue write SetSoftCropValue;
{!!
<FS>TImageEnView.LayersRotateStep
<FM>Declaration<FC>
property LayersRotateStep: Integer;
<FM>Description<FN>
Sets the degree of each rotation step when the user rotates a layer and presses the Shift key.
Default: 45
!!}
property LayersRotateStep: Integer read fLayersRotateStep write fLayersRotateStep;
{!!
<FS>TImageEnView.LayersMergeFilter
<FM>Declaration<FC>
property LayersMergeFilter: <A TResampleFilter>;
<FM>Description<FN>
Specifies the quality that is used when merging layers.
It applies to:
- <A TImageEnView.LayersMerge>
- <A TImageEnView.LayersMergeAll>
- <A TImageEnView.LayersMergeTo>
- <A TImageEnView.LayersSaveMergedTo>
Note: Has no effect on image layers, if they have a custom <A TIEImageLayer.UseResampleFilter>
Default: rfLanczos3
!!}
property LayersMergeFilter: TResampleFilter read fLayersMergeFilter write fLayersMergeFilter;
{!!
<FS>TImageEnView.LayersSelectConstrains
<FM>Declaration<FC>
property LayersSelectConstrains: Boolean;
<FM>Description<FN>
If true, selection constraints are active, i.e. whether the layer itself can be selected is controlled at the layer level by <A TIELayer.Selectable>.
Default: True
!!}
property LayersSelectConstrains: Boolean read fLayersSelectConstrains write fLayersSelectConstrains;
{!!
<FS>TImageEnView.Layers
<FM>Declaration<FC>
property Layers[idx: Integer]: <A TIELayer>;
<FM>Description<FN>
The Layers property provides access to properties related to a specified <L TIELayer>layer</L>.
<FM>Example<FC>
// Set the opacity of the first layer to 0.5 (50%)
ImageEnView.Layers[0].Opacity := 0.5;
ImageEnView.Update;
// Set the transparency of the first layer to 50
ImageEnView.Layers[0].Transparency := 50;
ImageEnView.Update;
// Hide layer 1
ImageEnView.Layers[1].Visible := False;
ImageEnView.Update;
// Create a magnify layer
idx := ImageEnView.LayersAdd;
with ImageEnView.Layers[idx] do
begin
Magnify.Enabled := true;
Magnify.Rate := 2; // x2 magnification
Width := 50;
Height := 50;
end;
!!}
property Layers[idx: integer]: TIELayer read GetLayer;
property LayerOptions: TIELayerOptions read fLayerOptions write SetLayerOptions;
property LayerDefaults: TStringList read GetLayerDefaults;
property LayersList: TList read fLayers;
{!!
<FS>TImageEnView.LayersResizeAspectRatio
<FM>Declaration<FC>
property LayersResizeAspectRatio: <A TIELayersResizeAspectRatio>;
<FM>Description<FN>
Specifies whether ImageEn respects the aspect ratio of layers when resizing.
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>iearDisabled</C> <C>Doesn't maintain the aspect ratio even when pressing ALT or setting <A TImageEnView.ForceALTKey>)</C> </R>
<R> <C>iearALTKey</C> <C>Maintains the aspect ratio only when pressing ALT or setting <A TImageEnView.ForceALTKey></C> </R>
<R> <C>iearAlways</C> <C>The aspect ratio is always maintained (using any grips)</C> </R>
<R> <C>iearAlwaysOnCornerGrip</C> <C>The aspect ratio is always maintained if the corner grips are used</C> </R>
<R> <C>iearLayerDefaultOnCornerGrip</C> <C>If the layer has a <L TIELayer.PreferredAspectRatio>preferred aspect ratio</L> then dragging the corner grip will maintain the aspect ratio, for side grips and other layer types the aspect ratio is not maintained</C> </R>
</TABLE>
Default: iearALTKey
Note: ImageEn will always enforce the aspect ratio if <A TIELayer.AspectRatioLocked> = true
<FM>See Also<FN>
- <A TIELayer.AspectRatioLocked>
- <A TIELayer.PreferredAspectRatio>
- <A TIELayer.RestoreAspectRatio>
!!}
property LayersResizeAspectRatio: TIELayersResizeAspectRatio read fLayersResizeAspectRatio write fLayersResizeAspectRatio;
property LayersCropped: Boolean read fLayersCropped write SetLayersCropped;
{!!
<FS>TImageEnView.LayersFastDrawing
<FM>Declaration<FC>
property LayersFastDrawing: <A TIEFastDrawing>;
<FM>Description<FN>
Improves the performance of <L TIELayer>layer</L> rendering by delayed or disabling slow operations.
<TABLE>
<R> <H>Item</H> <H>Description</H> </R>
<R> <C><FC>iefFast<FN></C> <C>Layer draw quality is reduced to speed up display (only affects drawing)</C> </R>
<R> <C><FC>iefDelayed<FN></C> <C>Fast drawing (iefFast) is used while rotating, moving or resizing layers. After a delay, layers are then drawn at normal quality (iefNormal)</C> </R>
<R> <C><FC>iefNormal<FN></C> <C>Layers are drawn at normal quality</C> </R>
</TABLE>
When fast drawing is active:
- Does not render <A TIELayer.AlphaEdgeFeathering> or <A TIELayer.SoftShadow>
- Zoom filters are not used for display (<A TImageEnView.ZoomFilter> and <A TIEImageLayer.ResampleFilter>)
- <A TIELayer.AntiAlias> is not rendered
- Anti-alias filters are not used for rotation preview (<A TImageEnView.LayersRotationAntialias>)
Default: iefDelayed
Note: <FC>LayersFastDrawing<FN> only affects what is displayed. It has no effect on the quality of the image when saved
<FM>Examples<FC>
// Use best display quality (even when manipulating layers)
ImageEnView1.LayersFastDrawing := iefNormal;
// Use best display quality, except when manipulating layers
ImageEnView1.LayersFastDrawing := iefDelayed;
// Best performance - always display layers at lower quality
ImageEnView1.LayersFastDrawing := iefFast;
// Allow changing of preview quality via a combo-box
procedure Tfmain.cmbPreviewQualityChange(Sender: TObject);
const
_cmbPreviewQuality_Fast = 0;
_cmbPreviewQuality_Delayed_Best = 1;
_cmbPreviewQuality_Best = 2;
begin
// Seperate code to make it easier to understand
if cmbPreviewQuality.ItemIndex = _cmbPreviewQuality_Fast then
begin
// FASTEST DISPLAY
// Zoom filter
ImageEnView1.ZoomFilter := rfNone;
ImageEnView1.DelayZoomFilter := False;
// Rotation anti-alias
ImageEnView1.LayersRotationFilter := ierBicubic;
ImageEnView1.LayersRotationAntialias := True;
ImageEnView1.LayersRotationUseFilterOnPreview := False;
// Fast drawing
ImageEnView1.LayersFastDrawing := iefFast;
end
else
if cmbPreviewQuality.ItemIndex = _cmbPreviewQuality_Delayed_Best then
begin
// DELAYED HIGH QUALITY
// Zoom filter
ImageEnView1.ZoomFilter := rfLanczos3;
ImageEnView1.DelayZoomFilter := True;
// Rotation anti-alias
ImageEnView1.LayersRotationFilter := ierBicubic;
ImageEnView1.LayersRotationAntialias := True;
ImageEnView1.LayersRotationUseFilterOnPreview := True;
// Fast drawing
ImageEnView1.LayersFastDrawing := iefDelayed;
end
else
begin
// HIGH QUALITY
// Zoom filter
ImageEnView1.ZoomFilter := rfLanczos3;
ImageEnView1.DelayZoomFilter := False;
// Rotation anti-alias
ImageEnView1.LayersRotationFilter := ierBicubic;
ImageEnView1.LayersRotationAntialias := True;
ImageEnView1.LayersRotationUseFilterOnPreview := True;
// Fast drawing
ImageEnView1.LayersFastDrawing := iefNormal;
end;
ImageEnView1.Update();
end;
<FM>See Also<FN>
- <A TImageEnView.LayersCaching>
!!}
property LayersFastDrawing: TIEFastDrawing read fLayersFastDrawing write fLayersFastDrawing;
function LayersFastDrawingActive: Boolean;
// Not exposed in doc index
{!!
<FS>TImageEnView.LayersFastOutput
<FM>Declaration<FC>
property LayersFastOutput: Boolean;
<FM>Description<FN>
Improves the performance of <L TIELayer>layer</L> output operations, such as merging, by disabling anti-aliasing and other quality properties.
Default: False
Note: <FC>LayersFastOutput<FN> only affects output. Drawing is controlled by <A TImageEnView.LayersFastDrawing>
!!}
property LayersFastOutput: Boolean read fLayersFastOutput write fLayersFastOutput;
function MaxLayerWidth() : integer;
function MaxLayerHeight() : integer;
function LayersRect(SelOnly: Boolean = False; ExcludeLayer0: Boolean = False): TIERectangle;
property LayersDrawBox: boolean read fLayersDrawBox write SetLayersDrawBox;
function FindLayerAt(x, y: integer; SelectablesOnly: Boolean=true): integer;
property CurrentLayer: TIELayer read GetCurrentLayer;
procedure LayersSaveToStream(Stream: TStream; CompressionFormat: TIOFileType = -1; SelectedOnly: Boolean = False; ExcludeImageLayers: Boolean = False; SaveThumbnail: Boolean = False; ProgressEvent: TIEProgressEvent = nil);
procedure LayersSaveToFile(const FileName: String; CompressionFormat: TIOFileType = -1); {$ifdef IEWarningForDeprecated} deprecated {$ifdef IESupportDeprecatedDescription} 'Use TImageEnIO.SaveToFileIEN instead - http://imageen.com/help/Compatibility.html' {$endif}; {$endif}
procedure LayersSelectAll(bIncludeLocked: Boolean = True);
procedure LayersAlign(Alignment: TIEAlignLayers; Index: Integer = -2);
function LayersSelCount(bCountBackgroundLayer: Boolean = True): Integer;
procedure LayersDeselectAll();
procedure LayersGroup(bSelectedOnly: Boolean = True);
procedure LayersConvertToImageLayersEx(LayerIdx: integer = -2; QualityFactor: Double = 2; CropAlpha: Boolean = True; ConvertImages: Boolean = False; DoSaveUndo: Boolean = False);
procedure LayersConvertToImageLayers(LayerIdx: integer = -2; QualityFactor: Double = 2; CropAlpha: Boolean = True; ConvertImages: Boolean = False);
procedure LayersUngroup(bSelectedOnly: Boolean = True);
function LayersLoadFromStream(Stream: TStream; Append: Boolean = False; ProgressEvent: TIEProgressEvent = nil): Boolean;
function LayersLoadFromFile(const FileName: String; Append: Boolean = False): Boolean; {$ifdef IEWarningForDeprecated} deprecated {$ifdef IESupportDeprecatedDescription} 'Use TImageEnIO.LoadFromFileIEN instead - http://imageen.com/help/Compatibility.html' {$endif}; {$endif}
function LayersCreateFromSelection: Integer;
function LayersCreateFromClipboard: Integer;
function LayersCreateFromFile(Filename : string = '') : integer;
function LayersImport(const FileName: String; Stream: TStream = nil; FileFormat: TIOFileType = ioUnknown; Append: Boolean = False): Integer;
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 7.0.0 (23/11/16)
function LayersCreateFromText(const Text : string;
const sFontName : string;
iFontSize : Integer;
cFontColor : TColor;
Style : TFontStyles;
bAddShadow: boolean = False;
iBlurRadius : Integer = 3;
iShadowOffset : Integer = 2;
Angle : Integer = 0;
bAntiAlias : Boolean = true) : integer; {$ifdef IEWarningForDeprecated} deprecated {$ifdef IESupportDeprecatedDescription} 'Use TIETextLayer instead - http://imageen.com/help/Compatibility.html' {$endif}; {$endif}
{$endif}
procedure LayersFixSizes(layer: Integer = -1);
procedure LayersFixRotations(layer: Integer = -1);
procedure LayersFixBorders(layer: Integer = -1);
// Other
procedure SetExternalBitmap(bmp: TIEBitmap);
property GradientEndColor: TColor read fGradientEndColor write SetGradientEndColor;
property MouseCapture;
procedure Assign(Source: TObject); reintroduce; virtual;
procedure AssignSelTo(Dest: TPersistent);
procedure ImageChange(); override;
function YScr2Bmp(y: integer; CurrentLayer: Boolean = False): integer;
function XScr2Bmp(x: integer; CurrentLayer: Boolean = False): integer;
function YBmp2Scr(y: integer; CurrentLayer: Boolean = False): integer;
function XBmp2Scr(x: integer; CurrentLayer: Boolean = False): integer;
procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
property SelColor1: TColor read fSelColor1 write SetSelColor1 default clBlack;
property SelColor2: TColor read fSelColor2 write SetSelColor2 default clWhite;
procedure Clear;
procedure ClearAll;
procedure Blank;
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 6.2.2 (8/2/2016)
// Note: deprecated 'Use DisplayGridKind instead'
property DisplayGrid: boolean read GetDisplayGrid write SetDisplayGrid;
{$endif}
property DisplayGridLyr: integer read fDisplayGridLyr write SetDisplayGridLyr;
property HighlightedPixel: TPoint read fHighlightedPixel write SetHighlightedPixel;
property HighlightedPixelColor: TColor read fHighlightedPixelColor write SetHighlightedPixelColor;
procedure SetInteractionHint(const Text: String; x, y: Integer; const minText: String);
property ImageHorizAlignment: TIEHAlign read fImageHorizAlignment write SetImageHorizAlignment;
property ImageVertAlignment: TIEVAlign read fImageVertAlignment write SetImageVertAlignment;
procedure SetChessboardStyle(Size: integer = 16; BrushStyle: TBrushStyle = bsSolid; Color1: TColor = clNone_; Color2: TColor = clNone_);
{!!
<FS>TImageEnView.ForceALTkey
<FM>Declaration<FC>
property ForceALTkey: Boolean;
<FM>Description<FN>
Set ForceALTkey to True to emulate pressing the ALT key.
When enabled (or the Alt key is pressed) the aspect ratio is maintained when making or modifying selections (or with vectorial objects for the <A TImageEnVect> component).
Default: False
!!}
property ForceALTkey: boolean read fForceALTkey write fForceALTkey;
{!!
<FS>TImageEnView.DelayDisplaySelection
<FM>Declaration<FC>
property DelayDisplaySelection: Boolean
<FM>Description<FN>
When True, the selection is displayed with a delay. This allows you to quickly navigate the image (zoom and scroll) and once you have finished navigation the selection is displayed.
!!}
property DelayDisplaySelection: boolean read fDelayDisplaySelection write fDelayDisplaySelection;
{!!
<FS>TImageEnView.DelayZoomTime
<FM>Declaration<FC>
property DelayZoomTime: Integer;
<FM>Description<FN>
ImageEn has a timer that decrements a counter at each tick (you can set the tick delay using <A TImageEnView.DelayTimer> property). This timer controls the selection animation and the application of filters on scrolling (when <A TImageEnView.DelayZoomFilter> is true).
DelayZoomTime controls only the delay zoom filter application.
Default: 4 (apply filter after 4 ticks since last scroll)
!!}
property DelayZoomTime: integer read fDelayZoomTicks write fDelayZoomTicks;
property DelayTimer: integer read GetDelayTimer write SetDelayTimer;
procedure CopyToBitmapWithAlpha(Dest: TBitmap; DestX, DestY: integer);
property UseDrawDibDraw: boolean read fUseDrawDibDraw write fUseDrawDibDraw;
function GetGripAt(x, y: integer): TIEGrip;
procedure RemoveAlphaChannel(Merge: boolean = false); override;
property IsEmpty: boolean read GetIsEmpty;
property IsEmpty2: boolean read GetIsEmpty2;
{!!
<FS>TImageEnView.ZoomSelectionAspectRatio
<FM>Declaration<FC>
property ZoomSelectionAspectRatio: Boolean;
<FM>Description<FN>
This property is active when MouseInteract contains miSelectZoom. If true, the selected rectangle is adjusted to maintain the aspect ratio. Otherwise (false) the image loses the aspect ratio (ZoomX <> ZoomY and Zoom value is invalid) making it stretched inside the window.
Default: True
!!}
property ZoomSelectionAspectRatio: Boolean read fZoomSelectionAspectRatio write fZoomSelectionAspectRatio;
{!!
<FS>TImageEnView.MouseScrollRate
<FM>Declaration<FC>
property MouseScrollRate: Double;
<FM>Description<FN>
If <A TImageEnView.MouseInteract> contains miScroll then MouseScrollRate specifies the relationship between mouse movement and image scrolling.
Default: 1 (one mouse point is one pixel at 100%).
<FM>Example<FC>
// set double rate scroll
ImageEnView1.MouseScrollRate := 2;
ImageEnView1.MouseInteract := [miScroll];
!!}
property MouseScrollRate: Double read fMouseScrollRate write fMouseScrollRate;
// encapsulated components
{!!
<FS>TImageEnView.IO
<FM>Declaration<FC>
property IO: <A TImageEnIO>;
<FM>Description<FN>
The IO property encapsulates the TImageEnIO component inside TImageEnView (The TImageEnIO component is automatically created the first time you access the IO property).
<A TImageEnIO> provides all functionality for loading and saving images, and other I/O functions.
<FM>Example<FC>
// Load image into the ImageEnView
ImageEnView1.IO.LoadFromFile('C:\Hello.jpeg');
// Retrieve an image from a Twain scanner and display in the ImageEnView
ImageEnView1.IO.Acquire;
!!}
property IO: TImageEnIO read GetImageEnIO;
{!!
<FS>TImageEnView.Proc
<FM>Declaration<FC>
property Proc: <A TImageEnProc>;
<FM>Description<FN>
Provides access to the TImageEnProc component inside TImageEnView (The TImageEnProc component is automatically created the first time you access the Proc property).
<A TImageEnProc> provides functionality for editing and manipulating images, clipboard and analysis.
<FM>Example<FC>
// Invert the colors of the image in the ImageEnView
ImageEnView1.Proc.Negative;
!!}
property Proc: TImageEnProc read GetImageEnProc;
// transitions
property TransitionRunning: boolean read GetTransitionRunning;
procedure PrepareTransition;
procedure RunTransition(Effect: TIETransitionType; Duration: integer); overload;
procedure RunTransition(Effect: TIETransitionType; Duration: integer; PanZoomEffect : TIEPanZoomType; iZoomLevel : Integer; Smoothing: Integer = 96); overload;
procedure RunTransition(Effect: TIETransitionType; Duration: integer; StartRect, EndRect : TRect; bMaintainAspectRatio : Boolean = True; Smoothing: Integer = 96); overload;
procedure AbortTransition;
{$ifdef IEIncludeDeprecatedInV4}
// Deprecated prior to v4.3.1
// DEPRECATED: Use TImageEnProc.PrepareTransitionBitmaps/CreateTransitionBitmaps
procedure PrepareTransitionBitmaps(OriginalBitmap, TargetBitmap : TBitmap; Effect : TIETransitionType; iWidth : Integer = -1; iHeight : Integer = -1); overload; {$ifdef IEWarningForDeprecated} deprecated; {$endif}
procedure PrepareTransitionBitmaps(OriginalBitmap, TargetBitmap : TBitmap; Effect : TIETransitionType; StartRect, EndRect : TRect; bMaintainAspectRatio : Boolean = True; iWidth : Integer = -1; iHeight : Integer = -1); overload; {$ifdef IEWarningForDeprecated} deprecated; {$endif}
procedure CreateTransitionBitmap(TransitionProgress : Single; DestBitmap : TBitmap); {$ifdef IEWarningForDeprecated} deprecated; {$endif}
{$endif}
property TransitionTiming: TIETransitionTiming read GetTransitionTiming write SetTransitionTiming;
// post frames
procedure BeginPostFrames(target: TImageEnView; delay: Integer; interval: Integer);
procedure EndPostFrames(target: TImageEnView);
// others undocumented
procedure DrawLayerBox(ABitmap: TBitmap; idx: integer);
procedure DrawLayerOuter(ABitmap: TBitmap; idx: Integer);
procedure DrawLayerGrips(ABitmap: TBitmap; idx: integer);
procedure DrawLayerRotationCenter(ABitmap: TBitmap; idx: integer);
procedure CalcLayerGripCoords(layeridx: integer; var coords: array of TRect);
function FindLayerGripAnySel(x, y: integer; bSetCurrent: Boolean = False): TIEGrip;
function FindLayerGrip(x, y: integer): TIEGrip;
function FindLayerGripDist(x, y: integer; var Distance: Double): TIEGrip;
{!!
<FS>TImageEnView.Gestures
<FM>Declaration<FC>
property Gestures: <A TIEViewerGestures>;
<FM>Description<FN>
TImageEnView supports native Windows gestures:
- Dragging the screen to pan an image
- Pinching to zoom in
- Reverse-pinching to zoom out
- Layer rotation and movement
ImageEn automatically handles the Windows gesture events, so adding gesture support to your applications requires only that you enable the relevant Gesture property.
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Display\Gestures\Gestures.dpr </C> </R>
</TABLE>
<FM>Example<FC>
// enable Pan (scroll) and Zoom gestures
ImageEnView1.Gestures.Pan.Enabled := True;
ImageEnView1.Gestures.Zoom.Enabled := True;
!!}
property Gestures: TIEViewerGestures read fGestures; // not: cannot stay in published section without an property editor
{!!
<FS>TImageEnView.RulerParams
<FM>Declaration<FC>
property RulerParams: <A TIEViewRulerParams>;
<FM>Description<FN>
Allows you to configure the properties of your <L TImageEnView.ShowRulers>rulers</L>
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Other\ImageEnViewRulers\ImageEnViewRulers.dpr </C> </R>
</TABLE>
<FM>Example<FC>
// Show rulers in TImageEnView
ImageEnView1.ShowRulers := [ rdHorizontal, rdVertical ];
// Set units to CM
ImageEnView1.RulerParams.Units := ieruCentimeters;
!!}
property RulerParams: TIEViewRulerParams read fRulerParams;
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 6.2.2 (2015-12-15)
// Since v6.2.2 these methods/properties are deprecated, and just point to IO.Params.DPI
property DpiX: integer read GetDpiX write SetDPIX;
property DpiY: integer read GetDpiY write SetDPIY;
procedure SetDPI(dpiX, dpiY: integer); virtual;
{$endif}
property Modified: Boolean read GetModified write SetModified;
property CropToolInteraction: TIECropToolInteraction read GetCropToolInteraction;
property UserInteractions: TObjectList read fUserInteractions;
published
// Remove default so we can change this property later
property LegacyBitmap: boolean read fLegacyBitmap write SetLegacyBitmap;
property ZoomFilter: TResampleFilter read fZoomFilter write SetZoomFilter default rfNone;
property ScrollBars: TIEScrollStyle read fScrollBars write SetScrollBars default ssBoth;
{!!
<FS>TImageEnView.OnViewChange
<FM>Declaration<FC>
property OnViewChange: <A TViewChangeEvent>;
<FM>Description<FN>
Notifies of changes to <A TImageEnView.Zoom>, <A TImageEnView.ViewX> or <A TImageEnView.ViewY>.
!!}
property OnViewChange: TViewChangeEvent read fOnViewChange write fOnViewChange;
{!!
<FS>TImageEnView.OnImageEnGesture
<FM>Declaration<FC>
property OnImageEnGesture: <A TIEImageEnGestureEvent>;
<FM>Description<FN>
Occurs whenever a gesture is handled by ImageEn.
!!}
property OnImageEnGesture: TIEImageEnGestureEvent read fOnImageEnGesture write fOnImageEnGesture;
{!!
<FS>TImageEnView.OnFinishSmoothTask
<FM>Declaration<FC>
property OnFinishSmoothTask: <A TIEFinishSmoothTaskEvent>;
<FM>Description<FN>
Notifies that a smooth task has finished (smooth scroll, smooth zoom, etc...).
<FM>See Also<FN>
- <A TImageEnView.SetZoomSmooth>
- <A TImageEnView.SetViewXYSmooth>
!!}
property OnFinishSmoothTask: TIEFinishSmoothTaskEvent read fOnFinishSmoothTask write fOnFinishSmoothTask;
{!!
<FS>TImageEnView.OnViewChanging
<FM>Declaration<FC>
property OnViewChanging: <A TViewChangeEvent>;
<FM>Description<FN>
Notifies that <A TImageEnView.Zoom>, <A TImageEnView.ViewX> or <A TImageEnView.ViewY> is going to change.
!!}
property OnViewChanging: TViewChangingEvent read fOnViewChanging write fOnViewChanging;
{!!
<FS>TImageEnView.OnImageChange
<FM>Declaration<FC>
property OnImageChange: TNotifyEvent;
<FM>Description<FN>
Occurs after the image is modified using <A TImageEnProc> (<A TImageEnView.Proc> property) or <A TImageEnIO> (<A TImageEnView.IO> property).
If you are using multiple <A TImageEnView.Layers>, then layer changes will also trigger <FC>OnImageChange<FN>.
Note: <A TImageEnProc> calls <A TImageEnView.ImageChange> after a modification which actuates <FC>OnImageChange<FN>.
<FM>See Also<FN>
- <A TImageEnView.Modified>
- <A TIEBitmap.Modified>
- <A TIELayer.Modified>
!!}
property OnImageChange: TNotifyEvent read fOnImageChange write fOnImageChange;
property SelectionOptions: TIESelectionOptions read fSelectionOptions write SetSelectionOptions default [iesoAnimated, iesoSizeable, iesoMoveable, iesoCanScroll, iesoAllowMoveByKeyboard];
property Center: boolean read GetCenter write SetCenter default true;
property MouseInteract: TIEMouseInteract read GetMouseInteract write SetMouseInteract default [];
{!!
<FS>TImageEnView.AutoFit
<FM>Declaration<FC>
property AutoFit: Boolean;
<FM>Description<FN>
If AutoFit is True and the image is updated (<A TImageEnView.Update> method) then <A TImageEnView.Fit> is automatically called to make the image fit within the display area (stretch if it is smaller than the window, shrink if it is larger).
This has the same effect as setting both <A TImageEnView.AutoStretch> and <A TImageEnView.AutoShrink> to true.
<FM>Example<FC>
procedure TForm1.chkAutoFitClick(Sender: TObject);
begin
ImageEnView1.AutoFit := chkAutoFit.Checked;
if not chkAutoFit.Checked then
ImageEnView1.Zoom := 100
else
ImageEnView1.Fit();
end;
<FM>See Also<FN>
- <L TImageEnView.LayerOptions>loFitToLayersWhenZooming</L>
!!}
property AutoFit: boolean read fAutoFit write fAutoFit default false;
{!!
<FS>TImageEnView.SelectionBase
<FM>Declaration<FC>
property SelectionBase: <A TIESelectionBase>;
<FM>Description<FN>
Specifies the meaning of coordinates used when creating a selection.
If SelectionBase is iesbClientArea, then all coordinates depend upon the actual zoom and window view/scrolling (i.e. pixels on screen). Otherwise, if SelectionBase is iesbBitmap, then all coordinates refer to bitmap pixels.
Default: iesbClientArea
Note: Specify before creating a selection. It has no effect on existing selections.
<FM>Examples<FC>
// Select a 100 pixel square in the top left corner of the bitmap
ImageEnView1.SelectionBase := iesbBitmap;
ImageEnView1.Select(0, 0, 100, 100);
// Select a 100 pixel square that is displayed in the top left corner of the ImageEnView window (100 x 100 pixels of screen area)
ImageEnView1.SelectionBase := iesbClientArea;
ImageEnView1.Select(0, 0, 100, 100);
Note: When ImageEnView1.Zoom = 100, ImageEnView1.ViewX = 0 and ImageEnView1.ViewY = 0, then both the preceeding examples will provide the same result
// Select 20% of the center of the screen
const
Selection_Size = 0.2; // 20% of screen area
begin
ImageEnView1.SelectionBase := iesbClientArea;
ImageEnView1.Select(Trunc((0.5 - Selection_Size / 2) * ImageEnView1.ClientWidth),
Trunc((0.5 - Selection_Size / 2) * ImageEnView1.ClientHeight),
Trunc((0.5 + Selection_Size / 2) * ImageEnView1.ClientWidth),
Trunc((0.5 + Selection_Size / 2) * ImageEnView1.ClientHeight));
end;
<FM>See Also<FN>
- <A TImageEnView.Select>
- <A TImageEnView.XBmp2Scr>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.YBmp2Scr>
- <A TImageEnView.YScr2Bmp>
!!}
property SelectionBase: TIESelectionBase read fSelectionBase write fSelectionBase default iesbClientArea;
property BackgroundStyle: TIEBackgroundStyle read fBackgroundStyle write SetBackgroundStyle default iebsSolid;
{!!
<FS>TImageEnView.OnSelectionChange
<FM>Declaration<FC>
property OnSelectionChange: TNotifyEvent;
<FM>Description<FN>
Occurs whenever the user finishes a change of the current selection.
Methods such as <A TImageEnView.AddSelPoint> or <A TImageEnView.Select> do not call the OnSelectionChange event.
<FM>Example 1<FC>
// Real time copy of user selected region to ImageEnView2 component
ImageEnView2.Clear;
ImageEnView2.CopyFromPolygon(ImageEnView2.Bitmap, ImageEnView2.PolySelPoints^, ImageEnView2.PolySelCount, Point(0, 0));
// An enhancement of above example: Display the current user selection as zoomed in ImageEnView2
ImageEnView2.Clear;
ImageEnView2.CopyFromPolygon(ImageEnView2.Bitmap, ImageEnView2.PolySelPoints^, ImageEnView2.PolySelCount, Point(0, 0));
ImageEnView2.Zoom := 200;
<FM>Example 2<FC>
procedure Tfmain.ImageEnView1LayerSelectionChange(Sender: TObject);
var
bmp: TIEBitmap;
begin
// Show a preview in ImageEnView2 of the layer selected in ImageEnView1
bmp := ImageEnView2.IEBitmap;
ImageEnView1.CurrentLayer.CopyToBitmap( bmp, ImageEnView2.IdealImageWidth, ImageEnView2.IdealImageWidth );
ImageEnView2.Update();
end;
!!}
property OnSelectionChange: TNotifyEvent read fOnSelectionChange write fOnSelectionChange;
{!!
<FS>TImageEnView.OnSelectionChanging
<FM>Declaration<FC>
property OnSelectionChanging: TNotifyEvent;
<FM>Description<FN>
Occurs whenever the user is modifying the current selection.
Note: Methods such as <A TImageEnView.AddSelPoint> or <A TImageEnView.Select> do not call the OnSelectionChanging event.
<FM>Example<FC>
// Show the user selection rect (MouseInteract includes miSelectZoom)
procedure TForm.ImageEnVect1SelectionChanging(Sender: TObject);
begin
StatusBar.Panels[0].Text := '(' + IntToStr( ImageEnVect1.SelX1 ) +
',' + IntToStr( ImageEnVect1.SelY1 ) + '), ' +
'(' + IntToStr( ImageEnVect1.SelX2 ) +
',' + IntToStr( ImageEnVect1.SelY2) + ')';
end;
!!}
property OnSelectionChanging: TNotifyEvent read fOnSelectionChanging write fOnSelectionChanging;
{!!
<FS>TImageEnView.OnMouseInSel
<FM>Declaration<FC>
property OnMouseInSel: TNotifyEvent;
<FM>Description<FN>
OnMouseInSel is called whenever the mouse is inside a selection or over its contours.
!!}
property OnMouseInSel: TNotifyEvent read fOnMouseInSel write fOnMouseInSel;
{!!
<FS>TImageEnView.DelayZoomFilter
<FM>Declaration<FC>
property DelayZoomFilter: Boolean
<FM>Description<FN>
If DelayZoomFilter is True, the zoom filter (which enhances the display quality) is only applied after a delay.
This allows you to quickly navigate the image (zoom and scroll) and once the navigation has finished the image will be displayed at best quality.
<FM>See Also<FN>
- <A TImageEnView.LayersFastDrawing>
!!}
property DelayZoomFilter: boolean read fDelayZoomFilter write fDelayZoomFilter default false;
{!!
<FS>TImageEnView.OnPaint
<FM>Declaration<FC>
property OnPaint: TNotifyEvent;
<FM>Description<FN>
OnPaint occurs whenever the component is redrawn. Use OnPaint to perform special processing when the component is redrawn.
!!}
property OnPaint: TNotifyEvent read fOnPaint write fOnPaint;
property EnableAlphaChannel: boolean read fEnableAlphaChannel write SetEnableAlphaChannel default true;
{!!
<FS>TImageEnView.OnMouseInResizingGrip
<FM>Declaration<FC>
property OnMouseInResizingGrip: <A TIEMouseInResizingGripEvent>;
<FM>Description<FN>
OnMouseInResizingGrip is called whenever the mouse moves over a grip and tells you which resizing grip is under the mouse position.
!!}
property OnMouseInResizingGrip: TIEMouseInResizingGripEvent read fOnMouseInResizingGrip write fOnMouseInResizingGrip;
{!!
<FS>TImageEnView.OnZoomIn
<FM>Declaration<FC>
property OnZoomIn: <A TIEZoomEvent>;
<FM>Description<FN>
An OnZoomIn event occurs whenever the user zooms into the current image (increases its display size).
You can programatically customize the zoom value by changing the <FC>NewZoom<FN> parameter.
<FM>Example<FC>
// Max zoom-in of 50%
Procedure Tform1.ImageEnView1ZoomIn(Sender: TObject; var NewZoom: Double);
Begin
if NewZoom < 50 then
NewZoom := 50;
End;
!!}
property OnZoomIn: TIEZoomEvent read fOnZoomIn write fOnZoomIn;
{!!
<FS>TImageEnView.OnZoomOut
<FM>Declaration<FC>
property OnZoomOut: <A TIEZoomEvent>;
<FM>Description<FN>
An OnZoomOut event occurs whenever the user zooms out of the current image (reduces its display size).
You can programatically customize the zoom value by changing the <FC>NewZoom<FN> parameter.
<FM>Example<FC>
// Max zoom-out to 200%
Procedure Tform1.ImageEnView1ZoomOut(Sender: TObject; var NewZoom: Double);
Begin
if NewZoom > 200 then
NewZoom := 200;
End;
!!}
property OnZoomOut: TIEZoomEvent read fOnZoomOut write fOnZoomOut;
property OnProgress: TIEProgressEvent read GetOnProgress write SetOnProgress;
property OnFinishWork: TNotifyEvent read GetOnFinishWork write SetOnFinishWork;
property OnAcquireBitmap: TIEAcquireBitmapEvent read GetOnAcquireBitmap write SetOnAcquireBitmap;
{!!
<FS>TImageEnView.AutoStretch
<FM>Declaration<FC>
property AutoStretch: Boolean;
<FM>Description<FN>
When enabled the following display rules are used:
- If an image is bigger than TImageEnView window it is displayed 100%;
- If an image is smaller than TImageEnView window it is stretched to fit
<FM>Example<FC>
procedure TForm1.chkAutoStretchClick(Sender: TObject);
begin
ImageEnView1.AutoStretch := chkAutoStretch.Checked;
if not chkAutoStretch.Checked then
ImageEnView1.Zoom := 100
else
ImageEnView1.Fit();
end;
<FM>See Also<FN>
- <A TImageEnView.AutoShrink>
- <A TImageEnView.AutoFit>
- <L TImageEnView.LayerOptions>loFitToLayersWhenZooming</L>
!!}
property AutoStretch: boolean read fAutoStretch write fAutoStretch default false;
{!!
<FS>TImageEnView.AutoShrink
<FM>Declaration<FC>
property AutoShrink: Boolean;
<FM>Description<FN>
When enabled the following display rules are used:
- If an image is bigger than TImageEnView window it is shrunk to fit;
- If an image is smaller than TImageEnView window it is displayed 100%
<FM>Example<FC>
procedure TForm1.chkAutoShrinkClick(Sender: TObject);
begin
ImageEnView1.AutoShrink := chkAutoShrink.Checked;
if not chkAutoShrink.Checked then
ImageEnView1.Zoom := 100
else
ImageEnView1.Fit();
end;
<FM>See Also<FN>
- <A TImageEnView.AutoStretch>
- <A TImageEnView.AutoFit>
- <L TImageEnView.LayerOptions>loFitToLayersWhenZooming</L>
!!}
property AutoShrink: boolean read fAutoShrink write fAutoShrink default false;
{!!
<FS>TImageEnView.OnBeforeSelectionChange
<FM>Declaration<FC>
property OnBeforeSelectionChange: TNotifyEvent;
<FM>Description<FN>
OnBeforeSelectionChange occurs prior to the change of a selection.
!!}
property OnBeforeSelectionChange: TNotifyEvent read fOnBeforeSelectionChange write fOnBeforeSelectionChange;
property DrawVersion: boolean read fDrawVersion write SetDrawVersion default false;
{!!
<FS>TImageEnView.OnDrawBackBuffer
<FM>Declaration<FC>
property OnDrawBackBuffer: TNotifyEvent;
<FM>Description<FN>
OnDrawBackBuffer provides access to the back buffer where ImageEn will draw the image (and layers) just before the paint event.
You can draw on the <A TImageEnView.BackBuffer> by handling the OnDrawBackBuffer event to paint onto the Backbuffer canvas.
<A TImageEnView.BackBuffer> is updated whenever <A TImageEnView.Update> is called.
<FM>Example<FC>
Procedure Form1OnDrawBackBuffer(Sender: TObject);
Begin
// Draw a red line on the back buffer
With ImageEnView1.BackBuffer.Canvas do
begin
Pen.Color := clRed;
MoveTo( 0, 0 );
LineTo( 100, 100 );
End;
End;
!!}
property OnDrawBackBuffer: TNotifyEvent read fOnDrawBackBuffer write fOnDrawBackBuffer;
{!!
<FS>TImageEnView.OnLayerNotify
<FM>Declaration<FC>
property OnLayerNotify: <A TIELayerNotify>;
<FM>Description<FN>
Occurs whenever a layer is selected, moved or resized. This event only fires on user actions (not programatic changes).
<FM>Example<FC>
// Log all resizing of layers
procedure TfrmMain.IEView1LayerNotify(Sender: TObject; layer: integer; event: TIELayerEvent);
var
ALayer: TIELayer;
i: Integer;
sChangedLayers: string;
begin
if event = ielResized then
begin
sChangedLayers := '';
for i := 0 to IEView1.LayersCount - 1 do
begin
ALayer := IEView1.Layers[ I ];
if ( ALayer.Locked = False ) and ALayer.Selected then
sChangedLayers := sChangedLayers + IntToStr( i ) +',';
end;
if sChangedLayers <> '' then
begin
SetLength( sChangedLayers, Length( sChangedLayers ) - 1 ); // Remove final comma
memLog.Lines.Add( 'Layers Resized: ' + sChangedLayers );
end;
end;
end;
<FM>See Also<FN>
- <A TImageEnView.OnLayerSelectionChange>
- <A TImageEnView.OnLayerMoveSize>
- <A TImageEnView.OnNewLayer>
!!}
property OnLayerNotify: TIELayerNotify read fOnLayerNotify write fOnLayerNotify;
{!!
<FS>TImageEnView.OnLayerMoveSize
<FM>Declaration<FC>
property OnLayerMoveSize: <A TIELayerMoveSizeEvent>;
<FM>Description<FN>
Occurs whenever a layer is moved or resized. This event only fires on user actions (not programatic changes). It will occur numerous times as the user moves the mouse.
Allows the layer size and position to be controlled and adjusted.
<FM>Example<FC>
// Force all layers to be created at 200 x 200
procedure TfrmMain.ImageEnView1MoveSizeLayer(Sender: TObject; layer: integer; event: TIELayerEvent; var PosX, PosY, Width, Height: Double);
begin
if event = ielCreating then
begin
Width := 200;
Height := 200;
end;
end;
// Force all layers to be centered on the image horizon
procedure TfMain.ImageEnView1MoveSizeLayer(Sender: TObject; layer: integer; event: TIELayerEvent; var PosX, PosY, Width, Height: Double);
begin
PosY := ( ImageEnView1.Layers[0].Height - Height ) / 2;
end;
<FM>See Also<FN>
- <A TImageEnView.OnNewLayer>
- <A TImageEnView.OnLayerNotify>
- <A TIELayer.AspectRatioLocked>
- <A TImageEnView.LayersResizeAspectRatio>
!!}
property OnLayerMoveSize: TIELayerMoveSizeEvent read fOnLayerMoveSize write fOnLayerMoveSize;
{!!
<FS>TImageEnView.OnNewLayer
<FM>Declaration<FC>
property OnNewLayer: <A TIENewLayerEvent>;
<FM>Description<FN>
Occurs whenever a layer is added. It is useful to assign default properties to the layer.
Both programmatic methods (e.g. using <A TImageEnView.LayersAdd>) and user methods (e.g. <L TImageEnView.MouseInteract>using miCreateShapeLayers</L>) trigger this event.
<FC>LayerIdx<FN> is the index of the new layer.
<FC>LayerKind<FN> is the kind of layer that was added.
Notes:
- <A TImageEnView.CurrentLayer> will represent the new layer
- You can also use <A TImageEnView.LayerDefaults> to assign properties to new layers
<FM>Examples<FC>
procedure Tfmain.ImageEnView1NewLayer(Sender: TObject; LayerIdx: Integer; LayerKind: TIELayerKind);
begin
// Assign default properties for new objects
case LayerKind of
ielkImage : TIEImageLayer( ImageEnView1.CurrentLayer ).AspectRatioLocked := True;
ielkPolyline : TIEPolylineLayer( ImageEnView1.CurrentLayer ).SetPoints( iesExplosion, True );
ielkText : TIETextLayer( ImageEnView1.CurrentLayer ).Text := 'Double-click to edit text';
end;
end;
// Make new shape layer a red star
procedure TfrmMain.ImageEnView1NewLayer(Sender: TObject; LayerIdx: integer; LayerKind: TIELayerKind);
begin
if LayerKind = ielkShape then
begin
TIEShapeLayer( ImageEnView1.CurrentLayer ).Shape := iesStar5;
TIEShapeLayer( ImageEnView1.CurrentLayer ).FillColor := clRed;
end;
end;
<FM>See Also<FN>
- <A TImageEnView.OnLayerMoveSize>
- <A TImageEnView.OnLayerNotify>
- <A TImageEnView.OnLayerSelectionChange>
- <A TImageEnView.LayerDefaults>
!!}
property OnNewLayer: TIENewLayerEvent read fOnNewLayer write fOnNewLayer;
{!!
<FS>TImageEnView.OnLayerSelectionChange
<FM>Declaration<FC>
property OnLayerSelectionChange: TNotifyEvent;
<FM>Description<FN>
Occurs whenever a layer is selected or deselected. This event only fires on user actions (not programatic changes).
A selected text layer:
<IMG help_images\text_Selected.gif>
<FM>Example<FC>
procedure TfrmMain.IEView1LayerSelectionChange(Sender: TObject);
begin
// Enable the cut and copy buttons if layer cut/copy is possible
btnCut.Enabled := IEView1.Proc.CanCutFromClipboard( iecpLayer );
btnCopy.Enabled := IEView1.Proc.CanCopyFromClipboard( iecpLayer );
end;
<FM>See Also<FN>
- <A TImageEnView.OnLayerNotify>
- <A TImageEnView.OnNewLayer>
!!}
property OnLayerSelectionChange: TNotifyEvent read fOnLayerSelectionChange write fOnLayerSelectionChange;
{!!
<FS>TImageEnView.OnSpecialKey
<FM>Declaration<FC>
property OnSpecialKey: <A TIESpecialKeyEvent>;
<FM>Description<FN>
OnSpecialKey is called whenever a special key is pressed. Special keys are arrows, "Home", "End", etc.
Notes:
- OnSpecialKey is called twice for each key press (On key down and on key up). This is by Microsoft design. You can use GetKeyState to determine whether this is the on key down or on key up call
- If you have problems receiving OnSpecialKey events, it is recommended that you place the TImageEnView (or its inherited components) on a TPanel instead of TForm
<FM>Example<FC>
// Allow the current layer to be moved using the Shift+Cursor keys
procedure Tfmain.ImageEnView1SpecialKey(Sender: TObject; CharCode: Word; Shift: TShiftState; var Handled: Boolean);
begin
if ( ssShift in Shift ) and ( ImageEnView1.LayersCurrent > 0 ) then
case CharCode of
VK_LEFT :
if IEIsKeyPressed( VK_LEFT ) then // Ensure this is a KeyDown call
begin
ImageEnView1.Layers[ImageEnView1.LayersCurrent].PosX := ImageEnView1.Layers[ImageEnView1.LayersCurrent].PosX - 10;
ImageEnView1.Update;
Handled := True;
end;
VK_RIGHT :
if IEIsKeyPressed( VK_RIGHT ) then // Ensure this is a KeyDown call
begin
ImageEnView1.Layers[ImageEnView1.LayersCurrent].PosX := ImageEnView1.Layers[ImageEnView1.LayersCurrent].PosX + 10;
ImageEnView1.Update;
Handled := True;
end;
VK_UP :
if IEIsKeyPressed( VK_UP ) then // Ensure this is a KeyDown call
begin
ImageEnView1.Layers[ImageEnView1.LayersCurrent].PosY := ImageEnView1.Layers[ImageEnView1.LayersCurrent].PosY - 10;
ImageEnView1.Update;
Handled := True;
end;
VK_DOWN :
if IEIsKeyPressed( VK_DOWN ) then // Ensure this is a KeyDown call
begin
ImageEnView1.Layers[ImageEnView1.LayersCurrent].PosY := ImageEnView1.Layers[ImageEnView1.LayersCurrent].PosY + 10;
ImageEnView1.Update;
Handled := True;
end;
end;
end;
!!}
property OnSpecialKey: TIESpecialKeyEvent read fOnSpecialKey write fOnSpecialKey;
property ImageEnVersion: String read GetImageEnVersion write SetImageEnVersion stored false;
{!!
<FS>TImageEnView.OnDrawBackground
<FM>Declaration<FC>
property OnDrawBackground: <A TIEOnDrawBackground>;
<FM>Description<FN>
OnDrawBackground occurs whenever background needs to be painted.
!!}
property OnDrawBackground: TIEOnDrawBackground read fOnDrawBackground write fOnDrawBackground;
{!!
<FS>TImageEnView.OnDrawCanvas
<FM>Declaration<FC>
property OnDrawCanvas: <A TIEOnDrawCanvas>;
<FM>Description<FN>
OnDrawCanvas occurs whenever the component canvas is updated.
!!}
property OnDrawCanvas: TIEOnDrawCanvas read fOnDrawCanvas write fOnDrawCanvas;
property Wallpaper: TPicture read fWallpaper write SetWallpaper;
property WallpaperStyle: TIEWallpaperStyle read fWallpaperStyle write SetWallpaperStyle default iewoNormal;
property OnTransitionStop: TNotifyEvent read GetOnTransitionStop write SetOnTransitionStop;
property OnTransitionStep: TIETransitionStepEvent read GetOnTransitionStep write SetOnTransitionStep;
property OnTransitionPaint: TIEOnTransitionPaint read GetOnTransitionPaint write SetOnTransitionPaint;
{!!
<FS>TImageEnView.OnDrawLayerBox
<FM>Declaration<FC>
property OnDrawLayerBox: <A TIEDrawLayerBoxEvent>;
<FM>Description<FN>
Occurs when a layer box must be painted (<A TImageEnView.LayersDrawBox> must be enabled). If you handle this event the default behavior is disabled.
<FC>ABitmap<FN> is the destination bitmap and <FC>layer<FN> is the layer index that we are drawing.
Use <A TImageEnView.Layers>[].<A TIELayer.ClientAreaBox> rectangle to get actual rectangle coordinates.
See the ImageEditing\Layers demo for more details.
Note: You can also use <A TImageEnView.SetLayersBoxStyle> to customize the layer box style
<FM>Example<FC>
procedure Tfmain.ImageEnView1DrawLayerBox(Sender: TObject;
ABitmap: TBitmap; layer: Integer);
begin
// a green line
with ABitmap.Canvas do
begin
Pen.Style := psSolid;
Pen.Width := 2;
Pen.mode := pmCopy;
Pen.Color := clGreen;
Brush.Style := bsClear;
with TIELayer(ImageEnView1.Layers[layer]).ClientAreaBox do
Rectangle(Left-1, Top-1, Right+1, Bottom+1);
end;
end;
!!}
property OnDrawLayerBox: TIEDrawLayerBoxEvent read fOnDrawLayerBox write fOnDrawLayerBox;
{!!
<FS>TImageEnView.OnDrawLayer
<FM>Declaration<FC>
property OnDrawLayer: <A TIEDrawLayerEvent>;
<FM>Description<FN>
Occurs immediately after a layer is painted.
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>Dest<FN></C> <C>The destination bitmap (usually the back buffer)</C> </R>
<R> <C><FC>LayerIndex<FN></C> <C>The layer index that we are drawing</C> </R>
</TABLE>
Use <A TImageEnView.Layers>[].<A TIELayer.ClientAreaBox> rectangle to know actual rectangle coordinates.
!!}
property OnDrawLayer: TIEDrawLayerEvent read fOnDrawLayer write fOnDrawLayer;
{!!
<FS>TImageEnView.OnDrawLayerGrip
<FM>Declaration<FC>
property OnDrawLayerGrip: <A TIEDrawLayerGrip>;
<FM>Description<FN>
Occurs when a layer grip is painted. If you handle this event the default behavior is disabled.
<FC>ABitmap<FN> is the destination bitmap and layer is the layer index that we are drawing.
<FC>Rect<FN> specifies the actual grip rectangle.
See the ImageEditing\Layers demo for more details.
<FM>Example<FC>
procedure Tfmain.ImageEnView1DrawLayerGrip(Sender: TObject;
ABitmap: TBitmap; layer, grip: Integer; rect: TRect);
begin
with ABitmap.Canvas do
begin
Pen.Style := psSolid;
Pen.Mode := pmCopy;
Pen.Color := clGreen;
Brush.Style := bsSolid;
Brush.Color := $0000FF00;
with rect do
Ellipse(Left-1, Top-1, Right+1, Bottom+1);
end;
end;
!!}
property OnDrawLayerGrip: TIEDrawLayerGrip read fOnDrawLayerGrip write fOnDrawLayerGrip;
{!!
<FS>TImageEnView.OnDrawPolygon
<FM>Declaration<FC>
property OnDrawPolygon: <A TIEOnDrawPolygon>;
<FM>Description<FN>
OnDrawPolygon occurs whenever a polygon is painted.
!!}
property OnDrawPolygon: TIEOnDrawPolygon read fOnDrawPolygon write fOnDrawPolygon;
property OnSaveUndo: TIESaveUndoEvent read GetOnSaveUndo write SetOnSaveUndo;
property OnMouseWheel;
property OnMouseWheelDown;
property OnMouseWheelUp;
property Anchors;
property DragCursor;
{$IFDEF IEINCLUDEDIRECTSHOW}
{!!
<FS>TImageEnView.OnDShowNewFrame
<FM>Declaration<FC>
property OnDShowNewFrame: TNotifyEvent;
<FM>Description<FN>
Occurs whenever a new frame is ready (when using the DirectShow functionality of TImageEnView). This event is active when you are using IO.<A TImageEnIO.DShowParams> and <A TIEDirectShow.EnableSampleGrabber> is True.
<FM>Example<FC>
procedure Tfmain.ImageEnView1DShowNewFrame(Sender: TObject);
begin
// copy current sample to ImageEnView bitmap
ImageEnView1.IO.DShowParams.GetSample(ImageEnView1.IEBitmap);
// refresh ImageEnView1
ImageEnView1.Update;
end;
!!}
property OnDShowNewFrame: TNotifyEvent read fOnDShowNewFrame write fOnDShowNewFrame;
{!!
<FS>TImageEnView.OnDShowEvent
<FM>Declaration<FC>
property OnDShowEvent: TNotifyEvent;
<FM>Description<FN>
Occurs when one or more events are ready (when using the DirectShow functionality of TImageEnView).
You should call <A TImageEnView.IO>.<A TImageEnIO.DShowParams>.<A TIEDirectShow.GetEventCode> until it returns false (no more events are available).
<FM>Example<FC>
procedure Tfmain.ImageEnView1DShowEvent;
var
event: Integer;
begin
while ImageEnView1.IO.DShowParams.GetEventCode(event) do
case event of
IEEC_COMPLETE:
begin
... end of stream!
end;
end;
end;
!!}
property OnDShowEvent: TNotifyEvent read fOnDShowEvent write fOnDShowEvent;
{$ENDIF}
{!!
<FS>TImageEnView.OnSetCursor
<FM>Declaration<FC>
property OnSetCursor: <A TIESetCursorEvent>;
<FM>Description<FN>
OnSetCursor occurs whenever the mouse cursor needs to be changed. You can set your custom cursor by setting the <FC>Cursor<FN> parameter.
This event may be called multiple times for the same cursor shape.
!!}
property OnSetCursor: TIESetCursorEvent read fOnSetCursor write fOnSetCursor;
{$IFDEF IEINCLUDEMEDIAFOUNDATION}
{!!
<FS>TImageEnView.OnMediaFoundationNotify
<FM>Declaration<FC>
property OnMediaFoundationNotify: <A TIEMediaFoundationNotifyEvent>;
<FM>Description<FN>
Occurs whenever Media Foundation sends a new frame or a notification to TImageEnView.
<FM>Example<FC>
// handler for TImageEnView.OnMediaFoundatioNotify event
procedure TForm1.ImageEnVect1MediaFoundationNotify(Sender, MediaFoundationObject: TObject; NotifyType: TIEMediaFountationNotifyType);
var
sample: TIEMFReceivedSample;
begin
if NotifyType = iemfnFRAME then // is this a frame?
begin
sample := ImageEnView1.IO.MediaFoundationSourceReader.GetNextSample(); // retrieve frame sample
try
sample.DecodeSample(ImageEnView1.IEBitmap); // convert frame sample to bitmap
ImageEnView1.Update(); // update TImageEnView to show the new bitmap
finally
sample.Free(); // free the sample
end;
end;
end;
!!}
property OnMediaFoundationNotify: TIEMediaFoundationNotifyEvent read fOnMediaFoundationNotify write fOnMediaFoundationNotify;
{$ENDIF}
{!!
<FS>TImageEnView.OnVirtualKey
<FM>Declaration<FC>
property OnVirtualKey: <A TIEVirtualKeyEvent>;
<FM>Description<FN>
Occurs whenever the component receives WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP or WM_SYSKEYUP message.
!!}
property OnVirtualKey: TIEVirtualKeyEvent read fOnVirtualKey write fOnVirtualKey;
{!!
<FS>TImageEnView.OnTextEditorKeyDown
<FM>Declaration<FC>
property OnTextEditorKeyDown: TKeyEvent;
<FM>Description<FN>
Occurs whenever a key is pressed when editing the text of a <A TIETextLayer> or <A TIELineLayer>.
<FM>Example<FC>
procedure TMainForm.ImageEnView1TextKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
// Make the Enter key cancel text input
if Key = VK_Return then
begin
Key := 0;
ImageEnView1.LayersCancelEditor( False );
end;
end;
!!}
property OnTextEditorKeyDown: TKeyEvent read fOnTextEditorKeyDown write fOnTextEditorKeyDown;
{!!
<FS>TImageEnView.OnActivateTextEditor
<FM>Declaration<FC>
property OnActivateTextEditor: <A TIETextEditorEvent>;
<FM>Description<FN>
Occurs whenever a text editor is activated for a <A TIETextLayer> or <A TIELineLayer>.
!!}
property OnActivateTextEditor: TIETextEditorEvent read fOnActivateTextEditor write fOnActivateTextEditor;
{!!
<FS>TImageEnView.OnDeactivateTextEditor
<FM>Declaration<FC>
property OnDeactivateTextEditor: <A TIETextEditorEvent>;
<FM>Description<FN>
Occurs whenever a text editor is closed after editing of a <A TIETextLayer> or <A TIELineLayer>.
!!}
property OnDeactivateTextEditor: TIETextEditorEvent read fOnDeactivateTextEditor write fOnDeactivateTextEditor;
{!!
<FS>TImageEnView.EnableInteractionHints
<FM>Declaration<FC>
property EnableInteractionHints: Boolean;
<FM>Description<FN>
When true, mouse interaction hints are displayed to the user (rotation angle of a layer, position when moving, etc.).
Default: True
!!}
property EnableInteractionHints: Boolean read fEnableInteractionHints write fEnableInteractionHints;
// play
property Playing: boolean read fPlaying write SetPlaying default false;
{!!
<FS>TImageEnView.PlayLoop
<FM>Declaration<FC>
property PlayLoop: boolean;
<FM>Description<FN>
Set to True to continuously loop playback of animated GIF and AVI files (when <A TImageEnView.Playing> is enabled).
Default: False
!!}
property PlayLoop: boolean read fPlayLoop write fPlayLoop default true;
property Cursor: TCursor read fCursor write SetCursor default 1785;
{$ifdef IEINCLUDEFLATSB}
property FlatScrollBars: Boolean read fFlatScrollBars write SetFlatScrollBars default False;
{$endif}
property DisplayGridKind: TIEGridKind read fDisplayGridKind write SetDisplayGridKind default iedgNone;
{!!
<FS>TImageEnView.MouseWheelParams
<FM>Declaration<FC>
property MouseWheelParams: <A TIEMouseWheelParams>;
<FM>Description<FN>
Properties to customize the behavior of the mouse wheel.
<FM>Example<FC>
// mouse wheel will scroll image of 15 % of component height
ImageEnView1.MouseWheelParams.Action := iemwVScroll;
ImageEnView1.MouseWheelParams.Variation := iemwPercentage;
ImageEnView1.MouseWheelParams.value := 15;
// mouse wheel will scroll image of 1 pixel
ImageEnView1.MouseWheelParams.Action := iemwVScroll;
ImageEnView1.MouseWheelParams.Variation := iemwAbsolute;
ImageEnView1.MouseWheelParams.value := 1;
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Other\MouseWheel\MouseWheelParams.dpr </C> </R>
</TABLE>
!!}
property MouseWheelParams: TIEMouseWheelParams read fMouseWheelParams write SetMouseWheelParams;
property ShowRulers: TRulerDirs read fShowRulers write SetShowRulers default [];
{!!
<FS>TImageEnView.AutoCursors
<FM>Declaration<FC>
property AutoCursors: Boolean;
<FM>Description<FN>
When AutoCursors is enabled ImageEn will automatically change the cursor when relevant:
- crIEHandDrag when the mouse can be dragged (miScroll in TImageEnView.MouseInteract)
- crIESizeNWSE/crIESizeNESW/crIESizeWE/crIESizeNS when resizing a layer
- crIERotateSE/crIERotateNE/crIERotateSW/crIERotateNW when rotating a layer
- crIESizeAll when moving a layer
Default: True
<FM>See Also<FN>
- <A TImageEnView.Cursor>
!!}
property AutoCursors: Boolean read fAutoCursors write fAutoCursors default True;
property OnRulerGripPosChange: TRulerGripPosChangeEvent read GetOnRulerGripPosChange write SetOnRulerGripPosChange;
property OnRulerGripClick: TRulerGripClickEvent read GetOnRulerGripClick write SetOnRulerGripClick;
property OnRulerGripDblClick: TRulerGripClickEvent read GetOnRulerGripDblClick write SetOnRulerGripDblClick;
property OnRulerClick: TRulerClickEvent read GetOnRulerClick write SetOnRulerClick;
property Align;
property DragMode;
property Enabled;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Visible;
property TabOrder;
property TabStop;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDrag;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnContextPopup;
{$ifdef IEHASONGESTURE}
property OnGesture;
{$endif}
end;
// TImageEnView
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TIEUserInteraction
// Base abstract class for user interactions
TIEUserInteraction = class
private
fParent: TImageEnView;
fEnabled: boolean;
public
constructor Create(Parent: TImageEnView);
destructor Destroy(); override;
// helpers
function GetParent(): TImageEnView;
property Enabled: boolean read fEnabled write fEnabled;
procedure SetTempCursor(Value: TCursor);
procedure RestoreCursor();
// methods to be implemented
// MouseDownExclusive, MouseMoveExclusive and MouseUpExclusive are executed first and only if previous calls to the same methods of other objects are failed (result=false)
// MouseDown, MouseMove, MouseUp are always executed after all MouseXXXXExclusive calls
function MouseDownExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean; virtual; abstract;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual; abstract;
function MouseMoveExclusive(Shift: TShiftState; X, Y: Integer; Captured: boolean): boolean; virtual; abstract;
procedure MouseMove(Shift: TShiftState; X, Y: Integer; Captured: boolean); virtual; abstract;
function MouseUpExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean; virtual; abstract;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual; abstract;
procedure VirtualKey(VKey: Dword; KeyData: Dword; KeyDown: Boolean); virtual; abstract;
procedure Paint(const UpdateRect: TRect); virtual; abstract;
end;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TIECropToolInteraction
TIECropToolInteractionState = (iectisUNSELECTED,
iectisCREATING,
iectisSELECTED,
iectisTOP_LEFT_SIZE,
iectisTOP_SIZE,
iectisTOP_RIGHT_SIZE,
iectisRIGHT_SIZE,
iectisBOTTOM_RIGHT_SIZE,
iectisBOTTOM_SIZE,
iectisBOTTOM_LEFT_SIZE,
iectisLEFT_SIZE,
iectisMOVING,
iectisROTATE);
{!!
<FS>TIECropToolInteractionMode
<FM>Declaration<FC>
}
TIECropToolInteractionMode = (iectmRECTANGLE,
iectmPERSPECTIVE);
{!!}
{!!
<FS>TIECropToolOptions
<FM>Declaration<FC>
TIECropToolOptions = set of (iecoAllowResizing, iecoAllowRotating, iecoAllowMoving, iecoSideGripsRespectLocks, iecoSizeLocksAreMinimums);
<FM>Description<FN>
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>iecoAllowResizing</C> <C>The user can resize the crop selection by dragging the corner or side grips </C> </R>
<R> <C>iecoAllowRotating</C> <C>The user can rotate the crop selection by dragging outside the corner grips </C> </R>
<R> <C>iecoAllowMoving</C> <C>The user can move the crop selection by clicking within the selection area </C> </R>
<R> <C>iecoSideGripsRespectLocks</C> <C>Determines whether the specified <L TIECropToolInteraction.LockAspectRatio>aspect ratio lock</L> is enforced when dragging the side, top and bottom grips. If this option is not set then only the corner grips will respect the lock</C> </R>
<R> <C>iecoSizeLocksAreMinimums</C> <C>If included, then the specified <A TIECropToolInteraction.LockWidth> or <A TIECropToolInteraction.LockHeight> are treated as minimum values, i.e. the user can make the selection larger than the specified values, but not smaller</C> </R>
</TABLE>
Default: [iecoAllowResizing, iecoAllowRotating, iecoAllowMoving]
<FM>Examples<FC>
// Do not allow the user to resize the crop tool selection (i.e. only rotation and creation would be allowed)
ImageEnView1.CropToolInteraction.Options := ImageEnView1.CropToolInteraction.Options - [ iecoAllowResizing ];
// Do not allow the user to rotate the crop tool selection (i.e. only resizing and creation would be allowed)
ImageEnView1.CropToolInteraction.Options := ImageEnView1.CropToolInteraction.Options - [ iecoAllowRotating ];
// Do not allow the user to resize or rotate the crop tool selection (i.e. only creation would be allowed)
ImageEnView1.CropToolInteraction.Options := ImageEnView1.CropToolInteraction.Options - [ iecoAllowResizing, iecoAllowRotating ];
// we want a fixed aspect ratio of 4:3, which is enforced even if the user drags the side grips
ImageEnView1.CropToolInteraction.LockAspectRatio := 4 / 3;
ImageEnView1.CropToolInteraction.Options := ImageEnView1.CropToolInteraction.Options + [iecoSideGripsRespectLocks];
// Do not allow a selection smaller than 100x100 pixels
ImageEnView1.CropToolInteraction.Options := [iecoSizeLocksAreMinimums];
ImageEnView1.CropToolInteraction.LockWidth := 100;
ImageEnView1.CropToolInteraction.LockHeight := 100;
!!}
TIECropToolOptions = set of (iecoAllowResizing, iecoAllowRotating, iecoAllowMoving, iecoSideGripsRespectLocks, iecoSizeLocksAreMinimums);
{!!
<FS>TIECropToolInteraction
<FM>Declaration<FC>
TIECropToolInteraction = class(TIEUserInteraction);
<FM>Description<FN>
A class of TIEUserInteraction that is used to control interaction for the crop tool (when <A TImageEnView.MouseInteract> is <FC>miCropTool<FN>).
The crop tool allows the user to select an area of the image to keep and then click "Enter" to apply the crop. The selection can also be rotated so the image is rotated and then cropped.
In Crop Tool mode:
- User can resize crop box by dragging grips
- User can rotate crop by dragging outside grips
- User can click "Enter" to enact the crop
- User can click "Esc" to cancel the crop
<FM>Rotated Crop<FN>
<IMG help_images\croptool.jpg>
<FM>Perspective Fix<FN>
<IMG help_images\Perspective2.jpg>
<FM>Example<FC>
// Disable guide lines (on image thirds)
ImageEnView1.CropToolInteraction.DrawGuides := False;
// Make larger grips
ImageEnView1.CropToolInteraction.GripSize := 12;
// High quality cropping
ImageEnView1.CropToolInteraction.AntialiasMode := ierBicubic;
// Enable crop mode
ImageEnView1.MouseInteract := [miCropTool];
// Enact crop (same as user clicking "Enter")
ImageEnView1.CropToolInteraction.Crop();
// Cancel crop tool (same as user clicking "Esc")
ImageEnView1.CropToolInteraction.Cancel();
<FM>Methods and Properties<FN>
<FI>General Properties<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.AntialiasMode></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.DrawGuides></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.GripSize></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.LockAspectRatio></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.LockWidth></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.LockHeight></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.Mode></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.Options></C> </R>
</TABLE>
<FI>Selection<FN>
<TABLE2>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.BitmapPolygon></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.Rotation> (Same as rotating selection)</C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.RotatedPolygon></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.ScreenPolygon></C> </R>
<R> <C_IMG_PROPERTY> <C><A TIECropToolInteraction.Selected></C> </R>
<R> <C_IMG_METHOD> <C><A TIECropToolInteraction.SetBitmapPolygon> (Same as resizing selection)</C> </R>
</TABLE>
<FI>Command Methods<FN>
<TABLE2>
<R> <C_IMG_METHOD> <C><A TIECropToolInteraction.Cancel> (Same as clicking "Esc")</C> </R>
<R> <C_IMG_METHOD> <C><A TIECropToolInteraction.Crop> (Same as clicking "Enter")</C> </R>
</TABLE>
<FM>See Also<FN>
- <A TImageEnView Actions>
!!}
TIECropToolInteraction = class(TIEUserInteraction)
private
// current state
fScreenPolygon: TIE2DPointArray; // screen coordinates
fBitmapPolygon: TIE2DPointArray; // bitmap coordinates
fRotatedPolygon: TIE2DPointArray; // rotated polygon (screen coordinates)
fRotation: double;
fState: TIECropToolInteractionState;
fMouseDownCoords: TPoint;
fMouseDownScreenPolygon: TIE2DPointArray; // selected polygon at mousedown time
fMouseDownRotation: double;
fMouseDownRotatedPolygon: TIE2DPointArray;
fGripRects: array [iectisTOP_LEFT_SIZE..iectisLEFT_SIZE] of array [0..1] of TPoint;
fAspectRatio: double;
fOptions: TIECropToolOptions;
// visual appearance
fGripSize: integer;
fDrawGuides: boolean;
// crop processing properties
fAntialiasMode: TIEAntialiasMode;
fMode: TIECropToolInteractionMode;
fLockAspectRatio: double; // -1: No locking, >0: Locked to specific aspect
fLockWidth, fLockHeight: Integer; // Lock selection to absolute sizes
function GetGrip(x, y: integer): TIECropToolInteractionState;
procedure Draw(Canvas: TCanvas);
function ScreenToBitmap(const P: TIE2DPoint): TIE2DPoint;
function BitmapToScreen(const P: TIE2DPoint): TIE2DPoint;
procedure UpdateBitmapPolygon();
procedure UpdateScreenPolygon();
function GetRotatedBitmapPolygon(): TIE2DPointArray;
procedure SetMode(const Value: TIECropToolInteractionMode);
procedure AdjustAspectRatio(quad: TIE2DPointArray; grip: TIECropToolInteractionState);
function GetSelected(): Boolean;
procedure SetSelected(const Value: Boolean);
public
constructor Create(Parent: TImageEnView);
// VISUAL APPEARANCE
{!!
<FS>TIECropToolInteraction.GripSize
<FM>Declaration<FC>
property GripSize: Integer;
<FM>Description<FN>
Specifies the size of the grips that appear around the crop selection (which can be dragged to enlarge).
Note: You must call <FC>TImageEnView.Invalidate<FN> if you change this property when the crop selection is visible
Default: 8
<FM>Example<FC>
// Make larger grips
ImageEnView1.CropToolInteraction.GripSize := 12;
!!}
property GripSize: integer read fGripSize write fGripSize;
{!!
<FS>TIECropToolInteraction.DrawGuides
<FM>Declaration<FC>
property DrawGuides: Boolean;
<FM>Description<FN>
Displays guide lines on the horizontal and vertical thirds of the crop selection.
Note: You must call <FC>TImageEnView.Invalidate<FN> if you change this property when the crop selection is visible
Default: True
<FM>Example<FC>
// Disable guide lines
ImageEnView1.CropToolInteraction.DrawGuides := False;
!!}
property DrawGuides: boolean read fDrawGuides write fDrawGuides;
{!!
<FS>TIECropToolInteraction.LockAspectRatio
<FM>Declaration<FC>
property LockAspectRatio: Double;
<FM>Description<FN>
Allows the crop selection to be locked to a specific ratio, e.g. 0.75 for a 4:3 image or 1.77 for a 16:9 image
If LockAspectRatio is -1, the aspect ratio is only locked when the user presses the ALT key
If LockAspectRatio is >0, ImageEn locks the selection to the specified aspect ratio
Note: By default, when dragging the side grips the aspect ratio is not enforced. Add <FC>iecoSideGripsRespectLocks<FN> to <A TIECropToolInteraction.Options> to force all grips to respect the ratio
<FM>Example<FC>
// we want standard behavior
ImageEnView1.CropToolInteraction.LockAspectRatio := -1;
// we want a fixed aspect ratio of 4:3 (standard landscape, i.e. height is 75% of width)
ImageEnView1.CropToolInteraction.LockAspectRatio := 4 / 3;
<FM>See Also<FN>
- <A TIECropToolInteraction.Options>
- <A TImageEnView.SelectionAspectRatio>
- <A TIECropToolInteraction.LockHeight>
- <A TIECropToolInteraction.LockWidth>
!!}
property LockAspectRatio: Double read fLockAspectRatio write fLockAspectRatio;
{!!
<FS>TIECropToolInteraction.LockWidth
<FM>Declaration<FC>
property LockWidth: Integer;
<FM>Description<FN>
Allows the width of the crop selection to be locked to a specific size. e.g. 200 pixels wide.
Notes:
- Values refer to bitmap pixels, not a screen dimension
- Can be used independently of <A TIECropToolInteraction.LockHeight>
- if <FC>iecoSizeLocksAreMinimums<FN> is specified in <A TIECropToolInteraction.Options>, then this value is a minimum only
- Has no effect if <A TImageEnView.SelectionAspectRatio> > 0 (unless <FC>iecoSizeLocksAreMinimums<FN> is used)
<FM>Example<FC>
// we want standard behavior
ImageEnView1.CropToolInteraction.LockWidth := 0;
ImageEnView1.CropToolInteraction.LockHeight := 0;
// Lock the width to 500 pixels (but height can be changed)
ImageEnView1.CropToolInteraction.LockAspectRatio := 0;
ImageEnView1.CropToolInteraction.LockWidth := 500;
ImageEnView1.CropToolInteraction.LockHeight := 0;
// Lock width and height to half the image size
ImageEnView1.CropToolInteraction.LockAspectRatio := 0;
ImageEnView1.CropToolInteraction.LockWidth := ImageEnView1.IEBitmap.Width div 2;
ImageEnView1.CropToolInteraction.LockHeight := ImageEnView1.IEBitmap.Height div 2;
// Do not allow a selection smaller than 100x100 pixels
ImageEnView1.CropToolInteraction.Options := [iecoSizeLocksAreMinimums];
ImageEnView1.CropToolInteraction.LockWidth := 100;
ImageEnView1.CropToolInteraction.LockHeight := 100;
<FM>See Also<FN>
- <A TIECropToolInteraction.LockHeight>
- <A TIECropToolInteraction.LockAspectRatio>
- <A TIECropToolInteraction.Options>
- <A TImageEnView.SelectionAbsWidth>
!!}
property LockWidth: Integer read fLockWidth write fLockWidth;
{!!
<FS>TIECropToolInteraction.LockHeight
<FM>Declaration<FC>
property LockHeight: Integer;
<FM>Description<FN>
Allows the height of the crop selection to be locked to a specific size. e.g. 200 pixels high.
Notes:
- Values refer to bitmap pixels, not a screen dimension
- Can be used independently of <A TIECropToolInteraction.LockWidth>
- if <FC>iecoSizeLocksAreMinimums<FN> is specified in <A TIECropToolInteraction.Options>, then this value is a minimum only
- Has no effect if <A TImageEnView.SelectionAspectRatio> > 0 (unless <FC>iecoSizeLocksAreMinimums<FN> is used)
<FM>Example<FC>
// we want standard behavior
ImageEnView1.CropToolInteraction.LockHeight := 0;
ImageEnView1.CropToolInteraction.LockHeight := 0;
// Lock the height to 500 pixels (but width can be changed)
ImageEnView1.CropToolInteraction.LockAspectRatio := 0;
ImageEnView1.CropToolInteraction.LockHeight := 500;
ImageEnView1.CropToolInteraction.LockWidth := 0;
// Lock width and height to half the image size
ImageEnView1.CropToolInteraction.LockAspectRatio := 0;
ImageEnView1.CropToolInteraction.LockWidth := ImageEnView1.IEBitmap.Width div 2;
ImageEnView1.CropToolInteraction.LockHeight := ImageEnView1.IEBitmap.Height div 2;
// Do not allow a selection smaller than 100x100 pixels
ImageEnView1.CropToolInteraction.Options := [iecoSizeLocksAreMinimums];
ImageEnView1.CropToolInteraction.LockWidth := 100;
ImageEnView1.CropToolInteraction.LockHeight := 100;
<FM>See Also<FN>
- <A TIECropToolInteraction.LockWidth>
- <A TIECropToolInteraction.LockAspectRatio>
- <A TIECropToolInteraction.Options>
- <A TImageEnView.SelectionAbsHeight>
!!}
property LockHeight: Integer read fLockHeight write fLockHeight;
// CURRENT SELECTION
{!!
<FS>TIECropToolInteraction.BitmapPolygon
<FM>Declaration<FC>
property BitmapPolygon: <A TIE2DPointArray>; (Read-only)
<FM>Description<FN>
Returns the area of the image the user has selected for cropping.
Note:
- <FC>BitmapPolygon<FN> does not consider the <A TIECropToolInteraction.Rotation>
- <FC>BitmapPolygon<FN> is an array [0..3] of type, <A TIE2DPoint>. Item 0 is Top-Left, item 1 is Top-Right, item 2 is Bottom-Right and item 3 is Bottom-Left
<FM>Example<FC>
// Show the size of the selection while resizing
procedure TForm1.ImageEnView1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if ImageEnView1.MouseCapture then // selection being made or altered
with ImageEnView1.CropToolInteraction do
Caption := 'Selection: ' + IntToStr( abs( Round( BitmapPolygon[2].X - BitmapPolygon[0].X ))) + ' x ' + IntToStr( abs( Round( BitmapPolygon[2].Y - BitmapPolygon[0].Y )));
end;
<FM>See Also<FC>
- <A TIECropToolInteraction.SetBitmapPolygon>
- <A TIECropToolInteraction.RotatedPolygon>
- <A TIECropToolInteraction.Rotation>
!!}
{$ifdef IESUPPORTPROPERTYOFARRAYTYPE}
property BitmapPolygon: TIE2DPointArray read fBitmapPolygon;
{$endif}
property RotatedBitmapPolygon: TIE2DPointArray read GetRotatedBitmapPolygon;
procedure SetBitmapPolygon(Rect: TRect);
{!!
<FS>TIECropToolInteraction.ScreenPolygon
<FM>Declaration<FC>
property ScreenPolygon: <A TIE2DPointArray>; (Read-only)
<FM>Description<FN>
Returns the area of the image the user has selected for cropping, using screen coordinates.
Note: <FC>ScreenPolygon<FN> is an array [0..3] of type, <A TIE2DPoint>. Item 0 is Top-Left, item 1 is Top-Right, item 2 is Bottom-Right and item 3 is Bottom-Left
!!}
{$ifdef IESUPPORTPROPERTYOFARRAYTYPE}
property ScreenPolygon: TIE2DPointArray read fScreenPolygon;
{$endif}
{!!
<FS>TIECropToolInteraction.RotatedPolygon
<FM>Declaration<FC>
property RotatedPolygon: <A TIE2DPointArray>; (Read-only)
<FM>Description<FN>
Returns the area of the image the user has selected for cropping with support for rotation (returning the four corners of the rotated polygon)
<FM>See Also<FC>
- <A TIECropToolInteraction.BitmapPolygon>
- <A TIECropToolInteraction.SetBitmapPolygon>
- <A TIECropToolInteraction.Rotation>
!!}
{$ifdef IESUPPORTPROPERTYOFARRAYTYPE}
property RotatedPolygon: TIE2DPointArray read fRotatedPolygon;
{$endif}
{!!
<FS>TIECropToolInteraction.Rotation
<FM>Declaration<FC>
property Rotation: Double;
<FM>Description<FN>
The rotation the user has applied to the crop selection (degrees counter-clockwise). This is the programatic equivalent of the user dragging outside the grips of the crop tool selection.
Note: You must call <FC>TImageEnView.Invalidate<FN> if you change this property when the crop selection is visible
<IMG help_images\croptool.jpg>
<FM>Example<FC>
// Change rotation to 45 deg. CCW
ImageEnView1.CropToolInteraction.Rotation := 45;
ImageEnView1.Invalidate();
!!}
property Rotation: double read fRotation write fRotation;
// OTHER PROPERTIES
{!!
<FS>TIECropToolInteraction.AntialiasMode
<FM>Declaration<FC>
property AntialiasMode: <A TIEAntialiasMode>;
<FM>Description<FN>
Specifies the quality of a rotated crop.
Default: ierFast
<FM>Example<FC>
// High quality cropping
ImageEnView1.CropToolInteraction.AntialiasMode := ierBicubic;
!!}
property AntialiasMode: TIEAntialiasMode read fAntialiasMode write fAntialiasMode;
property Mode: TIECropToolInteractionMode read fMode write SetMode;
{!!
<FS>TIECropToolInteraction.Options
<FM>Declaration<FC>
property Options: <A TIECropToolOptions>;
<FM>Description<FN>
Options that control the behaviour of the crop tool.
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>iecoAllowResizing</C> <C>The user can resize the crop selection by dragging the corner or side grips </C> </R>
<R> <C>iecoAllowRotating</C> <C>The user can rotate the crop selection by dragging outside the corner grips </C> </R>
<R> <C>iecoAllowMoving</C> <C>The user can move the crop selection by clicking within the selection area </C> </R>
<R> <C>iecoSideGripsRespectLocks</C> <C>Determines whether the specified <L TIECropToolInteraction.LockAspectRatio>aspect ratio lock</L> is enforced when dragging the side, top and bottom grips. If this option is not set then only the corner grips will respect the lock</C> </R>
<R> <C>iecoSizeLocksAreMinimums</C> <C>If included, then the specified <A TIECropToolInteraction.LockWidth> or <A TIECropToolInteraction.LockHeight> are treated as minimum values, i.e. the user can make the selection larger than the specified values, but not smaller</C> </R>
</TABLE>
Default: [iecoAllowResizing, iecoAllowRotating, iecoAllowMoving]
<FM>Examples<FC>
// Do not allow the user to resize the crop tool selection (i.e. only rotation and creation would be allowed)
ImageEnView1.CropToolInteraction.Options := ImageEnView1.CropToolInteraction.Options - [ iecoAllowResizing ];
// Do not allow the user to rotate the crop tool selection (i.e. only resizing and creation would be allowed)
ImageEnView1.CropToolInteraction.Options := ImageEnView1.CropToolInteraction.Options - [ iecoAllowRotating ];
// Do not allow the user to resize or rotate the crop tool selection (i.e. only creation would be allowed)
ImageEnView1.CropToolInteraction.Options := ImageEnView1.CropToolInteraction.Options - [ iecoAllowResizing, iecoAllowRotating ];
// we want a fixed aspect ratio of 4:3, which is enforced even if the user drags the side grips
ImageEnView1.CropToolInteraction.LockAspectRatio := 4 / 3;
ImageEnView1.CropToolInteraction.Options := ImageEnView1.CropToolInteraction.Options + [iecoSideGripsRespectLocks];
// Do not allow a selection smaller than 100x100 pixels
ImageEnView1.CropToolInteraction.Options := [iecoSizeLocksAreMinimums];
ImageEnView1.CropToolInteraction.LockWidth := 100;
ImageEnView1.CropToolInteraction.LockHeight := 100;
<FM>See Also<FN>
- <A TIECropToolInteraction.LockAspectRatio>
- <A TIECropToolInteraction.LockWidth>
- <A TIECropToolInteraction.LockHeight>
!!}
property Options: TIECropToolOptions read fOptions write fOptions;
property Selected: Boolean read GetSelected write SetSelected;
// commands
procedure Refresh();
procedure Cancel();
procedure Crop();
// abstract methods implementation
function MouseDownExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
function MouseMoveExclusive(Shift: TShiftState; X, Y: Integer; Captured: boolean): boolean; override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer; Captured: boolean); override;
function MouseUpExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean; override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure VirtualKey(VKey: Dword; KeyData: Dword; KeyDown: Boolean); override;
procedure Paint(const UpdateRect: TRect); override;
end;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TIECreateLayerInteraction
TIECreateLayerInteraction = class(TIEUserInteraction)
private
// current state
fScreenPt1: TIE2DPoint; // screen coordinates
fScreenPt2: TIE2DPoint; // screen coordinates
fMouseDownCoords: TPoint;
fLayerKind: TIELayerKind;
fCreatingRect: Boolean; // False until we commence creation of hte rect
// visual appearance
fLockAspectRatio: double; // -1: No locking, >0: Locked to specific aspect
fLockWidth, fLockHeight: Integer; // Lock selection to absolute sizes
procedure Draw(Canvas: TCanvas);
function GetSelX(): Integer;
function GetSelY(): Integer;
function GetSelWidth(): Integer;
function GetSelHeight(): Integer;
public
constructor Create(Parent: TImageEnView);
property LockAspectRatio: Double read fLockAspectRatio write fLockAspectRatio;
property LockWidth: Integer read fLockWidth write fLockWidth;
property LockHeight: Integer read fLockHeight write fLockHeight;
property LayerKind: TIELayerKind read fLayerKind write fLayerKind;
property SelX: Integer read GetSelX;
property SelY: Integer read GetSelY;
property SelWidth: Integer read GetSelWidth;
property SelHeight: Integer read GetSelHeight;
procedure SetSel(X, Y, Width, Height: Integer);
procedure Enact();
procedure Cancel();
procedure Refresh();
// abstract methods implementation
function MouseDownExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
function MouseMoveExclusive(Shift: TShiftState; X, Y: Integer; Captured: boolean): boolean; override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer; Captured: boolean); override;
function MouseUpExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean; override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure VirtualKey(VKey: Dword; KeyData: Dword; KeyDown: Boolean); override;
procedure Paint(const UpdateRect: TRect); override;
end;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function _IsRectangle(p: PPointArray; n: integer): boolean;
procedure IEDrawBackground(ComponentState: TComponentState; Canvas: TCanvas; Bitmap: TBitmap; fBackgroundStyle: TIEBackgroundStyle; fBackground: TColor; DestX, DestY, Width, Height: integer; x1, y1, x2, y2: integer; fChessboardSize: integer; fChessboardBrushStyle: TBrushStyle; fChessboardColor2Customized: Boolean; fGradientEndColor: TColor; Wallpaper: TPicture; WallpaperStyle: TIEWallpaperStyle; LiveBackground: TIEBitmap);
function IELayersLoadHeaderFromStream(Stream: TStream;
out Header: TLayerHeader;
out Width: Integer;
out Height: Integer;
out Description: Widestring;
var Thumbnail: TIEBitmap;
LoadThumb: Boolean = True
): Boolean;
const
// consts used by <A TImageEnView.LayersMove>
IEN_Send_To_Back = -9;
IEN_Send_Backward = -8;
IEN_Bring_Forward = -7;
IEN_Bring_To_Front = -6;
// Consts used by LayersFixBorders, LayersFixRotations, LayersFixSizes, LayersMove, etc.
LYR_ALL_LAYERS = -1;
LYR_SELECTED_LAYERS = -2;
// ImageEn embedded cursors
crIEZoomOut = TCursor(1778);
crIEZoomIn = TCursor(1779);
crIEThickCross2 = TCursor(1780);
crIEEraser = TCursor(1781);
crIEHandDrag = TCursor(1782);
crIEPencil = TCursor(1783);
crIECross = TCursor(1784);
crIECrossSight = TCursor(1785);
crIESizeNWSE = TCursor(1786);
crIESizeNS = TCursor(1787);
crIESizeNESW = TCursor(1788);
crIESizeWE = TCursor(1789);
crIESizeAll = TCursor(1790);
crIECrossSightPlus = TCursor(1791);
crIECrossSightMinus = TCursor(1792);
crIEThickCross = TCursor(1793);
crIEThickCrossPlus = TCursor(1794);
crIECrossSightMinus2 = TCursor(1795);
crIECrossSightMinus3 = TCursor(1796);
crIEBrush = TCursor(1797);
crIEEyeDropper3 = TCursor(1798);
crIEPaintFill = TCursor(1799);
crIEStamp = TCursor(1800);
crIECrop = TCursor(1801);
crIECrossSmallPlus = TCursor(1802);
crIESmallArrow = TCursor(1803);
crIEMultipleArrow = TCursor(1804);
crIEEyeDropper2 = TCursor(1805);
crIEEyeDropper = TCursor(1806);
crIECut = TCursor(1807);
crIESelectArrow = TCursor(1808);
crIEPen = TCursor(1809);
crIEBigZoomPlusMinus = TCursor(1810);
crIERotateNE = TCursor(1811);
crIERotateSW = TCursor(1812);
crIERotateNW = TCursor(1813);
crIERotateSE = TCursor(1814);
procedure IEInitialize_imageenview;
procedure IEFinalize_imageenview;
implementation
uses
iexThemes, math, NeurQuant, ievect, iegdiplus, iesettings, iexCanvasUtils
{$ifdef IEUSEVCLZLIB}, zlib{$else}, iezlib{$endif}
{$IfDef UNICODE}, AnsiStrings {$EndIf}
{$ifdef IEINCLUDEDIRECTSHOW}
, ieds
{$endif}
{$ifdef IEINCLUDEFLATSB}
, flatsb
{$endif}
{$IFDEF IEINCLUDEMEDIAFOUNDATION}
, iemmf
{$ENDIF}
{$ifdef IEVISION}
, ievision
{$endif}
{$ifdef IEHASTYPES}
, Types
{$endif}
;
{$R IMRES.RES}
{$R-}
type
// animated polygons item
// note: this item is replicated in ievect also
TIEAnimPoly = record
Poly: PPointArray; // coordinates
PolyCount: integer; // vertex count
PolyCapacity: integer; // memory capacity
Color1: TColor; // Color 1
Color2: TColor; // Color 2
//
Animated: boolean; // Animated
AniFt: integer; // frame counter
C1: integer; // DDA counter
Canvas: TCanvas; // destination canvas
RX1, RY1, RX2, RY2: integer; // bounds of the polygon
Enabled: boolean; // if True show the polygon
Sizeable: boolean; // shows and use resizing grips
ShowCenter: Boolean; // draw a cross to show center of the polygon
DrawPixelPtr: PRGB; // to replace SetPixel
DrawPixelBitmap: TBitmap; // to replace SetPixel
end;
PIEAnimPoly = ^TIEAnimPoly;
// Initialize (load) cursors
procedure InitCursors;
const
imcur = 1777;
curnum = 37;
curname: array[1..curnum] of string = (
'ZOOMMENO', // 1778
'ZOOMPIU', // 1779
'CROCE', // 1780
'GOMMA', // 1781
'MANO', // 1782
'PENNA', // 1783
'CROCE1', // 1784
'CROCE3', // 1785 // default
'MOVE2', // 1786
'MOVEY', // 1787
'MOVE1', // 1788
'MOVEX', // 1789
'MOVE', // 1790
'CROCE3+', // 1791
'CROCE3-', // 1792
'CROCE2', // 1793
'CROCE2+', // 1794
'CROCE2-', // 1795
'CROCE2-', // 1796 (FREE ENTRY)
'1795', // 1797
'1796', // 1798
'1797', // 1799
'1798', // 1800
'1799', // 1801
'1800', // 1802
'1801', // 1803
'1802', // 1804
'1803', // 1805
'1803', // 1806 (FREE ENTRY)
'1805', // 1807
'1806', // 1808
'1807', // 1809
'1808', // 1810
'ROT1', // 1811
'ROT2', // 1812
'ROT3', // 1813
'ROT4' // 1814
);
var
q: integer;
begin
for q := 1 to curnum do
Screen.Cursors[imcur + q] := LoadCursor(sysinit.hinstance, PChar(curname[q]));
end;
/////////////////////////////////////////////////////////////////////////////////////
constructor TImageEnView.Create(Owner: TComponent);
begin
LockUpdate;
fOffscreenPaint := false;
fGXScr2Bmp := nil;
fGYScr2Bmp := nil;
fGXScr2BmpSize := 0;
fGYScr2BmpSize := 0;
fGXBmp2Scr := nil;
fGYBmp2Scr := nil;
fXScr2BmpSize := 0;
fYScr2BmpSize := 0;
fXBmp2ScrSize := 0;
fYBmp2ScrSize := 0;
fXScr2Bmp := nil;
fYScr2Bmp := nil;
fXBmp2Scr := nil;
fYBmp2Scr := nil;
fBitmapWidth := 0;
fBitmapHeight := 0;
fUpdateReason := ieurDefault;
fWasScreenCursor := crNone;
fImageEnIO := nil;
fImageEnProc := nil;
{$IFDEF IEIncludeDeprecatedInV4}
fLegacyBitmap := True;
fBitmap := TBitmap.create;
fBitmap.PixelFormat := pf24bit;
{$IFNDEF OCXVERSION}
fBitmap.Width := Width;
fBitmap.Height := Height;
{$ENDIF}
{$ELSE}
fLegacyBitmap := False;
fBitmap := nil;
{$ENDIF}
inherited Create(Owner);
{$ifdef IEVISION}
if (csDesigning in ComponentState) then
begin
// design mode, unload ievision
IEFinalize_ievision();
end;
{$endif}
IEGDIPLoadLibrary();
fIEBitmapValid := True;
fIEBitmap := TIEBitmap.Create;
fIEBitmap.fOwner := Self;
{$IFDEF IEIncludeDeprecatedInV4}
fIEBitmap.EncapsulateTBitmap(fBitmap, true);
{$ELSE}
fIEBitmap.Location := ieFile;
fIEBitmap.Allocate( 1, 1, ie24RGB );
{$ENDIF}
fLayersRect.x := 0;
fLayersRect.y := 0;
fLayersRect.width := fIEBitmap.Width;
fLayersRect.height := fIEBitmap.Height;
fUpdateInsideUpdate := false;
fInsideUpdate := false;
// layers
fLayers := TList.Create;
fLayers.Add(TIEImageLayer.Create(self, fIEBitmap, true));
with TIEImageLayer(fLayers[0]) do
begin
VisibleBox := false;
Locked := true;
end;
fLayersCurrent := 0;
fDrawPixelBitmap := nil;
fDrawPixelPtr := nil;
fOnSetCursor := nil;
fOldHandle := 0;
fAutoCursors := assigned(Owner);
fTransition := nil;
fTransitionEffect := iettNone;
fTransitionDuration := 1000;
fVScrollBarVisible := false;
fHScrollBarVisible := false;
fScrollBarsAlwaysVisible := false;
fRXScroll := 1;
fRYScroll := 1;
fSelectionOptions := [iesoAnimated, iesoSizeable, iesoMoveable, iesoCanScroll, iesoAllowMoveByKeyboard];
fOnViewChange := nil;
fOnFinishSmoothTask := nil;
fOnViewChanging := nil;
fOnImageEnGesture := nil;
fOnImageChange := nil;
fOnDrawLayer := nil;
fOnDrawLayerBox := nil;
fOnDrawLayerGrip := nil;
fDelayZoomFilter := false;
fZoomFilter := rfNone;
fActualZoomFilter := rfNone;
fUpdateBackBuffer := true;
fFullUpdateRequest := true;
ZeroMemory(@fBitmapInfoHeader256, sizeof(TBitmapInfoHeader256));
fMouseInteract := [];
fLockPaint := 0;
fHDrawDib := IEDrawDibOpen;
{$ifndef IEDOTNETVERSION}
if Owner <> nil then
{$endif}
InitCursors;
fFirstTimeSetCursor := true;
fCursor := crIECrossSight;
fLCursor := fCursor;
fScrollBars := ssBoth;
fZoomX := 100;
fZoomY := 100;
fZoomD100X := fZoomX / 100;
f100DZoomX := 100 / fZoomX;
fZoomD100Y := fZoomY / 100;
f100DZoomY := 100 / fZoomY;
Height := 105;
Width := 105;
fOffX := 0;
fOffY := 0;
fExtX := 0;
fExtY := 0;
fImageHorizAlignment := iehCenter;
fImageVertAlignment := ievCenter;
fPolySelecting := False;
fLassoSelecting := False;
fRectSelecting := False;
fCircSelecting := False;
fRectResizing := ieNone;
fSelectMoving := -1;
fSel := false;
fAnimPoly := TList.Create;
fAnimPolyTimer := nil;
fDelayTimer := -20; // maximum 20% of cpu for selections
fHPolySel := PIEAnimPoly(AnimPolygonNew(clBlack, clWhite, True, True));
if not (csDesigning in ComponentState) then
begin
fBackBuffer := TIEBitmap.Create(10, 10, ie24RGB); // back buffer
fBackBuffer.Location := ieTBitmap;
HideSelectionEx(false);
end;
fUpdateInvalidate := true;
fSelectionBase := iesbClientArea;
fSavedSelectionBase := fSelectionBase;
fBackgroundStyle := iebsSolid;
fOnSelectionChange := nil;
fOnSelectionChanging := nil;
fOnBeforeSelectionChange := nil;
fMagicWandMaxFilter := false;
fMagicWandMode := iewInclusive;
fMagicWandTolerance := 15;
fOnMouseInSel := nil;
fOnMouseInResizingGrip := nil;
fOnZoomIn := nil;
fOnZoomOut := nil;
fOnVirtualKey := nil;
fDisplayGridKind := iedgNone;
fDisplayGridLyr := -1;
fVScrollBarParams := TIEScrollBarParams.Create;
fHScrollBarParams := TIEScrollBarParams.Create;
fMouseWheelParams := TIEMouseWheelParams.Create( iemwZoom );
fSelectionMaskDepth := 1;
fSelectionIntensity := 1;
fSelectionMask := TIEMask.Create;
fSelectionMask.AllocateBits(fIEBitmap.Width, fIEBitmap.Height, fSelectionMaskDepth);
fAniCounter := 0;
SelColor1 := clBlack;
SelColor2 := clWhite;
fGripColor1 := clBlack;
fGripColor2 := $00BAFFFF;
fGripBrushStyle := bsSolid;
fGripSize := 5;
fGripShape := iegsCircle;
fExtendedGrips := true;
fLyrGripColor1 := clBlack;
fLyrGripColor2 := $00BAFFFF;
fLyrGripBrushStyle := bsSolid;
fLyrGripSize := 5;
fLyrGripShape := iegsCircle;
fChessboardSize := 16;
fChessboardBrushStyle := bsSolid;
fChessboardColor2Customized := False;
fOnPaint := nil;
fOnProgress := nil;
fOnFinishWork := nil;
fOnAcquireBitmap := nil;
fForceALTkey := false;
fEnableAlphaChannel := true;
fDelayDisplaySelection := true;
fAutoStretch := false;
fAutoShrink := false;
fBlockPaint := false;
fRectMoving := false;
fMouseMoveScrolling := false;
fSavedSelection := TList.Create;
fEnableShiftKey := true;
fDelayZoomTicks := 4;
fDelayAniSelTicks := 2;
fLutLastZoomX := -1;
fLutLastZoomY := -1;
fLutLastFRX := -1;
fLutLastFRY := -1;
fLutLastMaxLayerWidth := -1;
fLutLastMaxLayerHeight := -1;
fGradientEndColor := clBtnShadow;
fUseDrawDibDraw := false; // for same behavior of old versions set it to True
fDrawVersion := false;
UnLockUpdate;
fOnDrawBackBuffer := nil;
fLayersDrawBox := false;
fLayersCaching := 0;
fMovingLayer := -1;
fLayerMoved := false;
fRotatingLayer := -1;
fRotatingLayerValue := 0;
fMovingRotationCenter := -1;
fLayerResizing := ieNone;
fMovResRotLayerStarted := false;
fEditingLayer := -1;
fOnLayerNotify := nil;
fOnLayerSelectionChange := nil;
{$IFDEF IEINCLUDEDIRECTSHOW}
fOnDShowNewFrame := nil;
fOnDShowEvent := nil;
{$ENDIF}
{$IFDEF IEINCLUDEMEDIAFOUNDATION}
fOnMediaFoundationNotify := nil;
{$ENDIF}
fOnTextEditorKeyDown := nil;
fOnActivateTextEditor := nil;
fOnDeactivateTextEditor := nil;
fSelectionAspectRatio := -1;
fSelectionAbsWidth := 100;
fSelectionAbsHeight := 100;
fOnSpecialKey := nil;
fFlatScrollBars := false;
fNavigator := nil;
fNavigatorInside := false;
fIsNavigator := false;
fMinBitmapSize := 2;
fOnDrawBackground := nil;
fOnDrawCanvas := nil;
{$ifdef IEDOTNETVERSION} // wallpaper created on request when under DOTNET
fWallpaper := nil;
{$else}
fWallpaper := TPicture.Create;
{$endif}
fWallpaperStyle := iewoNormal;
fZoomSelectionAspectRatio := true;
fMouseScrollRate := 1;
fVisibleSelection := true;
fOnDrawPolygon := nil;
fOnSaveUndo := nil;
fSoftCrop := iesfNone;
fSoftCropValue := 60;
fInteractionHint := '';
fEnableInteractionHints := true;
fLayersRotationFilter := ierFast;
fLayersRotationAntialias := false;
fLayersRotationUseFilterOnPreview := false;
fLayersRotateStep := 45;
fLayersSelectConstrains := true;
fLayersMergeFilter := rfLanczos3;
fUpdate_MaskCache := -1;
fSmoothScrollTimer := nil;
fSmoothScrollValue := 8;
fSmoothZoomTimer := nil;
fSmoothZoomValue := 8;
fHighlightedPixel := Point(-1, -1);
fPostFrames := TList.Create;
fSelectionGridWidth := 1;
fSelectionGridHeight := 1;
fNavigatorOptions := [];
fNavigatorBackBuffer := nil;
fNavigatorActualRect := Rect(0, 0, 0, 0);
fMarkOuterAlpha := -1;
fMarkOuterColor := clWhite;
fLayerDefaults := nil;
fLayerOptions := [ loAllowMultiSelect, loAutoUndoChangesByUser, loAutoPromptForImage, loAutoFixBorders ];
fLayersResizeAspectRatio := iearALTKey;
fLayersFastDrawing := iefDelayed;
fLayersFastOutput := False;
fLayerBoxPen := TPen.Create;
fLayerBoxPen.Style := psDot;
fLayerBoxPen.Mode := pmNot;
fLayerBoxPen.Width := 1;
fHighlightedPixelColor := clRed;
fPlayTimer := 0;
fPlayLoop := true;
fImageSet := False;
fNeedUpdateLiveBackground := True;
fLiveBackground := nil;
fGestures := TIEViewerGestures.Create();
fIEBitmap.Modified := False;
fModified := False;
fTextEditor := nil;
fShowRulers := [];
fRulerParams := TIEViewRulerParams.Create( Self );
fUserInteractions := TObjectList.Create();
// add default user interactions plugins
fUserInteractions.Add( TIECropToolInteraction.Create(self) );
fUserInteractions.Add( TIECreateLayerInteraction.Create(self) );
end;
procedure TImageEnView.CreateHandle();
begin
inherited;
end;
destructor TImageEnView.Destroy;
var
i: integer;
begin
IECleanupLayers();
FreeAndNil(fUserInteractions);
// free post frames
while fPostFrames.Count > 0 do
RemovePostFrames(0);
FreeAndNil(fPostFrames);
FreeAndNil( fRulerParams );
FreeAndNil( fGestures );
FreeAndNil( fTextEditor );
FreeAndNil(fSmoothScrollTimer);
FreeAndNil(fSmoothZoomTimer);
SyncLayers(); // Without this, in rare situation you will get an exception if current layer fBitmap points to a bitmap that no longer exists (e.g. an external bitmap that has been destroyed). Fixed known causes of this issue, but do not remove this line in case there are other instances
FreeAndNil(fTransition);
if assigned(fNavigator) then
SetNavigator(nil);
if assigned(fImageEnIO) then
FreeAndNil(fImageEnIO);
if assigned(fImageEnProc) then
FreeAndNil(fImageEnProc);
if not (csDesigning in ComponentState) then
begin
// free double buffer
FreeAndNil(fBackBuffer);
end;
FreeAndNil(fAnimPolyTimer);
for i := 0 to fAnimPoly.Count-1 do
begin
freemem(PIEAnimPoly(fAnimPoly[i])^.Poly); // free vertex array
freemem(PIEAnimPoly(fAnimPoly[i])); // free TIEAnimPoly structure
end;
FreeAndNil(fAnimPoly);
IEDrawDibClose(fHDrawDib);
FreeAndNil(fVScrollBarParams);
FreeAndNil(fHScrollBarParams);
FreeAndNil(fMouseWheelParams);
FreeAndNil(fSelectionMask);
FreeAndNil(fLayerDefaults);
// free layers (frees also fIEBitmap and fBitmap)
for i := 0 to fLayers.Count - 1 do
TIELayer(fLayers[i]).Free;
FreeAndNil(fLayers);
// deleted by previous loop
fBitmap := nil;
fIEBitmap := nil;
fIEBitmapValid := False;
for i := 0 to fSavedSelection.Count-1 do
TObject(fSavedSelection[i]).Free;
FreeAndNil(fSavedSelection);
if fGXScr2Bmp <> nil then
freemem(fGXScr2Bmp);
if fGYScr2Bmp <> nil then
freemem(fGYScr2Bmp);
if fGXBmp2Scr <> nil then
freemem(fGXBmp2Scr);
if fGYBmp2Scr <> nil then
freemem(fGYBmp2Scr);
fGXScr2Bmp := nil;
fGYScr2Bmp := nil;
fGXScr2BmpSize := 0;
fGYScr2BmpSize := 0;
fGXBmp2Scr := nil;
fGYBmp2Scr := nil;
fXScr2BmpSize := 0;
fYScr2BmpSize := 0;
fXBmp2ScrSize := 0;
fYBmp2ScrSize := 0;
FreeAndNil(fLayerBoxPen);
FreeAndNil(fWallpaper);
FreeAndNil(fDrawPixelBitmap);
FreeAndNil( fLiveBackground );
if assigned(fNavigatorBackBuffer) then
fNavigatorBackBuffer.Free;
IEGDIPUnLoadLibrary();
inherited;
end;
procedure TImageEnView.SetupAniPolyTimer;
begin
if not assigned(fAnimPolyTimer) then
begin
fAnimPolyTimer := TTimer.Create(nil);
fAnimPolyTimer.enabled := false;
fAnimPolyTimer.Interval := 210;
fAnimPolyTimer.OnTimer := TimerEvent;
end;
end;
procedure TImageEnView.SetupTransition;
begin
if not assigned(fTransition) then
fTransition := TIETransitionEffects.Create( Self );
end;
procedure TImageEnView.SetupDrawPixelBitmap;
begin
if not assigned(fDrawPixelBitmap) then
begin
fDrawPixelBitmap := TBitmap.Create;
fDrawPixelBitmap.Width := 1;
fDrawPixelBitmap.Height := 1;
fDrawPixelBitmap.PixelFormat := pf24bit;
fDrawPixelPtr := PRGB(fDrawPixelBitmap.Scanline[0]);
end;
end;
{!!
<FS>TImageEnView.Background
<FM>Declaration<FC>
property Background: TColor;
<FM>Description<FN>
Specifies the background color, which is shown in the unoccupied area of the window (when the current image is smaller than the ImageEnView).
This color is used also in geometric processing (such as rotation) to fill blank areas.
If you change the background color after the component has been created (at runtime) , you won't see the change because the bitmap overlaps the background.
For this reason, to change background color at runtime you must:
- empty Layer1 bitmap:
ImageEnView.Blank;
ImageEnView.Background := clRed;
- or fill the bitmap using the color you want:
ImageEnView.Proc.Fill(clRed);
Note: This value may be overridden if <A TIEImageEnGlobalSettings.EnableTheming> is enabled.
Default: clBtnFace
<FM>Example<FC>
// Rotate of 30 degrees and fill blank spaces with clBlack color
ImageEnView1.Background := clBlack;
ImageEnView1.Proc.Rotate(30, False);
<FM>See Also<FN>
- <A TImageEnView.BackgroundStyle>
!!}
procedure TImageEnView.SetBackground(cl: TColor);
begin
inherited SetBackground(cl);
if csDesigning in ComponentState then
Clear;
UpdateReason := ieurComponentStuffChanged;
Update;
UpdateReason := ieurComponentStuffChanged;
ImageChange;
if (csDesigning in ComponentState) then
Paint;
end;
procedure TImageEnView.DoSize;
begin
if assigned(fTransition) and fTransition.Running then
fTransition.stop;
fNeedUpdateLiveBackground := True;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
{!!
<FS>TImageEnView.ScrollBars
<FM>Declaration<FC>
property ScrollBars: TScrollType;
<FM>Description<FN>
ScrollBars determines whether the TImageEnView control displays scroll bars (they are only shown if they are needed unless <A TImageEnView.ScrollBarsAlwaysVisible> is true).
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C><FC>ssNone<FN></C> <C>The control has no scroll bars</C> </R>
<R> <C><FC>ssHorizontal<FN></C> <C>The control has a single scroll bar on the bottom edge when needed</C> </R>
<R> <C><FC>ssVertical<FN></C> <C>The control has a single scroll bar on the right edge when needed</C> </R>
<R> <C><FC>ssBoth<FN></C> <C>The control has a scroll bar on both the bottom and right edges when needed</C> </R>
</TABLE>
!!}
procedure TImageEnView.SetScrollBars(v: TIEScrollStyle);
begin
fScrollBars := v;
if (fScrollBars <> ssVertical) and (fScrollBars <> ssBoth) then
IEShowScrollBar(handle, SB_VERT, false, fFlatScrollBars);
if (fScrollBars <> ssHorizontal) and (fScrollBars <> ssBoth) then
IEShowScrollBar(handle, SB_HORZ, false, fFlatScrollBars);
UpdateReason := ieurComponentStuffChanged;
Update;
end;
// Stable = False: Delay full quality display
// Stable = True: Display at normal quality
procedure TImageEnView.SetDisplayStable(Value: Boolean);
begin
if Value then
begin
fStable2 := 0;
if fStable > 0 then
begin
fStable := 0;
Update();
end;
end
else
begin
if (fDelayZoomFilter) and (fZoomFilter <> rfNone) and ((fZoomX <> 100) or (fZoomY <> 100)) then
fStable := fDelayZoomTicks;
fStable2 := fDelayAniSelTicks;
end;
end;
{!!
<FS>TImageEnView.Zoom
<FM>Declaration<FC>
property Zoom: double;
<FM>Description<FN>
Use the Zoom property to zoom in or out of the image. The image itself is not modified, only its display size changes.
Zoom is expressed as a percentage of its native size, i.e. 100 is normal size, values less than 100 decrease the display size and values greater than 100 increase the display size.
Note: <FC>Zoom<FN> will have no effect if <A TImageEnView.AutoFit>, <A TImageEnView.AutoShrink> or <A TImageEnView.AutoStretch> are enabled.
<FM>See Also<FN>
- <A TImageEnView.ZoomX>
- <A TImageEnView.ZoomY>
- <A TImageEnView.ZoomIn>
- <A TImageEnView.ZoomOut>
<FM>Example<FC>
// Half size: a 200x200 pixel image is displayed at a size of 100x100
ImageEnView.Zoom := 50;
// Double size: a 200x200 pixel image is displayed at a size of 400x400
ImageEnView.Zoom := 200;
// floating point zoom
ImageEnView.Zoom := 30.25;
!!}
procedure TImageEnView.SetZoomNoUpdate(v: double);
var
zz: double;
minX, minY: integer;
maxX, maxY: integer;
x, y: integer;
begin
StopSmoothScroll();
zz := v / 100;
x := GetClientWidthExRulers shr 1;
x := trunc(round((x + fViewX - fOffX) * (f100DZoomX)) * zz - x);
y := GetClientHeightExRulers shr 1;
y := trunc(round((y + fViewY - fOffY) * (f100DZoomY)) * zz - y);
fZoomX := v;
fZoomD100X := fZoomX / 100;
f100DZoomX := 100 / fZoomX;
fZoomY := v;
fZoomD100Y := fZoomY / 100;
f100DZoomY := 100 / fZoomY;
// Center image when scrollbars showed the first time
GetMinViewXY(minX, minY);
GetMaxViewXY(maxX, maxY);
fViewX := ilimit(x, minX, maxX);
fViewY := ilimit(y, minY, maxY);
end;
procedure TImageEnView.SetZoom(v: double);
begin
StopSmoothScroll();
ViewChanging(2, v);
if (v > 0) and ((v <> fZoomX) or (v <> fZoomY)) then
begin
SetDisplayStable( False );
LockPaint;
if assigned(fNavigator) then
fNavigator.LockPaint;
SetZoomNoUpdate(v);
UpdateReason := ieurZoomed;
Update; // initial update
UpdateReason := ieurZoomed;
Update; // final update (without this scroll-bars aren't updated well)
CalcPaintCoords;
CreateCoordConvLUT; // recalculates coordinate conversion LUT
NPUnLockPaint;
if assigned(fNavigator) then
fNavigator.NPUnLockPaint;
Paint;
ViewChange(1);
end;
end;
procedure TImageEnView.SetZoomXNoUpdate(v: double);
var
zz: double;
minX, minY: integer;
maxX, maxY: integer;
x: integer;
begin
StopSmoothScroll();
zz := v / 100;
x := GetClientWidthExRulers shr 1;
x := trunc(round((x + fViewX - fOffX) * (f100DZoomX)) * zz - x);
GetMinViewXY(minX, minY);
GetMaxViewXY(maxX, maxY);
fViewX := ilimit(x, minX, maxX);
fZoomX := v;
fZoomD100X := fZoomX / 100;
f100DZoomX := 100 / fZoomX;
end;
{!!
<FS>TImageEnView.ZoomX
<FM>Declaration<FC>
property ZoomX: Double;
<FM>Description<FN>
ZoomX specifies the horizontal Zoom. Using this property (and/or ZoomY) you lose the image aspect ratio and some functions, which require aspect ratio, may not work properly.
Setting ZoomX and ZoomY to the same value is equivalent to setting <A TImageEnView.Zoom>.
<FM>See Also<FN>
- <A TImageEnView.Zoom>
- <A TImageEnView.ZoomY>
!!}
procedure TImageEnView.SetZoomX(v: double);
begin
StopSmoothScroll();
ViewChanging(2, v);
if (v > 0) and (v <> fZoomX) then
begin
SetDisplayStable( False );
LockPaint;
SetZoomXNoUpdate(v);
UpdateReason := ieurZoomed;
Update; // initial update
UpdateReason := ieurZoomed;
Update; // final update (without this scroll-bars aren't updated well)
CalcPaintCoords;
CreateCoordConvLUT; // recalculates coordinate conversion LUT
NPUnLockPaint;
Paint;
ViewChange(1);
end;
end;
procedure TImageEnView.SetZoomYNoUpdate(v: double);
var
zz: double;
minX, minY: integer;
maxX, maxY: integer;
y: integer;
begin
StopSmoothScroll();
zz := v / 100;
y := GetClientHeightExRulers shr 1;
y := trunc(round((y + fViewY - fOffY) * (f100DZoomY)) * zz - y);
GetMinViewXY(minX, minY);
GetMaxViewXY(maxX, maxY);
fViewY := ilimit(y, minX, maxY);
fZoomY := v;
fZoomD100Y := fZoomY / 100;
f100DZoomY := 100 / fZoomY;
end;
{!!
<FS>TImageEnView.ZoomY
<FM>Declaration<FC>
property ZoomY: Double;
<FM>Description<FN>
ZoomY specifies the vertical zoom. Using this property (and/or ZoomX) you lose the image aspect ratio and some functions, which require aspect ratio, may not work properly.
Setting <A TImageEnView.ZoomX> and ZoomY to the same value is equivalent to setting <A TImageEnView.Zoom>.
<FM>See Also<FN>
- <A TImageEnView.Zoom>
- <A TImageEnView.ZoomX>
!!}
procedure TImageEnView.SetZoomY(v: double);
begin
StopSmoothScroll();
ViewChanging(2, v);
if (v > 0) and (v<>fZoomY) then
begin
SetDisplayStable( False );
LockPaint;
SetZoomYNoUpdate(v);
UpdateReason := ieurZoomed;
Update; // initial update
UpdateReason := ieurZoomed;
Update; // final update (without this scroll-bars aren't updated well)
CalcPaintCoords;
CreateCoordConvLUT; // recalculates coordinate conversion LUT
NPUnLockPaint;
Paint;
ViewChange(1);
end;
end;
// TODO: Unused method
// creates a palette for the current bitmap
type
tpal = record
palVersion: word;
palNumEntries: word;
PaletteEntry: array[0..255] of TPALETTEENTRY;
end;
plogpalette = ^tlogpalette;
function TImageEnView._CreatePalette: HPalette;
var
pa: tpal;
pp: array[0..255] of TRGB;
q: integer;
qt: TIEQuantizer;
begin
qt := TIEQuantizer.Create(fIEBitmap, pp, 256);
for q := 0 to 255 do
begin
pa.PaletteEntry[q].peRed := pp[q].r;
pa.PaletteEntry[q].peGreen := pp[q].g;
pa.PaletteEntry[q].peBlue := pp[q].b;
pa.PaletteEntry[q].peFlags := 0;
end;
pa.palVersion := $300;
pa.palNumEntries := 256;
FreeAndNil(qt);
result := CreatePalette(plogpalette(@pa)^);
end;
{!!
<FS>TImageEnView.IdealComponentWidth
<FM>Declaration<FC>
property IdealComponentWidth: integer; (Read-only)
<FM>Description<FN>
Returns the width that the TImageEnView component should be to exactly fit the current image.
<FM>Example<FC>
// Resize ImageEnView to fully contain Carlotta.jpg
ImageEnView.LoadFromFile('C:\Carlotta.jpg');
ImageEnView1.Width := ImageEnView1.IdealComponentWidth;
ImageEnView1.Height := ImageEnView1.IdealComponentHeight;
<FM>See Also<FN>
- <L TImageEnView.LayerOptions>loFitToLayersWhenZooming</L>
- <A TImageEnView.IdealComponentHeight>
!!}
// ideal horizontal component size
function TImageEnView.GetIdealComponentWidth: integer;
var
z1: integer;
begin
z1 := trunc( fZoomX * MaxLayerWidth() / 100 ); // new width of the bitmap
result := z1 + 1;
if ( fScrollBars = ssHorizontal ) or ( fScrollBars = ssBoth ) or ( fShowRulers <> [] ) then
inc(result, width - GetClientWidthExRulers);
end;
{!!
<FS>TImageEnView.IdealComponentHeight
<FM>Declaration<FC>
property IdealComponentHeight: integer; (Read-only)
<FM>Description<FN>
Returns the height that the TImageEnView component should be to exactly fit the current image.
<FM>Example<FC>
// Resize ImageEnView to fully contains Carlotta.jpg
ImageEnView.LoadFromFile('C:\Carlotta.jpg');
ImageEnView1.Width := ImageEnView1.IdealComponentWidth;
ImageEnView1.Height := ImageEnView1.IdealComponentHeight;
<FM>See Also<FN>
- <L TImageEnView.LayerOptions>loFitToLayersWhenZooming</L>
- <A TImageEnView.IdealComponentWidth>
!!}
function TImageEnView.GetIdealComponentHeight: integer;
var
z2: integer;
begin
z2 := trunc(fZoomY * MaxLayerHeight() / 100 ); // new height of the bitmap
result := z2 + 1;
if ( fScrollBars = ssHorizontal ) or ( fScrollBars = ssBoth ) or ( fShowRulers <> [] ) then
inc(result, height - GetClientHeightExRulers);
end;
{!!
<FS>TImageEnView.IdealImageWidth
<FM>Declaration<FC>
property IdealImageWidth: integer; (Read-only)
<FM>Description<FN>
Returns the width that an image should be to fill the entire TImageEnView without requiring a scrollbar
<FM>Example<FC>
procedure Tfmain.ImageEnView1LayerSelectionChange(Sender: TObject);
var
bmp: TIEBitmap;
begin
// Show a preview in ImageEnView2 of the layer selected in ImageEnView1
bmp := ImageEnView2.IEBitmap;
ImageEnView1.CurrentLayer.CopyToBitmap( bmp, ImageEnView2.IdealImageWidth, ImageEnView2.IdealImageWidth );
ImageEnView2.Update();
end;
<FM>See Also<FN>
- <A TImageEnView.IdealImageHeight>
!!}
function TImageEnView.GetIdealImageWidth: integer;
var
edgeLeft, edgeTop, edgeRight, edgeBottom: integer;
begin
CalcEdges( edgeLeft, edgeTop, edgeRight, edgeBottom, true, true );
Result := ( Width - edgeLeft - edgeRight )
end;
{!!
<FS>TImageEnView.IdealImageHeight
<FM>Declaration<FC>
property IdealImageHeight: integer; (Read-only)
<FM>Description<FN>
Returns the width that an image should be to fill the entire TImageEnView without requiring a scrollbar
<FM>Example<FC>
procedure Tfmain.ImageEnView1LayerSelectionChange(Sender: TObject);
var
bmp: TIEBitmap;
begin
// Show a preview in ImageEnView2 of the layer selected in ImageEnView1
bmp := ImageEnView2.IEBitmap;
ImageEnView1.CurrentLayer.CopyToBitmap( bmp, ImageEnView2.IdealImageWidth, ImageEnView2.IdealImageWidth );
ImageEnView2.Update();
end;
<FM>See Also<FN>
- <A TImageEnView.IdealImageWidth>
!!}
function TImageEnView.GetIdealImageHeight: integer;
var
edgeLeft, edgeTop, edgeRight, edgeBottom: integer;
begin
CalcEdges( edgeLeft, edgeTop, edgeRight, edgeBottom, true, true );
Result := ( Height - edgeTop - edgeBottom );
end;
procedure TImageEnView.DoMouseWheelScroll(Delta: integer; mouseX, mouseY: integer; CtrlPressed: boolean);
var
zDelta, nPos, xTmp: integer;
dPos: double;
dir: integer;
mx, my: Integer;
begin
if (fMouseWheelParams.Action = iemwNone) or fAutoFit then
exit;
zDelta := Delta;
if zDelta > 0 then
dir := -1
else
dir := 1;
if fMouseWheelParams.InvertDirection then
dir := -1 * dir;
case fMouseWheelParams.Action of
iemwVScroll:
begin
GetMaxViewXY(mx, my);
case fMouseWheelParams.Variation of
iemwAbsolute: // bitmap based
if CtrlPressed or ((mx > 0) and (my = 0)) then
begin
// horizontal
nPos := fViewX + dir * fMouseWheelParams.Value * imax(trunc(fZoomD100X), 1);
SetViewX(nPos);
end
else
begin
// vertical
nPos := fViewY + dir * fMouseWheelParams.Value * imax(trunc(fZoomD100Y), 1);
SetViewY(nPos);
end;
iemwPercentage: // client based
if CtrlPressed or ((mx > 0) and (my = 0)) then
begin
// horizontal
xTmp := imax(round(GetClientWidthExRulers * fMouseWheelParams.Value / 100), 1);
nPos := fViewX + dir * xTmp;
SetViewX(nPos);
end
else
begin
// vertical
xTmp := imax(round(GetClientHeightExRulers * fMouseWheelParams.Value / 100), 1);
nPos := fViewY + dir * xTmp;
SetViewY(nPos);
end;
end;
end;
iemwZoom, iemwZoomView:
begin
dPos := fZoomX;
case fMouseWheelParams.Variation of
iemwAbsolute:
dPos := fZoomX + dir * fMouseWheelParams.Value;
iemwPercentage:
dPos := fZoomX + imax(round(fZoomX * fMouseWheelParams.Value / 100), 1) * dir;
end;
if (dPos > fZoomX) then
DoZoomIn(dPos);
if (dPos < fZoomX) then
DoZoomOut(dPos);
if fMouseWheelParams.ZoomPosition = iemwCenter then
SetZoom(dPos)
else
ZoomAt(mouseX, mouseY, dPos, false);
end;
iemwNavigate:
begin
if dir < 0 then
IO.Seek(ieioSeekPrior)
else
IO.Seek(ieioSeekNext);
end;
end;
end;
// Position is valid on when command is iescPosition
procedure TImageEnView.DoVertScroll(command: TIEScrollCommand; Position: integer);
var
nPos: integer;
mx, my: integer;
begin
nPos := 0;
case command of
iescPosition:
nPos := Position;
iescBottom:
begin
GetMaxViewXY( mx, my );
nPos := my;
end;
iescTop:
begin
GetMinViewXY( mx, my );
nPos := my;
end;
iescLineDown:
if fVScrollBarParams.LineStep = -1 then
nPos := fViewY + imax(trunc(fZoomD100Y), 1)
else
nPos := fViewY + fVScrollBarParams.LineStep * imax(trunc(fZoomD100Y), 1);
iescLineUp:
if fVScrollBarParams.LineStep = -1 then
nPos := fViewY - imax(trunc(fZoomD100Y), 1)
else
nPos := fViewY - fVScrollBarParams.LineStep * imax(trunc(fZoomD100Y), 1);
iescPageDown:
if fVScrollBarParams.PageStep = -1 then
nPos := fViewY + GetClientHeightExRulers
else
nPos := fViewY + fVScrollBarParams.PageStep * imax(trunc(fZoomD100Y), 1);
iescPageUp:
if fVScrollBarParams.PageStep = -1 then
nPos := fViewY - GetClientHeightExRulers
else
nPos := fViewY - fVScrollBarParams.PageStep * imax(trunc(fZoomD100Y), 1);
end;
SetViewY(nPos);
end;
// Position is valid on when command is scPosition
procedure TImageEnView.DoHorizScroll(command: TIEScrollCommand; Position: integer);
var
nPos: integer;
mx, my: integer;
begin
nPos := 0;
case command of
iescPosition:
nPos := Position;
iescBottom:
begin
GetMaxViewXY( mx, my );
nPos := mx;
end;
iescTop:
begin
GetMinViewXY( mx, my );
nPos := mx;
end;
iescLineDown:
if fHScrollBarParams.LineStep = -1 then
nPos := fViewX + imax(trunc(fZoomD100X), 1)
else
nPos := fViewX + fHScrollBarParams.LineStep * imax(trunc(fZoomD100X), 1);
iescLineUp:
if fHScrollBarParams.LineStep = -1 then
nPos := fViewX - imax(trunc(fZoomD100X), 1)
else
nPos := fViewX - fHScrollBarParams.LineStep * imax(trunc(fZoomD100X), 1);
iescPageDown:
if fHScrollBarParams.PageStep = -1 then
nPos := fViewX + GetClientWidthExRulers
else
nPos := fViewX + fHScrollBarParams.PageStep * imax(trunc(fZoomD100X), 1);
iescPageUp:
if fHScrollBarParams.PageStep = -1 then
nPos := fViewX - GetClientWidthExRulers
else
nPos := fViewX - fHScrollBarParams.PageStep * imax(trunc(fZoomD100X), 1);
end;
SetViewX(nPos);
end;
procedure TImageEnView.WMVScroll(var Message: TMessage);
var
cmd: TIEScrollCommand;
pos: integer;
begin
inherited;
pos := 0;
case Message.WParamLo of
SB_THUMBTRACK, SB_THUMBPOSITION:
begin
if (not fVScrollBarParams.Tracking) and (Message.WParamLo = SB_THUMBTRACK) then
exit;
cmd := iescPosition;
pos := round( Message.WParamHi * fRYScroll ); // "round" to avoid scrolling with a single click
end;
SB_BOTTOM: cmd := iescBottom;
SB_TOP: cmd := iescTop;
SB_LINEDOWN: cmd := iescLineDown;
SB_LINEUP: cmd := iescLineUp;
SB_PAGEDOWN: cmd := iescPageDown;
SB_PAGEUP: cmd := iescPageUp;
else
begin
cmd := iescPosition;
pos := fViewY;
end;
end;
DoVertScroll(cmd, pos);
end;
procedure TImageEnView.WMHScroll(var Message: TMessage);
var
cmd: TIEScrollCommand;
pos: integer;
begin
inherited;
pos := 0;
case Message.WParamLo of
SB_THUMBTRACK, SB_THUMBPOSITION:
begin
if (not fHScrollBarParams.Tracking) and (Message.WParamLo = SB_THUMBTRACK) then
exit;
cmd := iescPosition;
pos := round( Message.WParamHi * fRXScroll ); // "round" to avoid scrolling with a single click
end;
SB_BOTTOM: cmd := iescBottom;
SB_TOP: cmd := iescTop;
SB_LINEDOWN: cmd := iescLineDown;
SB_LINEUP: cmd := iescLineUp;
SB_PAGEDOWN: cmd := iescPageDown;
SB_PAGEUP: cmd := iescPageUp;
else
begin
cmd := iescPosition;
pos := fViewX;
end;
end;
DoHorizScroll(cmd, pos);
end;
procedure TImageEnView.WMMouseWheel(var Message: TMessage);
var
pt: TPoint;
begin
inherited;
pt.x := smallint(Message.LParamLo);
pt.y := smallint(Message.LParamHi);
pt := ScreenToClient(pt);
DoMouseWheelScroll(smallint($FFFF and (Message.wParam shr 16)), pt.x, pt.y, GetKeyState(VK_CONTROL) < 0);
end;
procedure TImageEnView.WMMouseHWheel(var Message: TMessage);
begin
inherited;
end;
// double click - end of polygonal selection
procedure TImageEnView.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
inherited;
DoDoubleClickEx((MK_SHIFT and Message.Keys) <> 0);
end;
procedure TImageEnView.WMKillFocus(var Msg: TWMKillFocus);
begin
inherited;
invalidate;
end;
procedure TImageEnView.WMSetFocus(var Msg: TWMSetFocus);
begin
inherited;
invalidate;
end;
procedure TImageEnView.WMPaint(var Message: TWMPaint);
begin
{$IFDEF IEINCLUDEDIRECTSHOW}
if assigned(fImageEnIO) and assigned(fImageEnIO.DShowParams) and fImageEnIO.DShowParams.RenderVideo and (fImageEnIO.DShowParams.State<>gsStopped) then
fImageEnIO.DShowParams.RepaintVideo(handle, Canvas.Handle);
{$ENDIF}
inherited;
end;
procedure TImageEnView.IEMUpdate(var Message: TMessage);
begin
Update;
end;
procedure TImageEnView.IEMProgress(var Message: TMessage);
begin
if assigned(fOnProgress) then
fOnProgress(self, Message.wParam);
end;
procedure TImageEnView.IEMFinishWork(var Message: TMessage);
begin
if assigned(fOnFinishWork) then
fOnFinishWork(self);
end;
procedure TImageEnView.WMSize(var Message: TWMSize);
begin
inherited;
if not fInsideUpdate then
DoSize;
end;
procedure TImageEnView.WMEraseBkgnd(var Message: TMessage);
begin
Message.Result := 0;
end;
{!!
<FS>TImageEnView.SetViewXY
<FM>Declaration<FC>
procedure SetViewXY(x, y: integer);
<FM>Description<FN>
Sets <A TImageEnView.ViewX> and <A TImageEnView.ViewY> in one step to change the displayed portion of the image (programatic scroll).
!!}
procedure TImageEnView.SetViewXY(x, y: integer);
var
minX, minY: integer;
maxX, maxY: integer;
lviewx, lviewy: integer;
begin
ViewChanging(0, x);
ViewChanging(1, y);
lviewx := fViewX;
lviewy := fViewY;
if (x = fViewX) and (y = fViewY) then
exit;
GetMinViewXY(minX, minY);
GetMaxViewXY(maxX, maxY);
fViewX := ilimit(x, minX, maxX);
fViewY := ilimit(y, minY, maxY);
if (fViewX = lviewx) and (fViewY = lviewy) then
exit;
SetDisplayStable( False );
fUpdateBackBuffer := true;
fFullUpdateRequest := true;
Paint;
ViewChange(0);
if fSel then
AniPolyFunc(self, true);
if (fScrollBars = ssHorizontal) or (fScrollBars = ssBoth) then
IESetScrollPos(Handle, SB_HORZ, trunc(fViewX / fRXScroll), true, fFlatScrollBars);
if (fScrollBars = ssVertical) or (fScrollBars = ssBoth) then
IESetScrollPos(Handle, SB_VERT, trunc(fViewY / fRYScroll), true, fFlatScrollBars);
end;
{!!
<FS>TImageEnView.ViewX
<FM>Declaration<FC>
property ViewX: integer;
<FM>Description<FN>
ViewX and <A TImageEnView.ViewY> specify the top-left of the image that is visible in the TImageEnView component window. Use ViewX/Y to programatically scroll images bigger than the TImageEnView.
<FM>Example<FC>
ImageEnView1.ViewX := 20; // View from column 20 of bitmap (i.e. first 20 pixels on the left of the image will no longer be visible)
ImageEnView1.Zoom := 200; // zoom image to 200%
ImageEnView1.ViewX := 20; // view from column 40 of bitmap (i.e. first 40 pixels on the left of the image will no longer be visible)
<FM>See Also<FN>
- <A TImageEnView.ViewY>
- <A TImageEnView.ExtentX>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.VisibleBitmapRect>
!!}
procedure TImageEnView.SetViewX(v: integer);
var
minX, minY: integer;
maxX, maxY: integer;
lviewx: Integer;
begin
ViewChanging(0, v);
if v = fViewX then
exit;
lviewx := fViewX;
GetMinViewXY(minX, minY);
GetMaxViewXY(maxX, maxY);
fViewX := ilimit(v, minX, maxX);
if fViewX=lviewx then
exit;
SetDisplayStable( False );
fUpdateBackBuffer := true;
fFullUpdateRequest := true;
paint;
ViewChange(0);
if fSel then
AniPolyFunc(self, true);
if (fScrollBars = ssHorizontal) or (fScrollBars = ssBoth) then
IESetScrollPos(Handle, SB_HORZ, trunc(fViewX / fRXScroll), true, fFlatScrollBars);
end;
{!!
<FS>TImageEnView.ViewY
<FM>Declaration<FC>
property ViewY: integer;
<FM>Description<FN>
<A TImageEnView.ViewX> and ViewY specify the top-left of the image that is visible in the TImageEnView component window. Use ViewX/Y to programatically scroll images bigger than the TImageEnView.
<FM>Example<FC>
ImageEnView1.ViewX := 20; // View from column 20 of bitmap (i.e. first 20 pixels on the left of the image will no longer be visible)
ImageEnView1.Zoom := 200; // zoom image to 200%
ImageEnView1.ViewX := 20; // view from column 40 of bitmap (i.e. first 40 pixels on the left of the image will no longer be visible)
<FM>See Also<FN>
- <A TImageEnView.ViewX>
- <A TImageEnView.ExtentY>
- <A TImageEnView.YScr2Bmp>
- <A TImageEnView.VisibleBitmapRect>
!!}
procedure TImageEnView.SetViewY(v: integer);
var
minX, minY: integer;
maxX, maxY: integer;
lviewy: Integer;
begin
ViewChanging(1, v);
if v = fViewY then
exit;
lviewy := fViewY;
GetMinViewXY(minX, minY);
GetMaxViewXY(maxX, maxY);
fViewY := ilimit(v, minY, maxY);
if fViewY = lviewy then
exit;
SetDisplayStable( False );
fUpdateBackBuffer := true;
fFullUpdateRequest := true;
paint;
ViewChange(0);
if fSel then
AniPolyFunc(self, true);
if (fScrollBars = ssVertical) or (fScrollBars = ssBoth) then
IESetScrollPos(Handle, SB_VERT, trunc(fViewY / fRYScroll), true, fFlatScrollBars);
end;
{!!
<FS>TImageEnView.GetMaxViewXY
<FM>Declaration<FC>
procedure GetMaxViewXY(var mx, my: integer);
<FM>Description<FN>
Returns the maximum values for <A TImageEnView.ViewX> and <A TImageEnView.ViewY> (which will fill the mx and my vars). I.e. this is the maximum that the image can be programatically scrolled.
!!}
procedure TImageEnView.GetMaxViewXY(var mx, my: integer);
var
zx, zy: integer;
begin
if fZoomX <> fZoomY then
begin
zx := trunc(fLayersRect.width * fZoomD100X); // new bitmap width (zoom)
zy := trunc(fLayersRect.height * fZoomD100Y); // new bitmap height (zoom)
if zx > (GetClientWidthExRulers + 10) then
// this avoids cutting last right/bottom pixel
zx := trunc((fLayersRect.width + 1) * fZoomD100X); // new bitmap width (zoom)
if zy > (GetClientHeightExRulers + 10) then
// this avoids cutting last right/bottom pixel
zy := trunc((fLayersRect.height + 1) * fZoomD100Y); // new bitmap height (zoom)
mx := 0;
my := 0;
if (zx > 0) and (zy > 0) then
begin
mx := zx - GetClientWidthExRulers;
my := zy - GetClientHeightExRulers;
if (mx < 0) or (XScr2Bmp( mx, False ) = 0) then
mx := 0;
if (my < 0) or (YScr2Bmp( my, False ) = 0) then
my := 0;
if zx <= Width - fRulerParams.RulerAreaLeft - fRulerParams.RulerAreaRight then
mx := 0;
if zy <= Height - fRulerParams.RulerAreaTop - fRulerParams.RulerAreaBottom then
my := 0;
end;
end
else
begin
zx := trunc(fLayersRect.width * fZoomD100X); // new bitmap width (zoom)
zy := trunc(fLayersRect.height * fZoomD100Y); // new bitmap height (zoom)
if ((zx > (GetClientWidthExRulers + 10)) or (zy > (GetClientHeightExRulers + 10))) and (ScrollBars <> ssNone) then
begin
// this is to avoid removing the last right/bottom pixel
zx := trunc((fLayersRect.width + 1) * fZoomD100X); // new bitmap width (zoom)
zy := trunc((fLayersRect.height + 1) * fZoomD100Y); // new bitmap height (zoom)
end;
mx := 0;
my := 0;
if (zx > 0) and (zy > 0) then
begin
mx := zx - GetClientWidthExRulers;
my := zy - GetClientHeightExRulers;
if (mx < 0) or (XScr2Bmp( mx, False ) = 0) then
mx := 0;
if (my < 0) or (YScr2Bmp( my, False ) = 0) then
my := 0;
if ( zx <= Width - fRulerParams.RulerAreaLeft - fRulerParams.RulerAreaRight ) and
( zy <= Height - fRulerParams.RulerAreaTop - fRulerParams.RulerAreaBottom ) then
begin
mx := 0;
my := 0;
end;
end;
end;
end;
// At this time always returns 0,0, because scrolling is not possible outside Layer 0
procedure TImageEnView.GetMinViewXY(var mx, my: integer);
begin
mx := 0;
my := 0;
end;
function TImageEnView.PaletteChanged(Foreground: Boolean): Boolean;
begin
{$IFNDEF OCXVERSION}
if assigned(application) and assigned(application.mainform) and assigned(application.mainform.canvas) then
begin
if IEDrawDibRealize(fHDrawDib, application.mainform.canvas.handle, false) > 0 then
invalidate;
end
else
invalidate;
{$ELSE}
invalidate;
{$ENDIF}
result := true;
end;
// makes an event OnImageChange
{!!
<FS>TImageEnView.ImageChange
<FM>Declaration<FC>
procedure ImageChange; virtual;
<FM>Description<FN>
Generates an <A TImageEnView.OnImageChange> event and sets <A TImageEnView.Modified> to true.
!!}
procedure TImageEnView.ImageChange();
begin
inherited;
fModified := True;
if assigned(fOnImageChange) then
fOnImageChange(self);
fNeedUpdateLiveBackground := True;
StartPlayTimer;
if ( fImageSet = False ) and ( UpdateReason <> ieurComponentStuffChanged ) and ( fLayersCurrent = 0 ) then
if ((csReading in ComponentState) or (csDesigning in ComponentState) or (csLoading in ComponentState)) = False then
fImageSet := True;
end;
{!!
<FS>TImageEnView.Clear
<FM>Declaration<FC>
procedure Clear;
<FM>Description<FN>
Fills the current image with the <A TImageEnView.Background> color.
<FM>Comparison of Methods<FN>
<TABLE>
<R> <H>Method</H> <H>Description</H> </R>
<R> <C> <A TImageEnView.Clear> </C> <C> Fills the current image with the <A TImageEnView.Background>background color</L> and <L TImageEnView.RemoveAlphaChannel>removes</L> the alpha channel </C> </R>
<R> <C> <A TImageEnView.Blank> </C> <C> Calls <A TImageEnView.Clear> and resets the image size to 1 x 1</C> </R>
<R> <C> <A TImageEnView.LayersClear> </C> <C> Removes all of the <A TImageEnView.Layers>layers</L> </C> </R>
<R> <C> <A TImageEnView.ClearAll> </C> <C> Resets the image (calling <A TImageEnView.Blank>) and removes all layers (calling <A TImageEnView.LayersClear>) </C> </R>
</TABLE>
<FM>Example<FC>
// Clear the image
ImageEn1.Background := clWhite;
ImageEn1.Clear;
// Clear the top-most layer
ImageEnView1.LayersCurrent := ImageEnView1.LayersCount - 1;
ImageEnView1.Clear;
<FM>See Also<FN>
- <A TImageEnView.Blank>
- <A TImageEnView.ClearAll>
!!}
procedure TImageEnView.Clear;
begin
if fIEBitmapValid then
fIEBitmap.Fill(Background);
RemoveAlphaChannel(false);
Update;
ImageChange();
end;
{!!
<FS>TImageEnView.ClearAll
<FM>Declaration<FC>
procedure ClearAll;
<FM>Description<FN>
Removes all content from the control, by removing all layers (with <A TImageEnView.LayersClear>) and resetting the background image (with <A TImageEnView.Blank>).
<FM>Comparison of Methods<FN>
<TABLE>
<R> <H>Method</H> <H>Description</H> </R>
<R> <C> <A TImageEnView.Clear> </C> <C> Fills the current image with the <A TImageEnView.Background>background color</L> and <L TImageEnView.RemoveAlphaChannel>removes</L> the alpha channel </C> </R>
<R> <C> <A TImageEnView.Blank> </C> <C> Calls <A TImageEnView.Clear> and resets the image size to 1 x 1</C> </R>
<R> <C> <A TImageEnView.LayersClear> </C> <C> Removes all of the <A TImageEnView.Layers>layers</L> </C> </R>
<R> <C> <A TImageEnView.ClearAll> </C> <C> Resets the image (calling <A TImageEnView.Blank>) and removes all layers (calling <A TImageEnView.LayersClear>) </C> </R>
</TABLE>
<FM>Example<FC>
ImageEn1.ClearAll;
<FM>See Also<FN>
- <A TImageEnView.LayersClear>
- <A TImageEnView.Clear>
- <A TImageEnView.Blank>
!!}
procedure TImageEnView.ClearAll;
begin
LockUpdate;
LayersClear;
Blank;
UnlockUpdate;
end;
{!!
<FS>TImageEnView.Blank
<FM>Declaration<FC>
procedure Blank;
<FM>Description<FN>
Resets the image size to 1x1 (Width x Height).
Note: <FC>Blank<FN> also calls <A TImageEnView.Clear>
<FM>Comparison of Methods<FN>
<TABLE>
<R> <H>Method</H> <H>Description</H> </R>
<R> <C> <A TImageEnView.Clear> </C> <C> Fills the current image with the <A TImageEnView.Background>background color</L> and <L TImageEnView.RemoveAlphaChannel>removes</L> the alpha channel </C> </R>
<R> <C> <A TImageEnView.Blank> </C> <C> Calls <A TImageEnView.Clear> and resets the image size to 1 x 1</C> </R>
<R> <C> <A TImageEnView.LayersClear> </C> <C> Removes all of the <A TImageEnView.Layers>layers</L> </C> </R>
<R> <C> <A TImageEnView.ClearAll> </C> <C> Resets the image (calling <A TImageEnView.Blank>) and removes all layers (calling <A TImageEnView.LayersClear>) </C> </R>
</TABLE>
<FM>Example<FC>
ImageEnView1.IO.LoadFromFile('image.tif'); // Load image
...
ImageEnView1.Blank; // free memory
<FM>See Also<FN>
- <A TImageEnView.LayersClear>
- <A TImageEnView.Clear>
- <A TImageEnView.ClearAll>
!!}
procedure TImageEnView.Blank;
begin
StopPlayTimer;
if assigned(fTransition) and fTransition.Running then
fTransition.stop;
if fIEBitmapValid then
begin
fIEBitmap.Width := 1;
fIEBitmap.Height := 1;
end;
Clear;
fImageSet := False;
end;
{!!
<FS>TImageEnView.IsEmpty
<FM>Declaration<FC>
property IsEmpty: Boolean;
<FM>Description<FN>
Returns True if the size of the image is less than 2 x 2 pixels.
Also, see <A TImageenView.IsEmpty2> which returns true if an image has not been assigned or loaded.
!!}
function TImageEnView.GetIsEmpty: boolean;
begin
result := True;
if fIEBitmapValid then
Result := (fIEBitmap.Width < 2) and (fIEBitmap.Height < 2);
end;
{!!
<FS>TImageEnView.IsEmpty2
<FM>Declaration<FC>
property IsEmpty2 : boolean;
<FM>Description<FN>
Returns True if the image is empty, i.e. no image has been assigned or loaded.
Also, see <A TImageenView.IsEmpty> which returns true if the image is less than 2 x 2 pixels.
!!}
function TImageEnView.GetIsEmpty2: boolean;
var
bBackgroundOnly: Boolean;
brgb, irgb: TRGB;
begin
Result := True;
if fIEBitmapValid = False then
exit;
if fImageSet and ( fLayersCurrent = 0 ) then
Result := False
else
if (fIEBitmap.Width > 1) and (fIEBitmap.Height > 1) then
begin
irgb := fIEBitmap.Pixels[0, 0];
brgb := TColor2TRGB(TColor(fBackground));
bBackgroundOnly := (fIEBitmap.Width = Width) and (fIEBitmap.Height = Height) and (irgb.r = brgb.r) and (irgb.g = brgb.g) and (irgb.b = brgb.b);
Result := bBackgroundOnly;
end;
end;
function TImageEnView.GetSelectedRect(): TIERectangle;
begin
if Selected then
result := IERectangle(SelX1, SelY1, SelX2 - SelX1 + 1, SelY2 - SelY1 + 1)
else
if fIEBitmapValid then
result := IERectangle(0, 0, fIEBitmap.Width, fIEBitmap.Height)
else
result := IERectangle(0, 0, 0, 0)
end;
{!!
<FS>TImageEnView.SelX1
<FM>Declaration<FC>
property SelX1: integer; (Read-only)
<FM>Description<FN>
Returns the top-left X (column) coordinate of the selected area.
The value is a coordinate of the Bitmap itself, i.e. it is independent of scrolling (<A TImageenView.ViewX> and <A TImageenView.ViewY>) and <A TImageenView.Zoom> (e.g. you will get the same result whether the zoom is 50% or 100%).
Note:
- The selected area can be specified using <A TImageEnView.Select>
- You can convert the returned value to a screen value using <A TImageEnView.XBmp2Scr>
<FM>Example<FC>
// Enlarge the selection by ten pixels on all sides
With ImageEnView1 do
begin
SelectionBase := iesbBitmap;
Select( SelX1 - 10, SelY1 - 10, SelX2 - 10, SelY2 - 10 );
End;
<FM>See Also<FN>
- <A TImageEnView.SelY1>
- <A TImageEnView.SelX2>
- <A TImageEnView.SelY2>
- <A TImageEnView.Select>
- <A TImageEnView.XBmp2Scr>
- <A TImageEnView.YBmp2Scr>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.YScr2Bmp>
!!}
function TImageEnView.GetSelX1: integer;
begin
if fPolySelecting or fRectSelecting or fCircSelecting or (fRectResizing <> ieNone) or fRectMoving then
result := PIEAnimPoly(fHPolySel)^.rx1
else
result := fSelectionMask.X1;
end;
{!!
<FS>TImageEnView.SelY1
<FM>Declaration<FC>
property SelY1: integer; (Read-only)
<FM>Description<FN>
Returns the top-left Y (row) coordinate of the selected area.
The value is a coordinate of the Bitmap itself, i.e. it is independent of scrolling (<A TImageenView.ViewX> and <A TImageenView.ViewY>) and <A TImageenView.Zoom> (e.g. you will get the same result whether the zoom is 50% or 100%).
Note:
- The selected area can be specified using <A TImageEnView.Select>
- You can convert the returned value to a screen value using <A TImageEnView.YBmp2Scr>
<FM>Example<FC>
// Enlarge the selection by ten pixels on all sides
With ImageEnView1 do
begin
SelectionBase := iesbBitmap;
Select( SelX1 - 10, SelY1 - 10, SelX2 - 10, SelY2 - 10 );
End;
<FM>See Also<FN>
- <A TImageEnView.SelX1>
- <A TImageEnView.SelX2>
- <A TImageEnView.SelY2>
- <A TImageEnView.Select>
- <A TImageEnView.XBmp2Scr>
- <A TImageEnView.YBmp2Scr>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.YScr2Bmp>
!!}
function TImageEnView.GetSelY1: integer;
begin
if fPolySelecting or fRectSelecting or fCircSelecting or (fRectResizing <> ieNone) or fRectMoving then
result := PIEAnimPoly(fHPolySel)^.ry1
else
result := fSelectionMask.Y1;
end;
{!!
<FS>TImageEnView.SelX2
<FM>Declaration<FC>
property SelX2: integer; (Read-only)
<FM>Description<FN>
Returns bottom-right X (column) coordinate of the selected area.
The value is a coordinate of the Bitmap itself, i.e. it is independent of scrolling (<A TImageenView.ViewX> and <A TImageenView.ViewY>) and <A TImageenView.Zoom> (e.g. you will get the same result whether the zoom is 50% or 100%).
Note:
- The selected area can be specified using <A TImageEnView.Select>
- You can convert the returned value to a screen value using <A TImageEnView.XBmp2Scr>
<FM>Example<FC>
// Enlarge the selection by ten pixels on all sides
With ImageEnView1 do
begin
SelectionBase := iesbBitmap;
Select( SelX1 - 10, SelY1 - 10, SelX2 - 10, SelY2 - 10 );
End;
<FM>See Also<FN>
- <A TImageEnView.SelX1>
- <A TImageEnView.SelY1>
- <A TImageEnView.SelY2>
- <A TImageEnView.Select>
- <A TImageEnView.XBmp2Scr>
- <A TImageEnView.YBmp2Scr>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.YScr2Bmp>
!!}
function TImageEnView.GetSelX2: integer;
begin
if fPolySelecting or fRectSelecting or fCircSelecting or (fRectResizing <> ieNone) or fRectMoving then
result := PIEAnimPoly(fHPolySel)^.rx2 - 1
else
result := fSelectionMask.X2;
end;
{!!
<FS>TImageEnView.SelY2
<FM>Declaration<FC>
property SelY2: integer; (Read-only)
<FM>Description<FN>
Returns the bottom-right Y (row) coordinate of the selected area.
The value is a coordinate of the Bitmap itself, i.e. it is independent of scrolling (<A TImageenView.ViewX> and <A TImageenView.ViewY>) and <A TImageenView.Zoom> (e.g. you will get the same result whether the zoom is 50% or 100%).
Note:
- The selected area can be specified using <A TImageEnView.Select>
- You can convert the returned value to a screen value using <A TImageEnView.YBmp2Scr>
<FM>Example<FC>
// Enlarge the selection by ten pixels on all sides
With ImageEnView1 do
begin
SelectionBase := iesbBitmap;
Select( SelX1 - 10, SelY1 - 10, SelX2 - 10, SelY2 - 10 );
End;
<FM>See Also<FN>
- <A TImageEnView.SelX1>
- <A TImageEnView.SelY1>
- <A TImageEnView.SelX2>
- <A TImageEnView.Select>
- <A TImageEnView.XBmp2Scr>
- <A TImageEnView.YBmp2Scr>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.YScr2Bmp>
!!}
function TImageEnView.GetSelY2: integer;
begin
if fPolySelecting or fRectSelecting or fCircSelecting or (fRectResizing <> ieNone) or fRectMoving then
result := PIEAnimPoly(fHPolySel)^.ry2 - 1
else
result := fSelectionMask.Y2;
end;
{!!
<FS>TImageEnView.XBmp2Scr
<FM>Declaration<FC>
function XBmp2Scr(x: Integer; CurrentLayer: Boolean): Integer;
<FM>Description<FN>
The XBmp2Scr and <A TImageEnView.YBmp2Scr> methods convert a bitmap coordinate to the window coordinate (considering the Zoom and View status). x and y are bitmap coordinates.
These methods are useful to select a bitmap region independently from the <A TImageEnView.Zoom>, <A TImageEnView.ViewX> and <A TImageEnView.ViewY> properties.
<FC>x<FN> is a bitmap coordinate.
If <FC>CurrentLayer<FN> is true, then <FC>x<FN> is assumed to be a coordinate of the <L TImageEnView.CurrentLayer>current layer</L>. If false, y is a coordinate of the background layer (layer 0).
Note: To convert a coordinate of a non-current layer, you can use TImageEnView.Layers[].<A TIELayer.ConvXBmp2Scr>
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Other\PixelView\PixelView.dpr </C> </R>
</TABLE>
<FM>Example<FC>
// Select rect 10,10,100,100 of bitmap
ImageEnView1.Zoom(300);
x1 := XBmp2Scr(10);
y1 := YBmp2Scr(10);
x2 := XBmp2Scr(100);
y2 := YBmp2Scr(100);
ImageEnView1.Select( x1, y1, x2, y2);
// Select rect 10,10,100,100 of the window
ImageEnView1.Select(10, 10, 100, 100)
<FM>See Also<FN>
- <A TImageEnView.YBmp2Scr>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.YScr2Bmp>
!!}
function TImageEnView.XBmp2Scr(x: integer; CurrentLayer: Boolean): integer;
var
xx: integer;
begin
if CurrentLayer then
result := self.CurrentLayer.ConvXBmp2Scr(x)
else
begin
xx := x - fo1x;
if (xx < 0) or (xx >= fXBmp2ScrSize) then
// approximated value
result := round(fOffX + (x + round((-QuantizeViewX(fViewX)) * f100DZoomX)) * fZoomD100X)
else
result := fOffX + fXBmp2Scr[xx];
end;
end;
{!!
<FS>TImageEnView.YBmp2Scr
<FM>Declaration<FC>
function YBmp2Scr(y: Integer; CurrentLayer: Boolean): Integer;
<FM>Description<FN>
The YBmp2Scr and <A TImageEnView.XBmp2Scr> methods convert a bitmap coordinate to the window coordinate (considering the Zoom and View status). x and y are bitmap coordinates.
These methods are useful to select a bitmap region independently from the <A TImageEnView.Zoom>, <A TImageEnView.ViewX> and <A TImageEnView.ViewY> properties.
<FC>y<FN> is a bitmap coordinate.
If <FC>CurrentLayer<FN> is true, then <FC>y<FN> is assumed to be a coordinate of the <L TImageEnView.CurrentLayer>current layer</L>. If false, y is a coordinate of the background layer (layer 0).
Note: To convert a coordinate of a non-current layer, you can use TImageEnView.Layers[].<A TIELayer.ConvYBmp2Scr>
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Other\PixelView\PixelView.dpr </C> </R>
</TABLE>
<FM>Example<FC>
// Select rect 10, 10, 100, 100 of bitmap
ImageEnView1.Zoom(300);
x1 := XBmp2Scr(10);
y1 := YBmp2Scr(10);
x2 := XBmp2Scr(100);
y2 := YBmp2Scr(100);
ImageEnView1.Select( x1, y1, x2, y2);
// Select rect 10,10,100,100 of the window
ImageEnView1.Select(10, 10, 100, 100)
<FM>See Also<FN>
- <A TImageEnView.XBmp2Scr>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.YScr2Bmp>
!!}
function TImageEnView.YBmp2Scr(y: integer; CurrentLayer: Boolean): integer;
var
yy: integer;
begin
if CurrentLayer then
result := self.CurrentLayer.ConvYBmp2Scr(y)
else
begin
yy := y - fo1y;
if (yy < 0) or (yy >= fYBmp2ScrSize) then
begin
// approximated value
result := round(fOffY + (y + round((-QuantizeViewY(fViewY)) * f100DZoomY)) * fZoomD100Y);
end
else
result := fOffY + fYBmp2Scr[yy];
end;
end;
{!!
<FS>TImageEnView.XScr2Bmp
<FM>Declaration<FC>
function XScr2Bmp(x: integer; CurrentLayer: Boolean): integer;
<FM>Description<FN>
The XScr2Bmp and <A TImageEnView.YScr2Bmp> methods convert a window coordinate to the corresponding bitmap coordinate (considering <A TImageEnView.Zoom> and <A TImageEnView.ViewX>, <A TImageEnView.ViewY> status).
<FC>x<FN> is a window coordinate.
If <FC>CurrentLayer<FN> is true, then the result will be a coordinate relative to the <L TImageEnView.CurrentLayer>current layer</L>. If false, it will be a coordinate relative to the background layer (layer 0).
Notes:
- The result may be negative or greater than the bitmap width if <FC>x<FN> is beyond the displayed image's boundaries
- To convert a coordinate of a non-current layer, use TImageEnView.Layers[].<A TIELayer.ConvXScr2Bmp>
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Other\PixelView\PixelView.dpr </C> </R>
</TABLE>
<FM>Example<FC>
// X and Y are MOUSE coordinates
bx := ImageEnView1.XScr2Bmp( X, False );
by := ImageEnView1.YScr2Bmp( Y, False );
// now bx and by are Bitmap coordinates (of ImageEnView1.Bitmap)
<FM>See Also<FN>
- <A TImageEnView.YScr2Bmp>
- <A TImageEnView.XBmp2Scr>
- <A TImageEnView.YBmp2Scr>
- <A TImageEnView.VisibleBitmapRect>
!!}
function TImageEnView.XScr2Bmp(x: integer; CurrentLayer: Boolean): integer;
var
xx: integer;
begin
if CurrentLayer then
result := self.CurrentLayer.ConvXScr2Bmp(x)
else
begin
xx := x - fOffX;
if (xx < 0) or (xx >= fXScr2BmpSize) then
// approximated value
result := trunc((x - fOffX) * f100DZoomX + fo1x)
else
result := fXScr2Bmp[xx];
end;
end;
{!!
<FS>TImageEnView.YScr2Bmp
<FM>Declaration<FC>
function YScr2Bmp(y: integer; CurrentLayer: Boolean): integer;
<FM>Description<FN>
The XScr2Bmp and <A TImageEnView.YScr2Bmp> methods convert a window coordinate to the corresponding bitmap coordinate (considering <A TImageEnView.Zoom> and <A TImageEnView.ViewX>, <A TImageEnView.ViewY> status).
<FC>y<FN> is a window coordinate.
If <FC>CurrentLayer<FN> is true, then the result will be a coordinate relative to the <L TImageEnView.CurrentLayer>current layer</L>. If false, it will be a coordinate relative to the background layer (layer 0).
Notes:
- The result may be negative or greater than the bitmap height if <FC>y<FN> is beyond the displayed image's boundaries
- To convert a coordinate of a non-current layer, use TImageEnView.Layers[].<A TIELayer.ConvYScr2Bmp>
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Other\PixelView\PixelView.dpr </C> </R>
</TABLE>
<FM>Example<FC>
// X and Y are MOUSE coordinates
bx := ImageEnView1.XScr2Bmp( X, False );
by := ImageEnView1.YScr2Bmp( Y, False );
// now bx and by are Bitmap coordinates (of ImageEnView1.Bitmap)
<FM>See Also<FN>
- <A TImageEnView.XScr2Bmp>
- <A TImageEnView.XBmp2Scr>
- <A TImageEnView.YBmp2Scr>
- <A TImageEnView.VisibleBitmapRect>
!!}
function TImageEnView.YScr2Bmp(y: integer; CurrentLayer: Boolean): integer;
var
yy: integer;
begin
if CurrentLayer then
result := self.CurrentLayer.ConvYScr2Bmp(y)
else
begin
yy := y - fOffY;
if (yy < 0) or (yy >= fYScr2BmpSize) then
// approximated value
result := trunc((y - fOffY) * f100DZoomY + fo1y)
else
result := fYScr2Bmp[yy];
end;
end;
{!!
<FS>TImageEnView.GetIdealZoom
<FM>Declaration<FC>
function TImageEnView.GetIdealZoom: Double;
<FM>Description<FN>
Returns the best zoom value to stretch the image to fit within the component.
Note: If the image is empty, result will be zero.
<FM>Example<FC>
// Both the following code snippets will have the same result
ImageEnView.Zoom := ImageEnView.GetIdealZoom;
// or
ImageEnView.Fit;
<FM>See Also<FN>
- <L TImageEnView.LayerOptions>loFitToLayersWhenZooming</L>
!!}
function TImageEnView.GetIdealZoom: double;
begin
result := 0;
if ( MaxLayerWidth() <> 0 ) and ( MaxLayerHeight() <> 0 ) then
Result := dmin(Width / ( MaxLayerWidth() / 100 ), Height / ( MaxLayerHeight() / 100 ));
end;
procedure TImageEnView.CalcEdges(var LeftEdge, TopEdge, RightEdge, BottomEdge: integer; NoVertSB, NoHorizSB: boolean);
var
edgeX, edgeY: Integer;
begin
case fBackgroundStyle of
iebsCropped:
begin
edgeX := IEGlobalSettings().edgeX;
edgeY := IEGlobalSettings().edgeY;
end;
iebsCropShadow:
begin
edgeX := 5;
edgeY := 5;
end;
iebsSoftShadow:
begin
edgeX := 9;
edgeY := 9;
end;
else
begin
if NoVertSB then
begin
if BorderStyle=bsNone then
edgeX := 0
else
if Ctl3D then
edgeX := IEGlobalSettings().edgeX
else
edgeX := IEGlobalSettings().BorderX;
end
else
edgeX := (Width - GetClientWidth) div 2;
if NoHorizSB then
begin
if BorderStyle=bsNone then
edgeY := 0
else
if Ctl3D then
edgeY := IEGlobalSettings().edgeY
else
edgeY := IEGlobalSettings().BorderY;
end
else
edgeY := (Height - GetClientHeight) div 2;
end;
end;
LeftEdge := edgeX + fRulerParams.RulerAreaLeft;
TopEdge := edgeY + fRulerParams.RulerAreaTop;
RightEdge := edgeX + fRulerParams.RulerAreaRight;
BottomEdge := edgeY + fRulerParams.RulerAreaBottom;
end;
procedure TImageEnView.GetFitValueXY(var x, y: double);
var
edgeLeft, edgeTop, edgeRight, edgeBottom: integer;
begin
CalcEdges( edgeLeft, edgeTop, edgeRight, edgeBottom, true, true );
x := ( Width - edgeLeft - edgeRight ) / ( MaxLayerWidth() / 100 );
y := ( Height - edgeTop - edgeBottom ) / ( MaxLayerHeight() / 100 );
end;
function TImageEnView.GetFitValue(): double;
var
x, y: double;
begin
GetFitValueXY( x, y);
Result := dmin(x, y);
end;
{!!
<FS>TImageEnView.Fit
<FM>Declaration<FC>
procedure Fit(StretchSmall : Boolean = True);
<FM>Description<FN>
This method adjusts <A TImageEnView.Zoom> so that the image fits within the client area of the component (while respecting the aspect ratio).
If <FC>StretchSmall<FN> is false, then images that are smaller than the window are shown 1:1 (i.e. are not zoomed more than 100%).
<FM>Example<FC>
// Display the entire image (including outlying layers) within the view
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loFitToLayersWhenZooming ];
ImageEnView1.Fit( False );
<FM>See Also<FN>
- <A TImageEnView.FitToHeight>
- <A TImageEnView.FitToWidth>
- <L TImageEnView.LayerOptions>loFitToLayersWhenZooming</L>
- <A TImageEnView.Stretch>
!!}
procedure TImageEnView.Fit(StretchSmall : Boolean = True);
var
dZoom : double;
begin
if ( MaxLayerWidth() > 1 ) and ( MaxLayerHeight() > 1 ) then
begin
HideHorizScrollBar;
HideVertScrollBar;
dZoom := GetFitValue();
if ( StretchSmall = False ) and ( dZoom > 100 ) then
dZoom := 100;
Zoom := dZoom;
end;
end;
{!!
<FS>TImageEnView.Stretch
<FM>Declaration<FC>
procedure Stretch;
<FM>Description<FN>
This method sets <A TImageEnView.ZoomX> and <A TImageEnView.ZoomY> values to stretch the image to fit within the component borders.
Note: Using this method you will lose the image aspect ratio and some functions, which require aspect ratio, may not work properly.
<FM>See Also<FN>
- <A TImageEnView.Fit>
- <A TImageEnView.FitToWidth>
- <A TImageEnView.FitToHeight>
- <L TImageEnView.LayerOptions>loFitToLayersWhenZooming</L>
!!}
procedure TImageEnView.Stretch;
var
x, y: Double;
begin
if ( MaxLayerWidth() > 1 ) and ( MaxLayerHeight() > 1 ) then
begin
HideHorizScrollBar;
HideVertScrollBar;
GetFitValueXY(x, y);
LockPaint;
ZoomX := x;
ZoomY := y;
UnlockPaint;
HideHorizScrollBar;
HideVertScrollBar;
end;
end;
{!!
<FS>TImageEnView.FitToWidth
<FM>Declaration<FC>
procedure FitToWidth;
<FM>Description<FN>
FitToWidth adjust the image display size (<A TImageEnView.Zoom>) so that it fits within the width of the control window.
<FM>See Also<FN>
- <A TImageEnView.Fit>
- <A TImageEnView.FitToHeight>
- <L TImageEnView.LayerOptions>loFitToLayersWhenZooming</L>
- <A TImageEnView.Stretch>
!!}
procedure TImageEnView.FitToWidth;
var
edgeLeft, edgeTop, edgeRight, edgeBottom: integer;
begin
CalcEdges( edgeLeft, edgeTop, edgeRight, edgeBottom, false, true );
if ( MaxLayerWidth() > 1 ) and ( MaxLayerHeight() > 1 ) then
begin
LockPaint;
Zoom := ( Width - edgeLeft - edgeRight ) / ( MaxLayerWidth() / 100 );
Zoom := ( GetClientWidthExRulers ) / ( MaxLayerWidth() / 100 ); // second time needed for scrollbars
UnLockPaint;
end;
end;
{!!
<FS>TImageEnView.FitToHeight
<FM>Declaration<FC>
procedure FitToHeight;
<FM>Description<FN>
FitToHeight adjust the image display size (<A TImageEnView.Zoom>) so that it fits within the height of the control window.
<FM>See Also<FN>
- <A TImageEnView.Fit>
- <A TImageEnView.FitToWidth>
- <L TImageEnView.LayerOptions>loFitToLayersWhenZooming</L>
- <A TImageEnView.Stretch>
!!}
procedure TImageEnView.FitToHeight;
var
edgeLeft, edgeTop, edgeRight, edgeBottom: integer;
begin
CalcEdges( edgeLeft, edgeTop, edgeRight, edgeBottom, true, false );
if ( MaxLayerWidth() > 1 ) and ( MaxLayerHeight() > 1 ) then
begin
LockPaint;
Zoom := ( Height - edgeTop - edgeBottom ) / ( MaxLayerHeight() / 100 );
Zoom := ( GetClientHeightExRulers ) / ( MaxLayerHeight() / 100 ); // second time needed for scrollbars
UnLockPaint;
end;
end;
function TImageEnView.GetOptimalZoom(CanStretch, CanShrink: Boolean): double;
var
bitmapW, bitmapH, ieviewW, ieviewH: integer;
edgeLeft, edgeTop, edgeRight, edgeBottom: integer;
begin
result := 100;
CalcEdges( edgeLeft, edgeTop, edgeRight, edgeBottom, False, False );
bitmapW := MaxLayerWidth();
bitmapH := MaxLayerHeight();
ieviewW := GetClientWidth - edgeLeft - edgeRight;
ieviewH := GetClientHeight - edgeTop - edgeBottom;
if ( ieviewW < 1 ) or ( ieviewH < 1 ) or ( bitmapW < 2 ) or ( bitmapH < 2 ) then
exit;
try
if bitmapH / ieviewH < bitmapW / ieviewW then
begin // width is important
if bitmapW > ieviewW then
begin
if CanShrink = False then
result := 100
else
result := GetFitValue();
end
else
begin
if CanStretch = False then
result := 100
else
result := GetFitValue();
end;
end
else
begin // height is important
if bitmapH > ieviewH then
begin
if CanShrink = False then
result := 100
else
result := GetFitValue();
end
else
begin
if CanStretch = False then
result := 100
else
result := GetFitValue();
end;
end; // if wider than high
except
end;
end;
// makes a zoom inside the selected area
{!!
<FS>TImageEnView.ZoomSelection
<FM>Declaration<FC>
procedure ZoomSelection(AspectRatio: Boolean);
<FM>Description<FN>
The ZoomSelection method zooms to a rectangular area specified with Select method.
If AspectRatio is true then the zoom respects the aspect ratio of the image, otherwise the image area is stretched to match the size of the component.
<FM>Example<FC>
ImageEnView1.Select(10, 10, 100, 100);
ImageEnView1.ZoomSelection; // fill client area (if possible) with rectangle 10,10,100,100
!!}
procedure TImageEnView.ZoomSelection(AspectRatio: Boolean);
begin
ZoomSelectionEx(AspectRatio, false);
end;
procedure TImageEnView.ZoomSelectionEx(AspectRatio: Boolean; FireDoZoomIn: Boolean);
var
dx, dy: integer;
x1, y1: integer;
cx, cy: integer;
zx, zy: double;
zm: Double;
begin
if fSel then
begin
dx := Selx2 - Selx1 + 1;
dy := Sely2 - Sely1 + 1;
x1 := SelX1;
y1 := SelY1;
if (x1 < 0) or (y1 < 0) or (dx < 2) or (dy < 2) then
exit;
Deselect;
zm := dmin(Width / (dx / 100), Height / (dy / 100)); // zoom with aspect ratio
if FireDoZoomIn then
DoZoomIn(zm);
if AspectRatio then
begin
Zoom := zm;
end
else
begin
ZoomX := Width / (dx / 100);
ZoomY := Height / (dy / 100);
end;
zx := ZoomX / 100;
zy := ZoomY / 100;
x1 := trunc(x1 * zx);
dx := trunc(dx * zx);
y1 := trunc(y1 * zy);
dy := trunc(dy * zy);
cy := (GetClientHeightExRulers - dy) div 2;
cx := (GetClientWidthExRulers - dx) div 2;
SetViewXY(x1 - cx, y1 - cy);
end;
end;
// called by "initialization"
procedure InitImageEnView;
begin
with IEGlobalSettings().GridPen do
begin
Mode := pmNot;
Style := psSolid;
Width := 1;
end;
end;
{!!
<FS>TImageEnView.LockPaint
<FM>Declaration<FC>
procedure LockPaint;
<FM>Description<FN>
The LockPaint method increases the lock counter's value. The ImageEnView window will not be repainted while <A TImageEnView.LockPaintCount> > 0.
See also: <A TImageEnView.UnlockPaint>.
<FM>Example<FC>
ImageEnView1.LockPaint;
ImageEnView1.ViewX := 10;
ImageEnView1.ViewY := 20;
ImageEnView1.Zoom := 300;
ImageEnView1.UnLockPaint;
!!}
// Inc fLockPaint counter
procedure TImageEnView.LockPaint;
begin
inc(fLockPaint);
end;
{!!
<FS>TImageEnView.UnlockPaint
<FM>Declaration<FC>
function UnlockPaint: integer;
<FM>Description<FN>
Use the UnlockPaint method to decrease the lock counter's value. The ImageEnView window will not be repainted while <A TImageEnView.LockPaintCount> > 0.
If calling UnlockPaint resets <A TImageEnView.LockPaintCount> to zero then the <A TImageEnView.Update> method is automatically called.
Returns the lock count.
See also: <A TImageEnView.LockPaint>
<FM>Example<FC>
ImageEnView1.LockPaint;
ImageEnView1.ViewX := 10;
ImageEnView1.ViewY := 20;
ImageEnView1.Zoom := 300;
ImageEnView1.UnLockPaint;
!!}
// Dec fLockPaint counter
// return current fLockPaint value (after Dec)
// Call Update if fLockPaint = 0
function TImageEnView.UnLockPaint: integer;
begin
if fLockPaint > 0 then
dec(fLockPaint);
if fLockPaint = 0 then
Update;
result := fLockPaint;
end;
// Dec fLockPaint counter
// return current fLockPaint value (after Dec)
function TImageEnView.NPUnLockPaint: integer;
begin
if fLockPaint > 0 then
dec(fLockPaint);
result := fLockPaint;
end;
{!!
<FS>TImageEnView.ZoomAt
<FM>Declaration<FC>
procedure ZoomAt(x, y: integer; ZoomVal: double; Center: Boolean=true);
<FM>Description<FN>
Zooms by the percentage specified by ZoomVal.
If the optional <FC>Center<FN> parameter if False, the zooming center is <FC>x, y<FN>, otherwise (the default) it is the center of image.
!!}
// zoom to ZoomVal at x,y coordinates (client area coordinates)
procedure TImageEnView.ZoomAt(x, y: integer; ZoomVal: double; Center: Boolean);
var
zz: double;
bx, by: Integer;
w2, h2: Integer;
begin
if assigned(fNavigator) then
fNavigator.LockPaint;
ViewChanging(2, x);
ViewChanging(2, y);
LockPaint;
bx := XScr2Bmp( x, False );
by := YScr2Bmp( y, False );
Zoom := ZoomVal; // fire ViewChange(1) event
zz := Zoom / 100;
w2 := GetClientWidthExRulers div 2;
h2 := GetClientHeightExRulers div 2;
if Center then
SetViewXY(trunc(bx * zz - w2), trunc(by * zz - h2)) // fire ViewChange(0) event
else
SetViewXY(trunc(bx*zz - x +Zoom/100), trunc(by*zz - y+Zoom/100)); // fire ViewChange(0) event
UnLockPaint;
CalcPaintCoords;
CreateCoordConvLUT; // recalculates coordinate conversion LUT
if assigned(fNavigator) then
begin
fNavigator.NPUnLockPaint;
SetNavigatorRect; // 3.0.4 (27/07/2009 14:18)
end;
end;
{!!
<FS>TImageEnView.ZoomIn
<FM>Declaration<FC>
procedure ZoomIn;
<FM>Description<FN>
Provides a convenient method to handle clicking of a "Zoom In" button. Each time <FC>ZoomIn<FN> is called it increases the zoom by a logical step, e.g. 50 to 75, 75 to 100, etc.
It also includes "Fit to Window" as one of the steps.
Note: <FC>ZoomIn<FN> will have no effect if <A TImageEnView.AutoFit>, <A TImageEnView.AutoShrink> or <A TImageEnView.AutoStretch> are enabled.
<FM>See Also<FN>
- <A TImageEnView.Zoom>
- <A TImageEnView.ZoomOut>
!!}
procedure TImageEnView.ZoomIn;
begin
Zoom := GetNextZoomValue(Zoom, True, GetIdealZoom);
end;
{!!
<FS>TImageEnView.ZoomOut
<FM>Declaration<FC>
procedure ZoomOut;
<FM>Description<FN>
Provides as a convenient method to handle clicking of a "Zoom Out" button. Each time <FC>ZoomOut<FN> is called it decreases the zoom by a logical step, e.g. 100 to 75, 75 to 50, etc.
It also includes "Fit to Window" as one of the steps.
Note: <FC>ZoomOut<FN> will have no effect if <A TImageEnView.AutoFit>, <A TImageEnView.AutoShrink> or <A TImageEnView.AutoStretch> are enabled.
<FM>See Also<FN>
- <A TImageEnView.Zoom>
- <A TImageEnView.ZoomIn>
!!}
procedure TImageEnView.ZoomOut;
begin
Zoom := GetNextZoomValue(Zoom, False, GetIdealZoom);
end;
{!!
<FS>TImageEnView.DrawTo
<FM>Declaration<FC>
procedure DrawTo(Canvas: TCanvas);
<FM>Description<FN>
This method draws the current view to the specified Canvas.
Note: The background is not painted, and the image has an up-left alignment.
<FM>Example<FC>
// draw in ImageEn2 all what is displayed in ImageEnView1
ImageEnView1.DrawTo(ImageEnView2.Bitmap.Canvas);
ImageEnView2.Update;
!!}
procedure TImageEnView.DrawTo(Canvas: TCanvas);
begin
Canvas.CopyRect(Rect(0, 0, frx, fry), fBackBuffer.Canvas, Rect(fOffX, fOffY, fOffX + frx, fOffY + fry));
end;
function TImageEnView.HasParentWindow: boolean;
begin
result := (GetParentForm(self) <> nil) or (ParentWindow <> 0);
end;
{!!
<FS>TImageEnView.UpdateNoPaint
<FM>Declaration<FC>
procedure UpdateNoPaint;
<FM>Description<FN>
The Update method refreshes the TImageEnView component view with the current state of the image.
You must call <A TImageEnView.Update> or UpdateNoPaint() whenever the <A TImageEnView.Bitmap> or <A TImageEnView.IEBitmap> property is modified by your code.
Unlike <A TImageEnView.Update>, UpdateNoPaint doesn't paint the image, just refreshes the bitmap data.
!!}
procedure TImageEnView.UpdateNoPaint;
begin
try
fBlockPaint := true;
Update;
finally
fBlockPaint := false;
end;
end;
function TImageEnView.RequiresScrollBars: boolean;
var
maxX, maxY: integer;
begin
GetMaxViewXY(maxX, maxY);
result := (maxX > 0) or (maxY > 0);
end;
procedure TImageEnView.HideHorizScrollBar;
begin
if fHScrollBarVisible then
begin
fHScrollBarVisible := false;
IEShowScrollBar(handle, SB_HORZ, false, fFlatScrollBars);
end;
end;
procedure TImageEnView.HideVertScrollBar;
begin
if fVScrollBarVisible then
begin
fVScrollBarVisible := false;
IEShowScrollBar(handle, SB_VERT, false, fFlatScrollBars);
end;
end;
// Update the fLiveBackground image which stores the background to draw when BackgroundStyle = iebsBlurredBitmap
procedure TImageEnView.UpdateLiveBackground(Src: TIEBitmap);
const
Opacity_Level = 0.75; // Background in 75% opaque
Blur_Radius = 8; // Background is blurred with a radius of 8
var
aRect: TRect;
DestRect : TIERectangle;
begin
fNeedUpdateLiveBackground := False;
try
if not assigned( fLiveBackground ) then
fLiveBackground := TIEBitmap.create;
fLiveBackground.Clear;
if ( Src = nil ) or ( Src.IsEmpty ) then
exit;
fLiveBackground.Allocate( ClientWidth, ClientHeight );
fLiveBackground.Fill( GetThemeColor( ietpControlBackground, fBackground ));
aRect := GetImageRectWithinArea( Src.Width, Src.Height, fLiveBackground.Width, fLiveBackground.Height,
0, 0, True, True, True, True, 0,
_fmFillRect_WithOverlap );
DestRect := IERectangle( aRect );
Src.RenderToTIEBitmapEx( fLiveBackground, DestRect.X, DestRect.Y, DestRect.Width, DestRect.Height,
0, 0, Src.Width, Src.Height,
True, 255, rfNone, ielNormal, Opacity_Level );
_IEGBlur(fLiveBackground, Blur_Radius, nil, nil);
except
if assigned( fLiveBackground ) then
fLiveBackground.Clear;
end;
end;
{!!
<FS>TImageEnView.Update
<FM>Declaration<FC>
procedure Update;
<FM>Description<FN>
The Update method refreshes the TImageEnView component view with the current state of the image and redraws the client area.
You must call <A TImageEnView.Update> or UpdateNoPaint() whenever the <A TImageEnView.Bitmap> or <A TImageEnView.IEBitmap> property is modified by your code.
Update is called automatically when an image processing or I/O operation is executed.
<FM>Example<FC>
// Draw a rectangle on the image and show it
ImageEnView1.Bitmap.Canvas.Rectangle(5, 5, 100, 100);
ImageEnView1.Update;
!!}
procedure TImageEnView.Update;
var
max_x, max_y: integer;
lClientWidth, lClientHeight: integer;
bb: boolean;
i: Integer;
newZoom: double;
begin
if fUpdateLocked <> 0 then
exit;
if (csDesigning in ComponentState) then
exit;
if (ComponentState <> []) and (ComponentState <> [csDesigning]) and (ComponentState <> [csFreeNotification]) then
exit;
{$IFDEF UNITTESTING}
inc( fDebugUpdatedCount );
{$ENDIF}
fInsideUpdate := true;
try
if fNeedUpdateLiveBackground and ( BackgroundStyle = iebsBlurredImage ) then
UpdateLiveBackground( Layers[0].Bitmap );
// empty layer mask caches
for i := 0 to fLayers.Count-1 do
if (fUpdate_MaskCache = -1) or (fUpdate_MaskCache=i) then
FreeAndNil( TIELayer(fLayers[i]).fCachedLayerMask );
fUpdate_MaskCache := -1;
// check layers sizes
SyncLayers();
if (fAutoStretch or fAutoShrink or fAutoFit) and ( MaxLayerWidth() <> 0 ) and ( MaxLayerHeight() <> 0 ) then
begin
ViewChanging(2, fZoomX);
NewZoom := GetOptimalZoom( fAutoStretch or fAutoFit, fAutoShrink or fAutoFit);
if NewZoom < 1 then
NewZoom := 1;
SetZoomNoUpdate(newZoom);
CalcPaintCoords;
CreateCoordConvLUT; // recalculates coordinate conversion LUT
ViewChange(1);
// to avoid flickering
fStable := 0;
fStable2 := 0;
end;
// check bitmap size changes
if fIEBitmapValid and ((fBitmapWidth <> fIEBitmap.Width) or (fBitmapHeight <> fIEBitmap.Height)) then
begin
fBitmapWidth := fIEBitmap.Width;
fBitmapHeight := fIEBitmap.Height;
if (fSelectionMask.Width <> fIEBitmap.Width) or (fSelectionMask.Height <> fIEBitmap.Height) then
begin
if fSel then
begin
fSelectionMask.Resize(fIEBitmap.Width, fIEBitmap.Height);
fSelectionMask.SyncFull;
fSelectionMask.SyncRect;
end
else
fSelectionMask.AllocateBits(fIEBitmap.Width, fIEBitmap.Height, fSelectionMaskDepth);
end;
if fIEBitmap.HasAlphaChannel and ((fIEBitmap.AlphaChannel.Width <> fIEBitmap.Width) or (fIEBitmap.AlphaChannel.Height <> fIEBitmap.Height)) then
begin
fIEBitmap.AlphaChannel.Allocate(fIEBitmap.Width, fIEBitmap.Height, ie8g);
fIEBitmap.AlphaChannel.Fill(255);
end;
end;
if not HandleAllocated then
exit;
if not (csDesigning in ComponentState) and (HasParentWindow) then
begin
// Save client sizes because the scrollbar changes the client area, then we must recall Update (recursive call)
lClientWidth := GetClientWidth;
lClientHeight := GetClientHeight;
//
bb := false;
GetMaxViewXY(max_x, max_y);
if fViewX > max_x then
begin
ViewChanging(0, max_x);
fViewX := max_x;
bb := true;
end;
if fViewY > max_y then
begin
ViewChanging(1, max_y);
fViewY := max_y;
bb := true;
end;
if bb then
ViewChange(0);
try
if HasParentWindow and ((fScrollBars = ssHorizontal) or (fScrollBars = ssBoth)) then
begin
if max_x > 0 then
begin
// we need horizontal scroll bar
fHScrollBarVisible := true;
fRXScroll := (max_x + GetClientWidthExRulers) / 65536;
IESetScrollBar(Handle, SB_HORZ, 0, 65535, trunc(GetClientWidthExRulers / fRXScroll), trunc(fViewX / fRXScroll), true, fFlatScrollBars);
IEEnableScrollBar(Handle, SB_HORZ, ESB_ENABLE_BOTH, fFlatScrollBars);
IEShowScrollBar(handle, SB_HORZ, true, fFlatScrollBars);
end
else
if fScrollBarsAlwaysVisible then
begin
// the scroll bar is always visible
fHScrollBarVisible := true;
IEEnableScrollBar(Handle, SB_HORZ, ESB_DISABLE_BOTH, fFlatScrollBars);
IEShowScrollBar(handle, SB_HORZ, true, fFlatScrollBars);
end
else
begin
// we don't need the horizontal scroll bar
HideHorizScrollBar;
end;
end;
if HasParentWindow and ((fScrollBars = ssVertical) or (fScrollBars = ssBoth)) then
begin
if max_y > 0 then
begin
// we need vertical scroll bar
fVScrollBarVisible := true;
fRYScroll := (max_y + GetClientHeightExRulers) / 65536;
IESetScrollBar(Handle, SB_VERT, 0, 65535, trunc(GetClientHeightExRulers / fRYScroll), trunc(fViewY / fRYScroll), true, fFlatScrollBars);
IEEnableScrollBar(Handle, SB_VERT, ESB_ENABLE_BOTH, fFlatScrollBars);
IEShowScrollBar(handle, SB_VERT, true, fFlatScrollBars);
end
else
if fScrollBarsAlwaysVisible then
begin
// the scroll bar is always visible
fVScrollBarVisible := true;
IEEnableScrollBar(Handle, SB_VERT, ESB_DISABLE_BOTH, fFlatScrollBars);
IEShowScrollBar(handle, SB_VERT, true, fFlatScrollBars);
end
else
begin
// we don't need the vertical scroll bar
HideVertScrollBar;
end;
end;
except
end;
if ((lClientWidth <> GetClientWidth) or (lClientHeight <> GetClientHeight)) and not fUpdateInsideUpdate then
begin
fUpdateInsideUpdate := true;
Update; // recursive, see above
fUpdateInsideUpdate := false;
end;
end;
UpdateLimits;
// Update Rulers
fRulerParams.Update( False );
UpdateTextEditor();
fUpdateBackBuffer := true;
fFullUpdateRequest := true;
if HasParentWindow and (fBlockPaint = false) and fUpdateInvalidate then
invalidate;
if assigned(fNavigator) and (fNavigator.fLockPaint = 0)then
begin
if fUpdateReason = ieurDefault then
fNavigator.Update;
SetNavigatorRect;
end;
finally
fUpdateReason := ieurDefault;
fInsideUpdate := false;
end;
end;
procedure TImageEnView.UpdateLimits;
var
cw, ch: Integer;
begin
cw := GetClientWidthExRulers;
ch := GetClientHeightExRulers;
fZZWW := round(fLayersRect.width * fZoomD100X); // new Bitmap width
fZZHH := round(fLayersRect.height * fZoomD100Y); // new Bitmap height
fOffX := 0;
fOffY := 0;
if not (csDesigning in ComponentState) then
begin
fExtX := imin( fZZWW, cw ); // width of target bitmap
fExtY := imin( fZZHH, ch ); // height of target bitmap
case fImageHorizAlignment of
//iehLeft:
iehCenter: if fExtX < cw then
fOffX := ( cw - fExtX ) div 2 - fLayersRect.X
else
fOffX := - fLayersRect.X;
iehRight: if fExtx < cw then
fOffX := imin( cw - fZZWW, cw - fRulerParams.RulerAreaRight);
end;
case fImageVertAlignment of
//ievTop:
ievCenter: if fExty < ch then
fOffY := ( ch - fExtY ) div 2 - fLayersRect.Y
else
fOffY := - fLayersRect.Y;
ievBottom: if fExty < ch then
fOffY := imin( ch - fZZHH, ch - fRulerParams.RulerAreaBottom );
end;
end
else
begin
fExtX := imin(fZZWW, Width);
fExtY := imin(fZZHH, Height);
end;
inc( fOffX, fRulerParams.RulerAreaLeft );
inc( fOffY, fRulerParams.RulerAreaTop );
end;
{!!
<FS>TImageEnView.Center
<FM>Declaration<FC>
property Center: boolean;
<FM>Description<FN>
If Center is True, the image is displayed in the center of the ImageEnViews client area.
Default: True, which is equivalent to <A TImageEnView.ImageHorizAlignment> = iehCenter and <A TImageEnView.ImageVertAlignment> = ievCenter.
<FM>See Also<FN>
- <A TImageEnView.ImageHorizAlignment>
- <A TImageEnView.ImageVertAlignment>
!!}
procedure TImageEnView.SetCenter(value: boolean);
begin
if value then
begin
ImageHorizAlignment := iehCenter;
ImageVertAlignment := ievCenter;
end
else
begin
ImageHorizAlignment := iehLeft;
ImageVertAlignment := ievTop;
end;
end;
function TImageEnView.GetCenter(): Boolean;
begin
result := (ImageHorizAlignment = iehCenter) and (ImageVertAlignment = ievCenter);
end;
{!!
<FS>TImageEnView.ImageHorizAlignment
<FM>Declaration<FC>
property ImageHorizAlignment: <A TIEHAlign>;
<FM>Description<FN>
Specifies where to align horizontally the image inside the component.
Default: iehCenter
<FM>See Also<FN>
- <A TImageEnView.ImageVertAlignment>
- <A TImageEnView.Center>
!!}
procedure TImageEnView.SetImageHorizAlignment(value: TIEHAlign);
begin
if value <> fImageHorizAlignment then
begin
fImageHorizAlignment := value;
UpdateReason := ieurScrolled;
Update();
end;
end;
{!!
<FS>TImageEnView.ImageVertAlignment
<FM>Declaration<FC>
property ImageVertAlignment: <A TIEVAlign>;
<FM>Description<FN>
Specifies where to align vertically the image inside the component.
Default: ievCenter
<FM>See Also<FN>
- <A TImageEnView.ImageHorizAlignment>
- <A TImageEnView.Center>
!!}
procedure TImageEnView.SetImageVertAlignment(value: TIEVAlign);
begin
if value <> fImageVertAlignment then
begin
fImageVertAlignment := value;
UpdateReason := ieurScrolled;
Update();
end;
end;
procedure TImageEnView.SetMouseWheelParams(v: TIEMouseWheelParams);
begin
fMouseWheelParams.Assign( v );
end;
procedure TImageEnView.SetMouseInteract(v: TIEMouseInteract);
var
exclusiveSelection : Boolean;
layerSelection: Boolean;
layerKind: TIELayerKind;
procedure _MakeSelExclusive(value: TIEMouseInteractItems);
var
createLayerOp: Boolean;
begin
if not ( value in v ) then
exit;
createLayerOp := value in [ miCreateImageLayers, miCreateShapeLayers,
miCreateLineLayers, miCreatePolylineLayers,
miCreateTextLayers ];
if not (value in fMouseInteract) then
begin
v := v - [miScroll, miSelect, miSelectPolygon, miSelectCircle, miSelectZoom, miSelectMagicWand, miSelectLasso,
miCropTool, miRotateLayers, miCropTool,
miCreateImageLayers, miCreateShapeLayers,
miCreateLineLayers, miCreatePolylineLayers,
miCreateTextLayers ];
if not createLayerOp then
v := v - [ miRotateLayers, miMoveLayers, miResizeLayers ]
else
if miRotateLayers in v then
v := v - [ miMoveLayers, miResizeLayers ]
else
v := v - [ miRotateLayers ];
v := v + [value];
exclusiveSelection := True;
end;
if createLayerOp then
begin
layerSelection := True;
case value of
miCreateImageLayers : layerKind := ielkImage;
miCreateShapeLayers : layerKind := ielkShape;
miCreateLineLayers : layerKind := ielkLine;
miCreatePolylineLayers : layerKind := ielkPolyline;
miCreateTextLayers : layerKind := ielkText;
end;
end;
end;
begin
if v <> fMouseInteract then
begin
if fPolySelecting and (miSelectPolygon in fMouseInteract) then
begin
AnimPolygonRemoveLastPoint(fHPolySel);
fSelectionMask.Empty;
EndSelect;
fPolySelecting := False;
invalidate;
end;
RestoreCursor();
// Items that need exclusive dragging over image
exclusiveSelection := False;
layerSelection := False;
_MakeSelExclusive( miScroll );
_MakeSelExclusive( miSelect );
_MakeSelExclusive( miSelectPolygon );
_MakeSelExclusive( miSelectCircle );
_MakeSelExclusive( miSelectLasso );
_MakeSelExclusive( miSelectZoom );
_MakeSelExclusive( miCreateImageLayers );
_MakeSelExclusive( miCreateShapeLayers );
_MakeSelExclusive( miCreateLineLayers );
_MakeSelExclusive( miCreatePolylineLayers );
_MakeSelExclusive( miCreateTextLayers );
if exclusiveSelection then
begin { // } end
else
if (miZoom in v) and not (miZoom in fMouseInteract) then // miZoom
v := v - [miSelectPolygon, miSelectMagicWand]
else
if (miSelectMagicWand in v) and not (miSelectMagicWand in fMouseInteract) then // miSelectMagicWand
v := v - [miSelect, miScroll, miSelectCircle, miSelectZoom, miZoom, miSelectPolygon, miSelectLasso, miMoveLayers, miResizeLayers, miRotateLayers, miCropTool]
else
if (miRotateLayers in v) then // miRotateLayers
v := v - [miSelect, miScroll, miSelectCircle, miSelectZoom, miSelectPolygon, miSelectLasso, miSelectMagicWand, miResizeLayers, miCropTool]
else
if (miMoveLayers in v) then // miMoveLayers
begin
v := v - [miSelect, miScroll, miSelectCircle, miSelectZoom, miSelectPolygon, miSelectLasso, miSelectMagicWand, miCropTool];
if miResizeLayers in v then
v := v-[miRotateLayers];
end
else
if (miResizeLayers in v) then // miResizeLayers
v := v - [miSelect, miScroll, miSelectCircle, miSelectZoom, miZoom, miSelectPolygon, miSelectLasso, miSelectMagicWand, miRotateLayers, miCropTool]
else
if (miCropTool in v) then // miCropTool
begin
v := v - [miSelectPolygon, miSelectCircle, miScroll, miSelectZoom, miSelectMagicWand, miSelectLasso, miMoveLayers, miResizeLayers, miRotateLayers];
end;
(fUserInteractions[fUserInteractions.FindInstanceOf(TIECropToolInteraction)] as TIECropToolInteraction).Enabled := miCropTool in v;
// Layer Creation
(fUserInteractions[fUserInteractions.FindInstanceOf(TIECreateLayerInteraction)] as TIECreateLayerInteraction).Enabled := layerSelection;
(fUserInteractions[fUserInteractions.FindInstanceOf(TIECreateLayerInteraction)] as TIECreateLayerInteraction).LayerKind := layerKind;
if miRotateLayers in fMouseInteract then
begin
// We were rotating...
if not (miRotateLayers in v) then
begin
if loAutoFixRotation in fLayerOptions then
LayersFixRotations // exiting from rotation mode? Anyway fix rotations.
end
else
if loAutoFixBorders in fLayerOptions then
LayersFixBorders; // entering rotation mode, fix borders
end;
fMouseInteract := v;
Update();
end;
end;
// track mouse move for polygonal selection
procedure TImageEnView.PolyDraw1;
var
x, y: integer;
begin
if fPolySelecting and (not fLassoSelecting) then
begin
canvas.pen.mode := pmNot;
canvas.pen.width := 1;
canvas.pen.style := psSolid;
with PIEAnimPoly(fHPolySel)^ do
if PolyCount > 0 then
begin
canvas.moveto(fMMoveX, fMMoveY);
x := XBmp2Scr( Poly^[PolyCount - 1].x, True );
y := YBmp2Scr( Poly^[PolyCount - 1].y, True );
canvas.lineto(x, y);
end;
end;
end;
procedure TImageEnView.DrawMarkOutNavigator(const rc: TRect);
var
dx, dy: integer;
begin
if not assigned(fNavigatorBackBuffer) then
begin
fNavigatorBackBuffer := TIEBitmap.Create(GetClientWidth, GetClientHeight, ie24RGB);
fNavigatorBackBuffer.Location := ieTBitmap;
end;
if (fNavigatorBackBuffer.Width <> GetClientWidth) or (fNavigatorBackBuffer.Height <> GetClientHeight) then
fNavigatorBackBuffer.Resize(GetClientWidth, GetClientHeight);
with rc do
begin
dx := imin(right - left, GetClientWidth);
dy := imin(bottom - top, GetClientHeight);
BitBlt(fNavigatorBackBuffer.Canvas.Handle, left, top, dx, dy, fBackBuffer.Canvas.Handle, left, top, SRCCOPY);
with CurrentLayer.DrawingInfo do
IEDrawGrayedOut(fNavigatorBackBuffer.Canvas, XDst, YDst, WidthDst, HeightDst,
fNavigatorActualRect.Left, fNavigatorActualRect.Top,
fNavigatorActualRect.Right, fNavigatorActualRect.Bottom);
BitBlt(Canvas.Handle, left, top, dx, dy, fNavigatorBackBuffer.Canvas.Handle, left, top, SRCCOPY);
end;
end;
{!!
<FS>TImageEnView.PaintRect
<FM>Declaration<FC>
procedure PaintRect(const rc: TRect);
<FM>Description<FN>
PaintRect repaints only the rectangle <FC>rc<FN> without waiting for the Windows Paint message.
<FM>Example<FC>
// this code paints an image (width=50 height=50) at bitmap coordinates 10,10
ImageEnView1.Bitmap.Canvas.Draw(10, 10, MyBitmap);
// map the bitmap coordinates to view coordinates (rc is a TRect type)
With ImageEnView1 do
rc := rect( XBmp2Scr(10), YBmp2Scr(10), XBmp2Scr(10+50), YBmp2Scr(10+50) );
ImageEnView1.UpdateRect(rc); // update internal ImageEn state
ImageEnView1.PaintRect(rc); // paint the changes on the screen
!!}
procedure TImageEnView.PaintRect(const rc: TRect);
var
dx, dy: integer;
ur: TRect;
{$IFDEF IEDEBUG}
t1: dword;
{$ENDIF}
begin
if (not HasParentWindow) and (not fOffscreenPaint) then
exit;
{$IFDEF IEDEBUG}
t1 := gettickcount;
{$ENDIF}
// prepare the back buffer
if (fBackBuffer.Width <> GetClientWidth) or (fBackBuffer.Height <> GetClientHeight) then
begin
fBackBuffer.Allocate(GetClientWidth, GetClientHeight, ie24RGB);
fUpdateBackBuffer := true;
end;
// draw on the back buffer
if fUpdateBackBuffer then
begin
ur := rc;
PaintToEx(fBackBuffer, @ur, true, true);
if assigned(fOnDrawBackBuffer) then
fOnDrawBackBuffer(self);
fUpdateBackBuffer := false;
end;
// draw the back buffer to the canvas
with fBitmapInfoHeader256 do
begin
biSize := sizeof(TBitmapInfoHeader);
biWidth := GetClientWidth;
biHeight := GetClientHeight;
biPlanes := 1;
if fBackBuffer.PixelFormat = ie1g then
begin
biBitCount := 1;
Palette[1].rgbRed := 255;
Palette[1].rgbGreen := 255;
Palette[1].rgbBlue := 255;
end
else
biBitCount := 24;
biCompression := BI_RGB;
end;
{$IFDEF IEINCLUDEDIRECTSHOW}
if assigned(fImageEnIO) and assigned(fImageEnIO.DShowParams) and fImageEnIO.DShowParams.RenderVideo and (fImageEnIO.DShowParams.State<>gsStopped) then
with CurrentLayer.DrawingInfo do
ExcludeClipRect(Canvas.Handle, XDst, YDst, XDst+WidthDst, YDst+HeightDst);
{$ENDIF}
with rc do
begin
dx := imin(right - left, GetClientWidth);
dy := imin(bottom - top, GetClientHeight);
if (fUseDrawDibDraw or (IEGlobalSettings().SystemColors <= 8)) and not IEGlobalSettings().IsRemoteSession then
// drawdibdraw. Good for dithering in system with <=256 colors
IEDrawDibDraw(fHDrawDib, Canvas.Handle, left, top, dx, dy, PBitmapInfoHeader(@fBitmapInfoHeader256)^, fBackBuffer.Scanline[fBackBuffer.Height - 1], left, top, dx, dy, 0)
else
begin
// new mode
if (IEGlobalSettings().SystemColors < 24) and not IEGlobalSettings().IsRemoteSession then
begin
// dithering needed (for example display with 16bit depth need dithering)
SetStretchBltMode(Canvas.Handle, HALFTONE);
StretchBlt(Canvas.Handle, left, top, dx, dy, fBackBuffer.Canvas.Handle, left, top, dx, dy, SRCCOPY);
end
else
begin
// no dithering needed (fastest way)
if fIsNavigator and (ienoMARKOUTER in fNavigatorOptions) then
DrawMarkOutNavigator(rc)
else
BitBlt(Canvas.Handle, left, top, dx, dy, fBackBuffer.Canvas.Handle, left, top, SRCCOPY);
end;
end;
end;
{$IFDEF IEDEBUG}
//OutputDebugString(PAnsiChar('PaintRect: '+IEIntToStr(gettickcount-t1)+' ms'));
{$ENDIF}
if fPolySelecting then
PolyDraw1;
UserInteractions_Paint(rc);
if (fOldHandle<>0) and (fOldHandle<>Handle) then
IO.RecreatedTImageEnViewHandle;
fOldHandle := Handle;
end;
procedure TImageEnView.DoDrawVersion;
var
x, y: integer;
ss: string;
begin
with Canvas do
begin
Font.Name := 'Arial';
Font.Height := 13;
Font.Color := clBlack;
Brush.style := bsSolid;
Brush.Color := clWhite;
ss := 'ImageEn Version ' + string(IEMAINVERSION) + ' - ' + IntToStr(IEMAINDATEDD) + '/' + IntToStr(IEMAINDATEMM) + '/' + IntToStr(IEMAINDATEYY);
x := ClientWidth - TextWidth(ss) - fRulerParams.RulerAreaRight;
y := ClientHeight - TextHeight(ss) - fRulerParams.RulerAreaBottom;
TextOut(x, y, ss);
end;
end;
procedure TImageEnView.Paint;
var
rc: TRect;
{$IFDEF IEINCLUDEDIRECTSHOW}
x1, y1, nw, nh: Integer;
zw, zh: Double;
{$ENDIF}
begin
{$ifdef IEDEBUG}
OutputDebugString('TImageEnView.Paint');
{$endif}
try
if (not HandleAllocated) or ((not HasParentWindow) or (GetClientWidth = 0) or (GetClientHeight = 0)) and not fOffscreenPaint then
exit; // environment not ready
if csDesigning in ComponentState then
begin
// draws only the background
IEDrawBackground(ComponentState, Canvas, nil, fBackgroundStyle,
GetThemeColor( ietpControlBackground, fBackground ),
0, 0, Width, Height, 0, 0, 0, 0, fChessboardSize, fChessboardBrushStyle, fChessboardColor2Customized,
GetThemeColor( ietpControlBackgroundGradientEnd, fGradientEndColor ),
fWallpaper, fWallpaperStyle, fLiveBackground);
end
else
begin
if fLockPaint = 0 then
begin
// ready to draw
if fFullUpdateRequest then
rc := Rect(0, 0, GetClientWidth, GetClientHeight)
else
begin
rc := Canvas.ClipRect;
if (rc.Right = 0) or (rc.bottom = 0) then
begin
rc.Right := GetClientWidth;
rc.Bottom := GetClientHeight;
end;
end;
// Exclude Rulers
rc.Left := imax( rc.Left , fRulerParams.RulerAreaLeft );
rc.Top := imax( rc.Top , fRulerParams.RulerAreaTop );
rc.Right := imin( rc.Right , GetClientWidth() - fRulerParams.RulerAreaRight );
rc.Bottom := imin( rc.Bottom, GetClientHeight() - fRulerParams.RulerAreaBottom );
PaintRect(rc);
PaintGuideLines( Canvas );
if Assigned(fOnDrawCanvas) then
fOnDrawCanvas(self, Canvas, Rect(0, 0, GetClientWidth, GetClientHeight));
end;
fFullUpdateRequest := False;
if not fRectMoving then
AniPolyFunc(self, false);
if assigned(fOnPaint) then
OnPaint(self);
end;
except
{$IFDEF IEDEBUG}
on E:Exception do
OutputDebugStringA(PAnsiChar( '[Paint] ' + e.message + #13#10 ));
{$ENDIF}
end;
if fDrawVersion then
DoDrawVersion;
// Draw rulers
if fLockPaint = 0 then
fRulerParams.Paint( Canvas );
if (fInteractionHint <> '') and (fEnableInteractionHints) then
IEDrawHint2(Canvas, fInteractionHintX, fInteractionHintY, fInteractionHint, fInteractionHintMinText);
{$IFDEF IEINCLUDEDIRECTSHOW}
// synchronize directshow video rendering with current layer
if assigned(fImageEnIO) and assigned(fImageEnIO.DShowParams) and fImageEnIO.DShowParams.RenderVideo and (fImageEnIO.DShowParams.State<>gsStopped) then
begin
with CurrentLayer.DrawingInfo do
begin
// Ensure an image layer is active
if ( fLayersCurrent > -1 ) and ( Layers[ fLayersCurrent ].Kind <> ielkImage ) then
SetLayersCurrent( 0 ); // Base layer always image
fImageEnIO.DShowParams.GetVideoRenderNativeSize(nw, nh);
if (nw > 0) and (nh > 0) and (CurrentLayer.Bitmap.Width > 0) and (CurrentLayer.Bitmap.Height > 0) then
begin
zw := CurrentLayer.Bitmap.Width / nw;
zh := CurrentLayer.Bitmap.Height / nh;
x1 := trunc(XSrc/zw);
y1 := trunc(YSrc/zh);
nw := trunc(WidthSrc/zw);
nh := trunc(HeightSrc/zh);
fImageEnIO.DShowParams.SetVideoRenderRect( Rect(x1, y1, x1+nw, y1+nh), Rect(XDst, YDst, XDst+WidthDst, YDst+HeightDst) );
end;
end;
end;
{$ENDIF}
if fAutoCursors and fFirstTimeSetCursor then
begin
SetCursor( Cursor );
fFirstTimeSetCursor := false;
end;
end;
procedure TImageEnView.TerminatePolySelection(shift: Boolean; removeLastPoint: Boolean);
begin
if removeLastPoint then
AnimPolygonRemoveLastPoint(fHPolySel);
if (not shift) or (not fEnableShiftKey) then
fSelectionMask.Empty;
EndSelect;
UpdateReason := ieurSelectionChanged;
Update;
fPolySelecting := False;
DoSelectionChange;
end;
procedure TImageEnView.DoDoubleClickEx(Shift: boolean);
begin
if fPolySelecting and (not fLassoSelecting) then
TerminatePolySelection(Shift, true);
end;
procedure TImageEnView.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
inherited;
{$IFNDEF OCXVERSION}
if (csReading in ComponentState) or (csDesigning in ComponentState) or (csLoading in ComponentState) then
if fIEBitmapValid then
begin
fIEBitmap.Width := Width;
fIEBitmap.Height := Height;
Clear;
end;
{$ENDIF}
end;
{!!
<FS>TImageEnView.PolySelPoints
<FM>Declaration<FC>
property PolySelPoints: <A PPointArray>;
<FM>Description<FN>
PolySelPoints returns the current selection as a pointer to an array of TPoint.
<FM>Example<FC>
// Copies current selection to ImageEnView2. Then enhance contrast of ImageEnView2 and copy back to ImageEnView1 (in some selection).
ImageEnView2.CopyFromPolygon(ImageEnView1.Bitmap, ImageEnView2.PolySelPoints^, ImageEnView2.PolySelCount, Point(0, 0));
ImageEnView2.Proc.Contrast(30);
ImageEnView2.CopyToPolygon(ImageEnView1.Bitmap, ImageEnView1.PolySelPoints^, ImageEnView1.PolySelCount, Point(0, 0));
ImageEnView1.Update;
!!}
function TImageEnView.GetPolySelArray: PPointArray;
begin
result := PIEAnimPoly(fHPolySel)^.Poly;
end;
{!!
<FS>TImageEnView.PolySel
<FM>Declaration<FC>
property PolySel[idx: Integer]: TPoint; (Read-only)
<FM>Description<FN>
Returns the idx point of the current polygonal selection. The point is specified in bitmap coordinates.
<FM>Example<FC>
var
MyPolySelArray: array of TPoint;
I: Integer;
begin
// Create an array of TPoints of the polygon selection in a TImageEnView
SetLength( MyPolySelArray, ImageEnView1.PolySelCount );
for I := 0 to ImageEnView1.PolySelCount - 1 do
MyPolySelArray[ I ] := ImageEnView1.PolySel[ I ];
end;
<FM>See Also<FN>
- <A TImageEnView.PolySelCount>
- <A TImageEnView.PolySelPoints>
- <A TImageEnView.AddSelPoint>
!!}
function TImageEnView.GetPolySel(idx: integer): TPoint;
begin
result := Point(0, 0);
with PIEAnimPoly(fHPolySel)^ do
if (idx >= 0) and (idx <= PolyCount) then
begin
if Poly^[idx].X = IESELBREAK then
result := Poly^[idx] // break code
else
result := Point(Poly^[idx].X, Poly^[idx].Y);
end;
end;
{!!
<FS>TImageEnView.PolySelCount
<FM>Declaration<FC>
property PolySelCount: Integer; (Read-only)
<FM>Description<FN>
Returns the points number of the current polygonal selection (size of <A TImageEnView.PolySel> array).
<FM>Example<FC>
var
MyPolySelArray: array of TPoint;
I: Integer;
begin
// Create an array of TPoints of the polygon selection in a TImageEnView
SetLength( MyPolySelArray, ImageEnView1.PolySelCount );
for I := 0 to ImageEnView1.PolySelCount - 1 do
MyPolySelArray[ I ] := ImageEnView1.PolySel[ I ];
end;
<FM>See Also<FN>
- <A TImageEnView.PolySel>
- <A TImageEnView.AddSelPoint>
!!}
function TImageEnView.GetPolySelCount: integer;
begin
result := PIEAnimPoly(fHPolySel)^.PolyCount;
end;
{!!
<FS>TImageEnView.Deselect
<FM>Declaration<FC>
procedure Deselect;
<FM>Description<FN>
This method removes current selection. After Deselect is called, the <A TImageEnView.Selected> property will be false.
!!}
procedure TImageEnView.Deselect;
begin
if fSel then
begin
fSel := false;
HideSelectionEx(true);
end;
AnimPolygonClear(fHPolySel);
fSelectionMask.Empty;
fSel := false;
fPolySelecting := false;
fLassoSelecting := false;
fRectSelecting := False;
fCircSelecting := False;
fRectResizing := ieNone;
fSelectMoving := -1;
RestoreCursor;
UpdateReason := ieurSelectionChanged;
Update;
end;
{!!
<FS>TImageEnView.Select
<FM>Declaration<FC>
procedure Select(x1, y1, x2, y2: integer; Op: <A TIESelOp> = iespReplace); virtual;
<FM>Description<FN>
This method selects a portion of the image.
The selected area will be outlined with an animated rectangle.
If <A TImageEnView.SelectionBase> is <FC>iesbClientArea<FN> (default) all coordinates depend on actual zoom and scrolling.
If <FC>SelectionBase<FN> is <FC>iesbBitmap<FN>, all coordinates refer to bitmap pixels.
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>x1<FN></C> <C>Top-left horizontal position</C> </R>
<R> <C><FC>y1<FN></C> <C>Top-left vertical position</C> </R>
<R> <C><FC>x2<FN></C> <C>Bottom-right horizontal position. Last column not selected</C> </R>
<R> <C><FC>y2<FN></C> <C>Bottom-right vertical position. Last row not selected</C> </R>
<R> <C><FC>Op<FN></C> <C>Selection operation (add or replace selection). If <FC>Op<FN> is <FC>iespReplace<FN>, <FC>Select<FN> replaces all previous selections. If Op is <FC>iespAdd<FN>, <FC>Select<FN> adds a new selection</C> </R>
</TABLE>
<FC>x2, y2<FN> represent the right/bottom margin (not selected). To select an area of 1x1 pixels we need to select using 0,0,1,1 (the column 1 and the row 1 aren't selected).
<FM>Example<FC>
// Increase contrast of bitmap rectangle (0,0,49,49)
ImageEnView1.SelectionBase := iesbBitmap;
ImageEnView1.Select(0, 0, 50, 50);
ImageEnView1.Proc.Contrast(10);
ImageEnView1.Deselect;
// Enlarge the selection by ten pixels on all sides
With ImageEnView1 do
begin
SelectionBase := iesbBitmap;
Select( SelX1 - 10, SelY1 - 10, SelX2 - 10, SelY2 - 10 );
End;
<FM>See Also<FN>
- <A TImageEnView.SelectEllipse>
- <A TImageEnView.SelectMagicWand>
- <A TImageEnView.SelectRoundRect>
- <A TImageEnView.InvertSelection>
- <A TImageEnView.Deselect>
- <A TImageEnView.SelX1>
- <A TImageEnView.SelY1>
- <A TImageEnView.SelX2>
- <A TImageEnView.SelY2>
!!}
// x2,y2 represent the right/bottom margin (not selected)
// to select an area of 1x1 pixels we need to select using 0,0,1,1 (the column 1 and the
// row 1 aren't selected)
procedure TImageEnView.Select(x1, y1, x2, y2: integer; Op: TIESelOp);
begin
if fIsNavigator then
LockPaint;
SelectEx(x1, y1, x2, y2, Op, false, false);
if Op = iespReplace then
fSelectionMask.Empty;
EndSelect;
if not fIsNavigator then // 3.0.3: to speed-up scrolling when navigator has a ZoomFilter
fUpdateBackBuffer := true;
if fIsNavigator then
begin
NPUnLockPaint;
AniPolyFunc(self, true);
Paint;
AniPolyFunc(self, true);
end
else
Paint;
end;
{!!
<FS>TImageEnView.SelectEllipse
<FM>Declaration<FC>
procedure SelectEllipse(CenterX, CenterY, Width, Height: Integer; Op: <A TIESelOp> = iespReplace);
<FM>Description<FN>
SelectEllipse makes an elliptical selection, centered at <FC>CenterX<FN> and <FC>CenterY<FN>, and with size specified by <FC>Width<FN> and <FC>Height<FN>.
<FC>Op<FN> specifies whether to add a new selection (<FC>iespAdd<FN>) or replace the current one (<FC>iespReplace<FN>).
<FM>Example<FC>
ImageEnView.SelectEllipse( 100, 100, 20, 20 );
!!}
procedure TImageEnView.SelectEllipse(CenterX, CenterY, Width, Height: integer; Op: TIESelOp);
var
dx, dy, p, g, xx, yy: integer;
begin
if fSelectionBase = iesbClientArea then
begin
CenterX := XScr2Bmp( CenterX, True );
CenterY := YScr2Bmp( CenterY, True );
Width := trunc(Width * f100DZoomX);
Height := trunc(Height * f100DZoomY);
end;
if (Width <= 0) or (Height <= 0) then
exit;
fSel := true;
if (Op = iespAdd) and (PIEAnimPoly(fHPolySel)^.PolyCount > 0) then
AnimPolygonAddBreak(fHPolySel)
else
begin
AnimPolygonClear(fHPolySel);
end;
dx := Width div 2;
dy := Height div 2;
p := trunc(2 * pi * imin(dx, dy)) shr 2;
if p < 3 then
p := 3;
for g := 0 to p - 1 do
begin
xx := round(CenterX + cos((2 * pi / p) * g) * dx);
yy := round(CenterY + sin((2 * pi / p) * g) * dy);
xx := imax(0, imin(fIEBitmap_Width, xx));
yy := imax(0, imin(fIEBitmap_Height, yy));
AnimPolygonAddPtEx(fHPolySel, xx, yy);
end;
if Op = iespReplace then
fSelectionMask.Empty;
EndSelect;
fUpdateBackBuffer := true;
Paint;
ShowSelectionEx(true);
end;
{!!
<FS>TImageEnView.SelectRoundRect
<FM>Declaration<FC>
procedure SelectRoundRect(Left, Top, Right, Bottom, RoundWidth, RoundHeight: integer; Op: <A TIESelOp> = iespReplace);
<FM>Description<FN>
Creates a rounded selection.
<FC>Left, Top, Right, Bottom<FN> are the rectangle coordinates.
<FC>RoundWidth<FN> and <FC>RoundHeight<FN> specify the size of the rounding (in pixels).
<FC>Op<FN> specifies whether to add a new selection (<FC>iespAdd<FN>) or replace the current one (<FC>iespReplace<FN>).
<FM>Example<FC>
ImageEnView1.SelectRoundRect(100, 100, 250, 250, 20, 20);
!!}
procedure TImageEnView.SelectRoundRect(Left, Top, Right, Bottom, RoundWidth, RoundHeight: integer; Op: TIESelOp);
var
nsteps: Integer;
ptr: ppointarray;
i: Integer;
xrect: TRect;
procedure AddPt(xx, yy: Integer);
begin
xx := imax(0, imin(fIEBitmap_Width, xx));
yy := imax(0, imin(fIEBitmap_Height, yy));
AnimPolygonAddPtEx(fHPolySel, xx, yy);
end;
begin
if fSelectionBase = iesbClientArea then
begin
Left := XScr2Bmp( Left, True );
Top := YScr2Bmp( Top, True );
Right := XScr2Bmp( Right, True );
Bottom := YScr2Bmp( Bottom, True );
RoundWidth := trunc(RoundWidth * f100DZoomX);
RoundHeight := trunc(RoundHeight * f100DZoomY);
end;
fSel := true;
if (Op = iespAdd) and (PIEAnimPoly(fHPolySel)^.PolyCount > 0) then
AnimPolygonAddBreak(fHPolySel)
else
AnimPolygonClear(fHPolySel);
xrect := rect(Left, Top, Right, Bottom);
nsteps := RoundWidth * RoundHeight;
getmem(ptr, sizeof(TPoint) * nsteps);
try
// top-left round
IEBezier2D4Controls(Point(xrect.Left, xrect.Top+RoundHeight), xrect.TopLeft, Point(xrect.Left+RoundWidth, xrect.Top), Point(xrect.Left+RoundWidth, xrect.Top), ptr, nsteps);
for i := 0 to nsteps-1 do
AddPt( ptr[i].x, ptr[i].y );
// top-right round
IEBezier2D4Controls(Point(xrect.Right, xrect.Top+RoundHeight), Point(xrect.Right, xrect.Top), Point(xrect.Right-RoundWidth, xrect.Top), Point(xrect.Right-RoundWidth, xrect.Top), ptr, nsteps);
for i := nsteps-1 downto 0 do
AddPt( ptr[i].x, ptr[i].y );
// bottom-right round
IEBezier2D4Controls(Point(xrect.Right, xrect.Bottom-RoundHeight), Point(xrect.Right, xrect.Bottom), Point(xrect.Right-RoundWidth, xrect.Bottom), Point(xrect.Right-RoundWidth, xrect.Bottom), ptr, nsteps);
for i := 0 to nsteps-1 do
AddPt( ptr[i].x, ptr[i].y );
// bottom-left round
IEBezier2D4Controls(Point(xrect.Left, xrect.Bottom-RoundHeight), Point(xrect.Left, xrect.Bottom), Point(xrect.Left+RoundWidth, xrect.Bottom), Point(xrect.Left+RoundWidth, xrect.Bottom), ptr, nsteps);
for i := nsteps-1 downto 0 do
AddPt( ptr[i].x, ptr[i].y );
finally
freemem(ptr);
end;
if Op = iespReplace then
fSelectionMask.Empty;
EndSelect;
fUpdateBackBuffer := true;
Paint;
ShowSelectionEx(true);
end;
procedure AdjustRectangle(var x1, y1, x2, y2: Integer; Width, Height: Integer);
begin
if x1<0 then
begin
x2 := x2-x1;
x1 := 0;
end;
if y1<0 then
begin
y2 := y2-y1;
y1 := 0;
end;
if x2>=Width then
begin
x1 := x1-(x2-Width+1);
x2 := Width-1;
end;
if y2>=Height then
begin
y1 := y1-(y2-Height+1);
y2 := Height-1;
end;
end;
// x2, y2 represent the right/bottom margin (not selected)
// to select an area of 1x1 pixels we need to select using 0, 0, 1, 1 (the column 1 and the
// row 1 aren't selected)
// IT DO NOT CALL "EndSelect"
// if adjRect is true, move the rectangle to make it inside the bitmap
procedure TImageEnView.SelectEx(x1, y1, x2, y2: integer; Op: TIESelOp; aspectratio: boolean; adjRect: Boolean);
begin
if (x1 = x2) or (y1 = y2) then
exit;
if fSelectionBase = iesbClientArea then
begin
x1 := XScr2Bmp( x1, True );
y1 := YScr2Bmp( y1, True );
x2 := XScr2Bmp( x2, True );
y2 := YScr2Bmp( y2, True );
end;
fSel := true;
if (Op = iespAdd) and (PIEAnimPoly(fHPolySel)^.PolyCount > 0) then
AnimPolygonAddBreak(fHPolySel)
else
begin
AnimPolygonClear(fHPolySel);
end;
if (Op = iespReplace) then
fSelectionMask.Empty;
if aspectratio and (fSelectionAspectRatio = -1) then
y2 := round(x2 - x1 + 1 + y1 - 1)
else
if fSelectionAspectRatio > 0 then
begin
y2 := y1 + round((x2 - x1) * fSelectionAspectRatio);
if y2 >= fIEBitmap_Height then
x2 := round( (fIEBitmap_Height-1-y1+x1*fSelectionAspectRatio)/fSelectionAspectRatio );
if y2 < 0 then
x2 := round( (0-y1+x1*fSelectionAspectRatio)/fSelectionAspectRatio );
end;
if adjRect then
AdjustRectangle(x1, y1, x2, y2, fIEBitmap_Width, fIEBitmap_Height);
OrdCor(x1, y1, x2, y2);
x1 := imax(0, imin(fIEBitmap_Width - 1, x1));
y1 := imax(0, imin(fIEBitmap_Height - 1, y1));
x2 := imax(0, imin(fIEBitmap_Width - 0, x2));
y2 := imax(0, imin(fIEBitmap_Height - 0, y2));
AnimPolygonAddPtEx(fHPolySel, x1, y1);
AnimPolygonAddPtEx(fHPolySel, x2, y1);
AnimPolygonAddPtEx(fHPolySel, x2, y2);
AnimPolygonAddPtEx(fHPolySel, x1, y2);
ShowSelectionEx(true);
end;
{!!
<FS>TImageEnView.SelectCustom
<FM>Declaration<FC>
procedure SelectCustom;
<FM>Description<FN>
Call SelectCustom after you have specified a pixmap of selected pixels of the image.
To select a pixmap use <A TImageEnView.SelectionMask> methods like <A TIEMask.SetPixel>.
<FM>Example<FC>
// only pixels at 10,10 and 15,15 are selected
ImageEnView.SelectionMask.SetPixel(10, 10, 1);
ImageEnView.SelectionMask.SetPixel(15, 15, 1);
ImageEnView.SelectCustom();
<FM>See Also<FN>
- <A TImageEnView.SelectionMask>
!!}
procedure TImageEnView.SelectCustom;
begin
fSel := not fSelectionMask.IsEmpty;
ShowSelectionEx(true);
fUpdateBackBuffer := true;
Paint;
end;
{!!
<FS>TImageEnView.SelectMagicWand
<FM>Declaration<FC>
procedure SelectMagicWand(x, y: Integer; Op: <A TIESelOp>);
<FM>Description<FN>
SelectMagicWand selects an irregular region that has similar colors.
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>x<FN></C> <C>Starting horizontal coordiante</C> </R>
<R> <C><FC>y<FN></C> <C>Starting vertical coordinate</C> </R>
<R> <C><FC>Op<FN></C> <C>If <FC>Op<FN> is <FC>iespReplace<FN> the region replaces the existing selection, otherwise if <FC>Op<FN> is <FC>iespAdd<FN>, the region is appended to the existing selection</C> </R>
</TABLE>
<FM>Example<FC>
ImageEnView1.SelectMagicWand(10, 10, iespReplace);
!!}
procedure TImageEnView.SelectMagicWand(x, y: integer; Op: TIESelOp);
var
points: TIEArrayOfTPoint;
q: integer;
xx, yy: integer;
begin
SetLength(points, 0);
if fSelectionBase = iesbClientArea then
begin
x := XScr2Bmp( x, True );
y := YScr2Bmp( y, True );
end;
if (fIEBitmapValid = False) or (x < 0) or (x > fIEBitmap.Width) or (y < 0) or (y > fIEBitmap.Height) then
exit;
case fMagicWandMode of
iewInclusive:
begin
points := IEMakeMagicWandPoints(fIEBitmap, x, y, fMagicWandMaxFilter, fMagicWandTolerance);
if length(points) > 0 then
begin
fSel := true;
if (Op = iespAdd) and (PIEAnimPoly(fHPolySel)^.PolyCount > 0) then
AnimPolygonAddBreak(fHPolySel)
else
begin
AnimPolygonClear(fHPolySel);
fSelectionMask.Empty;
end;
for q := 0 to length(points) - 1 do
begin
xx := imin(imax(0, points[q].x), fIEBitmap.Width);
yy := imin(imax(0, points[q].y), fIEBitmap.Height);
AnimPolygonAddPtEx(fHPolySel, xx, yy);
end;
if Op = iespReplace then
fSelectionMask.Empty;
EndSelect;
ShowSelectionEx(true);
end;
end;
iewExclusive:
begin
if (Op = iespReplace) then
begin
AnimPolygonClear(fHPolySel);
fSelectionMask.Empty;
end;
_MakeMagicWandPointsEx(fIEBitmap, x, y, fMagicWandMaxFilter, fMagicWandTolerance, fSelectionMask, fSelectionIntensity);
fSel := not fSelectionMask.IsEmpty;
ShowSelectionEx(true);
end;
iewGlobal:
begin
if (Op = iespReplace) then
begin
AnimPolygonClear(fHPolySel);
fSelectionMask.Empty;
end;
_MakeMagicWandPointsEx2(fIEBitmap, x, y, fMagicWandTolerance, fSelectionMask, fSelectionIntensity);
fSel := not fSelectionMask.IsEmpty;
ShowSelectionEx(true);
end;
end;
fUpdateBackBuffer := true;
Paint;
end;
{!!
<FS>TImageEnView.SelectColors
<FM>Declaration<FC>
procedure SelectColors(StartColor, FinalColor: <A TRGB>; Op: <A TIESelOp> = iespReplace);
procedure SelectColors(Color: <A TRGB>; Op: <A TIESelOp> = iespReplace);
procedure SelectColors(ColorIndex: integer; Op: <A TIESelOp> = iespReplace);
<FM>Description<FN>
Selects all colors inside the range <FC>StartColor<FN> up to <FC>FinalColor<FN>, or specific color or a color index.
If <FC>Op<FN> is <FC>iespReplace<FN> the region replaces the existing selection, otherwise if <FC>Op<FN> is <FC>iespAdd<FN>, the region is appended to the existing selection.
Third overload accepts <FC>ColorIndex<FN>, which is the index of a color in image colormap: the bitmat pixelformat must be ie8p (palette).
<FM>Example<FC>
// select from 200,200,200 to 255,255,255
ImageEnView1.SelectColors(CreateRGB(200, 200, 200), CreateRGB(255, 255, 255));
// select only color 255,0,0 (pure red)
ImageEnView1.SelectColors(CreateRGB(255, 0, 0));
// select color index 5 of a paletted bitmap (ie8p)
ImageEnView1.SelectColors(5);
<FM>See Also<FN>
- <A TImageEnView.SelectNonAlpha>
- <A CreateRGB>
- <A TRGB2TColor>
- <A TColor2TRGB>
!!}
procedure TImageEnView.SelectColors(StartColor, FinalColor: TRGB; Op: TIESelOp);
var
i, j: integer;
ww, hh: integer;
r1, g1, b1, r2, g2, b2: byte;
p_rgb: PRGB;
begin
if fIEBitmapValid = False then
exit;
if (Op = iespReplace) then
begin
AnimPolygonClear(fHPolySel);
fSelectionMask.Empty;
end;
r1 := StartColor.r;
g1 := StartColor.g;
b1 := StartColor.b;
r2 := FinalColor.r;
g2 := FinalColor.g;
b2 := FinalColor.b;
ww := fIEBitmap.Width;
hh := fIEBitmap.Height;
case fIEBitmap.PixelFormat of
ie24RGB:
begin
for i := 0 to hh - 1 do
begin
p_rgb := fIEBitmap.ScanLine[i];
for j := 0 to ww - 1 do
begin
with p_rgb^ do
if (r >= r1) and (g >= g1) and (b >= b1) and (r <= r2) and (g <= g2) and (b <= b2) then
fSelectionMask.SetPixel(j, i, fSelectionIntensity);
inc(p_rgb);
end;
end;
end;
else
begin
for i := 0 to hh - 1 do
for j := 0 to ww - 1 do
with fIEBitmap.Pixels[j, i] do
if (r >= r1) and (g >= g1) and (b >= b1) and (r <= r2) and (g <= g2) and (b <= b2) then
fSelectionMask.SetPixel(j, i, fSelectionIntensity);
end;
end;
fSel := not fSelectionMask.IsEmpty;
ShowSelectionEx(true);
fUpdateBackBuffer := true;
Paint;
end;
procedure TImageEnView.SelectColors(Color: TRGB; Op: TIESelOp);
var
i, j: integer;
ww, hh: integer;
rr, gg, bb: byte;
p_rgb: PRGB;
begin
if fIEBitmapValid = False then
exit;
if (Op = iespReplace) then
begin
AnimPolygonClear(fHPolySel);
fSelectionMask.Empty;
end;
rr := Color.r;
gg := Color.g;
bb := Color.b;
ww := fIEBitmap.Width;
hh := fIEBitmap.Height;
case fIEBitmap.PixelFormat of
ie24RGB:
begin
for i := 0 to hh - 1 do
begin
p_rgb := fIEBitmap.ScanLine[i];
for j := 0 to ww - 1 do
begin
with p_rgb^ do
if (r = rr) and (g = gg) and (b = bb) then
fSelectionMask.SetPixel(j, i, fSelectionIntensity);
inc(p_rgb);
end;
end;
end;
else
begin
for i := 0 to hh - 1 do
for j := 0 to ww - 1 do
with fIEBitmap.Pixels[j, i] do
if (r = rr) and (g = gg) and (b = bb) then
fSelectionMask.SetPixel(j, i, fSelectionIntensity);
end;
end;
fSel := not fSelectionMask.IsEmpty;
ShowSelectionEx(true);
fUpdateBackBuffer := true;
Paint;
end;
procedure TImageEnView.SelectColors(ColorIndex: integer; Op: TIESelOp);
var
i, j: integer;
ww, hh: integer;
begin
if ( fIEBitmapValid = False ) or ( fIEBitmap.PixelFormat <> ie8p ) then
exit;
if (Op = iespReplace) then
begin
AnimPolygonClear(fHPolySel);
fSelectionMask.Empty;
end;
ww := fIEBitmap.Width;
hh := fIEBitmap.Height;
for i := 0 to hh - 1 do
for j := 0 to ww - 1 do
if fIEBitmap.Pixels_ie8[j, i] = ColorIndex then
fSelectionMask.SetPixel(j, i, fSelectionIntensity);
fSel := not fSelectionMask.IsEmpty;
ShowSelectionEx(true);
fUpdateBackBuffer := true;
Paint;
end;
{!!
<FS>TImageEnView.SelectNonAlpha
<FM>Declaration<FC>
procedure SelectNonAlpha(Op: <A TIESelOp> = iespReplace);
<FM>Description<FN>
Selects all pixels based on the alpha channel. Pixels of Alpha value 0 will be unselected, 255 will be selected, and 1 - 254 partially selected.
If <FC>Op<FN> is <FC>iespReplace<FN> the region replaces the existing selection, otherwise if <FC>Op<FN> is <FC>iespAdd<FN>, the region is appended to the existing selection.
<FM>Example<FC>
// Load a transparent PNG file and select the non-alpha pixels
ImageEnView1.IO.LoadFromFile( 'D:\Transparent.png' );
ImageEnView1.SelectNonAlpha();
<FM>See Also<FN>
- <A TImageEnView.SelectColors>
!!}
procedure TImageEnView.SelectNonAlpha(Op: TIESelOp = iespReplace);
var
i, j: integer;
ww, hh: integer;
begin
if ( fIEBitmapValid = False ) or ( fEnableAlphaChannel = False ) then
exit;
if (Op = iespReplace) then
begin
AnimPolygonClear(fHPolySel);
fSelectionMask.Empty;
end;
ww := fIEBitmap.AlphaChannel.Width;
hh := fIEBitmap.AlphaChannel.Height;
for i := 0 to hh - 1 do
for j := 0 to ww - 1 do
fSelectionMask.SetPixel( j, i, fIEBitmap.AlphaChannel.Pixels_ie8[ j, i ]);
fSel := not fSelectionMask.IsEmpty;
ShowSelectionEx(true);
fUpdateBackBuffer := true;
Paint;
end;
procedure AdjustRectWithRatio(var x1, y1, x2, y2: Integer; lx1, ly1, lx2, ly2: Integer; Width, Height: Integer; z: Double);
begin
z := 1/z;
if y2>=Height then
x2 := round( (Height-1-y1+x1*z)/z )
else
if y1<0 then
x1 := round( (-y2+0+x2*z)/z )
else
if x2>=Width then
y2 := round( y1+(Width-1-x1)*z )
else
if x1<0 then
y1 := round( y2-(x2-0)*z );
end;
// Resize selection
// newx, newy: new point in client coords
// return new grip if changed
function TImageEnView.SelectResizeEx(grip: TIEGrip; newx, newy: integer; aspectratio: boolean): TIEGrip;
var
x1, y1, x2, y2, w, h: integer;
z: double;
begin
z := 1;
result := grip;
with PIEAnimPoly(fHPolySel)^ do
if _IsRectangle(Poly, PolyCount) then
begin
newx := XScr2Bmp( newx, True );
newy := YScr2Bmp( newy, True );
if aspectratio then
begin
w := flx2 - flx1 + 1;
h := fly2 - fly1 + 1;
if h = 0 then
exit;
if fSelectionAspectRatio>0 then
z := 1/fSelectionAspectRatio
else
z := w / h;
case grip of
ieTopLeft: // left-top
while true do
begin
newy := round((-flx2 + newx - 1 + z * fly2 + z) / z);
if newy >= 0 then
break;
inc(newx);
end;
ieTopRight: // right-top
while true do
begin
newy := round((-newx + flx1 - 1 + z * fly2) / z);
if newy >= 0 then
break;
dec(newx);
end;
ieBottomRight: // right-bottom
newy := round((newx - flx1 + 1 + z * fly1 - z) / z);
ieBottomLeft: // left-bottom
while true do
begin
newy := round((flx2 - newx + 1 + fly1 * z + z) / z);
if newy<=fIEBitmap_Height then
break;
inc(newx);
end;
ieLeftSide: // left side
begin
while true do
begin
newy := round((-flx2 + newx - 1 + z * fly2 + z) / z);
if newy >= 0 then
break;
inc(newx);
end;
grip := ieTopLeft;
end;
ieRightSide: // right side
begin
newy := round((newx - flx1 + 1 + z * fly1 - z) / z);
grip := ieBottomRight;
end;
ieTopSide: // top side
begin
while true do
begin
newx := round(-z * fly2 + z * newy - z + flx2 + 1);
if newx >= 0 then
break;
inc(newy);
end;
grip := ieTopLeft;
end;
ieBottomSide: // bottom side
begin
while true do
begin
newx := round(z * newy - z * fly1 + z + flx1 - 1);
if newx<=fIEBitmap_Width then
break;
dec(newy);
end;
grip := ieBottomRight;
end;
end;
end;
if (grip = ieTopRight) or (grip = ieBottomRight) or (grip = ieRightSide) then
inc(newx);
if (grip = ieBottomRight) or (grip = ieBottomLeft) or (grip = ieBottomSide) then
inc(newy);
x1 := RX1;
y1 := RY1;
x2 := RX2;
y2 := RY2;
case grip of
ieTopLeft, ieBottomLeft, ieLeftSide: // left
begin
x1 := newx;
if x1 = x2 then
dec(x1);
end;
ieTopRight, ieBottomRight, ieRightSide: // right
begin
x2 := newx;
if x1 = x2 then
inc(x2);
end;
end;
case grip of
ieTopLeft, ieTopRight, ieTopSide: // top
begin
y1 := newy;
if y1 = y2 then
dec(y1);
end;
ieBottomRight, ieBottomLeft, ieBottomSide: // bottom
begin
y2 := newy;
if y1 = y2 then
inc(y2);
end;
end;
if x1 > x2 then
begin
if (result = ieTopLeft) then
result := ieTopRight // from left-top to right-top
else
if (result = ieBottomLeft) then
result := ieBottomRight // from left-bottom to right-bottom
else
if (result = ieTopRight) then
result := ieTopLeft // from right-top to left-top
else
if (result = ieBottomRight) then
result := ieBottomLeft // from right-bottom to left-bottom
else
if (result = ieLeftSide) then
result := ieRightSide // from left side to right side
else
if (result = ieRightSide) then
result := ieLeftSide; // from right side to left side
end;
if y1 > y2 then
begin
if (result = ieTopRight) then
result := ieBottomRight // from right-top to right-bottom
else
if (result = ieTopLeft) then
result := ieBottomLeft // from left-top to left-bottom
else
if (result = ieBottomRight) then
result := ieTopRight // from right-bottom to right-top
else
if (result = ieBottomLeft) then
result := ieTopLeft // from left-bottom to left-top
else
if (result = ieTopSide) then
result := ieBottomSide // from top side to bottom side
else
if (result = ieBottomSide) then
result := ieTopSide; // from bottom side to top side
end;
AnimPolygonClear(fHPolySel);
OrdCor(x1, y1, x2, y2);
if aspectratio then
AdjustRectWithRatio(x1, y1, x2, y2, flx1, fly1, flx2, fly2, fIEBitmap_Width, fIEBitmap_Height, z);
x1 := imax(0, imin(fIEBitmap_Width, x1));
y1 := imax(0, imin(fIEBitmap_Height, y1));
x2 := imax(0, imin(fIEBitmap_Width, x2));
y2 := imax(0, imin(fIEBitmap_Height, y2));
OrdCor(x1, y1, x2, y2);
AnimPolygonAddPtEx(fHPolySel, x1, y1);
AnimPolygonAddPtEx(fHPolySel, x2, y1);
AnimPolygonAddPtEx(fHPolySel, x2, y2);
AnimPolygonAddPtEx(fHPolySel, x1, y2);
ShowSelectionEx(true);
end;
end;
// Move selection
// fMoving must be 0
// ox, oy are moving offsets
function TImageEnView.SelectMoveEx(fMoving, ox, oy: integer; cutBorders: Boolean): integer;
begin
result := fMoving;
with PIEAnimPoly(fHPolySel)^ do
fRectMoving := _IsRectangle(Poly, PolyCount);
if not fRectMoving then
begin
fSelectionMask.TranslateBitmap(ox, oy, cutBorders); // modify ox and oy
end
else
begin
fSelectionMask.Empty; // moving a rectangle, full handled by AnimPolygonMove
end;
if (ox = 0) and (oy = 0) then
exit;
AnimPolygonMove(fHPolySel, ox, oy, fIEBitmap_Width, fIEBitmap_Height, cutBorders);
AniPolyFunc(self, true);
if fIsNavigator then
begin
Paint;
AniPolyFunc(self, true);
end;
end;
{!!
<FS>TImageEnView.MoveSelection
<FM>Declaration<FC>
procedure MoveSelection(MoveX, MoveY: Integer; Sizing: Boolean = False);
<FM>Description<FN>
Moves the current selection by the specified horizontal and vertical offsets. If Sizing is true then selection is resized (top-left position does not move).
<FM>Example<FC>
// Move the selection up and left 5 pixels
ImageEnView.MoveSelection( -5, -5 );
// Enlarge the selection vertically and horizontally by 10 pixels
ImageEnView.MoveSelection( 10, 10 );
!!}
procedure TImageEnView.MoveSelection(MoveX, MoveY: integer; Sizing: Boolean = False);
var
prevSelectionBase: TIESelectionBase;
begin
if Sizing then
begin
// SIZING
// allows to work when SelectionBase is iesbClientArea
prevSelectionBase := fSelectionBase;
fSelectionBase := iesbBitmap;
// watch for sels less than 1
if MoveX < -1 * (SelX2 - SelX1 - 1) then
MoveX := -1 * (SelX2 - SelX1 - 1);
if MoveY < -1 * (SelY2 - SelY1 - 1) then
MoveY := -1 * (SelY2 - SelY1 - 1);
// Note: Sizing will probably fail if Offset is small and aspect ratio is enforced
Select( SelX1, SelY1, SelX2 + MoveX + 1, SelY2 + MoveY + 1, iespReplace );
fSelectionBase := prevSelectionBase;
end
else
begin
// MOVING
SelectMoveEx(0, MoveX, MoveY, iesoCutBorders in fSelectionOptions);
EndSelect;
end;
end;
{!!
<FS>TImageEnView.AddSelPoint
<FM>Declaration<FC>
procedure AddSelPoint(x, y: Integer);
<FM>Description<FN>
AddSelPoint adds a point to the current polygonal selection.
If <A TImageEnView.SelectionBase> is <FC>iesbClientArea<FN> (default), all coordinates depend upon actual zoom and scrolling.
Otherwise, if <FC>SelectionBase<FN> is <FC>iesbBitmap<FN> all coordinates refer to bitmap pixels.
Use <A TImageEnView.EndSelect> to terminate selection by code.
<FM>Example<FC>
// Create a polygon selection from an array of TPoints
for I := Low( MyPolySelArray ) to High( MyPolySelArray ) do
ImageEnView1.AddSelPoint( MyPolySelArray[ I ].x, MyPolySelArray[ I ].y );
ImageEnView1.EndSelect();
<FM>See Also<FN>
- <A TImageEnView.EndSelect>
- <A TImageEnView.PolySel>
- <A TImageEnView.PolySelPoints>
!!}
// add a point to current selection
// To deselect use Deselect
procedure TImageEnView.AddSelPoint(x, y: integer);
begin
AddSelPointEx(x, y);
fUpdateBackBuffer := true;
Paint;
end;
// Same as AddSelPoint
// add a point to current selection
// To deselect use Deselect
// DO NOT CALL "EndSelect"
procedure TImageEnView.AddSelPointEx(x, y: integer);
begin
if fSelectionBase = iesbClientArea then
begin
x := XScr2Bmp( x, True );
y := YScr2Bmp( y, True );
x := imax(0, imin(fIEBitmap_Width, x));
y := imax(0, imin(fIEBitmap_Height, y));
end;
AnimPolygonAddPt(fHPolySel, x, y);
ShowSelectionEx(true);
end;
{!!
<FS>TImageEnView.DelLastSelPoint
<FM>Declaration<FC>
procedure DelLastSelPoint();
<FM>Description<FN>
Removes the last inserted selection point.
<FM>Example<FC>
// Pressing ESC removes last polygon point (when MouseInteract = [miSelectPolygon])
procedure TForm1.ImageEnVect1KeyPress(Sender: TObject; var Key: Char);
begin
if key = #27 then
ImageEnView1.DelLastSelRegion();
end;
!!}
procedure TImageEnView.DelLastSelPoint();
begin
if PIEAnimPOly(fHPolySel)^.PolyCount > 1 then
begin
AnimPolygonRemoveLastPoint(fHPolySel);
fUpdateBackBuffer := true;
Paint;
end;
end;
{!!
<FS>TImageEnView.AddSelBreak
<FM>Declaration<FC>
procedure AddSelBreak;
<FM>Description<FN>
AddSelBreak ends the current selection and begins a new selection.
!!}
// add a "break" to insert a new selection
procedure TImageEnView.AddSelBreak;
begin
AddSelBreakEx;
end;
// add a "break" to insert a new selection
// DO NOT CALL "EndSelect"
procedure TImageEnView.AddSelBreakEx;
begin
if PIEAnimPoly(fHPolySel)^.PolyCount > 0 then
AnimPolygonAddBreak(fHPolySel);
end;
// Show current selection
// d=true : draw now
// d=false : draw at next timer tick
procedure TImageEnView.ShowSelectionEx(d: boolean);
begin
PIEAnimPoly(fHPolySel)^.Enabled := True;
if d then
AniPolyFunc(self, true);
end;
// Hides current selection
// dd=true : remove selection performing a Paint
// dd=false : do not remove selection now
procedure TImageEnView.HideSelectionEx(dd: boolean);
begin
PIEAnimPoly(fHPolySel)^.Enabled := False;
if dd and (fLockPaint = 0) then
Paint;
end;
procedure TImageEnView.SetVisibleSelection(vv: boolean);
begin
fVisibleSelection := vv;
if vv then
ShowSelectionEx(true)
else
HideSelectionEx(true);
UpdateReason := ieurSelectionChanged;
Update;
end;
{!!
<FS>TImageEnView.VisibleSelection
<FM>Declaration<FC>
property VisibleSelection: Boolean;
<FM>Description<FN>
When true the current selection is displayed. This property is still valid when the selection is hidden.
!!}
function TImageEnView.GetVisibleSelection: boolean;
begin
result := fVisibleSelection;
end;
{!!
<FS>TImageEnView.SelectionOptions
<FM>Declaration<FC>
property SelectionOptions: <A TIESelectionOptions>;
<FM>Description<FN>
Provides access to <L TIESelectionOptions>selection behavior options</L>.
Default: SelectionOptions = [iesoAnimated, iesoSizeable, iesoMoveable, iesoCanScroll, iesoAllowMoveByKeyboard];
!!}
procedure TImageEnView.SetSelectionOptions(v: TIESelectionOptions);
begin
fSelectionOptions := v;
with PIEAnimPoly(fHPolySel)^ do
begin
Animated := iesoAnimated in fSelectionOptions;
Sizeable := iesoSizeable in fSelectionOptions;
ShowCenter := iesoShowCenter in fSelectionOptions;
end;
UpdateReason := ieurSelectionChanged;
Update;
end;
// reset vertex number of the animated polygon "ap"
{!!
<FS>TImageEnView.AnimPolygonClear
<FM>Declaration<FC>
procedure AnimPolygonClear(ap: pointer);
<FM>Description<FN>
Removes all points of the specified polygon.
!!}
procedure TImageEnView.AnimPolygonClear(ap: pointer);
begin
with PIEAnimPOly(ap)^ do
begin
freemem(Poly);
GetMem(Poly, 0);
PolyCount := 0;
PolyCapacity := 0;
end;
if fLockPaint = 0 then
Paint;
end;
// Removes last added sub-polygon
procedure TImageEnView.AnimPolygonRemoveLast(ap: pointer);
var
q: integer;
begin
with PIEAnimPoly(ap)^ do
for q := PolyCount - 1 downto 0 do
if (Poly^[q].x = IESELBREAK) or (q = 0) then
begin
PolyCount := q;
if PolyCount = 0 then
AnimPolygonClear(ap)
else
if fLockPaint = 0 then
Paint;
break;
end;
end;
// removes last point
procedure TImageEnView.AnimPolygonRemoveLastPoint(ap: pointer);
begin
with PIEAnimPoly(ap)^ do
begin
dec(PolyCount);
while (PolyCount > 0) and (Poly^[PolyCount - 1].x = IESELBREAK) do
dec(PolyCount);
end;
end;
// Add a "break" in the animated polygon
procedure TImageEnView.AnimPolygonAddBreak(ap: pointer);
var
tmp: PPointArray;
begin
with PIEAnimPoly(ap)^ do
begin
if PolyCount = PolyCapacity then
begin
PolyCapacity := imax(PolyCapacity*2, PolyCount+1);
GetMem(tmp, sizeof(TPoint) * PolyCapacity);
if PolyCount > 0 then
begin
CopyMemory(tmp, Poly, sizeof(TPoint) * PolyCount);
FreeMem(Poly);
end;
Poly := tmp;
end;
if (PolyCount > 0) and (Poly^[PolyCount - 1].x = IESELBREAK) then
exit;
Poly^[PolyCount] := Point(IESELBREAK, IESELBREAK);
inc(PolyCount);
end;
end;
// add a vertex to animated polygon "ap", doesn't paint
procedure TImageEnView.AnimPolygonAddPtEx(ap: pointer; x, y: integer);
var
tmp: PPointArray;
begin
// add a point
with PIEAnimPoly(ap)^ do
begin
if PolyCount = PolyCapacity then
begin
PolyCapacity := imax(PolyCapacity*2, PolyCount+1);
GetMem(tmp, sizeof(TPoint) * PolyCapacity);
if PolyCount > 0 then
begin
CopyMemory(tmp, Poly, sizeof(TPoint) * PolyCount);
FreeMem(Poly);
end;
Poly := tmp;
end;
Poly^[PolyCount] := Point(x, y);
inc(PolyCount);
// calc rectangle
if PolyCount = 2 then
begin
// only two points
RX1 := Poly^[0].x;
RY1 := Poly^[0].y;
RX2 := Poly^[1].x;
RY2 := Poly^[1].y;
OrdCor(RX1, RY1, RX2, RY2);
end
else
if PolyCount > 2 then
begin
// more than two points (R__ are already valid)
RX1 := imin(RX1, x);
RY1 := imin(RY1, y);
RX2 := imax(RX2, x);
RY2 := imax(RY2, y);
end;
end;
fSel := true;
end;
procedure TImageEnView.AnimPolygonEnabled(ap: pointer; Value: Boolean);
begin
with PIEAnimPoly(ap)^ do
Enabled := Value;
AniPolyFunc(self, true);
end;
// move the polygon
procedure TImageEnView.AnimPolygonMove(ap: pointer; ox, oy: integer; max_x, max_y: integer; CutSel: boolean);
var
q: integer;
{}
procedure LimitOXOY;
begin
with PIEAnimPoly(ap)^ do
begin
if (ox + RX1) < 0 then
dec(ox, (ox + RX1));
if (ox + RX2) >= max_x then
dec(ox, (ox + RX2 - max_x + 0));
if (oy + RY1) < 0 then
dec(oy, (oy + RY1));
if (oy + RY2) >= max_y then
dec(oy, (oy + RY2 - max_y + 0));
end;
end;
{}
begin
if (ox = 0) and (oy = 0) then
exit;
if fLockPaint = 0 then
Paint;
with PIEAnimPoly(ap)^ do
begin
if not CutSel then
begin
LimitOXOY;
if RX1 + ox >= max_x then
exit;
if RY1 + oy >= max_y then
exit;
if RX2 + ox <= 0 then
exit;
if RY2 + oy <= 0 then
exit;
end
else
begin
if RX1 + ox >= max_x then
LimitOXOY;
if RY1 + oy >= max_y then
LimitOXOY;
if RX2 + ox <= 0 then
LimitOXOY;
if RY2 + oy <= 0 then
LimitOXOY;
end;
RX1 := ilimit(RX1 + ox, 0, max_x);
RY1 := ilimit(RY1 + oy, 0, max_y);
RX2 := ilimit(RX2 + ox, 0, max_x);
RY2 := ilimit(RY2 + oy, 0, max_y);
for q := 0 to PolyCount - 1 do
with Poly^[q] do
if x <> IESELBREAK then
begin
x := ilimit(x + ox, 0, max_x);
y := ilimit(y + oy, 0, max_y);
end;
end;
end;
{!!
<FS>TImageEnView.AnimPolygonAddPt
<FM>Declaration<FC>
procedure AnimPolygonAddPt(ap: pointer; x, y: Integer);
<FM>Description<FN>
Adds a new point to the polygon <FC>ap<FN>.
<FC>x, y<FN> are the point coordinates (bitmap based).
!!}
// add a vertex to the polygon "ap"
// x and y are bitmap based coordinate
procedure TImageEnView.AnimPolygonAddPt(ap: pointer; x, y: integer);
begin
AnimPolygonAddPtEx(ap, x, y);
if fLockPaint = 0 then
Paint;
end;
{!!
<FS>TImageEnView.AnimPolygonNew
<FM>Declaration<FC>
function TImageEnView.AnimPolygonNew(VColor1, VColor2: TColor; VAnimated: Boolean; VSizeable: Boolean): pointer;
<FM>Description<FN>
Creates a new animated polygon.
VColor1 and VColor2 specify the two colors of animation.
Set VAnimated to True to animate the polygon
Set VSizeable to True to allow resizing of the polygon.
Returns an index of the new polygon.
!!}
// Create a new animated polygon
// return the polygon handle
function TImageEnView.AnimPolygonNew(VColor1, VColor2: TColor; VAnimated: boolean; VSizeable: boolean): pointer;
var
pp: PIEAnimPoly;
begin
getmem(pp, sizeof(TIEAnimPoly));
fAnimPoly.Add(pp);
with pp^ do
begin
PolyCount := 0;
PolyCapacity := 0;
getmem(Poly, 0);
Color1 := VColor1;
Color2 := VColor2;
Animated := VAnimated;
Sizeable := VSizeable;
ShowCenter := False;
AniFt := 0;
if not (csDesigning in ComponentState) then
Canvas := self.Canvas;
Enabled := True;
RX1 := 0;
RY1 := 0;
RX2 := 0;
RY2 := 0;
end;
if (fAnimPoly.Count = 1) and not (csDesigning in ComponentState)
{$ifndef IEDOTNETVERSION}
and (assigned(Owner) or (ParentWindow <> 0))
{$endif}
then
begin
SetupDrawPixelBitmap;
SetupAniPolyTimer;
fAnimPolyTimer.Enabled := True;
end;
pp^.DrawPixelPtr := fDrawPixelPtr;
pp^.DrawPixelBitmap := fDrawPixelBitmap;
result := pp;
end;
{!!
<FS>TImageEnView.AnimPolygonDel
<FM>Declaration<FC>
procedure AnimPolygonDel(ap: pointer);
<FM>Description<FN>
Removes the animated polygon <FC>ap<FN> (value returned from <A TImageEnView.AnimPolygonNew>).
!!}
// removes the animated polygon "ap"
procedure TImageEnView.AnimPolygonDel(ap: pointer);
begin
if ap <> nil then
begin
freemem(PIEAnimPoly(ap)^.Poly);
freemem(PIEAnimPoly(ap));
fAnimPoly.Delete(fAnimPoly.IndexOf(pointer(ap)));
if (fAnimPoly.Count = 0) and assigned(fAnimPolyTimer) then
fAnimPolyTimer.Enabled := False;
if fLockPaint = 0 then
Paint;
end;
end;
// this makes selection animated
procedure TImageEnView.TimerEvent(Sender: TObject);
begin
AniPolyFunc(Sender, true);
end;
procedure TImageEnView.DoOnDrawPolygon(polygon: Integer; point: Integer; Canvas: TCanvas; x, y: Integer);
begin
if assigned(fOnDrawPolygon) then
fOnDrawPolygon(self, polygon, point, Canvas, x, y);
end;
function TImageEnView.GetSelectionGridSize(): Integer;
begin
result := imax( fSelectionGridWidth, fSelectionGridHeight );
end;
procedure TImageEnView.SetSelectionGridSize(value: Integer);
begin
fSelectionGridWidth := value;
fSelectionGridHeight := value;
end;
procedure TImageEnView.DrawSelectionGrid(x1, y1, x2, y2: Integer);
var
i: Integer;
p: Integer;
begin
if (fSelectionGridWidth > 1) or (fSelectionGridHeight > 1) then
begin
OrdCor(x1, y1, x2, y2);
Canvas.Pen.Color := IEGlobalSettings().SelectionGridColor;
Canvas.Pen.Mode := pmCopy;
Canvas.Pen.Style := psSolid;
for i := 1 to fSelectionGridHeight-1 do
begin
// horizontal lines
p := trunc( i*(y2-y1)/fSelectionGridHeight );
Canvas.MoveTo(x1, y1 + p);
Canvas.LineTo(x2, y1 + p);
end;
for i := 1 to fSelectionGridWidth-1 do
begin
// vertical lines
p := trunc( i*(x2-x1)/fSelectionGridWidth );
Canvas.MoveTo(x1 + p, y1);
Canvas.LineTo(x1 + p, y2);
end;
end;
end;
procedure LineDDAProc(x, y: integer; self: PIEAnimPoly); stdcall;
begin
with self^ do
begin
// Due to SetPixel bug on Vista - on Window Classic theme
if Assigned(DrawPixelPtr) then
begin
if C1 < 4 then
DrawPixelPtr^ := TColor2TRGB(Color1)
else
if C1 < 7 then
DrawPixelPtr^ := TColor2TRGB(Color2)
else
begin // fC1=6
DrawPixelPtr^ := TColor2TRGB(Color2);
C1 := -1;
end;
Canvas.Draw(x, y, DrawPixelBitmap);
inc(C1);
end;
end;
end;
// Draw the selection
procedure TImageEnView.AniPolyFunc(Sender: TObject; UseLockPaint: boolean);
const
Target_Size = 22;
procedure DrawGrip(iec: TIECanvas; x1, y1, x2, y2: Integer);
begin
case fGripShape of
iegsBox:
iec.Rectangle(x1, y1, x2, y2);
iegsCircle:
iec.Ellipse(x1, y1, x2, y2);
end;
end;
var
q, p: integer;
pp: PIEAnimPoly;
x1, y1, x2, y2: integer;
lbreak: TPoint;
grx1, gry1, grx2, gry2: integer;
xanimated: boolean;
xtime: Dword;
invisible: Boolean;
iec: TIECanvas;
rxWidth, ryHeight: Integer;
begin
if (not HasParentWindow) or (not fVisibleSelection) then
exit;
xanimated := false;
for q := 0 to fAnimPoly.Count - 1 do
begin
pp := PIEAnimPoly(fAnimPoly[q]);
with pp^ do
if Enabled then
begin
if Animated then
xanimated := true;
if Animated and (not fRectSelecting) and (not fPolySelecting)
and (not fCircSelecting) and (Sender is TTimer) and (fRectResizing = ieNone) and (fSelectMoving < 0) then
begin
inc(AniFt);
if AniFt >= 10 then
AniFt := 0;
inc(fAniCounter);
end;
with Canvas.ClipRect do
invisible := (Left = 0) and (Top = 0) and (Right = 0) and (Bottom = 0);
if (not invisible) and (fLockPaint = 0) and ((Canvas.LockCount = 0) or (not UseLockPaint)) and (not fMouseMoveScrolling) then
begin
// draw grips
if Sizeable and _IsRectangle(Poly, PolyCount) and (not fRectSelecting) and (fSelectMoving = -1) then
begin
iec := TIECanvas.Create(Canvas, true, true);
with iec do
begin
Brush.Style := fGripBrushStyle;
Brush.Color := fGripColor2;
Pen.Color := fGripColor1;
grx1 := 10000000;
gry1 := grx1;
gry2 := -1000000;
grx2 := gry2;
for p := 0 to PolyCount - 1 do
begin
if Poly^[p].x <> IESELBREAK then
begin
x1 := XBmp2Scr( Poly^[p].x, True );
y1 := YBmp2Scr( Poly^[p].y, True );
DrawGrip(iec, x1 - fGripSize, y1 - fGripSize, x1 + fGripSize, y1 + fGripSize);
if x1 < grx1 then
grx1 := x1;
if y1 < gry1 then
gry1 := y1;
if x1 > grx2 then
grx2 := x1;
if y1 > gry2 then
gry2 := y1;
end;
end;
if fExtendedGrips then
begin
x1 := (grx2 + grx1) div 2;
y1 := (gry2 + gry1) div 2;
DrawGrip(iec, x1 - fGripSize, gry1 - fGripSize, x1 + fGripSize, gry1 + fGripSize);
DrawGrip(iec, grx1 - fGripSize, y1 - fGripSize, grx1 + fGripSize, y1 + fGripSize);
DrawGrip(iec, grx2 - fGripSize, y1 - fGripSize, grx2 + fGripSize, y1 + fGripSize);
DrawGrip(iec, x1 - fGripSize, gry2 - fGripSize, x1 + fGripSize, gry2 + fGripSize);
end;
end;
iec.Free;
DrawSelectionGrid(grx1, gry1, grx2, gry2);
end; // end draw grips
if (pp = fHPolySel) and (not fSelectionMask.IsEmpty) and (fRectResizing = ieNone) and (not fRectSelecting)
and (not fCircSelecting) and (not fPolySelecting) and (not fRectMoving) then
begin
// draw selection
if (not fDelayDisplaySelection) or (fStable2 = 0) then
begin
xtime := GetTickCount;
with CurrentLayer.DrawingInfo do
fSelectionMask.DrawOutline(Canvas, XDst, YDst, WidthDst, HeightDst, XSrc, YSrc, WidthSrc, HeightSrc, fAniCounter, fSelColor1, fSelColor2, @fNavigatorActualRect);
xtime := GetTickCount - xtime;
if (fDelayTimer<0) and (assigned(Owner) or (ParentWindow<>0)) then
begin
SetupAniPolyTimer;
fAnimPolyTimer.Interval := imax(210, trunc(xtime*100/(-fDelayTimer)));
end;
end;
end
else
begin
C1 := AniFt;
Canvas.pen.mode := pmCopy;
// draw user polygon or building selection
if PolyCount > 0 then
begin
if _IsRectangle(Poly, PolyCount) then
begin
// this is a rectangle
x1 := XBmp2Scr( Poly^[0].x, True );
y1 := YBmp2Scr( Poly^[0].y, True );
x2 := XBmp2Scr( Poly^[2].x, True ) - 1;
y2 := YBmp2Scr( Poly^[2].y, True ) - 1;
fNavigatorActualRect := Rect(x1, y1, x2, y2);
// 3.0.4: rectangular selection painted here to avoid grips missalignment
LineDDA(x1, y1, x2, y1, @LineDDAProc, NativeInt(pp));
DoOnDrawPolygon(q, 1, Canvas, x2, y1);
LineDDA(x2, y1, x2, y2, @LineDDAProc, NativeInt(pp));
DoOnDrawPolygon(q, 2, Canvas, x2, y2);
LineDDA(x2, y2, x1, y2, @LineDDAProc, NativeInt(pp));
DoOnDrawPolygon(q, 3, Canvas, x1, y2);
LineDDA(x1, y1, x1, y2, @LineDDAProc, NativeInt(pp));
if fRectSelecting or fRectMoving then
DrawSelectionGrid(x1, y1, x2, y2);
DoOnDrawPolygon(q, 0, Canvas, x1, y1);
end
else
begin
// this is a polygon
lbreak := Poly^[0];
p := 1;
while p < PolyCount do
begin
if Poly^[p].x = IESELBREAK then
begin
// closes sub-polygon
x1 := XBmp2Scr( Poly^[p - 1].x, True );
y1 := YBmp2Scr( Poly^[p - 1].y, True );
x2 := XBmp2Scr( lbreak.x, True );
y2 := YBmp2Scr( lbreak.y, True );
LineDDA(x1, y1, x2, y2, @LineDDAProc, NativeInt(pp));
DoOnDrawPolygon(q, p-1, Canvas, x1, y1);
inc(p);
lbreak := Poly^[p];
end
else
begin
x1 := XBmp2Scr( Poly^[p - 1].x, True );
y1 := YBmp2Scr( Poly^[p - 1].y, True );
x2 := XBmp2Scr( Poly^[p].x, True );
y2 := YBmp2Scr( Poly^[p].y, True );
LineDDA(x1, y1, x2, y2, @LineDDAProc, NativeInt(pp));
DoOnDrawPolygon(q, p-1, Canvas, x1, y1);
end;
inc(p);
end;
// closes last sub-polygon
if (not fPolySelecting) then
begin
x1 := XBmp2Scr( Poly^[PolyCount - 1].x, True );
y1 := YBmp2Scr( Poly^[PolyCount - 1].y, True );
x2 := XBmp2Scr( lbreak.x, True );
y2 := YBmp2Scr( lbreak.y, True );
LineDDA(x1, y1, x2, y2, @LineDDAProc, NativeInt(pp));
DoOnDrawPolygon(q, p-1, Canvas, x1, y1);
end;
end; // Polygon, not rectange
// Draw a small cross to show the center of the current selection
rxWidth := XBmp2Scr( rx2, True ) - XBmp2Scr( rx1, True );
ryHeight := YBmp2Scr( ry2, True ) - YBmp2Scr( ry1, True );
if ShowCenter and ( PolyCount > 1 ) and ( rxWidth > 0 ) and ( ryHeight > 0 ) then
begin
LineDDA( XBmp2Scr( rx1, True ) + rxWidth div 2 - Target_Size div 2,
YBmp2Scr( ry1, True ) + ryHeight div 2,
XBmp2Scr( rx1, True ) + rxWidth div 2 + Target_Size div 2,
YBmp2Scr( ry1, True ) + ryHeight div 2,
@LineDDAProc, NativeInt ( pp ));
LineDDA( XBmp2Scr( rx1, True ) + rxWidth div 2,
YBmp2Scr( ry1, True ) + ryHeight div 2 - Target_Size div 2,
XBmp2Scr( rx1, True ) + rxWidth div 2,
YBmp2Scr( ry1, True ) + ryHeight div 2 + Target_Size div 2,
@LineDDAProc, NativeInt( pp ));
end;
end;
end;
end;
end; // end of polygon "enabled"
end;
if (assigned(Owner) or (ParentWindow <> 0)) then
begin
SetupAniPolyTimer;
if (fStable = 0) and (fStable2 = 0) and (not xanimated) then
fAnimPolyTimer.Enabled := false
else
if (fAnimPolyTimer.Enabled = false) then
fAnimPolyTimer.Enabled := true;
end;
if fStable > 0 then
begin
dec(fStable);
if fStable = 0 then
Update;
end;
if fStable2 > 0 then
dec(fStable2);
end;
{!!
<FS>TImageEnView.GetGripAt
<FM>Declaration<FC>
function GetGripAt(x, y: Integer): <A TIEGrip>;
<FM>Description<FN>
Returns the selection grip at mouse position specified by <FC>x, y<FN> (or ieNone if a grip is not found).
!!}
function TImageEnView.GetGripAt(x, y: integer): TIEGrip;
var
p, x1, y1, x2, y2: integer;
begin
result := ieNone;
with PIEAnimPoly(fHPolySel)^ do
if Enabled and Sizeable and _IsRectangle(Poly, PolyCount) and not fRectSelecting then
begin
// try grips
for p := 0 to PolyCount - 1 do
begin
if Poly^[p].x <> IESELBREAK then
begin
x1 := XBmp2Scr( Poly^[p].x, True );
y1 := YBmp2Scr( Poly^[p].y, True );
if IEPointInRect(x, y, x1 - fGripSize, y1 - fGripSize, x1 + fGripSize, y1 + fGripSize) then
begin
result := TIEGrip(p + 1);
break;
end;
end;
end;
if result = ieNone then
begin
// try sides
x1 := XBmp2Scr( RX1, True );
y1 := YBmp2Scr( RY1, True );
x2 := XBmp2Scr( RX2, True );
y2 := YBmp2Scr( RY2, True );
if _DistPoint2Seg(x, y, x1, y1, x1, y2) < 3 then
result := ieLeftSide // left
else
if _DistPoint2Seg(x, y, x2, y1, x2, y2) < 3 then
result := ieRightSide // right
else
if _DistPoint2Seg(x, y, x1, y1, x2, y1) < 3 then
result := ieTopSide // top
else
if _DistPoint2seg(x, y, x1, y2, x2, y2) < 3 then
result := ieBottomSide; // bottom
end;
end;
end;
// Return the grip on position x,y
// ieNone if none or Shift is pressed
function TImageEnView.GetResizingGrip(x, y: integer; Shift: TShiftState): TIEGrip;
begin
if (ssShift in Shift) then
result := ieNone
else
result := GetGripAt(x, y);
end;
// return 0 if x,y is inside selection and shift is not pressed
// -1 otherwise
function TImageEnView.GetMovingGrip(x, y: integer; Shift: TShiftState): integer;
begin
result := -1;
if (not (ssShift in Shift)) and (not fSelectionMask.IsEmpty) and (not fRectSelecting) and (not fPolySelecting) and (not fCircSelecting) and
fSelectionMask.IsPointInside( XScr2Bmp( x, True ), YScr2Bmp( y, True )) and (iesoMoveable in fSelectionOptions) then
result := 0;
end;
function TImageEnView.UserInteractions_MouseDownExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean;
var
i: integer;
begin
result := false;
for i := 0 to fUserInteractions.Count - 1 do
if (fUserInteractions[i] as TIEUserInteraction).Enabled and (fUserInteractions[i] as TIEUserInteraction).MouseDownExclusive(Button, Shift, X, Y) then
begin
result := true;
break;
end;
end;
procedure TImageEnView.UserInteractions_MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
i: integer;
begin
for i := 0 to fUserInteractions.Count - 1 do
if (fUserInteractions[i] as TIEUserInteraction).Enabled then
(fUserInteractions[i] as TIEUserInteraction).MouseDown(Button, Shift, X, Y);
end;
procedure TImageEnView.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
i: integer;
inLayerX, inLayerY: integer;
bDisableNewSelections: Boolean;
bDeselect: Boolean;
{$IFNDEF OCXVERSION}
ParForm: TCustomForm;
{$ENDIF}
begin
inherited;
{$IFDEF OCXVERSION}
SetFocus;
{$ELSE}
ParForm := GetParentForm(Self);
if (ParForm<>nil) and (ParForm.Visible) and CanFocus then
SetFocus;
{$ENDIF}
if (Button = mbRight) and (ssLeft in Shift) then
// right button pressed
exit;
fLayerMoved := false;
bDisableNewSelections := (iesoDisableNewSelection in fSelectionOptions) and (Selected = True);
if fDelayZoomFilter then
fActualZoomFilter := rfNone;
fSavedSelectionBase := fSelectionBase;
fSelectionBase := iesbClientArea;
fHSX0 := ilimit(X, fOffX, fOffX + fExtX);
fHSY0 := ilimit(Y, fOffY, fOffY + fExtY);
inLayerX := ilimit( X, CurrentLayer.ClientAreaBox.Left, CurrentLayer.ClientAreaBox.Right + 1 );
inLayerY := ilimit( Y, CurrentLayer.ClientAreaBox.Top, CurrentLayer.ClientAreaBox.Bottom + 1 );
fMouseDownX := X;
fMouseDownY := Y;
fPredLX := X;
fPredLY := Y;
fPredX := inLayerX;
fPredY := inLayerY;
fHSX1 := inLayerX;
fHSY1 := inLayerY;
fHSVX1 := ViewX;
fHSVY1 := ViewY;
fMMoveX := inLayerX;
fMMoveY := inLayerY;
fStartingPolyCount := PIEAnimPoly(fHPolySel)^.PolyCount;
if ( fEditingLayer >= 0 ) and ( Button = mbLeft ) then
begin
LayersCancelEditor();
end;
if not UserInteractions_MouseDownExclusive(Button, Shift, X, Y) then
begin
if (Button = mbLeft) and ((miSelectPolygon in fMouseInteract) or (miSelectLasso in fMouseInteract)) then
begin
if loAutoFixRotation in fLayerOptions then
LayersFixRotations;
DoBeforeSelectionChange;
fSelectMoving := GetMovingGrip(inLayerX, inLayerY, Shift);
if (fSelectMoving = -1) and (bDisableNewSelections = False) then
begin
if (ssAlt in Shift) or fForceALTkey then
with PIEAnimPoly(fHPolySel)^ do
if (PolyCount > 0) and (Poly^[PolyCount - 1].x <> IESELBREAK) then
_CastPolySelCC(Poly^[PolyCount - 1].x - fViewX + fOffX, Poly^[PolyCount - 1].y - fViewY + fOffY, inLayerX, inLayerY);
if (not fPolySelecting) then
begin
if (ssShift in Shift) and (fEnableShiftKey) then
AddSelBreakEx
else
if (iesoDisableOneClickDeselect in fSelectionOptions) = False then
DeSelect;
end;
AddSelPointEx(inLayerX, inLayerY);
fPolySelecting := true;
fLassoSelecting := miSelectLasso in fMouseInteract;
DoSelectionChanging;
end;
end
else
if (Button = mbLeft) and (miSelect in fMouseInteract) then
begin
if loAutoFixRotation in fLayerOptions then
LayersFixRotations;
DoBeforeSelectionChange;
fRectResizing := GetResizingGrip(X, Y, Shift);
if fRectResizing = ieNone then
fSelectMoving := GetMovingGrip(X, Y, Shift);
if (fRectResizing = ieNone) and (fSelectMoving = -1) and (bDisableNewSelections = False) then
begin
// new rectangular selection
fRectSelecting := True;
if (fSelectionAspectRatio = 0) then
begin
// fixed selection
MouseSelectRectangle(inLayerX, inLayerY, Shift);
end;
DoSelectionChanging;
end;
if fRectResizing <> ieNone then
with PIEAnimPoly(fHPolySel)^ do
begin
flx1 := RX1;
flx2 := RX2;
fly1 := RY1;
fly2 := RY2;
end;
end
else
if (Button = mbLeft) and (miSelectZoom in fMouseInteract) then
begin
fRectSelecting := True;
DoSelectionChanging;
if miSelectZoom in fMouseInteract then
SaveSelection;
end
else
if (Button = mbLeft) and (miSelectCircle in fMouseInteract) then
begin
if loAutoFixRotation in fLayerOptions then
LayersFixRotations;
DoBeforeSelectionChange;
fSelectMoving := GetMovingGrip(inLayerX, inLayerY, Shift);
if (fSelectMoving = -1) and (bDisableNewSelections = False) then
begin
fCircSelecting := True;
ShowSelectionEx(false);
if (fSelectionAspectRatio = 0) then
begin
// fixed selection
MouseSelectCircle(inLayerX, inLayerY, Shift);
end;
DoSelectionChanging;
end;
end
else
if (Button = mbLeft) and (miSelectMagicWand in fMouseInteract) then
begin
if loAutoFixRotation in fLayerOptions then
LayersFixRotations;
DoBeforeSelectionChange;
fSelectMoving := GetMovingGrip(inLayerX, inLayerY, Shift);
end
else
if ((Button = mbLeft) or ((Button=mbRight) and (iesoRightButtonSelectLayers in fSelectionOptions)))
and (((miMoveLayers in fMouseInteract) or (miResizeLayers in fMouseInteract) or (miRotateLayers in fMouseInteract))) then
begin
fMovResRotLayerStarted := false;
if miResizeLayers in fMouseInteract then
fLayerResizing := FindLayerGripAnySel( X, Y, True )
else
fLayerResizing := ieNone;
if (fLayerResizing <> ieNone) then
begin
if loAutoFixRotation in fLayerOptions then
LayersFixRotations;
with TIELayer(fLayers[fLayersCurrent]) do
begin
fLyrResizingClientAreaBox := ClientAreaBox;
for i := 0 to 3 do
fLyrResizingRotatedBox[i] := DrawingInfo.RotatedDest[i];
end;
DoLayerNotify(fLayersCurrent, ielBeginResizing);
end;
if (fLayerResizing = ieNone) and ((miMoveLayers in fMouseInteract) or (miRotatelayers in fMouseInteract)) then
begin
if (miRotateLayers in fMouseInteract) and (FindLayerGrip(X, Y) = ieRotationCenter) then
// begin rotation center moving
fMovingRotationCenter := LayersCurrent
else
begin
if (miRotateLayers in fMouseInteract) and (LayersCurrent > -1) and CurrentLayer.SupportsFeature(ielfRotation) and
( CurrentLayer.Locked = False ) and ( not IsPointInsideLayer(X, Y, LayersCurrent )) then
begin
// start rotating current layer
fRotatingLayer := LayersCurrent;
fRotatingLayerValue := CurrentLayer.Rotate;
if fLayersFastDrawing = iefDelayed then
fStable := fDelayZoomTicks;
end
else
begin
// begin layer moving
fMovingLayer := FindLayerAt(X, Y);
// Have they double clicked a text layer?
if ( fMovingLayer > -1 ) and
( ssDouble in Shift ) and
( Layers[ fMovingLayer ].SupportsFeature( ielfTextEditing )) then
begin
fEditingLayer := fMovingLayer;
fMovingLayer := -1;
LayersActivateTextEditor( fEditingLayer );
end;
// Deselect all layers
if ( fMovingLayer = -1 ) and
( fEditingLayer = -1 ) and
( not ( ssShift in Shift )) then
begin
if LayersAllowMultiSelect then
begin
LayersDeselectAll;
DoLayerNotify(fLayersCurrent, ielDeselected);
end
else
// Select background layer
if SetLayersCurrentEx( 0, True, False ) then
DoLayerNotify( fLayersCurrent, ielSelected );
end;
// Deselect current layer?
if ( fMovingLayer > -1 ) and
( LayersAllowMultiSelect ) and
( Layers[ fMovingLayer ].fSelected ) and
( ssShift in Shift ) then
begin
Layers[ fMovingLayer ].fSelected := False;
DoLayerNotify(fMovingLayer, ielDeselected);
SelectByGroupIndex( Layers[ fMovingLayer ].GroupIndex, False, True );
SelectMaskOfLayer( fMovingLayer, False, True );
fMovingLayer := -1;
Update;
end;
if fMovingLayer > -1 then
begin
bDeselect := NOT ( Layers[ fMovingLayer ].fSelected or ( ssShift in Shift ));
if SetLayersCurrentEx( fMovingLayer, True, bDeselect ) then
DoLayerNotify(fLayersCurrent, ielSelected);
DoLayerNotify(fLayersCurrent, ielBeginMoving);
if fLayersFastDrawing = iefDelayed then
fStable := fDelayZoomTicks;
end;
if (fMovingLayer > -1) and (not TIELayer(fLayers[fLayersCurrent]).Locked) then
SetTempCursor(crIESizeAll, true);
end;
end;
end;
end;
end;
// warning, does not follow the previous if..then...else
UserInteractions_MouseDown(Button, Shift, X, Y);
// Rulers
fRulerParams.HandleMouseDown( Button, Shift, X, Y );
if (Button = mbLeft) and (miScroll in fMouseInteract) and RequiresScrollBars then
SetTempCursor(crIEHandDrag, True);
fSelectionBase := fSavedSelectionBase;
end;
// Called by MouseMoveScroll, when it is scrolled the view (ViewXY)
// of scx and scy pixels.
procedure TImageEnView.SubMouseMoveScroll(scx, scy: integer);
begin
dec(fMMoveX, scx);
dec(fMMoveY, scy);
dec(fPredX, scx);
dec(fPredY, scy);
dec(fPredLX, scx);
dec(fPredLY, scy);
end;
// calls SubMouseMoveScroll method
// scroll if the mouse is out of the component
procedure TImageEnView.MouseMoveScroll();
var
pt: Tpoint;
scx, scy, lvx, lvy: integer;
begin
if MouseChangingLayers() then
exit;
fMouseMoveScrolling := true;
try
repeat
// calc movement
GetCursorPos(pt);
pt := ScreenToClient(pt);
if pt.x < fOffX then
scx := pt.x - fOffX
else
if pt.x > (fOffX + fExtX - 1) then
scx := pt.x - (fOffX + fExtX - 1)
else
scx := 0;
if pt.y < fOffY then
scy := pt.y - fOffY
else
if pt.y > (fOffY + fExtY - 1) then
scy := pt.y - (fOffY + fExtY - 1)
else
scy := 0;
if (scx = 0) and (scy = 0) then
break;
lvx := fViewX;
lvy := fViewY;
SetViewXY(fViewX + scx, fViewY + scy);
if (lvx = fViewX) and (lvy = fViewY) then
break;
dec(lvx, fViewX);
dec(lvy, fViewY);
inc(fHSX1, lvx);
inc(fHSY1, lvy);
inc(fHSX0, lvx);
inc(fHSY0, lvy);
SubMouseMoveScroll(scx, scy);
until false;
finally
fMouseMoveScrolling := false;
end;
end;
{!!
<FS>TImageEnView.LayersRepositionAll
<FM>Declaration<FC>
procedure LayersRepositionAll(MoveX, MoveY: Integer; SelectedOnly: Boolean = False; Sizing: Boolean = False);
<FM>Description<FN>
Updates <A TIELayer.PosX> and <A TIELayer.PosY> of all layers (or just selected if <FC>SelectedOnly<FN> = True) to move them to a new position. If Sizing is true then Layer is resized (top-left position does not move).
These consts are also for <FC>MoveX<FN>:
<TABLE>
<R> <H>Const</H> <H>Description</H> </R>
<R> <C><FC>IELayer_Pos_Left<FN></C> <C>Adjust PosX so layer is aligned to the left of the background layer</C> </R>
<R> <C><FC>IELayer_Pos_HCenter<FN></C> <C>Adjust PosX so layer is aligned to the horizontal center of the background layer</C> </R>
<R> <C><FC>IELayer_Pos_Right<FN></C> <C>Adjust PosX so layer is aligned to the right of the background layer</C> </R>
</TABLE>
These consts are also <FC>MoveY<FN>:
<TABLE>
<R> <H>Const</H> <H>Description</H> </R>
<R> <C><FC>IELayer_Pos_Top<FN></C> <C>Adjust PosY so layer is aligned to the top of the background layer</C> </R>
<R> <C><FC>IELayer_Pos_VCenter<FN></C> <C>Adjust PosY so layer is aligned to the vertical center of the background layer</C> </R>
<R> <C><FC>IELayer_Pos_Bottom<FN></C> <C>Adjust PosY so layer is aligned to the bottom of the background layer</C> </R>
</TABLE>
Note: Use <A TImageEnView.LayersSizeAll> to resize layers by percentage
<FM>Examples<FC>
// Move all layers 50 pixels up and left
ImageEnView1.LayersRepositionAll( -50, -50 );
// Move selected layers 100 pixels down
ImageEnView1.LayersRepositionAll( 0, 100, true );
// Enlarge all layers vertically and horizontally by 10 pixels
ImageEnView.LayersRepositionAll( 10, 10, False, True );
// Center all layers in the middle of the image
ImageEnView1.LayersRepositionAll( IELayer_Pos_HCenter, IELayer_Pos_VCenter );
<FM>See Also<FN>
- <A TImageEnView.LayersRotateAll>
- <A TImageEnView.LayersSizeAll>
- <A TImageEnView.LayersAlign>
!!}
procedure TImageEnView.LayersRepositionAll(MoveX, MoveY: Integer; SelectedOnly: Boolean = False; Sizing: Boolean = False);
var
ALayer: TIELayer;
i: Integer;
doMoveX: Integer;
doMoveY: Integer;
begin
if ( MoveX = 0 ) and ( MoveY = 0 ) then
exit;
for i := 0 to LayersCount - 1 do
begin
// Moving ALayer
ALayer := TIELayer( fLayers[ I ]);
if ( aLayer.Locked = False ) and (( SelectedOnly = False ) or aLayer.Selected ) then
begin
doMoveX := MoveX;
doMoveY := MoveY;
if Sizing then
begin
// SIZING
// Do not allow smaller than 1
if doMoveX < -1 * aLayer.Width + 1 then
doMoveX := -1 * aLayer.Width + 1;
if doMoveY < -1 * aLayer.Height + 1 then
doMoveY := -1 * aLayer.Height + 1;
aLayer.Width := aLayer.Width + doMoveX;
aLayer.Height := aLayer.Height + doMoveY;
end
else
begin
// MOVING
aLayer.PosX := aLayer.PosX + doMoveX;
aLayer.PosY := aLayer.PosY + doMoveY;
end;
end;
end;
Update;
end;
{!!
<FS>TImageEnView.LayersRotateAll
<FM>Declaration<FC>
procedure LayersRotateAll(Value: Double; bSelectedOnly: Boolean = False; bFixRotations: Boolean = False);
<FM>Description<FN>
Rotates all layers (or just selected if <FC>bSelectedOnly<FN> = True) by the specified angle (negative or positive degrees counter-clockwise). This method updates <A TIELayer.Rotate>.
If <FC>bFixRotations<FN> = True, then <A TImageEnView.LayersFixRotations> is called to finalize the rotation of the layer and improve quality.
<FM>Examples<FC>
// Rotate all layers 45<34> clockwise
ImageEnView1.LayersRotateAll( 315 );
// Rotate selected layers 90<39> clockwise
ImageEnView1.LayersRotateAll( 270, true );
<FM>See Also<FN>
- <A TImageEnView.LayersRepositionAll>
- <A TImageEnView.LayersSizeAll>
!!}
procedure TImageEnView.LayersRotateAll(Value: Double; bSelectedOnly: Boolean = False; bFixRotations: Boolean = False);
var
ALayer: TIELayer;
i: Integer;
begin
if Proc.AutoUndo and ( loAutoUndoChangesbyCode in fLayerOptions ) then
begin
if bFixRotations then
Proc.SaveUndo( IEMsg( IEMsg_RotateLayers ), ieuObjectsAndLayers, True, IEOP_ROTATELAYER )
else
Proc.SaveUndo( IEMsg( IEMsg_RotateLayers ), ieuLayer, True, IEOP_ROTATELAYER );
end;
for i := 0 to LayersCount - 1 do
begin
// Rotating ALayer
ALayer := TIELayer( fLayers[ I ]);
if ( aLayer.Locked = False ) and (( bSelectedOnly = False ) or aLayer.Selected ) then
ALayer.Rotate := ALayer.Rotate + Value;
end;
if bFixRotations then
begin
if bSelectedOnly then
LayersFixRotations( LYR_SELECTED_LAYERS )
else
LayersFixRotations( LYR_ALL_LAYERS );
end
else
Update();
end;
{!!
<FS>TImageEnView.LayersSizeAll
<FM>Declaration<FC>
procedure LayersSizeAll(HorzSizing, VertSizing: Double; bSelectedOnly: Boolean = False; bFixSizes: Boolean = False; ScalePosition: Boolean = False);
<FM>Description<FN>
Scales the <A TIELayer.Width> and <A TIELayer.Height> values for all layers (or just selected if <FC>bSelectedOnly<FN> = True). <FC>HorzSizing<FN> and <FC>VertSizing<FN> are percentage values, so 2 will double the layer size, and 0.5 will halve it.
If <FC>bFixSizes<FN> = True, then <A TImageEnView.LayersFixSizes> is called to resample the layer to the new sizes.
If <FC>ScalePosition<FN> is True, then <A TIELayer.PosX> and <A TIELayer.PosY> are scaled to keep their position relative to other layers (best if also scaling the background layer). If False the layer is centered after scaling (best when only scaling some layers).
Note: To size by pixels rather than scale, see <A TImageEnView.LayersRepositionAll>
<FM>Examples<FC>
// Halve the size of all layers
ImageEnView1.LayersSizeAll( 0.5, 0.5, False, False, True );
// Increase the size of selected layers by 25%
ImageEnView1.LayersSizeAll( 1.25, 1.25, True );
<FM>See Also<FN>
- <A TImageEnView.LayersRepositionAll>
- <A TImageEnView.LayersRotateAll>
!!}
procedure TImageEnView.LayersSizeAll(HorzSizing, VertSizing: Double; bSelectedOnly: Boolean = False; bFixSizes: Boolean = False; ScalePosition: Boolean = False);
var
aLayer: TIELayer;
lw, lh: Double;
i: Integer;
begin
for i := 0 to LayersCount - 1 do
begin
aLayer := TIELayer( fLayers[ I ]);
if ( aLayer.Locked = False ) and (( bSelectedOnly = False ) or aLayer.Selected ) then
begin
lw := aLayer.Width;
lh := aLayer.Height;
if HorzSizing = 0 then
aLayer.WidthD := 1
else
aLayer.WidthD := dmax( 1, lw * HorzSizing );
if VertSizing = 0 then
aLayer.HeightD := 1
else
aLayer.HeightD := dmax( 1, lh * VertSizing );
if ( aLayer.WidthD > 1 ) and ( lw > 1 ) then
begin
if ScalePosition then
aLayer.PosX := Round( aLayer.PosX * HorzSizing)
else
if ( HorzSizing < 0.1 ) or ( HorzSizing > 0.1 ) then // Avoid "wild" movements when scrolling up from very small sizes
aLayer.fPosX := aLayer.fPosX + ( lw - aLayer.WidthD ) / 2;
end;
if ( aLayer.HeightD > 1 ) and ( lh > 1 ) then
begin
if ScalePosition then
aLayer.PosY := Round( aLayer.PosY * VertSizing)
else
if ( VertSizing < 0.1 ) or ( VertSizing > 0.1 ) then // Avoid "wild" movements when scrolling up from very small sizes
aLayer.fPosY := aLayer.fPosY + ( lh - aLayer.HeightD ) / 2;
end;
end;
end;
if bFixSizes then
begin
if bSelectedOnly then
LayersFixSizes( LYR_SELECTED_LAYERS )
else
LayersFixSizes( LYR_ALL_LAYERS );
end;
ImageChange();
Update();
end;
procedure TImageEnView.MouseResizeLayer(clientlx, cliently: integer; AltPressed: Boolean);
var
w, h, lw, lh: Double;
lx, ly: integer;
layer: TIELayer;
plx, ply: Integer;
lr: TIEGrip;
aspectratio: Boolean;
i: Integer;
lxp, lyp: Double; // Percentage to resize layer in each dimension
BaseX, BaseY: Double;
BaseR, BaseB: Double;
NewR, NewB: Double;
newPosX, newPosY, newWidthD, newHeightD: Double;
begin
layer := CurrentLayer;
lx := XScr2Bmp( clientlx, False );
ly := YScr2Bmp( cliently, False );
plx := XScr2Bmp( fPredLX, False );
ply := YScr2Bmp( fPredLY, False );
lr := fLayerResizing;
if layer.AspectRatioLocked then
aspectratio := True
else
case fLayersResizeAspectRatio of
iearALTKey:
aspectratio := AltPressed;
iearAlways:
aspectratio := true;
iearAlwaysOnCornerGrip:
aspectratio := (lr = ieTopLeft) or (lr = ieTopRight) or (lr = ieBottomRight) or (lr = ieBottomLeft);
iearLayerDefaultOnCornerGrip:
aspectratio := ((lr = ieTopLeft) or (lr = ieTopRight) or (lr = ieBottomRight) or (lr = ieBottomLeft)) and
(layer.PreferredAspectRatio <> 0);
else // iearDisabled (suppress warning)
aspectratio := false;
end;
if aspectratio then
case lr of
ieTopLeft: // left-top
begin
ly := YScr2Bmp( trunc( ( clientlx - fLyrResizingClientAreaBox.Left ) * fLayersResizingAR ) + fLyrResizingClientAreaBox.Top, False );
end;
ieTopRight: // right-top
begin
ly := YScr2Bmp( trunc( ( -clientlx + fLyrResizingClientAreaBox.Right ) * fLayersResizingAR ) + fLyrResizingClientAreaBox.Top, False );
end;
ieBottomRight: // right-bottom
begin
ly := YScr2Bmp( trunc( ( clientlx - fLyrResizingClientAreaBox.Left ) * fLayersResizingAR ) + fLyrResizingClientAreaBox.Top, False );
end;
ieBottomLeft: // left-bottom
begin
ly := YScr2Bmp( trunc( ( -clientlx + fLyrResizingClientAreaBox.Left ) * fLayersResizingAR ) + fLyrResizingClientAreaBox.Bottom, False );
end;
ieLeftSide: // left side
begin
ly := YScr2Bmp( trunc( ( -clientlx + fLyrResizingClientAreaBox.Left ) * fLayersResizingAR ) + fLyrResizingClientAreaBox.Bottom, False );
lr := ieBottomLeft;
end;
ieRightSide: // right side
begin
ly := YScr2Bmp( trunc( ( clientlx - fLyrResizingClientAreaBox.Left ) * fLayersResizingAR ) + fLyrResizingClientAreaBox.Top, False );
lr := ieBottomRight;
end;
ieTopSide: // top side
begin
lx := XScr2Bmp( trunc( ( cliently - fLyrResizingClientAreaBox.Top ) / fLayersResizingAR ) + fLyrResizingClientAreaBox.Left, False );
lr := ieTopLeft;
end;
ieBottomSide: // bottom side
begin
lx := XScr2Bmp( trunc( ( cliently - fLyrResizingClientAreaBox.Top ) / fLayersResizingAR ) + fLyrResizingClientAreaBox.Left, False );
lr := ieBottomRight;
end;
else
end;
w := layer.Width;
h := layer.Height;
BaseX := layer.fPosX;
BaseR := layer.fPosX + w;
BaseY := layer.fPosY;
BaseB := layer.fPosY + h;
// Get resize percentage - Horizontal
lxp := 1;
try
case lr of
ieTopLeft : lxp := w / ( w + ( layer.fPosX - lx ));
ieTopRight : lxp := w / ( lx - layer.fPosX );
ieBottomRight : lxp := w / ( lx - XScr2Bmp( fLyrResizingClientAreaBox.Left, False ));
ieBottomLeft : lxp := w / ( w + ( layer.fPosX - lx ));
ieLeftSide : lxp := w / ( w - ( lx - plx ));
ieRightSide : lxp := w / ( w - ( -lx + plx ));
end;
except
lxp := 1;
end;
// Get resize percentage - Vertical
lyp := 1;
try
case lr of
ieTopLeft : lyp := h / ( h + ( layer.fPosY - ly ));
ieTopRight : lyp := h / ( h + ( layer.fPosY - ly ));
ieBottomRight : lyp := h / ( ly - YScr2Bmp( fLyrResizingClientAreaBox.Top, False ));
ieBottomLeft : lyp := h / ( ly - YScr2Bmp( fLyrResizingClientAreaBox.Top, False ));
ieTopSide : lyp := h / ( h + ( layer.fPosY - ly ));
ieBottomSide : lyp := h / ( h - ( -ly + ply ));
end;
except
lyp := 1;
end;
for i := 0 to LayersCount - 1 do
begin
Layer := TIELayer( fLayers[ I ]);
if ( Layer.Locked = False ) and Layer.Selected then
begin
lw := layer.Width;
lh := layer.Height;
if lxp = 0 then
newWidthD := 1
else
newWidthD := dmax( 1, lw / lxp );
if lyp = 0 then
newHeightD := 1
else
newHeightD := dmax( 1, lh / lyp );
newPosX := layer.fPosX;
newPosY := layer.fPosY;
if ( newWidthD > 1 ) and ( lw > 1 ) and (( lxp < 0.1 ) or ( lxp > 0.1 )) then // Avoid "wild" movements when scrolling up from very small sizes
if lr in [ ieTopLeft, ieLeftSide, ieBottomLeft ] then
begin
NewR := BaseR - (( BaseR - ( newPosX + lw )) / lxp );
newPosX := NewR - newWidthD;
end
else
begin
newPosX := BaseX + (( newPosX - BaseX ) / lxp );
end;
if ( newHeightD > 1 ) and ( lh > 1 ) and (( lyp < 0.1 ) or ( lyp > 0.1 )) then // Avoid "wild" movements when scrolling up from very small sizes
if lr in [ ieTopLeft, ieTopSide, ieTopRight ] then
begin
NewB := BaseB - (( BaseB - ( newPosY + lh )) / lyp );
newPosY := NewB - newHeightD;
end
else
begin
newPosY := BaseY + (( newPosY - BaseY ) / lyp );
end;
if assigned( fOnLayerMoveSize ) then
fOnLayerMoveSize( Self, i, ielResizing, newPosX, newPosY, newWidthD, newHeightD );
layer.fPosX := newPosX;
layer.fPosY := newPosY;
layer.WidthD := newWidthD;
layer.HeightD := newHeightD;
if not aspectratio then
fLayersResizingAR := layer.Height / layer.Width;
end;
end;
ImageChange();
end;
procedure TImageEnView.MouseSelectCircle(x, y: integer; Shift: TShiftState);
var
dx, dy, p, g: integer;
xx, yy: integer;
begin
MouseMoveScroll;
if (fSelectionAspectRatio = 0) then
begin
// fixed selection
dx := round(fSelectionAbsWidth * fZoomD100X) div 2;
dy := round(fSelectionAbsHeight * fZoomD100Y) div 2;
fHSX1 := x;
fHSY1 := y;
x := x + dx;
y := y + dy;
end;
if (ssShift in Shift) and (fEnableShiftKey) then
begin
if fStartingPolyCount <> PIEAnimPoly(fHPolySel)^.PolyCount then
AnimPolygonRemoveLast(fHPolySel);
AddSelBreakEx;
end
else
AnimPolygonClear(fHPolySel);
if ((ssAlt in Shift) or fForceALTkey) and (fSelectionAspectRatio = -1) then
begin
// circle (pressing ALT)
dx := imax(abs(fHSX1 - X), abs(fHSY1 - Y));
dy := dx;
end
else
if fSelectionAspectRatio>0 then
begin
dx := imax(abs(fHSX1 - X), abs(fHSY1 - Y));
dy := round(dx * fSelectionAspectRatio);
end
else
begin
dx := abs(fHSX1 - X);
dy := abs(fHSY1 - Y);
end;
dx := trunc(dx * f100DZoomX);
dy := trunc(dy * f100DZoomY);
p := trunc(2 * pi * imin(dx, dy));
if p < 50 then
p := 50;
for g := 0 to p - 1 do
begin
xx := round( XScr2Bmp( fHSX1, True ) + cos((2 * pi / p) * g) * dx);
yy := round( YScr2Bmp( fHSY1, True ) + sin((2 * pi / p) * g) * dy);
xx := imax(0, imin(fIEBitmap_Width, xx));
yy := imax(0, imin(fIEBitmap_Height, yy));
AnimPolygonAddPtEx(fHPolySel, xx, yy);
end;
AniPolyFunc(self, true);
DoSelectionChanging;
end;
procedure TImageEnView.MouseSelectRectangle(x, y: integer; Shift: TShiftState);
var
dx, dy: integer;
begin
MouseMoveScroll;
if (fSelectionAspectRatio = 0) then
begin
dx := round(fSelectionAbsWidth * fZoomD100X) div 2;
dy := round(fSelectionAbsHeight * fZoomD100Y) div 2;
fHSx1 := x - dx;
fHsy1 := y - dy;
x := x + dx - 1;
y := y + dy - 1;
end;
if (ssShift in Shift) and (fEnableShiftKey) then
begin
if fStartingPolyCount <> PIEAnimPoly(fHPolySel)^.PolyCount then
AnimPolygonRemoveLast(fHPolySel);
SelectEx(fHSx1, fHSy1, x, y, iespAdd, (ssAlt in Shift) or fForceALTkey, not(iesoSizeable in fSelectionOptions) )
end
else
SelectEx(fHSx1, fHSy1, x, y, iespReplace, (ssAlt in Shift) or fForceALTkey, not(iesoSizeable in fSelectionOptions) );
DoSelectionChanging;
end;
function TImageEnView.UserInteractions_MouseMoveExclusive(Shift: TShiftState; X, Y: Integer; Captured: boolean): boolean;
var
i: integer;
begin
result := false;
for i := 0 to fUserInteractions.Count - 1 do
if (fUserInteractions[i] as TIEUserInteraction).Enabled and (fUserInteractions[i] as TIEUserInteraction).MouseMoveExclusive(Shift, X, Y, Captured) then
begin
result := true;
break;
end;
end;
procedure TImageEnView.UserInteractions_MouseMove(Shift: TShiftState; X, Y: Integer; Captured: boolean);
var
i: integer;
begin
for i := 0 to fUserInteractions.Count - 1 do
if (fUserInteractions[i] as TIEUserInteraction).Enabled then
(fUserInteractions[i] as TIEUserInteraction).MouseMove(Shift, X, Y, Captured);
end;
procedure TImageEnView.MouseMove(Shift: TShiftState; X, Y: Integer);
procedure _MoveSelLayers(iMoveX, iMoveY: Integer);
var
aLayer: TIELayer;
i: Integer;
newPosX, newPosY, newWidthD, newHeightD: Double;
begin
if ( iMoveX = 0 ) and ( iMoveY = 0 ) then
exit;
for i := 0 to LayersCount - 1 do
begin
// Moving aLayer
aLayer := TIELayer( fLayers[ I ]);
if ( aLayer.Locked = False ) and aLayer.Selected then
begin
newPosX := aLayer.PosX - iMoveX;
newPosY := aLayer.PosY - iMoveY;
newWidthD := aLayer.WidthD;
newHeightD := aLayer.HeightD;
if assigned( fOnLayerMoveSize ) then
fOnLayerMoveSize( Self, i, ielMoving, newPosX, newPosY, newWidthD, newHeightD );
aLayer.fPosX := newPosX;
aLayer.fPosY := newPosY;
aLayer.WidthD := newWidthD;
aLayer.HeightD := newHeightD;
end;
end;
ImageChange();
end;
procedure _RotateSelLayers(Value: Double; bSnapToStep: Boolean);
var
aLayer: TIELayer;
i: Integer;
begin
for i := 0 to LayersCount - 1 do
begin
// Rotating aLayer
aLayer := TIELayer( fLayers[ I ]);
if ( aLayer.Locked = False ) and ( aLayer.Selected or ( i = fRotatingLayer )) then
begin
if bSnapToStep then
aLayer.Rotate := trunc( Value / fLayersRotateStep ) * fLayersRotateStep
else
aLayer.Rotate := Value;
end;
end;
end;
var
inLayerX, inLayerY: integer;
grip: TIEGrip;
ii: integer;
layer: TIELayer;
cx, cy: Integer;
dx, dy: Double;
max_x, max_y: Integer;
ox, oy: Integer;
begin
inherited;
fSavedSelectionBase := fSelectionBase;
fSelectionBase := iesbClientArea;
inLayerX := ilimit( X, CurrentLayer.ClientAreaBox.Left, CurrentLayer.ClientAreaBox.Right + 1 );
inLayerY := ilimit( Y, CurrentLayer.ClientAreaBox.Top, CurrentLayer.ClientAreaBox.Bottom + 1 );
if not UserInteractions_MouseMoveExclusive(Shift, X, Y, MouseCapture) then
begin
if MouseCapture then
begin
// inside Mouse Capture
if miScroll in fMouseInteract then
begin
// panning
SetViewXY(fHSVX1 - trunc((X - fMouseDownX)*fMouseScrollRate), fHSVY1 - trunc((Y - fMouseDownY)*fMouseScrollRate) ); // 3.0.2
end
else
if fRectResizing <> ieNone then
begin
// resize rectangular selection
MouseMoveScroll;
fRectResizing := SelectResizeEx(fRectResizing, inLayerX, inLayerY, (ssAlt in Shift) or fForceALTkey or (fSelectionAspectRatio>0));
DoSelectionChanging;
end
else
if fSelectMoving > -1 then
begin
// move selection
if iesoCanScroll in fSelectionOptions then
MouseMoveScroll;
fSelectMoving := SelectMoveEx(fSelectMoving,
XScr2Bmp( inLayerX, True ) - XScr2Bmp( fPredX, True ),
YScr2Bmp( inLayerY, True ) - YScr2Bmp( fPredY, True ),
iesoCutBorders in fSelectionOptions);
DoSelectionChanging;
end
else
if fRectSelecting then
begin
// select rectangle
MouseSelectRectangle(inLayerX, inLayerY, Shift);
end
else
if fPolySelecting then
begin
// select polygon
MouseMoveScroll;
Paint;
PolyDraw1;
AnimPolygonAddPtEx( fHPolySel, XScr2Bmp( inLayerX, True ), YScr2Bmp( inLayerY, True ));
AniPolyFunc(self, true);
DoSelectionChanging;
end
else
if fCircSelecting then
begin
// select circle
MouseSelectCircle(inLayerX, inLayerY, Shift);
end
else
if (fMovingLayer > -1) then
begin
// Moving a layer
layer := TIELayer(fLayers[fMovingLayer]);
if not layer.Locked then
begin
if not fMovResRotLayerStarted and Proc.AutoUndo and ( loAutoUndoChangesByUser in fLayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_MoveLayers ), ieuLayer, True, IEOP_MOVELAYER );
fMovResRotLayerStarted := true;
MouseMoveScroll;
ox := Layer.PosX + ( XScr2Bmp(X, False ) - XScr2Bmp(fPredLX, False ));
oy := Layer.PosY + ( YScr2Bmp(Y, False ) - YScr2Bmp(fPredLY, False ));
fLayerMoved := ( ox <> layer.PosX ) or ( oy <> layer.PosY ) or fLayerMoved;
_MoveSelLayers( layer.PosX - ox, layer.PosY - oy );
SetInteractionHint('X=' + IntToStr( Layer.PosX ) + ' Y=' + IntToStr( Layer.PosY ), X, Y, 'X=000 Y=000');
fUpdate_MaskCache := fMovingLayer;
if fLayersFastDrawing = iefDelayed then
fStable := fDelayZoomTicks;
Update;
if fLayerMoved then
DoLayerNotify(fLayersCurrent, ielMoving);
end;
end
else
if fMovingRotationCenter > -1 then
begin
// moving rotation center
layer := TIELayer(fLayers[fMovingRotationCenter]);
layer.RotateCenterX := layer.ConvXScr2Bmp(X) / layer.OriginalWidth;
layer.RotateCenterY := layer.ConvYScr2Bmp(Y) / layer.OriginalHeight;
SetInteractionHint('X='+IntToStr(trunc(layer.RotateCenterX*layer.Width)) + ' Y='+IntToStr(trunc(layer.RotateCenterY*layer.Height)), X, Y, 'X=000 Y=000');
fUpdate_MaskCache := fMovingRotationCenter;
if fLayersFastDrawing = iefDelayed then
fStable := fDelayZoomTicks;
Update;
end
else
if (fRotatingLayer > -1) then
begin
// Rotating a layer
layer := TIELayer(fLayers[fRotatingLayer]);
if not layer.Locked then
begin
if not fMovResRotLayerStarted and Proc.AutoUndo and ( loAutoUndoChangesByUser in fLayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_RotateLayers ), ieuFullLayer, True, IEOP_ROTATELAYER );
fMovResRotLayerStarted := true;
dx := ((XScr2Bmp( X, False ) - XScr2Bmp( fPredLX, False ))) / 4;
dy := ((YScr2Bmp( Y, False ) - YScr2Bmp( fPredLY, False ))) / 4;
cx := XBmp2Scr( trunc( CurrentLayer.RotateCenterX * CurrentLayer.OriginalWidth ), True );
cy := YBmp2Scr( trunc( CurrentLayer.RotateCenterY * CurrentLayer.OriginalHeight ), True );
if X > cx then
dy := -dy;
if Y < cy then
dx := -dx;
fRotatingLayerValue := fRotatingLayerValue + dx + dy;
_RotateSelLayers( fRotatingLayerValue, ssShift in Shift );
SetInteractionHint(IEFloatToStrS(layer.Rotate)+'<27>', X, Y, '0000<30>');
fUpdate_MaskCache := fRotatingLayer;
if fLayersFastDrawing = iefDelayed then
fStable := fDelayZoomTicks;
Update;
DoLayerNotify(fLayersCurrent, ielRotating);
end;
end
else
if (fLayerResizing <> ieNone) and (not CurrentLayer.Locked) then
begin
// Resizing a layer
if not fMovResRotLayerStarted and Proc.AutoUndo and ( loAutoUndoChangesByUser in fLayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_ResizeLayers ), ieuLayer, True, IEOP_RESIZELAYER );
if not fMovResRotLayerStarted then
fLayersResizingAR := CurrentLayer.Height / CurrentLayer.Width;
fMovResRotLayerStarted := true;
MouseMoveScroll;
MouseResizeLayer(X, Y, (ssAlt in Shift) or fForceALTkey);
SetInteractionHint(IntToStr(CurrentLayer.Width)+' x '+IntToStr(CurrentLayer.Height), X, Y, '0000 x 0000');
fUpdate_MaskCache := fLayersCurrent;
if fLayersFastDrawing = iefDelayed then
fStable := fDelayZoomTicks;
Update;
DoLayerNotify(fLayersCurrent, ielResizing);
end;
// out of Mouse Capture
end
else
if fPolySelecting and not (fLassoSelecting) then
begin
PolyDraw1;
if (ssAlt in Shift) or fForceALTkey then
with PIEAnimPoly(fHPolySel)^ do
if (PolyCount > 0) and (Poly^[PolyCount - 1].x <> IESELBREAK) then
_CastPolySelCC(Poly^[PolyCount - 1].x - fViewX + fOffX, Poly^[PolyCount - 1].y - fViewY + fOffY, inLayerX, inLayerY);
fMMoveX := inLayerX;
fMMoveY := inLayerY;
PolyDraw1;
DoSelectionChanging;
end
else
if (miSelect in fMouseInteract) or (miSelectPolygon in fMouseInteract) or
(miSelectCircle in fMouseInteract) or
(miSelectMagicWand in fMouseInteract) or (miSelectLasso in fMouseInteract) then
begin
grip := GetResizingGrip(X, Y, Shift);
DoMouseInResizingGrip(grip);
if grip = ieNone then
begin
ii := GetMovingGrip(X, Y, Shift);
if ii > -1 then
// moving cursor
SetTempCursor(crIESizeAll)
else
begin
// default cursor
if (ssShift in Shift) and fEnableShiftKey then
begin
case cursor of
crIECrossSight : SetTempCursor(crIECrossSightPlus);
crIEThickCross : SetTempCursor(crIEThickCrossPlus);
end;
end
else
RestoreCursor;
end;
end
else
begin
// resizing cursors
case grip of
ieTopLeft, ieBottomRight: SetTempCursor(crIESizeNWSE);
ieTopRight, ieBottomLeft: SetTempCursor(crIESizeNESW);
ieLeftSide, ieRightSide: SetTempCursor(crIESizeWE);
ieTopSide, ieBottomSide: SetTempCursor(crIESizeNS);
end;
end;
end
else
if (miRotateLayers in fMouseInteract) then
begin
// layers rotation. Rotate only the currently selected layer.
if IsPointInsideLayer(X, Y, fLayersCurrent) then
RestoreCursor
else
if ( fLayersCurrent > -1 ) and CurrentLayer.SupportsFeature( ielfRotation ) and ( CurrentLayer.Locked = False ) then
begin
cx := XBmp2Scr( trunc(CurrentLayer.RotateCenterX * CurrentLayer.OriginalWidth), True );
cy := YBmp2Scr( trunc(CurrentLayer.RotateCenterY * CurrentLayer.OriginalHeight), True );
if (X < cx) and (Y < cy) then
SetTempCursor(crIERotateSE)
else
if (X < cx) and (Y > cy) then
SetTempCursor(crIERotateNE)
else
if (X > cx) and (Y < cy) then
SetTempCursor(crIERotateSW)
else
if (X > cx) and (Y > cy) then
SetTempCursor(crIERotateNW);
end;
end
else
if ((miResizeLayers in fMouseInteract) or (miMoveLayers in fMouseInteract)) then
begin
grip := ieNone;
if (miResizeLayers in fMouseInteract) then
begin
if LayersAllowMultiSelect then
grip := FindLayerGripAnySel(X, Y)
else
if TIELayer(fLayers[fLayersCurrent]).Locked = False then
grip := FindLayerGrip(X, Y);
end;
if grip = ieNone then
begin
ii := FindLayerAt(X, Y);
if (ii > -1) and (not TIELayer(fLayers[ii]).Locked) and (miMoveLayers in fMouseInteract) then
// moving cursor
SetTempCursor(crIESizeAll)
else
// default cursor
RestoreCursor;
end
else
begin
// resizing cursors
case grip of
ieTopLeft, ieBottomRight : SetTempCursor(crIESizeNWSE);
ieTopRight, ieBottomLeft : SetTempCursor(crIESizeNESW);
ieLeftSide, ieRightSide : SetTempCursor(crIESizeWE);
ieTopSide, ieBottomSide : SetTempCursor(crIESizeNS);
end;
end;
end;
end;
UserInteractions_MouseMove(Shift, X, Y, MouseCapture);
// Rulers
if fRulerParams.HandleMouseMove( Shift, X, Y ) then
SetTempCursor( crDefault );
if (miMovingScroll in fMouseInteract) and ((fPredLx <> X) or (fPredLy <> Y)) and not MouseCapture then
begin
GetMaxViewXY(max_x, max_y);
cx := trunc( (imax(imin(ClientWidth -1, X), 0)/(ClientWidth )-0.02) * 1.05 * (max_x) );
cy := trunc( (imax(imin(ClientHeight-1, Y), 0)/(ClientHeight)-0.02) * 1.05 * (max_y) );
SetViewXYSmooth( cx, cy );
end;
if fSelectionMask.IsPointInside( XScr2Bmp( inLayerX, True ), YScr2Bmp( inLayerY, True )) then
DoMouseInSel;
fMMoveX := inLayerX;
fMMoveY := inLayerY;
fSelectionBase := fSavedSelectionBase;
fPredX := inLayerX;
fPredY := inLayerY;
fPredLx := X;
fPredLy := Y;
end;
{!!
<FS>TImageEnView.IsPointInsideSelection
<FM>Declaration<FC>
function IsPointInsideSelection(x, y: Integer): Boolean;
<FM>Description<FN>
IsPointInsideSelection returns true if the point specified with <FC>x, y<FN> is inside the current selection.
If <A TImageEnView.SelectionBase> is <FC>iesbClientArea<FN> (default) all coordinates depend on the zoom and image view (scrolling).
!!}
function TImageEnView.IsPointInsideSelection(x, y: integer): boolean;
begin
if fSelectionBase = iesbClientArea then
begin
x := XScr2Bmp( x, True );
y := YScr2Bmp( y, True );
end;
result := fSelectionMask.IsPointInside(x, y);
end;
function TImageEnView.UserInteractions_MouseUpExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean;
var
i: integer;
begin
result := false;
for i := 0 to fUserInteractions.Count - 1 do
if (fUserInteractions[i] as TIEUserInteraction).Enabled and (fUserInteractions[i] as TIEUserInteraction).MouseUpExclusive(Button, Shift, X, Y) then
begin
result := true;
break;
end;
end;
procedure TImageEnView.UserInteractions_MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
i: integer;
begin
for i := 0 to fUserInteractions.Count - 1 do
if (fUserInteractions[i] as TIEUserInteraction).Enabled then
(fUserInteractions[i] as TIEUserInteraction).MouseUp(Button, Shift, X, Y);
end;
procedure TImageEnView.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
const
NUMSCALES = 8;
scales: array[1..NUMSCALES] of integer = (2, 5, 10, 25, 33, 50, 75, 100);
Poly_Select_Near_Region = 3; // how close the end point must be near the first point of a poly selection to automatically close it
var
w, sl: integer;
d: double;
inLayerX, inLayerY: integer;
callselectionchange: boolean;
bDeselect: Boolean;
begin
inherited;
fInteractionHint := '';
callselectionchange := false;
inLayerX := ilimit( x, CurrentLayer.ClientAreaBox.Left, CurrentLayer.ClientAreaBox.Right + 1 );
inLayerY := ilimit( y, CurrentLayer.ClientAreaBox.Top, CurrentLayer.ClientAreaBox.Bottom + 1 );
fSavedSelectionBase := fSelectionBase;
fSelectionBase := iesbClientArea;
d := 0;
if not UserInteractions_MouseUpExclusive(Button, Shift, X, Y) then
begin
if (miZoom in fMouseInteract) and (fHSx1 = x) and (fHSy1 = y) and not IsEmpty then
begin
if Button = mbLeft then
begin
if (miScroll in fMouseInteract) then
RestoreCursor;
// zoom-in (MOUSE LEFT)
if Zoom < 100 then
begin
for w := 1 to NUMSCALES do
if scales[w] > Zoom then
begin
d := scales[w];
break;
end;
end
else
d := Zoom + 100;
DoZoomIn(d);
if (d <> fZoomX) or (d<>fZoomY) then
ZoomAt(x, y, d, false)
end
else
if (Button = mbRight) then
begin
// zoom-out (MOUSE RIGHT)
if Zoom < 200 then
begin
d := 2;
for w := NUMSCALES downto 1 do
if scales[w] < Zoom then
begin
d := scales[w];
break;
end;
end
else
d := Zoom - 100;
DoZoomOut(d);
if (d <> fZoomX) or (d<>fZoomY) then
ZoomAt(x, y, d, false);
end;
fPolySelecting := false;
end
else
if (Button = mbLeft) and (miScroll in fMouseInteract) and RequiresScrollBars then
begin
// set "view", free by MouseCapture
SetViewXY(fHSVX1 - trunc((x - fMouseDownX)*fMouseScrollRate), fHSVY1 - trunc((y - fMouseDownY)*fMouseScrollRate)); // 3.0.2
RestoreCursor;
end
else
if (Button = mbLeft) and (miSelectZoom in fMouseInteract) then
begin
ZoomSelectionEx(fZoomSelectionAspectRatio, true);
fRectSelecting := false;
RestoreSelection(true, iersMoveToAdapt);
end
else
if (Button = mbLeft) and (miSelect in fMouseInteract) then
begin
if (fHSX1 = inLayerX) and (fHSY1 = inLayerY) and (fRectResizing = ieNone) and (fSelectMoving = -1) then
begin
if fSel and ((iesoDisableOneClickDeselect in fSelectionOptions) = False) then
begin
DeSelect;
callselectionchange := true;
end;
end
else
begin
if fRectSelecting or (fRectResizing <> ieNone) then
begin
if (not (ssShift in Shift)) or (not fEnableShiftKey) then
fSelectionMask.Empty;
EndSelect;
end;
fRectSelecting := false;
fRectResizing := ieNone;
fSelectMoving := -1;
callselectionchange := true;
if fIsNavigator then
invalidate
else
begin
UpdateReason := ieurSelectionChanged;
Update;
end;
end;
end
else
if (Button = mbLeft) and (miSelectCircle in fMouseInteract) then
begin
if (fHSX1 = inLayerX) and (fHSY1 = inLayerY) and (fSelectMoving = -1) and (fSelectionAspectRatio <> 0) then
begin
if (iesoDisableOneClickDeselect in fSelectionOptions) = False then
DeSelect
end
else
begin
if fCircSelecting then
begin
if (not (ssShift in Shift)) or (not fEnableShiftKey) then
fSelectionMask.Empty;
EndSelect;
end;
fCircSelecting := False;
fSelectMoving := -1;
callselectionchange := true;
UpdateReason := ieurSelectionChanged;
Update;
end;
end
else
if (Button = mbLeft) and (miSelectLasso in fMouseInteract) then
begin
if (fHSX1 = inLayerX) and (fHSY1 = inLayerY) and (fSelectMoving = -1) then
begin
if (iesoDisableOneClickDeselect in fSelectionOptions) = False then
DeSelect
end
else
begin
if fLassoSelecting then
begin
if (not (ssShift in Shift)) or (not fEnableShiftKey) then
fSelectionMask.Empty;
EndSelect;
end;
fLassoSelecting := false;
fPolySelecting := false;
fSelectMoving := -1;
callselectionchange := true;
UpdateReason := ieurSelectionChanged;
Update;
end;
end
else
if (Button = mbLeft) and (miSelectPolygon in fMouseInteract) then
begin
if (fSelectMoving > -1) then
begin
fSelectMoving := -1;
callselectionchange := true;
end;
UpdateReason := ieurSelectionChanged;
Update;
end
else
if (Button = mbLeft) and (miSelectMagicWand in fMouseInteract) then
begin
if fSelectMoving > -1 then
begin
fSelectMoving := -1;
callselectionchange := true;
end
else
if NOT ((iesoDisableNewSelection in fSelectionOptions) and Selected) then
begin
SetTempCursor(crHourGlass);
if (ssShift in Shift) and fEnableShiftKey then
SelectMagicWand(x, y, iespAdd)
else
SelectMagicWand(x, y, iespReplace);
RestoreCursor;
callselectionchange := true;
end;
UpdateReason := ieurSelectionChanged;
Update;
end;
end;
UserInteractions_MouseUp(Button, Shift, X, Y);
// Rulers
fRulerParams.HandleMouseUp( Button, Shift, X, Y );
if Button = mbLeft then
begin
if fRectMoving then
begin
fSelectionMask.Empty;
EndSelect;
fRectMoving := false;
end;
if fMovingLayer > -1 then
begin
fMovingLayer := -1;
RestoreCursor;
if fLayerMoved then
DoLayerNotify(fLayersCurrent, ielMoved);
fLayerMoved := false;
If SyncLayers() and Center then
Update()
else
invalidate;
end;
if fRotatingLayer > -1 then
begin
fRotatingLayer := -1;
if (fHSX1 <> inLayerX) or (fHSY1 <> inLayerY) then
DoLayerNotify(fLayersCurrent, ielRotated);
If SyncLayers() and Center then
Update()
else
invalidate;
end
else
if (fMovingRotationCenter > -1) then
begin
fMovingRotationCenter := -1;
end;
if (miRotateLayers in fMouseInteract) and (fHSX1 = inLayerX) and (fHSY1 = inLayerY) then
begin
// just select a layer
sl := FindLayerAt(X, Y);
// Deselect current layer?
if ( sl > -1 ) and
( LayersAllowMultiSelect ) and
( Layers[ sl ].fSelected ) and
( ssShift in Shift ) then
begin
Layers[ sl ].fSelected := False;
DoLayerNotify( sl, ielDeselected );
SelectByGroupIndex( Layers[ sl ].GroupIndex, False, True );
SelectMaskOfLayer( sl, False, True );
sl := -1;
Update;
end;
if sl > -1 then
begin
bDeselect := NOT ( Layers[ sl ].fSelected or ( ssShift in Shift ));
if SetLayersCurrentEx( sl, True, bDeselect ) then
DoLayerNotify(fLayersCurrent, ielSelected);
end;
end;
if fLayerResizing <> ieNone then
begin
fLayerResizing := ieNone;
if (fHSX1 <> inLayerX) or (fHSY1 <> inLayerY) then
DoLayerNotify(fLayersCurrent, ielResized);
if ( Layers[ fLayersCurrent ] is TIELineLayer ) and
( TIELineLayer( Layers[ fLayersCurrent ] ).AutoSize ) then
TIELineLayer( Layers[ fLayersCurrent ] ).SizeToFit();
SyncLayers();
Update; // needed to update magnify-resized layer
end;
fSelectMoving := -1;
fRectResizing := ieNone;
fRectSelecting := false;
fCircSelecting := false;
fLassoSelecting := false;
if callselectionchange then
DoSelectionChange;
end;
if ( Button = mbRight ) and ( iesoRightButtonTerminatePolySelect in fSelectionOptions ) then
TerminatePolySelection( ssShift in Shift, false )
else
if ( Button = mbLeft ) and ( iesoAutoTerminatePolySelect in fSelectionOptions ) and
( GetPolySelCount > 1 ) and
( Abs( XScr2Bmp( X, True ) - GetPolySel( 0 ).X ) < Poly_Select_Near_Region ) and
( Abs( YScr2Bmp( Y, True ) - GetPolySel( 0 ).Y ) < Poly_Select_Near_Region ) then
TerminatePolySelection( ssShift in Shift, false );
if fZoomFilter <> fActualZoomFilter then
begin
fActualZoomFilter := fZoomFilter;
Update;
end;
fSelectionBase := fSavedSelectionBase;
end;
// Called whenever the zoom or viewx/y changes.
// It calls fOnViewChange
procedure TImageEnView.ViewChange(c: integer);
begin
fRulerParams.Update( False );
UpdateTextEditor();
if assigned(fOnViewChange) then
fOnViewChange(self, c);
if assigned(fNavigator) then
SetNavigatorRect;
end;
procedure TImageEnView.ViewChanging(c: integer; newValue: double);
begin
if assigned(fOnViewChanging) then
fOnViewChanging(self, c, newValue);
end;
// draw background on Width and Height area
procedure IEDrawBackground(ComponentState: TComponentState; Canvas: TCanvas; Bitmap: TBitmap; fBackgroundStyle: TIEBackgroundStyle; fBackground: TColor; DestX, DestY, Width, Height: integer; x1, y1, x2, y2: integer; fChessboardSize: integer; fChessboardBrushStyle: TBrushStyle; fChessboardColor2Customized: Boolean; fGradientEndColor: TColor; Wallpaper: TPicture; WallpaperStyle: TIEWallpaperStyle; LiveBackground: TIEBitmap);
const
BS: array[iebsSolid..iebsDiagCross] of TBrushStyle = (bsSolid, bsHorizontal,
bsVertical, bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross);
var
x, y, i1, i2: integer;
b1, b2: boolean;
rc: TRect;
px: PRGB;
c: array [0..1] of TRGB;
hh: Integer;
begin
with Canvas do
case fBackgroundStyle of
iebsGradient:
IEDrawGradient(Rect(DestX, DestY, width + DestX, height + DestY), Canvas.handle, fBackground, fGradientEndColor, true);
iebsSolid..iebsDiagCross:
begin
Brush.Color := fBackground;
Brush.Style := BS[fBackgroundStyle];
FillRect(rect(DestX, DestY, Width + DestX, Height + DestY));
end;
iebsChessBoard, iebsPhotoLike:
begin
if fBackgroundStyle = iebsPhotoLike then
begin
IEDrawBackground(ComponentState, Canvas, Bitmap, iebsSolid, $B0B0B0, DestX, DestY, Width, Height, x1, y1, x2, y2, fChessboardSize, fChessboardBrushStyle, fChessboardColor2Customized, fGradientEndColor, Wallpaper, WallpaperStyle, LiveBackground);
Canvas.Pen.Color := clBlack;
Canvas.Pen.Mode := pmCopy;
Canvas.Pen.Width := 1;
Canvas.Pen.Style := psSolid;
Canvas.Brush.Style := bsClear;
Canvas.Rectangle(x1-1, y1-1, x2+1, y2+1);
DestX := x1;
DestY := y1;
Width := x2-x1;
Height := y2-y1;
end;
if (Bitmap <> nil) and (fChessboardBrushStyle = bsSolid) and (Bitmap.PixelFormat = pf24bit) then
begin
c[0] := TColor2TRGB( fBackground );
if fChessboardColor2Customized then
c[1] := TColor2TRGB( fGradientEndColor )
else
c[1] := TColor2TRGB( not fBackground );
b1 := false;
i2 := 0;
hh := Bitmap.Height;
for y := DestY to DestY+Height-1 do
begin
b2 := b1;
if y<0 then
px := Bitmap.Scanline[0]
else
if y>=hh then
px := Bitmap.Scanline[hh-1]
else
px := Bitmap.Scanline[y];
inc(px, DestX);
i1 := 0;
for x := 0 to Width-1 do
begin
px^ := c[integer(b2)];
if i1=fChessboardSize then
begin
b2 := not b2;
i1 := 0;
end;
inc(i1);
inc(px);
end;
if i2=fChessboardSize then
begin
b1 := not b1;
i2 := 0;
end;
inc(i2);
end;
end
else
begin
Brush.Style := fChessboardBrushStyle;
y := DestY;
b1 := false;
while y < Height + DestY do
begin
b2 := b1;
b1 := not b1;
x := DestX;
while x < Width + DestX do
begin
if b2 then
Brush.Color := fBackground
else
if fChessboardColor2Customized then
Brush.Color := fGradientEndColor
else
Brush.Color := not fBackground;
b2 := not b2;
FillRect(rect(x, y, imin(x + fChessboardSize, DestX + Width), imin(y + fChessboardSize, DestY + Height)));
inc(x, fChessboardSize);
end;
inc(y, fChessboardSize);
end;
end;
end;
iebsDiagonals:
begin
Brush.Color := fBackground;
Brush.Style := bsSolid;
FillRect(rect(DestX, DestY, Width + DestX, Height + DestY));
Pen.Color := not fBackground;
Pen.Style := psSolid;
moveto(DestX, DestY);
lineto(Width + DestX, Height + DestY);
moveto(Width + DestX, DestY);
lineto(DestX, Height + DestY);
end;
iebsCropped:
begin
if (csDesigning in ComponentState) then
begin
Brush.Color := fBackground;
Brush.Style := bsSolid;
FillRect(rect(DestX, DestY, Width + DestX, Height + DestY));
end
else
begin
Brush.Color := clBtnFace;
Brush.Style := bsSolid;
FillRect(rect(DestX, DestY, Width + DestX, Height + DestY));
if ((x2 - x1) > 1) and ((y2 - y1) > 1) then
begin
rc := rect(x1 - IEGlobalSettings().EdgeX, y1 - IEGlobalSettings().EdgeY, x2 + IEGlobalSettings().EdgeX, y2 + IEGlobalSettings().EdgeY);
{$IFDEF IEHASTHEMING}
if IEStyleServices_Enabled then
begin
Canvas.Pen.Color := clGray;
Canvas.Pen.Mode := pmCopy;
Canvas.Pen.Width := 1;
Canvas.Pen.Style := psSolid;
Canvas.Brush.Style := bsClear;
Canvas.Rectangle(x1-1, y1-1, x2+1, y2+1);
end
else
{$ENDIF}
DrawEdge(Canvas.Handle, rc, EDGE_SUNKEN, BF_RECT);
end;
end;
end;
iebsCropShadow:
begin
if (csDesigning in ComponentState) then
begin
Brush.Color := fBackground;
Brush.Style := bsSolid;
FillRect(rect(DestX, DestY, Width + DestX, Height + DestY));
end
else
begin
Brush.Color := fBackground;
Brush.Style := bsSolid;
FillRect(rect(DestX, DestY, Width + DestX, Height + DestY));
if ((x2 - x1) > 1) and ((y2 - y1) > 1) then
begin
IERightShadow(Canvas, Bitmap, x2, y1, x2 + 5, y2 + 3, iestSmooth1, fBackground);
IEBottomShadow(Canvas, Bitmap, x1, y2, x2 + 4, y2 + 5, iestSmooth1, fBackground);
end;
end;
end;
iebsSoftShadow:
begin
if (csDesigning in ComponentState) then
begin
Brush.Color := fBackground;
Brush.Style := bsSolid;
FillRect(rect(DestX, DestY, Width + DestX, Height + DestY));
end
else
begin
if assigned(Bitmap) then
begin
Brush.Color := fBackground;
Brush.Style := bsSolid;
FillRect(rect(DestX, DestY, Width + DestX, Height + DestY));
if ((x2 - x1) > 1) and ((y2 - y1) > 1) then
IERectShadow(Bitmap, x1, y1, x2, y2, fBackground);
end;
end;
end;
iebsBlurredImage:
if assigned( LiveBackground ) and not LiveBackground.IsEmpty then
LiveBackground.DrawToCanvas( Canvas, 0, 0 )
else
begin
Brush.Color := fBackground;
FillRect(rect(DestX, DestY, Width + DestX, Height + DestY));
end;
end;
// Draw wallpaper
if assigned(Wallpaper) and assigned(Wallpaper.Graphic) then
case WallpaperStyle of
iewoNormal : Canvas.Draw( 0, 0, Wallpaper.Graphic );
iewoStretch : Canvas.StretchDraw( rect( 0, 0, Width, Height ), Wallpaper.Graphic );
iewoTile : TileBitmapOntoCanvas( Canvas, Width, Height, Wallpaper.Graphic );
end;
end;
// fo1x, fo1y, fo2x, fo2y , frx, fry
procedure TImageEnView.CalcPaintCoordsEx(var XSrc, YSrc, SrcWidth, SrcHeight: integer; var DstWidth, DstHeight: integer; tViewX, tViewY: integer);
var
rr: double;
begin
XSrc := 0;
SrcWidth := 0;
YSrc := 0;
SrcHeight := 0;
if fZZWW <> 0 then
begin
rr := fLayersRect.width / fZZWW;
XSrc := round(QuantizeViewX(tViewX) * rr);
SrcWidth := round(fExtx * rr);
if (XSrc + SrcWidth) > fLayersRect.width then
dec(SrcWidth);
end;
if fZZHH <> 0 then
begin
rr := fLayersRect.height / fZZHH;
YSrc := round(QuantizeViewY(tViewY) * rr);
SrcHeight := round(fExty * rr);
if (YSrc + SrcHeight) > fLayersRect.height then
dec(SrcHeight);
end;
if fZoomX > 100 then
begin
DstWidth := trunc(SrcWidth * fZoomD100X);
if (DstWidth < fExtX) and (XSrc + SrcWidth + 1 <= fLayersRect.width) then
begin
inc(SrcWidth);
DstWidth := trunc(SrcWidth * fZoomD100X);
end;
end
else
DstWidth := fExtX;
if fZoomY > 100 then
begin
DstHeight := trunc(SrcHeight * fZoomD100Y);
if (DstHeight < fExtY) and (YSrc + SrcHeight + 1 <= fLayersRect.height) then
begin
inc(SrcHeight);
DstHeight := trunc(SrcHeight * fZoomD100Y);
end;
end
else
DstHeight := fExtY;
end;
procedure TImageEnView.CalcPaintCoords;
begin
CalcPaintCoordsEx(fo1x, fo1y, fo2x, fo2y, frx, fry, fViewX, fViewY);
end;
{!!
<FS>TImageEnView.ZoomFilter
<FM>Declaration<FC>
property ZoomFilter: <A TResampleFilter>;
<FM>Description<FN>
Specifies the filter to apply when the image is not displayed at normal size (i.e. <A TImageEnView.Zoom> is not 100). It improves the quality of the image display.
For fastest display the ZoomFilter should be set to <FC>rfNone<FN> (default), but better quality is achieved with a filter such as <FC>rfLancoz3<FN>.
Default: rfNone
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Display\ZoomFilter\ZoomFilter.dpr </C> </R>
</TABLE>
<FM>Example<FC>
// Performs a 400% quality zoom
ImageEnView1.ZoomFilter := rfFastLinear;
ImageEnView1.Zoom := 400;
!!}
procedure TImageEnView.SetZoomFilter(v: TResampleFilter);
begin
if fZoomFilter <> v then
begin
fZoomFilter := v;
fActualZoomFilter := v;
Update;
end;
end;
{!!
<FS>TImageEnView.Assign
<FM>Declaration<FC>
procedure Assign(Source: TObject); reintroduce; overload;
<FM>Description<FN>
Copy the content of a TBitmap, <A TIEBitmap>, <A TImageEnView> or <A TImageEnVect> to the current control.
<FM>Example<FC>
// Copy content from another TImageEnView, including layers and IO params
ImageEnView1.Assign(ImageEnView2);
// Copy only the image of another TImageEnView
ImageEnView1.Assign(ImageEnView2.IEBitmap);
<FM>See Also<FN>
- <A TImageEnVect.CopyAllObjectsTo>
!!}
procedure TImageEnView.Assign(Source: TObject);
var
si: TImageEnView;
i: Integer;
iLayerID: Integer;
begin
if Source = nil then
Clear
else
if Source is TImageEnView then
begin
si := (Source as TImageEnView);
LockUpdate();
// Disable navigator
SetNavigator(nil);
fBackground := si.fBackground;
// IO Params
IO.Params.Assign( si.IO.Params );
// Layers
LayersClear;
for i := 0 to si.LayersCount - 1 do
begin
if i > 0 then // layer 0 already is here, no need to create
iLayerID := LayersAddEx( si.Layers[i].Kind, 0, 0 )
else
iLayerID := 0;
Layers[ iLayerID ].Assign( si.Layers[i] ); // here bitmaps are also copied
end;
// Vect objects
if (Source is TImageEnVect) and (Self is TImageEnVect) then
(Source as TImageEnVect).CopyAllObjectsTo(Self as TImageEnVect);
UnlockUpdate;
ImageChange();
// Navigator
if si.IsNavigator then
SetNavigator(si.fNavigator);
end
else
if Source is TBitmap then
begin
// Ensure an image layer is active
if ( fLayersCurrent > -1 ) and ( Layers[ fLayersCurrent ].Kind <> ielkImage ) then
SetLayersCurrent( 0 ); // Base layer always image
fIEBitmap.CopyFromTBitmap(source as TBitmap);
if (fIEBitmap.PixelFormat <> ie1g) and (fIEBitmap.PixelFormat <> ie24RGB) then
fIEBitmap.PixelFormat := ie24RGB;
Update;
ImageChange;
end
else
if Source is TIEBitmap then
begin
// Ensure an image layer is active
if ( fLayersCurrent > -1 ) and ( Layers[ fLayersCurrent ].Kind <> ielkImage ) then
SetLayersCurrent( 0 ); // Base layer always image
fIEBitmap.Assign(Source);
Update;
ImageChange;
end;
end;
// ret True if the polygon is a rectangle
// "n" is the number of vertex
function _IsRectangle(p: PPointArray; n: integer): boolean;
var
q: integer;
vv: boolean; // vv=false (x equals) vv=true (y equals)
begin
result := false;
if (n = 4) then
begin
if (p^[3].x = p^[0].x) and (p^[3].y <> p^[0].y) then
vv := true
else
if (p^[3].x <> p^[0].x) and (p^[3].y = p^[0].y) then
vv := false
else
exit;
for q := 0 to 2 do
if (p^[q].x = p^[q + 1].x) and (p^[q].y <> p^[q + 1].y) and (not vv) then
vv := true
else
if (p^[q].x <> p^[q + 1].x) and (p^[q].y = p^[q + 1].y) and vv then
vv := false
else
exit;
result := true;
end;
end;
{!!
<FS>TImageEnView.MouseInteract
<FM>Declaration<FC>
property MouseInteract: <A TIEMouseInteract>;
<FM>Description<FN>
Specify which mouse activities are performed when the user interacts with the ImageEnView component with the mouse.
Note: Multiple interactions can be specified, but activities that are not mutually compatible will be excluded
<FM>Examples<FC>
// Single left click zoom-in image, single right click zoom-out image, click and
// drag scroll the image (if it is bigger than client area).
ImageEnView1.MouseInteract := [ miZoom, miScroll ];
// Allow users to create image layers. Prompt for an image file after selection
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAutoPromptForImage ];
ImageEnView1.MouseInteract := [ miCreateImageLayers ];
// Allow user to move and resize layers (allow multiple layer selection and ensure masks are moved with layers)
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAllowMultiSelect, loAutoSelectMask ];
ImageEnView1.MouseInteract := [ miMoveLayers, miResizeLayers ];
A selected text layer with resize grips:
<IMG help_images\text_Selected.gif>
// Allow user to rotate layers (allow multiple layer selection and ensure masks are moved with layers)
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAllowMultiSelect, loAutoSelectMask ];
ImageEnView1.MouseInteract := [ miRotateLayers ];
// Allow circular selections
ImageEnView1.MouseInteract := [ miSelectCircle ];
<IMG help_images\Selection.gif>
// Allow the user to create, size and select red arrows
ImageEnView1.MouseInteract := [ miCreateLineLayers, miMoveLayers, miResizeLayers ];
ImageEnView1.LayerDefaults.Clear();
ImageEnView1.LayerDefaults.Add( IELP_LineColor +'=clRed' );
ImageEnView1.LayerDefaults.Add( IELP_LineWidth +'=6' );
ImageEnView1.LayerDefaults.Add( IELP_LineShapeSize +'=20' );
ImageEnView1.LayerDefaults.Add( IELP_LineStartShape +'=1' );
ImageEnView1.LayerDefaults.Add( IELP_Rotate +'=235' );
<FM>Demos<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\FullApps\PhotoEn3\ImageEx.dpr </C> </R>
<R> <C_IMG_DEMO> <C>Demos\Display\SoftPan\SoftPan.dpr </C> </R>
<R> <C_IMG_DEMO> <C>Demos\ImageEditing\RotateLayers\RotateLayers.dpr </C> </R>
<R> <C_IMG_DEMO> <C>Demos\ImageEditing\Layers_AllTypes\Layers.dpr </C> </R>
</TABLE>
<FM>See Also<FN>
- <A TImageEnView.SelectionOptions>
- <A TImageEnView.LayerDefaults>
!!}
function TImageEnView.GetMouseInteract: TIEMouseInteract;
begin
result := fMouseInteract;
end;
{!!
<FS>TImageEnView.CropToolInteraction
<FM>Declaration<FC>
property CropToolInteraction: <A TIECropToolInteraction>;
<FM>Description<FN>
Provides access to the methods and properties of the <A TIECropToolInteraction> class, which is used when <A TImageEnView.MouseInteract> is <FC>miCropTool<FN>.
The crop tool allows the user to select an area of the image to keep and then click "Enter" to apply the crop. The selection can also be rotated so the image is rotated and then cropped.
In Crop Tool mode:
- User can resize crop box by dragging grips
- User can rotate crop by dragging outside grips
- User can click "Enter" to enact the crop
- User can click "Esc" to cancel the crop
<FM>Rotated Crop<FN>
<IMG help_images\croptool.jpg>
<FM>Perspective Fix<FN>
<IMG help_images\Perspective2.jpg>
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\ImageEditing\CropTool\CropTool.dpr </C> </R>
</TABLE>
<FM>Example<FC>
// Disable guide lines (on image thirds)
ImageEnView1.CropToolInteraction.DrawGuides := False;
// Make larger grips
ImageEnView1.CropToolInteraction.GripSize := 12;
// High quality cropping
ImageEnView1.CropToolInteraction.AntialiasMode := ierBicubic;
// Enable crop mode
ImageEnView1.MouseInteract := [miCropTool];
// Enact crop (same as user clicking "Enter")
ImageEnView1.CropToolInteraction.Crop();
// Cancel crop tool (same as user clicking "Esc")
ImageEnView1.CropToolInteraction.Cancel();
!!}
function TImageEnView.GetCropToolInteraction: TIECropToolInteraction;
begin
Result := fUserInteractions[ fUserInteractions.FindInstanceOf( TIECropToolInteraction )] as TIECropToolInteraction;
end;
{!!
<FS>TImageEnView.BackgroundStyle
<FM>Declaration<FC>
property BackgroundStyle: <A TIEBackgroundStyle>;
<FM>Description<FN>
Specifies the style of the image background (the region of the ImageEnView window that is not filled by the image).
<FM>Example<FC>
ImageEnView1.BackgroundStyle := iebsPhotoLike;
<FM>See Also<FN>
- <A TImageEnView.Background>
- <A TImageEnView.GradientEndColor>
- <A TImageEnView.Wallpaper>
- <A TImageEnView.WallpaperStyle>
!!}
procedure TImageEnView.SetBackgroundStyle(v: TIEBackgroundStyle);
begin
fBackgroundStyle := v;
invalidate;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
procedure TImageEnView.DoSelectionChanging;
begin
SwapSelectionBase;
if Assigned(fOnSelectionChanging) then
OnSelectionChanging(self);
SwapSelectionBase;
end;
procedure TImageEnView.DoSelectionChange;
begin
if Assigned(fOnSelectionChange) then
OnSelectionChange(self);
end;
procedure TImageEnView.DoBeforeSelectionChange;
begin
SwapSelectionBase;
if Assigned(fOnBeforeSelectionChange) then
OnBeforeSelectionChange(self);
SwapSelectionBase;
end;
{!!
<FS>TImageEnView.CopyFromPolygon
<FM>Declaration<FC>
procedure CopyFromPolygon(Source: TBitmap; const Polygon: array of TPoint; PolygonLen: Integer; const Position: TPoint);
<FM>Description<FN>
CopyFromPolygon copies a region (Polygon) of source bitmap to current image.
CopyFromPolygon enlarges current bitmap when needed.
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>Source<FN></C> <C>Source bitmap</C> </R>
<R> <C><FC>Polygon<FN></C> <C>Array of polygon vertexes (pixel coordinates related to Source bitmap)</C> </R>
<R> <C><FC>PolygonLen<FN></C> <C>Number of vertexes in polygon</C> </R>
<R> <C><FC>Position<FN></C> <C>Destination point (pixel coordinate related to current image). The destination point is the top-left side of the rectangle that encloses source polygon</C> </R>
</TABLE>
<FM>Example<FC>
// Copies current selected area of ImageEnView1 to ImageEnView2 at position 0, 0
ImageEnView2.CopyFromPolygon(ImageEnView1.Bitmap, ImageEnView1.PolySelPoints^, ImageEnView1.PolySelCount, Point(0, 0));
// Copies the rect 0, 0, 100, 100 in ImageEnView2 at position 0, 0
ImageEnView2.CopyFromPolygon(ImageEnView1.Bitmap, [Point(0, 0), Point(100, 0), Point(100, 100), Point(0, 100)], 4, Point(0, 0));
// Copies current selection to ImageEnView2. Then enhance contrast of ImageEnView2 and copy back to ImageEnView1 (in some selection).
ImageEnView2.CopyFromPolygon(ImageEnView1.Bitmap, ImageEnView2.PolySelPoints^, ImageEnView2.PolySelCount, Point(0, 0));
ImageEnView2.Proc.Contrast(30);
ImageEnView2.CopyToPolygon(ImageEnView1.Bitmap, ImageEnView1.PolySelPoints^, ImageEnView1.PolySelCount, Point(0, 0));
ImageEnView1.Update;
!!}
// Copy the polygon Polygon of Source in Position of fBitmap
procedure TImageEnView.CopyFromPolygon(Source: TBitmap; const Polygon: array of TPoint; PolygonLen: integer; const Position: TPoint);
begin
_CopyPolygonToPoint(Source, @Polygon, PolygonLen, fBitmap, Position);
Update;
end;
{!!
<FS>TImageEnView.CopyToPolygon
<FM>Declaration<FC>
procedure CopyToPolygon(Dest: TBitmap; const Polygon: array of TPoint; PolygonLen: Integer; const Position: TPoint);
<FM>Description<FN>
CopyToPolygon copies a region (Polygon) of current image inside Dest bitmap.
Polygon is an array of TPoint (pixel coordinates related to Dest bitmap) and PolygonLen is the number of point in Polygon.
Position is the source point (pixel coordinate related to current image). The source point is the top-left side of the rectangle that encloses destination polygon.
CopyToPolygon enlarges destination bitmap when needed.
<FM>Example<FC>
// Copies current selection to ImageEnView2. Then enhance contrast of ImageEnView2 and copy back to ImageEnView1 (in some selection).
ImageEnView2.CopyFromPolygon(ImageEnView1.Bitmap, ImageEnView2.PolySelPoints^, ImageEnView2.PolySelCount, Point(0, 0));
ImageEnView2.Proc.Contrast(30);
ImageEnView2.CopyToPolygon(ImageEnView1.Bitmap, ImageEnView1.PolySelPoints^, ImageEnView1.PolySelCount, Point(0, 0));
ImageEnView1.Update;
!!}
// copy the rect at Position of fBitmap in Polygon of Dest
procedure TImageEnView.CopyToPolygon(Dest: TBitmap; const Polygon: array of TPoint; PolygonLen: integer; const Position: TPoint);
begin
_CopyPointToPolygon(fBitmap, @Polygon, PolygonLen, Dest, Position);
end;
function TImageEnView.GetScrollBarsAlwaysVisible: boolean;
begin
result := fScrollBarsAlwaysVisible;
end;
procedure TImageEnView.SetScrollBarsAlwaysVisible(v: boolean);
begin
fScrollBarsAlwaysVisible := v;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
{!!
<FS>TImageEnView.DisplayGridKind
<FM>Declaration<FC>
property DisplayGridKind: <A TIEGridKind>;
<FM>Description<FN>
Enables the display of helper lines over the image.
<TABLE>
<R> <H>Item</H> <H>Description</H> </R>
<R> <C><FC>iedgNone<FN></C> <C>No guide lines are shown</C> </R>
<R> <C><FC>iedgPixelGrid<FN></C> <C>A grid is shown marking each pixel when the image is zoomed in (e.g. for pixel editing in an image editor)</C> </R>
<R> <C><FC>iedgGuideLines<FN></C> <C>Guide lines are shown horizontally and vertically over the image (e.g. to help align objects when rotating)</C> </R>
</TABLE>
<FM>Examples<FC>
// Draw a grid to show pixels when we zoom above 500%
IEGlobalSettings().GridPen.Color := clSilver;
IEGlobalSettings().GridPen.Style := psSolid;
IEGlobalSettings().GridPen.Mode := pmNot;
IEGlobalSettings().MinZoomDisplayGrid := 500;
ImageEnView1.DisplayGridKind := iedgPixelGrid;
// Enable guide lines to help line up images when manually rotating
IEGlobalSettings().GridPen.Color := clSilver;
IEGlobalSettings().GridPen.Style := psDot;
IEGlobalSettings().GridPen.Mode := pmCopy;
IEGlobalSettings().GuidelineCount := 4;
ImageEnView1.DisplayGridKind := iedgGuideLines;
<FM>See Also<FN>
- <A TIEImageEnGlobalSettings.MinZoomDisplayGrid>
- <A TIEImageEnGlobalSettings.GridPen>
- <A TIEImageEnGlobalSettings.GridMajorStep>
- <A TIEImageEnGlobalSettings.GuidelineCount>
- <A TImageEnView.DisplayGridLyr>
!!}
procedure TImageEnView.SetDisplayGridKind(v: TIEGridKind);
begin
fDisplayGridKind := v;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 6.2.2 (8/2/2016)
function TImageEnView.GetDisplayGrid(): boolean;
begin
Result := fDisplayGridKind <> iedgNone;
end;
{$endif}
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 6.2.2 (8/2/2016)
procedure TImageEnView.SetDisplayGrid(v: boolean);
begin
if not v then
DisplayGridKind := iedgNone
else
if DisplayGridKind = iedgNone then
DisplayGridKind := iedgPixelGrid;
end;
{$endif}
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 7.0.0 (21/2/2017)
function TImageEnView.GetAutoFixRotationBorders(): boolean;
begin
Result := loAutoFixBorders in fLayerOptions;
end;
{$endif}
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 7.0.0 (21/2/2017)
procedure TImageEnView.SetAutoFixRotationBorders(v: boolean);
begin
if v then
fLayerOptions := fLayerOptions + [ loAutoFixBorders ]
else
fLayerOptions := fLayerOptions - [ loAutoFixBorders ]
end;
{$endif}
{!!
<FS>TImageEnView.DisplayGridLyr
<FM>Declaration<FC>
property DisplayGridLyr: Integer;
<FM>Description<FN>
Specifies where to draw the grid if <A TImageEnView.DisplayGridKind> is <FC>iedgPixelGrid<FN>.
-1 : current layer (default behavior)
>= 0 : specific layer
<FM>See Also<FN>
- <A TImageEnView.DisplayGridKind>
- <A TIEImageEnGlobalSettings.MinZoomDisplayGrid>
- <A TIEImageEnGlobalSettings.GridPen>
- <A TIEImageEnGlobalSettings.GridMajorStep>
!!}
procedure TImageEnView.SetDisplayGridLyr(v: integer);
begin
fDisplayGridLyr := v;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
{!!
<FS>TImageEnView.ShowRulers
<FM>Declaration<FC>
property ShowRulers: <A TRulerDirs>;
<FM>Description<FN>
Specify whether rulers are shown on the TImageEnView. Rulers show the current position of the cursor and can include optional grips to mark the position of other objects.
Use <A TImageEnView.RulerParams> to configure the ruler properties.
Default: [] (No rulers)
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Other\ImageEnViewRulers\ImageEnViewRulers.dpr </C> </R>
</TABLE>
<FM>Example<FC>
// Show rulers in TImageEnView
ImageEnView1.ShowRulers := [ rdHorizontal, rdVertical ];
// Set units to CM
ImageEnView1.RulerParams.Units := ieruCentimeters;
!!}
procedure TImageEnView.SetShowRulers(const v: TRulerDirs);
begin
if fShowRulers <> v then
begin
fShowRulers := v;
UpdateReason := ieurComponentStuffChanged;
fRulerParams.Update( False ); // May not be called in Update below, due to start up or design tiem
Update;
end;
end;
procedure TImageEnView.KeyDown(var Key: Word; Shift: TShiftState);
begin
inherited;
if (ssShift in Shift) and fEnableShiftKey then
begin
case cursor of
crIECrossSight : SetTempCursor(crIECrossSightPlus);
crIEThickCross : SetTempCursor(crIEThickCrossPlus);
end;
end;
if ( Key = VK_F2 ) and
( fLayersCurrent > -1 ) and
( Layers[ fLayersCurrent ].SupportsFeature( ielfTextEditing )) then
begin
LayersActivateTextEditor( fLayersCurrent );
end;
end;
procedure TImageEnView.KeyUp(var Key: Word; Shift: TShiftState);
begin
inherited;
if (not (ssShift in Shift)) and fEnableShiftKey then
begin
RestoreCursor;
end;
end;
procedure TImageEnView.KeyPress(var Key: Char);
begin
inherited;
end;
{!!
<FS>TImageEnView.EndSelect
<FM>Declaration<FC>
procedure EndSelect;
<FM>Description<FN>
Terminates a selection specified by code using the <A TImageEnView.AddSelPoint> and <A TImageEnView.AddSelBreak> methods.
<FM>Example<FC>
ImageEnView1.AddSelPoint(0, 0);
ImageEnView1.AddSelPoint(100, 100);
ImageEnView1.AddSelPoint(50, 50);
ImageEnView1.EndSelect;
!!}
// finalize selection (update fSelectionMask)
procedure TImageEnView.EndSelect;
begin
fSelectionMask.DrawPolygon(fSelectionIntensity, PIEAnimPoly(fHPolySel)^.Poly, PIEAnimPoly(fHPolySel)^.PolyCount); // update mask
if fSelectionMask.IsEmpty then
begin
// empty selection
fSel := false;
AnimPolygonClear(fHPolySel);
end;
end;
function TImageEnView.GetClientWidth: integer;
begin
if fOffscreenPaint then
result := Width
else
if HasParentWindow and HandleAllocated then
result := inherited ClientWidth
else
result := 50;
end;
function TImageEnView.GetClientWidthExRulers: integer;
begin
if fOffscreenPaint then
result := Width
else
if HasParentWindow and HandleAllocated then
result := inherited ClientWidth - fRulerParams.RulerAreaLeft - fRulerParams.RulerAreaRight
else
result := 50;
end;
function TImageEnView.GetClientHeight: integer;
begin
if fOffscreenPaint then
result := Height
else
if HasParentWindow and HandleAllocated then
result := inherited Clientheight
else
result := 50;
end;
function TImageEnView.GetClientHeightExRulers: integer;
begin
if fOffscreenPaint then
result := Height
else
if HasParentWindow and HandleAllocated then
result := inherited Clientheight - fRulerParams.RulerAreaLeft - fRulerParams.RulerAreaRight
else
result := 50;
end;
{!!
<FS>TImageEnView.SelColor1
<FM>Declaration<FC>
property SelColor1: TColor
<FM>Description<FN>
SelColor1 and <A TImageEnView.SelColor2> set the two colors of the animated selection polygon.
!!}
procedure TImageEnView.SetSelColor1(v: TColor);
begin
PIEAnimPoly(fHPolySel)^.Color1 := v;
fSelColor1 := v;
end;
{!!
<FS>TImageEnView.SelColor2
<FM>Declaration<FC>
property SelColor2: TColor
<FM>Description<FN>
<A TImageEnView.SelColor1> and SelColor2 set the two colors of the animated selection polygon.
!!}
procedure TImageEnView.SetSelColor2(v: TColor);
begin
PIEAnimPOly(fHPolySel)^.Color2 := v;
fSelColor2 := v;
end;
function TImageEnView.GetImageEnIO: TImageEnIO;
begin
if not assigned(fImageEnIO) then
begin
fImageEnIO := TImageEnIO.Create(self);
fImageEnIO.AttachedImageEn := self;
fImageEnIO.OnProgress := fOnProgress;
fImageEnIO.OnFinishWork := fOnFinishWork;
fImageEnIO.OnAcquireBitmap := fOnAcquireBitmap;
end;
result := fImageEnIO;
end;
function TImageEnView.GetImageEnProc: TImageEnProc;
begin
if not assigned(fImageEnProc) then
begin
fImageEnProc := TImageEnProc.Create(self);
fImageEnProc.AttachedImageEn := self;
fImageEnProc.OnProgress := fOnProgress;
fImageEnProc.OnFinishWork := fOnFinishWork;
fImageEnProc.OnSaveUndo := fOnSaveUndo;
end;
result := fImageEnProc;
end;
{!!
<FS>TImageEnView.OnProgress
<FM>Declaration<FC>
property OnProgress: <A TIEProgressEvent>;
<FM>Description<FN>
The OnProgress event is called when image processing or input/output operations are executed. If you are using it to update a progress bar then you can reset it in the <A TImageEnView.OnFinishWork> event.
Note: To determine what type of work is in progress, check the class of the Sender, e.g.
<FC>
if Sender is TImageEnIO then
MainForm.Caption := format( 'IO Task Progress: %d%%', [ per ])
else
if Sender is TImageEnProc then
MainForm.Caption := format( 'Processing Task Progress: %d%%', [ per ]);
<FN>
<FM>Example<FC>
// An example showing seperate progress display for I/O operations and processing operations
procedure TMainForm.ImageEnView1Progress(Sender: TObject; per: Integer);
begin
// I/O PROGRESS
if Sender is TImageEnIO then
begin
IOProgressBar.Position := per;
IOProgressBar.Visible := True;
end
else
// IMAGE PROCESSING PROGRESS
if Sender is TImageEnProc then
begin
ProcProgressBar.Position := per;
ProcProgressBar.Visible := True;
end
end;
// Hide the progress bar
procedure TMainForm.ImageEnView1FinishWork(Sender: TObject);
begin
// I/O PROGRESS
if Sender is TImageEnIO then
IOProgressBar.Visible := False
else
// IMAGE PROCESSING PROGRESS
if Sender is TImageEnProc then
ProcProgressBar.Visible := False;
end;
!!}
function TImageEnView.GetOnProgress: TIEProgressEvent;
begin
result := fOnProgress;
end;
procedure TImageEnView.SetOnProgress(v: TIEProgressEvent);
begin
fOnProgress := v;
if assigned(fImageEnIO) then
fImageEnIO.OnProgress := v;
if assigned(fImageEnProc) then
fImageEnProc.OnProgress := v;
end;
{!!
<FS>TImageEnView.OnAcquireBitmap
<FM>Declaration<FC>
property OnAcquireBitmap: <A TIEAcquireBitmapEvent>;
<FM>Description<FN>
Occurs whenever a new bitmap is acquired during an acquisition.
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C>Sender</C> <C>Will be either a TImageEnIO or TImageEnMIO control</C> </R>
<R> <C>ABitmap</C> <C>A <A TIEBitmap> object that contains the acquired image</C> </R>
<R> <C>DpiX, DpiY</C> <C>Tne DPI of the acquired image</C> </R>
<R> <C>Handled</C> <C>Has no effect when acquiring via a TImageEnView/TImageEnIO</C> </R>
</TABLE>
!!}
function TImageEnView.GetOnAcquireBitmap: TIEAcquireBitmapEvent;
begin
result := fOnAcquireBitmap;
end;
procedure TImageEnView.SetOnAcquireBitmap(v: TIEAcquireBitmapEvent);
begin
fOnAcquireBitmap := v;
if assigned(fImageEnIO) then
fImageEnIO.OnAcquireBitmap := v;
end;
{!!
<FS>TImageEnView.OnFinishWork
<FM>Declaration<FC>
property OnFinishWork: TNotifyEvent;
<FM>Description<FN>
Occurs whenever an image processing or input/output task terminates.
It is always called after <A TImageEnView.OnProgress> so is useful to reset a progress bar.
Note: To determine what type of work has finished, check the class of the Sender, e.g.
<FC>
if Sender is TImageEnIO then
ShowMessage( 'IO Task has finished' )
else
if Sender is TImageEnProc then
ShowMessage( 'Processing Task has finished' )
else
ShowMessage( 'Unexpected result!' )
<FN>
<FM>Example<FC>
// An example showing seperate progress display for I/O operations and processing operations
procedure TMainForm.ImageEnView1Progress(Sender: TObject; per: Integer);
begin
// I/O PROGRESS
if Sender is TImageEnIO then
begin
IOProgressBar.Position := per;
IOProgressBar.Visible := True;
end
else
// IMAGE PROCESSING PROGRESS
if Sender is TImageEnProc then
begin
ProcProgressBar.Position := per;
ProcProgressBar.Visible := True;
end
end;
// Hide the progress bar
procedure TMainForm.ImageEnView1FinishWork(Sender: TObject);
begin
// I/O PROGRESS
if Sender is TImageEnIO then
IOProgressBar.Visible := False
else
// IMAGE PROCESSING PROGRESS
if Sender is TImageEnProc then
ProcProgressBar.Visible := False;
end;
!!}
function TImageEnView.GetOnFinishWork: TNotifyEvent;
begin
result := fOnFinishWork;
end;
procedure TImageEnView.SetOnFinishWork(v: TNotifyEvent);
begin
fOnFinishWork := v;
if assigned(fImageEnIO) then
fImageEnIO.OnFinishWork := v;
if assigned(fImageEnProc) then
fImageEnProc.OnFinishWork := v;
end;
{!!
<FS>TImageEnView.TransitionRunning
<FM>Declaration<FC>
property TransitionRunning: Boolean;
<FM>Description<FN>
TransitionRunning is True whenever a transition effect is being displayed.
<FM>See Also<FN>
- <A TImageEnView.PrepareTransition>
- <A TImageEnView.RunTransition>
- <A TImageEnView.AbortTransition>
!!}
function TImageEnView.GetTransitionRunning: boolean;
begin
result := assigned(fTransition) and fTransition.Running;
end;
{!!
<FS>TImageEnView.PrepareTransition
<FM>Declaration<FC>
procedure PrepareTransition;
<FM>Description<FN>
PrepareTransition must be called prior to <L TImageEnView.RunTransition>running</L> a transition effect.
It copies the currently displayed image to an internal buffer, which allows you to change current image (load, update, etc.) before starting the transition with <A TImageEnView.RunTransition>.
<FM>Example<FC>
// Show a transition effect from the image already loaded in AImageEnView to our new image, Pic2.jpg
AImageEnView.PrepareTransition; // Prepare the transition
AImageEnView.IO.LoadFromFile('C:\Pic2.jpg'); // Load the new image (though it won't yet display)
AImageEnView.RunTransition(iettRandompoints, 500); // Start display of the transition effect
<FM>See Also<FN>
- <A TImageEnView.RunTransition>
- <A TImageEnView.TransitionRunning>
- <A TImageEnView.AbortTransition>
!!}
procedure TImageEnView.PrepareTransition;
var
iebmp: TIEBitmap;
begin
if (GetClientWidth = 0) or (GetClientHeight = 0) then
exit;
SetupTransition;
fTransition.SetSizes(ClientWidth, ClientHeight);
iebmp := TIEBitmap.Create;
try
iebmp.EncapsulateTBitmap(fTransition.SourceShot, false);
PaintToEx(iebmp, nil, true, true);
finally
iebmp.free;
end;
end;
{!!
<FS>TImageEnView.RunTransition
<FM>Declaration<FC>
procedure RunTransition(Effect : <A TIETransitionType>; Duration : integer); overload;
procedure RunTransition(Effect : <A TIETransitionType>; Duration : integer; StartRect, EndRect : TRect; bMaintainAspectRatio : Boolean; Smoothing: Integer = 96); overload;
procedure RunTransition(Effect : <A TIETransitionType>; Duration : integer; PanZoomEffect : <A TIEPanZoomType>; iZoomLevel : Integer; Smoothing: Integer = 96); overload;
<FM>Description<FN>
RunTransition starts the transition using <FC>Effect<FN> and <FC>Duration<FN> parameters.
Effect specifies the effect. Duration specifies the duration of the transition in milliseconds.
If effect is iettPanZoom then use one of the overloaded versions.
Pan-Zoom Overload 1:
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>StartRect</C> <C>The starting rectangle of an iettPanZoom transition (specified in bitmap points)</C> </R>
<R> <C>EndRect</C> <C>The ending rectangle of an iettPanZoom transition (specified in bitmap points)</C> </R>
<R> <C>bMaintainAspectRatio</C> <C>StartRect and EndRect will be automatically adjusted to ensure the image appears with the correct aspect ratio</C> </R>
<R> <C><FC>Smoothing<FN></C> <C>In order to reduce the "jumpiness" of pan zoom effects, transition frames are alpha blended. A low value will improve smoothness, but increase blurriness. A high value will improve clarity, but increase jumpiness. Typical range is 64 - 196. 255 means no alpha blending</C> </R>
</TABLE>
Pan-Zoom Overload 2:
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>PanZoomEffect</C> <C>One of ImageEn's <L TIEPanZoomType>built-in Pan Zoom effects</L></C> </R>
<R> <C>iZoomLevel</C> <C>If the specified <FC>PanZoomEffect<FN> is a "Zoom" type, then this specifies the maximum amount to zoom in, e.g. 20%</C> </R>
<R> <C><FC>Smoothing<FN></C> <C>In order to reduce the "jumpiness" of pan zoom effects, transition frames are alpha blended. A low value will improve smoothness, but increase blurriness. A high value will improve clarity, but increase jumpiness. Typical range is 64 - 196. 255 means no alpha blending</C> </R>
</TABLE>
<FM>iettShreddedFromLeft<FN>
<IMG help_images\Transition.jpg>
<FM>iettCubeRotateFromTop2<FN>
<IMG help_images\CubeTransition.jpg>
<FM>Demos<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Display\Transitions\Transitions.dpr </C> </R>
<R> <C_IMG_DEMO> <C>Demos\Display\PanZoomEffects\PanZoomEffects.dpr </C> </R>
</TABLE>
<FM>Example<FC>
// Transition from Image1.jpg to Image2.jpg with "Randon Points" transition
ImageEnView1.IO.LoadFromFile('D:\image1.jpg'); // Load initial image
ImageEnView1.PrepareTransition; // Prepare the transition
ImageEnView1.IO.LoadFromFile('D:\image2.jpg'); // Load the next image (but does not display it)
ImageEnView1.RunTransition(iettRandompoints, 500); // Execute the transition
// Pan from the top-left corner of the image to the bottom-right (Image is 600 x 800)
ImageEnView1.PrepareTransition;
ImageEnView1.IO.LoadFromFile('D:\image.jpg');
ImageEnView1.RunTransition(iettPanZoom,
2000, // 2 second transition
Rect(0, 0, 300, 400), // Top-left quarter
Rect(300, 400, 600, 800), // Bottom-right quarter
True); // Automatically adjust starting and ending rect to ensure the transition does not distort the image
// Pan from the top-left corner of the image to the bottom-right
ImageEnView1.PrepareTransition;
ImageEnView1.IO.LoadFromFile('D:\image.jpg');
ImageEnView1.RunTransition(iettPanZoom,
2000, // 2 second transition
iepzPanTopLeftToBottomRight,
20);
<FM>See Also<FN>
- <A TImageEnView.PrepareTransition>
- <A TImageEnView.TransitionRunning>
- <A TImageEnView.AbortTransition>
!!}
procedure TImageEnView.RunTransition(Effect: TIETransitionType; duration: integer);
var
iebmp: TIEBitmap;
begin
if (GetClientWidth = 0) or (GetClientHeight = 0) then
exit;
// Select base layer
SetLayersCurrent( 0 );
SetupTransition;
if (duration < 1) then
duration := 1;
fTransitionEffect := Effect;
fTransitionDuration := duration;
fTransition.Transition := fTransitionEffect;
fTransition.Duration := fTransitionDuration;
fTransition.Background := GetThemeColor( ietpControlBackground, Background );
iebmp := TIEBitmap.Create;
try
iebmp.EncapsulateTBitmap(fTransition.TargetShot, false);
PaintToEx(iebmp, nil, true, true);
finally
iebmp.free;
end;
fTransition.FullImage := fIEBitmap;
fTransition.Run(true);
// Now adjust the display to match the end position of the transition
if Effect = iettPanZoom then
VisibleBitmapRect := fTransition.EndRect;
fTransitionEffect := iettNone;
end;
procedure TImageEnView.RunTransition(Effect: TIETransitionType; Duration: integer; PanZoomEffect : TIEPanZoomType; iZoomLevel : Integer; Smoothing: Integer = 96);
var
StartRect, EndRect : TRect;
begin
// Select base layer
SetLayersCurrent( 0 );
GetPanZoomEffectStartEndRects(ClientWidth, ClientHeight,
fIEBitmap.Width, fIEBitmap.Height,
PanZoomEffect,
iZoomLevel,
StartRect, EndRect);
RunTransition(Effect, Duration, StartRect, EndRect, True, Smoothing);
end;
procedure TImageEnView.RunTransition(Effect: TIETransitionType; Duration: integer; StartRect, EndRect : TRect; bMaintainAspectRatio : Boolean = True; Smoothing: Integer = 96);
begin
SetupTransition;
if bMaintainAspectRatio then
begin
fTransition.StartRect := AdjustRectToAspectRatio( StartRect, True );
fTransition.EndRect := AdjustRectToAspectRatio( EndRect, True );
end
else
begin
fTransition.StartRect := StartRect;
fTransition.EndRect := EndRect;
end;
fTransition.Smoothing := Smoothing;
RunTransition( Effect, Duration );
end;
{$ifdef IEIncludeDeprecatedInV4}
// Deprecated prior to v4.3.1
// DEPRECATED: Use TImageEnProc.PrepareTransitionBitmaps/CreateTransitionBitmaps
procedure TImageEnView.PrepareTransitionBitmaps(OriginalBitmap, TargetBitmap : TBitmap; Effect : TIETransitionType; iWidth : Integer = -1; iHeight : Integer = -1);
begin
// Select base layer
SetLayersCurrent( 0 );
SetupTransition;
fTransition.Transition := Effect;
fTransition.FullImage := fIEBitmap;
fTransition.Background := GetThemeColor( ietpControlBackground, Background );
if Effect = iettPanZoom then
begin
fIEBitmap.assign(OriginalBitmap);
fTransition.PrepareBitmap(OriginalBitmap, OriginalBitmap);
end
else
begin
fTransition.PrepareBitmap(OriginalBitmap, TargetBitmap);
end;
if iWidth > 0 then
fTransition.SetSizes(iWidth, iHeight);
end;
{$endif}
{$ifdef IEIncludeDeprecatedInV4}
{$IFDEF Delphi6orNewer} {$WARN SYMBOL_DEPRECATED OFF} {$ENDIF}
// Deprecated prior to v4.3.1
// DEPRECATED: Use TImageEnProc.PrepareTransitionBitmaps/CreateTransitionBitmaps
procedure TImageEnView.PrepareTransitionBitmaps(OriginalBitmap, TargetBitmap : TBitmap; Effect : TIETransitionType; StartRect, EndRect : TRect; bMaintainAspectRatio : Boolean = True; iWidth : Integer = -1; iHeight : Integer = -1);
begin
// Select base layer
SetLayersCurrent( 0 );
SetupTransition;
if bMaintainAspectRatio then
begin
fTransition.StartRect := AdjustRectToAspectRatio( StartRect, True );
fTransition.EndRect := AdjustRectToAspectRatio( EndRect, True );
end
else
begin
fTransition.StartRect := StartRect;
fTransition.EndRect := EndRect;
end;
PrepareTransitionBitmaps( OriginalBitmap, TargetBitmap, Effect, iWidth, iHeight );
end;
{$IFDEF Delphi6orNewer} {$WARN SYMBOL_DEPRECATED ON} {$ENDIF}
{$endif}
{$ifdef IEIncludeDeprecatedInV4}
// Deprecated prior to v4.3.1
// DEPRECATED: Use TImageEnProc.PrepareTransitionBitmaps/CreateTransitionBitmaps
// TransitionProgress: The percentage that it is has progressed from the start image to the end image (ranging from 0.0 to 100.0)
// ToBitmap: the bitmap to write it to
procedure TImageEnView.CreateTransitionBitmap(TransitionProgress : Single; DestBitmap : TBitmap);
begin
SetupTransition;
fTransition.CreateBitmap(TransitionProgress, DestBitmap);
end;
{$endif}
{!!
<FS>TImageEnView.AbortTransition
<FM>Declaration<FC>
procedure AbortTransition;
<FM>Description<FN>
This method aborts current transition started with <A TImageEnView.RunTransition>.
<FM>See Also<FN>
- <A TImageEnView.PrepareTransition>
- <A TImageEnView.RunTransition>
- <A TImageEnView.TransitionRunning>
!!}
procedure TImageEnView.AbortTransition;
begin
if assigned(fTransition) then
fTransition.Stop;
end;
procedure TImageEnView.SetOnTransitionStop(value: TNotifyEvent);
begin
SetupTransition;
fTransition.OnTransitionStop := value;
end;
{!!
<FS>TImageEnView.OnTransitionStop
<FM>Declaration<FC>
property OnTransitionStop: TNotifyEvent;
<FM>Description<FN>
Occurs when the transition (that was started by using <A TImageEnView.RunTransition>) has finished its job (i.e. the transition effect has finished).
!!}
function TImageEnView.GetOnTransitionStop: TNotifyEvent;
begin
if assigned(fTransition) then
result := fTransition.OnTransitionStop
else
result := nil;
end;
procedure TImageEnView.SetOnTransitionPaint(const Value: TIEOnTransitionPaint);
begin
SetupTransition;
fTransition.OnTransitionPaint := Value;
end;
procedure TImageEnView.SetOnTransitionStep(value: TIETransitionStepEvent);
begin
SetupTransition;
fTransition.OnTransitionStep := value;
end;
{!!
<FS>TImageEnView.OnTransitionStep
<FM>Declaration<FC>
property OnTransitionStep: <A TIETransitionStepEvent>;
<FM>Description<FN>
Occurs prior to the painting of each transition frame. <FC>Step<FN> is a value from 0 to 1024.
!!}
function TImageEnView.GetOnTransitionStep: TIETransitionStepEvent;
begin
if assigned(fTransition) then
result := fTransition.OnTransitionStep
else
result := nil;
end;
{!!
<FS>TImageEnView.OnTransitionPaint
<FM>Declaration<FC>
property OnTransitionPaint: <A TIEOnTransitionPaint>;
<FM>Description<FN>
Occurs immediately before a new transition frame is painted. <FC>Step<FN> is a value from 0 to 1024.
!!}
function TImageEnView.GetOnTransitionPaint: TIEOnTransitionPaint;
begin
if fTransition = nil then
Result := nil
else
Result := fTransition.OnTransitionPaint;
end;
procedure TImageEnView.SetTransitionTiming(value: TIETransitionTiming);
begin
SetupTransition;
fTransition.Timing := value;
end;
{!!
<FS>TImageEnView.TransitionTiming
<FM>Declaration<FC>
property TransitionTiming: <A TIETransitionTiming>;
<FM>Description<FN>
TransitionTiming provides alternative timing options for transition effect progress. Generally this will be iettLinear (normal progress).
!!}
function TImageEnView.GetTransitionTiming: TIETransitionTiming;
begin
SetupTransition;
result := fTransition.Timing;
end;
{!!
<FS>TImageEnView.Playing
<FM>Declaration<FC>
property Playing : boolean;
<FM>Description<FN>
Set Playing to True to animate GIF and AVI files. If <A TImageEnView.PlayLoop> is enabled then the animation will replay continuously
Note: TImageEnView.Playing loads each frame on demand, which means that animations with short display times may appear jerky. It also cannot be used with LoadFromURL. You may find it more suitable to use TImageEnMView.<A TImageEnMView.Playing> instead which preloads all frames.
!!}
procedure TImageEnView.SetPlaying(const v : boolean);
// Xequte: 13/11/12
begin
if v = fPlaying then
exit;
fPlaying := v;
if v = False then
StopPlayTimer
else
StartPlayTimer;
end;
// Timer event for fPlayTimer
procedure TImageEnView.WMTimer(var Message: TWMTimer);
// Xequte: 13/11/12
begin
StopPlayTimer;
if IO.Params.ImageIndex >= IO.Params.ImageCount - 1 then
begin
IO.Seek(ieioSeekFirst);
if fPlayLoop = False then
begin
// After we have looped back (in case they want to play again)
Playing := False;
exit;
end;
end
else
begin
IO.Seek(ieioSeekNext);
end;
if IO.Aborting = False then
StartPlayTimer;
end;
// Enable fPlayTimer if the current file is an animated GIF or AVI
procedure TImageEnView.StartPlayTimer;
// Xequte: 13/11/12
var
iInterval: Integer;
begin
StopPlayTimer;
if fPlaying = False then
exit;
iInterval := 0;
if IO.Params.ImageCount > 1 then
begin
iInterval := IO.Params.ImageDelayTime;
if iInterval < 1 then
iInterval := Default_GIF_Animation_Delay_MS;
end;
if (iInterval > 0) and HasParentWindow then
fPlayTimer := SetTimer(self.handle, 1, iInterval, nil);
end;
// Reset fPlayTimer
procedure TImageEnView.StopPlayTimer;
// Xequte: 13/11/12
begin
// remove timer
if fPlayTimer <> 0 then
begin
KillTimer(self.handle, 1);
fPlayTimer := 0;
end;
end;
// Returns an adjusted rect taking into account the aspect ratio of the image and display window
// if TransitionActive it used whole client width, if false it excludes ruler area
function TImageEnView.AdjustRectToAspectRatio(ARect: TRect; TransitionActive: Boolean): TRect;
begin
if IsEmpty then
result := ARect
else
if TransitionActive then
result := IEAdjustRectToAspectRatio( ARect, fIEBitmap_Width, fIEBitmap_Height, ClientWidth, ClientHeight )
else
result := IEAdjustRectToAspectRatio( ARect, fIEBitmap_Width, fIEBitmap_Height, GetClientWidthExRulers, GetClientHeightExRulers );
end;
{!!
<FS>TImageEnView.AlphaChannel
<FM>Declaration<FC>
property AlphaChannel: <A TIEBitmap>;
<FM>Description<FN>
Some formats like GIF, PNG, PSD, TIFF, ICO, CUR and TGA contain an alpha channel that specifies the image's transparency.
The alpha channel is stored in the AlphaChannel property and in <A TImageEnView.IEBitmap>.<A TIEBitmap.AlphaChannel> property.
!!}
function TImageEnView.GetAlphaChannel: TIEBitmap;
begin
result := nil;
if fIEBitmapValid then
result := fIEBitmap.AlphaChannel;
end;
{!!
<FS>TImageEnView.HasAlphaChannel
<FM>Declaration<FC>
property HasAlphaChannel: Boolean; (Read-only)
<FM>Description<FN>
Returns True if the current image has an alpha channel.
!!}
function TImageEnView.GetHasAlphaChannel: boolean;
begin
result := False;
if fIEBitmapValid then
result := fIEBitmap.HasAlphaChannel;
end;
{!!
<FS>TImageEnView.InvertSelection
<FM>Declaration<FC>
procedure InvertSelection;
<FM>Description<FN>
InvertSelection changes the selection to everything except the current selection.
<FM>Example<FC>
ImageEnView1.Select(10, 10, 100, 100, iespReplace); // select box 10,10,100,100
ImageEnView1.InvertSelection; // select all excluding the box 10,10,100,100
!!}
procedure TImageEnView.InvertSelection;
begin
fSelectionMask.SyncFull;
if fSelectionMask.Full then
Deselect
else
begin
AnimPolygonClear(fHPolySel);
ShowSelectionEx(true);
fSelectionMask.Negative(fSelectionIntensity);
fUpdateBackBuffer := true;
Paint;
end;
end;
{!!
<FS>TImageEnView.EnableAlphaChannel
<FM>Declaration<FC>
property EnableAlphaChannel: Boolean;
<FM>Description<FN>
If EnableAlphaChannel is True, ImageEn uses the alpha channel to display the image.
Some formats like Gif, Png, Tiff, Ico, Cur and Tga contain an alpha channel that specifies the image's transparency.
The alpha channel is stored in the <A TImageEnView.AlphaChannel> property (a <A TIEBitmap> object) and in TIEBitmap.<A TIEBitmap.AlphaChannel> property.
!!}
procedure TImageEnView.SetEnableAlphaChannel(v: boolean);
begin
if fEnableAlphaChannel <> v then
begin
fEnableAlphaChannel := v;
Update;
end;
end;
{!!
<FS>TImageEnView.UpdateRect
<FM>Declaration<FC>
procedure UpdateRect(rclip: TRect);
<FM>Description<FN>
UpdateRect updates the rectangle, rclip. Use this function instead of Update when only a portion of the image has changed.
<FM>Example<FC>
// we assume that Zoom = 100
ImageEnView1.Bitmap.Canvas.Fill(0, 0, 10, 10);
ImageEnView1.UpdateRect(rect(0, 0, 10, 10));
!!}
procedure TImageEnView.UpdateRect(rclip: TRect);
begin
fUpdateBackBuffer := true;
InvalidateRect(handle, @rclip, false);
end;
{!!
<FS>TImageEnView.SetSelectedAreaAlpha
<FM>Declaration<FC>
procedure SetSelectedAreaAlpha(Alpha: Integer);
<FM>Description<FN>
Sets the Alpha value (transparency) for all pixels inside current selection.
To activate the alpha channel (transparency), set <A TImageEnView.EnableAlphaChannel> to True.
<FM>Example<FC>
// set transparency to 180 inside 10,10,100,100 rectangle
ImageEnView1.Select(10, 10, 100, 100);
ImageEnVIew1.SetSelectedAreaAlpha(180);
ImageEnView1.io.SaveToFile('C:\image.png'); // save with alpha channel
!!}
procedure TImageEnView.SetSelectedAreaAlpha(Alpha: integer);
var
y, x: integer;
palpha: pbyte;
psel: pbyte;
begin
if fIEBitmapValid = False then
exit;
if fSelectionMask.IsEmpty then
// entire image
GetAlphaChannel.Fill(Alpha)
else
begin
// selected area
for y := 0 to fSelectionMask.Height - 1 do
begin
palpha := fIEBitmap.AlphaChannel.ScanLine[y];
psel := fSelectionMask.ScanLine[y];
case fSelectionMask.BitsPerPixel of
1:
for x := 0 to fSelectionMask.Width - 1 do
begin
if (pbytearray(psel)^[x shr 3] and iebitmask1[x and $7]) <> 0 then
palpha^ := Alpha;
inc(palpha);
end;
8:
for x := 0 to fSelectionMask.Width - 1 do
begin
if psel^ <> 0 then
palpha^ := Alpha;
inc(palpha);
inc(psel);
end;
end;
end;
fIEBitmap.AlphaChannel.Full := false;
end;
Update;
end;
{!!
<FS>TImageEnView.SetSelectionGripStyle
<FM>Declaration<FC>
procedure SetSelectionGripStyle(GripColor1, GripColor2: TColor; GripBrushStyle: TBrushStyle; GripSize: Integer; ExtendedGrips: Boolean; boolean; Shape: <A TIEGripShape>);
<FM>Description<FN>
SetSelectionGripStyle determines the appearance of selection grips.
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>GripColor1<FN></C> <C>grip border color (default clBlack)</C> </R>
<R> <C><FC>GripColor2<FN></C> <C>grip brush color (default clWhite)</C> </R>
<R> <C><FC>GripBrushStyle<FN></C> <C>brush style (default bsSolid)</C> </R>
<R> <C><FC>GripSize<FN></C> <C>size in pixels of the grip (default 5)</C> </R>
<R> <C><FC>ExtendedGrips<FN></C> <C>if true enables grips on border with 8 resizing grips (deault true)</C> </R>
<R> <C><FC>Shape<FN></C> <C>specifies the grip shape</C> </R>
</TABLE>
Use <A TImageEnView.GetSelectionGripStyle> to know current values.
<FM>Example<FC>
ImageEnView1.SetSelectionGripStyle(clWhite, clWhite, bsSolid, 5, true, iegsCircle);
!!}
procedure TImageEnView.SetSelectionGripStyle(GripColor1, GripColor2: TColor; GripBrushStyle: TBrushStyle; GripSize: integer; ExtendedGrips: boolean; Shape: TIEGripShape);
begin
fGripColor1 := GripColor1;
fGripColor2 := GripColor2;
fGripBrushStyle := GripBrushStyle;
fGripSize := GripSize;
fGripShape := Shape;
fExtendedGrips := ExtendedGrips;
end;
{!!
<FS>TImageEnView.GetSelectionGripStyle
<FM>Declaration<FC>
procedure GetSelectionGripStyle(var GripColor1: TColor; var GripColor2: TColor; var GripBrushStyle: TBrushStyle; var GripSize: integer; var ExtendedGrips: boolean; var Shape: TIEGripShape);
<FM>Description<FN>
GetSelectionGripStyle returns properties which determines the appearance of selection grips.
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>GripColor1<FN></C> <C>grip border color (default clBlack)</C> </R>
<R> <C><FC>GripColor2<FN></C> <C>grip brush color (default clWhite)</C> </R>
<R> <C><FC>GripBrushStyle<FN></C> <C>brush style (default bsSolid)</C> </R>
<R> <C><FC>GripSize<FN></C> <C>size in pixels of the grip (default 5)</C> </R>
<R> <C><FC>ExtendedGrips<FN></C> <C>if true enables grips on border with 8 resizing grips (default: false)</C> </R>
<R> <C><FC>Shape<FN></C> <C>specifies the grip shape</C> </R>
</TABLE>
Use <A TImageEnView.SetSelectionGripStyle> to set current values.
!!}
procedure TImageEnView.GetSelectionGripStyle(var GripColor1: TColor; var GripColor2: TColor; var GripBrushStyle: TBrushStyle; var GripSize: integer; var ExtendedGrips: boolean; var Shape: TIEGripShape);
begin
GripColor1 := fGripColor1;
GripColor2 := fGripColor2;
GripBrushStyle := fGripBrushStyle;
GripSize := fGripSize;
Shape := fGripShape;
ExtendedGrips := fExtendedGrips;
end;
{!!
<FS>TImageEnView.SetLayersGripStyle
<FM>Declaration<FC>
procedure SetLayersGripStyle(GripColor1, GripColor2: TColor; GripBrushStyle: TBrushStyle; GripSize: Integer; Shape: <A TIEGripShape>);
<FM>Description<FN>
SetLayersGripStyle determines the appearance of layers grips.
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>GripColor1<FN></C> <C>grip border color</C> </R>
<R> <C><FC>GripColor2<FN></C> <C>grip brush color</C> </R>
<R> <C><FC>GripBrushStyle<FN></C> <C>brush style</C> </R>
<R> <C><FC>GripSize<FN></C> <C>size in pixels of the grip</C> </R>
<R> <C><FC>Shape<FN></C> <C>specifies the grip shape</C> </R>
</TABLE>
<FM>Example<FC>
ImageEnView1.SetLayersGripStyle(clWhite, clWhite, bsSolid, 5, iegsCircle);
!!}
procedure TImageEnView.SetLayersGripStyle(GripColor1, GripColor2: TColor; GripBrushStyle: TBrushStyle; GripSize: integer; Shape: TIEGripShape);
begin
fLyrGripColor1 := GripColor1;
fLyrGripColor2 := GripColor2;
fLyrGripBrushStyle := GripBrushStyle;
fLyrGripSize := GripSize;
fLyrGripShape := Shape;
end;
{!!
<FS>TImageEnView.SetChessboardStyle
<FM>Declaration<FC>
procedure SetChessboardStyle(Size: Integer; BrushStyle: TBrushStyle = bsSolid; Color1: TColor = clNone; Color2: TColor = clNone);
<FM>Description<FN>
Sets the size and brush of the chessboard background (when <A TImageEnView.BackgroundStyle> is iebsChessboard).
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>Size<FN></C> <C>Specifies the box size (default 16)</C> </R>
<R> <C><FC>BrushStyle<FN></C> <C>Specifies the brush style of the boxes (default bsSolid)</C> </R>
<R> <C><FC>Color1<FN></C> <C>Color 1 of chessboard. Specifying this as something other than clNone will set <A TImageEnView.Background></C> </R>
<R> <C><FC>Color2<FN></C> <C>Color 2 of chessboard. If specified as clNone, then color 2 will be a reverse of Color 1</C> </R>
</TABLE>
<FM>Example<FC>
// Set small chessboard background of Yellow and Blue
ImageEnView1.SetChessboardStyle( 5, bsSolid, clBlue, clYellow );
ImageEnView1.BackgroundStyle := iebsChessboard;
!!}
procedure TImageEnView.SetChessboardStyle(Size: integer = 16; BrushStyle: TBrushStyle = bsSolid; Color1: TColor = clNone_; Color2: TColor = clNone_);
begin
fChessboardSize := Size;
fChessboardBrushStyle := BrushStyle;
fChessboardColor2Customized := Color2 <> clNone_;
if Color1 <> clNone_ then
Background := Color1;
if Color2 <> clNone_ then
GradientEndColor := Color2;
end;
{!!
<FS>TImageEnView.ApplyBitmapToSelection
<FM>Declaration<FC>
procedure ApplyBitmapToSelection(SrcBitmap: TBitmap; MaintainAspectRatio: Boolean = True; CanStretch: Boolean = False);
procedure ApplyBitmapToSelection(SrcBitmap: <A TIEBitmap>; MergeAlpha: Boolean = True; MaintainAspectRatio: Boolean = True; CanStretch: Boolean = False);
<FM>Description<FN>
Applies a bitmap to the selected region, stretching the image to the selection size.
<FC>MergeAlpha<FN> will merge the alpha channel of the pasted bitmap with the background bitmap.
If <FC>MaintainAspectRatio<FN> is False the inserted image will fill the entire selection. Set <FC>MaintainAspectRatio<FN> to true to maintain the aspect ratio of the original bitmap. <FC>CanStretch<FN> determines whether a source image smaller than the selection is enlarged or maintains its original size.
Note: <FC>CanStretch<FN> has no effect if <FC>MaintainAspectRatio<FN> is False (image will always be stretched)
<FM>Example<FC>
// Flip just the selected portion of the image
aIEBitmap := TIEBitmap.create;
ImageEnView1.CopySelectionToBitmap( aIEBitmap );
aIEBitmap.Flip( fdVertical );
ImageEnView1.ApplyBitmapToSelection( aIEBitmap );
aIEBitmap.Free;
// PARAMETER CHANGE EXAMPLES
// Load Source Image
aIEBitmap.Read( 'C:\Source.png' );
<IMG help_images\PasteSource.png>
ImageEnView1.ApplyBitmapToSelection( aIEBitmap, True, False, False );
<IMG help_images\PasteStretch.png>
ImageEnView1.ApplyBitmapToSelection( aIEBitmap, True, True, False );
<IMG help_images\PasteAR.png>
ImageEnView1.ApplyBitmapToSelection( aIEBitmap, True, True, True );
<IMG help_images\PasteStretchAR.png>
<FM>See Also<FN>
- <A TImageEnView.CopySelectionToBitmap>
!!}
procedure TImageEnView.ApplyBitmapToSelection(SrcBitmap: TBitmap; MaintainAspectRatio: Boolean = True; CanStretch: Boolean = False);
var
TempBmp: TIEBitmap;
begin
TempBmp := TIEBitmap.Create;
if SrcBitmap.PixelFormat <> pf1bit then
SrcBitmap.PixelFormat := pf24bit;
TempBmp.EncapsulateTBitmap( SrcBitmap, true );
ApplyBitmapToSelection( TempBmp, False, MaintainAspectRatio, CanStretch );
FreeAndNil( TempBmp );
end;
procedure TImageEnView.ApplyBitmapToSelection(SrcBitmap: TIEBitmap; MergeAlpha: Boolean = True; MaintainAspectRatio: Boolean = True; CanStretch: Boolean = False);
var
iSX1, iSY1, iSX2, iSY2: Integer;
aBitmap: TIEBitmap;
Sz: TPoint;
begin
if (fIEBitmapValid = False) or (SrcBitmap.Width = 0) or (SrcBitmap.Height = 0) then
exit;
if assigned(fBitmap) then
fIEBitmap.EncapsulateTBitmap(fBitmap, false); // synchronize fBitmap with fIEBitmap
// Full image
iSX1 := 0;
iSY1 := 0;
iSX2 := fIEBitmap.Width;
iSY2 := fIEBitmap.Height;
// Selection
if Selected and not SelectionMask.IsEmpty then
begin
iSX1 := SelectionMask.X1;
iSY1 := SelectionMask.Y1;
iSX2 := SelectionMask.X2 + 1;
iSY2 := SelectionMask.Y2 + 1;
end;
aBitmap := TIEBitmap.Create();
try
if MaintainAspectRatio then
begin
Sz := GetImageSizeWithinArea( SrcBitmap.Width, SrcBitmap.Height, iSX2 - iSX1, iSY2 - iSY1, CanStretch );
iSX2 := iSX1 + Sz.X;
iSY2 := iSY1 + Sz.Y;
end;
aBitmap.Allocate( iSX2 - iSX1, iSY2 - iSY1, SrcBitmap.PixelFormat );
_IEBmpStretchEx( SrcBitmap, aBitmap, nil, nil );
if SrcBitmap.HasAlphaChannel then
begin
_IEBmpStretchEx( SrcBitmap.AlphaChannel, aBitmap.AlphaChannel, nil, nil );
if MergeAlpha then
SelectionMask.CombineWithAlpha( aBitmap.AlphaChannel, SelectionMask.x1, SelectionMask.y1, false );
end;
aBitmap.CopyWithMask2( fIEBitmap, SelectionMask );
finally
FreeAndNil(aBitmap);
end;
Update();
end;
{!!
<FS>TImageEnView.CopySelectionToBitmap
<FM>Declaration<FC>
procedure CopySelectionToBitmap(DestBitmap: TBitmap; FillBackground: Boolean = True); overload;
procedure CopySelectionToBitmap(DestBitmap: <A TIEBitmap>; FillBackground: Boolean = True); overload;
<FM>Description<FN>
Copies the current selection to the specified bitmap.
If <FC>FillBackground<FN> is enabled, then non-selected areas of the copied rectangle of the image will be filled with the <L TImageEnView.Background>background color</L>. Otherwise, it is transferred as alpha.
<FM>Examples<FC>
// Overload 1
ImageEnView1.CopySelectionToBitmap( ImageEnView2.Bitmap );
// Overload 2
ImageEnView1.CopySelectionToBitmap( ImageEnView2.IEBitmap );
// If an area of the image is selected, print the selection, otherwise print the whole image
procedure TForm1.PrintImageClick(Sender: TObject);
var
bmp: TIEBitmap;
IO: TImageEnIO;
begin
if ImageEnView1.Selected = False then
ImageEnView1.IO.DoPrintPreviewDialog( iedtDialog, '' )
else
begin
bmp := TIEBitmap.Create;
IO := TImageEnIO.CreateFromBitmap( bmp );
try
ImageEnView1.CopySelectionToBitmap( bmp );
IO.DoPrintPreviewDialog( iedtDialog, '' );
finally
IO.Free;
bmp.Free;
end;
end;
end;
<FM>See Also<FN>
- <A TImageEnView.ApplyBitmapToSelection>
!!}
// note: doesn't copy alpha channel
procedure TImageEnView.CopySelectionToBitmap(DestBitmap: TBitmap; FillBackground: Boolean = True);
var
tempbmp: TIEBitmap;
begin
if fIEBitmapValid = False then
raise EIEException.create( 'Method only supported for image layers' );
tempbmp := TIEBitmap.Create;
if DestBitmap.PixelFormat<>pf1bit then
DestBitmap.PixelFormat := pf24bit;
tempbmp.EncapsulateTBitmap(DestBitmap, true);
CopySelectionToBitmap(tempbmp, FillBackground);
FreeAndNil(tempbmp);
end;
procedure TImageEnView.CopySelectionToBitmap(DestBitmap: TIEBitmap; FillBackground: Boolean = True);
begin
if fIEBitmapValid = False then
raise EIEException.create( 'Method only supported for image layers' );
if EnableAlphaChannel and not FillBackground then
fIEBitmap.AlphaChannel; // Ensure we have an alpha channel so transparency is copied
if (fRectResizing <> ieNone) or (fSelectMoving > -1) or fRectSelecting then
begin
fSelectionMask.Empty;
EndSelect;
end;
if FillBackground then
fIEBitmap.CopyWithMask1(DestBitmap, fSelectionMask, fBackground)
else
fIEBitmap.CopyWithMask1(DestBitmap, fSelectionMask);
end;
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 6.2.0
procedure TImageEnView.CopySelectionToIEBitmap(DestBitmap: TIEBitmap; FillBackground: Boolean = True);
begin
CopySelectionToBitmap( DestBitmap, FillBackground );
end;
{$endif}
{!!
<FS>TImageEnView.AssignSelTo
<FM>Declaration<FC>
procedure AssignSelTo(Dest: TPersistent);
<FM>Description<FN>
Assigns the selected area to the <FC>Dest<FN> object (can be a <A TImageEnView>, TBitmap or TImage).
<FM>Example<FC>
// Create a triangular selection
ImageEnView1.Deselect;
ImageEnView1.AddSelPoint(100, 100);
ImageEnView1.AddSelPoint(200, 100);
ImageEnView1.AddSelPoint(150, 50);
ImageEnView1.EndSelect;
// assign selection to ImageEnView2
ImageEnView1.AssignSelTo( ImageEnView2 );
// assign selection to mybitmap (TBitmap)
ImageEnView1.AssignSelTo( mybitmap );
!!}
// Assign selection to Dest
// Dest can be TImageEnView, TBitmap, TImage
// Copy also background
procedure TImageEnView.AssignSelTo(Dest: TPersistent);
var
di: TImageEnView;
db: tbitmap;
im: TImage;
begin
if fIEBitmapValid = False then
raise EIEException.create( 'Method only supported for image layers' );
if Dest is TImageEnView then
begin
di := Dest as TImageEnView;
if fSelectionMask.IsEmpty then
di.IEBitmap.Assign(fIEBitmap)
else
CopySelectionToBitmap(di.IEBitmap, true);
di.fBackground := Background;
di.Update;
di.ImageChange;
end
else
if Dest is TBitmap then
begin
db := Dest as TBitmap;
if fSelectionMask.IsEmpty then
IECopyBitmap(fBitmap, db)
else
CopySelectionToBitmap(db);
db.Modified := true;
end
else
if Dest is TImage then
begin
im := Dest as TImage;
if fSelectionMask.IsEmpty then
IECopyBitmap(fBitmap, im.picture.bitmap)
else
CopySelectionToBitmap(im.picture.bitmap);
im.picture.bitmap.Modified := true;
end;
end;
{!!
<FS>TImageEnView.CopyToBitmapWithAlpha
<FM>Declaration<FC>
procedure TImageEnView.CopyToBitmapWithAlpha(Dest: TBitmap; DestX, DestY: Integer);
<FM>Description<FN>
Copies the current image to the <FC>Dest<FN> bitmap, at <FC>DestX, DestY<FN> position.
If the image has an alpha channel, CopyToBitmapWithAlpha copies only the visible area.
Note: An exception will be raised if the image does not have an alpha channel.
<FM>Example<FC>
ImageEnView1.CopyToBitmapWithAlpha( ImageEnView2.Bitmap, 0, 0);
ImageEnView2.Update;
!!}
// Dest must be pf24bit
procedure TImageEnView.CopyToBitmapWithAlpha(Dest: TBitmap; DestX, DestY: integer);
var
dummy2, dummy3: PInteger;
begin
if fIEBitmapValid = False then
raise EIEException.create( 'Method only supported for image layers' );
if Dest.PixelFormat <> pf24bit then
Dest.PixelFormat := pf24bit;
if fIEBitmap.HasAlphaChannel then
begin
dummy2 := nil;
dummy3 := nil;
fIEBitmap.RenderToTBitmap(Dest, dummy2, dummy3, nil, DestX, DestY, fIEBitmap.Width, fIEBitmap.Height, 0, 0, fIEBitmap.Width, fIEBitmap.Height, true, false, 255, rfNone, true, ielNormal);
end
else
raise EIEException.create( 'Bitmap does not have an alpha channel' );
end;
{!!
<FS>TImageEnView.SetSelectedPixelsColor
<FM>Declaration<FC>
procedure SetSelectedPixelsColor(color: <A TRGB>);
<FM>Description<FN>
Sets selected pixels to the specified color.
<FM>Example<FC>
// select all pixels of a similar color to that 0, 0. Then fills all selected pixels with White.
ImageEnView.SelectMagicWand(0, 0, iespReplace);
ImageEnView.SetSelectedPixelsColor( CreateRGB(255, 255, 255) );
<FM>See Also<FN>
- <A CreateRGB>
- <A TRGB2TColor>
- <A TColor2TRGB>
!!}
procedure TImageEnView.SetSelectedPixelsColor(color: TRGB);
var
col, row: integer;
px: PRGB;
bitmapWidth, bitmapHeight: Integer;
begin
if fIEBitmapValid = False then
raise EIEException.create( 'Method only supported for image layers' );
bitmapWidth := fIEBitmap.Width;
bitmapHeight := fIEBitmap.Height;
if fIEBitmap.PixelFormat = ie24RGB then
begin
for row := 0 to bitmapHeight - 1 do
begin
px := fIEBitmap.Scanline[row];
for col := 0 to bitmapWidth - 1 do
begin
if fSelectionMask.IsPointInside(col, row) then
px^ := color;
inc(px);
end;
end;
Update;
end;
end;
{!!
<FS>TImageEnView.SetAlphaRangePixelsColor
<FM>Declaration<FC>
procedure SetAlphaRangePixelsColor(alphaMin, alphaMax: Integer; color: <A TRGB>);
<FM>Description<FN>
Sets all pixels that have an alpha channel value between alphaMin and alphaMax to the specified color.
<FM>Example<FC>
// this example loads an image with alpha channel, then paints all transparent pixels (0...254 alpha values) using White color, finally saves in a jpeg where we cannot save the alpha channel.
ImageEnView.IO.LoadFromFile('C:\test.png');
ImageEnView.SetAlphaRangePixelsColor(0, 254, CreateRGB(255, 255, 255));
ImageEnView.IO.SaveToFile('C:\output.jpg');
<FM>See Also<FN>
- <A CreateRGB>
- <A TRGB2TColor>
- <A TColor2TRGB>
!!}
procedure TImageEnView.SetAlphaRangePixelsColor(alphaMin, alphaMax: integer; color: TRGB);
var
col, row: integer;
px: PRGB;
al: pbyte;
bitmapWidth, bitmapHeight: Integer;
begin
if fIEBitmapValid = False then
raise EIEException.create( 'Method only supported for image layers' );
if not HasAlphaChannel then
exit;
bitmapWidth := fIEBitmap.Width;
bitmapHeight := fIEBitmap.Height;
for row := 0 to bitmapHeight - 1 do
begin
px := fIEBitmap.Scanline[row];
al := fIEBitmap.AlphaChannel.ScanLine[row];
for col := 0 to bitmapWidth - 1 do
begin
if (al^ >= alphaMin) and (al^ <= alphaMax) then
px^ := color;
inc(px);
inc(al);
end;
end;
Update;
end;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Layers
{!!
<FS>TImageEnView.LayersCount
<FM>Declaration<FC>
property LayersCount: Integer;
<FM>Description<FN>
LayersCount returns the number of layers.
<FM>Examples<FC>
// Clear the top-most layer
ImageEnView1.LayersCurrent := ImageEnView1.LayersCount - 1;
ImageEnView1.Clear;
// Show each of the layers as a thumbnail in a TImageEnMView
procedure Tfmain.RefreshLayerViewer;
var
i, idx: integer;
begin
ImageEnMView1.Clear;
for i := 0 to ImageEnView1.LayersCount - 1 do
begin
idx := ImageEnMView1.AppendImage;
ImageEnMView1.SetImage( idx, ImageEnView1.Layers[ i ].Bitmap );
ImageEnMView1.ImageTopText[ i ] := 'Layer ' + inttostr( i );
end;
// Highlight current layer
ImageEnMView1.SelectedImage := ImageEnView1.LayersCurrent;
end;
// Set rotation of all selected layers
ImageEnView1.LockUpdate;
for i := 0 to ImageEnView1.LayersCount - 1 do
if ImageEnView1Layers[ I ].Selected then
ImageEnView1Layers[ I ].Rotate := 90;
ImageEnView1.LayersFixRotations( LYR_SELECTED_LAYERS );
ImageEnView1.UnlockUpdate;
!!}
function TImageEnView.GetLayersCount: integer;
begin
result := fLayers.Count;
end;
function TImageEnView.GetLayer(idx: integer): TIELayer;
begin
result := TIELayer(fLayers[idx]);
end;
{!!
<FS>TImageEnView.CurrentLayer
<FM>Declaration<FC>
property CurrentLayer: <A TIELayer>;
<FM>Description<FN>
Provides access to the currently active layer.
This is equivalent to:
<FC>ImageEnView.Layers[ ImageEnView.LayersCurrent ]<FN>
See the <A TImageEnView.Layers> property for more info.
Note: Use <A TImageEnView.LayersCurrent> to get or set the current layer index. Use <FC>CurrentLayer<FN> to return the layer object.
<FM>Example<FC>
ImageEnView.LayersAdd;
ImageEnView.CurrentLayer.Transparency := 200;
// Remove the fill from the current layer
ImageEnView1.CurrentLayer.FillColor := clNone;
ImageEnView1.Update();
<IMG help_images\Shape_NoFill.jpg>
!!}
function TImageEnView.GetCurrentLayer: TIELayer;
begin
result := TIELayer(fLayers[fLayersCurrent]);
end;
{!!
<FS>TImageEnView.LayersCurrent
<FM>Declaration<FC>
property LayersCurrent: Integer;
<FM>Description<FN>
Use <FC>LayersCurrent<FC> to get/set the active layer.
The first layer has the index of 0, the last is <A TImageEnView.LayersCount> - 1.
Making a layer current changes the <A TImageEnView.IEBitmap> and <A TImageEnView.Bitmap> properties, so they point to the current layer (allowing specification of which layer is active for input/output and image processing operations).
Notes:
- Use <A TImageEnView.CurrentLayer> to return the layer object. Use <FC>LayersCurrent<FC> to get or set the current layer index
- Setting LayersCurrent does NOT deselect existing layers (if <L TImageEnView.LayerOptions>multiple layer selection</L> is enabled)
<FM>Example<FC>
// load 'first.jpg' in layer 0 and 'second.jpg' in layer 1
ImageEnView1.LayersCurrent := 0;
ImageEnView1.IO.LoadFromFile('C:\first.jpg');
ImageEnView1.LayersCurrent := 1;
ImageEnView1.IO.LoadFromFile('C:\second.jpg');
// Clear the top-most layer
ImageEnView1.LayersCurrent := ImageEnView1.LayersCount - 1;
ImageEnView1.Clear;
// Flip the 2nd layer
ImageEnView1.LayersCurrent := 1;
ImageEnView1.Proc.Flip( fdHorizontal );
!!}
procedure TImageEnView.SetLayersCurrent(Value: integer);
begin
SetLayersCurrentEx( Value, False, False );
end;
// Result is true if selection has changed
function TImageEnView.SetLayersCurrentEx(Value: integer; bUserAction, bDeselectAll: Boolean; DoUpdate: Boolean = True): Boolean;
var
i: Integer;
needUpdate: Boolean;
begin
needUpdate := False;
if LayersAllowMultiSelect and bDeselectAll then
for i := 0 to LayersCount - 1 do
if ( i <> Value ) and TIELayer( fLayers[ I ]).fSelected then
begin
TIELayer( fLayers[ i ]).fSelected := False;
if bUserAction then
DoLayerNotify( i, ielDeselected );
needUpdate := True;
end;
if (Value >= 0) and (Value < fLayers.Count) and (Value <> fLayersCurrent) then
begin
// sync current layer (fLayersCurrent = -1 is a temporary state, where no layer is selected)
if (fLayersCurrent > -1) and (fLayersCurrent < fLayers.Count) then
begin
SyncBitmapToCurrentLayer();
if fSel then
SaveSelection;
end;
// set new layer
while true do
begin
fLayersCurrent := Value;
if (Layers[Value].Selectable = true) or (Value = 0) or (fLayersSelectConstrains = false) then
break;
dec(Value);
end;
// Non-image layers have no bitmap
fIEBitmapValid := Layers[Value] is TIEImageLayer;
if fIEBitmapValid then
fIEBitmap := Layers[Value].Bitmap
else
fIEBitmap := Layers[0].Bitmap;
if fIEBitmap.EncapsulatedFromTBitmap then
fBitmap := fIEBitmap.VclBitmap
else
fBitmap := nil;
if fSel then
RestoreSelection(true, iersSyncLayers);
CallBitmapChangeEvents;
needUpdate := True;
end;
if LayersAllowMultiSelect and ( Value >= 0 ) and ( Value < fLayers.Count ) and ( Layers[Value].fSelected = False ) then
begin
Layers[ Value ].fSelected := True;
SelectByGroupIndex( Layers[ Value ].GroupIndex, True, bUserAction );
SelectMaskOfLayer( Value, True, bUserAction );
needUpdate := True;
end;
if needUpdate and DoUpdate then
begin
if bUserAction and ( fLayersFastDrawing = iefDelayed ) then
fStable := fDelayZoomTicks
else
Update;
end;
Result := needUpdate;
end;
// Sets all layers of a groupIndex to selected or unselected
// DOES NOT CALL UPDATE
procedure TImageEnView.SelectByGroupIndex(iGroupIndex: Integer; bSelect: Boolean; bUserAction: Boolean);
var
i: Integer;
begin
if iGroupIndex = 0 then
exit;
for i := 0 to LayersCount - 1 do
if TIELayer( fLayers[ I ]).GroupIndex = iGroupIndex then
begin
TIELayer( fLayers[ i ]).fSelected := bSelect;
SelectMaskOfLayer( i, bSelect, bUserAction );
if bUserAction then
begin
if bSelect then
DoLayerNotify( i, ielSelected )
else
DoLayerNotify( i, ielDeselected );
end;
end;
end;
// If there is a layer mask associated with the specified layer then set it selected or unselected
// DOES NOT CALL UPDATE
procedure TImageEnView.SelectMaskOfLayer(iLayerIndex: Integer; bSelect: Boolean; bUserAction: Boolean);
begin
if not ( loAutoSelectMask in fLayerOptions ) then
exit;
if ( iLayerIndex + 1 < fLayers.Count ) and
( TIELayer( fLayers[ iLayerIndex + 1 ]).IsMask ) and
( TIELayer( fLayers[ iLayerIndex + 1 ]).fSelected <> bSelect ) then
begin
TIELayer( fLayers[ iLayerIndex + 1 ]).fSelected := bSelect;
if bUserAction then
begin
if bSelect then
DoLayerNotify( iLayerIndex + 1, ielSelected )
else
DoLayerNotify( iLayerIndex + 1, ielDeselected );
end;
end;
end;
{!!
<FS>TImageEnView.LayersAdd
<FM>Declaration<FC>
// General overload
function LayersAdd(Kind: <A TIELayerKind> = ielkImage): integer; overload;
// Blank image overload (creates <A TIEImageLayer>)
function LayersAdd(Width: Integer; Height: Integer; PixelFormat: <A TIEPixelFormat> = ie24RGB; PosX: Integer = -1; PosY: Integer = -1): Integer;
// Assign bitmap overload (creates <A TIEImageLayer>)
function LayersAdd(Bitmap: <A TIEBitmap>): integer;
// Loads image overload (creates <A TIEImageLayer>)
function LayersAdd(FileName: String; PosX: Integer = -1; PosY: Integer = -1): integer;
// Shape overload (creates <A TIEShapeLayer>)
function LayersAdd(Shape: <A TIEShape>; PosX: Integer = -1; PosY: Integer = -1; Width: Integer = 0; Height: Integer = 0): integer;
// Text overload (creates <A TIETextLayer>)
function LayersAdd(Text: String; FontSize : Integer; FontColor : TColor; FontName : string; FontStyle : TFontStyles = []; PosX: Integer = -1; PosY: Integer = -1): integer;
<FM>Description<FN>
Appends a new layer to the layers list. The new layer will become the current layer.
First overload allows you to specify the <A TIELayer.Kind>layer type</L>, all others create a <A TIEImageLayer>.
If the size and pixel format are not specified then the new layer assumes that of the current layer.
You can specify PosX and PosY for the destination position of the layer. Pass as -1, -1 to use the next available position.
Result is the index of the added layer.
<FM>Examples<FC>
ImageEnView1.IO.LoadFromFile( 'C:\first.jpg' ); // Load image into first layer (or current layer)
ImageEnView1.LayersAdd(); // Append a new layer
ImageEnView1.IO.LoadFromFile( 'C:\second.jpg' ); // Load image into the new layer (now the current layer)
// Load an image from file and add it as a layer
ImageEnView1.LayersAdd( 'C:\MyImage.jpg' );
// Apply a "Paid" stamp to image
with ImageEnView1 do
begin
LayersAdd( 'PAID', 42, clRed, 'Arial Black', [fsBold] );
CurrentLayer.Rotate := 30;
TIETextLayer( CurrentLayer ).SizeToText();
CurrentLayer.PosX := IELayer_Pos_HCenter;
CurrentLayer.PosY := IELayer_Pos_VCenter;
LayersMergeAll();
end;
// Add a yellow explosion shape layer at size 220 x 120
ImageEnView1.LayersAdd( iesExplosion, 50, 50, 220, 120 );
ImageEnView1.CurrentLayer.FillColor := clYellow;
ImageEnView1.CurrentLayer.BorderWidth := 0;
ImageEnView1.Update();
<IMG help_images\Polyline_NoBorder.gif>
// Append an image layer and assign a pink border
ImageEnView1.LayersAdd( ielkImage ); // Append an image layer
ImageEnView1.IO.LoadFromFile('C:\New Zealand.jpg'); // Load image into the new/active layer
ImageEnView1.CurrentLayer.BorderColor := $008000FF;
ImageEnView1.CurrentLayer.BorderWidth := 3;
<IMG help_images\Image_withBorder.gif>
// Append a text layer
ImageEnView1.LayersAdd( ielkText );
TIETextLayer( ImageEnView1.CurrentLayer ).Text := 'This is a Text Layer';
TIETextLayer( ImageEnView1.CurrentLayer ).BorderColor := clBlack;
TIETextLayer( ImageEnView1.CurrentLayer ).BorderWidth := 1;
TIETextLayer( ImageEnView1.CurrentLayer ).FillColor := clWhite;
ImageEnView1.Update();
<IMG help_images\TextLayer.gif>
<FM>See Also<FN>
-<A TIELayer>
-<A TImageEnView.LayersInsert>
-<A TImageEnView.LayersCreateFromSelection>
-<A TImageEnView.LayersCreateFromClipboard>
-<A TImageEnView.LayersCreateFromFile>
!!}
function TImageEnView.LayersAddEx(Kind: TIELayerKind;
PosX: Integer = -1; PosY: Integer = -1;
Width: Integer = 0; Height: Integer = 0;
SrcBitmap: TIEBitmap = nil; DoCopyBitmap: Boolean = true;
DoSaveUndo: Boolean = False; AssignDefaults: Boolean = False;
SelLayer: Boolean = True
): Integer;
begin
Result := LayersInsertEx( fLayers.Count, Kind, PosX, PosY, Width, Height, SrcBitmap, DoCopyBitmap, DoSaveUndo, AssignDefaults, SelLayer );
end;
function TImageEnView.LayersAdd(Kind: TIELayerKind = ielkImage): integer;
begin
result := fLayers.Count;
LayersInsert( result, Kind );
end;
function TImageEnView.LayersAdd(Width: Integer; Height: Integer; PixelFormat: TIEPixelFormat = ie24RGB; PosX: Integer = -1; PosY: Integer = -1): Integer;
begin
result := fLayers.Count;
LayersInsert( result, Width, Height, PixelFormat, PosX, PosY );
end;
function TImageEnView.LayersAdd(FileName: WideString; PosX: Integer = -1; PosY: Integer = -1): Integer;
begin
result := fLayers.Count;
LayersInsert( result, FileName, PosX, PosY );
end;
function TImageEnView.LayersAdd(Bitmap: TIEBitmap; DoCopy: Boolean = True): integer;
begin
result := fLayers.Count;
LayersInsert( Result, Bitmap, DoCopy );
end;
function TImageEnView.LayersAdd(Shape: TIEShape; PosX: Integer = -1; PosY: Integer = -1; Width: Integer = 0; Height: Integer = 0): Integer;
begin
result := fLayers.Count;
LayersInsert( result, Shape, PosX, PosY, Width, Height );
end;
function TImageEnView.LayersAdd(const Text: String;
FontSize : Integer;
FontColor : TColor;
const FontName : string;
FontStyle : TFontStyles = [];
PosX: Integer = -1; PosY: Integer = -1): integer;
begin
result := fLayers.Count;
LayersInsert( result, Text, FontSize, FontColor, FontName, FontStyle, PosX, PosY );
end;
{!!
<FS>TImageEnView.LayersInsert
<FM>Declaration<FC>
// General overload
procedure LayersInsert(Position: Integer; Kind: <A TIELayerKind> = ielkImage); overload;
// Blank image overload (creates <A TIEImageLayer>)
procedure LayersInsert(Position: Integer; Width: Integer; Height: Integer; PixelFormat: <A TIEPixelFormat> = ie24RGB; PosX: Integer = 0; PosY: Integer = 0);
// Assign bitmap overload (creates <A TIEImageLayer>)
procedure LayersInsert(Position: Integer; Bitmap: <A TIEBitmap>);
// Loads image overload (creates <A TIEImageLayer>)
procedure LayersInsert(Position: Integer; FileName: String; PosX: Integer = -1; PosY: Integer = -1);
// Shape overload (creates <A TIEShapeLayer>)
procedure LayersInsert(Position: Integer; Shape: <A TIEShape>; PosX: Integer = -1; PosY: Integer = -1; Width: Integer = 0; Height: Integer = 0);
// Text overload (creates <A TIETextLayer>)
procedure LayersInsert(Position: Integer; Text: String; FontSize : Integer; FontColor : TColor; FontName : string; FontStyle : TFontStyles = []; PosX: Integer = -1; PosY: Integer = -1);
<FM>Description<FN>
Inserts a new layer into the layers list at the specified position. The new layer will become the current layer.
First overload allows you to specify the <A TIELayer.Kind>layer type</L>, all others create a <A TIEImageLayer>.
If the size and pixel format are not specified then the new layer assumes that of the current layer.
You can specify PosX and PosY for the destination position of the layer. Pass as -1, -1 to use the next available position.
Note: Only a <A TIEImageLayer> can be inserted at layer 0 (i.e. background). If you attempt to insert a non-image layer at position 0, it will be inserted at 1
<FM>Examples<FC>
ImageEnView1.IO.LoadFromFile( 'C:\first.jpg' ); // Load image into first layer (will become background)
ImageEnView1.LayersInsert( 1 ); // Insert a new layer above the background layer
ImageEnView1.IO.LoadFromFile( 'C:\second.jpg' ); // Load image into the new layer (now the current layer)
// Load an image from file and add it as a layer
ImageEnView1.LayersInsert( 1, 'C:\MyImage.jpg' );
// Insert a text layer
ImageEnView1.LayersInsert( 1, 'My text layer', 14, clBlack, 'Arial');
// Insert a yellow explosion shape layer at size 220 x 120
ImageEnView1.LayersInsert( 1, iesExplosion, 50, 50, 220, 120 );
ImageEnView1.CurrentLayer.FillColor := clYellow;
ImageEnView1.CurrentLayer.BorderWidth := 0;
ImageEnView1.Update();
<IMG help_images\Polyline_NoBorder.gif>
// Insert an image layer and assign a pink border
ImageEnView1.LayersAdd( 3, ielkImage ); // Insert an image layer after layer 2 (i.e. 3rd layer)
ImageEnView1.IO.LoadFromFile('C:\New Zealand.jpg'); // Load image into the new/active layer
ImageEnView1.CurrentLayer.BorderColor := $008000FF;
ImageEnView1.CurrentLayer.BorderWidth := 3;
<IMG help_images\Image_withBorder.gif>
// Insert a text layer
ImageEnView1.LayersAdd( 1, ielkText );
TIETextLayer( ImageEnView1.CurrentLayer ).Text := 'This is a Text Layer';
TIETextLayer( ImageEnView1.CurrentLayer ).BorderColor := clBlack;
TIETextLayer( ImageEnView1.CurrentLayer ).BorderWidth := 1;
TIETextLayer( ImageEnView1.CurrentLayer ).FillColor := clWhite;
ImageEnView1.Update();
<IMG help_images\TextLayer.gif>
<FM>See Also<FN>
-<A TIELayer>
-<A TImageEnView.LayersAdd>
-<A TImageEnView.LayersCreateFromSelection>
-<A TImageEnView.LayersCreateFromClipboard>
-<A TImageEnView.LayersCreateFromFile>
!!}
function TImageEnView.LayersInsertEx(Position: Integer;
Kind: TIELayerKind;
PosX: Integer = -1; PosY: Integer = -1;
Width: Integer = 0; Height: Integer = 0;
SrcBitmap: TIEBitmap = nil; DoCopyBitmap: Boolean = true;
DoSaveUndo: Boolean = False; AssignDefaults: Boolean = False;
SelLayer: Boolean = True
): Integer;
const
Layer_Positioning_Left_Margin = 40;
Layer_Positioning_Right_Margin = 50;
Layer_Positioning_Increment = 40;
var
bmp: TIEBitmap;
idx: Integer;
begin
SyncBitmapToCurrentLayer();
if DoSaveUndo then
case Kind of
ielkImage : Proc.SaveUndo( IEMsg( IEMsg_AddImageLayer ), ieuObjectsAndLayers, True, IEOP_ADDIMAGELAYER );
ielkShape : Proc.SaveUndo( IEMsg( IEMsg_AddShapeLayer ), ieuObjectsAndLayers, True, IEOP_ADDSHAPELAYER );
ielkLine : Proc.SaveUndo( IEMsg( IEMsg_AddLineLayer ), ieuObjectsAndLayers, True, IEOP_ADDLINELAYER );
ielkPolyline : Proc.SaveUndo( IEMsg( IEMsg_AddPolylineLayer ), ieuObjectsAndLayers, True, IEOP_ADDPOLYLINELAYER );
ielkText : Proc.SaveUndo( IEMsg( IEMsg_AddTextLayer ), ieuObjectsAndLayers, True, IEOP_ADDTEXTLAYER );
end;
Result := Position;
if ( Result < fLayers.Count ) and Layers[ Result ].IsMask then
dec( Result );
if ( Result = 0 ) and ( Kind <> ielkImage ) then
inc( Result ); // Base layer must be image
case Kind of
ielkImage : begin
if SrcBitmap <> nil then
begin
// Create layer from passed bitmap
if DoCopyBitmap then
begin
bmp := TIEBitmap.Create;
bmp.Assign( SrcBitmap );
fLayers.Insert( Position, TIEImageLayer.Create(self, bmp, true));
end
else
fLayers.Insert( Position, TIEImageLayer.Create(self, SrcBitmap, true));
end
else
begin
// Create blank bitmap layer
if ( Width > 0 ) and ( Height > 0 ) then
fLayers.Insert( Result, TIEImageLayer.Create( self, Width, Height ))
else
begin
fLayers.Insert( Result, TIEImageLayer.Create( self, fIEBitmap, false ));
if fIEBitmapValid = False then
begin
// inherit size of current object
Layers[ Result ].Width := fIEBitmap_Width;
Layers[ Result ].Height := fIEBitmap_Height;
end;
end;
// Clear bitmap
Layers[ Result ].Bitmap.Fill( fBackground );
Layers[ Result ].Bitmap.RemoveAlphaChannel( False, fBackground );
end;
end;
ielkShape : fLayers.Insert( Result, TIEShapeLayer.Create( self ));
ielkLine : begin
fLayers.Insert( Result, TIELineLayer.Create( self ));
end;
ielkPolyline : fLayers.Insert( Result, TIEPolylineLayer.Create( self ));
ielkText : fLayers.Insert( Result, TIETextLayer.Create( self ));
else raise Exception.create( 'Layer insertion error - Invalid Type: ' + IntToStr( ord( Kind )));
end;
if Width > 0 then
Layers[ Result ].Width := Width;
if Height > 0 then
Layers[ Result ].Height := Height;
if ( PosX <> -1 ) and ( PosY <> -1 ) then
begin
Layers[ Result ].PosX := PosX;
Layers[ Result ].PosY := PosY;
end
else
if ( Layer_Positioning_Left_Margin + Layers[ Result ].Width + Layer_Positioning_Right_Margin < ClientWidth / fZoomD100X ) and
( Layer_Positioning_Left_Margin + Layers[ Result ].Height + Layer_Positioning_Right_Margin < ClientHeight / fZoomD100Y ) then
begin
inc( fNextLayerPosition, Layer_Positioning_Increment );
if ( fNextLayerPosition + Layers[ Result ].Width + Layer_Positioning_Right_Margin > ClientWidth / fZoomD100X ) or
( fNextLayerPosition + Layers[ Result ].Height + Layer_Positioning_Right_Margin > ClientHeight / fZoomD100Y ) then
fNextLayerPosition := Layer_Positioning_Left_Margin;
Layers[ Result ].PosX := fNextLayerPosition;
Layers[ Result ].PosY := fNextLayerPosition;
end;
If AssignDefaults and ( fLayerDefaults <> nil ) then
Layers[ Result ].SetProperties( fLayerDefaults );
idx := Result;
if not SelLayer then
idx := fLayersCurrent;
fLayersCurrent := -1;
SetLayersCurrentEx( idx, False, True ); // this calls update
end;
procedure TImageEnView.LayersInsert(Position: Integer; Kind: TIELayerKind = ielkImage);
begin
LayersInsertEx( Position, Kind, -1, -1, 0, 0, nil, False, Proc.AutoUndo and ( loAutoUndoChangesByCode in fLayerOptions ), True );
if assigned( fOnNewLayer ) then
fOnNewLayer( Self, Position, CurrentLayer.Kind );
ImageChange();
end;
procedure TImageEnView.LayersInsert(Position: Integer; Width: Integer; Height: Integer; PixelFormat: TIEPixelFormat = ie24RGB; PosX: Integer = -1; PosY: Integer = -1);
begin
LayersInsertEx( Position, ielkImage, PosX, PosY, -1, -1, TIEBitmap.Create( Max( 1, Width ), Max( 1, Height ), PixelFormat), False, Proc.AutoUndo and ( loAutoUndoChangesByCode in fLayerOptions ), True );
if assigned( fOnNewLayer ) then
fOnNewLayer( Self, Position, CurrentLayer.Kind );
ImageChange();
end;
procedure TImageEnView.LayersInsert(Position: Integer; FileName: WideString; PosX: Integer = -1; PosY: Integer = -1);
begin
LayersInsertEx( Position, ielkImage, PosX, PosY, 0, 0, nil, False, Proc.AutoUndo and ( loAutoUndoChangesByCode in fLayerOptions ), True );
IO.LoadFromFile( FileName );
if assigned( fOnNewLayer ) then
fOnNewLayer( Self, Position, CurrentLayer.Kind );
ImageChange();
end;
procedure TImageEnView.LayersInsert(Position: Integer; Bitmap: TIEBitmap; DoCopy: Boolean = True);
begin
LayersInsertEx( Position, ielkImage, -1, -1, 0, 0, Bitmap, DoCopy, Proc.AutoUndo and ( loAutoUndoChangesByCode in fLayerOptions ), True );
if assigned( fOnNewLayer ) then
fOnNewLayer( Self, Position, CurrentLayer.Kind );
ImageChange();
end;
procedure TImageEnView.LayersInsert(Position: Integer; Shape: TIEShape; PosX: Integer = -1; PosY: Integer = -1; Width: Integer = 0; Height: Integer = 0);
begin
LayersInsertEx( Position, ielkShape, PosX, PosY, Width, Height, nil, False, Proc.AutoUndo and ( loAutoUndoChangesByCode in fLayerOptions ), True );
TIEShapeLayer( CurrentLayer ).Shape := Shape;
if assigned( fOnNewLayer ) then
fOnNewLayer( Self, Position, CurrentLayer.Kind );
ImageChange();
end;
procedure TImageEnView.LayersInsert(Position: Integer;
const Text: String;
FontSize : Integer;
FontColor : TColor;
const FontName : string;
FontStyle : TFontStyles = [];
PosX: Integer = -1; PosY: Integer = -1);
begin
LayersInsertEx( Position, ielkText, PosX, PosY, 0, 0, nil, False, Proc.AutoUndo and ( loAutoUndoChangesByCode in fLayerOptions ), True );
TIETextLayer( CurrentLayer ).Text := Text;
if FontName <> '' then
TIETextLayer( CurrentLayer ).Font.Name := FontName;
if FontSize > 0 then
TIETextLayer( CurrentLayer ).Font.Size := FontSize;
if FontColor <> clNone_ then
TIETextLayer( CurrentLayer ).Font.Color := FontColor;
if FontStyle <> [] then
TIETextLayer( CurrentLayer ).Font.Style := FontStyle;
TIETextLayer( CurrentLayer ).SizeToText();
if assigned( fOnNewLayer ) then
fOnNewLayer( Self, Position, CurrentLayer.Kind );
ImageChange();
end;
{!!
<FS>TImageEnView.LayersRemove
<FM>Declaration<FC>
procedure LayersRemove(LyrIndex: Integer = LYR_SELECTED_LAYERS);
<FM>Description<FN>
Removes a layer and frees the related bitmap. You can specify the index of a layer of <FC>LYR_SELECTED_LAYERS<FN> to remove all selected layers.
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>idx<FN></C> <C>Index of the layer to remove (0 = background/first layer). You can also specify <FC>LYR_SELECTED_LAYERS<FN> or <FC>LYR_ALL_LAYERS<FN> </C> </R>
</TABLE>
Note: At least one layer must be present. Attempting to remove the final layer will empty its content.
<FM>Example<FC>
// Remove all selected layers
ImageEnView1.LayersRemove();
// Remove the top-most layer
ImageEnView1.LayersRemove( ImageEnView1.LayersCount - 1 );
<FM>See Also<FN>
- <A TImageEnView.LayersClear>
!!}
// one layer musts remain
procedure TImageEnView.LayersRemove(LyrIndex: Integer = LYR_SELECTED_LAYERS);
begin
LayersRemoveEx( LyrIndex, Proc.AutoUndo and ( loAutoUndoChangesbyCode in fLayerOptions ));
end;
procedure TImageEnView.LayersRemoveEx(LyrIndex: Integer = LYR_SELECTED_LAYERS; SaveUndo: Boolean = False);
var
idx: Integer;
iWasLC: Integer;
removeLayer: Boolean;
begin
if LyrIndex = LYR_ALL_LAYERS then
begin
LayersClear();
exit;
end;
if ( LyrIndex <> LYR_SELECTED_LAYERS ) and
(( LyrIndex < 0 ) or ( LyrIndex >= fLayers.Count )) then
exit; { Invalid selection }
if SaveUndo then
Proc.SaveUndo( IEMsg( IEMsg_RemoveLayer ), ieuObjectsAndLayers, True, IEOP_REMOVELAYER );
if ( LyrIndex = LYR_SELECTED_LAYERS ) and ( LayersAllowMultiSelect = False ) then
LyrIndex := LayersCurrent;
iWasLC := fLayersCurrent;
SyncBitmapToCurrentLayer();
for idx := fLayers.Count - 1 downto 0 do
begin
if LyrIndex = LYR_SELECTED_LAYERS then
removeLayer := TIELayer( fLayers[ idx ]).Selected
else
removeLayer := idx = LyrIndex;
if removeLayer and ( idx = 0 ) and (( fLayers.Count = 1 ) or ( Layers[ idx + 1 ].Kind <> ielkImage )) then
begin
// Can't delete this layer (either last layer, or layer above is not an image), so just clear
TIEImageLayer( Layers[ idx ]).Clear();
end
else
if removeLayer then
begin
// free idx + 1 layer if it is a layer mask
if ( idx < fLayers.Count - 1 ) and Layers[ idx + 1 ].IsMask then
begin
Layers[ idx + 1 ].Free;
fLayers.Delete( idx + 1 );
end;
// free idx layer
Layers[ idx ].Free;
fLayers.Delete( idx );
if ( fLayersCurrent = idx ) or ( fLayersCurrent >= fLayers.Count )then
fLayersCurrent := -1
else
if idx < fLayersCurrent then
dec( fLayersCurrent );
if LyrIndex <> LYR_SELECTED_LAYERS then
Break; { No more to process }
end;
end;
// set new current layer
if fLayersCurrent = -1 then
begin
SetLayersCurrent( imin( iWasLC, fLayers.Count - 1 ));
end
else
if fLayersCurrent > iWasLC then
begin
fLayersCurrent := -1;
SetLayersCurrent( iWasLC - 1 );
end
else
if fLayersCurrent = 0 then
Update();
ImageChange();
end;
{!!
<FS>TImageEnView.LayersClear
<FM>Declaration<FC>
procedure LayersClear;
<FM>Description<FN>
Removes all layers.
Note: After completion only the background layer will remain (as an ImageEnView always requires at least one layer).
<FM>Comparison of Methods<FN>
<TABLE>
<R> <H>Method</H> <H>Description</H> </R>
<R> <C> <A TImageEnView.Clear> </C> <C> Fills the current image with the <A TImageEnView.Background>background color</L> and <L TImageEnView.RemoveAlphaChannel>removes</L> the alpha channel </C> </R>
<R> <C> <A TImageEnView.Blank> </C> <C> Calls <A TImageEnView.Clear> and resets the image size to 1 x 1</C> </R>
<R> <C> <A TImageEnView.LayersClear> </C> <C> Removes all of the <A TImageEnView.Layers>layers</L> </C> </R>
<R> <C> <A TImageEnView.ClearAll> </C> <C> Resets the image (calling <A TImageEnView.Blank>) and removes all layers (calling <A TImageEnView.LayersClear>) </C> </R>
</TABLE>
<FM>See Also<FN>
- <A TImageEnView.LayersRemove>
!!}
// remove all layers
procedure TImageEnView.LayersClear;
var
i: Integer;
begin
for i := fLayers.Count - 1 downto 1 do
begin
Layers[i].Free;
fLayers.Delete(i);
end;
fLayersCurrent := -1;
SetLayersCurrent( 0 );
Layers[0].SetDefaults;
with Layers[0] do
begin
VisibleBox := false;
Locked := true;
end;
Clear;
end;
{!!
<FS>TImageEnView.LayersMove
<FM>Declaration<FC>
procedure LayersMove(CurIndex, NewIndex: Integer);
<FM>Description<FN>
Moves the layer at index, <FC>CurIndex<FN>, to the position, <FC>NewIndex<FN>.
You can specify <FC>LYR_SELECTED_LAYERS<FN> (-2) for <FC>CurIndex<FN> to move all selected layers.
<FC>NewIndex<FN> can be one of the following:
<TABLE>
<R> <C>>= 0</C> <C>The new insertion index</C> </R>
<R> <C>IEN_Send_To_Back</C> <C>The layer will become the new background layer (layer 0)</C> </R>
<R> <C>IEN_Send_Backward</C> <C>The layer will move closer to the background</C> </R>
<R> <C>IEN_Bring_Forward</C> <C>The layer will move closer to the foreground</C> </R>
<R> <C>IEN_Bring_To_Front</C> <C>The layer will become the topmost one (in front of all others)</C> </R>
</TABLE>
Notes:
- If the layer has a mask it will be moved too
- Only a <A TIEImageLayer> can be moved to layer 0 (i.e. background). If you attempt to move a non-image layer to position 0, it will be moved to 1
- Use <FC>LayersMove<FN> to arrange layer order. Use <A TImageEnView.LayersRepositionAll> to change the position of layers
<FM>Example<FC>
// Move the current layer forward
ImageEnView1.LayersMove( ImageEnView1.LayersCurrent, IEN_Bring_Forwards );
// Move selected layers backward
ImageEnView1.LayersMove( LYR_SELECTED_LAYERS, IEN_Send_Backward );
// Move the selected layers to the back (but not replace the background layer
ImageEnView1.LayersMove( LYR_SELECTED_LAYERS, 1 );
!!}
procedure TImageEnView.LayersMove(CurIndex, NewIndex: integer);
var
curLayer: TIELayer;
iLyrListLen: integer;
LyrList: array of TIELayer;
i: Integer;
layerMask, movingLayer: TIELayer;
a, b: Integer;
allImageLayers: Boolean;
bInclude: Boolean;
begin
// LYR_ALL_LAYERS is not relevant
if ( CurIndex <> LYR_SELECTED_LAYERS ) and
(( CurIndex < 0 ) or ( CurIndex >= fLayers.Count )) then
exit;
if Proc.AutoUndo and ( loAutoUndoChangesbyCode in fLayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_ArrangeLayers ), ieuObjectsAndLayers, True, IEOP_ARRANGELAYERS );
if ( CurIndex = LYR_SELECTED_LAYERS ) and
( LayersAllowMultiSelect = False ) then
CurIndex := fLayersCurrent;
SyncBitmapToCurrentLayer();
// save current layer index and make no current layer
if fLayersCurrent > -1 then
curLayer := Layers[fLayersCurrent]
else
curLayer := nil;
fLayersCurrent := -1;
try
if CurIndex <> LYR_SELECTED_LAYERS then
begin
// MOVE SPECIFIED LAYER
case NewIndex of
IEN_Send_To_Back : NewIndex := 0;
IEN_Send_Backward : NewIndex := CurIndex - 1;
IEN_Bring_Forward : NewIndex := CurIndex + 1;
IEN_Bring_To_Front : NewIndex := fLayers.Count - 1;
end;
if ( NewIndex = 0 ) and ( curLayer.Kind <> ielkImage ) then
NewIndex := 1; // only image layers at 0
if ( NewIndex < 0 ) or ( NewIndex >= fLayers.Count ) or ( CurIndex = NewIndex ) then
exit;
// check if it has a layer mask
layerMask := nil;
if ( CurIndex < fLayers.Count - 1 ) and Layers[ CurIndex + 1 ].IsMask then
begin
layerMask := Layers[ CurIndex + 1 ];
if NewIndex = CurIndex + 1 then
inc( NewIndex );
end;
// move layer
movingLayer := Layers[ CurIndex ];
fLayers.Move( CurIndex, NewIndex );
// move layer mask if exists
if layerMask <> nil then
begin
a := fLayers.IndexOf( layerMask );
b := fLayers.IndexOf( movingLayer );
if b < fLayers.Count - 1 then
inc( b );
fLayers.Move( a, b );
end;
end
else
begin
// MOVE ALL SELECTED
// Generate list of all selected layers
SetLength( LyrList, fLayers.Count );
iLyrListLen := 0;
allImageLayers := True;
for i := 0 to fLayers.Count - 1 do
begin
bInclude := Layers[ i ].Selected;
// check if its a layer mask of a selected layer
if ( bInclude = False ) and Layers[ i ].IsMask and ( i > 0 ) and Layers[ i - 1 ].Selected then
bInclude := True;
if bInclude then
begin
LyrList[ iLyrListLen ] := Layers[ i ];
inc( iLyrListLen );
if Layers[ i ].Kind <> ielkImage then
allImageLayers := False;
end;
end;
if iLyrListLen = 0 then
exit;
case NewIndex of
IEN_Send_To_Back : NewIndex := 0;
IEN_Send_Backward : begin
NewIndex := fLayers.IndexOf( LyrList[ 0 ] ) - 1;
if NewIndex < 0 then
NewIndex := 0;
end;
IEN_Bring_Forward : begin
NewIndex := fLayers.IndexOf( LyrList[ iLyrListLen - 1 ] ) + 1;
if NewIndex > fLayers.Count - 1 then
NewIndex := fLayers.Count - 1;
end;
IEN_Bring_To_Front : NewIndex := fLayers.Count - 1;
end;
if ( NewIndex = 0 ) and ( allImageLayers = False ) then
NewIndex := 1; // only image layers at 0
if ( NewIndex < 0 ) or ( NewIndex >= fLayers.Count ) or ( CurIndex = NewIndex ) then
exit;
// If we are moving things up the stack we need to iterate backwards through our list
if NewIndex <= fLayers.IndexOf( LyrList[ 0 ] ) then
begin
for i := iLyrListLen - 1 downto 0 do
begin
CurIndex := fLayers.IndexOf( LyrList[ i ] );
fLayers.Move( CurIndex, NewIndex );
end
end
else
begin
for i := 0 to iLyrListLen - 1 do
begin
CurIndex := fLayers.IndexOf( LyrList[ i ] );
fLayers.Move( CurIndex, NewIndex );
end
end;
end;
finally
// set old current layer
if curLayer <> nil then
SetLayersCurrent( fLayers.IndexOf(curLayer) ); // Which calls update
end;
end;
{!!
<FS>TImageEnView.LayersSetProperties
<FM>Declaration<FC>
procedure LayersSetProperties(LayerIndex: integer; Props: TStrings); overload;
procedure LayersSetProperties(LayerIndex: integer; const PropName, Value: Variant); overload;
<FM>Description<FN>
Sets properties of multiple layers in a batch (by calling <A TIELayer.SetProperties>).
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>LayerIndex<FN></C> <C>Index of the layer to update. Generally this will be <FC>LYR_SELECTED_LAYERS<FN> or <FC>LYR_ALL_LAYERS<FN> </C> </R>
<R> <C><FC>Props<FN></C> <C>A list of properties in Name=Value pairs </C> </R>
<R> <C><FC>PropName<FN></C> <C>A <L TIELayer Property Consts>property</L> to update </C> </R>
<R> <C><FC>Value<FN></C> <C>New value for the property </C> </R>
</TABLE>
Names will be drawn from the <A TIELayer Property Consts>. Properties can be retrieved using <A TIELayer.GetProperties>.
<FM>Example<FC>
// Rotate all layers 45 degrees
ImageEnView1.LayersSetProperties( LYR_ALL_LAYERS, IELP_Rotate, 45 );
// Set the text for all text layers (other layer types will be ignored)
ImageEnView1.LayersSetProperties( LYR_ALL_LAYERS, IELP_Text, 'Double-click to edit text' );
// Set style properties for selected layers
ss := TStringList.Create;
ss.Add( 'IELP_BorderColor=clNone' );
ss.Add( 'IELP_BorderWidth=0' );
ss.Add( 'IELP_FillColor=clYellow' );
ss.Add( 'IELP_FillColor2=clRed' );
ss.Add( 'IELP_FillGradient=1' );
LayersSetProperties( LYR_SELECTED_LAYERS, ss );
ss.Free;
<FM>See Also<FN>
- <A TIELayer.SetProperties>
- <A TIELayer.GetProperties>
!!}
procedure TImageEnView.LayersSetProperties(LayerIndex: integer; const PropName, Value: Variant);
begin
LayersSetPropertiesEx( LayerIndex, nil, PropName, Value, loAutoUndoChangesbyCode in fLayerOptions, '' );
end;
procedure TImageEnView.LayersSetProperties(LayerIndex: integer; Props: TStrings);
begin
LayersSetPropertiesEx( LayerIndex, Props, '', '', loAutoUndoChangesbyCode in fLayerOptions, '' );
end;
procedure TImageEnView.LayersSetPropertiesEx(LayerIndex: integer;
Props: TStrings; { OR } const PropName, Value: string;
SaveUndo: Boolean; UndoMsg: string);
var
idx: Integer;
doLayer: Boolean;
begin
if ( LayerIndex <> LYR_SELECTED_LAYERS ) and
( LayerIndex <> LYR_ALL_LAYERS ) and
(( LayerIndex < 0 ) or ( LayerIndex >= fLayers.Count )) then
exit; { Invalid selection }
if ( LayerIndex = LYR_SELECTED_LAYERS ) and ( LayersAllowMultiSelect = False ) then
LayerIndex := LayersCurrent;
if Proc.AutoUndo and SaveUndo then
begin
if UndoMsg = '' then
UndoMsg := IEMsg( IEMsg_SetLayerProperties );
Proc.SaveUndo( UndoMsg, ieuLayer, True, IEOP_LAYERPROPS );
end;
for idx := fLayers.Count - 1 downto 0 do
begin
case LayerIndex of
LYR_ALL_LAYERS : doLayer := True;
LYR_SELECTED_LAYERS : doLayer := TIELayer( fLayers[ idx ]).Selected
else doLayer := idx = LayerIndex;
end;
if doLayer then
begin
if Assigned( Props ) then
Layers[ idx ].SetProperties( Props )
else
Layers[ idx ].SetProperties( PropName, Value );
if LayerIndex >= 0 then
Break; { No more to process }
end;
end;
Update();
end;
{!!
<FS>TImageEnView.Modified
<FM>Declaration<FC>
property Modified: Boolean;
<FM>Description<FN>
Returns true if the image has been changed since it was loaded.
Modified will be set when there are changes to the using <A TImageEnProc> (<A TImageEnView.Proc> property), or to <A TImageEnView.Layers>.
<FM>Example<FC>
btnSave.Enabled := ImageEnView1.Modified;
// Reset modified after saving
ImageEnView1.IO.SaveToFile( Filename );
ImageEnView1.Modified := False;
<FM>See Also<FN>
- <A TIEBitmap.Modified>
- <A TIELayer.Modified>
- <A TImageEnView.OnImageChange>
!!}
function TImageEnView.GetModified(): Boolean;
begin
Result := fModified;
if assigned( fIEBitmap ) and fIEBitmap.Modified then
Result := True;
end;
procedure TImageEnView.SetModified(value: Boolean);
begin
fModified := value;
if ( Value = False ) and assigned( fIEBitmap ) then
fIEBitmap.Modified := False;
end;
// if fIEBitmapValid returns fIEBitmap.Width, otherwise returns width of current layer object
function TImageEnView.fIEBitmap_Width(): Integer;
begin
Result := 0;
if fIEBitmapValid then
Result := fIEBitmap.Width
else
if fLayersCurrent >= 0 then
Result := CurrentLayer.Width;
end;
// if fIEBitmapValid returns fIEBitmap.Height, otherwise returns Height of current layer object
function TImageEnView.fIEBitmap_Height(): Integer;
begin
Result := 0;
if fIEBitmapValid then
Result := fIEBitmap.Height
else
if fLayersCurrent >= 0 then
Result := CurrentLayer.Height;
end;
// Write fIEBitmap back to CurrentLayer.Bitmap
procedure TImageEnView.SyncBitmapToCurrentLayer();
begin
if ( fLayersCurrent > -1 ) and ( Layers[ fLayersCurrent ] is TIEImageLayer ) then
TIEImageLayer( Layers[ fLayersCurrent ]).fBitmap := fIEBitmap;
end;
function TImageEnView.MouseChangingLayers(): Boolean;
begin
result := (fMovingLayer >= 0) or (fRotatingLayer >= 0) or (fLayerResizing <> ieNone);
end;
// Synchronize current fBitmap and fAlphaChannel with layers list
// Update layer coords
// Result is true if the layers rect changed
function TImageEnView.SyncLayers(): Boolean;
var
wasRect: TIERectangle;
begin
Result := False;
if fBitmap <> nil then
fIEBitmap.EncapsulateTBitmap(fBitmap, false); // only sync properties
SyncBitmapToCurrentLayer();
if not (Center and MouseChangingLayers()) then
begin
wasRect := fLayersRect;
if loDynamicCanvas in fLayerOptions then
// Align view to all layers rect
fLayersRect := LayersRect()
else
// Align view to layer 0
fLayersRect := IERectangle( Layers[0].PosX, Layers[0].PosY, Layers[0].Width, Layers[0].Height );
Result := ( wasRect.x <> fLayersRect.x ) or
( wasRect.y <> fLayersRect.y ) or
( wasRect.Width <> fLayersRect.Width ) or
( wasRect.Height <> fLayersRect.Height );
end;
end;
{!!
<FS>TImageEnView.FindLayerAt
<FM>Declaration<FC>
function FindLayerAt(x, y: Integer; SelectablesOnly: Boolean = true): Integer;
<FM>Description<FN>
Returns the index of the layer at position x, y (in client area coordinates).
If SelectablesOnly is true, it won't return any layer which isn't selectable.
Returns -1 if no layer was found.
!!}
function TImageEnView.FindLayerAt(x, y: integer; SelectablesOnly: Boolean = True): integer;
var
rect: TRect;
lyr: TIELayer;
xx, yy: Integer;
begin
for result := fLayers.Count - 1 downto 0 do
begin
lyr := TIELayer(fLayers[result]);
rect := lyr.ClientAreaBox;
if (lyr.Selectable or not fLayersSelectConstrains or not SelectablesOnly) and lyr.Visible and IEPointInRect(x, y, Rect) then
begin
if (lyr.Kind <> ielkImage) or (not lyr.Bitmap.HasAlphaChannel) or (iesoSelectTranspLayers in fSelectionOptions) then
break
else
begin
xx := ilimit( lyr.ConvXScr2Bmp(x), 0, lyr.Bitmap.Width - 1 );
yy := ilimit( lyr.ConvYScr2Bmp(y), 0, lyr.Bitmap.Height - 1 );
if lyr.Bitmap.Alpha[ xx, yy ] > 0 then
break;
end;
end;
end;
end;
function TImageEnView.IsPointInsideLayer(x, y: Integer; layer: Integer): Boolean;
var
lyr: TIELayer;
begin
result := false;
if layer>-1 then
begin
lyr := TIELayer(fLayers[layer]);
if lyr.Rotate = 0 then
with lyr.ClientAreaBox do
result := lyr.Visible and IEPointInRect(x, y, Left, Top, Right, Bottom)
else
result := lyr.Visible and IEISPointInPoly(x, y, lyr.DrawingInfo.RotatedDest);
end;
end;
{!!
<FS>TImageEnView.LayersConvertToImageLayers
<FM>Declaration<FC>
procedure LayersConvertToImageLayers(LayerIdx: Integer = LYR_SELECTED_LAYERS; QualityFactor: Double = 2; CropAlpha: Boolean = True; ConvertImages: Boolean = False);
<FM>Description<FN>
Changes the <A TIELayer.Kind>type</L> of the specified layer to <A TIEImageLayer>.
This will change it from a vector-based layer to a standard bitmap layer. Bitmap layers can be edited using <L TImageEnProc>standard image modification features</L>, but the quality will be lost if you resize the layer.
<FC>QualityFactor<FN> determines the size that the layer bitmap is created. A <FC>QualityFactor<FN> of 1 will create the bitmap at the current display size, whereas a <FC>QualityFactor<FN> of 2 would create it at double the display size (allowing it to be zoomed up 200% without loss of quality).
If <FC>CropAlpha<FN> is true, then it remove any alpha from the edges of the layer.
<FC>ConvertImages<FN> affects only image layers. By default image layers are ignored, if <FC>ConvertImages<FN> is true, the rotated images are fixed using <A TImageEnView.LayersFixRotations>
Note:
- For text layers, a QualityFactor of 1 usually works best
- To convert an individual layers, you can also use <A TIELayer.ConvertToImageLayer>
<FM>Examples<FC>
// Convert all selected layers to image layers
ImageEnView1.LayersConvertToImageLayers();
// Convert the current layer to an image
ImageEnView1.LayersConvertToImageLayers( ImageEnView1.LayersCurrent );
// Which is the same as...
ImageEnView1.CurrentLayer.ConvertToImageLayer();
!!}
// OK to use: LYR_ALL_LAYERS and LYR_SELECTED_LAYERS
procedure TImageEnView.LayersConvertToImageLayers(LayerIdx: integer = LYR_SELECTED_LAYERS; QualityFactor: Double = 2; CropAlpha: Boolean = True; ConvertImages: Boolean = False);
begin
LayersConvertToImageLayersEx( LayerIdx, QualityFactor, CropAlpha, ConvertImages, Proc.AutoUndo and ( loAutoUndoChangesByCode in fLayerOptions ));
Update();
end;
procedure TImageEnView.LayersConvertToImageLayersEx(LayerIdx: integer = -2; QualityFactor: Double = 2; CropAlpha: Boolean = True; ConvertImages: Boolean = False; DoSaveUndo: Boolean = False);
var
aLayer : TIELayer;
doLayer: Boolean;
i, l: Integer;
begin
if ( LayerIdx <> LYR_ALL_LAYERS ) and
( LayerIdx <> LYR_SELECTED_LAYERS ) and
(( LayerIdx < 1 {Ignore BG} ) or ( LayerIdx >= LayersCount )) then
exit;
if DoSaveUndo then
Proc.SaveUndo( IEMsg( IEMsg_ConvertToImageLayer ), ieuObjectsAndLayers, True, IEOP_OTHER );
l := fLayersCurrent;
SyncBitmapToCurrentLayer();
for i := 0 to fLayers.Count - 1 do
begin
case LayerIdx of
LYR_ALL_LAYERS : doLayer := True;
LYR_SELECTED_LAYERS : doLayer := Layers[i].Selected;
else doLayer := i = LayerIdx;
end;
if doLayer and ( Layers[i].Kind <> ielkImage ) then
begin
l := i;
aLayer := TIEImageLayer.Create( self, Layers[ i ], QualityFactor, CropAlpha );
Layers[ i ].Free;
fLayers[ i ] := aLayer;
end
else
if doLayer and ( Layers[i].Kind = ielkImage ) and ConvertImages then
begin
LayersFixRotations( i );
end
end;
fLayersCurrent := -1;
SetLayersCurrentEx( l, False, True, False { Do NOT call update } );
end;
{!!
<FS>TImageEnView.LayersMergeTo
<FM>Declaration<FC>
procedure LayersMergeTo(Layer1, Layer2: integer; Destination: <A TIEBitmap>);
<FM>Description<FN>
Merges Layer1 and Layer2 into a TIEBitmap object.
The new bitmap will inherit the <A TImageEnView.Layers>[].<A TIELayer.Transparency> and alpha channels.
The resulting bitmap will always be 24 bit (ie24RGB).
Notes:
- If either layer is not a <A TIEImageLayer>, it will be <L TImageEnView.LayersConvertToImageLayers>converted to a TIEImageLayer</L>
- LayersMergeTo can merge a layer with its own layer mask (to create a layer with the transparency of the mask).
- <A TImageEnView.LayersMergeFilter> will specify the quality of image layers, if they do not have a custom <A TIEImageLayer.UseResampleFilter>
<FM>Example<FC>
// we want to get a background image and then merge over it another image in semitransparency.
ImageEnView.IO.LoadFromFile('C:\background.jpg');
ImageEnView.LayersAdd;
ImageEnView.IO.LoadFromFile('C:\foreground.jpg');
ImageEnView.Layers[1].Transparency := 128; // the second layer has 50% transparency
ImageEnView.LayersMergeTo(0, 1, ImageEnView2.IEBitmap);
ImageEnView2.IO.SaveToFile('C:\output.jpg');
!!}
procedure TImageEnView.LayersMergeTo(Layer1, Layer2: integer; Destination: TIEBitmap);
var
UpLayer, DownLayer: Integer;
LayerMask: TIELayer;
baseLayer: TIELayer;
begin
{$IFDEF UNITTESTING}
fDebugUpdatedCount := 0;
{$ENDIF}
if Layers[ Layer1 ].Kind <> ielkImage then
LayersConvertToImageLayersEx( Layer1 );
if Layers[ Layer2 ].Kind <> ielkImage then
LayersConvertToImageLayersEx( Layer2 );
DownLayer := imin(Layer1, Layer2);
UpLayer := imax(Layer1, Layer2);
if Layers[UpLayer].IsMask then
begin
// upper layer is a layermask (this case can happen only when application tries to merge a layer with its mask)
baseLayer := TIEImageLayer.Create(nil, Layers[DownLayer].Bitmap, false);
try
baseLayer.Bitmap.Fill(0);
baseLayer.Bitmap.AlphaChannel.Fill(0);
IELayersMerge( baseLayer, Layers[DownLayer], Layers[UpLayer], Destination, fLayersMergeFilter, fBackground, DownLayer > 0 );
finally
baseLayer.Free();
end;
end
else
begin
if (UpLayer < fLayers.Count - 1) and Layers[UpLayer + 1].IsMask then
LayerMask := Layers[UpLayer + 1]
else
LayerMask := nil;
IELayersMerge( Layers[DownLayer], Layers[UpLayer], LayerMask, Destination, fLayersMergeFilter, fBackground, DownLayer > 0 );
end;
{$IFDEF UNITTESTING}
if fDebugUpdatedCount > 0 then
raise Exception.create( 'Unexpected Update called' );
{$ENDIF}
end;
{!!
<FS>TImageEnView.LayersMerge
<FM>Declaration<FC>
procedure LayersMerge(); overload;
procedure LayersMerge(Layer1, Layer2: integer; RemoveUpperLayer: Boolean = true); overload;
procedure LayersMerge(LayerList: array of integer); overload;
procedure LayersMerge(LayerList: <A TIEArrayOfInteger>); overload;
<FM>Description<FN>
Merges two or more layers into one layer. The new layer has the lesser index.
The new layer will inherit the <A TImageEnView.Layers>.<A TIELayer.Transparency> and the bitmap's alpha channels.
The LayersMerge() overload will merge selected layers.
Notes:
- To merge a layer with its own mask (to create a layer with the transparency of the mask) pass only two indexes (i.e. the indexes of the layer and the mask).
- If any of the layers are not a <A TIEImageLayer>, they will be <L TImageEnView.LayersConvertToImageLayers>converted to a TIEImageLayer</L>
- <A TImageEnView.LayersMergeFilter> will specify the quality of image layers, if they do not have a custom <A TIEImageLayer.UseResampleFilter>
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>Layer1<FN></C> <C>Index of the first layer to merge.</C> </R>
<R> <C><FC>Layer2<FN></C> <C>Index of the second layer to merge.</C> </R>
<R> <C><FC>RemoveUpperLayer<FN></C> <C>If <FC>RemoveUpperLayer<FN> is <FC>false<FN>, the upper layer will not be removed.</C> </R>
<R> <C><FC>LayerList<FN></C> <C>An array of layer indexes to remove. The array must be ordered and all layers will be merged into the layer specified by the first index. Empty list means "all layers".</C> </R>
</TABLE>
<FM>Example<FC>
// merge layer 1 with background (layer 0)
ImageEnView1.LayersMerge([ 1, 0 ]);
// merge layers 0, 1 and 2
ImageEnView1.LayersMerge([ 0, 1, 2 ]);
// merge all layers
ImageEnView1.LayersMerge([]);
// merge all selected layers
ImageEnView1.LayersMerge();
// merge layer 1 with its mask
ImageEnView1.LayersMerge( 1, 2 );
// we want to get a background image and then merge over it another image in semi-transparency.
ImageEnView.IO.LoadFromFile('C:\background.jpg');
ImageEnView.LayersAdd;
ImageEnView.IO.LoadFromFile('C:\foreground.jpg');
ImageEnView.Layers[1].Transparency := 128; // the second layer has 50% transparency
ImageEnView.LayersMerge(0, 1); // from now we have only one layer
ImageEnView.IO.SaveToFile('C:\output.jpg');
!!}
procedure TImageEnView.LayersMerge(Layer1, Layer2: integer; RemoveUpperLayer: Boolean = True);
var
outlayer: TIELayer;
lamin, lamax: integer;
layerCropped: boolean;
oldnav: TImageEnView;
begin
{$IFDEF UNITTESTING}
fDebugUpdatedCount := 0;
{$ENDIF}
if Proc.AutoUndo and ( loAutoUndoChangesbyCode in fLayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_MergeLayers ), ieuObjectsAndLayers, True, IEOP_MERGELAYERS );
oldnav := fNavigator;
SetNavigator(nil);
lamin := imin(Layer1, Layer2);
lamax := imax(Layer1, Layer2);
outlayer := TIEImageLayer.Create(self, Layers[ lamin ].Bitmap, false);
// Ensure layer 2 is cropped if needed
if fLayersCropped then
Layers[ lamax ].Cropped := True;
LayersMergeTo( lamin, lamax, outlayer.Bitmap);
if lamin > 0 then
begin
// Not merging to background layer
outlayer.PosX := imin( Layers[ lamin ].PosX, Layers[ lamax ].PosX );
outlayer.PosY := imin( Layers[ lamin ].PosY, Layers[ lamax ].PosY );
end
else
begin
// Merging background layer. If layers are not cropped shift new layer to position of outside layer
layerCropped := fLayersCropped or Layers[lamax].Cropped;
outlayer.PosX := Layers[lamin].PosX;
if ( layerCropped = False ) and ( Layers[lamax].PosX < Layers[lamin].PosX ) then
outlayer.PosX := Layers[lamax].PosX;
outlayer.PosY := Layers[lamin].PosY;
if ( layerCropped = False ) and ( Layers[lamax].PosY < Layers[lamin].PosY ) then
outlayer.PosY := Layers[lamax].PosY;
end;
outlayer.Locked := TIELayer(Layers[lamin]).Locked;
outlayer.VisibleBox := TIELayer(Layers[lamin]).VisibleBox;
outlayer.Selectable := TIELayer(Layers[lamin]).Selectable;
// remove old layers
if RemoveUpperLayer then
begin
if (lamax<fLayers.Count - 1) and Layers[lamax + 1].IsMask then
begin
// remove layer mask
Layers[lamax + 1].Free;
fLayers.Delete(lamax + 1);
end;
Layers[lamax].Free;
fLayers.Delete(lamax);
end;
Layers[lamin].Free;
fLayers.Delete(lamin);
// insert new one
fLayersCurrent := -1;
fLayers.Insert(lamin, outlayer);
//
{$IFDEF UNITTESTING}
if fDebugUpdatedCount > 0 then
raise Exception.create( 'Unexpected Update called' );
{$ENDIF}
LayersCurrent := lamin; // this calls update
SetNavigator(oldnav);
end;
// Merge selected
procedure TImageEnView.LayersMerge();
begin
if MultiSelectedLayersCount() > 0 then
LayersMerge( MultiSelectedLayersList() );
end;
procedure TImageEnView.LayersMerge(LayerList: array of integer);
var
llist: TIEArrayOfInteger;
i: integer;
begin
SetLength(llist, length(LayerList));
for i := 0 to length(LayerList) - 1 do
llist[i] := LayerList[i];
LayersMerge(llist);
end;
// indexes in LayerList must be sorted (lower indexes first)
procedure TImageEnView.LayersMerge(LayerList: TIEArrayOfInteger);
var
i, idx: integer;
llistLen: integer;
llist: array of TIELayer;
oldnav: TImageEnView;
outLayer, lyrMask: TIELayer;
firstLastIndex: integer;
layerCropped: boolean;
begin
if length(LayerList) = 1 then // unique invalid case (0 = all layers)
exit;
if length(LayerList) = 2 then
begin
// Allow merging of layer with its mask
LayersMerge( LayerList[0], LayerList[1] );
exit;
end
else
if ( length(LayerList) = 0 ) and ( fLayers.Count = 2 ) then
begin
// Allow merging of background layer with its mask
LayersMerge( 0, 1 );
exit;
end;
{$IFDEF UNITTESTING}
fDebugUpdatedCount := 0;
{$ENDIF}
if Proc.AutoUndo and ( loAutoUndoChangesbyCode in fLayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_MergeLayers ), ieuObjectsAndLayers, True, IEOP_MERGELAYERS );
oldnav := fNavigator;
SetNavigator(nil);
fLayersCurrent := -1;
LockUpdate();
// creates a list of pointers to layers (excluding masks)
if length(LayerList) = 0 then
begin
// empty array means All layers
SetLength(llist, fLayers.Count);
llistLen := 0;
for i := 0 to fLayers.Count - 1 do
if not Layers[i].IsMask then
begin
llist[llistLen] := Layers[i];
inc(llistLen);
end;
firstLastIndex := 0;
end
else
begin
// get list from LayerList
SetLength(llist, length(LayerList));
llistLen := 0;
for i := 0 to length(LayerList) - 1 do
begin
if not Layers[LayerList[i]].IsMask then
begin
llist[llistLen] := Layers[LayerList[i]];
inc(llistLen);
end;
end;
firstLastIndex := LayerList[0];
end;
if llistLen > 1 then
begin
for i := 1 to llistLen - 1 do
begin
if llist[i].Kind <> ielkImage then
begin
idx := llist[i].GetIndex;
LayersConvertToImageLayersEx( idx, 1 );
llist[i] := Layers[idx];
end;
outLayer := TIEImageLayer.Create(self, llist[0].Bitmap, false);
IELayersMerge( llist[0], llist[i], llist[i].GetLayerMask(), outLayer.Bitmap, fLayersMergeFilter, fBackground, firstLastIndex > 0, fLayersCropped );
if firstLastIndex > 0 then
begin
// Not merging to background layer
outlayer.PosX := imin( llist[0].PosX, llist[i].PosX );
outlayer.PosY := imin( llist[0].PosY, llist[i].PosY );
end
else
begin
// Merging background layer. If layers are not cropped shift new layer to position of outside layer
layerCropped := fLayersCropped or llist[i].Cropped;
outlayer.PosX := llist[0].PosX;
if ( layerCropped = False ) and ( llist[i].PosX < llist[0].PosX ) then
outlayer.PosX := llist[i].PosX;
outlayer.PosY := llist[0].PosY;
if ( layerCropped = False ) and ( llist[i].PosY < llist[0].PosY ) then
outlayer.PosY := llist[i].PosY;
end;
outlayer.Locked := llist[0].Locked;
outlayer.VisibleBox := llist[0].VisibleBox;
outlayer.Selectable := llist[0].Selectable;
fLayers.Insert(firstLastIndex, outlayer);
lyrMask := llist[i].GetLayerMask(); // Get mask before we remove layer from list
fLayers.Remove( llist[ 0 ]);
fLayers.Remove( lyrMask );
fLayers.Remove( llist[ i ]);
FreeAndNil( llist[ 0 ]);
FreeAndNil( lyrMask );
FreeAndNil( llist[ i ]);
llist[0] := outlayer;
end;
end;
{$IFDEF UNITTESTING}
if fDebugUpdatedCount > 0 then
raise Exception.create( 'Unexpected Update called' );
{$ENDIF}
LayersCurrent := firstLastIndex; // this call update
SetNavigator(oldnav);
UnlockUpdate();
end;
// Layers
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{!!
<FS>TImageEnView.Bitmap
<FM>Declaration<FC>
property Bitmap: TBitmap;
<FM>Description<FN>
Bitmap contains the image (current layer) to display.
If <A TImageEnView.LegacyBitmap> is <FC>True<FN>, <A TImageEnView.IEBitmap> is just a wrapper for TBitmap that may be accessed using the Bitmap property.
!!}
// after changing sizes using Bitmap, we should call Update to sync fIEBitmap
function TImageEnView.GetFBitmap: TBitmap;
begin
result := fBitmap;
end;
{!!
<FS>TImageEnView.IEBitmap
<FM>Declaration<FC>
property IEBitmap: <A TIEBitmap>;
<FM>Description<FN>
Contains the image (current layer) to display. The object TIEBitmap also contains the alpha channel of the image.
If your image contains <L TImageEnView.Layers>multiple layers</L> then <FC>IEBitmap<FN> is the bitmap of the selected <L TIEImageLayer>image layer</L>. If a non-image layer is selected then IEBitmap will be the background layer (Layer 0).
Notes:
- You must call <A TImageEnView.Update> if you modify <FC>IEBitmap<FN> directly
- If <A TImageEnView.LegacyBitmap> is True, IEBitmap is just a wrapper for TBitmap that may be accessed using the Bitmap property
!!}
function TImageEnView.GetIEBitmap: TIEBitmap;
begin
result := fIEBitmap;
end;
{!!
<FS>TImageEnView.LegacyBitmap
<FM>Declaration<FC>
property LegacyBitmap: Boolean;
<FM>Description<FN>
If LegacyBitmap is <FC>True<FN>, ImageEn uses TBitmap to store the image, otherwise ImageEn uses <A TIEBitmap>.
TIEBitmap handles images using memory mapped file (for large images) or main memory.
This allows handling of large images and to include input/output and image processing in a multi-threaded environment.
Also TIEBitmap supports a larger number of pixel formats (<A TIEPixelFormat>).
Note: LegacyBitmap should be set to FALSE for new applications.
!!}
procedure TImageEnView.SetLegacyBitmap(Value: boolean);
begin
if Value <> fLegacyBitmap then
begin
// Select base layer
SetLayersCurrent( 0 );
if fLegacyBitmap = true then
begin
// from fBitmap to fIEBitmap
fIEBitmap.EncapsulatedFromTBitmap := false; // this allows fIEBitmap to fully own fBitmap
fIEBitmap.Location := ieFile;
fBitmap := nil; // fBitmap freedom by fIEBitmap
end
else
begin
// from fIEBitmap to fBitmap
fIEBitmap.Location := ieTBitmap;
fIEBitmap.EncapsulatedFromTBitmap := true; // this deny fIEBitmap to own fBitmap
fBitmap := fIEBitmap.VclBitmap;
end;
fLegacyBitmap := Value;
CallBitmapChangeEvents;
Update;
end;
end;
// fOffX, fOffY, frx, fry, fo1x, fo1y, fo2x, fo2y must be valid
procedure TImageEnView.PaintSelection(OutBitmap: TBitmap);
begin
if fVisibleSelection then
begin
if (iesoFilled in fSelectionOptions) and PIEAnimPoly(fHPolySel)^.Enabled then
with CurrentLayer.DrawingInfo do
fSelectionMask.InvertCanvas(OutBitmap.Canvas, XDst, YDst, WidthDst, HeightDst, XSrc, YSrc, WidthSrc, HeightSrc);
if (iesoMarkOuter in fSelectionOptions) and PIEAnimPoly(fHPolySel)^.Enabled then
with CurrentLayer.DrawingInfo do
fSelectionMask.DrawOuter(OutBitmap, XDst, YDst, WidthDst, HeightDst, XSrc, YSrc, WidthSrc, HeightSrc, fMarkOuterAlpha, fMarkOuterColor);
end;
end;
// fOffX, fOffY, frx, fry, fo1x, fo1y, fo2x, fo2y must be valid
procedure TImageEnView.PaintPixelGrid(OutBitmap: TBitmap);
var
x1, y1, x2, y2, y, x: integer;
dx, dy: double;
lyr: TIELayer;
MajorStepMode : TPenMode;
begin
if fDisplayGridLyr = -1 then
lyr := CurrentLayer
else
lyr := Layers[imin(fDisplayGridLyr, LayersCount-1)];
// DRAW GRID LINES (SEE ALSO: DRAW GUIDE LINES)
if ( fDisplayGridKind = iedgPixelGrid ) and lyr.Visible and
(( fZoomX >= IEGlobalSettings().MinZoomDisplayGrid ) or ( fZoomY >= IEGlobalSettings().MinZoomDisplayGrid )) and
(( fLayersRect.width > 1 ) or ( fLayersRect.height > 1 )) then
begin
OutBitmap.Canvas.Pen.Assign( IEGlobalSettings().GridPen );
if IEGlobalSettings().GridPen.Mode = pmCopy then
MajorStepMode := pmNotCopy
else
MajorStepMode := pmCopy;
// VERT GRID LINES
y1 := lyr.DrawingInfo.YDst;
y2 := lyr.DrawingInfo.YDst + lyr.DrawingInfo.HeightDst;
dx := lyr.DrawingInfo.WidthDst / lyr.DrawingInfo.WidthSrc;
x := 0;
while x <= lyr.DrawingInfo.WidthSrc do
begin
if IEGlobalSettings().GridMajorStep > 1 then
begin
// Draw inverted grid?
if x mod IEGlobalSettings().GridMajorStep = 0 then
OutBitmap.Canvas.Pen.Mode := MajorStepMode
else
OutBitmap.Canvas.Pen.Mode := IEGlobalSettings().GridPen.Mode;
end;
x1 := round( lyr.DrawingInfo.XDst + x * dx );
OutBitmap.Canvas.MoveTo(x1, y1);
OutBitmap.Canvas.LineTo(x1, y2);
inc(x);
end;
// HORZ GRID LINES
x1 := lyr.DrawingInfo.XDst;
x2 := lyr.DrawingInfo.XDst + lyr.DrawingInfo.WidthDst;
dy := lyr.DrawingInfo.HeightDst / lyr.DrawingInfo.HeightSrc;
y := 0;
while y <= lyr.DrawingInfo.HeightSrc do
begin
if IEGlobalSettings().GridMajorStep > 1 then
begin
// Draw inverted grid?
if y mod IEGlobalSettings().GridMajorStep = 0 then
OutBitmap.Canvas.Pen.Mode := MajorStepMode
else
OutBitmap.Canvas.Pen.Mode := IEGlobalSettings().GridPen.Mode;
end;
y1 := round(lyr.DrawingInfo.YDst + y * dy);
OutBitmap.Canvas.MoveTo(x1, y1);
OutBitmap.Canvas.LineTo(x2, y1);
inc(y);
end;
if (fHighlightedPixel.X >= 0) and (fHighlightedPixel.Y >= 0) then
begin
x1 := lyr.ConvXBmp2Scr(fHighlightedPixel.X);
y1 := lyr.ConvYBmp2Scr(fHighlightedPixel.Y);
x2 := lyr.ConvXBmp2Scr(fHighlightedPixel.X + 1);
y2 := lyr.ConvYBmp2Scr(fHighlightedPixel.Y + 1);
with TIECanvas.Create(OutBitmap.Canvas, true, true) do
begin
Pen.Color := fHighlightedPixelColor;
Pen.Width := 2;
Brush.Style := bsClear;
RoundRect(x1, y1, x2, y2, 5, 5);
Free;
end;
end;
end;
end;
procedure TImageEnView.PaintGuideLines(Canvas: TCanvas);
const
Square_Grid = True; // Adjust number of grid lines to make grid as square as possible
var
MajorStepMode : TPenMode;
iGLCount: Integer;
x1, y1, x2, y2, y, x: integer;
dx, dy: double;
iHLines, iVLines: Integer;
begin
if ( fDisplayGridKind <> iedgGuideLines ) or ( IEGlobalSettings().GuidelineCount < 1 ) then
exit;
// DRAW GUIDE LINES (SEE ALSO: DRAW GRID LINES)
Canvas.Pen.Assign( IEGlobalSettings().GridPen );
if IEGlobalSettings().GridPen.Mode = pmCopy then
MajorStepMode := pmNotCopy
else
MajorStepMode := pmCopy;
// Scale down guide lines if image is small in window
iGLCount := max( Round( IEGlobalSettings().GuidelineCount * fExtX / ClientWidth ),
Round( IEGlobalSettings().GuidelineCount * fExtY / ClientHeight ));
if iGLCount < 1 then
iGLCount := 1;
// VERT GUIDE LINES
y1 := fOffY;
y2 := fOffY + fExtY;
if Square_Grid = False then
iVLines := max( 1, Round( IEGlobalSettings().GuidelineCount * fExtX / ClientWidth ))
else
if ClientHeight > ClientWidth then
iVLines := iGLCount
else
iVLines := Round( iGLCount * fExtX / fExtY );
dx := fExtX / ( iVLines + 1 );
for x := 1 to iVLines do
begin
if IEGlobalSettings().GridMajorStep > 1 then
begin
// Draw inverted grid?
if x mod IEGlobalSettings().GridMajorStep = 0 then
Canvas.Pen.Mode := MajorStepMode
else
Canvas.Pen.Mode := IEGlobalSettings().GridPen.Mode;
end;
x1 := fOffX + round( x * dx );
Canvas.MoveTo(x1, y1);
Canvas.LineTo(x1, y2);
end;
// HORZ GUIDE LINES
x1 := fOffX;
x2 := fOffX + fExtX;
if Square_Grid = False then
iHLines := Round( iVlines * fExtY / fExtX )
else
iHLines := max( 1, Round( IEGlobalSettings().GuidelineCount * fExtY / ClientHeight ));
dy := fExtY / ( iHLines + 1 );
for y := 1 to iHLines do
begin
if IEGlobalSettings().GridMajorStep > 1 then
begin
// Draw inverted grid?
if y mod IEGlobalSettings().GridMajorStep = 0 then
Canvas.Pen.Mode := MajorStepMode
else
Canvas.Pen.Mode := IEGlobalSettings().GridPen.Mode;
end;
y1 := fOffY + round( y * dy );
Canvas.MoveTo(x1, y1);
Canvas.LineTo(x2, y1);
end;
end;
{!!
<FS>TImageEnView.GetRenderRectangles
<FM>Declaration<FC>
procedure GetRenderRectangles(var xDst, yDst, dxDst, dyDst: integer; var xSrc, ySrc, dxSrc, dySrc: integer);
<FM>Description<FN>
Returns the rendered rectangle related to the client area and the source rectangle related to the bitmap of current layer.
<FC>xDst, yDst, dxDst, dyDst<FN> : the destination x, y and width, height where the image has been rendered
<FC>xSrc, ySrc, dxSrc, dySrc<FN> : the source x, y and width, height of the source image.
!!}
procedure TImageEnView.GetRenderRectangles(var xDst, yDst, dxDst, dyDst: integer; var xSrc, ySrc, dxSrc, dySrc: integer);
begin
SyncLayers();
CalcPaintCoords;
CreateCoordConvLUT; // recalculates coordinate conversion LUT
xDst := fOffX;
yDst := fOffY;
dxDst := frx;
dyDst := fry;
xSrc := fo1x;
ySrc := fo1y;
dxSrc := fo2x;
dySrc := fo2y;
end;
procedure TImageEnView.UserInteractions_Paint(const UpdateRect: TRect);
var
i: integer;
begin
for i := 0 to fUserInteractions.Count - 1 do
if (fUserInteractions[i] as TIEUserInteraction).Enabled then
(fUserInteractions[i] as TIEUserInteraction).Paint(UpdateRect);
end;
// ABitmap must be pf24bit, width=clientwidth height=clientheight
// If UpdRect=nil updates entire ABitmap width*height
procedure TImageEnView.PaintToEx(ABitmap: TIEBitmap; UpdRect: PRect; drawBackground: boolean; drawGadgets: boolean);
var
i: integer;
IsFirst: boolean;
zf: TResampleFilter; // effective zoom filter
layer: TIELayer;
w, h: integer;
x1, y1, x2, y2: integer;
x, y: integer;
layerMask: TIELayer;
oldAlpha: TIEBitmap;
newAlpha: TIEBitmap;
FreeLayerMask: boolean;
px: PRGB;
rotateFilter: TIEAntialiasMode;
procedure SubDrawBackground(force: boolean; layerHasAlpha: Boolean = False);
var
bHandledBG: boolean;
begin
if force or
(layer.PosX <> 0) or
(layer.PosY <> 0) or
(layer.Rotate <> 0) or
(fOffX > 0) or
(fOffY > 0) or
(frx < ABitmap.Width) or
(fry < ABitmap.Height) or
(fEnableAlphaChannel and layerHasAlpha) or
(IsFirst and (layer.Operation <> ielNormal)) or
(layer.Transparency < 255) or
(layer.Opacity < 1.0) or
(fLayersRect.width <> Layers[0].OriginalWidth) or
(fLayersRect.height <> Layers[0].OriginalHeight)
then
begin
if UpdRect <> nil then
begin
with UpdRect^ do
IntersectClipRect(ABitmap.Canvas.Handle, left, top, right + 1, bottom + 1);
end;
bHandledBG := false;
if assigned(fOnDrawBackground) then
fOnDrawBackground(self, ABitmap.Canvas, Rect(0, 0, ABitmap.Width, ABitmap.Height), bHandledBG);
if ( bHandledBG = false ) and fNeedUpdateLiveBackground and ( BackgroundStyle = iebsBlurredImage ) then
UpdateLiveBackground( Layers[0].Bitmap );
if bHandledBG = false then
IEDrawBackground(ComponentState, ABitmap.Canvas, ABitmap.VclBitmap, fBackgroundStyle,
GetThemeColor( ietpControlBackground, fBackground ),
0, 0, ABitmap.Width, ABitmap.Height, fOffX, fOffY, fOffX + frx, fOffY + fry, fChessboardSize, fChessboardBrushStyle, fChessboardColor2Customized,
GetThemeColor( ietpControlBackgroundGradientEnd, fGradientEndColor ),
fWallpaper, fWallpaperStyle, fLiveBackground);
SelectClipRgn(ABitmap.canvas.handle, 0);
end;
end;
procedure SetLayerMask(lBitmap: TIEBitmap);
var
pold, pnew: pbyte;
i, j: integer;
aw, ah: integer; // alpha width and height
mw, mh: integer; // mask width and height
v: integer;
rx, ry: double;
zx, zy: double;
mx, my: integer;
mxarr: pintegerarray;
msk_p: pbytearray;
l0width, l0height: integer;
rmy: integer;
armx: pintegerarray;
doloop: boolean;
begin
if layer.fCachedLayerMask <> nil then
begin
// cached
oldAlpha := lBitmap.DetachAlphaChannel(true);
lBitmap.ReplaceAlphaChannel(layer.fCachedLayerMask);
newAlpha := layer.fCachedLayerMask;
end
else
begin
// check pixelformat of layer mask
if layerMask.Bitmap.PixelFormat <> ie8g then
begin
layerMask.Bitmap.PixelFormat := ie8g;
if layerMask.Bitmap.HasAlphaChannel then
layerMask.Bitmap.RemoveAlphaChannel; // a layer mask cannot have alpha channel
end;
l0width := Layers[0].Bitmap.Width;
l0height := Layers[0].Bitmap.Height;
oldAlpha := lBitmap.DetachAlphaChannel(true);
newAlpha := TIEBitmap.Create;
newAlpha.Allocate(oldAlpha.Width, oldAlpha.Height, ie8g);
lBitmap.ReplaceAlphaChannel(newAlpha);
aw := newAlpha.Width;
ah := newAlpha.Height;
mw := layerMask.Bitmap.Width;
mh := layerMask.Bitmap.Height;
zx := (layer.Width / layer.Bitmap.Width);
zy := (layer.Height / layer.Bitmap.Height);
rx := (layerMask.Width / layerMask.Bitmap.Width);
ry := (layerMask.Height / layerMask.Bitmap.Height);
armx := nil;
getmem(mxarr, sizeof(integer) * aw);
for j := 0 to aw - 1 do
mxarr[j] := round((j * zx / rx - layerMask.PosX / rx) + layer.PosX / rx);
if fSoftCrop = iesfAlphaBlend then
begin
getmem(armx, sizeof(integer) * aw);
for j := 0 to aw - 1 do
begin
i := round(mxarr[j] * rx) + layer.PosX;
if (i < 0) or (i > l0width) then
armx[j] := 0
else
armx[j] := - 1;
end;
end;
for i := 0 to ah - 1 do
begin
pold := oldAlpha.Scanline[i];
pnew := newAlpha.Scanline[i];
my := round((i * zy / ry - layerMask.PosY / ry) + layer.PosY / ry);
msk_p := layerMask.Bitmap.Scanline[imin(imax(0, my), layerMask.Bitmap.Height - 1)];
doloop := true;
if fSoftCrop = iesfAlphaBlend then
begin
rmy := round(my * ry) + layer.PosY;
if (rmy < 0) or (rmy > l0height) then
begin
FillChar(pnew^, aw, fSoftCropValue);
doloop := false;
end;
end;
if doloop then
begin
for j := 0 to aw - 1 do
begin
if (fSoftCrop = iesfAlphaBlend) and (armx[j] = 0) then
v := fSoftCropValue
else
begin
mx := mxarr[j];
if (my < mh) and (my >= 0) and (mx < mw) and (mx >= 0) then
v := msk_p[mx]
else
v := 0;
end;
if pold^ < v then
pnew^ := pold^
else
pnew^ := v;
inc(pold);
inc(pnew);
end;
end;
end;
freemem(mxarr);
if fSoftCrop = iesfAlphaBlend then
freemem(armx);
end;
end;
procedure UnSetLayerMask;
begin
if layerMask <> nil then
begin
layer.Bitmap.DetachAlphaChannel(); // detach "newAlpha" so it can be reused. Otherwise ReplaceAlphaChannel will free its alpha (newAlpha)
layer.Bitmap.ReplaceAlphaChannel(oldAlpha);
if layer.fCachedLayerMask = nil then
layer.fCachedLayerMask := newAlpha
else
if layer.fCachedLayerMask <> newAlpha then
begin
// this should never happen
FreeAndNil(layer.fCachedLayerMask);
layer.fCachedLayerMask := newAlpha;
end;
end;
end;
begin
SyncLayers();
CalcPaintCoords();
CreateCoordConvLUT(); // recalculates coordinate conversion LUT
// check for errors in current layer
if (csDesigning in ComponentState) or (fIEBitmap_Height = 0) or (fIEBitmap_Width = 0) then
exit; // EXIT!
if ( fIEBitmapValid ) and fIEBitmap.HasAlphaChannel and ((fIEBitmap.AlphaChannel.Width <> fIEBitmap.Width) or (fIEBitmap.AlphaChannel.Height <> fIEBitmap.Height)) then
exit; // EXIT!
// draw layers
IsFirst := true;
for i := 0 to fLayers.Count - 1 do
begin
layer := TIELayer(fLayers[i]);
if layer.Visible and ( i <> fEditingLayer { text editor is active } ) and
((layer.Kind <> ielkImage ) or (layer.Bitmap.Width >= fMinBitmapSize) or (layer.Bitmap.Height >= fMinBitmapSize)) then
begin
// layer mask and softcropping
layerMask := nil;
FreeLayerMask := false;
if (i < fLayers.Count - 1) and Layers[i + 1].IsMask and (not layer.IsMask) and (layer.Rotate = 0) and (layer.Kind = ielkImage) then
layerMask := Layers[i + 1];
if (i > 0) and (layerMask = nil) and (fSoftCrop = iesfAlphaBlend) then
begin
FreeLayerMask := true;
layerMask := TIEImageLayer.Create(self, nil, false);
layerMask.Bitmap.Allocate(layer.Bitmap.Width, layer.Bitmap.Height, ie8g);
layerMask.Bitmap.Fill(255);
layerMask.PosX := layer.PosX;
layerMask.PosY := layer.PosY;
layerMask.WidthD := layer.WidthD;
layerMask.HeightD := layer.HeightD;
end;
if layerMask <> nil then
SetLayerMask( layer.Bitmap );
if IsFirst and drawBackground then
SubDrawBackground(false, layer.Bitmap.HasAlphaChannel and (not layer.Bitmap.AlphaChannel.Full));
zf := fActualZoomFilter;
if (fStable > 0) or ((layer.WidthD = 0) and (layer.HeightD = 0) and (fZoomX = 100) and (fZoomY = 100)) then
zf := rfNone
else
if ( layer is TIEImageLayer ) and TIEImageLayer( layer ).UseResampleFilter then
zf := TIEImageLayer( layer ).ResampleFilter;
rotateFilter := ierNone;
if fLayersRotationUseFilterOnPreview and fLayersRotationAntialias then
rotateFilter := fLayersRotationFilter;
layer.PaintTo( ABitmap, i, pinteger(fXScr2Bmp), pinteger(fYScr2Bmp), UpdRect, fOffX, fOffY, frx, fry, fo1x, fo1y, fo2x, fo2y, fEnableAlphaChannel, (fBackgroundStyle = iebsSolid) and IsFirst, zf, rotateFilter, fLayerResizing <> ieNone );
AfterDrawLayer(i, ABitmap, updRect^);
if drawGadgets then
begin
if layer.DrawOuter and (i = fLayersCurrent) then
DrawLayerOuter(ABitmap.VclBitmap, i);
end;
if assigned(fOnDrawLayer) then
fOnDrawLayer(self, ABitmap, i);
UnSetLayerMask;
if FreeLayerMask then
begin
layerMask.Free;
end;
IsFirst := False;
end; // end of Visible and (frx<>0) and (fry<>0)
end;
if (fSoftCrop = iesfGrid) or (fSoftCrop = iesfAdd) then
begin
w := ABitmap.Width;
h := ABitmap.Height;
x1 := Layers[0].DrawingInfo.XDst;
y1 := Layers[0].DrawingInfo.YDst;
x2 := x1 + Layers[0].DrawingInfo.WidthDst;
y2 := y1 + Layers[0].DrawingInfo.HeightDst;
for y := 0 to h - 1 do
begin
px := ABitmap.Scanline[y];
case fSoftCrop of
iesfGrid:
for x := 0 to w - 1 do
begin
if (y < y1) or (y > y2) or (x < x1) or (x > x2) then
if ((x + y) and 1) = 0 then
with px^ do
begin
r := 128;
g := 128;
b := 128;
end;
inc(px);
end;
iesfAdd:
for x := 0 to w - 1 do
begin
if (y < y1) or (y >= y2) or (x < x1) or (x >= x2) then
with px^ do
begin
r := blimit(r + fSoftCropValue);
g := blimit(g + fSoftCropValue);
b := blimit(b + fSoftCropValue);
end;
inc(px);
end;
end;
end;
end;
// check if no layer is displayed
if IsFirst and drawBackground then
SubDrawBackground(true);
if drawGadgets then
begin
for i := 0 to fLayers.Count - 1 do
if Layers[i].VisibleBox then
begin
if Layers[i].Selected then
begin
if (miMoveLayers in fMouseInteract) or (miResizeLayers in fMouseInteract) or (miRotateLayers in fMouseInteract) then
DrawLayerBox(ABitmap.VclBitmap, i);
if (miResizeLayers in fMouseInteract) and (not Layers[i].Locked) then
DrawLayerGrips(ABitmap.VclBitmap, i);
if (miRotateLayers in fMouseInteract) and CurrentLayer.SupportsFeature(ielfMoveRotationCenter) then
DrawLayerRotationCenter(ABitmap.VclBitmap, i);
end
else
if fLayersDrawBox then
DrawLayerBox(ABitmap.VclBitmap, i);
end;
// draw selection
PaintSelection(ABitmap.VclBitmap);
// draw grid
PaintPixelGrid(ABitmap.VclBitmap);
end;
end;
procedure TImageEnView.AfterDrawLayer(layerIndex: Integer; DestBitmap: TIEBitmap; const DestRect: TRect);
begin
// nothing to do
end;
{!!
<FS>TImageEnView.LayersMergeAll
<FM>Declaration<FC>
procedure LayersMergeAll(AlphaCompositing: Boolean = False);
<FM>Description<FN>
Call LayersMergeAll to merge all layers in one step.
When <FC>AlphaCompositing<FN> is False then this method performs the same task as <A TImageEnView.LayersDrawTo>, but replaces all layers with the merged result.
This method is fast and works with all layer options, so should be used to merge layers before printing or saving. It does not support transparency.
When <FC>AlphaCompositing<FN> is True the result is equivalent to calling <A TImageEnView.LayersMerge>([]). <A TImageEnView.LayersMergeFilter> will specify the quality of image layers, if they do not have a custom <A TIEImageLayer.UseResampleFilter>
<FM>Example<FN>
// Apply a "Paid" stamp to image
with ImageEnView1 do
begin
LayersAdd( 'PAID', 42, clRed, 'Arial Black', [fsBold] );
CurrentLayer.Rotate := 30;
TIETextLayer( CurrentLayer ).SizeToText();
CurrentLayer.PosX := IELayer_Pos_HCenter;
CurrentLayer.PosY := IELayer_Pos_VCenter;
LayersMergeAll();
end;
<FM>See Also<FN>
- <A TImageEnView.LayersSaveMergedTo>
!!}
procedure TImageEnView.LayersMergeAll(AlphaCompositing: Boolean = False);
var
bmp: TIEBitmap;
posX, posY: Integer;
begin
if fLayers.Count < 2 then
exit;
if AlphaCompositing then
LayersMerge([])
else
begin
if Proc.AutoUndo and ( loAutoUndoChangesbyCode in fLayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_MergeLayers ), ieuObjectsAndLayers, True, IEOP_MERGELAYERS );
// Select base layer
SetLayersCurrent( 0 );
posX := LayersRect().X;
posY := LayersRect().Y;
bmp := TIEBitmap.Create(fIEBitmap.Width, fIEBitmap.Height);
try
bmp.Fill(Background);
LayersDrawTo(bmp);
LayersClear();
fIEBitmap.Assign(bmp);
Layers[0].PosX := posX;
Layers[0].PosY := posY;
finally
bmp.free;
end;
Update;
end;
end;
{!!
<FS>TImageEnView.LayersDrawTo
<FM>Declaration<FC>
procedure LayersDrawTo(Destination: <A TIEBitmap>; AdjustBitmap: Boolean = True);
<FM>Description<FN>
Merges all layers and draws the result to <FC>Destination<FN> bitmap.
This function should replace a sequence of <A TImageEnView.LayersMerge> calls. The destination bitmap will not have a transparency channel.
If <FC>AdjustBitmap<FN> is true the <FC>Destination<FN> bitmap will be sized to display all layers.
<FM>Example<FC>
// draws all layers of ImageEnView1 to ImageEnView2
ImageEnView1.LayersDrawTo( ImageEnView2.IEBitmap );
ImageEnView2.Update;
<FM>See Also<FN>
- <A TImageEnView.LayersSaveMergedTo>
!!}
procedure TImageEnView.LayersDrawTo(Destination: TIEBitmap; AdjustBitmap: Boolean = True);
var
lzoomx, lzoomy: Double;
lviewx, lviewy: Integer;
lMaxLayerWidth, lMaxLayerHeight: Integer;
allLayersRect: TIERectangle;
begin
SetDisplayStable( True ); // Ensure normal quality
allLayersRect := LayersRect();
if AdjustBitmap then
Destination.Resize( allLayersRect.Width, allLayersRect.Height, Background, 255, iehLeft, ievTop);
// paint layers
lzoomx := fZoomX;
lzoomy := fZoomY;
fZoomX := 100;
fZoomY := 100;
fZoomD100X := 1;
fZoomD100Y := 1;
f100DZoomX := 1;
f100DZoomY := 1;
lviewx := fViewX;
lviewy := fViewY;
fViewX := 0;
fViewY := 0;
fOffX := -1 * allLayersRect.x;
fOffY := -1 * allLayersRect.y;
fExtX := Layers[0].Bitmap.Width;
fExtY := Layers[0].Bitmap.Height;
fZZWW := Layers[0].Bitmap.Width;
fZZHH := Layers[0].Bitmap.Height;
lMaxLayerWidth := fLayersRect.width;
lMaxLayerHeight := fLayersRect.height;
fLayersRect.width := Layers[0].Bitmap.Width;
fLayersRect.height := Layers[0].Bitmap.Height;
PaintToEx(Destination, nil, false, false);
fLayersRect.width := lMaxLayerWidth;
fLayersRect.height := lMaxLayerHeight;
fViewX := lviewx;
fViewY := lviewy;
fZoomX := lzoomx;
fZoomY := lzoomy;
fZoomD100X := fZoomX / 100;
f100DZoomX := 100 / fZoomX;
fZoomD100Y := fZoomY / 100;
f100DZoomY := 100 / fZoomY;
fUpdateInvalidate := false;
Update(); // restores other old values
fUpdateInvalidate := true;
CalcPaintCoords();
CreateCoordConvLUT();
end;
{!!
<FS>TImageEnView.LayersSaveMergedTo
<FM>Declaration<FC>
procedure LayersSaveMergedTo(Destination: <A TIEBitmap>; FastOutput: Boolean = False); overload;
procedure LayersSaveMergedTo(const Filename: string; FastOutput: Boolean = False); overload;
procedure LayersSaveMergedTo(const Stream: TStream; FileType: <A TIOFileType>; FastOutput: Boolean = False); overload;
<FM>Description<FN>
Output a merged version of all layers to a <A TIEBitmap>, file or stream.
This method will preserve any alpha channel in the image.
Pass <FC>FastOutput<FN> as True, if you only require a low quality output (without quality filtering or anti-aliasing). This is useful if you will downsize to a thumbnail for example.
Notes:
- <A TImageEnView.LayersMergeFilter> will specify the quality of image layers, if they do not have a custom <A TIEImageLayer.UseResampleFilter>
- The existing image is not changed.
<FM>Example<FC>
// Save a merged version of the layers in ImageEnView1 (preserving the alpha channel
aBitmap := TIEBitmap.Create;
ImageEnView1.LayersSaveMergedTo( aBitmap );
aBitmap.Write( 'd:\Merged.png' );
aBitmap.Free;
// Or alternatively
ImageEnView1.LayersSaveMergedTo( 'd:\Merged.png' );
// Create a thumbnail of this image
aBitmap := TIEBitmap.Create;
ImageEnView1.LayersSaveMergedTo( aBitmap, True );
aBitmap.Resample( 100, 100, rfFastLinear, True );
aBitmap.Write( 'd:\Thumb.jpeg' );
aBitmap.Free;
<FM>See Also<FN>
- <A TImageEnIO.SaveToFileIEN>
- <A TImageEnIO.SaveToStreamIEN>
- <A TImageEnView.LayersMergeAll>
!!}
procedure TImageEnView.LayersSaveMergedTo(Destination: TIEBitmap; FastOutput: Boolean = False);
var
aIEView: TImageEnView;
begin
aIEView := TImageEnView.Create( nil );
try
aIEView.LayersFastOutput := FastOutput;
aIEView.Assign( Self );
aIEView.LayersMergeAll( True );
Destination.Assign( aIEView.IEBitmap );
finally
FreeAndNil( aIEView );
end;
end;
procedure TImageEnView.LayersSaveMergedTo(const Filename: string; FastOutput: Boolean = False);
var
aIEView: TImageEnView;
begin
aIEView := TImageEnView.Create( nil );
try
aIEView.LayersFastOutput := FastOutput;
aIEView.Assign( Self );
aIEView.LayersMergeAll( True );
aIEView.IO.SaveToFile( Filename );
finally
FreeAndNil( aIEView );
end;
end;
procedure TImageEnView.LayersSaveMergedTo(const Stream: TStream; FileType: TIOFileType; FastOutput: Boolean = False);
var
aIEView: TImageEnView;
begin
aIEView := TImageEnView.Create( nil );
try
aIEView.LayersFastOutput := FastOutput;
aIEView.Assign( Self );
aIEView.LayersMergeAll( True );
aIEView.IO.SaveToStream( Stream, FileType );
finally
FreeAndNil( aIEView );
end;
end;
{!!
<FS>TImageEnView.LayerOptions
<FM>Declaration<FC>
property LayerOptions: <A TIELayerOptions>;
<FM>Description<FN>
Options to control layer behavior:
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>loAllowMultiSelect</C> <C>If enabled, users can select multiple layers by holding down the Shift key. Moving, resizing, rotation, and other methods will apply to all selected layers</C> </R>
<R> <C>loAutoSelectMask</C> <C>If you have enabled a <L TIEImageLayer.IsMask>mask</L> for a layer, then when selecting the layer the mask will also be selected (so that any resizing actions apply to the mask layer too)</C> </R>
<R> <C>loAutoUndoChangesByUser</C> <C>If you have enabled <A TImageEnProc.AutoUndo>, then changes to layers made by users (using <L TImageEnView.MouseInteract>layer interactions<L> or <L TImageEnView Actions>layer TActions<L>) will be saved to the undo stack</C> </R>
<R> <C>loAutoUndoChangesByCode</C> <C>If you have enabled <A TImageEnProc.AutoUndo>, then programmatic changes to layers will be saved to the undo stack. This applies to the methods: <A TImageEnView.LayersAdd>, <A TImageEnView.LayersInsert>, <A TImageEnView.LayersCreateFromSelection>, <A TImageEnView.LayersCreateFromFile>, <A TImageEnView.LayersCreateFromEdge>, <A TImageEnView.LayersCreateFromAlpha>, <A TImageEnView.LayersRemove>, <A TImageEnView.LayersMerge>, <A TImageEnView.LayersMergeAll>, <A TImageEnView.LayersAlign>, <A TImageEnView.LayersMove>, <A TImageEnView.LayersRotateAll>, <A TImageEnView.LayersGroup>, <A TImageEnView.LayersUngroup>, <A TImageEnView.LayersCropBackground> </C> </R>
<R> <C>loAutoPromptForImage</C> <C>When setting <A TImageEnView.MouseInteract> to <FC>miCreateImageLayers<FN>, the user can drag select to create an <A TIEImageLayer> If <FC>loAutoPromptForImage<FN> is enabled, then the user will be prompted to browse for an image file when after completing the selection. If the user cancels, the image layer is not added.</C> </R>
<R> <C>loAutoFixBorders</C> <C>If enabled, <A TImageEnView.LayersFixBorders> will be called prior to rotation to removes any transparency around the edges of the image.</C> </R>
<R> <C>loAutoFixRotation</C> <C>If enabled, <A TImageEnView.LayersFixRotations> will be called after rotation to lock in the rotation angle of image layers.</C> </R>
<R> <C>loDynamicCanvas</C> <C>If enabled, ImageEn will align the view to the bounds of all layers (i.e. by <A TImageEnView.LayersRect>). You will be able to scroll to access layers that have been pushed beyond the bounds of Layer 0. However this may cause the view to scroll when moving layers around (so is best paired with <A TImageEnView.Center>=False). When not enabled, the view is aligned with Layer 0, and any layers pushed outside of the bounds of layer 0 may be inaccessible if they are off-screen.</C> </R>
</TABLE>
Default: [loAllowMultiSelect, loAutoUndoChangesByUser, loAutoPromptForImage, loAutoFixBorders]
Note: To disable movement of layers by the keyboard, see <FC>iesoAllowMoveByKeyboard<FN> of <A TImageEnView.SelectionOptions>
<FM>Examples<FC>
// Enable automatic mask selection
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAutoSelectMask ];
// Enable automatic undo for all image changes including layer changes
ImageEnView1.Proc.AutoUndo := True;
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAutoUndoChangesByUser ];
// Disable multiple selection of layers
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions - [ loAutoSelectMask ];
// Allow users to create image layers. Prompt for an image file after selection
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loAutoPromptForImage ];
ImageEnView1.MouseInteract := [ miCreateImageLayers ];
// Display the entire image (including outlying layers) within the view
ImageEnView1.LayerOptions := ImageEnView1.LayerOptions + [ loFitToLayersWhenZooming ];
ImageEnView1.Fit( False );
<FM>See Also<FN>
- <A TImageEnView.Layers>
- <A TImageEnView.LayersCurrent>
- <A TImageEnView.LayersSelectAll>
- <A TImageEnView.LayersDeselectAll>
- <A TIELayer.Selected>
!!}
procedure TImageEnView.SetLayerOptions(const Value: TIELayerOptions);
var
multiSelChanged, dynamicCanvasChanged: Boolean;
begin
multiSelChanged := ( loAllowMultiSelect in Value ) <> ( loAllowMultiSelect in fLayerOptions );
dynamicCanvasChanged := ( loDynamicCanvas in Value ) <> ( loDynamicCanvas in fLayerOptions );
fLayerOptions := Value;
if multiSelChanged and not LayersAllowMultiSelect then
LayersDeselectAll
else
if dynamicCanvasChanged then
Update();
end;
{!!
<FS>TImageEnView.LayerDefaults
<FM>Declaration<FC>
property LayerDefaults: TStringList;
<FM>Description<FN>
Specifies a list of properties that are assigned to all new layers.
The properties are a list of Name=Value pairs, with names from the <A TIELayer Property Consts>.
Notes:
- These properties apply to layers created programmatically (e.g. using <A TImageEnView.LayersAdd>) and by the user (e.g. <L TImageEnView.MouseInteract>using miCreateShapeLayers</L>).
- The <A TImageEnView.OnNewLayer> event can also be used to apply default properties
<FM>Sample Output<FC>
IELP_BorderColor=clNone
IELP_BorderWidth=0
IELP_FillColor=clYellow
IELP_FillColor2=clRed
IELP_FillGradient=1
IELP_Rotate=0
<FM>Example<FC>
ImageEnView1.LayerDefaults.Clear();
ImageEnView1.LayerDefaults.Add( IELP_BorderColor +'=$008000FF' );
ImageEnView1.LayerDefaults.Add( IELP_BorderWidth +'=3' );
ImageEnView1.LayersAdd( 'C:\New Zealand.jpg' ); // Added image will have a pink border
<IMG help_images\Image_withBorder.gif>
// Allow the user to create, size and select red arrows
ImageEnView1.LayerDefaults.Clear();
ImageEnView1.LayerDefaults.Add( IELP_LineColor +'=clRed' );
ImageEnView1.LayerDefaults.Add( IELP_LineWidth +'=6' );
ImageEnView1.LayerDefaults.Add( IELP_LineShapeSize +'=20' );
ImageEnView1.LayerDefaults.Add( IELP_LineStartShape +'=1' );
ImageEnView1.LayerDefaults.Add( IELP_Rotate +'=235' );
ImageEnView1.MouseInteract := [ miCreateLineLayers, miMoveLayers, miResizeLayers ];
<FM>See Also<FN>
- <A TIEImageEnGlobalSettings.DefaultLayerText>
- <A TImageEnView.OnNewLayer>
- <A TIELayer.GetProperties>
- <A TIELayer.SetProperties>
!!}
function TImageEnView.GetLayerDefaults() : TStringList;
begin
if not assigned( fLayerDefaults ) then
fLayerDefaults := TStringList.Create();
Result := fLayerDefaults;
end;
// Return true if the LayersFastDrawing property means we are currently using fast drawing
function TImageEnView.LayersFastDrawingActive: Boolean;
begin
if fLayersFastDrawing = iefDelayed then
Result := fStable <> 0
else
Result := fLayersFastDrawing = iefFast
end;
{!!
<FS>TImageEnView.LayersCropped
<FM>Declaration<FC>
property LayersCropped: Boolean;
<FM>Description<FN>
When enabled, any part of layers that are outside the background image (layer 0) area will not be displayed.
If true, it overrides the <A TIELayer.Cropped> property of individual layers.
Default: False
<FM>Example<FC>
// Crop the display of only the current layer
ImageEnView1.LayersCropped := False;
ImageEnView1.CurrentLayer.Cropped := True;
// Crop the display of all layers
ImageEnView1.LayersCropped := True;
// Don't crop the display of any layers
ImageEnView1.LayersCropped := False;
for i := 0 to ImageEnView1.LayersCount - 1 do
ImageEnView1.Layers[ I ].Cropped := False;
!!}
procedure TImageEnView.SetLayersCropped(const Value: Boolean);
begin
if fLayersCropped <> Value then
begin
fLayersCropped := Value;
Update();
end;
end;
// Count of selected layers
function TImageEnView.MultiSelectedLayersCount: Integer;
var
i: Integer;
begin
Result := 0;
for i := 0 to fLayers.Count - 1 do
if TIELayer( fLayers[ I ]).Selected then
Inc( Result );
end;
// List of indexes of selected layers
function TImageEnView.MultiSelectedLayersList(): TIEArrayOfInteger;
var
i: integer;
idx: Integer;
begin
idx := 0;
SetLength( result, MultiSelectedLayersCount() );
for i := 0 to fLayers.Count - 1 do
if TIELayer( fLayers[ I ]).Selected then
begin
result[ idx ] := i;
Inc( idx );
end;
end;
{!!
<FS>TImageEnView.SetLayersBoxStyle
<FM>Declaration<FC>
procedure SetLayersBoxStyle(PenStyle: TPenStyle = psDot; PenMode: TPenMode = pmNot; PenWidth: Integer = 1; PenColor: TColor = clBlack);
<FM>Description<FN>
Specifies the style of layer box border, if <A TImageEnView.LayersDrawBox> is enabled.
For more control, use <A TImageEnView.OnDrawLayerBox>
!!}
procedure TImageEnView.SetLayersBoxStyle(PenStyle: TPenStyle; PenMode: TPenMode; PenWidth: Integer; PenColor: TColor);
begin
fLayerBoxPen.Style := PenStyle;
fLayerBoxPen.Mode := PenMode;
fLayerBoxPen.Width := PenWidth;
fLayerBoxPen.Color := PenColor;
end;
procedure TImageEnView.DrawLayerBox(ABitmap: TBitmap; idx: integer);
var
lyr: TIELayer;
begin
if assigned(fOnDrawLayerBox) then
fOnDrawLayerBox(self, ABitmap, idx)
else
with ABitmap.Canvas do
begin
Pen.Assign( fLayerBoxPen );
Brush.Style := bsClear;
lyr := TIELayer(fLayers[idx]);
if lyr.Rotate = 0 then
begin
with lyr.ClientAreaBox do
Rectangle(Left, Top, Right, Bottom);
end
else
begin
Polygon(lyr.DrawingInfo.RotatedDest);
end;
end;
end;
procedure TImageEnView.DrawLayerOuter(ABitmap: TBitmap; idx: integer);
var
i, j: integer;
rgb: PRGB;
bwidth, bheight: integer;
begin
with ABitmap.Canvas do
begin
with TIELayer(fLayers[idx]).ClientAreaBox do
begin
bwidth := ABitmap.Width;
bheight := ABitmap.Height;
for i := 0 to bheight - 1 do
begin
rgb := ABitmap.Scanline[i];
for j := 0 to bwidth - 1 do
begin
if ((j < Left) or (j > Right) or (i < Top) or (i > Bottom)) and ((((i and 1) = 0) and ((j and 1) = 0)) or (((i and 1) = 1) and ((j and 1) = 1))) then
with rgb^ do
begin
r := 97;
g := 97;
b := 97;
end;
inc(rgb);
end;
end;
end;
end;
end;
// a layer must be selected (fLayersCurrent must be >-1)
function TImageEnView.FindLayerGrip(x, y: integer): TIEGrip;
var
coords: array[0..8] of TRect;
i: integer;
begin
result := ieNone;
if fLayersCurrent > -1 then
begin
CalcLayerGripCoords(fLayersCurrent, coords);
for i := 0 to 8 do
if IEPointInRect(x, y, coords[ i ].left, coords[ i ].top, coords[ i ].right, coords[ i ].bottom) then
begin
result := TIEGrip(i + 1);
if not (miRotatelayers in fMouseInteract) and (result = ieRotationCenter) then
result := ieNone;
break;
end;
end;
end;
// Returns the grip for any selected layer
// bSetCurrent = True: Makes the layer the active one
function TImageEnView.FindLayerGripAnySel(x, y: integer; bSetCurrent: Boolean = False): TIEGrip;
var
coords: array[0..8] of TRect;
i, iL: integer;
ALayer: TIELayer;
begin
result := ieNone;
if fLayersCurrent > -1 then
Result := FindLayerGrip( x, y );
if ( result <> ieNone ) or ( LayersAllowMultiSelect = False ) then
exit;
for iL := 1 to LayersCount - 1 do
begin
ALayer := TIELayer( fLayers[ iL ]);
if ( ALayer.Locked = False ) and ALayer.Selected then
begin
CalcLayerGripCoords(iL, coords);
for i := 0 to 8 do
if IEPointInRect(x, y, coords[ i ].left, coords[ i ].top, coords[ i ].right, coords[ i ].bottom) then
begin
result := TIEGrip(i + 1);
if not (miRotatelayers in fMouseInteract) and (result = ieRotationCenter) then
result := ieNone;
if ( Result <> ieNone ) and bSetCurrent then
begin
if SetLayersCurrentEx( iL, True, False ) then
DoLayerNotify(fLayersCurrent, ielSelected);
end;
exit;
end;
end;
end;
end;
function TImageEnView.FindLayerGripDist(x, y: integer; var Distance: Double): TIEGrip;
var
coords: array[0..8] of TRect;
i: integer;
d: Double;
begin
result := ieNone;
Distance := 10000000;
if fLayersCurrent > -1 then
begin
CalcLayerGripCoords(fLayersCurrent, coords);
for i := 0 to 8 do
with coords[i] do
begin
d := sqrt(sqr(x-((Left+Right)/2))+sqr(y-((Top+Bottom)/2)));
if d < Distance then
begin
Distance := d;
result := TIEGrip(i + 1);
end;
end;
end;
end;
// 0=left-top 1=right-top 2=right-bottom 3=left-bottom
// 4=left side
// 5=right side
// 6=top side
// 7=bottom side
// 8=rotation center grip
procedure TImageEnView.CalcLayerGripCoords(layeridx: integer; var coords: array of TRect);
var
lyr: TIELayer;
begin
lyr := TIELayer(fLayers[layeridx]);
if ( lyr.Kind <> ielkImage ) or ( lyr.Rotate = 0 ) then
begin
with lyr.ClientAreaBox do
begin
// left | top
coords[0].Left := left - fLyrGripSize;
coords[0].Top := top - fLyrGripSize;
coords[0].Right := left + fLyrGripSize;
coords[0].Bottom := top + fLyrGripSize;
// right | top
coords[1].Left := right - fLyrGripSize;
coords[1].Top := top - fLyrGripSize;
coords[1].Right := right + fLyrGripSize;
coords[1].Bottom := top + fLyrGripSize;
// right | bottom
coords[2].Left := right - fLyrGripSize;
coords[2].Top := bottom - fLyrGripSize;
coords[2].Right := right + fLyrGripSize;
coords[2].Bottom := bottom + fLyrGripSize;
// left | bottom
coords[3].Left := left - fLyrGripSize;
coords[3].Top := bottom - fLyrGripSize;
coords[3].Right := left + fLyrGripSize;
coords[3].Bottom := bottom + fLyrGripSize;
// left | center
coords[4].Left := left - fLyrGripSize;
coords[4].Top := ((top + bottom) div 2) - fLyrGripSize;
coords[4].Right := left + fLyrGripSize;
coords[4].Bottom := ((top + bottom) div 2) + fLyrGripSize;
// right | center
coords[5].Left := right - fLyrGripSize;
coords[5].Top := ((top + bottom) div 2) - fLyrGripSize;
coords[5].Right := right + fLyrGripSize;
coords[5].Bottom := ((top + bottom) div 2) + fLyrGripSize;
// top | center
coords[6].Left := ((left + right) div 2) - fLyrGripSize;
coords[6].Top := top - fLyrGripSize;
coords[6].Right := ((left + right) div 2) + fLyrGripSize;
coords[6].Bottom := top + fLyrGripSize;
// bottom | center
coords[7].Left := ((left + right) div 2) - fLyrGripSize;
coords[7].Top := bottom - fLyrGripSize;
coords[7].Right := ((left + right) div 2) + fLyrGripSize;
coords[7].Bottom := bottom + fLyrGripSize;
end;
end
else
begin
with lyr.DrawingInfo do
begin
// left | top
coords[0].Left := RotatedDest[0].X - fLyrGripSize;
coords[0].Top := RotatedDest[0].Y - fLyrGripSize;
coords[0].Right := RotatedDest[0].X + fLyrGripSize;
coords[0].Bottom := RotatedDest[0].Y + fLyrGripSize;
// right | top
coords[1].Left := RotatedDest[1].X - fLyrGripSize;
coords[1].Top := RotatedDest[1].Y - fLyrGripSize;
coords[1].Right := RotatedDest[1].X + fLyrGripSize;
coords[1].Bottom := RotatedDest[1].Y + fLyrGripSize;
// right | bottom
coords[2].Left := RotatedDest[2].X - fLyrGripSize;
coords[2].Top := RotatedDest[2].Y - fLyrGripSize;
coords[2].Right := RotatedDest[2].X + fLyrGripSize;
coords[2].Bottom := RotatedDest[2].Y + fLyrGripSize;
// left | bottom
coords[3].Left := RotatedDest[3].X - fLyrGripSize;
coords[3].Top := RotatedDest[3].Y - fLyrGripSize;
coords[3].Right := RotatedDest[3].X + fLyrGripSize;
coords[3].Bottom := RotatedDest[3].Y + fLyrGripSize;
// left | center
coords[4].Left := ((RotatedDest[0].X+RotatedDest[3].X) div 2) - fLyrGripSize;
coords[4].Top := ((RotatedDest[0].Y+RotatedDest[3].Y) div 2) - fLyrGripSize;
coords[4].Right := coords[4].Left + fLyrGripSize*2;
coords[4].Bottom := coords[4].Top + fLyrGripSize*2;
// right | center
coords[5].Left := ((RotatedDest[1].X+RotatedDest[2].X) div 2) - fLyrGripSize;
coords[5].Top := ((RotatedDest[1].Y+RotatedDest[2].Y) div 2) - fLyrGripSize;
coords[5].Right := coords[5].Left + fLyrGripSize*2;
coords[5].Bottom := coords[5].Top + fLyrGripSize*2;
// top | center
coords[6].Left := ((RotatedDest[0].X+RotatedDest[1].X) div 2) - fLyrGripSize;
coords[6].Top := ((RotatedDest[0].Y+RotatedDest[1].Y) div 2) - fLyrGripSize;
coords[6].Right := coords[6].Left + fLyrGripSize*2;
coords[6].Bottom := coords[6].Top + fLyrGripSize*2;
// bottom | center
coords[7].Left := ((RotatedDest[2].X+RotatedDest[3].X) div 2) - fLyrGripSize;
coords[7].Top := ((RotatedDest[2].Y+RotatedDest[3].Y) div 2) - fLyrGripSize;
coords[7].Right := coords[7].Left + fLyrGripSize*2;
coords[7].Bottom := coords[7].Top + fLyrGripSize*2;
end;
end;
// rotation center
coords[8].Left := lyr.ConvXBmp2Scr( round(lyr.OriginalWidth * lyr.RotateCenterX ) ) - fLyrGripSize;
coords[8].Top := lyr.ConvYBmp2Scr( round(lyr.OriginalHeight * lyr.RotateCenterY ) ) - fLyrGripSize;
coords[8].Right := coords[8].Left + fLyrGripSize*2;
coords[8].Bottom := coords[8].Top + fLyrGripSize*2;
end;
procedure TImageEnView.DrawLayerGrips(ABitmap: TBitmap; idx: integer);
var
i: integer;
coords: array[0..8] of TRect;
iec: TIECanvas;
begin
CalcLayerGripCoords(idx, coords);
if assigned(fOnDrawLayerGrip) then
begin
for i := 0 to 8 do
fOnDrawLayerGrip(self, ABitmap, idx, i, coords[i]);
end
else
begin
iec := TIECanvas.Create(ABitmap.Canvas, true, true);
with iec do
begin
Pen.Style := psSolid;
Pen.Mode := pmCopy;
Pen.Color := fLyrGripColor1;
Pen.Width := 1;
Brush.Style := fLyrGripBrushStyle;
Brush.Color := fLyrGripColor2;
for i := 0 to 7 do
with coords[i] do
case fLyrGripShape of
iegsBox: Rectangle(Left, Top, Right, Bottom);
iegsCircle: Ellipse(Left, Top, Right, Bottom);
end;
end;
iec.Free;
end;
end;
procedure TImageEnView.DrawLayerRotationCenter(ABitmap: TBitmap; idx: integer);
var
coords: array[0..8] of TRect;
iec: TIECanvas;
begin
CalcLayerGripCoords(idx, coords);
iec := TIECanvas.Create(ABitmap.Canvas, true, true);
with iec do
begin
Pen.Style := psSolid;
Pen.Mode := pmCopy;
Pen.Color := fLyrGripColor1;
Brush.Style := fLyrGripBrushStyle;
Brush.Color := fLyrGripColor2;
Ellipse(coords[8]);
end;
iec.Free;
end;
{!!
<FS>TImageEnView.SaveSelectionToStream
<FM>Declaration<FC>
procedure SaveSelectionToStream(Stream: TStream);
<FM>Description<FN>
Saves the current selection to the specified stream (just the user selection, not the image content)
<FM>Example<FC>
ImageEnView1.Select(10, 10, 100, 100);
ImageEnView1.SaveSelectionToStream(sel1);
..
sel1.Position := 0;
ImageEnView1.LoadSelectionFromStream(sel1); // this equates to Select(10, 10, 100, 100)
!!}
procedure TImageEnView.SaveSelectionToStream(Stream: TStream);
var
val: Integer;
begin
// main sizes
Stream.Write(fSelectionMask.Width, sizeof(fSelectionMask.Width));
Stream.Write(fSelectionMask.Height, sizeof(fSelectionMask.Height));
// TImageEnView
Stream.Write(PIEAnimPoly(fHPolySel)^.PolyCount, sizeof(PIEAnimPoly(fHPolySel)^.PolyCount));
if PIEAnimPoly(fHPolySel)^.Poly <> nil then
Stream.Write(PIEAnimPoly(fHPolySel)^.Poly^, sizeof(TPoint) * PIEAnimPoly(fHPolySel)^.PolyCount);
Stream.Write(PIEAnimPoly(fHPolySel)^.RX1, sizeof(PIEAnimPoly(fHPolySel)^.RX1));
Stream.Write(PIEAnimPoly(fHPolySel)^.RY1, sizeof(PIEAnimPoly(fHPolySel)^.RY1));
Stream.Write(PIEAnimPoly(fHPolySel)^.RX2, sizeof(PIEAnimPoly(fHPolySel)^.RX2));
Stream.Write(PIEAnimPoly(fHPolySel)^.RY2, sizeof(PIEAnimPoly(fHPolySel)^.RY2));
Stream.Write(fSel, sizeof(fSel));
val := CurrentLayer.PosX;
Stream.Write( val, sizeof( integer ));
val := CurrentLayer.PosY;
Stream.Write( val, sizeof( integer ));
// TIEMask
Stream.Write(fSelectionMask.BitsPerPixel, sizeof(fSelectionMask.BitsPerPixel));
Stream.Write(fSelectionMask.X1, sizeof(fSelectionMask.X1));
Stream.Write(fSelectionMask.Y1, sizeof(fSelectionMask.Y1));
Stream.Write(fSelectionMask.X2, sizeof(fSelectionMask.X2));
Stream.Write(fSelectionMask.Y2, sizeof(fSelectionMask.Y2));
Stream.Write(fSelectionMask.Full, sizeof(fSelectionMask.Full));
Stream.Write(fSelectionMask.Bits^, fSelectionMask.Height * fSelectionMask.RowLen);
end;
{!!
<FS>TImageEnView.LoadSelectionFromStream
<FM>Declaration<FC>
function LoadSelectionFromStream(Stream: TStream; Options: <A TIERSOptions> = iersMoveToAdapt): boolean;;
<FM>Description<FN>
Loads a selection from a stream (saved using <A TImageEnView.SaveSelectionToStream> or <A TImageEnView.SaveSelectionToFile>). Returns False if it fails.
Options specifies how the selection is adapted when it is applied to an image of a different size or position.
Note: To share the same selection among layers you must use iersSyncLayers.
<FM>Example<FC>
ImageEnView1.Select(10, 10, 100, 100);
ImageEnView1.SaveSelectionToStream(sel1);
..
sel1.Position := 0;
ImageEnView1.LoadSelectionFromStream(sel1); // this equates to Select(10, 10, 100, 100)
!!}
// return false if cannot load the selection (the background bitmap has wrong sizes)
function TImageEnView.LoadSelectionFromStream(Stream: TStream; Options: TIERSOptions): boolean;
var
w, h, b, x1, y1, x2, y2: integer;
f: boolean;
psx, psy: Integer;
ix, iy: Integer;
begin
// main sizes
result := true;
Stream.Read(w, sizeof(fSelectionMask.Width));
Stream.Read(h, sizeof(fSelectionMask.Height));
// TImageEnView
if PIEAnimPoly(fHPolySel)^.Poly <> nil then
freemem(PIEAnimPoly(fHPolySel)^.Poly);
PIEAnimPoly(fHPolySel)^.Poly := nil;
Stream.Read(PIEAnimPoly(fHPolySel)^.PolyCount, sizeof(PIEAnimPoly(fHPolySel)^.PolyCount));
getmem(PIEAnimPoly(fHPolySel)^.Poly, sizeof(TPoint) * (PIEAnimPoly(fHPolySel)^.PolyCount));
PIEAnimPoly(fHPolySel)^.PolyCapacity := PIEAnimPoly(fHPolySel)^.PolyCount;
if PIEAnimPoly(fHPolySel)^.Poly <> nil then
Stream.Read(PIEAnimPoly(fHPolySel)^.Poly^, sizeof(TPoint) * PIEAnimPoly(fHPolySel)^.PolyCount);
Stream.Read(PIEAnimPoly(fHPolySel)^.RX1, sizeof(PIEAnimPoly(fHPolySel)^.RX1));
Stream.Read(PIEAnimPoly(fHPolySel)^.RY1, sizeof(PIEAnimPoly(fHPolySel)^.RY1));
Stream.Read(PIEAnimPoly(fHPolySel)^.RX2, sizeof(PIEAnimPoly(fHPolySel)^.RX2));
Stream.Read(PIEAnimPoly(fHPolySel)^.RY2, sizeof(PIEAnimPoly(fHPolySel)^.RY2));
Stream.Read(fSel, sizeof(fSel));
Stream.Read(psx, sizeof(integer));
Stream.Read(psy, sizeof(integer));
// TIEMask
Stream.Read(b, sizeof(fSelectionMask.BitsPerPixel));
fSelectionMask.AllocateBits(w, h, b);
Stream.Read(x1, sizeof(fSelectionMask.X1));
Stream.Read(y1, sizeof(fSelectionMask.Y1));
Stream.Read(x2, sizeof(fSelectionMask.X2));
Stream.Read(y2, sizeof(fSelectionMask.Y2));
fSelectionMask.X1 := x1;
fSelectionMask.Y1 := y1;
fSelectionMask.X2 := x2;
fSelectionMask.Y2 := y2;
Stream.Read(f, sizeof(fSelectionMask.Full));
fSelectionMask.Full := f;
Stream.Read(fSelectionMask.Bits^, fSelectionMask.Height * fSelectionMask.RowLen);
LockUpdate;
// sync layers
if (Options = iersSyncLayers) then
begin
AnimPolygonClear(fHPolySel);
fSelectionMask.Resize(fIEBitmap_Width + imax(0, psx) + w, fIEBitmap_Height + imax(0, psy) + h);
fSelectionMask.TranslateBitmap(psx, psy, true);
ix := -CurrentLayer.PosX;
iy := -CurrentLayer.PosY;
fSelectionMask.TranslateBitmap(ix, iy, true );
fSelectionMask.Resize(fIEBitmap_Width, fIEBitmap_Height);
fSelectionMask.SyncFull;
fSelectionMask.SyncRect;
end
else
begin
if ((w <> fIEBitmap_Width) or (h <> fIEBitmap_Height)) then
begin
if (not fSelectionMask.IsEmpty) then
begin
// try to move the selection
if (Options = iersMoveToAdapt) and (x2 > fIEBitmap_Width) then
begin
x1 := fIEBitmap_Width - (fSelectionMask.X2-fSelectionMask.X1+1);
MoveSelection( -(fSelectionMask.X1-x1), 0 );
end;
if (Options = iersMoveToAdapt) and (y2 > fIEBitmap_Height) then
begin
y1 := fIEBitmap_Height - (fSelectionMask.Y2-fSelectionMask.Y1+1);
MoveSelection( 0, -(fSelectionMask.Y1-y1) );
end;
fSelectionMask.Resize(fIEBitmap_Width, fIEBitmap_Height);
fSelectionMask.SyncFull;
fSelectionMask.SyncRect;
end
else
fSelectionMask.Resize(fIEBitmap_Width, fIEBitmap_Height);
end;
end;
if fSelectionMask.IsEmpty then
Deselect
else
fSel := true;
ShowSelectionEx(true);
UnLockUpdate;
Update;
end;
{!!
<FS>TImageEnView.SaveSelectionToFile
<FM>Declaration<FC>
procedure SaveSelectionToFile(const FileName: String);
<FM>Description<FN>
Saves the current selection to the specified file (just the user selection, not the image content).
<FM>Example<FC>
ImageEnView1.Select(10, 10, 100, 100);
ImageEnView1.SaveSelectionToFile('selection1');
..
sel1.Position := 0;
ImageEnView1.LoadSelectionFromFile('selection1'); // this equates to Select(10, 10, 100, 100)
!!}
procedure TImageEnView.SaveSelectionToFile(const FileName: String);
var
fs: TFileStream;
begin
fs := TFileStream.Create(FileName, fmCreate);
try
SaveSelectionToStream(fs);
finally
FreeAndNil(fs);
end;
end;
{!!
<FS>TImageEnView.LoadSelectionFromFile
<FM>Declaration<FC>
function LoadSelectionFromFile(const FileName: String; Options: <A TIERSOptions> = iersMoveToAdapt): boolean;
<FM>Description<FN>
Loads a selection from a file (saved using <A TImageEnView.SaveSelectionToStream> or <A TImageEnView.SaveSelectionToFile>).
Returns False if it fails.
Options specifies how the selection is adapted when it comes from an image with different size or position.
Note: To share the same selection among layers you must use iersSyncLayers.
<FM>Example<FC>
ImageEnView1.Select(10, 10, 100, 100);
ImageEnView1.SaveSelectionToFile('C:\selection1');
..
sel1.Position := 0;
ImageEnView1.LoadSelectionFromFile('C:\selection1'); // this equates to Select(10, 10, 100, 100)
!!}
function TImageEnView.LoadSelectionFromFile(const FileName: String; Options: TIERSOptions = iersMoveToAdapt): boolean;
var
fs: TFileStream;
begin
fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
result := LoadSelectionFromStream(fs, Options);
finally
FreeAndNil(fs);
end;
end;
{!!
<FS>TImageEnView.MergeSelectionFromFile
<FM>Declaration<FC>
function MergeSelectionFromFile(const FileName: String): Boolean;
<FM>Description<FN>
Loads a selection from a file (saved using <A TImageEnView.SaveSelectionToStream> or <A TImageEnView.SaveSelectionToFile>) merging it with the currently one.
Returns False if it fails.
<FM>Example<FC>
ImageEnView1.Select(10, 10, 100, 100);
ImageEnView1.SaveSelectionToFile('C:\selection1');
..
sel1.Position := 0;
ImageEnView1.MergeSelectionFromFile('C:\selection1'); // this equates to Select(10, 10, 100, 100, iespAdd)
!!}
function TImageEnView.MergeSelectionFromFile(const FileName: String): Boolean;
var
fs: TFileStream;
begin
fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
result := MergeSelectionFromStream(fs);
finally
FreeAndNil(fs);
end;
end;
{!!
<FS>TImageEnView.MergeSelectionFromStream
<FM>Declaration<FC>
function MergeSelectionFromStream(Stream: TStream): Boolean;
<FM>Description<FN>
Loads a selection from a stream (saved using <A TImageEnView.SaveSelectionToStream> or <A TImageEnView.SaveSelectionToFile>) merging it with the currently one.
Returns False if it fails.
<FM>Example<FC>
ImageEnView1.Select(10, 10, 100, 100);
ImageEnView1.SaveSelectionToStream(sel1);
..
sel1.Position := 0;
ImageEnView1.MergeSelectionFromStream(sel1); // this equates to Select(10, 10, 100, 100, iespAdd)
!!}
function TImageEnView.MergeSelectionFromStream(Stream: TStream): boolean;
var
w, h, b, x1, y1, x2, y2: integer;
f: boolean;
msk2: TIEMask;
src, dst, x, y: Integer;
psx, psy: Integer;
begin
// main sizes
result := true;
Stream.Read(w, sizeof(fSelectionMask.Width));
Stream.Read(h, sizeof(fSelectionMask.Height));
// TImageEnView
if PIEAnimPoly(fHPolySel)^.Poly <> nil then
freemem(PIEAnimPoly(fHPolySel)^.Poly);
PIEAnimPoly(fHPolySel)^.Poly := nil;
Stream.Read(PIEAnimPoly(fHPolySel)^.PolyCount, sizeof(PIEAnimPoly(fHPolySel)^.PolyCount));
getmem(PIEAnimPoly(fHPolySel)^.Poly, sizeof(TPoint) * (PIEAnimPoly(fHPolySel)^.PolyCount));
PIEAnimPoly(fHPolySel)^.PolyCapacity := PIEAnimPoly(fHPolySel)^.PolyCount;
if PIEAnimPoly(fHPolySel)^.Poly <> nil then
Stream.Read(PIEAnimPoly(fHPolySel)^.Poly^, sizeof(TPoint) * PIEAnimPoly(fHPolySel)^.PolyCount);
Stream.Read(PIEAnimPoly(fHPolySel)^.RX1, sizeof(PIEAnimPoly(fHPolySel)^.RX1));
Stream.Read(PIEAnimPoly(fHPolySel)^.RY1, sizeof(PIEAnimPoly(fHPolySel)^.RY1));
Stream.Read(PIEAnimPoly(fHPolySel)^.RX2, sizeof(PIEAnimPoly(fHPolySel)^.RX2));
Stream.Read(PIEAnimPoly(fHPolySel)^.RY2, sizeof(PIEAnimPoly(fHPolySel)^.RY2));
Stream.Read(fSel, sizeof(fSel));
Stream.Read(psx, sizeof(integer));
Stream.Read(psy, sizeof(integer));
AnimPolygonClear(fHPolySel);
// TIEMask
Stream.Read(b, sizeof(fSelectionMask.BitsPerPixel));
msk2 := TIEMask.Create;
msk2.AllocateBits(w, h, b);
Stream.Read(x1, sizeof(fSelectionMask.X1));
Stream.Read(y1, sizeof(fSelectionMask.Y1));
Stream.Read(x2, sizeof(fSelectionMask.X2));
Stream.Read(y2, sizeof(fSelectionMask.Y2));
msk2.X1 := x1;
msk2.Y1 := y1;
msk2.X2 := x2;
msk2.Y2 := y2;
Stream.Read(f, sizeof(fSelectionMask.Full));
msk2.Full := f;
Stream.Read(msk2.Bits^, msk2.Height * msk2.RowLen);
for y := y1 to y2 do
for x := x1 to x2 do
begin
src := msk2.GetPixel(x, y);
dst := fSelectionMask.GetPixel(x, y);
fSelectionMask.SetPixel(x, y, imax(src, dst) );
end;
fSelectionMask.Full := msk2.Full and fSelectionMask.Full;
SelectCustom;
FreeAndNil(msk2);
end;
{!!
<FS>TImageEnView.SavedSelectionsCount
<FM>Declaration<FC>
property SavedSelectionsCount: Integer
<FM>Description<FN>
Returns the number of saved selections.
!!}
function TImageEnView.GetSavedSelectionsCount: Integer;
begin
result := fSavedSelection.Count;
end;
// push current selection in fSavedSelection list
{!!
<FS>TImageEnView.SaveSelection
<FM>Declaration<FC>
procedure SaveSelection;
<FM>Description<FN>
Adds the current selection to the stack (selections list).
<FM>Example<FC>
ImageEnView1.Select(10, 10, 100, 100);
ImageEnView1.SaveSelection;
ImageEnView1.Select(200, 200, 150, 150);
ImageEnView1.SaveSelection;
ImageEnView1.Deselect;
ImageEnView1.RestoreSelection; // reload 200,200,150,150
..
ImageEnView1.RestoreSelection; // reload 10,10,100,100
!!}
procedure TImageEnView.SaveSelection;
var
ms: TMemoryStream;
begin
ms := TMemoryStream.Create;
SaveSelectionToStream(ms);
fSavedSelection.Add(ms);
end;
// pop a selection from fSavedSelection list
{!!
<FS>TImageEnView.RestoreSelection
<FM>Declaration<FC>
function RestoreSelection(Remove: Boolean=true; Options: <A TIERSOptions> = iersMoveToAdapt): boolean;
<FM>Description<FN>
Restore a saved selection from the selections stack. RestoreSelection returns false when there aren't any selections saved in the stack.
Options specifies how the selection is adapted when it comes from an image with different size or position.
Note: To share the same selection among layers you must use iersSyncLayers.
Remove allows you to remove restored selection from selections stack.
<FM>Example<FC>
ImageEnView1.Select(10, 10, 100, 100);
ImageEnView1.SaveSelection;
ImageEnView1.Select(200, 200, 150, 150);
ImageEnView1.SaveSelection;
ImageEnView1.Deselect;
ImageEnView1.RestoreSelection; // reload 200,200,150,150
..
ImageEnVIew1.RestoreSelection; // reload 10,10,100,100
!!}
function TImageEnView.RestoreSelection(Remove: Boolean; Options: TIERSOptions): boolean;
var
ms: TMemoryStream;
begin
result := false;
if fSavedSelection.Count > 0 then
begin
ms := fSavedSelection[fSavedSelection.Count - 1];
ms.Position := 0;
result := LoadSelectionFromStream(ms, Options);
if Remove then
begin
fSavedSelection.Delete(fSavedSelection.Count - 1);
FreeAndNil(ms);
end;
end;
end;
{!!
<FS>TImageEnView.DiscardSavedSelection
<FM>Declaration<FC>
function DiscardSavedSelection: boolean;
<FM>Description<FN>
Removes the last saved selection from the stack (saved using <A TImageEnView.SaveSelection>).
You can use this method if you don't want to restore the saved selection.
!!}
function TImageEnView.DiscardSavedSelection: boolean;
var
ms: TMemoryStream;
begin
result := false;
if fSavedSelection.Count > 0 then
begin
ms := fSavedSelection[fSavedSelection.Count - 1];
FreeAndNil(ms);
fSavedSelection.Delete(fSavedSelection.Count - 1);
result := true;
end;
end;
{!!
<FS>TImageEnView.RemoveAlphaChannel
<FM>Declaration<FC>
procedure RemoveAlphaChannel(Merge: Boolean=false);
<FM>Description<FN>
Removes the alpha channel and its allocated memory. <A TImageEnView.HasAlphaChannel> will be set to false.
If <FC>Merge<FN> is true the image will be merged with the background color using its alpha channel.
<FM>Example<FC>
// remove alpha channel from a gif
ImageEnView.io.LoadFromFile('C:\test.gif');
ImageEnView.RemoveAlphaChannel;
// this add a shadow (that uses the alpha channel) then save it to a jpeg (that cannot support alpha channel)
ImageEnView.Proc.AddSoftShadow(4, 3, 3);
ImageEnView.RemoveAlphaChannel( True ); // merge the alpha channel
ImageEnView.IO.SaveToFile('C:\output.jpg');
!!}
// if merge is true merges the alpha channel with the background color
procedure TImageEnView.RemoveAlphaChannel(Merge: boolean);
begin
if fIEBitmapValid = False then
exit;
fIEBitmap.RemoveAlphaChannel(Merge, fBackground);
Update();
end;
function TImageEnView.QuantizeViewX(vx: integer): integer;
begin
result := vx;
if result < 0 then
result := 0;
if (result < fZZWW) and (fGXBmp2Scr <> nil) and (fXScr2BmpSize > 0) and (result < fGXScr2BmpSize) then
result := fGXBmp2Scr[fGXScr2Bmp[result]];
end;
function TImageEnView.QuantizeViewY(vy: integer): integer;
begin
result := vy;
if result < 0 then
result := 0;
if (result < fZZHH) and (fGYBmp2Scr <> nil) and (fYScr2BmpSize > 0) and (result < fGYScr2BmpSize) then
result := fGYBmp2Scr[fGYScr2Bmp[result]];
end;
// recalculate coordinate conversion LUT
procedure TImageEnView.CreateCoordConvLUT;
var
i, v: integer;
scrx, scry, bmpx, bmpy: integer;
qviewx, qviewy: integer;
begin
if (fLutLastZoomX <> fZoomX) or (fLutLastZoomY<>fZoomY) or
(fLutLastFRX <> frx) or (fLutLastFRY <> fry) or
(fLutLastMaxLayerWidth <> fLayersRect.width) or (fLutLastMaxLayerHeight <> fLayersRect.height) then
begin
// free olds
if fGXScr2Bmp <> nil then
freemem(fGXScr2Bmp);
if fGYScr2Bmp <> nil then
freemem(fGYScr2Bmp);
if fGXBmp2Scr <> nil then
freemem(fGXBmp2Scr);
if fGYBmp2Scr <> nil then
freemem(fGYBmp2Scr);
fGXScr2Bmp := nil;
fGYScr2Bmp := nil;
fGXScr2BmpSize := 0;
fGYScr2BmpSize := 0;
fGXBmp2Scr := nil;
fGYBmp2Scr := nil;
fXScr2Bmp := nil;
fYScr2Bmp := nil;
fXBmp2Scr := nil;
fYBmp2Scr := nil;
fXScr2BmpSize := 0;
fYScr2BmpSize := 0;
fXBmp2ScrSize := 0;
fYBmp2ScrSize := 0;
// check validity
if (fry = 0) or (frx = 0) then
exit;
if (fIEBitmap_Width = 0) or (fIEBitmap_Height = 0) then
exit;
// sizes
fXScr2BmpSize := frx;
fYScr2BmpSize := fry;
fXBmp2ScrSize := trunc(frx * f100DZoomX);
fYBmp2ScrSize := trunc(fry * f100DZoomY);
scrx := fZZWW + fXScr2BmpSize + 1;
scry := fZZHH + fYScr2BmpSize + 1;
bmpx := trunc(scrx * f100DZoomX) + 1;
bmpy := trunc(scry * f100DZoomY) + 1;
fGXScr2BmpSize := (scrx + 1);
fGYScr2BmpSize := (scry + 1);
fGXScr2Bmp := allocmem(fGXScr2BmpSize * sizeof(integer));
fGYScr2Bmp := allocmem(fGYScr2BmpSize * sizeof(integer));
fGXBmp2Scr := allocmem((bmpx + 1) * sizeof(integer));
fGYBmp2Scr := allocmem((bmpy + 1) * sizeof(integer));
if fZoomX > 100 then
begin
for i := scrx - 1 downto 0 do
begin
v := trunc(i * f100DZoomX);
fGXBmp2Scr[v] := i;
fGXScr2Bmp[i] := imin(v, imax(fLayersRect.width - 1, 0));
end;
end
else
begin
for i := bmpx - 1 downto 0 do
begin
v := trunc(i * fZoomD100X);
fGXBmp2Scr[i] := v;
fGXScr2Bmp[v] := imin(i, imax(fLayersRect.width - 1, 0));
end;
end;
if fZoomY > 100 then
begin
for i := scry - 1 downto 0 do
begin
v := trunc(i * f100DZoomY);
fGYBmp2Scr[v] := i;
fGYScr2Bmp[i] := imin(v, imax(fLayersRect.height - 1, 0));
end;
end
else
begin
for i := bmpy - 1 downto 0 do
begin
v := trunc(i * fZoomD100Y);
fGYBmp2Scr[i] := v;
fGYScr2Bmp[v] := imin(i, imax(fLayersRect.height - 1, 0));
end;
end;
fLutLastZoomX := fZoomX;
fLutLastZoomY := fZoomY;
fLutLastFRX := frx;
fLutLastFRY := fry;
fLutLastMaxLayerWidth := fLayersRect.width;
fLutLastMaxLayerHeight := fLayersRect.height;
end;
if fXScr2BmpSize > 0 then
begin // check one for all (don't care YScr2BmpSize...)
qviewx := QuantizeViewX(fViewX);
qviewy := QuantizeViewY(fViewY);
fXBmp2Scr := @fGXBmp2Scr[imax(0, fGXScr2Bmp[qViewX] - fo1x)];
fYBmp2Scr := @fGYBmp2Scr[imax(0, fGYScr2Bmp[qViewY] - fo1y)];
fXScr2Bmp := @fGXScr2Bmp[qViewX];
fYScr2Bmp := @fGYScr2Bmp[qViewY];
end;
end;
{!!
<FS>TImageEnView.Cursor
<FM>Declaration<FC>
property Cursor: TCursor;
<FM>Description<FN>
Specifies the image used for the mouse pointer when it passes over the control.
Note: If you have enabled <A TImageEnView.AutoCursors> then ImageEn will automatically change the cursor when relevant.
<FM>ImageEn Cursors<FN>
<TABLE>
<R> <H>Constant</H> <H>Value</H> <H>Cursor</H> </R>
<R> <C><FC>crIEZoomOut<FN></C> <C>1778</C> <C><IMG help_images\2.bmp></C> </R>
<R> <C><FC>crIEZoomIn<FN></C> <C>1779</C> <C><IMG help_images\3.bmp></C> </R>
<R> <C><FC>crIEThickCross2<FN></C> <C>1780</C> <C><IMG help_images\4.bmp></C> </R>
<R> <C><FC>crIEEraser<FN></C> <C>1781</C> <C><IMG help_images\5.bmp></C> </R>
<R> <C><FC>crIEHandDrag<FN></C> <C>1782</C> <C><IMG help_images\6.bmp></C> </R>
<R> <C><FC>crIEPencil<FN></C> <C>1783</C> <C><IMG help_images\7.bmp></C> </R>
<R> <C><FC>crIECross<FN></C> <C>1784</C> <C><IMG help_images\8.bmp></C> </R>
<R> <C><FC>crIECrossSight<FN></C> <C>1785</C> <C><IMG help_images\9.bmp></C> </R>
<R> <C><FC>crIESizeNWSE<FN></C> <C>1786</C> <C><IMG help_images\10.bmp></C> </R>
<R> <C><FC>crIESizeNS<FN></C> <C>1787</C> <C><IMG help_images\11.bmp></C> </R>
<R> <C><FC>crIESizeNESW<FN></C> <C>1788</C> <C><IMG help_images\12.bmp></C> </R>
<R> <C><FC>crIESizeWE<FN></C> <C>1789</C> <C><IMG help_images\13.bmp></C> </R>
<R> <C><FC>crIESizeAll<FN></C> <C>1790</C> <C><IMG help_images\14.bmp></C> </R>
<R> <C><FC>crIECrossSightPlus<FN></C> <C>1791</C> <C><IMG help_images\15.bmp></C> </R>
<R> <C><FC>crIECrossSightMinus<FN></C> <C>1792</C> <C><IMG help_images\16.bmp></C> </R>
<R> <C><FC>crIEThickCross<FN></C> <C>1793</C> <C><IMG help_images\17.bmp></C> </R>
<R> <C><FC>crIEThickCrossPlus<FN></C> <C>1794</C> <C><IMG help_images\18.bmp></C> </R>
<R> <C><FC>crIECrossSightMinus2<FN></C> <C>1795</C> <C><IMG help_images\19.bmp></C> </R>
<R> <C><FC>crIECrossSightMinus3<FN></C> <C>1796</C> <C><IMG help_images\20.bmp></C> </R>
<R> <C><FC>crIEBrush<FN></C> <C>1797</C> <C><IMG help_images\21.bmp></C> </R>
<R> <C><FC>crIEEyeDropper3<FN></C> <C>1798</C> <C><IMG help_images\29.bmp></C> </R>
<R> <C><FC>crIEPaintFill<FN></C> <C>1799</C> <C><IMG help_images\23.bmp></C> </R>
<R> <C><FC>crIEStamp<FN></C> <C>1800</C> <C><IMG help_images\24.bmp></C> </R>
<R> <C><FC>crIECrop<FN></C> <C>1801</C> <C><IMG help_images\25.bmp></C> </R>
<R> <C><FC>crIECrossSmallPlus<FN></C> <C>1802</C> <C><IMG help_images\26.bmp></C> </R>
<R> <C><FC>crIESmallArrow<FN></C> <C>1803</C> <C><IMG help_images\27.bmp></C> </R>
<R> <C><FC>crIEMultipleArrow<FN></C> <C>1804</C> <C><IMG help_images\28.bmp></C> </R>
<R> <C><FC>crIEEyeDropper2<FN></C> <C>1805</C> <C><IMG help_images\29.bmp></C> </R>
<R> <C><FC>crIEEyeDropper<FN></C> <C>1806</C> <C><IMG help_images\30.bmp></C> </R>
<R> <C><FC>crIECut<FN></C> <C>1807</C> <C><IMG help_images\31.bmp></C> </R>
<R> <C><FC>crIESelectArrow<FN></C> <C>1808</C> <C><IMG help_images\32.bmp></C> </R>
<R> <C><FC>crIEPen<FN></C> <C>1809</C> <C><IMG help_images\33.bmp></C> </R>
<R> <C><FC>crIEPlusMinus<FN></C> <C>1810</C> <C><IMG help_images\34.bmp></C> </R>
<R> <C><FC>crIERotateNE<FN></C> <C>1811</C> <C><IMG help_images\CursorRotateNE.bmp></C> </R>
<R> <C><FC>crIERotateSW<FN></C> <C>1812</C> <C><IMG help_images\CursorRotateSW.bmp></C> </R>
<R> <C><FC>crIERotateNW<FN></C> <C>1813</C> <C><IMG help_images\CursorRotateNW.bmp></C> </R>
<R> <C><FC>crIERotateSE<FN></C> <C>1814</C> <C><IMG help_images\CursorRotateSE.bmp></C> </R>
</TABLE>
<FM>System Cursors<FN>
<TABLE>
<R> <H>Constant</H> <H>Value</H> <H>Cursor</H> </R>
<R> <C><FC>crNone<FN></C> <C>-1</C> <C><IMG help_images\35.bmp></C> </R>
<R> <C><FC>crArrow<FN></C> <C>-2</C> <C><IMG help_images\36.bmp></C> </R>
<R> <C><FC>crCross<FN></C> <C>-3</C> <C><IMG help_images\37.bmp></C> </R>
<R> <C><FC>crIBeam<FN></C> <C>-4</C> <C><IMG help_images\38.bmp></C> </R>
<R> <C><FC>crSizeNESW<FN></C> <C>-6</C> <C><IMG help_images\39.bmp></C> </R>
<R> <C><FC>crSizeNS<FN></C> <C>-7</C> <C><IMG help_images\40.bmp></C> </R>
<R> <C><FC>crSizeNWSE<FN></C> <C>-8</C> <C><IMG help_images\41.bmp></C> </R>
<R> <C><FC>crSizeWE<FN></C> <C>-9</C> <C><IMG help_images\42.bmp></C> </R>
<R> <C><FC>crUpArrow<FN></C> <C>-10</C> <C><IMG help_images\43.bmp></C> </R>
<R> <C><FC>crHourGlass<FN></C> <C>-11</C> <C><IMG help_images\44.bmp></C> </R>
<R> <C><FC>crDrag<FN></C> <C>-12</C> <C><IMG help_images\45.bmp></C> </R>
<R> <C><FC>crNoDrop<FN></C> <C>-13</C> <C><IMG help_images\46.bmp></C> </R>
<R> <C><FC>crHSplit<FN></C> <C>-14</C> <C><IMG help_images\47.bmp></C> </R>
<R> <C><FC>crVSplit<FN></C> <C>-15</C> <C><IMG help_images\48.bmp></C> </R>
<R> <C><FC>crMultiDrag<FN></C> <C>-16</C> <C><IMG help_images\49.bmp></C> </R>
<R> <C><FC>crSQLWait<FN></C> <C>-17</C> <C><IMG help_images\50.bmp></C> </R>
<R> <C><FC>crNo<R> <C><FC><FN></C> <C>-18</C> <C><IMG help_images\51.bmp></C> </R>
<R> <C><FC>crAppStart<FN></C> <C>-19</C> <C><IMG help_images\52.bmp></C> </R>
<R> <C><FC>crHelp<FN></C> <C>-20</C> <C><IMG help_images\53.bmp></C> </R>
<R> <C><FC>crHandPoint<FN></C> <C>-21</C> <C><IMG help_images\54.bmp></C> </R>
<R> <C><FC>crSizeAll<FN></C> <C>-22</C> <C><IMG help_images\55.bmp></C> </R>
</TABLE>
!!}
procedure TImageEnView.SetCursor(Value: TCursor);
begin
if assigned(fOnSetCursor) then
fOnSetCursor(self, Value);
fLCursor := Value;
inherited Cursor := Value;
fCursor := Value;
//Windows.SetCursor(Screen.Cursors[Value]);
end;
procedure TImageEnView.SetTempCursor(Value: TCursor; bOnMouseDown : Boolean = False);
begin
if assigned(fOnSetCursor) then
fOnSetCursor(self, Value);
if fAutoCursors then
begin
inherited Cursor := Value;
fCursor := Value;
if bOnMouseDown then
begin
// Note: Need special handling for OnMouseDown because a control's cursor cannot be changed on mouse down because it has been captured (until mouse up). So need to change screen's cursor
fWasScreenCursor := Screen.Cursor;
Screen.Cursor := Value;
end;
end;
end;
// restore the cursor for fLCursor (default cursor)
procedure TImageEnView.RestoreCursor;
begin
SetTempCursor(fLCursor);
if fWasScreenCursor <> crNone then
begin
Screen.Cursor := fWasScreenCursor;
fWasScreenCursor := crNone;
end;
end;
procedure TImageEnView.SetDelayTimer(Value: integer);
begin
fDelayTimer := Value;
if fDelayTimer > 0 then
begin
SetupAniPolyTimer;
fAnimPolyTimer.Interval := Value;
end;
end;
{!!
<FS>TImageEnView.DelayTimer
<FM>Declaration<FC>
property DelayTimer: Integer;
<FM>Description<FN>
ImageEn has a timer that decrements a counter at each tick (you can set the tick delay using DelayTimer property).
This timer controls the selection animation and the application of filters on scrolling (when <A TImageEnView.DelayZoomFilter> is True).
If you set negative values DelayTimer changes its behavior. Negative values represent the maximum CPU time that ImageEn can use to show selections. For example setting:
<FC>ImageEnView.DelayTimer := -10;<FN>
ImageEn will not use much more than 10% of the CPU time.
The default DelayTimer's value is -20 (maximum 20% of the CPU time).
!!}
function TImageEnView.GetDelayTimer: integer;
begin
result := fDelayTimer;
end;
{!!
<FS>TImageEnView.GradientEndColor
<FM>Declaration<FC>
property GradientEndColor: TColor
<FM>Description<FN>
Specifies the ending color of the gradient when <A TImageEnView.BackgroundStyle> is <FC>iebsGradient<FN>. The gradient start color is set using <A TImageEnView.Background>.
Note: This value may be overridden if <A TIEImageEnGlobalSettings.EnableTheming> is enabled.
Default: clBtnShadow
!!}
procedure TImageEnView.SetGradientEndColor(Value: TColor);
begin
fGradientEndColor := Value;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
{!!
<FS>TImageEnView.CenterImage
<FM>Declaration<FC>
procedure CenterImage;
<FM>Description<FN>
If the image is larger than the component client area, CenterImage will scroll the image so that it is centered.
!!}
procedure TImageEnView.CenterImage;
begin
SetViewXY(trunc((fIEBitmap_Width*fZoomD100X - GetClientWidthExRulers)/2), trunc((fIEBitmap_Height*fZoomD100Y - ClientHeight)/2));
end;
{!!
<FS>TImageEnView.LockUpdate
<FM>Declaration<FC>
procedure LockUpdate;
<FM>Description<FN>
Disables all calls to the <A TImageEnView.Update> method (increasing <A TImageEnView.LockUpdateCount>. LockUpdate and <A TImageEnView.UnlockUpdate> are useful, for example, when resampling multiple layers.
<FM>Example<FC>
ImageEnView1.LockUpdate;
for i := 0 to ImageEnView1.LayersCount-1 do
begin
ImageEnView1.LayersCurrent := i;
ImageEnView1.Proc.Resample(300, -1, rfNone); // withtout LockUpdate this call resizes all other layers
end;
ImageEnView1.UnlockUpdate;
!!}
procedure TImageEnView.LockUpdate;
begin
inc(fUpdateLocked);
end;
{!!
<FS>TImageEnView.UnlockUpdate
<FM>Declaration<FC>
procedure UnlockUpdate;
<FM>Description<FN>
Re-enables all calls to the <A TImageEnView.Update> method, after a call to <A TImageEnView.LockUpdate>.
LockUpdate and UnLockUpdate are useful, for example, when you need to resample multiple layers.
UnLockUpdate descreases <A TImageEnView.LockUpdateCount> and calls <A TImageEnView.Update> when the count reverts to zero.
<FM>Example<FC>
ImageEnView1.LockUpdate;
for i := 0 to ImageEnView1.LayersCount-1 do
begin
ImageEnView1.LayersCurrent := i;
ImageEnView1.Proc.Resample(300, -1, rfNone); // without LockUpdate this call resizes all other layers
end;
ImageEnView1.UnlockUpdate;
!!}
procedure TImageEnView.UnLockUpdate;
begin
UnLockUpdateEx;
if fUpdateLocked = 0 then
Update;
end;
{!!
<FS>TImageEnView.UnLockUpdateEx
<FM>Declaration<FC>
procedure UnLockUpdateEx;
<FM>Description<FN>
UnLockUpdateEx re-enables all calls to the <A TImageEnView.Update> method, after a call to <A TImageEnView.LockUpdate>.
LockUpdate and UnLockUpdateEx are useful, for example, when you need to resample multiple layers.
UnLockUpdateEx doesn't call <A TImageEnView.Update> when the update can be unlocked.
!!}
procedure TImageEnView.UnLockUpdateEx;
begin
if fUpdateLocked>0 then
dec(fUpdateLocked);
end;
{!!
<FS>TImageEnView.DrawVersion
<FM>Declaration<FC>
property DrawVersion: Boolean;
<FM>Description<FN>
When DrawVersion is True, the ImageEn version and release date are displayed on bottom-left side of the ImageEnView window.
!!}
procedure TImageEnView.SetDrawVersion(v: boolean);
begin
fDrawVersion := v;
Paint;
end;
{!!
<FS>TImageEnView.SelectionMaskDepth
<FM>Declaration<FC>
property SelectionMaskDepth: Integer;
<FM>Description<FN>
The SelectionMaskDepth property allows the specification of the selection depth in bits.
The default is 1 bit which means a pixel has two states "unselected" (0) or "selected" (1).
If the setting is 8 (8 bit), a pixel can be "unselected" (0), "semi-selected" (1 to 254), or "fully selected" (255). This allows creating soft or feathered selections.
Note: <A TImageEnView.SelectionOptions>=<FC>iesoFilled<FN> is not supported when the SelectionMaskDepth = 8
<FM>Example<FC>
ImageEnView.SelectionMaskDepth := 8;
ImageEnView.SelectionIntensity := 128;
ImageEnView.Select( 10, 10, 100, 100 );
ImageEnView.Proc.Negative; // the negative is applied at 50 % (128=semi selected)
<IMG help_images\MaskDepth.jpg>
!!}
procedure TImageEnView.SetSelectionMaskDepth(value: integer);
var
owidth, oheight: Integer;
begin
if (value <> fSelectionMaskDepth) and ((value = 1) or (value = 8)) then
begin
if fSel then
Deselect;
fSelectionMaskDepth := value;
owidth := fSelectionMask.Width;
oheight := fSelectionMask.Height;
fSelectionMask.AllocateBits(owidth, oheight, fSelectionMaskDepth);
case value of
1: fSelectionIntensity := 1;
8: fSelectionIntensity := 255;
end;
end;
end;
{!!
<FS>TImageEnView.SelectionIntensity
<FM>Declaration<FC>
property SelectionIntensity: Integer;
<FM>Description<FN>
Specifies the selection intensity and is valid only when the <A TImageEnView.SelectionMaskDepth> is 8 (8 bit), otherwise it must be 1.
You can assign a value from 0 to 255 which will be applied to the next or current selection.
See also: <A TImageEnView.SelectionMaskDepth>.
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\ImageEditing\SoftSelections\SoftSel.dpr </C> </R>
</TABLE>
<FM>Example<FC>
ImageEnView.SelectionMaskDepth := 8;
ImageEnView.SelectionIntensity := 128;
ImageEnView.Select( 10, 10, 100, 100 );
ImageEnView.Proc.Negative; // the negative is applied at 50 % (128=semi selected)
!!}
procedure TImageEnView.SetSelectionIntensity(value: integer);
var
px: pbyte;
i, j: integer;
begin
fSelectionIntensity := value;
if fSel and (fSelectionMask.BitsPerPixel = 8) then
begin
// changes current selection
with fSelectionMask do
begin
for i := y1 to y2 do
begin
px := Scanline[i];
inc(px, x1);
for j := x1 to x2 do
begin
if px^ > 0 then
px^ := value;
inc(px);
end;
end;
end;
end;
end;
{!!
<FS>TImageEnView.MakeSelectionFeather
<FM>Declaration<FC>
procedure MakeSelectionFeather(radius: Double);
<FM>Description<FN>
Modifies the current selection by applying a blur effect to make soften the borders.
This property works only if <A TImageEnView.SelectionMaskDepth> is 8.
Radius specifies the feathering intensity (amount of blur).
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\ImageEditing\SoftSelections\SoftSel.dpr </C> </R>
</TABLE>
<FM>Example<FC>
ImageEnView.SelectionMaskDepth := 8;
ImageEnView.Select( 10, 10, 100, 100 );
ImageEnView.MakeSelectionFeather( 4 );
<IMG help_images\SelFeather.jpg>
!!}
procedure TImageEnView.MakeSelectionFeather(radius: double);
var
bmp: TIEBitmap;
ww, x1, y1, x2, y2: integer;
begin
if fSel and (fSelectionMask.BitsPerPixel = 8) then
begin
AnimPolygonClear(fHPolySel);
bmp := TIEBitmap.Create;
bmp.EncapsulateMemory(fSelectionMask.Bits, fSelectionMask.Width, fSelectionmask.Height, ie8g, false);
ww := imax(trunc(radius * 5), 1);
x1 := imax(fSelectionMask.X1 - ww, 0);
y1 := imax(fSelectionMask.Y1 - ww, 0);
x2 := imin(fSelectionMask.X2 + ww, fSelectionMask.Width - 1);
y2 := imin(fSelectionMask.Y2 + ww, fSelectionMask.Height - 1);
_IEGBlurRect8(bmp, x1, y1, x2, y2, radius);
FreeAndNil(bmp);
fSelectionMask.SyncRect;
ShowSelectionEx(true);
fUpdateBackBuffer := true;
Paint;
end;
end;
{!!
<FS>TImageEnView.LayersRect
<FM>Declaration<FC>
function LayersRect(SelOnly: Boolean = False; ExcludeLayer0: Boolean = False): <A TIERectangle>;
<FM>Description<FN>
Returns the coverage area of all or selected layers.
The result will include any layers outside the background layer (if <A TImageEnView.LayersCropped> and <A TIELayer.Cropped> are not set).
If <FC>ExcludeLayer0<FN> is False, then the background layer is not included in the result.
Notes:
- If layers extend to the left or above the background layer, then Result.X/Y will be less than PosX/Y of Layer 0
- If layers extend to the right or below the background layer, then Result.Width/Height may be greater than the dimensions of <A TImageEnView.IEBitmap>
<FM>See Also<FN>
- <A TImageEnView.MaxLayerWidth>
- <A TImageEnView.MaxLayerHeight>
!!}
// Returns the coverage area of all layers
// x,y will be less than zero if layers are left of layer 0 (and layer is not cropped)
// width, height will be greater than layer 0 size if they extend outside it (and layer is not cropped)
function TImageEnView.LayersRect(SelOnly: Boolean = False; ExcludeLayer0: Boolean = False): TIERectangle;
var
i: Integer;
minX, minY, maxX, maxY: Integer;
currX, currY, currX2, currY2: Integer;
begin
// calculates maximum extension and minimum left/top coordinates
Result.x := 0;
Result.y := 0;
Result.Width := 0;
Result.Height := 0;
maxX := 0;
maxY := 0;
minX := MAXINT;
minY := MAXINT;
if ( ExcludeLayer0 = False ) and (( SelOnly = False ) or Layers[0].Selected ) then
begin
minX := Layers[0].PosX;
minY := Layers[0].PosY;
maxX := Layers[0].PosX + Layers[0].Width;
maxY := Layers[0].PosY + Layers[0].Height;
end;
for i := 1 to LayersCount - 1 do
if ( SelOnly = False ) or Layers[i].Selected then
begin
currX := Layers[i].PosX;
currY := Layers[i].PosY;
currX2 := Layers[i].PosX + Layers[i].Width;
currY2 := Layers[i].PosY + Layers[i].Height;
if fLayersCropped or Layers[i].Cropped then
begin
currX := imax( currX, Layers[0].PosX );
currY := imax( currY, Layers[0].PosY );
currX2 := imin( currX2, Layers[0].PosX + Layers[0].Width );
currY2 := imin( currY2, Layers[0].PosY + Layers[0].Height );
end;
minX := imin( minX, currX );
minY := imin( minY, currY );
maxX := imax( maxX, currX2 );
maxY := imax( maxY, currY2 );
end;
if minX < MAXINT then
begin
Result.x := minX;
if maxX > 0 then
Result.Width := maxX - minX;
end;
if minY < MAXINT then
begin
Result.y := minY;
if maxY > 0 then
Result.Height := maxY - minY;
end;
end;
{!!
<FS>TImageEnView.MaxLayerWidth
<FM>Declaration<FC>
function MaxLayerWidth() : integer;
<FM>Description<FN>
Returns the maximum width of all layers.
The result will include any layers outside the background layer (if <A TImageEnView.LayersCropped> and <A TIELayer.Cropped> are not set).
<FM>See Also<FN>
- <A TImageEnView.MaxLayerHeight>
- <A TImageEnView.LayersRect>
!!}
function TImageEnView.MaxLayerWidth() : integer;
begin
result := LayersRect().width;
end;
{!!
<FS>TImageEnView.MaxLayerHeight
<FM>Declaration<FC>
function MaxLayerHeight() : integer;
<FM>Description<FN>
Returns the maximum height of all layers.
The result will include any layers outside the background layer (if <A TImageEnView.LayersCropped> and <A TIELayer.Cropped> are not set).
<FM>See Also<FN>
- <A TImageEnView.MaxLayerWidth>
- <A TImageEnView.LayersRect>
!!}
function TImageEnView.MaxLayerHeight() : integer;
begin
result := LayersRect().height
end;
{!!
<FS>TImageEnView.LayersDrawBox
<FM>Declaration<FC>
property LayersDrawBox: Boolean;
<FM>Description<FN>
Specify whether a box is displayed around layers.
This option has no effect if <A TImageEnView.Layers>[].<A TIELayer.VisibleBox> is set to false.
If Layers[].VisibleBox is true and LayersDrawBox is false, a box is drawn only on the selected layer.
If Layers[].VisibleBox is true and LayersDrawBox is true, a box is drawn on all layers.
Default: False
Note: You can customize the style of layer box using <A TImageEnView.SetLayersBoxStyle>, or for more control, <A TImageEnView.OnDrawLayerBox>
!!}
procedure TImageEnView.SetLayersDrawBox(value: boolean);
begin
if value <> fLayersDrawBox then
begin
fLayersDrawBox := value;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
end;
{!!
<FS>TImageEnView.LayersCaching
<FM>Declaration<FC>
property LayersCaching: Integer;
<FM>Description<FN>
Whether a cached view of every layer is stored in memory.
Supported values:
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>-1</C> <C>A cached view is stored for each layers. This provides the best performance, but can use a lot of memory if there are many layers</C> </R>
<R> <C>0</C> <C>No cached views of layers are stored. This uses the least memory </C> </R>
<R> <C>>0</C> <C>Specifies a maximum number of cached views to store </C> </R>
</TABLE>
Default: 0
<FM>Example<FC>
// Cache all layers
ImageEnView1.LayersCaching := -1;
// Cache a maximum of 30 layers
ImageEnView1.LayersCaching := 30;
// Disable all layer caching
ImageEnView1.LayersCaching := 0;
<FM>See Also<FN>
- <A TImageEnView.LayersFastDrawing>
!!}
procedure TImageEnView.SetLayersCaching(v: Integer);
begin
if v <> fLayersCaching then
begin
fLayersCaching := v;
if fLayersCaching <> -1 then
LayersSetProperties( LYR_ALL_LAYERS, IELP_LAYER_CACHE_CLEAR, True );
end;
end;
procedure TImageEnView.DoLayerNotify(layer: integer; event: TIELayerEvent);
begin
if assigned(fOnLayerNotify) then
begin
SwapSelectionBase;
fOnLayerNotify(self, layer, event);
SwapSelectionBase;
end;
if ( event in [ ielSelected, ielDeselected ]) and assigned( OnLayerSelectionChange ) then
OnLayerSelectionChange( Self );
if event in [ ielMoved, ielResized, ielRotated, ielEdited, ielCreated ] then
ImageChange();
end;
{$IFDEF IEINCLUDEDIRECTSHOW}
procedure TImageEnView.DShowNewFrame(var Message: TMessage);
var
dshow: TIEDirectShow;
begin
dshow := TIEDirectShow(pointer(Message.LParam));
dshow.AcceptNextFrame := false;
if assigned(dshow) and dshow.Connected and assigned(fOnDShowNewFrame) then
fOnDShowNewFrame(self);
dshow.AcceptNextFrame := true;
end;
procedure TImageEnView.DShowEvent(var Message: TMessage);
var
dshow: TIEDirectShow;
event: integer;
begin
dshow := TIEDirectShow(pointer(Message.LParam));
if assigned(dshow) and dshow.Connected then
begin
if assigned(fOnDShowEvent) then
fOnDShowEvent(self)
else
begin
dshow.GetEventCode(event);
case event of
IEEC_COMPLETE:
dshow.EndOfStream := true;
end;
end;
end;
end;
{$ENDIF}
{$IFDEF IEINCLUDEMEDIAFOUNDATION}
procedure TImageEnView.MediaFoundationNotify(var Message: TMessage);
var
notify: TIEMediaFountationNotifyType;
mediaFoundation: TObject;
begin
notify := TIEMediaFountationNotifyType(Message.WParam);
mediaFoundation := TObject(pointer(Message.LParam));
if assigned(mediaFoundation) then
begin
if assigned(fOnMediaFoundationNotify) then
fOnMediaFoundationNotify(self, mediaFoundation, notify);
end;
end;
{$ENDIF}
{!!
<FS>TImageEnView.SelectionAspectRatio
<FM>Declaration<FC>
property SelectionAspectRatio: Double;
<FM>Description<FN>
Specifies whether the selection is locked to a specific size or aspect ratio.
If SelectionAspectRatio is -1, the aspect ratio is locked only when user press the ALT key, and is automatically calculated.
If SelectionAspectRatio is 0, the size of the selection is fixed by the <A TImageEnView.SelectionAbsWidth> and <A TImageEnView.SelectionAbsHeight> properties.
If SelectionAspectRatio is >0, ImageEn locks the selection to the specified aspect ratio.
<FM>Example<FC>
// we want standard behavior
ImageEnView1.SelectionAspectRatio := -1;
// we want a fixed selection of 100 x 100 pixels
ImageEnView1.SelectionAbsWidth := 100;
ImageEnView1.SelectionAbsHeight := 100;
ImageEnView1.SelectionAspectRatio := 0;
// we want a fixed aspect ratio of 4:3 (standard landscape, i.e. height is 75% of height)
ImageEnView1.SelectionAspectRatio := 4 / 3;
<FM>See Also<FN>
- <A TIECropToolInteraction.LockAspectRatio>
!!}
procedure TImageEnView.SetSelectionAspectRatio(value: Double);
begin
if value<>fSelectionAspectRatio then
begin
fSelectionAspectRatio := value;
if fSelectionAspectRatio = 0 then
SelectionOptions := SelectionOptions - [iesoSizeable]
else
SelectionOptions := SelectionOptions + [iesoSizeable];
end;
end;
{$ifdef IEINCLUDEFLATSB}
{!!
<FS>TImageEnView.FlatScrollBars
<FM>Declaration<FC>
property FlatScrollBars: Boolean;
<FM>Description<FN>
Specifies whether the component's scroll bars are flat.
ImageEn only supports flat scroll bars if the system comctl32.dll is version 472 or later.
This property must be set at Design Time (has not effect at run-time)
!!}
procedure TImageEnView.SetFlatScrollBars(Value: Boolean);
begin
if Value<>fFlatScrollBars then
begin
fFlatScrollBars := Value;
if fFlatScrollBars then
begin
IESetScrollRange(Handle, SB_HORZ, 0, 65535, false, false); // Why this? Please ask to Microsoft programmers!
IESetScrollRange(Handle, SB_VERT, 0, 65535, false, false); // Why this? Please ask to Microsoft programmers!
InitializeFlatSB(Handle);
IEShowScrollBar(handle, SB_HORZ, false, true); // Why this? Please ask to Microsoft programmers!
IEShowScrollBar(handle, SB_VERT, false, true); // Why this? Please ask to Microsoft programmers!
end
else
UninitializeFlatSB(Handle);
end;
end;
{$endif}
procedure TImageEnView.SwapSelectionBase;
var
x: TIESelectionBase;
begin
x := fSavedSelectionBase;
fSavedSelectionBase := fSelectionBase;
fSelectionBase := x;
end;
procedure TImageEnView.DoZoomIn(var NewZoom: double);
begin
SwapSelectionBase;
if assigned(fOnZoomIn) then
fOnZoomIn(self, NewZoom);
SwapSelectionBase;
end;
procedure TImageEnView.DoZoomOut(var NewZoom: double);
begin
SwapSelectionBase;
if assigned(fOnZoomOut) then
fOnZoomOut(self, NewZoom);
SwapSelectionBase;
end;
procedure TImageEnView.DoMouseInResizingGrip(Grip: TIEGrip);
begin
SwapSelectionBase;
if assigned(fOnMouseInResizingGrip) then
fOnMouseInResizingGrip(self, Grip);
SwapSelectionBase;
end;
procedure TImageEnView.DoMouseInSel;
begin
SwapSelectionBase;
if assigned(fOnMouseInSel) then
fOnMouseInSel(self);
SwapSelectionBase;
end;
procedure TImageEnView.NavigatorSelectionChanging(Sender: TObject);
var
x, y: Integer;
z: double;
begin
if (ienoDontRefreshSrcIfNavNotFocused in fNavigator.fNavigatorOptions) and not fNavigator.Focused then
exit;
if fNavigatorInside then
exit;
fNavigatorInside := true;
z := fIEBitmap_Width / fNavigator.IEBitmap.Width;
x := round( z * Zoom / 100 * fNavigator.SelX1 );
y := round( z * Zoom / 100 * fNavigator.SelY1 );
SetViewXY(x, y);
fNavigatorInside := false;
end;
procedure TImageEnView.NavigatorMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var
dPos: double;
dir: integer;
begin
if WheelDelta > 0 then
dir := -1
else
dir := 1;
if fNavigator.fMouseWheelParams.InvertDirection then
dir := -1 * dir;
dPos := fZoomX;
case fNavigator.fMouseWheelParams.Variation of
iemwAbsolute:
dPos := fZoomX + dir * fNavigator.fMouseWheelParams.Value;
iemwPercentage:
dPos := fZoomX + imax(round(fZoomX * fNavigator.fMouseWheelParams.Value / 100), 1) * dir;
end;
if (dPos > fZoomX) then
DoZoomIn(dPos);
if (dPos < fZoomX) then
DoZoomOut(dPos);
if fNavigator.fMouseWheelParams.ZoomPosition = iemwCenter then
SetZoom(dPos)
else
ZoomAt(MousePos.X, MousePos.Y, dPos, false);
Handled := true;
end;
procedure TImageEnView.SetNavigatorRect;
var
x1, y1, x2, y2: Integer;
z: Double;
begin
if fNavigatorInside or (fLockPaint > 0) then
exit;
fNavigatorInside := true;
if not (ienoDontPaintSrcBitmap in fNavigator.fNavigatorOptions) then
Paint(); // this updates offsets
z := fNavigator.IEBitmap.Width / fIEBitmap_Width;
x1 := round(z * XScr2Bmp( OffsetX, False ));
y1 := round(z * YScr2Bmp( OffsetY, False ));
x2 := round(z * XScr2Bmp( OffsetX + ExtentX, False ));
y2 := round(z * YScr2Bmp( OffsetY + ExtentY, False ));
fNavigator.SelectionAbsWidth := x2-x1;
fNavigator.SelectionAbsHeight := y2-y1;
fNavigator.SelectionAspectRatio := 0;
fNavigator.Select(x1, y1, x2, y2, iespReplace);
fNavigatorInside := false;
end;
{!!
<FS>TImageEnView.SetNavigator
<FM>Declaration<FC>
procedure SetNavigator(nav: <A TImageEnView>; options: <A TIENavigatorOptions>);
<FM>Description<FN>
SetNavigator specifies a TImageEnView component which works as navigator of current image. A navigator shows a selection that controls the zoom and scroll of the main control.
Use <FC>options<FN> to fine-tune navigator behavior.
<FM>Demos<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Display\Navigator\Navigator.dpr </C> </R>
<R> <C_IMG_DEMO> <C>Demos\Display\Navigator2\Navigator.dpr </C> </R>
</TABLE>
<FM>Example<FC>
ImageEnView1.SetNavigator(ImageEnView2, [ienoMouseWheelZoom, ienoMarkOuter]);
ImageEnView1.IO.LoadFromFile('input.jpg');
!!}
procedure TImageEnView.SetNavigator(nav: TImageEnView; options: TIENavigatorOptions);
begin
if ( nav <> nil ) and ( fIEBitmapValid = False ) then
raise EIEException.create( 'Method only supported for image layers' );
if fNavigator <> nil then
begin
fNavigator.OnSelectionChanging := nil;
if not IEBitmap.IsVirtual then
fNavigator.SetExternalBitmap(nil);
fNavigator.fIsNavigator := false;
end;
fNavigator := nav;
if fNavigator<>nil then
begin
fNavigator.fIsNavigator := true;
fNavigator.EnableAlphaChannel := EnableAlphaChannel;
fNavigator.AutoFit := True;
fNavigator.MouseInteract := [miSelect];
fNavigator.SelectionBase := iesbBitmap;
fNavigator.SelectionOptions := [iesoMoveable, iesoCanScroll];
if ( not ( ienoDontAssignNavBitmap in Options )) and
( IEBitmap.IsVirtual = False ) then
fNavigator.SetExternalBitmap( IEBitmap );
fNavigator.fOnSelectionChanging := NavigatorSelectionChanging;
fNavigator.fNavigatorOptions := options;
if ienoMOUSEWHEELZOOM in options then
fNavigator.OnMouseWheel := NavigatorMouseWheel;
fNavigator.FreeNotification(self);
SetNavigatorRect;
end;
end;
procedure TImageEnView.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (AComponent = fNavigator) and (Operation = opRemove) then
begin
fNavigator := nil;
end;
end;
{!!
<FS>TImageEnView.SetExternalBitmap
<FM>Declaration<FC>
procedure SetExternalBitmap(bmp: <A TIEBitmap>);
<FM>Description<FN>
Allows you to connect the TImageEnView component to another <A TIEBitmap>, including sharing the bitmap of another TImageEnView.
This is useful to view the same image with multiple TImageEnView components, but loading the image only once.
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Display\ExternalBitmap\ExternalBMP.dpr </C> </R>
</TABLE>
<FM>Example<FC>
ImageEnView1.IO.LoadFromFile('C:\input.jpg');
ImageEnView2.SetExternalBitmap( ImageEnView1.IEBitmap );
..Both ImageEnView1 and ImageEnView2 now show input.jpg.
!!}
procedure TImageEnView.SetExternalBitmap(bmp: TIEBitmap);
begin
if fLayersCurrent > -1 then
begin
// Ensure an image layer is active
if Layers[ fLayersCurrent ].Kind <> ielkImage then
SetLayersCurrent( 0 ); // Base layer always image
fIEBitmap.fOwner := nil;
if bmp <> nil then
begin
if TIEImageLayer( CurrentLayer ).fFreeBitmapOnDestroy then
begin
if fIEBitmap.EncapsulatedFromTBitmap then
FreeAndNil(fBitmap);
FreeAndNil(fIEBitmap);
end;
bmp.fOwner := Self;
TIEImageLayer( CurrentLayer ).fBitmap := bmp;
TIEImageLayer( CurrentLayer ).fFreeBitmapOnDestroy := false;
fIEBitmap := TIEImageLayer( CurrentLayer ).Bitmap;
if fIEBitmap.EncapsulatedFromTBitmap then
fBitmap := fIEBitmap.VclBitmap
else
fBitmap := nil;
CallBitmapChangeEvents;
Update;
end
else
begin
TIEImageLayer( CurrentLayer ).fFreeBitmapOnDestroy := true;
fIEBitmap := TIEBitmap.Create;
fIEBitmap.fOwner := Self;
fBitmap := nil;
// Assign new fIEBitmap to current layer
SyncBitmapToCurrentLayer();
end;
end;
end;
{!!
<FS>TImageEnView.ImageEnVersion
<FM>Declaration<FC>
property ImageEnVersion: String;
<FM>Description<FN>
Returns the ImageEn version as a string.
!!}
function TImageEnView.GetImageEnVersion: String;
begin
result := IEMAINVERSION;
end;
procedure TImageEnView.SetImageEnVersion(Value: String);
begin
// this is a read-only property, but it must be displayed in object inspector
end;
{$ifdef IEDOTNETVERSION}
procedure TImageEnView.WMContextMenu(var Message: TWMContextMenu);
begin
// just to remove Delphi default behavior
end;
{$endif}
{!!
<FS>TImageEnView.ChangeResolution
<FM>Declaration<FC>
procedure ChangeResolution(NewDPI: Integer; ResampleFilter: <A TResampleFilter>);
<FM>Description<FN>
Modifies the DPI of the image (<L TIOParams.DpiX>IO.Params.DpiX</L> and <L TIOParams.DpiY>IO.Params.DpiY</L>), resampling it and setting the new DPI values.
Note: You should ensure the source DPI contains valid values before calling ChangeResolution. This method has no effect if DPI is 0.
<FM>Example<FC>
// load input.jpg which has 100 dpi
ImageEnView1.IO.LoadFromFile('C:\input.jpg');
// we want 75 dpi
ImageEnView1.ChangeResolution(75, rfTriangle);
// save back as 75 dpi
ImageEnView1.IO.SaveToFile('C:\output.jpg');
!!}
procedure TImageEnView.ChangeResolution(NewDPI: Integer; ResampleFilter: TResampleFilter);
var
iOldDpi: Integer;
iLayersCurrent: Integer;
I: Integer;
procedure _UpdateCurrentLayer(bSetDPI: Boolean);
var
iNewHeight: Integer;
Proc: TImageEnProc;
begin
iNewHeight := round( fIEBitmap.Height / iOldDpi * NewDPI );
Proc := TImageEnProc.CreateFromBitmap( fIEBitmap );
try
Proc.OnProgress := fOnProgress;
Proc.OnFinishWork := fOnFinishWork;
Proc.Resample( -1, iNewHeight, ResampleFilter );
finally
FreeAndNil( Proc );
end;
if bSetDPI then
begin
GetImageEnIO.Params.DpiY := NewDPI;
GetImageEnIO.Params.DpiX := round( GetImageEnIO.Params.DpiX / iOldDpi * NewDPI );
end;
end;
begin
if ( GetImageEnIO.Params.DpiX = 0 ) or ( GetImageEnIO.Params.DpiY = 0 ) then
exit;
iOldDpi := GetImageEnIO.Params.DpiY;
if LayersCount = 0 then
_UpdateCurrentLayer( True )
else
begin
// Iterate through all layers
LockUpdate();
iLayersCurrent := LayersCurrent;
try
for I := 0 to LayersCount - 1 do
begin
LayersCurrent := I;
if Layers[ fLayersCurrent ] is TIEImageLayer then
_UpdateCurrentLayer( I = 0 )
else
begin
CurrentLayer.Width := round( CurrentLayer.Width / iOldDpi * NewDPI );
CurrentLayer.Height := round( CurrentLayer.Height / iOldDpi * NewDPI );
end;
if I > 0 then
begin
CurrentLayer.PosX := round( CurrentLayer.PosX / iOldDpi * NewDPI );
CurrentLayer.PosY := round( CurrentLayer.PosY / iOldDpi * NewDPI );
end;
end;
finally
LayersCurrent := iLayersCurrent;
UnlockUpdate();
end;
end;
CallBitmapChangeEvents;
Update;
end;
// warning: ioTIFF not supported
// if compressionFormat = -1 then use internal compressed format (preserves pixelformat and transparency)
// if compressionFormat = -2 then use internal noncompressed format (preserves pixelformat and transparency)
procedure TImageEnView.LayersSaveToFile(const FileName: String; CompressionFormat: TIOFileType = -1);
var
fs: TFileStream;
begin
fs := TFileStream.Create(FileName, fmCreate);
try
LayersSaveToStream(fs, CompressionFormat);
finally
FreeAndNil(fs);
end;
end;
function TImageEnView.LayersLoadFromFile(const FileName: String; Append: Boolean = False): Boolean;
var
fs: TFileStream;
begin
fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
result := LayersLoadFromStream( fs, Append ); // this calls Update
finally
FreeAndNil(fs);
end;
end;
{!!
<FS>TImageEnView.LayersImport
<FM>Declaration<FC>
function LayersImport(const FileName: String; Stream: TStream = nil; FileFormat: <A TIOFileType> = ioUnknown; Append: Boolean = False): Integer;
<FM>Description<FN>
Imports layers from a file or stream in IEV, ALL or DXF format.
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>FileName<FN></C> <C>The full path to a file to import (or '' if importing from a stream)</C> </R>
<R> <C><FC>Stream<FN></C> <C>A stream to import from (or nil if importing from file)</C> </R>
<R> <C><FC>FileFormat<FN></C> <C>The type of file, if known. It should be one ioIEV, ioALL or ioDXF. You can also specify ioUnknown, whereby ImageEn will infer the format from the content (with DXF only the file extension is considered)</C> </R>
<R> <C><FC>Append<FN></C> <C>When false the existing content is cleared before importing</C> </R>
</TABLE>
Result is -1 in the case of an error, otherwise returns the number of layers added.
IEV and ALL are object formats of <A TImageEnVect>. Conversion is as follows:
<TABLE>
<R> <H>Object Kind</H> <H>Converted To</H> <H>Notes</H> </R>
<R> <C><FC>iekLINE<FN></C> <C><A TIELineLayer></C> <C> - </C> </R>
<R> <C><FC>iekBOX<FN></C> <C><A TIEShapeLayer></C> <C> - </C> </R>
<R> <C><FC>iekELLIPSE<FN></C> <C><A TIEShapeLayer></C> <C> - </C> </R>
<R> <C><FC>iekARC<FN></C> <C>Skipped</C> <C> - </C> </R>
<R> <C><FC>iekBITMAP<FN></C> <C><A TIEImageLayer></C> <C> - </C> </R>
<R> <C><FC>iekTEXT<FN></C> <C><A TIETextLayer></C> <C> - </C> </R>
<R> <C><FC>iekRULER<FN></C> <C>Skipped</C> <C> - </C> </R>
<R> <C><FC>iekPOLYLINE<FN></C> <C><A TIEPolylineLayer></C> <C> - </C> </R>
<R> <C><FC>iekANGLE<FN></C> <C>Skipped</C> <C> - </C> </R>
<R> <C><FC>iekMEMO<FN></C> <C><A TIETextLayer></C> <C>Text formatting is lost </C> </R>
<R> <C><FC>iekLINELABEL<FN></C> <C><A TIELineLayer></C> <C>Inward arrows are converted to outward arrows </C> </R>
</TABLE>
DXF is an Autocad vector format. DXF file can only be imported from file and only lines and ellipses objects are supported.
<FM>Example<FC>
// Import from an IEV file (replacing the existing layers)
ImageEnView1.LayersImport( 'C:\Vect.iev' );
// Import from an IEV file (adding to the existing layers)
ImageEnView1.LayersImport( 'C:\Vect.iev', nil, ioIEV, True );
// Import from an IEV stream (replacing the existing layers)
ImageEnView1.LayersImport( '', myStream );
// Import from an IEV stream (adding to the existing layers)
ImageEnView1.LayersImport( '', myStream, ioIEV, True );
// Import from an Autocad file
ImageEnView1.LayersImport( 'C:\cad.dxf', nil, ioDXF );
<FM>See Also<FN>
- <A TImageEnVect.CopyAllObjectsTo>
- <A TImageEnIO.LoadFromFileIEN>
- <A TImageEnIO.LoadFromStreamIEN>
- <A TImageEnVect.LoadFromFileIEV>
- <A TImageEnVect.LoadFromStreamIEV>
- <A TImageEnVect.LoadFromFileAll>
- <A TImageEnVect.LoadFromStreamAll>
- <A TImageEnVect.ImportDXF>
!!}
function TImageEnView.LayersImport(const FileName: String; Stream: TStream = nil; FileFormat: TIOFileType = ioUnknown; Append: Boolean = False): Integer;
var
IEVect : TImageEnVect;
loadOK: Boolean;
oldLayersCount: Integer;
lp: int64;
begin
Result := -1;
IEVect := TImageEnVect.create( nil );
try
if FileFormat = ioUnknown then
begin
if ( FileName <> '' ) and ( string(IEExtractFileExtW(FileName)) = '.dxf' ) then
FileFormat := ioDXF
else
if Stream <> nil then
begin
lp := Stream.Position;
FileFormat := FindStreamFormat( Stream );
Stream.Position := lp;
end
else
FileFormat := FindFileFormat( FileName );
if FileFormat = ioUnknown then
exit;
end;
loadOK := False;
if Stream <> nil then
case FileFormat of
ioIEV : loadOK := IEVect.LoadFromStreamIEV( Stream, False );
ioALL : loadOK := IEVect.LoadFromStreamALL( Stream );
else loadOK := IEVect.IO.LoadFromStream( Stream, FileFormat );
end
else
if FileName <> '' then
case FileFormat of
ioIEV : loadOK := IEVect.LoadFromFileIEV( FileName, False );
ioALL : loadOK := IEVect.LoadFromFileALL( FileName );
ioDXF : loadOK := IEVect.ImportDXF( FileName );
else loadOK := IEVect.IO.LoadFromFile( FileName, FileFormat );
end;
if not loadOK then
exit;
LockUpdate();
if not Append then
begin
ClearAll();
Assign( IEVect ); // get background image
end;
oldLayersCount := fLayers.Count;
IEVect.CopyAllObjectsTo( Self );
UnlockUpdate();
Result := fLayers.Count - oldLayersCount;
finally
IEVect.free;
end;
end;
{!!
<FS>TImageEnView.LayersAlign
<FM>Declaration<FC>
procedure LayersAlign(Alignment: <A TIEAlignLayers>; Index: Integer = LYR_SELECTED_LAYERS);
<FM>Description<FN>
Aligns all selected layers relative to the image or other layers.
The index of a specific layer can be specified, or <FC>LYR_ALL_LAYERS<FN> (-1) to process all layers, or <FC>LYR_SELECTED_LAYERS<FN> (-2)
<FM>Example<FC>
// Align selected layers on the left edge
ImageEnView1.LayersAlign( ilaAlignLeftEdges );
// Position all layers in the center of the document
ImageEnView1.LayersAlign( ilaAlignHorizontalCenters );
// Move layer #3 to the center of the image
ImageEnView1.LayersAlign( ilaAlignToVerticalCenter, 2 );
<FM>See Also<FN>
- <A TImageEnView.LayersRepositionAll>
!!}
procedure TImageEnView.LayersAlign(Alignment: TIEAlignLayers; Index: Integer = LYR_SELECTED_LAYERS);
var
bSet: Boolean;
I: Integer;
iCurr: integer;
iPos: integer;
lyrCount: integer;
{}
function _DoProcessLayer(LayerId: Integer) : boolean;
begin
case Index of
LYR_ALL_LAYERS : Result := True;
LYR_SELECTED_LAYERS : Result := TIELayer( fLayers[ LayerId ]).Selected;
else Result := LayerId = Index;
end;
end;
{}
begin
if Proc.AutoUndo and ( loAutoUndoChangesbyCode in fLayerOptions ) then
begin
if Alignment in [ ilaMatchWidth, ilaMatchHeight ] then
Proc.SaveUndo( IEMsg( IEMsg_ResizeLayers ), ieuLayer, True, IEOP_RESIZELAYER )
else
Proc.SaveUndo( IEMsg( IEMsg_MoveLayers ), ieuLayer, True, IEOP_MOVELAYER );
end;
// If there is no background layer then can't align relative to "image"
if ( Layers[ 0 ].Width < 2 ) and
( Layers[ 0 ].Height < 2 ) then
case Alignment of
ilaAlignToLeft : Alignment := ilaAlignLeftEdges ;
ilaAlignToRight : Alignment := ilaAlignRightEdges ;
ilaAlignToTop : Alignment := ilaAlignTopEdges ;
ilaAlignToBottom : Alignment := ilaAlignBottomEdges ;
ilaAlignToHorizontalCenter : Alignment := ilaAlignHorizontalCenters;
ilaAlignToVerticalCenter : Alignment := ilaAlignVerticalCenters ;
end;
// Check selection
case Index of
LYR_ALL_LAYERS : lyrCount := LayersCount - 1;
LYR_SELECTED_LAYERS : lyrCount := LayersSelCount();
else if ( Index > 0 ) and ( Index < LayersCount ) then
lyrCount := 1
else
lyrCount := 0;
end;
if ( not ( Alignment in [ ilaAlignToLeft, ilaAlignToRight, ilaAlignToTop, ilaAlignToBottom, ilaAlignToHorizontalCenter, ilaAlignToVerticalCenter, ilaMatchWidth, ilaMatchHeight ])) and
( lyrCount < 2 ) then
exit
else
if ( lyrCount < 1 ) then
exit;
// Get new position
case Alignment of
ilaAlignToLeft : iPos := Layers[ 0 ].PosX;
ilaAlignToRight : iPos := Layers[ 0 ].PosX + Layers[ 0 ].Width;
ilaAlignToTop : iPos := Layers[ 0 ].PosY;
ilaAlignToBottom : iPos := Layers[ 0 ].PosY + Layers[ 0 ].Height;
ilaAlignToHorizontalCenter : iPos := Layers[ 0 ].PosY + ( Layers[ 0 ].Height div 2 );
ilaAlignToVerticalCenter : iPos := Layers[ 0 ].PosX + ( Layers[ 0 ].Width div 2 );
ilaAlignLeftEdges ,
ilaAlignTopEdges ,
ilaAlignRightEdges ,
ilaAlignBottomEdges ,
ilaAlignHorizontalCenters ,
ilaAlignVerticalCenters ,
ilaMatchWidth ,
ilaMatchHeight : begin
bSet := False;
iPos := 0;
iCurr := 0;
for I := 1 to LayersCount - 1 do
if _DoProcessLayer( I ) then
begin
case Alignment of
ilaAlignLeftEdges : iCurr := Layers[ i ].PosX;
ilaAlignRightEdges : iCurr := Layers[ i ].PosX + Layers[ i ].Width;
ilaAlignTopEdges : iCurr := Layers[ i ].PosY;
ilaAlignBottomEdges : iCurr := Layers[ i ].PosY + Layers[ i ].Height;
ilaAlignHorizontalCenters : iCurr := Layers[ i ].PosY + ( Layers[ i ].Height div 2 );
ilaAlignVerticalCenters : iCurr := Layers[ i ].PosX + ( Layers[ i ].Width div 2 );
ilaMatchWidth : iCurr := Layers[ i ].Width;
ilaMatchHeight : iCurr := Layers[ i ].Height;
end;
if bSet = False then
iPos := iCurr
else
if Alignment in [ ilaAlignLeftEdges, ilaAlignTopEdges ] then
begin
if iCurr < iPos then
iPos := iCurr;
end
else
begin
if iCurr > iPos then
iPos := iCurr;
end;
bSet := True;
// Use first selection for centering
if Alignment in [ ilaAlignHorizontalCenters, ilaAlignVerticalCenters ] then
break;
end;
if not bSet then
exit;
end;
else exit;
end;
// Now set positions
for I := 1 to LayersCount - 1 do
if _DoProcessLayer( I ) then
case Alignment of
ilaAlignToLeft,
ilaAlignLeftEdges : Layers[ i ].PosX := iPos;
ilaAlignToRight,
ilaAlignRightEdges : Layers[ i ].PosX := iPos - Layers[ i ].Width;
ilaAlignToTop,
ilaAlignTopEdges : Layers[ i ].PosY := iPos;
ilaAlignToBottom,
ilaAlignBottomEdges : Layers[ i ].PosY := iPos - Layers[ i ].Height;
ilaAlignToHorizontalCenter,
ilaAlignHorizontalCenters : Layers[ i ].PosY := iPos - ( Layers[ i ].Height div 2 );
ilaAlignToVerticalCenter,
ilaAlignVerticalCenters : Layers[ i ].PosX := iPos - ( Layers[ i ].Width div 2 );
ilaMatchWidth : Layers[ i ].Width := iPos;
ilaMatchHeight : Layers[ i ].Height := iPos;
end;
Update;
end;
// Shortcut method to determine whether loAllowMultiSelect is defined in LayerOptions
function TImageEnView.LayersAllowMultiSelect: boolean;
begin
Result := loAllowMultiSelect in fLayerOptions;
end;
{!!
<FS>TImageEnView.LayersSelectAll
<FM>Declaration<FC>
procedure LayersSelectAll(bIncludeLocked: Boolean = True);
<FM>Description<FN>
Enables the <A TIELayer.Selected> property of all layers. If <FM>bIncludeLocked<FC> = False then <A TIELayer.Locked> layers are not selected.
Notes:
- Does not select the background layer (layer 0)
- Has no effect if <L TImageEnView.LayerOptions>multiple layer selection</L> is not enabled.
<FM>Example<FC>
ImageEnView1.LayersSelectAll();
!!}
procedure TImageEnView.LayersSelectAll(bIncludeLocked: Boolean = True);
var
i: Integer;
ALayer: TIELayer;
begin
if not LayersAllowMultiSelect then
exit;
TIELayer( fLayers[ 0 ]).fSelected := False;
for i := 1 { Skip BG } to LayersCount - 1 do
begin
ALayer := TIELayer( fLayers[ I ]);
if bIncludeLocked or ( ALayer.Locked = False ) then
ALayer.fSelected := True;
end;
Update;
end;
{!!
<FS>TImageEnView.LayersDeselectAll
<FM>Declaration<FC>
procedure LayersDeselectAll();
<FM>Description<FN>
Sets the <A TIELayer.Selected> property of all layers to false.
<FM>Example<FC>
ImageEnView1.LayersDeselectAll();
!!}
procedure TImageEnView.LayersDeselectAll();
var
i: Integer;
begin
for i := 0 to LayersCount - 1 do
TIELayer( fLayers[ I ]).fSelected := False;
Update;
end;
{!!
<FS>TImageEnView.LayersSelCount
<FM>Declaration<FC>
function LayersSelCount(bCountBackgroundLayer: Boolean = True): Integer;
<FM>Description<FN>
Return number of layers that are selected. If <FC>bCountBackgroundLayer<FN> is false, then the background layer (layer 0) is not included in the count.
If <L TImageEnView.LayerOptions>multiple layer selection</L> is enabled then it counts any layer that has <A TIELayer.Selected> set to true. Otherwise it just checks whether <A TImageEnView.LayersCurrent> is not -1.
A selected text layer:
<IMG help_images\text_Selected.gif>
<FM>Example<FC>
// Enable the "Remove Layers" button if a layer is selected (but not the background layer)
btnRemoveLayers.Enabled := ImageEnView1.LayersHaveSelection( False ) > 0;
!!}
function TImageEnView.LayersSelCount(bCountBackgroundLayer: Boolean = True): Integer;
var
I: Integer;
begin
Result := 0;
if LayersAllowMultiSelect = False then
begin
if bCountBackgroundLayer and ( fLayersCurrent >= 0 ) then
Result := 1
else
if fLayersCurrent >= 1 then
Result := 1;
end
else
begin
for I := 0 to LayersCount - 1 do
begin
if ( I = 0 ) and ( bCountBackgroundLayer = False ) then
begin { SKIP } end
else
if TIELayer( fLayers[ i ]).fSelected then
inc( Result );
end;
end;
end;
{!!
<FS>TImageEnView.LayersGroup
<FM>Declaration<FC>
procedure LayersGroup(bSelectedOnly: Boolean = True);
<FM>Description<FN>
Sets the <L TIELayer.GroupIndex>group index</L> of layers so they selected as a group (selecting one layer of the group will select all of them).
If <FC>bSelectedOnly<FN> is true, grouping only affects layers that are selected. If false, it applies to all layers.
Note: LayersGroup has no effect if <L TImageEnView.LayerOptions>multiple layer selection</L> is not enabled
<FM>Example<FC>
// Add all selected layers to a group
ImageEnView1.LayersGroup();
<FM>See Also<FN>
- <A TImageEnView.LayersUngroup>
- <A TIELayer.GroupIndex>
!!}
procedure TImageEnView.LayersGroup(bSelectedOnly: Boolean = True);
var
ALayer: TIELayer;
iNextID: Integer;
i: Integer;
begin
if Proc.AutoUndo and ( loAutoUndoChangesbyCode in fLayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_GroupLayers ), ieuLayer, True, IEOP_LAYERPROPS );
// Get a unique ID
iNextID := 1000;
for i := 0 to LayersCount - 1 do
begin
ALayer := TIELayer( fLayers[ I ]);
if ALayer.GroupIndex >= iNextID then
iNextID := ALayer.GroupIndex + 1;
end;
for i := 0 to LayersCount - 1 do
begin
ALayer := TIELayer( fLayers[ I ]);
if ( bSelectedOnly = false ) or ALayer.Selected then
ALayer.fGroupIndex := iNextID;
end;
end;
{!!
<FS>TImageEnView.LayersUngroup
<FM>Declaration<FC>
procedure LayersUngroup(bSelectedOnly: Boolean = True);
<FM>Description<FN>
Resets the <L TIELayer.GroupIndex>group index</L> of layers so they are not selected as a group.
If <FC>bSelectedOnly<FN> is true, ungrouping only affects layers that are selected. If false, it applies to all layers.
Note: LayersGroup has no effect if <L TImageEnView.LayerOptions>multiple layer selection</L> is not enabled
<FM>Example<FC>
// Remove grouping from selected layers
ImageEnView1.LayersUngroup();
// Unselect the layers
ImageEnView1.LayersDeselectAll();
<FM>See Also<FN>
- <A TImageEnView.LayersGroup>
- <A TIELayer.GroupIndex>
!!}
procedure TImageEnView.LayersUngroup(bSelectedOnly: Boolean = True);
var
ALayer: TIELayer;
i: Integer;
begin
if Proc.AutoUndo and ( loAutoUndoChangesbyCode in fLayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_UngroupLayers ), ieuLayer, True, IEOP_LAYERPROPS );
for i := 0 to LayersCount - 1 do
begin
ALayer := TIELayer( fLayers[ I ]);
if ( bSelectedOnly = false ) or ALayer.Selected then
ALayer.fGroupIndex := 0;
end;
end;
// warning: ioTIFF not supported
// if CompressionFormat = -1 then use internal compressed format (preserves pixelformat and transparency)
// if CompressionFormat = -2 then use internal non-compressed format (preserves pixelformat and transparency)
procedure TImageEnView.LayersSaveToStream(Stream: TStream; CompressionFormat: TIOFileType = -1; SelectedOnly: Boolean = False; ExcludeImageLayers: Boolean = False; SaveThumbnail: Boolean = False; ProgressEvent: TIEProgressEvent = nil);
var
headDesc: WideString;
headWidth, headHeight: integer;
recHead: TLayerHeader;
lcurrent: Integer;
lconstrains: Boolean;
Thumbnail: TIEBitmap;
l, sz: Integer;
//
function _WriteLayers(CountOnly: Boolean): Integer;
var
i: Integer;
writeLayer: Boolean;
begin
Result := 0;
for i := 0 to fLayers.Count - 1 do
begin
writeLayer := True;
if SelectedOnly then
writeLayer := TIELayer( fLayers[ i ]).Selected;
if writeLayer and ExcludeImageLayers and ( TIELayer( fLayers[ i ]) is TIEImageLayer ) then
writeLayer := False;
if writeLayer then
begin
inc( Result );
if not CountOnly then
begin
if assigned( ProgressEvent ) then
ProgressEvent( Self.IO, MulDiv( i + 1, 100, recHead.LayersCount + 1 )); // Include loading of header
Layers[i].SaveToStream( Stream, False, CompressionFormat );
end;
end;
end;
end;
//
begin
if assigned( ProgressEvent ) then
ProgressEvent( Self.IO, 0 );
lcurrent := LayersCurrent;
lconstrains := LayersSelectConstrains;
LayersSelectConstrains := false;
Thumbnail := nil;
try
// prepare header
recHead.Version := IELayers_File_Version;
recHead.LayersCount := _WriteLayers( True );
recHead.FileFormat := CompressionFormat;
move(IELayers_File_Magic[1], recHead.Magic[0], length(IELayers_File_Magic));
// write header
Stream.Write(recHead, sizeof(TLayerHeader));
headWidth := MaxLayerWidth();
headHeight := MaxLayerHeight();
headDesc := '';
if assigned(fImageEnIO) then
headDesc := IO.Params.IEN_Description;
Stream.Write( headWidth, SizeOf( integer ));
Stream.Write( headHeight, SizeOf( integer ));
IESaveStringToStreamW( Stream, headDesc );
if IEGlobalSettings().ThumbnailSize < 1 then
SaveThumbnail := False;
if SaveThumbnail then
begin
Thumbnail := TIEBitmap.create();
LayersSaveMergedTo( Thumbnail, IEGlobalSettings().ThumbnailSize < 1000 );
if ( Thumbnail.Width < 5 ) or ( Thumbnail.Height < 5 ) then
SaveThumbnail := False
else
if ( Thumbnail.Width < Thumbnail.Height ) and ( Thumbnail.Width > IEGlobalSettings().ThumbnailSize ) then
Thumbnail.Resample( IEGlobalSettings().ThumbnailSize, -1, rfFastLinear ) // Maintain height
else
if Thumbnail.Height > IEGlobalSettings().ThumbnailSize then
Thumbnail.Resample( -1, IEGlobalSettings().ThumbnailSize, rfFastLinear ); // Maintain width
end;
Stream.Write( SaveThumbnail, SizeOf( Boolean ));
if SaveThumbnail then
begin
l := Stream.Position;
Stream.Write(l, sizeof(integer)); // Place holder for size
Thumbnail.Write( Stream, Layers_Thumbnail_Format );
// Write size
sz := Stream.Position - l;
Stream.Position := l;
Stream.Write(sz, sizeof(integer)); // Write size
Stream.Position := l + sz; // Position at end of thumbnail
end;
// write layers data
_WriteLayers( False );
finally
{$IFDEF UNITTESTING}
if LayersCurrent <> lcurrent then
raise Exception.create( 'Unexpected LayersCurrent change' );
{$ENDIF}
if LayersCurrent <> lcurrent then
LayersCurrent := lcurrent;
LayersSelectConstrains := lconstrains;
FreeAndNil( Thumbnail );
end;
end;
// LoadThumb must be true at all times, except when we ONLY need to get the IO Params
function IELayersLoadHeaderFromStream(Stream: TStream;
out Header: TLayerHeader;
out Width: Integer;
out Height: Integer;
out Description: Widestring;
var Thumbnail: TIEBitmap;
LoadThumb: Boolean = True
): Boolean;
var
doFreeThumb: Boolean;
ThumbSaved: Boolean;
l, sz: Integer;
begin
result := false;
doFreeThumb := not assigned( Thumbnail );
try
// read header
Stream.Read( Header, sizeof(TLayerHeader));
if {$IfDef DelphiXE4orNewer}AnsiStrings.{$EndIf}StrLComp( Header.Magic, PAnsiChar(IELayers_File_Magic), length(IELayers_File_Magic)) <> 0 then
exit;
if Header.Version >= 7000 then
begin
Stream.Read( Width, SizeOf( integer ));
Stream.Read( Height, SizeOf( integer ));
IELoadStringFromStreamW(Stream, Description);
if LoadThumb then
Stream.Read( ThumbSaved, SizeOf( Boolean ))
else
ThumbSaved := False;
if ThumbSaved and ( Header.Version >= 7003 ) then
begin
l := Stream.Position;
Stream.Read(sz, sizeof(integer));
if assigned( Thumbnail ) then
Thumbnail.Read( Stream, Layers_Thumbnail_Format );
// Position at end of thumbnail
Stream.Position := l + sz;
end
else
if ThumbSaved and ( Header.Version < 7003 ) then
begin
if not assigned( Thumbnail ) then
Thumbnail := TIEBitmap.Create();
Thumbnail.Read( Stream, ioPNG );
end
else
if assigned( Thumbnail ) then
Thumbnail.Clear();
end;
Result := Header.Version > 0;
finally
if doFreeThumb then
FreeAndNil( Thumbnail );
end;
end;
function TImageEnView.LayersLoadFromStream(Stream: TStream; Append: Boolean = False; ProgressEvent: TIEProgressEvent = nil): Boolean;
var
recHead: TLayerHeader;
i: Integer;
lyrKind: TIELayerKind;
sz: Integer;
Guid: TGuid;
Thumbnail: TIEBitmap;
headWidth, headHeight: Integer;
headDesc: Widestring;
begin
LockUpdate();
try
if assigned( ProgressEvent ) then
ProgressEvent( Self.IO, 0 );
Thumbnail := nil;
result := IELayersLoadHeaderFromStream( Stream, recHead, headWidth, headHeight, headDesc, Thumbnail );
if Result = False then
exit;
if assigned(fImageEnIO) then
begin
IO.Params.IEN_Compression := recHead.FileFormat;
IO.Params.fIEN_LayerCount := recHead.LayersCount;
IO.Params.fIEN_Version := recHead.Version;
IO.Params.IEN_Description := '';
if recHead.Version >= 7000 then
begin
IO.Params.IEN_Description := headDesc;
IO.Params.Width := headWidth;
IO.Params.Height := headHeight;
IO.Params.OriginalWidth := headWidth;
IO.Params.OriginalHeight := headHeight;
end;
end;
if recHead.Version > 0 then
begin
if not Append then
for i := fLayers.Count - 1 downto 1 do
LayersRemoveEx( i );
// read layers data and images
for i := 0 to recHead.LayersCount - 1 do
begin
if assigned( ProgressEvent ) then
ProgressEvent( Self.IO, MulDiv( i + 1, 100, recHead.LayersCount + 1 )); // Include loading of header
if recHead.Version < 6400 then
lyrKind := ielkImage
else
ReadLayerPropsFromStream( Stream, True, sz, lyrKind, Guid );
if Append or ( i > 0 ) then
LayersAddEx( lyrKind, 0, 0 );
if recHead.Version < 6400 then
CurrentLayer.LoadFromLegacyStream( Stream, recHead.Version, recHead.FileFormat )
else
CurrentLayer.LoadFromStream( Stream );
end;
result := true;
end;
if ( recHead.Version < 7000 ) and assigned(fImageEnIO) then
begin
IO.Params.Width := Layers[0].Bitmap.Width;
IO.Params.Height := Layers[0].Bitmap.Height;
IO.Params.OriginalWidth := Layers[0].Bitmap.Width;
IO.Params.OriginalHeight := Layers[0].Bitmap.Height;
end;
// Default to first idx (if not appending)
if Append = False then
SetLayersCurrentEx( 0, False, False );
finally
UnlockUpdate();
end;
end;
{!!
<FS>TImageEnView.Wallpaper
<FM>Declaration<FC>
property Wallpaper: TPicture;
<FM>Description<FN>
The Wallpaper property allows you to use an image for your background (behind the main image and layers).
Use <A TImageEnView.WallpaperStyle> to specify how to paint the wallpaper.
<FM>Example<FC>
// Tile a bitmap over the background
ImageEnView1.WallpaperStyle := iewoTile
ImageEnView1.Wallpaper.LoadFromFile('D:\MyWallpaper.bmp');
// Clear the wallpaper
ImageEnView1.Wallpaper := nil;
!!}
procedure TImageEnView.SetWallpaper(Value: TPicture);
begin
if assigned(Value) then
begin
if not assigned(fWallpaper) then
fWallpaper := TPicture.Create;
fWallpaper.Assign(Value);
end
else
begin
// Reset TPicture
if assigned(fWallpaper) then
FreeAndNil(fWallpaper);
fWallpaper := TPicture.Create;
end;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
{!!
<FS>TImageEnView.WallpaperStyle
<FM>Declaration<FC>
property WallpaperStyle: <A TIEWallpaperStyle>;
<FM>Description<FN>
If you are using an image for your background (specified by <A TImageEnView.Wallpaper>), then WallpaperStyle defines how it will be painted.
<FM>Example<FC>
// Tile a bitmap over the background
ImageEnView1.WallpaperStyle := iewoTile
ImageEnView1.Wallpaper.LoadFromFile('D:\MyWallpaper.bmp');
!!}
procedure TImageEnView.SetWallpaperStyle(Value: TIEWallpaperStyle);
begin
if Value <> fWallpaperStyle then
begin
fWallpaperStyle := Value;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
end;
procedure TImageEnView.DrawBackgroundToCanvas(ACanvas: TCanvas; iWidth: Integer = -1; iHeight: Integer = -1);
begin
if (iWidth = -1) and (iHeight = -1) then
begin
iWidth := Width;
iHeight := Height;
end;
// draws only the background
IEDrawBackground(ComponentState, ACanvas, nil, fBackgroundStyle,
GetThemeColor( ietpControlBackground, fBackground ),
0, 0, iWidth, iHeight, 0, 0, 0, 0, fChessboardSize, fChessboardBrushStyle, fChessboardColor2Customized,
GetThemeColor( ietpControlBackgroundGradientEnd, fGradientEndColor ),
fWallpaper, fWallpaperStyle, fLiveBackground);
end;
{!!
<FS>TImageEnView.VisibleBitmapRect
<FM>Declaration<FC>
property VisibleBitmapRect : TRect; (read/write)
<FM>Description<FN>
Show a specific area of the current image, or return the area of the image that is shown.
Note: The rectangle passed will be automatically adjusted to ensure the correct aspect ratio.
<FM>Examples<FC>
// Show the central portion of an image
ImageEnView1.VisibleBitmapRect := Rect( 100, 100, 200, 200 );
// Output the visible bitmap in ImageEnView1 to ImageEnView2
ImageEnView2.IEBitmap.Width := IERectangle( ImageEnView1.VisibleBitmapRect ).Width;
ImageEnView2.IEBitmap.Height := IERectangle( ImageEnView1.VisibleBitmapRect ).Height;
ImageEnView1.IEBitmap.DrawToTIEBitmap( ImageEnView2.IEBitmap, 0, 0, IERectangle( ImageEnView1.VisibleBitmapRect ) );
ImageEnView2.Update;
<FM>See Also<FN>
- <A TImageEnView.ViewX>
- <A TImageEnView.ViewY>
- <A TImageEnView.ExtentX>
- <A TImageEnView.ExtentY>
!!}
function TImageEnView.GetVisibleBitmapRect : TRect;
begin
Result.Left := XScr2Bmp( OffsetX, False );
Result.Top := YScr2Bmp( OffsetY, False );
Result.Right := XScr2Bmp( GetClientWidthExRulers - OffsetX, False ) - 1;
Result.Bottom := YScr2Bmp( GetClientHeightExRulers - OffsetY, False ) - 1;
end;
procedure TImageEnView.SetVisibleBitmapRect(ARect: TRect);
var
dZoom: Double;
begin
LockPaint;
try
ARect := AdjustRectToAspectRatio( ARect, False );
if (ARect.Bottom-ARect.Top) = 0 then
exit;
dZoom := ClientHeight / (ARect.Bottom - ARect.Top);
Zoom := dZoom * 100;
ViewX := round(ARect.left * dZoom);
ViewY := round(ARect.top * dZoom );
finally
UnlockPaint;
end;
end;
{!!
<FS>TImageEnView.LayersCreateFromSelection
<FM>Declaration<FC>
function LayersCreateFromSelection(): Integer;
<FM>Description<FN>
Creates a new <L TIEImageLayer>image layer</L> from the current selection. This might be used to copy & paste selections, for example.
Returns the index of the new layer.
<FM>Example<FC>
// select an ellipse
ImageEnView1.SelectEllipse(150, 150, 50, 50);
// copy selected area and create a new layer
ImageEnView1.LayersCreateFromSelection;
// move the new layer
ImageEnView1.CurrentLayer.PosX := 100;
ImageEnView1.CurrentLayer.PosY := 100;
// paste to the background
ImageEnView1.LayersMerge(0, 1);
!!}
function TImageEnView.LayersCreateFromSelection: Integer;
var
baseLayer: Integer;
begin
baseLayer := LayersCurrent;
result := LayersAdd( ielkImage ); // Use LayersAdd so it saves Undo if needed
LayersCurrent := baseLayer;
if Selected then
begin
CopySelectionToBitmap( Layers[result].Bitmap, not EnableAlphaChannel );
Layers[result].PosX := SelX1 + CurrentLayer.PosX;
Layers[result].PosY := SelY1 + CurrentLayer.PosY;
end
else
begin
Layers[Result].Bitmap.Assign( CurrentLayer.Bitmap );
Layers[result].PosX := CurrentLayer.PosX;
Layers[result].PosY := CurrentLayer.PosY;
end;
SetLayersCurrentEx( result, False, True ); // this disables selection
end;
{!!
<FS>TImageEnView.LayersCreateFromClipboard
<FM>Declaration<FC>
function LayersCreateFromClipboard: Integer;
<FM>Description<FN>
Creates a new layer with the content of the clipboard.
The clipboard can either contain an image or a layer copied from a TImageEnView.
This is the same as calling TImageEnView.Proc.<A TImageEnProc.PasteFromClipboard>( iecpLayer ); However it returns an index of the new layer.
!!}
function TImageEnView.LayersCreateFromClipboard: Integer;
begin
Result := Proc.PasteFromClipboard_Layer();
end;
{!!
<FS>TImageEnView.LayersCreateFromFile
<FM>Declaration<FC>
function LayersCreateFromFile(Filename : string = '') : Integer;
<FM>Description<FN>
Loads an image from a file and adds it as a new <L TIEImageLayer>image layer</L>.
If Filename is '' then an open dialog is displayed (using <A TImageEnIO.ExecuteOpenDialog>) to allow the user to browse for an image to load for the layer.
Returns the index of the new layer, or -1 in case of error.
<FM>Examples<FC>
// Prompt the user to select an image to load as a layer
ImageEnView1.LayersCreateFromFile();
// Load an image as a new layer
ImageEnView1.LayersCreateFromFile( 'C:\MyImage.jpg' );
// Which is the same as...
ImageEnView1.LayersAdd( 'C:\MyImage.jpg' );
!!}
function TImageEnView.LayersCreateFromFile(Filename : string = '') : integer;
begin
if Filename = '' then
Filename := IO.ExecuteOpenDialog('', '', false);
if Filename = '' then
begin
Result := -1;
exit;
end;
Result := LayersAdd( ielkImage ); // Use LayersAdd so it saves Undo if needed
try
IO.LoadFromFile(Filename);
if IO.Aborting then
raise EIEException.create('Load Error');
except
LayersRemoveEx( Result );
Result := -1;
end;
end;
{!!
<FS>TImageEnView.LayersCreateFromEdge
<FM>Declaration<FC>
function LayersCreateFromEdge(ScreenX, ScreenY: integer; Tolerance: integer; MaxFilter: boolean = False): Integer;
<FM>Description<FN>
Creates a new <L TIEPolylineLayer>Polyline layer</L> with a closed polyline (polygon) along the edge of the background layer.
The edge is determined by simulating a flood fill starting at the point specified by ScreenX and ScreenY (these are points within the control, not points of the bitmap).
Tolerance specifies the color difference between the starting pixel and the pixel being tested.
Set MaxFilter to True to apply a maximum filter that removes noise.
Result is the index of the created layer, or -1 if the method fails
<FM>Examples<FC>
// Create a new polygon layer when the user clicks on the image
procedure TForm1.ImageEnView1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
ImageEnView1.LayersCreateFromEdge( X, Y, 25, true );
end;
// Create a new polygon using a floodfill from the top left of the background image
ImageEnView1.LayersCreateFromEdge( ImageEnView1.XBmp2Scr( 0 ), ImageEnView1.YBmp2Scr( 0 ), 25, true);
!!}
function TImageEnView.LayersCreateFromEdge(ScreenX, ScreenY: integer; Tolerance: integer; MaxFilter: boolean = False): Integer;
var
points: TIEArrayOfTPoint;
x, y: Integer;
minX, maxX, minY, maxY : Integer;
layerX, layerY: Integer;
begin
result := -1;
x := XScr2Bmp( ScreenX, True );
y := YScr2Bmp( ScreenY, True );
points := IEMakeMagicWandPoints(fIEBitmap, x, y, MaxFilter, Tolerance);
if length(points) > 0 then
begin
IEGetPointsRange( points, length(points), minX, minY, maxX, maxY );
if ( maxX < 1 ) or ( maxY < 1 ) then
exit;
layerX := CurrentLayer.PosX;
layerY := CurrentLayer.PosY;
result := LayersAdd( ielkPolyLine ); // Use LayersAdd so it saves Undo if needed
Layers[ result ].PosX := layerX + minX;
Layers[ result ].PosY := layerY + minY;
Layers[ result ].Width := maxX - minX;
Layers[ result ].Height := maxY - minY;
// Scale points to 0-1000 range of TIEPolyLineLayer
IEScalePoints( points, length(points), 0, 0, 1000, 1000, False);
TIEPolyLineLayer( Layers[ result ]).SetPoints( points, True );
Update();
end;
end;
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 7.0.0 (23/11/16)
function TImageEnView.LayersCreateFromText(const Text : string;
const sFontName : string;
iFontSize : Integer;
cFontColor : TColor;
Style : TFontStyles;
bAddShadow: boolean = False;
iBlurRadius : Integer = 3;
iShadowOffset : Integer = 2;
Angle : Integer = 0;
bAntiAlias : Boolean = true) : integer;
var
WasBackground: TColor;
begin
LockPaint();
try
// Add a new layer
Result := LayersAdd( ielkImage ); // Use LayersAdd so it saves Undo if needed
// TextOut will resize and fill the layer
fIEBitmap.Allocate(1, 1);
// Output our text
if Angle = 0 then
begin
fIEBitmap.AlphaChannel.Fill(0);
Proc.TextOut(0, 0, Text, sFontName, iFontSize, cFontColor, Style, Angle, True, True);
end
else
begin
WasBackground := fBackground;
fBackground := clWhite;
fIEBitmap.Pixels[ 0, 0 ] := CreateRGB( 255, 255, 255 );
Proc.TextOut(0, 0, Text, sFontName, iFontSize, cFontColor, Style, Angle, False, True);
// Make the white background transparent
Proc.SetTransparentColors( CreateRGB( 255, 255, 255 ), CreateRGB( 255, 255, 255 ), 0 );
fBackground := WasBackground;
end;
// Add our shadow
if bAddShadow then
Proc.AddSoftShadow(iBlurRadius, iShadowOffset, iShadowOffset);
// Remove the excess transparency
Proc.CropTransparentBorder;
finally
UnlockPaint();
end;
end;
{$ENDIF}
{!!
<FS>TImageEnView.BackBuffer
<FM>Declaration<FC>
property BackBuffer: TBitmap;
<FM>Description<FN>
Provides access to the back buffer where ImageEn will draw the image (and layers) prior to the paint event.
You can draw on BackBuffer by handling the <A TImageEnView.OnDrawBackBuffer> event to paint onto the Backbuffer canvas.
BackBuffer is updated whenever <A TImageEnView.Update> is called.
<FM>Example<FC>
Procedure Form1OnDrawBackBuffer(Sender: TObject);
Begin
With ImageEnView1.BackBuffer.Canvas do
begin
Pen.Color := clRed;
MoveTo( 0, 0 );
LineTo( 100, 100 );
End;
End;
!!}
function TImageEnView.GetBackBuffer: TBitmap;
begin
result := fBackBuffer.VclBitmap;
end;
procedure TImageEnView.SetOnSaveUndo(value: TIESaveUndoEvent);
begin
fOnSaveUndo := value;
if assigned(fImageEnProc) then
fImageEnProc.OnSaveUndo := value;
end;
{!!
<FS>TImageEnView.OnSaveUndo
<FM>Declaration<FC>
property OnSaveUndo: <A TIESaveUndoEvent>;
<FM>Description<FN>
OnSaveUndo occurs after <A TImageEnProc.SaveUndo> is called by user action or your code.
<FC>source<FN> is the same parameter used when calling <A TImageEnProc.SaveUndo>.
!!}
function TImageEnView.GetOnSaveUndo: TIESaveUndoEvent;
begin
result := fOnSaveUndo;
end;
{!!
<FS>TImageEnView.LayersFixSizes
<FM>Declaration<FC>
procedure LayersFixSizes(layer: Integer = LYR_ALL_LAYERS);
<FM>Description<FN>
Resamples the bitmap of layers to match their actual size. This will improve the quality of the layer.
The index of a specific layer can be specified, or <FC>LYR_ALL_LAYERS<FN> (-1) to process all layers, or <FC>LYR_SELECTED_LAYERS<FN> (-2) to process selected layers.
Note: <A TImageEnView.LayersMergeFilter> will specify the quality of image layers, if they do not have a custom <A TIEImageLayer.UseResampleFilter>
<FM>Example<FC>
// Resample top-most layer
ImageEnView1.LayersFixSizes( ImageEnView1.LayersCount - 1 );
// Resample all layers
ImageEnView1.LayersFixSizes( LYR_ALL_LAYERS );
// Resample selected layers
ImageEnView1.LayersFixSizes( LYR_SELECTED_LAYERS );
// Make all selected layers 1/3 size
ImageEnView1.LockUpdate;
for i := 0 to ImageEnView1.LayersCount - 1 do
if ImageEnView1Layers[ I ].Selected then
begin
ImageEnView1Layers[ I ].Width := ImageEnView1Layers[ I ].Width div 3;
ImageEnView1Layers[ I ].Height := ImageEnView1Layers[ I ].Height div 3;
end;
ImageEnView1.LayersFixSizes( LYR_SELECTED_LAYERS );
ImageEnView1.UnlockUpdate;
!!}
// layer = -1 : all layers
procedure TImageEnView.LayersFixSizes(layer: Integer = LYR_ALL_LAYERS);
var
lyr: TIELayer;
i, l: Integer;
rf: TResampleFilter;
tempproc: TImageEnProc;
bDoLayer: Boolean;
begin
tempproc := TImageEnProc.Create(nil);
try
l := fLayersCurrent;
for i := 0 to fLayers.Count - 1 do
begin
lyr := TIELayer(fLayers[i]);
case layer of
LYR_ALL_LAYERS : bDoLayer := true;
LYR_SELECTED_LAYERS : bDoLayer := lyr.Selected;
else bDoLayer := i = layer;
end;
if bDoLayer and (( lyr.WidthD <> 0 ) or ( lyr.HeightD <> 0 )) and ( lyr is TIEImageLayer ) then
begin
LayersCurrent := i;
if TIEImageLayer( lyr ).UseResampleFilter then
rf := TIEImageLayer( lyr ).ResampleFilter
else
rf := fLayersMergeFilter;
tempproc.AttachedIEBitmap := lyr.Bitmap;
tempproc.Resample(lyr.Width, lyr.Height, rf);
lyr.WidthD := 0;
lyr.HeightD := 0;
end;
end;
LayersCurrent := l;
finally
tempproc.Free();
end;
end;
{!!
<FS>TImageEnView.LayersFixBorders
<FM>Declaration<FC>
procedure LayersFixBorders(layer: Integer = LYR_ALL_LAYERS);
<FM>Description<FN>
Removes the transparent border around the bitmap of a layer, which typically occurs after multiple rotations.
The index of a specific layer can be specified, or <FC>LYR_ALL_LAYERS<FN> (-1) to process all layers, or <FC>LYR_SELECTED_LAYERS<FN> (-2) to process selected layers.
Note: This can be called automatically using <FC>loAutoFixBorders<FN> of <A TImageEnView.LayerOptions>
<FM>Example<FC>
// Remove border of top-most layer
ImageEnView1.LayersFixBorders( ImageEnView1.LayersCount - 1 );
// Remove border of all layers
ImageEnView1.LayersFixBorders( LYR_ALL_LAYERS );
// Remove border of selected layers
ImageEnView1.LayersFixBorders( LYR_SELECTED_LAYERS );
!!}
procedure TImageEnView.LayersFixBorders(layer: Integer = LYR_ALL_LAYERS);
var
lyr: TIELayer;
i: Integer;
p: TImageEnProc;
doLayer: Boolean;
begin
p := TImageEnProc.Create(nil);
try
for i := 0 to fLayers.Count-1 do
begin
lyr := TIELayer(fLayers[i]);
case layer of
LYR_ALL_LAYERS : doLayer := true;
LYR_SELECTED_LAYERS : doLayer := lyr.Selected;
else doLayer := i = layer;
end;
if doLayer and ( lyr.Kind = ielkImage ) then
begin
p.AttachedIEBitmap := lyr.Bitmap;
p.CropTransparentBorder();
end;
end;
finally
p.Free();
end;
end;
{!!
<FS>TImageEnView.LayersFixRotations
<FM>Declaration<FC>
procedure LayersFixRotations(layer: Integer = LYR_ALL_LAYERS);
<FM>Description<FN>
Rotates the bitmap of a layer to its actual rotation angle. This will improve the quality of the layer.
The index of a specific layer can be specified, or <FC>LYR_ALL_LAYERS<FN> (-1) to process all layers, or <FC>LYR_SELECTED_LAYERS<FN> (-2) to process selected layers.
Notes:
- This can be called automatically using <FC>loAutoFixRotation<FN> of <A TImageEnView.LayerOptions>
- This method will also call <A TImageEnView.LayersFixSizes>
<FM>Example<FC>
// Improve rotation of top-most layer
ImageEnView1.LayersFixRotations( ImageEnView1.LayersCount - 1 );
// Improve rotation of all layers
ImageEnView1.LayersFixRotations( LYR_ALL_LAYERS );
// Improve rotation of selected layers
ImageEnView1.LayersFixRotations( LYR_SELECTED_LAYERS );
!!}
// layer = -1 : all layers
procedure TImageEnView.LayersFixRotations(layer: Integer = LYR_ALL_LAYERS);
var
lyr: TIELayer;
i, l: Integer;
pts: array [0..3] of TPoint;
lposx, lposy, lresw, lresh: Integer;
r: TRect;
tempproc: TImageEnProc;
bDoLayer: Boolean;
begin
tempproc := TImageEnProc.Create(nil);
try
tempproc.Background := fBackground;
l := fLayersCurrent;
for i := 0 to fLayers.Count - 1 do
begin
lyr := TIELayer(fLayers[i]);
case layer of
LYR_ALL_LAYERS : bDoLayer := true;
LYR_SELECTED_LAYERS : bDoLayer := lyr.Selected;
else bDoLayer := i = layer;
end;
if bDoLayer and ( lyr.Rotate <> 0 ) and ( lyr.Kind = ielkImage ) then
begin
LayersFixSizes(i);
LayersCurrent := i;
lresw := lyr.Width;
lresh := lyr.Height;
pts[0].X := 0;
pts[0].Y := 0;
pts[1].X := lyr.Width;
pts[1].Y := 0;
pts[2].X := lyr.Width;
pts[2].Y := lyr.height;
pts[3].X := 0;
pts[3].Y := lyr.Height;
lposx := lyr.PosX;
lposy := lyr.PosY;
lyr.Bitmap.AlphaChannel; // creates alpha channel
tempproc.AttachedIEBitmap := lyr.Bitmap;
if fLayersRotationAntialias then
tempproc.Rotate(lyr.Rotate, fLayersRotationFilter, -1)
else
tempproc.Rotate(lyr.Rotate, ierNone, -1);
IERotatePoints(pts, 4, lyr.Rotate, trunc(lyr.RotateCenterX*lresw), trunc(lyr.RotateCenterY*lresh));
lyr.PosX := lposx + imin(imin(imin(pts[0].X, pts[1].X), pts[2].X), pts[3].X);
lyr.PosY := lposy + imin(imin(imin(pts[0].Y, pts[1].Y), pts[2].Y), pts[3].Y);
lyr.WidthD := 0;
lyr.HeightD := 0;
lyr.Rotate := 0;
if i = 0 then
begin
lyr.PosX := lposx;
lyr.PosY := lposy;
end
else
begin
r := IEGetVisibleArea(fIEBitmap, nil, nil);
tempproc.Crop(r);
lyr.PosX := lyr.PosX + r.Left;
lyr.PosY := lyr.posY + r.Top;
end;
end;
end;
LayersCurrent := l;
finally
tempproc.Free();
end;
end;
{!!
<FS>TImageEnView.SoftCrop
<FM>Declaration<FC>
property SoftCrop: <A TIESoftCropMode>;
<FM>Description<FN>
Specifies the operation to perform when a layer is outside of the background layer (layer 0). I.e. to provide a visual indication to the user.
Portion of layers that are outside the background layer are shown as:
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C>iesfNone</C> <C>Normal</C> </R>
<R> <C>iesfAlphaBlend</C> <C>Partially transparent (to the level specified by <A TImageEnView.SoftCropValue>)</C> </R>
<R> <C>iesfGrid</C> <C>Grayed (Grid matrix pattern)</C> </R>
<R> <C>iesfAdd</C> <C>Color shifted/washed out (to the level specified by <A TImageEnView.SoftCropValue>)</C> </R>
</TABLE>
!!}
procedure TImageEnView.SetSoftCrop(v: TIESoftCropMode);
begin
fSoftCrop := v;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
{!!
<FS>TImageEnView.SoftCropValue
<FM>Declaration<FC>
property SoftCropValue: Integer;
<FM>Description<FN>
If <A TImageEnView.SoftCrop> is iesfAlphaBlend then the SoftCropValue specifies the level of transparency from 0 (minimal) to 255 (fully)
If <A TImageEnView.SoftCrop> is iesfAdd then the SoftCropValue specifies the amount of color shift from 0 (minimal) to 255 (extreme)
!!}
procedure TImageEnView.SetSoftCropValue(v: Integer);
begin
fSoftCropValue := v;
UpdateReason := ieurComponentStuffChanged;
Update;
end;
{!!
<FS>TImageEnView.SetInteractionHint
<FM>Declaration<FC>
procedure SetInteractionHint(const Text: String; x, y: Integer; const minText: String);
<FM>Description<FN>
Specify a text hint to appear over the ImageEnView (which will be displayed at the next painting event).
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>Text<FN></C> <C>Text to draw</C> </R>
<R> <C><FC>x<FN></C> <C>Horizontal position</C> </R>
<R> <C><FC>y<FN></C> <C>Vertical position</C> </R>
<R> <C><FC>minText<FN></C> <C>Example (dummy) text to calculate the minimum text size</C> </R>
</TABLE>
<FM>Example<FC>
// display current position in pixels
procedure TForm1.ImageEnVect1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
xx, yy: integer;
begin
xx := ImageEnView1.XScr2Bmp(X);
yy := ImageEnView1.yscr2bmp(Y);
if (xx >= 0) and (xx < ImageEnView1.IEBitmap.Width) and (yy >= 0) and (yy < ImageEnView1.IEBitmap.Height) then
begin
ImageEnView1.SetInteractionHint(Format('%d %d', [xx, yy]), X, Y, '0000 0000');
ImageEnView1.Paint();
end;
end;
!!}
procedure TImageEnView.SetInteractionHint(const Text: String; x, y: Integer; const minText: String);
begin
fInteractionHint := Text;
fInteractionHintX := x;
fInteractionHintY := y;
fInteractionHintMinText := minText;
end;
{!!
<FS>TImageEnView.HighlightedPixel
<FM>Declaration<FC>
property HighlightedPixel: TPoint;
<FM>Description<FN>
Specify a pixel within your image and it will be highlighted by a colored box (to draw attention to it).
Set it to (-1, -1) to disable.
<FM>Demo<FN>
<TABLE2>
<R> <C_IMG_DEMO> <C>Demos\Display\ImageComp\ImageComp.dpr </C> </R>
</TABLE>
!!}
procedure TImageEnView.SetHighlightedPixel(v: TPoint);
var
x1, y1, x2, y2: Integer;
begin
if (fHighlightedPixel.X > -1) and (fHighlightedPixel.Y > -1) then
begin
x1 := XBmp2Scr( fHighlightedPixel.X, True );
y1 := YBmp2Scr( fHighlightedPixel.Y, True );
x2 := XBmp2Scr( fHighlightedPixel.X + 1, True );
y2 := YBmp2Scr( fHighlightedPixel.Y + 1, True );
fHighlightedPixel := Point(-1, -1);
UpdateRect(Rect(x1-5, y1-5, x2+5, y2+5));
end;
fHighlightedPixel := v;
Paint;
end;
{!!
<FS>TImageEnView.MoveContentTo
<FM>Declaration<FC>
procedure TImageEnView.MoveContentTo(Destination: <A TImageEnView>);
<FM>Description<FN>
Transfers the current image, all layers and input/output parameters to the destination TImageEnView component.
Note:
- This method doesn't copy the images, but transfers pointers to the image buffer
- After the operation the source component will be empty, and all previous images will be removed from "Destination"
- To "Copy" the content to another TImageEnView use the <A TImageEnView.Assign> method
!!}
procedure TImageEnView.MoveContentTo(Destination: TImageEnView);
var
src_oldnav, dst_oldnav: TImageEnView;
i: integer;
begin
if Destination = self then
exit;
// Select base layer
SetLayersCurrent( 0 );
// disable navigator
src_oldnav := fNavigator;
SetNavigator(nil);
dst_oldnav := Destination.fNavigator;
Destination.SetNavigator(nil);
// assign IO params
Destination.IO.Params.Assign( IO.Params );
// free destination images
for i := 0 to Destination.fLayers.Count - 1 do
TIELayer(Destination.fLayers[i]).Free;
FreeAndNil(Destination.fLayers);
// copy pointers to destination
Destination.fLayers := fLayers;
Destination.fIEBitmap := fIEBitmap;
Destination.fBitmap := fBitmap;
Destination.fLayersCurrent := fLayersCurrent;
// change destination layers owner
for i := 0 to Destination.fLayers.Count-1 do
TIELayer(Destination.fLayers[i]).fOwner := Destination;
// create empty images in source
fIEBitmap := TIEBitmap.Create;
if fLegacyBitmap then
begin
fBitmap := TBitmap.create;
fBitmap.pixelformat := pf24bit;
fIEBitmap.EncapsulateTBitmap(fBitmap, true);
end;
fLayers := TList.Create;
fLayers.Add(TIEImageLayer.Create(self, fIEBitmap, true));
with TIELayer(fLayers[0]) do
begin
VisibleBox := false;
Locked := true;
end;
fLayersCurrent := 0;
Destination.CallBitmapChangeEvents;
CallBitmapChangeEvents;
Destination.Update;
Update;
// enable navigators
SetNavigator(src_oldnav);
Destination.SetNavigator(dst_oldnav);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////
// Smooth scroll
{!!
<FS>TImageEnView.SetViewXYSmooth
<FM>Declaration<FC>
procedure SetViewXYSmooth(x, y: Integer);
<FM>Description<FN>
Repositions the image view (i.e. by setting <A TImageEnView.ViewX> and <A TImageEnView.ViewY>), but does so with smooth scrolling (image will slowly pan to the new location).
To control the smoothness set <A TImageEnView.SmoothScrollValue>.
<FM>See Also<FN>
- <A TImageEnView.OnFinishSmoothTask>
- <A TImageEnView.SmoothScrollValue>
!!}
procedure TImageEnView.SetViewXYSmooth(x, y: Integer);
begin
if fSmoothScrollValue = 0 then
SetViewXY(x, y)
else
begin
fSmoothScrollDestX := x;
fSmoothScrollDestY := y;
if not assigned(fSmoothScrollTimer) then
begin
fSmoothScrollTimer := TTimer.Create(self);
fSmoothScrollTimer.OnTimer := OnSmoothSetView;
fSmoothScrollTimer.Interval := 30;
fSmoothScrollTimer.Enabled := true;
OnSmoothSetView(self); // first call done manually
end;
end;
end;
{!!
<FS>TImageEnView.SmoothScrollValue
<FM>Declaration<FC>
property SmoothScrollValue: Integer;
<FM>Description<FN>
Specifies the smoothness (speed) of scrolling when <A TImageEnView.SetViewXYSmooth> is called or <A TImageEnView.MouseInteract> contains miMovingScroll.
A large values increases the smoothness. "0" disables smoothing (i.e. so that it acts like <A TImageEnView.SetViewXY>).
Default: 8.
!!}
procedure TImageEnView.SetSmoothScrollValue(v: Integer);
begin
fSmoothScrollValue := v;
end;
procedure TImageEnView.OnSmoothSetView(Sender: TObject);
var
x, y: Integer;
lx, ly: Integer;
begin
if assigned(fSmoothScrollTimer) then
begin
lx := fViewX;
ly := fViewY;
x := round(fViewX - (fViewX - fSmoothScrollDestX) / fSmoothScrollValue);
y := round(fViewY - (fViewY - fSmoothScrollDestY) / fSmoothScrollValue);
SetViewXY(x, y);
if ((fViewX = lx) and (fViewY = ly)) or IEIsLeftMouseButtonPressed then
StopSmoothScroll();
end;
end;
procedure TImageEnView.StopSmoothScroll();
begin
FreeAndNil(fSmoothScrollTimer);
if assigned(fOnFinishSmoothTask) then
OnFinishSmoothTask(self, iestScroll);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////
// Smooth zoom
{!!
<FS>TImageEnView.SetZoomSmooth
<FM>Declaration<FC>
procedure SetZoomSmooth(Zoom: Double);
<FM>Description<FN>
Sets the image zoom (i.e. by setting <A TImageEnView.Zoom>), but does so with smooth zoom (image will slowly zoom to the new value).
To control the smoothness set <A TImageEnView.SmoothZoomValue>.
<FM>See Also<FN>
- <A TImageEnView.OnFinishSmoothTask>
- <A TImageEnView.SmoothZoomValue>
!!}
procedure TImageEnView.SetZoomSmooth(Zoom: Double);
begin
if fSmoothZoomValue = 0 then
self.SetZoom(Zoom)
else
begin
fSmoothZoomDest := Zoom;
if not assigned(fSmoothZoomTimer) then
begin
fSmoothZoomTimer := TTimer.Create(self);
fSmoothZoomTimer.OnTimer := OnSmoothZoom;
fSmoothZoomTimer.Interval := 30;
fSmoothZoomTimer.Enabled := true;
OnSmoothZoom(self); // first call done manually
end;
end;
end;
{!!
<FS>TImageEnView.SmoothZoomValue
<FM>Declaration<FC>
property SmoothZoomValue: Integer;
<FM>Description<FN>
Specifies the smoothness (speed) of zooming when <A TImageEnView.SetZoomSmooth> is called.
A large values increases the smoothness. "0" disables smoothing (i.e. so that it acts like <A TImageEnView.Zoom>).
Default: 8.
!!}
procedure TImageEnView.SetSmoothZoomValue(v: Integer);
begin
fSmoothZoomValue := v;
end;
procedure TImageEnView.OnSmoothZoom(Sender: TObject);
var
delta: double;
begin
if assigned(fSmoothZoomTimer) then
begin
delta := (Zoom - fSmoothZoomDest) / fSmoothZoomValue;
SetZoom(Zoom - delta);
if (abs(delta) <= (1 / fSmoothZoomValue)) then
begin
SetZoom(fSmoothZoomDest);
StopSmoothZoom();
end
else
if IEIsLeftMouseButtonPressed then
StopSmoothZoom();
end;
end;
procedure TImageEnView.StopSmoothZoom();
begin
FreeAndNil(fSmoothZoomTimer);
if assigned(fOnFinishSmoothTask) then
OnFinishSmoothTask(self, iestZoom);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////
procedure IEInitialize_imageenview;
begin
InitImageEnView;
end;
procedure IEFinalize_imageenview;
begin
if assigned(IEGlobalSettings().GridPen) then
begin
IEGlobalSettings().GridPen.Free();
IEGlobalSettings().GridPen := nil;
end;
end;
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
// PostFrames support
type
TIEPostFramesTarget = class
public
FSource: TImageEnView;
FTarget: TImageEnView;
FDelay: integer; // in ms
FInterval: integer; // in ms
FTimer: TTimer;
FBuffers: TList; // a list of TIEBitmap objects (Delay/Interval+1)
FElapsed: integer; // in ms
FNextToRead: integer;
FNextToWrite: integer;
constructor Create(source, target: TImageEnView; delay: Integer; interval: Integer);
destructor Destroy; override;
private
procedure PostFrames(Sender: TObject);
end;
constructor TIEPostFramesTarget.Create(source, target: TImageEnView; delay: Integer; interval: Integer);
var
i, c: Integer;
begin
inherited Create;
FSource := source;
FTarget := target;
FDelay := delay;
FInterval := interval;
FTimer := TTimer.Create(nil);
FTimer.OnTimer := PostFrames;
FBuffers := TList.Create;
c := delay div interval +2;
for i := 0 to c-1 do
FBuffers.Add( TIEBitmap.Create );
FElapsed := 0;
FTimer.Interval := interval;
FTimer.Enabled := true;
FNextToRead := 0;
FNextToWrite := 0;
end;
destructor TIEPostFramesTarget.Destroy;
var
i: Integer;
begin
FTimer.Free;
for i := 0 to FBuffers.Count-1 do
TIEBitmap(FBuffers[i]).Free;
FBuffers.Free;
inherited;
end;
procedure TIEPostFramesTarget.PostFrames(Sender: TObject);
begin
inc(FElapsed, FInterval);
if FElapsed > FDelay then
begin
// move first frame to target viewer
if FTarget.IEBitmap <> nil then
FTarget.IEBitmap.Assign( TIEBitmap(FBuffers[FNextToRead]) );
FTarget.Update;
inc(FNextToRead);
if FNextToRead = FBuffers.Count-1 then
FNextToRead := 0;
end;
if FSource.IEBitmap <> nil then
TIEBitmap(FBuffers[FNextToWrite]).Assign( FSource.IEBitmap );
inc(FNextToWrite);
if FNextToWrite = FBuffers.Count-1 then
FNextToWrite := 0;
end;
{!!
<FS>TImageEnView.BeginPostFrames
<FM>Declaration<FC>
procedure BeginPostFrames(target: TImageEnView; delay: Integer; interval: Integer);
<FM>Description<FN>
Sends the current image to a target <A TImageEnView>, after "delay" milliseconds and then after each "interval" milliseconds.
This is useful to display captured frames to another control with delay (See the VideoCapture\DirectShow5 demo).
You can (but don't need to) end frame sending by calling <A TImageEnView.EndPostFrames>.
Multiple calls to BeginPostFrames are possible.
<FM>Example<FC>
// send the current image to ImageEnView2 after 5 seconds, and to ImageEnView3 after 10 seconds, at 50 ms for each frame
ImageEnView1.BeginPostFrames(ImageEnView2, 5000, 50);
ImageEnView1.BeginPostFrames(ImageEnView3, 10000, 50);
!!}
procedure TImageEnView.BeginPostFrames(target: TImageEnView; delay: Integer; interval: Integer);
begin
// removes if already exists
EndPostFrames( target );
// add new
fPostFrames.Add( TIEPostFramesTarget.Create(self, target, delay, interval) );
end;
procedure TImageEnView.RemovePostFrames(index: Integer);
begin
if assigned(fPostFrames) and (index>-1) then
begin
TIEPostFramesTarget(fPostFrames[index]).Free;
fPostFrames.Delete(index);
end;
end;
{!!
<FS>TImageEnView.EndPostFrames
<FM>Declaration<FC>
procedure EndPostFrames(target: TImageEnView);
<FM>Description<FN>
Stops sending of the current image to a target <A TImageEnView> that has been commenced using <A TImageEnView.BeginPostFrames>.
<FM>Example<FC>
ImageEnView1.EndPostFrames(ImageEnView2);
ImageEnView1.EndPostFrames(ImageEnView3);
!!}
procedure TImageEnView.EndPostFrames(target: TImageEnView);
var
i: Integer;
begin
for i := 0 to fPostFrames.Count-1 do
if TIEPostFramesTarget(fPostFrames[i]).FTarget = target then
begin
RemovePostFrames( i );
break;
end;
end;
// End of PostFrames support
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
{!!
<FS>TImageEnView.LayersCropBackground
<FM>Declaration<FC>
procedure TImageEnView.LayersCropBackground(SelectedOnly: Boolean = False; FillAlpha: Integer = 255; AllowReduce: Boolean = True; AllowEnlarge : Boolean = True);
<FM>Description<FN>
Resize the background (layer 0) to fit all layers (the image is sized, but its content is not stretched).
<TABLE>
<R> <H>Parameter</H> <H>Description</H> </R>
<R> <C><FC>SelectedOnly<FN></C> <C>The background will be cropped to fit selected layers only</C> </R>
<R> <C><FC>FillAlpha<FN></C> <C>Alpha value used to fill added regions (0: Fully Transparent - 255: Opaque)</C> </R>
<R> <C><FC>AllowReduce<FN></C> <C>Parts of the image can be cut so layers will align with image edges</C> </R>
<R> <C><FC>AllowEnlarge<FN></C> <C>Parts will be added to the image so layers are not beyond hte image area</C> </R>
</TABLE>
Note: The color of added background is specified by <A TImageEnView.Background>
<FM>Example<FC>
// Crop the background so that it matches the size of all layers
ImageEnView1.LayersCropBackground();
// Crop the background so that it matches the size of selected layers
ImageEnView1.LayersCropBackground( True );
// Crop the background to match layers, but do not cut any part of the image (new border area may be added)
ImageEnView1.LayersCropBackground( True, 255, False, True );
// Crop the background to match layers, but do not add to the image (some layers may be outside the image area)
ImageEnView1.LayersCropBackground( True, 255, True, False );
!!}
procedure TImageEnView.LayersCropBackground(SelectedOnly: Boolean = False; FillAlpha: Integer = 255; AllowReduce: Boolean = True; AllowEnlarge : Boolean = True);
var
layer0Rect, outLayersRect: TIERectangle;
addLeft, addTop, addRight, addBottom: Integer;
zoomX, zoomY: Double;
begin
if not ( AllowReduce or AllowEnlarge ) then
exit;
if Proc.AutoUndo and ( loAutoUndoChangesbyCode in fLayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_CropBackgroundToSelection ), ieuObjectsAndLayers, True, IEOP_CROP );
zoomX := 1;
zoomY := 1;
if Layers[0].Bitmap.Width > 1 then
zoomX := Layers[0].Width / Layers[0].Bitmap.Width;
if Layers[0].Bitmap.Height > 1 then
zoomY := Layers[0].Height / Layers[0].Bitmap.Height;
layer0Rect := IERectangle( 0, 0, Layers[0].Width, Layers[0].Height );
outLayersRect := LayersRect( SelectedOnly, True );
if ( outLayersRect.Width <= 0 ) or
( outLayersRect.Height <= 0 ) then
exit;
LockUpdate();
try
addLeft := Round(( layer0Rect.X - outLayersRect.X ) / zoomX );
addTop := Round(( layer0Rect.Y - outLayersRect.Y ) / zoomY );
addRight := Round(( outLayersRect.X + outLayersRect.Width - layer0Rect.X - layer0Rect.Width ) / zoomX );
addBottom := Round(( outLayersRect.Y + outLayersRect.Height - layer0Rect.Y - layer0Rect.Height ) / zoomY );
if not AllowReduce then
begin
// Don't adjust if within layer 0
if outLayersRect.X > 0 then
outLayersRect.X := 0;
if outLayersRect.Y > 0 then
outLayersRect.Y := 0;
// Don't allow resizing smaller
if addLeft < 0 then
addLeft := 0;
if addTop < 0 then
addTop := 0;
if addRight < 0 then
addRight := 0;
if addBottom < 0 then
addBottom := 0;
end;
if not AllowEnlarge then
begin
// Don't adjust if outside layer 0
if outLayersRect.X < 0 then
outLayersRect.X := 0;
if outLayersRect.Y < 0 then
outLayersRect.Y := 0;
// Don't allow resizing larger
if addLeft > 0 then
addLeft := 0;
if addTop > 0 then
addTop := 0;
if addRight > 0 then
addRight := 0;
if addBottom > 0 then
addBottom := 0;
end;
// Move all layers to the top left of layer 0
LayersRepositionAll( -1 * outLayersRect.X, -1 * outLayersRect.Y, SelectedOnly );
Layers[ 0 ].PosX := 0;
Layers[ 0 ].PosY := 0;
if ( Layers[0].Bitmap.Width + addLeft + addRight <= 0 ) or
( Layers[0].Bitmap.Height + addTop + addBottom <= 0 ) then
begin
// Background is completely outside the area of the layer
zoomX := 1;
zoomY := 1;
Layers[0].Bitmap.Clear;
Layers[0].Bitmap.Resize( outLayersRect.Width, outLayersRect.Height, Background, FillAlpha );
end
else
Layers[0].Bitmap.Resize( addLeft, addTop, addRight, addBottom, Background, FillAlpha );
Layers[0].Width := Round( Layers[0].Bitmap.Width * zoomX );
Layers[0].Height := Round( Layers[0].Bitmap.Height * zoomY );
finally
UnlockUpdate();
end;
end;
{!!
<FS>TImageEnView.LayersCopyToAlpha
<FM>Declaration<FC>
procedure LayersCopyToAlpha(DestLayer: Integer);
<FM>Description<FN>
Copies the current layer to the alpha channel of the specified destination layer.
This is useful to handle alpha channel of other bitmaps, applying the same image processing algorithms.
See also: <A TImageEnView.LayersCreateFromAlpha>
<FM>Example<FC>
ImageEnView1.IO.LoadFromFile('C:\image.jpg');
ImageEnView1.LayersCreateFromAlpha;
ImageEnView1.Proc.BumpMapping( 300, 300, 150, 150, 0, CreateRgb(255, 255, 255));
ImageEnView1.LayersCopyToAlpha(0);
ImageEnView1.LayersRemove(1);
!!}
procedure TImageEnView.LayersCopyToAlpha(DestLayer: Integer);
var
dst_w, dst_h: Integer;
begin
if fIEBitmapValid = False then
raise EIEException.create( 'Method only supported for image layers' );
dst_w := Layers[DestLayer].Bitmap.Width;
dst_h := Layers[DestLayer].Bitmap.Height;
with Layers[DestLayer].Bitmap.AlphaChannel do
begin
AssignImage( fIEBitmap );
Resize(dst_w, dst_h);
PixelFormat := ie8g;
end;
Update;
end;
{!!
<FS>TImageEnView.LayersCreateFromAlpha
<FM>Declaration<FC>
function LayersCreateFromAlpha: Integer;
<FM>Description<FN>
Creates a new <L TIEImageLayer>image layer</L> with the content of current bitmap's alpha channel.
This is useful to handle alpha channel of other bitmaps, applying the same image processing algorithms.
See also: <A TImageEnView.LayersCopyToAlpha>
<FM>Example<FC>
ImageEnView1.IO.LoadFromFile('C:\image.jpg');
ImageEnView1.LayersCreateFromAlpha;
ImageEnView1.Proc.BumpMapping( 300, 300, 150, 150, 0, CreateRgb(255, 255, 255));
ImageEnView1.LayersCopyToAlpha(0);
ImageEnView1.LayersRemove(1);
!!}
function TImageEnView.LayersCreateFromAlpha: Integer;
begin
if fIEBitmapValid = False then
raise EIEException.create( 'Method only supported for image layers' );
result := LayersAdd( fIEBitmap.AlphaChannel ); // Use LayersAdd so it saves Undo if needed
end;
procedure TImageEnView.TextEditorKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
inherited;
if assigned( fOnTextEditorKeyDown ) then
fOnTextEditorKeyDown( Sender, Key, Shift );
case Key of
VK_ESCAPE:
begin
Key := 0;
if Sender is TEdit then
SendMessage((Sender as TEdit).Handle, WM_UNDO, 0, 0);
LayersCancelEditor();
end;
VK_RETURN:
if not ( Sender is TIETextControl ) then
begin
Key := 0;
LayersCancelEditor();
end;
end;
end;
procedure TImageEnView.TextEditorOnChange(Sender: TObject);
var
tw, mm: integer;
begin
if Layers[ fEditingLayer ] is TIETextLayer then
begin
with TIETextLayer( Layers[ fEditingLayer ]) do
if AutoSize then
begin
Canvas.Font.Assign( fTextEditor.Font );
tw := Canvas.TextWidth( fTextEditor.Text );
mm := Canvas.TextWidth( 'M' );
fTextEditor.Width := tw;
tw := trunc((tw + mm) / fZoomD100X);
Width := tw;
Update();
end
end
else
if Layers[ fEditingLayer ] is TIELineLayer then
begin
with TIELineLayer( Layers[ fEditingLayer ]) do
begin
Canvas.Font.Assign( fTextEditor.Font );
fTextEditor.Width := Canvas.TextWidth( fTextEditor.Text );
end;
end;
end;
// Instead use: TIETextLayer.ActivateEditor(); or TIELineLayer.ActivateEditor();
procedure TImageEnView.LayersActivateTextEditor(LayerIdx: integer);
begin
if ( LayerIdx < 0 ) or ( LayerIdx >= fLayers.Count ) or (( Layers[ LayerIdx ].Kind in [ ielkText, ielkLine ]) = False ) then
exit;
fEditingLayer := LayerIdx;
if fTextEditor = nil then
fTextEditor := TIEEdit.Create( self );
with fTextEditor do
begin
Parent := self;
fTextEditor.BorderStyle := bsNone;
Ctl3D := false;
end;
UpdateTextEditor();
windows.SetFocus(fTextEditor.handle);
case syslocale.PriLangID of
LANG_GREEK:
Font.Charset := GREEK_CHARSET;
LANG_RUSSIAN:
Font.Charset := RUSSIAN_CHARSET;
end;
fTextEditor.OnKeyDown := TextEditorKeyDown;
fTextEditor.OnChange := TextEditorOnChange;
if Layers[ fEditingLayer ] is TIETextLayer then
with TIETextLayer( fLayers[ fEditingLayer ]) do
begin
fTextEditor.Text := Text;
fTextEditor.Font.assign( Font );
end
else
if Layers[ fEditingLayer ] is TIELineLayer then
with TIELineLayer( fLayers[ fEditingLayer ]) do
begin
fTextEditor.Text := LabelText;
fTextEditor.Font.assign( LabelFont );
end;
fTextEditor.Visible := True;
if assigned( fOnActivateTextEditor ) then
fOnActivateTextEditor( Self, fEditingLayer, fTextEditor );
end;
{!!
<FS>TImageEnView.LayersCancelEditor
<FM>Declaration<FC>
procedure LayersCancelEditor(SaveChanges: Boolean = True);
<FM>Description<FN>
Terminates any active text editors.
Users can edit the text of a <A TIETextLayer> or <A TIELineLayer> by double-clicking or selecting F2. They can then cancel it clicking "Esc" (or "Enter" to enact the change). <FC>LayersCancelEditor<FN> is the programmatic equivalent of clicking "Esc".
If <FC>SaveChanges<FN> is False, any text the user has specified will be lost. Set to true to enact their changes.
!!}
procedure TImageEnView.LayersCancelEditor(SaveChanges: Boolean = True);
var
isCanvasAvail: boolean;
begin
if fEditingLayer < 0 then
exit;
isCanvasAvail := not (csDestroying in ComponentState);
if SaveChanges and Proc.AutoUndo and ( loAutoUndoChangesbyUser in LayerOptions ) then
Proc.SaveUndo( IEMsg( IEMsg_EditLayerText ), ieuLayer, True, IEOP_LAYERPROPS );
if SaveChanges and ( Layers[ fEditingLayer ] is TIETextLayer ) then
TIETextLayer( fLayers[ fEditingLayer ]).Text := fTextEditor.Text
else
if SaveChanges and ( Layers[ fEditingLayer ] is TIELineLayer ) then
TIELineLayer( fLayers[ fEditingLayer ]).LabelText := fTextEditor.Text;
fTextEditor.Visible := false;
if assigned(fOnDeactivateTextEditor) then
fOnDeactivateTextEditor( Self, fEditingLayer, fTextEditor );
if isCanvasAvail then
SetFocus;
if SaveChanges then
DoLayerNotify( fEditingLayer, ielEdited );
fEditingLayer := -1;
Update();
end;
// updates text edit position
procedure TImageEnView.UpdateTextEditor();
begin
if fEditingLayer = -1 then
exit;
if Layers[ fEditingLayer ] is TIETextLayer then
with TIETextLayer( Layers[ fEditingLayer ]) do
begin
fTextEditor.Left := ClientAreaBox.Left;
fTextEditor.Top := ClientAreaBox.Top;
fTextEditor.Width := ClientAreaBox.Right - ClientAreaBox.Left;
fTextEditor.Height := ClientAreaBox.Bottom - ClientAreaBox.Top;
end
else
if Layers[ fEditingLayer ] is TIELineLayer then
with TIELineLayer( Layers[ fEditingLayer ]) do
begin
fTextEditor.Left := ClientAreaBox.Left + Round( LabelRect.Left * fZoomD100X );
fTextEditor.Top := ClientAreaBox.Top + Round( LabelRect.Top * fZoomD100X );
fTextEditor.Width := LabelRect.Right - LabelRect.Left; // don't scale
fTextEditor.Height := LabelRect.Bottom - LabelRect.Top;
end;
end;
{!!
<FS>TImageEnView.SetSelectionMarkOuterStyle
<FM>Declaration<FC>
procedure SetSelectionMarkOuterStyle(Alpha: Integer; Color: TColor);
<FM>Description<FN>
When <A TImageEnView.SelectionOptions> is <FC>iesoMarkOuter<FN>, this method specifies how the outer area will be drawn.
If Alpha = -1 (default) a gray grid is displayed.
If Alpha >= 0 and <= 255 a solid color is painted.
<FM>Example<FC>
// Areas outside the selection will be marked with red (with 50% transparency)
ImageEnView1.SelectionOptions := ImageEnView1.SelectionOptions+[iesoMarkOuter];
ImageEnView1.SetSelectionMarkOuterStyle(128, clRed);
!!}
procedure TImageEnView.SetSelectionMarkOuterStyle(Alpha: Integer; Color: TColor);
begin
fMarkOuterAlpha := Alpha;
fMarkOuterColor := Color;
end;
{!!
<FS>TImageEnView.SaveState
<FM>Declaration<FC>
procedure SaveState(const FileName: String);
procedure SaveState(Stream: TStream);
<FM>Description<FN>
Saves layers, selection and some other parameters such as Zoom and Scroll position.
See also: <A TImageEnView.LoadState> method.
!!}
procedure TImageEnView.SaveState(const FileName: String);
var
fs: TFileStream;
begin
fs := TFileStream.Create(FileName, fmCreate);
try
SaveState(fs);
finally
fs.Free;
end;
end;
const
ImageEnView_File_Version = 3;
ImageEnView_File_State : AnsiString = 'IMAGEENVIEWSTATE';
{
// Versions:
v3: Support for fDisplayGridKind
}
procedure TImageEnView.SaveState(Stream: TStream);
var
ver: Integer;
vi: Integer;
begin
// write magic string
Stream.Write(ImageEnView_File_State[1], length( ImageEnView_File_State ));
// write version
ver := ImageEnView_File_Version;
Stream.Write(ver, sizeof(integer));
// selection
SaveSelectionToStream(Stream);
// view and zoom
Stream.Write(fZoomX, sizeof(fZoomX));
Stream.Write(fZoomY, sizeof(fZoomY));
Stream.Write(fViewX, sizeof(fViewX));
Stream.Write(fViewY, sizeof(fViewY));
// others
Stream.Write(fLegacyBitmap, sizeof(boolean));
Stream.Write(fImageHorizAlignment, sizeof(TIEHAlign));
Stream.Write(fImageVertAlignment, sizeof(TIEVAlign));
Stream.Write(fZoomFilter, sizeof(TResampleFilter));
Stream.Write(fBackgroundStyle, sizeof(TIEBackgroundStyle));
Stream.Write(fDisplayGridKind, sizeof(TIEGridKind));
Stream.Write(fAutoFit, sizeof(boolean));
Stream.Write(fAutoStretch, sizeof(boolean));
Stream.Write(fAutoShrink, sizeof(boolean));
Stream.Write(fSelectionOptions, sizeof(TIESelectionOptions));
Stream.Write(fMouseInteract, sizeof(TIEMouseInteract));
Stream.Write(fScrollBarsAlwaysVisible, sizeof(boolean));
Stream.Write(fEnableAlphaChannel, sizeof(boolean));
Stream.Write(fScrollBars, sizeof(TIEScrollStyle));
// for debugging
vi := ClientWidth; Stream.Write(vi, sizeof(integer));
vi := ClientHeight; Stream.Write(vi, sizeof(integer));
vi := Width; Stream.Write(vi, sizeof(integer));
vi := Height; Stream.Write(vi, sizeof(integer));
// LAST: layers
SaveSelection;
LayersSaveToStream(Stream);
RestoreSelection;
end;
{!!
<FS>TImageEnView.LoadState
<FM>Declaration<FC>
procedure LoadState(const FileName: String);
procedure LoadState(Stream: TStream);
<FM>Description<FN>
Loads layers, selection and some other parameters such as Zoom and Scroll position.
See also: <A TImageEnView.SaveState> method.
!!}
procedure TImageEnView.LoadState(const FileName: String);
var
fs: TFileStream;
begin
fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
LoadState(fs);
finally
fs.Free;
end;
end;
procedure TImageEnView.LoadState(Stream: TStream);
var
ver: Integer;
magic: AnsiString;
r_ViewX, r_ViewY: Integer;
r_ZoomX, r_ZoomY: Double;
r_LegacyBitmap, r_Center: Boolean;
r_ZoomFilter: TResampleFilter;
r_BackgroundStyle: TIEBackgroundStyle;
r_Dummy: Boolean;
r_DisplayGrid: TIEGridKind;
r_ClientWidth, r_ClientHeight, r_Width, r_Height: Integer;
r_AutoFit, r_AutoStretch, r_AutoShrink: Boolean;
r_SelectionOptions: TIESelectionOptions;
r_MouseInteract: TIEMouseInteract;
r_ScrollBarsAlwaysVisible, r_EnableAlphaChannel: Boolean;
r_ScrollBars: TIEScrollStyle;
r_ImageHorizAlignment: TIEHAlign;
r_ImageVertAlignment: TIEVAlign;
begin
// read magic string
SetLength(magic, length( ImageEnView_File_State ));
Stream.Read(magic[1], length( ImageEnView_File_State ));
// read version
Stream.Read(ver, sizeof(integer));
if (magic = ImageEnView_File_State) and (ver > 0) then
begin
// selection
LoadSelectionFromStream(Stream);
SaveSelection;
// view and zoom
Stream.Read(r_ZoomX, sizeof(r_ZoomX));
Stream.Read(r_ZoomY, sizeof(r_ZoomY));
Stream.Read(r_ViewX, sizeof(r_ViewX));
Stream.Read(r_ViewY, sizeof(r_ViewY));
// others
Stream.Read(r_LegacyBitmap, sizeof(boolean));
if ver >= 2 then
begin
Stream.Read(r_ImageHorizAlignment, sizeof(TIEHAlign));
Stream.Read(r_ImageVertAlignment, sizeof(TIEVAlign));
end
else
begin
Stream.Read(r_Center, sizeof(boolean));
if r_Center then
begin
r_ImageHorizAlignment := iehCenter;
r_ImageVertAlignment := ievCenter;
end
else
begin
r_ImageHorizAlignment := iehLeft;
r_ImageVertAlignment := ievTop;
end;
end;
Stream.Read(r_ZoomFilter, sizeof(TResampleFilter));
Stream.Read(r_BackgroundStyle, sizeof(TIEBackgroundStyle));
if ver < 3 then
Stream.Read(r_Dummy, sizeof(boolean))
else
Stream.Read(r_DisplayGrid, sizeof(TIEGridKind));
Stream.Read(r_AutoFit, sizeof(boolean));
Stream.Read(r_AutoStretch, sizeof(boolean));
Stream.Read(r_AutoShrink, sizeof(boolean));
Stream.Read(r_SelectionOptions, sizeof(TIESelectionOptions));
Stream.Read(r_MouseInteract, sizeof(TIEMouseInteract));
Stream.Read(r_ScrollBarsAlwaysVisible, sizeof(boolean));
Stream.Read(r_EnableAlphaChannel, sizeof(boolean));
Stream.Read(r_ScrollBars, sizeof(TIEScrollStyle));
// for debugging
Stream.Read(r_ClientWidth, sizeof(integer));
Stream.Read(r_ClientHeight, sizeof(integer));
Stream.Read(r_Width, sizeof(integer));
Stream.Read(r_Height, sizeof(integer));
// LAST: layers
LayersLoadFromStream(Stream);
// Settings
LockPaint;
LegacyBitmap := r_LegacyBitmap;
ImageHorizAlignment := r_ImageHorizAlignment;
ImageVertAlignment := r_ImageVertAlignment;
ZoomX := r_ZoomX;
ZoomY := r_ZoomY;
ViewX := r_ViewX;
ViewY := r_ViewY;
ZoomFilter := r_ZoomFilter;
BackgroundStyle := r_BackgroundStyle;
DisplayGridKind := r_DisplayGrid;
AutoFit := r_AutoFit;
AutoStretch := r_AutoStretch;
AutoShrink := r_AutoShrink;
SelectionOptions := r_SelectionOptions;
MouseInteract := r_MouseInteract;
ScrollBarsAlwaysVisible := r_ScrollBarsAlwaysVisible;
EnableAlphaChannel := r_EnableAlphaChannel;
ScrollBars := r_ScrollBars;
RestoreSelection;
UnLockPaint;
end;
end;
{!!
<FS>TImageEnView.ResetState
<FM>Declaration<FC>
procedure ResetState();
<FM>Description<FN>
Resets some TImageEnView properties to their defaults.
ResetState will remove selection, empty layers, set input/output parameters to their defaults, reset Zoom and Scroll and clear the Undo buffer.
Leaves <A TImageEnView.LegacyBitmap> unchanged.
!!}
procedure TImageEnView.ResetState();
begin
Playing := False;
DeSelect();
LayersClear();
Blank();
IO.Params.SetDefaultParams();
Zoom := 100.0;
ViewX := 0;
ViewY := 0;
ImageHorizAlignment := iehCenter;
ImageVertAlignment := ievCenter;
Proc.ClearAllUndo();
Proc.ClearAllRedo();
if ( LegacyBitmap = false ) and ( IEBitmap <> nil ) then
IEBitmap.Location := ieMemory;
Update();
end;
procedure TImageEnView.UserInteractions_VirtualKey(VKey: Dword; KeyData: Dword; KeyDown: Boolean);
var
i: integer;
begin
for i := 0 to fUserInteractions.Count - 1 do
if (fUserInteractions[i] as TIEUserInteraction).Enabled then
(fUserInteractions[i] as TIEUserInteraction).VirtualKey(VKey, KeyData, KeyDown);
end;
{
KEYBOARD:
Move 1 pixel: Arrow keys
Move 10 pixels: Shift + arrow keys
Size 1 pixel: Ctrl + Arrow keys
Size 10 pixels: Ctrl + Shift + arrow keys
}
// If rectangular selection then it is moved, otherwise if layer > 0 active then it is moved
// VKey is one of VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN
procedure TImageEnView.MoveSelByKey(VKey: Word; ss: TShiftState);
var
sizing: Boolean;
moveBy: integer;
begin
if not VKey in [ VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN ] then
exit;
moveBy := 1;
if EnableShiftKey and ( ssShift in ss ) then
moveBy := 10;
sizing := ssCtrl in ss;
if fSel then
begin
// MOVE SELECTION
case VKey of
VK_LEFT : MoveSelection( -1 * moveBy, 0, sizing );
VK_RIGHT : MoveSelection( moveBy, 0, sizing );
VK_UP : MoveSelection( 0, -1 * moveBy, sizing );
VK_DOWN : MoveSelection( 0, moveBy, sizing );
end;
end
else
if fLayersCurrent > 0 then
begin
case VKey of
VK_LEFT : LayersRepositionAll( -1 * moveBy, 0, True, sizing );
VK_RIGHT : LayersRepositionAll( moveBy, 0, True, sizing );
VK_UP : LayersRepositionAll( 0, -1 * moveBy, True, sizing );
VK_DOWN : LayersRepositionAll( 0, moveBy, True, sizing );
end;
if Sizing then
DoLayerNotify( fLayersCurrent, ielResized )
else
DoLayerNotify( fLayersCurrent, ielMoved );
end;
end;
// this could not fired if the component is on TForm. Putting TImageEnView on TPanel it works.
procedure TImageEnView.CMWantSpecialKey(var Msg: TCMWantSpecialKey);
var
handled: Boolean;
begin
inherited;
handled := false;
if assigned(fOnSpecialKey) then
fOnSpecialKey(self, Msg.CharCode, KeyDataToShiftState(Msg.KeyData), handled);
if handled then
begin
msg.Result := 1;
exit;
end;
{$IFDEF OCXVERSION}
case msg.CharCode of
VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_PRIOR, VK_NEXT, VK_HOME, VK_END:
begin
msg.Result := 1;
KeyUp( Msg.CharCode, KeyDataToShiftState( Msg.KeyData ));
end;
end;
{$ELSE}
case msg.CharCode of
VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN:
if (iesoAllowMoveByKeyboard in fSelectionOptions) and IEIsKeyPressed(msg.CharCode) then
begin
msg.Result := 1;
MoveSelByKey( Msg.CharCode, KeyDataToShiftState( Msg.KeyData ));
if fShowRulers <> [] then
fRulerParams.HandleSpecialKey( Msg.CharCode, KeyDataToShiftState( Msg.KeyData ));
{$IFDEF IEDEBUG}
outputdebugstringA(pansichar(string( Format('CMWantSpecialKey %d %d', [Msg.Msg, Msg.CharCode]) )));
{$ENDIF}
end;
end;
{$ENDIF}
end;
procedure TImageEnView.CNKEYDOWN(var Message: TMessage);
begin
inherited;
UserInteractions_VirtualKey(Message.wParam, Message.lParam, true);
if assigned(fOnVirtualKey) then
fOnVirtualKey(self, Message.wParam, Message.lParam, true);
end;
procedure TImageEnView.CNKEYUP(var Message: TMessage);
begin
inherited;
UserInteractions_VirtualKey(Message.wParam, Message.lParam, false);
if assigned(fOnVirtualKey) then
fOnVirtualKey(self, Message.wParam, Message.lParam, false);
end;
procedure TImageEnView.WMSYSKEYDOWN(var Message: TMessage);
begin
inherited;
UserInteractions_VirtualKey(Message.wParam, Message.lParam, true);
if assigned(fOnVirtualKey) then
fOnVirtualKey(self, Message.wParam, Message.lParam, true);
end;
procedure TImageEnView.WMSYSKEYUP(var Message: TMessage);
begin
inherited;
UserInteractions_VirtualKey(Message.wParam, Message.lParam, false);
if assigned(fOnVirtualKey) then
fOnVirtualKey(self, Message.wParam, Message.lParam, false);
end;
{!!
<FS>TImageEnView.HighlightedPixelColor
<FM>Declaration<FC>
property HighlightedPixelColor: TColor;
<FM>Description<FN>
Colored box color.
<FM>See Also<FN>
- <A TImageEnView.HighlightedPixel>
!!}
procedure TImageEnView.SetHighlightedPixelColor(c: TColor);
begin
fHighlightedPixelColor := c;
end;
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 6.2.2 (2015-12-15)
function TImageEnView.GetDPIX() : integer;
begin
Result := GetImageEnIO.Params.DpiX;
end;
function TImageEnView.GetDPIY() : integer;
begin
Result := GetImageEnIO.Params.DpiY;
end;
procedure TImageEnView.SetDPIX(dpiX: integer);
begin
GetImageEnIO.Params.DpiX := dpiX;
end;
procedure TImageEnView.SetDPIY(dpiY: integer);
begin
GetImageEnIO.Params.DpiY := dpiY;
end;
procedure TImageEnView.SetDPI(dpiX, dpiY: integer);
begin
GetImageEnIO.Params.DpiX := dpiX;
GetImageEnIO.Params.DpiY := dpiY;
end;
{$endif}
{$ifdef IEIncludeDeprecatedInV6}
// Deprecated in 7.0.0 (8/11/2016)
function TImageEnView.GetLayersRotationDelayFilterOnPreview(): boolean;
begin
Result := fLayersFastDrawing = iefDelayed;
end;
procedure TImageEnView.SetLayersRotationDelayFilterOnPreview(value: Boolean);
begin
if value then
fLayersFastDrawing := iefDelayed
else
fLayersFastDrawing := iefNormal;
end;
{$endif}
{!!
<FS>TImageEnView.OnRulerClick
<FM>Declaration<FC>
property OnRulerClick: <A TRulerClickEvent>;
<FM>Description<FN>
Occurs when the user clicks on a <A TImageEnView.ShowRulers>ruler</L>.
<FM>Example<FC>
procedure TForm1.ImageEnView1RulerClick(Sender: TObject; RulerDir: TRulerDir; Ps: double);
begin
case RulerDir of
rdHorizontal : ShowMessage( 'Horizontal ruler clicked at: ' + FloatToStr( Ps ));
rdVertical : ShowMessage( 'Vertical ruler clicked at: ' + FloatToStr( Ps ));
end;
end;
!!}
function TImageEnView.GetOnRulerClick: TRulerClickEvent;
begin
result := fRulerParams.OnRulerClick;
end;
procedure TImageEnView.SetOnRulerClick(const Value: TRulerClickEvent);
begin
fRulerParams.OnRulerClick := Value;
end;
{!!
<FS>TImageEnView.OnRulerGripClick
<FM>Declaration<FC>
property OnRulerGripClick: <A TRulerGripClickEvent>;
<FM>Description<FN>
Occurs when the user clicks a grip on a <A TImageEnView.ShowRulers>ruler</L>.
<FM>Example<FC>
procedure TForm1.ImageEnView1RulerGripClick(Sender: TObject; RulerDir: TRulerDir; Grip: integer; GripPos: Double);
begin
case RulerDir of
rdHorizontal : ShowMessage( format( 'Grip %d clicked on horizontal ruler', [ Grip ]));
rdVertical : ShowMessage( format( 'Grip %d clicked on vertical ruler', [ Grip ]));
end;
end;
!!}
function TImageEnView.GetOnRulerGripClick: TRulerGripClickEvent;
begin
result := fRulerParams.OnRulerGripClick;
end;
procedure TImageEnView.SetOnRulerGripClick(const Value: TRulerGripClickEvent);
begin
fRulerParams.OnRulerGripClick := Value;
end;
{!!
<FS>TImageEnView.OnRulerGripDblClick
<FM>Declaration<FC>
property OnRulerGripDblClick: <A TRulerGripClickEvent>;
<FM>Description<FN>
Occurs when the user double-clicks a grip on a <A TImageEnView.ShowRulers>ruler</L>.
<FM>Example<FC>
procedure TForm1.ImageEnView1RulerGripDblClick(Sender: TObject; RulerDir: TRulerDir; Grip: integer; GripPos: Double);
begin
case RulerDir of
rdHorizontal : ShowMessage( format( 'Grip %d double-clicked on horizontal ruler', [ Grip ]));
rdVertical : ShowMessage( format( 'Grip %d double-clicked on vertical ruler', [ Grip ]));
end;
end;
!!}
function TImageEnView.GetOnRulerGripDblClick: TRulerGripClickEvent;
begin
result := fRulerParams.OnRulerGripDblClick;
end;
procedure TImageEnView.SetOnRulerGripDblClick(const Value: TRulerGripClickEvent);
begin
fRulerParams.OnRulerGripDblClick := Value;
end;
{!!
<FS>TImageEnView.OnRulerGripPosChange
<FM>Declaration<FC>
property OnRulerGripPosChange: <A TRulerGripPosChangeEvent>;
<FM>Description<FN>
Occurs when a grip is moved on a <A TImageEnView.ShowRulers>ruler</L>.
<FM>Example<FC>
procedure TForm1.ImageEnView1RulerGripPosChange(Sender: TObject; RulerDir: TRulerDir; Grip: integer; NewPos: Double);
begin
case RulerDir of
rdHorizontal : ShowMessage( format( 'Grip %d moved on horizontal ruler', [ Grip ]));
rdVertical : ShowMessage( format( 'Grip %d moved on vertical ruler', [ Grip ]));
end;
end;
!!}
function TImageEnView.GetOnRulerGripPosChange: TRulerGripPosChangeEvent;
begin
result := fRulerParams.OnRulerGripPosChange;
end;
procedure TImageEnView.SetOnRulerGripPosChange(const Value: TRulerGripPosChangeEvent);
begin
fRulerParams.OnRulerGripPosChange := Value;
end;
procedure TImageEnView.WMGestureNotify(var Msg: TIEWMGestureNotify);
var
c: integer;
gc: array of TIEGESTURECONFIG;
begin
inherited;
if fGestures.Enabled and IEHasGestures() then
begin
IEUnregisterTouchWindow(Handle);
if fGestures.fPan.Enabled or fGestures.fLayerMove.Enabled then
begin
c := length(gc);
SetLength(gc, c + 1);
gc[c].dwID := IEGID_PAN;
gc[c].dwWant := IEGC_PAN or IEGC_PAN_WITH_INERTIA;
if fGestures.fPan.PanWithSingleFingerVertically then
gc[c].dwWant := gc[c].dwWant or IEGC_PAN_WITH_SINGLE_FINGER_VERTICALLY;
if fGestures.fPan.PanWithSingleFingerHorizontally then
gc[c].dwWant := gc[c].dwWant or IEGC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
gc[c].dwBlock := IEGC_PAN_WITH_GUTTER;
end;
if fGestures.fZoom.Enabled then
begin
c := length(gc);
SetLength(gc, c + 1);
gc[c].dwID := IEGID_ZOOM;
gc[c].dwWant := IEGC_ALLGESTURES;
gc[c].dwBlock := 0;
end;
if fGestures.fLayerRotate.Enabled then
begin
c := length(gc);
SetLength(gc, c + 1);
gc[c].dwID := IEGID_ROTATE;
gc[c].dwWant := IEGC_ROTATE;
gc[c].dwBlock := 0;
end;
IESetGestureConfig(Handle, 0, length(gc), @gc[0], sizeof(TIEGESTURECONFIG));
end;
end;
function GID_ROTATE_ANGLE_FROM_ARGUMENT(arg: integer): double;
begin
result := ((arg / 65535.0) * 4.0 * 3.14159265) - 2.0 * 3.14159265;
end;
procedure TImageEnView.DoImageEnGestureEvent(const GInfo: TIEGESTUREINFO; var Handled: boolean);
var
Flags: TIEGestureFlags;
ID: TIEGestureID;
begin
if assigned(fOnImageEnGesture) then
begin
Flags := [];
if (IEGF_BEGIN and GInfo.dwFlags) <> 0 then
Flags := Flags + [iegfBegin];
if (IEGF_INERTIA and GInfo.dwFlags) <> 0 then
Flags := Flags + [iegfInertia];
if (IEGF_END and GInfo.dwFlags) <> 0 then
Flags := Flags + [iegfEnd];
ID := TIEGestureID(GInfo.dwID - 1);
fOnImageEnGesture(self, Flags, ID, GInfo.ptsLocation,
GInfo.ullArguments and $FFFFFFFF,
(GInfo.ullArguments shr 32) and $FFFF,
(GInfo.ullArguments shr 48) and $FFFF,
(GInfo.ullArguments shr 32),
Handled);
end;
end;
function TImageEnView.PerformRotateSnap(Value: double): double;
var
i: integer;
begin
result := Value;
if fGestures.fLayerRotate.SnapValues then
for i := 0 to 8 do
if result >= 0 then
begin
if (abs(45 * i - IEGetReferenceAngle(result)) <= fGestures.fLayerRotate.SnapDelta) then
result := 45 * i;
end
else
begin
if (abs(-45 * i - IEGetReferenceAngle(result)) <= fGestures.fLayerRotate.SnapDelta) then
result := - 45 * i;
end;
end;
function TImageEnView.PerformLayerMoveSnap(Value: double): double;
begin
result := Value;
if fGestures.fLayerMove.SnapValues then
begin
if abs(result) <= fGestures.fLayerMove.SnapDelta then
result := 0.0
end;
end;
function TImageEnView.PerformZoomSnap(Value: double): double;
begin
result := Value;
if fGestures.fZoom.SnapValues then
begin
if abs(result - 100.0) <= fGestures.fZoom.SnapDelta then
result := 100;
end;
end;
procedure TImageEnView.WMGesture(var Msg: TMessage);
var
gInfo: TIEGESTUREINFO;
value: integer;
v: double;
isBegin: boolean;
isInertia: boolean;
handled: boolean;
begin
Msg.Result := 1; // not handled
if fGestures.Enabled and IEHasGestures() then
begin
FillChar(gInfo, sizeof(gInfo), 0);
gInfo.cbSize := sizeof(gInfo);
if IEGetGestureInfo(Msg.LParam, @gInfo) then
begin
try
handled := false;
DoImageEnGestureEvent(gInfo, handled);
if not handled then
begin
value := gInfo.ullArguments and $FFFFFFFF;
isBegin := (IEGF_BEGIN and gInfo.dwFlags) <> 0;
isInertia := (IEGF_INERTIA and gInfo.dwFlags) <> 0;
case gInfo.dwID of
IEGID_ZOOM:
begin
// begin zoom
if isBegin then
begin
fGestureStartValue := value;
fGestureBaseZoom := Zoom;
end
// perform zoom
else
if fGestures.fZoom.Enabled and (not isInertia or fGestures.fZoom.Inertia) then
begin
v := fGestureBaseZoom * (value / fGestureStartValue);
v := dmax(v, fGestures.fZoom.Min);
v := dmin(v, fGestures.fZoom.Max);
v := v * fGestures.fZoom.Multiplier;
Zoom := PerformZoomSnap(v);
Msg.Result := 0; // handled
end
end;
IEGID_PAN:
begin
// begin pan
if isBegin then
begin
fGestureStartX := gInfo.ptsLocation.x;
fGestureStartY := gInfo.ptsLocation.y;
fGestureBaseViewX := ViewX;
fGestureBaseViewY := ViewY;
fGestureBasePosX := CurrentLayer.PosX;
fGestureBasePosY := CurrentLayer.PosY;
end
// perform pan
else
if fGestures.fPan.Enabled and (not isInertia or fGestures.fPan.Inertia) then
begin
v := fGestureBaseViewX + (fGestureStartX - gInfo.ptsLocation.x);
v := dmax(v, fGestures.fPan.BoundingBox.Left);
v := dmin(v, fGestures.fPan.BoundingBox.Right);
v := v * fGestures.fPan.Multiplier;
ViewX := trunc(v);
v := fGestureBaseViewY + (fGestureStartY - gInfo.ptsLocation.y);
v := dmax(v, fGestures.fPan.BoundingBox.Top);
v := dmin(v, fGestures.fPan.BoundingBox.Bottom);
v := v * fGestures.fPan.Multiplier;
ViewY := trunc(v);
Msg.Result := 0; // handled
end
// perform layer move
else
if fGestures.fLayerMove.Enabled and (not isInertia or fGestures.fLayerMove.Inertia) then
begin
v := fGestureBasePosX - trunc((fGestureStartX - gInfo.ptsLocation.x) / (ZoomX / 100));
v := dmax(v, fGestures.fLayerMove.BoundingBox.Left);
v := dmin(v, fGestures.fLayerMove.BoundingBox.Right);
v := v * fGestures.fLayerMove.Multiplier;
CurrentLayer.PosX := trunc(PerformLayerMoveSnap(v));
v := fGestureBasePosY - trunc((fGestureStartY - gInfo.ptsLocation.y) / (ZoomY / 100));
v := dmax(v, fGestures.fLayerMove.BoundingBox.Top);
v := dmin(v, fGestures.fLayerMove.BoundingBox.Bottom);
v := v * fGestures.fLayerMove.Multiplier;
CurrentLayer.PosY := trunc(PerformLayerMoveSnap(v));
Update();
Msg.Result := 0; // handled
end;
end;
IEGID_ROTATE:
begin
// begin rotate
if isBegin then
begin
fGestureBaseRotate := CurrentLayer.Rotate;
end
// perform layer rotate
else
if fGestures.fLayerRotate.Enabled and (not isInertia or fGestures.fLayerRotate.Inertia) then
begin
v := fGestureBaseRotate + GID_ROTATE_ANGLE_FROM_ARGUMENT(value) * 180 / PI;
v := dmax(v, fGestures.fLayerRotate.Min);
v := dmin(v, fGestures.fLayerRotate.Max);
v := v * fGestures.fLayerRotate.Multiplier;
CurrentLayer.Rotate := PerformRotateSnap(v);
Update();
Msg.Result := 0; // handled
end;
end;
end;
end
else
begin
// handled by user (in OnImageEnGesture event)
Msg.Result := 0; // handled
end;
finally
if Msg.Result = 0 then
IECloseGestureInfoHandle(Msg.LParam);
end;
end
end;
if Msg.Result <> 0 then
inherited;
end;
constructor TIEViewerGestures.Create();
begin
inherited;
fPan := TIEGesturePanOptions.Create();
fZoom := TIEGestureZoomOptions.Create();
fLayerRotate := TIEGestureLayerRotateOptions.Create();
fLayerMove := TIEGestureLayerMoveOptions.Create();
// Pan defaults
fPan.Enabled := false;
fPan.Inertia := true;
fPan.BoundingBox := Rect(Low(integer), Low(integer), High(integer), High(integer));
fPan.Multiplier := 1.0;
fPan.SnapValues := false;
fPan.SnapDelta := 0.0;
fPan.PanWithSingleFingerVertically := True;
fPan.PanWithSingleFingerHorizontally := True;
fPan.AddDisables(fLayerMove);
// Zoom defaults
fZoom.Enabled := false;
fZoom.Inertia := true;
fZoom.Min := 1.0;
fZoom.Max := 8000.0;
fZoom.Multiplier := 1.0;
fZoom.SnapValues := true;
fZoom.SnapDelta := 5.0;
// LayerRotate defaults
fLayerRotate.Enabled := false;
fLayerRotate.Inertia := false;
fLayerRotate.Min := - MaxDouble;
fLayerRotate.Max := MaxDouble;
fLayerRotate.Multiplier := 1.0;
fLayerRotate.SnapValues := true;
fLayerRotate.SnapDelta := 5.0;
// LayerMove defaults
fLayerMove.Enabled := false;
fLayerMove.Inertia := false;
fLayerMove.BoundingBox := Rect(Low(integer), Low(integer), High(integer), High(integer));
fLayerMove.Multiplier := 1.0;
fLayerMove.SnapValues := true;
fLayerMove.SnapDelta := 5.0;
fLayerMove.AddDisables(fPan);
end;
destructor TIEViewerGestures.Destroy();
begin
fPan.Free();
fZoom.Free();
fLayerRotate.Free();
fLayerMove.Free();
inherited;
end;
function TIEViewerGestures.GetEnabled(): boolean;
begin
result := fPan.Enabled or fZoom.Enabled or fLayerRotate.Enabled or fLayerMove.Enabled;
end;
procedure TIEGestureOptions.SetEnabled(Value: boolean);
var
i: integer;
begin
fEnabled := Value;
if Value then
for i := 0 to high(fDisables) do
fDisables[i].Enabled := false;
end;
procedure TIEGestureOptions.AddDisables(Value: TIEGestureOptions);
var
l: integer;
begin
l := length(fDisables);
SetLength(fDisables, l + 1);
fDisables[l] := Value;
end;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TIEUserInteraction
constructor TIEUserInteraction.Create(Parent: TImageEnView);
begin
inherited Create;
fParent := Parent;
fEnabled := false;
end;
destructor TIEUserInteraction.Destroy();
begin
inherited;
end;
function TIEUserInteraction.GetParent(): TImageEnView;
begin
result := fParent;
end;
procedure TIEUserInteraction.SetTempCursor(Value: TCursor);
begin
fParent.SetTempCursor(Value);
end;
procedure TIEUserInteraction.RestoreCursor();
begin
fParent.RestoreCursor();
end;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TIECropToolInteraction
constructor TIECropToolInteraction.Create(Parent: TImageEnView);
begin
inherited Create(Parent);
fState := iectisUNSELECTED;
fRotation := 0;
fLockAspectRatio := -1;
fLockWidth := 0;
fLockHeight := 0;
fOptions := [ iecoAllowResizing, iecoAllowRotating, iecoAllowMoving ];
SetLength(fScreenPolygon, 4);
SetLength(fBitmapPolygon, 4);
SetLength(fRotatedPolygon, 4);
// crop processing properties
fAntialiasMode := ierFast;
fMode := iectmRECTANGLE;
// visual appearance
fDrawGuides := true;
fGripSize := 8;
end;
{!!
<FS>TIECropToolInteraction.SetBitmapPolygon
<FM>Declaration<FC>
procedure SetBitmapPolygon(Rect: TRect);
<FM>Description<FN>
Sets the area of the image selected for cropping. This is the programatic equivalent of the user dragging the grips of the crop tool selection.
Note: You must call <FC>TImageEnView.Invalidate<FN> if you change this property when the crop selection is visible
<FM>Example<FC>
// Select 80% of image
ARect := Rect( MulDiv( ImageEnView1.IEBitmap.Width, 10, 100 ),
MulDiv( ImageEnView1.IEBitmap.Height, 10, 100 ),
MulDiv( ImageEnView1.IEBitmap.Width, 90, 100 ),
MulDiv( ImageEnView1.IEBitmap.Height, 90, 100 ));
ImageEnView1.CropToolInteraction.SetBitmapPolygon( ARect );
ImageEnView1.Invalidate();
<FM>See Also<FC>
- <A TIECropToolInteraction.BitmapPolygon>
- <A TIECropToolInteraction.Rotation>
!!}
procedure TIECropToolInteraction.SetBitmapPolygon(Rect: TRect);
begin
// TL
fBitmapPolygon[0] := IE2DPoint(Rect.Left, Rect.Top);
// TR
fBitmapPolygon[1] := IE2DPoint(Rect.Right, Rect.Top);
// BR
fBitmapPolygon[2] := IE2DPoint(Rect.Right, Rect.Bottom);
// BL
fBitmapPolygon[3] := IE2DPoint(Rect.Left, Rect.Bottom);
fState := iectisSELECTED;
end;
{!!
<FS>TIECropToolInteraction.Mode
<FM>Declaration<FC>
property Mode: <A TIECropToolInteractionMode>;
<FM>Description<FN>
Specifies the shape of cropping area.
<TABLE>
<R> <H>Value</H> <H>Description</H> </R>
<R> <C><FC>iectmRECTANGLE<FN></C> <C>The cropping area is rectangular (for standard cropping)</C> </R>
<R> <C><FC>iectmPERSPECTIVE<FN></C> <C>All corners of the cropping area can be adjusted as a quadrilateral to allow perspective distortion, i.e. the selected quadrilateral will be scaled to a rectangle when cropping is enacted.</C> </R>
</TABLE>
Note: You must call <FC>TImageEnView.Invalidate<FN> if you change this property when the crop selection is visible
Default: iectmRECTANGLE
<IMG help_images\Perspective.jpg>
<IMG help_images\Perspective2.jpg>
<FM>Example<FC>
// Enable perspective correction tool
ImageEnView1.CropToolInteraction.Mode := iectmPERSPECTIVE;
ImageEnView1.Invalidate();
!!}
procedure TIECropToolInteraction.SetMode(const Value: TIECropToolInteractionMode);
var
Rect: TRect;
begin
if ( fMode = iectmPERSPECTIVE ) and ( Value = iectmRECTANGLE ) then
begin
// Reset any perspetive in selection
Rect.Left := Round( dmax( fBitmapPolygon[ 0 ].X, fBitmapPolygon[ 3 ].X ));
Rect.Top := Round( dmax( fBitmapPolygon[ 0 ].Y, fBitmapPolygon[ 1 ].Y ));
Rect.Right := Round( dmax( fBitmapPolygon[ 1 ].X, fBitmapPolygon[ 2 ].X ));
Rect.Bottom := Round( dmax( fBitmapPolygon[ 2 ].Y, fBitmapPolygon[ 3 ].Y ));
SetBitmapPolygon( Rect );
end;
fMode := Value;
end;
{!!
<FS>TIECropToolInteraction.Selected
<FM>Declaration<FC>
property TIECropToolInteraction.Selected: Boolean;
<FM>Description<FN>
Returns true if a selection is active.
!!}
function TIECropToolInteraction.GetSelected(): Boolean;
begin
Result := fState = iectisSELECTED;
end;
procedure TIECropToolInteraction.SetSelected(const Value: Boolean);
begin
if ( fState = iectisSELECTED ) and ( value = False ) then
Cancel();
end;
{!!
<FS>TIECropToolInteraction.Cancel
<FM>Declaration<FC>
procedure TIECropToolInteraction.Cancel();
<FM>Description<FN>
Clears the crop selection. This is the same as the user clicking the "Esc" key.
<FM>Example<FC>
// "Cancel Crop" button clicked
procedure TMainForm.btnCancelCropClick(Sender: TObject);
begin
ImageEnView1.CropToolInteraction.Cancel();
end;
<FM>See Also<FC>
- <A TIECropToolInteraction.Crop>
!!}
procedure TIECropToolInteraction.Cancel();
begin
fState := iectisUNSELECTED;
fRotation := 0;
Refresh();
RestoreCursor;
end;
{!!
<FS>TIECropToolInteraction.Crop
<FM>Declaration<FC>
procedure TIECropToolInteraction.Crop();
<FM>Description<FN>
Crop the image to the selection. This is the same as the user clicking the "Enter" key.
<FM>Example<FC>
// "Apply Crop" button clicked
procedure TMainForm.btnApplyCropClick(Sender: TObject);
begin
ImageEnView1.CropToolInteraction.Crop();
end;
<FM>See Also<FC>
- <A TIECropToolInteraction.Cancel>
!!}
procedure TIECropToolInteraction.Crop();
begin
if fState = iectisUNSELECTED then
exit;
case fMode of
iectmRECTANGLE:
GetParent().Proc.Crop(Rect(trunc(fBitmapPolygon[0].X), trunc(fBitmapPolygon[0].Y), trunc(fBitmapPolygon[2].X), trunc(fBitmapPolygon[2].Y)), fRotation, fAntialiasMode);
iectmPERSPECTIVE:
GetParent().Proc.Crop(GetRotatedBitmapPolygon());
end;
Cancel();
end;
procedure TIECropToolInteraction.Refresh();
begin
GetParent().Paint();
Draw(GetParent().GetCanvas());
end;
// x,y: screen coordinates
function TIECropToolInteraction.GetGrip(x, y: integer): TIECropToolInteractionState;
var
g: TIECropToolInteractionState;
begin
result := iectisUNSELECTED;
if fState <> iectisUNSELECTED then
begin
// try rotated grips
for g := iectisTOP_LEFT_SIZE to iectisLEFT_SIZE do
if IEPointInRect(x, y, fGripRects[g][0].X, fGripRects[g][0].Y, fGripRects[g][1].X, fGripRects[g][1].Y) then
begin
if iecoAllowResizing in fOptions then
result := g;
exit;
end;
// try rotated polygon
if IEDISPointInPoly(x, y, fRotatedPolygon) then
begin
if iecoAllowMoving in fOptions then
result := iectisMOVING;
exit;
end;
// default is iectisROTATE (out of polygon area and grips)
if iecoAllowRotating in fOptions then
result := iectisROTATE;
end;
end;
function TIECropToolInteraction.ScreenToBitmap(const P: TIE2DPoint): TIE2DPoint;
var
lyr: TIELayer;
zx, zy: double;
begin
lyr := GetParent().CurrentLayer;
zx := GetParent().ZoomX / 100.0;
zy := GetParent().ZoomY / 100.0;
result.X := 1.0 * (((P.X - lyr.PosX * zx) - lyr.DrawingInfo.XDst) / zx + lyr.DrawingInfo.XSrc) * lyr.OriginalWidth / lyr.Width;
result.Y := 1.0 * (((P.Y - lyr.PosY * zy) - lyr.DrawingInfo.YDst) / zy + lyr.DrawingInfo.YSrc) * lyr.OriginalHeight / lyr.Height;
end;
function TIECropToolInteraction.BitmapToScreen(const P: TIE2DPoint): TIE2DPoint;
var
lyr: TIELayer;
zx, zy: double;
begin
lyr := GetParent().CurrentLayer;
zx := GetParent().ZoomX / 100.0;
zy := GetParent().ZoomY / 100.0;
result.X := lyr.DrawingInfo.XDst + (P.X * lyr.Width / lyr.OriginalWidth - GetParent().ViewX / zx) * zx + lyr.PosX * zx;
result.Y := lyr.DrawingInfo.YDst + (P.Y * lyr.Height / lyr.OriginalHeight - GetParent().ViewY / zy) * zy + lyr.PosY * zy;
end;
function TIECropToolInteraction.GetRotatedBitmapPolygon(): TIE2DPointArray;
var
i: integer;
begin
SetLength(result, length(fRotatedPolygon));
for i := 0 to high(fRotatedPolygon) do
result[i] := ScreenToBitmap(fRotatedPolygon[i]);
end;
procedure TIECropToolInteraction.UpdateBitmapPolygon();
var
i: integer;
begin
for i := 0 to high(fScreenPolygon) do
fBitmapPolygon[i] := ScreenToBitmap(fScreenPolygon[i]);
end;
procedure TIECropToolInteraction.UpdateScreenPolygon();
var
i: integer;
begin
for i := 0 to high(fScreenPolygon) do
fScreenPolygon[i] := BitmapToScreen(fBitmapPolygon[i]);
end;
function TIECropToolInteraction.MouseDownExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean;
var
grip: TIECropToolInteractionState;
begin
if Button = mbLeft then
begin
fMouseDownCoords := Point(X, Y);
fMouseDownScreenPolygon := Copy(fScreenPolygon, 0, length(fScreenPolygon));
fMouseDownRotation := fRotation;
fMouseDownRotatedPolygon := Copy(fRotatedPolygon, 0, length(fRotatedPolygon));
grip := GetGrip(X, Y);
// Locked aspect ratio enabled in parent
if fLockAspectRatio > 0 then
fAspectRatio := fLockAspectRatio;
// If user has no selection then allow creation of one
if fState = iectisUNSELECTED then
begin
// create selection
fState := iectisCREATING;
fScreenPolygon[0] := IE2DPoint(X, Y);
// initial aspect ratio from bitmap instead of selection
if fLockAspectRatio <= 0 then
fAspectRatio := GetParent().CurrentLayer.OriginalWidth / GetParent().CurrentLayer.OriginalHeight;
end
else
if grip <> iectisUNSELECTED then
begin
// resize, move, rotate
fState := grip;
end;
result := true;
end
else
result := false;
end;
procedure TIECropToolInteraction.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
{ // }
end;
function TIECropToolInteraction.MouseMoveExclusive(Shift: TShiftState; X, Y: Integer; Captured: boolean): boolean;
{}
procedure _UpdatePolySize(HAlign: TIEHAlign; VAlign: TIEVAlign);
var
dx, dy: Integer;
begin
if ( fLockAspectRatio <= 0 ) or ( iecoSizeLocksAreMinimums in fOptions ) then
begin
dx := round( fLockWidth * GetParent().ZoomX / 100.0 );
dy := round( fLockHeight * GetParent().ZoomY / 100.0 );
if fLockWidth > 0 then
if (( iecoSizeLocksAreMinimums in fOptions ) = False ) or ( abs( fScreenPolygon[2].X - fScreenPolygon[0].X ) < dx ) then
case HAlign of
iehLeft : fScreenPolygon[2].X := fScreenPolygon[0].X + dx;
iehRight : fScreenPolygon[0].X := fScreenPolygon[2].X - dx;
end;
if fLockHeight > 0 then
if (( iecoSizeLocksAreMinimums in fOptions ) = False ) or ( abs( fScreenPolygon[2].Y - fScreenPolygon[0].Y ) < dy ) then
case VAlign of
ievTop : fScreenPolygon[2].Y := fScreenPolygon[0].Y + dy;
ievBottom : fScreenPolygon[0].Y := fScreenPolygon[2].Y - dy;
end;
fScreenPolygon[1] := IE2DPoint( fScreenPolygon[2].X, fScreenPolygon[0].Y );
fScreenPolygon[3] := IE2DPoint( fScreenPolygon[0].X, fScreenPolygon[2].Y );
end;
end;
{}
const
GRIPCURSORBASEROTATION: array [iectisTOP_LEFT_SIZE..iectisLEFT_SIZE] of integer = (-45, 0, 45, 90, -45, 0, 45, 90);
var
cx, cy: double;
offX, offY: double;
angle, angleStart: double;
grip: TIECropToolInteractionState;
rotOffset: double;
doRefresh: boolean;
i: integer;
bLockAspect: Boolean;
HAlign: TIEHAlign;
VAlign: TIEVAlign;
begin
if fState = iectisUNSELECTED then
begin
result := false;
exit;
end;
if not Captured then
begin
// set mouse pointer
grip := GetGrip(X, Y);
case grip of
iectisTOP_LEFT_SIZE, iectisBOTTOM_RIGHT_SIZE, iectisTOP_RIGHT_SIZE, iectisBOTTOM_LEFT_SIZE,
iectisRIGHT_SIZE, iectisLEFT_SIZE, iectisTOP_SIZE, iectisBOTTOM_SIZE:
begin
Screen.Cursors[IEENTRYFORVARIABLECURSOR] := IECreateRotatedCursor(13, -fRotation + GRIPCURSORBASEROTATION[grip]);
SetTempCursor(IEENTRYFORVARIABLECURSOR);
end;
iectisMOVING:
SetTempCursor(crIESizeAll);
iectisROTATE:
begin
cx := (fScreenPolygon[0].X + fScreenPolygon[2].X) / 2.0;
cy := (fScreenPolygon[0].Y + fScreenPolygon[2].Y) / 2.0;
if (X < cx) and (Y < cy) then
SetTempCursor(crIERotateSE)
else
if (X < cx) and (Y > cy) then
SetTempCursor(crIERotateNE)
else
if (X > cx) and (Y < cy) then
SetTempCursor(crIERotateSW)
else
if (X > cx) and (Y > cy) then
SetTempCursor(crIERotateNW);
end;
else RestoreCursor();
end;
end;
if Captured then
begin
doRefresh := true;
bLockAspect := (( fMode <> iectmPERSPECTIVE ) and ( fLockAspectRatio > 0 )) or
( IEIsKeyPressed( VK_MENU { ALT } )) or
( GetParent().ForceALTkey );
case fState of
iectisCREATING:
begin
// creating
fScreenPolygon[1] := IE2DPoint(X, fScreenPolygon[0].Y);
fScreenPolygon[2] := IE2DPoint(X, Y);
fScreenPolygon[3] := IE2DPoint(fScreenPolygon[0].X, Y);
HAlign := iehLeft;
if X < fScreenPolygon[0].X then
HAlign := iehRight;
VAlign := ievTop;
if Y < fScreenPolygon[0].Y then
VAlign := ievBottom;
_UpdatePolySize( HAlign, VAlign );
if bLockAspect then
AdjustAspectRatio(fScreenPolygon, iectisBOTTOM_RIGHT_SIZE);
UpdateBitmapPolygon();
end;
iectisTOP_SIZE:
begin
// top resize
IEOrthogonalTranslate(fMouseDownRotatedPolygon[0], fMouseDownRotatedPolygon[1], IE2DPoint(X, Y), offX, offY);
fScreenPolygon[0] := IE2DPoint(fMouseDownRotatedPolygon[0].X + offX, fMouseDownRotatedPolygon[0].Y + offY);
fScreenPolygon[1] := IE2DPoint(fMouseDownRotatedPolygon[1].X + offX, fMouseDownRotatedPolygon[1].Y + offY);
fScreenPolygon[2] := fMouseDownRotatedPolygon[2];
fScreenPolygon[3] := fMouseDownRotatedPolygon[3];
IEDRotatePoints(fScreenPolygon, -fRotation);
_UpdatePolySize( iehCenter, ievBottom );
if bLockAspect and ( iecoSideGripsRespectLocks in fOptions ) then
AdjustAspectRatio(fScreenPolygon, iectisTOP_SIZE);
UpdateBitmapPolygon();
end;
iectisRIGHT_SIZE:
begin
// right resize
IEOrthogonalTranslate(fMouseDownRotatedPolygon[1], fMouseDownRotatedPolygon[2], IE2DPoint(X, Y), offX, offY);
fScreenPolygon[0] := fMouseDownRotatedPolygon[0];
fScreenPolygon[1] := IE2DPoint(fMouseDownRotatedPolygon[1].X + offX, fMouseDownRotatedPolygon[1].Y + offY);
fScreenPolygon[2] := IE2DPoint(fMouseDownRotatedPolygon[2].X + offX, fMouseDownRotatedPolygon[2].Y + offY);
fScreenPolygon[3] := fMouseDownRotatedPolygon[3];
IEDRotatePoints(fScreenPolygon, -fRotation);
_UpdatePolySize( iehLeft, ievCenter );
if bLockAspect and ( iecoSideGripsRespectLocks in fOptions ) then
AdjustAspectRatio(fScreenPolygon, iectisRIGHT_SIZE);
UpdateBitmapPolygon();
end;
iectisBOTTOM_SIZE:
begin
// bottom resize
IEOrthogonalTranslate(fMouseDownRotatedPolygon[2], fMouseDownRotatedPolygon[3], IE2DPoint(X, Y), offX, offY);
fScreenPolygon[0] := fMouseDownRotatedPolygon[0];
fScreenPolygon[1] := fMouseDownRotatedPolygon[1];
fScreenPolygon[2] := IE2DPoint(fMouseDownRotatedPolygon[2].X + offX, fMouseDownRotatedPolygon[2].Y + offY);
fScreenPolygon[3] := IE2DPoint(fMouseDownRotatedPolygon[3].X + offX, fMouseDownRotatedPolygon[3].Y + offY);
IEDRotatePoints(fScreenPolygon, -fRotation);
_UpdatePolySize( iehCenter, ievTop );
if bLockAspect and ( iecoSideGripsRespectLocks in fOptions ) then
AdjustAspectRatio(fScreenPolygon, iectisBOTTOM_SIZE);
UpdateBitmapPolygon();
end;
iectisLEFT_SIZE:
begin
// left resize
IEOrthogonalTranslate(fMouseDownRotatedPolygon[3], fMouseDownRotatedPolygon[0], IE2DPoint(X, Y), offX, offY);
fScreenPolygon[0] := IE2DPoint(fMouseDownRotatedPolygon[0].X + offX, fMouseDownRotatedPolygon[0].Y + offY);
fScreenPolygon[1] := fMouseDownRotatedPolygon[1];
fScreenPolygon[2] := fMouseDownRotatedPolygon[2];
fScreenPolygon[3] := IE2DPoint(fMouseDownRotatedPolygon[3].X + offX, fMouseDownRotatedPolygon[3].Y + offY);
IEDRotatePoints(fScreenPolygon, -fRotation);
_UpdatePolySize( iehRight, ievCenter );
if bLockAspect and ( iecoSideGripsRespectLocks in fOptions ) then
AdjustAspectRatio(fScreenPolygon, iectisLEFT_SIZE);
UpdateBitmapPolygon();
end;
iectisTOP_LEFT_SIZE:
begin
// top-left resize
if fMode = iectmPERSPECTIVE then
begin
fScreenPolygon := copy(fMouseDownRotatedPolygon, 0, length(fMouseDownRotatedPolygon));
fScreenPolygon[0] := IE2DPoint(X, Y);
IEDRotatePoints(fScreenPolygon, -fRotation);
end
else
begin
IEDRotateTwoPoints(-fRotation, IE2DPoint(X, Y), fMouseDownRotatedPolygon[2], fScreenPolygon[0], fScreenPolygon[2]);
fScreenPolygon[1] := IE2DPoint(fScreenPolygon[2].X, fScreenPolygon[0].Y);
fScreenPolygon[3] := IE2DPoint(fScreenPolygon[0].X, fScreenPolygon[2].Y);
end;
_UpdatePolySize( iehRight, ievBottom );
if bLockAspect then
AdjustAspectRatio(fScreenPolygon, iectisTOP_LEFT_SIZE);
UpdateBitmapPolygon();
end;
iectisTOP_RIGHT_SIZE:
begin
// top-right resize
if fMode = iectmPERSPECTIVE then
begin
fScreenPolygon := copy(fMouseDownRotatedPolygon, 0, length(fMouseDownRotatedPolygon));
fScreenPolygon[1] := IE2DPoint(X, Y);
IEDRotatePoints(fScreenPolygon, -fRotation);
end
else
begin
IEDRotateTwoPoints(-fRotation, IE2DPoint(X, Y), fMouseDownRotatedPolygon[3], fScreenPolygon[1], fScreenPolygon[3]);
fScreenPolygon[0] := IE2DPoint(fScreenPolygon[3].X, fScreenPolygon[1].Y);
fScreenPolygon[2] := IE2DPoint(fScreenPolygon[1].X, fScreenPolygon[3].Y);
end;
_UpdatePolySize( iehLeft, ievBottom );
if bLockAspect then
AdjustAspectRatio(fScreenPolygon, iectisTOP_RIGHT_SIZE);
UpdateBitmapPolygon();
end;
iectisBOTTOM_RIGHT_SIZE:
begin
// bottom-right resize
if fMode = iectmPERSPECTIVE then
begin
fScreenPolygon := copy(fMouseDownRotatedPolygon, 0, length(fMouseDownRotatedPolygon));
fScreenPolygon[2] := IE2DPoint(X, Y);
IEDRotatePoints(fScreenPolygon, -fRotation);
end
else
begin
IEDRotateTwoPoints(-fRotation, IE2DPoint(X, Y), fMouseDownRotatedPolygon[0], fScreenPolygon[2], fScreenPolygon[0]);
fScreenPolygon[1] := IE2DPoint(fScreenPolygon[2].X, fScreenPolygon[0].Y);
fScreenPolygon[3] := IE2DPoint(fScreenPolygon[0].X, fScreenPolygon[2].Y);
end;
_UpdatePolySize( iehLeft, ievTop );
if bLockAspect then
AdjustAspectRatio(fScreenPolygon, iectisBOTTOM_RIGHT_SIZE);
UpdateBitmapPolygon();
end;
iectisBOTTOM_LEFT_SIZE:
begin
// bottom-left resize
if fMode = iectmPERSPECTIVE then
begin
fScreenPolygon := copy(fMouseDownRotatedPolygon, 0, length(fMouseDownRotatedPolygon));
fScreenPolygon[3] := IE2DPoint(X, Y);
IEDRotatePoints(fScreenPolygon, -fRotation);
end
else
begin
IEDRotateTwoPoints(-fRotation, IE2DPoint(X, Y), fMouseDownRotatedPolygon[1], fScreenPolygon[3], fScreenPolygon[1]);
fScreenPolygon[0] := IE2DPoint(fScreenPolygon[3].X, fScreenPolygon[1].Y);
fScreenPolygon[2] := IE2DPoint(fScreenPolygon[1].X, fScreenPolygon[3].Y);
end;
_UpdatePolySize( iehRight, ievTop );
if bLockAspect then
AdjustAspectRatio(fScreenPolygon, iectisBOTTOM_LEFT_SIZE);
UpdateBitmapPolygon();
end;
iectisMOVING:
begin
// moving
offX := X - fMouseDownCoords.X; // offset X
offY := Y - fMouseDownCoords.Y; // offset Y
for i := 0 to high(fScreenPolygon) do
fScreenPolygon[i] := IE2DPoint(fMouseDownScreenPolygon[i].X + offX, fMouseDownScreenPolygon[i].Y + offY);
UpdateBitmapPolygon();
end;
iectisROTATE:
begin
// rotate
cx := (fScreenPolygon[0].X + fScreenPolygon[1].X) / 2.0;
cy := (fScreenPolygon[0].Y + fScreenPolygon[1].Y) / 2.0;
angleStart := ArcTan2(fMouseDownCoords.Y - cy, fMouseDownCoords.X - cx);
angle := ArcTan2(Y - cy, X - cx);
rotOffset := - (angle - angleStart) * 180.0 / PI;
// shift pressed? Rotate by 15 degrees steps
if ssShift in Shift then
begin
rotOffset := trunc(rotOffset / 15) * 15;
if fMouseDownRotation + rotOffset = fRotation then
begin
// avoid flickering
doRefresh := false;
end;
end;
fRotation := fMouseDownRotation + rotOffset
end;
else doRefresh := false; // Nothing to do
end;
if doRefresh then
Refresh();
end;
result := true;
end;
procedure TIECropToolInteraction.AdjustAspectRatio(quad: TIE2DPointArray; grip: TIECropToolInteractionState);
var
NewPt: TIE2DPoint;
begin
case grip of
iectisTOP_LEFT_SIZE:
begin
quad[0] := IECalcOrthogonalLinesIntersectingPoint(quad[2], 1.0 / fAspectRatio, quad[0]);
quad[1] := IE2DPoint(quad[2].X, quad[0].Y);
quad[3] := IE2DPoint(quad[0].X, quad[2].Y);
end;
iectisTOP_RIGHT_SIZE:
begin
quad[1] := IECalcOrthogonalLinesIntersectingPoint(quad[3], -1.0 / fAspectRatio, quad[1]);
quad[0] := IE2DPoint(quad[3].X, quad[1].Y);
quad[2] := IE2DPoint(quad[1].X, quad[3].Y);
end;
iectisBOTTOM_RIGHT_SIZE:
begin
quad[2] := IECalcOrthogonalLinesIntersectingPoint(quad[0], 1.0 / fAspectRatio, quad[2]);
quad[1] := IE2DPoint(quad[2].X, quad[0].Y);
quad[3] := IE2DPoint(quad[0].X, quad[2].Y);
end;
iectisBOTTOM_LEFT_SIZE:
begin
quad[3] := IECalcOrthogonalLinesIntersectingPoint(quad[1], -1.0 / fAspectRatio, quad[3]);
quad[0] := IE2DPoint(quad[3].X, quad[1].Y);
quad[2] := IE2DPoint(quad[1].X, quad[3].Y);
end;
iectisLEFT_SIZE:
begin
NewPt := IECalcOrthogonalLinesIntersectingPoint(quad[2], 1.0 / fAspectRatio, quad[0]);
quad[0] := IE2DPoint( NewPt.X, quad[0].Y + ( NewPt.Y - quad[0].Y ) / 2 );
quad[1] := IE2DPoint( quad[1].X, quad[0].Y );
quad[3] := IE2DPoint( NewPt.X, quad[3].Y - ( NewPt.Y - quad[0].Y ) / 2 );
quad[2] := IE2DPoint( quad[2].X, quad[3].Y );
end;
iectisRIGHT_SIZE:
begin
NewPt := IECalcOrthogonalLinesIntersectingPoint(quad[3], -1.0 / fAspectRatio, quad[1]);
quad[1] := IE2DPoint( NewPt.X, quad[1].Y + ( NewPt.Y - quad[1].Y ) / 2 );
quad[0] := IE2DPoint( quad[0].X, quad[1].Y );
quad[2] := IE2DPoint( NewPt.X, quad[2].Y - ( NewPt.Y - quad[1].Y ) / 2 );
quad[3] := IE2DPoint( quad[3].X, quad[2].Y );
end;
iectisTOP_SIZE:
begin
NewPt := IECalcOrthogonalLinesIntersectingPoint(quad[2], 1.0 / fAspectRatio, quad[0]);
quad[0] := IE2DPoint( quad[0].X + ( NewPt.X - quad[0].X ) / 2, NewPt.Y );
quad[3] := IE2DPoint( quad[0].X, quad[3].Y );
quad[1] := IE2DPoint( quad[1].X - ( NewPt.X - quad[0].X ) / 2, NewPt.Y );
quad[2] := IE2DPoint( quad[1].X, quad[2].Y );
end;
iectisBOTTOM_SIZE:
begin
NewPt := IECalcOrthogonalLinesIntersectingPoint(quad[1], -1.0 / fAspectRatio, quad[3]);
quad[3] := IE2DPoint( quad[3].X + ( NewPt.X - quad[3].X ) / 2, NewPt.Y );
quad[0] := IE2DPoint( quad[3].X, quad[0].Y );
quad[2] := IE2DPoint( quad[2].X - ( NewPt.X - quad[3].X ) / 2, NewPt.Y );
quad[1] := IE2DPoint( quad[2].X, quad[1].Y );
end;
end;
end;
procedure TIECropToolInteraction.MouseMove(Shift: TShiftState; X, Y: Integer; Captured: boolean);
begin
{ // }
end;
function TIECropToolInteraction.MouseUpExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean;
begin
result := false;
end;
procedure TIECropToolInteraction.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
tmp: TIE2DPointArray;
w, h: double;
begin
tmp := nil;
if (fState <> iectisUNSELECTED) then
begin
fState := iectisSELECTED;
if fMode = iectmRECTANGLE then
begin
// in case the polygon has inverted X, Y (only when it is a rectangle)
tmp := Copy(fScreenPolygon, 0, length(fScreenPolygon));
fScreenPolygon[0].X := dmin(tmp[0].X, tmp[2].X);
fScreenPolygon[0].Y := dmin(tmp[0].Y, tmp[2].Y);
fScreenPolygon[2].X := dmax(tmp[0].X, tmp[2].X);
fScreenPolygon[2].Y := dmax(tmp[0].Y, tmp[2].Y);
fScreenPolygon[1] := IE2DPoint(fScreenPolygon[2].X, fScreenPolygon[0].Y);
fScreenPolygon[3] := IE2DPoint(fScreenPolygon[0].X, fScreenPolygon[2].Y);
UpdateBitmapPolygon();
// update aspect ratio (now uses rectangle instead of original bitmap)
w := fBitmapPolygon[2].X - fBitmapPolygon[0].X + 1;
h := fBitmapPolygon[2].Y - fBitmapPolygon[0].Y + 1;
if h <> 0 then
fAspectRatio := w / h;
end;
end;
end;
procedure TIECropToolInteraction.VirtualKey(VKey: Dword; KeyData: Dword; KeyDown: Boolean);
begin
if KeyDown then
begin
case VKey of
VK_ESCAPE:
Cancel();
VK_RETURN:
Crop();
end;
end;
end;
procedure TIECropToolInteraction.Paint(const UpdateRect: TRect);
begin
if fState = iectisSELECTED then
Draw(GetParent().GetCanvas());
end;
procedure TIECropToolInteraction.Draw(Canvas: TCanvas);
var
g: TIECropToolInteractionState;
i: integer;
poly: array [0..3] of TPoint;
halfGSize: double;
begin
if fState <> iectisUNSELECTED then
begin
if fState = iectisSELECTED then
begin
// update fScreenPolygon because Zoom or Pan may be changed
UpdateScreenPolygon();
end;
// calc rotated polygon
fRotatedPolygon := copy(fScreenPolygon, 0, length(fScreenPolygon));
IEDRotatePoints(fRotatedPolygon, fRotation);
// calc grips coordinates
halfGSize := fGripSize / 2.0;
i := 0;
for g := iectisTOP_LEFT_SIZE to iectisLEFT_SIZE do
begin
case g of
iectisTOP_LEFT_SIZE, iectisTOP_RIGHT_SIZE, iectisBOTTOM_RIGHT_SIZE, iectisBOTTOM_LEFT_SIZE:
begin
fGripRects[g][0] := Point(trunc(fRotatedPolygon[i].X - fGripSize / 2.0), trunc(fRotatedPolygon[i].Y - halfGSize));
fGripRects[g][1] := Point(trunc(fRotatedPolygon[i].X + fGripSize / 2.0), trunc(fRotatedPolygon[i].Y + halfGSize));
end;
iectisTOP_SIZE, iectisRIGHT_SIZE, iectisBOTTOM_SIZE, iectisLEFT_SIZE:
begin
fGripRects[g][0].X := trunc((fRotatedPolygon[i].X + fRotatedPolygon[(i + 1) mod 4].X) / 2.0 - halfGSize);
fGripRects[g][0].Y := trunc((fRotatedPolygon[i].Y + fRotatedPolygon[(i + 1) mod 4].Y) / 2.0 - halfGSize);
fGripRects[g][1].X := trunc((fRotatedPolygon[i].X + fRotatedPolygon[(i + 1) mod 4].X) / 2.0 + halfGSize);
fGripRects[g][1].Y := trunc((fRotatedPolygon[i].Y + fRotatedPolygon[(i + 1) mod 4].Y) / 2.0 + halfGSize);
inc(i);
end;
end;
end;
//// drawings
Canvas.Brush.Style := bsClear;
Canvas.Pen.Mode := pmNot;
Canvas.Pen.Color := clWhite;
Canvas.Pen.Style := psSolid;
Canvas.Pen.Width := 2;
// grips
for g := iectisTOP_LEFT_SIZE to iectisLEFT_SIZE do
Canvas.Rectangle(fGripRects[g][0].X, fGripRects[g][0].Y, fGripRects[g][1].X, fGripRects[g][1].Y);
Canvas.Pen.Style := psDash;
Canvas.Pen.Width := 1;
// main polygon
for i := 0 to high(fRotatedPolygon) do
poly[i] := Point(trunc(fRotatedPolygon[i].X), trunc(fRotatedPolygon[i].Y));
Canvas.Polygon(poly);
if fDrawGuides then
begin
// vertical 1/3 guide
Canvas.MoveTo(trunc((2 * fRotatedPolygon[0].X + fRotatedPolygon[1].X) / 3), trunc((2 * fRotatedPolygon[0].Y + fRotatedPolygon[1].Y) / 3));
Canvas.LineTo(trunc((2 * fRotatedPolygon[3].X + fRotatedPolygon[2].X) / 3), trunc((2 * fRotatedPolygon[3].Y + fRotatedPolygon[2].Y) / 3));
// vertical 2/3 guide
Canvas.MoveTo(trunc((fRotatedPolygon[0].X + 2 * fRotatedPolygon[1].X) / 3), trunc((fRotatedPolygon[0].Y + 2 * fRotatedPolygon[1].Y) / 3));
Canvas.LineTo(trunc((fRotatedPolygon[3].X + 2 * fRotatedPolygon[2].X) / 3), trunc((fRotatedPolygon[3].Y + 2 * fRotatedPolygon[2].Y) / 3));
// horizontal 1/3 guide
Canvas.MoveTo(trunc((2 * fRotatedPolygon[0].X + fRotatedPolygon[3].X) / 3), trunc((2 * fRotatedPolygon[0].Y + fRotatedPolygon[3].Y) / 3));
Canvas.LineTo(trunc((2 * fRotatedPolygon[1].X + fRotatedPolygon[2].X) / 3), trunc((2 * fRotatedPolygon[1].Y + fRotatedPolygon[2].Y) / 3));
// horizontal 2/3 guide
Canvas.MoveTo(trunc((fRotatedPolygon[0].X + 2 * fRotatedPolygon[3].X) / 3), trunc((fRotatedPolygon[0].Y + 2 * fRotatedPolygon[3].Y) / 3));
Canvas.LineTo(trunc((fRotatedPolygon[1].X + 2 * fRotatedPolygon[2].X) / 3), trunc((fRotatedPolygon[1].Y + 2 * fRotatedPolygon[2].Y) / 3));
end;
end;
end;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TIECreateLayerInteraction
constructor TIECreateLayerInteraction.Create(Parent: TImageEnView);
begin
inherited Create(Parent);
fLockAspectRatio := -1;
fLockWidth := 0;
fLockHeight := 0;
fCreatingRect := False;
end;
function TIECreateLayerInteraction.MouseDownExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean;
begin
Result := False;
fCreatingRect := False;
if ( Button <> mbLeft ) or
// Cursor is over a grip
(( miResizeLayers in GetParent().fMouseInteract ) and ( GetParent().FindLayerGripAnySel( X, Y, True ) <> ieNone )) or
// Cursor is over another layer
(( miMoveLayers in GetParent().fMouseInteract ) and ( GetParent().FindLayerAt( X, Y ) > -1 )) then
exit;
{$ifdef IEDEBUG}
OutputDebugString( PWideChar( Format( 'No object at %d, %d, ', [ X, Y ])));
{$endif}
fMouseDownCoords := Point(X, Y);
fCreatingRect := True;
fScreenPt1 := IE2DPoint(X, Y);
fScreenPt2 := IE2DPoint(X, Y);
{$IFNDEF IEIncludeDeprecatedInV6}
// Avoid exceptions due to invalid layer index in older ImageEn versions
GetParent().DoLayerNotify( -1, ielBeginCreating );
{$ENDIF}
Result := True;
end;
procedure TIECreateLayerInteraction.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
{ // }
end;
function TIECreateLayerInteraction.MouseMoveExclusive(Shift: TShiftState; X, Y: Integer; Captured: boolean): boolean;
{}
procedure _UpdatePolySize(HAlign: TIEHAlign; VAlign: TIEVAlign);
const
Size_Locks_Are_Minimums = False;
var
dx, dy: Integer;
begin
if ( fLockAspectRatio <= 0 ) or Size_Locks_Are_Minimums then
begin
dx := round( fLockWidth * GetParent().ZoomX / 100.0 );
dy := round( fLockHeight * GetParent().ZoomY / 100.0 );
if fLockWidth > 0 then
if ( Size_Locks_Are_Minimums = False ) or ( abs( fScreenPt2.X - fScreenPt1.X ) < dx ) then
case HAlign of
iehLeft : fScreenPt2.X := fScreenPt1.X + dx;
iehRight : fScreenPt1.X := fScreenPt2.X - dx;
end;
if fLockHeight > 0 then
if ( Size_Locks_Are_Minimums = False ) or ( abs( fScreenPt2.Y - fScreenPt1.Y ) < dy ) then
case VAlign of
ievTop : fScreenPt2.Y := fScreenPt1.Y + dy;
ievBottom : fScreenPt1.Y := fScreenPt2.Y - dy;
end;
end;
end;
{}
var
HAlign: TIEHAlign;
VAlign: TIEVAlign;
newPosX, newPosY, newWidth, newHeight: Double;
begin
result := false;
if fCreatingRect = False then
exit;
if Captured then
begin
// Creating Layer
fScreenPt2 := IE2DPoint(X, Y);
HAlign := iehLeft;
if X < fScreenPt1.X then
HAlign := iehRight;
VAlign := ievTop;
if Y < fScreenPt1.Y then
VAlign := ievBottom;
_UpdatePolySize( HAlign, VAlign );
{$IFNDEF IEIncludeDeprecatedInV6}
// Avoid exceptions due to invalid layer index in older ImageEn versions
GetParent().DoLayerNotify( -1, ielCreating );
{$ENDIF}
if assigned( GetParent().fOnLayerMoveSize ) then
begin
newPosX := SelX;
newPosY := SelY;
newWidth := SelWidth;
newHeight := SelHeight;
GetParent().fOnLayerMoveSize( Self, -1, ielCreating, newPosX, newPosY, newWidth, newHeight );
SetSel( Round( newPosX ), Round( newPosY ), Round( newWidth ), Round( newHeight ));
end;
if ( SelWidth > 0 ) and ( SelHeight > 0 ) then
GetParent().SetInteractionHint( IntToStr( SelWidth ) + ' x ' + IntToStr( SelHeight ), X, Y, '0000 x 0000' );
Refresh();
end;
result := true;
end;
procedure TIECreateLayerInteraction.MouseMove(Shift: TShiftState; X, Y: Integer; Captured: boolean);
begin
{ // }
end;
function TIECreateLayerInteraction.MouseUpExclusive(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): boolean;
begin
result := false;
end;
procedure TIECreateLayerInteraction.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if fCreatingRect then
Enact();
end;
procedure TIECreateLayerInteraction.VirtualKey(VKey: Dword; KeyData: Dword; KeyDown: Boolean);
begin
if KeyDown then
begin
case VKey of
VK_ESCAPE: Cancel();
end;
end;
end;
procedure TIECreateLayerInteraction.Paint(const UpdateRect: TRect);
begin
{ // }
end;
procedure TIECreateLayerInteraction.Draw(Canvas: TCanvas);
begin
if fCreatingRect = False then
exit;
Canvas.Brush.Style := bsClear;
Canvas.Pen.Mode := pmNot;
Canvas.Pen.Color := clWhite;
Canvas.Pen.Style := psDash;
Canvas.Pen.Width := 1;
Canvas.Rectangle( Rect( trunc( fScreenPt1.X ),
trunc( fScreenPt1.Y ),
trunc( fScreenPt2.X ),
trunc( fScreenPt2.Y )));
end;
procedure TIECreateLayerInteraction.Cancel();
begin
fCreatingRect := False;
Refresh();
RestoreCursor;
end;
procedure TIECreateLayerInteraction.Enact();
const
Minimum_Size = 5;
var
doSaveUndo: Boolean;
imgFilename: WideString;
begin
if ( fCreatingRect = False ) or
( SelWidth < Minimum_Size ) or
( SelHeight < Minimum_Size ) then
exit;
imgFilename := '';
if ( fLayerKind = ielkImage ) and ( loAutoPromptForImage in GetParent().LayerOptions ) then
begin
imgFilename := GetParent().IO.ExecuteOpenDialog('');
if imgFilename = '' then
exit;
end;
doSaveUndo := GetParent().Proc.AutoUndo and ( loAutoUndoChangesByUser in GetParent().fLayerOptions );
GetParent().LayersAddEx( fLayerKind,
SelX,
SelY,
SelWidth,
SelHeight,
nil, True,
doSaveUndo,
True );
if imgFilename <> '' then
begin
GetParent().IO.LoadFromFile( imgFilename );
TIEImageLayer( GetParent().CurrentLayer ).RestoreAspectRatio();
end;
if assigned( GetParent().fOnNewLayer ) then
GetParent().fOnNewLayer( GetParent(), GetParent().LayersCurrent, fLayerKind );
GetParent().DoLayerNotify( GetParent().fLayersCurrent, ielCreated );
Cancel();
end;
function TIECreateLayerInteraction.GetSelX(): Integer;
begin
result := GetParent().XScr2Bmp( Round( dmin( fScreenPt1.X, fScreenPt2.X )), False );
end;
function TIECreateLayerInteraction.GetSelY(): Integer;
begin
result := GetParent().YScr2Bmp( Round( dmin( fScreenPt1.Y, fScreenPt2.Y )), False );
end;
function TIECreateLayerInteraction.GetSelWidth(): Integer;
begin
result := Abs( GetParent().XScr2Bmp( Round( fScreenPt2.X ), False ) -
GetParent().XScr2Bmp( Round( fScreenPt1.X ), False ));
end;
function TIECreateLayerInteraction.GetSelHeight(): Integer;
begin
result := Abs( GetParent().YScr2Bmp( Round( fScreenPt2.Y ), False ) -
GetParent().YScr2Bmp( Round( fScreenPt1.Y ), False ));
end;
procedure TIECreateLayerInteraction.SetSel(X, Y, Width, Height: Integer);
var
reverseX, reverseY: Boolean;
begin
reverseX := fScreenPt1.X > fScreenPt2.X;
reverseY := fScreenPt1.Y > fScreenPt2.Y;
fScreenPt1.X := GetParent().XBmp2Scr( X, False );
fScreenPt1.Y := GetParent().YBmp2Scr( Y, False );
fScreenPt2.X := GetParent().XBmp2Scr( X + Width, False );
fScreenPt2.Y := GetParent().YBmp2Scr( Y + Height, False );
if reverseX then
IESwap( fScreenPt1.X, fScreenPt2.X );
if reverseY then
IESwap( fScreenPt1.Y, fScreenPt2.Y );
end;
procedure TIECreateLayerInteraction.Refresh();
begin
GetParent().Paint();
Draw(GetParent().GetCanvas());
end;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
end.