/* * @(#)ColorCycle.java 1.0.1 2010-01-08 * * Copyright (c) 2009-2010 Werner Randelshofer, Goldau, Switzerland. * All rights reserved. * * You may not use, copy or modify this file, except in compliance with the * license agreement you entered into with Werner Randelshofer. * For details see accompanying license terms. */ package org.monte.media.ilbm; import java.util.Arrays; /** * Implements DRNG color cycling for an IFF ILBM image. * <p> * <pre> * ILBM DRNG DPaint IV enhanced color cycle chunk * -------------------------------------------- * * set { * active=1,DPReserved=4 * } drngFlags; * * /* True color cell * / * typedef struct { * UBYTE cell; * UBYTE r; * UBYTE g; * UBYTE b; * } ilbmDRNGDColor; * * /* Color register cell * / * typedef struct { * UBYTE cell; * UBYTE index; * } ilbmDRNGDIndex; * * /* DRNG chunk. * / * typedef struct { * UBYTE min; /* min cell value * / * UBYTE max; /* max cell value * / * UWORD rate; /* color cycling rate, 16384 = 60 steps/second * / * UWORD set drngFlags flags; /* 1=RNG_ACTIVE, 4=RNG_DP_RESERVED * / * UBYTE ntrue; /* number of DColorCell structs to follow * / * UBYTE ntregs; /* number of DIndexCell structs to follow * / * ilbmDRNGDColor[ntrue] trueColorCells; * ilbmDRNGDIndex[ntregs] colorRegisterCells; * } ilbmDRangeChunk; * </pre> * * @author Werner Randelshofer * @version 1.0.1 2010-11-08 Fixed color cycling rate. * <br>1.0 2009-12-23 Created. */ public class DRNGColorCycle extends ColorCycle { public abstract static class Cell implements Comparable<Cell> { protected int cell; protected int value; public Cell(int cell) { this.cell = cell; } /** Reads the initial value of the cell which is either taken * from the rgb palette or from an rgb value stored by the cell. * * @param rgbs the palette. * @param isHalfbright whether the halfbright value shall be taken. * */ public abstract void readValue(int[] rgbs, boolean isHalfbright); /** Writes the final value of the cell into the color palette - or * does nothing */ public abstract void writeValue(int[] rgbs, boolean isHalfbright); @Override public int compareTo(Cell that) { return this.cell - that.cell; } @Override public boolean equals(Object o) { if (o instanceof Cell) { Cell that = (Cell) o; return that.cell == this.cell; } return false; } @Override public int hashCode() { return cell; } } /** True color cell. */ public static class DColorCell extends Cell { private int rgb; public DColorCell(int cell, int rgb) { super(cell); this.rgb = rgb; } /** Sets the initial value of the cell from its rgb instance variable. */ @Override public void readValue(int[] rgbs, boolean isHalfbright) { value = isHalfbright ? rgb & 0x0f0f0f : rgb; } /** Does nothing. */ @Override public void writeValue(int[] rgbs, boolean isHalfbright) { // nothing to do } } /** Color register cell. */ public static class DIndexCell extends Cell { private int index; public DIndexCell(int cell, int index) { super(cell); this.index = index; } /** Sets the initial value of the cell from the rgb palette. */ @Override public void readValue(int[] rgbs, boolean isHalfbright) { value = isHalfbright ? rgbs[index + 32] : rgbs[index]; } /** Writes the final value of the cell into the color palette. */ @Override public void writeValue(int[] rgbs, boolean isHalfbright) { rgbs[isHalfbright ? index + 32 : index] = value; } } /** Lowest color register of the range. */ private int min; /** Highest color register of the range. */ private int max; /** Whether the image is in EHB mode. */ private boolean isEHB; /** List with interpolated cells. */ private Cell[] ic; /** Actual cells with values. */ private Cell[] cells; private boolean isReverse; /** * * @param rate * @param timeScale * @param min * @param max * @param isActive * @param isEHB * @param cells */ public DRNGColorCycle(int rate, int timeScale, int min, int max, boolean isActive, boolean isEHB, Cell[] cells) { super(rate, timeScale, isActive); this.min = min; this.max = max; this.isEHB = isEHB; this.cells = cells; } public int getMin() { return min; } public int getMax() { return max; } private void interpolateCells(int[] rgbs) { //System.out.println("DRNGColorCycle " + min + ".." + max + " number of cells:" + cells.length); ic = new Cell[max - min + 1]; Arrays.sort(cells); for (int i = 0; i < cells.length; i++) { ic[cells[i].cell - min] = cells[i]; cells[i].readValue(rgbs, false); } int left = cells.length - 1; int right = 0; for (int i = 0; i < ic.length; i++) { if (cells[right].cell == i) { left = right; right = (right == cells.length - 1) ? 0 : right + 1; } else { //System.out.println(" interpolating cell "+i+"("+(i+min)+")"+" with values from "+cells[left].cell+" and "+cells[right].cell); int levels=cells[left].cell<cells[right].cell?cells[right].cell-cells[left].cell:max-cells[left].cell+cells[right].cell+1; int blend=cells[right].cell>(i+min)?cells[right].cell-(i+min):max-(i+min)+cells[right].cell+1; int lrgb=cells[left].value; int rrgb=cells[right].value; ic[i]=new DColorCell(i,// ( ((lrgb&0xff0000)*blend+(rrgb&0xff0000)*(levels-blend))/levels&0xff0000)|// ( ((lrgb&0xff00)*blend+(rrgb&0xff00)*(levels-blend))/levels&0xff00)|// ( ((lrgb&0xff)*blend+(rrgb&0xff)*(levels-blend))/levels)// ); //System.out.println(" levels:"+levels+" blend:"+blend+" lrgb:"+Integer.toHexString(lrgb)+" rrgb:"+Integer.toHexString(rrgb)+" blend:"+Integer.toHexString(((DColorCell)ic[i]).rgb)); } } } @Override public void doCycle(int[] rgbs, long time) { if (isActive) { if (ic == null) { interpolateCells(rgbs); } int shift = (int) ((time * rate / timeScale / 1000) % (ic.length)); if (isReverse) { for (int i = 0; i < ic.length; i++) { ic[i].readValue(rgbs, false); } for (int j = 0; j < shift; j++) { int tmp = ic[0].value; for (int i = 1; i < ic.length; i++) { ic[i - 1].value = ic[i].value; } ic[ic.length - 1].value = tmp; } for (int i = 0; i < ic.length; i++) { ic[i].writeValue(rgbs, false); } if (isEHB) { for (int i = 0; i < ic.length; i++) { ic[i].readValue(rgbs, true); } for (int j = 0; j < shift; j++) { int tmp = ic[0].value; for (int i = 1; i < ic.length; i++) { ic[i - 1].value = ic[i].value; } ic[ic.length - 1].value = tmp; } for (int i = 0; i < ic.length; i++) { ic[i].writeValue(rgbs, true); } } } else { for (int i = 0; i < ic.length; i++) { ic[i].readValue(rgbs, false); } for (int j = 0; j < shift; j++) { int tmp = ic[ic.length - 1].value; for (int i = ic.length - 1; i > 0; i--) { ic[i].value = ic[i - 1].value; } ic[0].value = tmp; } for (int i = 0; i < ic.length; i++) { ic[i].writeValue(rgbs, false); } if (isEHB) { for (int i = 0; i < ic.length; i++) { ic[i].readValue(rgbs, true); } for (int j = 0; j < shift; j++) { int tmp = ic[ic.length - 1].value; for (int i = ic.length - 1; i > 0; i--) { ic[i].value = ic[i - 1].value; } ic[0].value = tmp; } for (int i = 0; i < ic.length; i++) { ic[i].writeValue(rgbs, true); } } } } } }