|
Thank you for posting this. Very helpful application to generate gradients.
I'm trying to implement this into an AutoCAD application for a small utility, and then convert the colors over to AutoCAD colors after the gradient is obtained in Windows colors. In doing this, I have brought the code into a Class Library and am calling the Gradient selection dialog from another dialog within AutoCAD - everything is working great, except for the generated buttons and their back colors.
It seems the screen location is not finding the correct location of the gradient panel. I can move the dialog around screen and regenerate and see the button colors change.
Any thoughts on how I would resolve this issue so that it finds the correct pixel location of the gradient panel? I've narrowed down the issue into '
generate_back_color ', with the two points it's generating.
point = new Point (
( ( button_PAN.Location.X +
button.Location.X ) +
( button.Size.Width / 2 ) ),
( gradient_PAN.Location.Y +
( gradient_PAN.Size.Height / 2 ) ) );
screen_point = PointToScreen ( point );
I believe it's the conversion of PointToScreen that is not corresponding properly.
|
|
|
|
|
It seems the screen location is not finding the correct location of the gradient panel. I can move the >dialog around screen and regenerate and see the button colors change.
This should not happen. Once the Generate button is clicked and the buttons are filled with colors, moving the dialog and clicking on Generate again (without reset) should generate the same colors as earlier.
calling the Gradient selection dialog from another dialog within AutoCAD
Could there be a problem between Windows Forms dialogs and AutoCAD dialogs?
I would open a debug session and track the locations of every object of interest to you. You may achieve an ah-ha. Also, try eliminating the PaintToScreen conversion and see what happens.
I'm sorry, I cannot be more precise.
Gus Gustafson
|
|
|
|
|
Thanks Gus. I'll give that a try.
The issue is on clicking the generate button... I haven't even gotten to the part of converting the colors into AutoCAD colors - that part is fairly easy with the RGB values.
When loading into AutoCAD, I click the start color button, then the end color button, and the Gradient panel appears.
Clicking gradient, the first and last buttons are correct as they are defined by the colors picked. The buttons in between are a mix of Black, Control color, white, etc. Mostly black because AutoCAD's background is black. I can actually change the effect of that if I change AutoCAD's background color.
I should add that I'm starting the dialog from another dialog as well as it's part of a larger tool.
Anyway, I'll do some more investigating and see if I can figure out the issue. Overall it just seems that the screen location for the pixels is returning incorrectly - I do run with multiple monitors as well.
|
|
|
|
|
Following up on my last post... Admittedly, I ported your code into my own project to use in my own application inside of AutoCAD / AutoCAD Civil 3D.
To make sure there were no errors in that, I decided to take a few minutes and convert your project over to a Class Library and then add the necessary references to have your exact code project launch from an AutoCAD command called 'GRADIENTCOLORPICK'.
I did this and launched your project and at the screenshot below, you can see the exact same behavior I'm experiencing. I narrowed it down in the code to the PointOnScreen() returning the wrong location on screen. Note the colors of the middle button that were generated... White-White (The white sheet of paper in AutoCAD), Gray, the area outside the sheet of paper, dark gray (the border of AutoCAD toolbar), and white again, which I think happens to be my next monitor over on a web browser with a white background.
It's quite odd...here's a link to an image you can view if you'd like. I'd appreciate any help as I'd love to use this tool. I have been looking for a way to do this.
GradientColorPicker.png - Google Drive[^]
|
|
|
|
|
In your screen capture, the two end points are correct. They are set explicitly to the start and end colors.
As you guessed, the problem appears to be in the return value of PointOnScreen().
Run the program in Windows (not in your application). Does it act as documented?
Try in generate_back_color comment out the line
screen_point = PointToScreen ( point );
and insert
screen_point = point;
See if that makes a difference.
I do not know the origin of the client/screen coordinates in AutoCAD. As I suggested earlier, you need to determine the values of each point of interest. The code uses the color of a point in the client areas (gradient_PAN and button_PAN) to paint the background color of the button in screen coordinates. Recall that a usable coordinate must take into effect the height of the ribbon, title bar, etc. It appears that the gradient_PAN coordinates are known (the gradient is painted correctly).
You could use the Bitmap.GetPixel(Int32, Int32) Method. The number of colors that are required should not adversely affect performance.
Some References:
Windows coordinates
Position child window
GetPixel
Gus Gustafson
|
|
|
|
|
Thanks for the reply.
Yes, the app works just fine as windows. It's within AutoCAD as a class library where it does not.
Without changing the code, I ran some additional tests - I think the issue is due to multiple monitors.
If AutoCAD is running on any of my monitors, as I move your dialog around to different monitors, it does in fact work properly if the dialog is on my primary monitor (laptop screen).
If I move to either of the other screens (regardless of where AutoCAD is at), I get the result shown on previous sreenshot. Windows version, works on any monitor.
I did try to comment out the code you mentioned and that did not work.
So overall, it seems as a Class Library, within AutoCAD, I need to add some way to calculate what monitor the dialog is being displayed on to properly line it up to the pixels where the control is.
Any thoughts on how to do that? Pixel location was something new to me with this. Very cool to see how you did it. For now I think I can get by knowing the dialog has to be on the primary monitor. But it would be nice to have a fix that makes it work in any situation.
|
|
|
|
|
public static Color get_pixel_color_at_location (
Point location )
{
Bitmap screen_pixel = new Bitmap (
1,
1,
PixelFormat.Format32bppArgb );
using ( Graphics destination = Graphics.FromImage (
screen_pixel ) )
{
using ( Graphics source = Graphics.FromHwnd (
IntPtr.Zero ) )
{
IntPtr source_DC = source.GetHdc ( );
IntPtr destination_DC = destination.GetHdc ( );
BitBlt ( destination_DC,
0,
0,
1,
1,
source_DC,
location.X,
location.Y,
( int ) CopyPixelOperation.SourceCopy );
}
}
return ( screen_pixel.GetPixel ( 0, 0 ) );
}
Gus Gustafson
|
|
|
|
|
Thanks. Unfortunately that still produces the same issue.
|
|
|
|
|
I wrote that code to work on multiple monitors. Either something changed in .NET (code was written 5+ years ago) or AutoCAD is interfering (something I suspect). I fear I can only offer you encouragement since I do not have access to AutoCAD.
Gus Gustafson
|
|
|
|
|
No problem. Thank you for your efforts. It does work on the primary screen, I'll deal with it for now.
Still is a great utility. Perhaps I also just use the Windows version (launch the EXE), copy to clipboard, and then read the clipboard value to pass back into my application for now. Not the prettiest solution, but it will get the result I need at least.
|
|
|
|
|
Gus, I found a better workaround.
Perhaps this might benefit you as well. Instead of getting the gradient color by finding the pixel on screen, I found that I can actually create a bitmap of the gradient generated on the panel.
From that, i can pass the point to the bitmap and get the color. Thus on the function generate_back_color(), I have changed to the following that works inside of AutoCAD. I would assume this works in Windows EXE as well - perhaps even simpler logic, though I don't know if there's implications on getting the correct color from bitmap or not. Seems to work ok for me.
Excuse the VB.NET - I converted for my current project that was already in VB.NET
Private Function generate_back_color(ByRef button As Button) As Boolean
Dim point As Point
'Dim screen_point As Point
'point = New Point(((button_PAN.Location.X + button.Location.X) + (button.Size.Width / 2)), (gradient_PAN.Location.Y + (gradient_PAN.Size.Height / 2)))
'screen_point = PointToScreen(point)
'screen_point = point
'screen_point = PointToClient(point)
'button.BackColor = Win32API.get_pixel_color_at_location(screen_point)
point = New Point(((button.Location.X) + (button.Size.Width / 2)), ((gradient_PAN.Size.Height / 2)))
Dim bmp As Bitmap = New Bitmap(gradient_PAN.ClientSize.Width, gradient_PAN.ClientSize.Height)
gradient_PAN.DrawToBitmap(bmp, gradient_PAN.ClientRectangle)
button.BackColor = bmp.GetPixel(point.X, point.Y)
Return (True)
End Function
|
|
|
|
|
I decided to implement Bill Woodruff's algorithm. I have completed the implementation and am currently in the process of publishing the revised article. It totally avoids retrieving pixel color at a specific screen location. I'll let you know when and where the revision is published.
Thanks for your comments.
FYI, your comments were actually the incentive for the revision.
Gus Gustafson
|
|
|
|
|
|
I made a gradient generator as part of a course I took recently, it only allows you to choose two colors and it has a random generation on page load and by clicking a button.
If you're interested in seeing it you can see the page <a href="https://jakepogo.github.io/background-generator/">here</a>[<a href="https://jakepogo.github.io/background-generator/" target="_blank" title="New Window">^</a>]
Also I have the source code public, you can see that <a href="https://github.com/jakepogo/background-generator">GitHub - jakepogo/background-generator: Gradient generator with randomizer</a>[<a href="https://github.com/jakepogo/background-generator" target="_blank" title="New Window">^</a>]
Have a good day
|
|
|
|
|
Nice tool. Thanks for the idea,
Gus Gustafson
|
|
|
|
|
Loading the solution into VS 2019 requires conversion ... that's not a problem, but:
GradientColorPicker\CodeProjectPaper\CodeProjectPaper.csproj : error : The project file could not be loaded. Could not find a part of the path ] That doesn't prevent running the project, but it is failing to produce interpolated colors:
(253,134,197),(222,222,222),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(185,66,134)
(0,0,0),(238,238,242),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(255,255,255)
(255,0,0),(238,238,242),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(76,74,72),(0,255,0)
«One day it will have to be officially admitted that what we have christened reality is an even greater illusion than the world of dreams.» Salvador Dali
|
|
|
|
|
I just found your message. Apparently notifications are turn off by default (I would have turned them on by default). Let me try to reproduce your problem. Please note that the development was VS 2008. I have a copy of VS 2019 so I will try that and report my results.
Thanks for the info.
Gus Gustafson
|
|
|
|
|
If I provide (253,134,197) and (185,66,134) as starting and ending colors, I am given
(253,134,197),(246,127,190),(240,121,185),(234,115,179),(229,110,174),(222,103,168),
(216,97,163),(211,92,158),(204,85,152),(199,80,147),(193,74,141),(185,66,134)
as the interpreted colors. You appear to obtain a shade of gray. I cannot duplicate your experience.
The CodeProjectPaper project should not have been included in the ZIP file. I will remove it and republish. Thanks for the bug report.
Regards,
Gus Gustafson
|
|
|
|
|
|
for linear interpolation, it's very simple compared to using PInvokes: however, if the MS bult-in linear interpolation gives different values, then I can see your rationale.
i've been using this a while:
public static class ColorExtensions
{
public static IEnumerable<Color> GetLinearGradientValues(this Color c1, Color c2, int nsteps)
{
int c1R = c1.R;
int c2R = c2.R;
double Rdiff = (c1.R - c2R) / nsteps;
int c1G = c1.G;
int c2G = c2.G;
double Gdiff = (c1.G - c2G) / nsteps;
int c1B = c1.B;
int c2B = c2.B;
double Bdiff = (c1.B - c2B) / nsteps;
yield return c1;
for (int i = 1; i < nsteps; i++)
{
yield return Color.FromArgb(c1R, c1G, c1B);
c1R = (int) (c1R - Rdiff);
c1G = (int) (c1G - Gdiff);
c1B = (int) (c1B - Bdiff);
}
yield return c2;
}
}
«One day it will have to be officially admitted that what we have christened reality is an even greater illusion than the world of dreams.» Salvador Dali
|
|
|
|
|
Gave it some thought. But as you say, to be assured of properly accessing the MS color space, I chose the GetPixel method.
Thanks for your thoughts (and code).
Stay Safe
Gus Gustafson
|
|
|
|
|
In view of steve-redTrans comments, I decided to implement your algorithm. I have completed the implementation and am currently in the process of publishing the revised article.
I modified your algorithm. During modification, I had to replace nsteps with ( nsteps - 1 ). I also used floating point throughout.
Thanks for your help.
Gus Gustafson
|
|
|
|
|
hi, Gus, i am curious if you did any comparison of interpolated colors and ms' generated colors.
i think i'll publish a fancier version of my interpolation extension for multiple color gradients as a tip-trick ... no ui ... i'll cite your articles.
cheers, bill
«One day it will have to be officially admitted that what we have christened reality is an even greater illusion than the world of dreams.» Salvador Dali
|
|
|
|
|
I had two versions of the tool running. Both returned 8 values. A comparison yielded no differences. I did not run a comprehensive test.
Do you suggest that I do?
Regards, Gus
Gus Gustafson
|
|
|
|
|
gggustafson wrote: Do you suggest that I do? I think if you have the time, and are curious ... it would be interesting
There's so much functionality in the LinearGradientBrush Class I have not explored.
«One day it will have to be officially admitted that what we have christened reality is an even greater illusion than the world of dreams.» Salvador Dali
|
|
|
|
|