Solved

BitBlt Crop

Posted on 2013-10-24
15
544 Views
Last Modified: 2013-10-28
I tried the solutions presented but I am still getting a cropped image that is just a black image.  What am I doing wrong?
        CImage imgOriginal;
        CImage imgCropped;

        imgOriginal.Load(_T("c:/original.tif"));
	imgCropped.Create(600, 600, imgOriginal.GetBPP(), 0);

	HDC hdcSource = imgOriginal.GetDC();
	HDC hdcCropped = imgCropped.GetDC();
	BitBlt(hdcCropped, 0, 0, 600, 600, hdcSource, 600, 600, SRCCOPY);
	imgCropped.ReleaseDC();
	imgOriginal.ReleaseDC();
	imgCropped.Save(_T("c:/Crop.tif"), Gdiplus::ImageFormatTIFF);

Open in new window

0
Comment
Question by:sepsj
  • 6
  • 4
  • 3
15 Comments
 

Author Comment

by:sepsj
ID: 39599485
This is visual c++.  I am programming in Visual Studio 2012
0
 
LVL 30

Expert Comment

by:Zoppo
ID: 39599671
Hi sepsj,

the problem is in your BitBlt call. This expects the parameters 7 and 8 (nXSrc and nYSrc) to define the left-top corner of the rect to be copied from the source bitmap. Since you pass the size of the bitmap instead the BitBlt tries to copy the pixes 600x600 to 1200x1200 from the source bitmap which leads to black pixel since they're outside of the source bitmap.

Just pass 0, 0 instead, i.e.:

> BitBlt(hdcCropped, 0, 0, 600, 600, hdcSource, 0, 0, SRCCOPY);

Hope that helps,

ZOPPO
0
 

Author Comment

by:sepsj
ID: 39599712
my source tiff is 2542x3296.
If I do a:
 imgOriginal.BitBlt(imgOriginal.GetDC(),0,0,600,600,600,600,SRCCOPY);
my imgOriginal will have the 600x600 chunk from the middle to the top left.

If I load up an 600x600 tif (precropped.tif) as my ImgCropped it works:

CImage imgOriginal;
CImage imgCropped;

imgOriginal.Load(_T("c:/original.tif"));
imgCropped.Load(_T("c:/precropped.tif"));
            
imgOriginal.BitBlt(imgCropped.GetDC(), 0, 0, 600, 600, 600, 600, SRCCOPY);
imgCropped.Save(_T("c:/Crop.tif"), Gdiplus::ImageFormatTIFF);

Thanks,

Sean
0
 
LVL 30

Expert Comment

by:Zoppo
ID: 39599833
ok, sorry, I guessed this because the values look so similar ...

Well, I now think it maybe is a problem caused by the original bitmap. Is it a monochrome bitmap? I managed to reproduce the problem with a TIFF with 1 BPP, when I use the same bitmap with 32 (or 24 or 16) BPP it works. Unfortunateley I have no idea yet why it doesn't work with a 1 BPP bitmap, I'll continue testing, but maybe it would be good to first check your problem has the same cause. To do so maybe just for test you can try to hardcode the imgCropped is created with i.e. 32 BPP like this:

    imgCropped.Create(600, 600, 32, 0);

If this works then at least we have a hint where the problem happens.

ZOPPO
0
 
LVL 30

Accepted Solution

by:
Zoppo earned 500 total points
ID: 39599934
Hm - still I'm not sure why CImage doesn't handle this correctly, but I found a workaround which implies the problem is the somehow caused by CImage::Create.

The workaround I found creates the destination bitmap using ::CreateBitmap API function instead of CImage::Create like in this code (I added some error checking too):
	CImage imgOriginal;
	CImage imgCropped;

	if ( !SUCCEEDED( imgOriginal.Load(_T("d:/temp/test1bpp.tif")) ) )
	{
		DWORD dwErr = GetLastError();
		ASSERT( 0 );
		return;
	}

	// Use ::CreateBitmap instead of CImage::Create
	imgCropped.Attach( ::CreateBitmap( 600, 600, 0, imgOriginal.GetBPP(), NULL ) );

	HDC hdcSource = imgOriginal.GetDC();
	HDC hdcCropped = imgCropped.GetDC();

	if ( FALSE == BitBlt(hdcCropped, 0, 0, 600, 600, hdcSource, 600, 600, SRCCOPY) )
	{
		DWORD dwErr = GetLastError();
		ASSERT( 0 );
		return;
	}

	imgCropped.ReleaseDC();
	imgOriginal.ReleaseDC();

	if ( !SUCCEEDED( imgCropped.Save(_T("d:/temp/testCrop.tif"), Gdiplus::ImageFormatTIFF) ) )
	{
		DWORD dwErr = GetLastError();
		ASSERT( 0 );
		return;
	}

Open in new window

IMO it's a bit unsatisfying not to know why this workaround is needed, I have no idea, maybe it's simply a wrong implementation in ATL's CImage, hard to say, but at least if I do the same only using GDI+ it works fine, i.e.:
	Gdiplus::Bitmap* pSrc = Gdiplus::Bitmap::FromFile( A2W( "d:/temp/test1bpp.tif") );

	if ( NULL == pSrc )
	{
		ASSERT( 0 );
		return;
	}

	Gdiplus::Bitmap* pDest = pSrc->Clone( 600, 600, 600, 600, pSrc->GetPixelFormat() );

	SaveToTIFF( pDest, _T( "d:/temp/test1bppGdiplus.tif" ) );

	delete pSrc;
	delete pDest;

Open in new window

ZOPPO
0
 
LVL 32

Expert Comment

by:sarabande
ID: 39600044
if you load the original by imgOriginal.Load(_T("c:/original.tif")); it is not a bitmap but a tif.

you would need to call GetBPP member function to transform it to a bitmap.

if I am right you may try the following:

CImage imgOriginal;
CImage imgOriginalTif;
CImage imgCropped;

imgOriginalTif.Load(_T("c:/original.tif"));
imgOriginal.Create(1200, 1200,   imgOriginalTif.GetBPP(), 0); 
imgCropped.Create(600, 600, imgOriginalTif.GetBPP(), 0);
....

Open in new window


Sara
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 30

Expert Comment

by:Zoppo
ID: 39600082
Ehm, sorry, Sara, but I don't see the point.imgOriginal.GetBPP() is used both in the original question and in my comment with the workaround.

Did you test the original code? Especially with a 1bpp monochrome TIFF?

ZOPPO
0
 

Author Closing Comment

by:sepsj
ID: 39600397
Thanks for the help.  
      imgCropped.Attach( ::CreateBitmap( 600, 600, 0, imgOriginal.GetBPP(), NULL ) );
did the trick.
0
 
LVL 32

Expert Comment

by:sarabande
ID: 39605215
imgOriginal.Load(_T("c:/original.tif"));
imgCropped.Create(600, 600, imgOriginal.GetBPP(), 0);
...
HDC hdcSource = imgOriginal.GetDC();
BitBlt(hdcCropped, 0, 0, 600, 600, hdcSource, 600, 600, SRCCOPY);

from the code above, I assumed that the original image was a tif and not a bitmap. the member function GetBPP obviously provides a bitmap image. if that is true, the device context the BitBlt call would use as source has no bitmap loaded and therefore it is not strange that the rectangle {600,600,1200,1200} would not show an image.

as said, it was an assumption (i don't have an environment where i could test it), which would not explain why the accepted solution worked (if it was used as it was posted by the Asker).

Sara
0
 
LVL 30

Expert Comment

by:Zoppo
ID: 39605255
Hm - so you mean loading the TIF failed? I tested this too, either by checking the return value, further by saving it directly after loading to another file with success. The GetBPP even returned the expected 1 in case of monochrome bitmap. Even when I changed the behavior to create a 24 bit destination bitmap it worked fine, so IMO it cannot have to do something with the source bitmap/DC ...

Best regards,

ZOPPO
0
 
LVL 32

Expert Comment

by:sarabande
ID: 39605261
Hm - so you mean loading the TIF failed?

No, I meant that the CImage has different graphic formats internally and that the original image fully need to be converted from tif to bitmap before it can be used as source in BitBlt call.

Sara
0
 
LVL 30

Expert Comment

by:Zoppo
ID: 39605280
Internally it uses GdiPlus bitmap functionality which can handle the conversion correctly (as I prooved with my last sample. Further with TIFFs with other color depth worked too.

Still I guess it's a problem/bug in the CImage class ...
0
 
LVL 32

Expert Comment

by:sarabande
ID: 39605626
Still I guess it's a problem/bug in the CImage class ...

perhaps. I wonder whether the sequence

imgOriginalTif.Load(_T("c:/original.tif"));
imgOriginal.Create(1200, 1200,   imgOriginalTif.GetBPP(), 0); 
imgCropped.Create(600, 600, imgOriginalTif.GetBPP(), 0);
...
if ( FALSE == BitBlt(hdcCropped, 0, 0, 600, 600, imgOriginal.GetDC(), 600, 600, SRCCOPY) )

Open in new window


also would solve the issue. note, the difference is that imgOriginal was created as a bitmap image rather than loaded as a tif.

maybe, i can find out at my home pc where I have the image library available.

Sara
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Unresolved External Symbols 3 53
endX challenge 2 50
countPairs challenge 7 58
word0 challenge 4 53
I know it’s not a new topic to discuss and it has lots of online contents already available over the net. But Then I thought it would be useful to this site’s visitors and can have online repository on vim most commonly used commands. This post h…
Does the idea of dealing with bits scare or confuse you? Does it seem like a waste of time in an age where we all have terabytes of storage? If so, you're missing out on one of the core tools in every professional programmer's toolbox. Learn how to …
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

23 Experts available now in Live!

Get 1:1 Help Now