7190349: [macosx] Text (Label) is incorrectly drawn with a rotated g2d
8013569: [macosx] JLabel preferred size incorrect on retina displays with non-default font size Reviewed-by: prr
This commit is contained in:
parent
969c84555e
commit
4a42ba3816
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,7 +31,7 @@ import java.util.*;
|
||||
|
||||
import sun.awt.SunHints;
|
||||
|
||||
public class CStrike extends FontStrike {
|
||||
public final class CStrike extends FontStrike {
|
||||
|
||||
// Creates the native strike
|
||||
private static native long createNativeStrikePtr(long nativeFontPtr,
|
||||
@ -68,10 +68,10 @@ public class CStrike extends FontStrike {
|
||||
Rectangle2D.Float result,
|
||||
double x, double y);
|
||||
|
||||
private CFont nativeFont;
|
||||
private final CFont nativeFont;
|
||||
private AffineTransform invDevTx;
|
||||
private GlyphInfoCache glyphInfoCache;
|
||||
private GlyphAdvanceCache glyphAdvanceCache;
|
||||
private final GlyphInfoCache glyphInfoCache;
|
||||
private final GlyphAdvanceCache glyphAdvanceCache;
|
||||
private long nativeStrikePtr;
|
||||
|
||||
CStrike(final CFont font, final FontStrikeDesc inDesc) {
|
||||
@ -84,11 +84,11 @@ public class CStrike extends FontStrike {
|
||||
// Normally the device transform should be the identity transform
|
||||
// for screen operations. The device transform only becomes
|
||||
// interesting when we are outputting between different dpi surfaces,
|
||||
// like when we are printing to postscript.
|
||||
// like when we are printing to postscript or use retina.
|
||||
if (inDesc.devTx != null && !inDesc.devTx.isIdentity()) {
|
||||
try {
|
||||
invDevTx = inDesc.devTx.createInverse();
|
||||
} catch (NoninvertibleTransformException e) {
|
||||
} catch (NoninvertibleTransformException ignored) {
|
||||
// ignored, since device transforms should not be that
|
||||
// complicated, and if they are - there is nothing we can do,
|
||||
// so we won't worry about it.
|
||||
@ -134,15 +134,13 @@ public class CStrike extends FontStrike {
|
||||
nativeStrikePtr = 0;
|
||||
}
|
||||
|
||||
// the fractional metrics default on our platform is OFF
|
||||
private boolean useFractionalMetrics() {
|
||||
return desc.fmHint == SunHints.INTVAL_FRACTIONALMETRICS_ON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumGlyphs() {
|
||||
return nativeFont.getNumGlyphs();
|
||||
}
|
||||
|
||||
@Override
|
||||
StrikeMetrics getFontMetrics() {
|
||||
if (strikeMetrics == null) {
|
||||
StrikeMetrics metrics = getFontMetrics(getNativeStrikePtr());
|
||||
@ -155,74 +153,24 @@ public class CStrike extends FontStrike {
|
||||
return strikeMetrics;
|
||||
}
|
||||
|
||||
float getGlyphAdvance(int glyphCode) {
|
||||
return getScaledAdvanceForAdvance(getCachedNativeGlyphAdvance(glyphCode));
|
||||
@Override
|
||||
float getGlyphAdvance(final int glyphCode) {
|
||||
return getCachedNativeGlyphAdvance(glyphCode);
|
||||
}
|
||||
|
||||
float getCodePointAdvance(int cp) {
|
||||
float advance = getCachedNativeGlyphAdvance(nativeFont.getMapper().charToGlyph(cp));
|
||||
|
||||
double glyphScaleX = desc.glyphTx.getScaleX();
|
||||
double devScaleX = desc.devTx.getScaleX();
|
||||
|
||||
if (devScaleX == 0) {
|
||||
glyphScaleX = Math.sqrt(desc.glyphTx.getDeterminant());
|
||||
devScaleX = Math.sqrt(desc.devTx.getDeterminant());
|
||||
}
|
||||
|
||||
if (devScaleX == 0) {
|
||||
devScaleX = Double.NaN; // this an undefined graphics state
|
||||
}
|
||||
advance = (float) (advance * glyphScaleX / devScaleX);
|
||||
return useFractionalMetrics() ? advance : Math.round(advance);
|
||||
@Override
|
||||
float getCodePointAdvance(final int cp) {
|
||||
return getGlyphAdvance(nativeFont.getMapper().charToGlyph(cp));
|
||||
}
|
||||
|
||||
// calculate an advance, and round if not using fractional metrics
|
||||
private float getScaledAdvanceForAdvance(float advance) {
|
||||
if (invDevTx != null) {
|
||||
advance *= invDevTx.getScaleX();
|
||||
}
|
||||
advance *= desc.glyphTx.getScaleX();
|
||||
return useFractionalMetrics() ? advance : Math.round(advance);
|
||||
@Override
|
||||
Point2D.Float getCharMetrics(final char ch) {
|
||||
return getGlyphMetrics(nativeFont.getMapper().charToGlyph(ch));
|
||||
}
|
||||
|
||||
Point2D.Float getCharMetrics(char ch) {
|
||||
return getScaledPointForAdvance(getCachedNativeGlyphAdvance(nativeFont.getMapper().charToGlyph(ch)));
|
||||
}
|
||||
|
||||
Point2D.Float getGlyphMetrics(int glyphCode) {
|
||||
return getScaledPointForAdvance(getCachedNativeGlyphAdvance(glyphCode));
|
||||
}
|
||||
|
||||
// calculate an advance point, and round if not using fractional metrics
|
||||
private Point2D.Float getScaledPointForAdvance(float advance) {
|
||||
Point2D.Float pt = new Point2D.Float(advance, 0);
|
||||
|
||||
if (!desc.glyphTx.isIdentity()) {
|
||||
return scalePoint(pt);
|
||||
}
|
||||
|
||||
if (!useFractionalMetrics()) {
|
||||
pt.x = Math.round(pt.x);
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
|
||||
private Point2D.Float scalePoint(Point2D.Float pt) {
|
||||
if (invDevTx != null) {
|
||||
// transform the point out of the device space first
|
||||
invDevTx.transform(pt, pt);
|
||||
}
|
||||
desc.glyphTx.transform(pt, pt);
|
||||
pt.x -= desc.glyphTx.getTranslateX();
|
||||
pt.y -= desc.glyphTx.getTranslateY();
|
||||
|
||||
if (!useFractionalMetrics()) {
|
||||
pt.x = Math.round(pt.x);
|
||||
pt.y = Math.round(pt.y);
|
||||
}
|
||||
|
||||
return pt;
|
||||
@Override
|
||||
Point2D.Float getGlyphMetrics(final int glyphCode) {
|
||||
return new Point2D.Float(getGlyphAdvance(glyphCode), 0.0f);
|
||||
}
|
||||
|
||||
Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
|
||||
@ -414,9 +362,7 @@ public class CStrike extends FontStrike {
|
||||
private SparseBitShiftingTwoLayerArray secondLayerCache;
|
||||
private HashMap<Integer, Long> generalCache;
|
||||
|
||||
public GlyphInfoCache(final Font2D nativeFont,
|
||||
final FontStrikeDesc desc)
|
||||
{
|
||||
GlyphInfoCache(final Font2D nativeFont, final FontStrikeDesc desc) {
|
||||
super(nativeFont, desc);
|
||||
firstLayerCache = new long[FIRST_LAYER_SIZE];
|
||||
}
|
||||
@ -527,7 +473,7 @@ public class CStrike extends FontStrike {
|
||||
final int shift;
|
||||
final int secondLayerLength;
|
||||
|
||||
public SparseBitShiftingTwoLayerArray(final int size, final int shift) {
|
||||
SparseBitShiftingTwoLayerArray(final int size, final int shift) {
|
||||
this.shift = shift;
|
||||
this.cache = new long[1 << shift][];
|
||||
this.secondLayerLength = size >> shift;
|
||||
@ -559,6 +505,12 @@ public class CStrike extends FontStrike {
|
||||
private SparseBitShiftingTwoLayerArray secondLayerCache;
|
||||
private HashMap<Integer, Float> generalCache;
|
||||
|
||||
// Empty non private constructor was added because access to this
|
||||
// class shouldn't be emulated by a synthetic accessor method.
|
||||
GlyphAdvanceCache() {
|
||||
super();
|
||||
}
|
||||
|
||||
public synchronized float get(final int index) {
|
||||
if (index < 0) {
|
||||
if (-index < SECOND_LAYER_SIZE) {
|
||||
@ -609,9 +561,7 @@ public class CStrike extends FontStrike {
|
||||
final int shift;
|
||||
final int secondLayerLength;
|
||||
|
||||
public SparseBitShiftingTwoLayerArray(final int size,
|
||||
final int shift)
|
||||
{
|
||||
SparseBitShiftingTwoLayerArray(final int size, final int shift) {
|
||||
this.shift = shift;
|
||||
this.cache = new float[1 << shift][];
|
||||
this.secondLayerLength = size >> shift;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,11 +31,12 @@
|
||||
@interface AWTStrike : NSObject {
|
||||
@public
|
||||
AWTFont * fAWTFont;
|
||||
CGFloat fSize;
|
||||
CGFloat fSize;
|
||||
JRSFontRenderingStyle fStyle;
|
||||
jint fAAStyle;
|
||||
jint fAAStyle;
|
||||
|
||||
CGAffineTransform fTx;
|
||||
CGAffineTransform fDevTx;
|
||||
CGAffineTransform fAltTx; // alternate strike tx used for Sun2D
|
||||
CGAffineTransform fFontTx;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -65,6 +65,7 @@ static CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 };
|
||||
invDevTx.b *= -1;
|
||||
invDevTx.c *= -1;
|
||||
fFontTx = CGAffineTransformConcat(CGAffineTransformConcat(tx, invDevTx), sInverseTX);
|
||||
fDevTx = CGAffineTransformInvert(invDevTx);
|
||||
|
||||
// the "font size" is the square root of the determinant of the matrix
|
||||
fSize = sqrt(abs(fFontTx.a * fFontTx.d - fFontTx.b * fFontTx.c));
|
||||
@ -148,7 +149,8 @@ Java_sun_font_CStrike_getNativeGlyphAdvance
|
||||
{
|
||||
CGSize advance;
|
||||
JNF_COCOA_ENTER(env);
|
||||
AWTFont *awtFont = ((AWTStrike *)jlong_to_ptr(awtStrikePtr))->fAWTFont;
|
||||
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
|
||||
AWTFont *awtFont = awtStrike->fAWTFont;
|
||||
|
||||
// negative glyph codes are really unicodes, which were placed there by the mapper
|
||||
// to indicate we should use CoreText to substitute the character
|
||||
@ -156,6 +158,10 @@ JNF_COCOA_ENTER(env);
|
||||
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
|
||||
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
CFRelease(fallback);
|
||||
advance = CGSizeApplyAffineTransform(advance, awtStrike->fFontTx);
|
||||
if (!JRSFontStyleUsesFractionalMetrics(awtStrike->fStyle)) {
|
||||
advance.width = round(advance.width);
|
||||
}
|
||||
|
||||
JNF_COCOA_EXIT(env);
|
||||
return advance.width;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -455,6 +455,7 @@ CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
|
||||
#define CGGI_GLYPH_BBOX_PADDING 2.0f
|
||||
static inline GlyphInfo *
|
||||
CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
|
||||
const AWTStrike *strike,
|
||||
const CGGI_RenderingMode *mode)
|
||||
{
|
||||
size_t pixelSize = mode->glyphDescriptor->pixelSize;
|
||||
@ -477,6 +478,12 @@ CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
|
||||
width = 1;
|
||||
height = 1;
|
||||
}
|
||||
advance = CGSizeApplyAffineTransform(advance, strike->fFontTx);
|
||||
if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) {
|
||||
advance.width = round(advance.width);
|
||||
advance.height = round(advance.height);
|
||||
}
|
||||
advance = CGSizeApplyAffineTransform(advance, strike->fDevTx);
|
||||
|
||||
#ifdef USE_IMAGE_ALIGNED_MEMORY
|
||||
// create separate memory
|
||||
@ -564,10 +571,10 @@ CGGI_CreateImageForUnicode
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
|
||||
|
||||
CGSize advance;
|
||||
JRSFontGetAdvancesForGlyphsAndStyle(fallback, &tx, strike->fStyle, &glyph, 1, &advance);
|
||||
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
|
||||
// create the Sun2D GlyphInfo we are going to strike into
|
||||
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, mode);
|
||||
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
|
||||
|
||||
// fix the context size, just in case the substituted character is unexpectedly large
|
||||
CGGI_SizeCanvas(canvas, info->width, info->height, mode->cgFontMode);
|
||||
@ -715,7 +722,7 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
|
||||
JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
|
||||
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes);
|
||||
JRSFontGetAdvancesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, strike->fStyle, glyphs, len, advances);
|
||||
CTFontGetAdvancesForGlyphs((CTFontRef)font->fFont, kCTFontDefaultOrientation, glyphs, advances, len);
|
||||
|
||||
size_t maxWidth = 1;
|
||||
size_t maxHeight = 1;
|
||||
@ -732,7 +739,7 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
|
||||
CGSize advance = advances[i];
|
||||
CGRect bbox = bboxes[i];
|
||||
|
||||
GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, mode);
|
||||
GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
|
||||
|
||||
if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width;
|
||||
if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;
|
||||
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 7190349
|
||||
* @summary Verifies that we get correct direction, when draw rotated string.
|
||||
* @author Sergey Bylokhov
|
||||
* @run main/othervm DrawRotatedString
|
||||
*/
|
||||
public final class DrawRotatedString {
|
||||
|
||||
private static final int SIZE = 500;
|
||||
|
||||
public static void main(final String[] args) throws IOException {
|
||||
BufferedImage bi = createBufferedImage(true);
|
||||
verify(bi);
|
||||
bi = createBufferedImage(false);
|
||||
verify(bi);
|
||||
System.out.println("Passed");
|
||||
}
|
||||
|
||||
private static void verify(BufferedImage bi) throws IOException {
|
||||
for (int i = 0; i < SIZE; ++i) {
|
||||
for (int j = 0; j < 99; ++j) {
|
||||
//Text should not appear before 100
|
||||
if (bi.getRGB(i, j) != Color.RED.getRGB()) {
|
||||
ImageIO.write(bi, "png", new File("image.png"));
|
||||
throw new RuntimeException("Failed: wrong text location");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static BufferedImage createBufferedImage(final boolean aa) {
|
||||
final BufferedImage bi = new BufferedImage(SIZE, SIZE,
|
||||
BufferedImage.TYPE_INT_RGB);
|
||||
final Graphics2D bg = bi.createGraphics();
|
||||
bg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
aa ? RenderingHints.VALUE_ANTIALIAS_ON
|
||||
: RenderingHints.VALUE_ANTIALIAS_OFF);
|
||||
bg.setColor(Color.RED);
|
||||
bg.fillRect(0, 0, SIZE, SIZE);
|
||||
bg.translate(100, 100);
|
||||
bg.rotate(Math.toRadians(90));
|
||||
bg.setColor(Color.BLACK);
|
||||
bg.setFont(bg.getFont().deriveFont(20.0f));
|
||||
bg.drawString("MMMMMMMMMMMMMMMM", 0, 0);
|
||||
bg.dispose();
|
||||
return bi;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8013569
|
||||
* @author Sergey Bylokhov
|
||||
*/
|
||||
public final class IncorrectTextSize {
|
||||
|
||||
static final int scale = 2;
|
||||
static final int width = 1200;
|
||||
static final int height = 100;
|
||||
static BufferedImage bi = new BufferedImage(width, height,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
static final String TEXT = "The quick brown fox jumps over the lazy dog"
|
||||
+ "The quick brown fox jumps over the lazy dog";
|
||||
|
||||
public static void main(final String[] args) throws IOException {
|
||||
for (int point = 5; point < 11; ++point) {
|
||||
Graphics2D g2d = bi.createGraphics();
|
||||
g2d.setFont(new Font(Font.DIALOG, Font.PLAIN, point));
|
||||
g2d.scale(scale, scale);
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.fillRect(0, 0, width, height);
|
||||
g2d.setColor(Color.green);
|
||||
g2d.drawString(TEXT, 0, 20);
|
||||
int length = g2d.getFontMetrics().stringWidth(TEXT);
|
||||
if (length < 0) {
|
||||
throw new RuntimeException("Negative length");
|
||||
}
|
||||
for (int i = (length + 1) * scale; i < width; ++i) {
|
||||
for (int j = 0; j < height; ++j) {
|
||||
if (bi.getRGB(i, j) != Color.white.getRGB()) {
|
||||
g2d.drawLine(length, 0, length, height);
|
||||
ImageIO.write(bi, "png", new File("image.png"));
|
||||
System.out.println("length = " + length);
|
||||
System.err.println("Wrong color at x=" + i + ",y=" + j);
|
||||
System.err.println("Color is:" + new Color(bi.getRGB(i,
|
||||
j)));
|
||||
throw new RuntimeException("Test failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
g2d.dispose();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user