#include #include #include #include #include #include #include "bitmapimage/BitmapImage.h" #include "pngimageio/PNGImageIO.h" static void printUsage() { fprintf(stderr, "Usage: circlegen [-w width] [-h height] [-t thickness] [-s samples_per_pixel] [-k softness] [-rgba] [-o out_file]\n"); } int main(int argc, char ** argv) { bool widthDefined = false, heightDefined = false; unsigned int width = 32, height = 32, thickness = 0, samplesPerPixel = 1; double softenedWidth, softenedHeight; float softness = 1.0f; bool rgba = false; int argIndex; const char * outFilePath = "a.png"; unsigned int sample; unsigned char * pixels; BitmapImage * image; unsigned int pixelIndexX, pixelIndexY; double fsqrtSamplesPerPixel; int isqrtSamplesPerPixel; double subpixelX, subpixelY, subpixelXLocal, subpixelYLocal; double distance; for (argIndex = 1; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "-w")) { if (argIndex >= argc - 1 || !sscanf(argv[++argIndex], "%u", &width)) { fprintf(stderr, "Error: Couldn't parse argument following -w as width\n"); printUsage(); return EXIT_FAILURE; } widthDefined = true; } else if (!strcmp(argv[argIndex], "-h")) { if (argIndex >= argc - 1 || !sscanf(argv[++argIndex], "%u", &height)) { fprintf(stderr, "Error: Couldn't parse argument following -h as height\n"); printUsage(); return EXIT_FAILURE; } heightDefined = true; } else if (!strcmp(argv[argIndex], "-t")) { if (argIndex >= argc - 1 || !sscanf(argv[++argIndex], "%u", &thickness)) { fprintf(stderr, "Error: Couldn't parse argument following -t as thickness\n"); printUsage(); return EXIT_FAILURE; } } else if (!strcmp(argv[argIndex], "-s")) { if (argIndex >= argc - 1 || !sscanf(argv[++argIndex], "%u", &samplesPerPixel)) { fprintf(stderr, "Error: Couldn't parse argument following -s as samples per pixel\n"); printUsage(); return EXIT_FAILURE; } } else if (!strcmp(argv[argIndex], "-k")) { if (argIndex >= argc - 1 || !sscanf(argv[++argIndex], "%f", &softness)) { fprintf(stderr, "Error: Couldn't parse argument following -k as softness\n"); printUsage(); return EXIT_FAILURE; } } else if (!strcmp(argv[argIndex], "-o")) { if (argIndex >= argc - 1) { fprintf(stderr, "Error: No output file path provided after -o argument\n"); printUsage(); return EXIT_FAILURE; } outFilePath = argv[++argIndex]; } else if (!strcmp(argv[argIndex], "-rgba")) { rgba = true; } else if (!strcmp(argv[argIndex], "-h") || !strcmp(argv[argIndex], "--help")) { printUsage(); return EXIT_SUCCESS; } } fsqrtSamplesPerPixel = sqrt(samplesPerPixel); isqrtSamplesPerPixel = fsqrtSamplesPerPixel; if (samplesPerPixel == 0) { fprintf(stderr, "Error: Samples per pixel must be greater than zero!\n"); return EXIT_FAILURE; } if (fsqrtSamplesPerPixel != isqrtSamplesPerPixel) { fprintf(stderr, "Error: Samples per pixel must be square (%u is not)\n", samplesPerPixel); return EXIT_FAILURE; } if (samplesPerPixel > 256) { fprintf(stderr, "Warning: Samples per pixel beyond 256 won't make a difference in output\n"); } if (widthDefined && !heightDefined) { height = width; } else if (heightDefined && !widthDefined) { width = height; } softenedWidth = width + (1 - softness); softenedHeight = height + (1 - softness); pixels = malloc(width * height * (rgba ? 4 : 1)); for (pixelIndexY = 0; pixelIndexY < height; pixelIndexY++) { for (pixelIndexX = 0; pixelIndexX < width; pixelIndexX++) { if (samplesPerPixel == 1) { subpixelX = (pixelIndexX + 0.5) / width * 2.0 - 1.0; subpixelY = (pixelIndexY + 0.5) / height * 2.0 - 1.0; distance = subpixelX * subpixelX + subpixelY * subpixelY; sample = (distance <= 1.0); if (thickness > 0) { subpixelX = (pixelIndexX + 0.5 - thickness) / (width - thickness - thickness) * 2.0 - 1.0; subpixelY = (pixelIndexY + 0.5 - thickness) / (height - thickness - thickness) * 2.0 - 1.0; distance = subpixelX * subpixelX + subpixelY * subpixelY; if (distance < 1.0) { sample = 0; } } } else { bool isSolid; sample = 0; for (int subpixelIndexY = 0; subpixelIndexY < isqrtSamplesPerPixel; subpixelIndexY++) { for (int subpixelIndexX = 0; subpixelIndexX < isqrtSamplesPerPixel; subpixelIndexX++) { subpixelXLocal = (((subpixelIndexX + 0.5f) - isqrtSamplesPerPixel / 2) * softness + isqrtSamplesPerPixel / 2) / fsqrtSamplesPerPixel; subpixelYLocal = (((subpixelIndexY + 0.5f) - isqrtSamplesPerPixel / 2) * softness + isqrtSamplesPerPixel / 2) / fsqrtSamplesPerPixel; subpixelX = (pixelIndexX + subpixelXLocal) / width * 2.0 - 1.0; subpixelY = (pixelIndexY + subpixelYLocal) / height * 2.0 - 1.0; distance = subpixelX * subpixelX + subpixelY * subpixelY; isSolid = (distance <= 1.0); if (thickness > 0) { subpixelX = ((pixelIndexX + subpixelXLocal) - width * 0.5) / ((softenedWidth - thickness - thickness) * 0.5); subpixelY = ((pixelIndexY + subpixelYLocal) - height * 0.5) / ((softenedHeight - thickness - thickness) * 0.5); distance = subpixelX * subpixelX + subpixelY * subpixelY; if (distance < 1.0) { isSolid = false; } } sample += isSolid; } } } if (rgba) { pixels[(pixelIndexY * width + pixelIndexX) * 4 + 0] = 0xFF; pixels[(pixelIndexY * width + pixelIndexX) * 4 + 1] = 0xFF; pixels[(pixelIndexY * width + pixelIndexX) * 4 + 2] = 0xFF; pixels[(pixelIndexY * width + pixelIndexX) * 4 + 3] = sample * 0xFF / samplesPerPixel; } else { pixels[pixelIndexY * width + pixelIndexX] = sample * 0xFF / samplesPerPixel; } } } image = BitmapImage_createWithPixelsNoCopy(rgba ? BITMAP_PIXEL_FORMAT_RGBA_8888 : BITMAP_PIXEL_FORMAT_GRAY_8, width, height, pixels, true); if (!PNGImageIO_writePNGFile(image, outFilePath, PNG_PIXEL_FORMAT_AUTOMATIC, false)) { fprintf(stderr, "Failed to write PNG to \"%s\" (errno = %d)\n", outFilePath, errno); return EXIT_FAILURE; } BitmapImage_dispose(image); return EXIT_SUCCESS; }