Place an image on top of another, make gradient background colors transparent - vb.net

I have 2 images, I need to put one on top of the other.
The second image is taken on a pink background (simulated below) and due to the light falloff the background is more sort of a gradient.
I need to place the image on the other one and remove the background color. I would like to define a Hue-range that represents my background, and have every pixel that falls into this range removed/being transparent so that it is pasted on top as if it had a transparent background.
This is the sample image I would like to paste on any random image:
I am able to paste the image onto another image by using this:
' Draw from the source to the destination.
gr.DrawImage(fr_bm, to_rect, fr_rect, GraphicsUnit.Pixel)
(image, destination rectangle, source rectangle)
But I cannot figure out how to remove the background.
Any help is greatly appreciated.

This is a standard Color replacement filter (simplified -> no pre Convolution, since you just want to make transparent all pixels with colors that fall within a range).
It takes a source image, copies it to a 32Bit ARGB bitmap, then generates an identical container, used as destination bitmap.
All colors are compared to the Color specified in the colorFrom argument and, if the Color's components are within a threshold defined by the tolerance argument, the Color is replaced with the Color specified in the colorTo argument.
The tolerance value should be in the range (1:100) (just because Photoshop and other graphic programs do that), the ColorReplacement method normalizes this value on its own.
Possible results:
With the image in your example, with colorFrom set to Color.Fucsia and colorTo set to Color.Transparent, the green region is isolated with a tolerance of ~56, then all remaining traces of the outer Color disappear (along with any anti-aliasing), between 80 and 90. After that, also the green area begins to fade away. Around 95, you have a completely transparent Bitmap.
With a colorFrom set to (255, 226, 18, 212), the same results appear at ~38, then 60 to 70 (the replacement is more subtle).
Which means you have to pick a source color that gives better result, in your view and context.
Try it out passing different values to the method.
C# version:
public Bitmap ColorReplacement(Bitmap image, Color colorFrom, Color colorTo, float tolerance)
{
tolerance = (byte)(255.0f / 100.0f * Math.Max(Math.Min(100.0f, tolerance), 0.1f));
Bitmap source = new(image.Width, image.Height, PixelFormat.Format32bppArgb);
source.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var g = Graphics.FromImage(source)) {
g.PixelOffsetMode = PixelOffsetMode.Half;
g.DrawImage(image, Point.Empty);
}
Bitmap destImage = new(source.Width, source.Height, PixelFormat.Format32bppArgb);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
foreach (PropertyItem item in image.PropertyItems) {
source.SetPropertyItem(item);
destImage.SetPropertyItem(item);
}
var dataFrom = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var dataTo = destImage.LockBits(new Rectangle(0, 0, destImage.Width, destImage.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
byte[] buffer = new byte[Math.Abs(dataTo.Stride) * dataTo.Height];
Marshal.Copy(dataFrom.Scan0, buffer, 0, buffer.Length);
source.UnlockBits(dataFrom);
int bytesPerPixel = Image.GetPixelFormatSize(source.PixelFormat) / 8;
for (int pos = 0; pos < buffer.Length; pos += bytesPerPixel) {
byte blue = buffer[pos];
byte green = buffer[pos + 1];
byte red = buffer[pos + 2];
if ((blue < colorFrom.B + tolerance && blue > colorFrom.B - tolerance) &&
(green < colorFrom.G + tolerance && green > colorFrom.G - tolerance) &&
(red < colorFrom.R + tolerance && red > colorFrom.R - tolerance)) {
int newBlue = colorFrom.B - blue + colorTo.B;
int newGreen = colorFrom.G - green + colorTo.G;
int newRed = colorFrom.R - red + colorTo.R;
buffer[pos] = (byte)Math.Max(Math.Min(255, newBlue), 0);
buffer[pos + 1] = (byte)Math.Max(Math.Min(255, newGreen), 0);
buffer[pos + 2] = (byte)Math.Max(Math.Min(255, newRed), 0);
buffer[pos + 3] = colorTo.A;
}
}
Marshal.Copy(buffer, 0, dataTo.Scan0, buffer.Length);
destImage.UnlockBits(dataTo);
return destImage;
}
VB.NET version:
Public Shared Function ColorReplacement(imageSource As Bitmap, colorFrom As Color, colorTo As Color, tolerance As Single) As Bitmap
tolerance = CByte(255.0F / 100.0F * Math.Max(Math.Min(100.0F, tolerance), 0.1F))
Dim source As New Bitmap(imageSource.Width, imageSource.Height, PixelFormat.Format32bppArgb)
source.SetResolution(imageSource.HorizontalResolution, imageSource.VerticalResolution)
Using g = Graphics.FromImage(source)
g.PixelOffsetMode = PixelOffsetMode.Half
g.DrawImage(imageSource, Point.Empty)
End Using
Dim destImage As Bitmap = New Bitmap(source.Width, source.Height, PixelFormat.Format32bppArgb)
destImage.SetResolution(imageSource.HorizontalResolution, imageSource.VerticalResolution)
For Each item As PropertyItem In imageSource.PropertyItems
source.SetPropertyItem(item)
destImage.SetPropertyItem(item)
Next
Dim dataFrom = source.LockBits(New Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
Dim dataTo = destImage.LockBits(New Rectangle(0, 0, destImage.Width, destImage.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb)
Dim buffer As Byte() = New Byte(Math.Abs(dataFrom.Stride) * dataFrom.Height - 1) {}
Marshal.Copy(dataFrom.Scan0, buffer, 0, buffer.Length)
source.UnlockBits(dataFrom)
Dim bytesPerPixel As Integer = Image.GetPixelFormatSize(source.PixelFormat) \ 8
For pos As Integer = 0 To buffer.Length - 1 Step bytesPerPixel
Dim blue As Integer = buffer(pos)
Dim green As Integer = buffer(pos + 1)
Dim red As Integer = buffer(pos + 2)
If (blue < colorFrom.B + tolerance AndAlso blue > colorFrom.B - tolerance) AndAlso
(green < colorFrom.G + tolerance AndAlso green > colorFrom.G - tolerance) AndAlso
(red < colorFrom.R + tolerance AndAlso red > colorFrom.R - tolerance) Then
Dim newBlue As Integer = colorFrom.B - blue + colorTo.B
Dim newGreen As Integer = colorFrom.G - green + colorTo.G
Dim newRed As Integer = colorFrom.R - red + colorTo.R
buffer(pos) = CByte(Math.Max(Math.Min(255, newBlue), 0))
buffer(pos + 1) = CByte(Math.Max(Math.Min(255, newGreen), 0))
buffer(pos + 2) = CByte(Math.Max(Math.Min(255, newRed), 0))
buffer(pos + 3) = colorTo.A
End If
Next
Marshal.Copy(buffer, 0, dataTo.Scan0, buffer.Length)
destImage.UnlockBits(dataTo)
Return destImage
End Function

Related

How to solve ArgumentException : The parameter is not valid for drawing Arcs

I'm making a custom winforms button in VB.Net with rounded edges and other features. I create a path using various inputs defined by the user and draw and fill it using pens and brushes.
When I call e.Graphics.FillEllipse(Brush1, Rect1) and e.Graphics.DrawEllips(Pen1, Rect1) it just works fine without any problems, but when I try e.Graphics.FillPath(Brush1, OuterPath) and e.Graphics.DrawPath(Pen1, OuterPath) it doesn't work at all. I get this error:
ArgumentException: The parameter is not valid
I tried giving the right types of each variable used in the process and not letting the compiler decide, creating more variables to calculate and manage the inputs individually to not make all the calculations in the inputs of each function, which makes my work easier honestly, and even using the CType function in the inputs of each function to make sure that the function understands what I want as inputs. But everything failed and I don't know what to do next to fix the issue.
Here is the code:
Private Sub MetaniumButton_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim PathWidth As Integer = Width - BorderSize / 2
Dim PathHeight As Integer = Height - BorderSize / 2
_Roundnes = RoundnesMemory
If PathHeight < Roundenes.Height Then
_Roundnes.Height = PathHeight - 1
End If
If PathWidth < Roundenes.Width Then
_Roundnes.Width = PathWidth - 1
End If
e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
Dim OuterPath As New GraphicsPath
Dim Rec1 As Rectangle = New Rectangle(CType(BorderSize / 2, Int32), CType(BorderSize / 2, Int32), CType(_Roundnes.Width, Int32), CType(_Roundnes.Height, Int32))
Dim Rec2 As Rectangle = New Rectangle(PathWidth - _Roundnes.Width, BorderSize / 2, _Roundnes.Width, _Roundnes.Height)
Dim Rec3 As Rectangle = New Rectangle(PathWidth - _Roundnes.Width, PathHeight - _Roundnes.Height, _Roundnes.Width, _Roundnes.Height)
Dim Rec4 As Rectangle = New Rectangle(BorderSize / 2, PathHeight - _Roundnes.Height, _Roundnes.Width, _Roundnes.Height)
OuterPath.StartFigure()
OuterPath.AddLine(CInt(_Roundnes.Width / 2 + BorderSize / 2), CInt(BorderSize / 2), CInt(PathWidth - _Roundnes.Width / 2), CInt(BorderSize / 2))
OuterPath.AddArc(Rec1, 180.0, 90.0) ' Here is the problem and it could probably in any AddArc Function i used
OuterPath.AddLine(PathWidth, CInt(_Roundnes.Height / 2 + BorderSize / 2), PathWidth, CInt(PathHeight - _Roundnes.Height / 2))
OuterPath.AddArc(Rec2, -90, 90)
OuterPath.AddLine(CInt(_Roundnes.Width / 2 + BorderSize / 2), PathHeight, CInt(PathWidth - _Roundnes.Width / 2), PathHeight)
OuterPath.AddArc(Rec3, 0, 90)
OuterPath.AddLine(CInt(BorderSize / 2), CInt(_Roundnes.Height / 2), CInt(BorderSize / 2), CInt(PathHeight - _Roundnes.Height / 2))
OuterPath.AddArc(Rec4, 90, 90)
OuterPath.CloseFigure()
e.Graphics.FillPath(Brush1, OuterPath)
e.Graphics.DrawPath(Pen1, OuterPath)
Dim LabelCount As Integer = 0
For Each l As Label In Controls
LabelCount += 1
Next
Dim TextPlace As New Label With {.Name = "TextLabel",
.Text = Text,
.AutoEllipsis = True,
.Size = New Size(Width -
Margin.Left + Margin.Right + 2 * _Roundnes.Width) / 2, Height - (Margin.Top + Margin.Bottom + 2 * _Roundnes.Height) / 2),
.TextAlign = _TextAlign,
.ForeColor = _FontColor,
.BackColor = _MetaniumBackColor,
.Location = New Point((Width - .Width) / 2, (Height - .Height) / 2)}
AddHandler TextPlace.TextChanged, AddressOf MetaniumButton_TextChanged
AddHandler Me.TextChanged, AddressOf MetaniumButton_TextChanged
Controls.Add(TextPlace)
T += 1
If LabelCount <= 0 Then
0: For Each l As Label In Controls
If l.Name = "TextLabel" Then
l.Text = Text
l.AutoEllipsis = True
l.Size = New Size(Width - (Margin.Left + Margin.Right + 2 * _Roundnes.Width) / 2, Height - (Margin.Top + Margin.Bottom + 2 * _Roundnes.Height) / 2)
l.TextAlign = _TextAlign
l.ForeColor = _FontColor
l.BackColor = _MetaniumBackColor
l.Location = New Point((Width - l.Width) / 2, (Height - l.Height) / 2)
End If
Next
ElseIf LabelCount = 1 Then
For Each l As Label In Controls
If l.Name <> "TextLabel" Then
Controls.Remove(l)
Else
GoTo 1
End If
1: GoTo 0
Next
Else
End If
End Sub
When I track down the bug it seems the problem is in the AddArc() function, and I really don't know why it doesn't work. Any help appreciated.
BTW, I use VB.Net Express 2010 with .Net Framework 4.8.
PS: you can post an answer using either VB.Net or C# I can translate the code from both of them.
I solved My problem, and the answer was to initialize the value or Roundnes to (1,1) at least because my code creates the arcs of the edges using Roundnes to know how wide and long the curving edge
so the solution is to add this line of code before the code responsible for creating the arc.
If _Roundnes = New Size(0, 0) Then _Roundnes = New Size(1, 1)
And that's pretty much it! Thank you for helping me out!

vb Image grid detection

Im looking for a way to detect the center of grid squares when VB.net is feed an image
I want to start with an image of a grid with blue squares like this:
Grid
and i want the program to make an array of points in the center of each square like this (points aren't centered in picture)
Grid with Red points
i dont want to modify the image, i just want to get the points. I've tried getpixel for x and y but that just returns the same point
Dim search_color As Color = Color.FromArgb(255, 64, 128, 192)
Dim background_color As Color = Color.FromArgb(255, 240, 240, 240)
Dim grid_color As Color = Color.FromArgb(255, 144, 144, 144)
Dim pix As Color
Dim liney = 0, linex = 0
Dim loc, sloc, gloc As Point
For ch As Integer = 1 To 64
For y As Integer = liney To Bmp.Height - 1
For x As Integer = linex To Bmp.Width - 1
If Bmp.GetPixel(x, y) = search_color Then
sloc = New Point(x, y)
linex = x
liney = y
x = Bmp.Width - 1
y = Bmp.Height - 1
End If
Next
Next
Dim xloc = 0
For x As Integer = sloc.X To Bmp.Width - 1
If Bmp.GetPixel(x, sloc.Y) = grid_color Then
xloc = x - 1
End If
If Bmp.GetPixel(x, sloc.Y) = background_color Then
xloc = x - 1
End If
Next
For y As Integer = sloc.Y To Bmp.Height - 1
If Bmp.GetPixel(xloc, y) = grid_color Or Bmp.GetPixel(xloc, y) = background_color Then
gloc = New Point(xloc, y - 1)
End If
Next
loc = New Point((gloc.X + sloc.X) / 2, (gloc.Y + sloc.Y) / 2)
liney = gloc.Y
linex = gloc.X + 20
ListBox1.Items.Add(loc.ToString)
Next
Try this:
I added the following controls to a form to test the code:
pbImageToScan (PictureBox) - btnAnalyzeIMG (Button) - lbResult (ListBox)
Public Class Form1
Dim arrCenters() As Point
Dim bmpToAnalyze As Bitmap
Dim search_color As Color = Color.FromArgb(255, 64, 128, 192)
Dim background_color As Color = Color.FromArgb(255, 240, 240, 240)
Dim grid_color As Color = Color.FromArgb(255, 144, 144, 144)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
bmpToAnalyze = New Bitmap(Application.StartupPath & "\Image.bmp")
pbImageToScan.Image = Image.FromFile(Application.StartupPath & "\Image.bmp")
End Sub
Private Sub btnAnalyzeIMG_Click(sender As Object, e As EventArgs) Handles btnAnalyzeIMG.Click
FindCenters()
End Sub
Private Sub FindCenters()
bmpToAnalyze = New Bitmap(Application.StartupPath & "\Image.bmp")
pbImageToScan.Image = Image.FromFile(Application.StartupPath & "\Image.bmp")
'arrCenters is the array who will contains all centers data
ReDim arrCenters(0)
'arrCenters already starts with an element; this boolean is used to handle the first point insertion
Dim bFirstElementAddedToArray As Boolean
lbResult.Items.Clear()
Dim iIMGWidth As Integer = bmpToAnalyze.Width
Dim iIMGHeight As Integer = bmpToAnalyze.Height
'X, Y coordinates used for iterations
Dim iX As Integer = 0
Dim iY As Integer = 0
'Bitmap limits reached
Dim bExit As Boolean
'Used to skip a great part of Ys, if a match has been found along the current examinated line
Dim iDeltaYMax As Integer = 0
'Main cycle
Do While Not bExit
Dim colCurrentColor As Color = bmpToAnalyze.GetPixel(iX, iY)
If colCurrentColor = search_color Then
Dim iXStart As Integer = iX
Dim iYStart As Integer = iY
Dim iXEnd As Integer
Dim iYEnd As Integer
'Width of the Blue square
For iXEnd = iX + 1 To iIMGWidth - 1
Dim colColorSearchX As Color = bmpToAnalyze.GetPixel(iXEnd, iY)
If (colColorSearchX = background_color) Or (colColorSearchX = grid_color) Then
iXEnd -= 1
Exit For
End If
Next
'Height of the Blue square
For iYEnd = iY + 1 To iIMGHeight - 1
Dim colColorSearchY As Color = bmpToAnalyze.GetPixel(iXEnd, iYEnd)
If (colColorSearchY = background_color) Or (colColorSearchY = grid_color) Then
iYEnd -= 1
Exit For
End If
Next
iDeltaYMax = iYEnd - iYStart
'Blue square center coordinates
Dim pCenter As New Point((iXStart + iXEnd) / 2, (iYStart + iYEnd) / 2)
Dim iArrLenght As Integer = 0
If Not bFirstElementAddedToArray Then
bFirstElementAddedToArray = True
Else
iArrLenght = arrCenters.GetLength(0)
ReDim Preserve arrCenters(iArrLenght)
End If
arrCenters(iArrLenght) = pCenter
lbResult.Items.Add(pCenter.ToString)
iX = iXEnd
'Checks if the Width limit of the bitmap has been reached
If iX = (iIMGWidth - 1) Then
iX = 0
iY += iDeltaYMax + 1
iDeltaYMax = 0
Else
iX += 1
End If
Else
'Checks if the Width limit of the bitmap has been reached
If iX = (iIMGWidth - 1) Then
iX = 0
iY += iDeltaYMax + 1
iDeltaYMax = 0
Else
iX += 1
End If
End If
'Width and Height limit of the bitmap have been reached
If (iX = iIMGWidth - 1) And (iY = iIMGHeight - 1) Then
bExit = True
End If
Loop
'Draws a Red point on every center found
For Each P As Point In arrCenters
bmpToAnalyze.SetPixel(P.X, P.Y, Color.Red)
Next
pbImageToScan.Image = bmpToAnalyze
End Sub
End Class

Show WinForm below a cell

How can I show I winform that I create in VB.NET just below the active cell?
I have no idea how to solve this. I found the following promising solutions:
Excel addin: Cell absolute position
-The accepted solution seems too complicated to work reliably. I got an error on the first row (Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long)
-The second solution looked promising, but it didn't give me the right positions for my windows form.
The following adaptations of the second proposed solution does not create any errors but does not put the windows form in the correct position:
Public Sub GetScreenPositionFromCell(cell As Excel.Range, excel As Excel.Application)
Dim x As Double
Dim y As Double
If Not excel.ActiveWindow Is Nothing Then
x = excel.ActiveWindow.PointsToScreenPixelsX(cell.Left)
y = excel.ActiveWindow.PointsToScreenPixelsY(cell.Top)
End If
Me.Left = x
Me.Top = y
Me.Show()
Me.TopMost = True
End Sub
EDIT: #Loating, here is how I have used your code. It's great and I am very happy that you are taking your time to help me with a solution. The x-coordinates seems to work while the x-coordinates are a bit off and more or less off depending on the zoom level.
Public Sub ShowMeBelowActiveCell()
Dim ExcelApp As Excel.Application = CType(AddinExpress.MSO.ADXAddinModule.CurrentInstance, AddinModule).ExcelApp
Dim excelWindow = ExcelApp.ActiveWindow
Dim cell = ExcelApp.ActiveCell
Dim zoomFactor As Double = excelWindow.Zoom / 100
Dim ws = cell.Worksheet
' PointsToScreenPixels returns different values if the scroll is not currently 1
' Temporarily set the scroll back to 1 so that PointsToScreenPixels returns a
' value we know how to handle.
Dim origScrollCol = excelWindow.ScrollColumn
Dim origScrollRow = excelWindow.ScrollRow
excelWindow.ScrollColumn = 1
excelWindow.ScrollRow = 1
' (x,y) are screen coordinates for the top left corner of the top left cell
Dim x As Integer = excelWindow.PointsToScreenPixelsX(0)
' e.g. window.x + row header width
Dim y As Integer = excelWindow.PointsToScreenPixelsY(0)
' e.g. window.y + ribbon height + column headers height
Dim dpiX As Single = 0
Dim dpiY As Single = 0
Using g = Drawing.Graphics.FromHwnd(IntPtr.Zero)
dpiX = g.DpiX
dpiY = g.DpiY
End Using
' Note: Each column width / row height has to be calculated individually.
' Before, tried to use this approach:
' var r2 = (Microsoft.Office.Interop.Excel.Range) cell.Worksheet.Cells[origScrollRow, origScrollCol];
' double dw = cell.Left - r2.Left;
' double dh = cell.Top - r2.Top;
' However, that only works when the zoom factor is a whole number.
' A fractional zoom (e.g. 1.27) causes each individual row or column to round to the closest whole number,
' which means having to loop through.
For i As Integer = origScrollCol To cell.Column - 1
Dim col = DirectCast(ws.Cells(cell.Row, i), Microsoft.Office.Interop.Excel.Range)
Dim ww As Double = col.Width * dpiX / 72
Dim newW As Double = zoomFactor * ww
x += CInt(Math.Round(newW))
Next
For i As Integer = origScrollRow To cell.Row - 1
Dim row = DirectCast(ws.Cells(i, cell.Column), Microsoft.Office.Interop.Excel.Range)
Dim hh As Double = row.Height * dpiY / 72
Dim newH As Double = zoomFactor * hh
y += CInt(Math.Round(newH))
Next
excelWindow.ScrollColumn = origScrollCol
excelWindow.ScrollRow = origScrollRow
Me.StartPosition = Windows.Forms.FormStartPosition.Manual
Me.Location = New Drawing.Point(x, y)
Me.Show()
End Sub
End Class
When the ScrollColumn and ScrollRow are both 1, then PointsToScreenPixelsX/Y seems to return the top left point of the top left visible cell in screen coordinates. Using this, the offset width and height to the active cell is calculated, taking into consideration the zoom setting.
var excelApp = Globals.ThisAddIn.Application;
var excelWindow = excelApp.ActiveWindow;
var cell = excelApp.ActiveCell;
double zoomFactor = excelWindow.Zoom / 100;
var ws = cell.Worksheet;
var ap = excelWindow.ActivePane; // might be split panes
var origScrollCol = ap.ScrollColumn;
var origScrollRow = ap.ScrollRow;
excelApp.ScreenUpdating = false;
// when FreezePanes == true, ap.ScrollColumn/Row will only reset
// as much as the location of the frozen splitter
ap.ScrollColumn = 1;
ap.ScrollRow = 1;
// PointsToScreenPixels returns different values if the scroll is not currently 1
// Temporarily set the scroll back to 1 so that PointsToScreenPixels returns a
// value we know how to handle.
// (x,y) are screen coordinates for the top left corner of the top left cell
int x = ap.PointsToScreenPixelsX(0); // e.g. window.x + row header width
int y = ap.PointsToScreenPixelsY(0); // e.g. window.y + ribbon height + column headers height
float dpiX = 0;
float dpiY = 0;
using (var g = Graphics.FromHwnd(IntPtr.Zero)) {
dpiX = g.DpiX;
dpiY = g.DpiY;
}
int deltaRow = 0;
int deltaCol = 0;
int fromCol = origScrollCol;
int fromRow = origScrollRow;
if (excelWindow.FreezePanes) {
fromCol = 1;
fromRow = 1;
deltaCol = origScrollCol - ap.ScrollColumn; // Note: ap.ScrollColumn/Row <> 1
deltaRow = origScrollRow - ap.ScrollRow; // see comment: when FreezePanes == true ...
}
// Note: Each column width / row height has to be calculated individually.
// Before, tried to use this approach:
// var r2 = (Microsoft.Office.Interop.Excel.Range) cell.Worksheet.Cells[origScrollRow, origScrollCol];
// double dw = cell.Left - r2.Left;
// double dh = cell.Top - r2.Top;
// However, that only works when the zoom factor is a whole number.
// A fractional zoom (e.g. 1.27) causes each individual row or column to round to the closest whole number,
// which means having to loop through.
for (int i = fromCol; i < cell.Column; i++) {
// skip the columns between the frozen split and the first visible column
if (i >= ap.ScrollColumn && i < ap.ScrollColumn + deltaCol)
continue;
var col = ((Microsoft.Office.Interop.Excel.Range) ws.Cells[cell.Row, i]);
double ww = col.Width * dpiX / 72;
double newW = zoomFactor * ww;
x += (int) Math.Round(newW);
}
for (int i = fromRow; i < cell.Row; i++) {
// skip the columns between the frozen split and the first visible column
if (i >= ap.ScrollRow && i < ap.ScrollRow + deltaRow)
continue;
var row = ((Microsoft.Office.Interop.Excel.Range) ws.Cells[i, cell.Column]);
double hh = row.Height * dpiY / 72;
double newH = zoomFactor * hh;
y += (int) Math.Round(newH);
}
ap.ScrollColumn = origScrollCol;
ap.ScrollRow = origScrollRow;
excelApp.ScreenUpdating = true;
Form f = new Form();
f.StartPosition = FormStartPosition.Manual;
f.Location = new Point(x, y);
f.Show();

Finding the ratio in an rgb value

I am fairly new to coding (started early this year) and I'm making a program in VB 2010 express that makes a line chart for values that have been given by the user.
In other words, I ask for values and make the program create rectangles on a canvas, one rectangle for every item added to my ArrayList.
This part of the code works, now I want a gradient color scheme, so another color for every rectangle. To achieve this I tried this:
Dim red As Integer = 254
Dim green As Integer = 141
Dim blue As Integer = 150
calcColor(red, green, blue)
Dim MyBrushColor As Color = Color.FromRgb(red, green, blue)
Private Sub calcColor(ByVal red As Integer, ByVal green As Integer, ByVal blue As Integer)
If (red <= 0 Or green <= 0 Or blue <= 0) Then
red = 254
green = 141
blue = 150
red = red + 8
green = green + 8
blue = blue + 8
End If
If (red >= 254 Or green >= 141 Or blue >= 150) Then
red = 254
green = 141
blue = 150
red = red - 8
green = green - 8
blue = blue - 8
End If
End Sub
Just doing -8 and +8 every time is not going to cut it and once they reach either zero or their inital value they'll have another ratio..
As a very inexperienced coder I have no idea how to calculate this ratio. I just know that it's this kind of code I want.
Don't reinvent the wheel. The GDI+ library provides linear gradient brushes. You define starting point and an end point and colors in between and just use this brush for painting.
Example (will comment below):
Dim bmp As New Bitmap(400, 400)
Using brush As Drawing2D.LinearGradientBrush = New Drawing2D.LinearGradientBrush(New Point(0, 0), _
New Point(400, 400), _
Color.Blue, _
Color.Red)
Using p As New Pen(brush)
Using g As Graphics = Graphics.FromImage(bmp)
For i = 1 To 400 Step 10
g.DrawRectangle(p, i - 5, i - 5, 10, 10)
Next
End Using
End Using
End Using
If PictureBox1.Image IsNot Nothing Then PictureBox1.Image.Dispose()
PictureBox1.Image = bmp
First I create a bitmap as a canvas (bmp).
I then create a new object of the paint class. In the constructor I provide an object of the LinearGradientBrush class, with a start point in the top left corner, and an end point in the lower right with colors blue at the start and red at the end.
I then just paint a row of rectangles along the diagonal with this pen for reference.
This brush can do much more, as well. It can use several points on planes and so on and does the color interpolation for you. You just draw with it. Refer to the MSDN for further details: http://msdn.microsoft.com/de-de/library/system.drawing.drawing2d.lineargradientbrush.aspx
Please only look at this if you get stuck. You will learn more by trying it yourself first. Your teacher has probably seen this.
If you use the HSL colour representation, you should be able to get a nice effect by keeping S (saturation) and L (lightness) constant while varying H (hue). You will need to write functions to convert between RGB and HSL - there are many instances of that on the Internet, so here's another one:
Public Class ColourRepresentation
' Adapted from http://www.geekymonkey.com/Programming/CSharp/RGB2HSL_HSL2RGB.htm
' with conversion from C# to VB.NET by http://www.developerfusion.com/tools/convert/csharp-to-vb/
Public Class HSLcolour
Property H As Double
Property S As Double
Property L As Double
Public Overrides Function ToString() As String
Return String.Format("H={0}, S={1}, L={2}", H, S, L)
End Function
End Class
''' <summary>
''' Convert from HSL to RGB.
''' </summary>
''' <param name="c">An HSLcolour</param>
''' <returns>A System.Drawing.Color with A set to 255.</returns>
''' <remarks>H, S, L in the range [0.0, 1.0].</remarks>
Public Shared Function HSLtoRGB(c As HSLcolour) As Color
Dim r As Double = c.L
Dim g As Double = c.L
Dim b As Double = c.L
Dim v As Double = If((c.L <= 0.5), (c.L * (1.0 + c.S)), (c.L + c.S - c.L * c.S))
If v > 0 Then
Dim m As Double = c.L + c.L - v
Dim sv As Double = (v - m) / v
c.H *= 6.0
Dim sextant As Integer = CInt(Math.Truncate(c.H))
Dim fract As Double = c.H - sextant
Dim vsf As Double = v * sv * fract
Dim mid1 As Double = m + vsf
Dim mid2 As Double = v - vsf
Select Case sextant
Case 0, 6
r = v
g = mid1
b = m
Case 1
r = mid2
g = v
b = m
Case 2
r = m
g = v
b = mid1
Case 3
r = m
g = mid2
b = v
Case 4
r = mid1
g = m
b = v
Case 5
r = v
g = m
b = mid2
End Select
End If
Return Color.FromArgb(255, CByte(r * 255), CByte(g * 255), CByte(b * 255))
End Function
' Given a Color (RGB Struct) in range of 0-255
' Return H,S,L in range of 0-1
''' <summary>
''' Convert from a Color to an HSLcolour.
''' </summary>
''' <param name="rgb">A System.Drawing.Color.</param>
''' <returns>An HSLcolour.</returns>
''' <remarks>Ignores Alpha value in the parameter.</remarks>
Public Shared Function RGBtoHSL(rgb As Color) As HSLcolour
Dim r As Double = rgb.R / 255.0
Dim g As Double = rgb.G / 255.0
Dim b As Double = rgb.B / 255.0
Dim v As Double = Math.Max(r, g)
v = Math.Max(v, b)
Dim m As Double = Math.Min(r, g)
m = Math.Min(m, b)
Dim l As Double = (m + v) / 2.0
If l <= 0.0 Then
Return New HSLcolour With {.H = 0, .L = 0, .S = 0}
End If
Dim vm As Double = v - m
Dim s As Double = vm
If s > 0.0 Then
s /= If((l <= 0.5), (v + m), (2.0 - v - m))
Else
Return New HSLcolour With {.H = 0, .L = 0, .S = 0}
End If
Dim r2 As Double = (v - r) / vm
Dim g2 As Double = (v - g) / vm
Dim b2 As Double = (v - b) / vm
Dim h As Double = 0
If r = v Then
h = (If(g = m, 5.0 + b2, 1.0 - g2))
ElseIf g = v Then
h = (If(b = m, 1.0 + r2, 3.0 - b2))
Else
h = (If(r = m, 3.0 + g2, 5.0 - r2))
End If
h /= 6.0
Return New HSLcolour With {.H = h, .L = l, .S = s}
End Function
End Class
Then you will need a way of varying the hue, which I have used in this crude example of drawing a bar chart (I put one PictureBox on a Form):
Option Strict On
Option Infer On
Public Class Form1
Dim rand As New Random
Dim data As List(Of Double)
Private Function DoubleModOne(value As Double) As Double
While value > 1.0
value -= 1.0
End While
While value < 0.0
value += 1.0
End While
Return value
End Function
Sub DrawBars(sender As Object, e As PaintEventArgs)
Dim target = DirectCast(sender, PictureBox)
e.Graphics.Clear(Color.DarkGray)
' an approximation of the bar width
'TODO: Improve the approximation.
Dim barWidth As Integer = CInt(CDbl(target.Width) / data.Count)
Dim maxBarHeight = target.Height
Using br As New SolidBrush(Color.Black)
Dim r As Rectangle
'TODO: make it work for Color.Gainsboro
Dim startColour = ColourRepresentation.RGBtoHSL(Color.Fuchsia)
' these components are broken out in case something needs to be done to them.
Dim startColourH = startColour.H
Dim startColourS = startColour.S
Dim startColourL = startColour.L
' Using 1.0 as the quotient makes the colours go through the whole spectrum.
Dim colourInc As Double = 1.0 / data.Count
' Only expects data to be in the range (0, 1).
For i = 0 To data.Count - 1
Dim thisHSLcolour As New ColourRepresentation.HSLcolour With {.H = DoubleModOne(startColourH + i * colourInc), .S = startColourS, .L = startColourL}
br.Color = ColourRepresentation.HSLtoRGB(thisHSLcolour)
r = New Rectangle(CInt(i * barWidth), CInt(data(i) * maxBarHeight), barWidth, maxBarHeight)
e.Graphics.FillRectangle(br, r)
Next
End Using
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim nBars = 100
data = New List(Of Double)(nBars)
For i = 0 To nBars - 1
data.Add(rand.NextDouble())
Next
AddHandler PictureBox1.Paint, AddressOf DrawBars
End Sub
End Class
Resulting in:
No-one ever accused me of choosing subtle colours, lol.

contrast of an image in vb2005

Adjust the contrast of an image in C# efficiently
solution for above question is not working in vb 2005
i need solution in vb2005
this is c# code below
public static Bitmap AdjustContrast(Bitmap Image, float Value)
{
Value = (100.0f + Value) / 100.0f;
Value *= Value;
Bitmap NewBitmap = (Bitmap)Image.Clone();
BitmapData data = NewBitmap.LockBits(
new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height),
ImageLockMode.ReadWrite,
NewBitmap.PixelFormat);
unsafe
{
for (int y = 0; y < NewBitmap.Height; ++y)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
int columnOffset = 0;
for (int x = 0; x < NewBitmap.Width; ++x)
{
byte B = row[columnOffset];
byte G = row[columnOffset + 1];
byte R = row[columnOffset + 2];
float Red = R / 255.0f;
float Green = G / 255.0f;
float Blue = B / 255.0f;
Red = (((Red - 0.5f) * Value) + 0.5f) * 255.0f;
Green = (((Green - 0.5f) * Value) + 0.5f) * 255.0f;
Blue = (((Blue - 0.5f) * Value) + 0.5f) * 255.0f;
int iR = (int)Red;
iR = iR > 255 ? 255 : iR;
iR = iR < 0 ? 0 : iR;
int iG = (int)Green;
iG = iG > 255 ? 255 : iG;
iG = iG < 0 ? 0 : iG;
int iB = (int)Blue;
iB = iB > 255 ? 255 : iB;
iB = iB < 0 ? 0 : iB;
row[columnOffset] = (byte)iB;
row[columnOffset + 1] = (byte)iG;
row[columnOffset + 2] = (byte)iR;
columnOffset += 4;
}
}
}
NewBitmap.UnlockBits(data);
return NewBitmap;
}
& here is vb2005 code
Public Shared Function AdjustContrast(Image As Bitmap, Value As Single) As Bitmap
Value = (100F + Value) / 100F
Value *= Value
Dim NewBitmap As Bitmap = DirectCast(Image.Clone(), Bitmap)
Dim data As BitmapData = NewBitmap.LockBits(New Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), ImageLockMode.ReadWrite, NewBitmap.PixelFormat)
For y As Integer = 0 To NewBitmap.Height - 1
Dim row As Pointer(Of Byte) = CType(data.Scan0, Pointer(Of Byte)) + (y * data.Stride)
Dim columnOffset As Integer = 0
For x As Integer = 0 To NewBitmap.Width - 1
Dim B As Byte = row(columnOffset)
Dim G As Byte = row(columnOffset + 1)
Dim R As Byte = row(columnOffset + 2)
Dim Red As Single = R / 255F
Dim Green As Single = G / 255F
Dim Blue As Single = B / 255F
Red = (((Red - 0.5F) * Value) + 0.5F) * 255F
Green = (((Green - 0.5F) * Value) + 0.5F) * 255F
Blue = (((Blue - 0.5F) * Value) + 0.5F) * 255F
Dim iR As Integer = CInt(Red)
iR = If(iR > 255, 255, iR)
iR = If(iR < 0, 0, iR)
Dim iG As Integer = CInt(Green)
iG = If(iG > 255, 255, iG)
iG = If(iG < 0, 0, iG)
Dim iB As Integer = CInt(Blue)
iB = If(iB > 255, 255, iB)
iB = If(iB < 0, 0, iB)
row(columnOffset) = CByte(iB)
row(columnOffset + 1) = CByte(iG)
row(columnOffset + 2) = CByte(iR)
columnOffset += 4
Next
Next
NewBitmap.UnlockBits(data)
Return NewBitmap
End Function
Dim row As Pointer(Of Byte) = CType(data.Scan0, Pointer(Of Byte)) + (y * data.Stride)
above line gives error since vb does not support
Dim row As Pointer(Of Byte)
Here's my attempt at the conversion. I'm using the IntPtr data structure to handle holding the unmanged pointer and to do the pointer addition to get an IntPtr for each row. Then as it goes through the row, I'm using the Marshal.ReadByte and Marshal.WriteByte methods to handle reading and writing the unmanaged data. I tested and it seems to work.
value = (100.0F + value) / 100.0F
value *= value
Dim NewBitmap As Bitmap = DirectCast(Image.Clone(), Bitmap)
Dim data As BitmapData = NewBitmap.LockBits(New Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), ImageLockMode.ReadWrite, NewBitmap.PixelFormat)
For y As Integer = 0 To NewBitmap.Height - 1
Dim RowPtr = IntPtr.Add(data.Scan0, y * data.Stride)
Dim columnOffset As Integer = 0
For x As Integer = 0 To NewBitmap.Width - 1
Dim B As Byte = System.Runtime.InteropServices.Marshal.ReadByte(RowPtr, columnOffset)
Dim G As Byte = System.Runtime.InteropServices.Marshal.ReadByte(RowPtr, columnOffset + 1)
Dim R As Byte = System.Runtime.InteropServices.Marshal.ReadByte(RowPtr, columnOffset + 2)
Dim Red As Single = R / 255.0F
Dim Green As Single = G / 255.0F
Dim Blue As Single = B / 255.0F
Red = (((Red - 0.5F) * value) + 0.5F) * 255.0F
Green = (((Green - 0.5F) * value) + 0.5F) * 255.0F
Blue = (((Blue - 0.5F) * value) + 0.5F) * 255.0F
Dim iR As Integer = CInt(Red)
iR = If(iR > 255, 255, iR)
iR = If(iR < 0, 0, iR)
Dim iG As Integer = CInt(Green)
iG = If(iG > 255, 255, iG)
iG = If(iG < 0, 0, iG)
Dim iB As Integer = CInt(Blue)
iB = If(iB > 255, 255, iB)
iB = If(iB < 0, 0, iB)
System.Runtime.InteropServices.Marshal.WriteByte(RowPtr, columnOffset, CByte(iB))
System.Runtime.InteropServices.Marshal.WriteByte(RowPtr, columnOffset + 1, CByte(iG))
System.Runtime.InteropServices.Marshal.WriteByte(RowPtr, columnOffset + 2, CByte(iR))
columnOffset += 4
Next
Next
NewBitmap.UnlockBits(data)