Solved

BitBlt Crop

Posted on 2013-10-24
15
567 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 31

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
Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
LVL 31

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 31

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 33

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
 
LVL 31

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 33

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 31

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 33

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 31

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 33

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

Networking for the Cloud Era

Join Microsoft and Riverbed for a discussion and demonstration of enhancements to SteelConnect:
-One-click orchestration and cloud connectivity in Azure environments
-Tight integration of SD-WAN and WAN optimization capabilities
-Scalability and resiliency equal to a data center

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Whether you’re a college noob or a soon-to-be pro, these tips are sure to help you in your journey to becoming a programming ninja and stand out from the crowd.
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

839 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