Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2796 lines
102 KiB

  1. // 32BitDibWrapper.cpp: implementation of the C32BitDibWrapper class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "precomp.h"
  5. #pragma hdrstop
  6. #include "32BitDib.h"
  7. // helper functions
  8. // sum of RGB vals
  9. inline ULONG Intensity(ULONG value)
  10. {
  11. return(value&0xff)+((value&0xff00)>>8)+((value&0xff0000)>>16);
  12. }
  13. // we should make a Difference Template to clean up this code
  14. inline UCHAR Difference(UCHAR a, UCHAR b)
  15. {
  16. if (a>b) return(a-b);
  17. else return(b-a);
  18. }
  19. inline ULONG Difference(ULONG a, ULONG b)
  20. {
  21. if (a>b) return(a-b);
  22. else return(b-a);
  23. }
  24. inline LONG Difference(LONG a, LONG b)
  25. {
  26. if (a>b) return(a-b);
  27. else return(b-a);
  28. }
  29. int inline MAX(int a, int b)
  30. {
  31. if (a>b) return(a);
  32. return(b);
  33. }
  34. int inline MIN(int a, int b)
  35. {
  36. if (a<b) return(a);
  37. return(b);
  38. }
  39. // shadows are gray... if you aint gray... you aint a shadow
  40. // this function hasn't yet been optimized
  41. inline ULONG DifferenceFromGray(ULONG value)
  42. {
  43. UCHAR g,b;//,r;
  44. // r=(UCHAR)(value& 0x0000ff);
  45. g=(UCHAR)((value& 0x00ff00)>>8);
  46. b=(UCHAR)((value& 0xff0000)>>16);
  47. // use this instead of the complete formula (uncomment the commented out code for the complete formula)
  48. // allow yellow scanner backgrounds
  49. return(ULONG)(Difference(b,g));//+Difference(r,g)+Difference(g,b));
  50. }
  51. // sets up a C32BitDibWrapper where each pixel (x,y) is the difference bettween the value of the pixel (x,y) on
  52. // bitmap1 and the pixel (x,y) on bitmap2
  53. int C32BitDibWrapper::CreateDifferenceBitmap(C32BitDibWrapper *pBitmap1, C32BitDibWrapper *pBitmap2) // constructs a new dib that is the difference of the two other dibs
  54. { // image - blur(image) = detect edges.
  55. //
  56. // Destroy the old bitmap
  57. //
  58. if (m_pBits)
  59. {
  60. delete[] m_pBits;
  61. m_pBits = NULL;
  62. }
  63. m_nBitmapWidth=-1;
  64. m_nBitmapHeight=-1;
  65. //
  66. // Validate arguments
  67. //
  68. if (pBitmap1==NULL || pBitmap2==NULL)
  69. {
  70. return(FALSE);
  71. }
  72. if (pBitmap1->m_nBitmapWidth != pBitmap2->m_nBitmapWidth)
  73. {
  74. return(FALSE);
  75. }
  76. if (pBitmap1->m_nBitmapHeight != pBitmap2->m_nBitmapHeight)
  77. {
  78. return(FALSE);
  79. }
  80. if (pBitmap1->m_pBits==NULL || pBitmap2->m_pBits==NULL)
  81. {
  82. return(FALSE);
  83. }
  84. //
  85. // How many bytes do we need?
  86. //
  87. int nNumBytes = pBitmap1->m_nBitmapWidth * pBitmap1->m_nBitmapHeight * sizeof(ULONG);
  88. //
  89. // Allocate the bytes, return false if we weren't successful
  90. //
  91. m_pBits = new BYTE[nNumBytes];
  92. if (m_pBits==NULL)
  93. {
  94. return(FALSE);
  95. }
  96. //
  97. // Save the dimensions
  98. //
  99. m_nBitmapWidth=pBitmap1->m_nBitmapWidth;
  100. m_nBitmapHeight=pBitmap1->m_nBitmapHeight;
  101. //
  102. // Compute the difference
  103. //
  104. for (int i=0;i<nNumBytes;i++)
  105. {
  106. m_pBits[i]=Difference(pBitmap1->m_pBits[i],pBitmap2->m_pBits[i]);
  107. }
  108. return(TRUE);
  109. }
  110. // creates a C32BitDibWrapper which is identical to the C32BitDibWrapper passed as *bitmap
  111. C32BitDibWrapper::C32BitDibWrapper(C32BitDibWrapper *pBitmap) // copy constructor
  112. : m_pBits(NULL),
  113. m_nBitmapWidth(-1),
  114. m_nBitmapHeight(-1)
  115. {
  116. if (pBitmap && pBitmap->IsValid())
  117. {
  118. int nNumWords=pBitmap->m_nBitmapWidth*pBitmap->m_nBitmapHeight;
  119. ULONG* pBitmapCopy = new ULONG[nNumWords];
  120. ULONG* pSourceBitmap = (ULONG*)pBitmap->m_pBits;
  121. if (pBitmapCopy && pSourceBitmap)
  122. {
  123. CopyMemory( pBitmapCopy, pSourceBitmap, nNumWords*sizeof(ULONG) );
  124. m_pBits=(BYTE *)pBitmapCopy;
  125. m_nBitmapHeight=pBitmap->m_nBitmapHeight;
  126. m_nBitmapWidth=pBitmap->m_nBitmapWidth;
  127. }
  128. }
  129. }
  130. // creates a blank dib wrapper w pixels wide and h pixels high
  131. C32BitDibWrapper::C32BitDibWrapper(int w, int h)
  132. : m_pBits(NULL),
  133. m_nBitmapWidth(-1),
  134. m_nBitmapHeight(-1)
  135. {
  136. int nNumWords=w*h;
  137. ULONG *pBitmapCopy = new ULONG[nNumWords];
  138. if (pBitmapCopy)
  139. {
  140. ZeroMemory(pBitmapCopy,nNumWords*sizeof(ULONG));
  141. m_pBits=(BYTE*)pBitmapCopy;
  142. m_nBitmapHeight=h;
  143. m_nBitmapWidth=w;
  144. }
  145. }
  146. // creates a C32BitDibWrapper given the bitmap refered to by pBitmap
  147. C32BitDibWrapper::C32BitDibWrapper(BITMAP bm)
  148. : m_pBits(NULL),
  149. m_nBitmapWidth(-1),
  150. m_nBitmapHeight(-1)
  151. {
  152. BYTE* pDibBits=(BYTE*)(bm.bmBits);
  153. if (pDibBits!=NULL && bm.bmWidth>0 && bm.bmHeight>0 && bm.bmBitsPixel>0 && bm.bmBitsPixel<=32) // is it a valid bitmap?
  154. {
  155. int nDepth = bm.bmBitsPixel;
  156. m_nBitmapWidth = bm.bmWidth;
  157. m_nBitmapHeight = bm.bmHeight;
  158. //
  159. // convert to a 32 bit bitmap
  160. //
  161. m_pBits=ConvertBitmap(pDibBits,nDepth,32);
  162. if (!m_pBits)
  163. {
  164. m_nBitmapWidth=-1;
  165. m_nBitmapHeight=-1;
  166. }
  167. }
  168. }
  169. // constructor from a memory mapped file bitmap
  170. C32BitDibWrapper::C32BitDibWrapper(BYTE* pDib)
  171. : m_pBits(NULL),
  172. m_nBitmapWidth(-1),
  173. m_nBitmapHeight(-1)
  174. {
  175. if (pDib)
  176. {
  177. //
  178. // get pointer to just the image bits:
  179. //
  180. PBITMAPINFO pBitmapInfo=(PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER));
  181. BYTE* pDibBits = NULL;
  182. switch (pBitmapInfo->bmiHeader.biBitCount)
  183. {
  184. case 24:
  185. pDibBits=pDib+GetBmiSize((PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER)))+ sizeof(BITMAPFILEHEADER);
  186. break;
  187. case 8:
  188. pDibBits=pDib+GetBmiSize((PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER)))+ sizeof(BITMAPFILEHEADER)-256*4+4;
  189. break;
  190. case 1:
  191. pDibBits=pDib+GetBmiSize((PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER)))+ sizeof(BITMAPFILEHEADER)-4;
  192. break;
  193. }
  194. if (pDibBits)
  195. {
  196. m_pBits=ConvertBitmap(pDibBits,pBitmapInfo->bmiHeader.biBitCount,32);// convert to a 32 bit bitmap
  197. if (m_pBits)
  198. {
  199. m_nBitmapWidth=pBitmapInfo->bmiHeader.biWidth;
  200. m_nBitmapHeight=pBitmapInfo->bmiHeader.biHeight;
  201. }
  202. }
  203. }
  204. }
  205. // create an empty wrapper
  206. // we later expect to fill the wrapper using CreateDifferenceBitmap
  207. C32BitDibWrapper::C32BitDibWrapper(void)
  208. : m_pBits(NULL),
  209. m_nBitmapWidth(-1),
  210. m_nBitmapHeight(-1)
  211. {
  212. }
  213. C32BitDibWrapper::~C32BitDibWrapper(void)
  214. {
  215. Destroy();
  216. }
  217. void C32BitDibWrapper::Destroy(void)
  218. {
  219. if (m_pBits)
  220. {
  221. delete[] m_pBits;
  222. m_pBits = NULL;
  223. }
  224. m_nBitmapWidth=-1;
  225. m_nBitmapHeight=-1;
  226. }
  227. //
  228. // helper function which converts between 32 bit and other less worthy formats
  229. // 32 bit dibs are stored in the following format
  230. // xxxxxxxxRRRRRRRRGGGGGGGGBBBBBBBB 8 blank bits followed by 8 bits for each RGB channel
  231. //
  232. // not optimized for speed
  233. //
  234. // if we are being handed a large number of 300 dpi bitmaps, this could become an important function to
  235. // optimize... otherwise its fine in its current form
  236. //
  237. BYTE* C32BitDibWrapper::ConvertBitmap( BYTE* pSource, int bitsPerSource, int bitsPerDest )
  238. {
  239. BYTE* pDest = NULL;
  240. long x, y, nSourceLocation=0, nTargetLocation=0;
  241. int i, nDWordAlign;
  242. //
  243. // Compute the dword align space for each line
  244. //
  245. if (m_nBitmapWidth%4!=0)
  246. nDWordAlign=4-(m_nBitmapWidth*3)%4;
  247. else nDWordAlign=0;
  248. //
  249. // Convert from a 24 bit bitmap to a 32 bit bitmap
  250. // Pretty straight forward except that we have to watch out for
  251. // DWORD align stuff with 24 bit bitmaps
  252. //
  253. if (bitsPerSource==24 && bitsPerDest==32)
  254. {
  255. pDest = new BYTE[m_nBitmapWidth*m_nBitmapHeight*sizeof(ULONG)];
  256. //
  257. // with fancy bit twiddling, we can get things done with one operation per 32
  258. // bit pixel instead of 3 without much trouble if this routine becomes a
  259. // performance bottlekneck, we should modify this code
  260. //
  261. // loop through all pixels adding 8 bits of zeroed out data at the end of
  262. // each pSource line. 00000000RRRRRRRRGGGGGGGGBBBBBBBB
  263. //
  264. if (pDest)
  265. {
  266. for (y=0;y<m_nBitmapHeight;y++)
  267. {
  268. for (x=0;x<m_nBitmapWidth;x++)
  269. {
  270. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  271. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  272. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  273. pDest[nTargetLocation++]=0;
  274. }
  275. nSourceLocation+=nDWordAlign; // skip nDWordAlign pixels... 24 bit bitmaps are DWORD alligned
  276. }
  277. }
  278. return(pDest);
  279. }
  280. //
  281. // Convert from an 8 bit bitmap to a 32 bit bitmap
  282. //
  283. else if (bitsPerSource==8 && bitsPerDest==32)
  284. {
  285. pDest = new BYTE[m_nBitmapWidth*m_nBitmapHeight*sizeof(ULONG)];
  286. if (pDest)
  287. {
  288. for (y=0;y<m_nBitmapHeight;y++) // loop through all pixels (x,y)
  289. {
  290. for (x=0;x<m_nBitmapWidth;x++)
  291. {
  292. pDest[nTargetLocation++]=pSource[nSourceLocation];
  293. pDest[nTargetLocation++]=pSource[nSourceLocation];
  294. pDest[nTargetLocation++]=pSource[nSourceLocation];
  295. pDest[nTargetLocation++]=0;
  296. nSourceLocation++;
  297. }
  298. if (m_nBitmapWidth%4!=0)
  299. {
  300. //
  301. // handle dword alignment issues for 8 bit dibs
  302. //
  303. nSourceLocation+=4-(m_nBitmapWidth)%4;
  304. }
  305. }
  306. }
  307. return(pDest);
  308. }
  309. //
  310. // Convert from a 1 bit B&W bitmap to a 32 bit bitmap
  311. //
  312. if (bitsPerSource==1 && bitsPerDest==32)
  313. {
  314. BYTE mask[8];
  315. BYTE convert[255];
  316. BYTE nCurrent;
  317. int nByte = 0,nBit = 0;
  318. int nLineWidth;
  319. //
  320. // mask[i] = 2^i
  321. //
  322. for (i=0;i<8;i++)
  323. {
  324. mask[i]=1<<i;
  325. }
  326. //
  327. // all values of convert other than convert[0] are set to 0
  328. //
  329. convert[0]=0;
  330. for (i=1;i<256;i++)
  331. {
  332. convert[i]=255;
  333. }
  334. nLineWidth=((m_nBitmapWidth+31)/32)*4;
  335. //
  336. // loop through all bitmap pixels keeping track of
  337. // byte which indicates the byte position of the pixel (x,y) in the pSource bitmap
  338. // bit which indicates the bit position of the pixel (x,y) within the byte
  339. // desLocation which represents the byte position of the 32 bit dib wrapper
  340. //
  341. // loop through all bitmap pixels keeping track of byte which indicates the
  342. // byte position of the pixel (x,y) in the pSource bitmap bit which indicates
  343. // the bit position of the pixel (x,y) within the byte desLocation which
  344. // represents the byte position of the 32 bit dib wrapper
  345. //
  346. pDest = new BYTE[m_nBitmapWidth*m_nBitmapHeight*sizeof(ULONG)];
  347. if (pDest)
  348. {
  349. for (y=0;y<m_nBitmapHeight;y++)
  350. {
  351. nBit=0;
  352. nByte=y*nLineWidth;
  353. for (x=0;x<m_nBitmapWidth;x++)
  354. {
  355. if (nBit==8)
  356. {
  357. nBit=0;
  358. nByte++;
  359. }
  360. nCurrent=pSource[nByte]&mask[nBit];
  361. nCurrent=convert[nCurrent];
  362. pDest[nTargetLocation++]=static_cast<BYTE>(nCurrent);
  363. pDest[nTargetLocation++]=static_cast<BYTE>(nCurrent);
  364. //
  365. // hack to prevent shadow detection for 1 nBit dibs.
  366. // set the blue channel to 150 so that shadow detection doesn't kick in
  367. //
  368. pDest[nTargetLocation++]=nCurrent&150;
  369. pDest[nTargetLocation++]=0;
  370. nBit++;
  371. }
  372. }
  373. }
  374. return(pDest);
  375. }
  376. //
  377. // Only used for debugging purposes
  378. // Converts a 32 bit bitmap down to 24 bits so that we can quickly display it
  379. //
  380. if (bitsPerSource==32 && bitsPerDest==24) // pretty straight forward except that we have to watch out for DWORD align stuff with 24 bit bitmaps
  381. {
  382. pDest = new BYTE[(m_nBitmapWidth*3+nDWordAlign)*m_nBitmapHeight];
  383. if (pDest)
  384. {
  385. for (y=0;y<m_nBitmapHeight;y++)
  386. {
  387. for (x=0;x<m_nBitmapWidth;x++)
  388. {
  389. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  390. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  391. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  392. //
  393. // pSource is 32 bits... ignore the first 8 bits
  394. //
  395. nSourceLocation++;
  396. }
  397. //
  398. // handle dword alignment issues for 24 bit dibs
  399. //
  400. for (i=0;i<nDWordAlign;i++)
  401. {
  402. pDest[nTargetLocation++]=255;
  403. }
  404. }
  405. }
  406. return(pDest);
  407. }
  408. return(pDest);
  409. }
  410. // blurs the bitmap both horizontally and vertically
  411. int C32BitDibWrapper::Blur(void)
  412. {
  413. BYTE *pBits=pointerToBlur();
  414. if (m_pBits)
  415. {
  416. delete[] m_pBits;
  417. }
  418. m_pBits = pBits;
  419. return(TRUE);
  420. }
  421. // this function should only be used if the current dib wrapper is blank
  422. int C32BitDibWrapper::CreateBlurBitmap(C32BitDibWrapper * pSource)
  423. {
  424. if (pSource!=NULL && pSource->m_pBits!=NULL)
  425. {
  426. Destroy();
  427. m_pBits=pSource->pointerToBlur();
  428. //
  429. // the blurred bitmap will have the same dimensions as the pSource bitmap
  430. //
  431. m_nBitmapWidth=pSource->m_nBitmapWidth;
  432. m_nBitmapHeight=pSource->m_nBitmapHeight;
  433. return(TRUE);
  434. }
  435. else
  436. {
  437. return(FALSE);
  438. }
  439. }
  440. // identical to the previous function, except that we use a horizontal blur instead of blur
  441. int C32BitDibWrapper::CreateHorizontalBlurBitmap(C32BitDibWrapper * pSource)
  442. {
  443. if (pSource!=NULL && pSource->IsValid())
  444. {
  445. Destroy();
  446. m_pBits=pSource->pointerToHorizontalBlur();
  447. if (m_pBits)
  448. {
  449. m_nBitmapWidth=pSource->m_nBitmapWidth;
  450. m_nBitmapHeight=pSource->m_nBitmapHeight;
  451. }
  452. return(TRUE);
  453. }
  454. else
  455. {
  456. return(FALSE);
  457. }
  458. }
  459. int C32BitDibWrapper::CreateVerticalBlurBitmap(C32BitDibWrapper * pSource)
  460. {
  461. //
  462. // Nuke the old bitmap
  463. //
  464. Destroy();
  465. if (pSource!=NULL && pSource->IsValid())
  466. {
  467. m_pBits=pSource->pointerToVerticalBlur();
  468. m_nBitmapWidth=pSource->m_nBitmapWidth;
  469. m_nBitmapHeight=pSource->m_nBitmapHeight;
  470. return(TRUE);
  471. }
  472. return(FALSE);
  473. }
  474. // blur the bitmap
  475. BYTE* C32BitDibWrapper::pointerToBlur(void)
  476. {
  477. if (m_pBits!=NULL)
  478. {
  479. int x,y;
  480. int position; // position in old bitmap
  481. ULONG* pBlurredBitmap;
  482. ULONG* pSource;
  483. int numPixels;
  484. numPixels=m_nBitmapWidth*m_nBitmapHeight; // calculate the total number of pixels in the bitmap
  485. pSource = (ULONG *)m_pBits; // we want to deal with data in 32 bit chunks
  486. pBlurredBitmap = new ULONG[numPixels]; // create an array to hold the blurred bitmap
  487. if (pBlurredBitmap==NULL) return(NULL); // unable to alloc memory
  488. // handle edge pixels
  489. // we do not blur edge pixels
  490. // if needed, edge pixels could be blurred here
  491. // blur top and bottom edge pixels
  492. for (x=0;x<m_nBitmapWidth;x++)
  493. {
  494. pBlurredBitmap[x] = pSource[x]; // top row
  495. pBlurredBitmap[numPixels-x-1] = pSource[numPixels-x-1]; // bottom row
  496. }
  497. // vertical sides
  498. for (position=m_nBitmapWidth;position+m_nBitmapWidth<numPixels;position+=m_nBitmapWidth)
  499. {
  500. pBlurredBitmap[position] = pSource[position]; // left edge
  501. pBlurredBitmap[position+m_nBitmapWidth-1] = pSource[position+m_nBitmapWidth-1]; // right edge
  502. }
  503. // now blur the bitmap
  504. // position indicates the location of the pixel (x,y) in the array
  505. position=m_nBitmapWidth-1;
  506. for (y=1;y<m_nBitmapHeight-1;y++) // loop through all pixels except for 1 pixel wide outside edge
  507. {
  508. position++;
  509. for (x=1;x<m_nBitmapWidth-1;x++)
  510. {
  511. position++;
  512. // we wish to take the average of the pixel directly below the pixel, the pixel directly below the pixel
  513. // the pixel directly to the right of the pixel and the pixel directly to the left of the pixel
  514. // we can do this 1 dword at a time and without any bit shifting by the following algorithm
  515. // problem. we cannot simply add the values of all four pixels and then divide by four
  516. // because or bit overflow from one RGB channel to another. to avoid this overflow, we start by
  517. // eliminating the two low order bits in each of the 3 color channels
  518. // we use the filter 0xfafafa to remove the 2 low order bits from each of the 3 color channels
  519. // e.g. RRRRRRRRGGGGGGGGBBBBBBBB goes to RRRRRR00GGGGGG00BBBBBB00
  520. // next shift each pixel over two bits so RRRRRR00GGGGGG00BBBBBB00 --> 00RRRRRR00GGGGGG00BBBBBB
  521. // note: we save 3 bit shifts by adding all four filtered pixels and then bitshifting which comes out to the same thing
  522. // we can now add the four pixel values without channels overflowing. the value we get is off by an error factor
  523. // because we eliminated the two lowest bits from each value
  524. // we compensate for this error factor by applying the filter 0x030303 which translates
  525. // RRRRRRRRGGGGGGGGBBBBBBBB to 000000RR000000GG000000BB
  526. // giving us the two lowest order bits for each pixel. we can then safely add the two lowest order
  527. // bits for each pixel. we then divide the result by 4 and add it
  528. // to the value we got by ignoring the two lowest order bits.
  529. pBlurredBitmap[position] =
  530. (((pSource[position-1]&16579836) + // 0xfafafa
  531. (pSource[position+1]&16579836) +
  532. (pSource[position-m_nBitmapWidth]&16579836) +
  533. (pSource[position+m_nBitmapWidth]&16579836))>>2)+
  534. // compensate for error factor:
  535. ((((pSource[position-1]&197379) + // 0x030303
  536. (pSource[position+1]&197379) +
  537. (pSource[position-m_nBitmapWidth]&197379) +
  538. (pSource[position+m_nBitmapWidth]&197379))>>2)&197379);
  539. }
  540. position++;
  541. }
  542. return(BYTE *)pBlurredBitmap;
  543. }
  544. else
  545. {
  546. return(NULL);
  547. }
  548. }
  549. //
  550. // identical to pointerToBlur bitmap except that we only use the pixel to
  551. // the left and the pixel to the right of the bitmap for detailed comments,
  552. // see pointerToBlur
  553. //
  554. BYTE* C32BitDibWrapper::pointerToHorizontalBlur(void)
  555. {
  556. if (m_pBits!=NULL)
  557. {
  558. int x,y;
  559. int position; // position in old bitmap
  560. ULONG* pBlurredBitmap;
  561. ULONG* pSource;
  562. int numPixels;
  563. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  564. pSource = (ULONG *)m_pBits;
  565. pBlurredBitmap = new ULONG[numPixels];
  566. if (pBlurredBitmap == NULL) return(NULL);
  567. // handle edge pixels
  568. // for edge pixels we simply copy the pixel into the pSource to the destination
  569. for (x=0;x<m_nBitmapWidth;x++)
  570. {
  571. pBlurredBitmap[x] = pSource[x]; // top row
  572. pBlurredBitmap[numPixels-x-1] = pSource[numPixels-x-1]; // bottom row
  573. }
  574. // vertical sides
  575. for (position=m_nBitmapWidth;position+m_nBitmapWidth<numPixels;position+=m_nBitmapWidth)
  576. {
  577. pBlurredBitmap[position] = pSource[position]; // left edge
  578. pBlurredBitmap[position+m_nBitmapWidth-1] = pSource[position+m_nBitmapWidth-1]; // right edge
  579. }
  580. // now blur the bitmap
  581. position=m_nBitmapWidth-1;
  582. for (y=1;y<m_nBitmapHeight-1;y++) // for all pixels, pSource[position] is the pixel at (x,y)
  583. {
  584. position++;
  585. for (x=1;x<m_nBitmapWidth-1;x++)
  586. {
  587. position++;
  588. pBlurredBitmap[position] =
  589. (((pSource[position-1]&0xfefefe) +
  590. (pSource[position+1]&0xfefefe))>>1)+
  591. ((((pSource[position-1]&0x010101) +
  592. (pSource[position+1]&0x010101))>>1)&0x010101);
  593. }
  594. position++;
  595. }
  596. return(BYTE *)pBlurredBitmap;
  597. }
  598. else
  599. {
  600. return(NULL);
  601. }
  602. }
  603. // blur vertically
  604. // same exact method as pointerToHorizontalBlur
  605. // useful for detecting vertical edges, etc.
  606. BYTE* C32BitDibWrapper::pointerToVerticalBlur(void)
  607. {
  608. if (m_pBits)
  609. {
  610. int x,y;
  611. int position; // position in old bitmap
  612. ULONG* pBlurredBitmap;
  613. ULONG* pSource;
  614. int numPixels;
  615. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  616. pSource = (ULONG *)m_pBits;
  617. pBlurredBitmap = new ULONG[numPixels];
  618. if (pBlurredBitmap == NULL) return(NULL);
  619. // handle edge pixels
  620. for (x=0;x<m_nBitmapWidth;x++)
  621. {
  622. pBlurredBitmap[x] = pSource[x]; // top row
  623. pBlurredBitmap[numPixels-x-1] = pSource[numPixels-x-1]; // bottom row
  624. }
  625. // vertical sides
  626. for (position=m_nBitmapWidth;position+m_nBitmapWidth<numPixels;position+=m_nBitmapWidth)
  627. {
  628. pBlurredBitmap[position] = pSource[position]; // left edge
  629. pBlurredBitmap[position+m_nBitmapWidth-1] = pSource[position+m_nBitmapWidth-1]; // right edge
  630. }
  631. // now blur the bitmap
  632. position=m_nBitmapWidth-1;
  633. for (y=1;y<m_nBitmapHeight-1;y++) // pSource[position] is the pixel at (x,y)
  634. {
  635. position++;
  636. for (x=1;x<m_nBitmapWidth-1;x++)
  637. {
  638. position++;
  639. pBlurredBitmap[position] =
  640. (((pSource[position-m_nBitmapWidth]&0xfefefe) +
  641. (pSource[position+m_nBitmapWidth]&0xfefefe))>>1)+
  642. ((((pSource[position-m_nBitmapWidth]&0x010101) +
  643. (pSource[position+m_nBitmapWidth]&0x010101))>>1)&0x010101);
  644. }
  645. position++;
  646. }
  647. return(BYTE *)pBlurredBitmap;
  648. }
  649. else
  650. {
  651. return(NULL);
  652. }
  653. }
  654. // cuts the intensity of each pixel in half
  655. // useful for certain graphics effects
  656. int C32BitDibWrapper::HalfIntensity(void)
  657. {
  658. if (m_pBits)
  659. {
  660. int numPixels;
  661. int i;
  662. ULONG* pBitmapPixels;
  663. pBitmapPixels=(ULONG*)m_pBits;
  664. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  665. // loop through all pixels
  666. for (i=0;i<numPixels;i++)
  667. pBitmapPixels[i]=(pBitmapPixels[i]&0xfefefe)>>1; // half intensity by first eliminating the lowest order bit from each pixel and then shifting all pixels by 1
  668. return(TRUE);
  669. }
  670. else
  671. {
  672. return(FALSE);
  673. }
  674. }
  675. // used repeatedly if the user hands us a 300 dpi image, etc.
  676. // HalfSize compacts a h x w bitmap down to a h/2 x w/2 bitmap
  677. int C32BitDibWrapper::HalfSize(void)
  678. {
  679. if (m_pBits)
  680. {
  681. int x,y;
  682. ULONG position; // position in old bitmap
  683. ULONG halfposition; // position in half (1/4 area) sized bitmap
  684. int oldWidth,oldHeight;
  685. ULONG* pOldBitmap;
  686. ULONG* pNewBitmap;
  687. pOldBitmap=(ULONG *)m_pBits; // we speed things up by dealing with 32 bit chunks instead of 8 bit chunks
  688. pNewBitmap = new ULONG[(m_nBitmapWidth/2)*(m_nBitmapHeight/2)]; // create an array to store a bitmap 1/4th the size of the origional bitmap
  689. if (pNewBitmap == NULL) return(FALSE); // out of memory
  690. position=0;
  691. halfposition=0;
  692. // loop through pixels 2 pixels at a time in each direction
  693. // at all times we insure that pOldBitmap[position] is the pixel at (x,y)
  694. // and pNewBitmap[halfposition] is the pixel at (x/2,y/2)
  695. for (y=0;y<m_nBitmapHeight-1;y+=2)
  696. {
  697. position=m_nBitmapWidth*y;
  698. for (x=0;x<m_nBitmapWidth-1;x+=2)
  699. {
  700. pNewBitmap[halfposition] = // we use the same algorithm for finding the average of four pixel values as used in pointerToBlur
  701. // see pointerToBlur for a detailed explaination
  702. (((pOldBitmap[position]&16579836) +
  703. (pOldBitmap[position+1]&16579836) +
  704. (pOldBitmap[position+m_nBitmapWidth]&16579836) +
  705. (pOldBitmap[position+m_nBitmapWidth+1]&16579836))>>2)+
  706. ((((pOldBitmap[position]&197379) +
  707. (pOldBitmap[position+1]&197379) +
  708. (pOldBitmap[position+m_nBitmapWidth]&197379) +
  709. (pOldBitmap[position+m_nBitmapWidth+1]&197379))>>2)&197379);
  710. position+=2;
  711. halfposition++;
  712. }
  713. }
  714. delete[] m_pBits; // destroy the old bitmap array
  715. m_nBitmapWidth=m_nBitmapWidth/2;
  716. m_nBitmapHeight=m_nBitmapHeight/2;
  717. m_pBits=(BYTE *)pNewBitmap;
  718. return(TRUE);
  719. }
  720. else
  721. {
  722. return(FALSE);
  723. }
  724. }
  725. // this function destorys regions where the edgeBitmapPixels
  726. // edgeBitmap holds edge informat, start defines the maximum color value for a pixel to start a shadow elimination search
  727. // maxPixel defines the maximum edge value allowed for a shadow pixel
  728. // differenceFromGrey defines the maximum difference from grey for a shadow pixel
  729. // enhanceEdges deals with edge enhancement
  730. int C32BitDibWrapper::KillShadows(C32BitDibWrapper * edgeBitmap, ULONG start, ULONG maxPixel, ULONG differenceFromGrey, ULONG min_guaranteed_not_shadow, bool enhanceEdges)
  731. {
  732. if (IsValid() && edgeBitmap && edgeBitmap->m_pBits)
  733. {
  734. int x,y,position, searchPosition, newPosition;
  735. ULONG searchEdge;
  736. ULONG * pEdgePixels;
  737. ULONG * pBitmapPixels;
  738. ULONG maxEdge;
  739. int numPixels=m_nBitmapWidth*m_nBitmapHeight;
  740. int *pShadowStack = new int[MAXSTACK];
  741. if (!pShadowStack)
  742. {
  743. //
  744. // we probably ran out of memory. die gracefully.
  745. //
  746. return(FALSE);
  747. }
  748. int stackHeight = 0;
  749. // we first mark all the border pixels so we don't go off the edge
  750. // this is much faster than other ways of doing bounds checking
  751. pEdgePixels=(ULONG *)edgeBitmap->m_pBits;
  752. pBitmapPixels=(ULONG *)m_pBits;
  753. for (x=0;x<m_nBitmapWidth;x++)
  754. {
  755. pEdgePixels[x] = BORDER_EDGE; // top row
  756. pEdgePixels[numPixels-x-1] = BORDER_EDGE; // bottom row
  757. }
  758. // vertical sides
  759. for (position=m_nBitmapWidth;position+m_nBitmapWidth<numPixels;position+=m_nBitmapWidth)
  760. {
  761. pEdgePixels[position] = BORDER_EDGE; // left edge
  762. pEdgePixels[position+m_nBitmapWidth-1] = BORDER_EDGE; // right edge
  763. }
  764. position=m_nBitmapWidth;
  765. maxEdge=maxPixel;
  766. for (y=1;y<m_nBitmapHeight-1;y++)
  767. {
  768. position++; // because we start at y=1 instead of y=0
  769. for (x=1;x<m_nBitmapWidth-1;x++)
  770. {
  771. if (pBitmapPixels[position]!=DEAD_PIXEL) // we ignore DEAD_PIXEL pixels completelly
  772. {
  773. // check for pixels to mark as not shadows
  774. if (pEdgePixels[position]!=BORDER_EDGE
  775. && Intensity(pEdgePixels[position])>min_guaranteed_not_shadow
  776. && enhanceEdges) // we only mark pixels as NOT_SHADOW if we are in enhanceEdges mode
  777. {
  778. pBitmapPixels[position]=NOT_SHADOW;
  779. }
  780. else // maybe this is a shadow pixel...
  781. if (pBitmapPixels[position]!=NOT_SHADOW
  782. && pBitmapPixels[position]!=DEAD_PIXEL
  783. && Intensity(pBitmapPixels[position])<=start
  784. && Intensity(pEdgePixels[position])<=maxEdge
  785. && pBitmapPixels[position]!=ERASEDSHADOW
  786. && DifferenceFromGray(pBitmapPixels[position])<=differenceFromGrey)
  787. { // pixel is a shadow pixel
  788. stackHeight=1;
  789. pShadowStack[0]=position;
  790. pBitmapPixels[position]=ERASEDSHADOW; // when we have decided a pixel is a shadow pixel, set it to zero
  791. // fighitng edges add extra complexity but potentially allow us greater accuracy
  792. // the concept is to mark pixels which cannot possibly be shadow pixels as such
  793. // fighting edges only come into effect if FIGHTING_EDGES is set to true and enhanceEdges is set to false
  794. // for the current KillShadows pass
  795. if (FIGHTING_EDGES)
  796. if (!enhanceEdges
  797. && Intensity(pEdgePixels[position])<=FIGHTING_EDGE_MAX_EDGE
  798. && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY
  799. && Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  800. && Intensity(pBitmapPixels[position])<=FIGHTING_EDGE_MAX_MARK_PIXEL
  801. )
  802. pBitmapPixels[position]=DEAD_PIXEL;
  803. while (stackHeight>0)
  804. {
  805. searchPosition=pShadowStack[--stackHeight];
  806. searchEdge=Intensity(pEdgePixels[searchPosition]); // key idea: we are on a search and destroy mission for smooth gradients
  807. // make sure our current edge value is similar to our last edge value
  808. newPosition=searchPosition-1;
  809. if ((pBitmapPixels[newPosition]!=NOT_SHADOW)
  810. && pBitmapPixels[newPosition]!=DEAD_PIXEL
  811. && Intensity(pEdgePixels[newPosition])<=maxPixel
  812. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  813. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  814. {
  815. pBitmapPixels[newPosition]=ERASEDSHADOW;
  816. if (FIGHTING_EDGES)
  817. if (!enhanceEdges
  818. && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE
  819. && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY
  820. &&Intensity(pBitmapPixels[newPosition])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  821. )
  822. pBitmapPixels[newPosition]=DEAD_PIXEL;
  823. pShadowStack[stackHeight++]=newPosition;
  824. }
  825. newPosition=searchPosition+1;
  826. if (pBitmapPixels[newPosition]!=NOT_SHADOW
  827. && pBitmapPixels[newPosition]!=DEAD_PIXEL
  828. && Intensity(pEdgePixels[newPosition])<=maxPixel
  829. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  830. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  831. {
  832. pBitmapPixels[newPosition]=ERASEDSHADOW;
  833. if (FIGHTING_EDGES)
  834. if (!enhanceEdges
  835. && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE
  836. && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY
  837. &&Intensity(pBitmapPixels[position])<=FIGHTING_EDGE_MAX_MARK_PIXEL
  838. &&Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  839. )
  840. pBitmapPixels[newPosition]=DEAD_PIXEL;
  841. pShadowStack[stackHeight++]=newPosition;
  842. }
  843. newPosition=searchPosition-m_nBitmapWidth;
  844. if (pBitmapPixels[newPosition]!=NOT_SHADOW
  845. && pBitmapPixels[newPosition]!=DEAD_PIXEL
  846. && Intensity(pEdgePixels[newPosition])<=maxPixel
  847. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  848. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  849. {
  850. pBitmapPixels[newPosition]=ERASEDSHADOW;
  851. if (FIGHTING_EDGES)
  852. if (!enhanceEdges
  853. && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE
  854. && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY
  855. &&Intensity(pBitmapPixels[newPosition])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  856. &&Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  857. )
  858. pBitmapPixels[newPosition]=DEAD_PIXEL;
  859. pShadowStack[stackHeight++]=newPosition;
  860. }
  861. newPosition=searchPosition+m_nBitmapWidth;
  862. if (pBitmapPixels[newPosition]!=NOT_SHADOW
  863. && pBitmapPixels[newPosition]!=DEAD_PIXEL
  864. && Intensity(pEdgePixels[newPosition])<=maxPixel
  865. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  866. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  867. {
  868. pBitmapPixels[newPosition]=ERASEDSHADOW;
  869. if (FIGHTING_EDGES)
  870. if (!enhanceEdges
  871. && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE
  872. &&Intensity(pBitmapPixels[newPosition])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  873. && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY
  874. &&Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  875. )
  876. pBitmapPixels[newPosition]=DEAD_PIXEL;
  877. pShadowStack[stackHeight++]=newPosition;
  878. }
  879. }
  880. }
  881. }
  882. position++;
  883. }
  884. position++;
  885. }
  886. delete[] pShadowStack;
  887. return(TRUE);
  888. }
  889. else
  890. {
  891. return(FALSE);
  892. }
  893. }
  894. // older simpler version... that includes comments:
  895. /*int C32BitDibWrapper::KillShadows(C32BitDibWrapper * edgeBitmap, UINT start, UINT maxPixel, UINT differenceFromGrey)
  896. {
  897. int x,y,position, searchPosition, newPosition;
  898. ULONG searchEdge;
  899. ULONG * pEdgePixels;
  900. ULONG * pBitmapPixels;
  901. int * pShadowStack;
  902. int stackHeight;
  903. int numPixels;
  904. UINT maxEdge;
  905. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  906. 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
  907. if (pShadowStack==NULL) return(FALSE); // we probably ran out of memory. die gracefully.
  908. stackHeight=0;
  909. // we first change all the border edge pixels to white so we don't go off the edge
  910. // KillShadows avoids pixels with high shadow values so this should keep us from going off
  911. // the edge of the bitmap... if maxPixel were set to 0xffffff, this would crash
  912. // but in such a case, KillShadows would kill the whole bitmap.
  913. // this is much faster than other ways of doing bounds checking
  914. // we set all edge pixels to values such that we are gauranteed to reject them
  915. pEdgePixels=(ULONG *)edgeBitmap->m_pBits;
  916. pBitmapPixels=(ULONG *)m_pBits;
  917. // horizontal sides
  918. for (x=0;x<m_nBitmapWidth;x++)
  919. {
  920. pEdgePixels[x] = 0xffffff; // top row
  921. pEdgePixels[numPixels-x-1] = 0xffffff; // bottom row
  922. }
  923. // vertical sides
  924. for (position=m_nBitmapWidth;position+m_nBitmapWidth<numPixels;position+=m_nBitmapWidth)
  925. {
  926. pEdgePixels[position] = 0xffffff; // left edge
  927. pEdgePixels[position+m_nBitmapWidth-1] = 0xffffff; // right edge
  928. }
  929. position=m_nBitmapWidth;
  930. maxEdge=maxPixel;
  931. for (y=1;y<m_nBitmapHeight-1;y++)
  932. {
  933. position++; // because we start at y=1 instead of y=0
  934. for (x=1;x<m_nBitmapWidth-1;x++)
  935. {
  936. // we only start a shadow kill search if
  937. if (Intensity(pBitmapPixels[position])<=start && Intensity(pEdgePixels[position])<=maxEdge && pBitmapPixels[position]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[position])<=differenceFromGrey)
  938. {
  939. // initialize the stack to do a DFS without recursion to find shadows
  940. stackHeight=1; // we are going to place the current position on the stack
  941. pShadowStack[0]=position;
  942. pBitmapPixels[position]=ERASEDSHADOW; // when we have decided a pixel is a shadow pixel, set it to zero
  943. while (stackHeight>0)
  944. {
  945. searchPosition=pShadowStack[--stackHeight];
  946. searchEdge=Intensity(pEdgePixels[searchPosition]); // key idea: we are on a search and destroy mission for smooth gradients
  947. // make sure our current edge value is similar to our last edge value
  948. newPosition=searchPosition-1; // try the pixel to the left of the current pixel
  949. if (Intensity(pEdgePixels[newPosition])<=maxPixel
  950. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  951. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey) // requirements for classifying a pixel as a shadow pixel if we have classified an adjacent pixel as a shadow pixel
  952. {
  953. pBitmapPixels[newPosition]=ERASEDSHADOW; // delete the pixel and mark it as an erased pixel so that our search doesn't go into an infinite loop
  954. pShadowStack[stackHeight++]=newPosition;
  955. }
  956. newPosition=searchPosition+1; // try the pixel to the right of the current pixel
  957. if (Intensity(pEdgePixels[newPosition])<=maxPixel
  958. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  959. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  960. {
  961. pBitmapPixels[newPosition]=ERASEDSHADOW;
  962. pShadowStack[stackHeight++]=newPosition;
  963. }
  964. newPosition=searchPosition-m_nBitmapWidth; // try the pixel directly above the current pixel
  965. if (Intensity(pEdgePixels[newPosition])<=maxPixel
  966. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  967. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  968. {
  969. pBitmapPixels[newPosition]=ERASEDSHADOW;
  970. pShadowStack[stackHeight++]=newPosition;
  971. }
  972. newPosition=searchPosition+m_nBitmapWidth; // try the pixel directly below the current pixel
  973. if (Intensity(pEdgePixels[newPosition])<=maxPixel
  974. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  975. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  976. {
  977. pBitmapPixels[newPosition]=ERASEDSHADOW;
  978. pShadowStack[stackHeight++]=newPosition;
  979. }
  980. }
  981. }
  982. position++;
  983. }
  984. position++;
  985. }
  986. if (pShadowStack!=NULL) delete pShadowStack;
  987. return(TRUE);
  988. }
  989. */
  990. // finds clumps of pixels above the minimum image threshold
  991. // pMap is an array which indicates which chunk each pixel in the bitmap is part of
  992. // pMap values:
  993. // 0 indicates that a pixel is not part of a chunk
  994. // 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
  995. // HORIZONTAL_EDGE is the same as vertical edge, just for horizontal edges
  996. //
  997. int C32BitDibWrapper::FindChunks(int * pMap) // return number of chunks... color the chunks on pMap
  998. {
  999. if (pMap && m_pBits)
  1000. {
  1001. int x,y,position, searchPosition;
  1002. ULONG * pBitmapPixels;
  1003. int * pChunkStack;
  1004. int stackHeight;
  1005. int numChunks;
  1006. int chunkSize;
  1007. int deltax, deltay;
  1008. int newPosition;
  1009. // prepare pMap
  1010. // indicate which pixels are edge pixels
  1011. // position represents the location of pixel (x,y)
  1012. // we indicate which pixels are edge pixels to prevent the search routines
  1013. // that follow from running off the edge of the bitmap
  1014. // to save time, the search routines will not keep track of x and y coordinates
  1015. // so it is neccessary to provide another and faster way of determining the bounds of the bitmap
  1016. position=0;
  1017. for (y=0;y<EDGEWIDTH;y++)
  1018. {
  1019. for (x=0;x<m_nBitmapWidth;x++)
  1020. pMap[position++]=VERTICAL_EDGE;
  1021. }
  1022. for (;y<m_nBitmapHeight-EDGEWIDTH;y++)
  1023. {
  1024. for (x=0;x<EDGEWIDTH;x++)
  1025. pMap[position++]=HORIZONTAL_EDGE;
  1026. for (;x<m_nBitmapWidth-EDGEWIDTH;x++) // for pixels that are not edge pixels, we set pMap to 0 to indicate
  1027. pMap[position++]=0; // that the pixel in question is ready to be set as part of a chunk
  1028. for (;x<m_nBitmapWidth;x++)
  1029. pMap[position++]=HORIZONTAL_EDGE;
  1030. }
  1031. for (;y<m_nBitmapHeight;y++)
  1032. {
  1033. for (x=0;x<m_nBitmapWidth;x++)
  1034. pMap[position++]=VERTICAL_EDGE;
  1035. }
  1036. // we are now ready to search for chunks
  1037. pChunkStack = NULL;
  1038. pChunkStack = new int[MAXSTACK];
  1039. if (pChunkStack == NULL) return(NULL);
  1040. stackHeight=0;
  1041. numChunks=0;
  1042. pBitmapPixels=(ULONG *)m_pBits; // its more convenient for this function to deal with pixels in 32 bit chunks instead of bytes .
  1043. // at all times we keep position set so that pBitmapPixels[position] represents the pixel at coordinates (x,y)
  1044. position=m_nBitmapWidth*EDGEWIDTH;
  1045. for (y=EDGEWIDTH;y<m_nBitmapHeight-EDGEWIDTH;y++) // check if we should start a floodfill search
  1046. // at each pixel (x,y) such that we are > EDGWIDTH from the edge
  1047. {
  1048. position+=EDGEWIDTH;
  1049. for (x=EDGEWIDTH;x<m_nBitmapWidth-EDGEWIDTH;x++)
  1050. {
  1051. // check if the pixel in question is not part of an existing chunk and that its intensity
  1052. // is greater than the minimum intensity required to be part of chunk
  1053. if (pMap[position]==0 && Intensity(pBitmapPixels[position])>MIN_CHUNK_INTENSITY)
  1054. {
  1055. // initialize stack used for doing a DFS of adjacent pixels
  1056. stackHeight=1;
  1057. pChunkStack[0]=position;
  1058. numChunks++;
  1059. chunkSize=0; // compute how many pixels are in the chunk. not used at the moment, but useful if we want
  1060. // to eliminate chunks which are too small at this stage instead of at some later point
  1061. pMap[position]=numChunks; // place this pixel in a chunk like it belongs
  1062. // continue searching for pixels while the stack is not empty
  1063. while (stackHeight>0)
  1064. {
  1065. searchPosition=pChunkStack[--stackHeight]; // pop the next pixel off the stack
  1066. chunkSize++; // increment the number of pixels in the chyunk by 1
  1067. // we check if we should add all pixels within EDGEWIDTH of the searchPosition pixel to the current chunk
  1068. // we then add any such pixels which are not edge pixels to the stack
  1069. for (deltay=-EDGEWIDTH*m_nBitmapWidth;deltay<=EDGEWIDTH*m_nBitmapWidth;deltay+=m_nBitmapWidth)
  1070. for (deltax=-EDGEWIDTH;deltax<=EDGEWIDTH;deltax++)
  1071. {
  1072. newPosition=searchPosition+deltay+deltax;
  1073. if (Intensity(pBitmapPixels[newPosition])>MIN_CHUNK_INTENSITY && pMap[newPosition]<=0)
  1074. {
  1075. if (pMap[newPosition]==0) // not an edge pixel
  1076. {
  1077. pChunkStack[stackHeight++]=newPosition;
  1078. pMap[newPosition]=numChunks; // mark the pixel as part of the chunk so that we do not go into an infinite loop
  1079. }
  1080. else // if a pixel is an edge pixel, we do not want to add that pixel to the stack
  1081. {
  1082. // (because of problems with scanners with black borders)
  1083. // furthermore... we only want to add an edge pixel if the current pixel is
  1084. // in a vertical or horizontal line with the edge pixel under consideration
  1085. // as this further minimizes problems related to black scanner edges
  1086. if (pMap[newPosition]==VERTICAL_EDGE)
  1087. {
  1088. if (deltax==0) // to minimize distortion due to black outside edge
  1089. pMap[newPosition]=numChunks;
  1090. }
  1091. else // HORIZONTAL_EDGE
  1092. {
  1093. if (deltay==0) // to minimize distortion due to black outside edge
  1094. pMap[newPosition]=numChunks;
  1095. }
  1096. }
  1097. }
  1098. }
  1099. }
  1100. }
  1101. position++;
  1102. }
  1103. position+=EDGEWIDTH;
  1104. }
  1105. delete[] pChunkStack;
  1106. return(numChunks);
  1107. }
  1108. else
  1109. {
  1110. return(0);
  1111. }
  1112. }
  1113. // for debugging purposes only
  1114. // displays where the chunks denoted by pMap are on the C32BitDibWrapper
  1115. // pMap must have the same dimensions as the current bitmap or this function will fail
  1116. void C32BitDibWrapper::ColorChunks(int *pMap)
  1117. { // color in the bitmap given the region map... for debugging purposes;
  1118. if (m_pBits && pMap)
  1119. {
  1120. ULONG* pBitmapPixels;
  1121. ULONG mapColor;
  1122. int x,y;
  1123. int position;
  1124. position=0;
  1125. pBitmapPixels=(ULONG *)m_pBits;
  1126. // loop through all pixels
  1127. for (y=0;y<m_nBitmapHeight;y++)
  1128. for (x=0;x<m_nBitmapWidth;x++)
  1129. {
  1130. if (pMap[position]>0) // are we part of a region?
  1131. {
  1132. mapColor=(((ULONG)pMap[position])*431234894)&0xffffff; // a poor man's random number generator
  1133. // if we cared about speed... we should make a lookup table instead
  1134. // but this function is only for debugging purposes
  1135. pBitmapPixels[position]=((pBitmapPixels[position] & 0xfefefe)>>1)+((mapColor& 0xfefefe)>>1); // average with slight loss
  1136. }
  1137. if (pMap[position]<0) pBitmapPixels[position]=0xffffff; // color in vertical and horizontal edges
  1138. position++;
  1139. }
  1140. }
  1141. }
  1142. // designed mainly for debugging purposes... this is a painfully slow way to draw a 32 bit dib
  1143. // because of the slow conversion step from 32 bits back to 24 bits
  1144. int C32BitDibWrapper::Draw(HDC hdc, int x, int y)
  1145. {
  1146. if (hdc && m_pBits)
  1147. {
  1148. BITMAPINFO BitmapInfo;
  1149. SetBMI(&BitmapInfo,m_nBitmapWidth, m_nBitmapHeight, 24);
  1150. BYTE* pDibData = ConvertBitmap(m_pBits,32,24);
  1151. if (pDibData)
  1152. {
  1153. StretchDIBits(hdc,
  1154. x,y,m_nBitmapWidth,m_nBitmapHeight,
  1155. 0,0,m_nBitmapWidth,m_nBitmapHeight,
  1156. pDibData,
  1157. &BitmapInfo,BI_RGB,SRCCOPY);
  1158. //
  1159. // destroy the temp 24 bit dib
  1160. //
  1161. delete[] pDibData;
  1162. return(TRUE);
  1163. }
  1164. }
  1165. return(FALSE);
  1166. }
  1167. // set pixel and get pixel are completelly unoptimized
  1168. // If you wish to make them faster, create a table of values storing y*m_nBitmapWidth for all values of y
  1169. // as GetPixel is used by the line drawing functions, this could result in a signifigant speed up as
  1170. // the line drawing functions are used for region collision detection
  1171. void inline C32BitDibWrapper::SetPixel(int x, int y, ULONG color)
  1172. {
  1173. if (m_pBits)
  1174. {
  1175. ULONG* pBitmapPixels=(ULONG*)m_pBits;
  1176. pBitmapPixels[y*m_nBitmapWidth+x]=color;
  1177. }
  1178. }
  1179. ULONG inline C32BitDibWrapper::GetPixel(int x, int y)
  1180. {
  1181. if (m_pBits)
  1182. {
  1183. ULONG* pBitmapPixels=(ULONG*)m_pBits;
  1184. return(pBitmapPixels[y*m_nBitmapWidth+x]);
  1185. }
  1186. return 0;
  1187. }
  1188. //
  1189. // calculates the total intensity along a line
  1190. //
  1191. // Line drawing code modified from VGA line drawing code from Michael
  1192. // Abrash's Graphics Programming Black Book
  1193. //
  1194. // this is the one function which I did not create from scratch so any bug
  1195. // questions should be directed to Michael Abrash =) why reinvent the wheel
  1196. // when Bresenham line drawing is kindof hard to beat. particularly in this
  1197. // case when we are using lines as tracers so we couldn't care less about if
  1198. // they are antialiased or otherwise made to look less jagged.
  1199. //
  1200. // t-jacobr
  1201. //
  1202. ULONG C32BitDibWrapper::Line(int X0, int Y0,int X1, int Y1)
  1203. {
  1204. if (m_pBits)
  1205. {
  1206. if (X0<0) X0=0;
  1207. if (Y0<0) Y0=0;
  1208. if (X1<0) X1=0;
  1209. if (Y1<0) Y1=0;
  1210. if (X0>=m_nBitmapWidth) X0=m_nBitmapWidth;
  1211. if (Y0>=m_nBitmapHeight) Y0=m_nBitmapHeight;
  1212. if (X1>=m_nBitmapWidth) X1=m_nBitmapWidth;
  1213. if (Y1>=m_nBitmapHeight) Y1=m_nBitmapHeight;
  1214. int DeltaX, DeltaY;
  1215. int Temp;
  1216. if (Y0>Y1)
  1217. {
  1218. Temp=Y0;
  1219. Y0=Y1;
  1220. Y1=Temp;
  1221. Temp = X0;
  1222. X0=X1;
  1223. X1=Temp;
  1224. }
  1225. DeltaX=X1-X0;
  1226. DeltaY=Y1-Y0;
  1227. if (DeltaX>0)
  1228. {
  1229. if (DeltaX>DeltaY)
  1230. {
  1231. return(Octant0(X0,Y0,DeltaX,DeltaY,1));
  1232. }
  1233. else
  1234. {
  1235. return(Octant1(X0,Y0,DeltaX,DeltaY,1));
  1236. }
  1237. }
  1238. else
  1239. {
  1240. DeltaX = -DeltaX;
  1241. if (DeltaX>DeltaY)
  1242. {
  1243. return(Octant0(X0,Y0,DeltaX,DeltaY,-1));
  1244. }
  1245. else
  1246. {
  1247. return(Octant1(X0,Y0,DeltaX,DeltaY,-1));
  1248. }
  1249. }
  1250. }
  1251. else
  1252. {
  1253. return(0); // invalid bitmap
  1254. }
  1255. }
  1256. // helper functions for line drawing
  1257. // these aint no normal nine drawing functions
  1258. // what we do is we calculate the total intensity of all of the above threshold pixels along the line
  1259. ULONG C32BitDibWrapper::Octant0(int X0, int Y0,int DeltaX,int DeltaY,int XDirection)
  1260. {
  1261. if (IsValid())
  1262. {
  1263. int DeltaYx2;
  1264. int DeltaYx2MinusDeltaXx2;
  1265. int ErrorTerm;
  1266. ULONG totalIntensity;
  1267. ULONG pixelIntensity;
  1268. totalIntensity=0;
  1269. DeltaYx2=DeltaY*2;
  1270. DeltaYx2MinusDeltaXx2=DeltaYx2 - (DeltaX*2);
  1271. ErrorTerm = DeltaYx2 - DeltaX;
  1272. // SetPixel(X0,Y0,0x0000ff);
  1273. while (2<DeltaX--) // skip the last pixel
  1274. {
  1275. if (ErrorTerm >=0)
  1276. {
  1277. Y0++;
  1278. ErrorTerm +=DeltaYx2MinusDeltaXx2;
  1279. }
  1280. else
  1281. {
  1282. ErrorTerm +=DeltaYx2;
  1283. }
  1284. X0+=XDirection;
  1285. //SetPixel(X0,Y0,0x0000ff);
  1286. pixelIntensity=Intensity(GetPixel(X0,Y0));
  1287. if (pixelIntensity>MIN_CHUNK_INTENSITY && pixelIntensity<COLLISION_DETECTION_HIGHPASS_VALUE) totalIntensity+=512;//pixelIntensity;
  1288. }
  1289. return(totalIntensity);
  1290. }
  1291. return 0;
  1292. }
  1293. ULONG C32BitDibWrapper::Octant1(int X0, int Y0, int DeltaX, int DeltaY, int XDirection)
  1294. {
  1295. if (IsValid())
  1296. {
  1297. int DeltaXx2;
  1298. int DeltaXx2MinusDeltaYx2;
  1299. int ErrorTerm;
  1300. ULONG totalIntensity;
  1301. ULONG pixelIntensity;
  1302. totalIntensity=0;
  1303. DeltaXx2 = DeltaX * 2;
  1304. DeltaXx2MinusDeltaYx2 = DeltaXx2 - (DeltaY*2);
  1305. ErrorTerm = DeltaXx2 - DeltaY;
  1306. //SetPixel(X0,Y0,0x0000ff);
  1307. while (2<DeltaY--)
  1308. { // skip last pixel
  1309. if (ErrorTerm >=0)
  1310. {
  1311. X0 +=XDirection;
  1312. ErrorTerm +=DeltaXx2MinusDeltaYx2;
  1313. }
  1314. else
  1315. {
  1316. ErrorTerm +=DeltaXx2;
  1317. }
  1318. Y0++;
  1319. pixelIntensity=Intensity(GetPixel(X0,Y0));
  1320. if (pixelIntensity>MIN_CHUNK_INTENSITY && pixelIntensity<COLLISION_DETECTION_HIGHPASS_VALUE) totalIntensity+=512;//pixelIntensity;
  1321. }
  1322. return(totalIntensity);
  1323. }
  1324. return 0;
  1325. }
  1326. //
  1327. // Compensate for Background Color could be made more than 3 times as fast if needed by caculating each pixel
  1328. // using 1 cycle instead of 3.
  1329. void C32BitDibWrapper::CompensateForBackgroundColor(int r, int g, int b)
  1330. {
  1331. if (IsValid())
  1332. {
  1333. int nNumBits=m_nBitmapWidth*m_nBitmapHeight*4;
  1334. for (int position=0;position<nNumBits;position+=4)
  1335. {
  1336. if (r<m_pBits[position]) m_pBits[position]=m_pBits[position]-r;
  1337. else m_pBits[position]=0;
  1338. if (g<m_pBits[position+1]) m_pBits[position+1]=m_pBits[position+1]-g;
  1339. else m_pBits[position+1]=0;
  1340. if (b<m_pBits[position+2]) m_pBits[position+2]=m_pBits[position+2]-b;
  1341. else m_pBits[position+2]=0;
  1342. }
  1343. }
  1344. }
  1345. // invert the bitmap
  1346. void C32BitDibWrapper::Invert(void)
  1347. {
  1348. if (IsValid())
  1349. {
  1350. int numPixels;
  1351. int i;
  1352. ULONG* pBitmapPixels;
  1353. pBitmapPixels=(ULONG*)m_pBits; // operate in 32 bit chunks instead of 8 bit chunks
  1354. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  1355. // loop through all pixels in the bitmap
  1356. for (i=0;i<numPixels;i++)
  1357. pBitmapPixels[i]^=0xffffff; // flipping all bits inverts the pixel
  1358. }
  1359. }
  1360. // note: we could get some wierd effects because despeckle edits the bitmap its examining
  1361. // but this shouldn't be a signifigant problem and often, the self referential aspect only acts to slightly increase accuracy
  1362. // this function is not the same as the standard photoshop despeckle filter.
  1363. // we only care about a small category of stray dots.
  1364. // stray dots which are surrounded by white pixels (or pixels which have been eliminated by remove shadow filters)
  1365. void C32BitDibWrapper::Despeckle(void)
  1366. {
  1367. if (IsValid())
  1368. {
  1369. ULONG* pBitmapPixels;
  1370. int numPixels;
  1371. int position;
  1372. int x,y;
  1373. pBitmapPixels=(ULONG*)m_pBits;
  1374. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  1375. position=4*m_nBitmapWidth;
  1376. // loop through all pixels which are not border pixels
  1377. // pBitmapPixels[position] should be the pixel at (x,y) in all cases
  1378. for (y=4;y<m_nBitmapHeight-4;y++)
  1379. {
  1380. position+=4;
  1381. for (x=4;x<m_nBitmapWidth-4;x++)
  1382. {
  1383. DespecklePixel(pBitmapPixels, position,false);
  1384. position++;
  1385. }
  1386. position+=4;
  1387. }
  1388. }
  1389. }
  1390. // we may want to despeckle the edges of an image more often than the rest of the image
  1391. // as image edges are often trouble spots...
  1392. // because of this, we should recommend that users place images in the center of the scanner
  1393. // when doing region detection to increase accuracy.
  1394. // the concept we are applying is that when we have to make sacrifices we make sacrifices in areas where we hurt cases that would have been very very hard anyway.
  1395. void C32BitDibWrapper::EdgeDespeckle(void)
  1396. {
  1397. if (IsValid())
  1398. {
  1399. ULONG* pBitmapPixels;
  1400. int x,y,position;
  1401. pBitmapPixels=(ULONG*)m_pBits;
  1402. position=m_nBitmapWidth*4;
  1403. // top edge
  1404. // as always, at all times we insure that pBitmapPixels[position] is the pixel at (x,y)
  1405. for (y=4;y<DESPECKLE_BORDER_WIDTH+4;y++)
  1406. {
  1407. position+=4;
  1408. for (x=4;x<m_nBitmapWidth-4;x++)
  1409. {
  1410. DespecklePixel(pBitmapPixels, position,true);
  1411. position++;
  1412. }
  1413. position+=4;
  1414. }
  1415. // side edges
  1416. for (;y<m_nBitmapHeight-DESPECKLE_BORDER_WIDTH-4;y++)
  1417. {
  1418. position+=4;
  1419. for (x=4;x<DESPECKLE_BORDER_WIDTH+4;x++)
  1420. {
  1421. DespecklePixel(pBitmapPixels, position,true); // left edge
  1422. DespecklePixel(pBitmapPixels, position+m_nBitmapWidth-DESPECKLE_BORDER_WIDTH-8,true); // right edge
  1423. position++;
  1424. }
  1425. position+=m_nBitmapWidth-DESPECKLE_BORDER_WIDTH-4;
  1426. }
  1427. // bottom edge
  1428. for (;y<m_nBitmapHeight-4;y++)
  1429. {
  1430. position+=4;
  1431. for (x=4;x<m_nBitmapWidth-4;x++)
  1432. {
  1433. DespecklePixel(pBitmapPixels, position,true);
  1434. position++;
  1435. }
  1436. position+=4;
  1437. }
  1438. }
  1439. }
  1440. // given the pixel at position i, figure out if it meets any of the requirements for eliminating the pixel
  1441. // if it does, eliminate the pixel. edgePixel specifies if the pixel is an edgePixel (in which case we may want
  1442. // to apply more strict requirements).
  1443. void C32BitDibWrapper::DespecklePixel(ULONG* pBitmapPixels, int i, bool edgePixel)
  1444. {
  1445. if (IsValid())
  1446. {
  1447. if (Intensity(pBitmapPixels[i])>MIN_CHUNK_INTENSITY)
  1448. {
  1449. // deletes:
  1450. //
  1451. // xx
  1452. // xx
  1453. //
  1454. if (
  1455. Intensity(pBitmapPixels[i-1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1456. && Intensity(pBitmapPixels[i-1])<MIN_CHUNK_INTENSITY
  1457. && Intensity(pBitmapPixels[i-1+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1458. && Intensity(pBitmapPixels[i-1+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1459. && Intensity(pBitmapPixels[i+2-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1460. && Intensity(pBitmapPixels[i+2])<MIN_CHUNK_INTENSITY
  1461. && Intensity(pBitmapPixels[i+2+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1462. && Intensity(pBitmapPixels[i+2+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1463. && Intensity(pBitmapPixels[i-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1464. && Intensity(pBitmapPixels[i+1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1465. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1466. && Intensity(pBitmapPixels[i+1+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1467. )
  1468. {
  1469. pBitmapPixels[i]=0;
  1470. pBitmapPixels[i+1]=0;
  1471. pBitmapPixels[i+m_nBitmapWidth]=0;
  1472. pBitmapPixels[i+1+m_nBitmapWidth]=0;
  1473. }
  1474. if (edgePixel==true)
  1475. {
  1476. // radius one speckle
  1477. // horizontal despeckle
  1478. if (Intensity(pBitmapPixels[i-1])<MIN_CHUNK_INTENSITY
  1479. && Intensity(pBitmapPixels[i-2])<MIN_CHUNK_INTENSITY
  1480. && Intensity(pBitmapPixels[i+2])<MIN_CHUNK_INTENSITY
  1481. && Intensity(pBitmapPixels[i+1])<MIN_CHUNK_INTENSITY)
  1482. pBitmapPixels[i]=0; // despeckle the speckle
  1483. // vertical despeckle
  1484. if (Intensity(pBitmapPixels[i-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1485. && Intensity(pBitmapPixels[i-m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1486. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1487. && Intensity(pBitmapPixels[i+m_nBitmapWidth])<MIN_CHUNK_INTENSITY)
  1488. pBitmapPixels[i]=0; // despeckle the speckle
  1489. // radius two despeckle
  1490. if (Intensity(pBitmapPixels[i-2])<MIN_CHUNK_INTENSITY
  1491. && Intensity(pBitmapPixels[i-3])<MIN_CHUNK_INTENSITY
  1492. && Intensity(pBitmapPixels[i+2])<MIN_CHUNK_INTENSITY
  1493. && Intensity(pBitmapPixels[i+3])<MIN_CHUNK_INTENSITY)
  1494. pBitmapPixels[i]=0; // despeckle the speckle
  1495. // vertical despeckle
  1496. if (Intensity(pBitmapPixels[i-m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1497. && Intensity(pBitmapPixels[i-m_nBitmapWidth*3])<MIN_CHUNK_INTENSITY
  1498. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1499. && Intensity(pBitmapPixels[i+m_nBitmapWidth*3])<MIN_CHUNK_INTENSITY)
  1500. pBitmapPixels[i]=0; // despeckle the speckle
  1501. // despeckle to eliminate clumps like this:
  1502. // clump: ? ?
  1503. // x
  1504. // ? ?
  1505. if (Intensity(pBitmapPixels[i-1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1506. && Intensity(pBitmapPixels[i+1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1507. && Intensity(pBitmapPixels[i-1+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1508. && Intensity(pBitmapPixels[i+1+m_nBitmapWidth])<MIN_CHUNK_INTENSITY)
  1509. pBitmapPixels[i]=0; // despeckle the speckle
  1510. }
  1511. // to eliminate this clump:
  1512. // ?
  1513. // ?x?
  1514. // ?
  1515. //
  1516. if (Intensity(pBitmapPixels[i-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1517. && Intensity(pBitmapPixels[i+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1518. && Intensity(pBitmapPixels[i-1])<MIN_CHUNK_INTENSITY
  1519. && Intensity(pBitmapPixels[i+1])<MIN_CHUNK_INTENSITY)
  1520. pBitmapPixels[i]=0; // despeckle the speckle
  1521. // these functions are insanely slow... if they become a major speed bottlekneck, they can be made
  1522. // 10x faster
  1523. // radius one speckle 3 pixel search depth
  1524. // horizontal despeckle
  1525. if (
  1526. Intensity(pBitmapPixels[i-1])<MIN_CHUNK_INTENSITY
  1527. && Intensity(pBitmapPixels[i-2])<MIN_CHUNK_INTENSITY
  1528. && Intensity(pBitmapPixels[i-3])<MIN_CHUNK_INTENSITY
  1529. && Intensity(pBitmapPixels[i-4])<MIN_CHUNK_INTENSITY
  1530. && Intensity(pBitmapPixels[i+4])<MIN_CHUNK_INTENSITY
  1531. && Intensity(pBitmapPixels[i+3])<MIN_CHUNK_INTENSITY
  1532. && Intensity(pBitmapPixels[i+2])<MIN_CHUNK_INTENSITY
  1533. && Intensity(pBitmapPixels[i+1])<MIN_CHUNK_INTENSITY
  1534. && Intensity(pBitmapPixels[i-1+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1535. && Intensity(pBitmapPixels[i-2+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1536. && Intensity(pBitmapPixels[i-3+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1537. && Intensity(pBitmapPixels[i-4+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1538. && Intensity(pBitmapPixels[i+4+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1539. && Intensity(pBitmapPixels[i+3+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1540. && Intensity(pBitmapPixels[i+2+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1541. && Intensity(pBitmapPixels[i+1+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1542. && Intensity(pBitmapPixels[i-1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1543. && Intensity(pBitmapPixels[i-2-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1544. && Intensity(pBitmapPixels[i-3-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1545. && Intensity(pBitmapPixels[i-4-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1546. && Intensity(pBitmapPixels[i+4-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1547. && Intensity(pBitmapPixels[i+3-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1548. && Intensity(pBitmapPixels[i+2-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1549. && Intensity(pBitmapPixels[i+1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1550. )
  1551. pBitmapPixels[i]=0; // despeckle the speckle
  1552. // vertical despeckle
  1553. if (
  1554. Intensity(pBitmapPixels[i-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1555. && Intensity(pBitmapPixels[i-m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1556. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1557. && Intensity(pBitmapPixels[i-m_nBitmapWidth*3])<MIN_CHUNK_INTENSITY
  1558. && Intensity(pBitmapPixels[i+m_nBitmapWidth*3])<MIN_CHUNK_INTENSITY
  1559. && Intensity(pBitmapPixels[i-m_nBitmapWidth*4])<MIN_CHUNK_INTENSITY
  1560. && Intensity(pBitmapPixels[i+m_nBitmapWidth*4])<MIN_CHUNK_INTENSITY
  1561. && Intensity(pBitmapPixels[i+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1562. && Intensity(pBitmapPixels[i-m_nBitmapWidth+1])<MIN_CHUNK_INTENSITY
  1563. && Intensity(pBitmapPixels[i-m_nBitmapWidth*2+1])<MIN_CHUNK_INTENSITY
  1564. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2+1])<MIN_CHUNK_INTENSITY
  1565. && Intensity(pBitmapPixels[i-m_nBitmapWidth*3+1])<MIN_CHUNK_INTENSITY
  1566. && Intensity(pBitmapPixels[i+m_nBitmapWidth*3+1])<MIN_CHUNK_INTENSITY
  1567. && Intensity(pBitmapPixels[i-m_nBitmapWidth*4+1])<MIN_CHUNK_INTENSITY
  1568. && Intensity(pBitmapPixels[i+m_nBitmapWidth*4+1])<MIN_CHUNK_INTENSITY
  1569. && Intensity(pBitmapPixels[i+m_nBitmapWidth+1])<MIN_CHUNK_INTENSITY
  1570. && Intensity(pBitmapPixels[i-m_nBitmapWidth-1])<MIN_CHUNK_INTENSITY
  1571. && Intensity(pBitmapPixels[i-m_nBitmapWidth*2-1])<MIN_CHUNK_INTENSITY
  1572. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2-1])<MIN_CHUNK_INTENSITY
  1573. && Intensity(pBitmapPixels[i-m_nBitmapWidth*3-1])<MIN_CHUNK_INTENSITY
  1574. && Intensity(pBitmapPixels[i+m_nBitmapWidth*3-1])<MIN_CHUNK_INTENSITY
  1575. && Intensity(pBitmapPixels[i-m_nBitmapWidth*4-1])<MIN_CHUNK_INTENSITY
  1576. && Intensity(pBitmapPixels[i+m_nBitmapWidth*4-1])<MIN_CHUNK_INTENSITY
  1577. && Intensity(pBitmapPixels[i+m_nBitmapWidth-1])<MIN_CHUNK_INTENSITY
  1578. )
  1579. pBitmapPixels[i]=0; // despeckle the speckle
  1580. }
  1581. }
  1582. }
  1583. // its easy to correct for if the user adjusted brightness contrast
  1584. // or worse... if the scanners gamma settings are off
  1585. // not unlikely if they have a really old or really cheap scanner
  1586. void C32BitDibWrapper::CorrectBrightness(void)
  1587. {
  1588. if (IsValid())
  1589. {
  1590. int r,g,b;
  1591. int position;
  1592. int nNumBits;
  1593. r=255;
  1594. g=255;
  1595. b=255;
  1596. nNumBits=m_nBitmapWidth*(m_nBitmapHeight-4)*4;
  1597. // find the minimum, r, g, and b values;
  1598. for (position=m_nBitmapWidth*4;position<nNumBits;position+=4)
  1599. {
  1600. if (r>m_pBits[position]) r=m_pBits[position];
  1601. if (g>m_pBits[position+1]) g=m_pBits[position+1];
  1602. if (b>m_pBits[position+2]) b=m_pBits[position+2];
  1603. }
  1604. if (r!=0 || g!=0 || b!=0) // if the r, g, or b vals are off, correct them
  1605. CompensateForBackgroundColor(r,g,b);
  1606. }
  1607. }
  1608. //
  1609. // stretch out the color spectrum if the darn user adjusted brightness so that no parts of the image are black anymore
  1610. // otherwise we can get embarassing failures if the user simply tweaks brightness and contrast too much
  1611. //
  1612. // stretches upwards... if you need to compensate downwards, call correctBrightness first
  1613. void C32BitDibWrapper::MaxContrast(UINT numPixelsRequired)
  1614. {
  1615. if (IsValid())
  1616. {
  1617. int position;
  1618. int nNumBits;
  1619. int max;
  1620. int i;
  1621. int temp;
  1622. BYTE pConversionTable[256];
  1623. ULONG pNum[256];
  1624. for (i=0;i<256;i++)
  1625. pNum[i]=0;
  1626. nNumBits=m_nBitmapWidth*m_nBitmapHeight*4;
  1627. // compute the number of pixels of each intensity level
  1628. for (position=0;position<nNumBits;position+=4)
  1629. {
  1630. pNum[m_pBits[position]]++;
  1631. pNum[m_pBits[position+1]]++;
  1632. pNum[m_pBits[position+2]]++;
  1633. }
  1634. max=1;
  1635. // find max intensity which has at least numPixelsRequired of that intensity
  1636. for (i=1;i<256;i++)
  1637. if (pNum[i]>numPixelsRequired) max=i;
  1638. // create conversion table
  1639. for (i=0;i<256;i++)
  1640. {
  1641. temp=(255*i)/max;
  1642. if (temp>255) temp=255; // high pass
  1643. pConversionTable[i]=(BYTE)temp;
  1644. }
  1645. // now apply the conversion table to all pixels in the image
  1646. for (position=0;position<nNumBits;position+=4)
  1647. {
  1648. m_pBits[position]=pConversionTable[m_pBits[position]];
  1649. m_pBits[position+1]=pConversionTable[m_pBits[position+1]];
  1650. m_pBits[position+2]=pConversionTable[m_pBits[position+2]];
  1651. }
  1652. }
  1653. }
  1654. /// we don't want to use intensity here
  1655. // because this is a function used for detecting text regions
  1656. // and text regions are more likely to have grey background than yellow backgrounds
  1657. // hence doing a chan by chan test is more effective
  1658. // IMPORTANT NOTE: this function is designed for use with a non-inverted bitmap unlike most of the other functions in this library
  1659. int C32BitDibWrapper::PixelsBelowThreshold(C32BitDibWrapper* pProccessedBitmap, C32BitDibWrapper * pEdgesBitmap, RECT region)
  1660. {
  1661. if (IsValid() && pProccessedBitmap && pEdgesBitmap && pProccessedBitmap->IsValid() && pEdgesBitmap->IsValid())
  1662. {
  1663. int x,y;
  1664. int position;
  1665. int numPixels;
  1666. ULONG* pBitmapPixels;
  1667. ULONG* pEdgePixels;
  1668. ULONG* pProccessedPixels; // bitmap with shadows removed, etc
  1669. // 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
  1670. numPixels=0;
  1671. pBitmapPixels=(ULONG *)m_pBits;
  1672. pEdgePixels=(ULONG *)(pEdgesBitmap->m_pBits);
  1673. pProccessedPixels=(ULONG *)(pProccessedBitmap->m_pBits);
  1674. position=region.top*m_nBitmapWidth;
  1675. // search through all pixels in the region
  1676. // at all times, pBitmapPixels[position] is the pixel at point (x,y)
  1677. for (y=region.top;y<=region.bottom;y++)
  1678. {
  1679. position+=region.left;
  1680. for (x=region.left;x<=region.right;x++)
  1681. {
  1682. if ((
  1683. (pBitmapPixels[position]&0xff) > TEXT_REGION_BACKGROUND_THRESHOLD
  1684. && (pBitmapPixels[position]&0xff00) > (TEXT_REGION_BACKGROUND_THRESHOLD<<8)
  1685. && (pBitmapPixels[position]&0xff0000)> (TEXT_REGION_BACKGROUND_THRESHOLD<<16) // below threshold
  1686. && Intensity(pEdgePixels[position]) > MIN_TEXT_REGION_BACKGROUND_EDGE) // does it have the requisite edge val?
  1687. || (pProccessedPixels[position]==0
  1688. && Intensity(pEdgePixels[position])>MIN_TEXT_REGION_BACKGROUND_EDGE_CLIPPED_PIXEL
  1689. && (pBitmapPixels[position]&0xff) > CLIPPED_TEXT_REGION_BACKGROUND_THRESHOLD
  1690. && (pBitmapPixels[position]&0xff00) > (CLIPPED_TEXT_REGION_BACKGROUND_THRESHOLD<<8)
  1691. && (pBitmapPixels[position]&0xff0000)> (CLIPPED_TEXT_REGION_BACKGROUND_THRESHOLD<<16) // below threshold
  1692. )) // we coulda been a dead shadow pixel too.. this is risky because depending on the settings, we may have culled plenty of deserving pixels
  1693. {
  1694. // we hold pixels to much higher standards if they are clipped pixels... to avoid too much stray clipping
  1695. numPixels++;
  1696. }
  1697. position++;
  1698. }
  1699. position+=m_nBitmapWidth-region.right-1;
  1700. }
  1701. return(numPixels);
  1702. }
  1703. else
  1704. {
  1705. return(0); // invalid bitmap
  1706. }
  1707. }
  1708. // the name of the game here is whatever works
  1709. // this function may be ugly, but its the easiest way to get rid of
  1710. // black borders without hurting overly many innocent pixels
  1711. void C32BitDibWrapper::RemoveBlackBorder(int minBlackBorderPixel, C32BitDibWrapper * outputBitmap, C32BitDibWrapper * debugBitmap)
  1712. {
  1713. if (IsValid() && m_nBitmapWidth>100 && m_nBitmapHeight>100 && outputBitmap) // these tests are designed for reasonably large bitmaps
  1714. {
  1715. // bottom border
  1716. KillBlackBorder(minBlackBorderPixel,m_nBitmapWidth*m_nBitmapHeight-m_nBitmapWidth,m_nBitmapWidth,m_nBitmapHeight,1,-m_nBitmapWidth, outputBitmap, debugBitmap);
  1717. // top border
  1718. KillBlackBorder(minBlackBorderPixel,0,m_nBitmapWidth,m_nBitmapHeight,1,m_nBitmapWidth, outputBitmap, debugBitmap);
  1719. // left side
  1720. KillBlackBorder(minBlackBorderPixel,0,m_nBitmapHeight,m_nBitmapWidth, m_nBitmapWidth,1, outputBitmap, debugBitmap);
  1721. // right side
  1722. KillBlackBorder(minBlackBorderPixel,m_nBitmapWidth-1,m_nBitmapHeight,m_nBitmapWidth, m_nBitmapWidth,-1, outputBitmap, debugBitmap);
  1723. }
  1724. }
  1725. // this function encapsulates the single purpose algorithm used to
  1726. // remove particularly troublesome shadows from the sides of images
  1727. // this function is poorly tweaked and it iss very likely that we could either
  1728. // greatly improve the number of errors detected
  1729. // or the number of false errors that are unfairly zapped
  1730. // debugBitmap is edited to give a graphical representation of which shadows have been eliminated
  1731. // debugBitmap is only edited if the VISUAL_DEBUG flag is set
  1732. // as shown in RemoveBlackBorder, KillBlackBorder is called with different startPosition, width, height, dx, and dy values
  1733. // depending on whether we are working on the top border, the left border, the right border, or the bottom border.
  1734. // from the perspective of KillBlackBorder, it is working on eliminating shadows from a bitmap which is width pixels wide
  1735. // height pixels high and the location of pixel (0,0) is startPosition. Where to move one pixel in the x direction
  1736. // you increment startPosition by dx and to move one pixel in the y direction, you increment dy by 1.
  1737. void C32BitDibWrapper::KillBlackBorder(int minBlackBorderPixel, int startPosition, int width, int height, int dx, int dy, C32BitDibWrapper *pOutputBitmap, C32BitDibWrapper * pDebugBitmap)
  1738. {
  1739. if (IsValid() && pOutputBitmap && pOutputBitmap->IsValid() && width>100 && height>100)
  1740. {
  1741. int x,y,position, searchPosition, newPosition;
  1742. ULONG * pBitmapPixels;
  1743. int endPoint;
  1744. int r,g,b;
  1745. int dr,dg,db;
  1746. int i;
  1747. int sourceR,sourceG, sourceB;
  1748. int errors;
  1749. int step;
  1750. int* pShadowDepths;
  1751. int* pTempShadowDepths;
  1752. int longestBackgroundPixelString;
  1753. int borderPixels;
  1754. int nonBackgroundPixels;
  1755. int backgroundPixels;
  1756. BYTE* pBlurredBits = m_pBits;
  1757. ULONG* pDebugPixels;
  1758. BYTE* pOutputBits;
  1759. pOutputBits=pOutputBitmap->m_pBits;
  1760. pShadowDepths=new int[width]; // we keep an array of how many pixels we think the black border is for each scan line
  1761. if (pShadowDepths==NULL) return;
  1762. pTempShadowDepths=NULL;
  1763. pTempShadowDepths=new int[width];
  1764. if (pTempShadowDepths==NULL)
  1765. {
  1766. delete[] pShadowDepths;
  1767. return;
  1768. }
  1769. int numPixels=height*width; // total pixels in the image
  1770. pBitmapPixels=(ULONG *)(pOutputBitmap->m_pBits);
  1771. if (pBitmapPixels)
  1772. {
  1773. pDebugPixels=(ULONG *)(pDebugBitmap->m_pBits);
  1774. 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
  1775. // reset all vals to 0
  1776. for (i=0;i<width;i++)
  1777. {
  1778. pShadowDepths[i]=0;
  1779. pTempShadowDepths[i]=0;
  1780. }
  1781. position=startPosition*4;
  1782. for (x=0;x<width;x++) // loop through all pixels on the top row of the image
  1783. {
  1784. r=pBlurredBits[position];
  1785. g=pBlurredBits[position+1];
  1786. b=pBlurredBits[position+2];
  1787. if (r>minBlackBorderPixel&&g>minBlackBorderPixel&&b>minBlackBorderPixel) // if the pixel is dark enough
  1788. {
  1789. // start a kill shadows search
  1790. searchPosition=position+step;
  1791. errors=0;
  1792. borderPixels=0;
  1793. for (y=1;y<SHADOW_HEIGHT;y++) // we don't expect a shadow to be more than SHADOW_HEIGHT pixels high
  1794. {
  1795. dr=(int)pBlurredBits[searchPosition]-r;
  1796. dg=(int)pBlurredBits[searchPosition+1]-g;
  1797. db=(int)pBlurredBits[searchPosition+2]-b;
  1798. r=(int)pBlurredBits[searchPosition];
  1799. g=(int)pBlurredBits[searchPosition+1];
  1800. b=(int)pBlurredBits[searchPosition+2];
  1801. if (dr<MAX_BLACK_BORDER_DELTA && dg<MAX_BLACK_BORDER_DELTA &&db<MAX_BLACK_BORDER_DELTA)
  1802. // only requirement is the intensity in each pixel must be less than the intensity of the previous pixel
  1803. // a shadow should be darkest at the edge of the image, not hte other way round
  1804. {
  1805. borderPixels++;
  1806. if (borderPixels>5)
  1807. break; // if we have found five pixels which meet the borderPixel specs, break;
  1808. }
  1809. else
  1810. {
  1811. errors++;
  1812. if (errors>3)
  1813. break; // if we recieve more than 3 errors, break
  1814. }
  1815. searchPosition+=step;
  1816. }
  1817. endPoint=y+5; // because of edge enhancement, we set the shadow width to be a bit more than it actually is
  1818. searchPosition+=2*step; // skip a couple of pixels because we may have missed the last couple of pixels of the shadow
  1819. nonBackgroundPixels=0;
  1820. backgroundPixels=0;
  1821. for (;y<20;y++) // we expect the next few pixels to be background pixels
  1822. {
  1823. r=(int)pOutputBits[searchPosition];
  1824. g=(int)pOutputBits[searchPosition+1];
  1825. b=(int)pOutputBits[searchPosition+2];
  1826. sourceR=(int)pBlurredBits[searchPosition];
  1827. sourceG=(int)pBlurredBits[searchPosition+1];
  1828. sourceB=(int)pBlurredBits[searchPosition+2];
  1829. if (r < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION
  1830. && g < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION
  1831. && b < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION
  1832. // WARNING: commenting out the following 3 lines may greatly increases the number of innocent pixels that are deleted
  1833. && sourceR < MAX_KILL_SHADOW_BACKGROUND_UNEDITED
  1834. && sourceG < MAX_KILL_SHADOW_BACKGROUND_UNEDITED
  1835. && sourceB < MAX_KILL_SHADOW_BACKGROUND_UNEDITED
  1836. )
  1837. backgroundPixels++;
  1838. else
  1839. {
  1840. nonBackgroundPixels++;
  1841. }
  1842. if ((nonBackgroundPixels)>(backgroundPixels+4))
  1843. { // no way this is actually a shadow we are deleting
  1844. y=0;
  1845. break;
  1846. }
  1847. if (backgroundPixels>7) break;
  1848. searchPosition+=step;
  1849. }
  1850. // we only have a shadow if we get a number of dark pixels followed by a number light pixels
  1851. if (nonBackgroundPixels<3 && backgroundPixels>5 && borderPixels>errors && y!=0)
  1852. {
  1853. pShadowDepths[x]=MAX(pShadowDepths[x],endPoint);
  1854. }
  1855. }
  1856. // this is designed to kill a different kind of shadow, a light shadow far from any objects
  1857. // this code can be safely eliminated
  1858. //
  1859. r=pBlurredBits[position];
  1860. g=pBlurredBits[position+1];
  1861. b=pBlurredBits[position+2];
  1862. if (r>(minBlackBorderPixel/6)&&g>(minBlackBorderPixel/6)&&b>(minBlackBorderPixel/6))
  1863. {
  1864. searchPosition=position+step;
  1865. errors=0;
  1866. borderPixels=0;
  1867. for (y=1;y<11;y++)
  1868. {
  1869. dr=(int)pBlurredBits[searchPosition]-r;
  1870. dg=(int)pBlurredBits[searchPosition+1]-g;
  1871. db=(int)pBlurredBits[searchPosition+2]-b;
  1872. r=(int)pBlurredBits[searchPosition];
  1873. g=(int)pBlurredBits[searchPosition+1];
  1874. b=(int)pBlurredBits[searchPosition+2];
  1875. // much looser requirements for being a shadow
  1876. if (r>minBlackBorderPixel/7&&g>minBlackBorderPixel/7&&b>minBlackBorderPixel/7)
  1877. {
  1878. borderPixels++;
  1879. }
  1880. else
  1881. {
  1882. errors++;
  1883. }
  1884. searchPosition+=step;
  1885. }
  1886. endPoint=y-3;
  1887. searchPosition+=5*step;
  1888. nonBackgroundPixels=0;
  1889. backgroundPixels=0;
  1890. for (;y<35;y++)
  1891. {
  1892. r=(int)pOutputBits[searchPosition];
  1893. g=(int)pOutputBits[searchPosition+1];
  1894. b=(int)pOutputBits[searchPosition+2];
  1895. sourceR=(int)pBlurredBits[searchPosition];
  1896. sourceG=(int)pBlurredBits[searchPosition+1];
  1897. sourceB=(int)pBlurredBits[searchPosition+2];
  1898. // much stricter requirements for being a background pixel
  1899. // with these stricter requirements, we are almost guaranteed not to eliminate any
  1900. // valid pixels while searching for black borders
  1901. // the idea is balancing looser requirements in one area with stricter requirements in another
  1902. if (r < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION/29
  1903. && g < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION/29
  1904. && b < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION/29
  1905. && sourceR < MAX_KILL_SHADOW_BACKGROUND_UNEDITED/39
  1906. && sourceG < MAX_KILL_SHADOW_BACKGROUND_UNEDITED/39
  1907. && sourceB < MAX_KILL_SHADOW_BACKGROUND_UNEDITED/39
  1908. )
  1909. backgroundPixels++;
  1910. else
  1911. {
  1912. nonBackgroundPixels++;
  1913. break;
  1914. }
  1915. searchPosition+=step;
  1916. }
  1917. if (nonBackgroundPixels==0) // the pixel isn't a shadow pixel unless all of the backgroundPixels tested were background pixels
  1918. {
  1919. pShadowDepths[x]=MAX(pShadowDepths[x],endPoint); // update the shadowDepth for the pixel
  1920. // corners can be very problematic
  1921. // because this algorithm will by definition fail on any corner line
  1922. // so we cheat...
  1923. if (x<CORNER_WIDTH)
  1924. {
  1925. for (i=0;i<CORNER_WIDTH;i++)
  1926. {
  1927. pShadowDepths[i]=MAX(pShadowDepths[i],endPoint);
  1928. }
  1929. }
  1930. if (x+CORNER_WIDTH>width)
  1931. {
  1932. for (i=width-CORNER_WIDTH;i<width;i++)
  1933. {
  1934. pShadowDepths[i]=MAX(pShadowDepths[i],endPoint);
  1935. }
  1936. }
  1937. }
  1938. }
  1939. // this is designed to kill a different kind of shadow, a small light shadow close to objects
  1940. // this code can be safely eliminated
  1941. // it was mainly written simply to explore the problem space of border elimination
  1942. //
  1943. // if this code is saved beyond a couple of test runs, we will need to turn some of its constants into real constants
  1944. // it seems from prelim tests that this code may be preferable to the previous test function
  1945. {
  1946. searchPosition=position+step;
  1947. errors=0;
  1948. borderPixels=0;
  1949. nonBackgroundPixels=0;
  1950. backgroundPixels=0;
  1951. longestBackgroundPixelString=0;
  1952. endPoint=0;
  1953. // we don't bother with looking for a string of black pixels in this case
  1954. // which is probably more intelegent than the previous code blocks
  1955. // instead we simply look for long strings of background pixels
  1956. // while at the same time, terminating the search of we come across too many non-background pixels
  1957. for (y=0;y<16;y++)
  1958. {
  1959. r=(int)pOutputBits[searchPosition];
  1960. g=(int)pOutputBits[searchPosition+1];
  1961. b=(int)pOutputBits[searchPosition+2];
  1962. sourceR=(int)pBlurredBits[searchPosition];
  1963. sourceG=(int)pBlurredBits[searchPosition+1];
  1964. sourceB=(int)pBlurredBits[searchPosition+2];
  1965. if (r < 24
  1966. && g < 24
  1967. && b < 24
  1968. && sourceR < 12
  1969. && sourceG < 12
  1970. && sourceB < 12
  1971. )
  1972. backgroundPixels++;
  1973. else
  1974. {
  1975. if (y>5) nonBackgroundPixels++;
  1976. if (backgroundPixels>longestBackgroundPixelString)
  1977. {
  1978. endPoint=y;
  1979. longestBackgroundPixelString=backgroundPixels;
  1980. }
  1981. backgroundPixels=0;
  1982. if (nonBackgroundPixels>1) break;
  1983. }
  1984. searchPosition+=step;
  1985. }
  1986. if (backgroundPixels>longestBackgroundPixelString) // was the longestBackgroundPixelString the last?
  1987. {
  1988. longestBackgroundPixelString=backgroundPixels;
  1989. endPoint=16;
  1990. }
  1991. if (longestBackgroundPixelString>6)
  1992. {
  1993. pShadowDepths[x]=MAX(pShadowDepths[x],endPoint-4);
  1994. // corners can be problematic
  1995. // because this algorithm will by definition fail on a black corner
  1996. // so we cheat...
  1997. if (x<CORNER_WIDTH)
  1998. {
  1999. for (i=0;i<CORNER_WIDTH;i++)
  2000. {
  2001. pShadowDepths[i]=MAX(pShadowDepths[i],endPoint);
  2002. }
  2003. }
  2004. if (x+CORNER_WIDTH>width)
  2005. {
  2006. for (i=width-CORNER_WIDTH;i<width;i++)
  2007. {
  2008. pShadowDepths[i]=MAX(pShadowDepths[i],endPoint);
  2009. }
  2010. }
  2011. }
  2012. }
  2013. position+=dx*4; // increment the position by 1 unit to go to the next row
  2014. }
  2015. for (x=0;x<width;x++)
  2016. {
  2017. pTempShadowDepths[x]=pShadowDepths[x];
  2018. }
  2019. if (SMOOTH_BORDER) // shadows don't just come out of nowhere, if row x has a depth 20 shadow, its likely that we made a mistake and pixel x-1 also has a depth 20 shadow
  2020. {
  2021. for (x=2;x<width-2;x++)
  2022. {
  2023. pTempShadowDepths[x]=MAX(pTempShadowDepths[x],pShadowDepths[x-1]);
  2024. pTempShadowDepths[x]=MAX(pTempShadowDepths[x],pShadowDepths[x+1]);
  2025. pTempShadowDepths[x]=MAX(pTempShadowDepths[x],pShadowDepths[x-2]);
  2026. pTempShadowDepths[x]=MAX(pTempShadowDepths[x],pShadowDepths[x+2]);
  2027. }
  2028. }
  2029. // now remove the black border
  2030. // we loop through all rows x and then eliminate the first pTempShadowDepths[x] pixels in that row
  2031. position=startPosition;
  2032. step=dy;
  2033. for (x=0;x<width;x++)
  2034. {
  2035. newPosition=position;
  2036. for (y=0;y<pTempShadowDepths[x];y++)
  2037. {
  2038. pBitmapPixels[newPosition]=DEAD_PIXEL; // set each shadow to be a dead pixel
  2039. // dead pixels are the only pixels not vulnerable to edge enhancements...
  2040. // important if we do any KillShadows edge enhancement passes after calling kill black border
  2041. if (VISUAL_DEBUG)
  2042. pDebugPixels[newPosition]=((pDebugPixels[newPosition] & 0xfefefe)>>1)+((DEBUGCOLOR& 0xfefefe)>>1);
  2043. newPosition+=step;
  2044. }
  2045. position+=dx;
  2046. }
  2047. }
  2048. // clean up our memory
  2049. delete[] pTempShadowDepths;
  2050. delete[] pShadowDepths;
  2051. }
  2052. }
  2053. // dib manipulation functions
  2054. // the following are dib wrapper functions stolen and then modified... from utils.cpp
  2055. // these functions are now obsolete and only used in region debug mode
  2056. // where we need to load bitmaps from files
  2057. /*******************************************************************************
  2058. *
  2059. * SetBMI
  2060. *
  2061. * DESCRIPTION:
  2062. * Setup bitmap info.
  2063. *
  2064. * PARAMETERS:
  2065. *
  2066. *******************************************************************************/
  2067. void SetBMI( PBITMAPINFO pbmi, LONG width, LONG height, LONG depth)
  2068. {
  2069. pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  2070. pbmi->bmiHeader.biWidth = width;
  2071. pbmi->bmiHeader.biHeight = height;
  2072. pbmi->bmiHeader.biPlanes = 1;
  2073. pbmi->bmiHeader.biBitCount = (WORD) depth;
  2074. pbmi->bmiHeader.biCompression = BI_RGB;
  2075. pbmi->bmiHeader.biSizeImage = 0;
  2076. pbmi->bmiHeader.biXPelsPerMeter = 0;
  2077. pbmi->bmiHeader.biYPelsPerMeter = 0;
  2078. pbmi->bmiHeader.biClrUsed = 0;
  2079. pbmi->bmiHeader.biClrImportant = 0;
  2080. }
  2081. /*******************************************************************************
  2082. *
  2083. * AllocDibFileFromBits
  2084. *
  2085. * DESCRIPTION:
  2086. * Given an unaligned bits buffer, allocate a buffer lager enough to hold the
  2087. * DWORD aligned DIB file and fill it in.
  2088. *
  2089. * PARAMETERS:
  2090. *
  2091. *******************************************************************************/
  2092. PBYTE AllocDibFileFromBits( PBYTE pBits, UINT width, UINT height, UINT depth)
  2093. {
  2094. PBYTE pdib;
  2095. UINT uiScanLineWidth, uiSrcScanLineWidth, cbDibSize;
  2096. int bitsSize;
  2097. // Align scanline to ULONG boundary
  2098. uiSrcScanLineWidth = (width * depth) / 8;
  2099. uiScanLineWidth = (uiSrcScanLineWidth + 3) & 0xfffffffc;
  2100. // DEBUG:
  2101. // uiSrcScanLineWidth=uiScanLineWidth;
  2102. // Calculate DIB size and allocate memory for the DIB.
  2103. bitsSize=height * uiScanLineWidth;
  2104. cbDibSize = bitsSize+sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO);
  2105. pdib = (PBYTE) LocalAlloc(LMEM_FIXED, cbDibSize);
  2106. if (pdib)
  2107. {
  2108. PBITMAPFILEHEADER pbmfh = (PBITMAPFILEHEADER)pdib;
  2109. PBITMAPINFO pbmi = (PBITMAPINFO)(pdib + sizeof(BITMAPFILEHEADER));
  2110. PBYTE pb = (PBYTE)pbmi+ sizeof(BITMAPINFO);
  2111. // Setup bitmap file header.
  2112. pbmfh->bfType = 'MB';
  2113. pbmfh->bfSize = cbDibSize;
  2114. pbmfh->bfOffBits = static_cast<DWORD>(pb - pdib);
  2115. // Setup bitmap info.
  2116. SetBMI(pbmi,width, height, depth);
  2117. // WIA_TRACE(("AllocDibFileFromBits, uiScanLineWidth: %d, pdib: 0x%08X, pbmi: 0x%08X, pbits: 0x%08X", uiScanLineWidth, pdib, pbmi, pb));
  2118. // Copy the bits.
  2119. pb-=3;
  2120. pBits-=3; // BUG FIX BECAUSE THE PERSON WHO WROTE THIS COULDN'T KEEP THEIR BITS STRAIGHT
  2121. memcpy(pb, pBits, bitsSize);
  2122. }
  2123. else
  2124. {
  2125. // WIA_ERROR(("AllocDibFileFromBits, LocalAlloc of %d bytes failed", cbDibSize));
  2126. }
  2127. return(pdib);
  2128. }
  2129. /*******************************************************************************
  2130. *
  2131. * DIBBufferToBMP
  2132. *
  2133. * DESCRIPTION:
  2134. * Make a BMP object from a DWORD aligned DIB file memory buffer
  2135. *
  2136. * PARAMETERS:
  2137. *
  2138. *******************************************************************************/
  2139. HBITMAP DIBBufferToBMP(HDC hDC, PBYTE pDib, BOOLEAN bFlip)
  2140. {
  2141. HBITMAP hBmp = NULL;
  2142. PBITMAPINFO pbmi = (BITMAPINFO*)(pDib);
  2143. PBYTE pBits = pDib + GetBmiSize(pbmi);
  2144. if (bFlip)
  2145. {
  2146. pbmi->bmiHeader.biHeight = -pbmi->bmiHeader.biHeight;
  2147. }
  2148. hBmp = CreateDIBitmap(hDC, &pbmi->bmiHeader, CBM_INIT, pBits, pbmi, DIB_RGB_COLORS);
  2149. if (!hBmp)
  2150. {
  2151. ;//WIA_ERROR(("DIBBufferToBMP, CreateDIBitmap failed %d", GetLastError(void)));
  2152. }
  2153. return(hBmp);
  2154. }
  2155. /*******************************************************************************
  2156. *
  2157. * ReadDIBFile
  2158. *
  2159. * DESCRIPTION:
  2160. *
  2161. * PARAMETERS:
  2162. *
  2163. *******************************************************************************/
  2164. HRESULT ReadDIBFile(LPTSTR pszFileName, PBYTE *ppDib)
  2165. {
  2166. HRESULT hr = S_FALSE;
  2167. HANDLE hFile, hMap;
  2168. PBYTE pFile, pBits;
  2169. *ppDib = NULL;
  2170. hFile = CreateFile(pszFileName,
  2171. GENERIC_WRITE | GENERIC_READ,
  2172. FILE_SHARE_WRITE,
  2173. NULL,
  2174. OPEN_EXISTING,
  2175. FILE_ATTRIBUTE_NORMAL,
  2176. NULL);
  2177. if (hFile == INVALID_HANDLE_VALUE)
  2178. {
  2179. //WIA_ERROR(("ReadDIBFile, unable to open %s", pszFileName));
  2180. return(hr);
  2181. }
  2182. hMap = CreateFileMapping(hFile,
  2183. NULL,
  2184. PAGE_READWRITE,
  2185. 0,
  2186. 0,
  2187. NULL);
  2188. if (!hMap)
  2189. {
  2190. //WIA_ERROR(("ReadDIBFile, CreateFileMapping failed"));
  2191. goto close_hfile_exit;
  2192. }
  2193. pFile = (PBYTE)MapViewOfFileEx(hMap,
  2194. FILE_MAP_READ | FILE_MAP_WRITE,
  2195. 0,
  2196. 0,
  2197. 0,
  2198. NULL);
  2199. if (pFile)
  2200. {
  2201. PBITMAPFILEHEADER pbmFile = (PBITMAPFILEHEADER)pFile;
  2202. PBITMAPINFO pbmi = (PBITMAPINFO)(pFile + sizeof(BITMAPFILEHEADER));
  2203. // validate bitmap
  2204. if (pbmFile->bfType == 'MB')
  2205. {
  2206. // Calculate color table size.
  2207. LONG bmiSize, ColorMapSize = 0;
  2208. if (pbmi->bmiHeader.biBitCount == 1)
  2209. {
  2210. ColorMapSize = 2 - 1;
  2211. }
  2212. else if (pbmi->bmiHeader.biBitCount == 4)
  2213. {
  2214. ColorMapSize = 16 - 1;
  2215. }
  2216. else if (pbmi->bmiHeader.biBitCount == 8)
  2217. {
  2218. ColorMapSize = 256 - 1;
  2219. }
  2220. bmiSize = sizeof(BITMAPINFO) + sizeof(RGBQUAD) * ColorMapSize;
  2221. pBits = pFile + sizeof(BITMAPFILEHEADER) + bmiSize;
  2222. *ppDib = AllocDibFileFromBits(pBits,
  2223. pbmi->bmiHeader.biWidth,
  2224. pbmi->bmiHeader.biHeight,
  2225. pbmi->bmiHeader.biBitCount);
  2226. if (*ppDib)
  2227. {
  2228. hr = S_OK;
  2229. }
  2230. }
  2231. else
  2232. {
  2233. //WIA_ERROR(("ReadDIBFile, %s is not a valid bitmap file", pszFileName));
  2234. }
  2235. }
  2236. else
  2237. {
  2238. //WIA_ERROR(("ReadDIBFile, MapViewOfFileEx failed"));
  2239. goto close_hmap_exit;
  2240. }
  2241. UnmapViewOfFile(pFile);
  2242. close_hmap_exit:
  2243. CloseHandle(hMap);
  2244. close_hfile_exit:
  2245. CloseHandle(hFile);
  2246. return(hr);
  2247. }
  2248. /*******************************************************************************
  2249. *
  2250. * GetBmiSize
  2251. *
  2252. * DESCRIPTION:
  2253. * Should never get biCompression == BI_RLE.
  2254. *
  2255. * PARAMETERS:
  2256. *
  2257. *******************************************************************************/
  2258. LONG GetBmiSize(PBITMAPINFO pbmi)
  2259. {
  2260. // determine the size of bitmapinfo
  2261. LONG lSize = pbmi->bmiHeader.biSize;
  2262. // no color table cases
  2263. if (
  2264. (pbmi->bmiHeader.biBitCount == 24) ||
  2265. ((pbmi->bmiHeader.biBitCount == 32) &&
  2266. (pbmi->bmiHeader.biCompression == BI_RGB)))
  2267. {
  2268. // no colors unless stated
  2269. lSize += sizeof(RGBQUAD) * pbmi->bmiHeader.biClrUsed;
  2270. return(lSize);
  2271. }
  2272. // bitfields cases
  2273. if (((pbmi->bmiHeader.biBitCount == 32) &&
  2274. (pbmi->bmiHeader.biCompression == BI_BITFIELDS)) ||
  2275. (pbmi->bmiHeader.biBitCount == 16))
  2276. {
  2277. lSize += 3 * sizeof(RGBQUAD);
  2278. return(lSize);
  2279. }
  2280. // palette cases
  2281. if (pbmi->bmiHeader.biBitCount == 1)
  2282. {
  2283. LONG lPal = pbmi->bmiHeader.biClrUsed;
  2284. if ((lPal == 0) || (lPal > 2))
  2285. {
  2286. lPal = 2;
  2287. }
  2288. lSize += lPal * sizeof(RGBQUAD);
  2289. return(lSize);
  2290. }
  2291. // palette cases
  2292. if (pbmi->bmiHeader.biBitCount == 4)
  2293. {
  2294. LONG lPal = pbmi->bmiHeader.biClrUsed;
  2295. if ((lPal == 0) || (lPal > 16))
  2296. {
  2297. lPal = 16;
  2298. }
  2299. lSize += lPal * sizeof(RGBQUAD);
  2300. return(lSize);
  2301. }
  2302. // palette cases
  2303. if (pbmi->bmiHeader.biBitCount == 8)
  2304. {
  2305. LONG lPal = pbmi->bmiHeader.biClrUsed;
  2306. if ((lPal == 0) || (lPal > 256))
  2307. {
  2308. lPal = 256;
  2309. }
  2310. lSize += lPal * sizeof(RGBQUAD);
  2311. return(lSize);
  2312. }
  2313. // error
  2314. return(0);
  2315. }
  2316. INT GetColorTableSize (UINT uBitCount, UINT uCompression)
  2317. {
  2318. INT nSize;
  2319. switch (uBitCount)
  2320. {
  2321. case 32:
  2322. if (uCompression != BI_BITFIELDS)
  2323. {
  2324. nSize = 0;
  2325. break;
  2326. }
  2327. // fall through
  2328. case 16:
  2329. nSize = 3 * sizeof(DWORD);
  2330. break;
  2331. case 24:
  2332. nSize = 0;
  2333. break;
  2334. default:
  2335. nSize = ((UINT)1 << uBitCount) * sizeof(RGBQUAD);
  2336. break;
  2337. }
  2338. return(nSize);
  2339. }
  2340. DWORD CalcBitsSize (UINT uWidth, UINT uHeight, UINT uBitCount, UINT uPlanes, int nAlign)
  2341. {
  2342. int nAWidth,nHeight,nABits;
  2343. DWORD dwSize;
  2344. nABits = (nAlign << 3);
  2345. nAWidth = nABits-1;
  2346. //
  2347. // Determine the size of the bitmap based on the (nAlign) size. Convert
  2348. // this to size-in-bytes.
  2349. //
  2350. nHeight = uHeight * uPlanes;
  2351. dwSize = (DWORD)(((uWidth * uBitCount) + nAWidth) / nABits) * nHeight;
  2352. dwSize = dwSize * nAlign;
  2353. return(dwSize);
  2354. }
  2355. //
  2356. // Converts hBitmap to a DIB
  2357. //
  2358. HGLOBAL BitmapToDIB (HDC hdc, HBITMAP hBitmap)
  2359. {
  2360. BITMAP bm = {0};
  2361. HANDLE hDib;
  2362. PBYTE lpDib,lpBits;
  2363. DWORD dwLength;
  2364. DWORD dwBits;
  2365. UINT uColorTable;
  2366. INT iNeedMore;
  2367. BOOL bDone;
  2368. INT nBitCount;
  2369. //
  2370. // Get the size of the bitmap. These values are used to setup the memory
  2371. // requirements for the DIB.
  2372. //
  2373. if (GetObject(hBitmap,sizeof(BITMAP),reinterpret_cast<PVOID>(&bm)))
  2374. {
  2375. nBitCount = bm.bmBitsPixel * bm.bmPlanes;
  2376. uColorTable = GetColorTableSize((UINT)nBitCount, BI_RGB);
  2377. dwBits = CalcBitsSize(bm.bmWidth,bm.bmHeight,nBitCount,1,sizeof(DWORD));
  2378. do
  2379. {
  2380. bDone = TRUE;
  2381. dwLength = dwBits + sizeof(BITMAPINFOHEADER) + uColorTable;
  2382. // Create the DIB. First, to the size of the bitmap.
  2383. //
  2384. if (hDib = GlobalAlloc(GHND,dwLength))
  2385. {
  2386. if (lpDib = reinterpret_cast<PBYTE>(GlobalLock(hDib)))
  2387. {
  2388. ((LPBITMAPINFOHEADER)lpDib)->biSize = sizeof(BITMAPINFOHEADER);
  2389. ((LPBITMAPINFOHEADER)lpDib)->biWidth = (DWORD)bm.bmWidth;
  2390. ((LPBITMAPINFOHEADER)lpDib)->biHeight = (DWORD)bm.bmHeight;
  2391. ((LPBITMAPINFOHEADER)lpDib)->biPlanes = 1;
  2392. ((LPBITMAPINFOHEADER)lpDib)->biBitCount = (WORD)nBitCount;
  2393. ((LPBITMAPINFOHEADER)lpDib)->biCompression = 0;
  2394. ((LPBITMAPINFOHEADER)lpDib)->biSizeImage = 0;
  2395. ((LPBITMAPINFOHEADER)lpDib)->biXPelsPerMeter = 0;
  2396. ((LPBITMAPINFOHEADER)lpDib)->biYPelsPerMeter = 0;
  2397. ((LPBITMAPINFOHEADER)lpDib)->biClrUsed = 0;
  2398. ((LPBITMAPINFOHEADER)lpDib)->biClrImportant = 0;
  2399. // Get the size of the bitmap.
  2400. // The biSizeImage contains the bytes
  2401. // necessary to store the DIB.
  2402. //
  2403. GetDIBits(hdc,hBitmap,0,bm.bmHeight,NULL,(LPBITMAPINFO)lpDib,DIB_RGB_COLORS);
  2404. iNeedMore = ((LPBITMAPINFOHEADER)lpDib)->biSizeImage - dwBits;
  2405. if (iNeedMore > 0)
  2406. {
  2407. dwBits = dwBits + (((iNeedMore + 3) / 4)*4);
  2408. bDone = FALSE;
  2409. }
  2410. else
  2411. {
  2412. lpBits = lpDib+sizeof(BITMAPINFOHEADER)+uColorTable;
  2413. GetDIBits(hdc,hBitmap,0,bm.bmHeight,lpBits,(LPBITMAPINFO)lpDib,DIB_RGB_COLORS);
  2414. GlobalUnlock(hDib);
  2415. return(hDib);
  2416. }
  2417. GlobalUnlock(hDib);
  2418. }
  2419. GlobalFree(hDib);
  2420. }
  2421. }
  2422. while (!bDone);
  2423. }
  2424. return(NULL);
  2425. }