~ubuntu-branches/ubuntu/hardy/libgdiplus/hardy

« back to all changes in this revision

Viewing changes to src/graphics-path.c

  • Committer: Bazaar Package Importer
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2007-12-18 13:08:10 UTC
  • mfrom: (1.1.15 upstream)
  • Revision ID: james.westby@ubuntu.com-20071218130810-hlmitxfddf6h511j
Tags: 1.2.6-1ubuntu1
* Sync with Debian:
  - debian/control:
    + Add lpia and sparc to the architectures. We support them.
    + Change Maintainer to Ubuntu Mono Team.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 *      Duncan Mak (duncan@ximian.com)
23
23
 *      Ravindra (rkumar@novell.com)
24
24
 *      Sebastien Pouliot  <sebastien@ximian.com>
25
 
 *
26
 
 * Additional copyrights:
27
 
 *      gdip_point_in_polygon is based on INPOLY.C (http://www.visibone.com/inpoly/inpoly.c.txt)
28
 
 *      by Bob Stein (stein@visibone.com) & Craig Yap (craig@cse.fau.edu)
29
 
 *      Copyright (c) 1995-1996 Galacticomm, Inc.  Freeware source code.
30
25
 */
31
26
 
32
27
#include "graphics-path-private.h"
33
28
#include "matrix-private.h"
34
29
#include "font-private.h"
 
30
#include "graphics-cairo-private.h"
 
31
 
 
32
#ifdef USE_PANGO_RENDERING
 
33
        #include "text-pango-private.h"
 
34
#endif
35
35
 
36
36
static GArray *
37
37
array_to_g_array (const GpPointF *pt, int length)
459
459
                return InvalidParameter;
460
460
 
461
461
        if (path->count > 0) {
462
 
                BYTE current = g_array_index (path->types, BYTE, path->count - 1);
463
 
                g_byte_array_remove_index (path->types, path->count - 1);
464
 
                current |= PathPointTypeCloseSubpath;
465
 
                g_byte_array_append (path->types, &current, 1);
 
462
                BYTE *last = &g_array_index (path->types, BYTE, path->count - 1);
 
463
                *last |= PathPointTypeCloseSubpath;
466
464
        }
467
465
        path->start_new_fig = TRUE;
468
466
 
569
567
        return Ok;
570
568
}
571
569
 
 
570
/*
 
571
 * Append old_types[start, end] to new_types, adjusting flags.
 
572
 */
 
573
static void
 
574
reverse_subpath_adjust_flags (int start, int end, GByteArray *old_types, GByteArray *new_types, BOOL *prev_had_marker)
 
575
{
 
576
        BYTE t, prev_first, prev_last;
 
577
        int i;
 
578
        
 
579
        /* Copy all but PathPointTypeStart */
 
580
        if (end != start)
 
581
                g_byte_array_append (new_types, old_types->data + start + 1, end - start);
 
582
        
 
583
        /* Append PathPointTypeStart */
 
584
        t = PathPointTypeStart;
 
585
        g_byte_array_append (new_types, &t, 1);
 
586
        
 
587
        g_assert (new_types->len == end + 1);
 
588
        
 
589
        prev_first = g_array_index (old_types, BYTE, start);
 
590
        prev_last = g_array_index (old_types, BYTE, end);
 
591
        
 
592
        /* Remove potential flags from our future start point */
 
593
        if (end != start)
 
594
                new_types->data[end - 1] &= PathPointTypePathTypeMask;
 
595
        /* Set the flags on our to-be-last point */
 
596
        if (prev_last & PathPointTypeDashMode)
 
597
                new_types->data[start] |= PathPointTypeDashMode;
 
598
        if (prev_last & PathPointTypeCloseSubpath)
 
599
                new_types->data[start] |= PathPointTypeCloseSubpath;
 
600
        
 
601
        /*
 
602
         * Swap markers
 
603
         */
 
604
        for (i = start + 1; i < end; i++) {
 
605
                if (g_array_index (old_types, BYTE, i - 1) & PathPointTypePathMarker)
 
606
                        new_types->data[i] |= PathPointTypePathMarker;
 
607
                else
 
608
                        new_types->data[i] &= ~PathPointTypePathMarker;
 
609
        }
 
610
        
 
611
        /* If the last point of the previous subpath had a marker, we inherit it */
 
612
        if (*prev_had_marker)
 
613
                new_types->data[start] |= PathPointTypePathMarker;
 
614
        else
 
615
                new_types->data[start] &= ~PathPointTypePathMarker;
 
616
        
 
617
        *prev_had_marker = ((prev_last & PathPointTypePathMarker) == PathPointTypePathMarker);
 
618
}
 
619
 
572
620
GpStatus
573
621
GdipReversePath (GpPath *path)
574
622
{
575
623
        int length, i;
576
624
        GArray *points;
 
625
        GByteArray *types;
 
626
        int start = 0;
 
627
        BOOL prev_had_marker = FALSE;
577
628
 
578
629
        if (!path)
579
630
                return InvalidParameter;
580
631
 
581
632
        length = path->count;
582
 
        /* shortcut to avoid allocations */
 
633
        /* shortcut */
583
634
        if (length <= 1)
584
635
                return Ok;
585
636
 
586
 
        /* NOTE: PathTypes are NOT reversed */
 
637
        /* PathTypes reversal */
587
638
 
588
 
        /* FIXME: this could be done without allocations */
589
 
        points = g_array_sized_new (FALSE, TRUE, sizeof (GpPointF), length);
590
 
        if (!points)
 
639
        /* First adjust the flags for each subpath */
 
640
        types = g_byte_array_sized_new (length);
 
641
        if (!types)
591
642
                return OutOfMemory;
592
643
 
593
 
        for (i = length - 1; i >= 0; i--) {
594
 
                GpPointF pt = g_array_index (path->points, GpPointF, i);
595
 
                g_array_append_val (points, pt);
 
644
        for (i = 1; i < length; i++) {
 
645
                BYTE t = g_array_index (path->types, BYTE, i);
 
646
                if ((t & PathPointTypePathTypeMask) == PathPointTypeStart) {
 
647
                        reverse_subpath_adjust_flags (start, i - 1, path->types, types, &prev_had_marker);
 
648
                        start = i;
 
649
                }
 
650
        }
 
651
        if (start < length - 1)
 
652
                reverse_subpath_adjust_flags (start, length - 1, path->types, types, &prev_had_marker);
 
653
 
 
654
        /* Then reverse the resulting array */
 
655
        for (i = 0; i < (length >> 1); i++) {
 
656
                BYTE *a = &g_array_index (types, BYTE, i);
 
657
                BYTE *b = &g_array_index (types, BYTE, length - i - 1);
 
658
                BYTE temp = *a;
 
659
                *a = *b;
 
660
                *b = temp;
 
661
        }
 
662
        g_byte_array_free (path->types, TRUE);
 
663
        path->types = types;
 
664
 
 
665
        /* PathPoints reversal
 
666
         * note: if length is odd then the middle point doesn't need to switch side
 
667
         */
 
668
        for (i = 0; i < (length >> 1); i++) {
 
669
                GpPointF *first = &g_array_index (path->points, GpPointF, i);
 
670
                GpPointF *last = &g_array_index (path->points, GpPointF, length - i - 1);
 
671
 
 
672
                GpPointF temp;
 
673
                temp.X = first->X;
 
674
                temp.Y = first->Y;
 
675
                first->X = last->X;
 
676
                first->Y = last->Y;
 
677
                last->X = temp.X;
 
678
                last->Y = temp.Y;
596
679
        }
597
 
        g_array_free (path->points, TRUE);
598
 
        path->points = points;
599
680
        
600
681
        return Ok;
601
682
}
669
750
        }
670
751
 
671
752
        delta = beta - alpha;
 
753
        // http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
672
754
        bcp = 4.0 / 3 * (1 - cos (delta / 2)) / sin (delta / 2);
673
755
 
674
756
        sin_alpha = sin (alpha);
676
758
        cos_alpha = cos (alpha);
677
759
        cos_beta = cos (beta);
678
760
 
679
 
        /* starting point */
680
 
        sx = cx + rx * cos_alpha;
681
 
        sy = cy + ry * sin_alpha;
682
 
        
683
 
        /* move to the starting point if we're not continuing a curve */
684
 
        if (start)
685
 
                append (path, sx, sy, PathPointTypeLine);
 
761
        /* move to the starting point if we're not continuing a curve */
 
762
        if (start) {
 
763
                /* starting point */
 
764
                sx = cx + rx * cos_alpha;
 
765
                sy = cy + ry * sin_alpha;
 
766
                append (path, sx, sy, PathPointTypeLine);
 
767
        }
686
768
 
687
769
        append_bezier (path, 
688
770
                cx + rx * (cos_alpha - bcp * sin_alpha),
900
982
GpStatus
901
983
GdipAddPathEllipse (GpPath *path, float x, float y, float width, float height)
902
984
{
903
 
        float C1 = 0.552285;
904
985
        double rx = width / 2;
905
986
        double ry = height / 2;
906
987
        double cx = x + rx;
1104
1185
                return status;
1105
1186
        }
1106
1187
 
1107
 
        cairo_set_font_face (cr, font->cairofnt);
 
1188
        if (layoutRect)
 
1189
                cairo_move_to (cr, layoutRect->X, layoutRect->Y + font->sizeInPixels);
 
1190
 
 
1191
#ifdef USE_PANGO_RENDERING
 
1192
        {
 
1193
        GpRectF box;
 
1194
        PangoLayout* layout; 
 
1195
 
 
1196
        cairo_save (cr);
 
1197
        layout = gdip_pango_setup_layout (cr, string, length, font, layoutRect, &box, format);
 
1198
        pango_cairo_layout_path (cr, layout);
 
1199
        g_object_unref (layout);
 
1200
        cairo_restore (cr);
 
1201
        }
 
1202
#else
 
1203
        cairo_set_font_face (cr, gdip_get_cairo_font_face (font));
1108
1204
        cairo_set_font_size (cr, font->sizeInPixels);
1109
 
 
1110
 
#if FALSE
1111
 
WCHAR name[LF_FACESIZE];
1112
 
GdipGetFamilyName (family, name, 0);
1113
 
g_warning ("GdipAddString \"%s\" (family: %s, size %g)", utf8, ucs2_to_utf8 ((const gunichar2 *)name, -1), font->sizeInPixels);
1114
 
#endif
1115
1205
        /* TODO - deal with layoutRect, format... ideally we would be calling a subset
1116
1206
           of GdipDrawString that already does everything *and* preserve the whole path */
1117
 
        if (layoutRect)
1118
 
                cairo_move_to (cr, layoutRect->X, layoutRect->Y + font->sizeInPixels);
1119
1207
        cairo_text_path (cr, (const char*)utf8);
 
1208
#endif
1120
1209
 
1121
1210
        /* get the font data from the cairo path and translate it as a gdi+ path */
1122
1211
        cp = cairo_copy_path (cr);
1808
1897
        return Ok;
1809
1898
}
1810
1899
 
1811
 
/* adapted from http://www.visibone.com/inpoly/inpoly.c.txt */
1812
 
static BOOL
1813
 
gdip_point_in_polygon (GpPath *path, int start, int end, float x, float y)
1814
 
{
1815
 
        GpPointF old, new;
1816
 
        float x1, y1, x2, y2;
1817
 
        int x0 = iround (x);
1818
 
        int y0 = iround (y);
1819
 
        BOOL inside = FALSE;
1820
 
        int npoints = end - start + 1;
1821
 
        int i;
1822
 
 
1823
 
        if (npoints < 3)
1824
 
                return FALSE; /* not a polygon */
1825
 
 
1826
 
        old = g_array_index (path->points, GpPointF, end);
1827
 
        for (i = 0; i < npoints ; i++) {
1828
 
                new = g_array_index (path->points, GpPointF, start + i);
1829
 
                if (new.X > old.X) {
1830
 
                        x1 = old.X;
1831
 
                        x2 = new.X;
1832
 
                        y1 = old.Y;
1833
 
                        y2 = new.Y;
1834
 
                } else {
1835
 
                        x1 = new.X;
1836
 
                        x2 = old.X;
1837
 
                        y1 = new.Y;
1838
 
                        y2 = old.Y;
1839
 
                }
1840
 
 
1841
 
                if ((new.X <= x0) == (x0 < old.X) && ((y0 - y1) * (x2 - x1) < (y2 - y1) * (x0 - x1))) {
1842
 
                        inside = !inside;
1843
 
                }
1844
 
                old = new;
1845
 
        }
1846
 
        return inside;
1847
 
}
1848
 
 
1849
 
/* MonoTODO - GpGraphics is ignored */
1850
1900
GpStatus 
1851
1901
GdipIsVisiblePathPoint (GpPath *path, float x, float y, GpGraphics *graphics, BOOL *result)
1852
1902
{
1853
1903
        GpStatus status = Ok;
1854
 
        GpPath *workpath = NULL;
1855
 
        int start, end;
 
1904
        cairo_surface_t* s = NULL;
 
1905
        GpGraphics *g;
 
1906
        GpUnit page_unit = UnitPixel;
1856
1907
 
1857
1908
        if (!path || !result)
1858
1909
                return InvalidParameter;
1859
1910
 
1860
 
        *result = FALSE;
1861
 
 
1862
 
        /* we clone the supplied path if it contains curves (we only deal with lines) */
1863
 
        if (gdip_path_has_curve (path)) {
1864
 
                status = GdipClonePath (path, &workpath);
1865
 
                if (status != Ok) {
1866
 
                        if (workpath)
1867
 
                                GdipDeletePath (workpath);
1868
 
                        return status;
1869
 
                }
1870
 
 
1871
 
                status = GdipFlattenPath (workpath, NULL, 25.0f);
1872
 
        } else {
1873
 
                workpath = path;
1874
 
        }
1875
 
 
1876
 
        if (graphics) {
1877
 
                /* FIXME - graphics isn't always ignored, e.g. when we set the matrix, pageunit and pagescale */
1878
 
        }
1879
 
 
1880
 
        /* there may be multiple polygons inside a path */
1881
 
        for (start = 0, end = 0; end < workpath->count && !*result; end++) {
1882
 
                BYTE type = g_array_index (workpath->types, BYTE, end);
1883
 
                if (type & PathPointTypeCloseSubpath) {
1884
 
                        *result = gdip_point_in_polygon (workpath, start, end, x, y);
1885
 
                } else if (type == PathPointTypeStart) {
1886
 
                        /* reset the start index */
1887
 
                        start = end;
1888
 
                }
1889
 
        }
1890
 
 
1891
 
        if (workpath != path)
1892
 
                GdipDeletePath (workpath);
 
1911
        if (graphics) {
 
1912
                g = graphics;
 
1913
                cairo_save (g->ct);
 
1914
                page_unit = g->page_unit;
 
1915
        } else {
 
1916
                /* create a temporary context */
 
1917
                s = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
 
1918
                g = gdip_graphics_new (s);
 
1919
        }
 
1920
 
 
1921
        cairo_new_path (g->ct);
 
1922
        /* unit tests shows that PageUnit isn't consireded (well x, y are probably considered to be the same unit format ) */
 
1923
        g->page_unit = UnitPixel;
 
1924
        status = gdip_plot_path (g, path, FALSE);
 
1925
        if (status == Ok) {
 
1926
                cairo_set_fill_rule (g->ct, gdip_convert_fill_mode (path->fill_mode));
 
1927
                cairo_set_antialias (g->ct, CAIRO_ANTIALIAS_NONE);
 
1928
                *result = cairo_in_fill (g->ct, x + CAIRO_AA_OFFSET_X, y + CAIRO_AA_OFFSET_Y);
 
1929
        } else {
 
1930
                *result = FALSE;
 
1931
        }
 
1932
 
 
1933
        if (graphics) {
 
1934
                /* restore GpGraphics to original state */
 
1935
                cairo_restore (graphics->ct);
 
1936
                g->page_unit = page_unit;
 
1937
        } else {
 
1938
                /* delete temporary context */
 
1939
                cairo_surface_destroy (s);
 
1940
                GdipDeleteGraphics (g);
 
1941
        }
 
1942
 
1893
1943
        return status;
1894
1944
}
1895
1945
 
1896
 
/* MonoTODO - GpGraphics is ignored */
1897
1946
GpStatus 
1898
1947
GdipIsVisiblePathPointI (GpPath *path, int x, int y, GpGraphics *graphics, BOOL *result)
1899
1948
{
1900
1949
        return GdipIsVisiblePathPoint (path, x, y, graphics, result);
1901
1950
}
1902
1951
 
1903
 
static BOOL
1904
 
gdip_check_point_within_distance (float x0, float y0, GpPointF *p1, GpPointF *p2, float distance)
1905
 
{
1906
 
        float x1 = p1->X;
1907
 
        float y1 = p1->Y;
1908
 
        float x2 = p2->X;
1909
 
        float y2 = p2->Y;
1910
 
        float x2x1, y2y1;
1911
 
 
1912
 
        /* quick out (to avoid heavy calculations) for out of range points */
1913
 
        if ((x0 < min (x1, x2) - distance) || (x0 > max (x1, x2) + distance) ||
1914
 
                (y0 < min (y1, y2) - distance) || (y0 > max (y1, y2) + distance))
1915
 
                return FALSE;
1916
 
 
1917
 
        /* close enough, do the full math */
1918
 
        x2x1 = x2 - x1;
1919
 
        y2y1 = y2 - y1;
1920
 
        /* if the provided line is a point (simpler calculation and avoids a division by zero) */
1921
 
        if ((x2x1 == 0.0) && (y2y1 == 0.0)) {
1922
 
                /* check distance between two points */
1923
 
                /* ref: http://mathworld.wolfram.com/Point-PointDistance2-Dimensional.html */
1924
 
                float x1x0 = x1 - x0;
1925
 
                float y1y0 = y1 - y0;
1926
 
                return (sqrt ((x1x0 * x1x0) + (y1y0 * y1y0)) <= distance);
1927
 
        } else {
1928
 
                /* normal case: distance of a point to a line */
1929
 
                /* ref: http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html */
1930
 
                float d1 = fabs ((x2x1 * (y1 - y0)) - ((x1 - x0) * y2y1));
1931
 
                float d2 = sqrt ((x2x1 * x2x1) + (y2y1 * y2y1));
1932
 
                return (d1 / d2 <= distance);
1933
 
        }
1934
 
}
1935
 
 
1936
 
/* MonoTODO - GpGraphics is ignored */
1937
1952
GpStatus 
1938
1953
GdipIsOutlineVisiblePathPoint (GpPath *path, float x, float y, GpPen *pen, GpGraphics *graphics, BOOL *result)
1939
1954
{
1940
1955
        GpStatus status = Ok;
1941
 
        GpPath *workpath = NULL;
 
1956
        cairo_surface_t* s = NULL;
 
1957
        GpGraphics *g;
 
1958
        GpUnit page_unit = UnitPixel;
1942
1959
 
1943
1960
        if (!path || !pen || !result)
1944
1961
                return InvalidParameter;
1945
1962
 
1946
 
        *result = FALSE;
1947
 
 
1948
 
        if (path->count < 2) {
1949
 
                /* FIXME - equality check ? */
1950
 
                return Ok;
1951
 
        }
1952
 
 
1953
 
        /* we clone the supplied path if it contains curves (we only deal with lines) */
1954
 
        if (gdip_path_has_curve (path)) {
1955
 
                status = GdipClonePath (path, &workpath);
1956
 
                if (status != Ok) {
1957
 
                        if (workpath)
1958
 
                                GdipDeletePath (workpath);
1959
 
                        return status;
1960
 
                }
1961
 
 
1962
 
                status = GdipFlattenPath (workpath, NULL, 25.0f);
 
1963
        if (graphics) {
 
1964
                g = graphics;
 
1965
                cairo_save (graphics->ct);
 
1966
                page_unit = g->page_unit;
1963
1967
        } else {
1964
 
                workpath = path;
1965
 
        }
1966
 
 
1967
 
        if (graphics) {
1968
 
                /* FIXME - graphics isn't always ignored, e.g. when we set the matrix, pageunit and pagescale */
1969
 
        }
1970
 
 
 
1968
                /* create a temporary context */
 
1969
                s = cairo_image_surface_create (CAIRO_FORMAT_A1, 1, 1);
 
1970
                g = gdip_graphics_new (s);
 
1971
        }
 
1972
 
 
1973
        cairo_new_path (g->ct);
 
1974
        /* unit tests shows that PageUnit isn't consireded (well x, y are probably considered to be the same unit format ) */
 
1975
        g->page_unit = UnitPixel;
 
1976
        status = gdip_plot_path (g, path, FALSE);
1971
1977
        if (status == Ok) {
1972
 
                /* check if the supplied point is within half the pen's width of any path segment */
1973
 
                float half_width = pen->width / 2;
1974
 
                int start_index = 0;
1975
 
                int i;
1976
 
                BYTE type = 0;
1977
 
 
1978
 
                GpPointF p1 = g_array_index (workpath->points, GpPointF, 0);
1979
 
                GpPointF p2;
1980
 
 
1981
 
                for (i = 1; i < path->count && !*result; i++) {
1982
 
                        /* check the line between the previous point and this point */
1983
 
                        p2 = g_array_index (workpath->points, GpPointF, i);
1984
 
                        *result = gdip_check_point_within_distance (x, y, &p1, &p2, half_width);
1985
 
 
1986
 
                        /* check for closure (to match with the last starting point) */
1987
 
                        type = g_array_index (path->types, BYTE, i);
1988
 
                        if (!*result && (type & PathPointTypeCloseSubpath)) {
1989
 
                                p1 = g_array_index (workpath->points, GpPointF, start_index);
1990
 
                                /* compare last point with first (if the path is closed) */
1991
 
                                *result = gdip_check_point_within_distance (x, y, &p2, &p1, half_width);
1992
 
                        }
1993
 
 
1994
 
                        /* switch point for the next line */
1995
 
                        p1 = p2;
1996
 
 
1997
 
                        /* reset the start index */
1998
 
                        if (type == PathPointTypeStart)
1999
 
                                start_index = i;
2000
 
                }
2001
 
        }
2002
 
 
2003
 
        if (workpath != path)
2004
 
                GdipDeletePath (workpath);
 
1978
                /* we must fight around cairo AA */
 
1979
                cairo_set_antialias (g->ct, CAIRO_ANTIALIAS_NONE);
 
1980
                cairo_set_line_width (g->ct, pen->width - CAIRO_AA_OFFSET_Y);
 
1981
                *result = cairo_in_stroke (g->ct, x, y);
 
1982
        } else {
 
1983
                *result = FALSE;
 
1984
        }
 
1985
 
 
1986
        if (graphics) {
 
1987
                /* restore GpGraphics to original state */
 
1988
                cairo_restore (graphics->ct);
 
1989
                g->page_unit = page_unit;
 
1990
        } else {
 
1991
                /* delete temporary context */
 
1992
                cairo_surface_destroy (s);
 
1993
                GdipDeleteGraphics (g);
 
1994
        }
 
1995
 
2005
1996
        return status;
2006
1997
}
2007
1998
 
2008
 
/* MonoTODO - GpGraphics is ignored */
2009
1999
GpStatus 
2010
2000
GdipIsOutlineVisiblePathPointI (GpPath *path, int x, int y, GpPen *pen, GpGraphics *graphics, BOOL *result)
2011
2001
{
2012
2002
        return GdipIsOutlineVisiblePathPoint (path, x, y, pen, graphics, result);
2013
2003
}
 
2004