package Pixels;
public class Canvas
{
public static void drawLines(
int[] image,
int width,
int height,
int[] index, // optional
int[] points,
int arraySize,
int pointSize,
int rgbColor,
double strokeWidth
) {
pointSize = Math.max(pointSize, 2);
double alpha = ((rgbColor >> 24) & 0xff) / 255.0;
double r1 = ((rgbColor >> 16) & 0xff) / 255.0;
double g1 = ((rgbColor >> 8) & 0xff) / 255.0;
double b1 = (rgbColor & 0xff) / 255.0;
double strokeDistance = 0.5 + strokeWidth * 0.5;
int idxJump = index != null ? 1 : pointSize;
for (int i = 0; i < arraySize; i += idxJump) {
int next = (i+idxJump) % arraySize;
int idx, idxNext;
if (index != null) {
idx = index[i];
idxNext = index[next];
}
else {
idx = i;
idxNext = next;
}
int x1 = points[i];
int y1 = points[i+1];
int x2 = points[next];
int y2 = points[next+1];
int firstX, lastX;
if (x1 < x2) {
firstX = x1;
lastX = x2;
}
else {
firstX = x2;
lastX = x1;
}
int firstY, lastY;
if (y1 < y2) {
firstY = y1;
lastY = y2;
}
else {
firstY = y2;
lastY = y1;
}
int startX = Math.min(Math.max(firstX - (int)strokeWidth, 0), width - 1);
int startY = Math.min(Math.max(firstY - (int)strokeWidth, 0), height - 1);
int endX = Math.min(Math.max(lastX + (int)strokeWidth, 0), width - 1);
int endY = Math.min(Math.max(lastY + (int)strokeWidth, 0), height - 1);
int dx = x2 - x1;
int dy = y2 - y1;
double maxDistance = (dx*dx + dy*dy) * strokeDistance * strokeDistance;
for (int y = startY; y <= endY; y++) {
for (int x = startX; x <= endX; x++) {
double distance = 0;
if (dx == 0) {
if (y < firstY || y > lastY)
distance = maxDistance;
else
distance = dy * dy * (x1 - x) * (x1 - x);
}
else {
// using <= and >= for y covers the horizontal line case
if ((x < firstX || x > lastX) && (y <= firstY || y >= lastY)) {
distance = maxDistance;
}
else {
distance = dx * (double)(y2 - y) - dy * (double)(x2 - x);
distance *= distance;
}
}
if (distance >= maxDistance)
continue;
double a1 = alpha * (1.0 - (distance / maxDistance));
int p = image[x + width * y];
double a2 = ((p >> 24) & 0xff) / 255.0;
double r2 = ((p >> 16) & 0xff) / 255.0;
double g2 = ((p >> 8) & 0xff) / 255.0;
double b2 = (p & 0xff) / 255.0;
a2 = 1.0 - ((1.0 - a1) * (1.0 - a2));
r2 = (1.0 - a1) * r2 + a1 * r1;
g2 = (1.0 - a1) * g2 + a1 * g1;
b2 = (1.0 - a1) * b2 + a1 * b1;
image[x + width * y] =
((int)(a2 * 255.0) << 24 & 0xff000000) |
((int)(r2 * 255.0) << 16 & 0xff0000) |
((int)(g2 * 255.0) << 8 & 0xff00) |
((int)(b2 * 255.0) & 0xff);
}
}
}
}
public static void drawCircles(
int[] image,
int width,
int height,
int[] index, // optional
int[] points,
int arraySize,
int pointSize,
int rgbColor,
double radius
) {
pointSize = Math.max(pointSize, 2);
double alpha = ((rgbColor >> 24) & 0xff) / 255.0;
rgbColor &= 0xffFFff;
double r1 = ((rgbColor >> 16) & 0xff) / 255.0;
double g1 = ((rgbColor >> 8) & 0xff) / 255.0;
double b1 = (rgbColor & 0xff) / 255.0;
int pixDiameter = (int)Math.ceil(radius * 2);
pixDiameter += 1 - (pixDiameter % 2);
int startOff = -pixDiameter / 2;
int idxJump = index != null ? 1 : pointSize;
radius += 0.5;
for (int i = 0; i < arraySize; i += idxJump) {
int idx = index != null ? index[i] : i;
int x = points[idx];
int y = points[idx+1];
for (int j = 0; j < pixDiameter; j++) {
int yy = y + j + startOff;
if (yy < 0 || yy >= height)
continue;
for (int k = 0; k < pixDiameter; k++) {
int xx = x + k + startOff;
if (xx < 0 || xx >= width)
continue;
double dx = xx - x;
double dy = yy - y;
double distance = Math.sqrt(dx*dx + dy*dy);
double weight = Math.min(1.0, Math.max(0.0, 3.0 * (1.0 - (distance / radius))));
double a1 = weight * alpha;
int p = image[xx + width * yy];
double a2 = ((p >> 24) & 0xff) / 255.0;
double r2 = ((p >> 16) & 0xff) / 255.0;
double g2 = ((p >> 8) & 0xff) / 255.0;
double b2 = (p & 0xff) / 255.0;
a2 = 1.0 - ((1.0 - a1) * (1.0 - a2));
r2 = (1.0 - a1) * r2 + a1 * r1;
g2 = (1.0 - a1) * g2 + a1 * g1;
b2 = (1.0 - a1) * b2 + a1 * b1;
image[xx + width * yy] =
((int)(a2 * 255.0) << 24 & 0xff000000) |
((int)(r2 * 255.0) << 16 & 0xff0000) |
((int)(g2 * 255.0) << 8 & 0xff00) |
((int)(b2 * 255.0) & 0xff);
}
}
}
}
public static void blurArgbImage(int[] outPixels, int[] inPixels, int width, int height, int[] row, float[] blurWnd)
{
int[] dstPixels = row;
int[] srcPixels = inPixels;
int wndSize = Math.min(Math.max(
Math.min(width - 1 + (width % 2), height - 1 + (height % 2)),
1), blurWnd.length / 3
);
int halfWnd = wndSize / 2;
int shouldWriteToOutPixels = 0;
int nA = width;
int nB = height;
int d1 = width;
int d2 = 1;
for (int axis = 0; axis < 2; axis++) {
int temp = nA;
nA = nB;
nB = temp;
temp = d1;
d1 = d2;
d2 = temp;
for (int a = 0; a < nA; a++) {
float sumRed = 0f;
float sumGreen = 0f;
float sumBlue = 0f;
for (int i = 0; i < wndSize; i++) {
int rgb = inPixels[Math.min(wndSize - i, nB - 1) * d1 + a * d2];
sumRed += blurWnd[i*3] = (float)((rgb >> 16) & 0xff);
sumGreen += blurWnd[i*3+1] = (float)((rgb >> 8) & 0xff);
sumBlue += blurWnd[i*3+2] = (float)(rgb & 0xff);
}
int offset = a * d2 * shouldWriteToOutPixels;
int l1 = Math.max(d1 * shouldWriteToOutPixels, 1);
for (int b = 0; b < nB; b++) {
int nextIdx = b + halfWnd;
int rgb = srcPixels[Math.min(nextIdx, 2*(nB-1) - nextIdx) * d1 + a * d2];
float nextRed = (float)((rgb >> 16) & 0xff);
float nextGreen = (float)((rgb >> 8) & 0xff);
float nextBlue = (float)(rgb & 0xff);
int idx = nextIdx % wndSize;
sumRed += nextRed - blurWnd[3*idx];
sumGreen += nextGreen - blurWnd[3*idx+1];
sumBlue += nextBlue - blurWnd[3*idx+2];
blurWnd[3*idx] = nextRed;
blurWnd[3*idx+1] = nextGreen;
blurWnd[3*idx+2] = nextBlue;
float red = sumRed / wndSize;
float green = sumGreen / wndSize;
float blue = sumBlue / wndSize;
dstPixels[b * l1 + offset] = 0xff000000 |
(Math.min(Math.max((int)red, 0), 255) << 16) |
(Math.min(Math.max((int)green, 0), 255) << 8) |
Math.min(Math.max((int)blue, 0), 255);
}
if (dstPixels == row) {
for (int b = 0; b < nB; b++)
outPixels[b * d1 + a * d2] = row[b];
}
}
if (outPixels != inPixels) {
dstPixels = outPixels;
srcPixels = outPixels;
shouldWriteToOutPixels = 1;
}
}
}
}