8177393: Result of RescaleOp for 4BYTE_ABGR images may be 25% black

Reviewed-by: flar, psadhukhan
This commit is contained in:
Phil Race 2017-05-19 14:57:51 -07:00
parent ed2434de14
commit de926ebe30
2 changed files with 201 additions and 84 deletions

View File

@ -27,6 +27,8 @@ package java.awt.image;
import java.awt.color.ColorSpace;
import java.awt.geom.Rectangle2D;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.RenderingHints;
@ -193,9 +195,10 @@ public class RescaleOp implements BufferedImageOp, RasterOp {
int nBands,
int nElems) {
byte[][] lutData = new byte[scale.length][nElems];
byte[][] lutData = new byte[nBands][nElems];
int band;
for (int band=0; band<scale.length; band++) {
for (band=0; band<scale.length; band++) {
float bandScale = scale[band];
float bandOff = off[band];
byte[] bandLutData = lutData[band];
@ -212,6 +215,17 @@ public class RescaleOp implements BufferedImageOp, RasterOp {
}
}
int maxToCopy = (nBands == 4 && scale.length == 4) ? 4 : 3;
while (band < lutData.length && band < maxToCopy) {
System.arraycopy(lutData[band-1], 0, lutData[band], 0, nElems);
band++;
}
if (nBands == 4 && band < nBands) {
byte[] bandLutData = lutData[band];
for (int i=0; i<nElems; i++) {
bandLutData[i] = (byte)i;
}
}
return new ByteLookupTable(0, lutData);
}
@ -228,9 +242,10 @@ public class RescaleOp implements BufferedImageOp, RasterOp {
int nBands,
int nElems) {
short[][] lutData = new short[scale.length][nElems];
short[][] lutData = new short[nBands][nElems];
int band = 0;
for (int band=0; band<scale.length; band++) {
for (band=0; band<scale.length; band++) {
float bandScale = scale[band];
float bandOff = off[band];
short[] bandLutData = lutData[band];
@ -246,6 +261,17 @@ public class RescaleOp implements BufferedImageOp, RasterOp {
bandLutData[i] = (short)val;
}
}
int maxToCopy = (nBands == 4 && scale.length == 4) ? 4 : 3;
while (band < lutData.length && band < maxToCopy) {
System.arraycopy(lutData[band-1], 0, lutData[band], 0, nElems);
band++;
}
if (nBands == 4 && band < nBands) {
short[] bandLutData = lutData[band];
for (int i=0; i<nElems; i++) {
bandLutData[i] = (short)i;
}
}
return new ShortLookupTable(0, lutData);
}
@ -300,6 +326,19 @@ public class RescaleOp implements BufferedImageOp, RasterOp {
}
}
if (dstSM instanceof ComponentSampleModel) {
ComponentSampleModel dsm = (ComponentSampleModel)dstSM;
if (dsm.getPixelStride() != dst.getNumBands()) {
return false;
}
}
if (srcSM instanceof ComponentSampleModel) {
ComponentSampleModel csm = (ComponentSampleModel)srcSM;
if (csm.getPixelStride() != src.getNumBands()) {
return false;
}
}
return true;
}
@ -344,6 +383,7 @@ public class RescaleOp implements BufferedImageOp, RasterOp {
}
boolean needToConvert = false;
boolean needToDraw = false;
// Include alpha
if (scaleConst > numSrcColorComp && srcCM.hasAlpha()) {
@ -381,95 +421,34 @@ public class RescaleOp implements BufferedImageOp, RasterOp {
}
boolean scaleAlpha = true;
//
// The number of sets of scaling constants may be one,
// in which case the same constants are applied to all color
// (but NOT alpha) components. Otherwise, the number of sets
// of scaling constants may equal the number of Source color
// components, in which case NO rescaling of the alpha component
// (if present) is performed.
//
if (numSrcColorComp == scaleConst || scaleConst == 1) {
scaleAlpha = false;
}
//
// Try to use a native BI rescale operation first
//
if (ImagingLib.filter(this, src, dst) == null) {
if (src.getRaster().getNumBands() !=
dst.getRaster().getNumBands()) {
needToDraw = true;
dst = createCompatibleDestImage(src, null);
}
//
// Native BI rescale failed - convert to rasters
//
WritableRaster srcRaster = src.getRaster();
WritableRaster dstRaster = dst.getRaster();
if (!scaleAlpha) {
if (srcCM.hasAlpha()) {
// Do not rescale Alpha component
int minx = srcRaster.getMinX();
int miny = srcRaster.getMinY();
int[] bands = new int[numSrcColorComp];
for (int i=0; i < numSrcColorComp; i++) {
bands[i] = i;
}
srcRaster =
srcRaster.createWritableChild(minx, miny,
srcRaster.getWidth(),
srcRaster.getHeight(),
minx, miny,
bands);
}
if (dstCM.hasAlpha()) {
int minx = dstRaster.getMinX();
int miny = dstRaster.getMinY();
int[] bands = new int[numSrcColorComp];
for (int i=0; i < numSrcColorComp; i++) {
bands[i] = i;
}
dstRaster =
dstRaster.createWritableChild(minx, miny,
dstRaster.getWidth(),
dstRaster.getHeight(),
minx, miny,
bands);
}
}
//
// Call the raster filter method
//
filterRasterImpl(srcRaster, dstRaster, scaleConst);
//
// here copy the unscaled src alpha to destination alpha channel
//
if (!scaleAlpha) {
Raster srcAlphaRaster = null;
WritableRaster dstAlphaRaster = null;
if (srcCM.hasAlpha()) {
srcAlphaRaster = src.getAlphaRaster();
}
if (dstCM.hasAlpha()) {
dstAlphaRaster = dst.getAlphaRaster();
if (srcAlphaRaster != null) {
dstAlphaRaster.setRect(srcAlphaRaster);
} else {
int alpha = 0xff << 24;
for (int cy=0; cy < dst.getHeight(); cy++) {
for (int cx=0; cx < dst.getWidth(); cx++) {
int color = dst.getRGB(cx, cy);
dst.setRGB(cx, cy, color | alpha);
}
}
}
}
}
filterRasterImpl(srcRaster, dstRaster, scaleConst, false);
}
if (needToDraw) {
Graphics2D g = origDst.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(dst, 0, 0, width, height, null);
g.dispose();
}
if (needToConvert) {
// ColorModels are not the same
ColorConvertOp ccop = new ColorConvertOp(hints);
@ -497,10 +476,11 @@ public class RescaleOp implements BufferedImageOp, RasterOp {
* stated in the class comments.
*/
public final WritableRaster filter (Raster src, WritableRaster dst) {
return filterRasterImpl(src, dst, length);
return filterRasterImpl(src, dst, length, true);
}
private WritableRaster filterRasterImpl(Raster src, WritableRaster dst, int scaleConst) {
private WritableRaster filterRasterImpl(Raster src, WritableRaster dst,
int scaleConst, boolean sCheck) {
int numBands = src.getNumBands();
int width = src.getWidth();
int height = src.getHeight();
@ -527,7 +507,7 @@ public class RescaleOp implements BufferedImageOp, RasterOp {
// Make sure that the arrays match
// Make sure that the low/high/constant arrays match
if (scaleConst != 1 && scaleConst != src.getNumBands()) {
if (sCheck && scaleConst != 1 && scaleConst != src.getNumBands()) {
throw new IllegalArgumentException("Number of scaling constants "+
"does not equal the number of"+
" of bands in the src raster");
@ -598,8 +578,14 @@ public class RescaleOp implements BufferedImageOp, RasterOp {
srcPix = src.getPixel(sX, sY, srcPix);
tidx = 0;
for (int z=0; z<numBands; z++, tidx += step) {
if ((scaleConst == 1 || scaleConst == 3) &&
(z == 3) && (numBands == 4)) {
val = srcPix[z];
} else {
val = (int)(srcPix[z]*scaleFactors[tidx]
+ offsets[tidx]);
}
// Clamp
if ((val & dstMask[z]) != 0) {
if (val < 0) {

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8177393
* @summary Verify RescaleOp applied to BufferedImages.
* @run main ImageRescaleOpTest
*/
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import static java.awt.image.BufferedImage.*;
import java.awt.image.RescaleOp;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageRescaleOpTest {
int w = 10, h = 10;
float scaleFactor = 0.5f;
float offset = 0.0f;
static boolean saveImage = false;
public static void main(String[] args) throws Exception {
saveImage = args.length > 0;
ImageRescaleOpTest test = new ImageRescaleOpTest();
test.startTest();
}
String getFileName(int s, int d) {
return textFor(s)+"_to_"+textFor(d)+".png";
}
String getMsgText(int s, int d) {
return textFor(s)+"->"+textFor(d)+": ";
}
String textFor(int t) {
switch (t) {
case TYPE_INT_ARGB : return "ARGB";
case TYPE_INT_RGB : return "RGB";
case TYPE_4BYTE_ABGR : return "4BYTEABGR";
case TYPE_3BYTE_BGR : return "3BYTEBGR";
case TYPE_USHORT_555_RGB : return "USHORT_555_RGB";
case TYPE_USHORT_565_RGB : return "USHORT_565_RGB";
case TYPE_USHORT_GRAY : return "USHORT_GRAY";
default : return "OTHER";
}
}
private void startTest() throws Exception {
int expect = 0xff7f7f7f;
runTest(TYPE_INT_RGB, TYPE_INT_RGB, expect);
runTest(TYPE_INT_ARGB, TYPE_INT_ARGB, expect);
runTest(TYPE_INT_ARGB, TYPE_INT_RGB, expect);
runTest(TYPE_INT_RGB, TYPE_INT_ARGB, expect);
runTest(TYPE_3BYTE_BGR, TYPE_3BYTE_BGR, expect);
runTest(TYPE_3BYTE_BGR, TYPE_4BYTE_ABGR, expect);
runTest(TYPE_4BYTE_ABGR, TYPE_3BYTE_BGR, expect);
runTest(TYPE_4BYTE_ABGR, TYPE_4BYTE_ABGR, expect);
/* Slightly different values here due to limited precision */
runTest(TYPE_USHORT_555_RGB, TYPE_USHORT_555_RGB, 0xff7b7b7b);
runTest(TYPE_USHORT_565_RGB, TYPE_USHORT_565_RGB, 0xff7b7d7b);
/* 565->555 and 555->565 results are wrong as the slow code
* path used is not accounting for the difference in the range.
*/
//runTest(TYPE_USHORT_555_RGB, TYPE_USHORT_565_RGB, expect);
//runTest(TYPE_USHORT_565_RGB, TYPE_USHORT_555_RGB, expect);
runTest(TYPE_USHORT_GRAY, TYPE_USHORT_GRAY, 0xffbcbcbc);
}
private void check(BufferedImage bi, int expect, String msg) {
int argb = bi.getRGB(w-1, h-1);
System.out.println(msg + Integer.toHexString(argb));
if (argb != expect) {
throw new RuntimeException(msg +
" expected " + Integer.toHexString(expect) +
" but got " + Integer.toHexString(argb));
}
}
private void runTest(int sType, int dType, int expect) {
BufferedImage src = new BufferedImage(w, h, sType);
BufferedImage dst = new BufferedImage(w, h, dType);
String msg = getMsgText(sType, dType);
Graphics2D g2d = src.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, w, h);
RescaleOp res = new RescaleOp(scaleFactor, offset, null);
res.filter(src, dst);
if (saveImage) {
try {
String fname = getFileName(sType, dType);
ImageIO.write(dst, "png", new File(fname));
} catch (IOException e) {
}
}
check(dst, expect, msg);
}
}