pixel-draw
Version:
draw stuff directly onto a pixel buffer similar to a canvas
305 lines (264 loc) • 11 kB
JavaScript
var td = require('triangle-draw');
var dpt = require('draw-pixel-text').drawTextToBuffer;
var is = require('image-sync');
function pixelCanvas(w=512,h=512,bg=[255,255,255]){
var img = is.blank(w,h,bg);
this.image = img;
this.w=w;
this.h=h;
this.bgColor = bg;
var _this = this;
function getCropped(x,y,_w,_h, bgColor=bg){ //crop canvas, return new canvas, bigger or smaller
var newCanvas = new pixelCanvas(_w,_h,bgColor);
for(var _x=0; _x<_w; _x++){
for(var _y=0; _y<_h; _y++){
var ix = x + _x;
var iy = y + _y;
if(ix>=0 && ix<w && iy>=0 && iy<h){
newCanvas._putPixel(_x,_y,_getPixel(ix,iy));
}else{
newCanvas._putPixel(_x,_y,bgColor);
}
}
}
return newCanvas;
}
this.getCropped = getCropped;
function drawCanvas(x,y,_canvas,transparentColor=[255,255,255], enableTransparent=true){ //draw contents of _canvas on this canvas at coords x,y
var data = _canvas.image.data;
var _w = _canvas.w;
var _h = _canvas.h;
var minX = Math.max(0, x);
var minY = Math.max(0, y);
var maxX = Math.min(w-1, x+_w);
var maxY = Math.min(h-1, y+_h);
if(minX > 0 && minY > 0 && maxX < w-1 && maxY < h-1){
//without bounds checks
if(enableTransparent){
for(var _x=0;_x<_w;_x++){
for(var _y=0;_y<_h;_y++){
var _o = (_x+_y*_w)*4;
var rgb = [data[_o],data[_o+1],data[_o+2]];
var isTransparent = Math.abs(data[_o]-transparentColor[0]) + Math.abs(data[_o+1]-transparentColor[1]) + Math.abs(data[_o+2]-transparentColor[2]) == 0;
var o = ((x+_x)+(y+_y)*w)*4;
if(!isTransparent){
img.data[o] = rgb[0];
img.data[o+1] = rgb[1];
img.data[o+2] = rgb[2];
}
}
}
}else{
for(var _x=0;_x<_w;_x++){
for(var _y=0;_y<_h;_y++){
var _o = (_x+_y*_w)*4;
var rgb = [data[_o],data[_o+1],data[_o+2]];
var o = ((x+_x)+(y+_y)*w)*4;
img.data[o] = rgb[0];
img.data[o+1] = rgb[1];
img.data[o+2] = rgb[2];
}
}
}
}else{
//with bounds checks
if(enableTransparent){
for(var _x=0;_x<_w;_x++){
if((x+_x) >= 0 && (x+_x)<w){
for(var _y=0;_y<_h;_y++){
var _o = (_x+_y*_w)*4;
var rgb = [data[_o],data[_o+1],data[_o+2]];
var o = ((x+_x)+(y+_y)*w)*4;
if((y+_y) >= 0 && (y+_y)<h){
var isTransparent = Math.abs(data[_o]-transparentColor[0]) + Math.abs(data[_o+1]-transparentColor[1]) + Math.abs(data[_o+2]-transparentColor[2]) == 0;
if(!isTransparent){
img.data[o] = rgb[0];
img.data[o+1] = rgb[1];
img.data[o+2] = rgb[2];
}
}
}
}
}
}else{
for(var _x=0;_x<_w;_x++){
if((x+_x) >= 0 && (x+_x)<w){
for(var _y=0;_y<_h;_y++){
var _o = (_x+_y*_w)*4;
var rgb = [data[_o],data[_o+1],data[_o+2]];
var o = ((x+_x)+(y+_y)*w)*4;
if((y+_y) >= 0 && (y+_y)<h){
img.data[o] = rgb[0];
img.data[o+1] = rgb[1];
img.data[o+2] = rgb[2];
}
}
}
}
}
}
}
this.drawCanvas=drawCanvas;
this.crop = function(x,y,_w,_h, bgColor=bg){
_this = getCropped(x,y,_w,_h, bgColor);
return this;
}
function drawText(text, offset, color=[0,0,0], doExpandText=true, maxLineLen=50){
//note offset must be integers
dpt(text, offset,color,img.data,w,h,doExpandText,maxLineLen);
}
this.drawText=drawText;
function drawTriangle(tri, color=[255,0,0], edgesOnly=false){
if(Array.isArray(color[0])){ //vert colors
td.drawTriangleColored(tri,color,img.data,w,h,edgesOnly);
}else{
td.drawTriangle(tri,color,img.data,w,h,edgesOnly);
}
}
this.drawTriangle=drawTriangle;
function drawRectangle(x,y,_w,_h, color=[255,0,0], filled=true, thickness=2.0){
var tri0 = [[x,y],[x+_w,y],[x+_w,y+_h]].map(ptFloor);
var tri1 = [[x+_w,y+_h],[x,y+_h],[x,y]].map(ptFloor);
if(Array.isArray(color[0])){ //vert colors
td.drawTriangleColored(tri0,[color[0],color[2],color[1]],img.data,w,h,false);
td.drawTriangleColored(tri1,[color[2],color[0],color[3]],img.data,w,h,false);
}else{
if(filled){
var minX = Math.floor(Math.max(x,0));
var maxX = Math.floor(Math.min(x+_w,w-1));
var minY = Math.floor(Math.max(y,0));
var maxY = Math.floor(Math.min(y+_h,h-1));
for(var _x=minX; _x<maxX; _x++){
for(var _y=minY; _y<maxY; _y++){
var o = (_y*w+_x)*4;
img.data[o] = color[0];
img.data[o+1] = color[1];
img.data[o+2] = color[2];
}
}
//td.drawTriangle(tri0,color,img.data,w,h,false);
//td.drawTriangle(tri1,color,img.data,w,h,false);
}else{
//draw edges only
drawRectangle(x,y,_w,thickness,color,true);
drawRectangle(x,y+_h-thickness,_w,thickness,color,true);
drawRectangle(x,y,thickness,_h,color,true);
drawRectangle(x+_w-thickness,y,thickness,_h,color,true);
}
}
}
this.drawRectangle=drawRectangle;
function drawCircle(x,y,radius,color=[255,0,0], filled=true, thickness=2.0){
var radiusSquared = radius*radius;
var radSmaller = Math.max(0,radius-thickness);
var radiusMinSquared = filled ? 0 : radSmaller*radSmaller;
var minX = Math.max(0,x-radius);
var maxX = Math.min(w-1,x+radius);
var minY = Math.max(0,y-radius);
var maxY = Math.min(h-1,y+radius);
for(var _x=minX;_x<maxX;_x++){
for(var _y=minY;_y<maxY;_y++){
var xd = (_x-x); var yd = (_y-y);
var distSquared = xd*xd+yd*yd;
if(distSquared>= radiusMinSquared && distSquared <= radiusSquared){
var o = (_y*w+_x)*4;
img.data[o] = color[0];
img.data[o+1] = color[1];
img.data[o+2] = color[2];
}
}
}
}
this.drawCircle=drawCircle;
function ptScale(a,s){return [a[0]*s,a[1]*s];}
function ptAdd(a,b){return [a[0]+b[0],a[1]+b[1]];}
function ptDiff(a,b){return [a[0]-b[0],a[1]-b[1]];}
function ptLen(p){return Math.sqrt(p[0]*p[0]+p[1]*p[1]);}
function ptFloor(p){return p.map(Math.floor)}
//function ptsAverage(a,b){return ptScale(ptAdd(a,b),0.5)}
//line = [x0,y0,x1,y1]
function drawLine(line,color=[0,255,0],thickness=2,endcaps=true){
var t0;
var t1;
if(Array.isArray(thickness)){ //diff start/end thickness
t0=thickness[0];
t1=thickness[1];
}else{
t0=thickness;
t1=thickness;
}
var lineDir = ptDiff(line[1],line[0]);
var lineLen = ptLen(lineDir);
var orthoLineDir = [lineDir[1],-lineDir[0]];
var orthoLineDirNormalized = ptScale(orthoLineDir,1.0/lineLen);
var orthoOffset0 = ptScale(orthoLineDirNormalized, t0);
var orthoOffset1 = ptScale(orthoLineDirNormalized, t1);
var A = [ptAdd(line[0],orthoOffset0),ptDiff(line[0],orthoOffset0)]; //orthoLine0
var B = [ptAdd(line[1],orthoOffset1),ptDiff(line[1],orthoOffset1)]; //orthoLine1
var tri0 = [A[1],A[0],B[0]].map(ptFloor);
var tri1 = [B[1],B[0],A[1]].map(ptFloor);
var colorArr = Array.isArray(color[0]);
if(endcaps){
var c0 = colorArr ? color[0] : color;
var c1 = colorArr ? color[1] : color;
drawCircle(line[0][0],line[0][1],t0,c0);
drawCircle(line[1][0],line[1][1],t1,c1);
}
if(colorArr){ //vert colors
td.drawTriangleColored(tri0,[color[0],color[1],color[0]],img.data,w,h,false);
td.drawTriangleColored(tri1,[color[1],color[0],color[1]],img.data,w,h,false);
}else{
td.drawTriangle(tri0,color,img.data,w,h,false);
td.drawTriangle(tri1,color,img.data,w,h,false);
}
}
this.drawLine = drawLine;
function _putPixel(x,y,rgb){
x=Math.floor(x);
y=Math.floor(y);
var o = (x+y*w)*4;
img.data[o] = rgb[0];
img.data[o+1] = rgb[1];
img.data[o+2] = rgb[2];
}
function putPixel(x,y,rgb){
if(x>=0 && x<w && y>=0 && y<h){_putPixel(x,y,rgb);}
}
function _getPixel(x,y){
var o = (x+y*w)*4;
return [img.data[o],img.data[o+1],img.data[o+2]];
}
function getPixel(x,y){
if(x>=0 && x<w && y>=0 && y<h){
return _getPixel(x,y);
}
return [0,0,0];
}
function clear(color){
drawRectangle(0,0,w,h,color || [255,255,255])
}
function drawFunction(x,y,_w,_h, colorFunc){
var minX = Math.floor(Math.max(x,0));
var maxX = Math.floor(Math.min(x+_w,w-1));
var minY = Math.floor(Math.max(y,0));
var maxY = Math.floor(Math.min(y+_h,h-1));
for(var _x=minX; _x<maxX; _x++){
for(var _y=minY; _y<maxY; _y++){
var o = (_y*w+_x)*4;
var color = colorFunc(_x,_y, [img.data[o], img.data[o+1], img.data[o+2]]);
img.data[o] = color[0];
img.data[o+1] = color[1];
img.data[o+2] = color[2];
}
}
}
this.clear = clear;
this.getPixel = getPixel;
this._getPixel = _getPixel;
this.putPixel = putPixel;
this._putPixel = _putPixel;
this.saveAs = img.saveAs;
this.drawFunction = drawFunction;
return this;
}
module.exports = pixelCanvas;