Comments (4)
Lol, sorry for any confusion - quite literally I've had three people today contact me via youtube, discord and github regarding improved algorithms for this, so I'm getting mixed up - If I've mistakenly interacted with you in anyway, I apologise - The imagery looks great - I'll do a follow up video to this to show off your findings.
from javidx9.
This is the Simplify version (higher performance with lower quality)
result image https://imgur.com/6BwxkJU https://imgur.com/fKzXyNs https://imgur.com/Y8yENnp
color distribution in coordinate https://imgur.com/PjBUlAT
(because my algorithm generate RGB lookuptable on the fly ,the 16 default color palletes could be change for better color distribution in the RGB coordinate. In the machine and terminal section of this link(https://en.wikipedia.org/wiki/List_of_color_palettes ) show lots of color palletes of difference console if we choose their color palletes we could mimic those console color style(well need to modify the color value accordingly in RGB ColorTable[16] then the algorithm will generate newRGB table)
the original implementation need some optimization though,in the 3,3,3 surrounding cube look up I silly
create vector to store all color in those cube to lookup this could be change by only remember the small's distance's r,g,b,coordinate.It will perform much better.(already updated)
#include<stdio.h>
#include"olcConsoleGameEngine.h"
#include<algorithm>
#include<string>
#include"lodepng.h"
std::vector<unsigned char> vecPng;
unsigned imageWidth, imageHeight;
class testConsole :public olcConsoleGameEngine
{
virtual bool OnUserCreate()
{
//png
pngWidth = imageWidth;
pngHeight = imageHeight;
//dither buff
dither = new RGB[sizeof(RGB)*pngWidth*pngHeight * 3];
memset(dither, 0, sizeof(RGB)*pngWidth*pngHeight * 3);
ccbToRGB.clear();
for (int f = 0; f < 16; ++f)
for (int b = 0; b < 16; ++b)
for (int p = 0; p < 4; ++p)
{
RGB newColor = ColorTable[f] * percent[p] + ColorTable[b] * (1 - percent[p]);
newColor.clip();
ccbToRGB.push_back({ {f,b,p} ,newColor });
}
//delete duplicate color
for (auto i = ccbToRGB.begin(); i != ccbToRGB.end();)
{
CTR::iterator next = i + 1;
while (next != ccbToRGB.end())
{
if (i->second == next->second)
next = ccbToRGB.erase(next);
else
++next;
}
if (i != ccbToRGB.end())++i;
}
int n = 0;
for (auto a : ccbToRGB)
{
int r = a.second.r, b = a.second.b, g = a.second.g;
_3DTABLE[r / 64][g / 64][b / 64].push_back(a);
++n;
}
//
for (int _x = 0; _x < pngWidth; ++_x)
for (int _y = 0; _y < pngHeight; ++_y)
{
int pngIndex = (_y*pngWidth + _x);
//how to find a color
RGB color = { vecPng[pngIndex * 4 + 0],vecPng[pngIndex * 4 + 1],vecPng[pngIndex * 4 + 2] };
color = color + dither[pngIndex];
color.clip();
CTR* colorToCompare=&_3DTABLE[color.r/64][color.g/64][color.b/64];
int l = 12288+1;
size_t index = 0;
for (size_t i = 0; i < colorToCompare->size(); ++i)
{
int newl = (colorToCompare->at(i).second - color).sqr();
if (newl < l)
{
l = newl;
index = i;
}
}
CCB ccb = colorToCompare->at(index).first;
RGB error = color - colorToCompare->at(index).second;// use for error diffusion, totally up to you
if (_x + 1 < pngWidth)
dither[_y*pngWidth + _x + 1] = error * (7.0f / 16.0f);
if (_y + 1 < pngHeight)
{
if (_x - 1 >= 0)
dither[(_y + 1)*pngWidth + _x - 1] = error * (3.0f / 16.0f);
dither[(_y + 1)*pngWidth + _x] = error * (5.0f / 16.0f);
if (_x + 1 < pngWidth)
dither[(_y + 1)*pngWidth + _x + 1] = error * (1.0f / 16.0f);
}
Draw(_x, _y, pixelP[ccb.p], ccb.f | (ccb.b << 4));
}
return true;
}
virtual bool OnUserUpdate(float _fDeltaTime)
{
return true;
}
struct RGB
{
int r;
int g;
int b;
RGB operator+(RGB _t)
{
return { r + _t.r,g + _t.g,b + _t.b };
}
RGB operator-(RGB _t)
{
return { r - _t.r,g - _t.g,b - _t.b };
}
RGB operator*(float _f)
{
return { (int)((float)r*_f),(int)((float)g*_f),(int)((float)b*_f) };
}
bool operator==(RGB _t)
{
return r == _t.r&&g == _t.g&&b == _t.b;
}
int sqr()
{
return r * r + g * g + b * b;
}
void clip()
{
if (r < 0) r = 0;
if (r >= 256) r = 255;
if (g < 0) g = 0;
if (g >= 256) g = 255;
if (b < 0) b = 0;
if (b >= 256) b = 255;
}
};
//console color block
struct CCB
{
//foreground color
int f;
//background color
int b;
//block type
int p;
};
RGB ColorTable[16] =
{
{0,0,0},
{0,0,128},
{0,128,0},
{0,128,128},
{128,0,0},
{128,0,128},
{128,128,0},
{192,192,192},
{128,128,128},
{0,0,256},
{0,256,0},
{0,256,256},
{256,0,0},
{256,0,256},
{256,256,0},
{256,256,256},
};
typedef std::vector<std::pair<CCB, RGB>> CTR;
std::vector<std::pair<CCB, RGB>> ccbToRGB;
CTR _3DTABLE[4][4][4];
//ForeGroundBlockPercent
float percent[4] = { 1,0.75,0.5,0.25 };
short pixelP[4] = { PIXEL_SOLID,PIXEL_THREEQUARTERS,PIXEL_HALF,PIXEL_QUARTER };
RGB *dither;
unsigned pngWidth, pngHeight;
};
int main()
{
const char* filename = "C:/test_PNG/van_gogh.png";
// const char* filename = "C:/test_PNG/olc.png";
unsigned error = lodepng::decode(vecPng, imageWidth, imageHeight, filename);
if (error) {
std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
return false;
}
testConsole test;
test.ConstructConsole(imageWidth, imageHeight, 4, 4);
// test.ConstructConsole(100, 100, 8, 8);
test.Start();
return 0;
}
from javidx9.
here's the 9x9x9 version without dither effect (I just thinking well the 9x9x9 is making the best approximation maybe I could get out of dither overhead for performance gain this is about 32 error range according to the dither method(I didn't do the 4x4x4 without dither because the 64 error range will have significant visual difference when I pick color to compare in photoshop)
some result of this version https://imgur.com/bd4ZHZv https://imgur.com/yVSZhLA https://imgur.com/erAl3D4
#include<stdio.h>
#include"olcConsoleGameEngine.h"
#include<algorithm>
#include<string>
#include"lodepng.h"
std::vector<unsigned char> vecPng;
unsigned imageWidth, imageHeight;
class testConsole :public olcConsoleGameEngine
{
virtual bool OnUserCreate()
{
pngWidth = imageWidth;
pngHeight = imageHeight;
// dither = new RGB[sizeof(RGB)*pngWidth*pngHeight * 3];
// memset(dither, 0, sizeof(RGB)*pngWidth*pngHeight * 3);
ccbToRGB.clear();
for (int f = 0; f < 16; ++f)
for (int b = 0; b < 16; ++b)
for (int p = 0; p < 4; ++p)
{
ccbToRGB.push_back({ {f,b,p} ,ColorTable[f] * percent[p] + ColorTable[b] * (1 - percent[p]) });
}
//delete duplicate color
for (auto i = ccbToRGB.begin(); i != ccbToRGB.end();)
{
CTR::iterator next = i + 1;
while (next != ccbToRGB.end())
{
if (i->second == next->second)
next = ccbToRGB.erase(next);
else
++next;
}
if (i != ccbToRGB.end())++i;
}
int n = 0;
for (auto a : ccbToRGB)
{
int r = a.second.r, b = a.second.b, g = a.second.g;
_3DTABLE[r / 32][g / 32][b / 32].push_back(a);
++n;
}
//
for (int _x = 0; _x < pngWidth; ++_x)
for (int _y = 0; _y < pngHeight; ++_y)
{
int pngIndex = (_y*pngWidth + _x);
//how to find a color
RGB color = { vecPng[pngIndex * 4 + 0],vecPng[pngIndex * 4 + 1],vecPng[pngIndex * 4 + 2] };
// color = color + dither[pngIndex];
color.clip();
int foundr, foundg, foundb;
// first find the cube surround it
int l = 8192;
size_t index = 0;
for (int x = -1; x <= 1; ++x)
for (int y = -1; y <= 1; ++y)
for (int z = -1; z <= 1; ++z)
{
int xr = color.r / 32 + x, yg = y + color.g / 32, zb = z + color.b / 32;
if (xr >= 0 && xr <= 8 && yg >= 0 && yg <= 8 && zb >= 0 && zb <= 8)
{
int the_index = 0;
for (auto i : _3DTABLE[xr][yg][zb])
{
int newl = (i.second - color).sqr();
if (newl < l)
{
l = newl;
index = the_index;
foundr = xr;
foundg = yg;
foundb = zb;
}
++the_index;
}
}
}
//maximum distance will not bigger than the cubic diagonal
CCB ccb = _3DTABLE[foundr][foundg][foundb][index].first;
// RGB error = color -_3DTABLE[foundr][foundg][foundb][index].second;// use for error diffusion, totally up to you
// if (_x + 1 < pngWidth)
// dither[_y*pngWidth + _x + 1] = error * (7.0f / 16.0f);
// if (_y + 1 < pngHeight)
// {
// if (_x - 1 >= 0)
// dither[(_y + 1)*pngWidth + _x - 1] = error * (3.0f / 16.0f);
// dither[(_y + 1)*pngWidth + _x] = error * (5.0f / 16.0f);
// if (_x + 1 < pngWidth)
// dither[(_y + 1)*pngWidth + _x + 1] = error * (1.0f / 16.0f);
// }
Draw(_x, _y, pixelP[ccb.p], ccb.f | (ccb.b << 4));
}
return true;
}
virtual bool OnUserUpdate(float _fDeltaTime)
{
return true;
}
struct RGB
{
int r;
int g;
int b;
RGB operator+(RGB _t)
{
return { r + _t.r,g + _t.g,b + _t.b };
}
RGB operator-(RGB _t)
{
return { r - _t.r,g - _t.g,b - _t.b };
}
RGB operator*(float _f)
{
return { (int)((float)r*_f),(int)((float)g*_f),(int)((float)b*_f) };
}
bool operator==(RGB _t)
{
return r == _t.r&&g == _t.g&&b == _t.b;
}
int sqr()
{
return r * r + g * g + b * b;
}
void clip()
{
if (r < 0) r = 0;
if (r > 256) r = 256;
if (g < 0) g = 0;
if (g > 256) g = 256;
if (b < 0) b = 0;
if (b > 256) b = 256;
}
};
//console color block
struct CCB
{
//foreground color
int f;
//background color
int b;
//block type
int p;
};
RGB ColorTable[16] =
{
{0,0,0},
{0,0,128},
{0,128,0},
{0,128,128},
{128,0,0},
{128,0,128},
{128,128,0},
{192,192,192},
{128,128,128},
{0,0,256},
{0,256,0},
{0,256,256},
{256,0,0},
{256,0,256},
{256,256,0},
{256,256,256},
};
typedef std::vector<std::pair<CCB, RGB>> CTR;
std::vector<std::pair<CCB, RGB>> ccbToRGB;
CTR _3DTABLE[9][9][9];
//ForeGroundBlockPercent
float percent[4] = { 1,0.75,0.5,0.25 };
short pixelP[4] = { PIXEL_SOLID,PIXEL_THREEQUARTERS,PIXEL_HALF,PIXEL_QUARTER };
RGB *dither;
unsigned pngWidth, pngHeight;
};
int main()
{
const char* filename = "C:/test_PNG/van_gogh.png";
//const char* filename = "C:/test_PNG/mario2.png";
//const char* filename = "C:/test_PNG/olc.png";
unsigned error = lodepng::decode(vecPng, imageWidth, imageHeight, filename);
if (error) {
std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
return false;
}
testConsole test;
test.ConstructConsole(imageWidth, imageHeight, 4, 4);
// test.ConstructConsole(100, 100, 8, 8);
test.Start();
return 0;
}
from javidx9.
I already update the first version to increase the performance of searching the color.Hope you could do some performance comparision of these 3 versions even with the version you optimized.(because my computer can't...)
If its efficient enough I think it's an opportunity to upgrade the engine to support RGB value function(with RGB support we could do all crazy color composition stuff and output the rgb buffer layer in our code and the engine will handle the rgb buffer for display itself.)
from javidx9.
Related Issues (20)
- olcConsoleGameEngine.h does not display correctly HOT 3
- olcCosoleEngine doesn't work with linux HOT 7
- Non-circular Spline - looks strange or I don't get it :)
- ERROR: SCREEN Height / Font Height Too Big
- olcConsoleGameEngine.h - ERROR: Screen Height / Font Height Too Big
- Unhandled exception thrown: write access violation. this->m_bufScreen was 0x1110112. HOT 2
- Video
- Should reset dw1_step to zero here too.
- OneLoneCoder_olcEngine3D_Part3.cpp line 330 can be deleted
- 3D Graphics Engine Part #3. Clip against the frustum
- onUserSoundSample bug at 127 seconds after start HOT 2
- Not an issue but a request. Hidden line removal
- Small bug with Perlin-like noise generator HOT 1
- 3d console game engine does not display properly in Windows Terminal HOT 3
- Out of bounds? HOT 1
- Missing assets from multiple videos HOT 1
- Shame on David for boasting about his stupidity. HOT 3
- RPG_Main.cpp(55,24): error C2440: '=': cannot convert from 'OneLoneCoder_RPG *' to 'RPG_Engine *' HOT 2
- DDA vMapCheck truncation only works in positive axis
- Operator Precedence problem in Shunting Yard Algo HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from javidx9.