问题
By drawing layer I mean a layer where the user can manually draw lines, circles or other shapes. And by normal layers I mean the layers described in the graphics32 layers example (the layers that can be moved or resized at runtime using mouse events) So I am having difficulties combining these 2 types of layers. In my test project, for now, I will assume I only have one drawing layer and multiple PNG layers. So in my project I set properties for the ImgView32 in the OnFormCreate like:
procedure TForm1.FormCreate(Sender: TObject);
begin
AWidth:= 800;
AHeight:= 600;
FillColor:=clWhite;
with ImgView do
begin
Selection := nil;
RBLayer := nil;
Layers.Clear;
Scale := 1;
Scaled:=true;
Bitmap.SetSize(AWidth, AHeight);
Bitmap.DrawMode := dmTransparent;
Bitmap.Clear(FillColor);
end;
end;
After this, onClick of a button, I add a number of layers (containing transparent PNG images). So it's like this
procedure TForm1.Button1Click(Sender: TObject);
begin
AddPNGLayer(1);
AddPNGLayer(2);
AddDrawingLayer;
AddPNGLayer(3);
end;
(I wont elaborate here the adding of PNG layers in order to keep the question short. I will only say that it uses a different onMouseDown event (layerMouseDown) than the one used in the drawingLayer) and the AddDrawingLayer is as follows:
procedure TForm1.AddDrawingLayer;
var
P:TPoint;
jumaH, JumaW, W, H: Single;
begin
imwidth := ImgView.Bitmap.Width;
imheight := ImgView.Bitmap.Height;
xofx := (ImgView.ClientWidth - 17 - imwidth) div 2; // substracting the width of the scrollbar
yofy := (ImgView.ClientHeight - 17 - imheight) div 2; // same here with height
bm32 := TBitmap32.Create;
bm32.DrawMode := dmTransparent;
bm32.SetSize(ImgView.Bitmap.Width,ImgView.Bitmap.Height);
bm32.Canvas.Pen.Width := 3;
bm32.Canvas.Pen.Color := clBlack32;//pencolor;
BB := TBitmapLayer.Create(ImgView.Layers);
try
BB.Bitmap.DrawMode := dmTransparent;
BB.Bitmap.SetSize(imwidth,imheight);
BB.Bitmap.Canvas.Pen.Width := 3;
BB.Bitmap.Canvas.Pen.Color := pencolor;
BB.Location := GR32.FloatRect(0, 0, imwidth, imheight);
BB.Scaled := true;
BB.Tag:=3;
//// Selection:=BB; // if I use this then I cant draw because the entire layer is selected and the mouseDown event works as a mover/resizer
// BB.OnMouseDown := DrLayerMouseDown;
// BB.OnMouseUp := DrLayerMouseUp;
// BB.OnMouseMove := DrLayerMouseMove;
// BB.OnPaint := DrLayerOnPaint;
RBLayer:=nil;
EdLayerIndex.Text:=IntToStr(BB.Index);
finally
BB.Free;
end;
FDrawingLine := false;
// swapBuffers32; // needed when mouse events are active
end;
EdLayerIndex is a EditBox where I display the created/selected Layer index (for debugging)
- As you can see above, if I keep
Selection:=BB
andRBLayer:=nil
then the drawingLayer is only movable and resizable, so it's not a good solution since I want to use my Mouse events in this particular layer to draw. If I comment only the
RBLayer:=nil
while keepingSelection:=BB
then the drawingLayer is not movable anymore, but I cannot select other layers that are under the drawingLayer. I can only access the top layer (the last added PNG layer)If I comment the
Selection:=BB
then I cannot select other layers with my mouse. So in my case I declared 2 png layers before my drawingLayer and one after it. On runtime I can only select the last layer (the one 'above' the drawingLayer) So this is not a solution either.
How can I do it that when I click on the drawing layer (or select it otherwise, like in a listbox or something), the drawingLayer wont be movable, but my drawing Mouse Events will kick in? And all this while I can go away from the drawingLayer whenever I want and select other layers to move around and play with. So basically I need a particular layer to NOT act like the other layers.
What I want to achieve is having a classic Photoshop-like or paint.net like behavior using graphics32. And it is very confusing how these layer properties actually work.
So far I figured out how to draw (lines, circles, rectangles) on a transparent layer dynamically (using mouse events). So I can have a drawing layer. The drawing happens in my DrLayerMouseDown
, DrLayerMouseUp
, DrLayerMouseMove
, DrLayerPaint
events. But I cannot seem to understand how to combine such a drawing layer with regular movable/resizable layers.
The rest of the code (like setSelection
, RBResizing
and layerMouseDown
) is mostly taken from the layers example of the graphics32 library.
EDIT
In order to test your idea with layerOptions
, I did the following:
1.Started a new test project with an ImgView on it, and a button
2.On create i used the same code as before
3.OnButtonClick I added ONE layer using a modified AddDrawingLayer like this:
...
BB.Scaled := true;
Selection:=BB;
Selection.LayerOptions:=Selection.LayerOptions and (not LOB_MOUSE_EVENTS); // I also tried it with BB instead of Selection
BB.OnMouseDown := DrLayerMouseDown;
BB.OnMouseUp := DrLayerMouseUp;
BB.OnMouseMove := DrLayerMouseMove;
BB.OnPaint := DrLayerOnPaint;
...
expecting it to become insensitive to Mouse Events. But the layer is still movable instead of being insensitive to mouse. So it's like I did not do anything
So I do not think it helps me using this option unless I am doing it wrong So onCreate of the layer, this option does not seem to stick. But if I disable mouse events for all layers, one-by-one like in the next EDIT, then the drawing layer gets disabled (mouse events)
EDIT
Also I tried another test project, same idea: same onCreate, and onButtonClick I add 3 layers (using the Layers example of the library) containing an image each (no drawing layer this time, to keep it simple). Then I added a new button where if you click it, the next code is executed:
for i := 0 to ImgView.Layers.Count-1 do
(ImgView.Layers.Items[i] as TPositionedLayer).LayerOptions:= (ImgView.Layers.Items[i] as TPositionedLayer).LayerOptions and (not LOB_MOUSE_EVENTS);
My purpose was to make all layers insensitive to mouse events. I succeeded, after clicking the new button, the layers could not be selected anymore, however when I wanted to re-enable mouse events for the layers (adding a third button with the next code onClick):
for i := 0 to ImgView.Layers.Count-1 do
(ImgView.Layers.Items[i] as TPositionedLayer).LayerOptions:= (ImgView.Layers.Items[i] as TPositionedLayer).LayerOptions and (LOB_MOUSE_EVENTS);
No error was shown, but when I tried to select a layer in order to move it ... all the images of the layers disappeared from the view... leaving me with a white background empty ImgView.
What am I doing wrong? In order to do what you suggested with LayerOptions, I need to be able to disable mouse events for all layers, and enable mouse events for a specific layer, and then when editing is done, I need to be able to re-enable mouse events for all layers, but I am doing it wrong I guess.
回答1:
Following items affect mouse events
Layers.MouseEvents (boolean). Layers is the TLayerCollection of TCustomImage32 that manages the layers. If MouseEvents is False, mouse events are not propagated to the layers.
Layers.MouseListener (TCustomLayer). The layer that 'captures' mouse events between left button MouseDown and MouseUp. 'Captures' in quoutes because it is not capturing the mouse as understood in Windows context.
Layer Option Bits. Each layer has a 32 bit LayerOptions property. The interesting bit is LOB_MOUSE_EVENTS (bit 29) which specifies whether the layer reacts to mouse events. A layer may also specify LOB_NO_CAPTURE bit (bit 27) which prevents mouse events, even if LOB_MOUSE_EVENTS is set.
Layer index. Layers are checked (in order topmost down to lowest) for LOB_MOUSE_EVENTS option bit. When a layer is found with this bit, the X and Y coordinates are checked in the layers HitTest function. If the X and Y coordinates are within the layers location HitTest succeeds. The result of built-in HitTest can be overridden in your own OnHitTest event. Finally, if the layers options does not contain the LOB_NO_CAPTURE bit, the layers MouseDown event is called.
Based on previous I suggest that when the user enters an 'edit' mode, you disable all other layers, except the drawing layer, by setting their LayerOptions to not include the LOB_MOUSE_EVENTS bit
Layer.LayerOptions := Layer.LayerOptions and (not LOB_MOUSE_EVENTS);
Further info about using layers is available here
Edit
To manage the LOB_MOUSE_EVENTS create for example something like following
procedure TForm7.LayerMouseDisEnable(Enable: boolean);
var
i: integer;
Lo: cardinal;
begin
for i := 0 to ImgView.Layers.Count-1 do
begin
Lo := ImgView.Layers.Items[i].LayerOptions;
if Enable then
ImgView.Layers.Items[i].LayerOptions := Lo or LOB_MOUSE_EVENTS
else
ImgView.Layers.Items[i].LayerOptions := Lo and (not LOB_MOUSE_EVENTS);
end;
end;
Call this (with False) to disable mouse events in the layers just before you create the drawing layer. The drawing layer will have mouse events enabled as newly created layers have both LOB_VISIBLE and LOB_MOUSE_EVENTS set. Call again (with True) when you stop drawing to enable the mouse event.
来源:https://stackoverflow.com/questions/28623999/delphi-graphics32-combining-normal-layers-with-drawing-layers