Skip to content

Instantly share code, notes, and snippets.

@damywise
Last active October 21, 2023 08:44
Show Gist options
  • Save damywise/8d8328ad77b9eb2add7d39012864ce38 to your computer and use it in GitHub Desktop.
Save damywise/8d8328ad77b9eb2add7d39012864ce38 to your computer and use it in GitHub Desktop.
Using SDL to generate images with dart and isolates.
import 'dart:ffi';
import 'dart:io';
import 'dart:math';
import 'package:ffi/ffi.dart';
import 'package:sdl2/sdl2.dart';
class FontData {
FontData(this.fontSource, this.fontSize);
final String fontSource;
final int fontSize;
}
int exportImages(List<String> texts, int outputNumber, bool premium) {
final fontData = FontData('assets/fonts/ALS-Script.ttf', 148);
for (var i = 0; i < texts.length; i++) {
while (texts[0].contains(' ')) {
texts[0] = texts[0].replaceAll(RegExp(r'\s{2,}'), ' ');
}
}
final outputDir = 'outputs_KMO$outputNumber';
final cert = {true: 'cert_premium', false: 'cert_normal'};
final backgroundPath = 'assets/${cert[premium]}.jpg';
final img = imgLoad(backgroundPath);
if (img == nullptr) {
return -1;
}
final window = sdlCreateWindow(
'Render Certs_${texts[0]}',
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
img.ref.w,
img.ref.h,
SDL_WINDOW_HIDDEN);
for (var i = 0; i < texts.length; i++) {
final name = texts[i];
renderImage(window, img, fontData, name,
'$outputDir${Platform.pathSeparator}$name.jpg', premium);
}
sdlFreeSurface(img);
sdlDestroyWindow(window);
return 0;
}
void renderImage(Pointer<SdlWindow> window, Pointer<SdlSurface> img,
FontData fontData, String text, String output, bool premium) {
final renderer = sdlCreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
final tImg = sdlCreateTextureFromSurface(renderer, img);
final zw = calloc<Int32>()..value = 0;
final zh = calloc<Int32>()..value = 0;
sdlQueryTexture(tImg, nullptr, nullptr, zw, zh);
final dstRect =
Rectangle<double>(0, 0, zw.value.toDouble(), zh.value.toDouble());
final dstRectPointer = dstRect.calloc();
sdlRenderCopy(renderer, tImg, nullptr, dstRectPointer);
var surface = sdlCreateRgbSurfaceWithFormat(
0, img.ref.w, img.ref.h, 32, SDL_PIXELFORMAT_RGBA8888);
sdlRenderReadPixels(
renderer,
nullptr,
surface.ref.format.ref.format,
surface.ref.pixels,
surface.ref.pitch,
);
renderText(renderer, fontData, text, premium);
sdlRenderReadPixels(
renderer,
nullptr,
surface.ref.format.ref.format,
surface.ref.pixels,
surface.ref.pitch,
);
zw.callocFree();
zh.callocFree();
imgSaveJpg(surface, output, 99);
sdlDestroyTexture(tImg);
sdlFreeSurface(surface);
sdlDestroyRenderer(renderer);
}
void renderText(Pointer<SdlRenderer> renderer, FontData fontData, String text,
bool premium) {
var font = ttfOpenFont(fontData.fontSource, fontData.fontSize);
ttfSetFontHinting(font, TTF_HINTING_NORMAL);
final color = premium ? hexToRGBA('000000FF') : hexToRGBA('38241BFF');
final zw = calloc<Int32>()..value = 0;
final zh = calloc<Int32>()..value = 0;
final textWidth = calloc<Int32>()..value = 0;
final textHeight = calloc<Int32>()..value = 0;
ttfSizeText(font, text, textWidth, textHeight);
var newFontSize = fontData.fontSize;
const textMaxHeight = 150;
while (textHeight.value > textMaxHeight) {
newFontSize -= 1;
font = ttfOpenFont(fontData.fontSource, newFontSize);
ttfSizeText(font, text, textWidth, textHeight);
}
final leftMargin = premium ? 300 : 187;
final rightMargin = premium ? 300 : 187;
final imgWid = premium ? 2000 : 2339;
final textMaxWidth = imgWid - leftMargin - rightMargin;
var newRectW = textWidth;
var newRectH = textHeight;
if (newRectW.value > textMaxWidth) {
newRectH.value = textHeight.value * textMaxWidth ~/ textWidth.value;
newRectW.value = textWidth.value * textMaxWidth ~/ textWidth.value;
}
final tSurf = ttfRenderTextBlendedWrapped(font, text,
(calloc<SdlColor>()..setRgba(color.r, color.g, color.b, color.a)).ref, 0);
final tText = sdlCreateTextureFromSurface(renderer, tSurf);
final topMargin =
(premium ? 660 : 840) + (textMaxHeight - newRectH.value) / 2;
sdlQueryTexture(tText, nullptr, nullptr, zw, zh);
final dstRect = Rectangle<double>(imgWid / 2 - newRectW.value / 2, topMargin,
newRectW.value.toDouble(), newRectH.value.toDouble())
.calloc();
sdlRenderCopy(renderer, tText, nullptr, dstRect);
sdlDestroyTexture(tText);
sdlFreeSurface(tSurf);
}
class RGBA {
final int r;
final int g;
final int b;
final int a;
RGBA(this.r, this.g, this.b, this.a);
@override
String toString() {
return 'RGBA($r, $g, $b, $a)';
}
}
RGBA hexToRGBA(String hexColor) {
hexColor = hexColor.toUpperCase().replaceAll("#", "");
if (hexColor.length == 6) {
hexColor = "FF$hexColor";
}
int hexVal = int.parse(hexColor, radix: 16);
return RGBA(
(hexVal >> 24) & 255,
(hexVal >> 16) & 255,
(hexVal >> 8) & 255,
hexVal & 255,
);
}
import 'dart:ffi';
import 'dart:io';
import 'dart:math';
import 'package:ffi/ffi.dart';
import 'package:sdl2/sdl2.dart';
class FontData {
FontData(this.fontSource, this.fontSize);
final String fontSource;
final int fontSize;
}
int exportImages(List<String> texts, int outputNumber, bool premium) {
final fontData = FontData('assets/fonts/ALS-Script.ttf', 148);
for (var i = 0; i < texts.length; i++) {
while (texts[0].contains(' ')) {
texts[0] = texts[0].replaceAll(RegExp(r'\s{2,}'), ' ');
}
}
final outputDir = 'outputs_KMO$outputNumber';
final cert = {true: 'cert_premium', false: 'cert_normal'};
final backgroundPath = 'assets/${cert[premium]}.jpg';
final img = imgLoad(backgroundPath);
if (img == nullptr) {
return -1;
}
final window = sdlCreateWindow(
'Render Certs_${texts[0]}',
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
img.ref.w,
img.ref.h,
SDL_WINDOW_HIDDEN);
for (var i = 0; i < texts.length; i++) {
final name = texts[i];
renderImage(window, img, fontData, name,
'$outputDir${Platform.pathSeparator}$name.jpg', premium);
}
sdlFreeSurface(img);
sdlDestroyWindow(window);
return 0;
}
void renderImage(Pointer<SdlWindow> window, Pointer<SdlSurface> img,
FontData fontData, String text, String output, bool premium) {
final renderer = sdlCreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
final tImg = sdlCreateTextureFromSurface(renderer, img);
final zw = calloc<Int32>()..value = 0;
final zh = calloc<Int32>()..value = 0;
sdlQueryTexture(tImg, nullptr, nullptr, zw, zh);
final dstRect =
Rectangle<double>(0, 0, zw.value.toDouble(), zh.value.toDouble());
final dstRectPointer = dstRect.calloc();
sdlRenderCopy(renderer, tImg, nullptr, dstRectPointer);
var surface = sdlCreateRgbSurfaceWithFormat(
0, img.ref.w, img.ref.h, 32, SDL_PIXELFORMAT_RGBA8888);
sdlRenderReadPixels(
renderer,
nullptr,
surface.ref.format.ref.format,
surface.ref.pixels,
surface.ref.pitch,
);
renderText(renderer, fontData, text, premium);
sdlRenderReadPixels(
renderer,
nullptr,
surface.ref.format.ref.format,
surface.ref.pixels,
surface.ref.pitch,
);
zw.callocFree();
zh.callocFree();
imgSaveJpg(surface, output, 99);
sdlDestroyTexture(tImg);
sdlFreeSurface(surface);
sdlDestroyRenderer(renderer);
}
void renderText(Pointer<SdlRenderer> renderer, FontData fontData, String text,
bool premium) {
var font = ttfOpenFont(fontData.fontSource, fontData.fontSize);
ttfSetFontHinting(font, TTF_HINTING_NORMAL);
final color = premium ? hexToRGBA('000000FF') : hexToRGBA('38241BFF');
final zw = calloc<Int32>()..value = 0;
final zh = calloc<Int32>()..value = 0;
final textWidth = calloc<Int32>()..value = 0;
final textHeight = calloc<Int32>()..value = 0;
ttfSizeText(font, text, textWidth, textHeight);
var newFontSize = fontData.fontSize;
const textMaxHeight = 150;
while (textHeight.value > textMaxHeight) {
newFontSize -= 1;
font = ttfOpenFont(fontData.fontSource, newFontSize);
ttfSizeText(font, text, textWidth, textHeight);
}
final leftMargin = premium ? 300 : 187;
final rightMargin = premium ? 300 : 187;
final imgWid = premium ? 2000 : 2339;
final textMaxWidth = imgWid - leftMargin - rightMargin;
var newRectW = textWidth;
var newRectH = textHeight;
if (newRectW.value > textMaxWidth) {
newRectH.value = textHeight.value * textMaxWidth ~/ textWidth.value;
newRectW.value = textWidth.value * textMaxWidth ~/ textWidth.value;
}
final tSurf = ttfRenderTextBlendedWrapped(font, text,
(calloc<SdlColor>()..setRgba(color.r, color.g, color.b, color.a)).ref, 0);
final tText = sdlCreateTextureFromSurface(renderer, tSurf);
final topMargin =
(premium ? 660 : 840) + (textMaxHeight - newRectH.value) / 2;
sdlQueryTexture(tText, nullptr, nullptr, zw, zh);
final dstRect = Rectangle<double>(imgWid / 2 - newRectW.value / 2, topMargin,
newRectW.value.toDouble(), newRectH.value.toDouble())
.calloc();
sdlRenderCopy(renderer, tText, nullptr, dstRect);
sdlDestroyTexture(tText);
sdlFreeSurface(tSurf);
}
class RGBA {
final int r;
final int g;
final int b;
final int a;
RGBA(this.r, this.g, this.b, this.a);
@override
String toString() {
return 'RGBA($r, $g, $b, $a)';
}
}
RGBA hexToRGBA(String hexColor) {
hexColor = hexColor.toUpperCase().replaceAll("#", "");
if (hexColor.length == 6) {
hexColor = "FF$hexColor";
}
int hexVal = int.parse(hexColor, radix: 16);
return RGBA(
(hexVal >> 24) & 255,
(hexVal >> 16) & 255,
(hexVal >> 8) & 255,
hexVal & 255,
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment