List of usage examples for java.awt Graphics2D setRenderingHint
public abstract void setRenderingHint(Key hintKey, Object hintValue);
From source file:org.encuestame.business.images.ImageThumbnailGeneratorImpl.java
/** * Create a thumbnail image and save it to disk. * * This algorithm is based on:// w w w . ja v a2s . co m * http://www.philreeve.com/java_high_quality_thumbnails.php * * @param imageIn The image you want to scale. * @param fileOut The output file. * @param largestDimension The largest dimension, so that neither the width nor height * will exceed this value. * * @return the image that was created, null if imageIn or fileOut is null. * @throws java.io.IOException if something goes wrong when saving as jpeg */ public BufferedImage createThumbnailImage(Image imageIn, File fileOut, int largestDimension) throws IOException { if ((imageIn == null) || (fileOut == null)) { return null; } //it seems to not return the right size until the methods get called for the first time imageIn.getWidth(null); imageIn.getHeight(null); // Find biggest dimension int nImageWidth = imageIn.getWidth(null); int nImageHeight = imageIn.getHeight(null); int nImageLargestDim = Math.max(nImageWidth, nImageHeight); double scale = (double) largestDimension / (double) nImageLargestDim; int sizeDifference = nImageLargestDim - largestDimension; //create an image buffer to draw to BufferedImage imageOut = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); // 8-bit RGB Graphics2D g2d; AffineTransform tx; // Use a few steps if the sizes are drastically different, and only scale // if the desired size is smaller than the original. int numSteps = 0; if (scale < 1.0d) { // Make sure we have at least 1 step numSteps = Math.max(1, (sizeDifference / 100)); } if (numSteps > 0) { int stepSize = sizeDifference / numSteps; int stepWeight = stepSize / 2; int heavierStepSize = stepSize + stepWeight; int lighterStepSize = stepSize - stepWeight; int currentStepSize, centerStep; double scaledW = imageIn.getWidth(null); double scaledH = imageIn.getHeight(null); if ((numSteps % 2) == 1) //if there's an odd number of steps centerStep = (int) Math.ceil((double) numSteps / 2d); //find the center step else centerStep = -1; //set it to -1 so it's ignored later Integer intermediateSize; Integer previousIntermediateSize = nImageLargestDim; for (Integer i = 0; i < numSteps; i++) { if (i + 1 != centerStep) { //if this isn't the center step if (i == numSteps - 1) { //if this is the last step //fix the stepsize to account for decimal place errors previously currentStepSize = previousIntermediateSize - largestDimension; } else { if (numSteps - i > numSteps / 2) //if we're in the first half of the reductions currentStepSize = heavierStepSize; else currentStepSize = lighterStepSize; } } else { //center step, use natural step size currentStepSize = stepSize; } intermediateSize = previousIntermediateSize - currentStepSize; scale = intermediateSize / (double) previousIntermediateSize; scaledW = Math.max((int) (scaledW * scale), 1); scaledH = Math.max((int) (scaledH * scale), 1); log.info("step " + i + ": scaling to " + scaledW + " x " + scaledH); imageOut = new BufferedImage((int) scaledW, (int) scaledH, BufferedImage.TYPE_INT_RGB); // 8 bit RGB g2d = imageOut.createGraphics(); g2d.setBackground(Color.WHITE); g2d.clearRect(0, 0, imageOut.getWidth(), imageOut.getHeight()); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); tx = new AffineTransform(); tx.scale(scale, scale); g2d.drawImage(imageIn, tx, null); g2d.dispose(); imageIn = new ImageIcon(imageOut).getImage(); previousIntermediateSize = intermediateSize; } } else { // This enforces a rule that we always have an 8-bit image with white background for the thumbnail. Plus, for large // images, this makes subsequent downscaling really fast because we are working on a large 8-bit image // instead of a large 12 or 24 bit image, so the downstream effect is very noticable. imageOut = new BufferedImage(imageIn.getWidth(null), imageIn.getHeight(null), BufferedImage.TYPE_INT_RGB); g2d = imageOut.createGraphics(); g2d.setBackground(Color.WHITE); g2d.clearRect(0, 0, imageOut.getWidth(), imageOut.getHeight()); tx = new AffineTransform(); tx.setToIdentity(); //use identity matrix so image is copied exactly g2d.drawImage(imageIn, tx, null); g2d.dispose(); } //saveImageAsJPEG(imageOut, fileOut); ImageIO.write(imageOut, "jpg", fileOut); return imageOut; }
From source file:net.geoprism.dashboard.DashboardMap.java
private BufferedImage getLegendTitleImage(DashboardLayer layer) { FontMetrics fm;/*ww w . j a v a 2 s. co m*/ int textWidth; int textHeight; int textBoxHorizontalPadding = 4; int textBoxVerticalPadding = 4; int borderWidth = 2; int paddedTitleHeight; int paddedTitleWidth; int titleLeftPadding = textBoxHorizontalPadding; BufferedImage newLegendTitleBase; Graphics2D newLegendTitleBaseGraphic = null; try { // Build the Font object Font titleFont = new Font(layer.getName(), Font.BOLD, 14); // Build variables for base legend graphic construction try { newLegendTitleBase = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); newLegendTitleBaseGraphic = newLegendTitleBase.createGraphics(); newLegendTitleBaseGraphic.setFont(titleFont); fm = newLegendTitleBaseGraphic.getFontMetrics(); textHeight = fm.getHeight(); textWidth = fm.stringWidth(layer.getName()); paddedTitleWidth = textWidth + (textBoxHorizontalPadding * 2) + (borderWidth * 2); paddedTitleHeight = textHeight + (textBoxVerticalPadding * 2) + (borderWidth * 2); } finally { // dispose of temporary graphics context if (newLegendTitleBaseGraphic != null) { newLegendTitleBaseGraphic.dispose(); } } titleLeftPadding = ((paddedTitleWidth / 2) - ((textWidth + (textBoxHorizontalPadding * 2) + (borderWidth * 2)) / 2)) + textBoxHorizontalPadding; newLegendTitleBase = new BufferedImage(paddedTitleWidth, paddedTitleHeight, BufferedImage.TYPE_INT_ARGB); newLegendTitleBaseGraphic = newLegendTitleBase.createGraphics(); newLegendTitleBaseGraphic.drawImage(newLegendTitleBase, 0, 0, null); newLegendTitleBaseGraphic.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); newLegendTitleBaseGraphic.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); newLegendTitleBaseGraphic.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); newLegendTitleBaseGraphic.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); newLegendTitleBaseGraphic.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); newLegendTitleBaseGraphic.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); newLegendTitleBaseGraphic.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); newLegendTitleBaseGraphic.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); newLegendTitleBaseGraphic.setFont(titleFont); // draw title text fm = newLegendTitleBaseGraphic.getFontMetrics(); newLegendTitleBaseGraphic.setColor(Color.WHITE); newLegendTitleBaseGraphic.drawString(layer.getName(), titleLeftPadding, fm.getAscent() + textBoxVerticalPadding); newLegendTitleBaseGraphic.drawImage(newLegendTitleBase, 0, 0, null); } finally { if (newLegendTitleBaseGraphic != null) { newLegendTitleBaseGraphic.dispose(); } } return newLegendTitleBase; }
From source file:Thumbnail.java
/** * Reads an image in a file and creates a thumbnail in another file. * largestDimension is the largest dimension of the thumbnail, the other dimension is scaled accordingly. * Utilises weighted stepping method to gradually reduce the image size for better results, * i.e. larger steps to start with then smaller steps to finish with. * Note: always writes a JPEG because GIF is protected or something - so always make your outFilename end in 'jpg'. * PNG's with transparency are given white backgrounds *//*from www . ja va2 s .c o m*/ public String createThumbnail(String inFilename, String outFilename, int largestDimension) { try { double scale; int sizeDifference, originalImageLargestDim; if (!inFilename.endsWith(".jpg") && !inFilename.endsWith(".jpeg") && !inFilename.endsWith(".gif") && !inFilename.endsWith(".png")) { return "Error: Unsupported image type, please only either JPG, GIF or PNG"; } else { Image inImage = Toolkit.getDefaultToolkit().getImage(inFilename); if (inImage.getWidth(null) == -1 || inImage.getHeight(null) == -1) { return "Error loading file: \"" + inFilename + "\""; } else { //find biggest dimension if (inImage.getWidth(null) > inImage.getHeight(null)) { scale = (double) largestDimension / (double) inImage.getWidth(null); sizeDifference = inImage.getWidth(null) - largestDimension; originalImageLargestDim = inImage.getWidth(null); } else { scale = (double) largestDimension / (double) inImage.getHeight(null); sizeDifference = inImage.getHeight(null) - largestDimension; originalImageLargestDim = inImage.getHeight(null); } //create an image buffer to draw to BufferedImage outImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); //arbitrary init so code compiles Graphics2D g2d; AffineTransform tx; if (scale < 1.0d) //only scale if desired size is smaller than original { int numSteps = sizeDifference / 100; int stepSize = sizeDifference / numSteps; int stepWeight = stepSize / 2; int heavierStepSize = stepSize + stepWeight; int lighterStepSize = stepSize - stepWeight; int currentStepSize, centerStep; double scaledW = inImage.getWidth(null); double scaledH = inImage.getHeight(null); if (numSteps % 2 == 1) //if there's an odd number of steps centerStep = (int) Math.ceil((double) numSteps / 2d); //find the center step else centerStep = -1; //set it to -1 so it's ignored later Integer intermediateSize = originalImageLargestDim, previousIntermediateSize = originalImageLargestDim; Integer calculatedDim; for (Integer i = 0; i < numSteps; i++) { if (i + 1 != centerStep) //if this isn't the center step { if (i == numSteps - 1) //if this is the last step { //fix the stepsize to account for decimal place errors previously currentStepSize = previousIntermediateSize - largestDimension; } else { if (numSteps - i > numSteps / 2) //if we're in the first half of the reductions currentStepSize = heavierStepSize; else currentStepSize = lighterStepSize; } } else //center step, use natural step size { currentStepSize = stepSize; } intermediateSize = previousIntermediateSize - currentStepSize; scale = (double) intermediateSize / (double) previousIntermediateSize; scaledW = (int) scaledW * scale; scaledH = (int) scaledH * scale; outImage = new BufferedImage((int) scaledW, (int) scaledH, BufferedImage.TYPE_INT_RGB); g2d = outImage.createGraphics(); g2d.setBackground(Color.WHITE); g2d.clearRect(0, 0, outImage.getWidth(), outImage.getHeight()); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); tx = new AffineTransform(); tx.scale(scale, scale); g2d.drawImage(inImage, tx, null); g2d.dispose(); inImage = new ImageIcon(outImage).getImage(); previousIntermediateSize = intermediateSize; } } else { //just copy the original outImage = new BufferedImage(inImage.getWidth(null), inImage.getHeight(null), BufferedImage.TYPE_INT_RGB); g2d = outImage.createGraphics(); g2d.setBackground(Color.WHITE); g2d.clearRect(0, 0, outImage.getWidth(), outImage.getHeight()); tx = new AffineTransform(); tx.setToIdentity(); //use identity matrix so image is copied exactly g2d.drawImage(inImage, tx, null); g2d.dispose(); } //JPEG-encode the image and write to file. OutputStream os = new FileOutputStream(outFilename); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os); encoder.encode(outImage); os.close(); } } } catch (Exception ex) { String errorMsg = ""; errorMsg += "<br>Exception: " + ex.toString(); errorMsg += "<br>Cause = " + ex.getCause(); errorMsg += "<br>Stack Trace = "; StackTraceElement stackTrace[] = ex.getStackTrace(); for (int traceLine = 0; traceLine < stackTrace.length; traceLine++) { errorMsg += "<br>" + stackTrace[traceLine]; } return errorMsg; } return ""; //success }
From source file:com.flexoodb.common.FlexUtils.java
static public void createTextBanner(String text, int fontsize, int width, int height, int x, int y, String outputfile) throws Exception { BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); img.createGraphics();//from w w w . ja v a 2 s. c o m Graphics2D g = (Graphics2D) img.getGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, img.getWidth(), img.getHeight()); Font font = new Font("Arial", Font.BOLD, fontsize); g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); g.setFont(font); g.setColor(Color.BLUE.darker()); g.drawString(text, x, y); ImageIO.write(img, "png", new File(outputfile)); }
From source file:com.flexoodb.common.FlexUtils.java
static public BufferedImage createCaptcha(String text, Color background, Color fontcolor, int fontsize, int width, int height, int x, int y) throws Exception { BufferedImage img = new BufferedImage(width + 10, height, BufferedImage.TYPE_INT_RGB); img.createGraphics();/*from w w w . j av a2 s.com*/ Graphics2D g = (Graphics2D) img.getGraphics(); g.setColor(background); g.fillRect(0, 0, img.getWidth(), img.getHeight()); Font font = new Font("Monospaced", Font.BOLD + Font.ITALIC, fontsize); g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); g.setFont(font); g.setColor(fontcolor); char[] c = text.toCharArray(); java.util.Random random = new java.util.Random(); for (int i = 0; i < c.length; i++) { int j = random.nextInt(9); if (j > 5) { j = j - 5; } else { j = -1 * (j - 2); } char[] c1 = new char[1]; c1[0] = c[i]; g.drawChars(c1, 0, 1, x + (i * 16), y + j); } for (int i = -10; i < 20; i++) { int j = random.nextInt(8); g.drawOval((i + j) - 20, ((i * 8) - (j > 3 ? 4 : -4)), width + 20, height); } //ImageIO.write(img,"png",new File(outputfile)); return img; }
From source file:org.photovault.swingui.PhotoCollectionThumbView.java
private void paintThumbnail(Graphics2D g2, PhotoInfo photo, int startx, int starty, boolean isSelected) { log.debug("paintThumbnail entry " + photo.getUuid()); long startTime = System.currentTimeMillis(); long thumbReadyTime = 0; long thumbDrawnTime = 0; long endTime = 0; // Current position in which attributes can be drawn int ypos = starty + rowHeight / 2; boolean useOldThumbnail = false; Thumbnail thumbnail = null;//from w w w .jav a 2 s . c o m log.debug("finding thumb"); boolean hasThumbnail = photo.hasThumbnail(); log.debug("asked if has thumb"); if (hasThumbnail) { log.debug("Photo " + photo.getUuid() + " has thumbnail"); thumbnail = photo.getThumbnail(); log.debug("got thumbnail"); } else { /* Check if the thumbnail has been just invalidated. If so, use the old one until we get the new thumbnail created. */ thumbnail = photo.getOldThumbnail(); if (thumbnail != null) { useOldThumbnail = true; } else { // No success, use default thumnail. thumbnail = Thumbnail.getDefaultThumbnail(); } // Inform background task scheduler that we have some work to do ctrl.getBackgroundTaskScheduler().registerTaskProducer(this, TaskPriority.CREATE_VISIBLE_THUMBNAIL); } thumbReadyTime = System.currentTimeMillis(); log.debug("starting to draw"); // Find the position for the thumbnail BufferedImage img = thumbnail.getImage(); if (img == null) { thumbnail = Thumbnail.getDefaultThumbnail(); img = thumbnail.getImage(); } float scaleX = ((float) thumbWidth) / ((float) img.getWidth()); float scaleY = ((float) thumbHeight) / ((float) img.getHeight()); float scale = Math.min(scaleX, scaleY); int w = (int) (img.getWidth() * scale); int h = (int) (img.getHeight() * scale); int x = startx + (columnWidth - w) / (int) 2; int y = starty + (rowHeight - h) / (int) 2; log.debug("drawing thumbnail"); // Draw shadow int offset = isSelected ? 2 : 0; int shadowX[] = { x + 3 - offset, x + w + 1 + offset, x + w + 1 + offset }; int shadowY[] = { y + h + 1 + offset, y + h + 1 + offset, y + 3 - offset }; GeneralPath polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD, shadowX.length); polyline.moveTo(shadowX[0], shadowY[0]); for (int index = 1; index < shadowX.length; index++) { polyline.lineTo(shadowX[index], shadowY[index]); } ; BasicStroke shadowStroke = new BasicStroke(4.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); Stroke oldStroke = g2.getStroke(); g2.setStroke(shadowStroke); g2.setColor(Color.DARK_GRAY); g2.draw(polyline); g2.setStroke(oldStroke); // Paint thumbnail g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g2.drawImage(img, new AffineTransform(scale, 0f, 0f, scale, x, y), null); if (useOldThumbnail) { creatingThumbIcon.paintIcon(this, g2, startx + (columnWidth - creatingThumbIcon.getIconWidth()) / (int) 2, starty + (rowHeight - creatingThumbIcon.getIconHeight()) / (int) 2); } log.debug("Drawn, drawing decorations"); if (isSelected) { Stroke prevStroke = g2.getStroke(); Color prevColor = g2.getColor(); g2.setStroke(new BasicStroke(3.0f)); g2.setColor(Color.BLUE); g2.drawRect(x, y, w, h); g2.setColor(prevColor); g2.setStroke(prevStroke); } thumbDrawnTime = System.currentTimeMillis(); boolean drawAttrs = (thumbWidth >= 100); if (drawAttrs) { // Increase ypos so that attributes are drawn under the image ypos += ((int) h) / 2 + 3; // Draw the attributes // Draw the qualoity icon to the upper left corner of the thumbnail int quality = photo.getQuality(); if (showQuality && quality != 0) { int qx = startx + (columnWidth - quality * starIcon.getIconWidth()) / (int) 2; for (int n = 0; n < quality; n++) { starIcon.paintIcon(this, g2, qx, ypos); qx += starIcon.getIconWidth(); } ypos += starIcon.getIconHeight(); } ypos += 6; if (photo.getRawSettings() != null) { // Draw the "RAW" icon int rx = startx + (columnWidth + w - rawIcon.getIconWidth()) / (int) 2 - 5; int ry = starty + (columnWidth - h - rawIcon.getIconHeight()) / (int) 2 + 5; rawIcon.paintIcon(this, g2, rx, ry); } if (photo.getHistory().getHeads().size() > 1) { // Draw the "unresolved conflicts" icon int rx = startx + (columnWidth + w - 10) / (int) 2 - 20; int ry = starty + (columnWidth - h - 10) / (int) 2; g2.setColor(Color.RED); g2.fillRect(rx, ry, 10, 10); } Color prevBkg = g2.getBackground(); if (isSelected) { g2.setBackground(Color.BLUE); } else { g2.setBackground(this.getBackground()); } Font attrFont = new Font("Arial", Font.PLAIN, 10); FontRenderContext frc = g2.getFontRenderContext(); if (showDate && photo.getShootTime() != null) { FuzzyDate fd = new FuzzyDate(photo.getShootTime(), photo.getTimeAccuracy()); String dateStr = fd.format(); TextLayout txt = new TextLayout(dateStr, attrFont, frc); // Calculate the position for the text Rectangle2D bounds = txt.getBounds(); int xpos = startx + ((int) (columnWidth - bounds.getWidth())) / 2 - (int) bounds.getMinX(); g2.clearRect(xpos - 2, ypos - 2, (int) bounds.getWidth() + 4, (int) bounds.getHeight() + 4); txt.draw(g2, xpos, (int) (ypos + bounds.getHeight())); ypos += bounds.getHeight() + 4; } String shootPlace = photo.getShootingPlace(); if (showPlace && shootPlace != null && shootPlace.length() > 0) { TextLayout txt = new TextLayout(photo.getShootingPlace(), attrFont, frc); // Calculate the position for the text Rectangle2D bounds = txt.getBounds(); int xpos = startx + ((int) (columnWidth - bounds.getWidth())) / 2 - (int) bounds.getMinX(); g2.clearRect(xpos - 2, ypos - 2, (int) bounds.getWidth() + 4, (int) bounds.getHeight() + 4); txt.draw(g2, xpos, (int) (ypos + bounds.getHeight())); ypos += bounds.getHeight() + 4; } g2.setBackground(prevBkg); } endTime = System.currentTimeMillis(); log.debug("paintThumbnail: exit " + photo.getUuid()); log.debug("Thumb fetch " + (thumbReadyTime - startTime) + " ms"); log.debug("Thumb draw " + (thumbDrawnTime - thumbReadyTime) + " ms"); log.debug("Deacoration draw " + (endTime - thumbDrawnTime) + " ms"); log.debug("Total " + (endTime - startTime) + " ms"); }
From source file:org.yccheok.jstock.gui.charting.ChartLayerUI.java
private void drawInformationBox(Graphics2D g2, JXLayer<? extends V> layer) { if (JStock.instance().getJStockOptions() .getYellowInformationBoxOption() == JStockOptions.YellowInformationBoxOption.Hide) { return;/*ww w. j a v a 2 s. com*/ } final Font oldFont = g2.getFont(); final Font paramFont = oldFont; final FontMetrics paramFontMetrics = g2.getFontMetrics(paramFont); final Font valueFont = oldFont.deriveFont(oldFont.getStyle() | Font.BOLD, (float) oldFont.getSize() + 1); final FontMetrics valueFontMetrics = g2.getFontMetrics(valueFont); final Font dateFont = oldFont.deriveFont((float) oldFont.getSize() - 1); final FontMetrics dateFontMetrics = g2.getFontMetrics(dateFont); final List<ChartData> chartDatas = this.chartJDialog.getChartDatas(); List<String> values = new ArrayList<String>(); final ChartData chartData = chartDatas.get(this.mainTraceInfo.getDataIndex()); // Number formats are generally not synchronized. It is recommended to create separate format instances for each thread. // If multiple threads access a format concurrently, it must be synchronized externally. // http://stackoverflow.com/questions/2213410/usage-of-decimalformat-for-the-following-case final DecimalFormat integerFormat = new DecimalFormat("###,###"); // It is common to use OHLC for chat, instead of using PrevPrice. values.add(org.yccheok.jstock.gui.Utils.stockPriceDecimalFormat(chartData.openPrice)); values.add(org.yccheok.jstock.gui.Utils.stockPriceDecimalFormat(chartData.highPrice)); values.add(org.yccheok.jstock.gui.Utils.stockPriceDecimalFormat(chartData.lowPrice)); values.add(org.yccheok.jstock.gui.Utils.stockPriceDecimalFormat(chartData.lastPrice)); values.add(integerFormat.format(chartData.volume)); final List<String> indicatorParams = new ArrayList<String>(); final List<String> indicatorValues = new ArrayList<String>(); final DecimalFormat decimalFormat = new DecimalFormat("0.00"); for (TraceInfo indicatorTraceInfo : this.indicatorTraceInfos) { final int plotIndex = indicatorTraceInfo.getPlotIndex(); final int seriesIndex = indicatorTraceInfo.getSeriesIndex(); final int dataIndex = indicatorTraceInfo.getDataIndex(); final String name = this.getLegendName(plotIndex, seriesIndex); final Number value = this.getValue(plotIndex, seriesIndex, dataIndex); if (name == null || value == null) { continue; } indicatorParams.add(name); indicatorValues.add(decimalFormat.format(value)); } assert (values.size() == params.size()); int index = 0; final int paramValueWidthMargin = 10; final int paramValueHeightMargin = 0; // Slightly larger than dateInfoHeightMargin, as font for indicator is // larger than date's. final int infoIndicatorHeightMargin = 8; int maxInfoWidth = -1; // paramFontMetrics will always "smaller" than valueFontMetrics. int totalInfoHeight = Math.max(paramFontMetrics.getHeight(), valueFontMetrics.getHeight()) * values.size() + paramValueHeightMargin * (values.size() - 1); for (String param : params) { final String value = values.get(index++); final int paramStringWidth = paramFontMetrics.stringWidth(param + ":") + paramValueWidthMargin + valueFontMetrics.stringWidth(value); if (maxInfoWidth < paramStringWidth) { maxInfoWidth = paramStringWidth; } } if (indicatorValues.size() > 0) { totalInfoHeight += infoIndicatorHeightMargin; totalInfoHeight += Math.max(paramFontMetrics.getHeight(), valueFontMetrics.getHeight()) * indicatorValues.size() + paramValueHeightMargin * (indicatorValues.size() - 1); index = 0; for (String indicatorParam : indicatorParams) { final String indicatorValue = indicatorValues.get(index++); final int paramStringWidth = paramFontMetrics.stringWidth(indicatorParam + ":") + paramValueWidthMargin + valueFontMetrics.stringWidth(indicatorValue); if (maxInfoWidth < paramStringWidth) { maxInfoWidth = paramStringWidth; } } } final Date date = new Date(chartData.timestamp); // Date formats are not synchronized. It is recommended to create separate format instances for each thread. // If multiple threads access a format concurrently, it must be synchronized externally. final SimpleDateFormat simpleDateFormat = this.simpleDataFormatThreadLocal.get(); final String dateString = simpleDateFormat.format(date); final int dateStringWidth = dateFontMetrics.stringWidth(dateString); final int dateStringHeight = dateFontMetrics.getHeight(); // We want to avoid information box from keep changing its width while // user moves along the mouse. This will prevent user from feeling, // information box is flickering, which is uncomfortable to user's eye. final int maxStringWidth = Math.max(dateFontMetrics.stringWidth(longDateString), Math.max(this.maxWidth, Math.max(dateStringWidth, maxInfoWidth))); if (maxStringWidth > this.maxWidth) { this.maxWidth = maxStringWidth; } final int dateInfoHeightMargin = 5; final int maxStringHeight = dateStringHeight + dateInfoHeightMargin + totalInfoHeight; final int padding = 5; final int boxPointMargin = 8; final int width = maxStringWidth + (padding << 1); final int height = maxStringHeight + (padding << 1); /* Get Border Rect Information. */ /* fillRect(1, 1, 1, 1); // O is rect pixel xxx xOx xxx drawRect(0, 0, 2, 2); // O is rect pixel OOO OxO OOO */ final int borderWidth = width + 2; final int borderHeight = height + 2; // On left side of the ball. final double suggestedBorderX = this.mainTraceInfo.getPoint().getX() - borderWidth - boxPointMargin; final double suggestedBorderY = this.mainTraceInfo.getPoint().getY() - (borderHeight >> 1); double bestBorderX = 0; double bestBorderY = 0; if (JStock.instance().getJStockOptions() .getYellowInformationBoxOption() == JStockOptions.YellowInformationBoxOption.Stay) { if (this.mainTraceInfo.getPoint() .getX() > ((int) (this.mainDrawArea.getX() + this.mainDrawArea.getWidth() + 0.5) >> 1)) { bestBorderX = this.mainDrawArea.getX(); bestBorderY = this.mainDrawArea.getY(); } else { bestBorderX = this.mainDrawArea.getX() + this.mainDrawArea.getWidth() - borderWidth; bestBorderY = this.mainDrawArea.getY(); } } else { assert (JStock.instance().getJStockOptions() .getYellowInformationBoxOption() == JStockOptions.YellowInformationBoxOption.Follow); bestBorderX = suggestedBorderX > this.mainDrawArea.getX() ? (suggestedBorderX + borderWidth) < (this.mainDrawArea.getX() + this.mainDrawArea.getWidth()) ? suggestedBorderX : this.mainDrawArea.getX() + this.mainDrawArea.getWidth() - borderWidth - boxPointMargin : this.mainTraceInfo.getPoint().getX() + boxPointMargin; bestBorderY = suggestedBorderY > this.mainDrawArea.getY() ? (suggestedBorderY + borderHeight) < (this.mainDrawArea.getY() + this.mainDrawArea.getHeight()) ? suggestedBorderY : this.mainDrawArea.getY() + this.mainDrawArea.getHeight() - borderHeight - boxPointMargin : this.mainDrawArea.getY() + boxPointMargin; } final double x = bestBorderX + 1; final double y = bestBorderY + 1; final Object oldValueAntiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); final Composite oldComposite = g2.getComposite(); final Color oldColor = g2.getColor(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(COLOR_BORDER); g2.drawRoundRect((int) (bestBorderX + 0.5), (int) (bestBorderY + 0.5), borderWidth - 1, borderHeight - 1, 15, 15); g2.setColor(COLOR_BACKGROUND); g2.setComposite(Utils.makeComposite(0.75f)); g2.fillRoundRect((int) (x + 0.5), (int) (y + 0.5), width, height, 15, 15); g2.setComposite(oldComposite); g2.setColor(oldColor); int yy = (int) (y + padding + dateFontMetrics.getAscent() + 0.5); g2.setFont(dateFont); g2.setColor(COLOR_BLUE); g2.drawString(dateString, (int) (((width - dateFontMetrics.stringWidth(dateString)) >> 1) + x + 0.5), yy); index = 0; yy += dateFontMetrics.getDescent() + dateInfoHeightMargin + valueFontMetrics.getAscent(); final String CLOSE_STR = GUIBundle.getString("StockHistory_Close"); for (String param : params) { final String value = values.get(index++); g2.setColor(Color.BLACK); if (param.equals(CLOSE_STR)) { // It is common to use OHLC for chat, instead of using PrevPrice. final double changePrice = chartData.lastPrice - chartData.openPrice; if (changePrice > 0.0) { g2.setColor(JStockOptions.DEFAULT_HIGHER_NUMERICAL_VALUE_FOREGROUND_COLOR); } else if (changePrice < 0.0) { g2.setColor(JStockOptions.DEFAULT_LOWER_NUMERICAL_VALUE_FOREGROUND_COLOR); } } g2.setFont(paramFont); g2.drawString(param + ":", (int) (padding + x + 0.5), yy); g2.setFont(valueFont); g2.drawString(value, (int) (width - padding - valueFontMetrics.stringWidth(value) + x + 0.5), yy); // Same as yy += valueFontMetrics.getDescent() + paramValueHeightMargin + valueFontMetrics.getAscent() yy += paramValueHeightMargin + valueFontMetrics.getHeight(); } g2.setColor(Color.BLACK); yy -= paramValueHeightMargin; yy += infoIndicatorHeightMargin; index = 0; for (String indicatorParam : indicatorParams) { final String indicatorValue = indicatorValues.get(index++); g2.setFont(paramFont); g2.drawString(indicatorParam + ":", (int) (padding + x + 0.5), yy); g2.setFont(valueFont); g2.drawString(indicatorValue, (int) (width - padding - valueFontMetrics.stringWidth(indicatorValue) + x + 0.5), yy); // Same as yy += valueFontMetrics.getDescent() + paramValueHeightMargin + valueFontMetrics.getAscent() yy += paramValueHeightMargin + valueFontMetrics.getHeight(); } g2.setColor(oldColor); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldValueAntiAlias); g2.setFont(oldFont); }