Android Open Source - abstract-art Layer






From Project

Back to project page abstract-art.

License

The source code is released under:

GNU General Public License

If you think the Android project abstract-art listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package net.georgewhiteside.android.abstractart;
//  w w w .ja v a  2s  .  c  o m
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.preference.PreferenceManager;
import android.util.Log;

// TODO: scrolling bug on background 227?

// layers with second palette cycle: 60 61 (probably others, haven't checked them all)

/*
 * Note to self: don't pull the image-related stuff from Layer out into its own class without thinking it through very
 * carefully... the images themselves rely on data that belongs to Layer. Most notably, the bit depth information which
 * is necessary to even *decompress* an image (not just display it properly), is part of the Layer data.
 */

public class Layer
{
  private final String TAG = "Layer";
  private ByteBuffer romData;
  private static final int OFFSET = 0xA0200;
  
  private Context context;
  private SharedPreferences sharedPreferences;
  
  //private static final int ATTRIBUTES = 0xADEA1 - OFFSET;
  
  //private byte[] bgData = new byte[17];
  //private short[][] scrollingData = new short[4][5];
  //private byte[][] distortionData = new byte[4][17];
  
  private ByteBuffer bgData;
  
  // max sizes were computed in advance; no need to waste time making
  // multiple decompression passes to determine sizes, and no need
  // to constantly reallocate graphic buffers
  
  private static final int TILE_MAX = 0x3740;
  private static final int ARRANGE_MAX = 0x800; // every bg arrangement is this size actually
  private static final int PALETTE_MAX = 0x10; // palettes are either 2bpp or 4bpp
  
  private byte[] tileData = new byte[TILE_MAX];
  private byte[] arrangeData = new byte[ARRANGE_MAX];
  private byte[][][] paletteData = new byte[8][PALETTE_MAX][3];
  
  private int tileDataLength;
  private int arrangeDataLength;  // in case I want to dynamically allocate space
  
  private int loadedIndex = -1;
  
  private List<byte[][]> tiles;
  private byte[] image;
  private byte[] palette;
  
  private int paletteId; // hack for disabled color effects
  
  private int paletteRotation, triangle;
  private int mTick;
   
   public static final int DIST_NONE = 0x00;
   public static final int DIST_HORIZONTAL = 0x01;
   public static final int DIST_INTERLACED = 0x02;
   public static final int DIST_VERTICAL = 0x03;
   public static final int DIST_UNKNOWN = 0x04;
   
   public static final int CYCLE_NONE = 0x00;
  public static final int CYCLE_ROTATE1 = 0x01;
  public static final int CYCLE_ROTATE2 = 0x02;
  public static final int CYCLE_TRIANGLE = 0x03;
   
   public int getImageIndex() { return bgData.get(0); }
   public int getPaletteIndex() { return bgData.get(1); }
   public int getBPP() { return bgData.get(2); }

   // TODO: wrap these Distortion and Translation objects?
  public Distortion distortion;
  public Translation translation;
  
  public Layer(ByteBuffer data, Context context)
  {
    this.context = context;
    sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
    image = new byte[256 * 256 * 1];
    palette = new byte[16 * 1 * 4];
    romData = data;
    romData.order(ByteOrder.LITTLE_ENDIAN);
  }
  
  public void doTick()
  {
    distortion.doTick();
    translation.doTick();
    
    // handle palette cycling animation
    if(getPaletteCycleType() != Layer.CYCLE_NONE)
    {
      mTick++;
      if(mTick == getPaletteCycleSpeed())
      {
        mTick = 0;
        
        switch(getPaletteCycleType())
        {
          case Layer.CYCLE_ROTATE1:
          case Layer.CYCLE_ROTATE2:
            // TODO: for cycle type 0x02, if the lengths of both cycle ranges is not equal, this will give incorrect output...
            // I may need to figure something else out, but for now it's tricky because there's no integer modulus in the GLSL and I can't get it
            // to work consistently with floats. Sending the modified palette indices over isn't an attractive option either because that could take
            // up to 32 varyings, or one 16x2 texture upload every frame... maybe look into the palette texture re-ups, it might be feasible
            // OTHERWISE, I may want to consider adding an extra varying for each layer to track cycle rotation independently for type 0x02
            if(paletteRotation >= (getPaletteCycle1End() - getPaletteCycle1Begin() + 1))
            {
              paletteRotation = 1;
            }
            else
            {
              paletteRotation++;
            }
            
            break;
            
          case Layer.CYCLE_TRIANGLE:
            if(paletteRotation >= (getPaletteCycle1End() - getPaletteCycle1Begin() + 1) * 2)
            {
              paletteRotation = 1;
            }
            else
            {
              paletteRotation++;
            }
            break;
        }
      }
    }
  }
  
  public int getPaletteCycle1Begin()
  {
    return bgData.get(4);
  }
  
  public int getPaletteCycle1End()
  {
    return bgData.get(5);
  }
  
  public int getPaletteCycle2Begin()
  {
    return bgData.get(6);
  }
  
  public int getPaletteCycle2End()
  {
    return bgData.get(7);
  }
  
  public int getPaletteCycleSpeed()
  {
    return RomUtil.unsigned(bgData.get(8));
  }
  
  /**
   * Gets the type of palette cycling animation for this layer:
   * 
   * <ul><li><code>0x00</code> - no cycling</li>
   * <li><code>0x01</code> - rotate right</li>
   * <li><code>0x02</code> - double rotate right</li>
   * <li><code>0x03</code> - triangle rotation</ul>
   * 
   * @return the type of palette cycling animation for this layer
   */
  public int getPaletteCycleType()
  {
    return bgData.get(3);
  }
  
  public int getPaletteRotation()
  {
    return paletteRotation;
  }
  
  public void loadLayer(int index)
  {
    if( index < 0 || index > 326 ) {
      return;
    }

    //Log.d(TAG, "layer index: " + index);

    // load background attribute data
    
    romData.position(0xADEA1 - OFFSET + index * 17);
    bgData = romData.slice().order(ByteOrder.LITTLE_ENDIAN);
    
    //Log.d(TAG, String.format("layer %d (image %d) bytes 3-8: %02X %02X %02X %02X %02X %02X", index, getImageIndex(), bgData.get(3), bgData.get(4), bgData.get(5), bgData.get(6), bgData.get(7), bgData.get(8)));
    
    romData.position(0xAF458 - OFFSET);
    bgData.position(9);
    if(translation == null)
      translation = new Translation(romData.slice(), bgData.slice());
    else
      translation.load(romData.slice(), bgData.slice());
    
    romData.position(0xAF908 - OFFSET);
    bgData.position(13);
    if(distortion == null)
      distortion = new Distortion(romData.slice(), bgData.slice());
    else
      distortion.load(romData.slice(), bgData.slice());
    
    //Log.d(TAG, String.format("bbg: %d: image %d: %02X %02X %02X %02X", index, getImageIndex(), distortionData[0].get(2), distortionData[1].get(2), distortionData[2].get(2), distortionData[3].get(2)));
    
    
    
    // load color palette
    
    romData.position(0xADCD9 - OFFSET + getPaletteIndex() * 4);
    int pPaletteData = RomUtil.toHex(romData.getInt()) - OFFSET;
    paletteId = pPaletteData;  // hack for disabled color effects
    
    
    // TODO: read palettes correctly?
        
    // values are packed RGB555:
    // 0BBBBBGG GGGRRRRR
    
    romData.position(pPaletteData);
    
    // c = color, p = palette
    for(int p = 0; p < 8; p++) {
      for(int c = 0; c < (1 << getBPP()); c++)
      {
        short color = romData.getShort();
        int r = 0, g = 0, b = 0;
        b = (color >> 10) & 0x1F;
        g = (color >> 5) & 0x1F;
        r = color & 0x1F;
        
        // scale to rgb888 values
        paletteData[p][c][0] = (byte)(r << 3 | r >> 2);
        paletteData[p][c][1] = (byte)(g << 3 | g >> 2);
        paletteData[p][c][2] = (byte)(b << 3 | b >> 2);
      }
    }
    
    loadSubPalette(0);
    
    loadImage(getImageIndex());
    
    loadedIndex = index;
    paletteRotation = 0;
    mTick = 0;
    
    romData.rewind();
  }
  
  
  
  private void loadImage(int index)
  {
    // TODO: the image data ultimately needs to be a Buffer; change the byte[] image to a ByteBuffer
    
    boolean enablePaletteEffects = sharedPreferences.getBoolean("enablePaletteEffects", false); // SharedPreference
    String cacheFileName;
    File cacheDir;
    int bufferSize;
    
    if(enablePaletteEffects == true) {
      cacheFileName = String.valueOf(index);
      cacheDir = new File(context.getCacheDir(), "layers-indexed");
      bufferSize = 256 * 256 * 1;
      
    } else {
      cacheFileName = String.format("%03d-%d", index, paletteId); // hack for disabled color effects
      cacheDir = new File(context.getCacheDir(), "layers-rgba");
      bufferSize = 256 * 256 * 4;
    }
    
    if(image.length != bufferSize) {
      image = new byte[bufferSize];
    }
    
    File cacheFile = new File(cacheDir, cacheFileName);
    
    if(cacheFile.exists())
    {
      //Log.i(TAG, String.format("Reading previously cached image from %s", cacheFile.getPath()));
      
      if(enablePaletteEffects) {
        Cache.readCompressed(cacheFile, image, 256 * 256);
        //Cache.read(cacheFile, image, 256 * 256);
      } else {
        // TODO: integrate the nitty-gritty into the Cache class
        ByteBuffer buffer = ByteBuffer.allocate(256 * 256 * 4);
        
        // can cause a crash on rare occasions ... mainly when adding new features ;)
        // SO, just trapping any potential problems here so I don't get slowed down
        try {
          BitmapFactory.decodeFile(cacheFile.getPath()).copyPixelsToBuffer(buffer);
        }
        catch(Exception e) {
          Log.e("AbstractArt", "Couldn't open " + cacheFile.getPath() + " ... resuming with blank texture");
        }
        
        image = buffer.array();
      }
    }
    else
    {
      //Log.i(TAG, "Building image from compressed data");
      
      // decompress tiles, decompress tile arrangements, and assemble the former according to the latter
      // the 65,536-byte result is assigned to the byte[] image member of this instance
      
      prepareImageData(index);
      buildTiles();
      buildImage(enablePaletteEffects);
      
      //Log.i(TAG, String.format("Caching image to %s", cacheFile.getPath()));
      
      if(enablePaletteEffects) {
        Cache.writeCompressed(cacheFile, image);
        //Cache.write(cacheFile, image);
      } else {
        cacheFile.getParentFile().mkdirs(); // safely does nothing if path exists
         
         try {
           Bitmap img = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_8888); //Bitmap.createBitmap(buffer.asIntBuffer().array(), 256, 256, Bitmap.Config.ARGB_8888); // createBitmap(int[] colors, int width, int height, Bitmap.Config config)
           img.copyPixelsFromBuffer(ByteBuffer.wrap(image));
           FileOutputStream fileOutputStream = new FileOutputStream(cacheFile);
           img.compress(CompressFormat.PNG, 80, fileOutputStream); // quality is irrelevant for PNGs
           fileOutputStream.close();
         } catch (FileNotFoundException e) {
           e.printStackTrace();
         } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    }
  }
  
  private void prepareImageData(int index)
  {
    // load tile graphics
    
    romData.position(0xAD9A1 - OFFSET + index * 4);
    int pTileData = RomUtil.toHex(romData.getInt()) - OFFSET;
    tileDataLength = RomUtil.decompress(pTileData, tileData, TILE_MAX, romData);
    
    // load tile arrangement data
    
    romData.position(0xADB3D - OFFSET + index * 4);
    int pArrangeData = RomUtil.toHex(romData.getInt()) - OFFSET;
    arrangeDataLength = RomUtil.decompress(pArrangeData, arrangeData, ARRANGE_MAX, romData);
  }
  
  public boolean checkIfCached(int imageNumber)
  {
    return false;
  }
  
  private void buildImage(boolean indexedColor)
  {
    int b1, b2;
    int block, tile, subpal = 0;
    int n;
    boolean vflip, hflip;
    
    // for every tile location
    for (int y = 0; y < 32; y++)
    {
      for (int x = 0; x < 32; x++)
      {
        // prepare the attributes
        n = y * 32 + x;

        b1 = arrangeData[n * 2];
        b2 = arrangeData[n * 2 + 1] << 8;
        block = b1 + b2;

        tile = block & 0x3FF;
        vflip = (block & 0x8000) != 0;
        hflip = (block & 0x4000) != 0;
        subpal = (block >> 10) & 7;
        
        // TODO what am I doing wrong/different that I had to make this hack???
        // BEGIN HACK
        if(tile >= 0x80 && tile <= 0xFF) {
          tile += 0x100;
        }
        if(subpal == 7) {
          tile = block & 0xFF;
          subpal = 0;
        }
        // END HACK
        
        // and draw its pixels
        for (int i = 0; i < 8; i++)
        {
          for (int j = 0; j < 8; j++)
          {
            int px = 0, py = 0;
            
            if (hflip)
              px = (x * 8) + 7 - i;
            else
              px = (x * 8) + i;

            if (vflip)
              py = (y * 8) + 7 - j;
            else
              py = (y * 8) + j;

            int stride = 256;
            byte color = tiles.get(tile)[i][j];
            
            if(indexedColor) {
              int pos = px + py * stride;
              image[pos] = color;
            } else {
              int pos = px * 4 + py * 4 * stride;
              
              image[pos + 0] = paletteData[subpal][color][0];
              image[pos + 1] = paletteData[subpal][color][1];
              image[pos + 2] = paletteData[subpal][color][2];
              image[pos + 3] = color == 0 ? (byte)0x00 : (byte)0xFF;
              
            }
            
          }
        }
      }
    }
    //loadSubPalette(subpal);
  }
  
  private void loadSubPalette(int sub)
  {
    int subpal = sub; // just a temp thing
    for(int i = 0; i < (1 << getBPP()); i++)
    {
      palette[i * 4 + 0] = paletteData[subpal][i][0];
      palette[i * 4 + 1] = paletteData[subpal][i][1];
      palette[i * 4 + 2] = paletteData[subpal][i][2];
      palette[i * 4 + 3] = (byte)0xFF;
    }
    palette[3] = (byte)0x00; // opacity 0 for color 0
  }
  
  protected void buildTiles()
  {
    int n = tileDataLength / (8 * getBPP());

    tiles = new ArrayList<byte[][]>();
    
    for (int i = 0; i < n; i++)
    {
      tiles.add(new byte[8][]);

      int o = i * 8 * getBPP();

      for (int y = 0; y < 8; y++)
      {
        tiles.get(i)[y] = new byte[8];
        for (int x = 0; x < 8; x++)
        {
          int c = 0;
          for (int bp = 0; bp < getBPP(); bp++)
          {
            c += (( (tileData[o + x * 2 + ((bp / 2) * 16 + (bp & 1))]) & (1 << 7 - y)) >> 7 - y) << bp;
          }
          tiles.get(i)[y][x] = (byte)c;
        }
      }
    }
  }
  
  public byte[] getImage()
  {
    if(loadedIndex == -1)
    {
      loadLayer(0);
    }
    return image;
  }
  
  public int getIndex()
  {
    return loadedIndex;
  }
  
  public byte[] getPalette()
  {
    if(loadedIndex == -1)
    {
      loadLayer(0);
    }
    return palette;
  }
}




Java Source Code List

net.georgewhiteside.android.abstractart.AbstractArt.java
net.georgewhiteside.android.abstractart.BattleBackground.java
net.georgewhiteside.android.abstractart.BattleGroup.java
net.georgewhiteside.android.abstractart.Cache.java
net.georgewhiteside.android.abstractart.Distortion.java
net.georgewhiteside.android.abstractart.Enemy.java
net.georgewhiteside.android.abstractart.FPSCounter.java
net.georgewhiteside.android.abstractart.GLOffscreenSurface.java
net.georgewhiteside.android.abstractart.ImageLoader.java
net.georgewhiteside.android.abstractart.Layer.java
net.georgewhiteside.android.abstractart.Renderer.java
net.georgewhiteside.android.abstractart.RomUtil.java
net.georgewhiteside.android.abstractart.ServiceDialog.java
net.georgewhiteside.android.abstractart.Settings.java
net.georgewhiteside.android.abstractart.ShaderFactory.java
net.georgewhiteside.android.abstractart.Translation.java
net.georgewhiteside.android.abstractart.UniformGridView.java
net.georgewhiteside.android.abstractart.Wallpaper.java
net.georgewhiteside.android.abstractart.settings.BackgroundSelector.java
net.georgewhiteside.android.abstractart.settings.ClearCachePreference.java
net.georgewhiteside.android.abstractart.settings.CreateImageCachePreference.java
net.georgewhiteside.android.abstractart.settings.FrameRatePreference.java
net.georgewhiteside.android.abstractart.settings.ThumbnailAdapter.java
net.georgewhiteside.utility.Dimension.java
net.starmen.pkhack.HackModule.java
org.jf.GLWallpaper.GLWallpaperService.java
sheetrock.panda.changelog.ChangeLog.java