usp10: Rewrite ScriptXtoCP.

Signed-off-by: Aric Stewart <aric@codeweavers.com>
Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Aric Stewart 2016-01-18 07:50:44 -06:00 committed by Alexandre Julliard
parent 09dac2f482
commit c160ad9e89
2 changed files with 318 additions and 100 deletions

View File

@ -2438,6 +2438,70 @@ static void _test_item_ScriptXtoX(SCRIPT_ANALYSIS *psa, int cChars, int cGlyphs,
}
}
#define test_caret_item_ScriptXtoCP(a,b,c,d,e,f) _test_caret_item_ScriptXtoCP(__LINE__,a,b,c,d,e,f)
static void _test_caret_item_ScriptXtoCP(int line, SCRIPT_ANALYSIS *psa, int cChars, int cGlyphs, const int* offsets, const WORD *pwLogClust, const int* piAdvance )
{
int iX, iCP, i;
int icChars, icGlyphs;
int piCP;
int clusterSize;
HRESULT hr;
SCRIPT_VISATTR psva[10];
int piTrailing;
int direction;
memset(psva,0,sizeof(psva));
direction = (psa->fRTL)?-1:+1;
for(iX = -1, i = iCP = 0; i < cChars; i++)
{
if (offsets[i] != iX)
{
iX = offsets[i];
iCP = i;
}
icChars = cChars;
icGlyphs = cGlyphs;
hr = ScriptXtoCP(iX, icChars, icGlyphs, pwLogClust, psva, piAdvance, psa, &piCP, &piTrailing);
ok_(__FILE__,line)(hr == S_OK, "ScriptXtoCP: should return S_OK not %08x\n", hr);
ok_(__FILE__,line)(piCP == iCP, "ScriptXtoCP: iX=%d should return piCP=%d not %d\n", iX, iCP, piCP);
ok_(__FILE__,line)(piTrailing == 0, "ScriptXtoCP: iX=%d should return piTrailing=0 not %d\n", iX, piTrailing);
}
for(iX = -2, i = 0; i < cChars; i++)
{
if (offsets[i]+direction != iX)
{
iX = offsets[i] + direction;
iCP = i;
}
icChars = cChars;
icGlyphs = cGlyphs;
hr = ScriptXtoCP(iX, icChars, icGlyphs, pwLogClust, psva, piAdvance, psa, &piCP, &piTrailing);
ok_(__FILE__,line)(hr == S_OK, "ScriptXtoCP leading: should return S_OK not %08x\n", hr);
ok_(__FILE__,line)(piCP == iCP, "ScriptXtoCP leading: iX=%d should return piCP=%d not %d\n", iX, iCP, piCP);
ok_(__FILE__,line)(piTrailing == 0, "ScriptXtoCP leading: iX=%d should return piTrailing=0 not %d\n", iX, piTrailing);
}
for(clusterSize = 0, iCP = 0, iX = -2, i = 0; i < cChars; i++)
{
clusterSize++;
if (offsets[i] != offsets[i+1])
{
iX = offsets[i+1]-direction;
icChars = cChars;
icGlyphs = cGlyphs;
hr = ScriptXtoCP(iX, icChars, icGlyphs, pwLogClust, psva, piAdvance, psa, &piCP, &piTrailing);
ok_(__FILE__,line)(hr == S_OK, "ScriptXtoCP trailing: should return S_OK not %08x\n", hr);
ok_(__FILE__,line)(piCP == iCP, "ScriptXtoCP trailing: iX=%d should return piCP=%d not %d\n", iX, iCP, piCP);
ok_(__FILE__,line)(piTrailing == clusterSize, "ScriptXtoCP trailing: iX=%d should return piTrailing=%d not %d\n", iX, clusterSize, piTrailing);
iCP = i+1;
clusterSize = 0;
}
}
}
static void test_ScriptXtoX(void)
/****************************************************************************************
* This routine tests the ScriptXtoCP and ScriptCPtoX functions using static variables *
@ -2446,17 +2510,28 @@ static void test_ScriptXtoX(void)
WORD pwLogClust[10] = {0, 0, 0, 1, 1, 2, 2, 3, 3, 3};
WORD pwLogClust_RTL[10] = {3, 3, 3, 2, 2, 1, 1, 0, 0, 0};
WORD pwLogClust_2[7] = {4, 3, 3, 2, 1, 0 ,0};
WORD pwLogClust_3[17] = {0, 1, 1, 1, 1, 4, 5, 6, 6, 8, 8, 8, 8, 11, 11, 13, 13};
WORD pwLogClust_3_RTL[17] = {13, 13, 11, 11, 8, 8, 8, 8, 6, 6, 5, 4, 1, 1, 1, 1, 0};
int piAdvance[10] = {201, 190, 210, 180, 170, 204, 189, 195, 212, 203};
int piAdvance_2[5] = {39, 26, 19, 17, 11};
int piAdvance_3[15] = {6, 6, 0, 0, 10, 5, 10, 0, 12, 0, 0, 9, 0, 10, 0};
static const int offsets[13] = {0, 67, 134, 201, 296, 391, 496, 601, 1052, 1503, 1954, 1954, 1954};
static const int offsets_RTL[13] = {781, 721, 661, 601, 496, 391, 296, 201, 134, 67, 0, 0, 0};
static const int offsets_2[10] = {112, 101, 92, 84, 65, 39, 19, 0, 0, 0};
SCRIPT_VISATTR psva[10];
static const int offsets_3[19] = {0, 6, 6, 6, 6, 12, 22, 27, 27, 37, 37, 37, 37, 49, 49, 58, 58, 68, 68};
static const int offsets_3_RTL[19] = {68, 68, 58, 58, 49, 49, 49, 49, 37, 37, 27, 22, 12, 12, 12, 12, 6, 6};
SCRIPT_VISATTR psva[15];
SCRIPT_ANALYSIS sa;
int iX;
SCRIPT_ITEM items[2];
int iX, i;
int piCP;
int piTrailing;
HRESULT hr;
static const WCHAR hebrW[] = { 0x5be, 0};
static const WCHAR thaiW[] = { 0xe2a, 0};
const SCRIPT_PROPERTIES **ppScriptProperties;
memset(&sa, 0 , sizeof(SCRIPT_ANALYSIS));
memset(psva, 0, sizeof(psva));
@ -2518,6 +2593,34 @@ static void test_ScriptXtoX(void)
sa.fRTL = TRUE;
test_item_ScriptXtoX(&sa, 10, 10, offsets_RTL, pwLogClust_RTL, piAdvance);
test_item_ScriptXtoX(&sa, 7, 5, offsets_2, pwLogClust_2, piAdvance_2);
/* Get thai eScript, This will do LTR and fNeedsCaretInfo */
hr = ScriptItemize(thaiW, 1, 2, NULL, NULL, items, &i);
ok(hr == S_OK, "got %08x\n", hr);
ok(i == 1, "got %d\n", i);
sa = items[0].a;
test_caret_item_ScriptXtoCP(&sa, 17, 15, offsets_3, pwLogClust_3, piAdvance_3);
/* Get hebrew eScript, This will do RTL and fNeedsCaretInfo */
hr = ScriptItemize(hebrW, 1, 2, NULL, NULL, items, &i);
ok(hr == S_OK, "got %08x\n", hr);
ok(i == 1, "got %d\n", i);
sa = items[0].a;
/* Note: This behavior CHANGED in uniscribe versions...
* so we only want to test if fNeedsCaretInfo is set */
hr = ScriptGetProperties(&ppScriptProperties, &i);
if (ppScriptProperties[sa.eScript]->fNeedsCaretInfo)
{
test_caret_item_ScriptXtoCP(&sa, 17, 15, offsets_3_RTL, pwLogClust_3_RTL, piAdvance_3);
hr = ScriptXtoCP(0, 17, 15, pwLogClust_3_RTL, psva, piAdvance_3, &sa, &piCP, &piTrailing);
ok(hr == S_OK, "ScriptXtoCP: should return S_OK not %08x\n", hr);
ok(piCP == 16, "ScriptXtoCP: iX=0 should return piCP=16 not %d\n", piCP);
ok(piTrailing == 1, "ScriptXtoCP: iX=0 should return piTrailing=1 not %d\n", piTrailing);
}
else
win_skip("Uniscribe version too old to test Hebrew clusters\n");
}
static void test_ScriptString(HDC hdc)

View File

@ -26,6 +26,7 @@
#include <stdarg.h>
#include <stdlib.h>
#include <math.h>
#include "windef.h"
#include "winbase.h"
@ -2573,9 +2574,100 @@ HRESULT WINAPI ScriptCPtoX(int iCP,
return S_OK;
}
/* Count the number of characters in a cluster and its starting index*/
static inline BOOL get_cluster_data(const WORD *pwLogClust, int cChars, int cluster_index, int *cluster_size, int *start_index)
{
int size = 0;
int i;
for (i = 0; i < cChars; i++)
{
if (pwLogClust[i] == cluster_index)
{
if (!size && start_index)
{
*start_index = i;
if (!cluster_size)
return TRUE;
}
size++;
}
else if (size) break;
}
if (cluster_size)
*cluster_size = size;
return (size > 0);
}
/*
To handle multi-glyph clusters we need to find all the glyphs that are
represented in the cluster. This involves finding the glyph whose
index is the cluster index as well as whose glyph indices are greater than
our cluster index but not part of a new cluster.
Then we sum all those glyphs' advances.
*/
static inline int get_cluster_advance(const int* piAdvance,
const SCRIPT_VISATTR *psva,
const WORD *pwLogClust, int cGlyphs,
int cChars, int cluster, int direction)
{
int glyph_start;
int glyph_end;
int i, advance;
if (direction > 0)
i = 0;
else
i = (cChars - 1);
for (glyph_start = -1, glyph_end = -1; i < cChars && i >= 0 && (glyph_start < 0 || glyph_end < 0); i+=direction)
{
if (glyph_start < 0 && pwLogClust[i] != cluster) continue;
if (pwLogClust[i] == cluster && glyph_start < 0) glyph_start = pwLogClust[i];
if (glyph_start >= 0 && glyph_end < 0 && pwLogClust[i] != cluster) glyph_end = pwLogClust[i];
}
if (glyph_end < 0)
{
if (direction > 0)
glyph_end = cGlyphs;
else
{
/* Don't fully understand multi-glyph reversed clusters yet,
* do they occur for real or just in our test? */
FIXME("multi-glyph reversed clusters found\n");
glyph_end = glyph_start + 1;
}
}
/* Check for fClusterStart, finding this generally would mean a malformed set of data */
for (i = glyph_start+1; i< glyph_end; i++)
{
if (psva[i].fClusterStart)
{
glyph_end = i;
break;
}
}
for (advance = 0, i = glyph_start; i < glyph_end; i++)
advance += piAdvance[i];
return advance;
}
/***********************************************************************
* ScriptXtoCP (USP10.@)
*
* Basic algorithm :
* use piAdvance to find the cluster we are looking at
* Find the character that is the first character of the cluster
* That is our base piCP
* If the script snaps to cluster boundries (Hebrew, Indic, Thai) then we
* are good Otherwise if the cluster is larger than 1 glyph we need to
* determine how far through the cluster to advance the cursor.
*/
HRESULT WINAPI ScriptXtoCP(int iX,
int cChars,
@ -2587,16 +2679,11 @@ HRESULT WINAPI ScriptXtoCP(int iX,
int *piCP,
int *piTrailing)
{
int item;
float iPosX;
float iLastPosX;
int iSpecial = -1;
int iCluster = -1;
int clust_size = 1;
int cjump = 0;
int advance;
float special_size = 0.0;
int direction = 1;
int iPosX;
int i;
int glyph_index, cluster_index;
int cluster_size;
TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
@ -2605,128 +2692,156 @@ HRESULT WINAPI ScriptXtoCP(int iX,
if (psa->fRTL && ! psa->fLogicalOrder)
direction = -1;
if (direction<0)
/* Handle an iX < 0 */
if (iX < 0)
{
int max_clust = pwLogClust[0];
if (iX < 0)
if (direction < 0)
{
*piCP = cChars;
*piTrailing = 0;
return S_OK;
}
else
{
*piCP = -1;
*piTrailing = 1;
}
return S_OK;
}
for (item=0; item < cChars; item++)
if (pwLogClust[item] > max_clust)
/* Looking for non-reversed clusters in a reversed string */
if (direction < 0)
{
int max_clust = pwLogClust[0];
for (i=0; i< cChars; i++)
if (pwLogClust[i] > max_clust)
{
ERR("We do not handle non reversed clusters properly\n");
FIXME("We do not handle non reversed clusters properly\n");
break;
}
}
if (iX < 0)
/* find the glyph_index based in iX */
if (direction > 0)
{
*piCP = -1;
*piTrailing = 1;
return S_OK;
for (glyph_index = -1, iPosX = iX; iPosX >=0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
;
}
else
{
for (glyph_index = -1, iPosX = iX; iPosX > 0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
;
}
iPosX = iLastPosX = 0;
if (direction > 0)
item = 0;
else
item = cChars - 1;
for (; iPosX <= iX && item < cChars && item >= 0; item+=direction)
TRACE("iPosX %i -> glyph_index %i (%i)\n", iPosX, glyph_index, cGlyphs);
*piTrailing = 0;
if (glyph_index >= 0 && glyph_index < cGlyphs)
{
iLastPosX = iPosX;
if (iSpecial == -1 &&
(iCluster == -1 ||
(iCluster != -1 &&
((direction > 0 && iCluster+clust_size <= item) ||
(direction < 0 && iCluster-clust_size >= item))
)
)
)
/* find the cluster */
if (direction > 0 )
for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] <= glyph_index; cluster_index=pwLogClust[i++])
;
else
for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] >= glyph_index; cluster_index=pwLogClust[i++])
;
TRACE("cluster_index %i\n", cluster_index);
if (direction < 0 && iPosX >= 0 && glyph_index != cluster_index)
{
int check;
int clust = pwLogClust[item];
/* We are off the end of the string */
*piCP = -1;
*piTrailing = 1;
return S_OK;
}
iCluster = -1;
cjump = 0;
clust_size = get_cluster_size(pwLogClust, cChars, item, direction,
&iCluster, &check);
advance = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, clust, direction);
get_cluster_data(pwLogClust, cChars, cluster_index, &cluster_size, &i);
if (check >= cChars && direction > 0)
TRACE("first char index %i\n",i);
if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
{
/* Check trailing */
if (glyph_index != cluster_index ||
(direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
(direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
*piTrailing = cluster_size;
}
else
{
if (cluster_size > 1)
{
int glyph;
for (glyph = clust; glyph < cGlyphs; glyph++)
special_size += get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, glyph, direction);
iSpecial = item;
special_size /= (cChars - item);
iPosX += special_size;
}
else
{
if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
/* Be part way through the glyph cluster based on size and position */
int cluster_advance = get_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, cluster_index, direction);
double cluster_part_width = cluster_advance / (float)cluster_size;
double adv;
int part_index;
/* back up to the beginning of the cluster */
for (adv = iPosX, part_index = cluster_index; part_index <= glyph_index; part_index++)
adv += piAdvance[part_index];
if (adv > iX) adv = iX;
TRACE("Multi-char cluster, no snap\n");
TRACE("cluster size %i, pre-cluster iPosX %f\n",cluster_size, adv);
TRACE("advance %i divides into %f per char\n", cluster_advance, cluster_part_width);
if (direction > 0)
{
if (!cjump)
iPosX += advance;
cjump++;
for (part_index = 0; adv >= 0; adv-=cluster_part_width, part_index++)
;
if (part_index) part_index--;
}
else
iPosX += advance / (float)clust_size;
}
}
else if (iSpecial != -1)
iPosX += special_size;
else /* (iCluster != -1) */
{
int adv = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, pwLogClust[iCluster], direction);
if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
{
if (!cjump)
iPosX += adv;
cjump++;
{
for (part_index = 0; adv > 0; adv-=cluster_part_width, part_index++)
;
if (part_index > cluster_size)
{
adv += cluster_part_width;
part_index=cluster_size;
}
}
TRACE("base_char %i part_index %i, leftover advance %f\n",i, part_index, adv);
if (direction > 0)
i += part_index;
else
i += (cluster_size - part_index);
/* Check trailing */
if ((direction > 0 && fabs(adv) <= (cluster_part_width / 2.0)) ||
(direction < 0 && adv && fabs(adv) >= (cluster_part_width / 2.0)))
*piTrailing = 1;
}
else
iPosX += adv / (float)clust_size;
{
/* Check trailing */
if ((direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
(direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
*piTrailing = 1;
}
}
}
if (direction > 0)
{
if (iPosX > iX)
item--;
if (item < cChars && ((iPosX - iLastPosX) / 2.0) + iX >= iPosX)
{
if (scriptInformation[psa->eScript].props.fNeedsCaretInfo && clust_size > 1)
item+=(clust_size-1);
*piTrailing = 1;
}
else
*piTrailing = 0;
}
else
{
if (iX == iLastPosX)
item++;
if (iX >= iLastPosX && iX <= iPosX)
item++;
TRACE("Point falls outside of string\n");
if (glyph_index < 0)
i = cChars-1;
else /* (glyph_index >= cGlyphs) */
i = cChars;
if (iLastPosX == iX)
*piTrailing = 0;
else if (item < 0 || ((iLastPosX - iPosX) / 2.0) + iX <= iLastPosX)
/* If not snaping in the reverse direction (such as Hebrew) Then 0
point flow to the next character */
if (direction < 0)
{
if (scriptInformation[psa->eScript].props.fNeedsCaretInfo && clust_size > 1)
item-=(clust_size-1);
*piTrailing = 1;
if (!scriptInformation[psa->eScript].props.fNeedsCaretInfo && abs(iPosX) == piAdvance[glyph_index])
i++;
else
*piTrailing = 1;
}
else
*piTrailing = 0;
}
*piCP = item;
*piCP = i;
TRACE("*piCP=%d\n", *piCP);
TRACE("*piTrailing=%d\n", *piTrailing);