From c8d59bf9932d6d54bd9d01d1569c7be8b186a1d6 Mon Sep 17 00:00:00 2001 From: Ben Soares Date: Wed, 7 Feb 2024 01:00:31 +0000 Subject: [PATCH] JAL-4375 Clipped label text in annotations to annotation rectangle. Added property to change text antialias method. Changed translation/scaling of Graphics2D object more robust with a copy of the object. --- src/jalview/renderer/AnnotationRenderer.java | 112 +++++++++++++++++++------- 1 file changed, 85 insertions(+), 27 deletions(-) diff --git a/src/jalview/renderer/AnnotationRenderer.java b/src/jalview/renderer/AnnotationRenderer.java index 0fbfb02..734e33c 100644 --- a/src/jalview/renderer/AnnotationRenderer.java +++ b/src/jalview/renderer/AnnotationRenderer.java @@ -142,6 +142,51 @@ public class AnnotationRenderer */ private boolean canClip = false; + /** + * Property to set text antialiasing method + */ + private static final String TEXT_ANTIALIAS_METHOD = "TEXT_ANTIALIAS_METHOD"; + + private static final Object textAntialiasMethod; + + static + { + final String textAntialiasMethodPref = Cache + .getDefault(TEXT_ANTIALIAS_METHOD, "GASP"); + switch (textAntialiasMethodPref) + { + case "ON": + textAntialiasMethod = RenderingHints.VALUE_TEXT_ANTIALIAS_ON; + break; + case "GASP": + textAntialiasMethod = RenderingHints.VALUE_TEXT_ANTIALIAS_GASP; + break; + case "LCD_HBGR": + textAntialiasMethod = RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR; + break; + case "LCD_HRGB": + textAntialiasMethod = RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB; + break; + case "LCD_VBGR": + textAntialiasMethod = RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR; + break; + case "LCD_VRGB": + textAntialiasMethod = RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB; + break; + case "OFF": + textAntialiasMethod = RenderingHints.VALUE_TEXT_ANTIALIAS_OFF; + break; + case "DEFAULT": + textAntialiasMethod = RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT; + break; + default: + jalview.bin.Console.warn(TEXT_ANTIALIAS_METHOD + " value '" + + textAntialiasMethodPref + + "' not recognised, defaulting to 'GASP'. See https://docs.oracle.com/javase/8/docs/api/java/awt/RenderingHints.html#KEY_TEXT_ANTIALIASING"); + textAntialiasMethod = RenderingHints.VALUE_TEXT_ANTIALIAS_GASP; + } + } + public AnnotationRenderer() { this(false); @@ -518,7 +563,6 @@ public class AnnotationRenderer .getComplementConsensusAnnotation(); BitSet graphGroupDrawn = new BitSet(); - int charOffset = 0; // offset for a label // \u03B2 \u03B1 // debug ints int yfrom = 0, f_i = 0, yto = 0, f_to = 0; @@ -695,9 +739,7 @@ public class AnnotationRenderer if (validCharWidth && validRes && displayChar != null && (displayChar.length() > 0)) { - // Graphics2D gg = (g); - float fmWidth = fm.charsWidth(displayChar.toCharArray(), 0, - displayChar.length()); + float fmWidth = fm.stringWidth(displayChar); /* * shrink label width to fit in column, if that is @@ -708,13 +750,12 @@ public class AnnotationRenderer if (scaleColLabel && fmWidth > charWidth) { scaledToFit = true; - fmScaling = charWidth; - fmScaling /= fmWidth; + fmScaling = (float) charWidth / fmWidth; // and update the label's width to reflect the scaling. fmWidth = charWidth; } - charOffset = (int) ((charWidth - fmWidth) / 2f); + float charOffset = (charWidth - fmWidth) / 2f; if (row_annotations[column].colour == null) { @@ -729,26 +770,39 @@ public class AnnotationRenderer * draw the label, unless it is the same secondary structure * symbol (excluding RNA Helix) as the previous column */ - final int xPos = (x * charWidth) + charOffset; - final int yPos = y + iconOffset; - + final float xPos = (x * charWidth) + charOffset; + final float yPos = y + iconOffset; + + // Act on a copy of the Graphics2d object + Graphics2D g2dCopy = (Graphics2D) g2d.create(); + // Clip to this annotation line (particularly width). + // This removes artifacts from text when side scrolling + // (particularly in wrap format), but can result in clipped + // characters until a full paint is drawn. + g2dCopy.setClip(0, (int) yPos - charHeight, imgWidth - 1, + charHeight); /* * translate to drawing position _before_ applying any scaling */ - g2d.translate(xPos, yPos); + g2dCopy.translate(xPos, yPos); + // narrow the clipping rectangle to the size of a character square + // or the character width + g2dCopy.clipRect(0, 0 - charHeight, + (int) Math.ceil(Math.max(charWidth, fmWidth)), + charHeight); if (scaledToFit) { /* * use a scaling transform to make the label narrower * (JalviewJS doesn't have Font.deriveFont(AffineTransform)) */ - g2d.transform( + g2dCopy.transform( AffineTransform.getScaleInstance(fmScaling, 1.0)); } - setAntialias(g); + setAntialias(g2dCopy, true); if (column == 0 || row.graph > 0) { - g2d.drawString(displayChar, 0, 0); + g2dCopy.drawString(displayChar, 0, 0); } else if (row_annotations[column - 1] == null || (labelAllCols || !displayChar.equals( @@ -756,18 +810,9 @@ public class AnnotationRenderer || (displayChar.length() < 2 && row_annotations[column].secondaryStructure == ' '))) { - g2d.drawString(displayChar, 0, 0); - } - if (scaledToFit) - { - /* - * undo scaling before translating back - * (restoring saved transform does NOT work in JS PDFGraphics!) - */ - g2d.transform(AffineTransform - .getScaleInstance(1D / fmScaling, 1.0)); + g2dCopy.drawString(displayChar, 0, 0); } - g2d.translate(-xPos, -yPos); + g2dCopy.dispose(); } } if (row.hasIcons) @@ -1939,6 +1984,11 @@ public class AnnotationRenderer private void setAntialias(Graphics g) { + setAntialias(g, false); + } + + private void setAntialias(Graphics g, boolean text) + { if (isVectorRendering()) { // no need to antialias vector drawings @@ -1947,8 +1997,16 @@ public class AnnotationRenderer if (Cache.getDefault("ANTI_ALIAS", true)) { Graphics2D g2d = (Graphics2D) g; - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); + if (text) + { + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + this.textAntialiasMethod); + } + else + { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } } } -- 1.7.10.2