PixelEngine/src/sig/engine/Panel.java
sigonasr2 9fe872b7c2 Corrected multiplication error in scalex
Co-authored-by: sigonasr2 <sigonasr2@gmail.com>
2022-12-02 13:36:17 -06:00

1042 lines
36 KiB
Java

package sig.engine;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.ColorModel;
import java.awt.image.MemoryImageSource;
import java.util.ArrayList;
import java.util.List;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentAdapter;
import java.awt.GraphicsEnvironment;
import java.awt.GraphicsConfiguration;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.MouseInputListener;
import java.awt.event.KeyListener;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Cursor;
import java.awt.Dimension;
import sig.JavaProjectTemplate;
public class Panel extends JPanel implements Runnable,KeyListener {
JFrame window;
static JavaProjectTemplate gameInstance;
public int pixel[];
final int CIRCLE_PRECISION=32;
final int OUTLINE_COL=Color.BRIGHT_WHITE.getColor();
private Thread thread;
private Image imageBuffer;
private MemoryImageSource mImageProducer;
private ColorModel cm;
int scanLine=0;
int nextScanLine=0;
double x_offset=0;
double y_offset=0;
int frameCount=0;
long lastSecond=0;
boolean resizing=false;
long lastUpdate=System.nanoTime();
final long TARGET_FRAMETIME = 8333333l;
public double nanaX = 0;
public double nanaY = 0;
public int button = 0;
public static final int UPDATE_LOOP_FRAMERATE = 244;
public static final long UPDATE_LOOP_NANOTIME = (long)((1d/UPDATE_LOOP_FRAMERATE)*1000000000l);
public static final double UPDATE_MULT = 1d / UPDATE_LOOP_FRAMERATE;
public static RenderingHints RENDERHINTS = new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
static long lastReportedTime = System.currentTimeMillis();
public static long TIME = 0;
public static long scaleTime;
public static Panel p;
public static JFrame f;
int ACTUAL_WINDOW_WIDTH=JavaProjectTemplate.WINDOW_WIDTH,ACTUAL_WINDOW_HEIGHT=JavaProjectTemplate.WINDOW_HEIGHT;
Point<Integer> vViewSize = new Point<Integer>(1,1);
Point<Integer> vViewPos = new Point<Integer>(1,1);
java.awt.Color borderCol = java.awt.Color.BLACK;
static Cursor currentCursor = new Cursor(Cursor.DEFAULT_CURSOR);
// Recursive function to return gcd of a and b in single line
static int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
static void setupNonPixelCohesion(){
Dimension d = p.getSize();
int ww = JavaProjectTemplate.WINDOW_WIDTH;
int wh = JavaProjectTemplate.WINDOW_HEIGHT;
double wasp = (double)ww/(double)wh;
p.vViewSize.x=(int)d.getWidth();
p.vViewSize.y=(int)((double)p.vViewSize.x/wasp);
if (p.vViewSize.y>d.getHeight()) {
p.vViewSize.y=(int)d.getHeight();
p.vViewSize.x=(int)((double)p.vViewSize.y*wasp);
}
}
public static void InitializeEngine(JavaProjectTemplate instance){
System.setProperty("sun.java2d.transaccel", "True");
System.setProperty("sun.java2d.d3d", "True");
System.setProperty("sun.java2d.ddforcevram", "True");
System.setProperty("sun.java2d.xrender", "True");
gameInstance=instance;
RENDERHINTS.put(RenderingHints.KEY_COLOR_RENDERING,RenderingHints.VALUE_COLOR_RENDER_SPEED);
RENDERHINTS.put(RenderingHints.KEY_DITHERING,RenderingHints.VALUE_DITHER_DISABLE);
RENDERHINTS.put(RenderingHints.KEY_FRACTIONALMETRICS,RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
RENDERHINTS.put(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_SPEED);
f = new JFrame(JavaProjectTemplate.PROGRAM_NAME);
f.setResizable(true);
f.setSize(JavaProjectTemplate.WINDOW_WIDTH,JavaProjectTemplate.WINDOW_HEIGHT);
f.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
p.ACTUAL_WINDOW_WIDTH=JavaProjectTemplate.WINDOW_WIDTH;
p.ACTUAL_WINDOW_HEIGHT=JavaProjectTemplate.WINDOW_HEIGHT;
int bw = p.ACTUAL_WINDOW_WIDTH;
int bh = p.ACTUAL_WINDOW_HEIGHT;
Dimension d = p.getSize();
int xmult = (int)d.getWidth()/bw;
if (xmult!=0) {
p.vViewSize.x=(int)(d.getWidth()/bw)*bw;
p.vViewSize.y=bh*xmult;
if (d.getWidth()/bw>d.getHeight()/bh){
int ymult=(int)d.getHeight()/bh;
if (ymult==0) {
setupNonPixelCohesion();
} else {
p.vViewSize.y=(int)(d.getHeight()/bh)*bh;
p.vViewSize.x=bw*ymult;
}
}
} else {
setupNonPixelCohesion();
}
p.vViewPos.set((int)((d.getWidth()-p.vViewSize.x)/2),(int)((d.getHeight()-p.vViewSize.y)/2));
p.init(false);
}
});
p = new Panel(f);
JavaProjectTemplate.game=p;
p.init(true);
f.add(p);
f.addKeyListener(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
p.render();
gameInstance.initializeGame();
long lastGameTime = System.nanoTime();
long dt = 0;
while (true) {
dt += System.nanoTime() - lastGameTime;
lastGameTime = System.nanoTime();
while (dt >= UPDATE_LOOP_NANOTIME) {
gameInstance.updateGame(UPDATE_LOOP_NANOTIME/1000000000d);
Mouse.pressMap.clear();
Mouse.releaseMap.clear();
Key.KEYS_PRESS.clear();
Key.KEYS_RELEASE.clear();
Mouse.mouseWheel=MouseScrollValue.NONE;
dt -= UPDATE_LOOP_NANOTIME;
TIME += UPDATE_LOOP_NANOTIME;
}
gameUpdateLoopStabilizer(dt); //This is hackish. Removing this slows down the game by about 30%. The timer runs slower. ???
}
}
private static void gameUpdateLoopStabilizer(long dt) {
if (dt < UPDATE_LOOP_NANOTIME) {
lastReportedTime = System.currentTimeMillis();
} else {
if (System.currentTimeMillis() - lastReportedTime > 5000) {
System.out.println("WARNING! Game is lagging behind! Frames Behind: " + (dt / UPDATE_LOOP_NANOTIME));
lastReportedTime = System.currentTimeMillis();
}
}
try {
Thread.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public Panel(JFrame f) {
super(true);
this.window=f;
thread = new Thread(this, "MyPanel Thread");
this.addMouseListener(new MouseInputListener(){
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
Mouse.clickMap.put(e.getButton(),true);
Mouse.pressMap.put(e.getButton(),true);
}
@Override
public void mouseReleased(MouseEvent e) {
Mouse.clickMap.put(e.getButton(),false);
Mouse.releaseMap.put(e.getButton(),true);
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
});
this.addMouseMotionListener(new MouseMotionListener(){
@Override
public void mouseDragged(MouseEvent e) {
Mouse.x=e.getX();
Mouse.y=e.getY();
Mouse.mousePosition.set(e.getX(),e.getY());
}
@Override
public void mouseMoved(MouseEvent e) {
Mouse.x=e.getX();
Mouse.y=e.getY();
Mouse.mousePosition.set(e.getX(),e.getY());
}
});
this.addMouseWheelListener(new MouseWheelListener(){
//-1 is UP, 1 is DOWN
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
Mouse.mouseWheel=MouseScrollValue.getValue(e.getWheelRotation());
}
});
}
/**
* Get Best Color model available for current screen.
* @return color model
*/
protected static ColorModel getCompatibleColorModel(){
GraphicsConfiguration gfx_config = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
return gfx_config.getColorModel();
}
/**
* Call it after been visible and after resizes.
*/
public void init(boolean runThread){
cm = getCompatibleColorModel();
int screenSize = ACTUAL_WINDOW_WIDTH * ACTUAL_WINDOW_HEIGHT;
if(pixel == null || pixel.length < screenSize){
pixel = new int[screenSize];
}
mImageProducer = new MemoryImageSource(ACTUAL_WINDOW_WIDTH, ACTUAL_WINDOW_HEIGHT, cm, pixel,0, ACTUAL_WINDOW_WIDTH);
mImageProducer.setAnimated(true);
mImageProducer.setFullBufferUpdates(true);
if (p.ACTUAL_WINDOW_WIDTH!=0&&p.ACTUAL_WINDOW_HEIGHT!=0) {
imageBuffer = Toolkit.getDefaultToolkit().createImage(mImageProducer).getScaledInstance(ACTUAL_WINDOW_WIDTH,ACTUAL_WINDOW_HEIGHT,Image.SCALE_FAST);
}
if(runThread && thread.isInterrupted() || !thread.isAlive()){
thread.start();
}
}
public void SetBorderColor(Color col){
borderCol=new java.awt.Color(col.r,col.g,col.b);
}
@Override
public void paintComponent(Graphics g) {
// perform draws on pixels
long startTime = System.currentTimeMillis();
g.setColor(borderCol);
g.fillRect(0,0,getWidth(),getHeight());
g.drawImage(imageBuffer,vViewPos.x,vViewPos.y,vViewSize.x+vViewPos.x,vViewSize.y+vViewPos.y,0,0,ACTUAL_WINDOW_WIDTH,ACTUAL_WINDOW_HEIGHT,this);
scaleTime=System.currentTimeMillis()-startTime;
}
/**
* Overrides ImageObserver.imageUpdate.
* Always return true, assuming that imageBuffer is ready to go when called
*/
@Override
public boolean imageUpdate(Image image, int a, int b, int c, int d, int e) {
return true;
}
/**
* Do your draws in here !!
* pixel is your canvas!
*/
public boolean Cursor_IsVisible(){
return !f.getCursor().getName().equals("INVISIBLE");
}
public void Cursor_Hide(){
f.setCursor(Toolkit.getDefaultToolkit().createCustomCursor(Toolkit.getDefaultToolkit().getImage(""), new java.awt.Point(0,0), "INVISIBLE"));
}
public void Cursor_Show(){
f.setCursor(currentCursor);
}
public void Cursor_SetCursor(Sprite cursorimg,int x_origin,int y_origin){
currentCursor=Toolkit.getDefaultToolkit().createCustomCursor(cursorimg.img, new java.awt.Point(x_origin,y_origin), "CUSTOM");
f.setCursor(currentCursor);
}
public void Cursor_ResetCursor(){
f.setCursor(Cursor.getDefaultCursor());
}
public /* abstract */ void render(){
gameInstance.drawGame();
}
public void FillCircle(double center_x,double center_y,double r,Color col) {
List<Point<Double>> points = new ArrayList<Point<Double>>();
points.add(new Point<Double>(center_x,center_y));
for (double theta=0;theta<Math.PI*2;theta+=((Math.PI*2)/CIRCLE_PRECISION)) {
//System.out.println("Loop "+counter+++". Theta:"+theta);
//System.out.println("X:"+(Math.sin(theta)*r+center_x)+" Y:"+(Math.cos(theta)*r+center_y));
points.add(new Point<Double>((double)(Math.round(Math.sin(theta)*r+center_x)),(double)(Math.round(Math.cos(theta)*r+center_y))));
}
points.add(points.get(1));
FillPolygon(0d,0d,col,points,PolygonStructure.FAN);
}
public void FillOval(double center_x,double center_y,double w,double h,Color col) {
List<Point<Double>> points = new ArrayList<Point<Double>>();
double r = Math.max(w,h);
double ratio = Math.min(w,h)/r;
points.add(new Point<Double>(center_x,center_y));
for (double theta=0;theta<Math.PI*2;theta+=((Math.PI*2)/CIRCLE_PRECISION)) {
Point<Double> newP = new Point<Double>((double)(Math.round(Math.sin(theta)*r)),(double)(Math.round(Math.cos(theta)*r)));
if (w<h) {
newP.x=(double)Math.round(newP.x*ratio);
} else {
newP.y=(double)Math.round(newP.y*ratio);
}
newP.x+=center_x;
newP.y+=center_y;
points.add(newP);
}
points.add(points.get(1));
FillPolygon(0d,0d,col,points,PolygonStructure.FAN);
}
public void FillPolygon(double x_offset,double y_offset,Color col,List<Point<Double>>points,PolygonStructure structure) {
List<Point<Double>> tex = new ArrayList<>();
List<Color> cols = new ArrayList<>();
for (int i=0;i<points.size();i++) {
tex.add(new Point<Double>((double)0,(double)0));
cols.add(col);
}
FillTexturedPolygon(points,tex,cols,null,structure);
}
public void Draw(int x, int y, Color col) {
if (x<0||y<0||x>=ACTUAL_WINDOW_WIDTH||y>=ACTUAL_WINDOW_HEIGHT) return;
Draw(y*ACTUAL_WINDOW_WIDTH+x,col.getColor());
}
void Draw(int index, int col) {
if (((col>>>24)&0xff)==0) return;
if (((col>>>24)&0xff)!=255) {
pixel[index]=ColorLerpNoAlpha(new Color(pixel[index]),new Color(col),((col>>>24)&0xff)/255f).getColor();
} else {
pixel[index]=col;
}
}
@Override
public void run() {
while (true) {
// request a JPanel re-drawing
render();
mImageProducer.newPixels();
if (f!=null) {
Graphics2D g2 = (Graphics2D)f.getGraphics();
if (g2!=null) {
g2.setRenderingHints(RENDERHINTS);
try {
repaint();
} finally {
g2.dispose();
}
}
}
updateFPSCounter();
//System.out.println("Repaint "+frameCount++);
waitForNextFrame();
}
}
private void waitForNextFrame() {
long newTime = System.nanoTime();
if (newTime-lastUpdate<TARGET_FRAMETIME) {
long timeRemaining=TARGET_FRAMETIME-(newTime-lastUpdate);
long millis = timeRemaining/1000000l;
int nanos = (int)(timeRemaining-millis*1000000l);
//System.out.println(timeRemaining+"/"+millis+" Nanos:"+nanos);
try {
Thread.sleep(millis,nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lastUpdate=newTime;
}
private void updateFPSCounter() {
if (window!=null&&System.currentTimeMillis()-lastSecond>=1000) {
window.setTitle(JavaProjectTemplate.PROGRAM_NAME+" - FPS: "+(frameCount));
frameCount=0;
lastSecond=System.currentTimeMillis();
}
frameCount++;
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
if (!Key.isHeld(e.getKeyCode())) {
Key.setKeyHeld(e.getKeyCode(), true);
Key.KEYS_PRESS.put(e.getKeyCode(),true);
}
//System.out.println("Key List: "+KEYS);
}
@Override
public void keyReleased(KeyEvent e) {
Key.setKeyHeld(e.getKeyCode(), false);
Key.KEYS_RELEASE.put(e.getKeyCode(),true);
//System.out.println("Key List: "+KEYS);
}
public void Draw_Text(double x, double y, java.lang.String s, Font f){
Draw_Text(x,y,new String(s),f);
}
public void Draw_Text_Ext(double x, double y, java.lang.String s, Font f, Color col, int scalex, int scaley){
Draw_Text_Ext(x,y,new String(s),f,col,scalex,scaley);
}
private void Draw_Text(double x, double y, String s, Font f) {
Draw_Text_Ext(x,y,s,f,Color.BLACK,1,1);
}
private void Draw_Text_Ext(double x, double y, String s, Font f, Color col, int scalex, int scaley) {
java.lang.String finalS = s.toString();
int charCount=0;
int yOffset=0;
int xOffset=0;
Color currentCol = col;
for (int i=0;i<finalS.length();i++) {
if (finalS.charAt(i)=='\n') {
xOffset+=(charCount+1)*f.getGlyphWidth()*scalex;
yOffset+=f.getGlyphHeight()*scaley;
charCount=0;
} else {
Draw_Sprite_Partial_Ext(x+(i*f.getGlyphWidth())*scalex-xOffset, y+yOffset, f.getCharInfo(finalS.charAt(i)).getX(), f.getCharInfo(finalS.charAt(i)).getY(), f.getCharInfo(finalS.charAt(i)).getWidth(), f.getCharInfo(finalS.charAt(i)).getHeight(), f.getSprite(), currentCol,Transform.NONE,scalex,scaley);
charCount++;
}
}
}
public void Draw_Line(int x1,int y1,int x2,int y2,Color col) {
int x,y,dx,dy,dx1,dy1,px,py,xe,ye;
dx=x2-x1;dy=y2-y1;
dx1=Math.abs(dx);dy1=Math.abs(dy);
px=2*dy1-dx1;py=2*dx1-dy1;
if (dy1<=dx1) {
if (dx>=0) {
x=x1;y=y1;xe=x2-1;
} else {
x=x2-1;y=y2-1;xe=x1;
}
while (x<xe) {
x=x+1;
if (px<0) {
px=px+2*dy1;
} else {
if ((dx<0&&dy<0)||(dx>0&&dy>0)) {
y=y+1;
} else {
y=y-1;
}
px=px+2*(dy1-dx1);
}
Draw(y*ACTUAL_WINDOW_WIDTH+x,col.getColor());
}
} else {
if (dy>=0) {
x=x1;y=y1;ye=y2-1;
} else {
x=x2-1;y=y2-1;ye=y1;
}
Draw(y*ACTUAL_WINDOW_WIDTH+x,col.getColor());
while (y<ye) {
y=y+1;
if (py<=0) {
py=py+2*dx1;
} else {
if ((dx<0&&dy<0)||(dx>0&&dy>0)) {
x=x+1;
} else {
x=x-1;
}
py=py+2*(dx1-dy1);
}
Draw(y*ACTUAL_WINDOW_WIDTH+x,col.getColor());
}
}
}
public void Fill_Rect(double x,double y,double w,double h,Color col) {
for (int xx=0;xx<w;xx++) {
for (int yy=0;yy<h;yy++) {
if (x+xx>=0&&y+yy>=0&&x+xx<ACTUAL_WINDOW_WIDTH&&y+yy<ACTUAL_WINDOW_HEIGHT) {
int index = ((int)y+yy)*ACTUAL_WINDOW_WIDTH+(int)x+xx;
Draw(index,col.getColor());
}
}
}
}
public void Draw_Sprite(double x, double y, Sprite sprite){
Draw_Sprite_Partial(x,y,0,0,sprite.getWidth(),sprite.getHeight(),sprite,Color.WHITE,Transform.NONE);
}
public void Draw_Animated_Sprite(double x, double y, AnimatedSprite sprite, double frameIndex){
Rectangle frameRectangle=sprite.getFrame((int)frameIndex);
Draw_Sprite_Partial(x,y,frameRectangle.getX(),frameRectangle.getY(),frameRectangle.getWidth(),frameRectangle.getHeight(),sprite,Color.WHITE,Transform.NONE);
}
public void Draw_Sprite(double x, double y, Sprite sprite, Color col){
Draw_Sprite_Partial(x,y,0,0,sprite.getWidth(),sprite.getHeight(),sprite,col,Transform.NONE);
}
public void Draw_Sprite(double x, double y, Sprite sprite, Transform transform){
Draw_Sprite_Partial(x,y,0,0,sprite.getWidth(),sprite.getHeight(),sprite,Color.WHITE,transform);
}
public void Draw_Sprite(double x, double y, Sprite sprite, Color col, Transform transform, int scalex, int scaley){
Draw_Sprite_Partial_Ext(x,y,0,0,sprite.getWidth(),sprite.getHeight(),sprite,col,transform,scalex,scaley);
}
public void Draw_Animated_Sprite(double x, double y, AnimatedSprite sprite, double frameIndex, Color col, Transform transform, int scalex, int scaley){
Rectangle frameRectangle=sprite.getFrame((int)frameIndex);
Draw_Sprite_Partial_Ext(x,y,frameRectangle.getX(),frameRectangle.getY(),frameRectangle.getWidth(),frameRectangle.getHeight(),sprite,col,transform,scalex,scaley);
}
public void Draw_Sprite_Partial(double x, double y, double xOffset, double yOffset, double w, double h, Sprite sprite, Color col, Transform transform){
Draw_Sprite_Partial_Ext(x,y,xOffset,yOffset,w,h,sprite,col,transform,1,1);
}
public void Draw_Animated_Sprite_Partial_Ext(double x, double y, double xOffset, double yOffset, double w, double h, AnimatedSprite sprite, double frameIndex, Color col, Transform transform, int scalex, int scaley){
Rectangle frameRectangle=sprite.getFrame((int)frameIndex);
Draw_Sprite_Partial_Ext(x, y, frameRectangle.getX(), frameRectangle.getY(), frameRectangle.getWidth(), frameRectangle.getHeight(), sprite, col, transform, scalex, scaley);
}
public void Draw_Sprite_Partial_Ext(double x, double y, double xOffset, double yOffset, double w, double h, Sprite sprite, Color col, Transform transform, int scalex, int scaley){
boolean horizontal = transform==Transform.HORIZONTAL||transform==Transform.HORIZ_VERTIC;
boolean vertical = transform==Transform.VERTICAL||transform==Transform.HORIZ_VERTIC;
for(int X=(int)xOffset;X<(int)(w+xOffset);X++){
for(int Y=(int)yOffset;Y<(int)(h+yOffset);Y++){
if (x+(X-xOffset)*scalex<0||y+(Y-yOffset)*scaley<0||(X-xOffset)*scalex+x>=ACTUAL_WINDOW_WIDTH||(Y-yOffset)*scaley+y>=ACTUAL_WINDOW_HEIGHT) {
continue;
} else {
int index =
((vertical?
sprite.getHeight()-(Y-(int)yOffset):
(Y-(int)yOffset))*scaley
+(int)y)*ACTUAL_WINDOW_WIDTH+
(horizontal?
sprite.getWidth()-(X-(int)xOffset):
(X-(int)xOffset))*scalex
+(int)x;
if (((sprite.getImg().getRGB(X,Y)>>>24)&0xFF)==0||index<0||index>=pixel.length) {
continue;
} else {
if (col==Color.WHITE) {
for (int XX=0;XX<scalex;XX++) {
int newindex=index;
newindex+=XX; //Everytime we reset we move the X marker over.
for (int YY=0;YY<scaley;YY++) {
newindex+=ACTUAL_WINDOW_WIDTH; //The Y marker only moves one pixel down at a time, because we do not reset it.
Draw(newindex,sprite.getImg().getRGB(X,Y));
}
}
} else {
Color img = new Color(sprite.getImg().getRGB(X,Y));
for (int XX=0;XX<scalex;XX++) {
int newindex=index;
newindex+=XX; //Everytime we reset we move the X marker over.
for (int YY=0;YY<scaley;YY++) {
newindex+=ACTUAL_WINDOW_WIDTH; //The Y marker only moves one pixel down at a time, because we do not reset it.
Draw(newindex,new Color(
(int)Math.min(255.0f,Math.max(0,(float)img.r * col.r / 255.0f)),
(int)Math.min(255.0f,Math.max(0,(float)img.g * col.g / 255.0f)),
(int)Math.min(255.0f,Math.max(0,(float)img.b * col.b / 255.0f)),
(int)Math.min(255.0f,Math.max(0,(float)img.a * col.a / 255.0f))).getColor()
);
}
}
}
}
}
}
}
}
public void Draw_Rect(double x,double y, double w, double h, Color col) {
Draw_Line((int)x, (int)y, (int)x+(int)w, (int)y, col);
Draw_Line((int)x+(int)w, (int)y, (int)x+(int)w, (int)y+(int)h, col);
Draw_Line((int)x+(int)w, (int)y+(int)h, (int)x, (int)y+(int)h, col);
Draw_Line((int)x, (int)y+(int)h, (int)x, (int)y, col);
}
public void Draw_Triangle(double x1, double y1, double x2, double y2, double x3, double y3, Color col) {
Draw_Line((int)x1, (int)y1, (int)x2, (int)y2, col);
Draw_Line((int)x2, (int)y2, (int)x3, (int)y3, col);
Draw_Line((int)x3, (int)y3, (int)x1, (int)y1, col);
}
private void drawline(int sx,int ex,int ny,Color col){
for (int i = sx; i <= ex; i++) Draw(i, ny, col);
}
public void Fill_Triangle(double x1, double y1, double x2, double y2, double x3, double y3, Color col){
int t1x, t2x, y, minx, maxx, t1xp, t2xp;
boolean changed1 = false;
boolean changed2 = false;
int signx1, signx2, dx1, dy1, dx2, dy2;
int e1, e2;
// Sort vertices
int temp;
if (y1 > y2) {temp=(int)y1;y1=y2;y2=temp;temp=(int)x1;x1=x2;x2=temp;}
if (y1 > y3) {temp=(int)y1;y1=y3;y3=temp;temp=(int)x1;x1=x3;x3=temp;}
if (y2 > y3) {temp=(int)y2;y2=y3;y3=temp;temp=(int)x2;x2=x3;x3=temp;}
t1x = t2x = (int)x1; y = (int)y1; // Starting points
dx1 = (int)(x2 - x1);
if (dx1 < 0) { dx1 = -dx1; signx1 = -1; }
else signx1 = 1;
dy1 = (int)(y2 - y1);
dx2 = (int)(x3 - x1);
if (dx2 < 0) { dx2 = -dx2; signx2 = -1; }
else signx2 = 1;
dy2 = (int)(y3 - y1);
if (dy1 > dx1) {temp=dx1;dx1=dy1;dy1=temp;changed1 = true; }
if (dy2 > dx2) {temp=dx2;dx2=dy2;dy2=temp;changed2 = true; }
e2 = (int)(dx2 >> 1);
// Flat top, just process the second half
boolean firstHalfDone=false;
if (y1 == y2) firstHalfDone=true;
if (!firstHalfDone) {
e1 = (int)(dx1 >> 1);
for (int i = 0; i < dx1;) {
t1xp = 0; t2xp = 0;
if (t1x < t2x) { minx = t1x; maxx = t2x; }
else { minx = t2x; maxx = t1x; }
// process first line until y value is about to change
next0:
while (i < dx1) {
i++;
e1 += dy1;
while (e1 >= dx1) {
e1 -= dx1;
if (changed1) t1xp = signx1;//t1x += signx1;
else {
break next0;
}
}
if (changed1) break;
else t1x += signx1;
}
// Move line
next1:
// process second line until y value is about to change
while (true) {
e2 += dy2;
while (e2 >= dx2) {
e2 -= dx2;
if (changed2) t2xp = signx2;//t2x += signx2;
else {
break next1;
}
}
if (changed2) break;
else t2x += signx2;
}
if (minx > t1x) minx = t1x;
if (minx > t2x) minx = t2x;
if (maxx < t1x) maxx = t1x;
if (maxx < t2x) maxx = t2x;
drawline(minx, maxx, y, col); // Draw line from min to max points found on the y
// Now increase y
if (!changed1) t1x += signx1;
t1x += t1xp;
if (!changed2) t2x += signx2;
t2x += t2xp;
y += 1;
if (y == y2) break;
}
}
// Second half
dx1 = (int)(x3 - x2); if (dx1 < 0) { dx1 = -dx1; signx1 = -1; }
else signx1 = 1;
dy1 = (int)(y3 - y2);
t1x = (int)x2;
if (dy1 > dx1) { // swap values
temp=dy1;dx1=dy1;dy1=temp;
changed1 = true;
}
else changed1 = false;
e1 = (int)(dx1 >> 1);
for (int i = 0; i <= dx1; i++) {
t1xp = 0; t2xp = 0;
if (t1x < t2x) { minx = t1x; maxx = t2x; }
else { minx = t2x; maxx = t1x; }
// process first line until y value is about to change
next3:
while (i < dx1) {
e1 += dy1;
while (e1 >= dx1) {
e1 -= dx1;
if (changed1) { t1xp = signx1; break; }//t1x += signx1;
else break next3;
}
if (changed1) break;
else t1x += signx1;
if (i < dx1) i++;
}
next4:
// process second line until y value is about to change
while (t2x != x3) {
e2 += dy2;
while (e2 >= dx2) {
e2 -= dx2;
if (changed2) t2xp = signx2;
else break next4;
}
if (changed2) break;
else t2x += signx2;
}
if (minx > t1x) minx = t1x;
if (minx > t2x) minx = t2x;
if (maxx < t1x) maxx = t1x;
if (maxx < t2x) maxx = t2x;
drawline(minx, maxx, y,col);
if (!changed1) t1x += signx1;
t1x += t1xp;
if (!changed2) t2x += signx2;
t2x += t2xp;
y += 1;
if (y > y3) return;
}
}
public void FillTexturedTriangle(List<Point<Double>>vPoints,List<Point<Double>>vTex,List<Color>vColour,Sprite sprTex){
try {
vColour=new ArrayList<Color>(vColour);
Point<Double> p1 = vPoints.get(0).clone();
Point<Double> p2 = vPoints.get(1).clone();
Point<Double> p3 = vPoints.get(2).clone();
Double tempP;
Color tempC;
if (p2.y < p1.y){tempP=(double)p1.y;p1.y=p2.y;p2.y=tempP;tempP=(double)p1.x;p1.x=p2.x;p2.x=tempP;tempP=vTex.get(0).x;vTex.get(0).x=vTex.get(1).x;vTex.get(1).x=tempP;vTex.get(0).y=vTex.get(1).y;vTex.get(1).y=tempP;tempC=vColour.get(0);vColour.set(0,vColour.get(1));vColour.set(1,tempC);}
if (p3.y < p1.y){tempP=(double)p1.y;p1.y=p3.y;p3.y=tempP;tempP=(double)p1.x;p1.x=p3.x;p3.x=tempP;tempP=vTex.get(0).x;vTex.get(0).x=vTex.get(2).x;vTex.get(2).x=tempP;vTex.get(0).y=vTex.get(2).y;vTex.get(2).y=tempP;tempC=vColour.get(0);vColour.set(0,vColour.get(2));vColour.set(2,tempC);}
if (p3.y < p2.y){tempP=(double)p2.y;p2.y=p3.y;p3.y=tempP;tempP=(double)p2.x;p2.x=p3.x;p3.x=tempP;tempP=vTex.get(1).x;vTex.get(1).x=vTex.get(2).x;vTex.get(2).x=tempP;vTex.get(1).y=vTex.get(2).y;vTex.get(2).y=tempP;tempC=vColour.get(1);vColour.set(1,vColour.get(2));vColour.set(2,tempC);}
Point<Integer> dPos1 = new Point<Integer>((int)(p2.x-p1.x),(int)(p2.y-p1.y));
Point<Integer> dTex1 = new Point<Integer>((int)(vTex.get(1).x-vTex.get(0).x),(int)(vTex.get(1).y-vTex.get(0).y));
int dcr1 = vColour.get(1).r - vColour.get(0).r;
int dcg1 = vColour.get(1).g - vColour.get(0).g;
int dcb1 = vColour.get(1).b - vColour.get(0).b;
int dca1 = vColour.get(1).a - vColour.get(0).a;
Point<Integer> dPos2 = new Point<Integer>((int)(p3.x-p1.x),(int)(p3.y-p1.y));
Point<Integer> dTex2 = new Point<Integer>((int)(vTex.get(2).x-vTex.get(0).x),(int)(vTex.get(2).y-vTex.get(0).y));
int dcr2 = vColour.get(2).r - vColour.get(0).r;
int dcg2 = vColour.get(2).g - vColour.get(0).g;
int dcb2 = vColour.get(2).b - vColour.get(0).b;
int dca2 = vColour.get(2).a - vColour.get(0).a;
float dax_step = 0, dbx_step = 0, dcr1_step = 0, dcr2_step = 0, dcg1_step = 0, dcg2_step = 0, dcb1_step = 0, dcb2_step = 0, dca1_step = 0, dca2_step = 0;
Point<Float> vTex1Step=null, vTex2Step=null;
if (dPos1.y!=0)
{
dax_step = dPos1.x / (float)Math.abs(dPos1.y);
vTex1Step = new Point<Float>(dTex1.x / (float)Math.abs(dPos1.y),dTex1.y / (float)Math.abs(dPos1.y));
dcr1_step = dcr1 / (float)Math.abs(dPos1.y);
dcg1_step = dcg1 / (float)Math.abs(dPos1.y);
dcb1_step = dcb1 / (float)Math.abs(dPos1.y);
dca1_step = dca1 / (float)Math.abs(dPos1.y);
}
if (dPos2.y!=0)
{
dbx_step = dPos2.x / (float)Math.abs(dPos2.y);
vTex2Step = new Point<Float>(dTex2.x / (float)Math.abs(dPos2.y),dTex2.y / (float)Math.abs(dPos2.y));
dcr2_step = dcr2 / (float)Math.abs(dPos2.y);
dcg2_step = dcg2 / (float)Math.abs(dPos2.y);
dcb2_step = dcb2 / (float)Math.abs(dPos2.y);
dca2_step = dca2 / (float)Math.abs(dPos2.y);
}
Point<Double> vStart;
Point<Double> vEnd;
int vStartIdx;
for (int pass = 0; pass < 2; pass++)
{
if (pass == 0)
{
vStart = p1; vEnd = p2; vStartIdx = 0;
}
else
{
dPos1 = new Point<Integer>((int)(p3.x-p2.x),(int)(p3.y-p2.y));
dTex1 = new Point<Integer>((int)(vTex.get(2).x-vTex.get(1).x),(int)(vTex.get(2).y-vTex.get(1).y));
dcr1 = vColour.get(2).r - vColour.get(1).r;
dcg1 = vColour.get(2).g - vColour.get(1).g;
dcb1 = vColour.get(2).b - vColour.get(1).b;
dca1 = vColour.get(2).a - vColour.get(1).a;
dcr1_step = 0; dcg1_step = 0; dcb1_step = 0; dca1_step = 0;
if (dPos2.y!=0) dbx_step = dPos2.x / (float)Math.abs(dPos2.y);
if (dPos1.y!=0)
{
dax_step = dPos1.x / (float)Math.abs(dPos1.y);
vTex1Step = new Point<Float>(dTex1.x/(float)Math.abs(dPos1.y),dTex1.y/(float)Math.abs(dPos1.y));
dcr1_step = dcr1 / (float)Math.abs(dPos1.y);
dcg1_step = dcg1 / (float)Math.abs(dPos1.y);
dcb1_step = dcb1 / (float)Math.abs(dPos1.y);
dca1_step = dca1 / (float)Math.abs(dPos1.y);
}
vStart = p2; vEnd = p3; vStartIdx = 1;
}
if (dPos1.y!=0)
{
for (int i = (int)Math.floor(vStart.y); i <= vEnd.y; i++)
{
int ax = (int)(vStart.x + (float)(i - vStart.y) * dax_step);
int bx = (int)(p1.x + (float)(i - p1.y) * dbx_step);
Point<Float> tex_s = new Point<Float>((float)(vTex.get(vStartIdx).x + (float)(i - vStart.y) * vTex1Step.x),(float)(vTex.get(vStartIdx).y + (float)(i - vStart.y) * vTex1Step.y));
Point<Float> tex_e = new Point<Float>((float)(vTex.get(0).x + (float)(i - p1.y) * vTex2Step.x),(float)(vTex.get(0).y + (float)(i - p1.y) * vTex2Step.y));
Color col_s= new Color(vColour.get(vStartIdx).r + (int)((float)(i - vStart.y) * dcr1_step), vColour.get(vStartIdx).g + (int)((float)(i - vStart.y) * dcg1_step),
vColour.get(vStartIdx).b + (int)((float)(i - vStart.y) * dcb1_step), vColour.get(vStartIdx).a + (int)((float)(i - vStart.y) * dca1_step));
Color col_e= new Color(vColour.get(0).r + (int)((float)(i - p1.y) * dcr2_step), vColour.get(0).g + (int)((float)(i - p1.y) * dcg2_step),
vColour.get(0).b + (int)((float)(i - p1.y) * dcb2_step), vColour.get(0).a + (int)((float)(i - p1.y) * dca2_step));
int temp;
Point<Float> tempF;
if (ax > bx) {temp=ax;ax=bx;bx=temp;tempF=tex_s.clone();tex_s=tex_e.clone();tex_e=tempF;tempC=col_s.clone();col_s=col_e.clone();col_e=tempC;}
float tstep = 1.0f / ((float)(bx - ax));
float t = 0.0f;
for (int j = ax; j < bx; j++)
{
Color pixel = ColorLerp(col_s, col_e, t);
if (sprTex != null) {
Point<Float> lerpComponents = new Point<Float>(
tex_s.x*(1.0f-t)+tex_e.x*t,
tex_s.y*(1.0f-t)+tex_e.y*t
);
Color c=new Color(sprTex.getImg().getRGB((int)Math.min(lerpComponents.x*sprTex.width,sprTex.width-1),(int)Math.min(lerpComponents.y*sprTex.height,sprTex.height-1)));
pixel.r = (int)Math.min(255.0f,Math.max(0,(float)pixel.r * c.r / 255.0f));
pixel.g = (int)Math.min(255.0f,Math.max(0,(float)pixel.g * c.g / 255.0f));
pixel.b = (int)Math.min(255.0f,Math.max(0,(float)pixel.b * c.b / 255.0f));
pixel.a = (int)Math.min(255.0f,Math.max(0,(float)pixel.a * c.a / 255.0f));
}
Draw(j, i, pixel);
t += tstep;
}
}
}
}
} catch (CloneNotSupportedException e) {}
}
public void Draw_Circle(int x, int y, int radius, Color col) {
Draw_Circle(x,y,radius,col,(byte)0xFF);
}
//The mask indicates which pie slices to draw, each bit of the mask represents 1/8th of a pie slice. By default all pie slices are drawn.
public void Draw_Circle(int x, int y, int radius, Color col, byte mask) {
if (radius < 0 || x < -radius || y < -radius || x - ACTUAL_WINDOW_WIDTH > radius || y - ACTUAL_WINDOW_HEIGHT > radius)
return;
if (radius > 0)
{
int x0 = 0;
int y0 = radius;
int d = 3 - 2 * radius;
while (y0 >= x0) // only formulate 1/8 of circle
{
// Draw even octants
if ((mask & 0x01)!=0) Draw(x + x0, y - y0, col);// Q6 - upper right right
if ((mask & 0x04)!=0) Draw(x + y0, y + x0, col);// Q4 - lower lower right
if ((mask & 0x10)!=0) Draw(x - x0, y + y0, col);// Q2 - lower left left
if ((mask & 0x40)!=0) Draw(x - y0, y - x0, col);// Q0 - upper upper left
if (x0 != 0 && x0 != y0)
{
if ((mask & 0x02)!=0) Draw(x + y0, y - x0, col);// Q7 - upper upper right
if ((mask & 0x08)!=0) Draw(x + x0, y + y0, col);// Q5 - lower right right
if ((mask & 0x20)!=0) Draw(x - y0, y + x0, col);// Q3 - lower lower left
if ((mask & 0x80)!=0) Draw(x - x0, y - y0, col);// Q1 - upper left left
}
if (d < 0)
d += 4 * x0++ + 6;
else
d += 4 * (x0++ - y0--) + 10;
}
}
else
Draw(x, y, col);
}
public void Fill_Circle(int x, int y, int radius, Color col) {
if (radius < 0 || x < -radius || y < -radius || x - ACTUAL_WINDOW_WIDTH > radius || y - ACTUAL_WINDOW_HEIGHT > radius)
return;
if (radius > 0)
{
int x0 = 0;
int y0 = radius;
int d = 3 - 2 * radius;
while (y0 >= x0)
{
drawline(x - y0, x + y0, y - x0, col);
if (x0 > 0) drawline(x - y0, x + y0, y + x0, col);
if (d < 0)
d += 4 * x0++ + 6;
else
{
if (x0 != y0)
{
drawline(x - x0, x + x0, y - y0, col);
drawline(x - x0, x + x0, y + y0, col);
}
d += 4 * (x0++ - y0--) + 10;
}
}
}
else
Draw(x, y, col);
}
public void FillTexturedPolygon(List<Point<Double>>vPoints,List<Point<Double>>vTex,List<Color>vColour,Sprite sprTex,PolygonStructure structure) {
try {
if (vPoints.size() < 3 || vTex.size() < 3 || vColour.size() < 3)
return;
if (structure == PolygonStructure.LIST)
{
for (int tri = 0; tri < vPoints.size() / 3; tri++)
{
List<Point<Double>> vP = new ArrayList<Point<Double>>(List.of(vPoints.get(tri * 3 + 0), vPoints.get(tri * 3 + 1), vPoints.get(tri * 3 + 2)));
List<Point<Double>> vT = new ArrayList<Point<Double>>(List.of(vTex.get(tri * 3 + 0), vTex.get(tri * 3 + 1), vTex.get(tri * 3 + 2)));
List<Color> vC = new ArrayList<Color>(List.of(vColour.get(tri * 3 + 0), vColour.get(tri * 3 + 1), vColour.get(tri * 3 + 2)));
FillTexturedTriangle(vP, vT, vC, sprTex);
}
return;
}
if (structure == PolygonStructure.STRIP)
{
for (int tri = 2; tri < vPoints.size(); tri++)
{
List<Point<Double>> vP = new ArrayList<Point<Double>>(List.of(vPoints.get(tri - 2), vPoints.get(tri - 1), vPoints.get(tri)));
List<Point<Double>> vT = new ArrayList<Point<Double>>(List.of(vTex.get(tri - 2), vTex.get(tri - 1), vTex.get(tri)));
List<Color> vC = new ArrayList<Color>(List.of(vColour.get(tri - 2), vColour.get(tri - 1), vColour.get(tri)));
FillTexturedTriangle(vP, vT, vC, sprTex);
}
return;
}
if (structure == PolygonStructure.FAN)
{
for (int tri = 2; tri < vPoints.size(); tri++)
{
List<Point<Double>> vP;
vP = new ArrayList<Point<Double>>(List.of(vPoints.get(0).clone(), vPoints.get(tri - 1).clone(), vPoints.get(tri).clone()));
List<Point<Double>> vT = new ArrayList<Point<Double>>(List.of(vTex.get(0).clone(), vTex.get(tri - 1).clone(), vTex.get(tri).clone()));
List<Color> vC = new ArrayList<Color>(List.of(vColour.get(0).clone(), vColour.get(tri - 1).clone(), vColour.get(tri).clone()));
FillTexturedTriangle(vP, vT, vC, sprTex);
}
return;
}
} catch (CloneNotSupportedException e) {}
}
Color ColorLerp(Color c1, Color c2, float t)
{ return new Color((int)((c2.r * t) + c1.r * (1.0f - t)),(int)((c2.g * t) + c1.g * (1.0f - t)),(int)((c2.b * t) + c1.b * (1.0f - t)),(int)((c2.a * t) + c1.a * (1.0f - t))); }
Color ColorLerpNoAlpha(Color c1, Color c2, float t)
{ return new Color((int)((c2.r * t) + c1.r * (1.0f - t)),(int)((c2.g * t) + c1.g * (1.0f - t)),(int)((c2.b * t) + c1.b * (1.0f - t))); }
public void Clear(Color col){
for (int y=0;y<ACTUAL_WINDOW_HEIGHT;y++) {
for (int x=0;x<ACTUAL_WINDOW_WIDTH;x++) {
Draw(y*ACTUAL_WINDOW_WIDTH+x,col.getColor());
}
}
}
}