/* * $Id$ * * Copyright (c) 2007 by Joel Uckelman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ package VASSAL.tools.imageop; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Collections; import java.util.List; import org.apache.commons.lang.builder.HashCodeBuilder; import VASSAL.build.GameModule; import VASSAL.tools.DataArchive; import VASSAL.tools.image.ImageIOException; import VASSAL.tools.image.ImageNotFoundException; import VASSAL.tools.image.ImageUtils; import VASSAL.tools.image.svg.SVGRenderer; /** * An {@link ImageOp} which rotates and scales its source. Rotation * is about the center of the source image, and scaling ranges from * <code>(0,Double.MAX_VALUE)</code>. If a source is to be both rotated * and scaled, using one <code>RotateScaleOp</code> will produce better * results than doing the rotation and scaling separately with * one {@link RotateOp} and one {@link ScaleOp}. * * @since 3.1.0 * @author Joel Uckelman */ public class RotateScaleOpSVGImpl extends AbstractTileOpImpl implements RotateScaleOp, SVGOp { private final SVGOp sop; private final double scale; private final double angle; private final RenderingHints hints; private final int hash; /** * Constructs an <code>ImageOp</code> which will rotate and scale * the image produced by its source <code>ImageOp</code>. * * @param sop the source operation * @param angle the angle of rotation, in degrees * @param scale the scale factor */ public RotateScaleOpSVGImpl(SVGOp sop, double angle, double scale) { this(sop, angle, scale, ImageUtils.getDefaultHints()); } /** * Constructs an <code>ImageOp</code> which will rotate and scale * the image produced by its source <code>ImageOp</code>. * * @param sop the source operation * @param angle the angle of rotation, in degrees * @param scale the scale factor * @param hints rendering hints * @throws IllegalArgumentException if <code>sop == null</code>. */ public RotateScaleOpSVGImpl(SVGOp sop, double angle, double scale, RenderingHints hints) { if (sop == null) throw new IllegalArgumentException(); if (scale <= 0) throw new IllegalArgumentException("scale = " + scale); this.sop = sop; this.angle = angle; this.scale = scale; this.hints = hints; hash = new HashCodeBuilder().append(sop) .append(scale) .append(angle) .append(hints) .toHashCode(); } public List<VASSAL.tools.opcache.Op<?>> getSources() { return Collections.emptyList(); } /** * {@inheritDoc} * * @throws Exception passed up from the source <code>ImageOp</code>. */ public BufferedImage eval() throws Exception { final DataArchive archive = GameModule.getGameModule().getDataArchive(); final String name = getName(); try { final SVGRenderer renderer = new SVGRenderer( archive.getURL(name), new BufferedInputStream(archive.getInputStream(name)) ); if (size == null) fixSize(); return renderer.render(angle, scale); } catch (FileNotFoundException e) { throw new ImageNotFoundException(name, e); } catch (IOException e) { throw new ImageIOException(name, e); } } /** {@inheritDoc} */ protected void fixSize() { if ((size = getSizeFromCache()) == null) { size = ImageUtils.transform( new Rectangle(sop.getSize()), scale, angle).getSize(); } } /** * Returns the angle of rotation. * * @return the angle of rotation, in degrees. */ public double getAngle() { return angle; } /** * Returns the scale factor. * * @return the scale factor, in the range <code>(0,Double.MAX_VALUE]</code>. */ public double getScale() { return scale; } public String getName() { return sop.getName(); } public RenderingHints getHints() { return hints; } /** {@inheritDoc} */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || o.getClass() != this.getClass()) return false; final RotateScaleOpSVGImpl op = (RotateScaleOpSVGImpl) o; return scale == op.getScale() && angle == op.getAngle() && hints.equals(op.getHints()) && sop.equals(op.sop); } /** {@inheritDoc} */ @Override public int hashCode() { return hash; } }