// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.gui.converter;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.IndexColorModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.SpinnerNumberModel;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.infinity.gui.ViewerUtil;
import org.infinity.resource.graphics.PseudoBamDecoder.PseudoBamFrameEntry;
import org.infinity.util.Misc;
/**
* Transform filter: adjust the size of the BAM frames.
*/
public class BamFilterTransformResize extends BamFilterBaseTransform
implements ActionListener, ChangeListener
{
private static final String FilterName = "Resize BAM frames";
private static final String FilterDesc = "This filter allows you to adjust the size of each BAM frame.";
private static final int TYPE_NEAREST_NEIGHBOR = 0;
private static final int TYPE_BILINEAR = 1;
private static final int TYPE_BICUBIC = 2;
private static final int TYPE_SCALEX = 3;
private static final String[] ScalingTypeItems = {"Nearest neighbor", "Bilinear",
"Bicubic", "Scale2x/3x/4x"};
private JComboBox<String> cbType;
private JCheckBox cbAdjustCenter;
private JSpinner spinnerFactor;
private JTextArea taInfo;
public static String getFilterName() { return FilterName; }
public static String getFilterDesc() { return FilterDesc; }
public BamFilterTransformResize(ConvertToBam parent)
{
super(parent, FilterName, FilterDesc);
}
@Override
public PseudoBamFrameEntry process(PseudoBamFrameEntry entry) throws Exception
{
return applyEffect(entry);
}
@Override
public PseudoBamFrameEntry updatePreview(PseudoBamFrameEntry entry)
{
return applyEffect(entry);
}
@Override
public void updateControls()
{
updateStatus();
}
@Override
public String getConfiguration()
{
StringBuilder sb = new StringBuilder();
sb.append(cbType.getSelectedIndex()).append(';');
sb.append(((SpinnerNumberModel)spinnerFactor.getModel()).getNumber().doubleValue()).append(';');
sb.append(cbAdjustCenter.isSelected());
return sb.toString();
}
@Override
public boolean setConfiguration(String config)
{
if (config != null) {
config = config.trim();
if (!config.isEmpty()) {
String[] params = config.split(";");
int type = -1;
Double factor = Double.MIN_VALUE;
boolean adjust = true;
if (params.length > 0) {
type = Misc.toNumber(params[0], -1);
if (type < 0 || type >= cbType.getModel().getSize()) {
return false;
}
}
if (params.length > 1) {
double min = ((Number)((SpinnerNumberModel)spinnerFactor.getModel()).getMinimum()).doubleValue();
double max = ((Number)((SpinnerNumberModel)spinnerFactor.getModel()).getMaximum()).doubleValue();
factor = decodeDouble(params[1], min, max, Double.MIN_VALUE);
if (factor == Double.MIN_VALUE) {
return false;
}
}
if (params.length > 2) {
if (params[2].equalsIgnoreCase("true")) {
adjust = true;
} else if (params[2].equalsIgnoreCase("false")) {
adjust = false;
} else {
return false;
}
}
if (type >= 0) {
cbType.setSelectedIndex(type);
}
if (factor != Double.MIN_VALUE) {
spinnerFactor.setValue(factor);
}
cbAdjustCenter.setSelected(adjust);
}
return true;
}
return false;
}
@Override
protected JPanel loadControls()
{
/*
* Possible scaling algorithms:
* - nearest neighbor (BamV1, BamV2) -> use Java's internal filters
* - bilinear (BamV2) -> use Java's internal filters
* - bicubic (BamV2) -> use Java's internal filters
* - scale2x/scale3x (BamV1, BamV2) -> http://en.wikipedia.org/wiki/Image_scaling
* - [?] lanczos (BamV2) -> http://en.wikipedia.org/wiki/Lanczos_resampling
* - [?] xBR (BamV1, BamV2) -> http://board.byuu.org/viewtopic.php?f=10&t=2248
*/
GridBagConstraints c = new GridBagConstraints();
JLabel l1 = new JLabel("Type:");
JLabel l2 = new JLabel("Factor:");
cbType = new JComboBox<>(ScalingTypeItems);
cbType.addActionListener(this);
spinnerFactor = new JSpinner(new SpinnerNumberModel(1.0, 0.01, 10.0, 0.05));
spinnerFactor.addChangeListener(this);
taInfo = new JTextArea(2, 0);
taInfo.setEditable(false);
taInfo.setFont(UIManager.getFont("Label.font"));
Color bg = UIManager.getColor("Label.background");
taInfo.setBackground(bg);
taInfo.setSelectionColor(bg);
taInfo.setSelectedTextColor(bg);
taInfo.setWrapStyleWord(true);
taInfo.setLineWrap(true);
cbAdjustCenter = new JCheckBox("Adjust center position", true);
cbAdjustCenter.addActionListener(this);
JPanel p = new JPanel(new GridBagLayout());
ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
p.add(l1, c);
ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0);
p.add(cbType, c);
ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.NONE, new Insets(0, 12, 0, 0), 0, 0);
p.add(l2, c);
ViewerUtil.setGBC(c, 3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0);
p.add(spinnerFactor, c);
ViewerUtil.setGBC(c, 0, 1, 4, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0);
p.add(taInfo, c);
ViewerUtil.setGBC(c, 0, 2, 4, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0);
p.add(cbAdjustCenter, c);
JPanel panel = new JPanel(new GridBagLayout());
ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER,
GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
panel.add(p, c);
updateStatus();
return panel;
}
//--------------------- Begin Interface ActionListener ---------------------
@Override
public void actionPerformed(ActionEvent event)
{
if (event.getSource() == cbType) {
updateStatus();
fireChangeListener();
} else if (event.getSource() == cbAdjustCenter) {
fireChangeListener();
}
}
//--------------------- End Interface ActionListener ---------------------
//--------------------- Begin Interface ChangeListener ---------------------
@Override
public void stateChanged(ChangeEvent event)
{
if (event.getSource() == spinnerFactor) {
fireChangeListener();
}
}
//--------------------- Begin Interface ChangeListener ---------------------
// Updates controls depending on current scaling type
private void updateStatus()
{
final String fmtSupport1 = "Supported target: %1$s";
final String fmtSupport2 = "Supported targets: %1$s, %2$s";
int type = cbType.getSelectedIndex();
SpinnerNumberModel snm = (SpinnerNumberModel)spinnerFactor.getModel();
double factor;
if (snm.getValue() instanceof Double) {
factor = ((Double)snm.getValue()).doubleValue();
} else {
factor = ((Integer)snm.getValue()).doubleValue();
}
switch (type) {
case TYPE_NEAREST_NEIGHBOR:
taInfo.setText(String.format(fmtSupport2, ConvertToBam.BamVersionItems[ConvertToBam.VERSION_BAMV1],
ConvertToBam.BamVersionItems[ConvertToBam.VERSION_BAMV2]));
setFactor(factor, 0.01, 10.0, 0.05);
spinnerFactor.setEnabled(true);
break;
case TYPE_BILINEAR:
taInfo.setText(String.format(fmtSupport1, ConvertToBam.BamVersionItems[ConvertToBam.VERSION_BAMV2]));
setFactor(factor, 0.01, 10.0, 0.05);
spinnerFactor.setEnabled(!getConverter().isBamV1Selected());
break;
case TYPE_BICUBIC:
taInfo.setText(String.format(fmtSupport1, ConvertToBam.BamVersionItems[ConvertToBam.VERSION_BAMV2]));
setFactor(factor, 0.01, 10.0, 0.05);
spinnerFactor.setEnabled(!getConverter().isBamV1Selected());
break;
case TYPE_SCALEX:
taInfo.setText(String.format(fmtSupport2, ConvertToBam.BamVersionItems[ConvertToBam.VERSION_BAMV1],
ConvertToBam.BamVersionItems[ConvertToBam.VERSION_BAMV2]));
setFactor((int)factor, 2, 4, 1);
spinnerFactor.setEnabled(true);
break;
default:
taInfo.setText("");
setFactor(factor, 0.01, 10.0, 0.05);
}
}
private void setFactor(Number current, Number min, Number max, Number step)
{
if (spinnerFactor.getModel() instanceof SpinnerNumberModel) {
SpinnerNumberModel snm = (SpinnerNumberModel)spinnerFactor.getModel();
boolean isDouble = ((current instanceof Double) || (min instanceof Double) ||
(max instanceof Double) || (step instanceof Double));
int curI = 0, minI = 0, maxI = 0, stepI = 0;
double curD = 0, minD = 0, maxD = 0, stepD = 0;
if (isDouble) {
curD = ((Double)current).doubleValue();
minD = ((Double)min).doubleValue();
maxD = ((Double)max).doubleValue();
stepD = ((Double)step).doubleValue();
} else {
curI = ((Integer)current).intValue();
minI = ((Integer)min).intValue();
maxI = ((Integer)max).intValue();
stepI = ((Integer)step).intValue();
}
if (isDouble) {
if (snm.getValue() instanceof Integer) {
curD = Math.max(Math.min(curD, maxD), minD);
spinnerFactor.setModel(new SpinnerNumberModel(curD, minD, maxD, stepD));
} else {
snm.setMinimum(minD);
snm.setMaximum(maxD);
snm.setValue(curD);
snm.setStepSize(stepD);
}
} else {
if (snm.getValue() instanceof Double) {
curI = Math.max(Math.min(curI, maxI), minI);
spinnerFactor.setModel(new SpinnerNumberModel(curI, minI, (maxI < 10) ? 10 : maxI, stepI));
if (maxI < 10) {
((SpinnerNumberModel)spinnerFactor.getModel()).setMaximum(Integer.valueOf(maxI));
}
} else {
snm.setMinimum(minI);
snm.setMaximum(maxI);
snm.setValue(curI);
snm.setStepSize(stepI);
}
}
}
}
private double getFactor()
{
SpinnerNumberModel snm = (SpinnerNumberModel)spinnerFactor.getModel();
return ((Number)snm.getValue()).doubleValue();
}
private PseudoBamFrameEntry applyEffect(PseudoBamFrameEntry entry)
{
if (entry != null && entry.getFrame() != null) {
BufferedImage dstImage;
double factor = getFactor();
int type = cbType.getSelectedIndex();
switch (type) {
case TYPE_NEAREST_NEIGHBOR:
dstImage = scaleNative(entry.getFrame(), factor, AffineTransformOp.TYPE_NEAREST_NEIGHBOR, true);
break;
case TYPE_BILINEAR:
dstImage = scaleNative(entry.getFrame(), factor, AffineTransformOp.TYPE_BILINEAR, false);
break;
case TYPE_BICUBIC:
dstImage = scaleNative(entry.getFrame(), factor, AffineTransformOp.TYPE_BICUBIC, false);
break;
case TYPE_SCALEX:
dstImage = scaleScaleX(entry.getFrame(), (int)factor);
break;
default:
dstImage = entry.getFrame();
}
if (dstImage != null) {
// adjusting center
if (cbAdjustCenter.isSelected()) {
double fx = (double)dstImage.getWidth() / (double)entry.getFrame().getWidth();
double fy = (double)dstImage.getHeight() / (double)entry.getFrame().getHeight();
entry.setCenterX((int)((double)entry.getCenterX()*fx));
entry.setCenterY((int)((double)entry.getCenterY()*fy));
}
entry.setFrame(dstImage);
}
}
return entry;
}
// Scales the specified image using Java's native scalers
private BufferedImage scaleNative(BufferedImage srcImage, double factor, int scaleType, boolean paletteSupported)
{
BufferedImage dstImage = srcImage;
boolean isValid = paletteSupported || srcImage.getType() != BufferedImage.TYPE_BYTE_INDEXED;
if (isValid && srcImage != null && factor > 0.0 && factor != 1.0) {
int width = srcImage.getWidth();
int height = srcImage.getHeight();
int newWidth = (int)((double)width * factor);
if (newWidth < 1) newWidth = 1;
int newHeight = (int)((double)height * factor);
if (newHeight < 1) newHeight = 1;
// preparing target image
if (paletteSupported && srcImage.getType() == BufferedImage.TYPE_BYTE_INDEXED) {
IndexColorModel cm = (IndexColorModel)srcImage.getColorModel();
int[] colors = new int[1 << cm.getPixelSize()];
cm.getRGBs(colors);
IndexColorModel cm2 = new IndexColorModel(cm.getPixelSize(), colors.length,
colors, 0, cm.hasAlpha(), cm.getTransparentPixel(),
DataBuffer.TYPE_BYTE);
dstImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_BYTE_INDEXED, cm2);
} else if (srcImage.getType() != BufferedImage.TYPE_BYTE_INDEXED) {
dstImage = new BufferedImage(newWidth, newHeight, srcImage.getType());
} else {
// not supported
return dstImage;
}
// scaling image
Graphics2D g = dstImage.createGraphics();
try {
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
BufferedImageOp op = new AffineTransformOp(AffineTransform.getScaleInstance(factor, factor),
scaleType);
g.drawImage(srcImage, op, 0, 0);
} finally {
g.dispose();
g = null;
}
}
return dstImage;
}
// Uses the Scale2x/Scale3x algorithm
private BufferedImage scaleScaleX(BufferedImage srcImage, int factor)
{
BufferedImage dstImage = srcImage;
if (srcImage != null) {
switch (factor) {
case 2:
dstImage = scaleScale2X(srcImage);
break;
case 3:
dstImage = scaleScale3X(srcImage);
break;
case 4:
dstImage = scaleScale4X(srcImage);
break;
}
}
return dstImage;
}
// Applies the Scale2x algorithm
private BufferedImage scaleScale2X(BufferedImage srcImage)
{
BufferedImage dstImage = srcImage;
if (srcImage != null) {
int srcWidth = srcImage.getWidth();
int srcHeight = srcImage.getHeight();
int dstWidth = 2*srcWidth;
int dstHeight = 2*srcHeight;
byte[] srcB = null, dstB = null;
int[] srcI = null, dstI = null;
byte transIndex = -1;
if (srcImage.getType() == BufferedImage.TYPE_BYTE_INDEXED) {
srcB = ((DataBufferByte)srcImage.getRaster().getDataBuffer()).getData();
IndexColorModel cm = (IndexColorModel)srcImage.getColorModel();
int[] colors = new int[1 << cm.getPixelSize()];
cm.getRGBs(colors);
IndexColorModel cm2 = new IndexColorModel(cm.getPixelSize(), colors.length, colors, 0,
cm.hasAlpha(), cm.getTransparentPixel(),
DataBuffer.TYPE_BYTE);
dstImage = new BufferedImage(dstWidth, dstHeight, BufferedImage.TYPE_BYTE_INDEXED, cm2);
dstB = ((DataBufferByte)dstImage.getRaster().getDataBuffer()).getData();
for (int i = 0; i < colors.length; i++) {
if (transIndex < 0 && (colors[i] & 0x00ffffff) == 0x0000ff00) {
transIndex = (byte)i;
break;
}
}
if (transIndex < 0) {
transIndex = 0;
}
} else {
srcI = ((DataBufferInt)srcImage.getRaster().getDataBuffer()).getData();
dstImage = new BufferedImage(dstWidth, dstHeight, srcImage.getType());
dstI = ((DataBufferInt)dstImage.getRaster().getDataBuffer()).getData();
}
// applying scaling
int srcOfs = 0, dstOfs = 0;
for (int y = 0; y < srcHeight; y++) {
for (int x = 0; x < srcWidth; x++) {
if (srcB != null) {
byte p = srcB[srcOfs];
byte a = (y > 0) ? srcB[srcOfs-srcWidth] : transIndex;
byte b = (x+1 < srcWidth) ? srcB[srcOfs+1] : transIndex;
byte c = (x > 0) ? srcB[srcOfs-1] : transIndex;
byte d = (y+1 < srcHeight) ? srcB[srcOfs+srcWidth] : transIndex;
byte t1 = p, t2 = p, t3 = p, t4 = p;
if (c == a && c != d && a != b) t1 = a;
if (a == b && a != c && b != d) t2 = b;
if (b == d && b != a && d != c) t4 = d;
if (d == c && d != b && c != a) t3 = c;
dstB[dstOfs] = t1;
dstB[dstOfs+1] = t2;
dstB[dstOfs+dstWidth] = t3;
dstB[dstOfs+dstWidth+1] = t4;
}
if (srcI != null) {
int p = srcI[srcOfs];
int a = (y > 0) ? srcI[srcOfs-srcWidth] : 0;
int b = (x+1 < srcWidth) ? srcI[srcOfs+1] : 0;
int c = (x > 0) ? srcI[srcOfs-1] : 0;
int d = (y+1 < srcHeight) ? srcI[srcOfs+srcWidth] : 0;
int t1 = p, t2 = p, t3 = p, t4 = p;
if (c == a && c != d && a != b) t1 = a;
if (a == b && a != c && b != d) t2 = b;
if (b == d && b != a && d != c) t4 = d;
if (d == c && d != b && c != a) t3 = c;
dstI[dstOfs] = t1;
dstI[dstOfs+1] = t2;
dstI[dstOfs+dstWidth] = t3;
dstI[dstOfs+dstWidth+1] = t4;
}
srcOfs++;
dstOfs += 2;
}
dstOfs += dstWidth;
}
}
return dstImage;
}
// Applies the Scale3x algorithm
private BufferedImage scaleScale3X(BufferedImage srcImage)
{
BufferedImage dstImage = srcImage;
if (srcImage != null) {
int srcWidth = srcImage.getWidth();
int srcHeight = srcImage.getHeight();
int dstWidth = 3*srcWidth;
int dstWidth2 = dstWidth+dstWidth; // for optimization purposes
int dstHeight = 3*srcHeight;
byte[] srcB = null, dstB = null;
int[] srcI = null, dstI = null;
byte transIndex = -1;
if (srcImage.getType() == BufferedImage.TYPE_BYTE_INDEXED) {
srcB = ((DataBufferByte)srcImage.getRaster().getDataBuffer()).getData();
IndexColorModel cm = (IndexColorModel)srcImage.getColorModel();
int[] colors = new int[1 << cm.getPixelSize()];
cm.getRGBs(colors);
IndexColorModel cm2 = new IndexColorModel(cm.getPixelSize(), colors.length, colors, 0,
cm.hasAlpha(), cm.getTransparentPixel(),
DataBuffer.TYPE_BYTE);
dstImage = new BufferedImage(dstWidth, dstHeight, BufferedImage.TYPE_BYTE_INDEXED, cm2);
dstB = ((DataBufferByte)dstImage.getRaster().getDataBuffer()).getData();
for (int i = 0; i < colors.length; i++) {
if (transIndex < 0 && (colors[i] & 0x00ffffff) == 0x0000ff00) {
transIndex = (byte)i;
break;
}
}
if (transIndex < 0) {
transIndex = 0;
}
} else {
srcI = ((DataBufferInt)srcImage.getRaster().getDataBuffer()).getData();
dstImage = new BufferedImage(dstWidth, dstHeight, srcImage.getType());
dstI = ((DataBufferInt)dstImage.getRaster().getDataBuffer()).getData();
}
// applying scaling
int srcOfs = 0, dstOfs = 0;
for (int y = 0; y < srcHeight; y++) {
for (int x = 0; x < srcWidth; x++) {
if (srcB != null) {
byte e = srcB[srcOfs];
byte a = (x > 0 && y > 0) ? srcB[srcOfs-srcWidth-1] : transIndex;
byte b = (y > 0) ? srcB[srcOfs-srcWidth] : transIndex;
byte c = (x+1 < srcWidth && y > 0) ? srcB[srcOfs-srcWidth+1] : transIndex;
byte d = (x > 0) ? srcB[srcOfs-1] : transIndex;
byte f = (x+1 < srcWidth) ? srcB[srcOfs+1] : transIndex;
byte g = (x > 0 && y+1 < srcHeight) ? srcB[srcOfs+srcWidth-1] : transIndex;
byte h = (y+1 < srcHeight) ? srcB[srcOfs+srcWidth] : transIndex;
byte i = (x+1 < srcWidth && y+1 < srcHeight) ? srcB[srcOfs+srcWidth+1] : transIndex;
byte t1 = e, t2 = e, t3 = e, t4 = e, t5 = e, t6 = e, t7 = e, t8 = e, t9 = e;
if (d == b && d != h && b != f) t1 = d;
if ((d == b && d != h && b != f && e != c) || (b == f && b != d && f != h && e != a)) t2 = b;
if (b == f && b != d && f != h) t3 = f;
if ((h == d && h != f && d != b && e != a) || (d == b && d != h && b != f && e != g)) t4 = d;
if ((b == f && b != d && f != h && e != i) || (f == h && f != b && h != d && e != c)) t6 = f;
if (h == d && h != f && d != b) t7 = d;
if ((f == h && f != b && h != d && e != g) || (h == d && h != f && d != b && e != i)) t8 = h;
if (f == h && f != b && h != d) t9 = f;
dstB[dstOfs] = t1;
dstB[dstOfs+1] = t2;
dstB[dstOfs+2] = t3;
dstB[dstOfs+dstWidth] = t4;
dstB[dstOfs+dstWidth+1] = t5;
dstB[dstOfs+dstWidth+2] = t6;
dstB[dstOfs+dstWidth2] = t7;
dstB[dstOfs+dstWidth2+1] = t8;
dstB[dstOfs+dstWidth2+2] = t9;
}
if (srcI != null) {
int e = srcI[srcOfs];
int a = (x > 0 && y > 0) ? srcI[srcOfs-srcWidth-1] : 0;
int b = (y > 0) ? srcI[srcOfs-srcWidth] : 0;
int c = (x+1 < srcWidth && y > 0) ? srcI[srcOfs-srcWidth+1] : 0;
int d = (x > 0) ? srcI[srcOfs-1] : 0;
int f = (x+1 < srcWidth) ? srcI[srcOfs+1] : 0;
int g = (x > 0 && y+1 < srcHeight) ? srcI[srcOfs+srcWidth-1] : 0;
int h = (y+1 < srcHeight) ? srcI[srcOfs+srcWidth] : 0;
int i = (x+1 < srcWidth && y+1 < srcHeight) ? srcI[srcOfs+srcWidth+1] : 0;
int t1 = e, t2 = e, t3 = e, t4 = e, t5 = e, t6 = e, t7 = e, t8 = e, t9 = e;
if (d == b && d != h && b != f) t1 = d;
if ((d == b && d != h && b != f && e != c) || (b == f && b != d && f != h && e != a)) t2 = b;
if (b == f && b != d && f != h) t3 = f;
if ((h == d && h != f && d != b && e != a) || (d == b && d != h && b != f && e != g)) t4 = d;
if ((b == f && b != d && f != h && e != i) || (f == h && f != b && h != d && e != c)) t6 = f;
if (h == d && h != f && d != b) t7 = d;
if ((f == h && f != b && h != d && e != g) || (h == d && h != f && d != b && e != i)) t8 = h;
if (f == h && f != b && h != d) t9 = f;
dstI[dstOfs] = t1;
dstI[dstOfs+1] = t2;
dstI[dstOfs+2] = t3;
dstI[dstOfs+dstWidth] = t4;
dstI[dstOfs+dstWidth+1] = t5;
dstI[dstOfs+dstWidth+2] = t6;
dstI[dstOfs+dstWidth2] = t7;
dstI[dstOfs+dstWidth2+1] = t8;
dstI[dstOfs+dstWidth2+2] = t9;
}
srcOfs++;
dstOfs += 3;
}
dstOfs += dstWidth2;
}
}
return dstImage;
}
// Applies Scale2x algorithm twice
private BufferedImage scaleScale4X(BufferedImage srcImage)
{
BufferedImage dstImage = srcImage;
if (srcImage != null) {
dstImage = scaleScale2X(dstImage);
dstImage = scaleScale2X(dstImage);
}
return dstImage;
}
}