Java-Gaming.org    
Featured games (78)
games approved by the League of Dukes
Games in Showcase (429)
Games in Android Showcase (89)
games submitted by our members
Games in WIP (468)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  TinyGame Resources  (Read 7284 times)
0 Members and 1 Guest are viewing this topic.
Offline Markus_Persson

JGO Wizard


Medals: 12
Projects: 19


Mojang Specifications


« Posted 2010-04-15 17:19:22 »

EgaImage.java:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
public class EgaImage {
   // Read only values
  public final int width, height;
   public final byte[] pixels; // (You can still change the contents of this! And you should!)

   public EgaImage(int width, int height) {
      this.width = width;
      this.height = height;
      pixels = new byte[width * height];
   }

   // Blits another EgaImage on top of this one, treating -1 as transparent.
  public void draw(EgaImage image, int xPos, int yPos) {
      // Calculate region
     int x1 = xPos;
      int x2 = xPos + image.width;
      int y1 = yPos;
      int y2 = yPos + image.height;

      // Clip to edges
     if (x1 < 0)
         x1 = 0;
      if (y1 < 0)
         y1 = 0;
      if (x2 > width)
         x2 = width;
      if (y2 > height)
         y2 = height;

      for (int y = y1; y < y2; y++) {
         // Calculate scanline offsets in pixel arrays
        int inOffset = -xPos + (y - yPos) * image.width;
         int outOffset = y * width;

         for (int x = x1; x < x2; x++) {
            byte in = image.pixels[inOffset + x];
            if (in >= 0) {
               pixels[outOffset + x] = in;
            }
         }
      }
   }
}


EgaScreen.java:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;

public class EgaScreen extends EgaImage {
   // Static immutable EGA palette lookup table
  // See http://upload.wikimedia.org/wikipedia/en/d/df/EGA_Table.PNG
  private static final int[] EGA_PALETTE = new int[64];
   static {
      for (int i = 0; i < 64; i++) {
         int b = ((i >> 0) & 1) * 0xaa + ((i >> 3) & 1) * 0x55;
         int g = ((i >> 1) & 1) * 0xaa + ((i >> 4) & 1) * 0x55;
         int r = ((i >> 2) & 1) * 0xaa + ((i >> 5) & 1) * 0x55;
         EGA_PALETTE[i] = (r << 16) | (g << 8) | b;
      }
   }

   // Public mutable frame palette, fixed at 16 bytes
  // The default values are from http://upload.wikimedia.org/wikipedia/en/d/df/EGA_Table.PNG
  public final byte[] palette = { 0, 1, 2, 3, 4, 5, 20, 7, 56, 57, 58, 59, 60, 61, 62, 63 };

   private BufferedImage image;
   private int[] outPixels;

   public EgaScreen(int width, int height) {
      super(width, height);
      image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
      outPixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
   }

   // Display this screen onto an AWT Graphics object, scaled to fit the provided width and height
  public void display(Graphics g, int width, int height) {
      for (int i = 0; i < 40 * 30; i++) {
         outPixels[i] = EGA_PALETTE[palette[pixels[i]]];
      }
      g.drawImage(image, 0, 0, width, height, null);
   }
}

Play Minecraft!
Offline Markus_Persson

JGO Wizard


Medals: 12
Projects: 19


Mojang Specifications


« Reply #1 - Posted 2010-04-15 17:19:47 »

And a test applet:

EgaTest.java:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
import java.applet.Applet;
import java.awt.Graphics;
import java.util.Random;

public class EgaTest extends Applet implements Runnable {
   private EgaScreen screen = new EgaScreen(40, 30);

   private boolean running;

   public void start() {
      running = true;
      new Thread(this).start();
   }

   public void stop() {
      running = false;
   }

   public void run() {
      Random random = new Random();

      EgaImage background = new EgaImage(40, 30);
      EgaImage image = new EgaImage(32, 32);
      for (int x = 0; x < 32; x++) {
         for (int y = 0; y < 32; y++) {
            double xd = (x / 31.0) * 2 - 1;
            double yd = (y / 31.0) * 2 - 1;
            double dist = Math.sqrt(xd * xd + yd * yd);
            if (dist < 1) {
               image.pixels[x + y * 32] = (byte) (random.nextDouble() + dist);
            } else {
               image.pixels[x + y * 32] = -1;
            }
         }
      }

      while (running) {
         for (int i = 0; i < 100; i++)
            background.pixels[random.nextInt(40 * 30)] = (byte) random.nextInt(16);

         screen.draw(background, 0, 0);

         double time = System.currentTimeMillis() / 100.0;
         int xBall = (int) (Math.sin(time) * 20);
         int yBall = (int) (Math.cos(time * 1.2) * 20);
         screen.draw(image, xBall - 16 + 20, yBall - 16 + 15);

         Graphics g = getGraphics();
         screen.display(g, getWidth(), getHeight());
         g.dispose();

         try {
            Thread.sleep(5);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Play Minecraft!
Offline ShannonSmith
« Reply #2 - Posted 2010-04-15 17:35:24 »

You should probably change the title to TinyGame Resources, may as well keep it all in one thread. I have started my framework as well and it is very close to yours except I called the classes TinyImage, TinyScreen and TinyGame. I went for an int array of pixels and an explicit transparent index so I can base 64 encode images into strings using 4bits per pixel. It drops the colours to 15 per image with transparency but I don't think that is an issue.

Here is the code I use for updating the screen BufferedImage using an explicit integer upScale factor.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
   private void updateScreenBuffer(){
      int[] pixels = ((DataBufferInt)screenBuffer.getRaster().getDataBuffer()).getData();
      for(int x = 0; x < WIDTH; x++){
         for(int y = 0; y < HEIGHT; y++){
            int rgb = EGA_COLORS[palette[buffer.pixels[y*WIDTH + x]]];
            for(int i = 0; i < upScale*upScale; i++){
               pixels[(y*upScale + i%upScale)*upScale*WIDTH + (x*upScale + i/upScale)] = rgb;
            }
         }
      }
   }
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Markus_Persson

JGO Wizard


Medals: 12
Projects: 19


Mojang Specifications


« Reply #3 - Posted 2010-04-15 17:39:20 »

Thread renamed, good idea!

Play Minecraft!
Offline jimsmi6

Senior Newbie





« Reply #4 - Posted 2010-04-16 02:39:22 »

I finally got rid of my hex value array and wrote my own formula for it. I screwed up the order in the process but it doesn't really matter.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public static final Color[] COLORS = new Color[64];

static {
   for(int i = 0, j = 0; i < COLORS.length; i++){
      if((i & 15) == 0){
         j = (i >> 4) << 8;
      }
     
      COLORS[i] = new Color(21760*(j+((i >> 2) & 3))+(i & 3)*85);
   }
}


Spent a long time getting it optimized/shrunk but it works.

Here's an image to use as a reference for the array indexes: http://i41.tinypic.com/fk03h0.png
Offline Bob Dorian

Junior Newbie





« Reply #5 - Posted 2010-04-16 09:45:34 »

This is a half-finished framework I stiched together when I started on my entry.

EGAColor

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
[pre]package egalib.ega;

import java.awt.Color;

public final class EGAColor
{
    private static final int[] HEX_VALUES = new int[]{
      0x000000, 0x0000aa, 0x00aa00, 0x00aaaa, 0xaa0000, 0xaa00aa, 0xaaaa00, 0xaaaaaa,
      0x000055, 0x0000ff, 0x00aa55, 0x00aaff, 0xaa0055, 0xaa00ff, 0xaaaa55, 0xaaaaff,
      0x005500, 0x0055aa, 0x00ff00, 0x00ffaa, 0xaa5500, 0xaa55aa, 0xaaff00, 0xaaffaa,
      0x005555, 0x0055ff, 0x00ff55, 0x00ffff, 0xaa5555, 0xaa55ff, 0xaaff55, 0xaaffff,
      0x550000, 0x5500aa, 0x55aa00, 0x55aaaa, 0xff0000, 0xff00aa, 0xffaa00, 0xffaaaa,
      0x550055, 0x5500ff, 0x55aa55, 0x55aaff, 0xff0055, 0xff00ff, 0xffaa55, 0xffaaff,
      0x555500, 0x5555aa, 0x55ff00, 0x55ffaa, 0xff5500, 0xff55aa, 0xffff00, 0xffffaa,
      0x555555, 0x5555ff, 0x55ff55, 0x55ffff, 0xff5555, 0xff55ff, 0xffff55, 0xffffff
   };
   
   public static final EGAColor[] EGA_COLORS = new EGAColor[64];
   
   static
   {
       for (int x=0; x<64; x++)
       {
           EGA_COLORS[x] = new EGAColor(x);
       }
   }
   
   public final int index;
   
   public final Color awtColor;
   
   public EGAColor(int index)
   {
       this.index=index;
       this.awtColor = new Color(HEX_VALUES[index]);
   }
}
         
          [/pre]



EGAGraphics

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
[pre]package egalib.ega;

import java.awt.*;

public final class EGAGraphics
{
    private EGAColor[] palette = new EGAColor[16];
   
    private Graphics awt;
   
    private int cIndex;
   
    private static final Font
        F_BIG = new Font("SansSerif",Font.PLAIN,10),
        F_MED = new Font("SansSerif",Font.PLAIN,8),
        F_SMALL = new Font("SansSerif",Font.PLAIN,6);
       
    public static final byte BIG = 1, MED = 2, SMALL = 3;
   
    public EGAGraphics(Graphics g)
    {
        awt=g;
        for (int x=0; x<16; x++)
        {
            palette[x] = EGAColor.EGA_COLORS[x];
        }
        cIndex=0;
        awt.setColor(palette[cIndex].awtColor);
        awt.setFont(F_SMALL);
    }
   
    public void updatePalette(EGAColor color, int index)
    {
        palette[index]=color;
        if (cIndex==index)
            awt.setColor(palette[index].awtColor);
    }
   
    public void setPalette(EGAColor[] palette)
    {
        if (palette.length!=16)
            throw new RuntimeException("Palette must be an array of length 16!");
        for (int x=0; x<16; x++)
        {
            if (palette[x]==null)
                throw new NullPointerException("EGAColor in palette was null!");
            this.palette[x]=palette[x];
        }
    }
   
    public void setColor(int index)
    {
        cIndex=index;
        awt.setColor(palette[index].awtColor);
    }
   
    public void drawRect(int x, int y, int w, int h)
    {
        awt.drawRect(x,y,w,h);
    }
   
    public void fillRect(int x, int y, int w, int h)
    {
        awt.fillRect(x,y,w,h);
    }
   
    public void drawString(String string, int x, int y)
    {
        awt.drawString(string,x,y);
    }
   
    public void drawShape(Shape shape)
    {
        ((Graphics2D)awt).draw(shape);
    }
   
    public void fillShape(Shape shape)
    {
        ((Graphics2D)awt).fill(shape);
    }
   
    public void fontSize(byte _enum)
    {
        switch (_enum)
        {
            case BIG:
                awt.setFont(F_BIG);
                break;
            case SMALL:
                awt.setFont(F_SMALL);
                break;
            case MED:
                awt.setFont(F_MED);
                break;
        }
    }
}
   
    [/pre]


EGADisplay

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
[pre]package egalib.ega;

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;

public final class EGADisplay
{
    private Applet applet;
   
    private Canvas canvas;
   
    private BufferedImage buffer;
   
    private EGAGraphics graphics;
   
    public EGADisplay(Applet applet)
    {
        this.applet=applet;
       
        applet.setIgnoreRepaint(true);
        applet.setLayout(new BorderLayout());
       
        GraphicsConfiguration config =
            GraphicsEnvironment.getLocalGraphicsEnvironment().
            getDefaultScreenDevice().getDefaultConfiguration();
        canvas = new Canvas(config);
        canvas.setIgnoreRepaint(true);
        applet.add(canvas,BorderLayout.CENTER);
        buffer = config.createCompatibleImage(40,30,Transparency.OPAQUE);
        canvas.requestFocus();
        graphics = new EGAGraphics(buffer.getGraphics());
    }
   
    public EGAGraphics getGraphics()
    {
        return graphics;
    }
   
    public void show()
    {
        Graphics2D g = (Graphics2D)canvas.getGraphics();
        g.setTransform(AffineTransform.getScaleInstance(canvas.getWidth()/buffer.getWidth(),canvas.getHeight()/buffer.getHeight()));
        g.drawImage(buffer,0,0,null);
    }
   
    public void attachInput(EGAInput input)
    {
        canvas.addKeyListener(input);
        canvas.addMouseListener(input);
    }
}[/pre]


EGAInput

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
[pre]package egalib.ega;

import java.awt.event.*;

public final class EGAInput implements KeyListener, MouseListener
{
    private boolean[] keys = new boolean[256];
    private boolean[] mouse = new boolean[4];
    private int mx,my;
   
    public int mouseX()
    {
        return mx;
    }
   
    public int mouseY()
    {
        return my;
    }
   
    public boolean mouseFocus()
    {
        return mouse[3];
    }
   
    public boolean mouseDown(int button)
    {
        return mouse[button-1];
    }
   
    public boolean keyDown(int key)
    {
        return keys[key];
    }
   
    public void keyPressed(KeyEvent event)
    {
        int code = event.getKeyCode();
        if (code<256)
            keys[code]=true;
    }
   
    public void keyReleased(KeyEvent event)
    {
        int code = event.getKeyCode();
        if (code<256)
            keys[code]=false;
    }
   
    public void mousePressed(MouseEvent event)
    {
        mouse[event.getButton()-1]=true;
        mx = event.getX();
        my = event.getY();
    }
   
    public void mouseReleased(MouseEvent event)
    {
        mouse[event.getButton()-1]=false;
        mx = event.getX();
        my = event.getY();
    }
   
    public void mouseEntered(MouseEvent event)
    {
        mouse[3]=true;
        mx = event.getX();
        my = event.getY();
    }
   
    public void mouseExited(MouseEvent event)
    {
        mouse[3]=false;
        mx = event.getX();
        my = event.getY();
    }
   
    public void keyTyped(KeyEvent e){}
    public void mouseClicked(MouseEvent e){}
}
    [/pre]


EGAGame

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
[pre]package egalib.game;

import java.applet.Applet;
import egalib.ega.*;

public abstract class EGAGame extends Applet implements Runnable
{
    protected volatile EGADisplay ega_display;
   
    protected volatile EGAInput ega_input;
   
    protected volatile Thread ega_thread;
   
    public final void init()
    {
        ega_display = new EGADisplay(this);
        ega_input = new EGAInput();
        ega_display.attachInput(ega_input);
        ega_load();
    }
   
    public final void start()
    {
        ega_thread = new Thread(this);
        ega_thread.start();
    }
   
    public final void stop()
    {
        ega_thread.interrupt();
        ega_thread = null;
    }
   
    public final void destroy()
    {
       
    }
   
    public final void run()
    {
        while (!ega_thread.isInterrupted())
        {
            ega_update();
            ega_paint(ega_display.getGraphics());
            ega_display.show();
            try
            {
                Thread.sleep(30);
            }
            catch (Exception e){}
        }
    }
   
    public abstract void ega_update();
    public abstract void ega_paint(EGAGraphics g);
    public abstract void ega_load();
}[/pre]


Its not complete but maybe somebody can use it.[/code][/code]
Offline jojoh

JGO Knight


Medals: 5
Projects: 7


games4j.com


« Reply #6 - Posted 2010-04-16 12:46:36 »

I added a section at the wiki for this compo, since I find it so hard to know what the latest version of things are in a forum.

Feel free to add latest versions of code and hints, techniques in there, but naturally keep the discussions here.

[size=13pt]"Tiny game" wiki page[/size]

Let me know of any ideas for how to improve or better use the wiki.

Offline Alan_W

JGO Knight


Medals: 8
Projects: 3


Java tames rock!


« Reply #7 - Posted 2010-04-16 21:52:42 »

Yet another framework  Tongue

EGAImage can be used where you would normally use a BufferedImage.  You can draw on it as you would a BufferedImage, except that all colours are automatically converted into the EGA 64 colour palette.

You can store sprite Images in a suitable sized EGAImage.  You can also use it for double buffering the screen.  When double buffering the screen, use the paint() method to draw the EGAImage onto the provided graphics context.  The paint method takes a scaling parameter, which allows the image to be scaled up as required.  The paint method also enforces the 16 colour palette rule.  If there are too many colours, the least frequent are changed to near neighbours, or failing that, black.

There are two code snippits: The first contains the EGAImage class and the second a test framework for the class.

If you are thinking this must be too slow, I get around 200fps on this Intel Core 2 Duo.

Edit: Added changing to nearest neighbour

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
/*
 * EGAImage class for TinyGame Competition
 *
 * Copyright: Alan Waddington 2010
 * Licence:   Public Domain
 * Warranty:  None
 *
 * All drawing on an EGAImage is converted to the EGA 64 Colour palette.
 *
 * An EGAImage can be used as a screen double buffer.  Use the paint method
 * to render the image using a palette of the 16 most common colours in the
 * image at the specified magnification.
 *
 * Any image colours that do not fit in the palette are converted to near
 * neighbours or failing that black. If black is not in the palette, the
 * least frequent palette colour is converted to black.
 *
 * Frame rate depends on image size and the number of colours used, but
 * should be 100fps to 200fps plus depending on processor speed.
 */



import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.DataBufferByte;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

/** EGAImage is a subclass of BufferedImage with the 64 colour EGA palette */
public class EGAImage extends BufferedImage {

    private final static int COLOURS = 64;  // Number of colours in EGA palette
   private final static int PALETTE = 16;  // Number of simultaneous colours
   private final static int BITS    = 6;   // Number of bits in palette index

    private static IndexColorModel model = null;        // EGA Colour Model

    private byte[]     data;                            // Image data
   private int[]      count   = new int[COLOURS];      // No. of each colour

    private int[]      parent  = new int[COLOURS];      // Colour Tree
   private int[]      lower   = new int[COLOURS];
    private int[]      higher  = new int[COLOURS];

    private boolean[]  palette = new boolean[COLOURS];  // Colour is in palette

    /** Create an image based on the 64 colours of the EGA palette */
    public EGAImage(int width, int height) {
        super(width, height, BufferedImage.TYPE_BYTE_INDEXED, EGAColourModel());
        data = ((DataBufferByte)getRaster().getDataBuffer()).getData();
    }

    /** Helper function to create a shared instance of the EGA palette */
    private static IndexColorModel EGAColourModel() {
        if (model==null) {
            byte[] red   = new byte[COLOURS];
            byte[] green = new byte[COLOURS];
            byte[] blue  = new byte[COLOURS];
            for (int i=0; i<COLOURS; i++) {
                red[i]   = (byte)(((i>>2)&1)*0xaa + ((i>>5)&1)*0x55);
                green[i] = (byte)(((i>>1)&1)*0xaa + ((i>>4)&1)*0x55);
                blue[i]  = (byte)(((i>>0)&1)*0xaa + ((i>>3)&1)*0x55);
            }
            model = new IndexColorModel(BITS, COLOURS, red, green, blue);
        }
        return model;
    }

    /** Verify that the image contains 16 colours maximum */
    public int countColors() {
        int i, colours = 0;
        for (i=0; i<COLOURS; i++)
            count[i] = 0;
        for (i=0; i<data.length; i++)
            count[data[i]]++;
        for (i=0; i<COLOURS; i++)
            if (count[i]>0)
                colours++;
        return colours;
    }

    /** Reduce the number of image colours to 16 maximum. */
    public boolean consolidate() {
        int i, node, lastnode;
        boolean excessColours = countColors()>PALETTE;
        if (excessColours) {

            // Initialise search
           for (i=0; i<COLOURS; i++) {
                parent[i] = lower[i] = higher[i] = -1;
                palette[i] = false;
            }

            // Constuct search tree
           for (i=1; i<COLOURS; i++) {
                node = 0;
                while (true) {
                    if (count[i]<count[node]) {
                        if (lower[node]==-1) {
                            lower[node] = i;
                            parent[i] = node;
                            break;
                        } else {
                            node = lower[node];
                        }
                    } else {
                        if (higher[node]==-1) {
                            higher[node] = i;
                            parent[i] = node;
                            break;
                        } else {
                            node = higher[node];
                        }
                    }
                }
            }

            // Traverse tree and put most frequent colours in the palette
           node = 0;
            lastnode = -1;
            i = 0;

            while (true) {
                if (lastnode==parent[node]) { // Down
                   if (higher[node]==-1) {
                        palette[node] = true;
                        i++;
                        if (i==PALETTE)
                            break;
                        if (lower[node]==-1) {
                            if (parent[node]==-1)
                                break;
                            else {
                                lastnode = node;
                                node = parent[node];
                            }
                        } else {
                            lastnode = node;
                            node = lower[node];
                        }
                    } else {
                        lastnode = node;
                        node = higher[node];
                    }
                } else if (lastnode==higher[node]) { // Right
                   palette[node] = true;
                    i++;
                    if (i==PALETTE)
                        break;
                    if (lower[node]==-1) {
                        if (parent[node]==-1)
                            break;
                        else {
                            lastnode = node;
                            node = parent[node];
                        }
                    } else {
                        lastnode = node;
                        node = lower[node];
                    }
                } else { // Left
                   if (parent[node]==-1)
                        break;
                    else {
                        lastnode = node;
                        node = parent[node];
                    }
                }
            }

            // Ensure black is in the palette
           if (!palette[0]) {
                palette[node] = false;
                palette[0] = true;
            }

            // Look for near neighbours
           for (i=0;i<data.length; i++) {
                int colour = data[i];
                if (!palette[colour]) {
                    if (palette[colour ^ 0x08])
                        data[i] ^= 0x08;
                    else if (palette[colour ^ 0x10])
                        data[i] ^= 0x10;
                    else if (palette[colour ^ 0x20])
                        data[i] ^= 0x20;
                    else
                        data[i] = 0; // Give up and mark it black
               }
            }
        }
        return excessColours;
    }

    /** Paint image at specified magnification */
    public void paint(Graphics g, int x, int y, int magnify) {
        consolidate();
        ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
        g.drawImage(this, x, y, magnify*getWidth(), magnify*getHeight(), null);
    }

}

Time flies like a bird. Fruit flies like a banana.
Offline Alan_W

JGO Knight


Medals: 8
Projects: 3


Java tames rock!


« Reply #8 - Posted 2010-04-16 21:53:54 »

The test framework:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
/*
 * Test Package for EGAImage
 *
 * Copyright: Alan Waddington 2010
 * Licence:   Public Domain
 * Warranty:  None
 */


import javax.swing.JFrame;
import java.awt.*;
import java.util.Random;

public class EGAImageTest extends JFrame {

    private final static int MAGNIFY = 4;

    public EGAImageTest() {

        EGAImage ega = new EGAImage(40, 30);
        Random rand = new Random();
        Graphics  g;
        setSize(100+40*MAGNIFY, 100+30*MAGNIFY);
        setVisible(true);
        while (isVisible()) {

            // Benchmark EGAImage
           long start = System.nanoTime();

            // Draw on EGA Image using full 24bit colour
           g = ega.getGraphics();
            g.setColor(Color.BLACK);
            g.fillRect(0,0,40,30);
            for (int x=0; x<40; x++)
                for (int y=0; y<30; y++) {
                    g.setColor(new Color(rand.nextInt(256),
                        rand.nextInt(256), rand.nextInt(256)));
                    g.fillRect(x, y, 1, 1);
                }
            g.dispose();

            // Draw EGAImage to Screen
           g = getGraphics();
            g.setColor(Color.LIGHT_GRAY);
            g.fillRect(0, 0, 100+40*MAGNIFY, 100+30*MAGNIFY);
            System.out.println("EGA Colours requested ="+ega.countColors());
            ega.paint(g, 50, 50, MAGNIFY);
            System.out.println("EGA Colours used      ="+ega.countColors());
           
            // Benchmark EGAImage
           long stop = System.nanoTime();
            long fps = 1000000000l/(stop-start);
            g.setColor(Color.BLACK);
            g.drawString("FPS="+fps, 50, 80+30*MAGNIFY);
            g.dispose();
            try {
                Thread.sleep(100);
            } catch (Exception e) {}
        }
    }

    public static void main(String[] args) {
        new EGAImageTest();
        System.exit(0);
    }
}

Time flies like a bird. Fruit flies like a banana.
Offline Alan_W

JGO Knight


Medals: 8
Projects: 3


Java tames rock!


« Reply #9 - Posted 2010-04-17 00:06:24 »

EGAImage handling an image:


The image loaded was


Time flies like a bird. Fruit flies like a banana.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline ShannonSmith
« Reply #10 - Posted 2010-04-17 17:10:03 »

Rather redundant at this point but here is another framework. It has focus listening with a titlescreen and pause state also text rendering and sub-image drawing. Demo with 4 running at once here


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
public class TinyGame extends Applet implements FocusListener, Runnable, MouseListener {

   public static final int FRAMES_PER_SEC = 30;
   public enum GameState {
      TITLE_SCREEN,
      PAUSED,
      RUNNING
   }
   
   private TinyScreen screen;
   private Thread gameThread;
   private boolean done;
   private GameState state;

   // test game
  private Random rand = new Random();
   private TinyImage ball;
   private int ballX, ballY;
   private int ballVelX, ballVelY;
   
   public TinyGame(){
      setLayout(new BorderLayout());
   }
   
   public void init(){
      int upScale = getWidth()/TinyScreen.WIDTH;
      screen = new TinyScreen(upScale);
      screen.addFocusListener(this);
      state = GameState.TITLE_SCREEN;
      add(screen, BorderLayout.CENTER);
      screen.addMouseListener(this);
      gameThread = new Thread(this);
      gameThread.start();

      // test game
     ball = new TinyImage(7,7,0);
      for(int i = 0; i < ball.width; i++){
         for(int j = 0; j < ball.height; j++){
            int dx = i - 3;
            int dy = j - 3;
            if (dy*dy + dx*dx <= 9){
               ball.setPixel(i, j, 1);
            }
         }  
      }
      ball.setPixel(2, 2, 15);
      ball.setPixel(2, 3, 15);
      ball.setPixel(3, 2, 15);
      ballVelX = 2;
      ballVelY = 1;
      ballX = 10;
      ballY = 12;
   }

   public void start(){
      if (state == GameState.PAUSED){
         state = GameState.RUNNING;
      }
   }
   
   public void stop(){
      if (state == GameState.RUNNING){
         state = GameState.PAUSED;
      }
   }
   
   public void destroy(){
      done = true;
      try {
         gameThread.join();
      } catch (InterruptedException e) {}
   }

   public void focusGained(FocusEvent event) {
      start();
   }

   public void focusLost(FocusEvent event) {
      stop();
   }
   
   public void stepGame(){
      if (ballX < 4 || ballX > 36){
         ballVelX = -ballVelX;
      }
      if (ballY < 4 || ballY > 26){
         ballVelY = -ballVelY;
      }
      ballX += ballVelX;
      ballY += ballVelY;
   }

   public void drawGame(){
      screen.fill(0);
      screen.setPixel(rand.nextInt(40), rand.nextInt(30), rand.nextInt(16));
      screen.drawImage(ball,ballX - 3,ballY - 3);
      screen.drawString("test", 12,12,2);
      screen.show();
   }
   
   public void drawTitle(){
      screen.fill(0);
      screen.drawString("click to", 4, 8, 3);
      screen.drawString("  play", 4, 15, 4);
      screen.show();
   }
   
   public void drawPause(){
      screen.fill(0);
      screen.drawString("paused", 8, 12, 3);
      screen.show();
   }

   public void run() {
      while(!done){
         long delta = System.nanoTime();
         synchronized (this){
            switch(state){
            case RUNNING:
               stepGame();
               drawGame();
               delta = System.nanoTime() - delta;
               delta /= 1000000; // to milisecs.
              break;
            case PAUSED:
               delta = 0;
               drawPause();
               break;
            case TITLE_SCREEN:
               delta = 0;
               drawTitle();
               break;
            }
         }
         // try to lock us at 30fps.
        int targetDelta = 1000/FRAMES_PER_SEC;
         if (delta < targetDelta){
            try {
               Thread.sleep(targetDelta - (int)delta);
            } catch (InterruptedException e) {}
         }
      }
   }

   public void mouseClicked(MouseEvent me) {
      state = GameState.RUNNING;
      screen.requestFocusInWindow();
   }

   public void mouseEntered(MouseEvent me) {
   }

   public void mouseExited(MouseEvent me) {
   }

   public void mousePressed(MouseEvent me) {
   }

   public void mouseReleased(MouseEvent me) {
   }
   
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
public class TinyScreen extends Canvas {
   
   public static final int[] EGA_COLORS = new int[]{
         0xFF000000, 0xFF0000AA, 0xFF00AA00, 0xFF00AAAA, 0xFFAA0000, 0xFFAA00AA, 0xFFAAAA00, 0xFFAAAAAA,
         0xFF000055, 0xFF0000FF, 0xFF00AA55, 0xFF00AAFF, 0xFFAA0055, 0xFFAA00FF, 0xFFAAAA55, 0xFFAAAAFF,
         0xFF005500, 0xFF0055AA, 0xFF00FF00, 0xFF00FFAA, 0xFFAA5500, 0xFFAA55AA, 0xFFAAFF00, 0xFFAAFFAA,
         0xFF005555, 0xFF0055FF, 0xFF00FF55, 0xFF00FFFF, 0xFFAA5555, 0xFFAA55FF, 0xFFAAFF55, 0xFFAAFFFF,
         0xFF550000, 0xFF5500AA, 0xFF55AA00, 0xFF55AAAA, 0xFFFF0000, 0xFFFF00AA, 0xFFFFAA00, 0xFFFFAAAA,
         0xFF550055, 0xFF5500FF, 0xFF55AA55, 0xFF55AAFF, 0xFFFF0055, 0xFFFF00FF, 0xFFFFAA55, 0xFFFFAAFF,
         0xFF555500, 0xFF5555AA, 0xFF55FF00, 0xFF55FFAA, 0xFFFF5500, 0xFFFF55AA, 0xFFFFFF00, 0xFFFFFFAA,
         0xFF555555, 0xFF5555FF, 0xFF55FF55, 0xFF55FFFF, 0xFFFF5555, 0xFFFF55FF, 0xFFFFFF55, 0xFFFFFFFF
   };
   
   private static final int[] FONT = new int[]{
      0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
      0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2000,0x17D9,
      0x2B6A,0x749A,0x772A,0x38A3,0x49ED,0x39CF,0x2ACE,0x12A7,0x7BEF,0x39AA,
      0x0410,0x1410,0x4454,0x0E38,0x1511,0x252A,0x0000,0x5BEA,0x3AEB,0x2A6A,
      0x3B6B,0x72CF,0x12CF,0x2B4E,0x5BED,0x3497,0x5AED,0x7249,0x5B7D,0x5FFD,
      0x2B6A,0x12EB,0x456A,0x5AEB,0x388E,0x2497,0x2B6D,0x256D,0x2FED,0x5AAD,
      0x24AD,0x72A7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x6B50,
      0x3B59,0x6270,0x6B74,0x6750,0x25D6,0x3D50,0x5B59,0x2482,0x1482,0x5749,
      0x2492,0x5BF8,0x5B50,0x2B50,0x1758,0x4D70,0x12C8,0x1450,0x24BA,0x2B68,
      0x2568,0x7F68,0x54A8,0x3D68,0x77B8,0x0000,0x0000,0x0000,0x0000,0x0000,
      0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   };
   
   public static final int WIDTH = 40;
   public static final int HEIGHT = 30;
   
   private final BufferedImage screenBuffer;
   private final int upScale;
   public final int[] palette;
   public final TinyImage buffer;
   
   public TinyScreen(int upScale){
      screenBuffer = new BufferedImage(WIDTH*upScale,HEIGHT*upScale,BufferedImage.TYPE_INT_RGB);
      buffer = new TinyImage(WIDTH,HEIGHT);
      palette = new int[]{0,1,2,3,4,5,20,7,56,57,58,59,60,61,62,63};
      this.upScale = upScale;
      setFocusable(true);
      setIgnoreRepaint(true);

   }
   
   public void setPixel(int x, int y, int value){
      buffer.setPixel(x,y,value);
   }

   private void updateScreenBuffer(){
      int[] pixels = ((DataBufferInt)screenBuffer.getRaster().getDataBuffer()).getData();
      for(int x = 0; x < WIDTH; x++){
         for(int y = 0; y < HEIGHT; y++){
            int rgb = EGA_COLORS[palette[buffer.pixels[y*WIDTH + x]]];
            for(int i = 0; i < upScale*upScale; i++){
               pixels[(y*upScale + i%upScale)*upScale*WIDTH + (x*upScale + i/upScale)] = rgb;
            }
         }
      }
   }
   
   public void show(){
      BufferStrategy bs = getBufferStrategy();
      if (bs == null){
         createBufferStrategy(2);
         bs = getBufferStrategy();
      }
      Graphics g = bs.getDrawGraphics();
      updateScreenBuffer();
      g.drawImage(screenBuffer,0,0,null);
      g.dispose();
      bs.show();
   }

   public void fill(int value) {
      Arrays.fill(buffer.pixels, value);
   }
   
   public void drawString(String s, int x, int y, int color){
      for(int i = 0; i < s.length(); i++){
         int value = FONT[s.charAt(i) - 28];
         for(int j = 0; j < 3*5; j++){
            int px = x + j%3 + i*4;
            int py = y + j/3;
            if (((1 << j) & value) != 0 && px < WIDTH && py < HEIGHT && px >= 0 && py >= 0){
               setPixel(px, py, color);
            }
         }
      }
   }
   
   public void drawImage(TinyImage src, int dx, int dy){
      drawImage(src,dx,dy,src.width,src.height,0,0);
   }
   
   public void drawImage(TinyImage src, int dx, int dy, int w, int h, int sx, int sy){
      int x1 = Math.max(0,dx);
      int x2 = Math.min(buffer.width-1, dx + w);
      int y1 = Math.max(0,dy);
      int y2 = Math.min(buffer.height-1, dy + h);
      w = x2 - x1;
      h = y2 - y1;
      int transparent = src.getTransparentIndex();
      if (w > 0 && h > 0){
         for(int row = y1; row < y2; row++){
            for(int col = x1; col < x2; col++){
               int val = src.pixels[((row - dy) + sy)*src.width + ((col - dx) + sx)];
               if (val != transparent){
                  buffer.pixels[row*buffer.width + col] = val;
               }
            }
         }
      }
   }
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
public class TinyImage {

   public final int width;
   public final int height;
   public final int[] pixels;
   private int transparentIndex;
   
   public TinyImage(int width, int height, int transparentIndex){
      this.width = width;
      this.height = height;
      this.pixels = new int[width*height];
   }

   public TinyImage(int width, int height){
      this(width,height,-1);
   }
   
   public void setPixel(int x, int y, int value){
      pixels[y*width + x] = value;
   }
   
   public int getTransparentIndex(){
      return transparentIndex;
   }
}


Offline Galaxy613

Senior Newbie





« Reply #11 - Posted 2010-04-17 17:33:45 »

Awesome Shannon, I'll update my applet right now to add all those nice features.

If anyone else is a super-n00b and want to get in on this keyboard-input action, here is my SimpleKeyboard.java file:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
// Honestly, this class borrowed heavily from a GameDev.net article.
// I couldn't think of a way to make it simpler without being too restrictive.

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class SimpleKeyboard implements KeyListener {
   private static final int KEY_COUNT = 256;
   private enum KeyState {
      RELEASED,
      PRESSED,
      ONCE
   }
   
   private boolean[] currentKeys = null;
   private KeyState[] keys = null;
   
   public SimpleKeyboard() {
      currentKeys = new boolean[ KEY_COUNT ];
      keys = new KeyState[KEY_COUNT];
     
      for (int i = 0; i < KEY_COUNT; i++)
         keys[i] = KeyState.RELEASED;
   }
   
   public synchronized void poll() { // Polls the system for input and updates based on received events.
     for (int i = 0; i < KEY_COUNT; i++) {
         if (currentKeys[i]) {
            if (keys[i] == KeyState.RELEASED)
               keys[i] = KeyState.ONCE;
            else
               keys[i] = KeyState.PRESSED;
         } else
            keys[i] = KeyState.RELEASED;
      }
   }
   
   public boolean keyDown( int keyCode ){
      return    keys[ keyCode ] == KeyState.ONCE ||
              keys[ keyCode ] == KeyState.PRESSED;
   }
   
   public boolean keyHit ( int keyCode ) {
      return   keys[keyCode] == KeyState.ONCE;
   }
   
   @Override
   public void keyPressed(KeyEvent e) {
      if ( e.getKeyCode() >= 0 && e.getKeyCode() < KEY_COUNT)
         currentKeys [e.getKeyCode()] = true;
   }

   @Override
   public void keyReleased(KeyEvent e) {
      if ( e.getKeyCode() >= 0 && e.getKeyCode() < KEY_COUNT)
         currentKeys [e.getKeyCode()] = false;
   }

   @Override
   public void keyTyped(KeyEvent e) {
      // TODO Auto-generated method stub

   }

}


You just need to make a SimpleKeyboard object and then use keyDown() and keyHit() methods while calling poll() during the main loop. The key codes are static ints of KeyEvent. (i.e. KeyEvent.VK_UP and so on)

I hope this helps some other guy starting out :3 The code is mostly copypasta from a GameDev.net with some of my own tweaks.

I've also been adding functions and making changes to Markus' EgaImage class, for instance I added:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
   public void fill(byte color){
      for (int x = 0; x < width; x++){
         for (int y = 0; y < height; y++){
            pixels[x + y*width] = color;
         }
      }
   }
   
   public void clear(){
      fill((byte)0);
   }



Programming since 2001 (Technically Tongue )
Uses: BlitzMax, Java, C++
Computers: W7 Desktop and a Triple Booting Netbook.
Offline Galaxy613

Senior Newbie





« Reply #12 - Posted 2010-04-18 05:17:07 »

Here's my TOTALLY AWESOME (not really :S) ToneSong class:

**EDIT: I totally forgot! you NEED bobjob's Tone.java file for this to work!!**

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
// Bee Boop! Public Domain? Why not!

/* Use it like this:
 *       ToneSong bkground = new ToneSong (   new int[][]{
            {185, 1900, 50}, // Freq, MS, Volume 100..0
            {-1,-1,-1},
            {185, 1900, 50},
            {-1,-1,-1},
            {185, 1900, 50},
            {-1,-1,-1},
            {185, 900, 50},
            {164, 900, 50}
      }, 1000); // Each beat is 1000 Millisecs
      bkground.setLoop(true);
      bkground.play();
 * then just use bkground.update(); in the main loop!
 */


public class ToneSong {
   private int[][] song;
   private int note = 0; // Current note
  private int pace; // In milliseconds
  private long lastMS;
   private boolean playing = false;
   private boolean loop = false;
   
   public ToneSong (int[][] song, int pace) {
      this.song = song;
      this.pace = pace;
      note = 0;
      lastMS = System.currentTimeMillis();
   }
   
   public void play() {
      setPlaying(true);
   }
   
   public void update(){
      if (!playing)
         return;
     
      long curMilli = System.currentTimeMillis();
      if (lastMS+pace <= curMilli) {
         if ( song[note][0] > 0 &&
               (song[note][2] >= 0 && song[note][2] <= 100) ) {
            Tone.sound(song[note][0], song[note][1], (double)(song[note][2])*0.01);
         }
         lastMS = curMilli;
         note++;
         if (note >= song.length){
            if (loop)
               setPlaying(true);
            else
               setPlaying(false);
         }
      }
   }

   public void setPlaying(boolean playing) {
      this.playing = playing;
      this.note = 0;
   }

   public boolean isPlaying() {
      return playing;
   }

   public void setLoop(boolean loop) {
      this.loop = loop;
   }

   public boolean isLoop() {
      return loop;
   }
}


Great for making crap-tastic musics.

Programming since 2001 (Technically Tongue )
Uses: BlitzMax, Java, C++
Computers: W7 Desktop and a Triple Booting Netbook.
Offline bobjob

JGO Knight


Medals: 10
Projects: 4


David Aaron Muhar


« Reply #13 - Posted 2010-04-18 05:32:45 »

In that case:
for those who want retro style sounds fx, generates tones at runtime, no sound resources are required.
example applet: Test Tone
source: Tone Source

simple to use you just make static call to the Tone class as follows:
1  
Tone.sound(int tone,int time,double volume);


queue sound, for music and such:
1  
Tone.soundQueue(int tone,int time,double volume);



My Projects
Games, Webcam chat, Video screencast, PDF tools.

Javagaming.org with chat room
Offline Alan_W

JGO Knight


Medals: 8
Projects: 3


Java tames rock!


« Reply #14 - Posted 2010-04-18 08:39:55 »

A revision of my EGAImage class that also considers inter colour distance as well as colour frequency when choosing colours.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
/*
 * EGAImage class for TinyGame Competition
 *
 * Copyright: Alan Waddington 2010
 * Licence:   Public Domain
 * Warranty:  None
 *
 * All drawing on an EGAImage is converted to the EGA 64 Colour palette.
 *
 * An EGAImage can be used as a screen double buffer.  Use the paint method
 * to render the image using a palette of the 16 most common colours in the
 * image at the specified magnification.
 *
 * Any image colours that do not fit in the palette are converted to near
 * neighbours.
 *
 * Frame rate depends on image size and the number of colours used, but
 * should be 100fps to 200fps plus depending on processor speed.
 */



import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.DataBufferByte;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

/** EGAImage is a subclass of BufferedImage with the 64 colour EGA palette */
public class EGAImage extends BufferedImage {

    private final static int COLOURS = 64;  // Number of colours in EGA palette
   private final static int PALETTE = 16;  // Number of simultaneous colours
   private final static int BITS    = 6;   // Number of bits in palette index
   private final static int PRIMARIES = 8;   // Number of priority colours

    private static IndexColorModel model = null;        // EGA Colour Model

    private byte[]     data;                            // Image data
   private int[]      count   = new int[COLOURS];      // No. of each colour

    private int[]      parent  = new int[COLOURS];      // Colour Tree
   private int[]      lower   = new int[COLOURS];
    private int[]      higher  = new int[COLOURS];

    private boolean[]  palette = new boolean[COLOURS];  // Colour is in palette
   private boolean[]  primary = new boolean[PRIMARIES];// Primary is used

    private int colours, primaries;                     // Colour counts

    /** Create an image based on the 64 colours of the EGA palette */
    public EGAImage(int width, int height) {
        super(width, height, BufferedImage.TYPE_BYTE_INDEXED, EGAColourModel());
        data = ((DataBufferByte)getRaster().getDataBuffer()).getData();
    }

    /** Helper function to create a shared instance of the EGA palette */
    private static IndexColorModel EGAColourModel() {
        if (model==null) {
            byte[] red   = new byte[COLOURS];
            byte[] green = new byte[COLOURS];
            byte[] blue  = new byte[COLOURS];
            for (int i=0; i<COLOURS; i++) {
                red[i]   = (byte)(((i>>2)&1)*0xaa + ((i>>5)&1)*0x55);
                green[i] = (byte)(((i>>1)&1)*0xaa + ((i>>4)&1)*0x55);
                blue[i]  = (byte)(((i>>0)&1)*0xaa + ((i>>3)&1)*0x55);
            }
            model = new IndexColorModel(BITS, COLOURS, red, green, blue);
        }
        return model;
    }

    /** Verify that the image contains 16 colours maximum */
    public int countColors() {
        int i;
        colours = 0;
        primaries = 0;
        for (i=0; i<COLOURS; i++)
            count[i] = 0;
        for (i=0; i<PRIMARIES; i++)
            primary[i] = false;
        for (i=0; i<data.length; i++) {
            count[data[i]]++;
            primary[data[i] & 0x07] = true;
        }
        for (i=0; i<COLOURS; i++)
            if (count[i]>0)
                colours++;
        for (i=0;i<PRIMARIES;i++)
            if (primary[i])
                primaries++;
        return colours;
    }

    /** Reduce the number of image colours to 16 maximum. */
    public boolean consolidate() {
        int i, node, lastnode;
        boolean excessColours = countColors()>PALETTE;
        if (excessColours) {

            // Initialise search
           for (i=0; i<COLOURS; i++) {
                parent[i] = lower[i] = higher[i] = -1;
                palette[i] = false;
            }

            // Constuct search tree
           for (i=1; i<COLOURS; i++) {
                node = 0;
                while (true) {
                    if (count[i]<count[node]) {
                        if (lower[node]==-1) {
                            lower[node] = i;
                            parent[i] = node;
                            break;
                        } else {
                            node = lower[node];
                        }
                    } else {
                        if (higher[node]==-1) {
                            higher[node] = i;
                            parent[i] = node;
                            break;
                        } else {
                            node = higher[node];
                        }
                    }
                }
            }

            // Traverse tree and put most frequent colours in the palette
           node = 0;
            lastnode = -1;
            colours = 0;

            while (true) {
                if (lastnode==parent[node]) { // Down
                   if (higher[node]==-1) {
                        if (primary[node & 0x07]) {
                            primary[node & 0x07] = false;
                            primaries--;
                        }
                        if (colours<PALETTE-primaries) {
                            palette[node] = true;
                            colours++;
                            if (colours==PALETTE)
                                break;
                        }
                        if (lower[node]==-1) {
                            lastnode = node;
                            node = parent[node];
                        } else {
                            lastnode = node;
                            node = lower[node];
                        }
                    } else {
                        lastnode = node;
                        node = higher[node];
                    }
                } else if (lastnode==higher[node]) { // Right
                   if (primary[node & 0x07]) {
                        primary[node & 0x07] = false;
                        primaries--;
                    }
                    if (colours<PALETTE-primaries) {
                        palette[node] = true;
                        colours++;
                        if (colours==PALETTE)
                            break;
                    }
                    if (lower[node]==-1) {
                        lastnode = node;
                        node = parent[node];
                    } else {
                        lastnode = node;
                        node = lower[node];
                    }
                } else { // Left
                   lastnode = node;
                    node = parent[node];
                }
            }

            // Look for near neighbours (Eye is least sensitive to blue)
           for (i=0;i<data.length; i++) {
                int colour = data[i];
                if (!palette[colour]) {
                    if (palette[colour ^ 0x08])         // Blue
                       data[i] ^= 0x08;
                    else if (palette[colour ^ 0x20])    // Red
                       data[i] ^= 0x20;
                    else if (palette[colour ^ 0x10])    // Green
                       data[i] ^= 0x10;
                    else if (palette[colour ^ 0x28])    // Blue and Red
                       data[i] ^= 0x28;
                    else if (palette[colour ^ 0x18])    // Blue and Green
                       data[i] ^= 0x18;
                    else if (palette[colour ^ 0x30])    // Red and Green
                       data[i] ^= 0x30;
                    else if (palette[colour ^ 0x38])    // All
                       data[i] ^= 0x38;
                    else
                        data[i] = (byte)node;           // Should never happen
               }
            }
        }
        return excessColours;
    }

    /** Paint image at specified magnification */
    public void paint(Graphics g, int x, int y, int magnify) {
        consolidate();
        ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
        g.drawImage(this, x, y, magnify*getWidth(), magnify*getHeight(), null);
    }

}

Time flies like a bird. Fruit flies like a banana.
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 613
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #15 - Posted 2010-04-19 21:43:02 »

(I realized I posted it in the wrong thread, so here we go again)



EgaCompressor:
Input: full color RGB
Output: indexed & dithered RGB (16 colors)


Executable JARs:
http://indiespot.net/files/ega-no-dithering.jar
http://indiespot.net/files/ega-dithering.jar

Webstartable JNLPs:
http://indiespot.net/files/ega-no-dithering.jnlp
http://indiespot.net/files/ega-dithering.jnlp


Sourcecode:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
import java.util.Random;

public class EgaCompressor
{
   public static final int[]  RGB_COLORS    = new int[64];
   private static final int[] HISTOGRAM     = new int[64];
   private static final int[] PALETTE16     = new int[16];
   private static final int[] FROM_64_TO_16 = new int[64];

   static
   {
      for (int i = 0; i < 64; i++)
      {
         int r = (((i >> 4) & 3) * 0x55) << 16;
         int g = (((i >> 2) & 3) * 0x55) << 8;
         int b = (((i >> 0) & 3) * 0x55) << 0;
         RGB_COLORS[i] = r | g | b;
      }
   }

   public static void compress(int[] pixels)
   {
      squeezeTo64(pixels);
      squeeze64to16(pixels);
      extractFrom64(pixels);
   }

   private static Random random = new Random();

   private static void extractFrom64(int[] pixels)
   {
      for (int i = 0; i < pixels.length; i++)
      {
         int index64 = pixels[i];
         int r = (((index64 >> 4) & 3) * 0x55) << 16;
         int g = (((index64 >> 2) & 3) * 0x55) << 8;
         int b = (((index64 >> 0) & 3) * 0x55) << 0;
         pixels[i] = r | g | b;
      }
   }

   private static void squeezeTo64(int[] pixels)
   {
      for (int i = 0; i < pixels.length; i++)
      {
         int pixel = pixels[i];

         int r = (pixel >> 16) & 0xFF;
         int g = (pixel >> 8) & 0xFF;
         int b = (pixel >> 0) & 0xFF;

         int rOff = r / 0x55;
         int gOff = g / 0x55;
         int bOff = b / 0x55;

         // dithering
        rOff += (random.nextInt(0x55) - (r - rOff * 0x55)) >>> 31;
         gOff += (random.nextInt(0x55) - (g - gOff * 0x55)) >>> 31;
         bOff += (random.nextInt(0x55) - (b - bOff * 0x55)) >>> 31;

         pixels[i] = (rOff << 4) | (gOff << 2) | (bOff << 0);
      }
   }

   private static void squeeze64to16(int[] pixels)
   {
      // fill frequency table
     for (int i = 0; i < HISTOGRAM.length; i++)
      {
         HISTOGRAM[i] = 0;
      }
      for (int i = 0; i < pixels.length; i++)
      {
         HISTOGRAM[pixels[i]]++;
      }

      // find 16 most used colors
     for (int i = 0; i < 16; i++)
      {
         int maxCount = -1;
         int maxIndex = -1;
         for (int m = 0; m < HISTOGRAM.length; m++)
         {
            if (HISTOGRAM[m] > maxCount)
            {
               maxCount = HISTOGRAM[m];
               maxIndex = m;
            }
         }

         // add color to palette
        PALETTE16[i] = maxIndex;

         // clear entry
        HISTOGRAM[maxIndex] = 0;
      }

      // create lookup table from EGA64 to EGA16
     for (int i = 0; i < 64; i++)
      {
         // find nearest match in 16 color palette
        int r = (i >> 4) & 3;
         int g = (i >> 2) & 3;
         int b = (i >> 0) & 3;
         int minDiffIndex = -1;
         int minDiffValue = Integer.MAX_VALUE;
         for (int p = 0; p < PALETTE16.length; p++)
         {
            int rDiff = Math.abs(r - ((PALETTE16[p] >> 4) & 3));
            int gDiff = Math.abs(g - ((PALETTE16[p] >> 2) & 3));
            int bDiff = Math.abs(b - ((PALETTE16[p] >> 0) & 3));
            int nDiff = (rDiff + gDiff + bDiff);
            if (minDiffValue < nDiff)
            {
               continue;
            }
            minDiffValue = nDiff;
            minDiffIndex = p;
         }
         FROM_64_TO_16[i] = PALETTE16[minDiffIndex];
      }

      // convert
     for (int i = 0; i < pixels.length; i++)
      {
         pixels[i] = FROM_64_TO_16[pixels[i]];
      }
   }
}

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline moogie

JGO Knight


Medals: 11
Projects: 6
Exp: 10 years


Java games rock!


« Reply #16 - Posted 2010-04-19 22:53:48 »

In my experiments, random dither works well at larger resolutions... at the lower 40x30, the illusion does not seem to be as effective Sad well for my purposes anyway Tongue
Offline ShannonSmith
« Reply #17 - Posted 2010-04-19 22:56:48 »

To my eyes dithering looks bad at any resolution. I would rather go for a carefully chosen palette with colours that might be slightly off than dither.
Offline Galaxy613

Senior Newbie





« Reply #18 - Posted 2010-04-20 05:37:17 »

More functions for Markus' EgaImage class/framework:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
   // Copies the pixels of the supplied EgaImage
  public void copyFrom(EgaImage img){
      if (!(pixels.length == img.pixels.length))
         return;
      for (int x = 0; x < width; x++){
         for (int y = 0; y < height; y++){
            pixels[x + y*width] = img.pixels[x + y*width];
         }
      }      
   }
   
   // Flips the image
  public void flip(boolean horiz, boolean verti) {
      if (horiz) {
         int halfwidth = width;
         
         if (width % 2 == 1) {
            halfwidth--;
         }
         halfwidth /= 2;
         
         for (int x = 0; x < halfwidth; x++){
            for (int y = 0; y < height; y++){
               byte tmpColor = pixels[x + y*width];
               pixels[x + y*width] = pixels[(width - x -1) + y*width];
               pixels[(width - x -1) + y*width] = tmpColor;
            }
         }
      }
      if (verti) {
         int halfheight = height;
         
         if (height % 2 == 1) {
            halfheight--;
         }
         halfheight /= 2;
         
         for (int x = 0; x < width; x++){
            for (int y = 0; y < halfheight; y++){
               byte tmpColor = pixels[x + y*width];
               pixels[x + y*width] = pixels[x + (height - y -1)*width];
               pixels[x + (height - y -1)*width] = tmpColor;
            }
         }
      }
   }


flip() probably isn't completely optimized and what not, but it's supposed to be used while setting up an image..

Programming since 2001 (Technically Tongue )
Uses: BlitzMax, Java, C++
Computers: W7 Desktop and a Triple Booting Netbook.
Offline pjt33
« Reply #19 - Posted 2010-04-20 22:09:02 »

Galaxy, any reason you're not using System.arraycopy in copyFrom?
Offline Galaxy613

Senior Newbie





« Reply #20 - Posted 2010-04-20 23:41:37 »

Galaxy, any reason you're not using System.arraycopy in copyFrom?

No good reason, just that I didn't know System.arraycopy existed, I'm still a n00b you see. Tongue I searched for the Array class for such a function, but there wasn't one I could see so I gave up then and made my silly for loops.

Programming since 2001 (Technically Tongue )
Uses: BlitzMax, Java, C++
Computers: W7 Desktop and a Triple Booting Netbook.
Offline Wyrframe

Senior Newbie





« Reply #21 - Posted 2010-04-21 04:15:07 »

Galaxy613; try java.util.Arrays; it has several handy functions on arrays, in all appropriate type variations.
Offline ShannonSmith
« Reply #22 - Posted 2010-04-24 21:05:10 »

Finished the first cut of my tiny paint program here. The image and a 16 colour palette are encoded in tightly packed base64 strings, to use in a game just copy the string from the text box and use the below function to decode. The palette array argument will be filled with the palette that was used to generate the image if it is not null.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
   public static TinyImage decode64(String src, int[] palette){
      return decode(Base64.decodeBase64(src),palette);
   }
   
   public static TinyImage decode(byte[] src, int[] palette){
      if (src.length < 17){
         // must be at least 17 bytes for a 1x1 pixel image;
        return null;
      }
     
      int width = ((src[0]&0xFF) << 4) | ((src[1]&0xFF) >> 4);
      int height = ((src[1]&0xF) << 8) | (src[2]&0xFF);
      boolean useTransparent = ((src[3] >> 4) & 1) == 1;
      int transparent = useTransparent ? (src[3] & 0xF) : -1;
      TinyImage img = new TinyImage(width,height,transparent);
     
      // row width in bytes
     int rowWidth = (width+1)/2;
      if (src.length < (rowWidth*height + 16)){
         // don't even bother
        return null;
      }

      for(int row = 0; row < height; row++){
         for(int col = 0; col < width/2; col++){
            int val = src[row*rowWidth + col + 16]&0xFF;
            img.pixels[row*width + col*2] = (val >> 4);
            img.pixels[row*width + col*2 + 1] = (val & 0xF);
         }
      }
     
      if (palette != null){
         //decode the image palette
        palette[0] = (((src[4]&0xFF) >> 2)) & 0x3F;
         palette[1] = ((((src[4]&0xFF) << 4)) | ((src[5]&0xFF) >> 4 )) & 0x3F;
         palette[2] = ((((src[5]&0xFF) << 2)) | ((src[6]&0xFF) >> 6 )) & 0x3F;
         palette[3] = ((src[6]&0xFF)) & 0x3F;
         palette[4] = ((src[7]&0xFF) >> 2) & 0x3F;
         palette[5] = (((src[7]&0xFF) << 4) | ((src[8]&0xFF) >> 4 )) & 0x3F;
         palette[6] = (((src[8]&0xFF) << 2) | ((src[9]&0xFF) >> 6 )) & 0x3F;
         palette[7] = (src[9]&0xFF) & 0x3F;
         palette[8] = ((src[10]&0xFF) >> 2) & 0x3F;
         palette[9] = ((((src[10]&0xFF) << 4)) | ((src[11]&0xFF) >> 4 )) & 0x3F;
         palette[10] = ((((src[11]&0xFF) << 2)) | ((src[12]&0xFF) >> 6 )) & 0x3F;
         palette[11] = (src[12]&0xFF) & 0x3F;
         palette[12] = ((src[13]&0xFF) >> 2) & 0x3F;
         palette[13] = ((((src[13]&0xFF) << 4)) | ((src[14]&0xFF) >> 4 )) & 0x3F;
         palette[14] = ((((src[14]&0xFF) << 2)) | ((src[15]&0xFF) >> 6 )) & 0x3F;
         palette[15] = (src[15]&0xFF) & 0x3F;
      }
     
      return img;
   }


You can use any Base64 to byte[] decoder you like I wrote my own, it's not particularly efficient but is pretty straight forward and could be useful for encoding other resources.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
   private final static byte[] ALPHABET = {
       (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
       (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
       (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
       (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
       (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
       (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
       (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
       (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
       (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
       (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
     };
   
   public static String encodeBase64(byte[] bytes){
      // used 4 base64 chars (24 bits) to encode 3 bytes (24 bits)
     int[] block = new int[3];
      StringBuilder sb = new StringBuilder();
      for(int i = 0; i < bytes.length; i+=3){
         block[0] = bytes[i];
         block[1] = i < (bytes.length - 1) ? bytes[i+1] : 0;
         block[2] = i < (bytes.length - 2) ? bytes[i+2] : 0;
         sb.append(encodeBlock(block));
      }
      return sb.toString();
   }
   
   private static final int MASK = 0x3F;
   
   public static byte[] decodeBase64(String s){
      byte[] bytes = new byte[(s.length()/4)*3];
      int byteIndex = 0;
      int[] block = new int[3];
      int blockCount = s.length()/4; // 4 chars per 3 bytes.
     for(int i = 0; i < blockCount; i++){
         decodeBlock(s, i*4, block);
         bytes[byteIndex] = (byte)block[0];
         bytes[byteIndex + 1] = (byte)block[1];
         bytes[byteIndex + 2] = (byte)block[2];
         byteIndex += 3;
      }
      return bytes;
   }

   
   private static int decodeChar(char c){
      if (c >= 65 && c <= 90){
         return c - 65;
      } else if (c >= 97 && c <= 122){
         return c - 71;
      } else if (c >= 48 && c <= 57){
         return c + 4;
      } else if (c == '-'){
         return 62;
      } else if (c == '_'){
         return 63;
      } else {
         return -1;
      }
   }
   
   private static String encodeBlock(int[] bytes){
      return new String(
         new byte[]{
               ALPHABET[bytes[0] & MASK],
               ALPHABET[bytes[1] & MASK],
               ALPHABET[bytes[2] & MASK],
               ALPHABET[(((bytes[0] >> 6) & 3) | ((bytes[1] >> 4) & 0xC) | ((bytes[2] >> 2) & 0x30))],
         }
      );
     
   }
   
   
   private static void decodeBlock(String s, int offset, int[] dest){
      int b4 = decodeChar(s.charAt(offset + 3));
      for(int i = 0; i < 3; i++){
         dest[i] = decodeChar(s.charAt(offset + i)) | (((b4 >> i*2) & 3) << 6);
      }
   }
   


The image format if anyone wants to make a compatible tool is below.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
Header 16 bytes

     msb  lsb
0000 wwwwwwww   w = width (most significant bits are in lower bytes)
0001 wwwwhhhh   h = height
0002 hhhhhhhh   x = format (0 = no transparency, 1 = use transparent index)
0003 uuuxtttt   t = transparent index (not necessarily used)      
0004 aaaaaabb   a-p EGA paltte index of colour 0-15
0005 bbbbcccc   u = unused (set to zero)
0006 ccdddddd  
0007 eeeeeeff
0008 ffffgggg
0009 gghhhhhh
0010 iiiiiijj
0011 jjjjkkkk
0012 kkllllll
0013 mmmmmmnn
0014 nnnnoooo
0015 oopppppp
               
Data

0016 xxxx xxxx  raster data 2 pixels per byte scanning from top left
0017 xxxx xxxx  the most significant bits of each byte is the leftmost pixel
0018 xxxx 0000  end of each row is padded with 4 zero bits if it is odd
0020 xxxx xxxx  
.
.
.

Offline Galaxy613

Senior Newbie





« Reply #23 - Posted 2010-04-25 00:50:35 »

How easy would it be to change that image loader for use with Markus' EgaImage class? I think the only major difference is that he uses a Byte array and you use a Int array for the pixels it seems.

I'll probably try to convert it soon either way I guess.

Programming since 2001 (Technically Tongue )
Uses: BlitzMax, Java, C++
Computers: W7 Desktop and a Triple Booting Netbook.
Online Roquen
« Reply #24 - Posted 2010-04-30 09:33:26 »


Yet another full palette intializer (linear form):

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
private static final int[] EGA_PALETTE;
 
static {
  int x = 0;
  int r;
 
  EGA_PALETTE  = new int[64];
 
  for (int i = 0; i < 64; i++) {
    r  = x & 0x30303;
    r |= r << 2;
    r |= r << 4;
    x += 0x1041;
   
    EGA_PALETTE[i] = r;
  }
}


Here's the point of this post. Some example validation code, easy to convert stuff like histograms, etc.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
/** 
 * Returns a packed bit array of the EGA colors used.
 * Zero indicates an invalid color was encountered.
 */

public static final long colorUsage(int[] data, int len)
{
  long c = 0;
  int  x = 0;
  int  y = 0;
  int  d,r;
 
  // Assume all the color are valid and only
 // checks at the end of processing.
 // x - checks that the nibbles are R,G,B are the same
 // y - checks that each bit pair of R,G,B are the same
 // r - calculates current color index (linear form)
 // c - packed bit array of used EGA colors
 
  for(int i=0; i<len; i++) {
    d  = data[i];
    x |= (d ^ (d >>4));
    y |= (d ^ (d >>2));
    r  = d & 0x030303;
    r |= (r >>  6);
    r |= (r >> 12);
    c |= 1L << (r & 0x3f);
  }
 
  // Check if there were any invalid RGB values.
 if (((x|y) & 0xF0F0F) != 0)
    return 0;
 
  return c;
}

/** Returns true if the buffer contains only 16 or less EGA colors. */
public static final boolean isValidRGB(int[] data, int len)
{
  int count = Long.bitCount(colorUsage(data,len));
 
  return (count > 0 && count <= 16);
}



Pretty much the same stuff as above, but broken into component functions for reference:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
/** Check if RGB is a valid EGA color. Ignores the upper byte. */
public static final boolean isValidRGB(int rgb)
{
  int x = (rgb ^ (rgb >> 4)); // each nibble must be the same
 int y = (rgb ^ (rgb >> 2)); // each bit pair must be the same
 
  return ((x|y) & 0xF0F0F) == 0;
}

/** Convert RGB value (assumed valid) into linear index.*/
public static final int RGBToIndex(int rgb)
{
  int r = rgb & 0x030303;
 
  r |= (r >>  6);
  r |= (r >> 12);
 
  return r & 0x3f;
}

/** Directly create an RGB from a linear index. */
public static final int indexToRGB(int r)
{
  // scatter the counter (bit pairs to each byte)
 r  = (r | (r << 6) | (r << 12)) & 0x030303;
  r |= r << 2;  // duplicate bit pairs
 r |= r << 4;  // duplicate nibbles
 
  return r;
}



Offline Markus_Persson

JGO Wizard


Medals: 12
Projects: 19


Mojang Specifications


« Reply #25 - Posted 2010-04-30 11:59:21 »

Neato! Cheesy

That's some creepy compressed obfuscated magic, I love it! =D

Play Minecraft!
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

theagentd (6 views)
2014-04-24 23:00:44

xsi3rr4x (83 views)
2014-04-15 18:08:23

BurntPizza (75 views)
2014-04-15 03:46:01

UprightPath (86 views)
2014-04-14 17:39:50

UprightPath (69 views)
2014-04-14 17:35:47

Porlus (86 views)
2014-04-14 15:48:38

tom_mai78101 (109 views)
2014-04-10 04:04:31

BurntPizza (169 views)
2014-04-08 23:06:04

tom_mai78101 (265 views)
2014-04-05 13:34:39

trollwarrior1 (217 views)
2014-04-04 12:06:45
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!