Solved

BitBlt Crop

Posted on 2013-10-24
15
552 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
 
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
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
topping1 challenge 7 73
oracle query help 18 99
c++ reading data from file into two dimensional array 3 96
max float value 3 26
Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
Although it can be difficult to imagine, someday your child will have a career of his or her own. He or she will likely start a family, buy a home and start having their own children. So, while being a kid is still extremely important, it’s also …
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.
In this fifth video of the Xpdf series, we discuss and demonstrate the PDFdetach utility, which is able to list and, more importantly, extract attachments that are embedded in PDF files. It does this via a command line interface, making it suitable …

867 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

16 Experts available now in Live!

Get 1:1 Help Now