Created
November 28, 2017 03:16
-
-
Save eulereadgbe/b4af8b0db84d9aabd1832fb4aa9224a7 to your computer and use it in GitHub Desktop.
Media filter to create watermarked image
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* The contents of this file are subject to the license and copyright | |
* detailed in the LICENSE and NOTICE files at the root of the source | |
* tree and available online at | |
* | |
* http://www.dspace.org/license/ | |
*/ | |
package org.dspace.app.mediafilter; | |
import org.dspace.core.ConfigurationManager; | |
import javax.imageio.ImageIO; | |
import java.awt.*; | |
import java.awt.image.BufferedImage; | |
import java.awt.image.BufferedImageOp; | |
import java.awt.image.ConvolveOp; | |
import java.awt.image.Kernel; | |
import java.io.ByteArrayInputStream; | |
import java.io.ByteArrayOutputStream; | |
import java.io.InputStream; | |
import java.awt.geom.AffineTransform; | |
import java.awt.geom.Rectangle2D; | |
import static org.dspace.app.mediafilter.MediaFilterManager.getCurrentItem; | |
import javax.swing.ImageIcon; | |
/** | |
* Filter image bitstreams, scaling the image to be within the bounds of | |
* thumbnail.maxwidth, thumbnail.maxheight, the size we want our thumbnail to be | |
* no bigger than. Creates only JPEGs. | |
* | |
* @author Jason Sherman <jsherman@usao.edu> | |
*/ | |
public class WaterMark extends MediaFilter | |
{ | |
public String getFilteredName(String oldFilename) | |
{ | |
return oldFilename + ".preview.jpg"; | |
} | |
/** | |
* @return String bundle name | |
* | |
*/ | |
public String getBundleName() | |
{ | |
return "WATERMARK_PREVIEW"; | |
} | |
/** | |
* @return String bitstreamformat | |
*/ | |
public String getFormatString() | |
{ | |
return "JPEG"; | |
} | |
/** | |
* @return String description | |
*/ | |
public String getDescription() | |
{ | |
return "Generated Watermarked Preview"; | |
} | |
/** | |
* @param source | |
* source input stream | |
* | |
* @return InputStream the resulting input stream | |
*/ | |
public InputStream getDestinationStream(InputStream source) | |
throws Exception | |
{ | |
// read in bitstream's image | |
BufferedImage buf = ImageIO.read(source); | |
// get config params | |
float xmax = (float) ConfigurationManager | |
.getIntProperty("webui.preview.maxwidth"); | |
float ymax = (float) ConfigurationManager | |
.getIntProperty("webui.preview.maxheight"); | |
boolean blurring = (boolean) ConfigurationManager | |
.getBooleanProperty("webui.preview.blurring"); | |
boolean hqscaling = (boolean) ConfigurationManager | |
.getBooleanProperty("webui.preview.hqscaling"); | |
int brandHeight = ConfigurationManager.getIntProperty("webui.preview.brand.height"); | |
String brandFont = ConfigurationManager.getProperty("webui.preview.brand.font"); | |
int brandFontPoint = ConfigurationManager.getIntProperty("webui.preview.brand.fontpoint"); | |
String waterMarkLabel = ConfigurationManager.getProperty("webui.preview.watermark"); | |
// now get the image dimensions | |
float xsize = (float) buf.getWidth(null); | |
float ysize = (float) buf.getHeight(null); | |
// if verbose flag is set, print out dimensions | |
// to STDOUT | |
if (MediaFilterManager.isVerbose) | |
{ | |
System.out.println("original size: " + xsize + "," + ysize); | |
} | |
// scale by x first if needed | |
if (xsize > xmax) | |
{ | |
// calculate scaling factor so that xsize * scale = new size (max) | |
float scaleFactor = xmax / xsize; | |
// if verbose flag is set, print out extracted text | |
// to STDOUT | |
if (MediaFilterManager.isVerbose) | |
{ | |
System.out.println("x scale factor: " + scaleFactor); | |
} | |
// now reduce x size | |
// and y size | |
xsize = xsize * scaleFactor; | |
ysize = ysize * scaleFactor; | |
// if verbose flag is set, print out extracted text | |
// to STDOUT | |
if (MediaFilterManager.isVerbose) | |
{ | |
System.out.println("new size: " + xsize + "," + ysize); | |
} | |
} | |
// scale by y if needed | |
if (ysize > ymax) | |
{ | |
float scaleFactor = ymax / ysize; | |
// now reduce x size | |
// and y size | |
xsize = xsize * scaleFactor; | |
ysize = ysize * scaleFactor; | |
} | |
// if verbose flag is set, print details to STDOUT | |
if (MediaFilterManager.isVerbose) | |
{ | |
System.out.println("created thumbnail size: " + xsize + ", " | |
+ ysize); | |
} | |
// create an image buffer for the preview with the new xsize, ysize | |
// we add | |
BufferedImage branded = new BufferedImage((int) xsize, (int) ysize + brandHeight, | |
BufferedImage.TYPE_INT_RGB); | |
// Use blurring if selected in config. | |
// a little blur before scaling does wonders for keeping moire in check. | |
if (blurring) | |
{ | |
// send the buffered image off to get blurred. | |
buf = getBlurredInstance((BufferedImage) buf); | |
} | |
// Use high quality scaling method if selected in config. | |
// this has a definite performance penalty. | |
if (hqscaling) | |
{ | |
// send the buffered image off to get an HQ downscale. | |
buf = getScaledInstance((BufferedImage) buf, (int) xsize, (int) ysize, | |
(Object) RenderingHints.VALUE_INTERPOLATION_BICUBIC, (boolean) true); | |
} | |
// now render the image into the preview buffer | |
//Graphics2D g2d = branded.createGraphics(); | |
//g2d.drawImage(buf, 0, 0, (int) xsize, (int) ysize, null); | |
//Brand brand = new Brand((int) xsize, brandHeight, new Font(brandFont, Font.PLAIN, brandFontPoint), 5); | |
//BufferedImage brandImage = brand.create(ConfigurationManager.getProperty("webui.preview.brand"), | |
// ConfigurationManager.getProperty("webui.preview.brand.abbrev"), | |
// MediaFilterManager.getCurrentItem() == null ? "" : "hdl:" + MediaFilterManager.getCurrentItem().getHandle()); | |
String watermark = waterMarkLabel; | |
ImageIcon photo = new ImageIcon(buf); | |
BufferedImage bufferedImage = new BufferedImage(buf.getWidth(), | |
buf.getHeight(), BufferedImage.TYPE_INT_RGB); | |
Graphics2D g2d = (Graphics2D)bufferedImage.getGraphics(); | |
g2d.drawImage(photo.getImage(), 0, 0, null); | |
//Create an alpha composite of 50% | |
AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f); | |
g2d.setComposite(alpha); | |
g2d.setColor(Color.white); | |
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, | |
RenderingHints.VALUE_TEXT_ANTIALIAS_ON); | |
// Move our origin to the center | |
g2d.translate(photo.getIconWidth() / 2.0f, photo.getIconHeight() / 2.0f); | |
//Font f1 = new Font("Arial", Font.BOLD, 24); | |
Font f1 = new Font(brandFont, Font.BOLD, brandFontPoint); | |
g2d.setFont(f1); | |
FontMetrics fontMetrics = g2d.getFontMetrics(); | |
Rectangle2D rect = fontMetrics.getStringBounds(watermark, g2d); | |
// Create a rotation transform to rotate the text based on the diagonal | |
// angle of the picture aspect ratio | |
AffineTransform at2 = new AffineTransform(); | |
double opad = photo.getIconHeight() / (double)photo.getIconWidth(); | |
double angle = Math.toDegrees(Math.atan(opad)); | |
double idegrees = -1 * angle; | |
double theta = (2 * Math.PI * idegrees) / 360; | |
at2.rotate(theta); | |
g2d.transform(at2); | |
//Now reposition our coordinates based on size (same as we would normally | |
//do to center on straight line but based on starting at center | |
float x1 = (int)rect.getWidth() / 2.0f *-1; | |
float y1 = (int)rect.getHeight() / 2.0f; | |
g2d.translate(x1, y1); | |
//finally let's draw the string | |
g2d.drawString(watermark, 0.0f, 0.0f); | |
//Free graphic resources | |
g2d.dispose(); | |
// now create an input stream for the thumbnail buffer and return it | |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
ImageIO.write(bufferedImage, "jpeg", baos); | |
// now get the array | |
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |
return bais; // hope this gets written out before its garbage collected! | |
} | |
public BufferedImage getNormalizedInstance(BufferedImage buf) | |
{ | |
int type = (buf.getTransparency() == Transparency.OPAQUE) ? | |
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB_PRE; | |
int w, h; | |
w = buf.getWidth(); | |
h = buf.getHeight(); | |
BufferedImage normal = new BufferedImage(w, h, type); | |
Graphics2D g2d = normal.createGraphics(); | |
g2d.drawImage(buf, 0, 0, w, h, Color.WHITE, null); | |
g2d.dispose(); | |
return normal; | |
} | |
public BufferedImage getBlurredInstance(BufferedImage buf) | |
{ | |
/** | |
* Convenience method that returns a blurred instance of the | |
* provided {@code BufferedImage}. | |
* | |
*/ | |
buf = getNormalizedInstance(buf); | |
// kernel for blur op | |
float[] matrix = { | |
0.111f, 0.111f, 0.111f, | |
0.111f, 0.111f, 0.111f, | |
0.111f, 0.111f, 0.111f, | |
}; | |
// perform the blur and return the blurred version. | |
BufferedImageOp blur = new ConvolveOp( new Kernel(3, 3, matrix) ); | |
BufferedImage blurbuf = blur.filter(buf, null); | |
return blurbuf; | |
} | |
/** | |
* Convenience method that returns a scaled instance of the | |
* provided {@code BufferedImage}. | |
* | |
* @param buf 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 | |
* {@code RenderingHints.KEY_INTERPOLATION} (e.g. | |
* {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR}, | |
* {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR}, | |
* {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC}) | |
* @param higherQuality if true, this method will use a multi-step | |
* scaling technique that provides higher quality than the usual | |
* one-step technique (only useful in downscaling cases, where | |
* {@code targetWidth} or {@code targetHeight} is | |
* smaller than the original dimensions, and generally only when | |
* the {@code BILINEAR} hint is specified) | |
* @return a scaled version of the original {@code BufferedImage} | |
*/ | |
public BufferedImage getScaledInstance(BufferedImage buf, | |
int targetWidth, | |
int targetHeight, | |
Object hint, | |
boolean higherQuality) | |
{ | |
int type = (buf.getTransparency() == Transparency.OPAQUE) ? | |
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; | |
BufferedImage scalebuf = (BufferedImage)buf; | |
int w, h; | |
if (higherQuality) { | |
// Use multi-step technique: start with original size, then | |
// scale down in multiple passes with drawImage() | |
// until the target size is reached | |
w = buf.getWidth(); | |
h = buf.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 (higherQuality && w > targetWidth) { | |
w /= 2; | |
if (w < targetWidth) { | |
w = targetWidth; | |
} | |
} | |
if (higherQuality && h > targetHeight) { | |
h /= 2; | |
if (h < targetHeight) { | |
h = targetHeight; | |
} | |
} | |
BufferedImage tmp = new BufferedImage(w, h, type); | |
Graphics2D g2d = tmp.createGraphics(); | |
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); | |
g2d.drawImage(scalebuf, 0, 0, w, h, Color.WHITE, null); | |
g2d.dispose(); | |
scalebuf = tmp; | |
} while (w != targetWidth || h != targetHeight); | |
return scalebuf; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment