Click here to Skip to main content
15,907,001 members
Please Sign up or sign in to vote.
5.00/5 (8 votes)
See more:
INTRODUCTION AND RELEVANT INFORMATION:

I have an owner drawn static control.

It should have bitmap on top of a gradient background.

Static controls dimensions are not fixed, they are calculated during the creation of parent window.

I load a bitmap with background color of RGB( 255, 163, 94 ) from resource file.

Using TransparentBlt( ... , RGB( 255, 163, 94 ) ) API I stretch/shrink bitmap to fit the static controls client area ( bitmap is Device Dependant Bitmap, not a DIB, if that matters ).

Bitmap picture has both horizontal and vertical resolution 150 DPI, 24 color bit depth, width 4395 pixels, and height 5613 pixels.

Bitmap is loaded via LoadBitmap( ... ) API on WM_CREATE.

The original picture is bellow, but I have reduced its size, so I can upload it:

http://pbrd.co/1a4P87E[^]

THE PROBLEM:

The problem is in quality of the picture.

Bellow is the result I get after owner drawing the static control:

http://pbrd.co/1a4PkDU[^]

The pixelation is obvious.

Aspect ratio is lost as well, since I use TransparentBlt().

MY EFFORTS TO SOLVE THIS PROBLEM:

Browsing through StackOverflow and CodeProject archive, I have found similar questions, but they were related to Android. None of them helped me. Maybe my inexperience prevents me to see useful information.

Following one link from those questions I have learned about existence of image scaling algorithms. This is the page I have in mind:
http://en.wikipedia.org/wiki/Resampling_%28bitmap%29[^]

I have tried to use smaller bitmap, but picture is then blurry, even though I resolve pixelation to some point.

It seems that my problem lies in insufficient, or to much, data in bitmap so when I try to "push" large bitmap into small button, it just can't fit so I get pixelation and jagged effects, yet If I try to put small bitmap into big button, then data is missing which results in a blurry picture.

After browsing through Internet, I have seen advices and suggestions that this can be solved with vector graphics and meta files.

Since I am dumbstruck by this, I will ask several questions.

If the topic is broad then I ask the community to inform me of it through comments, and I will remove it and will try to rephrase it to fit the CP rules of posting questions better.

QUESTIONS:

This problem really happened unexpected to me, so I do not know how to start solving it, since I am fairly new to WIN32.

I must admit I am shocked, since I have expected that TransparentBlt() would do all the work for me.

How can I solve this problem, so I can keep aspect ratio and quality of the picture ?

I will consider the following solutions:

1. Combining certain GDI API calls into my existing handler for stretching/shrinking, so I can achieve high quality and preserve aspect ratio of the picture, without modifying the code too much ?

2. Using GDI+ to solve my problem. I haven't used GDI+ so far, but one has to start some day, why not now, if that is what will solve my problems ?

3. If vector graphics can solve my problem I must URGE the community to do me the following favor: ANY recommendation of book/tutorial/documentation/article/code example on Vector Graphics in PURE WIN32 would be greatly appreciated, since I myself haven't found any.

4. If I must implement one of the scaling algorithms please recommend some books/tutorials/links/code example, since I myself haven't found any.

I can't use libraries, it is the restriction imposed upon me, however, if the solution is acceptable and can be implemented fast, I could try to convince my employers to loosen their criteria, so libraries can be used.

If my criteria to preserve both aspect ratio and quality of the picture is too much, I will choose the quality of the picture then. I can always post a new question about aspect ratio, and maybe I will find a solution myself in the meanwhile.

I work on Windows XP, using C++ and PURE WIN32 API.

If additional source code is required ask for it, I will gladly post it ( I have omitted it here to preserve space ).

That would be all. Thank you for your patience and time.

Regards.
Posted
Comments
nv3 18-Sep-13 17:10pm    
You can try to play with SetStretchBltMode; my best guess is the HALFTONE settings, which will do kind of interpolation. The other approach is: As your original bitmap is very large, I would first reduce it manually with a good image editing program (Photoshop, Gimp, ...). Then you program has to do only a smaller amount of scaling by TransparentBlt.

Regarding the aspect ratio: You have all cards in your hand. Just make sure the new dimensions that you blt into have the same ratio as your original ones.
Sergey Alexandrovich Kryukov 18-Sep-13 18:00pm    
My 5, just for thoroughly prepared question.
—SA
Thanks7872 19-Sep-13 0:11am    
Mine too. Very very rare these days.
Thanks7872 19-Sep-13 0:11am    
Like the way,the question is framed. BTW,Great job. I appreciate it.
AlwaysLearningNewStuff 19-Sep-13 12:06pm    
Thank you!

Regards.

You may try my DLL[^], however I don't know if it performs well with your 'crisp' images.
 
Share this answer
 
v2
Comments
Sergey Alexandrovich Kryukov 18-Sep-13 17:59pm    
5ed. I would add: even if you use best algorithms and understand the problem well (I must admit OP made a proper research before asking the question), no matter how hard you try, you cannot enlarge the picture considerable with acceptable quality. The best software I tried is the fractal algorithm (results are quite impressive), but is still cannot do a miracle. If you have not enough information in first place, it will be not enough.
—SA
pasztorpisti 19-Sep-13 1:26am    
+5. To downscale performing bliniear or similar algorithms give very good results. Upscaling: There are a few ultra-simple algorithms to do edge preserving upscale on palettized images (they are used with old nintendo/console emulators like mame). One and probably the simplest/fastest (and my favorite) of these algorithms is scale2x. scale2x is a super-simple-dumb algorithm with pretty good output. (A few other similar but more complicated and slower algorithms: http://en.wikipedia.org/wiki/Image_scaling) These upscale by preserving edges and without adding new colors, the scaled image contains only colors used by the original image. (scaling can be done for example in 256 color/plaettized mode). The drawback is that you can upscale only to a multiple of the original size. Still, if you want to upscale with a factor of 1,5 you can upscale for example with a factor of 2 with a simple scale2x and then you can downscale with a more complicated (bilinear or similar algorithm). The upscale is very fast with scale2x, it requires only ram. These algorithms avoid blurring and The edge preserving nature results in much less pixelization and bilinear or similar algorithm is used only for shrinking. Try scale2x (you can use it with scale factors 2, 3, 4 and you can of course repeat scaling). There is a test binary on the site that works on pngs. The linked picture is a very good candidate for this procedure.

EDIT: some additional notes: I usually use scale2x to do real time scaling, for offline scaling (when time isn't that important) I recommend xBRZ but even in case of real time scaling xBRZ can stand its ground depending on the scenario.
EDIT: example: original image, xBRZ scaled
Sergey Alexandrovich Kryukov 19-Sep-13 2:00am    
Interesting, thank you for the comments.
I hope your "+5" goes not to my comment but to Carlo's answer. :-)
—SA
CPallini 19-Sep-13 3:17am    
:-)
pasztorpisti 19-Sep-13 4:31am    
The five was for Carlo, for some reason I was simply lazy to post again. :-)
Top marks for the manner in which the question has been posed. This question is a real treat!
Unfortunately, I can only rate it once..

Given the very large dimensions of your image, I would suggest a technique borrowed from 3d graphics. Mip-mapping. That is to say - you store several copies of the same image, at different resolutions. At display time, you simply pick the appropriately sized image and display it. The advantage is that you can resize images in your favourite image editing program using any of the resizing methods it includes. The downside is a larger executable and a minor increase in complexity in the code that loads and displays the image. Naturally, you could implement such a scaling algorithm in your own code and then use it to dynamically create the appropriately sized images.
Unless you're planning to display a button with the dimensions of 4395x5613 (+ non-client area) then having an image this large serves absolutely no purpose at all. A seperate and appropriately sized image should be included in addition to the button image, should you wish to display the same image at a larger size.

An everyday example of 'mip-mapping' may be found inside .ico files - it's very common for a single file to include copies of the same icon at multiple resolutions. Take for example a window's small icon - it's about 12x12px. Contrast this to a window's large-icon (displayed in task-bar or during alt-tab switching in older versions of windows) - this is 48x48. Vista introduced 128x128 or 256x256 icons (I forget). It's just not practical to resize these small images algorithmically. Icons are hand-drawn or at least, touched-up such that they still provide a good representation of the original graphic.

Wikipedia: Mipmap
It's probably worth pointing out - there's no real need to store the different sized images in a single bitmap. I'd store different sizes as seperate image files. Unless you need the kind of speed often required in high-performance graphics apps, there's no need to minimize cache-misses and increase complexity by stuffing different sizes images into a single file. It would be optimized, but less clear to read.

A fella by the name of Santosh has done some interesting articles regarding the use of openGL to resize images with different filters. They may or may not have utility to you.

Articles by Santosh G_[^]


EDIT:
Assorted code snippets to load and display 32bit images with transparency:

void CSomeClassName::displayImage()
{
    RECT myRect;
    BITMAP bm;
    HDC screenDC, memDC;
    HBITMAP oldBmp;
    BLENDFUNCTION bf;

    GetObject(mBmp, sizeof(bm), &bm);

    bf.BlendOp = AC_SRC_OVER;
    bf.BlendFlags = 0;
    bf.SourceConstantAlpha = 0xff;

    bf.AlphaFormat = AC_SRC_ALPHA;

    screenDC = GetDC(mHwnd);
    GetClientRect(mHwnd, &myRect);

    if (mBmp == NULL)
        FillRect(screenDC, &myRect, WHITE_BRUSH);

    else
    {
        memDC = CreateCompatibleDC(screenDC);
        oldBmp = (HBITMAP)SelectObject(memDC, mBmp);
        AlphaBlend (screenDC, 0, 0, myRect.right,myRect.bottom, memDC, 0, 0, bm.bmWidth,bm.bmHeight, bf);
        SelectObject(memDC, oldBmp);
        DeleteDC(memDC);
        ReleaseDC(mHwnd, screenDC);
    }
}


// BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF
HBITMAP mLoadImg(WCHAR *szFilename)
{
   HBITMAP result=NULL;

   Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(szFilename,false);
   bitmap->GetHBITMAP(0, &result);
   delete bitmap;
   return result;
}

// BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF
HBITMAP loadImgResource(LPCSTR pName, LPCSTR pType, HMODULE hInst)
{
    Gdiplus::Bitmap *m_pBitmap;
    HBITMAP result=NULL;

    HRSRC hResource = FindResource(hInst, pName, pType);
    if (!hResource)
        return NULL;

    DWORD imgSize = SizeofResource(hInst, hResource);
    if (!imgSize)
        return NULL;

    const void *pResourceData = LockResource(LoadResource(hInst, hResource));
    if (!pResourceData)
        return NULL;

    HANDLE m_hBuffer = GlobalAlloc(GMEM_MOVEABLE, imgSize);
	if (m_hBuffer)
	{
		void* pBuffer = GlobalLock(m_hBuffer);
		if (pBuffer)
		{
			CopyMemory(pBuffer, pResourceData, imgSize);
			IStream* pStream = NULL;
			if (CreateStreamOnHGlobal(m_hBuffer, FALSE, &pStream) == S_OK)
			{
				m_pBitmap = Gdiplus::Bitmap::FromStream(pStream);
				pStream->Release();
				if (m_pBitmap)
				{
					if (m_pBitmap->GetLastStatus() == Gdiplus::Ok)
					{
					    m_pBitmap->GetHBITMAP(0, &result);
                        delete m_pBitmap;
					}
				}
			}
			GlobalUnlock(m_hBuffer);
		}
		GlobalFree(m_hBuffer);
	}
	return result;
}

// example of use: tmpBmp = loadImgResource(IDR_PNG1, "PNG"); 
HBITMAP loadImgResource(WORD resNum, LPCSTR pType)
{
    return loadImgResource(MAKEINTRESOURCE(resNum), pType, NULL);
}



Example of GDI+ initialization
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
   static Gdiplus::GdiplusStartupInput gdiplusStartupInput;
   static ULONG_PTR gdiplusToken;

    // so we can load all the image formats that windows supports natively - (I'm using a transparent PNG on the main dialog)
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // run the app
    DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DialogProc);

    // clean-up stuff
    Gdiplus::GdiplusShutdown(gdiplusToken);
    WSACleanup();

    return 0;
}
 
Share this answer
 
v2
Comments
AlwaysLearningNewStuff 19-Sep-13 12:11pm    
Thank you, I will give this a look!

Results will be reported as soon as possible.

Regards.
AlwaysLearningNewStuff 22-Sep-13 14:02pm    
I have made a small test program in VS 2008, and have managed to sharpen the image by using StretchBlt() and SetStretchBltMode() to BLACKONWHITE.

I need to make the background of the bitmap transparent.

Maybe there is a way to combine StretchBlt and TransparentBlt API, so I can achieve this ?

Maybe stretching the bitmap in memory DC, and then somehow to use TransparentBlt ?

By solving this problem, I could tackle the original problem with your advice.

Regards.
enhzflep 22-Sep-13 19:26pm    
I'd be tempted to use a 32bit image - one that includes an alpha channel. I'd then aim to draw it using AlphaBlend (AlphaBlend function

Failing that, you could look at Hans Dietrich's article XCPClock that's here at CP somewhere for some tips for drawing with transparency. Although, as Hans Passant said at SO - fine lines never scale well with cheap scaling algorithms.

I've shown transparent PNG images over dialogs in the past using GDI+ to load the 32bit image, followed by AlphaBlend to display them. I'll add a code snippet to my solution.
AlwaysLearningNewStuff 22-Sep-13 19:57pm    
Thank you, I really really appreciate this.

I am tired right now, so I will try this out during the week, and report my results.

Thanks again.

Regards.
I have "battled" this problem for nearly three months, and after all that research I must admit that Mr. Kryukov and Mr.Pallini were right-no matter how good the algorithm is, for the crisp images is better to use vector graphics. Therefore, I have decided to use vector graphics. Maybe I will use Enhanced Windows Metafiles or will try with some library for SVG.

I hereby thank to everyone trying to help.

Best regards.
 
Share this answer
 
Comments
CPallini 14-Jan-14 16:01pm    
"Mr. Kryukov and Mr.Pallini were right"
This well deserves a 5. :-D

Good luck with vector graphics.
AlwaysLearningNewStuff 15-Jan-14 5:26am    
Thank you for everything-I have 5ed your solution before and thought that it will help me, but the crisp images are just too much for your DLL.

Maybe I will have more luck next time.

Wish you very best in the New Year!

Best regards.
CHill60 14-Jan-14 19:45pm    
5'd also
AlwaysLearningNewStuff 15-Jan-14 5:26am    
Thank you.

Wish you very best in the New Year!

Best regards.
CPallini 15-Jan-14 5:28am    
Best wishes to you, Happy New Year.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900