Path: blob/master/src/java.desktop/share/classes/sun/java2d/pipe/BufferedBufImgOps.java
41159 views
/*1* Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.pipe;2627import java.awt.color.ColorSpace;28import java.awt.image.BufferedImage;29import java.awt.image.BufferedImageOp;30import java.awt.image.ByteLookupTable;31import java.awt.image.ColorModel;32import java.awt.image.ConvolveOp;33import java.awt.image.IndexColorModel;34import java.awt.image.Kernel;35import java.awt.image.LookupOp;36import java.awt.image.LookupTable;37import java.awt.image.RescaleOp;38import java.awt.image.ShortLookupTable;39import sun.java2d.SurfaceData;40import static sun.java2d.pipe.BufferedOpCodes.*;4142public class BufferedBufImgOps {4344public static void enableBufImgOp(RenderQueue rq, SurfaceData srcData,45BufferedImage srcImg,46BufferedImageOp biop)47{48if (biop instanceof ConvolveOp) {49enableConvolveOp(rq, srcData, (ConvolveOp)biop);50} else if (biop instanceof RescaleOp) {51enableRescaleOp(rq, srcData, srcImg, (RescaleOp)biop);52} else if (biop instanceof LookupOp) {53enableLookupOp(rq, srcData, srcImg, (LookupOp)biop);54} else {55throw new InternalError("Unknown BufferedImageOp");56}57}5859public static void disableBufImgOp(RenderQueue rq, BufferedImageOp biop) {60if (biop instanceof ConvolveOp) {61disableConvolveOp(rq);62} else if (biop instanceof RescaleOp) {63disableRescaleOp(rq);64} else if (biop instanceof LookupOp) {65disableLookupOp(rq);66} else {67throw new InternalError("Unknown BufferedImageOp");68}69}7071/**************************** ConvolveOp support ****************************/7273public static boolean isConvolveOpValid(ConvolveOp cop) {74Kernel kernel = cop.getKernel();75int kw = kernel.getWidth();76int kh = kernel.getHeight();77// REMIND: we currently can only handle 3x3 and 5x5 kernels,78// but hopefully this is just a temporary restriction;79// see native shader comments for more details80if (!(kw == 3 && kh == 3) && !(kw == 5 && kh == 5)) {81return false;82}83return true;84}8586private static void enableConvolveOp(RenderQueue rq,87SurfaceData srcData,88ConvolveOp cop)89{90// assert rq.lock.isHeldByCurrentThread();91boolean edgeZero =92cop.getEdgeCondition() == ConvolveOp.EDGE_ZERO_FILL;93Kernel kernel = cop.getKernel();94int kernelWidth = kernel.getWidth();95int kernelHeight = kernel.getHeight();96int kernelSize = kernelWidth * kernelHeight;97int sizeofFloat = 4;98int totalBytesRequired = 4 + 8 + 12 + (kernelSize * sizeofFloat);99100RenderBuffer buf = rq.getBuffer();101rq.ensureCapacityAndAlignment(totalBytesRequired, 4);102buf.putInt(ENABLE_CONVOLVE_OP);103buf.putLong(srcData.getNativeOps());104buf.putInt(edgeZero ? 1 : 0);105buf.putInt(kernelWidth);106buf.putInt(kernelHeight);107buf.put(kernel.getKernelData(null));108}109110private static void disableConvolveOp(RenderQueue rq) {111// assert rq.lock.isHeldByCurrentThread();112RenderBuffer buf = rq.getBuffer();113rq.ensureCapacity(4);114buf.putInt(DISABLE_CONVOLVE_OP);115}116117/**************************** RescaleOp support *****************************/118119public static boolean isRescaleOpValid(RescaleOp rop,120BufferedImage srcImg)121{122int numFactors = rop.getNumFactors();123ColorModel srcCM = srcImg.getColorModel();124125if (srcCM instanceof IndexColorModel) {126throw new127IllegalArgumentException("Rescaling cannot be "+128"performed on an indexed image");129}130if (numFactors != 1 &&131numFactors != srcCM.getNumColorComponents() &&132numFactors != srcCM.getNumComponents())133{134throw new IllegalArgumentException("Number of scaling constants "+135"does not equal the number of"+136" color or color/alpha"+137" components");138}139140int csType = srcCM.getColorSpace().getType();141if (csType != ColorSpace.TYPE_RGB &&142csType != ColorSpace.TYPE_GRAY)143{144// Not prepared to deal with other color spaces145return false;146}147148if (numFactors == 2 || numFactors > 4) {149// Not really prepared to handle this at the native level, so...150return false;151}152153return true;154}155156private static void enableRescaleOp(RenderQueue rq,157SurfaceData srcData,158BufferedImage srcImg,159RescaleOp rop)160{161// assert rq.lock.isHeldByCurrentThread();162ColorModel srcCM = srcImg.getColorModel();163boolean nonPremult =164srcCM.hasAlpha() &&165srcCM.isAlphaPremultiplied();166167/*168* Note: The user-provided scale factors and offsets are arranged169* in R/G/B/A order, regardless of the raw data order of the170* underlying Raster/DataBuffer. The source image data is ultimately171* converted into RGBA data when uploaded to an OpenGL texture172* (even for TYPE_GRAY), so the scale factors and offsets are already173* in the order expected by the native OpenGL code.174*175* However, the offsets provided by the user are in a range dictated176* by the size of each color/alpha band in the source image. For177* example, for 8/8/8 data each offset is in the range [0,255],178* for 5/5/5 data each offset is in the range [0,31], and so on.179* The OpenGL shader only thinks in terms of [0,1], so below we need180* to normalize the user-provided offset values into the range [0,1].181*/182int numFactors = rop.getNumFactors();183float[] origScaleFactors = rop.getScaleFactors(null);184float[] origOffsets = rop.getOffsets(null);185186// To make things easier, we will always pass all four bands187// down to native code...188float[] normScaleFactors;189float[] normOffsets;190191if (numFactors == 1) {192normScaleFactors = new float[4];193normOffsets = new float[4];194for (int i = 0; i < 3; i++) {195normScaleFactors[i] = origScaleFactors[0];196normOffsets[i] = origOffsets[0];197}198// Leave alpha untouched...199normScaleFactors[3] = 1.0f;200normOffsets[3] = 0.0f;201} else if (numFactors == 3) {202normScaleFactors = new float[4];203normOffsets = new float[4];204for (int i = 0; i < 3; i++) {205normScaleFactors[i] = origScaleFactors[i];206normOffsets[i] = origOffsets[i];207}208// Leave alpha untouched...209normScaleFactors[3] = 1.0f;210normOffsets[3] = 0.0f;211} else { // (numFactors == 4)212normScaleFactors = origScaleFactors;213normOffsets = origOffsets;214}215216// The user-provided offsets are specified in the range217// of each source color band, but the OpenGL shader only wants218// to deal with data in the range [0,1], so we need to normalize219// each offset value to the range [0,1] here.220if (srcCM.getNumComponents() == 1) {221// Gray data222int nBits = srcCM.getComponentSize(0);223int maxValue = (1 << nBits) - 1;224for (int i = 0; i < 3; i++) {225normOffsets[i] /= maxValue;226}227} else {228// RGB(A) data229for (int i = 0; i < srcCM.getNumComponents(); i++) {230int nBits = srcCM.getComponentSize(i);231int maxValue = (1 << nBits) - 1;232normOffsets[i] /= maxValue;233}234}235236int sizeofFloat = 4;237int totalBytesRequired = 4 + 8 + 4 + (4 * sizeofFloat * 2);238239RenderBuffer buf = rq.getBuffer();240rq.ensureCapacityAndAlignment(totalBytesRequired, 4);241buf.putInt(ENABLE_RESCALE_OP);242buf.putLong(srcData.getNativeOps());243buf.putInt(nonPremult ? 1 : 0);244buf.put(normScaleFactors);245buf.put(normOffsets);246}247248private static void disableRescaleOp(RenderQueue rq) {249// assert rq.lock.isHeldByCurrentThread();250RenderBuffer buf = rq.getBuffer();251rq.ensureCapacity(4);252buf.putInt(DISABLE_RESCALE_OP);253}254255/**************************** LookupOp support ******************************/256257public static boolean isLookupOpValid(LookupOp lop,258BufferedImage srcImg)259{260LookupTable table = lop.getTable();261int numComps = table.getNumComponents();262ColorModel srcCM = srcImg.getColorModel();263264if (srcCM instanceof IndexColorModel) {265throw new266IllegalArgumentException("LookupOp cannot be "+267"performed on an indexed image");268}269if (numComps != 1 &&270numComps != srcCM.getNumComponents() &&271numComps != srcCM.getNumColorComponents())272{273throw new IllegalArgumentException("Number of arrays in the "+274" lookup table ("+275numComps+276") is not compatible with"+277" the src image: "+srcImg);278}279280int csType = srcCM.getColorSpace().getType();281if (csType != ColorSpace.TYPE_RGB &&282csType != ColorSpace.TYPE_GRAY)283{284// Not prepared to deal with other color spaces285return false;286}287288if (numComps == 2 || numComps > 4) {289// Not really prepared to handle this at the native level, so...290return false;291}292293// The LookupTable spec says that "all arrays must be the294// same size" but unfortunately the constructors do not295// enforce that. Also, our native code only works with296// arrays no larger than 256 elements, so check both of297// these restrictions here.298if (table instanceof ByteLookupTable) {299byte[][] data = ((ByteLookupTable)table).getTable();300for (int i = 1; i < data.length; i++) {301if (data[i].length > 256 ||302data[i].length != data[i-1].length)303{304return false;305}306}307} else if (table instanceof ShortLookupTable) {308short[][] data = ((ShortLookupTable)table).getTable();309for (int i = 1; i < data.length; i++) {310if (data[i].length > 256 ||311data[i].length != data[i-1].length)312{313return false;314}315}316} else {317return false;318}319320return true;321}322323private static void enableLookupOp(RenderQueue rq,324SurfaceData srcData,325BufferedImage srcImg,326LookupOp lop)327{328// assert rq.lock.isHeldByCurrentThread();329boolean nonPremult =330srcImg.getColorModel().hasAlpha() &&331srcImg.isAlphaPremultiplied();332333LookupTable table = lop.getTable();334int numBands = table.getNumComponents();335int offset = table.getOffset();336int bandLength;337int bytesPerElem;338boolean shortData;339340if (table instanceof ShortLookupTable) {341short[][] data = ((ShortLookupTable)table).getTable();342bandLength = data[0].length;343bytesPerElem = 2;344shortData = true;345} else { // (table instanceof ByteLookupTable)346byte[][] data = ((ByteLookupTable)table).getTable();347bandLength = data[0].length;348bytesPerElem = 1;349shortData = false;350}351352// Adjust the LUT length so that it ends on a 4-byte boundary353int totalLutBytes = numBands * bandLength * bytesPerElem;354int paddedLutBytes = (totalLutBytes + 3) & (~3);355int padding = paddedLutBytes - totalLutBytes;356int totalBytesRequired = 4 + 8 + 20 + paddedLutBytes;357358RenderBuffer buf = rq.getBuffer();359rq.ensureCapacityAndAlignment(totalBytesRequired, 4);360buf.putInt(ENABLE_LOOKUP_OP);361buf.putLong(srcData.getNativeOps());362buf.putInt(nonPremult ? 1 : 0);363buf.putInt(shortData ? 1 : 0);364buf.putInt(numBands);365buf.putInt(bandLength);366buf.putInt(offset);367if (shortData) {368short[][] data = ((ShortLookupTable)table).getTable();369for (int i = 0; i < numBands; i++) {370buf.put(data[i]);371}372} else {373byte[][] data = ((ByteLookupTable)table).getTable();374for (int i = 0; i < numBands; i++) {375buf.put(data[i]);376}377}378if (padding != 0) {379buf.position(buf.position() + padding);380}381}382383private static void disableLookupOp(RenderQueue rq) {384// assert rq.lock.isHeldByCurrentThread();385RenderBuffer buf = rq.getBuffer();386rq.ensureCapacity(4);387buf.putInt(DISABLE_LOOKUP_OP);388}389}390391392