txlyre 10 months ago
commit
e4a5d80ac3
15 changed files with 166 additions and 0 deletions
  1. 66 0
      app.py
  2. 9 0
      config.py
  3. 60 0
      counter.c/counter.c
  4. 11 0
      counter.c/counter.h
  5. 20 0
      counter.c/main.c
  6. BIN
      images/0.png
  7. BIN
      images/1.png
  8. BIN
      images/2.png
  9. BIN
      images/3.png
  10. BIN
      images/4.png
  11. BIN
      images/5.png
  12. BIN
      images/6.png
  13. BIN
      images/7.png
  14. BIN
      images/8.png
  15. BIN
      images/9.png

+ 66 - 0
app.py

@@ -0,0 +1,66 @@
+import asyncio
+from typing import Annotated
+
+import redis.asyncio as aioredis
+from aiofiles.tempfile import NamedTemporaryFile
+from fastapi import FastAPI, Path
+from fastapi.responses import Response
+
+from config import Config
+
+config = Config('config.hjson')
+redis = None
+
+app = FastAPI()
+
+@app.get(
+  '/counter/{name}',
+  responses = {
+    200: {
+      'content': {'image/png': {}}
+    }
+  },
+  response_class=Response
+)
+async def get_counter(
+  name: Annotated[
+    str,
+    Path(
+      min_length=1,
+      max_length=50,
+      pattern=r'^[a-zA-Z0-9_]+$'
+    )
+  ]
+):
+  if await redis.exists(name):
+    number = await redis.get(name)
+    number = int(number) + 1
+  else:
+    number = 1
+
+  await redis.set(name, str(number))
+
+  async with NamedTemporaryFile('rb') as f:
+    proc = await asyncio.create_subprocess_shell(
+      f'{config.CounterPath} {number} {f.name}'
+    )
+
+    await proc.communicate()
+
+    return Response(
+      content=await f.read(),
+      media_type='image/png'
+    )
+
+@app.on_event('startup')
+async def startup_event():
+  global redis
+
+  redis = await aioredis.from_url(
+    config.RedisURL
+  )
+
+@app.on_event('shutdown')
+async def shutdown_event():
+  if redis is not None:
+    await redis.close()

+ 9 - 0
config.py

@@ -0,0 +1,9 @@
+import hjson
+
+class Config:
+  def __init__(self, filename):
+    with open(filename, 'r') as f:
+      self.config = hjson.load(f)
+
+  def __getattr__(self, name):
+    return self.config[name]

+ 60 - 0
counter.c/counter.c

@@ -0,0 +1,60 @@
+#include <math.h>
+#include <stdio.h>
+#include <cairo/cairo.h>
+#include "counter.h"
+
+static cairo_surface_t *digits[10];
+
+void counter_init(void) {
+  char buffer[16];
+
+  for (int i = 0; i < 10; i++) {
+    snprintf(buffer, sizeof(buffer), "./images/%d.png", i);
+
+    digits[i] = cairo_image_surface_create_from_png(buffer);
+  }
+}
+
+void counter_release(void) {
+  for (int i = 0; i < 10; i++)
+    cairo_surface_destroy(digits[i]);
+}
+
+static int count_digits(int number) {
+  if (number < 10) return 1;
+  if (number < 100) return 2;
+  if (number < 1000) return 3;
+  if (number < 10000) return 4;
+  if (number < 100000) return 5;
+  if (number < 1000000) return 6;
+  if (number < 10000000) return 7;
+  if (number < 100000000) return 8;
+  if (number < 1000000000) return 9;
+
+  return 10;
+}
+
+static int powersof10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
+
+static int nth_digit(int number, int n) {
+  return (number / powersof10[n]) % 10;
+}
+
+void counter_create(int number, char *filename) {
+  int digits_count;
+  cairo_surface_t *surface;
+  cairo_t *cr;
+
+  digits_count = count_digits(number);
+  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, digits_count * DIGIT_WIDTH, DIGIT_HEIGHT);
+  cr = cairo_create(surface);
+ 
+  for (int i = 0; i < digits_count; i++) {
+    cairo_set_source_surface(cr, digits[nth_digit(number, digits_count-1-i)], i * DIGIT_WIDTH, 0);
+    cairo_paint(cr);
+  }
+
+  cairo_surface_write_to_png(surface, filename);
+  cairo_surface_destroy(surface);
+  cairo_destroy(cr);
+}

+ 11 - 0
counter.c/counter.h

@@ -0,0 +1,11 @@
+#ifndef _COUNTER_H
+#define _COUNTER_H
+
+#define DIGIT_WIDTH  68
+#define DIGIT_HEIGHT 150
+
+void counter_init(void);
+void counter_release(void);
+void counter_create(int number, char *filename);
+
+#endif

+ 20 - 0
counter.c/main.c

@@ -0,0 +1,20 @@
+#include <stdlib.h>
+
+#include "counter.h"
+
+int main(int argc, char **argv) {
+  int number;
+
+  if (argc != 3)
+    return 1;
+
+  number = atoi(*argv[1] == '-'? argv[1]+1: argv[1]);
+
+  counter_init();
+
+  counter_create(number, argv[2]);
+
+  counter_release();
+
+  return 0;
+}

BIN
images/0.png


BIN
images/1.png


BIN
images/2.png


BIN
images/3.png


BIN
images/4.png


BIN
images/5.png


BIN
images/6.png


BIN
images/7.png


BIN
images/8.png


BIN
images/9.png