Solved

BitBlt Crop

Posted on 2013-10-24
15
579 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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 34

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 34

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 34

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 34

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

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

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.

Question has a verified solution.

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

If you’re thinking to yourself “That description sounds a lot like two people doing the work that one could accomplish,” you’re not alone.
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 seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…

751 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