1130 lines
28 KiB
C
1130 lines
28 KiB
C
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
|
//
|
|
// Copyright (c) 2001 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Module:
|
|
// volcano/dll/BoxApi.c
|
|
//
|
|
// Description:
|
|
// Implement external Box input API for DLL.
|
|
//
|
|
// Author:
|
|
// hrowley
|
|
//
|
|
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "volcanop.h"
|
|
#include "zillap.h"
|
|
#include "fugu.h"
|
|
#if defined(USE_HOUND) || defined(USE_ZILLAHOUND)
|
|
# include "math16.h"
|
|
# include "hound.h"
|
|
#endif
|
|
|
|
#include "tpgHandle.h"
|
|
|
|
// Sanity check limits on guide structures.
|
|
#define GUIDE_MIN_COORD ((INT)0xC0000000)
|
|
#define GUIDE_MAX_COORD ((INT)0x3FFFFFFF)
|
|
#define GUIDE_MIN_BOXES 1
|
|
#define GUIDE_MAX_BOXES (32 * 1024)
|
|
#define GUIDE_MIN_SIZE 8
|
|
#define GUIDE_MAX_SIZE (256 * 1024)
|
|
|
|
// iUseCount tells if we have successfully loaded and is incremented to 1 when that happens.
|
|
static int g_iUseCount = 0;
|
|
|
|
// COM code for IFELang3
|
|
#ifdef USE_IFELANG3
|
|
# include <windows.h>
|
|
# include <ole2.h>
|
|
# include <objbase.h>
|
|
// Make sure the GUIDs defined in imlang.h actually get instantiated
|
|
# define INITGUID
|
|
# include "imlang.h"
|
|
#endif
|
|
|
|
// g_hInstanceDll is refered to to load resources.
|
|
HINSTANCE g_hInstanceDll;
|
|
|
|
// g_hInstanceDllCode is refered to to find the code DLL..
|
|
HINSTANCE g_hInstanceDllCode;
|
|
|
|
// Global data loaded by LoadCharRec.
|
|
LOCRUN_INFO g_locRunInfo;
|
|
|
|
// What language the recognizer is recognizing
|
|
wchar_t *g_szRecognizerLanguage=NULL;
|
|
|
|
//#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
|
|
|
|
// Forward declaration for function to unload the recognizer
|
|
// Unload the recognizer. The flag indicates whether we should try to unload IFELang3
|
|
// as well. This flag should usually be TRUE, but must be FALSE during a call from DllMain(),
|
|
// because it is not safe to do COM operations inside DllMain().
|
|
BOOL HwxUnconfig(BOOL fCanUnloadIFELang3);
|
|
|
|
static CRITICAL_SECTION g_csRecognizer = {0};
|
|
static BOOL g_bInitializedRecognizerCS = FALSE;
|
|
|
|
// Capture the dll handle, also handle unloading.
|
|
BOOL WINAPI DllMain(HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
|
|
{
|
|
if (dwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
// If we are using resources and they are in another DLL, we need to load that DLL.
|
|
# if defined(USE_RESOURCES)
|
|
wchar_t aFileName[_MAX_PATH];
|
|
int length;
|
|
|
|
// Build up name of data DLL from the name of this DLL. We replace .DLL with R.DLL.
|
|
length = GetModuleFileName(hDll, aFileName, _MAX_PATH);
|
|
if (length < 5 || _MAX_PATH - 1 < length)
|
|
{
|
|
ASSERT(length >= 5);
|
|
return FALSE;
|
|
}
|
|
wcscpy(aFileName + length - 4, L"R.DLL");
|
|
|
|
// Load the data library.
|
|
g_hInstanceDll = LoadLibraryEx(aFileName, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
|
ASSERT(g_hInstanceDll);
|
|
|
|
// If we can't find our resources, just give up. Nothing will work.
|
|
if (!g_hInstanceDll)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!InitializeCriticalSectionAndSpinCount(&g_csRecognizer, 4000))
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
g_bInitializedRecognizerCS = TRUE;
|
|
}
|
|
# else
|
|
g_hInstanceDll = hDll;
|
|
# endif
|
|
|
|
// init the handle manager
|
|
if (FALSE == initTpgHandleManager())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Always let the Wisp code know where the code DLL is.
|
|
g_hInstanceDllCode = hDll;
|
|
}
|
|
else if (dwReason == DLL_PROCESS_DETACH)
|
|
{
|
|
BOOL fRet;
|
|
|
|
// If we haven't cleaned up.
|
|
// In the future, we should force g_iUseCount to be zero, to make
|
|
// sure the client has cleaned us up properly. Until everyone is
|
|
// using the WISP API we cannot enforce this, because the old API
|
|
// has no "clean up" call.
|
|
// ASSERT(g_iUseCount == 0);
|
|
if (g_iUseCount > 0)
|
|
{
|
|
// Set the use count to 1 to force a cleanup
|
|
g_iUseCount = 1;
|
|
// Clean up, but don't do COM calls to discard IFELang3,
|
|
// because we can't make COM calls inside DllMain.
|
|
fRet = HwxUnconfig(FALSE);
|
|
}
|
|
else
|
|
{
|
|
// If we have already cleaned up, return
|
|
fRet = TRUE;
|
|
}
|
|
|
|
# if defined(USE_RESOURCES)
|
|
if (!FreeLibrary(g_hInstanceDll))
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
if (g_bInitializedRecognizerCS)
|
|
{
|
|
DeleteCriticalSection(&g_csRecognizer);
|
|
g_bInitializedRecognizerCS = FALSE;
|
|
}
|
|
# endif
|
|
|
|
// close the handle manager
|
|
closeTpgHandleManager();
|
|
|
|
return fRet;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// HwxConfig
|
|
//
|
|
// Initialize the recognizer when it loads.
|
|
#ifdef USE_RESOURCES
|
|
|
|
#include "res.h"
|
|
|
|
BOOL HwxConfig()
|
|
{
|
|
const ZILLADB_HEADER *pHeader;
|
|
|
|
EnterCriticalSection(&g_csRecognizer);
|
|
|
|
LogMessage("HwxConfig()\n");
|
|
|
|
if (g_iUseCount > 0) {
|
|
goto exitSuccess;
|
|
}
|
|
|
|
// Load the Locale database.
|
|
if (!LocRunLoadRes(
|
|
&g_locRunInfo, g_hInstanceDll, RESID_LOCRUN, VOLCANO_RES
|
|
)) {
|
|
ASSERT(("Error in LocRunLoadRes", FALSE));
|
|
goto exitFailure;
|
|
}
|
|
|
|
// Load the recognizer databases.
|
|
if (!LoadCharRec(g_hInstanceDll)) {
|
|
ASSERT(("Error in LoadCharRec", FALSE));
|
|
goto exitFailure;
|
|
}
|
|
|
|
pHeader = (ZILLADB_HEADER*)DoLoadResource(NULL, g_hInstanceDll, RESID_ZILLA, VOLCANO_RES);
|
|
if (pHeader == NULL) {
|
|
ASSERT(("Couldn't load zilla database", FALSE));
|
|
goto exitFailure;
|
|
}
|
|
g_szRecognizerLanguage = ExternAlloc(sizeof(wchar_t) * (wcslen(pHeader->locale) + 1));
|
|
if (g_szRecognizerLanguage == NULL) {
|
|
ASSERT(("Couldn't generate recognizer language string", FALSE));
|
|
goto exitFailure;
|
|
}
|
|
wcscpy(g_szRecognizerLanguage, pHeader->locale);
|
|
|
|
// Configure the factoid table
|
|
if (!FactoidTableConfig(&g_locRunInfo, g_szRecognizerLanguage))
|
|
{
|
|
goto exitFailure;
|
|
}
|
|
|
|
// Configure the lattice code.
|
|
if (!LatticeConfig(g_hInstanceDll)) {
|
|
ASSERT(("Error in LatticeConfig", FALSE));
|
|
goto exitFailure;
|
|
}
|
|
|
|
exitSuccess:
|
|
g_iUseCount++;
|
|
LeaveCriticalSection(&g_csRecognizer);
|
|
return TRUE;
|
|
|
|
exitFailure:
|
|
LeaveCriticalSection(&g_csRecognizer);
|
|
return FALSE;
|
|
}
|
|
|
|
// Unload the recognizer. The flag indicates whether we should try to unload IFELang3
|
|
// as well. This flag should usually be TRUE, but must be FALSE during a call from DllMain(),
|
|
// because it is not safe to do COM operations inside DllMain().
|
|
BOOL HwxUnconfig(BOOL fCanUnloadIFELang3)
|
|
{
|
|
BOOL ok = TRUE;
|
|
|
|
EnterCriticalSection(&g_csRecognizer);
|
|
|
|
if (g_iUseCount == 0) {
|
|
ASSERT(("HwxUnconfig() called more times that HwxConfig()\n", 0));
|
|
LeaveCriticalSection(&g_csRecognizer);
|
|
return FALSE;
|
|
}
|
|
g_iUseCount--;
|
|
if (g_iUseCount == 0)
|
|
{
|
|
if (g_szRecognizerLanguage != NULL)
|
|
{
|
|
ExternFree(g_szRecognizerLanguage);
|
|
}
|
|
if (!UnloadCharRec()) ok = FALSE;
|
|
if (!LatticeUnconfig()) ok = FALSE;
|
|
#ifdef USE_IFELANG3
|
|
if (fCanUnloadIFELang3 && !LatticeUnconfigIFELang3()) ok = FALSE;
|
|
#endif
|
|
FactoidTableUnconfig();
|
|
}
|
|
LeaveCriticalSection(&g_csRecognizer);
|
|
return ok;
|
|
}
|
|
|
|
# else
|
|
|
|
BOOL HwxConfigEx(wchar_t *pLocale, wchar_t *pLocaleDir, wchar_t *pRecogDir)
|
|
{
|
|
LogMessage("HwxConfigEx()\n");
|
|
|
|
if (g_iUseCount > 0) {
|
|
g_iUseCount++;
|
|
return(TRUE);
|
|
}
|
|
|
|
g_szRecognizerLanguage = ExternAlloc(sizeof(wchar_t) * (wcslen(pLocale) + 1));
|
|
if (g_szRecognizerLanguage==NULL) {
|
|
ASSERT(("Out of memory copying recognizer language", FALSE));
|
|
return FALSE;
|
|
}
|
|
wcscpy(g_szRecognizerLanguage, pLocale);
|
|
|
|
// Load the Locale database.
|
|
if (!LocRunLoadFile(&g_locRunInfo, pRecogDir)) {
|
|
ASSERT(("Error in LocRunLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
|
|
// Configure the factoid table
|
|
if (!FactoidTableConfig(&g_locRunInfo, g_szRecognizerLanguage))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Load the recognizer databases.
|
|
// They are loaded in this order: otter, zilla, crane/prob or hawk,
|
|
if (!LoadCharRec(pRecogDir)) {
|
|
ASSERT(("Error in LoadCharRec", FALSE));
|
|
return FALSE;
|
|
}
|
|
|
|
// Configure the lattice code.
|
|
// Loads the following databases: unigrams, bigrams, class bigrams, tuning, centipede, free input
|
|
if (!LatticeConfigFile(pRecogDir)) {
|
|
ASSERT(("Error in LatticeConfig", FALSE));
|
|
return FALSE;
|
|
}
|
|
|
|
g_iUseCount++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Initialize the recognizer partially.
|
|
// When iLevel is set to 0, only the locale database, tuning database,
|
|
// otter and zilla (and other core recognizers) are loaded.
|
|
// When iLevel is set to 1, all the above databases are loaded, as well as
|
|
// unigrams and crane/hawk.
|
|
BOOL HwxConfigExPartial(wchar_t *pLocale, wchar_t *pRecogDir, int iLevel)
|
|
{
|
|
extern OTTER_LOAD_INFO g_OtterLoadInfo;
|
|
extern FUGU_LOAD_INFO g_FuguLoadInfo;
|
|
extern JAWS_LOAD_INFO g_JawsLoadInfo;
|
|
extern SOLE_LOAD_INFO g_SoleLoadInfo;
|
|
#if defined(USE_HOUND) || defined(USE_ZILLAHOUND)
|
|
extern LOAD_INFO g_HoundLoadInfo;
|
|
#endif
|
|
#ifdef USE_OLD_DATABASES
|
|
extern CRANE_LOAD_INFO g_CraneLoadInfo;
|
|
#else
|
|
extern LOAD_INFO g_HawkLoadInfo;
|
|
#endif
|
|
|
|
LogMessage("HwxConfigExPartial()\n");
|
|
|
|
if (g_iUseCount > 0) {
|
|
g_iUseCount++;
|
|
return(TRUE);
|
|
}
|
|
|
|
g_szRecognizerLanguage = ExternAlloc(sizeof(wchar_t) * (wcslen(pLocale) + 1));
|
|
if (g_szRecognizerLanguage==NULL) {
|
|
ASSERT(("Out of memory copying recognizer language", FALSE));
|
|
return FALSE;
|
|
}
|
|
wcscpy(g_szRecognizerLanguage, pLocale);
|
|
|
|
LatticeConfigInit();
|
|
|
|
if (iLevel >= 0) {
|
|
// Load the Locale database.
|
|
if (!LocRunLoadFile(&g_locRunInfo, pRecogDir)) {
|
|
ASSERT(("Error in LocRunLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
|
|
// Configure the factoid table
|
|
if (!FactoidTableConfig(&g_locRunInfo, g_szRecognizerLanguage))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Load the tuning info
|
|
if (!VTuneLoadFile(&g_vtuneInfo, pRecogDir)) {
|
|
ASSERT(("Error in VTuneLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
|
|
// Load the tuning info
|
|
if (!TTuneLoadFile(&g_ttuneInfo, pRecogDir)) {
|
|
ASSERT(("Error in TTuneLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
|
|
if (JawsLoadFile(&g_JawsLoadInfo, pRecogDir))
|
|
{
|
|
// Load the Fugu database
|
|
if (!FuguLoadFile(&g_FuguLoadInfo, pRecogDir, &g_locRunInfo))
|
|
{
|
|
ASSERT(("Error in FuguLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
// Load the Sole database
|
|
if (!SoleLoadFile(&g_SoleLoadInfo, pRecogDir, &g_locRunInfo))
|
|
{
|
|
ASSERT(("Error in SoleLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
g_fUseJaws = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Load the Otter database
|
|
if (!OtterLoadFile(&g_locRunInfo, &g_OtterLoadInfo, pRecogDir))
|
|
{
|
|
ASSERT(("Error in OtterLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
g_fUseJaws = FALSE;
|
|
}
|
|
|
|
#if defined(USE_ZILLA) || defined(USE_ZILLAHOUND)
|
|
// Load the Zilla database
|
|
if (!ZillaLoadFile(&g_locRunInfo, pRecogDir, TRUE)) {
|
|
ASSERT(("Error in ZillaLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
#if defined(USE_HOUND)
|
|
// Load the Hound database
|
|
if (!HoundLoadFile(&g_locRunInfo, &g_HoundLoadInfo, pRecogDir)) {
|
|
ASSERT(("Error in HoundLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
g_fUseZillaHound = FALSE;
|
|
#if defined(USE_ZILLAHOUND)
|
|
// Load the Hound & Hound-Zilla databases (This is optional).
|
|
if (HoundLoadFile(&g_locRunInfo, &g_HoundLoadInfo, pRecogDir)) {
|
|
if (ZillaHoundLoadFile(pRecogDir)) {
|
|
g_fUseZillaHound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
HoundUnLoadFile(&g_HoundLoadInfo);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (iLevel >= 1) {
|
|
// Load unigrams
|
|
if (!UnigramLoadFile(&g_locRunInfo, &g_unigramInfo, pRecogDir)) {
|
|
ASSERT(("Error in UnigramLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef USE_OLD_DATABASES
|
|
// Load the Hawk database.
|
|
if (!HawkLoadFile(&g_locRunInfo, &g_HawkLoadInfo, pRecogDir)) {
|
|
ASSERT(("Error in HawkLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
#else
|
|
// Load Crane
|
|
if (!CraneLoadFile(&g_locRunInfo,&g_CraneLoadInfo, pRecogDir)) {
|
|
ASSERT(("Error in CraneLoadFile", FALSE));
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
g_iUseCount++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Unload the recognizer. The flag indicates whether we should try to unload IFELang3
|
|
// as well. This flag should usually be TRUE, but must be FALSE during a call from DllMain(),
|
|
// because it is not safe to do COM operations inside DllMain().
|
|
BOOL HwxUnconfig(BOOL fCanUnloadIFELang3)
|
|
{
|
|
BOOL ok = TRUE;
|
|
if (g_iUseCount == 0) {
|
|
ASSERT(("HwxUnconfig() called more times that HwxConfig()\n", 0));
|
|
return FALSE;
|
|
}
|
|
g_iUseCount--;
|
|
if (g_iUseCount > 0) return TRUE;
|
|
if (g_szRecognizerLanguage != NULL)
|
|
{
|
|
ExternFree(g_szRecognizerLanguage);
|
|
}
|
|
if (!LocRunUnloadFile(&g_locRunInfo)) ok = FALSE;
|
|
if (!UnloadCharRec()) ok = FALSE;
|
|
if (!LatticeUnconfigFile()) ok = FALSE;
|
|
#ifdef USE_IFELANG3
|
|
if (fCanUnloadIFELang3 && !LatticeUnconfigIFELang3()) ok = FALSE;
|
|
#endif
|
|
FactoidTableUnconfig();
|
|
return ok;
|
|
}
|
|
|
|
# endif
|
|
|
|
////
|
|
// HwxCreate
|
|
//
|
|
// Create an HRC.
|
|
////
|
|
HRC HwxCreate(HRC hrcTemplate)
|
|
{
|
|
VRC *pVRC;
|
|
|
|
LogMessage("HwxCreate(%08X)\n",hrcTemplate);
|
|
|
|
// Alloc the VRC.
|
|
pVRC = ExternAlloc(sizeof(VRC));
|
|
if (!pVRC) {
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
goto error1;
|
|
}
|
|
|
|
// Initialize it.
|
|
pVRC->fBoxedInput = TRUE;
|
|
pVRC->fHaveInput = FALSE;
|
|
pVRC->fEndInput = FALSE;
|
|
pVRC->fBeginProcess = FALSE;
|
|
pVRC->pLatticePath = (LATTICE_PATH *)0;
|
|
|
|
if (!hrcTemplate) {
|
|
pVRC->pLattice = AllocateLattice();
|
|
if (!pVRC->pLattice) {
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
goto error2;
|
|
}
|
|
} else {
|
|
VRC *pVRCTemplate = (VRC *)hrcTemplate;
|
|
|
|
if (!pVRCTemplate->pLattice || !pVRCTemplate->fBoxedInput) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
pVRC->pLattice = CreateCompatibleLattice(
|
|
pVRCTemplate->pLattice
|
|
);
|
|
if (!pVRC->pLattice) {
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
goto error2;
|
|
}
|
|
}
|
|
|
|
LogMessage("HwxCreate(%08X) -> %08X\n", hrcTemplate, pVRC);
|
|
// Success, cast to HRC and return it.
|
|
return (HRC)pVRC;
|
|
|
|
error2:
|
|
ExternFree(pVRC);
|
|
|
|
error1:
|
|
return (HRC)0;
|
|
}
|
|
|
|
////
|
|
// HwxDestroy
|
|
//
|
|
// Destroy an HRC.
|
|
////
|
|
BOOL HwxDestroy(HRC hrc)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("HwxDestroy(%08X)\n",hrc);
|
|
|
|
// Did we get a handle? Is if a free input handle?
|
|
if (!hrc || !pVRC->fBoxedInput) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
// 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 TRUE;
|
|
}
|
|
|
|
////
|
|
// HwxSetGuide
|
|
//
|
|
// Sets the guide structure to use.
|
|
////
|
|
BOOL HwxSetGuide(HRC hrc, HWXGUIDE *pGuide)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("HwxSetGuide(%08X,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)\n", hrc,
|
|
pGuide->cHorzBox,
|
|
pGuide->cVertBox,
|
|
pGuide->xOrigin,
|
|
pGuide->yOrigin,
|
|
pGuide->cxBox,
|
|
pGuide->cyBox,
|
|
pGuide->cxOffset,
|
|
pGuide->cyOffset,
|
|
pGuide->cxWriting,
|
|
pGuide->cyWriting,
|
|
pGuide->cyMid,
|
|
pGuide->cyBase,
|
|
pGuide->nDir);
|
|
|
|
// Did we get a handle? Does it have a lattice?
|
|
if (!hrc || !pVRC->pLattice || !pVRC->fBoxedInput)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
// Make sure that we haven't begun receiving input
|
|
if (pVRC->fHaveInput || pVRC->fEndInput)
|
|
{
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
}
|
|
|
|
// Validate the individual GUIDE values are not totally out of bounds.
|
|
if ((pGuide == (HWXGUIDE *)NULL) ||
|
|
(pGuide->cHorzBox < GUIDE_MIN_BOXES) || (GUIDE_MAX_BOXES < pGuide->cHorzBox) ||
|
|
(pGuide->cVertBox < GUIDE_MIN_BOXES) || (GUIDE_MAX_BOXES < pGuide->cVertBox) ||
|
|
(pGuide->xOrigin < GUIDE_MIN_COORD) || (GUIDE_MAX_COORD < pGuide->xOrigin) ||
|
|
(pGuide->yOrigin < GUIDE_MIN_COORD) || (GUIDE_MAX_COORD < pGuide->yOrigin) ||
|
|
(GUIDE_MAX_SIZE < pGuide->cxOffset) ||
|
|
(GUIDE_MAX_SIZE < pGuide->cyOffset) ||
|
|
(pGuide->cxBox < GUIDE_MIN_SIZE) || (GUIDE_MAX_SIZE < pGuide->cxBox) ||
|
|
(pGuide->cyBox < GUIDE_MIN_SIZE) || (GUIDE_MAX_SIZE < pGuide->cyBox) ||
|
|
(pGuide->cxWriting < GUIDE_MIN_SIZE) || (GUIDE_MAX_SIZE < pGuide->cxWriting) ||
|
|
(pGuide->cyWriting < GUIDE_MIN_SIZE) || (GUIDE_MAX_SIZE < pGuide->cyWriting) ||
|
|
(GUIDE_MAX_SIZE < pGuide->cyMid) ||
|
|
(GUIDE_MAX_SIZE < pGuide->cyBase) ||
|
|
(pGuide->nDir != HWX_HORIZONTAL)
|
|
) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
}
|
|
|
|
// Now that we know the values make some sense, we can check for internal consistancy.
|
|
if ((((GUIDE_MAX_COORD - pGuide->xOrigin) / pGuide->cHorzBox) < pGuide->cxBox) ||
|
|
(((GUIDE_MAX_COORD - pGuide->yOrigin) / pGuide->cVertBox) < pGuide->cyBox) ||
|
|
((pGuide->cxOffset + pGuide->cxWriting) > pGuide->cxBox) ||
|
|
((pGuide->cyOffset + pGuide->cyWriting) > pGuide->cyBox) ||
|
|
// By spec. cyMid and cyBase should be zero, but old code still sets them,
|
|
// so we must allow plausable values.
|
|
// (pGuide->cyMid != 0 || pGuide->cyBase != 0)
|
|
(pGuide->cyMid > pGuide->cyBase || pGuide->cyBase > pGuide->cyWriting)
|
|
) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
}
|
|
|
|
SetLatticeGuide(pVRC->pLattice, pGuide);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
////
|
|
// HwxALCValid
|
|
//
|
|
// Tells what character sets to select.
|
|
////
|
|
BOOL HwxALCValid(HRC hrc, ALC alc)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("HwxALCValid(%08X,%08X)\n",hrc,alc);
|
|
|
|
// Check parameters.
|
|
if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
}
|
|
|
|
// Pass the ALC on to the lattice.
|
|
SetLatticeALCValid(pVRC->pLattice, alc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
////
|
|
// HwxALCPriority
|
|
//
|
|
// Tells what character sets to move to the top of the alt list..
|
|
////
|
|
BOOL HwxALCPriority(HRC hrc, ALC alc)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("HwxALCPriority(%08X,%08X)\n",hrc,alc);
|
|
|
|
// Check parameters.
|
|
if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
}
|
|
|
|
// Pass the ALC on to the lattice.
|
|
SetLatticeALCPriority(pVRC->pLattice, alc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////
|
|
// HwxProcess
|
|
//
|
|
// Process the ink and return the results.
|
|
////
|
|
BOOL HwxProcess(HRC hrc)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("HwxProcess(%08X)\n",hrc);
|
|
|
|
// 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) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
// Do any processing we can.
|
|
if (!ProcessLattice(pVRC->pLattice, pVRC->fEndInput))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#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 up any previous path
|
|
if (pVRC->pLatticePath != NULL) {
|
|
FreeLatticePath(pVRC->pLatticePath);
|
|
}
|
|
|
|
// Get path (which may be partial if we have not reached the end of input)
|
|
if (!GetCurrentPath(pVRC->pLattice,&pVRC->pLatticePath)) {
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
////
|
|
// HwxEndInput
|
|
//
|
|
// No more ink is coming (or can be added) once this is called.
|
|
////
|
|
BOOL HwxEndInput(HRC hrc)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("HwxEndInput(%08X)\n",hrc);
|
|
|
|
// 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) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
pVRC->fEndInput = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
////
|
|
// HwxInput
|
|
//
|
|
// Add Ink to the HRC.
|
|
////
|
|
BOOL HwxInput(HRC hrc, POINT *pPnt, UINT cPnt, DWORD dwTick)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
#ifdef DEBUG_LOG_API
|
|
int i;
|
|
LogMessage("HwxInput(%08X,%d,%d)\n",hrc,cPnt,dwTick);
|
|
for (i = 0; i < (int) cPnt; i++)
|
|
LogMessage(" xy[%i]=%d,%d\n", i, pPnt[i].x, pPnt[i].y);
|
|
#endif
|
|
|
|
// Did we get a handle? Does it have a lattice?
|
|
if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
} else if (pVRC->fEndInput) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
} else if (!cPnt || pPnt==NULL) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
}
|
|
|
|
// Mark as having input.
|
|
pVRC->fHaveInput = TRUE;
|
|
|
|
// Add stroke to the lattice.
|
|
if (AddStrokeToLattice(pVRC->pLattice, cPnt, pPnt, dwTick) >= 0) {
|
|
return TRUE;
|
|
} else {
|
|
// JRB: FIXME: Is this always the correct error code?
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
////
|
|
// HwxGetResults
|
|
//
|
|
// Returns the results from the recognizer.
|
|
////
|
|
int HwxGetResults(
|
|
HRC hrc, // HRC containing results
|
|
UINT cAlt, // number of alternates
|
|
UINT iFirst, // index of first character to return
|
|
UINT cBoxRes, // number of characters to return
|
|
HWXRESULTS *rgBoxResults // array of cBoxRes ranked lists
|
|
) {
|
|
UINT ii, jj;
|
|
VRC *pVRC = (VRC *)hrc;
|
|
LATTICE_PATH *pLP;
|
|
UINT iBNFirst, iBNLast;
|
|
HWXRESULTS *pResults;
|
|
int sizeResults;
|
|
|
|
LogMessage("HwxGetResults(%08X,%d,%d,%d)\n",hrc,cAlt,iFirst,cBoxRes);
|
|
|
|
// Check parameters.
|
|
if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
LogMessage(" invalid handle\n");
|
|
return -1;
|
|
} else if (!rgBoxResults) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
LogMessage(" bad rgBoxResults\n");
|
|
return -1;
|
|
// } else if (!pVRC->fEndInput) {
|
|
// SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
// LogMessage(" not at end of input\n");
|
|
// return -1;
|
|
} else if (GetLatticeStrokeCount(pVRC->pLattice) < 1) {
|
|
// No ink to process.
|
|
LogMessage(" no strokes\n");
|
|
return 0;
|
|
} else if (!pVRC->pLatticePath) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
LogMessage(" no path\n");
|
|
return -1;
|
|
} else if (rgBoxResults == NULL || 0 == cAlt) {
|
|
// Asked for nothing.
|
|
LogMessage(" no results requested\n");
|
|
return 0;
|
|
}
|
|
|
|
// JBENN: WARNING: This code assumes that the lattice sorts
|
|
// the strokes by the box order, so that the box numbers are
|
|
// in acending order.
|
|
|
|
// Find the first box in the requested range.
|
|
pLP = pVRC->pLatticePath;
|
|
for (iBNFirst = 0; iBNFirst < (UINT)pLP->nChars; ++iBNFirst) {
|
|
ASSERT(iBNFirst == 0 || pLP->pElem[iBNFirst - 1].iBoxNum < pLP->pElem[iBNFirst].iBoxNum);
|
|
if ((UINT)pLP->pElem[iBNFirst].iBoxNum >= iFirst) {
|
|
break;
|
|
}
|
|
}
|
|
if (iBNFirst >= (UINT)pLP->nChars) {
|
|
// Nothing found.
|
|
LogMessage(" not enough results %d vs %d\n",iBNFirst,pLP->nChars);
|
|
return 0;
|
|
}
|
|
|
|
// Compute last box.
|
|
iBNLast = iBNFirst + cBoxRes;
|
|
if (iBNLast > (UINT)pLP->nChars) {
|
|
iBNLast = (UINT)pLP->nChars;
|
|
}
|
|
|
|
// Copy out the results.
|
|
pResults = rgBoxResults;
|
|
sizeResults = sizeof(HWXRESULTS) + (cAlt - 1) * sizeof(pResults->rgChar[0]);
|
|
for (ii = iBNFirst; ii < iBNLast; ++ii) {
|
|
UINT cAltUsed;
|
|
wchar_t oldTemp, newTemp;
|
|
|
|
// Retyrn what box was this in.
|
|
pResults->indxBox = (short)pLP->pElem[ii].iBoxNum;
|
|
|
|
// Get the alternates.
|
|
cAltUsed = GetAlternatesForCharacterInCurrentPath(
|
|
pVRC->pLattice, pVRC->pLatticePath, ii, cAlt, pResults->rgChar
|
|
);
|
|
|
|
// Move top choice to top of list, if it is not already there.
|
|
// We slide everything above it down.
|
|
oldTemp = pLP->pElem[ii].wChar;
|
|
for (jj = 0; jj < cAltUsed; ++jj) {
|
|
newTemp = pResults->rgChar[jj];
|
|
pResults->rgChar[jj] = oldTemp;
|
|
if (newTemp == pLP->pElem[ii].wChar) {
|
|
break;
|
|
}
|
|
oldTemp = newTemp;
|
|
}
|
|
|
|
// Zero any unused slots.
|
|
for (jj = cAltUsed; jj < cAlt; ++jj) {
|
|
pResults->rgChar[jj] = L'\0';
|
|
}
|
|
#if 0
|
|
{
|
|
/*
|
|
FILE *f=_wfopen(L"c:/box-results.utf",L"ab");
|
|
if (ftell(f)==0) fwprintf(f,L"%c",0xfeff);
|
|
for (jj=0; jj<cAltUsed; jj++)
|
|
fwprintf(f,L"%c U+%04X ",pResults->rgChar[jj],pResults->rgChar[jj]);
|
|
fwprintf(f,L"\r\n");
|
|
fclose(f);
|
|
*/
|
|
FILE *f=fopen("c:/box-results.utf","a");
|
|
for (jj=0; jj<cAltUsed; jj++)
|
|
fprintf(f,"U+%04X ",pResults->rgChar[jj]);
|
|
fprintf(f,"\n");
|
|
fclose(f);
|
|
}
|
|
#endif
|
|
for (jj=0; jj<cAlt; jj++) {
|
|
LogMessage(" Box %d alt %d wch U+%04X\n",ii,jj,pResults->rgChar[jj]);
|
|
}
|
|
|
|
// Sequence to next results structure.
|
|
pResults = (HWXRESULTS *)(((BYTE *)pResults) + sizeResults);
|
|
|
|
}
|
|
|
|
// Return the number of character positions filled in.
|
|
return iBNLast - iBNFirst;
|
|
}
|
|
|
|
////
|
|
// HwxSetContext
|
|
//
|
|
// Handwriting recognition performance can be improved if the recognizer has context
|
|
// information available during processing. Context information is added to an HRC
|
|
// via the HwxSetContext function which provides one character of prior context for
|
|
// the recognizer. This function should be called prior to using the HwxProcess
|
|
// function.
|
|
//
|
|
// Remarks
|
|
// If this function is not called, the recognizer will assume that no prior context
|
|
// is available. Performance of the recognizer is improved if context can be
|
|
// provided. Currently this function improves performance only for the first
|
|
// character in the HRC. If the HRC contains ink for multiple characters,
|
|
// the recognition process itself will provide context information for characters
|
|
// after the first character, but no context information is available for the first
|
|
// character in the HRC unless it is provided via the HwxSetContext function.
|
|
// This is especially important for situations where ink are recognized one
|
|
// character at a time.
|
|
////
|
|
BOOL HwxSetContext(HRC hrc, WCHAR wchContext)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
wchar_t wszBefore[2];
|
|
|
|
LogMessage("HwxSetContext(%08X,U+%04X)\n",hrc,wchContext);
|
|
|
|
// Check parameters.
|
|
if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
}
|
|
|
|
wszBefore[0] = wchContext;
|
|
wszBefore[1] = 0;
|
|
return SetHwxCorrectionContext(hrc, wszBefore, NULL);
|
|
}
|
|
|
|
////
|
|
// HwxResultsAvailable
|
|
//
|
|
// FIXME: Make this work for partial results!
|
|
//
|
|
// Warnings: This assumes the writer can't go back and touch up previous
|
|
// characters.
|
|
//
|
|
// Returns the number of characters that can be gotten and displayed safely
|
|
// because the viterbi search has folded to one path at this point.
|
|
//
|
|
// Return the number of characters available in the
|
|
// path that are ready to get. This API looks at the viterbi search and
|
|
// any characters far enough back in the input that all the paths merge
|
|
// to 1 are done and can be displayed because nothing further out in the
|
|
// input will change the best path back once it's merged to a single path.
|
|
////
|
|
INT HwxResultsAvailable(HRC hrc)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("HwxResultsAvailable(%08X)\n",hrc);
|
|
|
|
// Check parameters.
|
|
if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return -1;
|
|
// } else if (!pVRC->fEndInput) {
|
|
// SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
// return -1;
|
|
} else if (GetLatticeStrokeCount(pVRC->pLattice) < 1) {
|
|
// No ink to process.
|
|
return 0;
|
|
}
|
|
|
|
// Currently, can't actually do partial results, so if we have
|
|
// not finished processing, we return zero!
|
|
if (!pVRC->pLatticePath) {
|
|
return 0;
|
|
}
|
|
|
|
// OK, we have results, so return the number available.
|
|
LogMessage(" %d results\n",pVRC->pLatticePath->nChars);
|
|
return pVRC->pLatticePath->nChars;
|
|
}
|
|
|
|
////
|
|
// HwxSetPartial
|
|
//
|
|
// Set parameters for recognition
|
|
////
|
|
BOOL HwxSetPartial(HRC hrc, UINT partialMode)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("HwxSetPartial(%08X,%d)\n",hrc,partialMode);
|
|
|
|
// Check parameters.
|
|
if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
}
|
|
if (partialMode > HWX_PARTIAL_FREE) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
}
|
|
|
|
pVRC->pLattice->recogSettings.partialMode = partialMode;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
////
|
|
// HwxSetAbort
|
|
//
|
|
// Set address for abort detection
|
|
////
|
|
BOOL HwxSetAbort(HRC hrc, UINT *pAbort)
|
|
{
|
|
VRC *pVRC = (VRC *)hrc;
|
|
|
|
LogMessage("HwxSetAbort(%08X,%08X)\n",hrc,pAbort);
|
|
|
|
// Check parameters.
|
|
if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
|
|
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
|
return FALSE;
|
|
}
|
|
|
|
pVRC->pLattice->recogSettings.pAbort = pAbort;
|
|
|
|
return TRUE;
|
|
}
|