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.

1576 lines
42 KiB

  1. #include "crane.h"
  2. #include "algo.h" // Must include first to get new definitions
  3. #include "common.h"
  4. #include "cranep.h"
  5. QHEAD *gapqhList[30];
  6. QNODE *gapqnList[30];
  7. #define FRAME_MAXSTEPS 128
  8. // Index of the four points that make up the bounds of the line in a self relitive
  9. // bounding box. We store the index of each point and its offset.
  10. typedef struct BOUNDS {
  11. int dX; // Delta X and Y of line segment
  12. int dY;
  13. int iAlongPlus; // Along the line in plus direction
  14. int oAlongPlus;
  15. int iAlongMinus; // Along to the line in minus direction
  16. int oAlongMinus;
  17. int iAwayPlus; // Away from the line in plus direction
  18. int oAwayPlus;
  19. int iAwayMinus; // Away from the line in minus direction
  20. int oAwayMinus;
  21. } BOUNDS;
  22. // Map simple features into index used for two feature map table
  23. static const aMapFeat[] =
  24. {
  25. 0, 1, 2, 3, 4, // FEAT_RIGHT, FEAT_DOWN_RIGHT, FEAT_DOWN, FEAT_OTHER, FEAT_COMPLEX
  26. 5, 5, 5, 5, // FEAT_CLOCKWISE_4, FEAT_CLOCKWISE_3, FEAT_CLOCKWISE_2, FEAT_CLOCKWISE_1
  27. 6, 6, 6, 6, // FEAT_C_CLOCKWISE_1, FEAT_C_CLOCKWISE_2, FEAT_C_CLOCKWISE_3, FEAT_C_CLOCKWISE_4
  28. };
  29. // Map two feature features into correct feature codes
  30. static const aMapTwoFeat[7][7] =
  31. {
  32. // Right Down-Right Down Other Complex Clockwise Counter-Clockwise
  33. { FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_COMPLEX, FEAT_2F_RI_CW, FEAT_2F_RI_CC }, // Right
  34. { FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_COMPLEX, FEAT_2F_DR_CW, FEAT_2F_DR_CC }, // Down-Right
  35. { FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_COMPLEX, FEAT_2F_DN_CW, FEAT_2F_DN_CC }, // Down
  36. { FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_2F_OTHER, FEAT_COMPLEX, FEAT_2F_OT_CW, FEAT_2F_OT_CC }, // Other
  37. { FEAT_COMPLEX, FEAT_COMPLEX, FEAT_COMPLEX, FEAT_COMPLEX, FEAT_COMPLEX, FEAT_COMPLEX, FEAT_COMPLEX }, // Complex
  38. { FEAT_2F_CW_RI, FEAT_2F_CW_DR, FEAT_2F_CW_DN, FEAT_2F_CW_OT, FEAT_COMPLEX, FEAT_2F_CW_CW, FEAT_2F_CW_CC }, // Clockwise
  39. { FEAT_2F_OTHER, FEAT_2F_CC_DR, FEAT_2F_CC_DN, FEAT_2F_CC_OT, FEAT_COMPLEX, FEAT_2F_CC_CW, FEAT_2F_CC_CC }, // C-Clockwise
  40. };
  41. // Find the points that define the bounding box reletive to the line between
  42. // the start and end points.
  43. void FindBounds(int cXYStart, int cXYEnd, XY *pXY, BOUNDS *pBounds)
  44. {
  45. int iXY;
  46. int dX, dY;
  47. BOUNDS initial; // Store initial values for later normalization
  48. ASSERT(cXYStart <= cXYEnd);
  49. // Get delta X and delta Y of (end - start) for our calculations.
  50. dX = pXY[cXYEnd].x - pXY[cXYStart].x;
  51. dY = pXY[cXYEnd].y - pXY[cXYStart].y;
  52. if (dX == 0 && dY == 0)
  53. {
  54. dX = 1; // If both dX and dY are 0, force dX to be 1 so we have a real line
  55. }
  56. // Set initial limits.
  57. initial.dX = dX;
  58. initial.dY = dY;
  59. initial.iAlongPlus = cXYEnd;
  60. initial.oAlongPlus = pXY[cXYEnd].y * dY + pXY[cXYEnd].x * dX;
  61. initial.iAlongMinus = cXYStart;
  62. initial.oAlongMinus = pXY[cXYStart].y * dY + pXY[cXYStart].x * dX;
  63. initial.iAwayPlus = cXYStart;
  64. initial.oAwayPlus = pXY[cXYStart].y * dX - pXY[cXYStart].x * dY;
  65. initial.iAwayMinus = cXYStart;
  66. initial.oAwayMinus = initial.oAwayPlus;
  67. *pBounds = initial;
  68. // Now process all the other points to see how they fall relitive to the line between
  69. // the start and the end.
  70. for (iXY = cXYStart + 1; iXY < cXYEnd; ++iXY)
  71. {
  72. int along, away;
  73. // First calculate the along and away offset.
  74. along = pXY[iXY].y * dY + pXY[iXY].x * dX;
  75. away = pXY[iXY].y * dX - pXY[iXY].x * dY;
  76. // Now see how it compares to the current limits.
  77. if (along > pBounds->oAlongPlus)
  78. {
  79. pBounds->oAlongPlus = along;
  80. pBounds->iAlongPlus = iXY;
  81. }
  82. else if (along < pBounds->oAlongMinus)
  83. {
  84. pBounds->oAlongMinus = along;
  85. pBounds->iAlongMinus = iXY;
  86. }
  87. if (away > pBounds->oAwayPlus)
  88. {
  89. pBounds->oAwayPlus = away;
  90. pBounds->iAwayPlus = iXY;
  91. }
  92. else if (away < pBounds->oAwayMinus)
  93. {
  94. pBounds->oAwayMinus = away;
  95. pBounds->iAwayMinus = iXY;
  96. }
  97. }
  98. // Finally normalize the offsets.
  99. pBounds->oAlongPlus -= initial.oAlongPlus;
  100. pBounds->oAlongMinus -= initial.oAlongMinus;
  101. pBounds->oAwayPlus -= initial.oAwayPlus;
  102. pBounds->oAwayMinus -= initial.oAwayMinus;
  103. }
  104. // Recursively step line segements
  105. // iRecursion is available for debug/tunning use, and not otherwise used.
  106. void StepLineSegment(int cXYStart, int cXYEnd, XY *pXY, int *pCStep, STEP *rgStep, int iRecursion)
  107. {
  108. BOUNDS bounds;
  109. int points[6];
  110. int ii;
  111. int normalize;
  112. int ratio;
  113. // Check for 1 or two point lines.
  114. if (cXYEnd - cXYStart < 2)
  115. {
  116. int dX, dY;
  117. int deltaAngle;
  118. ASSERT(cXYEnd >= cXYStart);
  119. if (cXYEnd == cXYStart)
  120. {
  121. ASSERT(cXYStart == 0); // Only should be passed one point if stroke is one point.
  122. ASSERT(*pCStep == 0);
  123. // We have to fake a feature for one point.
  124. rgStep[*pCStep].length = 0;
  125. rgStep[*pCStep].angle = 0;
  126. ANGLEDIFF(0, rgStep[*pCStep].angle, deltaAngle);
  127. rgStep[*pCStep].deltaAngle = (short)deltaAngle; // Delta from 0.
  128. if (*pCStep < FRAME_MAXSTEPS - 1)
  129. {
  130. ++*pCStep;
  131. }
  132. else
  133. {
  134. //ASSERT(*pCStep < FRAME_MAXSTEPS - 1);
  135. }
  136. rgStep[*pCStep].x = pXY[cXYEnd].x;
  137. rgStep[*pCStep].y = pXY[cXYEnd].y;
  138. return;
  139. }
  140. // Two points always form a line (as long as they arn't the same point).
  141. // So go ahead and add it. Calculate slope times 10 and the direction of
  142. // segment. Also length and angle
  143. dX = pXY[cXYEnd].x - pXY[cXYStart].x;
  144. dY = pXY[cXYEnd].y - pXY[cXYStart].y;
  145. rgStep[*pCStep].length = (short)Distance(dX, dY);
  146. rgStep[*pCStep].angle = (short)Arctan2(dY, dX);
  147. if (*pCStep == 0)
  148. {
  149. ANGLEDIFF(0, rgStep[*pCStep].angle, deltaAngle);
  150. rgStep[*pCStep].deltaAngle = (short)deltaAngle; // Delta from 0.
  151. }
  152. else
  153. {
  154. ANGLEDIFF(rgStep[*pCStep - 1].angle, rgStep[*pCStep].angle, deltaAngle);
  155. rgStep[*pCStep].deltaAngle = (short)deltaAngle; // Delta from last angle
  156. }
  157. if (*pCStep < FRAME_MAXSTEPS - 1)
  158. {
  159. ++*pCStep;
  160. }
  161. else
  162. {
  163. //ASSERT(*pCStep < FRAME_MAXSTEPS - 1);
  164. }
  165. rgStep[*pCStep].x = pXY[cXYEnd].x;
  166. rgStep[*pCStep].y = pXY[cXYEnd].y;
  167. return;
  168. }
  169. // Find initial bounds.
  170. FindBounds(cXYStart, cXYEnd, pXY, &bounds);
  171. // Figure out which points to use as boundries. Build them up in an array.
  172. points[0] = cXYStart;
  173. ii = 1;
  174. // Start with 'along' points. We assume any extent in the 'along' direction
  175. // requires new points.
  176. if (bounds.iAlongMinus != cXYStart)
  177. {
  178. points[ii++] = bounds.iAlongMinus;
  179. }
  180. if (bounds.iAlongPlus != cXYEnd)
  181. {
  182. points[ii++] = bounds.iAlongPlus;
  183. }
  184. // Now the 'away' points. These require a minimum ratio to be selected.
  185. // Since the ration is usually a fraction, and we want to handle integers,
  186. // multiply by 1000.
  187. normalize = bounds.dX * bounds.dX + bounds.dY * bounds.dY;
  188. normalize = max(1,normalize);
  189. ratio = (bounds.oAwayPlus * 1000) / normalize;
  190. if (ratio >= 180) // Ratio > ??
  191. {
  192. points[ii++] = bounds.iAwayPlus;
  193. }
  194. ratio = -(bounds.oAwayMinus * 1000) / normalize;
  195. if (ratio >= 180) // Ratio > ??
  196. {
  197. points[ii++] = bounds.iAwayMinus;
  198. }
  199. // See if we need to recurse or not.
  200. if (ii == 1)
  201. {
  202. int deltaAngle;
  203. // We have a straight line. Add the end point, and we are done with this path down.
  204. // Calculate slope times 10 and the direction of segment
  205. rgStep[*pCStep].length = (short)Distance(bounds.dX, bounds.dY);
  206. rgStep[*pCStep].angle = (short)Arctan2(bounds.dY, bounds.dX);
  207. if (*pCStep == 0)
  208. {
  209. ANGLEDIFF(0, rgStep[*pCStep].angle, deltaAngle);
  210. rgStep[*pCStep].deltaAngle = (short)deltaAngle; // Delta from 0.
  211. }
  212. else
  213. {
  214. ANGLEDIFF(rgStep[*pCStep - 1].angle, rgStep[*pCStep].angle, deltaAngle);
  215. rgStep[*pCStep].deltaAngle = (short)deltaAngle; // Delta from last angle
  216. }
  217. if (*pCStep < FRAME_MAXSTEPS - 1)
  218. {
  219. ++*pCStep;
  220. }
  221. else
  222. {
  223. //ASSERT(*pCStep < FRAME_MAXSTEPS - 1);
  224. }
  225. rgStep[*pCStep].x = pXY[cXYEnd].x;
  226. rgStep[*pCStep].y = pXY[cXYEnd].y;
  227. }
  228. else
  229. {
  230. int jj;
  231. // Have at least two sub-segments, process them. First sort into index order.
  232. // Note that start point is already in the correct position.
  233. //
  234. // privsort(points + 1, ii - 1);
  235. //
  236. // The following code replaces the call to privsort because
  237. // it's such a tiny array (5 or less I think) and privsort is so big.
  238. //
  239. {
  240. int bSort;
  241. do
  242. {
  243. bSort = FALSE;
  244. for (jj = 1; jj < ii - 1; jj++)
  245. {
  246. if (points[jj] > points[jj + 1])
  247. {
  248. int tmp;
  249. tmp = points[jj];
  250. points[jj] = points[jj + 1];
  251. points[jj + 1] = tmp;
  252. bSort = TRUE;
  253. }
  254. }
  255. } while (bSort);
  256. }
  257. // Add end point, it will also be automatically in the correct position.
  258. points[ii] = cXYEnd;
  259. // Sequence through each adjacent pair of point to process the segments.
  260. for (jj = 0; jj < ii; ++jj)
  261. {
  262. // We can have duplicate points, ignore them.
  263. if (points[jj] != points[jj + 1])
  264. {
  265. StepLineSegment(points[jj], points[jj + 1], pXY, pCStep, rgStep, iRecursion + 1);
  266. }
  267. }
  268. }
  269. }
  270. // Remove the 'splash' of random points sometimes caused by pen down or pen up.
  271. UINT RemovePenNoise(XY *rgxy, UINT cxy, BOOL fBegin, UINT ixyStop)
  272. {
  273. UINT ixy;
  274. int dx, dy;
  275. BOOL fSmall;
  276. UINT ixyEnd;
  277. ixyEnd = cxy - 1;
  278. // Limit splash zone if the stroke is smaller than 4 times the normal splash zone.
  279. dx = rgxy[0].x - rgxy[ixyEnd].x;
  280. dy = rgxy[0].y - rgxy[ixyEnd].y;
  281. dx /= 4;
  282. dy /= 4;
  283. fSmall = LineInSplash(dx, dy) ? 1 : 0;
  284. if (fBegin)
  285. {
  286. for (ixy = 0; ixy < ixyStop; ixy++)
  287. {
  288. dx = rgxy[ixy].x - rgxy[ixy + 1].x;
  289. dy = rgxy[ixy].y - rgxy[ixy + 1].y;
  290. if (!LineSmall(dx, dy))
  291. {
  292. // Next jump is too big to just remove, check distance from start point.
  293. dx = rgxy[0].x - rgxy[ixy + 1].x;
  294. dy = rgxy[0].y - rgxy[ixy + 1].y;
  295. if (fSmall || !LineInSplash(dx, dy))
  296. {
  297. // Doesn't fall in splash zone.
  298. break;
  299. }
  300. }
  301. }
  302. }
  303. else
  304. {
  305. for (ixy = ixyEnd; ixy > ixyStop; ixy--)
  306. {
  307. dx = rgxy[ixy].x - rgxy[ixy - 1].x;
  308. dy = rgxy[ixy].y - rgxy[ixy - 1].y;
  309. if (!LineSmall(dx, dy))
  310. {
  311. // Next jump is too big to just remove, check distance from end point.
  312. dx = rgxy[ixyEnd].x - rgxy[ixy - 1].x;
  313. dy = rgxy[ixyEnd].y - rgxy[ixy - 1].y;
  314. if (fSmall || !LineInSplash(dx, dy))
  315. {
  316. // Doesn't fall in splash zone.
  317. break;
  318. }
  319. }
  320. }
  321. }
  322. return(ixy);
  323. }
  324. UINT DebounceStrokePoints(XY *rgxy, UINT cxy)
  325. {
  326. UINT iBounceBegin, iBounceEnd;
  327. // Verify that we have enough points that we can debounce.
  328. if (cxy < 3) {
  329. return(cxy);
  330. }
  331. // Remove pen noise from pen-down and pen-up
  332. iBounceBegin = RemovePenNoise(rgxy, cxy, TRUE, cxy - 2);
  333. iBounceEnd = RemovePenNoise(rgxy, cxy, FALSE, iBounceBegin + 1);
  334. if (iBounceBegin > 0 || iBounceEnd < cxy - 1)
  335. {
  336. ASSERT(iBounceBegin < iBounceEnd);
  337. ASSERT(iBounceEnd < cxy);
  338. cxy = iBounceEnd - iBounceBegin + 1;
  339. memmove((VOID *)rgxy, (VOID *)(&rgxy[iBounceBegin]), sizeof(XY) * cxy);
  340. }
  341. return(cxy);
  342. }
  343. int StepsFromFRAME(FRAME *frame, STEP *rgStep, int cstepmax)
  344. {
  345. int cStep;
  346. int cXY;
  347. XY *pXY;
  348. // Get pointer to data and count of points
  349. cXY = frame->info.cPnt;
  350. pXY = frame->rgrawxy;
  351. // If a previous recognizer used this buffer, release it first
  352. if (frame->rgsmoothxy)
  353. ExternFree(frame->rgsmoothxy);
  354. // Smoothing has been replaced by de-splashing. JRB: Clean up to call de-splash directly.
  355. frame->rgsmoothxy = (XY *) ExternAlloc(cXY * sizeof(XY));
  356. if (frame->rgsmoothxy == (XY *) NULL)
  357. return 0;
  358. memcpy(frame->rgsmoothxy, pXY, cXY * sizeof(XY));
  359. frame->csmoothxy = cXY;
  360. frame->csmoothxy = DebounceStrokePoints(frame->rgsmoothxy, frame->csmoothxy);
  361. cXY = frame->csmoothxy;
  362. pXY = frame->rgsmoothxy;
  363. // Recursivly segment the line.
  364. cStep = 0;
  365. rgStep[0].x = pXY[0].x;
  366. rgStep[0].y = pXY[0].y;
  367. StepLineSegment(0, cXY - 1, pXY, &cStep, rgStep, 0);
  368. return(cStep);
  369. }
  370. // Convert an angle into a feature.
  371. BYTE Code(int angle)
  372. {
  373. if (angle < 12)
  374. return FEAT_RIGHT;
  375. else if (angle < 70)
  376. return FEAT_DOWN_RIGHT;
  377. else if (angle < 160)
  378. return FEAT_DOWN;
  379. else if (angle < 300)
  380. return FEAT_OTHER; // very rare directions
  381. else
  382. return FEAT_RIGHT;
  383. }
  384. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  385. // We set this to 0xFF, this should be changed if the number of valid codes is excpected
  386. // to be more than 255
  387. #define FEATURE_NULL 0xFF // avoid any valid code.
  388. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  389. // Convert steps into first pass at features.
  390. // Note that cFrame is not used.
  391. // JRB: Rewrite so that we just create one feature, instead of creating many and undoing it!!!
  392. VOID AddStepsKPROTO(KPROTO *pKProto, STEP *rgStep, int cStep, int cFrame, FRAME *frame)
  393. {
  394. int iStep; // Step being processed
  395. int cFeat; // Count of features so far in character
  396. int iStartStep; // Start of current feature.
  397. int iEndStep; // Current quess at end of step.
  398. int cumAngle; // Cumulitive change in angle for the current feature.
  399. int netAngle; // Net angle traversed by stroke (sum of step angles)
  400. int absAngle; // Total angle of stroke (sum of abs. val. of step angles)
  401. int nLength; // Total length of stroke
  402. int x, y;
  403. PRIMITIVE *pFeat = NULL;
  404. ASSERT(cStep > 0);
  405. // Spot to add next feature.
  406. cFeat = pKProto->cfeat;
  407. pFeat = (PRIMITIVE *)0;
  408. // Loop over all the steps finding features and gathering information about them.
  409. iStartStep = 0;
  410. iEndStep = 0;
  411. x = rgStep[0].x;
  412. y = rgStep[0].y;
  413. netAngle = 0;
  414. absAngle = 0;
  415. nLength = 0;
  416. for (iStep = 0; iStep < cStep; iStep++)
  417. {
  418. // Handle start of a feature.
  419. if (iStep == iEndStep)
  420. {
  421. // Get pointer to feature structure.
  422. pFeat = &(pKProto->rgfeat[cFeat]);
  423. // The array of primitives is CPRIMMAX in size so
  424. // the largest index it can handle is (CPRIMMAX-1).
  425. if (cFeat < (CPRIMMAX - 1))
  426. {
  427. cFeat++;
  428. }
  429. else
  430. {
  431. //ASSERT(cFeat < (CPRIMMAX - 1));
  432. }
  433. // Record start of feature as initial point or end of last feature.
  434. pFeat->x1 = (short)x;
  435. pFeat->y1 = (short)y;
  436. // Feature not known yet
  437. pFeat->code = FEATURE_NULL;
  438. // Default to making feature the rest of the line.
  439. iStartStep = iStep;
  440. iEndStep = cStep;
  441. cumAngle = 0;
  442. }
  443. ASSERT(pFeat);
  444. // End of current step
  445. x = rgStep[iStep + 1].x;
  446. y = rgStep[iStep + 1].y;
  447. // Add the step length to the total length
  448. nLength += Distance(x - rgStep[iStep].x, y - rgStep[iStep].y);
  449. // If not first step in this feature, add angle from last step.
  450. if (iStep > iStartStep)
  451. {
  452. cumAngle += rgStep[iStep].deltaAngle;
  453. }
  454. // If not first step, add angle from last step.
  455. if (iStep > 0)
  456. {
  457. netAngle += rgStep[iStep].deltaAngle;
  458. if (rgStep[iStep].deltaAngle >= 0)
  459. {
  460. absAngle += rgStep[iStep].deltaAngle;
  461. }
  462. else
  463. {
  464. absAngle -= rgStep[iStep].deltaAngle;
  465. }
  466. }
  467. // Decide if we need to split and start a new step. Can only be an inflection
  468. // if there are at least three segements left. Also if we are already breaking after
  469. // this step, we shouldn't check until we are past the break.
  470. if (cStep - iStep >= 3 && iEndStep != iStep + 1)
  471. {
  472. int angle1_2, angle2_3;
  473. // Figure angles between first & second segments and second & third segments.
  474. angle1_2 = rgStep[iStep + 1].deltaAngle;
  475. angle2_3 = rgStep[iStep + 2].deltaAngle;
  476. // Should never have two consecutive steps with exactly the same angle.
  477. // ASSERT(angle1_2 != 0);
  478. // ASSERT(angle2_3 != 0);
  479. // Are they in different directions? E.g. are signs different.
  480. // JRB: What about 180 or angles close to it?
  481. if (angle1_2 * angle2_3 < 0)
  482. {
  483. int abs1_2, abs2_3;
  484. // We have an inflection, which side do we break on? First get absolute
  485. // value of both angles.
  486. if (angle1_2 < 0)
  487. {
  488. abs1_2 = -angle1_2;
  489. abs2_3 = angle2_3;
  490. }
  491. else
  492. {
  493. abs1_2 = angle1_2;
  494. abs2_3 = -angle2_3;
  495. }
  496. // Now which is larger (e.g. tighter turn)
  497. if (abs1_2 >= abs2_3)
  498. {
  499. iEndStep = iStep + 1; // First is tighter, break there.
  500. }
  501. else
  502. {
  503. iEndStep = iStep + 2; // Second is tighter, break there.
  504. }
  505. }
  506. }
  507. // Handle end of a feature.
  508. if (iStep == iEndStep - 1)
  509. {
  510. int dX, dY;
  511. // Record end of feature.
  512. pFeat->x2 = (short)x;
  513. pFeat->y2 = (short)y;
  514. // Figure out feature code.
  515. if (cumAngle == 0)
  516. {
  517. pFeat->code = Code(rgStep[iStep].angle); // Streight line
  518. }
  519. else if (cumAngle > 0)
  520. {
  521. // Clockwise, how strong?
  522. if (cumAngle >= 390)
  523. {
  524. pFeat->code = FEAT_COMPLEX;
  525. }
  526. else if (cumAngle >= 300)
  527. {
  528. pFeat->code = FEAT_CLOCKWISE_4;
  529. }
  530. else if (cumAngle >= 182)
  531. {
  532. pFeat->code = FEAT_CLOCKWISE_3;
  533. }
  534. else if (cumAngle >= 120)
  535. {
  536. pFeat->code = FEAT_CLOCKWISE_2;
  537. }
  538. else if (cumAngle >= 38)
  539. {
  540. pFeat->code = FEAT_CLOCKWISE_1;
  541. }
  542. else
  543. {
  544. // Close enough to streight.
  545. dX = pFeat->x2 - pFeat->x1;
  546. dY = pFeat->y2 - pFeat->y1;
  547. pFeat->code = Code(Arctan2(dY, dX));
  548. }
  549. }
  550. else // cumAngle < 0
  551. {
  552. // Counter clockwise, how strong?
  553. if (cumAngle <= -360)
  554. {
  555. pFeat->code = FEAT_COMPLEX;
  556. }
  557. else if (cumAngle <= -182)
  558. {
  559. pFeat->code = FEAT_C_CLOCKWISE_4;
  560. }
  561. else if (cumAngle <= -115)
  562. {
  563. pFeat->code = FEAT_C_CLOCKWISE_3;
  564. }
  565. else if (cumAngle <= -60)
  566. {
  567. pFeat->code = FEAT_C_CLOCKWISE_2;
  568. }
  569. else if (cumAngle <= -38)
  570. {
  571. pFeat->code = FEAT_C_CLOCKWISE_1;
  572. }
  573. else
  574. {
  575. // Close enough to streight.
  576. dX = pFeat->x2 - pFeat->x1;
  577. dY = pFeat->y2 - pFeat->y1;
  578. pFeat->code = Code(Arctan2(dY, dX));
  579. }
  580. }
  581. }
  582. }
  583. ASSERT(pFeat->code != FEATURE_NULL);
  584. // Map multi-feature strokes down to a single feature.
  585. pFeat = &(pKProto->rgfeat[pKProto->cfeat]);
  586. pFeat->cFeatures = cFeat - pKProto->cfeat;
  587. pFeat->cSteps = (BYTE)cStep;
  588. pFeat->fDakuTen = 0;
  589. pFeat->nLength = nLength;
  590. pFeat->netAngle = netAngle;
  591. pFeat->absAngle = absAngle;
  592. pFeat->boundingBox = *RectFRAME(frame);
  593. switch (pFeat->cFeatures) {
  594. case 0 : case 1 :
  595. // Zero or one feature. Zero should not actually happen, but if it does ...
  596. goto hadSingle;
  597. case 2 : {
  598. BYTE feat0, feat1;
  599. // Two features, look up what the replacement feature is.
  600. feat0 = pFeat[0].code;
  601. feat1 = pFeat[1].code;
  602. pFeat->code = (BYTE)aMapTwoFeat[aMapFeat[feat0]][aMapFeat[feat1]];
  603. break;
  604. }
  605. case 3 : {
  606. BYTE feat0, feat1, feat2;
  607. // A few special cases, otherwise call it complex.
  608. feat0 = pFeat[0].code;
  609. feat1 = pFeat[1].code;
  610. feat2 = pFeat[2].code;
  611. switch (aMapFeat[feat0]) {
  612. case 0 : // Right
  613. switch (aMapFeat[feat1]) {
  614. case 3 : // Down
  615. pFeat->code = (aMapFeat[feat2] == 5)
  616. ? FEAT_3F_RI_DN_CW : FEAT_COMPLEX; // Clockwise
  617. break;
  618. case 5 : // Clockwise
  619. pFeat->code = (feat2 == FEAT_DOWN)
  620. ? FEAT_3F_RI_CW_DN : FEAT_COMPLEX; // Down
  621. break;
  622. case 6 : // Counter clockwise
  623. pFeat->code = (feat2 == FEAT_DOWN) // Down
  624. ? FEAT_3F_RI_CC_DN
  625. : (aMapFeat[feat2] == 5)
  626. ? FEAT_3F_RI_CC_CW : FEAT_COMPLEX; // Clockwise
  627. break;
  628. default :
  629. pFeat->code = FEAT_COMPLEX;
  630. break;
  631. }
  632. break;
  633. case 2 : // Down
  634. if (feat1 == FEAT_RIGHT) { // Right
  635. // Counter clockwise
  636. pFeat->code = (aMapFeat[feat2] == 6)
  637. ? FEAT_3F_DN_RI_CC : FEAT_COMPLEX;
  638. } else if (aMapFeat[feat1] == 5) { // Clockwise
  639. switch (aMapFeat[feat2]) {
  640. case 0 : pFeat->code = FEAT_3F_DN_CW_RI; break; // Right
  641. case 5 : pFeat->code = FEAT_3F_DN_CW_CW; break; // Clockwise
  642. case 6 : pFeat->code = FEAT_3F_DN_CW_CC; break; // Counter clockwise
  643. default : pFeat->code = FEAT_COMPLEX; break;
  644. }
  645. } else {
  646. pFeat->code = FEAT_COMPLEX;
  647. }
  648. break;
  649. case 5 : // Clockwise
  650. // Any and Clockwise
  651. pFeat->code = (aMapFeat[feat2] == 5)
  652. ? FEAT_3F_CW_XX_CW : FEAT_COMPLEX;
  653. break;
  654. case 6 : // Counter Clockwise
  655. // Clockwise and Right
  656. pFeat->code = (aMapFeat[feat1] == 5 && feat2 == FEAT_RIGHT)
  657. ? FEAT_3F_CC_CW_RI : FEAT_COMPLEX;
  658. break;
  659. default:
  660. pFeat->code = FEAT_COMPLEX;
  661. }
  662. }
  663. default:
  664. // Anything else (> 3) is always complex.
  665. pKProto->rgfeat[cFeat].code = FEAT_COMPLEX;
  666. break;
  667. }
  668. // Actually merge the position information and fix cFeat.
  669. pFeat->x2 = pKProto->rgfeat[cFeat - 1].x2;
  670. pFeat->y2 = pKProto->rgfeat[cFeat - 1].y2;
  671. cFeat = pKProto->cfeat + 1;
  672. hadSingle: // Jump here to skip merging of features.
  673. // Record number of features after our additions.
  674. pKProto->cfeat = (WORD)cFeat;
  675. }
  676. // Clean up the stepped stroke. cFrame is not used.
  677. int CraneSmoothSteps(STEP *rgStep, int cStep, int cFrame)
  678. {
  679. int ii;
  680. int cRemove;
  681. int totalLength, hookLength;
  682. int maxLength, maxSegment;
  683. int length;
  684. int lengths[6]; // First three and last three segment lengths. JRB: already calculated
  685. // Make sure we have enough segments to think about removing some.
  686. if (cStep < 2)
  687. {
  688. return cStep; // Only one segment, don't want to remove it.
  689. }
  690. else if (cStep == 2)
  691. {
  692. int percentLength;
  693. int angle;
  694. int partA, partB;
  695. int experA, experB;
  696. // Special processing for two step strokes. The only thing we may do
  697. // is drop one of the steps. We calculate the length of the first step
  698. // as a percent of the total line length. We also calculate the angle
  699. // between the lines. This gives us a rectangle with the ranges,
  700. // percentLength 0 - 100 and angle -179 to 180. We want to find if
  701. // this sample falls in one of the four corners of this that indicates
  702. // an extranious hook. The four lines (and there equations) we use are:
  703. // Lower left -180, 40 -> -30, 0 -- angle, percentLength
  704. // Lower right 180, 40 -> 30, 0
  705. // Upper left -180, 60 -> -30, 100
  706. // Upper right 180, 60 -> 30, 100
  707. // The matching formulas for the areas beyound the lines are:
  708. // Lower left 4 * angle + 15 * percentLength <= - 120
  709. // Lower right 4 * angle - 15 * percentLength >= 120
  710. // Upper left 4 * angle - 15 * percentLength <= -1620
  711. // Upper right 4 * angle + 15 * percentLength >= 1620
  712. totalLength = rgStep[0].length + rgStep[1].length;
  713. percentLength = (rgStep[0].length * 100) / totalLength;
  714. angle = rgStep[1].deltaAngle;
  715. // Precalculate the pieces.
  716. partA = 4 * angle;
  717. partB = 15 * percentLength;
  718. experA = partA + partB;
  719. experB = partA - partB;
  720. // Looks OK, keep both segments.
  721. // Test the two corners that indicate the first segment is two small.
  722. // Then test the other two, to check the second segment.
  723. if (experA <= -120 || experB >= 120)
  724. {
  725. // Delete the first segment.
  726. rgStep[0] = rgStep[1];
  727. rgStep[1] = rgStep[2];
  728. return 1;
  729. }
  730. else if (experB <= -1620 || experA >= 1620)
  731. {
  732. // Delete the second segment.
  733. return 1;
  734. }
  735. return 2;
  736. }
  737. // Check for hooks.
  738. totalLength = 0;
  739. maxLength = 0;
  740. maxSegment = -1;
  741. for (ii = 0; ii < cStep; ++ii)
  742. {
  743. int fromEnd;
  744. // Figure segment length
  745. length = rgStep[ii].length;
  746. // Keep track of longest segment.
  747. if (maxLength < length)
  748. {
  749. maxLength = length;
  750. maxSegment = ii;
  751. }
  752. // Sum all segment lengths.
  753. totalLength += length;
  754. // Record the lengths that we will need later.
  755. if (ii < 3)
  756. {
  757. lengths[ii] = length;
  758. }
  759. fromEnd = cStep - 1 - ii;
  760. if (fromEnd < 3)
  761. {
  762. lengths[5 - fromEnd] = length;
  763. }
  764. }
  765. // Check for a streight line with small curves at one or both ends.
  766. ASSERT(maxSegment >= 0);
  767. if ((maxLength * 100) / totalLength > 80)
  768. {
  769. // throw away all other segments.
  770. rgStep[0] = rgStep[maxSegment];
  771. rgStep[1] = rgStep[maxSegment + 1];
  772. return 1;
  773. }
  774. // Try to find hook at the end. First try to remove two segments.
  775. cRemove = 0;
  776. if (cStep >= 3)
  777. {
  778. hookLength = lengths[5] + lengths[4];
  779. if (hookLength * 10 < totalLength)
  780. {
  781. cRemove = 2;
  782. }
  783. }
  784. // If we couldn't remove two segments, try one.
  785. if (cRemove == 0 && lengths[5] * 10 < totalLength)
  786. {
  787. cRemove = 1;
  788. }
  789. // Drop points at the end as needed. Verify that there are enough points to continue.
  790. cStep -= cRemove;
  791. if (cStep < 2)
  792. {
  793. return cStep; // Only one segment left.
  794. }
  795. // Try to find hook at the start. First try to remove two segments.
  796. cRemove = 0;
  797. if (cStep >= 3)
  798. {
  799. hookLength = lengths[0] + lengths[1];
  800. if (hookLength * 10 < totalLength)
  801. {
  802. cRemove = 2;
  803. }
  804. }
  805. // If we couldn't remove two segments, try one.
  806. if (cRemove == 0 && lengths[0] * 10 < totalLength)
  807. {
  808. cRemove = 1;
  809. }
  810. // Remove extra steps from start of array.
  811. if (cRemove > 0)
  812. {
  813. int from, to;
  814. to = 0;
  815. from = cRemove;
  816. while (from <= cStep)
  817. {
  818. rgStep[to++] = rgStep[from++];
  819. }
  820. cStep -= cRemove;
  821. }
  822. return cStep;
  823. }
  824. int IsDakuten(RECT *pBoundingBox, FRAME *pFrame1, FRAME *pFrame2, KPROTO *pKProto)
  825. {
  826. int cXY;
  827. XY *pXY;
  828. int x1_1, y1_1, x2_1, y2_1; // Start and end of first stroke.
  829. int x1_2, y1_2, x2_2, y2_2; // Start and end of second stroke.
  830. int x1Per, y1Per, x2Per, y2Per;
  831. int dX, dY;
  832. int cFeat;
  833. PRIMITIVE *pFeat;
  834. // Figure size of bounding box.
  835. dX = pBoundingBox->right - pBoundingBox->left;
  836. dY = pBoundingBox->bottom - pBoundingBox->top;
  837. // Get pointer to data and count of points of first stroke.
  838. cXY = pFrame1->info.cPnt;
  839. pXY = pFrame1->rgrawxy;
  840. // Get end points
  841. x1_1 = pXY[0].x;
  842. y1_1 = pXY[0].y;
  843. x2_1 = pXY[cXY - 1].x;
  844. y2_1 = pXY[cXY - 1].y;
  845. // Get pointer to data and count of points of second stroke.
  846. cXY = pFrame2->info.cPnt;
  847. pXY = pFrame2->rgrawxy;
  848. // Get end points
  849. x1_2 = pXY[0].x;
  850. y1_2 = pXY[0].y;
  851. x2_2 = pXY[cXY - 1].x;
  852. y2_2 = pXY[cXY - 1].y;
  853. // Sometimesd strokes are reveresed.
  854. if (x1_1 > x1_2 && x2_1 > x2_2) {
  855. // Try switching strokes.
  856. return IsDakuten(pBoundingBox, pFrame2, pFrame1, pKProto);
  857. }
  858. // Get end positions relitive to the upper right corner of the bounding box.
  859. // Scale by 100 so we can get percents as integers.
  860. x1Per = ((pBoundingBox->right - x1_1) * 100) / dX;
  861. y1Per = ((y1_1 - pBoundingBox->top) * 100) / dY;
  862. x2Per = ((pBoundingBox->right - x2_1) * 100) / dX;
  863. y2Per = ((y2_1 - pBoundingBox->top) * 100) / dY;
  864. // Check if end points match expected locations for first stroke.
  865. if (x1Per > 70 || y1Per > 40 || x2Per > 60 || y2Per > 60) {
  866. // First stroke wrong position.
  867. return FALSE;
  868. }
  869. // Compare relitive positions of strokes.
  870. x1Per = ((x1_2 - x1_1) * 100) / dX;
  871. y1Per = ((y1_2 - y1_1) * 100) / dY;
  872. x2Per = ((x2_2 - x2_1) * 100) / dX;
  873. y2Per = ((y2_2 - y2_1) * 100) / dY;
  874. if (x1Per <= 0 || 50 <= x1Per || y1Per <= -30 || 25 <= y1Per
  875. || x2Per <= 0 || 55 <= x2Per || y2Per <= -35 || 30 <= y2Per
  876. ) {
  877. // Reletive positions of the strokes wrong.
  878. return FALSE;
  879. }
  880. // Get end positions relitive to the upper right corner of the bounding box.
  881. // Scale by 100 so we can get percents as integers.
  882. x1Per = ((pBoundingBox->right - x1_2) * 100) / dX;
  883. y1Per = ((y1_2 - pBoundingBox->top) * 100) / dY;
  884. x2Per = ((pBoundingBox->right - x2_2) * 100) / dX;
  885. y2Per = ((y2_2 - pBoundingBox->top) * 100) / dY;
  886. // Check if end points match expected locations for second stroke.
  887. if (x1Per > 40 || y1Per > 45 || x2Per > 25 || y2Per > 55) {
  888. // Second stroke wrong position.
  889. return FALSE;
  890. }
  891. {
  892. BOUNDS bounds;
  893. int normalize;
  894. int ratioPlus, ratioMinus;
  895. // End point tests pass, verify that we really have stokes that look mostly like lines.
  896. // We ignore the along direction because that does not seem to be a problem
  897. // We keep the away in loose bound since we only need to catch ones way out of line.
  898. // Since the ratio is usually a fraction, and we want to handle integers,
  899. // multiply by 1000.
  900. // Note that for very small lines, we don't care what the look like.
  901. FindBounds(0, pFrame1->info.cPnt - 1, pFrame1->rgrawxy, &bounds);
  902. if (bounds.dX > 20 || bounds.dX < -20 || bounds.dY > 20 || bounds.dY < -20) { // Is it large?
  903. normalize = bounds.dX * bounds.dX + bounds.dY * bounds.dY;
  904. normalize = max(1,normalize);
  905. ratioPlus = (bounds.oAwayPlus * 1000) / normalize;
  906. ratioMinus = -(bounds.oAwayMinus * 1000) / normalize;
  907. if (ratioPlus >= 1000 || ratioMinus >= 1000) { // Ratio > 1/1
  908. return FALSE;
  909. }
  910. }
  911. FindBounds(0, pFrame2->info.cPnt - 1, pFrame2->rgrawxy, &bounds);
  912. if (bounds.dX > 20 || bounds.dX < -20 || bounds.dY > 20 || bounds.dY < -20) { // Is it large?
  913. normalize = bounds.dX * bounds.dX + bounds.dY * bounds.dY;
  914. normalize = max(1,normalize);
  915. ratioPlus = (bounds.oAwayPlus * 1000) / normalize;
  916. ratioMinus = -(bounds.oAwayMinus * 1000) / normalize;
  917. if (ratioPlus >= 1000 || ratioMinus >= 1000) { // Ratio > 1/1
  918. return FALSE;
  919. }
  920. }
  921. }
  922. // Passes all the test. So add the two stokes to the feature list.
  923. cFeat = pKProto->cfeat;
  924. //// Handle first stroke
  925. // The array of primitives is CPRIMMAX in size so
  926. // the largest index it can handle is (CPRIMMAX-1).
  927. pFeat = &(pKProto->rgfeat[cFeat]);
  928. if (cFeat < (CPRIMMAX - 1)) {
  929. cFeat++;
  930. } else {
  931. //ASSERT(cFeat < (CPRIMMAX - 1));
  932. }
  933. // Record start & end of feature.
  934. pFeat->x1 = (short)x1_1;
  935. pFeat->y1 = (short)y1_1;
  936. pFeat->x2 = (short)x2_1;
  937. pFeat->y2 = (short)y2_2;
  938. // Feature is small down right.
  939. pFeat->code = FEAT_DOWN_RIGHT;
  940. // Record CART info
  941. pFeat->cFeatures = 1;
  942. pFeat->cSteps = 1;
  943. pFeat->fDakuTen = 1;
  944. pFeat->nLength = Distance(pFeat->x2 - pFeat->x1, pFeat->y2 - pFeat->y1);
  945. pFeat->netAngle = 0;
  946. pFeat->absAngle = 0;
  947. pFeat->boundingBox = *RectFRAME(pFrame1);
  948. /// Now on to stroke two
  949. // The array of primitives is CPRIMMAX in size so
  950. // the largest index it can handle is (CPRIMMAX-1).
  951. pFeat = &(pKProto->rgfeat[cFeat]);
  952. if (cFeat < (CPRIMMAX - 1)) {
  953. cFeat++;
  954. } else {
  955. //ASSERT(cFeat < (CPRIMMAX - 1));
  956. }
  957. // Record start & end of feature.
  958. pFeat->x1 = (short)x1_2;
  959. pFeat->y1 = (short)y1_2;
  960. pFeat->x2 = (short)x2_2;
  961. pFeat->y2 = (short)y2_2;
  962. // Feature is small down right.
  963. pFeat->code = FEAT_DOWN_RIGHT;
  964. // Record number of features after our additions.
  965. pKProto->cfeat = (WORD)cFeat;
  966. // Record CART info
  967. pFeat->cFeatures = 1;
  968. pFeat->cSteps = 1;
  969. pFeat->fDakuTen = 1;
  970. pFeat->nLength = Distance(pFeat->x2 - pFeat->x1, pFeat->y2 - pFeat->y1);
  971. pFeat->netAngle = 0;
  972. pFeat->absAngle = 0;
  973. pFeat->boundingBox = *RectFRAME(pFrame2);
  974. return TRUE;
  975. }
  976. int FeaturizeGLYPH(GLYPH *glyph, PRIMITIVE *rgprim, RECT *pRect)
  977. {
  978. KPROTO *kproto;
  979. int cstepmax, cframe, cstep, cprim;
  980. STEP *rgstep;
  981. FRAME *frame;
  982. int iframe;
  983. ASSERT(glyph);
  984. kproto = (KPROTO *) ExternAlloc(sizeof(KPROTO));
  985. if (kproto == (KPROTO *) NULL)
  986. return 0;
  987. memset(kproto, '\0', sizeof(KPROTO));
  988. kproto->rgfeat = rgprim;
  989. GetRectGLYPH(glyph, &kproto->rect);
  990. *pRect = kproto->rect; // Pass bounding rect up.
  991. rgstep = (STEP *) ExternAlloc(FRAME_MAXSTEPS * sizeof(STEP));
  992. if (rgstep == (STEP *) NULL)
  993. goto cleanup;
  994. cstepmax = FRAME_MAXSTEPS;
  995. cframe = CframeGLYPH(glyph);
  996. for (iframe = 0; iframe < cframe; iframe++)
  997. {
  998. frame = FrameAtGLYPH(glyph, iframe);
  999. // Special check for dakuten. If we have 3 to 6 strokes and are looking
  1000. // at the next to last stroke, we need to check if the last two strokes form
  1001. // a dakuten.
  1002. if (3 <= cframe && cframe <= 6 && iframe == cframe - 2)
  1003. {
  1004. FRAME *frame2;
  1005. // Get the last frame as well
  1006. frame2 = FrameAtGLYPH(glyph, iframe + 1);
  1007. // Check the strokes, adds two features if it is a match.
  1008. if (IsDakuten(&kproto->rect, frame, frame2, kproto))
  1009. {
  1010. // OK, Have it abort the rest of the processing on the last two strokes.
  1011. break;
  1012. }
  1013. }
  1014. cstep = StepsFromFRAME(frame, rgstep, cstepmax);
  1015. if (cstep == 0)
  1016. {
  1017. kproto->cfeat = 0; // Indicate a failure condition
  1018. goto cleanu2;
  1019. }
  1020. cstep = CraneSmoothSteps(rgstep, cstep, cframe);
  1021. AddStepsKPROTO(kproto, rgstep, cstep, cframe, frame);
  1022. }
  1023. cleanu2:
  1024. ExternFree(rgstep);
  1025. cleanup:
  1026. cprim = kproto->cfeat;
  1027. ExternFree(kproto);
  1028. return cprim;
  1029. }
  1030. BOOL AllocFeature(SAMPLE *, int);
  1031. BOOL MakeFeatures(SAMPLE *_this, void *pv)
  1032. {
  1033. PRIMITIVE rgprim[CPRIMMAX];
  1034. RECT rc;
  1035. int ifeat;
  1036. int iprim, cprim;
  1037. int xShift, yShift; // Normalize position
  1038. double xScale, yScale; // Normalize size
  1039. double xyRatio; // Ratio of xy (scaled to fit in a WORD)
  1040. cprim = FeaturizeGLYPH((GLYPH *) pv, rgprim, &rc);
  1041. if (!cprim)
  1042. return FALSE;
  1043. // Figure normilization constants.
  1044. xShift = -rc.left;
  1045. xScale = 1000.0 / (double)(rc.right - rc.left);
  1046. yShift = -rc.top;
  1047. yScale = 1000.0 / (double)(rc.bottom - rc.top);
  1048. xyRatio = (xScale / yScale) * 256.0;
  1049. // Stroke count MUST be set before any per stroke data can be allocated
  1050. _this->cstrk = (short)CframeGLYPH((GLYPH *) pv);
  1051. // Now allocate per stroke features (currently, this is all of them)
  1052. for (ifeat = 0; ifeat < FEATURE_COUNT; ifeat++)
  1053. {
  1054. if (!AllocFeature(_this, ifeat))
  1055. return FALSE;
  1056. }
  1057. // Set per character information (stroke count, dakuten, et.al.)
  1058. _this->fDakuten = rgprim[cprim - 1].fDakuTen;
  1059. // Set per stroke information (angle, stepping, end point position, et.al.)
  1060. for (iprim = 0; iprim < cprim; iprim++)
  1061. {
  1062. ((SHORT *) _this->apfeat[FEATURE_ANGLE_NET]->data)[iprim]
  1063. = max(min(rgprim[iprim].netAngle, 32767), -32768);
  1064. ((USHORT *) _this->apfeat[FEATURE_ANGLE_ABS]->data)[iprim]
  1065. = min(rgprim[iprim].absAngle, 0x0000ffff);
  1066. ((USHORT *) _this->apfeat[FEATURE_LENGTH]->data)[iprim]
  1067. = min(rgprim[iprim].nLength, 0x0000ffff);
  1068. ((BYTE *) _this->apfeat[FEATURE_STEPS]->data)[iprim]
  1069. = rgprim[iprim].cSteps;
  1070. ((BYTE *) _this->apfeat[FEATURE_FEATURES]->data)[iprim]
  1071. = rgprim[iprim].cFeatures;
  1072. ((END_POINTS *) _this->apfeat[FEATURE_XPOS]->data)[iprim].start
  1073. = (short) ((double) (rgprim[iprim].x1 + xShift) * xScale);
  1074. ((END_POINTS *) _this->apfeat[FEATURE_XPOS]->data)[iprim].end
  1075. = (short) ((double) (rgprim[iprim].x2 + xShift) * xScale);
  1076. ((END_POINTS *) _this->apfeat[FEATURE_YPOS]->data)[iprim].start
  1077. = (short) ((double) (rgprim[iprim].y1 + yShift) * yScale);
  1078. ((END_POINTS *) _this->apfeat[FEATURE_YPOS]->data)[iprim].end
  1079. = (short) ((double) (rgprim[iprim].y2 + yShift) * yScale);
  1080. ((RECTS *) _this->apfeat[FEATURE_STROKE_BBOX]->data)[iprim].x1
  1081. = (short) ((double) (rgprim[iprim].boundingBox.left + xShift) * xScale);
  1082. ((RECTS *) _this->apfeat[FEATURE_STROKE_BBOX]->data)[iprim].y1
  1083. = (short) ((double) (rgprim[iprim].boundingBox.top + yShift) * yScale);
  1084. ((RECTS *) _this->apfeat[FEATURE_STROKE_BBOX]->data)[iprim].x2
  1085. = (short) ((double) (rgprim[iprim].boundingBox.right + xShift) * xScale);
  1086. ((RECTS *) _this->apfeat[FEATURE_STROKE_BBOX]->data)[iprim].y2
  1087. = (short) ((double) (rgprim[iprim].boundingBox.bottom + yShift) * yScale);
  1088. };
  1089. return TRUE;
  1090. }
  1091. BOOL CraneLoadFromPointer(LOCRUN_INFO *pLocRunInfo, QHEAD ** ppHead,QNODE **ppNode,BYTE *pbRes)
  1092. {
  1093. int *pOffsetQBT;
  1094. int *pOffsetQBH;
  1095. int iLoad;
  1096. const CRANEDB_HEADER *pHeader = (CRANEDB_HEADER *)pbRes;
  1097. if ( (pHeader->fileType != CRANEDB_FILE_TYPE)
  1098. || (pHeader->headerSize < sizeof(*pHeader))
  1099. || (pHeader->minFileVer > CRANEDB_CUR_FILE_VERSION)
  1100. || (pHeader->curFileVer < CRANEDB_OLD_FILE_VERSION)
  1101. || memcmp (pHeader->adwSignature, pLocRunInfo->adwSignature,sizeof(pHeader->adwSignature))
  1102. )
  1103. {
  1104. return FALSE;
  1105. }
  1106. pOffsetQBT = (int *)(pbRes + pHeader->headerSize);
  1107. pOffsetQBH = pOffsetQBT + 29;
  1108. for (iLoad = 1; iLoad <= 30; iLoad++)
  1109. {
  1110. ppHead[iLoad - 1] = (QHEAD *) (((BYTE *) pOffsetQBT) + pOffsetQBH[iLoad - 1]);
  1111. ppNode[iLoad - 1] = (QNODE *) (((BYTE *) pOffsetQBT) + pOffsetQBT[iLoad - 1]);
  1112. }
  1113. return TRUE;
  1114. }
  1115. BOOL CraneLoadRes(HINSTANCE hInst, int resNumber, int resType, LOCRUN_INFO *pLocRunInfo)
  1116. {
  1117. HANDLE hres;
  1118. HGLOBAL hglb;
  1119. BYTE * pBase;
  1120. hres = FindResource(hInst, (LPCTSTR) resNumber, (LPCTSTR) resType);
  1121. ASSERT(hres);
  1122. if (hres == NULL)
  1123. return FALSE;
  1124. hglb = LoadResource(hInst, hres);
  1125. ASSERT(hglb);
  1126. if (hglb == NULL)
  1127. return FALSE;
  1128. pBase = (BYTE *) LockResource(hglb);
  1129. if (pBase == NULL)
  1130. return FALSE;
  1131. return CraneLoadFromPointer(pLocRunInfo, gapqhList, gapqnList, pBase);
  1132. }
  1133. QNODE *SkipAltList(UNIQART *puni)
  1134. {
  1135. while (puni->unicode)
  1136. puni++;
  1137. return (QNODE *) ++puni;
  1138. }
  1139. //FILE *pDebugFile = 0;
  1140. QNODE *CraneFindAnswer(SAMPLE *psample, QNODE *pnode)
  1141. {
  1142. SAMPLE_INFO sample;
  1143. if ((pnode->uniqart.qart.flag & 0xf0) != QART_QUESTION)
  1144. return pnode;
  1145. // Now ask the question
  1146. sample.pSample = psample;
  1147. AnswerQuestion(pnode->uniqart.qart.question, pnode->param1, pnode->param2, &sample, 1);
  1148. //if (pDebugFile) {
  1149. // fwprintf(pDebugFile, L" CART: (%2d:%5d:%5d) -> %5d ?< %5d\n", pnode->uniqart.qart,
  1150. // pnode->param1, pnode->param2, sample.iAnswer, pnode->value
  1151. // );
  1152. //}
  1153. if (sample.iAnswer <= pnode->value)
  1154. {
  1155. if (pnode->uniqart.qart.flag & QART_NOBRANCH)
  1156. return CraneFindAnswer(psample, SkipAltList((UNIQART *) pnode->extra));
  1157. else
  1158. return CraneFindAnswer(psample, (QNODE *) pnode->extra);
  1159. }
  1160. else
  1161. {
  1162. if (pnode->uniqart.qart.flag & QART_NOBRANCH)
  1163. return CraneFindAnswer(psample, (QNODE *) &pnode->offset);
  1164. else
  1165. return CraneFindAnswer(psample, (QNODE *) (((BYTE *) pnode->extra) + pnode->offset));
  1166. }
  1167. }
  1168. BOOL CraneMatch(ALT_LIST *pAlt,
  1169. int cAlt,
  1170. GLYPH *pGlyph,
  1171. CHARSET *pCS,
  1172. DRECTS *pdrcs,
  1173. FLOAT eCARTWeight,
  1174. LOCRUN_INFO *pLocRunInfo)
  1175. {
  1176. int cstrk = CframeGLYPH(pGlyph) - 1; // Our arrays are zero based
  1177. int index;
  1178. WORD wStart;
  1179. WORD wFinal;
  1180. QNODE *pqnode;
  1181. SAMPLE sample;
  1182. // See if Afterburner even has something with which to work
  1183. if ((!pAlt->cAlt) || (cstrk < 0) || (cstrk >= 30))
  1184. return FALSE;
  1185. // Get the page number of the top choice from the previous shape matcher
  1186. index = HIGH_INDEX(pAlt->awchList[0]);
  1187. if ((index < 0) || (index >= HIGH_INDEX_LIMIT - 1))
  1188. return FALSE;
  1189. wStart = gapqhList[cstrk]->awIndex[index];
  1190. wFinal = gapqhList[cstrk]->awIndex[index + 1];
  1191. // Note that wStart may equal wFinal, in which case there are no valid selections.
  1192. // Next get the low byte from the top choice, shift it to the left-most byte position.
  1193. // Also, assume we didn't find a CART to run.
  1194. index = (pAlt->awchList[0] & 0x00ff) << 24;
  1195. pqnode = (QNODE *) NULL;
  1196. while (wStart < wFinal)
  1197. {
  1198. if ((gapqhList[cstrk]->aqIndex[wStart] & 0xff000000) == ((DWORD) index))
  1199. {
  1200. pqnode = (QNODE *) &(((BYTE *) gapqnList[cstrk])[gapqhList[cstrk]->aqIndex[wStart] & 0x00ffffff]);
  1201. break;
  1202. }
  1203. if (gapqhList[cstrk]->aqIndex[wStart] > ((DWORD) index))
  1204. break;
  1205. wStart++;
  1206. }
  1207. // If no tree was found, exit
  1208. if (pqnode == (QNODE *) NULL)
  1209. return FALSE;
  1210. // OK, now we know we have a CART to run. Featurize the ink and run the tree
  1211. InitFeatures(&sample);
  1212. sample.drcs = *pdrcs;
  1213. wFinal = MakeFeatures(&sample, pGlyph) ? *((WORD *) CraneFindAnswer(&sample, pqnode)) : 0;
  1214. FreeFeatures(&sample);
  1215. // Did CART give us back a meaningful result?
  1216. if (!wFinal)
  1217. return FALSE;
  1218. // Short cut processing if we are top 1 already.
  1219. if (wFinal == pAlt->awchList[0]) {
  1220. pAlt->aeScore[0] += (FLOAT)eCARTWeight;
  1221. return TRUE;
  1222. }
  1223. // Did CART give us a code point that is enabled by the current recmask
  1224. {
  1225. if (!IsAllowedChar(pLocRunInfo, pCS, wFinal))
  1226. {
  1227. return FALSE; // Can't stick it in.
  1228. }
  1229. }
  1230. // Now, walk through the previous ALT_LIST and see if this character was already proposed
  1231. index = 0;
  1232. while (((UINT) index) < pAlt->cAlt)
  1233. {
  1234. if (pAlt->awchList[index] == wFinal)
  1235. break;
  1236. index++;
  1237. }
  1238. if ((UINT) index >= pAlt->cAlt) {
  1239. if (pAlt->cAlt < (UINT) cAlt) {
  1240. pAlt->cAlt++;
  1241. } else {
  1242. index--;
  1243. }
  1244. }
  1245. while (index)
  1246. {
  1247. pAlt->awchList[index] = pAlt->awchList[index - 1];
  1248. pAlt->aeScore[index] = pAlt->aeScore[index - 1];
  1249. index--;
  1250. }
  1251. pAlt->awchList[0] = wFinal;
  1252. pAlt->aeScore[0] = pAlt->aeScore[1] + (FLOAT)eCARTWeight;
  1253. return TRUE;
  1254. }