1 /*
2 * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
1841 return false;
1842 }
1843 if (transformState > TRANSFORM_INT_TRANSLATE) {
1844 // Note: Technically the most accurate test would be to
1845 // raster scan the parallelogram of the transformed rectangle
1846 // and do a span for span hit test against the clip, but for
1847 // speed we approximate the test with a bounding box of the
1848 // transformed rectangle. The cost of rasterizing the
1849 // transformed rectangle is probably high enough that it is
1850 // not worth doing so to save the caller from having to call
1851 // a rendering method where we will end up discovering the
1852 // same answer in about the same amount of time anyway.
1853 // This logic breaks down if this hit test is being performed
1854 // on the bounds of a group of shapes in which case it might
1855 // be beneficial to be a little more accurate to avoid lots
1856 // of subsequent rendering calls. In either case, this relaxed
1857 // test should not be significantly less accurate than the
1858 // optimal test for most transforms and so the conservative
1859 // answer should not cause too much extra work.
1860
1861 double d[] = {
1862 x, y,
1863 x+width, y,
1864 x, y+height,
1865 x+width, y+height
1866 };
1867 transform.transform(d, 0, d, 0, 4);
1868 x = (int) Math.floor(Math.min(Math.min(d[0], d[2]),
1869 Math.min(d[4], d[6])));
1870 y = (int) Math.floor(Math.min(Math.min(d[1], d[3]),
1871 Math.min(d[5], d[7])));
1872 width = (int) Math.ceil(Math.max(Math.max(d[0], d[2]),
1873 Math.max(d[4], d[6])));
1874 height = (int) Math.ceil(Math.max(Math.max(d[1], d[3]),
1875 Math.max(d[5], d[7])));
1876 } else {
1877 x += transX;
1878 y += transY;
1879 width += x;
1880 height += y;
1881 }
1888 return false;
1889 }
1890 // REMIND: We could go one step further here and examine the
1891 // non-rectangular clip shape more closely if there is one.
1892 // Since the clip has already been rasterized, the performance
1893 // penalty of doing the scan is probably still within the bounds
1894 // of a good tradeoff between speed and quality of the answer.
1895 return true;
1896 }
1897
1898 protected void validateCompClip() {
1899 int origClipState = clipState;
1900 if (usrClip == null) {
1901 clipState = CLIP_DEVICE;
1902 clipRegion = devClip;
1903 } else if (usrClip instanceof Rectangle2D) {
1904 clipState = CLIP_RECTANGULAR;
1905 clipRegion = devClip.getIntersection((Rectangle2D) usrClip);
1906 } else {
1907 PathIterator cpi = usrClip.getPathIterator(null);
1908 int box[] = new int[4];
1909 ShapeSpanIterator sr = LoopPipe.getFillSSI(this);
1910 try {
1911 sr.setOutputArea(devClip);
1912 sr.appendPath(cpi);
1913 sr.getPathBox(box);
1914 Region r = Region.getInstance(box, sr);
1915 clipRegion = r;
1916 clipState =
1917 r.isRectangular() ? CLIP_RECTANGULAR : CLIP_SHAPE;
1918 } finally {
1919 sr.dispose();
1920 }
1921 }
1922 if (origClipState != clipState &&
1923 (clipState == CLIP_SHAPE || origClipState == CLIP_SHAPE))
1924 {
1925 validFontInfo = false;
1926 invalidatePipe();
1927 }
1928 }
1975 rect.getHeight());
1976 }
1977
1978 if (tx == 0 && ty == 0) {
1979 return cloneShape(s);
1980 }
1981
1982 AffineTransform mat = AffineTransform.getTranslateInstance(tx, ty);
1983 return mat.createTransformedShape(s);
1984 }
1985
1986 protected static Shape transformShape(AffineTransform tx, Shape clip) {
1987 if (clip == null) {
1988 return null;
1989 }
1990
1991 if (clip instanceof Rectangle2D &&
1992 (tx.getType() & NON_RECTILINEAR_TRANSFORM_MASK) == 0)
1993 {
1994 Rectangle2D rect = (Rectangle2D) clip;
1995 double matrix[] = new double[4];
1996 matrix[0] = rect.getX();
1997 matrix[1] = rect.getY();
1998 matrix[2] = matrix[0] + rect.getWidth();
1999 matrix[3] = matrix[1] + rect.getHeight();
2000 tx.transform(matrix, 0, matrix, 0, 2);
2001 fixRectangleOrientation(matrix, rect);
2002 return new Rectangle2D.Double(matrix[0], matrix[1],
2003 matrix[2] - matrix[0],
2004 matrix[3] - matrix[1]);
2005 }
2006
2007 if (tx.isIdentity()) {
2008 return cloneShape(clip);
2009 }
2010
2011 return tx.createTransformedShape(clip);
2012 }
2013
2014 /**
2015 * Sets orientation of the rectangle according to the clip.
2334 }
2335
2336 public void fillArc(int x, int y, int w, int h,
2337 int startAngl, int arcAngl) {
2338 try {
2339 fillpipe.fillArc(this, x, y, w, h, startAngl, arcAngl);
2340 } catch (InvalidPipeException e) {
2341 try {
2342 revalidateAll();
2343 fillpipe.fillArc(this, x, y, w, h, startAngl, arcAngl);
2344 } catch (InvalidPipeException e2) {
2345 // Still catching the exception; we are not yet ready to
2346 // validate the surfaceData correctly. Fail for now and
2347 // try again next time around.
2348 }
2349 } finally {
2350 surfaceData.markDirty();
2351 }
2352 }
2353
2354 public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {
2355 try {
2356 drawpipe.drawPolyline(this, xPoints, yPoints, nPoints);
2357 } catch (InvalidPipeException e) {
2358 try {
2359 revalidateAll();
2360 drawpipe.drawPolyline(this, xPoints, yPoints, nPoints);
2361 } catch (InvalidPipeException e2) {
2362 // Still catching the exception; we are not yet ready to
2363 // validate the surfaceData correctly. Fail for now and
2364 // try again next time around.
2365 }
2366 } finally {
2367 surfaceData.markDirty();
2368 }
2369 }
2370
2371 public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {
2372 try {
2373 drawpipe.drawPolygon(this, xPoints, yPoints, nPoints);
2374 } catch (InvalidPipeException e) {
2375 try {
2376 revalidateAll();
2377 drawpipe.drawPolygon(this, xPoints, yPoints, nPoints);
2378 } catch (InvalidPipeException e2) {
2379 // Still catching the exception; we are not yet ready to
2380 // validate the surfaceData correctly. Fail for now and
2381 // try again next time around.
2382 }
2383 } finally {
2384 surfaceData.markDirty();
2385 }
2386 }
2387
2388 public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {
2389 try {
2390 fillpipe.fillPolygon(this, xPoints, yPoints, nPoints);
2391 } catch (InvalidPipeException e) {
2392 try {
2393 revalidateAll();
2394 fillpipe.fillPolygon(this, xPoints, yPoints, nPoints);
2395 } catch (InvalidPipeException e2) {
2396 // Still catching the exception; we are not yet ready to
2397 // validate the surfaceData correctly. Fail for now and
2398 // try again next time around.
2399 }
2400 } finally {
2401 surfaceData.markDirty();
2402 }
2403 }
2404
2405 public void drawRect (int x, int y, int w, int h) {
2406 try {
2407 drawpipe.drawRect(this, x, y, w, h);
2408 } catch (InvalidPipeException e) {
2569 }
2570
2571 /**
2572 * Returns a rectangle in image coordinates that may be required
2573 * in order to draw the given image into the given clipping region
2574 * through a pair of AffineTransforms. In addition, horizontal and
2575 * vertical padding factors for antialising and interpolation may
2576 * be used.
2577 */
2578 private static Rectangle getImageRegion(RenderedImage img,
2579 Region compClip,
2580 AffineTransform transform,
2581 AffineTransform xform,
2582 int padX, int padY) {
2583 Rectangle imageRect =
2584 new Rectangle(img.getMinX(), img.getMinY(),
2585 img.getWidth(), img.getHeight());
2586
2587 Rectangle result = null;
2588 try {
2589 double p[] = new double[8];
2590 p[0] = p[2] = compClip.getLoX();
2591 p[4] = p[6] = compClip.getHiX();
2592 p[1] = p[5] = compClip.getLoY();
2593 p[3] = p[7] = compClip.getHiY();
2594
2595 // Inverse transform the output bounding rect
2596 transform.inverseTransform(p, 0, p, 0, 4);
2597 xform.inverseTransform(p, 0, p, 0, 4);
2598
2599 // Determine a bounding box for the inverse transformed region
2600 double x0,x1,y0,y1;
2601 x0 = x1 = p[0];
2602 y0 = y1 = p[1];
2603
2604 for (int i = 2; i < 8; ) {
2605 double pt = p[i++];
2606 if (pt < x0) {
2607 x0 = pt;
2608 } else if (pt > x1) {
2609 x1 = pt;
2999 if (gv == null) {
3000 throw new NullPointerException("GlyphVector is null");
3001 }
3002
3003 try {
3004 textpipe.drawGlyphVector(this, gv, x, y);
3005 } catch (InvalidPipeException e) {
3006 try {
3007 revalidateAll();
3008 textpipe.drawGlyphVector(this, gv, x, y);
3009 } catch (InvalidPipeException e2) {
3010 // Still catching the exception; we are not yet ready to
3011 // validate the surfaceData correctly. Fail for now and
3012 // try again next time around.
3013 }
3014 } finally {
3015 surfaceData.markDirty();
3016 }
3017 }
3018
3019 public void drawChars(char data[], int offset, int length, int x, int y) {
3020
3021 if (data == null) {
3022 throw new NullPointerException("char data is null");
3023 }
3024 if (offset < 0 || length < 0 || offset + length > data.length) {
3025 throw new ArrayIndexOutOfBoundsException("bad offset/length");
3026 }
3027 if (font.hasLayoutAttributes()) {
3028 if (data.length == 0) {
3029 return;
3030 }
3031 new TextLayout(new String(data, offset, length),
3032 font, getFontRenderContext()).draw(this, x, y);
3033 return;
3034 }
3035
3036 try {
3037 textpipe.drawChars(this, data, offset, length, x, y);
3038 } catch (InvalidPipeException e) {
3039 try {
3040 revalidateAll();
3041 textpipe.drawChars(this, data, offset, length, x, y);
3042 } catch (InvalidPipeException e2) {
3043 // Still catching the exception; we are not yet ready to
3044 // validate the surfaceData correctly. Fail for now and
3045 // try again next time around.
3046 }
3047 } finally {
3048 surfaceData.markDirty();
3049 }
3050 }
3051
3052 public void drawBytes(byte data[], int offset, int length, int x, int y) {
3053 if (data == null) {
3054 throw new NullPointerException("byte data is null");
3055 }
3056 if (offset < 0 || length < 0 || offset + length > data.length) {
3057 throw new ArrayIndexOutOfBoundsException("bad offset/length");
3058 }
3059 /* Byte data is interpreted as 8-bit ASCII. Re-use drawChars loops */
3060 char chData[] = new char[length];
3061 for (int i = length; i-- > 0; ) {
3062 chData[i] = (char)(data[i+offset] & 0xff);
3063 }
3064 if (font.hasLayoutAttributes()) {
3065 if (data.length == 0) {
3066 return;
3067 }
3068 new TextLayout(new String(chData),
3069 font, getFontRenderContext()).draw(this, x, y);
3070 return;
3071 }
3072
3073 try {
3074 textpipe.drawChars(this, chData, 0, length, x, y);
3075 } catch (InvalidPipeException e) {
3076 try {
3077 revalidateAll();
3078 textpipe.drawChars(this, chData, 0, length, x, y);
3079 } catch (InvalidPipeException e2) {
3080 // Still catching the exception; we are not yet ready to
|
1 /*
2 * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
1841 return false;
1842 }
1843 if (transformState > TRANSFORM_INT_TRANSLATE) {
1844 // Note: Technically the most accurate test would be to
1845 // raster scan the parallelogram of the transformed rectangle
1846 // and do a span for span hit test against the clip, but for
1847 // speed we approximate the test with a bounding box of the
1848 // transformed rectangle. The cost of rasterizing the
1849 // transformed rectangle is probably high enough that it is
1850 // not worth doing so to save the caller from having to call
1851 // a rendering method where we will end up discovering the
1852 // same answer in about the same amount of time anyway.
1853 // This logic breaks down if this hit test is being performed
1854 // on the bounds of a group of shapes in which case it might
1855 // be beneficial to be a little more accurate to avoid lots
1856 // of subsequent rendering calls. In either case, this relaxed
1857 // test should not be significantly less accurate than the
1858 // optimal test for most transforms and so the conservative
1859 // answer should not cause too much extra work.
1860
1861 double[] d = {
1862 x, y,
1863 x+width, y,
1864 x, y+height,
1865 x+width, y+height
1866 };
1867 transform.transform(d, 0, d, 0, 4);
1868 x = (int) Math.floor(Math.min(Math.min(d[0], d[2]),
1869 Math.min(d[4], d[6])));
1870 y = (int) Math.floor(Math.min(Math.min(d[1], d[3]),
1871 Math.min(d[5], d[7])));
1872 width = (int) Math.ceil(Math.max(Math.max(d[0], d[2]),
1873 Math.max(d[4], d[6])));
1874 height = (int) Math.ceil(Math.max(Math.max(d[1], d[3]),
1875 Math.max(d[5], d[7])));
1876 } else {
1877 x += transX;
1878 y += transY;
1879 width += x;
1880 height += y;
1881 }
1888 return false;
1889 }
1890 // REMIND: We could go one step further here and examine the
1891 // non-rectangular clip shape more closely if there is one.
1892 // Since the clip has already been rasterized, the performance
1893 // penalty of doing the scan is probably still within the bounds
1894 // of a good tradeoff between speed and quality of the answer.
1895 return true;
1896 }
1897
1898 protected void validateCompClip() {
1899 int origClipState = clipState;
1900 if (usrClip == null) {
1901 clipState = CLIP_DEVICE;
1902 clipRegion = devClip;
1903 } else if (usrClip instanceof Rectangle2D) {
1904 clipState = CLIP_RECTANGULAR;
1905 clipRegion = devClip.getIntersection((Rectangle2D) usrClip);
1906 } else {
1907 PathIterator cpi = usrClip.getPathIterator(null);
1908 int[] box = new int[4];
1909 ShapeSpanIterator sr = LoopPipe.getFillSSI(this);
1910 try {
1911 sr.setOutputArea(devClip);
1912 sr.appendPath(cpi);
1913 sr.getPathBox(box);
1914 Region r = Region.getInstance(box, sr);
1915 clipRegion = r;
1916 clipState =
1917 r.isRectangular() ? CLIP_RECTANGULAR : CLIP_SHAPE;
1918 } finally {
1919 sr.dispose();
1920 }
1921 }
1922 if (origClipState != clipState &&
1923 (clipState == CLIP_SHAPE || origClipState == CLIP_SHAPE))
1924 {
1925 validFontInfo = false;
1926 invalidatePipe();
1927 }
1928 }
1975 rect.getHeight());
1976 }
1977
1978 if (tx == 0 && ty == 0) {
1979 return cloneShape(s);
1980 }
1981
1982 AffineTransform mat = AffineTransform.getTranslateInstance(tx, ty);
1983 return mat.createTransformedShape(s);
1984 }
1985
1986 protected static Shape transformShape(AffineTransform tx, Shape clip) {
1987 if (clip == null) {
1988 return null;
1989 }
1990
1991 if (clip instanceof Rectangle2D &&
1992 (tx.getType() & NON_RECTILINEAR_TRANSFORM_MASK) == 0)
1993 {
1994 Rectangle2D rect = (Rectangle2D) clip;
1995 double[] matrix = new double[4];
1996 matrix[0] = rect.getX();
1997 matrix[1] = rect.getY();
1998 matrix[2] = matrix[0] + rect.getWidth();
1999 matrix[3] = matrix[1] + rect.getHeight();
2000 tx.transform(matrix, 0, matrix, 0, 2);
2001 fixRectangleOrientation(matrix, rect);
2002 return new Rectangle2D.Double(matrix[0], matrix[1],
2003 matrix[2] - matrix[0],
2004 matrix[3] - matrix[1]);
2005 }
2006
2007 if (tx.isIdentity()) {
2008 return cloneShape(clip);
2009 }
2010
2011 return tx.createTransformedShape(clip);
2012 }
2013
2014 /**
2015 * Sets orientation of the rectangle according to the clip.
2334 }
2335
2336 public void fillArc(int x, int y, int w, int h,
2337 int startAngl, int arcAngl) {
2338 try {
2339 fillpipe.fillArc(this, x, y, w, h, startAngl, arcAngl);
2340 } catch (InvalidPipeException e) {
2341 try {
2342 revalidateAll();
2343 fillpipe.fillArc(this, x, y, w, h, startAngl, arcAngl);
2344 } catch (InvalidPipeException e2) {
2345 // Still catching the exception; we are not yet ready to
2346 // validate the surfaceData correctly. Fail for now and
2347 // try again next time around.
2348 }
2349 } finally {
2350 surfaceData.markDirty();
2351 }
2352 }
2353
2354 public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
2355 try {
2356 drawpipe.drawPolyline(this, xPoints, yPoints, nPoints);
2357 } catch (InvalidPipeException e) {
2358 try {
2359 revalidateAll();
2360 drawpipe.drawPolyline(this, xPoints, yPoints, nPoints);
2361 } catch (InvalidPipeException e2) {
2362 // Still catching the exception; we are not yet ready to
2363 // validate the surfaceData correctly. Fail for now and
2364 // try again next time around.
2365 }
2366 } finally {
2367 surfaceData.markDirty();
2368 }
2369 }
2370
2371 public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
2372 try {
2373 drawpipe.drawPolygon(this, xPoints, yPoints, nPoints);
2374 } catch (InvalidPipeException e) {
2375 try {
2376 revalidateAll();
2377 drawpipe.drawPolygon(this, xPoints, yPoints, nPoints);
2378 } catch (InvalidPipeException e2) {
2379 // Still catching the exception; we are not yet ready to
2380 // validate the surfaceData correctly. Fail for now and
2381 // try again next time around.
2382 }
2383 } finally {
2384 surfaceData.markDirty();
2385 }
2386 }
2387
2388 public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
2389 try {
2390 fillpipe.fillPolygon(this, xPoints, yPoints, nPoints);
2391 } catch (InvalidPipeException e) {
2392 try {
2393 revalidateAll();
2394 fillpipe.fillPolygon(this, xPoints, yPoints, nPoints);
2395 } catch (InvalidPipeException e2) {
2396 // Still catching the exception; we are not yet ready to
2397 // validate the surfaceData correctly. Fail for now and
2398 // try again next time around.
2399 }
2400 } finally {
2401 surfaceData.markDirty();
2402 }
2403 }
2404
2405 public void drawRect (int x, int y, int w, int h) {
2406 try {
2407 drawpipe.drawRect(this, x, y, w, h);
2408 } catch (InvalidPipeException e) {
2569 }
2570
2571 /**
2572 * Returns a rectangle in image coordinates that may be required
2573 * in order to draw the given image into the given clipping region
2574 * through a pair of AffineTransforms. In addition, horizontal and
2575 * vertical padding factors for antialising and interpolation may
2576 * be used.
2577 */
2578 private static Rectangle getImageRegion(RenderedImage img,
2579 Region compClip,
2580 AffineTransform transform,
2581 AffineTransform xform,
2582 int padX, int padY) {
2583 Rectangle imageRect =
2584 new Rectangle(img.getMinX(), img.getMinY(),
2585 img.getWidth(), img.getHeight());
2586
2587 Rectangle result = null;
2588 try {
2589 double[] p = new double[8];
2590 p[0] = p[2] = compClip.getLoX();
2591 p[4] = p[6] = compClip.getHiX();
2592 p[1] = p[5] = compClip.getLoY();
2593 p[3] = p[7] = compClip.getHiY();
2594
2595 // Inverse transform the output bounding rect
2596 transform.inverseTransform(p, 0, p, 0, 4);
2597 xform.inverseTransform(p, 0, p, 0, 4);
2598
2599 // Determine a bounding box for the inverse transformed region
2600 double x0,x1,y0,y1;
2601 x0 = x1 = p[0];
2602 y0 = y1 = p[1];
2603
2604 for (int i = 2; i < 8; ) {
2605 double pt = p[i++];
2606 if (pt < x0) {
2607 x0 = pt;
2608 } else if (pt > x1) {
2609 x1 = pt;
2999 if (gv == null) {
3000 throw new NullPointerException("GlyphVector is null");
3001 }
3002
3003 try {
3004 textpipe.drawGlyphVector(this, gv, x, y);
3005 } catch (InvalidPipeException e) {
3006 try {
3007 revalidateAll();
3008 textpipe.drawGlyphVector(this, gv, x, y);
3009 } catch (InvalidPipeException e2) {
3010 // Still catching the exception; we are not yet ready to
3011 // validate the surfaceData correctly. Fail for now and
3012 // try again next time around.
3013 }
3014 } finally {
3015 surfaceData.markDirty();
3016 }
3017 }
3018
3019 public void drawChars(char[] data, int offset, int length, int x, int y) {
3020
3021 if (data == null) {
3022 throw new NullPointerException("char data is null");
3023 }
3024 if (offset < 0 || length < 0 || offset + length > data.length) {
3025 throw new ArrayIndexOutOfBoundsException("bad offset/length");
3026 }
3027 if (font.hasLayoutAttributes()) {
3028 if (data.length == 0) {
3029 return;
3030 }
3031 new TextLayout(new String(data, offset, length),
3032 font, getFontRenderContext()).draw(this, x, y);
3033 return;
3034 }
3035
3036 try {
3037 textpipe.drawChars(this, data, offset, length, x, y);
3038 } catch (InvalidPipeException e) {
3039 try {
3040 revalidateAll();
3041 textpipe.drawChars(this, data, offset, length, x, y);
3042 } catch (InvalidPipeException e2) {
3043 // Still catching the exception; we are not yet ready to
3044 // validate the surfaceData correctly. Fail for now and
3045 // try again next time around.
3046 }
3047 } finally {
3048 surfaceData.markDirty();
3049 }
3050 }
3051
3052 public void drawBytes(byte[] data, int offset, int length, int x, int y) {
3053 if (data == null) {
3054 throw new NullPointerException("byte data is null");
3055 }
3056 if (offset < 0 || length < 0 || offset + length > data.length) {
3057 throw new ArrayIndexOutOfBoundsException("bad offset/length");
3058 }
3059 /* Byte data is interpreted as 8-bit ASCII. Re-use drawChars loops */
3060 char[] chData = new char[length];
3061 for (int i = length; i-- > 0; ) {
3062 chData[i] = (char)(data[i+offset] & 0xff);
3063 }
3064 if (font.hasLayoutAttributes()) {
3065 if (data.length == 0) {
3066 return;
3067 }
3068 new TextLayout(new String(chData),
3069 font, getFontRenderContext()).draw(this, x, y);
3070 return;
3071 }
3072
3073 try {
3074 textpipe.drawChars(this, chData, 0, length, x, y);
3075 } catch (InvalidPipeException e) {
3076 try {
3077 revalidateAll();
3078 textpipe.drawChars(this, chData, 0, length, x, y);
3079 } catch (InvalidPipeException e2) {
3080 // Still catching the exception; we are not yet ready to
|