I am having some difficulties in correctly populating a CListCtrl with thumbnails of monitor displays.
On the right of my CDialog I have a static control and I render the image on a white canvas like this:
void CCenterCursorOnScreenDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if (nIDCtl == IDC_STATIC_MONITOR && !m_imgPreview.IsNull())
{
// Set the mode
SetStretchBltMode(lpDrawItemStruct->hDC, HALFTONE);
// Wipe the canvas
FillRect(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
// Get canvas rectangle
const CRect rectCanvas(lpDrawItemStruct->rcItem);
// Calculate ratio factors
const float nRatioImage = m_imgPreview.GetWidth() / static_cast<float>(m_imgPreview.GetHeight());
const float nRatioCanvas = rectCanvas.Width() / static_cast<float>(rectCanvas.Height());
// Calculate new rectangle size
// Account for portrait images (negative values)
CRect rectDraw = rectCanvas;
if (nRatioImage > nRatioCanvas)
rectDraw.SetRect(0, 0, rectDraw.right, static_cast<int>(rectDraw.right / nRatioImage));
else if (nRatioImage < nRatioCanvas)
rectDraw.SetRect(0, 0, static_cast<int>((rectDraw.bottom * nRatioImage)), rectDraw.bottom);
// Add a margin
rectDraw.DeflateRect(5, 5);
// Move to center
const CSize ptOffset = rectCanvas.CenterPoint() - rectDraw.CenterPoint();
rectDraw.OffsetRect(ptOffset);
// Add a black frame
FrameRect(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
// Draw
m_imgPreview.Draw(lpDrawItemStruct->hDC, rectDraw);
return;
}
CDialogEx::OnDrawItem(nIDCtl, lpDrawItemStruct);
}
The above works beautifully:
But I have problems with the CListCtrl versions of the images. For instance, I am losing the colouring as you can see.
My CImageList is created like this:
m_ImageListThumb.Create(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, ILC_COLOR32, 0, 1);
m_ListThumbnail.SetImageList(&m_ImageListThumb, LVSIL_NORMAL);
I then create all the thumbnails by calling DrawThumbnails() in OnInitDialog:
void CCenterCursorOnScreenDlg::DrawThumbnails()
{
int monitorIndex = 0;
m_ListThumbnail.SetRedraw(FALSE);
for (auto& strMonitor : m_monitors.strMonitorNames)
{
CImage img;
CreateMonitorThumbnail(monitorIndex, img, true);
CBitmap* pImage = new CBitmap();
pImage->Attach((HBITMAP)img);
m_ImageListThumb.Add(pImage, nullptr);
CString strMonitorDesc = m_monitors.strMonitorNames.at(monitorIndex);
strMonitorDesc.AppendFormat(L" (Screen %d)", monitorIndex + 1);
m_ListThumbnail.InsertItem(monitorIndex, strMonitorDesc, monitorIndex);
monitorIndex++;
delete pImage;
}
m_ListThumbnail.SetRedraw(TRUE);
}
The CreateMonitorThumbnail function:
BOOL CCenterCursorOnScreenDlg::CreateMonitorThumbnail(const int iMonitorIndex, CImage &rImage, bool bSmall)
{
const CRect rcCapture = m_monitors.rcMonitors.at(iMonitorIndex);
// destroy the currently contained bitmap to create a new one
rImage.Destroy();
auto nWidth = rcCapture.Width();
auto nHeight = rcCapture.Height();
if (bSmall)
{
nWidth = THUMBNAIL_WIDTH;
nHeight = THUMBNAIL_HEIGHT;
}
// create bitmap and attach it to this object
if (!rImage.Create(nWidth, nHeight, 32, 0))
{
AfxMessageBox(L"Cannot create image!", MB_ICONERROR);
return FALSE;
}
// create virtual screen DC
CDC dcScreen;
dcScreen.CreateDC(_T("DISPLAY"), nullptr, nullptr, nullptr);
// copy the contents from the virtual screen DC
BOOL bRet = FALSE;
if (bSmall)
{
CRect rt(0, 0, nWidth, nHeight);
//::FillRect(rImage.GetDC(), rt, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
bRet = ::StretchBlt(rImage.GetDC(), 0, 0,
nWidth,
nHeight,
dcScreen.m_hDC,
rcCapture.left,
rcCapture.top,
rcCapture.Width(),
rcCapture.Height(), SRCCOPY | CAPTUREBLT);
}
else
{
bRet = ::BitBlt(rImage.GetDC(), 0, 0,
rcCapture.Width(),
rcCapture.Height(),
dcScreen.m_hDC,
rcCapture.left,
rcCapture.top, SRCCOPY | CAPTUREBLT);
}
// do cleanup and return
dcScreen.DeleteDC();
rImage.ReleaseDC();
return bRet;
}
Ideally I want to have exactly the same kind of visual image as on the right, but obviously resized down. How do I fix this?
I simplified the converting from CImage to CBitmap but it made no difference:
void CCenterCursorOnScreenDlg::DrawThumbnails()
{
int monitorIndex = 0;
// Stop redrawing the CListCtrl
m_ListThumbnail.SetRedraw(FALSE);
// Loop monitor info
for (auto& strMonitor : m_monitors.strMonitorNames)
{
// Create the thumbnail image
CImage monitorThumbnail;
CreateMonitorThumbnail(monitorIndex, monitorThumbnail, true);
// Convert it to a CBitmap
CBitmap* pMonitorThumbnailBitmap = CBitmap::FromHandle(monitorThumbnail);
// Add the CBitmap to the CImageList
m_ImageListThumb.Add(pMonitorThumbnailBitmap, nullptr);
// Build the caption description
CString strMonitorDesc = m_monitors.strMonitorNames.at(monitorIndex);
strMonitorDesc.AppendFormat(L" (Screen %d)", monitorIndex + 1);
// Add the item to the CListCtrl
m_ListThumbnail.InsertItem(monitorIndex, strMonitorDesc, monitorIndex);
monitorIndex++;
}
// Start redrawiung the CListCtrl again
m_ListThumbnail.SetRedraw(TRUE);
}
If I change my code to pass false for the last parameter, so that it uses the original captured images without scaling down:
The colours are god there, so it is when I do:
if (bSmall)
{
CRect rt(0, 0, nWidth, nHeight);
//::FillRect(rImage.GetDC(), rt, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
bRet = ::StretchBlt(rImage.GetDC(), 0, 0,
nWidth,
nHeight,
dcScreen.m_hDC,
rcCapture.left,
rcCapture.top,
rcCapture.Width(),
rcCapture.Height(), SRCCOPY | CAPTUREBLT);
}
that it messes up.
My issue was did not have anything to do with OnDrawItem. I simply included that to indicate how the image on the right was being rendered. I thought it may helped as background information. But it has probably confused the question and I may take it out in the long run!
Based on the comments I was reminded about SetStretchBltMode which was missing from CreateMonitorThumbnail. So, I now have this function:
BOOL CCenterCursorOnScreenDlg::CreateMonitorThumbnail(const int iMonitorIndex, CImage &rImage, bool bResizeAsThumbnail)
{
const CRect rcCapture = m_monitors.rcMonitors.at(iMonitorIndex);
// Destroy the currently contained bitmap to create a new one
rImage.Destroy();
// Massage the dimensions as we want a thumbnail
auto nWidth = rcCapture.Width();
auto nHeight = rcCapture.Height();
if (bResizeAsThumbnail)
{
nWidth = m_iThumbnailWidth;
auto dRatio = rcCapture.Width() / nWidth;
//nHeight = m_iThumbnailHeight;
nHeight = static_cast<int>(rcCapture.Height() / dRatio);
if (nHeight > m_iThumbnailHeight)
{
AfxMessageBox(L"Need to investigate!");
}
}
// Create bitmap and attach it to this object
if (!rImage.Create(nWidth, nHeight, 32, 0))
{
AfxMessageBox(L"Cannot create image!", MB_ICONERROR);
return FALSE;
}
// Create virtual screen DC
CDC dcScreen;
dcScreen.CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);
// Copy (or resize) the contents from the virtual screen DC
BOOL bRet = FALSE;
auto dcImage = rImage.GetDC();
if (bResizeAsThumbnail)
{
// Set the mode first!
SetStretchBltMode(dcImage, COLORONCOLOR);
CPen penBlack;
penBlack.CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
::Rectangle(dcImage, 0, 0, m_iThumbnailWidth, m_iThumbnailHeight);
int iTop = (m_iThumbnailHeight - nHeight) / 2;
// Copy (and resize)
bRet = ::StretchBlt(dcImage, 0, iTop,
nWidth,
nHeight,
dcScreen.m_hDC,
rcCapture.left,
rcCapture.top,
rcCapture.Width(),
rcCapture.Height(), SRCCOPY | CAPTUREBLT);
}
else
{
// Copy
bRet = ::BitBlt(dcImage, 0, 0,
rcCapture.Width(),
rcCapture.Height(),
dcScreen.m_hDC,
rcCapture.left,
rcCapture.top, SRCCOPY | CAPTUREBLT);
}
// Do cleanup and return
dcScreen.DeleteDC();
rImage.ReleaseDC();
return bRet;
}
That was the key to getting the thumbnail showing with the right colours:
Related
I have some code to create thumbnails of the monitors connected to a PC. They are rendered into a list control.
This is the code that iterates the monitors to create the thumbnails:
void CCenterCursorOnScreenDlg::DrawThumbnails()
{
int monitorIndex = 0;
// Stop redrawing the CListCtrl
m_ListThumbnail.SetRedraw(FALSE);
// Loop monitor info
for (auto& strMonitor : m_monitors.strMonitorNames)
{
// Create the thumbnail image
CImage monitorThumbnail;
CreateMonitorThumbnail(monitorIndex, monitorThumbnail, true);
// Convert it to a CBitmap
CBitmap* pMonitorThumbnailBitmap = CBitmap::FromHandle(monitorThumbnail);
// Add the CBitmap to the CImageList
m_ImageListThumb.Add(pMonitorThumbnailBitmap, nullptr);
// Build the caption description
CString strMonitorDesc = m_monitors.strMonitorNames.at(monitorIndex);
strMonitorDesc.AppendFormat(L" (Screen %d)", monitorIndex + 1);
// Add the item to the CListCtrl
m_ListThumbnail.InsertItem(monitorIndex, strMonitorDesc, monitorIndex);
monitorIndex++;
}
// Start redrawing the CListCtrl again
m_ListThumbnail.SetRedraw(TRUE);
}
The m_monitors variable is an instance of:
struct MonitorRects
{
std::vector<RECT> rcMonitors;
std::vector<CString> strMonitorNames;
static BOOL CALLBACK MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData)
{
MonitorRects* pThis = reinterpret_cast<MonitorRects*>(pData);
pThis->rcMonitors.push_back(*lprcMonitor);
MONITORINFOEX sMI{};
sMI.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hMon, &sMI);
pThis->strMonitorNames.emplace_back(sMI.szDevice);
return TRUE;
}
MonitorRects()
{
EnumDisplayMonitors(nullptr, nullptr, MonitorEnum, (LPARAM)this);
}
};
The initial thumbnail sizes is determined in OnInitDialog:
// Use monitor 1 to work out the thumbnail sizes
CRect rcMonitor = m_monitors.rcMonitors.at(0);
m_iThumbnailWidth = rcList.Width();
double dHt = ((double)rcMonitor.Height() / (double)rcMonitor.Width()) * (double)m_iThumbnailWidth;
m_iThumbnailHeight = (int)dHt;
These values are used when creating the CImageList to show the images.
Finally, I have the function that is supposed to make the thumbnails:
bool CCenterCursorOnScreenDlg::CreateMonitorThumbnail(const int iMonitorIndex, CImage &rImage, bool bResizeAsThumbnail)
{
const CRect rcCapture = m_monitors.rcMonitors.at(iMonitorIndex);
// Destroy the currently contained bitmap to create a new one
rImage.Destroy();
// Massage the dimensions as we want a thumbnail
auto nWidth = rcCapture.Width();
auto nHeight = rcCapture.Height();
if (bResizeAsThumbnail)
{
nWidth = m_iThumbnailWidth;
double dHt = ((double)rcCapture.Height() / (double)rcCapture.Width()) * (double)m_iThumbnailWidth;
nHeight = (int)dHt;
if (nHeight > m_iThumbnailHeight)
{
// Aspect ratio of this monitor is not the same as the primary monitor
}
}
// Create bitmap and attach it to this object
if (!rImage.Create(nWidth, nHeight, 32, 0))
{
AfxMessageBox(L"Cannot create image!", MB_ICONERROR);
return false;
}
// Create virtual screen DC
CDC dcScreen;
dcScreen.CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);
// Copy (or resize) the contents from the virtual screen DC
BOOL bRet = FALSE;
auto dcImage = rImage.GetDC();
if (bResizeAsThumbnail)
{
// Set the mode first!
SetStretchBltMode(dcImage, COLORONCOLOR);
int iTop = (m_iThumbnailHeight - nHeight) / 2;
// Copy (and resize)
bRet = ::StretchBlt(dcImage, 0, iTop,
nWidth,
nHeight,
dcScreen.m_hDC,
rcCapture.left,
rcCapture.top,
rcCapture.Width(),
rcCapture.Height(), SRCCOPY | CAPTUREBLT);
}
else
{
// Copy
bRet = ::BitBlt(dcImage, 0, 0,
rcCapture.Width(),
rcCapture.Height(),
dcScreen.m_hDC,
rcCapture.left,
rcCapture.top, SRCCOPY | CAPTUREBLT);
}
// Do cleanup and return
dcScreen.DeleteDC();
rImage.ReleaseDC();
return bRet;
}
On my PC at how where I have two monitors of the same dimensions it works fine. But when I tried it at another site, which has two large TVs connected to a laptop, and an additional monitor connected to the laptop, the thumbnails render wrong:
I would say that the thumbnail of screen two (the TVs) is about 2/3 of the size.
I was hoping to create a set of thumbnails for my list control of all monitors and did not anticipate this. What am I doing wrong?
I am wondering if the SetStretchBltMode / StretchBlt logic is incorrect that does the transform.
Update
I just realised:
GetMonitorInfo provides the screen data in virtual coordinates.
StretchBlt uses logical screen coordinates.
Is this the reason I am ended up with an incorrect thumbnail when I am trying to take another monitors screen and scale it down?
I need to print some text (using font specified), than print a bitmap, using MFC. I can draw text on bitmap, than print this bitmap, using code below - but I need to print text, and than print bitmap in the bottom. The bitmap must be loaded from file.
CFont j1;
j1.CreateFont(
120, // nHeight
120, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
RUSSIAN_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
"Arial"); // lpszFacename // lpszFacename
int w = 600, h = 400;
CClientDC dc(this);
CBitmap bmp;
CDC memdc;
memdc.CreateCompatibleDC(&dc);
bmp.CreateCompatibleBitmap(&dc, w, h);
if (!bmp.Attach(::LoadImage(
::GetModuleHandle(NULL), "D:\\UPM\\BMP\\Login.bmp", IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE))) {
AfxMessageBox(_T("Error loading bitmap!")); return;
}
BITMAP bm;
bmp.GetBitmap(&bm);
auto oldbmp = memdc.SelectObject(bmp);
CFont* pOldFont = memdc.SelectObject(&j1);
//draw on bitmap
///memdc.FillSolidRect(0, 0, w, h, RGB(200, 200, 200));
memdc.SetTextColor(RGB(255, 0, 0));
CRect rc(0, 0, w, h);
memdc.DrawText("qwerty\nrtrtrt\nttttt", &rc, DT_WORDBREAK | DT_EXPANDTABS | DT_CENTER);
///pDC->DrawText(dpu, strRect, DT_WORDBREAK | DT_EXPANDTABS | DT_CENTER);
//dc.BitBlt(0, 0, w, h, &memdc, 0, 0, SRCCOPY);//optional: draw the bitmap on dialog
CPrintDialog pd(false);
if (pd.DoModal() == IDOK)
{
CDC PrintDC;
HDC hdc = pd.GetPrinterDC();
PrintDC.Attach(hdc);
DOCINFO docinfo = { sizeof(docinfo) };
docinfo.lpszDocName = "Print test";
PrintDC.StartDoc(&docinfo);
PrintDC.StartPage();
PrintDC.BitBlt(0, 0, w, h, &memdc, 0, 0, SRCCOPY);
PrintDC.EndPage();
PrintDC.EndDoc();
}
dc.SelectObject(oldbmp);
CClientDC dc(this);
dc.DrawText(...);
...
PrintDC.BitBlt(0, 0, w, h, &memdc, 0, 0, SRCCOPY);
You are drawing text on display's "client DC", then drawing a bitmap on a "printer DC".
Use instead a paint function for everything, and a paint_bitmap function to make things easier.
void paint_bitmap(CDC& dc, CBitmap &bmp, CRect rc)
{
CDC memdc;
memdc.CreateCompatibleDC(&dc);
auto oldbmp = memdc.SelectObject(&bmp);
dc.BitBlt(rc.left, rc.top, rc.Width(), rc.Height(), &memdc, 0, 0, SRCCOPY);
memdc.SelectObject(oldbmp);
}
void paint(CDC& dc)
{
CBitmap bmp;
if (!bmp.Attach(...))...
dc.DrawText(L"text", &rc, ...);
rc.OffsetRect(0, 200);
paint_bitmap(dc, bmp, rc);
}
...
CPrintDialog pd(false);
if (pd.DoModal() == IDOK)
{
HDC hdc = pd.GetPrinterDC();
if (hdc)
{
...
dc.StartPage();
paint(dc); //<- use this for printer or display
dc.EndPage();
}
To print directly to default printer:
CPrintDialog pd(false);
if (pd.GetDefaults() && pd.m_pd.hDC)
{
CDC dc;
dc.Attach(pd.m_pd.hDC);
DOCINFO docinfo = { sizeof(docinfo) };
docinfo.lpszDocName = L"Print test";
dc.StartDoc(&docinfo);
dc.StartPage();
paint(dc);
dc.EndPage();
dc.EndDoc();
}
else
{
MessageBox(L"no default printer...");
}
or if you want a particular printer which you are sure is there, use
HDC hdc = CreateDC(L"WINSPOOL", L"Microsoft Print to PDF", NULL, NULL);
if (hdc)
{
CDC dc;
dc.Attach(hdc);
...
//DeleteDC(hdc); CDC will take care of delete
}
I want to print a bitmap. To avoid printing small bitmap I set CScrollView mode as MM_LOMETRIC with sizes 3830 x 1995. I have created the bitmap and made the bitblt to the screen. There were everythig just like I want on the screen and on the print preview but when I printed the document I've got very bad result.
The same picture on print preview.
It seems to me that printer does not see a bitmap the same way as print preview does.
Pay attantion that the first ractangle puts directly on the DC and memDC puts into it.
Are there any ideas how to fix this mismatch between print previw and the real printing?
Project files
void OnDraw()
{
CPen pen;
pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
CPen* OldPen = pDC->SelectObject(&pen);
CRect rcView;
GetClientRect(rcView);
int iClientWidth = rcView.right;
int iClientHeight = rcView.bottom;
int iMemWidth = 1900;
int iMemHeight = 950;
CDC memDC;
CBitmap memBitmap;
memDC.CreateCompatibleDC(pDC);
memBitmap.CreateCompatibleBitmap(pDC, iMemWidth, iMemHeight);
memDC.SelectObject(&memBitmap);
memDC.SetMapMode(MM_LOMETRIC);
CPen pen1;
pen1.CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
memDC.SelectObject(&pen1);
CBrush brBK;
brBK.CreateSolidBrush(RGB(255, 255, 255));
memDC.SelectObject(&brBK);
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = iMemWidth;
rc.bottom = iMemHeight;
memDC.FillRect(&rc, &brBK);
memDC.Rectangle(rc.left, rc.top, rc.right, -rc.bottom);
memDC.MoveTo(0, 0);
memDC.LineTo(1900, -950);
memDC.MoveTo(0, -950);
memDC.LineTo(200, -750);
CFont font;
font.CreateFont(
50, // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
_T("Arial"));
memDC.SelectObject(&font);
memDC.TextOut(100, -100, _T("Hello"));
pDC->BitBlt(10, -10, iMemWidth, -iMemHeight, &memDC, 0, 0, SRCCOPY);
font.DeleteObject();
brBK.DeleteObject();
memDC.DeleteDC();
memBitmap.DeleteObject();
pen.DeleteObject();
pen1.DeleteObject();
}
void OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal;
sizeTotal.cx = 3830;
sizeTotal.cy = 1995;
SetScrollSizes(MM_LOMETRIC, sizeTotal);
}
How can I apply gradient effect on image like this image in c#. I have a transparent image with black drawing I want to apply 2 color gradient on the image is this possible in gdi?
Here is the effect i want to achieve
http://postimg.org/image/ikz1ie7ip/
You create a PathGradientBrush and then you draw your texts with that brush.
To create a bitmap filled with a gradient brush you could do something like:
public Bitmap GradientImage(int width, int height, Color color1, Color color2, float angle)
{
var r = new Rectangle(0, 0, width, height);
var bmp = new Bitmap(width, height);
using (var brush = new LinearGradientBrush(r, color1, color2, angle, true))
using (var g = Graphics.FromImage(bmp))
g.FillRectangle(brush, r);
return bmp;
}
So now that you have an image with the gradient in it, all you have to do is to bring over the alpha channel from your original image into the newly created image. We can take the transferOneARGBChannelFromOneBitmapToAnother function from a blog post I once wrote:
public enum ChannelARGB
{
Blue = 0,
Green = 1,
Red = 2,
Alpha = 3
}
public static void transferOneARGBChannelFromOneBitmapToAnother(
Bitmap source,
Bitmap dest,
ChannelARGB sourceChannel,
ChannelARGB destChannel )
{
if ( source.Size!=dest.Size )
throw new ArgumentException();
Rectangle r = new Rectangle( Point.Empty, source.Size );
BitmapData bdSrc = source.LockBits( r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb );
BitmapData bdDst = dest.LockBits( r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb );
unsafe
{
byte* bpSrc = (byte*)bdSrc.Scan0.ToPointer();
byte* bpDst = (byte*)bdDst.Scan0.ToPointer();
bpSrc += (int)sourceChannel;
bpDst += (int)destChannel;
for ( int i = r.Height * r.Width; i > 0; i-- )
{
*bpDst = *bpSrc;
bpSrc += 4;
bpDst += 4;
}
}
source.UnlockBits( bdSrc );
dest.UnlockBits( bdDst );
}
Now you could do something like:
var newImage = GradientImage( original.Width, original.Height, Color.Yellow, Color.Blue, 45 );
transferOneARGBChannelFromOneBitmapToAnother( original, newImage, ChannelARGB.Alpha, ChannelARGB.Alpha );
And there you are. :-)
public static class ImageHelper
{
public static UIImage Scale (UIImage source, SizeF newSize)
{
UIGraphics.BeginImageContext (newSize);
var context = UIGraphics.GetCurrentContext ();
context.InterpolationQuality=CGInterpolationQuality.High;
context.TranslateCTM (0, newSize.Height);
context.ScaleCTM (1f, -1f);
context.DrawImage (new RectangleF (0, 0, newSize.Width, newSize.Height), source.CGImage);
var scaledImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return scaledImage;
}
public static UIImage Rotate (UIImage image)
{
UIImage res;
using (CGImage imageRef = image.CGImage) {
CGImageAlphaInfo alphaInfo = imageRef.AlphaInfo;
CGColorSpace colorSpaceInfo = CGColorSpace.CreateDeviceRGB ();
if (alphaInfo == CGImageAlphaInfo.None) {
alphaInfo = CGImageAlphaInfo.NoneSkipLast;
}
int width, height;
width = imageRef.Width;
height = imageRef.Height;
int maxSize = Math.Max (width, height);
if (height >= width) {
width = (int)Math.Floor ((double)width * ((double)maxSize / (double)height));
height = maxSize;
} else {
height = (int)Math.Floor ((double)height * ((double)maxSize / (double)width));
width = maxSize;
}
CGBitmapContext bitmap;
if (image.Orientation == UIImageOrientation.Up || image.Orientation == UIImageOrientation.Down) {
bitmap = new CGBitmapContext (IntPtr.Zero, width, height, imageRef.BitsPerComponent, imageRef.BytesPerRow, colorSpaceInfo, alphaInfo);
} else {
bitmap = new CGBitmapContext (IntPtr.Zero, height, width, imageRef.BitsPerComponent, imageRef.BytesPerRow, colorSpaceInfo, alphaInfo);
}
switch (image.Orientation) {
case UIImageOrientation.Left:
bitmap.RotateCTM ((float)Math.PI / 2);
bitmap.TranslateCTM (0, -height);
break;
case UIImageOrientation.Right:
bitmap.RotateCTM (-((float)Math.PI / 2));
bitmap.TranslateCTM (-width, 0);
break;
case UIImageOrientation.Up:
break;
case UIImageOrientation.Down:
bitmap.TranslateCTM (width, height);
bitmap.RotateCTM (-(float)Math.PI);
break;
}
bitmap.DrawImage (new Rectangle (0, 0, width, height), imageRef);
res = UIImage.FromImage (bitmap.ToImage ());
bitmap = null;
}
return res;
}
}
Calling method=> UIImage ScaledImage =ImageHelper.Scale (ImageHelper.Rotate (downloadedImage), new SizeF (270, 260));
Please provide any solution to maintain quality as well as it will scale to required size.
Xamarin's UIImage implementation contains two different Scale() methods
Scale(System.Drawing.SizeF) : UIImage
Scale(System.Drawing.SizeF, float) : UIImage