/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package bootdragon.demo.preview;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageTranscoder;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;
import org.springframework.stereotype.Component;
import bootdragon.demo.DemoRequest;
import bootdragon.demo.sourcecode.DemoSourceCode;
/**
* Factory to create a {@link PreviewGif}. Creates an animated GIF by adding standard
* frames showing 'spring run' followed by a single customized frame showing the result in
* a web browser.
*
* @author Phillip Webb
*/
@Component
public class PreviewGifFactory {
/**
* Create a preview GIF for the specified request and source.
* @param request the reequest
* @param sourceCode the source code
* @return a preview GIF
* @throws IOException
*/
public PreviewGif createPreview(DemoRequest request, DemoSourceCode sourceCode)
throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageOutputStream imageOutputStream = ImageIO
.createImageOutputStream(outputStream);
try {
ImageWriter writer = getWriter(imageOutputStream);
write(writer, request.getMessage());
}
finally {
imageOutputStream.close();
}
return new PreviewGif(outputStream.toByteArray(), TimeUnit.SECONDS.toMillis(14));
}
private void write(ImageWriter writer, String message) throws IOException {
ImageReader reader = getReader(PreviewGifFactory.class
.getResourceAsStream("run.gif"));
writer.prepareWriteSequence(null);
ImageWriteParam imageWriteParam = writer.getDefaultWriteParam();
writeInitialFrames(reader, writer, imageWriteParam);
writeResultFrame(reader.getImageMetadata(0), writer, imageWriteParam, message);
writer.endWriteSequence();
}
private void writeInitialFrames(ImageReader reader, ImageWriter writer,
ImageWriteParam imageWriteParam) throws IOException {
int numberOfFrames = reader.getNumImages(true);
for (int i = 0; i < numberOfFrames; i++) {
BufferedImage image = reader.read(i);
IIOMetadata metadata = reader.getImageMetadata(i);
writer.writeToSequence(new IIOImage(image, null, metadata), imageWriteParam);
}
}
private void writeResultFrame(IIOMetadata imageMetadata, ImageWriter writer,
ImageWriteParam imageWriteParam, String message) throws IOException {
ImageReader reader = getReader(getClass().getResourceAsStream("webbrowser.gif"));
BufferedImage image = reader.read(0);
renderMessageOverlay(image, message);
ImageTypeSpecifier imageType = reader.getImageTypes(0).next();
IIOMetadata metadata = getResultFrameMetadata(imageMetadata, imageType, writer);
writer.writeToSequence(new IIOImage(image, null, metadata), imageWriteParam);
}
private IIOMetadata getResultFrameMetadata(IIOMetadata sourceMetadata,
ImageTypeSpecifier sourceType, ImageTranscoder transcoder)
throws IIOInvalidTreeException {
IIOMetadata metadata = transcoder.convertImageMetadata(sourceMetadata,
sourceType, null);
String formatName = metadata.getNativeMetadataFormatName();
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(formatName);
IIOMetadataNode graphicsControlExtension = getNode(root,
"GraphicControlExtension");
graphicsControlExtension.setAttribute("delayTime", "2000");
metadata.setFromTree(formatName, root);
return metadata;
}
private void renderMessageOverlay(BufferedImage image, String message) {
Graphics2D graphics = image.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
graphics.setColor(Color.BLACK);
graphics.setFont(new Font("Serif", Font.PLAIN, 48));
FontMetrics metrics = graphics.getFontMetrics();
int lineHeight = metrics.getHeight() + 8;
int x = 30;
int y = 100 + lineHeight;
String[] lines = message.replace("\t", "\t\t").split("[\n\r]");
for (String line : lines) {
String[] words = line.split(" ");
int i = 0;
while (i < words.length) {
String output = words[i++];
while (i < words.length
&& (x + metrics.stringWidth(output + " " + words[i]) < image
.getWidth())) {
output += " " + words[i++];
}
graphics.drawString(output, x, y);
y = y + lineHeight;
}
}
graphics.dispose();
}
private IIOMetadataNode getNode(IIOMetadataNode node, String name) {
int nNodes = node.getLength();
for (int i = 0; i < nNodes; i++) {
if (node.item(i).getNodeName().equalsIgnoreCase(name)) {
return ((IIOMetadataNode) node.item(i));
}
}
IIOMetadataNode child = new IIOMetadataNode(name);
node.appendChild(child);
return child;
}
private ImageReader getReader(InputStream inputStream) throws IOException {
ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next();
reader.setInput(ImageIO.createImageInputStream(inputStream));
return reader;
}
private ImageWriter getWriter(ImageOutputStream outputStream) throws IOException {
ImageWriter writer = ImageIO.getImageWritersByFormatName("gif").next();
writer.setOutput(outputStream);
return writer;
}
}