Package org.apache.harmony.awt.gl.color

Source Code of org.apache.harmony.awt.gl.color.NativeImageFormat

/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
/**
* @author Oleg V. Khaschansky
*/
package org.apache.harmony.awt.gl.color;

import java.util.ArrayList;

import org.apache.harmony.awt.internal.nls.Messages;

import com.jgraph.gaeawt.java.awt.image.BufferedImage;
import com.jgraph.gaeawt.java.awt.image.ColorModel;
import com.jgraph.gaeawt.java.awt.image.Raster;
import com.jgraph.gaeawt.java.awt.image.SampleModel;
import com.jgraph.gaeawt.java.awt.image.SinglePixelPackedSampleModel;

/**
* This class converts java color/sample models to the LCMS pixel formats.
* It also encapsulates all the information about the image format, which native CMM
* needs to have in order to read/write data.
*
* At present planar formats (multiple bands) are not supported
* and they are handled as a common (custom) case.
* Samples other than 1 - 7 bytes and multiple of 8 bits are
* also handled as custom (and won't be supported in the nearest future).
*/
class NativeImageFormat
{
  //////////////////////////////////////////////
  //  LCMS Pixel types
  private static final int PT_ANY = 0; // Don't check colorspace

  // 1 & 2 are reserved
  private static final int PT_GRAY = 3;

  private static final int PT_RGB = 4;

  // Skipping other since we don't use them here
  ///////////////////////////////////////////////

  // Conversion of predefined BufferedImage formats to LCMS formats
  private static final int INT_RGB_LCMS_FMT = colorspaceSh(PT_RGB)
      | extraSh(1) | channelsSh(3) | bytesSh(1) | doswapSh(1)
      | swapfirstSh(1);

  private static final int INT_ARGB_LCMS_FMT = INT_RGB_LCMS_FMT;

  private static final int INT_BGR_LCMS_FMT = colorspaceSh(PT_RGB)
      | extraSh(1) | channelsSh(3) | bytesSh(1);

  private static final int THREE_BYTE_BGR_LCMS_FMT = colorspaceSh(PT_RGB)
      | channelsSh(3) | bytesSh(1) | doswapSh(1);

  private static final int FOUR_BYTE_ABGR_LCMS_FMT = colorspaceSh(PT_RGB)
      | extraSh(1) | channelsSh(3) | bytesSh(1) | doswapSh(1);

  private static final int BYTE_GRAY_LCMS_FMT = colorspaceSh(PT_GRAY)
      | channelsSh(1) | bytesSh(1);

  // LCMS format packed into 32 bit value. For description
  // of this format refer to LCMS documentation.
  private int cmmFormat = 0;

  // Dimensions
  private int rows = 0;

  private int cols = 0;

  //  Scanline may contain some padding in the end
  private int scanlineStride = -1;

  private Object imageData;

  // It's possible to have offset from the beginning of the array
  private int dataOffset;

  // Has the image alpha channel? If has - here its band band offset goes
  private int alphaOffset = -1;

  // initializes proper field IDs
  private static native void initIDs();

  static
  {
    NativeCMM.loadCMM();
    initIDs();
  }

  ////////////////////////////////////
  // LCMS image format encoders
  ////////////////////////////////////
  private static int colorspaceSh(int s)
  {
    return (s << 16);
  }

  private static int swapfirstSh(int s)
  {
    return (s << 14);
  }

  private static int flavorSh(int s)
  {
    return (s << 13);
  }

  private static int planarSh(int s)
  {
    return (s << 12);
  }

  private static int endianSh(int s)
  {
    return (s << 11);
  }

  private static int doswapSh(int s)
  {
    return (s << 10);
  }

  private static int extraSh(int s)
  {
    return (s << 7);
  }

  private static int channelsSh(int s)
  {
    return (s << 3);
  }

  private static int bytesSh(int s)
  {
    return s;
  }

  ////////////////////////////////////
  // End of LCMS image format encoders
  ////////////////////////////////////

  // Accessors
  Object getChannelData()
  {
    return imageData;
  }

  int getNumCols()
  {
    return cols;
  }

  int getNumRows()
  {
    return rows;
  }

  // Constructors
  public NativeImageFormat()
  {
  }

  /**
   * Simple image layout for common case with
   * not optimized workflow.
   *
   * For hifi colorspaces with 5+ color channels imgData
   * should be <code>byte</code> array.
   *
   * For common colorspaces with up to 4 color channels it
   * should be <code>short</code> array.
   *
   * Alpha channel is handled by caller, not by CMS.
   *
   * Color channels are in their natural order (not BGR but RGB).
   *
   * @param imgData - array of <code>byte</code> or <code>short</code>
   * @param nChannels - number of channels
   * @param nRows - number of scanlines in the image
   * @param nCols - number of pixels in one row of the image
   */
  public NativeImageFormat(Object imgData, int nChannels, int nRows, int nCols)
  {
    if (imgData instanceof short[])
    {
      cmmFormat |= bytesSh(2);
    }
    else if (imgData instanceof byte[])
    {
      cmmFormat |= bytesSh(1);
    }
    else
      // awt.47=First argument should be byte or short array
      throw new IllegalArgumentException(Messages.getString("awt.47")); //$NON-NLS-1$

    cmmFormat |= channelsSh(nChannels);

    rows = nRows;
    cols = nCols;

    imageData = imgData;

    dataOffset = 0;
  }

  /**
   * Deduces image format from the buffered image type
   * or color and sample models.
   * @param bi - image
   * @return image format object
   */
  public static NativeImageFormat createNativeImageFormat(BufferedImage bi)
  {
    NativeImageFormat fmt = new NativeImageFormat();

    switch (bi.getType())
    {
      case BufferedImage.TYPE_INT_RGB:
      {
        fmt.cmmFormat = INT_RGB_LCMS_FMT;
        break;
      }

      case BufferedImage.TYPE_INT_ARGB:
      {
        fmt.cmmFormat = INT_ARGB_LCMS_FMT;
        fmt.alphaOffset = 3;
        break;
      }

      default:
        break; // Try to look at sample model and color model
    }

    if (fmt.cmmFormat == 0)
    {
      ColorModel cm = bi.getColorModel();
      SampleModel sm = bi.getSampleModel();

      if (sm instanceof SinglePixelPackedSampleModel)
      {
        SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
        fmt.cmmFormat = getFormatFromSPPSampleModel(sppsm,
            cm.hasAlpha());
        fmt.scanlineStride = calculateScanlineStrideSPPSM(sppsm,
            bi.getRaster());
      }

      if (cm.hasAlpha())
        fmt.alphaOffset = calculateAlphaOffset(sm, bi.getRaster());
    }

    if (fmt.cmmFormat == 0)
      return null;

    if (!fmt.setImageData(bi.getRaster().getDataBuffer()))
    {
      return null;
    }

    fmt.rows = bi.getHeight();
    fmt.cols = bi.getWidth();

    fmt.dataOffset = 0;

    return fmt;
  }

  /**
   * Deduces image format from the raster sample model.
   * @param r - raster
   * @return image format object
   */
  public static NativeImageFormat createNativeImageFormat(Raster r)
  {
    NativeImageFormat fmt = new NativeImageFormat();
    SampleModel sm = r.getSampleModel();

    if (sm instanceof SinglePixelPackedSampleModel)
    {
      SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
      fmt.cmmFormat = getFormatFromSPPSampleModel(sppsm, false);
      fmt.scanlineStride = calculateScanlineStrideSPPSM(sppsm, r);
    }

    if (fmt.cmmFormat == 0)
      return null;

    fmt.cols = r.getWidth();
    fmt.rows = r.getHeight();
    fmt.dataOffset = 0;

    if (!fmt.setImageData(r.getDataBuffer()))
      return null;

    return fmt;
  }

  /**
   * Obtains LCMS format from the single pixel packed sample model
   * @param sm - sample model
   * @param hasAlpha - true if there's an alpha channel
   * @return LCMS format
   */
  private static int getFormatFromSPPSampleModel(
      SinglePixelPackedSampleModel sm, boolean hasAlpha)
  {
    // Can we extract bytes?
    int mask = sm.getBitMasks()[0] >>> sm.getBitOffsets()[0];
    if (!(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF))
      return 0;

    // All masks are same?
    for (int i = 1; i < sm.getNumBands(); i++)
    {
      if ((sm.getBitMasks()[i] >>> sm.getBitOffsets()[i]) != mask)
        return 0;
    }

    int pixelSize = 4; // INT

    int bytes = 0;
    switch (mask)
    {
      case 0xFF:
        bytes = 1;
        break;
      case 0xFFFF:
        bytes = 2;
        break;
      case 0xFFFFFFFF:
        bytes = 4;
        break;
      default:
        return 0;
    }

    int channels = hasAlpha ? sm.getNumBands() - 1 : sm.getNumBands();
    int extra = hasAlpha ? 1 : 0;
    extra += pixelSize / bytes - sm.getNumBands(); // Unused bytes?

    // Form an ArrayList containing offset for each band
    ArrayList<Integer> offsetsLst = new ArrayList<Integer>();
    for (int k = 0; k < sm.getNumBands(); k++)
    {
      offsetsLst.add(new Integer(sm.getBitOffsets()[k] / (bytes * 8)));
    }

    // Add offsets for unused space
    for (int i = 0; i < pixelSize / bytes; i++)
    {
      if (offsetsLst.indexOf(new Integer(i)) < 0)
        offsetsLst.add(new Integer(i));
    }

    int offsets[] = new int[pixelSize / bytes];
    for (int i = 0; i < offsetsLst.size(); i++)
    {
      offsets[i] = offsetsLst.get(i).intValue();
    }

    int doSwap = 0;
    int swapFirst = 0;
    boolean knownFormat = false;

    int i;

    // "RGBA"
    for (i = 0; i < pixelSize; i++)
    {
      if (offsets[i] != i)
        break;
    }
    if (i == pixelSize)
    { // Ok, it is it
      doSwap = 0;
      swapFirst = 0;
      knownFormat = true;
    }

    // "ARGB"
    if (!knownFormat)
    {
      for (i = 0; i < pixelSize - 1; i++)
      {
        if (offsets[i] != i + 1)
          break;
      }
      if (offsets[i] == 0)
        i++;
      if (i == pixelSize)
      { // Ok, it is it
        doSwap = 0;
        swapFirst = 1;
        knownFormat = true;
      }
    }

    // "BGRA"
    if (!knownFormat)
    {
      for (i = 0; i < pixelSize - 1; i++)
      {
        if (offsets[i] != pixelSize - 2 - i)
          break;
      }
      if (offsets[i] == pixelSize - 1)
        i++;
      if (i == pixelSize)
      { // Ok, it is it
        doSwap = 1;
        swapFirst = 1;
        knownFormat = true;
      }
    }

    // "ABGR"
    if (!knownFormat)
    {
      for (i = 0; i < pixelSize; i++)
      {
        if (offsets[i] != pixelSize - 1 - i)
          break;
      }
      if (i == pixelSize)
      { // Ok, it is it
        doSwap = 1;
        swapFirst = 0;
        knownFormat = true;
      }
    }

    // XXX - Planar formats are not supported yet
    if (!knownFormat)
      return 0;

    return channelsSh(channels) | bytesSh(bytes) | extraSh(extra)
        | doswapSh(doSwap) | swapfirstSh(swapFirst);
  }

  /**
   * Obtains data array from the DataBuffer object
   * @param db - data buffer
   * @return - true if successful
   */
  private boolean setImageData(int[] db)
  {
    imageData = db;
    return true;
  }

  /**
   * Calculates scanline stride in bytes
   * @param sppsm - sample model
   * @param r - raster
   * @return scanline stride in bytes
   */
  private static int calculateScanlineStrideSPPSM(
      SinglePixelPackedSampleModel sppsm, Raster r)
  {
    if (sppsm.getScanlineStride() != sppsm.getWidth())
    {
      int dataTypeSize = 32 / 8;
      return sppsm.getScanlineStride() * dataTypeSize;
    }
    return -1;
  }

  /**
   * Calculates byte offset of the alpha channel from the beginning of the pixel data
   * @param sm - sample model
   * @param r - raster
   * @return byte offset of the alpha channel
   */
  private static int calculateAlphaOffset(SampleModel sm, Raster r)
  {
    if (sm instanceof SinglePixelPackedSampleModel)
    {
      SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
      return sppsm.getBitOffsets()[sppsm.getBitOffsets().length - 1] / 8;
    }
    else
    {
      return -1; // No offset, don't copy alpha
    }
  }
}
TOP

Related Classes of org.apache.harmony.awt.gl.color.NativeImageFormat

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.