worst init ever

This commit is contained in:
2026-01-19 22:25:41 +01:00
commit 53ad022bba
7 changed files with 653 additions and 0 deletions

37
.clang-format Normal file
View File

@@ -0,0 +1,37 @@
Language: Cpp
BasedOnStyle: LLVM
AlignAfterOpenBracket: DontAlign
BreakConstructorInitializers: AfterColon
ConstructorInitializerAllOnOneLineOrOnePerLine: true
PointerAlignment: Left
SortIncludes: true
SpacesBeforeTrailingComments: 2
UseTab: Always
MaxEmptyLinesToKeep: 5
TabWidth: 4
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
IndentWidth: 4
IndentCaseLabels: true
ColumnLimit: 135
AlwaysBreakTemplateDeclarations: Yes
AllowShortFunctionsOnASingleLine: Empty
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
bad_apple.mp4 filter=lfs diff=lfs merge=lfs -text

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
# Xmake cache
.xmake/
build/
# MacOS Cache
.DS_Store
*.mp4
*.mkv

20
README.md Normal file
View File

@@ -0,0 +1,20 @@
# VideoToAscii
## Usage
Have a file called bad_apple.mp4 in the root.
Then,
```sh
xmake run
```
## Install libs
```sh
apt install libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev libpostproc-dev libswresample-dev libswscale-dev
```
## Future
I'm too lazy right now to continue this.

BIN
bad_apple.mp4 LFS Normal file

Binary file not shown.

499
src/main.cpp Normal file
View File

@@ -0,0 +1,499 @@
#include <EGL/egl.h>
#include <GL/glew.h>
#include <algorithm>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static constexpr const char vertexShader[] = R"(
#version 330
// Input vertex attributes
in vec2 vertexPosition;
in vec2 vertexTexCoord;
// Output vertex attributes (to fragment shader)
out vec2 fragTexCoord;
// NOTE: Add your custom variables here
void main()
{
// Send vertex attributes to fragment shader
fragTexCoord = vertexTexCoord;
// Calculate final vertex position
gl_Position = vec4(vertexPosition, 0.0, 1.0);
}
)";
static constexpr const char fragmentShader[] = R"(
#version 330
// Input from the vertex shader
in vec2 fragTexCoord;
// Output color for the screen
out vec4 finalColor;
uniform sampler2D texture0;
uniform vec2 resolution;
// Fontsize less then 9 may be not complete
uniform float fontSize;
float GreyScale(in vec3 col)
{
return dot(col, vec3(0.2126, 0.7152, 0.0722));
}
float GetCharacter(int n, vec2 p)
{
p = floor(p*vec2(-4.0, 4.0) + 2.5);
// Check if the coordinate is inside the 5x5 grid (0 to 4)
if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y)
{
int a = int(round(p.x) + 5.0*round(p.y));
if (((n >> a) & 1) == 1)
{
return 1.0;
}
}
return 0.0; // The bit is off, or we are outside the grid
}
// -----------------------------------------------------------------------------
// Main shader logic
// -----------------------------------------------------------------------------
void main()
{
vec2 charPixelSize = vec2(fontSize, fontSize);
vec2 uvCellSize = charPixelSize/resolution;
// The cell size is based on the fontSize set by application
vec2 cellUV = floor(fragTexCoord/uvCellSize)*uvCellSize;
vec3 cellColor = texture(texture0, cellUV).rgb;
// Gray is used to define what character will be selected to draw
float gray = GreyScale(cellColor);
int n = 4096;
// Character set from https://www.shadertoy.com/view/lssGDj
// Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/
// limited character set
// if (gray > 0.2) n = 65600; // :
// if (gray > 0.3) n = 163153; // *
// if (gray > 0.4) n = 15255086; // o
// if (gray > 0.5) n = 13121101; // &
// if (gray > 0.6) n = 15252014; // 8
// if (gray > 0.7) n = 13195790; // @
// if (gray > 0.8) n = 11512810; // #
if (gray > 0.0233) n = 4096;
if (gray > 0.0465) n = 131200;
if (gray > 0.0698) n = 4329476;
if (gray > 0.0930) n = 459200;
if (gray > 0.1163) n = 4591748;
if (gray > 0.1395) n = 12652620;
if (gray > 0.1628) n = 14749828;
if (gray > 0.1860) n = 18393220;
if (gray > 0.2093) n = 15239300;
if (gray > 0.2326) n = 17318431;
if (gray > 0.2558) n = 32641156;
if (gray > 0.2791) n = 18393412;
if (gray > 0.3023) n = 18157905;
if (gray > 0.3256) n = 17463428;
if (gray > 0.3488) n = 14954572;
if (gray > 0.3721) n = 13177118;
if (gray > 0.3953) n = 6566222;
if (gray > 0.4186) n = 16269839;
if (gray > 0.4419) n = 18444881;
if (gray > 0.4651) n = 18400814;
if (gray > 0.4884) n = 33061392;
if (gray > 0.5116) n = 15255086;
if (gray > 0.5349) n = 32045584;
if (gray > 0.5581) n = 18405034;
if (gray > 0.5814) n = 15022158;
if (gray > 0.6047) n = 15018318;
if (gray > 0.6279) n = 16272942;
if (gray > 0.6512) n = 18415153;
if (gray > 0.6744) n = 32641183;
if (gray > 0.6977) n = 32540207;
if (gray > 0.7209) n = 18732593;
if (gray > 0.7442) n = 18667121;
if (gray > 0.7674) n = 16267326;
if (gray > 0.7907) n = 32575775;
if (gray > 0.8140) n = 15022414;
if (gray > 0.8372) n = 15255537;
if (gray > 0.8605) n = 32032318;
if (gray > 0.8837) n = 32045617;
if (gray > 0.9070) n = 33081316;
if (gray > 0.9302) n = 32045630;
if (gray > 0.9535) n = 33061407;
if (gray > 0.9767) n = 11512810;
vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0]
vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0]
vec3 color = cellColor*GetCharacter(n, p);
finalColor = vec4(color, 1.0);
// finalColor = texture(texture0, fragTexCoord);
}
)";
static unsigned int vertexShaderID, fragmentShaderID, programID;
static unsigned int location_resolution, location_fontSize;
static unsigned int location_texture0;
static constexpr float vertexData[] = {
-1,
-1,
0,
0,
1,
-1,
1,
0,
-1,
1,
0,
1,
1,
1,
1,
1,
-1,
1,
0,
1,
1,
-1,
1,
0,
};
static const EGLint configAttribs[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8,
EGL_DEPTH_SIZE, 8,
// Uncomment the following to enable MSAA
// EGL_SAMPLE_BUFFERS, 1, // <-- Must be set to 1 to enable multisampling!
// EGL_SAMPLES, 4, // <-- Number of samples
// Uncomment the following to enable stencil buffer
EGL_STENCIL_SIZE, 1,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE};
static const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
static const char* eglGetErrorStr() {
switch (eglGetError()) {
case EGL_SUCCESS:
return "The last function succeeded without error.";
case EGL_NOT_INITIALIZED:
return "EGL is not initialized, or could not be initialized, for the "
"specified EGL display connection.";
case EGL_BAD_ACCESS:
return "EGL cannot access a requested resource (for example a context "
"is bound in another thread).";
case EGL_BAD_ALLOC:
return "EGL failed to allocate resources for the requested operation.";
case EGL_BAD_ATTRIBUTE:
return "An unrecognized attribute or attribute value was passed in the "
"attribute list.";
case EGL_BAD_CONTEXT:
return "An EGLContext argument does not name a valid EGL rendering "
"context.";
case EGL_BAD_CONFIG:
return "An EGLConfig argument does not name a valid EGL frame buffer "
"configuration.";
case EGL_BAD_CURRENT_SURFACE:
return "The current surface of the calling thread is a window, pixel "
"buffer or pixmap that is no longer valid.";
case EGL_BAD_DISPLAY:
return "An EGLDisplay argument does not name a valid EGL display "
"connection.";
case EGL_BAD_SURFACE:
return "An EGLSurface argument does not name a valid surface (window, "
"pixel buffer or pixmap) configured for GL rendering.";
case EGL_BAD_MATCH:
return "Arguments are inconsistent (for example, a valid context "
"requires buffers not supplied by a valid surface).";
case EGL_BAD_PARAMETER:
return "One or more argument values are invalid.";
case EGL_BAD_NATIVE_PIXMAP:
return "A NativePixmapType argument does not refer to a valid native "
"pixmap.";
case EGL_BAD_NATIVE_WINDOW:
return "A NativeWindowType argument does not refer to a valid native "
"window.";
case EGL_CONTEXT_LOST:
return "A power management event has occurred. The application must "
"destroy all contexts and reinitialise OpenGL ES state and "
"objects to continue rendering.";
default:
break;
}
return "Unknown error!";
}
static GLint width, height;
static EGLContext context;
static EGLDisplay display;
static EGLSurface surface;
bool createContext(const int& viewport_width, const int& viewport_height) {
width = viewport_width;
height = viewport_height;
int major, minor;
if ((display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) {
fprintf(stderr, "Failed to get EGL display! Error: %s\n", eglGetErrorStr());
return false;
}
if (eglInitialize(display, &major, &minor) == EGL_FALSE) {
fprintf(stderr, "Failed to get EGL version! Error: %s\n", eglGetErrorStr());
eglTerminate(display);
return false;
}
printf("Initialized EGL version: %d.%d\n", major, minor);
EGLint numConfigs;
EGLConfig config;
if (!eglChooseConfig(display, configAttribs, &config, 1, &numConfigs)) {
fprintf(stderr, "Failed to get EGL config! Error: %s\n", eglGetErrorStr());
eglTerminate(display);
return false;
}
//----------------------------------------------
EGLint pbufferAttribs[] = {
EGL_WIDTH,
width,
EGL_HEIGHT,
height,
EGL_NONE,
};
surface = eglCreatePbufferSurface(display, config, pbufferAttribs);
if (surface == EGL_NO_SURFACE) {
fprintf(stderr, "Failed to create EGL surface! Error: %s\n", eglGetErrorStr());
eglTerminate(display);
return false;
}
eglBindAPI(EGL_OPENGL_API);
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (context == EGL_NO_CONTEXT) {
fprintf(stderr, "Failed to create EGL context! Error: %s\n", eglGetErrorStr());
eglDestroySurface(display, surface);
eglTerminate(display);
return false;
}
eglMakeCurrent(display, surface, surface, context);
// Set GL Viewport size, always needed!
glViewport(0, 0, width, height);
// Get GL Viewport size and test if it is correct.
// NOTE! DO NOT UPDATE EGL LIBRARY ON RASPBERRY PI AS IT WILL INSTALL FAKE
// EGL! If you have fake/faulty EGL library, the glViewport and
// glGetIntegerv won't work! The following piece of code checks if the gl
// functions are working as intended!
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
// viewport[2] and viewport[3] are viewport width and height respectively
printf("GL Viewport size: %dx%d\n", viewport[2], viewport[3]);
// Test if the desired width and height match the one returned by
// glGetIntegerv
if (width != viewport[2] || height != viewport[3]) {
fprintf(stderr, "Error! The glViewport/glGetIntegerv are not working! "
"EGL might be faulty!\n");
return false;
}
return true;
}
unsigned char* getRawPixels() {
// Create buffer to hold entire front buffer pixels
// We multiply width and height by 4 to because we use RGBA!
const int comp = 3;
const int dataSize = width * height * comp;
unsigned char* buffer = new unsigned char[dataSize];
glReadPixels(0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, buffer);
/*if (flipped){
unsigned char flipPixels[dataSize];
for (int i = 0; i < width; ++i){
for (int j = 0; j < height; ++j){
for (int k = 0; k < comp; ++k){
flipPixels[(i + j * width) * comp + k] = buffer[(i
+ (height - 1 - j) * width) * comp + k];
}
}
}
std::copy(flipPixels, flipPixels + dataSize, buffer);
}*/
return buffer;
}
void destroyContext() {
// Cleanup
eglDestroyContext(display, context);
eglDestroySurface(display, surface);
eglTerminate(display);
}
unsigned int initShader(GLenum type, const char* shader, int size) {
unsigned int shaderID = glCreateShader(type);
glShaderSource(shaderID, 1, &shader, &size); // @suppress("Function cannot be resolved")
glCompileShader(shaderID);
GLint compilesuccessful;
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compilesuccessful);
if (compilesuccessful == (int)GL_FALSE) {
std::cout << "Could not compile shader !" << std::endl;
GLsizei size;
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &size);
char error[size];
glGetShaderInfoLog(shaderID, size, &size, error);
std::cout << error;
}
return shaderID;
}
void initShaders(int width, int height) {
programID = glCreateProgram();
vertexShaderID = initShader(GL_VERTEX_SHADER, vertexShader, sizeof(vertexShader));
fragmentShaderID = initShader(GL_FRAGMENT_SHADER, fragmentShader, sizeof(fragmentShader));
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
glBindAttribLocation(vertexShaderID, 0, "vertexPosition");
glBindAttribLocation(vertexShaderID, 1, "vertexTexCoord");
glLinkProgram(programID);
glValidateProgram(programID);
location_texture0 = glGetUniformLocation(programID, "texture0");
location_resolution = glGetUniformLocation(programID, "resolution");
location_fontSize = glGetUniformLocation(programID, "fontSize");
glUseProgram(programID);
glUniform2f(location_resolution, width, height);
glUniform1f(location_fontSize, 6.0f);
}
unsigned int vboID;
void initGL(int width, int height) {
if (!glewInit()) {
std::cout << "Failed to initialize glew!\n";
}
initShaders(width, height);
glCreateBuffers(1, &vboID);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 4 * sizeof(float), (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 4 * sizeof(float), (void*)8);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
}
void loadTextureImage(int textureID, int width, int height, const cv::Mat& image) {
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// This prevents having weird edges
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, image.ptr());
}
void draw() {
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
}
int main() {
cv::VideoCapture video("bad_apple.mp4");
int width = video.get(cv::VideoCaptureProperties::CAP_PROP_FRAME_WIDTH);
int height = video.get(cv::VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT);
int frameCount = video.get(cv::VideoCaptureProperties::CAP_PROP_FRAME_COUNT);
int fps = video.get(cv::VideoCaptureProperties::CAP_PROP_FPS);
cv::VideoWriter output("outcpp.mkv", cv::VideoWriter::fourcc('H', '2', '6', '4'), fps, cv::Size(width, height));
createContext(width, height);
initGL(width, height);
int currentFrame = 0;
unsigned int texture0;
glGenTextures(1, &texture0);
glEnable(GL_TEXTURE_2D);
glUseProgram(programID);
int writtenFrame = 0;
std::cout << "Converting...\n";
auto start = std::chrono::system_clock::now();
for (int i = 0; i < frameCount; i++) {
std::cout << "\rFrame " << i << " / " << frameCount;
cv::Mat image;
video >> image;
glActiveTexture(GL_TEXTURE0);
loadTextureImage(texture0, width, height, image);
writtenFrame++;
draw();
unsigned char* pixels = getRawPixels();
cv::Mat pixelArray(cv::Size(width, height), CV_8UC3, cv::Scalar(0, 0, 0));
pixelArray.data = pixels;
output << pixelArray;
pixelArray.release();
delete pixels;
writtenFrame++;
image.release();
}
std::cout << std::endl;
auto end = std::chrono::system_clock::now();
std::cout << "Done in " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << "s!\n";
destroyContext();
}

83
xmake.lua Normal file
View File

@@ -0,0 +1,83 @@
add_rules("mode.debug", "mode.release")
add_requires("ffmpeg", {system = true})
add_requires("opencv", "glew")
add_requires("raylib")
target("VideoToAscii")
set_kind("binary")
add_packages("opencv", "glew")
add_links("EGL")
add_files("src/main.cpp")
set_rundir(".")
--
-- If you want to known more usage about xmake, please see https://xmake.io
--
-- ## FAQ
--
-- You can enter the project directory firstly before building project.
--
-- $ cd projectdir
--
-- 1. How to build project?
--
-- $ xmake
--
-- 2. How to configure project?
--
-- $ xmake f -p [macosx|linux|iphoneos ..] -a [x86_64|i386|arm64 ..] -m [debug|release]
--
-- 3. Where is the build output directory?
--
-- The default output directory is `./build` and you can configure the output directory.
--
-- $ xmake f -o outputdir
-- $ xmake
--
-- 4. How to run and debug target after building project?
--
-- $ xmake run [targetname]
-- $ xmake run -d [targetname]
--
-- 5. How to install target to the system directory or other output directory?
--
-- $ xmake install
-- $ xmake install -o installdir
--
-- 6. Add some frequently-used compilation flags in xmake.lua
--
-- @code
-- -- add debug and release modes
-- add_rules("mode.debug", "mode.release")
--
-- -- add macro definition
-- add_defines("NDEBUG", "_GNU_SOURCE=1")
--
-- -- set warning all as error
-- set_warnings("all", "error")
--
-- -- set language: c99, c++11
-- set_languages("c99", "c++11")
--
-- -- set optimization: none, faster, fastest, smallest
-- set_optimize("fastest")
--
-- -- add include search directories
-- add_includedirs("/usr/include", "/usr/local/include")
--
-- -- add link libraries and search directories
-- add_links("tbox")
-- add_linkdirs("/usr/local/lib", "/usr/lib")
--
-- -- add system link libraries
-- add_syslinks("z", "pthread")
--
-- -- add compilation and link flags
-- add_cxflags("-stdnolib", "-fno-strict-aliasing")
-- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true})
--
-- @endcode
--