#include "graph.h" #include "platform.h" #define vram Graph_getVramAddress() #define setByte(offset, color, mask) do { \ if(color==COLOR_BLACK) vram[offset]|=(mask); \ else if(color==COLOR_WHITE) vram[offset]&=(char) ~(mask); \ else if(color==COLOR_XOR) vram[offset]^=(mask); \ else return 0; \ } while(0) #define getOffset(x, y) ((y)*VRAM_BYTES_PER_LINE+(x)/8) #define sgn(a) ((a)>0?1:(a)?-1:0) #define abs(a) ((a)<0?-(a):(a)) int Graph_fill(int color) { //check if we have to actually draw if(color==COLOR_TRANSPARENT) return 1; //fill everything for(int i=0; i=VRAM_WIDTH) return 1; if(y<0||y>=VRAM_HEIGHT) return 1; int byte=getOffset(x, y); char mask=1<<(8-(x%8)-1); //set pixel setByte(byte, color, mask); return 1; } int Graph_pixelGet(int x, int y) { //locate pixel in VRAM if(x<0||x>=VRAM_WIDTH) return 0; if(y<0||y>=VRAM_HEIGHT) return 0; int byte=getOffset(x, y); char mask=1<<(8-(x%8)-1); //get pixel return !!(vram[byte]&mask); } #define lineLow(x0, y0, x1, y1) do { \ dx=x1-x0; \ dy=y1-y0; \ int yi=1; \ if(dy<0) { \ yi=-1; \ dy=-dy; \ } \ int d=2*dy-dx; \ int y=y0; \ for(int x=x0; x<=x1; x++) { \ if(!Graph_pixelSet(x, y, color)) return 0; \ if(d>0) {\ y+=yi; \ d-=2*dx; \ } \ d+=2*dy; \ } \ } while(0) #define lineHigh(x0, y0, x1, y1) do { \ dx=x1-x0; \ dy=y1-y0; \ int xi=1; \ if(dx<0) { \ xi=-1; \ dx=-dx; \ } \ int d=2*dx-dy; \ int x=x0; \ for(int y=y0; y<=y1; y++) { \ if(!Graph_pixelSet(x, y, color)) return 0; \ if(d>0) {\ x+=xi; \ d-=2*dy; \ } \ d+=2*dx; \ } \ } while(0) int Graph_line(int x1, int y1, int x2, int y2, int color) { //check if we have to actually draw if(color==COLOR_TRANSPARENT) return 1; //check if we can use horizontal or vertical line if(x1==x2) return Graph_lineV(x1, y1, y2, color); if(y1==y2) return Graph_lineH(y1, x1, x2, color); //see: https://en.wikipedia.org/wiki/Bresenham's_line_algorithm int dx=x2-x1; int dy=y2-y1; if(abs(dy)x2) lineLow(x2, y2, x1, y1); else lineLow(x1, y1, x2, y2); } else { if(x1>y2) lineHigh(x2, y2, x1, y1); else lineHigh(x1, y1, x2, y2); } return 1; } int Graph_lineV(int x, int y1, int y2, int color) { //check if we have to actually draw if(color==COLOR_TRANSPARENT) return 1; //sanitize coordinates if(x<0||x>=VRAM_WIDTH) return 1; if(y1<0) y1=0; if(y1>=VRAM_HEIGHT) y1=VRAM_HEIGHT-1; if(y2<0) y2=0; if(y2>=VRAM_HEIGHT) y2=VRAM_HEIGHT-1; if(y2=VRAM_HEIGHT) return 1; if(x1<0) x1=0; if(x1>=VRAM_WIDTH) x1=VRAM_WIDTH-1; if(x2<0) x2=0; if(x2>=VRAM_WIDTH) x2=VRAM_WIDTH-1; if(x2>(x2%8))); return 1; } if(x1%8) count--; //set the left-hand byte if(x1%8) setByte(byte++, color, 0xff^(0xff<<(8-x1%8))); //set the middle bytes for(int i=0; i>(x2%8))); return 1; } int Graph_horizontal(int y, int color) { //make sure we need to draw if(color==COLOR_TRANSPARENT) return 1; if(y<0||y>=VRAM_HEIGHT) return 1; //select the bytes int byte=y*VRAM_BYTES_PER_LINE; //fill the line for(int i=0; i=VRAM_WIDTH) return 1; //select the bytes and the mask int byte=x%8; int mask=1<<(8-(x%8)-1); //fill the line for(int i=0; i0) { y0--; m-=8*y0; } x0++; m+=8*x0+4; } return 1; } int Graph_circleFill(int x, int y, int radius, int color) { //check if we have to draw if(color==COLOR_TRANSPARENT) return 1; if(color==COLOR_XOR) return 0; //see: https://fr.wikipedia.org/wiki/Algorithme_de_tracé_d'arc_de_cercle_de_Bresenham int x0=0; int y0=radius; int m=5-4*radius; while(x0<=y0) { Graph_lineH(y0+y, -x0+x, x0+x, color); Graph_lineH(x0+y, -y0+x, y0+x, color); Graph_lineH(-y0+y, -x0+x, x0+x, color); if(!Graph_lineH(-x0+y, -y0+x, y0+x, color)) return 0; if(m>0) { y0--; m-=8*y0; } x0++; m+=8*x0+4; } return 1; } int Graph_shiftV(int dist, int color) { //check if we have something to do if(!dist) return 1; if(abs(dist)>=VRAM_HEIGHT) return Graph_fill(color); //check the direction if(dist>0) { //downwards for(int y=VRAM_HEIGHT-1; y>=dist; y--) { for(int i=0; i=VRAM_WIDTH) return Graph_fill(color); int bytes=dist/8; int bits=abs(dist)%8; //move whole bytes first for(int y=0; y0) { for(int x=VRAM_BYTES_PER_LINE-1; x>=bytes; x--) { vram[y*VRAM_BYTES_PER_LINE+x]=vram[y*VRAM_BYTES_PER_LINE+x-bytes]; } } else { for(int x=0; x0) { int mask=0xff^(0xff<=0; x--) { vram[y*VRAM_BYTES_PER_LINE+x]>>=bits; if(x) vram[y*VRAM_BYTES_PER_LINE+x]|=(vram[y*VRAM_BYTES_PER_LINE+x-1]<<(8-bits-1))&mask; } } else { int mask=0xff^(0xff>>8-bits-1); for(int x=VRAM_BYTES_PER_LINE-1; x>=0; x--) { vram[y*VRAM_BYTES_PER_LINE+x]<<=bits; if(x!=VRAM_BYTES_PER_LINE-1) vram[y*VRAM_BYTES_PER_LINE+x]|=(vram[y*VRAM_BYTES_PER_LINE+x+1]>>(8-bits-1))&mask; } } } } //draw the remaining rectangle if(dist>0) { if(dist==1) return Graph_lineV(0, 0, VRAM_HEIGHT-1, color); else return Graph_rectFill(0, 0, dist, VRAM_HEIGHT, color); } else { if(dist==-1) return Graph_lineH(VRAM_WIDTH-1, 0, VRAM_HEIGHT-1, color); else return Graph_rectFill(VRAM_WIDTH+dist, 0, -dist, VRAM_HEIGHT, color); } } int Graph_copy(int srcX, int srcY, int w, int h, int destX, int destY) { //check the width and height if(w<=0) return w==0; if(h<=0) return h==0; //check if we're in bounds if(srcX<0||srcY<0) return 0; if(srcX+w>=VRAM_WIDTH||srcY+h>=VRAM_HEIGHT) return 0; if(destX<0||destY<0) return 0; if(destX+w>=VRAM_WIDTH||destY+h>=VRAM_HEIGHT) return 0; //Note that we don't promise anything if the two regions overlap! //check if we can optimize heavily if(srcX%8==0&&destX%8==0) { int bytes=w/8; int bits=w%8; int mask=0xff^(0xff>>bits); for(int y=0; y