// 32BitDibWrapper.cpp: implementation of the C32BitDibWrapper class. // ////////////////////////////////////////////////////////////////////// #include "precomp.h" #pragma hdrstop #include "32BitDib.h" // helper functions // sum of RGB vals inline ULONG Intensity(ULONG value) { return(value&0xff)+((value&0xff00)>>8)+((value&0xff0000)>>16); } // we should make a Difference Template to clean up this code inline UCHAR Difference(UCHAR a, UCHAR b) { if (a>b) return(a-b); else return(b-a); } inline ULONG Difference(ULONG a, ULONG b) { if (a>b) return(a-b); else return(b-a); } inline LONG Difference(LONG a, LONG b) { if (a>b) return(a-b); else return(b-a); } int inline MAX(int a, int b) { if (a>b) return(a); return(b); } int inline MIN(int a, int b) { if (a>8); b=(UCHAR)((value& 0xff0000)>>16); // use this instead of the complete formula (uncomment the commented out code for the complete formula) // allow yellow scanner backgrounds return(ULONG)(Difference(b,g));//+Difference(r,g)+Difference(g,b)); } // sets up a C32BitDibWrapper where each pixel (x,y) is the difference bettween the value of the pixel (x,y) on // bitmap1 and the pixel (x,y) on bitmap2 int C32BitDibWrapper::CreateDifferenceBitmap(C32BitDibWrapper *pBitmap1, C32BitDibWrapper *pBitmap2) // constructs a new dib that is the difference of the two other dibs { // image - blur(image) = detect edges. // // Destroy the old bitmap // if (m_pBits) { delete[] m_pBits; m_pBits = NULL; } m_nBitmapWidth=-1; m_nBitmapHeight=-1; // // Validate arguments // if (pBitmap1==NULL || pBitmap2==NULL) { return(FALSE); } if (pBitmap1->m_nBitmapWidth != pBitmap2->m_nBitmapWidth) { return(FALSE); } if (pBitmap1->m_nBitmapHeight != pBitmap2->m_nBitmapHeight) { return(FALSE); } if (pBitmap1->m_pBits==NULL || pBitmap2->m_pBits==NULL) { return(FALSE); } // // How many bytes do we need? // int nNumBytes = pBitmap1->m_nBitmapWidth * pBitmap1->m_nBitmapHeight * sizeof(ULONG); // // Allocate the bytes, return false if we weren't successful // m_pBits = new BYTE[nNumBytes]; if (m_pBits==NULL) { return(FALSE); } // // Save the dimensions // m_nBitmapWidth=pBitmap1->m_nBitmapWidth; m_nBitmapHeight=pBitmap1->m_nBitmapHeight; // // Compute the difference // for (int i=0;im_pBits[i],pBitmap2->m_pBits[i]); } return(TRUE); } // creates a C32BitDibWrapper which is identical to the C32BitDibWrapper passed as *bitmap C32BitDibWrapper::C32BitDibWrapper(C32BitDibWrapper *pBitmap) // copy constructor : m_pBits(NULL), m_nBitmapWidth(-1), m_nBitmapHeight(-1) { if (pBitmap && pBitmap->IsValid()) { int nNumWords=pBitmap->m_nBitmapWidth*pBitmap->m_nBitmapHeight; ULONG* pBitmapCopy = new ULONG[nNumWords]; ULONG* pSourceBitmap = (ULONG*)pBitmap->m_pBits; if (pBitmapCopy && pSourceBitmap) { CopyMemory( pBitmapCopy, pSourceBitmap, nNumWords*sizeof(ULONG) ); m_pBits=(BYTE *)pBitmapCopy; m_nBitmapHeight=pBitmap->m_nBitmapHeight; m_nBitmapWidth=pBitmap->m_nBitmapWidth; } } } // creates a blank dib wrapper w pixels wide and h pixels high C32BitDibWrapper::C32BitDibWrapper(int w, int h) : m_pBits(NULL), m_nBitmapWidth(-1), m_nBitmapHeight(-1) { int nNumWords=w*h; ULONG *pBitmapCopy = new ULONG[nNumWords]; if (pBitmapCopy) { ZeroMemory(pBitmapCopy,nNumWords*sizeof(ULONG)); m_pBits=(BYTE*)pBitmapCopy; m_nBitmapHeight=h; m_nBitmapWidth=w; } } // creates a C32BitDibWrapper given the bitmap refered to by pBitmap C32BitDibWrapper::C32BitDibWrapper(BITMAP bm) : m_pBits(NULL), m_nBitmapWidth(-1), m_nBitmapHeight(-1) { BYTE* pDibBits=(BYTE*)(bm.bmBits); if (pDibBits!=NULL && bm.bmWidth>0 && bm.bmHeight>0 && bm.bmBitsPixel>0 && bm.bmBitsPixel<=32) // is it a valid bitmap? { int nDepth = bm.bmBitsPixel; m_nBitmapWidth = bm.bmWidth; m_nBitmapHeight = bm.bmHeight; // // convert to a 32 bit bitmap // m_pBits=ConvertBitmap(pDibBits,nDepth,32); if (!m_pBits) { m_nBitmapWidth=-1; m_nBitmapHeight=-1; } } } // constructor from a memory mapped file bitmap C32BitDibWrapper::C32BitDibWrapper(BYTE* pDib) : m_pBits(NULL), m_nBitmapWidth(-1), m_nBitmapHeight(-1) { if (pDib) { // // get pointer to just the image bits: // PBITMAPINFO pBitmapInfo=(PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER)); BYTE* pDibBits = NULL; switch (pBitmapInfo->bmiHeader.biBitCount) { case 24: pDibBits=pDib+GetBmiSize((PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER)))+ sizeof(BITMAPFILEHEADER); break; case 8: pDibBits=pDib+GetBmiSize((PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER)))+ sizeof(BITMAPFILEHEADER)-256*4+4; break; case 1: pDibBits=pDib+GetBmiSize((PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER)))+ sizeof(BITMAPFILEHEADER)-4; break; } if (pDibBits) { m_pBits=ConvertBitmap(pDibBits,pBitmapInfo->bmiHeader.biBitCount,32);// convert to a 32 bit bitmap if (m_pBits) { m_nBitmapWidth=pBitmapInfo->bmiHeader.biWidth; m_nBitmapHeight=pBitmapInfo->bmiHeader.biHeight; } } } } // create an empty wrapper // we later expect to fill the wrapper using CreateDifferenceBitmap C32BitDibWrapper::C32BitDibWrapper(void) : m_pBits(NULL), m_nBitmapWidth(-1), m_nBitmapHeight(-1) { } C32BitDibWrapper::~C32BitDibWrapper(void) { Destroy(); } void C32BitDibWrapper::Destroy(void) { if (m_pBits) { delete[] m_pBits; m_pBits = NULL; } m_nBitmapWidth=-1; m_nBitmapHeight=-1; } // // helper function which converts between 32 bit and other less worthy formats // 32 bit dibs are stored in the following format // xxxxxxxxRRRRRRRRGGGGGGGGBBBBBBBB 8 blank bits followed by 8 bits for each RGB channel // // not optimized for speed // // if we are being handed a large number of 300 dpi bitmaps, this could become an important function to // optimize... otherwise its fine in its current form // BYTE* C32BitDibWrapper::ConvertBitmap( BYTE* pSource, int bitsPerSource, int bitsPerDest ) { BYTE* pDest = NULL; long x, y, nSourceLocation=0, nTargetLocation=0; int i, nDWordAlign; // // Compute the dword align space for each line // if (m_nBitmapWidth%4!=0) nDWordAlign=4-(m_nBitmapWidth*3)%4; else nDWordAlign=0; // // Convert from a 24 bit bitmap to a 32 bit bitmap // Pretty straight forward except that we have to watch out for // DWORD align stuff with 24 bit bitmaps // if (bitsPerSource==24 && bitsPerDest==32) { pDest = new BYTE[m_nBitmapWidth*m_nBitmapHeight*sizeof(ULONG)]; // // with fancy bit twiddling, we can get things done with one operation per 32 // bit pixel instead of 3 without much trouble if this routine becomes a // performance bottlekneck, we should modify this code // // loop through all pixels adding 8 bits of zeroed out data at the end of // each pSource line. 00000000RRRRRRRRGGGGGGGGBBBBBBBB // if (pDest) { for (y=0;y(nCurrent); pDest[nTargetLocation++]=static_cast(nCurrent); // // hack to prevent shadow detection for 1 nBit dibs. // set the blue channel to 150 so that shadow detection doesn't kick in // pDest[nTargetLocation++]=nCurrent&150; pDest[nTargetLocation++]=0; nBit++; } } } return(pDest); } // // Only used for debugging purposes // Converts a 32 bit bitmap down to 24 bits so that we can quickly display it // if (bitsPerSource==32 && bitsPerDest==24) // pretty straight forward except that we have to watch out for DWORD align stuff with 24 bit bitmaps { pDest = new BYTE[(m_nBitmapWidth*3+nDWordAlign)*m_nBitmapHeight]; if (pDest) { for (y=0;ym_pBits!=NULL) { Destroy(); m_pBits=pSource->pointerToBlur(); // // the blurred bitmap will have the same dimensions as the pSource bitmap // m_nBitmapWidth=pSource->m_nBitmapWidth; m_nBitmapHeight=pSource->m_nBitmapHeight; return(TRUE); } else { return(FALSE); } } // identical to the previous function, except that we use a horizontal blur instead of blur int C32BitDibWrapper::CreateHorizontalBlurBitmap(C32BitDibWrapper * pSource) { if (pSource!=NULL && pSource->IsValid()) { Destroy(); m_pBits=pSource->pointerToHorizontalBlur(); if (m_pBits) { m_nBitmapWidth=pSource->m_nBitmapWidth; m_nBitmapHeight=pSource->m_nBitmapHeight; } return(TRUE); } else { return(FALSE); } } int C32BitDibWrapper::CreateVerticalBlurBitmap(C32BitDibWrapper * pSource) { // // Nuke the old bitmap // Destroy(); if (pSource!=NULL && pSource->IsValid()) { m_pBits=pSource->pointerToVerticalBlur(); m_nBitmapWidth=pSource->m_nBitmapWidth; m_nBitmapHeight=pSource->m_nBitmapHeight; return(TRUE); } return(FALSE); } // blur the bitmap BYTE* C32BitDibWrapper::pointerToBlur(void) { if (m_pBits!=NULL) { int x,y; int position; // position in old bitmap ULONG* pBlurredBitmap; ULONG* pSource; int numPixels; numPixels=m_nBitmapWidth*m_nBitmapHeight; // calculate the total number of pixels in the bitmap pSource = (ULONG *)m_pBits; // we want to deal with data in 32 bit chunks pBlurredBitmap = new ULONG[numPixels]; // create an array to hold the blurred bitmap if (pBlurredBitmap==NULL) return(NULL); // unable to alloc memory // handle edge pixels // we do not blur edge pixels // if needed, edge pixels could be blurred here // blur top and bottom edge pixels for (x=0;x 00RRRRRR00GGGGGG00BBBBBB // note: we save 3 bit shifts by adding all four filtered pixels and then bitshifting which comes out to the same thing // we can now add the four pixel values without channels overflowing. the value we get is off by an error factor // because we eliminated the two lowest bits from each value // we compensate for this error factor by applying the filter 0x030303 which translates // RRRRRRRRGGGGGGGGBBBBBBBB to 000000RR000000GG000000BB // giving us the two lowest order bits for each pixel. we can then safely add the two lowest order // bits for each pixel. we then divide the result by 4 and add it // to the value we got by ignoring the two lowest order bits. pBlurredBitmap[position] = (((pSource[position-1]&16579836) + // 0xfafafa (pSource[position+1]&16579836) + (pSource[position-m_nBitmapWidth]&16579836) + (pSource[position+m_nBitmapWidth]&16579836))>>2)+ // compensate for error factor: ((((pSource[position-1]&197379) + // 0x030303 (pSource[position+1]&197379) + (pSource[position-m_nBitmapWidth]&197379) + (pSource[position+m_nBitmapWidth]&197379))>>2)&197379); } position++; } return(BYTE *)pBlurredBitmap; } else { return(NULL); } } // // identical to pointerToBlur bitmap except that we only use the pixel to // the left and the pixel to the right of the bitmap for detailed comments, // see pointerToBlur // BYTE* C32BitDibWrapper::pointerToHorizontalBlur(void) { if (m_pBits!=NULL) { int x,y; int position; // position in old bitmap ULONG* pBlurredBitmap; ULONG* pSource; int numPixels; numPixels=m_nBitmapWidth*m_nBitmapHeight; pSource = (ULONG *)m_pBits; pBlurredBitmap = new ULONG[numPixels]; if (pBlurredBitmap == NULL) return(NULL); // handle edge pixels // for edge pixels we simply copy the pixel into the pSource to the destination for (x=0;x>1)+ ((((pSource[position-1]&0x010101) + (pSource[position+1]&0x010101))>>1)&0x010101); } position++; } return(BYTE *)pBlurredBitmap; } else { return(NULL); } } // blur vertically // same exact method as pointerToHorizontalBlur // useful for detecting vertical edges, etc. BYTE* C32BitDibWrapper::pointerToVerticalBlur(void) { if (m_pBits) { int x,y; int position; // position in old bitmap ULONG* pBlurredBitmap; ULONG* pSource; int numPixels; numPixels=m_nBitmapWidth*m_nBitmapHeight; pSource = (ULONG *)m_pBits; pBlurredBitmap = new ULONG[numPixels]; if (pBlurredBitmap == NULL) return(NULL); // handle edge pixels for (x=0;x>1)+ ((((pSource[position-m_nBitmapWidth]&0x010101) + (pSource[position+m_nBitmapWidth]&0x010101))>>1)&0x010101); } position++; } return(BYTE *)pBlurredBitmap; } else { return(NULL); } } // cuts the intensity of each pixel in half // useful for certain graphics effects int C32BitDibWrapper::HalfIntensity(void) { if (m_pBits) { int numPixels; int i; ULONG* pBitmapPixels; pBitmapPixels=(ULONG*)m_pBits; numPixels=m_nBitmapWidth*m_nBitmapHeight; // loop through all pixels for (i=0;i>1; // half intensity by first eliminating the lowest order bit from each pixel and then shifting all pixels by 1 return(TRUE); } else { return(FALSE); } } // used repeatedly if the user hands us a 300 dpi image, etc. // HalfSize compacts a h x w bitmap down to a h/2 x w/2 bitmap int C32BitDibWrapper::HalfSize(void) { if (m_pBits) { int x,y; ULONG position; // position in old bitmap ULONG halfposition; // position in half (1/4 area) sized bitmap int oldWidth,oldHeight; ULONG* pOldBitmap; ULONG* pNewBitmap; pOldBitmap=(ULONG *)m_pBits; // we speed things up by dealing with 32 bit chunks instead of 8 bit chunks pNewBitmap = new ULONG[(m_nBitmapWidth/2)*(m_nBitmapHeight/2)]; // create an array to store a bitmap 1/4th the size of the origional bitmap if (pNewBitmap == NULL) return(FALSE); // out of memory position=0; halfposition=0; // loop through pixels 2 pixels at a time in each direction // at all times we insure that pOldBitmap[position] is the pixel at (x,y) // and pNewBitmap[halfposition] is the pixel at (x/2,y/2) for (y=0;y>2)+ ((((pOldBitmap[position]&197379) + (pOldBitmap[position+1]&197379) + (pOldBitmap[position+m_nBitmapWidth]&197379) + (pOldBitmap[position+m_nBitmapWidth+1]&197379))>>2)&197379); position+=2; halfposition++; } } delete[] m_pBits; // destroy the old bitmap array m_nBitmapWidth=m_nBitmapWidth/2; m_nBitmapHeight=m_nBitmapHeight/2; m_pBits=(BYTE *)pNewBitmap; return(TRUE); } else { return(FALSE); } } // this function destorys regions where the edgeBitmapPixels // edgeBitmap holds edge informat, start defines the maximum color value for a pixel to start a shadow elimination search // maxPixel defines the maximum edge value allowed for a shadow pixel // differenceFromGrey defines the maximum difference from grey for a shadow pixel // enhanceEdges deals with edge enhancement int C32BitDibWrapper::KillShadows(C32BitDibWrapper * edgeBitmap, ULONG start, ULONG maxPixel, ULONG differenceFromGrey, ULONG min_guaranteed_not_shadow, bool enhanceEdges) { if (IsValid() && edgeBitmap && edgeBitmap->m_pBits) { int x,y,position, searchPosition, newPosition; ULONG searchEdge; ULONG * pEdgePixels; ULONG * pBitmapPixels; ULONG maxEdge; int numPixels=m_nBitmapWidth*m_nBitmapHeight; int *pShadowStack = new int[MAXSTACK]; if (!pShadowStack) { // // we probably ran out of memory. die gracefully. // return(FALSE); } int stackHeight = 0; // we first mark all the border pixels so we don't go off the edge // this is much faster than other ways of doing bounds checking pEdgePixels=(ULONG *)edgeBitmap->m_pBits; pBitmapPixels=(ULONG *)m_pBits; for (x=0;xmin_guaranteed_not_shadow && enhanceEdges) // we only mark pixels as NOT_SHADOW if we are in enhanceEdges mode { pBitmapPixels[position]=NOT_SHADOW; } else // maybe this is a shadow pixel... if (pBitmapPixels[position]!=NOT_SHADOW && pBitmapPixels[position]!=DEAD_PIXEL && Intensity(pBitmapPixels[position])<=start && Intensity(pEdgePixels[position])<=maxEdge && pBitmapPixels[position]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[position])<=differenceFromGrey) { // pixel is a shadow pixel stackHeight=1; pShadowStack[0]=position; pBitmapPixels[position]=ERASEDSHADOW; // when we have decided a pixel is a shadow pixel, set it to zero // fighitng edges add extra complexity but potentially allow us greater accuracy // the concept is to mark pixels which cannot possibly be shadow pixels as such // fighting edges only come into effect if FIGHTING_EDGES is set to true and enhanceEdges is set to false // for the current KillShadows pass if (FIGHTING_EDGES) if (!enhanceEdges && Intensity(pEdgePixels[position])<=FIGHTING_EDGE_MAX_EDGE && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY && Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL && Intensity(pBitmapPixels[position])<=FIGHTING_EDGE_MAX_MARK_PIXEL ) pBitmapPixels[position]=DEAD_PIXEL; while (stackHeight>0) { searchPosition=pShadowStack[--stackHeight]; searchEdge=Intensity(pEdgePixels[searchPosition]); // key idea: we are on a search and destroy mission for smooth gradients // make sure our current edge value is similar to our last edge value newPosition=searchPosition-1; if ((pBitmapPixels[newPosition]!=NOT_SHADOW) && pBitmapPixels[newPosition]!=DEAD_PIXEL && Intensity(pEdgePixels[newPosition])<=maxPixel && pBitmapPixels[newPosition]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey) { pBitmapPixels[newPosition]=ERASEDSHADOW; if (FIGHTING_EDGES) if (!enhanceEdges && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY &&Intensity(pBitmapPixels[newPosition])>=FIGHTING_EDGE_MIN_MARK_PIXEL ) pBitmapPixels[newPosition]=DEAD_PIXEL; pShadowStack[stackHeight++]=newPosition; } newPosition=searchPosition+1; if (pBitmapPixels[newPosition]!=NOT_SHADOW && pBitmapPixels[newPosition]!=DEAD_PIXEL && Intensity(pEdgePixels[newPosition])<=maxPixel && pBitmapPixels[newPosition]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey) { pBitmapPixels[newPosition]=ERASEDSHADOW; if (FIGHTING_EDGES) if (!enhanceEdges && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY &&Intensity(pBitmapPixels[position])<=FIGHTING_EDGE_MAX_MARK_PIXEL &&Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL ) pBitmapPixels[newPosition]=DEAD_PIXEL; pShadowStack[stackHeight++]=newPosition; } newPosition=searchPosition-m_nBitmapWidth; if (pBitmapPixels[newPosition]!=NOT_SHADOW && pBitmapPixels[newPosition]!=DEAD_PIXEL && Intensity(pEdgePixels[newPosition])<=maxPixel && pBitmapPixels[newPosition]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey) { pBitmapPixels[newPosition]=ERASEDSHADOW; if (FIGHTING_EDGES) if (!enhanceEdges && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY &&Intensity(pBitmapPixels[newPosition])>=FIGHTING_EDGE_MIN_MARK_PIXEL &&Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL ) pBitmapPixels[newPosition]=DEAD_PIXEL; pShadowStack[stackHeight++]=newPosition; } newPosition=searchPosition+m_nBitmapWidth; if (pBitmapPixels[newPosition]!=NOT_SHADOW && pBitmapPixels[newPosition]!=DEAD_PIXEL && Intensity(pEdgePixels[newPosition])<=maxPixel && pBitmapPixels[newPosition]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey) { pBitmapPixels[newPosition]=ERASEDSHADOW; if (FIGHTING_EDGES) if (!enhanceEdges && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE &&Intensity(pBitmapPixels[newPosition])>=FIGHTING_EDGE_MIN_MARK_PIXEL && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY &&Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL ) pBitmapPixels[newPosition]=DEAD_PIXEL; pShadowStack[stackHeight++]=newPosition; } } } } position++; } position++; } delete[] pShadowStack; return(TRUE); } else { return(FALSE); } } // older simpler version... that includes comments: /*int C32BitDibWrapper::KillShadows(C32BitDibWrapper * edgeBitmap, UINT start, UINT maxPixel, UINT differenceFromGrey) { int x,y,position, searchPosition, newPosition; ULONG searchEdge; ULONG * pEdgePixels; ULONG * pBitmapPixels; int * pShadowStack; int stackHeight; int numPixels; UINT maxEdge; numPixels=m_nBitmapWidth*m_nBitmapHeight; pShadowStack = new int[MAXSTACK]; // we use a stack as part of a depth first search for potential shadow pixels next to a shadow pixel we have found if (pShadowStack==NULL) return(FALSE); // we probably ran out of memory. die gracefully. stackHeight=0; // we first change all the border edge pixels to white so we don't go off the edge // KillShadows avoids pixels with high shadow values so this should keep us from going off // the edge of the bitmap... if maxPixel were set to 0xffffff, this would crash // but in such a case, KillShadows would kill the whole bitmap. // this is much faster than other ways of doing bounds checking // we set all edge pixels to values such that we are gauranteed to reject them pEdgePixels=(ULONG *)edgeBitmap->m_pBits; pBitmapPixels=(ULONG *)m_pBits; // horizontal sides for (x=0;x0) { searchPosition=pShadowStack[--stackHeight]; searchEdge=Intensity(pEdgePixels[searchPosition]); // key idea: we are on a search and destroy mission for smooth gradients // make sure our current edge value is similar to our last edge value newPosition=searchPosition-1; // try the pixel to the left of the current pixel if (Intensity(pEdgePixels[newPosition])<=maxPixel && pBitmapPixels[newPosition]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey) // requirements for classifying a pixel as a shadow pixel if we have classified an adjacent pixel as a shadow pixel { pBitmapPixels[newPosition]=ERASEDSHADOW; // delete the pixel and mark it as an erased pixel so that our search doesn't go into an infinite loop pShadowStack[stackHeight++]=newPosition; } newPosition=searchPosition+1; // try the pixel to the right of the current pixel if (Intensity(pEdgePixels[newPosition])<=maxPixel && pBitmapPixels[newPosition]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey) { pBitmapPixels[newPosition]=ERASEDSHADOW; pShadowStack[stackHeight++]=newPosition; } newPosition=searchPosition-m_nBitmapWidth; // try the pixel directly above the current pixel if (Intensity(pEdgePixels[newPosition])<=maxPixel && pBitmapPixels[newPosition]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey) { pBitmapPixels[newPosition]=ERASEDSHADOW; pShadowStack[stackHeight++]=newPosition; } newPosition=searchPosition+m_nBitmapWidth; // try the pixel directly below the current pixel if (Intensity(pEdgePixels[newPosition])<=maxPixel && pBitmapPixels[newPosition]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey) { pBitmapPixels[newPosition]=ERASEDSHADOW; pShadowStack[stackHeight++]=newPosition; } } } position++; } position++; } if (pShadowStack!=NULL) delete pShadowStack; return(TRUE); } */ // finds clumps of pixels above the minimum image threshold // pMap is an array which indicates which chunk each pixel in the bitmap is part of // pMap values: // 0 indicates that a pixel is not part of a chunk // VERTICAL_EDGE indicates that a pixel is not part of a chunk and that the pixel is very close to the vertical edge of the image // HORIZONTAL_EDGE is the same as vertical edge, just for horizontal edges // int C32BitDibWrapper::FindChunks(int * pMap) // return number of chunks... color the chunks on pMap { if (pMap && m_pBits) { int x,y,position, searchPosition; ULONG * pBitmapPixels; int * pChunkStack; int stackHeight; int numChunks; int chunkSize; int deltax, deltay; int newPosition; // prepare pMap // indicate which pixels are edge pixels // position represents the location of pixel (x,y) // we indicate which pixels are edge pixels to prevent the search routines // that follow from running off the edge of the bitmap // to save time, the search routines will not keep track of x and y coordinates // so it is neccessary to provide another and faster way of determining the bounds of the bitmap position=0; for (y=0;y EDGWIDTH from the edge { position+=EDGEWIDTH; for (x=EDGEWIDTH;xMIN_CHUNK_INTENSITY) { // initialize stack used for doing a DFS of adjacent pixels stackHeight=1; pChunkStack[0]=position; numChunks++; chunkSize=0; // compute how many pixels are in the chunk. not used at the moment, but useful if we want // to eliminate chunks which are too small at this stage instead of at some later point pMap[position]=numChunks; // place this pixel in a chunk like it belongs // continue searching for pixels while the stack is not empty while (stackHeight>0) { searchPosition=pChunkStack[--stackHeight]; // pop the next pixel off the stack chunkSize++; // increment the number of pixels in the chyunk by 1 // we check if we should add all pixels within EDGEWIDTH of the searchPosition pixel to the current chunk // we then add any such pixels which are not edge pixels to the stack for (deltay=-EDGEWIDTH*m_nBitmapWidth;deltay<=EDGEWIDTH*m_nBitmapWidth;deltay+=m_nBitmapWidth) for (deltax=-EDGEWIDTH;deltax<=EDGEWIDTH;deltax++) { newPosition=searchPosition+deltay+deltax; if (Intensity(pBitmapPixels[newPosition])>MIN_CHUNK_INTENSITY && pMap[newPosition]<=0) { if (pMap[newPosition]==0) // not an edge pixel { pChunkStack[stackHeight++]=newPosition; pMap[newPosition]=numChunks; // mark the pixel as part of the chunk so that we do not go into an infinite loop } else // if a pixel is an edge pixel, we do not want to add that pixel to the stack { // (because of problems with scanners with black borders) // furthermore... we only want to add an edge pixel if the current pixel is // in a vertical or horizontal line with the edge pixel under consideration // as this further minimizes problems related to black scanner edges if (pMap[newPosition]==VERTICAL_EDGE) { if (deltax==0) // to minimize distortion due to black outside edge pMap[newPosition]=numChunks; } else // HORIZONTAL_EDGE { if (deltay==0) // to minimize distortion due to black outside edge pMap[newPosition]=numChunks; } } } } } } position++; } position+=EDGEWIDTH; } delete[] pChunkStack; return(numChunks); } else { return(0); } } // for debugging purposes only // displays where the chunks denoted by pMap are on the C32BitDibWrapper // pMap must have the same dimensions as the current bitmap or this function will fail void C32BitDibWrapper::ColorChunks(int *pMap) { // color in the bitmap given the region map... for debugging purposes; if (m_pBits && pMap) { ULONG* pBitmapPixels; ULONG mapColor; int x,y; int position; position=0; pBitmapPixels=(ULONG *)m_pBits; // loop through all pixels for (y=0;y0) // are we part of a region? { mapColor=(((ULONG)pMap[position])*431234894)&0xffffff; // a poor man's random number generator // if we cared about speed... we should make a lookup table instead // but this function is only for debugging purposes pBitmapPixels[position]=((pBitmapPixels[position] & 0xfefefe)>>1)+((mapColor& 0xfefefe)>>1); // average with slight loss } if (pMap[position]<0) pBitmapPixels[position]=0xffffff; // color in vertical and horizontal edges position++; } } } // designed mainly for debugging purposes... this is a painfully slow way to draw a 32 bit dib // because of the slow conversion step from 32 bits back to 24 bits int C32BitDibWrapper::Draw(HDC hdc, int x, int y) { if (hdc && m_pBits) { BITMAPINFO BitmapInfo; SetBMI(&BitmapInfo,m_nBitmapWidth, m_nBitmapHeight, 24); BYTE* pDibData = ConvertBitmap(m_pBits,32,24); if (pDibData) { StretchDIBits(hdc, x,y,m_nBitmapWidth,m_nBitmapHeight, 0,0,m_nBitmapWidth,m_nBitmapHeight, pDibData, &BitmapInfo,BI_RGB,SRCCOPY); // // destroy the temp 24 bit dib // delete[] pDibData; return(TRUE); } } return(FALSE); } // set pixel and get pixel are completelly unoptimized // If you wish to make them faster, create a table of values storing y*m_nBitmapWidth for all values of y // as GetPixel is used by the line drawing functions, this could result in a signifigant speed up as // the line drawing functions are used for region collision detection void inline C32BitDibWrapper::SetPixel(int x, int y, ULONG color) { if (m_pBits) { ULONG* pBitmapPixels=(ULONG*)m_pBits; pBitmapPixels[y*m_nBitmapWidth+x]=color; } } ULONG inline C32BitDibWrapper::GetPixel(int x, int y) { if (m_pBits) { ULONG* pBitmapPixels=(ULONG*)m_pBits; return(pBitmapPixels[y*m_nBitmapWidth+x]); } return 0; } // // calculates the total intensity along a line // // Line drawing code modified from VGA line drawing code from Michael // Abrash's Graphics Programming Black Book // // this is the one function which I did not create from scratch so any bug // questions should be directed to Michael Abrash =) why reinvent the wheel // when Bresenham line drawing is kindof hard to beat. particularly in this // case when we are using lines as tracers so we couldn't care less about if // they are antialiased or otherwise made to look less jagged. // // t-jacobr // ULONG C32BitDibWrapper::Line(int X0, int Y0,int X1, int Y1) { if (m_pBits) { if (X0<0) X0=0; if (Y0<0) Y0=0; if (X1<0) X1=0; if (Y1<0) Y1=0; if (X0>=m_nBitmapWidth) X0=m_nBitmapWidth; if (Y0>=m_nBitmapHeight) Y0=m_nBitmapHeight; if (X1>=m_nBitmapWidth) X1=m_nBitmapWidth; if (Y1>=m_nBitmapHeight) Y1=m_nBitmapHeight; int DeltaX, DeltaY; int Temp; if (Y0>Y1) { Temp=Y0; Y0=Y1; Y1=Temp; Temp = X0; X0=X1; X1=Temp; } DeltaX=X1-X0; DeltaY=Y1-Y0; if (DeltaX>0) { if (DeltaX>DeltaY) { return(Octant0(X0,Y0,DeltaX,DeltaY,1)); } else { return(Octant1(X0,Y0,DeltaX,DeltaY,1)); } } else { DeltaX = -DeltaX; if (DeltaX>DeltaY) { return(Octant0(X0,Y0,DeltaX,DeltaY,-1)); } else { return(Octant1(X0,Y0,DeltaX,DeltaY,-1)); } } } else { return(0); // invalid bitmap } } // helper functions for line drawing // these aint no normal nine drawing functions // what we do is we calculate the total intensity of all of the above threshold pixels along the line ULONG C32BitDibWrapper::Octant0(int X0, int Y0,int DeltaX,int DeltaY,int XDirection) { if (IsValid()) { int DeltaYx2; int DeltaYx2MinusDeltaXx2; int ErrorTerm; ULONG totalIntensity; ULONG pixelIntensity; totalIntensity=0; DeltaYx2=DeltaY*2; DeltaYx2MinusDeltaXx2=DeltaYx2 - (DeltaX*2); ErrorTerm = DeltaYx2 - DeltaX; // SetPixel(X0,Y0,0x0000ff); while (2=0) { Y0++; ErrorTerm +=DeltaYx2MinusDeltaXx2; } else { ErrorTerm +=DeltaYx2; } X0+=XDirection; //SetPixel(X0,Y0,0x0000ff); pixelIntensity=Intensity(GetPixel(X0,Y0)); if (pixelIntensity>MIN_CHUNK_INTENSITY && pixelIntensity=0) { X0 +=XDirection; ErrorTerm +=DeltaXx2MinusDeltaYx2; } else { ErrorTerm +=DeltaXx2; } Y0++; pixelIntensity=Intensity(GetPixel(X0,Y0)); if (pixelIntensity>MIN_CHUNK_INTENSITY && pixelIntensityMIN_CHUNK_INTENSITY) { // deletes: // // xx // xx // if ( Intensity(pBitmapPixels[i-1-m_nBitmapWidth])m_pBits[position]) r=m_pBits[position]; if (g>m_pBits[position+1]) g=m_pBits[position+1]; if (b>m_pBits[position+2]) b=m_pBits[position+2]; } if (r!=0 || g!=0 || b!=0) // if the r, g, or b vals are off, correct them CompensateForBackgroundColor(r,g,b); } } // // stretch out the color spectrum if the darn user adjusted brightness so that no parts of the image are black anymore // otherwise we can get embarassing failures if the user simply tweaks brightness and contrast too much // // stretches upwards... if you need to compensate downwards, call correctBrightness first void C32BitDibWrapper::MaxContrast(UINT numPixelsRequired) { if (IsValid()) { int position; int nNumBits; int max; int i; int temp; BYTE pConversionTable[256]; ULONG pNum[256]; for (i=0;i<256;i++) pNum[i]=0; nNumBits=m_nBitmapWidth*m_nBitmapHeight*4; // compute the number of pixels of each intensity level for (position=0;positionnumPixelsRequired) max=i; // create conversion table for (i=0;i<256;i++) { temp=(255*i)/max; if (temp>255) temp=255; // high pass pConversionTable[i]=(BYTE)temp; } // now apply the conversion table to all pixels in the image for (position=0;positionIsValid() && pEdgesBitmap->IsValid()) { int x,y; int position; int numPixels; ULONG* pBitmapPixels; ULONG* pEdgePixels; ULONG* pProccessedPixels; // bitmap with shadows removed, etc // we assume that the edge bitmap has the same width and height as this bitmap to shave a couple of 1/1000ths of a second... and cause we are lazy numPixels=0; pBitmapPixels=(ULONG *)m_pBits; pEdgePixels=(ULONG *)(pEdgesBitmap->m_pBits); pProccessedPixels=(ULONG *)(pProccessedBitmap->m_pBits); position=region.top*m_nBitmapWidth; // search through all pixels in the region // at all times, pBitmapPixels[position] is the pixel at point (x,y) for (y=region.top;y<=region.bottom;y++) { position+=region.left; for (x=region.left;x<=region.right;x++) { if (( (pBitmapPixels[position]&0xff) > TEXT_REGION_BACKGROUND_THRESHOLD && (pBitmapPixels[position]&0xff00) > (TEXT_REGION_BACKGROUND_THRESHOLD<<8) && (pBitmapPixels[position]&0xff0000)> (TEXT_REGION_BACKGROUND_THRESHOLD<<16) // below threshold && Intensity(pEdgePixels[position]) > MIN_TEXT_REGION_BACKGROUND_EDGE) // does it have the requisite edge val? || (pProccessedPixels[position]==0 && Intensity(pEdgePixels[position])>MIN_TEXT_REGION_BACKGROUND_EDGE_CLIPPED_PIXEL && (pBitmapPixels[position]&0xff) > CLIPPED_TEXT_REGION_BACKGROUND_THRESHOLD && (pBitmapPixels[position]&0xff00) > (CLIPPED_TEXT_REGION_BACKGROUND_THRESHOLD<<8) && (pBitmapPixels[position]&0xff0000)> (CLIPPED_TEXT_REGION_BACKGROUND_THRESHOLD<<16) // below threshold )) // we coulda been a dead shadow pixel too.. this is risky because depending on the settings, we may have culled plenty of deserving pixels { // we hold pixels to much higher standards if they are clipped pixels... to avoid too much stray clipping numPixels++; } position++; } position+=m_nBitmapWidth-region.right-1; } return(numPixels); } else { return(0); // invalid bitmap } } // the name of the game here is whatever works // this function may be ugly, but its the easiest way to get rid of // black borders without hurting overly many innocent pixels void C32BitDibWrapper::RemoveBlackBorder(int minBlackBorderPixel, C32BitDibWrapper * outputBitmap, C32BitDibWrapper * debugBitmap) { if (IsValid() && m_nBitmapWidth>100 && m_nBitmapHeight>100 && outputBitmap) // these tests are designed for reasonably large bitmaps { // bottom border KillBlackBorder(minBlackBorderPixel,m_nBitmapWidth*m_nBitmapHeight-m_nBitmapWidth,m_nBitmapWidth,m_nBitmapHeight,1,-m_nBitmapWidth, outputBitmap, debugBitmap); // top border KillBlackBorder(minBlackBorderPixel,0,m_nBitmapWidth,m_nBitmapHeight,1,m_nBitmapWidth, outputBitmap, debugBitmap); // left side KillBlackBorder(minBlackBorderPixel,0,m_nBitmapHeight,m_nBitmapWidth, m_nBitmapWidth,1, outputBitmap, debugBitmap); // right side KillBlackBorder(minBlackBorderPixel,m_nBitmapWidth-1,m_nBitmapHeight,m_nBitmapWidth, m_nBitmapWidth,-1, outputBitmap, debugBitmap); } } // this function encapsulates the single purpose algorithm used to // remove particularly troublesome shadows from the sides of images // this function is poorly tweaked and it iss very likely that we could either // greatly improve the number of errors detected // or the number of false errors that are unfairly zapped // debugBitmap is edited to give a graphical representation of which shadows have been eliminated // debugBitmap is only edited if the VISUAL_DEBUG flag is set // as shown in RemoveBlackBorder, KillBlackBorder is called with different startPosition, width, height, dx, and dy values // depending on whether we are working on the top border, the left border, the right border, or the bottom border. // from the perspective of KillBlackBorder, it is working on eliminating shadows from a bitmap which is width pixels wide // height pixels high and the location of pixel (0,0) is startPosition. Where to move one pixel in the x direction // you increment startPosition by dx and to move one pixel in the y direction, you increment dy by 1. void C32BitDibWrapper::KillBlackBorder(int minBlackBorderPixel, int startPosition, int width, int height, int dx, int dy, C32BitDibWrapper *pOutputBitmap, C32BitDibWrapper * pDebugBitmap) { if (IsValid() && pOutputBitmap && pOutputBitmap->IsValid() && width>100 && height>100) { int x,y,position, searchPosition, newPosition; ULONG * pBitmapPixels; int endPoint; int r,g,b; int dr,dg,db; int i; int sourceR,sourceG, sourceB; int errors; int step; int* pShadowDepths; int* pTempShadowDepths; int longestBackgroundPixelString; int borderPixels; int nonBackgroundPixels; int backgroundPixels; BYTE* pBlurredBits = m_pBits; ULONG* pDebugPixels; BYTE* pOutputBits; pOutputBits=pOutputBitmap->m_pBits; pShadowDepths=new int[width]; // we keep an array of how many pixels we think the black border is for each scan line if (pShadowDepths==NULL) return; pTempShadowDepths=NULL; pTempShadowDepths=new int[width]; if (pTempShadowDepths==NULL) { delete[] pShadowDepths; return; } int numPixels=height*width; // total pixels in the image pBitmapPixels=(ULONG *)(pOutputBitmap->m_pBits); if (pBitmapPixels) { pDebugPixels=(ULONG *)(pDebugBitmap->m_pBits); step=dy*4; // when dealing with data in 8 bit chunks instead of 32 bit chunks, we need to multiply the dy step by 4 // reset all vals to 0 for (i=0;iminBlackBorderPixel&&g>minBlackBorderPixel&&b>minBlackBorderPixel) // if the pixel is dark enough { // start a kill shadows search searchPosition=position+step; errors=0; borderPixels=0; for (y=1;y5) break; // if we have found five pixels which meet the borderPixel specs, break; } else { errors++; if (errors>3) break; // if we recieve more than 3 errors, break } searchPosition+=step; } endPoint=y+5; // because of edge enhancement, we set the shadow width to be a bit more than it actually is searchPosition+=2*step; // skip a couple of pixels because we may have missed the last couple of pixels of the shadow nonBackgroundPixels=0; backgroundPixels=0; for (;y<20;y++) // we expect the next few pixels to be background pixels { r=(int)pOutputBits[searchPosition]; g=(int)pOutputBits[searchPosition+1]; b=(int)pOutputBits[searchPosition+2]; sourceR=(int)pBlurredBits[searchPosition]; sourceG=(int)pBlurredBits[searchPosition+1]; sourceB=(int)pBlurredBits[searchPosition+2]; if (r < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION && g < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION && b < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION // WARNING: commenting out the following 3 lines may greatly increases the number of innocent pixels that are deleted && sourceR < MAX_KILL_SHADOW_BACKGROUND_UNEDITED && sourceG < MAX_KILL_SHADOW_BACKGROUND_UNEDITED && sourceB < MAX_KILL_SHADOW_BACKGROUND_UNEDITED ) backgroundPixels++; else { nonBackgroundPixels++; } if ((nonBackgroundPixels)>(backgroundPixels+4)) { // no way this is actually a shadow we are deleting y=0; break; } if (backgroundPixels>7) break; searchPosition+=step; } // we only have a shadow if we get a number of dark pixels followed by a number light pixels if (nonBackgroundPixels<3 && backgroundPixels>5 && borderPixels>errors && y!=0) { pShadowDepths[x]=MAX(pShadowDepths[x],endPoint); } } // this is designed to kill a different kind of shadow, a light shadow far from any objects // this code can be safely eliminated // r=pBlurredBits[position]; g=pBlurredBits[position+1]; b=pBlurredBits[position+2]; if (r>(minBlackBorderPixel/6)&&g>(minBlackBorderPixel/6)&&b>(minBlackBorderPixel/6)) { searchPosition=position+step; errors=0; borderPixels=0; for (y=1;y<11;y++) { dr=(int)pBlurredBits[searchPosition]-r; dg=(int)pBlurredBits[searchPosition+1]-g; db=(int)pBlurredBits[searchPosition+2]-b; r=(int)pBlurredBits[searchPosition]; g=(int)pBlurredBits[searchPosition+1]; b=(int)pBlurredBits[searchPosition+2]; // much looser requirements for being a shadow if (r>minBlackBorderPixel/7&&g>minBlackBorderPixel/7&&b>minBlackBorderPixel/7) { borderPixels++; } else { errors++; } searchPosition+=step; } endPoint=y-3; searchPosition+=5*step; nonBackgroundPixels=0; backgroundPixels=0; for (;y<35;y++) { r=(int)pOutputBits[searchPosition]; g=(int)pOutputBits[searchPosition+1]; b=(int)pOutputBits[searchPosition+2]; sourceR=(int)pBlurredBits[searchPosition]; sourceG=(int)pBlurredBits[searchPosition+1]; sourceB=(int)pBlurredBits[searchPosition+2]; // much stricter requirements for being a background pixel // with these stricter requirements, we are almost guaranteed not to eliminate any // valid pixels while searching for black borders // the idea is balancing looser requirements in one area with stricter requirements in another if (r < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION/29 && g < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION/29 && b < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION/29 && sourceR < MAX_KILL_SHADOW_BACKGROUND_UNEDITED/39 && sourceG < MAX_KILL_SHADOW_BACKGROUND_UNEDITED/39 && sourceB < MAX_KILL_SHADOW_BACKGROUND_UNEDITED/39 ) backgroundPixels++; else { nonBackgroundPixels++; break; } searchPosition+=step; } if (nonBackgroundPixels==0) // the pixel isn't a shadow pixel unless all of the backgroundPixels tested were background pixels { pShadowDepths[x]=MAX(pShadowDepths[x],endPoint); // update the shadowDepth for the pixel // corners can be very problematic // because this algorithm will by definition fail on any corner line // so we cheat... if (xwidth) { for (i=width-CORNER_WIDTH;i5) nonBackgroundPixels++; if (backgroundPixels>longestBackgroundPixelString) { endPoint=y; longestBackgroundPixelString=backgroundPixels; } backgroundPixels=0; if (nonBackgroundPixels>1) break; } searchPosition+=step; } if (backgroundPixels>longestBackgroundPixelString) // was the longestBackgroundPixelString the last? { longestBackgroundPixelString=backgroundPixels; endPoint=16; } if (longestBackgroundPixelString>6) { pShadowDepths[x]=MAX(pShadowDepths[x],endPoint-4); // corners can be problematic // because this algorithm will by definition fail on a black corner // so we cheat... if (xwidth) { for (i=width-CORNER_WIDTH;i>1)+((DEBUGCOLOR& 0xfefefe)>>1); newPosition+=step; } position+=dx; } } // clean up our memory delete[] pTempShadowDepths; delete[] pShadowDepths; } } // dib manipulation functions // the following are dib wrapper functions stolen and then modified... from utils.cpp // these functions are now obsolete and only used in region debug mode // where we need to load bitmaps from files /******************************************************************************* * * SetBMI * * DESCRIPTION: * Setup bitmap info. * * PARAMETERS: * *******************************************************************************/ void SetBMI( PBITMAPINFO pbmi, LONG width, LONG height, LONG depth) { pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pbmi->bmiHeader.biWidth = width; pbmi->bmiHeader.biHeight = height; pbmi->bmiHeader.biPlanes = 1; pbmi->bmiHeader.biBitCount = (WORD) depth; pbmi->bmiHeader.biCompression = BI_RGB; pbmi->bmiHeader.biSizeImage = 0; pbmi->bmiHeader.biXPelsPerMeter = 0; pbmi->bmiHeader.biYPelsPerMeter = 0; pbmi->bmiHeader.biClrUsed = 0; pbmi->bmiHeader.biClrImportant = 0; } /******************************************************************************* * * AllocDibFileFromBits * * DESCRIPTION: * Given an unaligned bits buffer, allocate a buffer lager enough to hold the * DWORD aligned DIB file and fill it in. * * PARAMETERS: * *******************************************************************************/ PBYTE AllocDibFileFromBits( PBYTE pBits, UINT width, UINT height, UINT depth) { PBYTE pdib; UINT uiScanLineWidth, uiSrcScanLineWidth, cbDibSize; int bitsSize; // Align scanline to ULONG boundary uiSrcScanLineWidth = (width * depth) / 8; uiScanLineWidth = (uiSrcScanLineWidth + 3) & 0xfffffffc; // DEBUG: // uiSrcScanLineWidth=uiScanLineWidth; // Calculate DIB size and allocate memory for the DIB. bitsSize=height * uiScanLineWidth; cbDibSize = bitsSize+sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO); pdib = (PBYTE) LocalAlloc(LMEM_FIXED, cbDibSize); if (pdib) { PBITMAPFILEHEADER pbmfh = (PBITMAPFILEHEADER)pdib; PBITMAPINFO pbmi = (PBITMAPINFO)(pdib + sizeof(BITMAPFILEHEADER)); PBYTE pb = (PBYTE)pbmi+ sizeof(BITMAPINFO); // Setup bitmap file header. pbmfh->bfType = 'MB'; pbmfh->bfSize = cbDibSize; pbmfh->bfOffBits = static_cast(pb - pdib); // Setup bitmap info. SetBMI(pbmi,width, height, depth); // WIA_TRACE(("AllocDibFileFromBits, uiScanLineWidth: %d, pdib: 0x%08X, pbmi: 0x%08X, pbits: 0x%08X", uiScanLineWidth, pdib, pbmi, pb)); // Copy the bits. pb-=3; pBits-=3; // BUG FIX BECAUSE THE PERSON WHO WROTE THIS COULDN'T KEEP THEIR BITS STRAIGHT memcpy(pb, pBits, bitsSize); } else { // WIA_ERROR(("AllocDibFileFromBits, LocalAlloc of %d bytes failed", cbDibSize)); } return(pdib); } /******************************************************************************* * * DIBBufferToBMP * * DESCRIPTION: * Make a BMP object from a DWORD aligned DIB file memory buffer * * PARAMETERS: * *******************************************************************************/ HBITMAP DIBBufferToBMP(HDC hDC, PBYTE pDib, BOOLEAN bFlip) { HBITMAP hBmp = NULL; PBITMAPINFO pbmi = (BITMAPINFO*)(pDib); PBYTE pBits = pDib + GetBmiSize(pbmi); if (bFlip) { pbmi->bmiHeader.biHeight = -pbmi->bmiHeader.biHeight; } hBmp = CreateDIBitmap(hDC, &pbmi->bmiHeader, CBM_INIT, pBits, pbmi, DIB_RGB_COLORS); if (!hBmp) { ;//WIA_ERROR(("DIBBufferToBMP, CreateDIBitmap failed %d", GetLastError(void))); } return(hBmp); } /******************************************************************************* * * ReadDIBFile * * DESCRIPTION: * * PARAMETERS: * *******************************************************************************/ HRESULT ReadDIBFile(LPTSTR pszFileName, PBYTE *ppDib) { HRESULT hr = S_FALSE; HANDLE hFile, hMap; PBYTE pFile, pBits; *ppDib = NULL; hFile = CreateFile(pszFileName, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { //WIA_ERROR(("ReadDIBFile, unable to open %s", pszFileName)); return(hr); } hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); if (!hMap) { //WIA_ERROR(("ReadDIBFile, CreateFileMapping failed")); goto close_hfile_exit; } pFile = (PBYTE)MapViewOfFileEx(hMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, NULL); if (pFile) { PBITMAPFILEHEADER pbmFile = (PBITMAPFILEHEADER)pFile; PBITMAPINFO pbmi = (PBITMAPINFO)(pFile + sizeof(BITMAPFILEHEADER)); // validate bitmap if (pbmFile->bfType == 'MB') { // Calculate color table size. LONG bmiSize, ColorMapSize = 0; if (pbmi->bmiHeader.biBitCount == 1) { ColorMapSize = 2 - 1; } else if (pbmi->bmiHeader.biBitCount == 4) { ColorMapSize = 16 - 1; } else if (pbmi->bmiHeader.biBitCount == 8) { ColorMapSize = 256 - 1; } bmiSize = sizeof(BITMAPINFO) + sizeof(RGBQUAD) * ColorMapSize; pBits = pFile + sizeof(BITMAPFILEHEADER) + bmiSize; *ppDib = AllocDibFileFromBits(pBits, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight, pbmi->bmiHeader.biBitCount); if (*ppDib) { hr = S_OK; } } else { //WIA_ERROR(("ReadDIBFile, %s is not a valid bitmap file", pszFileName)); } } else { //WIA_ERROR(("ReadDIBFile, MapViewOfFileEx failed")); goto close_hmap_exit; } UnmapViewOfFile(pFile); close_hmap_exit: CloseHandle(hMap); close_hfile_exit: CloseHandle(hFile); return(hr); } /******************************************************************************* * * GetBmiSize * * DESCRIPTION: * Should never get biCompression == BI_RLE. * * PARAMETERS: * *******************************************************************************/ LONG GetBmiSize(PBITMAPINFO pbmi) { // determine the size of bitmapinfo LONG lSize = pbmi->bmiHeader.biSize; // no color table cases if ( (pbmi->bmiHeader.biBitCount == 24) || ((pbmi->bmiHeader.biBitCount == 32) && (pbmi->bmiHeader.biCompression == BI_RGB))) { // no colors unless stated lSize += sizeof(RGBQUAD) * pbmi->bmiHeader.biClrUsed; return(lSize); } // bitfields cases if (((pbmi->bmiHeader.biBitCount == 32) && (pbmi->bmiHeader.biCompression == BI_BITFIELDS)) || (pbmi->bmiHeader.biBitCount == 16)) { lSize += 3 * sizeof(RGBQUAD); return(lSize); } // palette cases if (pbmi->bmiHeader.biBitCount == 1) { LONG lPal = pbmi->bmiHeader.biClrUsed; if ((lPal == 0) || (lPal > 2)) { lPal = 2; } lSize += lPal * sizeof(RGBQUAD); return(lSize); } // palette cases if (pbmi->bmiHeader.biBitCount == 4) { LONG lPal = pbmi->bmiHeader.biClrUsed; if ((lPal == 0) || (lPal > 16)) { lPal = 16; } lSize += lPal * sizeof(RGBQUAD); return(lSize); } // palette cases if (pbmi->bmiHeader.biBitCount == 8) { LONG lPal = pbmi->bmiHeader.biClrUsed; if ((lPal == 0) || (lPal > 256)) { lPal = 256; } lSize += lPal * sizeof(RGBQUAD); return(lSize); } // error return(0); } INT GetColorTableSize (UINT uBitCount, UINT uCompression) { INT nSize; switch (uBitCount) { case 32: if (uCompression != BI_BITFIELDS) { nSize = 0; break; } // fall through case 16: nSize = 3 * sizeof(DWORD); break; case 24: nSize = 0; break; default: nSize = ((UINT)1 << uBitCount) * sizeof(RGBQUAD); break; } return(nSize); } DWORD CalcBitsSize (UINT uWidth, UINT uHeight, UINT uBitCount, UINT uPlanes, int nAlign) { int nAWidth,nHeight,nABits; DWORD dwSize; nABits = (nAlign << 3); nAWidth = nABits-1; // // Determine the size of the bitmap based on the (nAlign) size. Convert // this to size-in-bytes. // nHeight = uHeight * uPlanes; dwSize = (DWORD)(((uWidth * uBitCount) + nAWidth) / nABits) * nHeight; dwSize = dwSize * nAlign; return(dwSize); } // // Converts hBitmap to a DIB // HGLOBAL BitmapToDIB (HDC hdc, HBITMAP hBitmap) { BITMAP bm = {0}; HANDLE hDib; PBYTE lpDib,lpBits; DWORD dwLength; DWORD dwBits; UINT uColorTable; INT iNeedMore; BOOL bDone; INT nBitCount; // // Get the size of the bitmap. These values are used to setup the memory // requirements for the DIB. // if (GetObject(hBitmap,sizeof(BITMAP),reinterpret_cast(&bm))) { nBitCount = bm.bmBitsPixel * bm.bmPlanes; uColorTable = GetColorTableSize((UINT)nBitCount, BI_RGB); dwBits = CalcBitsSize(bm.bmWidth,bm.bmHeight,nBitCount,1,sizeof(DWORD)); do { bDone = TRUE; dwLength = dwBits + sizeof(BITMAPINFOHEADER) + uColorTable; // Create the DIB. First, to the size of the bitmap. // if (hDib = GlobalAlloc(GHND,dwLength)) { if (lpDib = reinterpret_cast(GlobalLock(hDib))) { ((LPBITMAPINFOHEADER)lpDib)->biSize = sizeof(BITMAPINFOHEADER); ((LPBITMAPINFOHEADER)lpDib)->biWidth = (DWORD)bm.bmWidth; ((LPBITMAPINFOHEADER)lpDib)->biHeight = (DWORD)bm.bmHeight; ((LPBITMAPINFOHEADER)lpDib)->biPlanes = 1; ((LPBITMAPINFOHEADER)lpDib)->biBitCount = (WORD)nBitCount; ((LPBITMAPINFOHEADER)lpDib)->biCompression = 0; ((LPBITMAPINFOHEADER)lpDib)->biSizeImage = 0; ((LPBITMAPINFOHEADER)lpDib)->biXPelsPerMeter = 0; ((LPBITMAPINFOHEADER)lpDib)->biYPelsPerMeter = 0; ((LPBITMAPINFOHEADER)lpDib)->biClrUsed = 0; ((LPBITMAPINFOHEADER)lpDib)->biClrImportant = 0; // Get the size of the bitmap. // The biSizeImage contains the bytes // necessary to store the DIB. // GetDIBits(hdc,hBitmap,0,bm.bmHeight,NULL,(LPBITMAPINFO)lpDib,DIB_RGB_COLORS); iNeedMore = ((LPBITMAPINFOHEADER)lpDib)->biSizeImage - dwBits; if (iNeedMore > 0) { dwBits = dwBits + (((iNeedMore + 3) / 4)*4); bDone = FALSE; } else { lpBits = lpDib+sizeof(BITMAPINFOHEADER)+uColorTable; GetDIBits(hdc,hBitmap,0,bm.bmHeight,lpBits,(LPBITMAPINFO)lpDib,DIB_RGB_COLORS); GlobalUnlock(hDib); return(hDib); } GlobalUnlock(hDib); } GlobalFree(hDib); } } while (!bDone); } return(NULL); }