I am working with delphi. I have an array of points which are continues as shown in image.
Then I give this array to CreatePolygonRgn and create the region say rgn1.
rgn1 := CreatePolygonRgn(tmpary1[0],Count,WINDING);
Then I fill the region and show it on my TImage control as shown in image. The problem is from the left side, the points are also covered in region but from right side the points of array are not covered. This can be seen in image that from left side green border is not shown but from right side border is visible. Am I mistaking somewhere??? If my question is not clear to you then please ask.
Thank You.
Edit:
for cnt := 0 to Count - 1 do begin
p1 := imgmain.Picture.Bitmap.ScanLine[tmpary[cnt].Y];
p1[tmpary[cnt].X].rgbtBlue := 0;
p1[tmpary[cnt].X].rgbtGreen := 255;
p1[tmpary[cnt].X].rgbtRed := 0;
end;
rgn1 := CreatePolygonRgn(tmpary1[0],tmpseq1.Count,WINDING);
imgmain.Picture.Bitmap.Canvas.Brush.Color := clRed;
FillRgn(imgmain.Picture.Bitmap.Canvas.Handle,rgn1,imgmain.Picture.Bitmap.Canvas.Brush.Handle);
It may just be the way it works. FillRect, for example, includes the left and top borders, but excludes the right and bottom borders of the rectangle.
I think the same probably applies to FillRgn.
Edit: Confirmed here, too.
At last I found the feasible solution to my problem and also the solution of this problem as both question are related to each other.
I was filling the region and then tried to get boundary of that region. I was getting some points of original array as boundary and some points were actual boundary points. I wanted all points of actual boundary.
So, now I fill the region with red color then fill the pixels of array with red color and then I run floodfill algorithm. It will give all points I needed.
Related
I draw a path consisting of 2 lines going up and then back down to the same spot, or almost the same spot, but the first line is drawn too high. If I then draw the same lines using DrawLine I don't see the issue. Why is this happening?
Below is an example. Just drop a 400x400 TImage on a blank multiplatform form. The code draws 2 red paths, one with close to a 180 degree angle between the lines and one with less of an angle. The same lines are then drawn using DrawLine in blue. If the DrawPath function works correctly then the blue lines should completely cover the red lines, but they don't. In this example with a scale of 1.5 the path extends 7 pixels too high for the first path. The extent of the error reduces as the lines get further apart. The issue still happens with a scale of 1, but is less obvious.
procedure TForm1.FormActivate(Sender: TObject);
var
LPath1, LPath2 : TPathData;
i : Integer;
begin
// A path of 2 lines going up and then back down to almost the same spot
LPath1 := TPathData.Create;
LPath1.MoveTo(PointF(100,200));
LPath1.LineTo(PointF(100,50 ));
LPath1.LineTo(PointF(105,200));
// A path of 2 lines going up and then back down at a wider angle
LPath2 := TPathData.Create;
LPath2.MoveTo(PointF(200,200));
LPath2.LineTo(PointF(200,50 ));
LPath2.LineTo(PointF(260,200));
Image1.Bitmap.BitmapScale := 1.5; // The issue shows up more at larger scales
Image1.Bitmap.SetSize(Trunc(Image1.Width), Trunc(Image1.Height));
with Image1.Bitmap.Canvas do if BeginScene then begin
Clear(TAlphaColorRec.White);
// Draw the paths using DrawPath in red
Stroke.Color := TAlphaColorRec.Red;
Stroke.Thickness := 1;
DrawPath(LPath1, 1);
DrawPath(LPath2, 1);
// Draw the paths using DrawLine in blue over the top
// The red lines should be completely hidden under the blue
Stroke.Color := TAlphaColorRec.Blue;
for i := 1 to LPath1.Count - 1 do
DrawLine(LPath1.Points[i-1].Point, LPath1.Points[i].Point, 1);
for i := 1 to LPath2.Count - 1 do
DrawLine(LPath2.Points[i-1].Point, LPath2.Points[i].Point, 1);
EndScene;
end;
LPath1.Free;
LPath2.Free;
Image1.Bitmap.SaveToFile('test.png');
end;
Result of the code when run in Windows 10. I'm using Delphi 11, but the same issue happens with Delphi 10. I've tried switching GPU but the same issue occurs.
Enlarged view:
I've come to the conclusion that this isn't a glitch at all. It's because the default setting of TCanvas.Stroke.Join is TStrokeJoin.Miter. The artefact seen is just the sharp spike of the mitred corner. Using MoveTo before each line segment when constructing the path does solve the issue (because there's no join between the separate line segments) but so does setting the TCanvas.Stroke.Join parameter to TStrokeJoin.Round or TStrokeJoin.Bevel.
Note that at very sharp angles approaching 180 degrees, the miter join would become infinite. However, it appears to be limited somehow, perhaps in proportion to the stroke thickness. I don't think there's a way to change this miter limit in delphi.
This is because by default TPath is making smooth transitions between different path segments. I'm guessing it might be using Quadratic interpolation for making these smooth transitions.
Yes making smooth transition between two lines doesn't seem logical but it looks this is how it is implemented.
Now you can avoid this by telling the TPath that your two lines are not connected and thus should be treated as two separate lines even thou in reality they are connected. And you can do this by simply calling Path.MoveTo which is intended to shift position so you can create another unconnected line that dos not continue from your last path point.
Here is how modified code for your first sharp cornered line would look like:
NOTE that I'm specifying the exact same position for MoveTo command that was used for rendering of previous path line since you don't want the new line to start at new position.
// A path of 2 lines going up and then back down to almost the same spot
LPath1 := TPathData.Create;
LPath1.MoveTo(PointF(100,200));
LPath1.LineTo(PointF(100,50 ));
//Add move to command to prevent Path from trying to make smooth transitions between two lines
LPath1.MoveTo(PointF(100,50));
LPath1.LineTo(PointF(105,200));
I'm trying to place an outside program right into an FMX TPanel using SetWindowPOS from WinAPI.
In order to do that I need the exact Top and Left values of the TPanel to pass it to SetWindowPOS. However, this value is always according to its parent, so i.e. if the Tpanel's parent is a TLayout and no margins are set, then the value of Top will be 0.
I need the Top value according to the screen.
I tried searching for that quite a bit but I can't figure it out.
Any help with that would be greatly appreciated.
After just posting the question I got the simple solution
var
pnt: TPointF;
begin
pnt := myRctngl.LocalRect.TopLeft;
pnt := myRctngl.LocalToAbsolute(pnt);
pnt := ClientToScreen(pnt);
End;
pnt should now have the value of the Point according to the screen.
You can now use pnt.x and pnt.y to get the top/left values.
I'm using a drawgrid to display a "dungeon" map. I have an array filled with data describing what types of physical features are located in each cell. The basic layout of rooms and corridors, etc. There's a PNG for each permutation.
I'm trying to "draw" the map by drawing the appropriate PNG into each cell of the drawgrid. I can do it more or less directly using DrawGrid1.Canvas.Draw(x,y,pngImage), but figuring out the exact x,y in pixels is frustrating because of the gridlines (at least, I'm finding it frustrating), and I'm not sure what other issues I'll run into down the road.
I also tried pngImage.Draw(DrawGrid1.Canvas,Rect), but once again, I have to calculate the Rect, which really seems unnecessary as the cells and PNGs are all 40x40 pixels.
Reading related articles and examples, OnDrawCell seems to be a better way, because given ARow and ACol, the Rect for the given cell is apparently precalculated (unless I'm misinterpreting what I've read in several places). But none of the examples I've found actually show how OnDrawCell is triggered. The easy answer "when something gets drawn into a cell" doesn't really explain it much better. That's what I'm trying to do.
(I've often found this to be the case with Delphi's documentation: How is explained (not always exactly for your given use case), but when or why is left shrouded in mystery..)
Of course, there are a few other things to figure out as well, like controlling the drawgrid so it doesn't erase the PNG when a cell is clicked..
Any relevant suggestions will be greatly appreciated. TFRM
Why is figuring out exact X and Y in pixels so hard? It is actually pretty simple.
In order to get left border of your rectangle you simply multiply width of you column plus width of your gridline by number of the column. And if you have gridline on the very left of the first column then add the width of your gridline.
And in order to get right border of your rectangle simply add width of the column to the previous result.
And for calculating Y you just use height instead of width
So the code would look something like this (written from my mind and untested)
const
CellWidth = 40;
CellHeight = 40;
GridLineThickness = 1;
procedure DrawCell(Row, Column: Integer; Image: Bitmap);
var Rect: TRect;
begin
Rect.Left := ((CellWidth + GridLineThickness) * Column) + GridLineThickness;
Rect.Right := Rect.Left + CellWidth;
Rect.Top := ((CellHeight + GridLineThickness) * Row) + GridLineThickness;
Rect.Bottom := Rect.Top + CellHeight;
PaintBox1.Canvas.StretchDraw(Rect, Image);
end;
And if you want an ability to zoom in or zoom out just multiply the CellWidth and CellHeight by a zoom factor. And when being zoomed out a lot just omit rendering grid lines.
i Have this code for drawing a profile of one plane figure.
Chart1.Series.Clear;
Lser:=TLineSeries.Create(Chart1);
for x:= 1 to 20 do
Lser.AddXY(strtofloat(stringgrid1.Cells[1,x]),strtofloat(stringgrid1.Cells[2,x]));
chart1.AddSeries(Lser);
lser.LinePen.Width:=2;
lser.SeriesColor:=clred;
Now, how can change the background color of my plane figure?
Thank you very much
if u see the picture, i want draw only the yellow area (i fill it on paint of windows)
Thanks to all, i solved it with TAreaSeries.
chart1.Backwall.Transparent := False;
chart1.Backwall.Color := clgreen;
I have been experimenting with the Change HUE/Saturation example found on the EFG website: http://www.efg2.com/Lab/Library/Delphi/Graphics/Color.htm
I want to use the techniques found in that example to change the color values of some bitmaps (to be used on a TImage). There is one problem I am facing, and that is with TImages that have transparent bitmaps; I don't want the background color to change its HUE but rather just the actual image data.
Take this sample image we can work with (though any image with transparency can be used):
Download the Change HUE/Saturation demo from the link at the top.
Now Load the button image above into the ImageOriginal.
Set the Delta Saturation to 1.
Run the Project.
Some output results:
What I would like to happen here is keep the original background color (defined by the bottom left most pixel) and only adjust the hue for the rest of the image. So, the output results would actually look like this (edited in Paint.NET):
Allow me to use another sample image to test with that contains more image data:
As before with the first image sample, the results could be something like:
When the results I desire should be like so:
One idea I had was after the HUE change, replace the bottom left most color with the color from the original, but I am not sure if this is the correct approach or not, and even if it is I am not sure how to replace color X for another color. (Graphics and Math is beyond me).
For example, if I drew two blue color circles on Mickey's ear (the blue been the original transparent color from bottom left most pixel) and also colored his eyes:
Changing the HUE again may look like:
when in fact it should like this:
Simply put I would like to change the HUE of an image whether it be by the methods used from the EFG demo or another solution. The transparent color as defined by the bottom most left pixel should not be changed when the HUE is changed, this should remain the same as demonstrated in the sample images.
How can I change the HUE of a bitmap whilst retaining the background color?
The code you refer walks all pixels on the image to change hue/saturation. You can modify the code so that it does not interfere if a pixel has a specific color:
PROCEDURE TFormChangeHS.UpdateBitmap;
..
S : TReal;
V : TReal;
FixColor: TRGBTriple; // New variable
BEGIN
DeltaSaturation := SpinEditDeltaSaturation.Value;
NewHue := TrackBarHue.Position;
Bitmap := TBitmap.Create;
TRY
Bitmap.Width := ImageOriginal.Width;
Bitmap.Height := ImageOriginal.Height;
Bitmap.PixelFormat := ImageOriginal.Picture.Bitmap.PixelFormat;
ASSERT(Bitmap.PixelFormat = pf24bit);
// save bottom left color
FixColor := PRGBTripleArray(
ImageOriginal.Picture.Bitmap.ScanLine[Bitmap.Height - 1])^[0];
//--
FOR j := 0 TO Bitmap.Height-1 DO
BEGIN
RowIn := ImageOriginal.Picture.Bitmap.Scanline[j];
RowOut := Bitmap.Scanline[j];
FOR i := 0 TO Bitmap.Width-1 DO
BEGIN
WITH RowIn[i] DO
BEGIN
// copy the color to target and continue with next iteration
if (RowIn[i].rgbtBlue = FixColor.rgbtBlue) and
(RowIn[i].rgbtGreen = FixColor.rgbtGreen) and
(RowIn[i].rgbtRed = FixColor.rgbtRed) then begin
RowOut[i] := FixColor;
Continue;
end;
//--
// convert pixel coordinates to HSV
RGBtoHSV(rgbtRed, rgbtGreen, rgbtBlue, H, S, V);
END;
..
..
Output:
In theory, you can for example:
First, you have to determine what is the background... (probably picking the color of a specific pixel like delphi does...)
Then, with this background color, you can create a mask based on this color...
You duplicate the image... apply the hue...
You merge your 2 bitmaps using the mask...