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.
 
 
 
 
 
 

1293 lines
27 KiB

// Panel.c
// James A. Pittman
// July 23, 1998
// Recognizes an entire panel at once by looping over lines, then looping over
// ink blobs that have large gaps between them, and finally looping over the strokes
// within a blob, using recognition scores to help decide what is a word and what is not.
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "common.h"
#include "inferno.h"
#include "nfeature.h"
#include "engine.h"
#include "Panel.h"
#include "Normal.h"
#include "linebrk.h"
#include "bear.h"
#include "Avalanche.h"
#include "wordbrk.h"
#include "recdefs.h"
#include "multsegm.h"
#include "recoutil.h"
#include <baseline.h>
#define STREQ(s,t) !strcmp(s,t)
#define STRDIFF(s,t) strcmp(s,t)
#if defined(TRAINTIME_AVALANCHE)
int ComputePromptWordMaps (XRC *pxrc); // function used to compute the word mapping of the prompt
void FreeWordMaps ();
#endif
#if (defined HWX_INTERNAL) && (defined HWX_SAVEGLYPH)
void SaveGlyph (GLYPH *pGlyph)
{
FILE *fp;
int cStrk = CframeGLYPH (pGlyph);
GLYPH *gl;
fp = fopen ("glyph.bin", "wb");
if (!fp)
return;
fwrite (&cStrk, 1, sizeof (int), fp);
for (gl = pGlyph; gl; gl = gl->next)
{
fwrite (&gl->frame->info.cPnt, 1, sizeof (int), fp);
fwrite (gl->frame->rgrawxy, gl->frame->info.cPnt, sizeof (XY), fp);
}
fclose (fp);
}
#endif
static int _cdecl ResultCmp(const XRCRESULT *a, const XRCRESULT *b)
{
if (a->cost > b->cost)
return(1);
else if (a->cost < b->cost)
return(-1);
return(0);
}
// Add a map structure to the XRCRESULT containing the stroke
// iDs in the glyph
static int AddStrokeIdFromGlyph(GLYPH *pGlyph, XRCRESULT *pAns)
{
int iRet = HRCR_ERROR;
ASSERT(pGlyph);
ASSERT(pAns);
ASSERT(pAns->pMap);
if (pGlyph && pAns && pAns->pMap)
{
int iLastIdx = -1, *piIdx;
pAns->pMap->cStrokes = CframeGLYPH(pGlyph);
piIdx = pAns->pMap->piStrokeIndex = (int *)ExternAlloc(sizeof(*pAns->pMap->piStrokeIndex) * pAns->pMap->cStrokes);
ASSERT(pAns->pMap->piStrokeIndex);
if (!pAns->pMap->piStrokeIndex)
{
return HRCR_MEMERR;
}
// Add the strokes in ascending order
for ( ; pGlyph ; pGlyph = pGlyph->next)
{
if (pGlyph->frame)
{
int iFrame = pGlyph->frame->iframe;
ASSERT(iFrame != iLastIdx);
if (iFrame < iLastIdx)
{
int *piInsert = pAns->pMap->piStrokeIndex;
// Search to find insertion point
for ( ; piInsert < piIdx ; ++piInsert)
{
if (iFrame < *piInsert)
{
int iSwap = *piInsert;
*piInsert = iFrame;
iFrame = iSwap;
}
}
}
iLastIdx = *piIdx = iFrame;
++piIdx;
ASSERT (piIdx - pAns->pMap->piStrokeIndex <= pAns->pMap->cStrokes);
}
}
iRet = HRCR_OK;
}
return iRet;
}
//!!! BUG - This should be setting up the HRC from the values set in the xrc
//!!! for madcow. We need that xrc passed down to here and set the exact
//!!! same alc, dict mode, lang mode etc in from it.
// set up a the HRC for word recognition (Do not allow white space)
static int initWordHRC(XRC *pMainXrc, GLYPH *pGlyph, HRC *phrc)
{
int cFrame;
HRC hrc = CreateCompatibleHRC((HRC)pMainXrc, NULL);
int iRet = HRCR_OK;
XRC *pxrcNew;
*phrc = (HRC)0;
if (!hrc) // don't go to exit as we do not need to destroy an HRC
return HRCR_MEMERR;
iRet = SetHwxFlags(hrc, pMainXrc->flags | RECOFLAG_WORDMODE);
if (iRet != HRCR_OK)
goto exit;
pxrcNew = (XRC *) hrc;
pxrcNew->answer.cAltMax = MAX_ALT_WORD;
// build glyph of specific frames inside hrc
// we later may be able to alter the API to allow additional frames to be
// added after recognition has already been run
for ( cFrame = 0 ; pGlyph; pGlyph = pGlyph->next, ++cFrame)
{
FRAME *pFrame = pGlyph->frame, *pAddedFrame;
ASSERT(pFrame);
if (!pFrame)
{
iRet = HRCR_ERROR;
goto exit;
}
iRet = AddPenInputHRC(hrc, RgrawxyFRAME(pFrame), NULL, 0, &(pFrame->info));
if (iRet != HRCR_OK)
goto exit;
// Keep globally allocated frame numbers
if ( (pAddedFrame = FrameAtGLYPH(((XRC *)hrc)->pGlyph, cFrame)))
{
pAddedFrame->iframe = pFrame->iframe;
pAddedFrame->rect = pFrame->rect;
}
}
*phrc = hrc;
return HRCR_OK;
exit:
DestroyHRC(hrc);
*phrc = (HRC)0;
return iRet;
}
// set up a the HRC for phrase recognition (Allow white space)
int initPhraseHRC(XRC *pMainXrc, GLYPH *pGlyph, HRC *phrc)
{
int iRet = HRCR_ERROR;
if (HRCR_OK == initWordHRC(pMainXrc, pGlyph, phrc))
{
XRC *pxrcNew = (XRC *)(*phrc);
iRet = SetHwxFlags(*phrc, pMainXrc->flags & ~RECOFLAG_WORDMODE);
pxrcNew->answer.cAltMax = MAX_ALT_PHRASE;
if (iRet != HRCR_OK)
goto exit;
return HRCR_OK;
}
exit:
DestroyHRC(*phrc);
*phrc = (HRC)0;
return iRet;
}
// The purpose of this function is to run a wordmap thru inferno and return an XRC
HRC InfernoRecognize (XRC *pMainXrc, GLYPH *pGlyph, GUIDE *pGuide, int yDev, BOOL bWordMode)
{
HRC full;
int iRet;
XRC *pxrc;
// Use panel mode
if (!bWordMode)
{
iRet = initPhraseHRC(pMainXrc, pGlyph, &full);
if (iRet != HRCR_OK)
return NULL;
}
// Or word mode
else
{
iRet = initWordHRC(pMainXrc, pGlyph, &full);
if (iRet != HRCR_OK)
return NULL;
}
pxrc = (XRC *)full;
// set the guide
if (pGuide)
{
pxrc->guide = *pGuide;
}
iRet = InfProcessHRC(full, yDev);
if (HRCR_OK != iRet)
{
DestroyHRC (full);
return NULL;
}
// Inferno, if it cannot featurize the ink,
// Destroy HRC
if (!(pxrc->nfeatureset))
{
DestroyHRC (full);
return NULL;
}
return full;
}
// recognize in word mode (~ALC_WHITE) the ink corresponding to a specific wordmap
// The results will fill in the ALTERNATES structure. yDev is supplied by the caller
// The boolean parameter specifies whether the caller wants to run Avalanche or not
int RecognizeWord (XRC *pxrcInferno, BEARXRC *pBearXrc, WORDMAP *pWordMap, BOOL bInfernoMap, int yDev, ALTERNATES *pAlt, int bAval)
{
int iRet = FALSE;
HRC full = NULL;
XRC *pxrc = NULL;
GLYPH *pGlyph = NULL;
int iWordBearIdx;
// This is an an efficiency hack: When the words agree in the top 1 panel mode choice
// for pMap, we can save a lot of time by nor reerunning in word mode
// but just extracting an alt list from the panel mode result.
if (bAval && pBearXrc && pWordMap && Top1WordsEqual(pxrcInferno, pBearXrc, pWordMap, &iWordBearIdx))
{
GLYPH *pWordGlyph;
// init
pAlt->cAltMax = MAXMAXALT;
if (FALSE == copySingleWordAltList(pAlt, &(pBearXrc->answer.aAlt[0].pMap[iWordBearIdx].alt), pWordMap, pxrcInferno))
{
goto exit;
}
insertLatinLayoutMetrics(pxrcInferno, pAlt, NULL);
// call sole to handle one char processing
pWordGlyph = GlyphFromWordMap (pxrcInferno->pGlyph, pWordMap);
ASSERT (pWordGlyph);
if (pWordGlyph)
{
CallSole (pxrcInferno, pWordGlyph, pxrcInferno->bGuide ? &pxrcInferno->guide : NULL, pAlt);
DestroyGLYPH (pWordGlyph);
}
return TRUE;
}
// if a word map is provided, construct its glyph,
// and pass it to be recognized by inferno
if (pWordMap)
{
pGlyph = GlyphFromWordMap (pxrcInferno->pGlyph, pWordMap);
}
else
{
pGlyph = pxrcInferno->pGlyph;
}
if (!pGlyph)
{
goto exit;
}
if (pxrcInferno->bGuide)
{
pxrc = (XRC *)InfernoRecognize (pxrcInferno, pGlyph, &pxrcInferno->guide, yDev, TRUE);
if (!pxrc)
goto exit;
}
else
{
pxrc = (XRC *)InfernoRecognize (pxrcInferno, pGlyph, NULL, yDev, TRUE);
if (!pxrc)
goto exit;
}
// now we'll run Avalanche if this is what the caller wants
if (bAval)
{
// We only need to pass Bear's ALT to Avalalanche so that we do not run BEAR again
// when there are no candidates this must be Inferno's word map
if (bInfernoMap)
Avalanche (pxrc, NULL);
else
Avalanche (pxrc, &pWordMap->alt);
}
// success, we'll return the number of segments
iRet = TRUE;
// copy the Alt list
memcpy(pAlt, &pxrc->answer, sizeof(pxrc->answer));
// Because we copied over the pointers to allocated
// memory make sure allocated buffers in the XRC are not freed
memset(&(pxrc->answer), 0, sizeof(pxrc->answer));
exit:
if (full)
DestroyHRC(full);
if (pWordMap)
{
if (pGlyph)
DestroyGLYPH (pGlyph);
if (pxrc)
DestroyHRC ((HRC)pxrc);
}
return iRet;
}
int RecognizeWordEx (XRC *pxrcInferno, WORDMAP *pWordMap, int yDev, ALTERNATES *pAlt, XRC **ppxrc)
{
int iRet = 0;
HRC full = NULL;
XRC *pxrc = NULL;
GLYPH *pGlyph = NULL;
// if a word map is provided, construct its glyph,
// and pass it to be recognized by inferno
if (pWordMap)
{
pGlyph = GlyphFromWordMap (pxrcInferno->pGlyph, pWordMap);
}
else
{
pGlyph = pxrcInferno->pGlyph;
}
if (!pGlyph)
{
goto exit;
}
if (pxrcInferno->bGuide)
{
pxrc = (XRC *)InfernoRecognize (pxrcInferno, pGlyph, &pxrcInferno->guide, yDev, TRUE);
if (!pxrc)
goto exit;
}
else
{
pxrc = (XRC *)InfernoRecognize (pxrcInferno, pGlyph, NULL, yDev, TRUE);
if (!pxrc)
goto exit;
}
// success, we'll return the number of segments
iRet = pxrc->nfeatureset->cSegment;
// copy the Alt list
memcpy(pAlt, &pxrc->answer, sizeof(pxrc->answer));
// Because we copied over the pointers to allocated
// memory make sure allocated buffers in the XRC are not freed
memset(&(pxrc->answer), 0, sizeof(pxrc->answer));
exit:
if (pWordMap && pGlyph)
{
DestroyGLYPH (pGlyph);
}
if (iRet <= 0)
{
if (pxrc)
{
DestroyHRC ((HRC)pxrc);
}
pxrc = NULL;
}
if (ppxrc)
{
(*ppxrc) = pxrc;
}
return iRet;
}
// Special efficient version of ClearRCRESALT() that knows
// there are no mappings.
static void clearAlt(ALTERNATES *p)
{
unsigned int i;
for (i = 0; i < p->cAlt; i++)
ExternFree(p->aAlt[i].szWord);
p->cAlt = 0;
}
/******************************************************************
*
* isolate
*
* Store away a recognized isolated word together with all its alternates
* in the answer set
*
**********************************************************************/
static int isolate(WORDINFO *pWordInfo, ANSWER_SET *pAnsSet)
{
ALTERNATES *pAlt;
// Only add cases that were succesfully recognized
if (pWordInfo->alt.cAlt > 0)
{
if (pAnsSet->cAnsSets >= pAnsSet->capSegments)
{
pAnsSet->pAlternates = ExternRealloc(pAnsSet->pAlternates, sizeof(*pAnsSet->pAlternates) * (pAnsSet->cAnsSets + PHRASE_GROW_SIZE) );
}
ASSERT(pAnsSet->pAlternates);
if (! pAnsSet->pAlternates)
{
return HRCR_MEMERR;
}
pAlt = pAnsSet->pAlternates + pAnsSet->cAnsSets;
// Copy over the answers
memcpy(pAlt, &pWordInfo->alt, sizeof(*pAnsSet->pAlternates));
// Because we copied over the pointers to allocated
// memory make sure allocated buffers in the XRC are not freed
memset(&(pWordInfo->alt), 0, sizeof(pWordInfo->alt));
++pAnsSet->cAnsSets;
#ifndef NDEBUG
ValidateALTERNATES(pAlt);
#endif
}
return HRCR_OK;
}
/******************************************************************
*
* isolate
*
* Store away a recognized isolated word together with all its alternates
* in the answer set (using new style word_map)
*
**********************************************************************/
static int IsolateWordMap(XRC *pxrc, WORD_MAP *pMap, ANSWER_SET *pAnsSet)
{
ALTERNATES *pAlt;
// Only add cases that were succesfully recognized
if (pMap->pFinalAltList && pMap->pFinalAltList->cAlt > 0)
{
if (pAnsSet->cAnsSets >= pAnsSet->capSegments)
{
pAnsSet->pAlternates = ExternRealloc(pAnsSet->pAlternates, sizeof(*pAnsSet->pAlternates) * (pAnsSet->cAnsSets + PHRASE_GROW_SIZE) );
}
ASSERT(pAnsSet->pAlternates);
if (! pAnsSet->pAlternates)
{
return HRCR_MEMERR;
}
pAlt = pAnsSet->pAlternates + pAnsSet->cAnsSets;
// Copy over the answers
if (!AltListNew2Old ((HRC)pxrc, pMap, pMap->pFinalAltList, pAlt, TRUE))
{
return HRCR_MEMERR;
}
++pAnsSet->cAnsSets;
#ifndef NDEBUG
ValidateALTERNATES(pAlt);
#endif
}
return HRCR_OK;
}
/******************************************************************
*
* isolate
*
* Store away a recognized isolated word together with all its alternates
* in the answer set (using new style word_map)
*
**********************************************************************/
static int IsolateLineSeg(XRC *pxrc, LINE_SEGMENTATION *pResults, ANSWER_SET *pAnsSet)
{
int iSegCol, iWord;
SEG_COLLECTION *pSegCol;
SEGMENTATION *pSeg;
for (iSegCol = 0; iSegCol < pResults->cSegCol; iSegCol++)
{
pSegCol = pResults->ppSegCol[iSegCol];
if (pSegCol && pSegCol->cSeg > 0)
{
pSeg = pSegCol->ppSeg[0];
for (iWord = 0; iWord < pSeg->cWord; iWord++)
{
if (IsolateWordMap (pxrc, pSeg->ppWord[iWord], pAnsSet) != HRCR_OK)
{
return HRCR_ERROR;
}
}
}
}
return HRCR_OK;
}
/***********************************************************************
*
* RecognizeLine
*
* Run recognition on a "chunk" (or phrase) of ink.
* Bear had already been run on this piece of ink
* Inferno is run on the same piece of ink.
* ResolveWordBreaks is called to resolve word breaks and call avalanche on the
* Words it decides on
*
***********************************************************************/
static int RecognizeLine ( XRC *pMainXrc,
INKLINE *pLine,
ANSWER_SET *pAnsSet
)
{
HRC hrcInf = NULL;
XRC *pxrcInf = NULL;
int iRet = HRCR_ERROR;
WORDINFO *pWordInfo = NULL;
BEARXRC *pxrcBear = NULL;
GLYPH *pScaledGlyph = NULL;
WORDINFO *pWrd;
int cWord;
int iWord;
int yDev;
GUIDE ScaledGuide,
*pScaledGuide;
if (!pMainXrc || !pLine || !pAnsSet || !pLine->pGlyph)
{
goto exit;
}
// init the output line segmentation
pLine->pResults = NULL;
// point to the guide
if (pMainXrc->bGuide)
{
ScaledGuide = pMainXrc->guide;
pScaledGuide = &ScaledGuide;
}
else
{
pScaledGuide = NULL;
}
// scale the ink
pScaledGlyph = TranslateAndScaleLine (pLine, pScaledGuide);
if (NULL == pScaledGlyph)
{
goto exit;
}
// compute yDev of the scaled ink
yDev = YDeviation (pScaledGlyph);
// Let inferno segment the line
hrcInf = InfernoRecognize (pMainXrc, pScaledGlyph, pScaledGuide, yDev, FALSE);
if (!hrcInf)
{
goto exit;
}
pxrcInf = (XRC *)hrcInf;
cWord = 0;
pWordInfo = NULL;
// inferno has to have produced some answer for us to proceed
if (pxrcInf->answer.cAlt > 0)
{
// Let bear segment it too
pxrcBear = (BEARXRC *) BearRecognize (pMainXrc, pLine->pGlyph, NULL, FALSE);
// if bear failed or if bear assigned no words to this line, then only consider inferno
if (!pxrcBear || pxrcBear->answer.cAlt <= 0)
{
// Just use information from inferno
XRC *pxrc = (XRC *)hrcInf;
WORDMAP *pMap;
pMap = pxrc->answer.aAlt[0].pMap;
cWord = pxrc->answer.aAlt[0].cWords;
pWordInfo = ExternAlloc(pxrc->answer.aAlt[0].cWords * sizeof(*pWordInfo));
if (!pWordInfo)
{
goto exit;
}
if ( (iWord = RecognizeWholeWords(pxrc, NULL, pMap, TRUE, cWord, yDev, pWordInfo)) < 0)
{
goto exit;
}
ASSERT(cWord == iWord);
}
else
{
// is multiple segmentation enabled
if (!(pMainXrc->flags & RECOFLAG_SINGLESEG))
{
pWordInfo =
ResolveMultiWordBreaks (pxrcInf, pxrcBear, &cWord, &pLine->pResults);
}
if (!pWordInfo)
pWordInfo = ResolveWordBreaks (yDev, pxrcInf, pxrcBear, &cWord);
if (!pWordInfo)
goto exit;
}
}
// generate the linesegmentation if necessary
if (!pLine->pResults)
{
pLine->pResults = GenLineSegm (cWord, pWordInfo, pMainXrc);
}
// go thru all the words
for (iWord = 0, pWrd = pWordInfo; iWord < cWord; iWord++, pWrd++)
{
TruncateWordALTERNATES (&pWrd->alt, pMainXrc->answer.cAltMax);
if (isolate(pWrd, pAnsSet) != HRCR_OK)
goto exit;
}
// success
iRet = HRCR_OK;
exit:
if (pScaledGlyph)
{
DestroyFramesGLYPH (pScaledGlyph);
DestroyGLYPH (pScaledGlyph);
}
if (hrcInf)
DestroyHRC (hrcInf);
// We will not free the alternates as they point to buffers in the hrcs
if (pWordInfo)
ExternFree (pWordInfo);
if (pxrcBear)
BearDestroyHRC ((HRC)pxrcBear);
return iRet;
}
/***********************************************************************
*
* BuildStringFromParts
*
* Merge the isolated words in a array of alternates into a single string
* and keep the alternates for each word in the compound
*************************************************************************/
int BuildStringFromParts(XRC *pxrc, ALTERNATES *ppWords, unsigned int cWords)
{
XRCRESULT *pRes;
WORDMAP *pMaps = NULL;
unsigned int len, pos;
char *sz;
int cStroke = 0;
int cTotStroke;
int *piIndex, iLine;
BOOL bAllWords = TRUE;
// count the number of strokes for the ink that was recognized
cTotStroke = 0;
for (iLine = 0; iLine < pxrc->pLineBrk->cLine; iLine++)
{
// ignore lines that were not recognized
if ( !pxrc->pLineBrk->pLine[iLine].pGlyph ||
!pxrc->pLineBrk->pLine[iLine].pResults ||
pxrc->pLineBrk->pLine[iLine].pResults->cSegCol == 0
)
continue;
cTotStroke += CframeGLYPH(pxrc->pLineBrk->pLine[iLine].pGlyph);
}
ASSERT(pxrc);
pRes = pxrc->answer.aAlt;
ASSERT(pRes);
pRes->cWords = cWords;
if (cWords <=0)
{
pRes->pMap = NULL;
pRes->szWord = NULL;
pxrc->answer.cAlt = 0;
return cWords;
}
pxrc->answer.cAlt = 1;
ASSERT(cWords);
pMaps = (WORDMAP *)ExternAlloc(sizeof(WORDMAP) * cWords);
ASSERT(pMaps);
if (!pMaps)
{
goto exit;
}
// Count total number of chars and number of strokes
// across all alternates
for (len = 0, pos = 0; pos < cWords; pos++)
{
if (ppWords[pos].cAlt)
{
len += strlen(ppWords[pos].aAlt[0].szWord) + 1;
if (ppWords[pos].aAlt[0].pMap)
{
cStroke += ppWords[pos].aAlt[0].pMap->cStrokes;
}
}
else
{
bAllWords = FALSE;
}
}
ASSERT(len);
// ??? Are all strokes accounted for or some of the words had no answers
ASSERT(cStroke == cTotStroke || !bAllWords);
piIndex = (int *)ExternAlloc(sizeof(*pMaps->piStrokeIndex) * cStroke);
ASSERT (piIndex);
if (!piIndex)
{
goto exit;
}
pRes->cWords = cWords;
pRes->pMap = pMaps;
pRes->szWord = (char *)ExternAlloc(len * sizeof(*pRes->szWord));
ASSERT(pRes->szWord);
if (!(pRes->szWord))
{
goto exit;
}
pRes->cost = 0;
pRes->pXRC = pxrc;
pos = 0;
sz = pRes->szWord;
// Finally build the string and
// set alternate lists for each word in the string
for (; cWords; cWords--, ppWords++, pMaps++)
{
int cAlt = ppWords->cAlt;
char *szWord;
XRCRESULT *pAltRes;
unsigned int iAlt;
// Should always have something recognized
ASSERT (cAlt > 0);
if (cAlt <= 0)
{
continue;
}
szWord = ppWords->aAlt[0].szWord;
if (pos)
{
pos++;
*sz++ = ' ';
}
pMaps->start = (unsigned short int)pos;
strcpy(sz, szWord);
pMaps->len = (unsigned short int)strlen(szWord);
pos += pMaps->len;
sz += pMaps->len;
if (cAlt > 0)
{
pMaps->cStrokes = ppWords->aAlt->pMap->cStrokes;
cStroke -= pMaps->cStrokes;
ASSERT(cStroke >= 0);
pMaps->piStrokeIndex = piIndex + cStroke;
memcpy(pMaps->piStrokeIndex, ppWords->aAlt->pMap->piStrokeIndex, sizeof(*pMaps->piStrokeIndex) * pMaps->cStrokes);
}
else
{
pMaps->cStrokes = 0;
ASSERT(cStroke >= 0);
pMaps->piStrokeIndex = piIndex + cStroke;
}
// Special Case Check for recognition exit
if (1 == cAlt && strcmp(szWord, NOT_RECOGNIZED) == 0)
{
// Free up the memory associated with the alternates
// because we now say there are 0 alternates
ExternFree(ppWords->aAlt[0].szWord);
ExternFree(ppWords->aAlt->pMap->piStrokeIndex);
ExternFree(ppWords->aAlt->pMap);
memset(&pMaps->alt, 0, sizeof(pMaps->alt));
}
else
{
memcpy(&(pMaps->alt), ppWords, sizeof(ALTERNATES));
}
// Set correct backPointers for each alternate
pAltRes = pMaps->alt.aAlt;
for (iAlt = 0 ; iAlt < pMaps->alt.cAlt ; ++iAlt, ++pAltRes)
{
pAltRes->pXRC = pxrc;
}
pRes->cost += ppWords->aAlt->cost;
if (pRes->cost < 0)
pRes->cost = INT_MAX;
ppWords->cAlt = 0;
}
// Check that we have not forgot a terminating null
ASSERT(strlen(pRes->szWord) < len);
ASSERT(cStroke == 0);
return 1;
exit:
if (pMaps)
{
ExternFree(pMaps);
}
pRes->cWords = 0;
pRes->pMap = NULL;
pRes->szWord = NULL;
pRes->cost = 0;
pRes->pXRC = NULL;
return 0;
}
// Update the line information in an xrc presumeably after new ink had been added
BOOL UpdateLineInfo (XRC *pxrc)
{
BOOL bRet = FALSE;
GUIDE *pGuide = &(pxrc->guide);
LINEBRK LineBrk;
// do we have a guide?
if (pxrc->bGuide)
{
if (GuideLineSep (pxrc->pGlyph, pGuide, &LineBrk) < 1)
goto exit;
}
// We do not have a guide
else
{
// Try Bear line breaking then
if (BearLineSep (pxrc->pGlyph, &LineBrk) < 1)
{
// then we have no choice but to run the NN LineBrk if this fails
if (NNLineSep (pxrc->pGlyph, &LineBrk) < 1)
goto exit;
}
}
// Allocate a line breaking structure in the XRC if needed
if (!pxrc->pLineBrk)
{
// alloc a line brk structure if needed
pxrc->pLineBrk = (LINEBRK *) ExternAlloc (sizeof (*pxrc->pLineBrk));
if (!pxrc->pLineBrk)
{
goto exit;
}
memset (pxrc->pLineBrk, 0, sizeof (*pxrc->pLineBrk));
}
// compare the lines with the old configuration
CompareLineBrk (&LineBrk, pxrc->pLineBrk);
// free the contents of the old structure
FreeLines (pxrc->pLineBrk);
// copy the new one
memcpy (pxrc->pLineBrk, &LineBrk, sizeof (*pxrc->pLineBrk));
bRet = TRUE;
exit:
return bRet;
}
/******************************Public*Routine******************************\
* PanelModeRecognize
*
* Function to recognize a whole panel of ink.
* It first breaks the lines and then each line is recognized separately.
*
* The return value on success is as follows:
* ProcessHRC did something | there is more to do | return value
* --------------------------+-----------------------+------------------
* yes | no | HRCR_OK
* yes | yes | HRCR_INCOMPLETE
* no | no | HRCR_COMPLETE
* no | yes | HRCR_NORESULTS
*
* History:
* 11-Mar-2002 -by- Angshuman Guha aguha
* Modified to have 4 success return values instead of 2 (HRCR_INCOMPLETE and HRCR_OK).
\**************************************************************************/
int PanelModeRecognize (XRC *pxrc, DWORD dwRecoMode)
{
ANSWER_SET AnsSet;
INKLINE *pLine;
int iRet, iLine;
BOOL bDidSomething = FALSE;
BOOL bMoreToDo = FALSE;
// check the validity of the xrc
if (!pxrc)
{
return HRCR_ERROR;
}
// Preset in case we abort
iRet = HRCR_ERROR;
pxrc->answer.cAlt = 0;
// init the AnsSet
memset(&AnsSet, 0, sizeof(AnsSet));
// Prepare the AnsSet
AnsSet.capSegments = 0;
AnsSet.cAnsSets = 0;
AnsSet.pAlternates = NULL;
#if defined(TRAINTIME_AVALANCHE)
// ONLY in training mode. Estimate the wordmaps
ComputePromptWordMaps (pxrc);
#endif
// refresh the the line information
if (!UpdateLineInfo (pxrc) || !pxrc->pLineBrk)
{
goto exit;
}
// go thru all the lines
for (iLine = 0; iLine < pxrc->pLineBrk->cLine; iLine++)
{
pLine = pxrc->pLineBrk->pLine + iLine;
// skip empty lines
if (pLine->cStroke <= 0)
{
// create an empty results structure if there is none
if (!pLine->pResults)
{
// create an empty line segmentation
pLine->pResults = (LINE_SEGMENTATION *) ExternAlloc (sizeof (*pLine->pResults));
if (!pLine->pResults)
goto exit;
memset (pLine->pResults, 0, sizeof (*pLine->pResults));
}
continue;
}
// has this line been recognized before, just generate the answer(s)
if (pLine->bRecognized)
{
// are there any results
if (pLine->pResults)
{
if (IsolateLineSeg (pxrc, pLine->pResults, &AnsSet) != HRCR_OK)
{
goto exit;
}
}
continue;
}
// If we are in remaining mode, we'llgo ahead and recognize this line
// if we are in partial mode, check to see if this is the last line, we will only
// recognize the last line if endpeninput was called
if ( dwRecoMode == RECO_MODE_REMAINING ||
( dwRecoMode == RECO_MODE_INCREMENTAL &&
( pxrc->bEndPenInput ||
iLine < (pxrc->pLineBrk->cLine - 1)
)
)
)
{
// make sure that the line segmentation info is freed
if (pLine->pResults)
{
FreeLineSegm (pLine->pResults);
ExternFree (pLine->pResults);
pLine->pResults = NULL;
}
// Recognize this line
iRet = RecognizeLine (pxrc, pLine, &AnsSet);
if (iRet != HRCR_OK)
{
goto exit;
}
// label it as being recognized
pLine->bRecognized = TRUE;
bDidSomething = TRUE;
}
// if we are in partial incremental mode, then we stop processing here
if (dwRecoMode == RECO_MODE_INCREMENTAL)
{
break;
}
} // iLine Loop
// do we have any more unprocessed ink, if so we are going to return HRCR_INCOMPLETE
if (dwRecoMode == RECO_MODE_INCREMENTAL)
{
for (iLine = 0; iLine < pxrc->pLineBrk->cLine; iLine++)
{
pLine = pxrc->pLineBrk->pLine + iLine;
// is there a line that has ink and has no results
if (pLine->pGlyph && (!pLine->pResults || pLine->pResults->cSegCol == 0))
{
bMoreToDo = TRUE;
break;
}
}
}
if (bDidSomething)
{
if (bMoreToDo)
iRet = HRCR_INCOMPLETE;
else
iRet = HRCR_OK;
}
else
{
if (bMoreToDo)
iRet = HRCR_NORESULTS;
else
iRet = HRCR_COMPLETE;
}
exit:
// if we succeeded, build the answer
if (iRet == HRCR_OK || iRet == HRCR_INCOMPLETE || iRet == HRCR_COMPLETE || iRet == HRCR_NORESULTS)
{
BuildStringFromParts(pxrc, AnsSet.pAlternates, AnsSet.cAnsSets);
}
// free the answer set
ExternFree(AnsSet.pAlternates);
#if defined(TRAINTIME_AVALANCHE)
// free word maps
FreeWordMaps ();
#endif
return iRet;
}
// Performs recognition of a piece of ink in word mode
int WordModeRecognize (XRC *pxrc)
{
GLYPH *pglAll;
INKLINE *pLine;
GUIDE LocalGuide, *pLocalGuide, OrigGuide;
int iRet;
// check the validity of the xrc
if (!pxrc)
{
return HRCR_ERROR;
}
// Preset in case we abort
iRet = HRCR_ERROR;
pxrc->answer.cAlt = 0;
// save the original glyph
pglAll = pxrc->pGlyph;
#if defined(TRAINTIME_AVALANCHE)
// ONLY in training mode. Estimate the wordmaps
ComputePromptWordMaps (pxrc);
#endif
// point to the guide if any
if (TRUE == pxrc->bGuide)
{
LocalGuide = pxrc->guide;
pLocalGuide = &LocalGuide;
OrigGuide = pxrc->guide;
}
else
{
pLocalGuide = NULL;
}
// refresh the the line information
if (!CreateSingleLine (pxrc) || !pxrc->pLineBrk)
{
goto exit;
}
pLine = pxrc->pLineBrk->pLine;
// if there is no ink exit
if (!pLine->pGlyph || pLine->cStroke <= 0)
{
goto exit;
}
// Ink preprocessing: scale and translate the line ink if necessary
pxrc->pGlyph = TranslateAndScaleLine (pLine, pLocalGuide);
if (!pxrc->pGlyph)
{
goto exit;
}
if (NULL != pLocalGuide && TRUE == pxrc->bGuide)
{
pxrc->guide = *pLocalGuide;
}
// make sure that the line segmentation info is freed
if (pLine->pResults)
{
FreeLineSegm (pLine->pResults);
ExternFree (pLine->pResults);
pLine->pResults = NULL;
}
// Recognize the word
PerformRecognition (pxrc, -1);
Avalanche (pxrc, NULL);
// we no longer need this glyph
DestroyFramesGLYPH (pxrc->pGlyph);
DestroyGLYPH (pxrc->pGlyph);
// generate the line segmentation
// if this function fail, we'll fail as well
if (!WordModeGenLineSegm (pxrc))
{
goto exit;
}
iRet = HRCR_OK;
exit:
// restore back the original ink
pxrc->pGlyph = pglAll;
#if defined(TRAINTIME_AVALANCHE)
// free word maps
FreeWordMaps ();
#endif
if (TRUE == pxrc->bGuide)
{
pxrc->guide = OrigGuide;
}
return iRet;
}