IndexedImage.java
上传用户:haobig99
上传日期:2022-06-15
资源大小:369k
文件大小:15k
源码类别:

J2ME

开发平台:

Java

  1. /*
  2.  * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
  3.  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4.  *
  5.  * This code is free software; you can redistribute it and/or modify it
  6.  * under the terms of the GNU General Public License version 2 only, as
  7.  * published by the Free Software Foundation.  Sun designates this
  8.  * particular file as subject to the "Classpath" exception as provided
  9.  * by Sun in the LICENSE file that accompanied this code.
  10.  *
  11.  * This code is distributed in the hope that it will be useful, but WITHOUT
  12.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14.  * version 2 for more details (a copy is included in the LICENSE file that
  15.  * accompanied this code).
  16.  *
  17.  * You should have received a copy of the GNU General Public License version
  18.  * 2 along with this work; if not, write to the Free Software Foundation,
  19.  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20.  *
  21.  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22.  * CA 95054 USA or visit www.sun.com if you need additional information or
  23.  * have any questions.
  24.  */
  25. package com.sun.lwuit;
  26. import com.sun.lwuit.geom.Dimension;
  27. import java.io.ByteArrayInputStream;
  28. import java.io.ByteArrayOutputStream;
  29. import java.io.DataInputStream;
  30. import java.io.DataOutputStream;
  31. import java.io.IOException;
  32. /**
  33.  * An indexed image is an image "compressed" in memory to occupy as little memory 
  34.  * as possible in this sense it is slower to draw and only a single indexed image
  35.  * can be drawn at any given time. However, this allows images with low color counts
  36.  * to use as little as one byte per pixel which can save up to 4 times of the memory
  37.  * overhead. 
  38.  *
  39.  * @deprecated This class should no longer be referenced directly. Use Image.createIndexed instead
  40.  * @author Shai Almog
  41.  */
  42. public class IndexedImage extends Image {
  43.     private int width;
  44.     private int height;
  45.     
  46.     // package protected for access by the resource editor
  47.     byte[] imageDataByte;
  48.     int[] palette; 
  49.     
  50.     /**
  51.      * Creates an indexed image with byte data
  52.      * 
  53.      * @param width image width
  54.      * @param height image height
  55.      * @param palette the color palette to use with the byte data
  56.      * @param data byte data containing palette offsets to map to ARGB colors
  57.      * @deprecated use Image.createIndexed instead
  58.      */
  59.     public IndexedImage(int width, int height, int[] palette, byte[] data) {
  60.         super(null);
  61.         this.width = width;
  62.         this.height = height;
  63.         this.palette = palette;
  64.         this.imageDataByte = data;
  65.         initOpaque();
  66.     }
  67.     private void initOpaque() {
  68.         if(palette != null) {
  69.             for(int iter = 0 ; iter < palette.length ; iter++) {
  70.                 if((palette[iter] & 0xff000000) != 0xff000000) {
  71.                     setOpaque(false);
  72.                     return;
  73.                 }
  74.             }
  75.             setOpaque(true);
  76.         } else {
  77.             setOpaque(false);
  78.         }
  79.     }
  80.     /**
  81.      * Converts an image to a package image after which the original image can be GC'd
  82.      */
  83.     private IndexedImage(int width, int height, int[] palette, int[] rgb) {
  84.         super(null);
  85.         
  86.         this.width = width;
  87.         this.height = height;
  88.         this.palette = palette;
  89.         
  90.         // byte based package image
  91.         imageDataByte = new byte[width * height];
  92.         for(int iter = 0 ; iter < imageDataByte.length ; iter++) {
  93.             imageDataByte[iter] = (byte)paletteOffset(rgb[iter]);
  94.         }
  95.         initOpaque();
  96.     }
  97.     /**
  98.      * Finds the offset within the palette of the given rgb value
  99.      * 
  100.      * @param value ARGB value from the image
  101.      * @return offset within the palette array
  102.      */
  103.     private int paletteOffset(int rgb) {
  104.         for(int iter = 0 ; iter < palette.length ; iter++) {
  105.             if(rgb == palette[iter]) {
  106.                 return iter;
  107.             }
  108.         }
  109.         throw new IllegalStateException("Invalid palette request in paletteOffset");
  110.     }
  111.     /**
  112.      * Packs the image loaded by MIDP
  113.      * 
  114.      * @param imageName a name to load using Image.createImage()
  115.      * @return a packed image
  116.      * @throws IOException when create fails
  117.      */
  118.     public static Image pack(String imageName) throws IOException {
  119.         return pack(Image.createImage(imageName));
  120.     }
  121.     /**
  122.      * @inheritDoc
  123.      */
  124.     public Image subImage(int x, int y, int width, int height, boolean processAlpha)  {
  125.         byte[] arr = new byte[width * height];
  126.         for(int iter = 0 ; iter < arr.length ; iter++) {
  127.             int destY = iter / width;
  128.             int destX = iter % width;
  129.             int offset = x + destX + ((y + destY) * this.width);
  130.             arr[iter] = imageDataByte[offset];
  131.         }
  132.         
  133.         return new IndexedImage(width, height, palette, arr);
  134.     }
  135.     /**
  136.      * Unsupported in the current version, this method will be implemented in a future release
  137.      */
  138.     public Image rotate(int degrees) {
  139.         throw new RuntimeException("The rotate method is not supported by indexed images at the moment");
  140.     }
  141.     /**
  142.      * @inheritDoc
  143.      */
  144.     public Image modifyAlpha(byte alpha) {
  145.         int[] newPalette = new int[palette.length];
  146.         System.arraycopy(palette, 0, newPalette, 0, palette.length);
  147.         int alphaInt = (((int)alpha) << 24) & 0xff000000;
  148.         for(int iter = 0 ; iter < palette.length ; iter++) {
  149.             if((palette[iter] & 0xff000000) != 0) {
  150.                 newPalette[iter] = (palette[iter] & 0xffffff) | alphaInt;
  151.             }
  152.         }
  153.         return new IndexedImage(width, height, newPalette, imageDataByte);
  154.     }
  155.     
  156.     /**
  157.      * This method is unsupported in this image type
  158.      */
  159.     public Graphics getGraphics() {
  160.         throw new RuntimeException("Indexed image objects are immutable");
  161.     }
  162.     /**
  163.      * @inheritDoc
  164.      */
  165.     void getRGB(int[] rgbData,
  166.             int offset,
  167.             int x,
  168.             int y,
  169.             int width,
  170.             int height){
  171.         // need to support scanlength???
  172.         int startPoint = y * this.width + x;
  173.         for(int rows = 0 ; rows < height ; rows++) {
  174.             int currentRow = rows * width;
  175.             for(int columns = 0 ; columns < width ; columns++) {
  176.                 int i = imageDataByte[startPoint + columns] & 0xff;
  177.                 rgbData[offset + currentRow + columns] = palette[i];
  178.             }
  179.             startPoint += this.width;
  180.         }
  181.     }
  182.     
  183.     
  184.     /**
  185.      * Packs the source rgba image and returns null if it fails
  186.      * 
  187.      * @param rgb array containing ARGB data
  188.      * @param width width of the image in the rgb array
  189.      * @param height height of the image
  190.      * @return a packed image or null
  191.      */
  192.     public static IndexedImage pack(int[] rgb, int width, int height) {
  193.         int arrayLength = width * height;
  194.         
  195.         // using a Vector is slower for a small scale device and this is mission critical code
  196.         int[] tempPalette = new int[256];
  197.         int paletteLocation = 0;
  198.         for(int iter = 0 ; iter < arrayLength ; iter++) {
  199.             int current = rgb[iter];
  200.             if(!contains(tempPalette, paletteLocation, current)) {
  201.                 if(paletteLocation > 255) {
  202.                     return null;
  203.                 }
  204.                 tempPalette[paletteLocation] = current;
  205.                 paletteLocation++;
  206.             }
  207.         }
  208.         // we need to "shrink" the palette array
  209.         if(paletteLocation != tempPalette.length) {
  210.             int[] newArray = new int[paletteLocation];
  211.             System.arraycopy(tempPalette, 0, newArray, 0, paletteLocation);
  212.             tempPalette = newArray;
  213.         }
  214.         
  215.         
  216.         IndexedImage i = new IndexedImage(width, height, tempPalette, rgb);
  217.         return i;
  218.     }
  219.     
  220.     /**
  221.      * Tries to pack the given image and would return the packed image or source
  222.      * image if packing failed
  223.      * 
  224.      * @param sourceImage the image which would be converted to a packed image if possible
  225.      * @return the source image if packing failed or a newly packed image if it succeeded
  226.      */
  227.     public static Image pack(final Image sourceImage) {
  228.         int width = sourceImage.getWidth();
  229.         int height = sourceImage.getHeight();
  230.         int[] rgb = sourceImage.getRGBCached();
  231.         
  232.         Image i = pack(rgb, width, height);
  233.         if(i == null) {
  234.             return sourceImage;
  235.         }
  236.         return i;
  237.     }
  238.     
  239.     
  240.     /**
  241.      * Searches the array up to "length" and returns true if value is within the
  242.      * array up to that point.
  243.      */
  244.     private static boolean contains(int[] array, int length, int value) {
  245.         for(int iter = 0 ; iter < length ; iter++) {
  246.             if(array[iter] == value) {
  247.                 return true;
  248.             }
  249.         }
  250.         return false;
  251.     }
  252.     
  253.     static int[] lineCache;
  254.     
  255.     /**
  256.      * @inheritDoc
  257.      */
  258.     protected void drawImage(Graphics g, Object nativeGraphics, int x, int y) {
  259.          if(lineCache == null || lineCache.length < width * 3) {
  260.              lineCache = new int[width * 3];
  261.          }
  262.         
  263.         // for performance we can calculate the visible drawing area so we don't have to
  264.         // calculate the whole array
  265.         int clipY = g.getClipY();
  266.         int clipBottomY = g.getClipHeight() + clipY;
  267.         int firstLine = 0;
  268.         int lastLine = height;
  269.         if(clipY > y) {
  270.             firstLine = clipY - y;
  271.         } 
  272.         if(clipBottomY < y + height) {
  273.             lastLine = clipBottomY - y;
  274.         }
  275.         
  276.         
  277.         for(int line = firstLine ; line < lastLine ; line += 3) {
  278.             int currentPos = line * width;
  279.             int rowsToDraw = Math.min(3, height - line);
  280.             int amount = width * rowsToDraw;
  281.             for(int position = 0 ; position < amount ; position++) {
  282.                 int i = imageDataByte[position + currentPos] & 0xff;                
  283.                 lineCache[position] = palette[i];
  284.             }
  285.             g.drawRGB(lineCache, 0, x, y + line, width, rowsToDraw, true);
  286.         }
  287.     }    
  288.     /**
  289.      * @inheritDoc
  290.      */
  291.     public int getWidth() {
  292.         return width;
  293.     }
  294.     
  295.     /**
  296.      * @inheritDoc
  297.      */
  298.     public int getHeight() {
  299.         return height;
  300.     }
  301.     
  302.     /**
  303.      * @inheritDoc
  304.      */
  305.     public void scale(int width, int height) {
  306.         IndexedImage p = (IndexedImage)scaled(width, height);
  307.         this.imageDataByte = p.imageDataByte;
  308.         this.width = width;
  309.         this.height = height;
  310.     }
  311.     
  312.     /**
  313.      * @inheritDoc
  314.      */
  315.     public Image scaled(int width, int height) {
  316.         int srcWidth = getWidth();
  317.         int srcHeight = getHeight();
  318.         // no need to scale
  319.         if(srcWidth == width && srcHeight == height){
  320.             return this;
  321.         }
  322.         Dimension d = new Dimension(width, height);
  323.         Image i = getCachedImage(d);
  324.         // currently we only support byte data...
  325.         i = new IndexedImage(width, height, palette, scaleArray(imageDataByte, width, height));
  326.         cacheImage(d, i);
  327.         return i;
  328.     }
  329.     
  330.     byte[] scaleArray(byte[] sourceArray, int width, int height) {
  331.         int srcWidth = getWidth();
  332.         int srcHeight = getHeight();
  333.         // no need to scale
  334.         if(srcWidth == width && srcHeight == height){
  335.             return sourceArray;
  336.         }
  337.         byte[] destinationArray = new byte[width * height];
  338.         
  339.         //Horizontal Resize
  340.         int yRatio = (srcHeight << 16) / height;
  341.         int xRatio = (srcWidth << 16) / width;
  342.         int xPos = xRatio / 2;
  343.         int yPos = yRatio / 2;
  344.         for (int x = 0; x < width; x++) {
  345.             int srcX = xPos >> 16;
  346.             for(int y = 0 ; y < height ; y++) {
  347.                 int srcY = yPos >> 16;
  348.                 int destPixel = x + y * width;
  349.                 int srcPixel = srcX + srcY * srcWidth;
  350.                 if((destPixel >= 0 && destPixel < destinationArray.length) && 
  351.                     (srcPixel >= 0 && srcPixel < sourceArray.length)) {
  352.                     destinationArray[destPixel] = sourceArray[srcPixel];
  353.                 }
  354.                 yPos += yRatio;
  355.             }
  356.             yPos = yRatio / 2;
  357.             xPos += xRatio;
  358.         }
  359.         return destinationArray;
  360.     }
  361.     
  362.     /**
  363.      * @inheritDoc
  364.      */
  365.     int[] getRGBImpl() {
  366.         int[] rgb = new int[width * height];
  367.         for(int iter = 0 ; iter < rgb.length ; iter++) {
  368.             int i = imageDataByte[iter] & 0xff;
  369.             rgb[iter] = palette[i];
  370.         }
  371.         return rgb;
  372.     }
  373.     /**
  374.      * Retrieves the palette for the indexed image drawing
  375.      *
  376.      * @return the palette data
  377.      */
  378.     public final int[] getPalette() {
  379.         return palette;
  380.     }
  381.     /**
  382.      * Retrieves the image data as offsets into the palette array
  383.      *
  384.      * @return the image data
  385.      */
  386.     public final byte[] getImageDataByte() {
  387.         return imageDataByte;
  388.     }
  389.     
  390.     /**
  391.      * This method allows us to store a package image into a persistent stream easily
  392.      * thus allowing us to store the image in RMS.
  393.      * 
  394.      * @return a byte array that can be loaded using the load method
  395.      */
  396.     public byte[] toByteArray() {
  397.         try {
  398.             ByteArrayOutputStream array = new ByteArrayOutputStream();
  399.             DataOutputStream out = new DataOutputStream(array);
  400.             out.writeShort(width);
  401.             out.writeShort(height);
  402.             out.writeByte(palette.length);
  403.             for (int iter = 0; iter < palette.length; iter++) {
  404.                 out.writeInt(palette[iter]);
  405.             }
  406.             out.write(imageDataByte);
  407.             out.close();
  408.             return array.toByteArray();
  409.         } catch (IOException ex) {
  410.             // will never happen since IO is purely in memory
  411.             ex.printStackTrace();
  412.             return null;
  413.         }
  414.     }
  415.     
  416.     /**
  417.      * Loads a packaged image that was stored in a stream using the toByteArray method
  418.      * 
  419.      * @param data previously stored image data
  420.      * @return newly created packed image
  421.      */
  422.     public static IndexedImage load(byte[] data) {
  423.         try {
  424.             DataInputStream input = new DataInputStream(new ByteArrayInputStream(data));
  425.             int width = input.readShort();
  426.             int height = input.readShort();
  427.             int[] palette = new int[input.readByte() & 0xff];
  428.             for (int iter = 0; iter < palette.length; iter++) {
  429.                 palette[iter] = input.readInt();
  430.             }
  431.             byte[] arr = new byte[width * height];
  432.             input.readFully(arr);
  433.             return new IndexedImage(width, height, palette, arr);
  434.         } catch (IOException ex) {
  435.             ex.printStackTrace();
  436.             return null;
  437.         }
  438.     }
  439. }