Exemplo n.º 1
0
        public IEnumerable <NoForms.Common.Rectangle> HitTextRange(UTextGlyphingInfo ti, int start, int length, NoForms.Common.Point offset)
        {
            int cc = 0;

            foreach (var glyph in ti.glyphRuns)
            {
                // stride through glyphruns that are not involved.
                if (cc + glyph.run.runLength <= start)
                {
                    cc += glyph.run.runLength;
                    continue;
                }
                float cx = 0;
                foreach (var gchar in glyph.run.charSizes)
                {
                    // end iteration when we fall past this bound
                    if (cc >= start + length)
                    {
                        yield break;
                    }

                    // calculate and yield the current char rect
                    yield return(new NoForms.Common.Rectangle(glyph.location.X + cx,
                                                              glyph.location.Y + (glyph.run.runSize.height - gchar.height),
                                                              gchar.width,
                                                              gchar.height));

                    // inc counters for following glyphs
                    cx += gchar.width;
                }
            }
        }
Exemplo n.º 2
0
        public NoForms.Common.Point HitText(UTextGlyphingInfo ti, int pos, bool trailing)
        {
            // Find the hit glyphrun
            int g, cc = 0;

            for (g = 0; g < ti.glyphRuns.Count; g++)
            {
                if ((cc += ti.glyphRuns[g].run.runLength) > pos)
                {
                    break;
                }
            }
            if (g == ti.glyphRuns.Count)
            {
                g--;
            }
            Point retloc = new Point(0, 0);

            if (g > 0)
            {
                retloc = ti.glyphRuns[g].location;
                float cx = 0;
                int   i  = 0;
                cc -= ti.glyphRuns[g].run.runLength;

                // Get x-location of hit char in glyph
                for (i = 0; i < pos - cc; i++)
                {
                    cx += ti.glyphRuns[g].run.charSizes[i].width;
                }
                if (i == ti.glyphRuns[g].run.charSizes.Length)
                {
                    i--;
                }
                if (trailing)
                {
                    cx -= ti.glyphRuns[g].run.charSizes[i].width;
                }

                // Get any y-offset of hit char in the glyph and return
                retloc += new Point(cx, ti.glyphRuns[g].run.runSize.height - ti.glyphRuns[g].run.charSizes[i].height);
            }
            // return retloc
            return(retloc);
        }
Exemplo n.º 3
0
        // WARNING this all assumes that char rects and lines are "tightly packed", except  differing font sizes
        //         on same line, which get "baselined".  Is this what really happens?  What about line,word and char spacing adjustments?
        //         dirty way would be to adjust char rects, but does SDG do that? (does it even support that?)
        // Text Measuring - isText tells you if you actually hit a part of the string...
        public UTextHitInfo HitPoint(UTextGlyphingInfo ti, Point hitPoint)
        {
            int charPos = 0;

            // points
            float hx = hitPoint.X, cx;
            float hy = hitPoint.Y, cy;

            // Find the line we're hitting on
            int hitLine = 0;

            cy = 0;
            for (hitLine = 0; hitLine < ti.lineSizes.Count; hitLine++)
            {
                if (hy >= cy && hy < (cy += ti.lineSizes[hitLine].height))
                {
                    break;
                }
                charPos += ti.lineLengths[hitLine];
            }

            // Get glyphruns on this line
            int sgr = -1, egr = -1;

            for (int gr = 0; gr < ti.glyphRuns.Count; gr++)
            {
                if (ti.glyphRuns[gr].lineNumber == hitLine)
                {
                    if (sgr == -1)
                    {
                        sgr = gr;
                    }
                    else if (sgr != -1)
                    {
                        egr = gr;                 // WARNING this is first on next line (will be loopy on i<egr, so is ok)
                    }
                }
            }

            // Find the glyphrun we're hitting on (in x direction)
            int hitGlyph = sgr;
            cx = 0;
            for (hitGlyph = sgr; hitGlyph < egr; hitGlyph++)
            {
                if (hx >= cx && hx < (cx += ti.glyphRuns[hitGlyph].run.runSize.width))
                {
                    break;
                }
                charPos += ti.glyphRuns[hitGlyph].run.content.Length;
            }
            cx -= ti.glyphRuns[hitGlyph].run.runSize.width; // reset to start of glyphrun hit

            // find the intersecting char rect (x direction)
            int hitGlyphChar;
            var hgr = ti.glyphRuns[hitGlyph].run;
            for (hitGlyphChar = 0; hitGlyphChar < hgr.charSizes.Length; hitGlyphChar++)
            {
                if (hx >= cx && hx < (cx += hgr.charSizes[hitGlyphChar].width))
                {
                    break;
                }
            }

            // we went all way to end...so come back
            if (hitGlyphChar == hgr.charSizes.Length)
            {
                hitGlyphChar--;
            }

            // add on the x inside this glyphrun
            charPos += hitGlyphChar;
            cx      -= hgr.charSizes[hitGlyphChar].width; // reset to start of char.

            // determine if we've hit text, done simply by checking if we are inside the hit glyph
            Point charlocation = ti.glyphRuns[hitGlyph].location + new Point(cx, hgr.runSize.height - hgr.charSizes[hitGlyphChar].height);
            Rectangle charRect = new Rectangle(charlocation.X, charlocation.Y, hgr.charSizes[hitGlyphChar].width, hgr.charSizes[hitGlyphChar].height);
            bool isText        = charRect.Contains(hitPoint);

            // Determine trailing or leading hit
            bool leading = hitPoint.X > cx + charRect.width / 2;

            return(new UTextHitInfo(charPos, leading, isText));
        }
Exemplo n.º 4
0
        // FIXME Cache!! (most importantly)
        public UTextGlyphingInfo GetTextInfo(UText text)
        {
            // gotta do a line or end before we can decide the char tops (baseline aligned, presumably...)
            float             currX = 0, currY = 0, maxGlyphHeight = 0;
            int               lglst         = 0;  // last glyphrun line start
            int               lastWordBreak = -1; // last wordbreaking glyph on the current line
            int               currLine      = 0;
            UTextGlyphingInfo ret           = new UTextGlyphingInfo();

            // begin on assumption we're top left align..then correct after
            UGlyphRun lastGr = null;

            foreach (var gr in GetGlyphRuns(text))
            {
                // tracking max height for the line baselineing
                maxGlyphHeight = Math.Max(gr.runSize.height, maxGlyphHeight);
                // whichever way, this line is this many chars longer

                // Potential runs include words, spaces and linebreaks, and font breaks
                if (gr.breakingType == BreakType.line)
                {//LineBreaking
                    // add the glyphrun, resolve info for the line, and begin on new line!
                    ret.glyphRuns.Add(new UGlyphRunLayoutInfo()
                    {
                        lineNumber = currLine,
                        location   = new Point(currX, 0), // dunno about y position yet
                        run        = gr
                    });

                    currY         += maxGlyphHeight;
                    maxGlyphHeight = 0;
                    lastWordBreak  = -1;
                    float xAlighnShifty = GetShift(text.width, (currX + gr.runSize.width), text.halign);
                    currX = 0;

                    // resolving the glyphruns of this line
                    do
                    {
                        var igr = ret.glyphRuns[lglst];
                        igr.location = new Point(igr.location.X + xAlighnShifty, currY - igr.run.runSize.height);
                    } while (++lglst < ret.glyphRuns.Count);

                    // begin a new line
                    currLine++;
                }
                else if (lastWordBreak > -1 && currX + gr.runSize.width > text.width && text.wrapped)
                {// WordWrapping
                    // Must define the concept of glyph groups here.  Those between line and/or word breaks.
                    // The whole of such a group must be broken.  We need to define the breaking glyph.
                    currY         += maxGlyphHeight;
                    maxGlyphHeight = 0;
                    currX          = 0;
                    // #1 Is there a word break previous to this glyphrun on this line?
                    //    then put all glyphs following that on the next line.
                    for (int i = lastWordBreak + 1; i < ret.glyphRuns.Count; i++)
                    {
                        ret.glyphRuns[i].lineNumber++;
                        ret.glyphRuns[i].location = new Point(currX, 0);
                    }
                    lastWordBreak = -1;
                    currLine++;

                    // Is this glyphrun a wordbreak? who cares, next iteration will take care via #1.
                    // Not wordbreak? no prev worbreak? who cares, carry on.
                    ret.glyphRuns.Add(new UGlyphRunLayoutInfo()
                    {
                        lineNumber = currLine,
                        location   = new Point(currX, 0), // dunno about y position yet
                        run        = gr
                    });

                    // resolving the glyphruns of the last line
                    do
                    {
                        var igr = ret.glyphRuns[lglst];
                        igr.location = new Point(igr.location.X, currY - igr.run.runSize.height);
                    } while (++lglst <= lastWordBreak);
                    lastGr = gr;
                }
                else
                {// Buisness as Normal
                    // add glyphrun, increment currX
                    ret.glyphRuns.Add(new UGlyphRunLayoutInfo()
                    {
                        lineNumber = currLine,
                        location   = new Point(currX, 0),
                        run        = gr
                    });
                    if (gr.breakingType == BreakType.word)
                    {
                        lastWordBreak = ret.glyphRuns.Count - 1;
                    }
                    currX += gr.runSize.width;
                }
            }
            currY         += maxGlyphHeight;
            maxGlyphHeight = 0;
            lastWordBreak  = -1;
            float lastXShift = GetShift(text.width, (currX + (lastGr == null ? 0 :lastGr.runSize.width)), text.halign);

            currX = 0;
            // resolving the glyphruns of the final line
            for (; lglst < ret.glyphRuns.Count; lglst++)
            {
                var igr = ret.glyphRuns[lglst];
                igr.location.Y  = currY - igr.run.runSize.height;
                igr.location.X += lastXShift;
            }

            float yAlignShifty = GetShift(text.height, currY, text.valign);

            // assign the linelengths to textinfo and do y alignment
            int   cl = 0; int cc = 0; int cnl = 0;
            float cx = 0, cy = 0, maxy = 0, maxx = 0;

            for (int i = 0; i < ret.glyphRuns.Count; i++)
            {
                var gri = ret.glyphRuns[i];
                gri.location.Y += yAlignShifty;
                if (gri.lineNumber > cl)
                {
                    // add lineinfo (and reset counters)
                    cl = gri.lineNumber;
                    ret.lineLengths.Add(cc);
                    ret.newLineLengths.Add(cl);
                    ret.lineSizes.Add(new Size(cx, maxy));
                    cc   = cnl = 0;
                    cy  += maxy;
                    maxx = Math.Max(maxx, cx);
                    maxy = cx = 0;
                }

                // Continue adding lineinfo data
                cc += gri.run.runLength;
                cx += gri.run.runSize.width;
                if (gri.run.breakingType == BreakType.line)
                {
                    cnl += gri.run.runLength;
                }
                maxy = Math.Max(gri.run.runSize.height, maxy);
            }
            ret.minSize = new Size(maxx, cy);
            return(ret);
        }