WarpPI/src/teavm-specific/java/ar/com/hjg/pngj/pixels/PixelsWriterDefault.java

159 lines
5.3 KiB
Java

package ar.com.hjg.pngj.pixels;
import java.util.Arrays;
import ar.com.hjg.pngj.FilterType;
import ar.com.hjg.pngj.ImageInfo;
import ar.com.hjg.pngj.PngjOutputException;
/**
* Default implementation of PixelsWriter, with fixed filters and also adaptive strategies.
*/
public class PixelsWriterDefault extends PixelsWriter {
/** current raw row */
protected byte[] rowb;
/** previous raw row */
protected byte[] rowbprev;
/** buffer for filtered row */
protected byte[] rowbfilter;
/** evaluates different filters, for adaptive strategy */
protected FiltersPerformance filtersPerformance;
/** currently concrete selected filter type */
protected FilterType curfilterType;
/** parameters for adaptive strategy */
protected int adaptMaxSkip; // set in initParams, does not change
protected int adaptSkipIncreaseSinceRow; // set in initParams, does not change
protected double adaptSkipIncreaseFactor; // set in initParams, does not change
protected int adaptNextRow = 0;
public PixelsWriterDefault(ImageInfo imgInfo) {
super(imgInfo);
filtersPerformance = new FiltersPerformance(imgInfo);
}
@Override
protected void initParams() {
super.initParams();
if (rowb == null || rowb.length < buflen)
rowb = new byte[buflen];
if (rowbfilter == null || rowbfilter.length < buflen)
rowbfilter = new byte[buflen];
if (rowbprev == null || rowbprev.length < buflen)
rowbprev = new byte[buflen];
else
Arrays.fill(rowbprev, (byte) 0);
// if adaptative but too few rows or columns, use default
if (imgInfo.cols < 3 && !FilterType.isValidStandard(filterType))
filterType = FilterType.FILTER_DEFAULT;
if (imgInfo.rows < 3 && !FilterType.isValidStandard(filterType))
filterType = FilterType.FILTER_DEFAULT;
if (imgInfo.getTotalPixels() <= 1024 && !FilterType.isValidStandard(filterType))
filterType = getDefaultFilter();
if (FilterType.isAdaptive(filterType)) {
// adaptCurSkip = 0;
adaptNextRow = 0;
if (filterType == FilterType.FILTER_ADAPTIVE_FAST) {
adaptMaxSkip = 200;
adaptSkipIncreaseSinceRow = 3;
adaptSkipIncreaseFactor = 1 / 4.0; // skip ~ row/3
} else if (filterType == FilterType.FILTER_ADAPTIVE_MEDIUM) {
adaptMaxSkip = 8;
adaptSkipIncreaseSinceRow = 32;
adaptSkipIncreaseFactor = 1 / 80.0;
} else if (filterType == FilterType.FILTER_ADAPTIVE_FULL) {
adaptMaxSkip = 0;
adaptSkipIncreaseSinceRow = 128;
adaptSkipIncreaseFactor = 1 / 120.0;
} else
throw new PngjOutputException("bad filter " + filterType);
}
}
@Override
protected void filterAndWrite(final byte[] rowb) {
if (rowb != this.rowb)
throw new RuntimeException("??"); // we rely on this
decideCurFilterType();
byte[] filtered = filterRowWithFilterType(curfilterType, rowb, rowbprev, rowbfilter);
sendToCompressedStream(filtered);
// swap rowb <-> rowbprev
byte[] aux = this.rowb;
this.rowb = rowbprev;
rowbprev = aux;
}
protected void decideCurFilterType() {
// decide the real filter and store in curfilterType
if (FilterType.isValidStandard(getFilterType())) {
curfilterType = getFilterType();
} else if (getFilterType() == FilterType.FILTER_PRESERVE) {
curfilterType = FilterType.getByVal(rowb[0]);
} else if (getFilterType() == FilterType.FILTER_CYCLIC) {
curfilterType = FilterType.getByVal(currentRow % 5);
} else if (getFilterType() == FilterType.FILTER_DEFAULT) {
setFilterType(getDefaultFilter());
curfilterType = getFilterType(); // this could be done once
} else if (FilterType.isAdaptive(getFilterType())) {// adaptive
if (currentRow == adaptNextRow) {
for (FilterType ftype : FilterType.getAllStandard())
filtersPerformance.updateFromRaw(ftype, rowb, rowbprev, currentRow);
curfilterType = filtersPerformance.getPreferred();
int skip =
(currentRow >= adaptSkipIncreaseSinceRow ? (int) Math
.round((currentRow - adaptSkipIncreaseSinceRow) * adaptSkipIncreaseFactor) : 0);
if (skip > adaptMaxSkip)
skip = adaptMaxSkip;
if (currentRow == 0)
skip = 0;
adaptNextRow = currentRow + 1 + skip;
}
} else {
throw new PngjOutputException("not implemented filter: " + getFilterType());
}
if (currentRow == 0 && curfilterType != FilterType.FILTER_NONE
&& curfilterType != FilterType.FILTER_SUB)
curfilterType = FilterType.FILTER_SUB; // first row should always be none or sub
}
@Override
public byte[] getRowb() {
if (!initdone)
init();
return rowb;
}
@Override
public void close() {
super.close();
}
/**
* Only for adaptive strategies. See {@link FiltersPerformance#setPreferenceForNone(double)}
*/
public void setPreferenceForNone(double preferenceForNone) {
filtersPerformance.setPreferenceForNone(preferenceForNone);
}
/**
* Only for adaptive strategies. See {@link FiltersPerformance#tuneMemory(double)}
*/
public void tuneMemory(double m) {
filtersPerformance.tuneMemory(m);
}
/**
* Only for adaptive strategies. See {@link FiltersPerformance#setFilterWeights(double[])}
*/
public void setFilterWeights(double[] weights) {
filtersPerformance.setFilterWeights(weights);
}
}