Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 614
  • Last Modified:

BitBlt Crop

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
sepsj
Asked:
sepsj
  • 6
  • 4
  • 3
1 Solution
 
sepsjAuthor Commented:
This is visual c++.  I am programming in Visual Studio 2012
0
 
ZoppoCommented:
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
 
sepsjAuthor Commented:
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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
ZoppoCommented:
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
 
ZoppoCommented:
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
 
sarabandeCommented:
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
 
ZoppoCommented:
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
 
sepsjAuthor Commented:
Thanks for the help.  
      imgCropped.Attach( ::CreateBitmap( 600, 600, 0, imgOriginal.GetBPP(), NULL ) );
did the trick.
0
 
sarabandeCommented:
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
 
ZoppoCommented:
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
 
sarabandeCommented:
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
 
ZoppoCommented:
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
 
sarabandeCommented:
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

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

  • 6
  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now