Example usage for java.awt Graphics2D setRenderingHint

List of usage examples for java.awt Graphics2D setRenderingHint

Introduction

In this page you can find the example usage for java.awt Graphics2D setRenderingHint.

Prototype

public abstract void setRenderingHint(Key hintKey, Object hintValue);

Source Link

Document

Sets the value of a single preference for the rendering algorithms.

Usage

From source file:org.openbravo.erpCommon.utility.Utility.java

/**
 * Resize an image giving the image input as byte[]
 * /*www  .j ava  2 s.c  om*/
 * @param bytea
 *          The contents of the image as a byte array
 * @param maxW
 *          Maximum width that the image will be resized.
 * @param maxH
 *          Maximum height that the image will be resized.
 * @param maintainAspectRatio
 *          If true, the image will be resized exactly to the maximum parameters. If false, the
 *          imagen will be resized closest to the maximum parameters keeping aspect ratio
 * @param canMakeLargerImage
 *          If true and the original image is smaller than maximum parameters, the resized image
 *          could be larger than the original one. If false, not.
 * @return The resized image
 */
public static byte[] resizeImageByte(byte[] bytea, int maxW, int maxH, boolean maintainAspectRatio,
        boolean canMakeLargerImage) throws IOException {
    ByteArrayInputStream bis = new ByteArrayInputStream(bytea);
    BufferedImage rImage = ImageIO.read(bis);
    int newW = maxW;
    int newH = maxH;
    int oldW = rImage.getWidth();
    int oldH = rImage.getHeight();
    if (newW == 0 && newH == 0) {
        return bytea;
    } else if (newW == 0) {
        if (maintainAspectRatio) {
            newW = 99999;
        } else {
            newW = oldW;
        }
    } else if (newH == 0) {
        if (maintainAspectRatio) {
            newH = 99999;
        } else {
            newH = oldH;
        }
    }
    if (oldW == newW && oldH == newH) {
        return bytea;
    }
    if (!canMakeLargerImage && newW > oldW && newH > oldH) {
        return bytea;
    }
    if (maintainAspectRatio) {
        float oldRatio = (float) oldW / (float) oldH;
        float newRatio = (float) newW / (float) newH;
        if (oldRatio < newRatio) {
            newW = (int) (newH * oldRatio);
        } else if (oldRatio > newRatio) {
            newH = (int) (newW / oldRatio);
        }
    }
    BufferedImage dimg = new BufferedImage(newW, newH, rImage.getType());
    Graphics2D g = dimg.createGraphics();
    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.drawImage(rImage, 0, 0, newW, newH, 0, 0, oldW, oldH, null);
    g.dispose();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    String mimeType = MimeTypeUtil.getInstance().getMimeTypeName(bytea);
    if (mimeType.contains("jpeg")) {
        mimeType = "jpeg";
    } else if (mimeType.contains("png")) {
        mimeType = "png";
    } else if (mimeType.contains("gif")) {
        mimeType = "gif";
    } else if (mimeType.contains("bmp")) {
        mimeType = "bmp";
    } else {
        return bytea;
    }
    ImageIO.write(dimg, mimeType, baos);
    byte[] bytesOut = baos.toByteArray();
    return bytesOut;
}

From source file:PictureScaler.java

/**
 * Convenience method that returns a scaled instance of the
 * provided BufferedImage.//from   w  w w. j a v  a  2 s  .  c o  m
 * 
 * 
 * @param img the original image to be scaled
 * @param targetWidth the desired width of the scaled instance,
 *    in pixels
 * @param targetHeight the desired height of the scaled instance,
 *    in pixels
 * @param hint one of the rendering hints that corresponds to
 *    RenderingHints.KEY_INTERPOLATION (e.g.
 *    RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR,
 *    RenderingHints.VALUE_INTERPOLATION_BILINEAR,
 *    RenderingHints.VALUE_INTERPOLATION_BICUBIC)
 * @param progressiveBilinear if true, this method will use a multi-step
 *    scaling technique that provides higher quality than the usual
 *    one-step technique (only useful in down-scaling cases, where
 *    targetWidth or targetHeight is
 *    smaller than the original dimensions)
 * @return a scaled version of the original BufferedImage
 */
public BufferedImage getFasterScaledInstance(BufferedImage img, int targetWidth, int targetHeight, Object hint,
        boolean progressiveBilinear) {
    int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB
            : BufferedImage.TYPE_INT_ARGB;
    BufferedImage ret = img;
    BufferedImage scratchImage = null;
    Graphics2D g2 = null;
    int w, h;
    int prevW = ret.getWidth();
    int prevH = ret.getHeight();
    boolean isTranslucent = img.getTransparency() != Transparency.OPAQUE;

    if (progressiveBilinear) {
        // Use multi-step technique: start with original size, then
        // scale down in multiple passes with drawImage()
        // until the target size is reached
        w = img.getWidth();
        h = img.getHeight();
    } else {
        // Use one-step technique: scale directly from original
        // size to target size with a single drawImage() call
        w = targetWidth;
        h = targetHeight;
    }

    do {
        if (progressiveBilinear && w > targetWidth) {
            w /= 2;
            if (w < targetWidth) {
                w = targetWidth;
            }
        }

        if (progressiveBilinear && h > targetHeight) {
            h /= 2;
            if (h < targetHeight) {
                h = targetHeight;
            }
        }

        if (scratchImage == null || isTranslucent) {
            // Use a single scratch buffer for all iterations
            // and then copy to the final, correctly-sized image
            // before returning
            scratchImage = new BufferedImage(w, h, type);
            g2 = scratchImage.createGraphics();
        }
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
        g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null);
        prevW = w;
        prevH = h;

        ret = scratchImage;
    } while (w != targetWidth || h != targetHeight);

    if (g2 != null) {
        g2.dispose();
    }

    // If we used a scratch buffer that is larger than our target size,
    // create an image of the right size and copy the results into it
    if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) {
        scratchImage = new BufferedImage(targetWidth, targetHeight, type);
        g2 = scratchImage.createGraphics();
        g2.drawImage(ret, 0, 0, null);
        g2.dispose();
        ret = scratchImage;
    }

    return ret;
}

From source file:tufts.vue.RichTextBox.java

public void paintComponent(Graphics g) {
    if (TestDebug || DEBUG.TEXT)
        out("paintComponent @ " + getX() + "," + getY() + " parent=" + getParent());

    final MapViewer viewer = (MapViewer) javax.swing.SwingUtilities.getAncestorOfClass(MapViewer.class, this);
    Graphics2D g2d = (Graphics2D) g;
    if (viewer != null) {
        g2d.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, viewer.AA_ON);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);

    }//from w  w w.  jav  a2s.c o  m
    // turn on anti-aliasing -- the cursor repaint loop doesn't
    // set anti-aliasing, so text goes jiggy around cursor/in selection if we don't do this
    ////  g.clipRect(0, 0,getWidth(), getAdjustedHeight());

    super.paintComponent(g2d);

    //super.paintComponent(g);
    if (true) {
        // draw a border (we don't want to add one because that changes the preferred size at a bad time)
        //  g.setColor(Color.gray);
        g.setClip(null);
        final int xpad = 1;
        final int ypad = 1;
        g.drawRect(-xpad, -ypad, (int) ((getWidth()) + xpad * 2 - 1), (int) ((getHeight()) + ypad * 2 - 1));
    }
}

From source file:org.pentaho.reporting.engine.classic.core.modules.output.pageable.graphics.internal.LogicalPageDrawable.java

protected boolean drawDrawable(final RenderableReplacedContentBox content, final Graphics2D g2,
        final DrawableWrapper d) {
    final double x = StrictGeomUtility.toExternalValue(content.getX());
    final double y = StrictGeomUtility.toExternalValue(content.getY());
    final double width = StrictGeomUtility.toExternalValue(content.getWidth());
    final double height = StrictGeomUtility.toExternalValue(content.getHeight());

    if ((width < 0 || height < 0) || (width == 0 && height == 0)) {
        return false;
    }//  www  .  j  a  v a  2 s  .  c  o m

    final Graphics2D clone = (Graphics2D) g2.create();

    final StyleSheet styleSheet = content.getStyleSheet();
    final Object attribute = styleSheet.getStyleProperty(ElementStyleKeys.ANTI_ALIASING);
    if (attribute != null) {
        if (Boolean.TRUE.equals(attribute)) {
            clone.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        } else if (Boolean.FALSE.equals(attribute)) {
            clone.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        }

    }
    if (RenderUtility.isFontSmooth(styleSheet, metaData)) {
        clone.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    } else {
        clone.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
    }

    if (strictClipping == false) {
        final double extraPadding;
        final Object o = styleSheet.getStyleProperty(ElementStyleKeys.STROKE);
        if (o instanceof BasicStroke) {
            final BasicStroke stroke = (BasicStroke) o;
            extraPadding = stroke.getLineWidth() / 2.0;
        } else {
            extraPadding = 0.5;
        }

        final Rectangle2D.Double clipBounds = new Rectangle2D.Double(x - extraPadding, y - extraPadding,
                width + 2 * extraPadding, height + 2 * extraPadding);

        clone.clip(clipBounds);
        clone.translate(x, y);
    } else {
        final Rectangle2D.Double clipBounds = new Rectangle2D.Double(x, y, width + 1, height + 1);

        clone.clip(clipBounds);
        clone.translate(x, y);
    }
    configureGraphics(styleSheet, clone);
    configureStroke(styleSheet, clone);
    final Rectangle2D.Double bounds = new Rectangle2D.Double(0, 0, width, height);
    d.draw(clone, bounds);
    clone.dispose();
    return true;
}

From source file:edu.ku.brc.ui.UIRegistry.java

/**
 * Writes a string message into the BufferedImage on GlassPane and sets the main component's visibility to false and
 * shows the GlassPane./*  www .j a va 2  s  .  c  o  m*/
 * @param msg the message
 * @param pointSize the Font point size for the message to be writen in
 */
public static GhostGlassPane writeGlassPaneMsg(final String msg, final int pointSize) {
    GhostGlassPane glassPane = getGlassPane();
    if (glassPane != null) {
        glassPane.finishDnD();
    }

    glassPane.setMaskingEvents(true);

    Component mainComp = get(MAINPANE);
    if (mainComp != null && glassPane != null) {
        JFrame frame = (JFrame) get(FRAME);
        frameRect = frame.getBounds();

        int y = 0;
        JMenuBar menuBar = null;
        Dimension size = mainComp.getSize();
        if (UIHelper.getOSType() != UIHelper.OSTYPE.MacOSX) {
            menuBar = frame.getJMenuBar();
            size.height += menuBar.getSize().height;
            y += menuBar.getSize().height;
        }
        BufferedImage buffer = getGlassPaneBufferedImage(size.width, size.height);
        Graphics2D g2 = buffer.createGraphics();
        if (menuBar != null) {
            menuBar.paint(g2);
        }
        g2.translate(0, y);
        mainComp.paint(g2);
        g2.translate(0, -y);

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(new Color(255, 255, 255, 128));
        g2.fillRect(0, 0, size.width, size.height);

        g2.setFont(new Font((new JLabel()).getFont().getName(), Font.BOLD, pointSize));
        FontMetrics fm = g2.getFontMetrics();

        int tw = fm.stringWidth(msg);
        int th = fm.getHeight();
        int tx = (size.width - tw) / 2;
        int ty = (size.height - th) / 2;

        int expand = 20;
        int arc = expand * 2;
        g2.setColor(Color.WHITE);
        g2.fillRoundRect(tx - (expand / 2), ty - fm.getAscent() - (expand / 2), tw + expand, th + expand, arc,
                arc);

        g2.setColor(Color.DARK_GRAY);
        g2.drawRoundRect(tx - (expand / 2), ty - fm.getAscent() - (expand / 2), tw + expand, th + expand, arc,
                arc);

        g2.setColor(Color.BLACK);
        g2.drawString(msg, tx, ty);
        g2.dispose();

        glassPane.setImage(buffer);
        glassPane.setPoint(new Point(0, 0), GhostGlassPane.ImagePaintMode.ABSOLUTE);
        glassPane.setOffset(new Point(0, 0));

        glassPane.setVisible(true);
        mainComp.setVisible(false);

        //Using paintImmediately fixes problems with glass pane not showing, such as for workbench saves initialed
        //during workbench or app shutdown. Don't know if there is a better way to fix it.
        //glassPane.repaint();
        glassPane.paintImmediately(glassPane.getBounds());
        showingGlassPane = true;
    }

    return glassPane;
}

From source file:org.yccheok.jstock.gui.charting.InvestmentFlowLayerUI.java

@Override
protected void paintLayer(Graphics2D g2, JXLayer<? extends V> layer) {
    super.paintLayer(g2, layer);

    final ChartPanel chartPanel = ((ChartPanel) layer.getView());

    final Rectangle2D _plotArea = chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea();

    this.drawArea.setRect(_plotArea);

    if (false == this.investmentFlowChartJDialog.isFinishLookUpPrice()) {
        this.drawBusyBox(g2, layer);
    }//from w  w w  .j  a  v a2s .  c  o  m

    if (this.investPoint != null) {
        final int RADIUS = 8;
        final Object oldValueAntiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
        final Color oldColor = g2.getColor();

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(COLOR_RED);
        g2.fillOval((int) (this.investPoint.getX() - (RADIUS >> 1) + 0.5),
                (int) (this.investPoint.getY() - (RADIUS >> 1) + 0.5), RADIUS, RADIUS);
        g2.setColor(oldColor);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldValueAntiAlias);
        this.updateInvestInformationBox(g2);
    }

    if (this.ROIPoint != null) {
        final int RADIUS = 8;
        final Object oldValueAntiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
        final Color oldColor = g2.getColor();

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(COLOR_BLUE);
        g2.fillOval((int) (this.ROIPoint.getX() - (RADIUS >> 1) + 0.5),
                (int) (this.ROIPoint.getY() - (RADIUS >> 1) + 0.5), RADIUS, RADIUS);
        g2.setColor(oldColor);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldValueAntiAlias);
        this.updateROIInformationBox(g2);
    }

    this.solveConflict();

    if (this.investPoint != null) {
        this.drawInformationBox(g2, this.investmentFlowChartJDialog.getInvestActivities(this.investPointIndex),
                investRect, investParams, investValues,
                GUIBundle.getString("InvestmentFlowLayerUI_Total_Invest"), totalInvestValue,
                COLOR_RED_BACKGROUND, COLOR_RED_BORDER);
    }

    if (this.ROIPoint != null) {
        this.drawInformationBox(g2, this.investmentFlowChartJDialog.getROIActivities(this.ROIPointIndex),
                ROIRect, ROIParams, ROIValues, GUIBundle.getString("InvestmentFlowLayerUI_Total_Return"),
                totalROIValue, COLOR_BLUE_BACKGROUND, COLOR_BLUE_BORDER);
    }

    this.drawTitle(g2);
}

From source file:org.pentaho.reporting.engine.classic.core.modules.output.pageable.graphics.internal.LogicalPageDrawable.java

/**
 * Renders the glyphs stored in the text node.
 *
 * @param renderableText// w  w w  . ja  va 2  s.  c  om
 *          the text node that should be rendered.
 * @param contentX2
 */
protected void drawText(final RenderableText renderableText, final long contentX2) {
    if (renderableText.getLength() == 0) {
        // This text is empty.
        return;
    }

    final long posX = renderableText.getX();
    final long posY = renderableText.getY();

    final Graphics2D g2;
    if (getTextSpec() == null) {
        g2 = (Graphics2D) getGraphics().create();
        final StyleSheet layoutContext = renderableText.getStyleSheet();
        configureGraphics(layoutContext, g2);
        g2.setStroke(LogicalPageDrawable.DEFAULT_STROKE);

        if (RenderUtility.isFontSmooth(layoutContext, metaData)) {
            g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        } else {
            g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
        }
    } else {
        g2 = getTextSpec().getGraphics();
    }

    // This shifting is necessary to make sure that all text is rendered like in the previous versions.
    // In the earlier versions, we did not really obey to the baselines of the text, we just hoped and prayed.
    // Therefore, all text was printed at the bottom of the text elements. With the introduction of the full
    // font metrics setting, this situation got a little bit better, for the price that text-elements became
    // nearly unpredictable ..
    //
    // The code below may be weird, but at least it is predictable weird.

    final FontMetrics fm = g2.getFontMetrics();
    final Rectangle2D rect = fm.getMaxCharBounds(g2);
    final long awtBaseLine = StrictGeomUtility.toInternalValue(-rect.getY());

    final GlyphList gs = renderableText.getGlyphs();
    if (metaData.isFeatureSupported(OutputProcessorFeature.FAST_FONTRENDERING)
            && isNormalTextSpacing(renderableText)) {
        final int maxLength = renderableText.computeMaximumTextSize(contentX2);
        final String text = gs.getText(renderableText.getOffset(), maxLength, codePointBuffer);
        final float y = (float) StrictGeomUtility.toExternalValue(posY + awtBaseLine);
        g2.drawString(text, (float) StrictGeomUtility.toExternalValue(posX), y);
    } else {
        final ExtendedBaselineInfo baselineInfo = renderableText.getBaselineInfo();
        final int maxPos = renderableText.getOffset() + renderableText.computeMaximumTextSize(contentX2);
        long runningPos = posX;
        final long baseline = baselineInfo.getBaseline(baselineInfo.getDominantBaseline());
        final long baselineDelta = awtBaseLine - baseline;
        final float y = (float) (StrictGeomUtility.toExternalValue(posY + awtBaseLine + baselineDelta));
        for (int i = renderableText.getOffset(); i < maxPos; i++) {
            final Glyph g = gs.getGlyph(i);
            g2.drawString(gs.getGlyphAsString(i, codePointBuffer),
                    (float) StrictGeomUtility.toExternalValue(runningPos), y);
            runningPos += RenderableText.convert(g.getWidth()) + g.getSpacing().getMinimum();
        }
    }
    g2.dispose();
}

From source file:com.moviejukebox.plugin.DefaultImagePlugin.java

/**
 * Draw rounded corners on the image//from w w  w  .j a v a 2 s .  c  o  m
 *
 * @param bi
 * @return
 */
protected BufferedImage drawRoundCorners(BufferedImage bi) {
    BufferedImage newImg = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D newGraphics = newImg.createGraphics();
    newGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    RoundRectangle2D.Double rect = new RoundRectangle2D.Double(0, 0, bi.getWidth(), bi.getHeight(),
            rcqFactor * cornerRadius, rcqFactor * cornerRadius);
    newGraphics.setClip(rect);
    newGraphics.drawImage(bi, 0, 0, null);

    newGraphics.dispose();
    return newImg;
}

From source file:com.centurylink.mdw.designer.pages.ExportHelper.java

public byte[] printImage(float scale, CanvasCommon canvas, Dimension graphsize, String format)
        throws IOException {
    int h_margin = 72, v_margin = 72;
    BufferedImage image = new BufferedImage(graphsize.width + h_margin, graphsize.height + v_margin,
            BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = image.createGraphics();
    if (scale > 0)
        g2.scale(scale, scale);/*from  www  .jav a2s.c  om*/
    g2.setBackground(Color.WHITE);
    g2.clearRect(0, 0, image.getWidth(), image.getHeight());
    // canvas.paint(g2);
    Color bgsave = canvas.getBackground();
    boolean edsave = canvas.editable;
    canvas.editable = false;
    canvas.setBackground(Color.white);
    g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    canvas.paintComponent(g2);
    canvas.setBackground(bgsave);
    canvas.editable = edsave;
    g2.dispose();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ImageIO.write(image, format, baos);
    image = null;
    Runtime r = Runtime.getRuntime();
    r.gc();
    return baos.toByteArray();
}

From source file:com.moviejukebox.plugin.DefaultImagePlugin.java

/**
 * Draw an overlay on the image, such as a box cover specific for videosource, container, certification if wanted
 *
 * @param movie/*from  w  w w . j  a va 2 s  .  com*/
 * @param bi
 * @param offsetY
 * @param offsetX
 * @return
 */
private BufferedImage drawOverlay(Movie movie, BufferedImage bi, int offsetX, int offsetY) {

    String source;
    if (overlaySource.equalsIgnoreCase(VIDEOSOURCE)) {
        source = movie.getVideoSource();
    } else if (overlaySource.equalsIgnoreCase(CERTIFICATION)) {
        source = movie.getCertification();
    } else if (overlaySource.equalsIgnoreCase(CONTAINER)) {
        source = movie.getContainer();
    } else {
        source = DEFAULT;
    }

    // Make sure the source is formatted correctly
    source = source.toLowerCase().trim();

    // Check for a blank or an UNKNOWN source and correct it
    if (StringTools.isNotValidString(source)) {
        source = DEFAULT;
    }

    String overlayFilename = source + "_overlay_" + imageType + ".png";

    try {
        BufferedImage biOverlay = GraphicTools.loadJPEGImage(getResourcesPath() + overlayFilename);

        BufferedImage returnBI = new BufferedImage(biOverlay.getWidth(), biOverlay.getHeight(),
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2BI = returnBI.createGraphics();
        g2BI.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2BI.drawImage(bi, offsetX, offsetY, offsetX + bi.getWidth(), offsetY + bi.getHeight(), 0, 0,
                bi.getWidth(), bi.getHeight(), null);
        g2BI.drawImage(biOverlay, 0, 0, null);

        g2BI.dispose();

        return returnBI;
    } catch (FileNotFoundException ex) {
        LOG.warn(LOG_FAILED_TO_LOAD, overlayFilename);
    } catch (IOException ex) {
        LOG.warn("Failed drawing overlay to {}. Please check that {} is in the resources directory.",
                movie.getBaseName(), overlayFilename);
    }

    return bi;
}