/*
* (c) Copyright 2009 Tim Jenkins
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* ITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.screenrunner.ui.displays;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.Panel;
import java.awt.RenderingHints;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.screenrunner.data.Displayable;
import com.screenrunner.data.I18n;
import com.screenrunner.data.Style;
import com.screenrunner.ui.Display;
import com.screenrunner.ScreenRunner;
public class SoftwareDisplay extends Display implements ComponentListener {
static {
try {
Class.forName("org.gstreamer.Gst");
Class.forName("com.sun.jna.Platform");
_gstreamerSupport = true;
I18n.print("sys/libs/gst/loadsuccess");
} catch (ClassNotFoundException e) {
I18n.print("sys/libs/gst/loadfail");
I18n.print("sys/libs/gst/plugdlmsg");
} catch (UnsatisfiedLinkError e) {
I18n.print("sys/libs/gst/nativelibfail");
I18n.print("sys/libs/gst/gstdlmsg");
}
Display.registerDisplay(new SoftwareDisplay());
}
private Panel _display;
private Panel _preview;
private BufferedImage _displayCache;
private BufferedImage _displayLockCache;
private BufferedImage _displayBgCache;
private BufferedImage _displayFgCache;
private BufferedImage _displayNextBg;
private BufferedImage _displayNextFg;
private Dimension _displaySize;
private Style _curStyle;
private String _curStyleName;
private static boolean _gstreamerSupport = false;
public SoftwareDisplay() {
super();
_display = null;
_preview = null;
_displayCache = null;
_displayLockCache = null;
_displayBgCache = null;
_displayFgCache = null;
_displayNextBg = null;
_displayNextFg = null;
_displaySize = new Dimension(800, 600);
}
@Override
public void setBackgroundTransition(int transition, int speed) { }
@Override
public void setForegroundTransition(int transition, int speed) { }
@Override
public void applyChanges() {
// set background if prepped
if(_displayNextBg != null) {
_displayBgCache = _displayNextBg;
_displayNextBg = null;
}
// set foreground if prepped
if(_displayNextFg != null) {
_displayFgCache = _displayNextFg;
_displayNextFg = null;
}
rebuildDisplayCache();
}
@Override
public void initialize(int screen) {
if(_display == null) {
getDisplayComponent();
}
if(_preview == null) {
getPreviewComponent();
}
GraphicsDevice dev = Display.getDisplayScreen();
if(dev == null) {
dev = Display.getPrimaryScreen();
}
ScreenRunner.log.config("Output display: " + Integer.toString(screen) + "; Resolution is: " + Integer.toString(dev.getDisplayMode().getWidth()) + "x" + Integer.toString(dev.getDisplayMode().getHeight()));
_displaySize = new Dimension(dev.getDisplayMode().getWidth(), dev.getDisplayMode().getHeight());
_displayFgCache = null;
_displayBgCache = null;
_displayCache = null;
_curStyle = null;
_curStyleName = null;
}
@Override
public void prepareNextBackground(Style style, String styleName) {
if(!needsNewBackground(style, styleName)) return;
_curStyle = style;
_curStyleName = styleName;
_displayNextBg = new BufferedImage(_displaySize.width, _displaySize.height, BufferedImage.TYPE_INT_RGB);
drawBackground(_displayNextBg, style, styleName);
}
@Override
public void prepareNextSlide(Displayable slide, int page, Style style) {
_displayNextFg = new BufferedImage(_displaySize.width, _displaySize.height, BufferedImage.TYPE_INT_ARGB);
if(slide == null || page < 0) return;
drawForeground(_displayNextFg, slide, page, style);
}
@Override
public void dispose() { }
@SuppressWarnings("serial")
private Panel createSpecialCanvas(boolean isDisplay) {
Panel c;
if(isDisplay) {
c = new Panel() {
@Override
public void paint(Graphics g) {
drawDisplayCache(this, true);
}
};
} else {
c = new Panel() {
@Override
public void paint(Graphics g) {
drawDisplayCache(this, false);
}
};
}
c.setSize(100, 100);
c.addComponentListener(this);
return c;
}
@Override
public Component getDisplayComponent() {
if(_display == null) {
_display = createSpecialCanvas(true);
}
return _display;
}
@Override
public Component getPreviewComponent() {
if(_preview == null) {
_preview = createSpecialCanvas(false);
}
return _preview;
}
private void drawBackground(BufferedImage cache, Style style, String bgStyle) {
if(cache == null || style == null || bgStyle == null) return;
int w = cache.getWidth();
int h = cache.getHeight();
Graphics2D g = (Graphics2D)cache.getGraphics();
g.setColor(style.getBackgroundColor(bgStyle));
g.fillRect(0, 0, w, h);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
String bgurl = style.getBackgroundUrl(bgStyle);
if(bgurl != null && bgurl.length() > 0) {
BufferedImage img;
File imgfile = new File(style.getBackgroundUrl(bgStyle));
if(imgfile == null || !imgfile.exists()) return;
try {
img = ImageIO.read(imgfile);
} catch (IOException e) {
System.err.println("Error reading background image!");
e.printStackTrace();
return;
}
if(img == null) return;
int iw = img.getWidth();
int ih = img.getHeight();
int bgmode = style.getBackgroundMode(bgStyle);
if(bgmode == Style.BGMODE_STRETCH) {
while(!g.drawImage(img, 0, 0, w, h, 0, 0, iw, ih, _display)) continue;
}
else if(bgmode == Style.BGMODE_CENTER) {
int x = (w - iw) / 2;
int y = (h - ih) / 2;
while(!g.drawImage(img, x, y, _display)) continue;
}
else if(bgmode == Style.BGMODE_ZOOM) {
float aspectScreen = (float)h / (float)w;
float aspectImage = (float)ih / (float)iw;
int nw = w;
int nh = h;
if(aspectScreen <= aspectImage) {
nw = (int)Math.floor(((float)h / (float)ih) * iw);
} else {
nh = (int)Math.floor(((float)w / (float)iw) * ih);
}
int x = (w - nw) / 2;
int y = (h - nh) / 2;
while(!g.drawImage(img, x, y, nw, nh, _display)) continue;
}
else {
System.err.println("Invalid background mode: " + Integer.toString(bgmode));
}
}
}
private void drawForeground(BufferedImage cache, Displayable slide, int page, Style style) {
if(cache == null || slide == null || style == null) return;
Graphics2D g = cache.createGraphics();
Composite comp = g.getComposite();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
g.fillRect(0, 0, cache.getWidth(null), cache.getHeight(null));
g.setComposite(comp);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
slide.drawPage(page, g, _displaySize, style, 1.0f);
}
private void rebuildDisplayCache() {
if(_displaySize.width <= 0 || _displaySize.height <= 0) return;
if(_displayCache == null)
_displayCache = new BufferedImage(_displaySize.width, _displaySize.height, BufferedImage.TYPE_INT_RGB);
if(_displayBgCache == null)
_displayBgCache = new BufferedImage(_displaySize.width, _displaySize.height, BufferedImage.TYPE_INT_RGB);
if(_displayFgCache == null)
_displayFgCache = new BufferedImage(_displaySize.width, _displaySize.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = _displayCache.createGraphics();
if(getDisplayMode() == DISPMODE_OFF || getDisplayMode() == DISPMODE_NORMAL) {
while(!g.drawImage(_displayBgCache, 0, 0, _displaySize.width, _displaySize.height, null)) continue;
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
while(!g.drawImage(_displayFgCache, 0, 0, _displaySize.width, _displaySize.height, null)) continue;
}
else if(getDisplayMode() == DISPMODE_BACKGROUND) {
while(!g.drawImage(_displayBgCache, 0, 0, _displaySize.width, _displaySize.height, null)) continue;
}
else if(getDisplayMode() == DISPMODE_BLACK) {
g.setColor(Color.black);
g.fillRect(0, 0, _displaySize.width, _displaySize.height);
}
else if(getDisplayMode() == DISPMODE_LOGO) {
while(!g.drawImage(_displayBgCache, 0, 0, _displaySize.width, _displaySize.height, null)) continue;
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
if(ScreenRunner.logo != null) {
int scw = (int)(_displaySize.width * 0.8);
int sch = (int)(_displaySize.height * 0.8);
int lgw = ScreenRunner.logo.getWidth();
int lgh = ScreenRunner.logo.getHeight();
double scasp = (double)scw / (double)sch;
double asp = (double)lgw / (double)lgh;
if(asp >= scasp) {
sch = (int)(((double)scw / (double)lgw) * lgh);
} else {
scw = (int)(((double)sch / (double)lgh) * lgw);
}
int scx = (_displaySize.width - scw) / 2;
int scy = (_displaySize.height - sch) / 2;
while(!g.drawImage(ScreenRunner.logo, scx, scy, scw, sch, null)) continue;
}
}
if(_display != null) drawDisplayCache(_display, true);
if(_preview != null) drawDisplayCache(_preview, false);
}
public void drawDisplayCache(Component target, boolean isDisplay) {
if(target == null) return;
if(!target.isVisible()) return;
int tw = target.getWidth();
int th = target.getHeight();
if(tw == 0 || th == 0) return;
BufferedImage cache = ((isDisplay && isDisplayLocked()) ? _displayLockCache : _displayCache);
Graphics2D g = (Graphics2D)target.getGraphics();
if(g == null) return;
if(cache == null) {
g.setColor(Color.black);
g.fillRect(0, 0, tw, th);
} else {
if(cache.getWidth() != tw || cache.getHeight() != th) {
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
}
while(!g.drawImage(cache, 0, 0, tw, th, null)) continue;
}
if(!isDisplay && isDisplayLocked()) {
g.drawImage(ScreenRunner.lockImg, tw - 48, th - 48, 48, 48, null);
}
g.dispose();
}
@Override
public void componentHidden(ComponentEvent e) { }
@Override
public void componentMoved(ComponentEvent e) { }
@Override
public void componentResized(ComponentEvent e) {
Component c = e.getComponent();
if(c == _display || c == _preview) {
drawDisplayCache(c, (c == _display));
}
}
@Override
public void componentShown(ComponentEvent e) {
Component c = e.getComponent();
if(c == _display || c == _preview) {
drawDisplayCache(c, (c == _display));
}
}
@Override
public void updateDisplays() {
_displayCache = new BufferedImage(_displaySize.width, _displaySize.height, BufferedImage.TYPE_INT_RGB);
rebuildDisplayCache();
if(_display != null && _display.isVisible()) drawDisplayCache(_display, true);
if(_preview != null && _preview.isVisible()) drawDisplayCache(_preview, false);
}
@Override
protected void onDisplayLockChanged(boolean locked) {
if(locked) {
_displayLockCache = new BufferedImage(_displaySize.width, _displaySize.height, BufferedImage.TYPE_INT_RGB);
while(!_displayLockCache.getGraphics().drawImage(_displayCache, 0, 0, _displaySize.width, _displaySize.height, null)) continue;
}
if(_display != null) drawDisplayCache(_display, true);
if(_preview != null) drawDisplayCache(_preview, false);
}
@Override
protected void onDisplayModeChanged(int mode) {
updateDisplays();
}
private boolean needsNewBackground(Style style, String styleName) {
return ((style != _curStyle) || (styleName != _curStyleName));
}
@Override
public String getFriendlyName() { return "Software"; }
@Override
public boolean isAccelerated() { return false; }
@Override
public boolean supportsMedia() { return _gstreamerSupport; }
}