Created
November 18, 2013 19:58
-
-
Save ClickerMonkey/7534326 to your computer and use it in GitHub Desktop.
Software graphics library with blending, textures, and simple primitives.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class Blend | |
{ | |
public static final Blend Alpha = new Blend() | |
{ | |
public int blend( int o, int n ) | |
{ | |
return Color.mixRGB( o, n, Color.alpha( n ), Color.alpha( o ) ); | |
} | |
}; | |
public static final Blend Additive = new Blend() | |
{ | |
public int blend( int o, int n ) | |
{ | |
return Color.add( o, Color.scaleRGB( n, Color.alpha( n ), 0 ) ); | |
} | |
}; | |
public static final Blend Invert = new Blend() | |
{ | |
public int blend( int o, int n ) | |
{ | |
return Color.subRGB( Color.scaleRGB( n, Color.alpha( n ), Color.COMPONENT_MAX ), o, Color.alpha( o ) ); | |
} | |
}; | |
public static final Blend Replace = new Blend() | |
{ | |
public int blend( int o, int n ) | |
{ | |
return n; | |
} | |
}; | |
public static final Blend Ignore = new Blend() | |
{ | |
public int blend( int o, int n ) | |
{ | |
return o; | |
} | |
}; | |
public abstract int blend( int o, int n ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class CircleDraw | |
{ | |
public Gfx g; | |
public CircleDraw( Gfx g ) | |
{ | |
this.g = g; | |
} | |
public void fillSmooth( int cx, int cy, int radius, int color ) | |
{ | |
// Adjust for anti-aliasing of pixels around borders | |
cx++; | |
cy++; | |
radius -= 2; | |
final int radius2 = radius << 1; | |
final int radius4 = radius << 2; | |
int error = -radius - 1; | |
int x = radius; | |
int y = 1; | |
scanline( cy, cx - x, cx + x + 1, color ); | |
int alpha = (((error + radius2) << 8) / radius4) ^ 255; | |
int alphad = Color.mulAlpha( color, alpha ); | |
g.apply( cx - x - 1, cy, alphad ); | |
g.apply( cx + x, cy, alphad ); | |
g.apply( cx, cy - x - 1, alphad ); | |
g.apply( cx, cy + x + 1, alphad ); | |
while (x > y) | |
{ | |
alpha = (((error + radius2) << 8) / radius4) ^ 255; | |
alphad = Color.mulAlpha( color, alpha ); | |
g.apply( cx - x - 1, cy - y, alphad ); | |
g.apply( cx + x, cy - y, alphad ); | |
g.apply( cx - x - 1, cy + y, alphad ); | |
g.apply( cx + x, cy + y, alphad ); | |
g.apply( cx - y, cy - x - 1, alphad ); | |
g.apply( cx - y, cy + x + 1, alphad ); | |
if (y != 1) | |
{ | |
g.apply( cx + y - 1, cy - x - 1, alphad ); | |
g.apply( cx + y - 1, cy + x + 1, alphad ); | |
} | |
scanline( cy - y, cx - x, cx + x + 1, color ); | |
scanline( cy + y, cx - x, cx + x + 1, color ); | |
error += y + ++y; | |
if (error >= 0) | |
{ | |
scanline( cy - x, cx - y + 1, cx + y, color ); | |
scanline( cy + x, cx - y + 1, cx + y, color ); | |
error -= x + --x; | |
} | |
} | |
alpha = (((error + radius2) << 8) / radius4) ^ 255; | |
alphad = Color.mulAlpha( color, alpha ); | |
g.apply( cx - x - 1, cy - y, alphad ); | |
g.apply( cx + x, cy - y, alphad ); | |
g.apply( cx - x - 1, cy + y, alphad ); | |
g.apply( cx + x, cy + y, alphad ); | |
if (x == y) | |
{ | |
g.apply( cx - x, cy - y - 1, alphad ); | |
g.apply( cx + x - 1, cy - y - 1, alphad ); | |
g.apply( cx - x, cy + y + 1, alphad ); | |
g.apply( cx + x - 1, cy + y + 1, alphad ); | |
scanline( cy - y, cx - x, cx + x + 1, color ); | |
scanline( cy + y, cx - x, cx + x + 1, color ); | |
} | |
} | |
public void fillFast( int cx, int cy, int radius, int color ) | |
{ | |
int error = -radius + 1; | |
int x = radius; | |
int y = 1; | |
scanline( cy, cx - x, cx + x, color ); | |
while (x > y) | |
{ | |
scanline( cy - y, cx - x, cx + x, color ); | |
scanline( cy + y, cx - x, cx + x, color ); | |
error += y + ++y; | |
if (error >= 0) | |
{ | |
scanline( cy - x, cx - y, cx + y, color ); | |
scanline( cy + x, cx - y, cx + y, color ); | |
error -= x + --x; | |
} | |
} | |
if (x == y) | |
{ | |
scanline( cy - y, cx - x, cx + x, color ); | |
scanline( cy + y, cx - x, cx + x, color ); | |
} | |
} | |
private void scanline( int y, int x0, int x1, int color ) | |
{ | |
final int bw = g.width(); | |
final int bh = g.height(); | |
if (y < 0 || y >= bh || (x0 < 0 && x1 < 0) || (x0 > bw && x1 > bw)) | |
{ | |
return; | |
} | |
x0 = (x0 < 0 ? 0 : (x0 > bw ? bw : x0)); | |
x1 = (x1 < 0 ? 0 : (x1 > bw ? bw : x1)); | |
int offset = g.getOffset( x0, y ); | |
while (x0 < --x1) | |
{ | |
g.apply( offset++, color ); | |
} | |
} | |
public void outlineFast( int cx, int cy, int radius, int color ) | |
{ | |
int error = -radius; | |
int x = radius; | |
int y = 0; | |
while (x > y) | |
{ | |
apply8( cx, cy, x, y, color ); | |
error += y++ + y; | |
if (error >= 0) | |
{ | |
error -= x + --x; | |
} | |
} | |
apply4( cx, cy, x, y, color ); | |
} | |
private void apply8( int cx, int cy, int x, int y, int color ) | |
{ | |
apply4( cx, cy, x, y, color ); | |
apply4( cx, cy, y, x, color ); | |
} | |
private void apply4( int cx, int cy, int x, int y, int color ) | |
{ | |
g.applyCheck( cx + x, cy + y, color ); | |
g.applyCheck( cx - x, cy + y, color ); | |
g.applyCheck( cx + x, cy - y, color ); | |
g.applyCheck( cx - x, cy - y, color ); | |
} | |
public void outlineSmooth( int cx, int cy, int r, int color ) | |
{ | |
// TODO not suck | |
int R2 = r * r; | |
int y = 0; | |
int x = r; | |
int B = x * x; | |
int xTop = x + 1; | |
int T = xTop * xTop; | |
while (y < x) | |
{ | |
int E = R2 - y * y; | |
int L = E - B; | |
int U = T - E; | |
if (L < 0) | |
{ | |
xTop = x; | |
x--; | |
T = B; | |
U = -L; | |
B = x * x; | |
L = E - B; | |
} | |
octants( cx, cy, x, xTop, y, color, 255 * U / (U + L) ); | |
y++; | |
} | |
} | |
public void octants( int cx, int cy, int x0, int x1, int y, int color, int u ) | |
{ | |
int ucolor = Color.mulAlpha( color, u ); | |
int vcolor = Color.mulAlpha( color, Color.COMPONENT_MAX - u ); | |
g.applyCheck( cx + x0, cy + y, ucolor ); | |
g.applyCheck( cx + x1, cy + y, vcolor ); | |
g.applyCheck( cx + x0, cy - y, ucolor ); | |
g.applyCheck( cx + x1, cy - y, vcolor ); | |
g.applyCheck( cx - x0, cy + y, ucolor ); | |
g.applyCheck( cx - x1, cy + y, vcolor ); | |
g.applyCheck( cx - x0, cy - y, ucolor ); | |
g.applyCheck( cx - x1, cy - y, vcolor ); | |
g.applyCheck( cx + y, cy + x0, ucolor ); | |
g.applyCheck( cx + y, cy + x1, vcolor ); | |
g.applyCheck( cx - y, cy + x0, ucolor ); | |
g.applyCheck( cx - y, cy + x1, vcolor ); | |
g.applyCheck( cx + y, cy - x0, ucolor ); | |
g.applyCheck( cx + y, cy - x1, vcolor ); | |
g.applyCheck( cx - y, cy - x0, ucolor ); | |
g.applyCheck( cx - y, cy - x1, vcolor ); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Color | |
{ | |
public static final int A_SHIFT = 24; | |
public static final int A_MASK = 0xFF000000; | |
public static final int R_SHIFT = 16; | |
public static final int R_MASK = 0x00FF0000; | |
public static final int G_SHIFT = 8; | |
public static final int G_MASK = 0x0000FF00; | |
public static final int B_SHIFT = 0; | |
public static final int B_MASK = 0x000000FF; | |
public static final int COMPONENT_MASK = 0xFF; | |
public static final int COMPONENT_MIN = 0; | |
public static final int COMPONENT_MAX = 255; | |
public static final int COMPONENT_POWER = 8; | |
public static int clamp( int c ) | |
{ | |
return ( c < COMPONENT_MIN ? COMPONENT_MIN : ( c > COMPONENT_MAX ? COMPONENT_MAX : c ) ); | |
} | |
public static int create( int r, int g, int b ) | |
{ | |
return create( r, g, b, COMPONENT_MAX ); | |
} | |
public static int create( int r, int g, int b, int a ) | |
{ | |
return ( a << A_SHIFT ) | ( r << R_SHIFT ) | ( g << G_SHIFT ) | ( b << B_SHIFT ); | |
} | |
public static int mulComponents(int c0, int c1) | |
{ | |
return (c0 * c1 + COMPONENT_MAX) >> COMPONENT_POWER; | |
} | |
public static int mixComponents(int c0, int c1, int delta) | |
{ | |
return mulComponents( c0, COMPONENT_MAX ^ delta ) + mulComponents( c1, delta ); | |
} | |
public static int mulAlpha(int c, int alpha) | |
{ | |
return (c & ~A_MASK) | (mulComponents( alpha(c), alpha ) << A_SHIFT); | |
} | |
public static int mulRed(int c, int red) | |
{ | |
return (c & ~R_MASK) | (mulComponents( red(c), red ) << R_SHIFT); | |
} | |
public static int mulGreen(int c, int green) | |
{ | |
return (c & ~G_MASK) | (mulComponents( green(c), green ) << G_SHIFT); | |
} | |
public static int mulBlue(int c, int blue) | |
{ | |
return (c & ~B_MASK) | (mulComponents( blue(c), blue ) << B_SHIFT); | |
} | |
public static int withAlpha(int c, int alpha) | |
{ | |
return (c & ~A_MASK) | (alpha << A_SHIFT); | |
} | |
public static int withRed(int c, int red) | |
{ | |
return (c & ~R_MASK) | (red << R_SHIFT); | |
} | |
public static int withGreen(int c, int green) | |
{ | |
return (c & ~G_MASK) | (green << G_SHIFT); | |
} | |
public static int withBlue(int c, int blue) | |
{ | |
return (c & ~B_MASK) | (blue << B_SHIFT); | |
} | |
public static int createAndClamp( int r, int g, int b ) | |
{ | |
return create( clamp( r ), clamp( g ), clamp( b ) ); | |
} | |
public static int createAndClamp( int r, int g, int b, int a ) | |
{ | |
return create( clamp( r ), clamp( g ), clamp( b ), clamp( a ) ); | |
} | |
public static int alpha( int color ) | |
{ | |
return ( color >> A_SHIFT ) & COMPONENT_MASK; | |
} | |
public static int red( int color ) | |
{ | |
return ( color >> R_SHIFT ) & COMPONENT_MASK; | |
} | |
public static int green( int color ) | |
{ | |
return ( color >> G_SHIFT ) & COMPONENT_MASK; | |
} | |
public static int blue( int color ) | |
{ | |
return ( color >> B_SHIFT ) & COMPONENT_MASK; | |
} | |
public static int add( int c0, int c1 ) | |
{ | |
return createAndClamp( | |
red( c0 ) + red( c1 ), | |
green( c0 ) + green( c1 ), | |
blue( c0 ) + blue( c1 ), | |
alpha( c0 ) + alpha( c1 ) | |
); | |
} | |
public static int addRGB( int c0, int c1, int alpha ) | |
{ | |
return createAndClamp( | |
red( c0 ) + red( c1 ), | |
green( c0 ) + green( c1 ), | |
blue( c0 ) + blue( c1 ), | |
alpha | |
); | |
} | |
public static int sub( int c0, int c1 ) | |
{ | |
return createAndClamp( | |
red( c0 ) - red( c1 ), | |
green( c0 ) - green( c1 ), | |
blue( c0 ) - blue( c1 ), | |
alpha( c0 ) - alpha( c1 ) | |
); | |
} | |
public static int subRGB( int c0, int c1, int alpha ) | |
{ | |
return createAndClamp( | |
red( c0 ) - red( c1 ), | |
green( c0 ) - green( c1 ), | |
blue( c0 ) - blue( c1 ), | |
alpha | |
); | |
} | |
public static int mul( int c0, int c1 ) | |
{ | |
return create( | |
mulComponents(red( c0 ), red( c1 )), | |
mulComponents(green( c0 ), green( c1 )), | |
mulComponents(blue( c0 ), blue( c1 )), | |
mulComponents(alpha( c0 ), alpha( c1 )) | |
); | |
} | |
public static int mulRGB( int c0, int c1, int alpha ) | |
{ | |
return create( | |
mulComponents(red( c0 ), red( c1 )), | |
mulComponents(green( c0 ), green( c1 )), | |
mulComponents(blue( c0 ), blue( c1 )), | |
alpha | |
); | |
} | |
public static int lighten( int c, int delta ) | |
{ | |
return create( | |
mixComponents( red( c ), COMPONENT_MAX, delta ), | |
mixComponents( green( c ), COMPONENT_MAX, delta ), | |
mixComponents( blue( c ), COMPONENT_MAX, delta ), | |
mixComponents( alpha( c ), COMPONENT_MAX, delta ) | |
); | |
} | |
public static int lightenRGB( int c, int delta, int alpha ) | |
{ | |
return create( | |
mixComponents( red( c ), COMPONENT_MAX, delta ), | |
mixComponents( green( c ), COMPONENT_MAX, delta ), | |
mixComponents( blue( c ), COMPONENT_MAX, delta ), | |
alpha | |
); | |
} | |
public static int darken( int c, int delta ) | |
{ | |
return create( | |
mixComponents( red( c ), COMPONENT_MIN, delta ), | |
mixComponents( green( c ), COMPONENT_MIN, delta ), | |
mixComponents( blue( c ), COMPONENT_MIN, delta ), | |
mixComponents( alpha( c ), COMPONENT_MIN, delta ) | |
); | |
} | |
public static int darkenRGB( int c, int delta, int alpha ) | |
{ | |
return create( | |
mixComponents( red( c ), COMPONENT_MIN, delta ), | |
mixComponents( green( c ), COMPONENT_MIN, delta ), | |
mixComponents( blue( c ), COMPONENT_MIN, delta ), | |
alpha | |
); | |
} | |
public static int mix(int c0, int c1, int delta) | |
{ | |
return create( | |
mixComponents( red(c0), red(c1), delta ), | |
mixComponents( green(c0), green(c1), delta ), | |
mixComponents( blue(c0), blue(c1), delta ), | |
mixComponents( alpha(c0), alpha(c1), delta ) | |
); | |
} | |
public static int mixRGB(int c0, int c1, int delta, int alpha) | |
{ | |
return create( | |
mixComponents( red(c0), red(c1), delta ), | |
mixComponents( green(c0), green(c1), delta ), | |
mixComponents( blue(c0), blue(c1), delta ), | |
alpha | |
); | |
} | |
public static int scale(int c, int delta) | |
{ | |
return create( | |
mixComponents( COMPONENT_MIN, red(c), delta ), | |
mixComponents( COMPONENT_MIN, green(c), delta ), | |
mixComponents( COMPONENT_MIN, blue(c), delta ), | |
mixComponents( COMPONENT_MIN, alpha(c), delta ) | |
); | |
} | |
public static int scaleRGB(int c, int delta, int alpha) | |
{ | |
return create( | |
mixComponents( COMPONENT_MIN, red(c), delta ), | |
mixComponents( COMPONENT_MIN, green(c), delta ), | |
mixComponents( COMPONENT_MIN, blue(c), delta ), | |
alpha | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Colors | |
{ | |
public static final int White = Color.create( 255, 255, 255 ); | |
public static final int Black = Color.create( 0, 0, 0 ); | |
public static final int Red = Color.create( 255, 0, 0 ); | |
public static final int Blue = Color.create( 0, 0, 255 ); | |
public static final int Green = Color.create( 0, 255, 0 ); | |
public static final int Magenta = Color.create( 255, 0, 255 ); | |
public static final int Gray = Color.create( 128, 128, 128 ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public final class Gfx | |
{ | |
public Image renderBuffer; | |
public MemoryImageSource renderBufferSource; | |
public Texture texture; | |
public Blend blend = Blend.Alpha; | |
public Gfx( int w, int h ) | |
{ | |
resize( w, h ); | |
} | |
public void resize( int w, int h ) | |
{ | |
texture = new Texture( w, h ); | |
renderBufferSource = new MemoryImageSource( w, h, texture.pixels, 0, w ); | |
renderBufferSource.setAnimated( true ); | |
renderBuffer = Toolkit.getDefaultToolkit().createImage( renderBufferSource ); | |
} | |
public void clear( int color ) | |
{ | |
texture.clear( color ); | |
} | |
public void apply( int x, int y, int color ) | |
{ | |
final int[] pixels = texture.pixels; | |
final int offset = texture.getOffset( x, y ); | |
pixels[offset] = blend.blend( pixels[offset], color ); | |
} | |
public void applyCheck( int x, int y, int color ) | |
{ | |
if (x < 0 || x >= texture.width || y < 0 || y >= texture.height) | |
{ | |
return; | |
} | |
apply( x, y, color ); | |
} | |
public void apply( int offset, int color ) | |
{ | |
final int[] pixels = texture.pixels; | |
pixels[offset] = blend.blend( pixels[offset], color ); | |
} | |
public int get( int x, int y ) | |
{ | |
return texture.get( x, y ); | |
} | |
public int width() | |
{ | |
return texture.width; | |
} | |
public int height() | |
{ | |
return texture.height; | |
} | |
public int getOffset(int x, int y) | |
{ | |
return texture.getOffset( x, y ); | |
} | |
public void flush( Graphics gr ) | |
{ | |
renderBufferSource.newPixels(); | |
gr.drawImage( renderBuffer, 0, 0, null ); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LineDraw | |
{ | |
public Gfx g; | |
public LineDraw( Gfx g ) | |
{ | |
this.g = g; | |
} | |
public void smooth( int x0, int y0, int x1, int y1, int color ) | |
{ | |
int temp = 0; | |
if (y0 > y1) | |
{ | |
temp = y1; y1 = y0; y0 = temp; | |
temp = x1; x1 = x0; x0 = temp; | |
} | |
int dx = x1 - x0; | |
int dy = y1 - y0; | |
if (dx == 0) | |
{ | |
vertical( x0, y0, y1, color ); | |
} | |
else if (dy == 0) | |
{ | |
horizontal( y0, x0, x1, color ); | |
} | |
else | |
{ | |
g.applyCheck( x0, y0, color ); | |
g.applyCheck( x1, y1, color ); | |
int sx = Integer.signum( dx ); | |
dx = StrictMath.abs( dx ); | |
dy = StrictMath.abs( dy ); | |
if (dy > dx) | |
{ | |
char errorAdj = (char)((dx << 16) / dy); | |
char errorAccTemp = 0; | |
char errorAcc = 0; | |
int weighting = 0; | |
while (--dy > 0) | |
{ | |
errorAccTemp = errorAcc; | |
errorAcc += errorAdj; | |
if (errorAcc <= errorAccTemp) | |
{ | |
x0 += sx; | |
} | |
y0++; | |
weighting = (errorAcc >> 8); | |
g.applyCheck( x0, y0, Color.mulAlpha( color, weighting ^ 0xFF ) ); | |
g.applyCheck( x0 + sx, y0, Color.mulAlpha( color, weighting ) ); | |
} | |
} | |
else | |
{ | |
char errorAdj = (char)((dy << 16) / dx); | |
char errorAccTemp = 0; | |
char errorAcc = 0; | |
int weighting = 0; | |
while (--dx > 0) | |
{ | |
errorAccTemp = errorAcc; | |
errorAcc += errorAdj; | |
if (errorAcc <= errorAccTemp) | |
{ | |
y0++; | |
} | |
x0 += sx; | |
weighting = (errorAcc >> 8); | |
g.applyCheck( x0, y0, Color.mulAlpha( color, weighting ^ 0xFF ) ); | |
g.applyCheck( x0, y0 + 1, Color.mulAlpha( color, weighting ) ); | |
} | |
} | |
} | |
} | |
public void fast( int x0, int y0, int x1, int y1, int color ) | |
{ | |
// TODO clipping | |
int dx = x1 - x0; | |
int dy = y1 - y0; | |
int adx = StrictMath.abs( dx ); | |
int ady = StrictMath.abs( dy ); | |
int sx = Integer.signum( dx ); | |
int sy = Integer.signum( dy ); | |
int err = adx - ady; | |
int e2 = 0; | |
for (;;) | |
{ | |
g.apply( x0, y0, color ); | |
if (x0 == x1 && y0 == y1) | |
{ | |
break; | |
} | |
e2 = 2 * err; | |
if (e2 > -ady) | |
{ | |
err -= ady; | |
x0 += sx; | |
} | |
if (e2 < adx) | |
{ | |
err += adx; | |
y0 += sy; | |
} | |
} | |
} | |
public void horizontal( int y, int x0, int x1, int color ) | |
{ | |
final int bw = g.width(); | |
final int bh = g.height(); | |
if (y < 0 || y >= bh || (x0 < 0 && x1 < 0) || (x0 > bw && x1 > bw)) | |
{ | |
return; | |
} | |
x0 = (x0 < 0 ? 0 : (x0 > bw ? bw : x0)); | |
x1 = (x1 < 0 ? 0 : (x1 > bw ? bw : x1)); | |
int d = x1 - x0; | |
int ad = StrictMath.abs( d ); | |
int s = Integer.signum( d ); | |
int offset = g.getOffset( x0, y ); | |
while (--ad > 0) | |
{ | |
g.apply( offset, color ); | |
offset += s; | |
} | |
} | |
public void vertical( int x, int y0, int y1, int color ) | |
{ | |
final int bw = g.width(); | |
final int bh = g.height(); | |
if (x < 0 || x >= bw || (y0 < 0 && y1 < 0) || (y0 > bh && y1 > bh)) | |
{ | |
return; | |
} | |
y0 = (y0 < 0 ? 0 : (y0 > bh ? bh : y0)); | |
y1 = (y1 < 0 ? 0 : (y1 > bh ? bh : y1)); | |
int d = y1 - y0; | |
int ad = StrictMath.abs( d ); | |
int s = Integer.signum( d ) * g.width(); | |
int offset = g.getOffset( x, y0 ); | |
while (--ad > 0) | |
{ | |
g.apply( offset, color ); | |
offset += s; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class RectDraw | |
{ | |
public Gfx g; | |
public RectDraw( Gfx g ) | |
{ | |
this.g = g; | |
} | |
public void fill( int x, int y, int w, int h, int color ) | |
{ | |
final int backWidth = g.width(); | |
final int backHeight = g.height(); | |
if (x < 0) x = 0; | |
if (y < 0) y = 0; | |
if (x + w > backWidth) w = backWidth - x; | |
if (y + h > backHeight) h = backHeight - y; | |
final int stride = backWidth - w; | |
int offset = g.getOffset( x, y ); | |
for (int yy = 0; yy < h; yy++) | |
{ | |
for (int xx = 0; xx < w; xx++) | |
{ | |
g.apply( offset++, color ); | |
} | |
offset += stride; | |
} | |
} | |
public void outline( int x, int y, int w, int h, int color ) | |
{ | |
// TODO clipping | |
final int stride = g.width(); | |
int toffset = g.getOffset( x, y ); | |
int boffset = g.getOffset( x, y + h - 1 ); | |
for (int i = 0; i < w; i++ ) | |
{ | |
g.apply( toffset++, color ); | |
g.apply( boffset++, color ); | |
} | |
int loffset = g.getOffset( x, y + 1 ); | |
int roffset = g.getOffset( x + w - 1, y + 1 ); | |
for (int i = 2; i < h; i++ ) | |
{ | |
g.apply( loffset, color ); | |
g.apply( roffset, color ); | |
loffset += stride; | |
roffset += stride; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Test | |
{ | |
public static void main(String[] args) throws Exception | |
{ | |
final Gfx gfx = new Gfx( 480, 320 ); | |
final LineDraw line = new LineDraw( gfx ); | |
final RectDraw rect = new RectDraw( gfx ); | |
final TileDraw tile = new TileDraw( gfx ); | |
final CircleDraw circle = new CircleDraw( gfx ); | |
final Texture texture = TextureLoader.fromUrl( "http://cdn1.iconfinder.com/data/icons/musthave/256/Cancel.png" ); | |
final TimeTracker tracker = new TimeTracker( "Gfx FPS: %.1f", 500, TimeUnit.MILLISECONDS ); | |
final JPanel panel = new JPanel(); | |
panel.setPreferredSize( new Dimension( gfx.width(), gfx.height() ) ); | |
final JFrame window = new JFrame(); | |
window.setResizable( false ); | |
window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); | |
window.setVisible( true ); | |
window.add( panel ); | |
window.pack(); | |
final Thread thread = new Thread() { | |
public void run() { | |
Graphics gr = null; | |
tracker.reset(); | |
for(;;) | |
{ | |
if (gr == null) | |
{ | |
gr = panel.getGraphics(); | |
} | |
if (gr != null ) | |
{ | |
gfx.clear( Colors.Black ); | |
gfx.blend = Blend.Alpha; | |
tile.draw( texture.tile(), 20, 20/*, Color.lighten( Colors.Red, 128 )*/ ); // halfway between red and white | |
gfx.blend = Blend.Additive; | |
rect.fill( 50, 50, 100, 100, Color.withAlpha( Colors.Blue, 128 ) ); | |
rect.fill( 125, 100, 100, 100, Color.withAlpha( Colors.Green, 128 ) ); | |
rect.fill( 100, 50, 100, 100, Color.withAlpha( Colors.Red, 128 ) ); | |
gfx.blend = Blend.Alpha; | |
line.fast( 5, 5, 50, 100, Colors.White ); | |
line.smooth( 10, 5, 55, 100, Colors.White ); | |
rect.outline( 75, 75, 100, 100, Color.withAlpha( Colors.White, 128 ) ); | |
gfx.blend = Blend.Alpha; | |
circle.fillSmooth( 400, 100, 50, Color.withAlpha( Colors.White, 128 ) ); | |
gfx.flush( gr ); | |
} | |
if (tracker.update()) | |
{ | |
window.setTitle( tracker.rateString ); | |
} | |
} | |
} | |
}; | |
thread.start(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Texture | |
{ | |
public int[] pixels; | |
public int width; | |
public int height; | |
public Texture( int w, int h ) | |
{ | |
resize( w, h ); | |
} | |
public Texture (int w, int h, int[] pixels) | |
{ | |
this.width = w; | |
this.height = h; | |
this.pixels = pixels; | |
} | |
public void resize( int w, int h ) | |
{ | |
width = w; | |
height = h; | |
pixels = new int[w * h]; | |
} | |
public void clear( int color ) | |
{ | |
for (int i = 0; i < pixels.length; i++) | |
{ | |
pixels[i] = color; | |
} | |
} | |
public int get( int x, int y ) | |
{ | |
return pixels[getOffset( x, y )]; | |
} | |
public void set( int x, int y, int color ) | |
{ | |
pixels[getOffset( x, y )] = color; | |
} | |
public Tile tile( int x, int y, int w, int h ) | |
{ | |
return new Tile( this, x, y, w, h ); | |
} | |
public Tile tile() | |
{ | |
return new Tile( this ); | |
} | |
public int getOffset( int x, int y ) | |
{ | |
return y * width + x; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class TextureLoader | |
{ | |
public static Texture fromStream( InputStream in, boolean close ) throws IOException | |
{ | |
try | |
{ | |
BufferedImage image = ImageIO.read( in ); | |
Texture texture = new Texture( image.getWidth(), image.getHeight() ); | |
int[] pixels = texture.pixels; | |
int offset = 0; | |
for (int y = 0; y < texture.height; y++) | |
{ | |
for (int x = 0; x < texture.width; x++) | |
{ | |
pixels[offset++] = image.getRGB( x, y ); | |
} | |
} | |
return texture; | |
} | |
finally | |
{ | |
if (close) | |
{ | |
in.close(); | |
} | |
} | |
} | |
public static Texture fromUrl( String url ) throws MalformedURLException, IOException | |
{ | |
return fromStream( new URL( url ).openStream(), true ); | |
} | |
public static Texture fromClasspath(String name) throws IOException | |
{ | |
return fromStream( TextureLoader.class.getResourceAsStream( name ), true ); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Tile | |
{ | |
public Texture texture; | |
public int l, t, r, b; | |
public Tile() | |
{ | |
} | |
public Tile( Texture texture ) | |
{ | |
set( texture, 0, 0, texture.width, texture.height ); | |
} | |
public Tile( Texture texture, int x, int y, int w, int h ) | |
{ | |
set( texture, x, y, x + w, y + h ); | |
} | |
public void set( Texture texture, int l, int t, int r, int b ) | |
{ | |
this.texture = texture; | |
this.l = l; | |
this.t = t; | |
this.r = r; | |
this.b = b; | |
} | |
public int width() | |
{ | |
return (r - l); | |
} | |
public int height() | |
{ | |
return (b - t); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class TileDraw | |
{ | |
public Gfx g; | |
public TileDraw(Gfx g) | |
{ | |
this.g = g; | |
} | |
public void draw(Tile tile, int x, int y) | |
{ | |
int bw = g.width(); | |
int bh = g.height(); | |
int tl = tile.l; | |
int tt = tile.t; | |
int tr = tile.r; | |
int rl = x; | |
int rt = y; | |
int rr = x + tile.width(); | |
int rb = y + tile.height(); | |
if (rl < 0) | |
{ | |
tl -= rl; | |
rl = 0; | |
} | |
if (rt < 0) | |
{ | |
tt -= rt; | |
rt = 0; | |
} | |
if (rr > bw) | |
{ | |
tr -= rr - bw; | |
rr = bw; | |
} | |
if (rb > bh) | |
{ | |
rb = bh; | |
} | |
final Texture tex = tile.texture; | |
final int[] src = tex.pixels; | |
final int tstride = tile.width() - (tr - tl); | |
final int rstride = bw - (rr - rl); | |
int toffset = tex.getOffset( tl, tt ); | |
int roffset = g.getOffset( rl, rt ); | |
for (y = rt; y < rb; y++) | |
{ | |
for (x = rl; x < rr; x++) | |
{ | |
g.apply( roffset++, src[toffset++] ); | |
} | |
toffset += tstride; | |
roffset += rstride; | |
} | |
} | |
public void draw(Tile tile, int x, int y, int color) | |
{ | |
int bw = g.width(); | |
int bh = g.height(); | |
int tl = tile.l; | |
int tt = tile.t; | |
int tr = tile.r; | |
int rl = x; | |
int rt = y; | |
int rr = x + tile.width(); | |
int rb = y + tile.height(); | |
if (rl < 0) | |
{ | |
tl -= rl; | |
rl = 0; | |
} | |
if (rt < 0) | |
{ | |
tt -= rt; | |
rt = 0; | |
} | |
if (rr > bw) | |
{ | |
tr -= rr - bw; | |
rr = bw; | |
} | |
if (rb > bh) | |
{ | |
rb = bh; | |
} | |
final Texture tex = tile.texture; | |
final int[] src = tex.pixels; | |
final int tstride = tile.width() - (tr - tl); | |
final int rstride = bw - (rr - rl); | |
int toffset = tex.getOffset( tl, tt ); | |
int roffset = g.getOffset( rl, rt ); | |
for (y = rt; y < rb; y++) | |
{ | |
for (x = rl; x < rr; x++) | |
{ | |
g.apply( roffset++, Color.mul( color, src[toffset++] ) ); | |
} | |
toffset += tstride; | |
roffset += rstride; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class TimeTracker | |
{ | |
public long lastTime; | |
public long time; | |
public long interval; | |
public long frames; | |
public double rate; | |
public String rateFormat; | |
public String rateString; | |
public char[] rateText; | |
public TimeTracker( String rateStringFormat, long refreshInterval, TimeUnit unit ) | |
{ | |
rateFormat = rateStringFormat; | |
setRefreshInterval( refreshInterval, unit ); | |
} | |
public void setRefreshInterval( long refreshInterval, TimeUnit unit ) | |
{ | |
interval = unit.toNanos( refreshInterval ); | |
} | |
public void reset() | |
{ | |
lastTime = System.nanoTime(); | |
time = 0; | |
frames = 0; | |
rate = 0; | |
updateRateString(); | |
} | |
public boolean update() | |
{ | |
boolean updated = false; | |
long currentTime = System.nanoTime(); | |
long elapsed = (currentTime - lastTime); | |
time += elapsed; | |
frames++; | |
if ( time >= interval ) | |
{ | |
rate = (frames / (time * 0.000000001)); | |
updateRateString(); | |
time -= interval; | |
frames = 0; | |
updated = true; | |
} | |
lastTime = currentTime; | |
return updated; | |
} | |
private void updateRateString() | |
{ | |
rateString = String.format( rateFormat, rate); | |
rateText = rateString.toCharArray(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment