1265 lines
30 KiB
C
1265 lines
30 KiB
C
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
|
//
|
|
// Copyright (c) 2001 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Module:
|
|
// volcano/dll/FreeApi.c
|
|
//
|
|
// Description:
|
|
// Implement external free input API for DLL.
|
|
//
|
|
// Author:
|
|
// hrowley
|
|
//
|
|
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
|
|
|
#include "volcanop.h"
|
|
#include "factoid.h"
|
|
#include "recdefs.h"
|
|
#include "brknet.h"
|
|
|
|
/*
|
|
* Definitions from penwin.h needed for inksets. We can't include it because
|
|
* it has conflicts with recog.h
|
|
*/
|
|
|
|
// inkset returns:
|
|
#define ISR_ERROR (-1) // Memory or other error
|
|
#define ISR_BADINKSET (-2) // bad source inkset
|
|
#define ISR_BADINDEX (-3) // bad inkset index
|
|
|
|
#define IX_END 0xFFFF // to or past last available index
|
|
|
|
#ifdef HWX_TUNE
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
//#define DEBUG_LOG_API
|
|
|
|
#ifdef DEBUG_LOG_API
|
|
#include <stdio.h>
|
|
static void LogMessage(char *format, ...)
|
|
{
|
|
FILE *f=fopen("c:/log.txt","a");
|
|
va_list marker;
|
|
va_start(marker,format);
|
|
vfprintf(f,format,marker);
|
|
va_end(marker);
|
|
fclose(f);
|
|
}
|
|
#else
|
|
static void LogMessage(char *format, ...)
|
|
{
|
|
va_list marker;
|
|
va_start(marker,format);
|
|
va_end(marker);
|
|
}
|
|
#endif
|
|
|
|
HRC CreateCompatibleHRC(HRC hrctemplate, HREC hrec)
|
|
{
|
|
VRC *pVRC;
|
|
|
|
LogMessage("CreateCompatibleHRC()\n");
|
|
|
|
hrec = hrec;
|
|
|
|
// Alloc the VRC.
|
|
pVRC = ExternAlloc(sizeof(VRC));
|
|
if (!pVRC) {
|
|
goto error1;
|
|
}
|
|
|
|
// Initialize it.
|
|
pVRC->fBoxedInput = FALSE;
|
|
pVRC->fHaveInput = FALSE;
|
|
pVRC->fEndInput = FALSE;
|
|
pVRC->fBeginProcess = FALSE;
|
|
pVRC->pLatticePath = (LATTICE_PATH *)0;
|
|
|
|
if (!hrctemplate) {
|
|
pVRC->pLattice = AllocateLattice();
|
|
if (!pVRC->pLattice) {
|
|
goto error2;
|
|
}
|
|
} else {
|
|
VRC *pVRCTemplate = (VRC *)hrctemplate;
|
|
|
|
pVRC->pLattice = CreateCompatibleLattice(
|
|
pVRCTemplate->pLattice
|
|
);
|
|
if (!pVRC->pLattice) {
|
|
goto error2;
|
|
}
|
|
}
|
|
|
|
// Success, cast to HRC and return it.
|
|
return (HRC)pVRC;
|
|
|
|
error2:
|
|
ExternFree(pVRC);
|
|
|
|
error1:
|
|
return (HRC)0;
|
|
}
|
|
|
|
int DestroyHRC(HRC hrc)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("DestroyHRC()\n");
|
|
|
|
// Did we get a handle? Is if a free input handle?
|
|
if (!hrc || pVRC->fBoxedInput) {
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// Free the lattice. Should it be an error if there is not one?
|
|
if (pVRC->pLattice) {
|
|
FreeLattice(pVRC->pLattice);
|
|
} else {
|
|
ASSERT(pVRC->pLattice);
|
|
}
|
|
|
|
// Free the lattice path.
|
|
if (pVRC->pLatticePath) {
|
|
FreeLatticePath(pVRC->pLatticePath);
|
|
}
|
|
|
|
// Free the VRC itself.
|
|
ExternFree(pVRC);
|
|
|
|
return HRCR_OK;
|
|
}
|
|
|
|
int AddPenInputHRC(
|
|
HRC hrc,
|
|
POINT *ppnt,
|
|
void *pvOem,
|
|
UINT oemdatatype,
|
|
STROKEINFO *psi
|
|
) {
|
|
VRC *pVRC = (VRC *)hrc;
|
|
int retVal;
|
|
|
|
LogMessage("AddPenInputHRC()\n");
|
|
|
|
pvOem = pvOem;
|
|
oemdatatype = oemdatatype;
|
|
|
|
// Did we get a handle? Does it have a lattice?
|
|
if (!hrc || pVRC->fBoxedInput
|
|
|| !pVRC->pLattice || pVRC->fEndInput
|
|
) {
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// Do we have valid ink?
|
|
if (psi->cPnt == 0 || !ppnt) {
|
|
return HRCR_ERROR;
|
|
}
|
|
if (!(psi->wPdk & PDK_DOWN)) {
|
|
return HRCR_OK;
|
|
}
|
|
|
|
// Add stroke to the lattice.
|
|
retVal = AddStrokeToLattice(pVRC->pLattice, psi->cPnt, ppnt, psi->dwTick);
|
|
|
|
// Mark as having input.
|
|
pVRC->fHaveInput = TRUE;
|
|
|
|
return(retVal ? HRCR_OK : HRCR_MEMERR);
|
|
}
|
|
|
|
int EndPenInputHRC(HRC hrc)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("EndPenInputHRC()\n");
|
|
|
|
// Did we get a handle? Is it free input?
|
|
// Does it have a lattice?
|
|
// JBENN: Should it be an error if we have no strokes?
|
|
if (!hrc || pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
pVRC->fEndInput = TRUE;
|
|
|
|
return HRCR_OK;
|
|
}
|
|
|
|
int ProcessHRC(HRC hrc, DWORD timeout)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("ProcessHRC()\n");
|
|
|
|
timeout = timeout;
|
|
|
|
// Did we get a handle? Is it free input?
|
|
// Does it have a lattice?
|
|
// JBENN: Should it be an error if we have no strokes?
|
|
if (!hrc || pVRC->fBoxedInput || !pVRC->pLattice)
|
|
{
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// Have we already finished all processing?
|
|
// if (pVRC->pLatticePath) {
|
|
// return HRCR_OK;
|
|
// }
|
|
|
|
// Do any processing we can.
|
|
if (!ProcessLattice(pVRC->pLattice, pVRC->fEndInput))
|
|
{
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
pVRC->fBeginProcess = TRUE;
|
|
|
|
// in free mode, run the brknet to update the lattice
|
|
// we only call this if end input had been called
|
|
if (!pVRC->pLattice->fUseGuide && pVRC->fEndInput && !pVRC->pLattice->fSepMode && !pVRC->pLattice->fWordMode
|
|
#ifdef HWX_TUNE
|
|
&& g_pTuneFile == NULL
|
|
#endif
|
|
)
|
|
{
|
|
UpdateLattice(pVRC->pLattice);
|
|
}
|
|
|
|
#ifdef USE_IFELANG3
|
|
// Do we have all the input? Also, make sure we are not in separator mode
|
|
if (pVRC->fEndInput && !pVRC->pLattice->fSepMode)
|
|
{
|
|
// Apply the language model.
|
|
#ifdef HWX_TUNE
|
|
// If we have tuning enabled, then never call IFELang3 directly.
|
|
if (g_pTuneFile == NULL)
|
|
#endif
|
|
{
|
|
ApplyLanguageModel(pVRC->pLattice, NULL);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Free the old path, in case we got called before
|
|
if (pVRC->pLatticePath != NULL)
|
|
{
|
|
FreeLatticePath(pVRC->pLatticePath);
|
|
pVRC->pLatticePath = NULL;
|
|
}
|
|
|
|
// Get our final path. Note that the path may get changed
|
|
// if ProcessHRC is called again, particularly after
|
|
// EndPenInputHRC is called, because the language model will
|
|
// be applied.
|
|
if (!GetCurrentPath(pVRC->pLattice, &pVRC->pLatticePath))
|
|
{
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
ASSERT(pVRC->pLatticePath!=NULL);
|
|
|
|
return HRCR_OK;
|
|
}
|
|
|
|
// Hacked free input results call.
|
|
int GetResultsHRC(
|
|
HRC hrc,
|
|
UINT uType,
|
|
HRCRESULT *pHRCResults,
|
|
UINT cResults
|
|
) {
|
|
VRC *pVRC = (VRC *)hrc;
|
|
VRCRESULT *pVRCResults;
|
|
|
|
LogMessage("GetResultsHRC()\n");
|
|
|
|
// Check parameters.
|
|
if (!hrc || pVRC->fBoxedInput || !pVRC->pLattice || !pHRCResults) {
|
|
ASSERT(("Bad lattice\n",0));
|
|
return HRCR_ERROR;
|
|
}
|
|
if (!pVRC->pLatticePath) {
|
|
ASSERT(("Haven't processed input\n",0));
|
|
return HRCR_ERROR;
|
|
}
|
|
if (uType == GRH_GESTURE || cResults == 0) {
|
|
ASSERT(("Requested zero alternates\n",0));
|
|
return 0;
|
|
}
|
|
// if (GetLatticeStrokeCount(pVRC->pLattice) < 1) {
|
|
// ASSERT(("No strokes\n",0));
|
|
// return 0;
|
|
// }
|
|
|
|
// Allocate space to hold results.
|
|
pVRCResults = ExternAlloc(sizeof(VRCRESULT));
|
|
if (!pVRCResults) {
|
|
ASSERT(("No memory\n",0));
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// OK we have results. We always return a special code to indicate
|
|
// return top one for everything.
|
|
pVRCResults->pVRC = pVRC;
|
|
pVRCResults->wch = ALL_TOP_ONE;
|
|
*pHRCResults = (HRCRESULT)pVRCResults;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// The other hacked free input results call.
|
|
int GetAlternateWordsHRCRESULT(
|
|
HRCRESULT hrcResult,
|
|
UINT iChar,
|
|
UINT cChar,
|
|
HRCRESULT *pHRCResults,
|
|
UINT cHRCResults
|
|
) {
|
|
VRCRESULT *pVRCResult = (VRCRESULT *)hrcResult;
|
|
VRC *pVRC;
|
|
UINT ii;
|
|
UINT maxAlts, cAlts;
|
|
UINT iCorrect;
|
|
HRCRESULT hCorrect;
|
|
wchar_t aAlts[MAX_ALTERNATES];
|
|
|
|
LogMessage("GetAlternateWordsHRCRESULT()\n");
|
|
|
|
// Check parameters.
|
|
if (!hrcResult || !pHRCResults) {
|
|
return HRCR_ERROR;
|
|
}
|
|
pVRC = pVRCResult->pVRC;
|
|
if (!pVRC || pVRC->fBoxedInput || !pVRC->pLatticePath) {
|
|
ASSERT(("Bad lattice or haven't processed input",0));
|
|
return HRCR_ERROR;
|
|
}
|
|
if (pVRCResult->wch != ALL_TOP_ONE || cChar != 1) {
|
|
return HRCR_UNSUPPORTED;
|
|
}
|
|
if (iChar >= (UINT)pVRC->pLatticePath->nChars) {
|
|
return HRCR_UNSUPPORTED;
|
|
}
|
|
|
|
// Compute limit on alternates to return.
|
|
maxAlts = min(MAX_ALTERNATES, cHRCResults);
|
|
|
|
// Get the alternates.
|
|
cAlts = GetAlternatesForCharacterInCurrentPath(
|
|
pVRC->pLattice, pVRC->pLatticePath, iChar, maxAlts, aAlts
|
|
);
|
|
|
|
// Now build up array of results structures.
|
|
iCorrect = (UINT)-1;
|
|
for (ii = 0; ii < cAlts; ++ii) {
|
|
VRCRESULT *pNew;
|
|
|
|
// Allocate space to hold results.
|
|
pNew = ExternAlloc(sizeof(VRCRESULT));
|
|
if (!pNew) {
|
|
UINT jj;
|
|
|
|
// Clean up any allocated results
|
|
for (jj = 0 ; jj < ii; ++jj) {
|
|
ExternFree(pHRCResults[jj]);
|
|
}
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// Fill in this alternate.
|
|
pNew->pVRC = pVRC;
|
|
pNew->wch = aAlts[ii];
|
|
pNew->iChar = (short)iChar;
|
|
pHRCResults[ii] = (HRCRESULT)pNew;
|
|
|
|
if (pVRC->pLatticePath->pElem[iChar].wChar == aAlts[ii]) {
|
|
iCorrect = ii;
|
|
}
|
|
}
|
|
|
|
// If answer in path is not in list, over-write the last entry.
|
|
if (iCorrect == (UINT)-1) {
|
|
iCorrect = cAlts - 1;
|
|
((VRCRESULT *)(pHRCResults[iCorrect]))->wch = pVRC->pLatticePath->pElem[iChar].wChar;
|
|
}
|
|
|
|
// Now do the reorder as needed.
|
|
hCorrect = pHRCResults[iCorrect];
|
|
for (ii = iCorrect; ii > 0 ; --ii) {
|
|
pHRCResults[ii] = pHRCResults[ii - 1];
|
|
}
|
|
pHRCResults[0] = hCorrect;
|
|
|
|
// For debug version, be paranoid, and zero any unused elements in
|
|
// the output array.
|
|
# ifdef DBG
|
|
for (ii = cAlts ; ii < cHRCResults; ++ii) {
|
|
pHRCResults[ii] = (HRCRESULT)0;
|
|
}
|
|
# endif
|
|
|
|
return cAlts;
|
|
}
|
|
|
|
// This does NOT include a null symbol at the end.
|
|
int GetSymbolCountHRCRESULT(HRCRESULT hrcResult)
|
|
{
|
|
VRCRESULT *pVRCResult = (VRCRESULT *)hrcResult;
|
|
VRC *pVRC;
|
|
|
|
LogMessage("GetSymbolCountHRCRESULT()\n");
|
|
|
|
if (!hrcResult) {
|
|
return HRCR_ERROR;
|
|
}
|
|
pVRC = pVRCResult->pVRC;
|
|
if (!pVRC || pVRC->fBoxedInput
|
|
|| !pVRC->pLatticePath
|
|
) {
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// Terminal HRCResults reflect only one character.
|
|
if (pVRCResult->wch != ALL_TOP_ONE) {
|
|
return 1;
|
|
}
|
|
|
|
// The path tells us how many character there are.
|
|
LogMessage(" %d chars\n",pVRC->pLatticePath->nChars);
|
|
return pVRC->pLatticePath->nChars;
|
|
}
|
|
|
|
// Convert an HRCRESULT into the correct character(s).
|
|
int GetSymbolsHRCRESULT(
|
|
HRCRESULT hrcResult,
|
|
UINT iSyv,
|
|
SYV *pSyv,
|
|
UINT cSyv
|
|
) {
|
|
VRCRESULT *pVRCResult = (VRCRESULT *)hrcResult;
|
|
VRC *pVRC;
|
|
|
|
LogMessage("GetSymbolsHRCRESULT()\n");
|
|
|
|
if (!hrcResult) {
|
|
return HRCR_ERROR;
|
|
}
|
|
pVRC = pVRCResult->pVRC;
|
|
if (!pVRC || pVRC->fBoxedInput || pVRC->fBoxedInput
|
|
|| !pVRC->pLatticePath
|
|
) {
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// Did they really ask for anything.
|
|
if (cSyv == 0 || iSyv >= (UINT)pVRC->pLatticePath->nChars) {
|
|
return 0;
|
|
}
|
|
|
|
// Are we getting the full string, or just an alternate.
|
|
if (pVRCResult->wch != ALL_TOP_ONE) {
|
|
// Just one alternate.
|
|
pSyv[0] = MAKELONG(pVRCResult->wch, SYVHI_UNICODE);
|
|
LogMessage(" result U+%04X\n",pVRCResult->wch);
|
|
return 1;
|
|
} else {
|
|
// Getting a full string.
|
|
UINT ii;
|
|
UINT cValid;
|
|
|
|
// Characters available from requested starting position.
|
|
cValid = pVRC->pLatticePath->nChars - iSyv;
|
|
|
|
// Does the caller want fewer than we have available.
|
|
if (cValid > cSyv) {
|
|
cValid = cSyv;
|
|
}
|
|
|
|
// OK, copy over what we have.
|
|
for (ii = 0; ii < cValid; ++ii) {
|
|
wchar_t wch;
|
|
|
|
wch = pVRC->pLatticePath->pElem[ii + iSyv].wChar;
|
|
//wch = LocRunDense2Unicode(&g_locRunInfo, wch);
|
|
LogMessage(" char %d is U+%04X\n",ii,wch);
|
|
// Convert to SYV and put in output array.
|
|
pSyv[ii] = MAKELONG(wch, SYVHI_UNICODE);
|
|
}
|
|
|
|
return cValid;
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Translates an array of symbols into a Unicode string.
|
|
// Code copied from \hwx\common, probably should be shared.
|
|
BOOL SymbolToCharacterW(SYV *pSyv, int cSyv, WCHAR *wsz, int *pCount)
|
|
{
|
|
int c = 0;
|
|
int ret = 1;
|
|
|
|
LogMessage("SymbolToCharacterW()\n");
|
|
|
|
for (; cSyv; pSyv++, wsz++, cSyv--, c++) {
|
|
if (HIWORD(*pSyv) == SYVHI_UNICODE) {
|
|
*wsz = LOWORD(*pSyv);
|
|
} else if (HIWORD(*pSyv) == SYVHI_ANSI) {
|
|
// No support for ANSI in EA recognizer.
|
|
ASSERT(0);
|
|
ret = 0;
|
|
} else if (*pSyv == SYV_NULL) {
|
|
*wsz = '\0';
|
|
} else {
|
|
*wsz = '\0';
|
|
ret = 0;
|
|
}
|
|
|
|
if (*wsz!=0) LogMessage(" result=U+%04X\n",*wsz);
|
|
|
|
// Break on NULL done here rather than at SYV_NULL check above,
|
|
// because an ANSI or UNICODE char might also be NULL.
|
|
if (!*wsz) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pCount)
|
|
*pCount = c;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Free memory allocated for hrcResult.
|
|
int DestroyHRCRESULT(HRCRESULT hrcResult)
|
|
{
|
|
LogMessage("DestroyHRCRESULT()\n");
|
|
|
|
if (!hrcResult) {
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
ExternFree(hrcResult);
|
|
return HRCR_OK;
|
|
}
|
|
|
|
int SetGuideHRC(HRC hrc, LPGUIDE lpguide, UINT nFirstVisible)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("SetGuideHRC()\n");
|
|
|
|
// Check parameters.
|
|
if (!hrc || pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
ASSERT(("Invalid lattice or boxed mode",0));
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// The following condition should really be: fHaveInput || fEndInput || pLatticePath
|
|
// but this was remove to get the free input UI working.
|
|
if (pVRC->fBeginProcess || pVRC->pLatticePath) {
|
|
ASSERT(("Already processed some strokes in SetGuideHRC",0));
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// We only work with no guide.
|
|
if (lpguide!=NULL && (lpguide->cHorzBox != 0 || lpguide->cVertBox != 0)) {
|
|
ASSERT(("Wrong kind of guide",0));
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
return HRCR_OK;
|
|
}
|
|
|
|
int SetAlphabetHRC(HRC hrc, ALC alc, LPBYTE rgbfAlc)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
rgbfAlc = rgbfAlc;
|
|
|
|
// Check parameters.
|
|
if (!hrc || pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// The following condition should really be: fHaveInput || fEndInput || pLatticePath
|
|
// but this was remove to get the free input UI working.
|
|
if (pVRC->fBeginProcess || pVRC->pLatticePath) {
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// Pass the ALC on to the lattice.
|
|
SetLatticeALCValid(pVRC->pLattice, alc);
|
|
|
|
return HRCR_OK;
|
|
}
|
|
|
|
HINKSET CreateInksetHRCRESULT(
|
|
HRCRESULT hrcResult,
|
|
unsigned int iChar,
|
|
unsigned int cChar
|
|
) {
|
|
VRCRESULT *pVRCResult = (VRCRESULT *)hrcResult;
|
|
VRC *pVRC;
|
|
VINKSET *pVInkSet;
|
|
DWORD begin, end;
|
|
|
|
LogMessage("CreateInksetHRCRESULT()\n");
|
|
|
|
// Check parameters.
|
|
if (!hrcResult) {
|
|
return NULL;
|
|
}
|
|
pVRC = pVRCResult->pVRC;
|
|
if (!pVRC || pVRC->fBoxedInput
|
|
|| !pVRC->pLatticePath
|
|
) {
|
|
return NULL;
|
|
}
|
|
if (pVRCResult->wch != 0xFFFF) {
|
|
return NULL; // Not top level result.
|
|
}
|
|
if (cChar < 1 || (iChar + cChar) > (UINT)pVRC->pLatticePath->nChars) {
|
|
return NULL; // Not one or more characters in valid range.
|
|
}
|
|
|
|
// Allocate an inkset structure.
|
|
pVInkSet = ExternAlloc(sizeof(VINKSET));
|
|
if (!pVInkSet) {
|
|
return NULL;
|
|
}
|
|
|
|
// Fill it in.
|
|
pVInkSet->pVRC = pVRC;
|
|
pVInkSet->cChar = cChar;
|
|
pVInkSet->iChar = iChar;
|
|
|
|
// Get the tick counts.
|
|
if (GetCharacterTimeRange(
|
|
pVRC->pLattice, pVRC->pLatticePath, pVInkSet->iChar,
|
|
pVInkSet->iChar + pVInkSet->cChar, &begin, &end))
|
|
{
|
|
pVInkSet->cIntervals = 1;
|
|
}
|
|
else
|
|
{
|
|
pVInkSet->cIntervals = 0;
|
|
}
|
|
|
|
// Return it.
|
|
return (HINKSET)pVInkSet;
|
|
}
|
|
|
|
BOOL DestroyInkset(HINKSET hInkset)
|
|
{
|
|
LogMessage("DestroyInkset()\n");
|
|
|
|
if (!hInkset) {
|
|
return FALSE;
|
|
}
|
|
|
|
ExternFree(hInkset);
|
|
return TRUE;
|
|
}
|
|
|
|
int GetInksetInterval(
|
|
HINKSET hInkset,
|
|
unsigned int uIndex,
|
|
INTERVAL *pI
|
|
) {
|
|
VINKSET *pVInkSet = (VINKSET *)hInkset;
|
|
VRC *pVRC;
|
|
DWORD begin, end;
|
|
|
|
LogMessage("GetInksetInterval()\n");
|
|
|
|
// Check parameters
|
|
if (!hInkset || !pVInkSet->pVRC) {
|
|
return ISR_ERROR;
|
|
}
|
|
pVRC = pVInkSet->pVRC;
|
|
if (!pVRC || pVRC->fBoxedInput
|
|
|| !pVRC->pLatticePath
|
|
) {
|
|
return ISR_ERROR;
|
|
}
|
|
|
|
// Only one range per string.
|
|
if (pVInkSet->cIntervals == 0 || (uIndex != 0 && uIndex != IX_END)) {
|
|
return ISR_BADINDEX;
|
|
}
|
|
|
|
// Get the tick counts.
|
|
GetCharacterTimeRange(
|
|
pVRC->pLattice, pVRC->pLatticePath, pVInkSet->iChar,
|
|
pVInkSet->iChar + pVInkSet->cChar, &begin, &end
|
|
);
|
|
|
|
// OK convert from ms to ABSTIME.
|
|
MakeAbsTime(&pI->atBegin, 0, begin);
|
|
MakeAbsTime(&pI->atEnd, 0, end);
|
|
|
|
LogMessage(" interval %d to %d\n",begin,end);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int GetInksetIntervalCount(HINKSET hInkset)
|
|
{
|
|
VINKSET *pVInkSet = (VINKSET *)hInkset;
|
|
LogMessage("GetInksetIntervalCount()\n");
|
|
|
|
if (!hInkset) {
|
|
return ISR_ERROR;
|
|
}
|
|
|
|
return pVInkSet->cIntervals;
|
|
}
|
|
|
|
// Given a character, make a guess at what the bounding box around it would have been.
|
|
int GetBaselineHRCRESULT(
|
|
HRCRESULT hrcResult,
|
|
RECT *pRect,
|
|
BOOL *pfBaselineValid,
|
|
BOOL *pfMidlineValid)
|
|
{
|
|
VRCRESULT *pVRCResult = (VRCRESULT *)hrcResult;
|
|
VRC *pVRC;
|
|
|
|
LogMessage("GetBaselineHRCRESULT(%08X)\n",hrcResult);
|
|
|
|
// Check parameters.
|
|
if (!hrcResult) {
|
|
return HRCR_ERROR;
|
|
}
|
|
pVRC = pVRCResult->pVRC;
|
|
if (!pVRC || pVRC->fBoxedInput || !pVRC->pLatticePath) {
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
if (pVRCResult->wch == ALL_TOP_ONE) {
|
|
// They want a bbox for the whole ink... just
|
|
// return an error in this case, since it isn't
|
|
// meaningful.
|
|
return HRCR_ERROR;
|
|
} else {
|
|
if (!GetBoxOfAlternateInCurrentPath(pVRC->pLattice, pVRC->pLatticePath, pVRCResult->iChar, pRect)) {
|
|
*pfBaselineValid = FALSE;
|
|
*pfMidlineValid = FALSE;
|
|
} else {
|
|
*pfBaselineValid = TRUE;
|
|
*pfMidlineValid = TRUE;
|
|
}
|
|
}
|
|
|
|
LogMessage(" result left,right=%d,%d top,bottom=%d,%d valid=%d,%d\n",
|
|
pRect->left,pRect->right,pRect->top,pRect->bottom,
|
|
*pfBaselineValid,*pfMidlineValid);
|
|
|
|
return HRCR_OK;
|
|
}
|
|
|
|
// Set the context for the ink that is being recognized.
|
|
// wszBefore and wszAfter can both be NULL. The function
|
|
// return TRUE on success, and FALSE on a memory allocation
|
|
// error.
|
|
BOOL SetHwxCorrectionContext(HRC hrc, wchar_t *wszBefore, wchar_t *wszAfter)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
int iDest, i;
|
|
|
|
LogMessage("SetHwxCorrectionContext(%d,%d)\n",
|
|
(wszBefore == NULL ? 0 : wcslen(wszBefore)),
|
|
(wszAfter == NULL ? 0 : wcslen(wszAfter)));
|
|
|
|
// Check parameters.
|
|
if (!hrc || !pVRC->pLattice) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Make sure we do this before any input
|
|
if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Free up previous context settings
|
|
ExternFree(pVRC->pLattice->wszBefore);
|
|
ExternFree(pVRC->pLattice->wszAfter);
|
|
pVRC->pLattice->wszBefore = NULL;
|
|
pVRC->pLattice->wszAfter = NULL;
|
|
|
|
// If we are given any pre-context
|
|
if (wszBefore != NULL && wcslen(wszBefore) > 0)
|
|
{
|
|
// Make a space for the context and check for allocation failure
|
|
pVRC->pLattice->wszBefore = ExternAlloc(sizeof(wchar_t) * (wcslen(wszBefore) + 1));
|
|
if (pVRC->pLattice->wszBefore == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
// Translate the string to dense codes, reversing the order of the
|
|
// characters and stopping at the first one not supported by the recognizer.
|
|
iDest = 0;
|
|
for (i = wcslen(wszBefore) - 1; i >= 0; i--)
|
|
{
|
|
wchar_t dch = LocRunUnicode2Dense(&g_locRunInfo, wszBefore[i]);
|
|
pVRC->pLattice->wszBefore[iDest] = dch;
|
|
if (dch == LOC_TRAIN_NO_DENSE_CODE)
|
|
{
|
|
break;
|
|
}
|
|
iDest++;
|
|
}
|
|
pVRC->pLattice->wszBefore[iDest] = 0;
|
|
// If the context was zero length after translation, set it to NULL.
|
|
if (wcslen(pVRC->pLattice->wszBefore) == 0)
|
|
{
|
|
ExternFree(pVRC->pLattice->wszBefore);
|
|
pVRC->pLattice->wszBefore = NULL;
|
|
}
|
|
}
|
|
|
|
// If we are given any post-context
|
|
if (wszAfter != NULL && wcslen(wszAfter) > 0)
|
|
{
|
|
// Make a space for the context and check for allocation failure
|
|
pVRC->pLattice->wszAfter = ExternAlloc(sizeof(wchar_t) * (wcslen(wszAfter) + 1));
|
|
if (pVRC->pLattice->wszAfter == NULL)
|
|
{
|
|
ExternFree(pVRC->pLattice->wszBefore);
|
|
pVRC->pLattice->wszBefore = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
// Translate the string to dense codes, stopping at the first character
|
|
// not supported by the recognizer.
|
|
for (i = 0; i < (int) wcslen(wszAfter); i++)
|
|
{
|
|
wchar_t dch = LocRunUnicode2Dense(&g_locRunInfo, wszAfter[i]);
|
|
pVRC->pLattice->wszAfter[i] = dch;
|
|
if (dch == LOC_TRAIN_NO_DENSE_CODE)
|
|
break;
|
|
}
|
|
pVRC->pLattice->wszAfter[i] = 0;
|
|
// If the context was zero length after translation, set it to NULL
|
|
if (wcslen(pVRC->pLattice->wszAfter) == 0)
|
|
{
|
|
ExternFree(pVRC->pLattice->wszAfter);
|
|
pVRC->pLattice->wszAfter = NULL;
|
|
}
|
|
}
|
|
|
|
// Return success
|
|
return TRUE;
|
|
}
|
|
|
|
#ifndef USE_RESOURCES
|
|
// The three functions below are private APIs
|
|
// They are only defined in the multi-language version of the recognizer,
|
|
// not the ones that get shipped.
|
|
|
|
#ifdef HWX_TUNE
|
|
FILE *g_pTuneFile = NULL;
|
|
int g_iTuneMode = 0;
|
|
#endif
|
|
|
|
// Configures the lattice to record tuning information. Must be called before
|
|
// any strokes are added to the lattice.
|
|
int RecordTuningInformation(wchar_t *wszTuneFile)
|
|
{
|
|
LogMessage("RecordTuningInformation()\n");
|
|
|
|
#ifdef HWX_TUNE
|
|
if (g_pTuneFile != NULL)
|
|
{
|
|
g_iTuneMode = 0;
|
|
if (fclose(g_pTuneFile) < 0)
|
|
{
|
|
g_pTuneFile = NULL;
|
|
return HRCR_ERROR;
|
|
}
|
|
g_pTuneFile = NULL;
|
|
}
|
|
|
|
if (wszTuneFile != NULL)
|
|
{
|
|
BOOL fBinary = FALSE;
|
|
// Get the tuning mode based on a file name component
|
|
if (wcsstr(wszTuneFile, L".lintuneV.") != NULL)
|
|
{
|
|
g_iTuneMode = 1;
|
|
fBinary = TRUE;
|
|
}
|
|
if (wcsstr(wszTuneFile, L".threshold.") != NULL)
|
|
{
|
|
g_iTuneMode = 2;
|
|
}
|
|
if (wcsstr(wszTuneFile, L".lattice.") != NULL)
|
|
{
|
|
g_iTuneMode = 3;
|
|
}
|
|
g_pTuneFile = _wfopen(wszTuneFile, (fBinary ? L"wb" : L"w"));
|
|
if (g_pTuneFile == NULL)
|
|
{
|
|
g_iTuneMode = 0;
|
|
return HRCR_ERROR;
|
|
}
|
|
}
|
|
return HRCR_OK;
|
|
#else
|
|
return HRCR_ERROR;
|
|
#endif
|
|
}
|
|
|
|
// Given a lattice and a string of unicode characters, find the best path through the lattice
|
|
// which gives that sequence of characters. Baring that, it will find the most likely path
|
|
// through the lattice with the same number of characters and the minimum number of mismatches
|
|
// to the prompt. In case no such path can be found, the current path becomes empty.
|
|
// The function returns the number of substitutions used, or -1 if there is no path with
|
|
// the desired number of characters, -2 if a memory allocation error occurs, or -3 if a
|
|
// file write error occurs.
|
|
int SearchForTargetResult(HRC hrc, wchar_t *wsz)
|
|
{
|
|
int nSubs = 0;
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("SearchForTargetResult()\n");
|
|
|
|
// Check parameters.
|
|
if (hrc == NULL || pVRC->pLattice == NULL) {
|
|
ASSERT(("Bad lattice\n",0));
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// Processing done?
|
|
if (!(pVRC->fEndInput && pVRC->pLatticePath != NULL)) {
|
|
ASSERT(("Lattice not processed yet\n",0));
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
// Free the old path, in case we got called before
|
|
if (pVRC->pLatticePath != NULL) {
|
|
FreeLatticePath(pVRC->pLatticePath);
|
|
pVRC->pLatticePath = NULL;
|
|
}
|
|
|
|
if (g_iTuneMode == 3)
|
|
{
|
|
// Tuning mode 3 means dump out the IFELang3 lattices and correct answers.
|
|
ApplyLanguageModel(pVRC->pLattice, wsz);
|
|
}
|
|
else
|
|
{
|
|
nSubs = SearchForTargetResultInternal(pVRC->pLattice, wsz);
|
|
}
|
|
|
|
// Get the final path.
|
|
if (!GetCurrentPath(pVRC->pLattice, &pVRC->pLatticePath))
|
|
{
|
|
return -2;
|
|
}
|
|
|
|
return nSubs;
|
|
}
|
|
|
|
// Accessor macros for the bitmask above
|
|
#define SetAllowedChar(bpMask, dch) (bpMask)[(dch) / 8] |= 1 << ((dch) % 8)
|
|
|
|
BOOL HwxSetAnswerW(HRC hrc, wchar_t *wsz, int iMode)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("HwxSetAnswerW()\n");
|
|
|
|
// Check parameters.
|
|
if (hrc == NULL || pVRC->pLattice == NULL)
|
|
{
|
|
ASSERT(("Bad lattice\n",0));
|
|
return FALSE;
|
|
}
|
|
|
|
pVRC->pLattice->wszAnswer = ExternAlloc((wcslen(wsz) + 1) * sizeof(wchar_t));
|
|
if (pVRC->pLattice->wszAnswer == NULL)
|
|
{
|
|
ASSERT(("Out of memory allocating space.\n", 0));
|
|
return FALSE;
|
|
}
|
|
wcscpy(pVRC->pLattice->wszAnswer, wsz);
|
|
|
|
// Mode one means running the separator
|
|
if (iMode == 1)
|
|
{
|
|
pVRC->pLattice->fSepMode = TRUE;
|
|
}
|
|
|
|
// Mode 1 means we limit the characters that can be returned to those
|
|
// in the answer.
|
|
if (iMode == 1)
|
|
{
|
|
int iChar;
|
|
|
|
// Allocate a bit mask which holds all the folded and dense codes
|
|
int iMaskSize = (g_locRunInfo.cCodePoints + g_locRunInfo.cFoldingSets + 7) / 8;
|
|
BYTE *pbMask = ExternAlloc(iMaskSize);
|
|
if (pbMask == NULL)
|
|
{
|
|
ASSERT(("Out of memory allocating space.\n", 0));
|
|
return FALSE;
|
|
}
|
|
|
|
// Fill in the mask based on the prompt
|
|
memset(pbMask, 0, iMaskSize);
|
|
for (iChar = 0; iChar < (int) wcslen(wsz); iChar++)
|
|
{
|
|
wchar_t dch = LocRunUnicode2Dense(&g_locRunInfo, wsz[iChar]);
|
|
if (dch != LOC_TRAIN_NO_DENSE_CODE)
|
|
{
|
|
// Try folding the character
|
|
wchar_t fdch = LocRunDense2Folded(&g_locRunInfo, dch);
|
|
if (fdch != 0)
|
|
{
|
|
// Set the mask allowing this folding set
|
|
SetAllowedChar(pbMask, fdch);
|
|
}
|
|
// Set the mask allowing the unfolded character
|
|
SetAllowedChar(pbMask, dch);
|
|
}
|
|
}
|
|
|
|
// Store the mask in the recog settings.
|
|
pVRC->pLattice->recogSettings.pbAllowedChars = pbMask;
|
|
pVRC->pLattice->recogSettings.alcValid = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif // !USE_RESOURCES
|
|
|
|
BOOL SetHwxFlags(HRC hrc, DWORD dwFlags)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("SetHwxFlags(%08X,%08X)\n", hrc, dwFlags);
|
|
|
|
// Check parameters.
|
|
if (hrc == NULL || pVRC->pLattice == NULL)
|
|
{
|
|
ASSERT(("Bad lattice\n",0));
|
|
return FALSE;
|
|
}
|
|
|
|
if (pVRC->fBeginProcess)
|
|
{
|
|
// ASSERT(("Already started processing in SetHwxFlags", 0));
|
|
return FALSE;
|
|
}
|
|
|
|
if (dwFlags & ~(RECOFLAG_WORDMODE | RECOFLAG_SINGLESEG | RECOFLAG_COERCE))
|
|
{
|
|
// ASSERT(("Unknown flag set\n",0));
|
|
return FALSE;
|
|
}
|
|
|
|
pVRC->pLattice->fWordMode = ((dwFlags & RECOFLAG_WORDMODE) != 0);
|
|
pVRC->pLattice->fCoerceMode = ((dwFlags & RECOFLAG_COERCE) != 0);
|
|
pVRC->pLattice->fSingleSeg = ((dwFlags & RECOFLAG_SINGLESEG) != 0);
|
|
return TRUE;
|
|
}
|
|
|
|
#define MAX_FACTOIDS 10
|
|
|
|
/******************************Public*Routine******************************\
|
|
* SetHwxFactoid
|
|
*
|
|
* New API for factoids.
|
|
*
|
|
* Return values:
|
|
* HRCR_OK success
|
|
* HRCR_ERROR failure
|
|
* HRCR_CONFLICT ProcessHRC has already been called, cannot call me now
|
|
* HRCR_UNSUPPORTED don't support this factoid string
|
|
\**************************************************************************/
|
|
int SetHwxFactoid(HRC hrc, wchar_t *wszFactoid)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
int nFactoids, i;
|
|
DWORD aFactoids[MAX_FACTOIDS];
|
|
BYTE *pbOldFactoidChars;
|
|
ALC alcOldFactoid;
|
|
|
|
LogMessage("SetHwxFactoid(%08X,%S)\n", hrc, wszFactoid);
|
|
|
|
// Check parameters.
|
|
if (hrc == NULL || pVRC->pLattice == NULL)
|
|
{
|
|
return HRCR_ERROR;
|
|
}
|
|
|
|
if (pVRC->fBeginProcess)
|
|
{
|
|
return HRCR_CONFLICT;
|
|
}
|
|
|
|
// Special case to reset back to the default
|
|
if (wszFactoid == NULL)
|
|
{
|
|
// Clear out any previous factoid settings
|
|
SetFactoidDefaultInternal(pVRC->pLattice);
|
|
return HRCR_OK;
|
|
}
|
|
|
|
// Parse the string
|
|
nFactoids = ParseFactoidString(wszFactoid, MAX_FACTOIDS, aFactoids);
|
|
if (nFactoids <= 0)
|
|
{
|
|
return HRCR_UNSUPPORTED;
|
|
}
|
|
|
|
// Check to see if all the factoids set are supported
|
|
for (i = 0; i < nFactoids; i++)
|
|
{
|
|
if (!IsSupportedFactoid(aFactoids[i]))
|
|
{
|
|
return HRCR_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
// Reset to the empty set of chars and clear out any ALCs
|
|
alcOldFactoid = pVRC->pLattice->alcFactoid;
|
|
pbOldFactoidChars = pVRC->pLattice->pbFactoidChars;
|
|
|
|
pVRC->pLattice->alcFactoid = 0;
|
|
pVRC->pLattice->pbFactoidChars = NULL;
|
|
|
|
// For each factoid set
|
|
for (i = 0; i < nFactoids; i++)
|
|
{
|
|
if (!SetFactoidInternal(&g_locRunInfo, pVRC->pLattice, aFactoids[i]))
|
|
{
|
|
// Roll back to original settings
|
|
pVRC->pLattice->alcFactoid = alcOldFactoid;
|
|
ExternFree(pVRC->pLattice->pbFactoidChars);
|
|
pVRC->pLattice->pbFactoidChars = pbOldFactoidChars;
|
|
return HRCR_ERROR;
|
|
}
|
|
}
|
|
|
|
pVRC->pLattice->fUseFactoid = TRUE;
|
|
|
|
// Turn off the default language model if an empty factoid is set.
|
|
// May want to add this to other factoids in the future, or just
|
|
// use a real language model.
|
|
if (nFactoids == 1 && aFactoids[0] == FACTOID_NONE)
|
|
{
|
|
pVRC->pLattice->fUseLM = FALSE;
|
|
}
|
|
else
|
|
{
|
|
pVRC->pLattice->fUseLM = TRUE;
|
|
}
|
|
|
|
// Clear out the ALCs
|
|
pVRC->pLattice->recogSettings.alcValid = 0xFFFFFFFF;
|
|
ExternFree(pVRC->pLattice->recogSettings.pbAllowedChars);
|
|
pVRC->pLattice->recogSettings.pbAllowedChars = NULL;
|
|
|
|
pVRC->pLattice->recogSettings.alcPriority = 0;
|
|
ExternFree(pVRC->pLattice->recogSettings.pbPriorityChars);
|
|
pVRC->pLattice->recogSettings.pbPriorityChars = NULL;
|
|
|
|
return HRCR_OK;
|
|
}
|
|
|
|
// Note that for now this function only considers the factoid, and ignores
|
|
// any ALC settings.
|
|
BOOL IsWStringSupportedHRC(HRC hrc, wchar_t *wsz)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
BOOL fSupported = TRUE;
|
|
CHARSET charset;
|
|
|
|
LogMessage("IsWStringSupportedHRC()\n");
|
|
|
|
// Check parameters.
|
|
if (hrc == NULL || pVRC->pLattice == NULL)
|
|
{
|
|
ASSERT(("Bad lattice\n",0));
|
|
return FALSE;
|
|
}
|
|
|
|
// If no factoid has been set yet, then use the default.
|
|
if (!pVRC->pLattice->fUseFactoid)
|
|
{
|
|
SetFactoidDefaultInternal(pVRC->pLattice);
|
|
}
|
|
|
|
charset.recmask = pVRC->pLattice->alcFactoid;
|
|
charset.pbAllowedChars = pVRC->pLattice->pbFactoidChars;
|
|
|
|
// Loop over the string
|
|
while (*wsz != 0 && fSupported)
|
|
{
|
|
// First check if it is supported in the dense codes
|
|
wchar_t dch = LocRunUnicode2Dense(&g_locRunInfo, *wsz);
|
|
if (dch == LOC_TRAIN_NO_DENSE_CODE)
|
|
{
|
|
fSupported = FALSE;
|
|
break;
|
|
}
|
|
|
|
// Then check if it is allowed by the factoid
|
|
if (!IsAllowedChar(&g_locRunInfo, &charset, dch))
|
|
{
|
|
fSupported = FALSE;
|
|
break;
|
|
}
|
|
wsz++;
|
|
}
|
|
|
|
return fSupported;
|
|
}
|
|
|
|
#ifndef USE_RESOURCES
|
|
BOOL GetSupportedChars(HRC hrc, wchar_t wsz[65536])
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
CHARSET charset;
|
|
int i, iDest;
|
|
|
|
LogMessage("GetSupportedChars()\n");
|
|
|
|
// Check parameters.
|
|
if (hrc == NULL || pVRC->pLattice == NULL)
|
|
{
|
|
ASSERT(("Bad lattice\n",0));
|
|
return FALSE;
|
|
}
|
|
|
|
// If no factoid has been set yet, then use the default.
|
|
if (!pVRC->pLattice->fUseFactoid)
|
|
{
|
|
SetFactoidDefaultInternal(pVRC->pLattice);
|
|
}
|
|
|
|
charset.recmask = pVRC->pLattice->alcFactoid;
|
|
charset.pbAllowedChars = pVRC->pLattice->pbFactoidChars;
|
|
|
|
iDest = 0;
|
|
for (i = 1; i < g_locRunInfo.cCodePoints; i++)
|
|
{
|
|
if (IsAllowedChar(&g_locRunInfo, &charset, (wchar_t) i))
|
|
{
|
|
wsz[iDest++] = LocRunDense2Unicode(&g_locRunInfo, (wchar_t) i);
|
|
}
|
|
}
|
|
wsz[iDest] = 0;
|
|
return TRUE;
|
|
}
|
|
#endif
|