Skip to content

Instantly share code, notes, and snippets.

@cjxgm
Last active August 29, 2015 14:04
Show Gist options
  • Save cjxgm/05d71ad8f69ec91fabb6 to your computer and use it in GitHub Desktop.
Save cjxgm/05d71ad8f69ec91fabb6 to your computer and use it in GitHub Desktop.
a simple animation renderer written in c++
#include <iostream>
#include <array>
#include <vector>
#include <string>
#include <sstream>
#include <functional>
#include <cmath>
using namespace std;
namespace pro
{
struct ColorARGB
{
using Type = ColorARGB;
using Component = float;
using Array = array<Component, 4>;
using Raw = unsigned int; // 0xAARRGGBB
using Index = int;
ColorARGB() : ColorARGB{0xFFFFFFFF}
{
}
ColorARGB(Raw color)
: ColorARGB{
((color >> 24) & 0xFF) / 255.0f,
((color >> 16) & 0xFF) / 255.0f,
((color >> 8) & 0xFF) / 255.0f,
((color >> 0) & 0xFF) / 255.0f}
{
clamp();
}
ColorARGB(Component a, Component r, Component g, Component b)
: array{{a, r, g, b}}
{
}
const Component& operator[](Index idx) const { return array[idx]; }
Component& operator[](Index idx) { return array[idx]; }
operator Raw() { return raw(); }
Raw raw() const
{
return (raw(0)<<24) | (raw(1)<<16) | (raw(2)<<8) | raw(3);
}
Raw raw(Index idx) const
{
int c = clamp(idx) * 0xFF;
if (c < 0x00) c = 0x00;
if (c > 0xFF) c = 0xFF;
return static_cast<Raw>(c);
}
Component clamp(Index idx) const
{
auto c = array[idx];
if (c < 0.0f) c = 0.0f;
if (c > 1.0f) c = 1.0f;
return c;
}
void clamp()
{
array[0] = clamp(0);
array[1] = clamp(1);
array[2] = clamp(2);
array[3] = clamp(3);
}
void value(float v)
{
array[1] *= v;
array[2] *= v;
array[3] *= v;
}
void blend(const Type& color)
{
blend(color, color[0]);
}
void blend(const Type& color, float a)
{
auto c = 1.0f - a;
array[0] = array[0]*c + color[0]*a;
array[1] = array[1]*c + color[1]*a;
array[2] = array[2]*c + color[2]*a;
array[3] = array[3]*c + color[3]*a;
}
void alpha(float v)
{
array[0] *= v;
}
private:
Array array;
};
struct Pixmap
{
using Color = ColorARGB;
using Array = vector<Color>;
using Coord = int;
using Index = Coord;
using Size = Coord;
using SubCoord = float; // subpixel coordinate
template <class Iterable>
Pixmap(const Iterable& iterable, Size w) :
Pixmap(begin(iterable), end(iterable), w)
{
}
template <class IterIn>
Pixmap(IterIn begin, IterIn end, Size w) :
array{begin, end},
_w{w},
_h{static_cast<Size>(array.size())/w}
{
struct bad_size {};
if (array.size() % w) throw bad_size{};
}
Pixmap(Size w, Size h) :
array(w*h),
_w{w},
_h{h}
{
}
Size w() const { return _w; }
Size h() const { return _h; }
Index index(Coord x, Coord y) const
{
if (x < 0) x = x%w() + w();
if (y < 0) y = y%h() + h();
if (x >= w()) x %= w();
if (y >= h()) y %= h();
return y*w() + x;
}
const Color& at(Index idx) const { return array[idx]; }
Color& at(Index idx) { return array[idx]; }
const Color& at(int x, int y) const { return at(index(x, y)); }
Color& at(int x, int y) { return at(index(x, y)); }
operator string() const { return ppm(); }
using Transformer = function<void(Color&)>;
void transform(Transformer f)
{
for (auto& color: array)
f(color);
}
void alpha(float a)
{
transform([a](Color& color) { color.alpha(a); });
}
void ppm(ostream& o) const
{
o << dec;
o << "P3" << endl;
o << w() << " " << h() << " 255" << endl;
for (Coord y=0; y<h(); y++) {
for (Coord x=0; x<w(); x++) {
auto c = at(x, y);
//c.value(c[0]);
o << c.raw(1) << " " << c.raw(2) << " " << c.raw(3);
if (x != w()-1) o << " ";
}
o << endl;
}
}
string ppm() const
{
stringstream ss;
ppm(ss);
return ss.str();
}
Color xget(SubCoord sx, Coord y) const
{
Coord x = sx;
auto color = at(x, y);
color.blend(at(x+1, y), sx-x);
return color;
}
Color yget(Coord x, SubCoord sy) const
{
Coord y = sy;
auto color = at(x, y);
color.blend(at(x, y+1), sy-y);
return color;
}
Pixmap xscale(Size w) const
{
Pixmap result{w, h()};
for (Coord y=0; y<result.h(); y++)
for (Coord x=0; x<result.w(); x++)
result.at(x, y) = xget(
lirp(
x,
0, result.w()-1,
0, this->w()-1),
y);
return result;
}
Pixmap yscale(Size h) const
{
Pixmap result{w(), h};
for (Coord y=0; y<result.h(); y++)
for (Coord x=0; x<result.w(); x++)
result.at(x, y) = yget(
x,
lirp(
y,
0, result.h()-1,
0, this->h()-1));
return result;
}
Pixmap scale(Size w, Size h) const
{
return xscale(w).yscale(h);
}
Pixmap crop(Coord x, Coord y, Size w, Size h) const
{
Pixmap result{w, h};
result.blit(0, 0, *this, x, y, w, h);
return result;
}
void blit(Coord x, Coord y, const Pixmap& src)
{
blit(x, y, src, 0, 0, src.w(), src.h());
}
void blit(Coord x, Coord y, const Pixmap& src,
Coord sx, Coord sy, Size sw, Size sh)
{
for (Coord iy=sy, ry=y; iy<sy+sh; iy++, ry++)
for (Coord ix=sx, rx=x; ix<sx+sw; ix++, rx++)
at(rx, ry).blend(src.at(ix, iy));
}
Pixmap border_scale(Size w, Size h,
Size nsize, Size ssize, Size wsize, Size esize)
{
Pixmap result{w, h};
// corners
result.blit( 0, 0, *this, 0, 0, wsize, nsize); // nw
result.blit(w-esize, 0, *this, this->w()-esize, 0, esize, nsize); // ne
result.blit( 0, h-ssize, *this, 0, this->h()-ssize, wsize, ssize); // sw
result.blit(w-esize, h-ssize, *this, this->w()-esize, this->h()-ssize, esize, ssize); // se
// edges
result.blit( wsize, 0, crop( wsize, 0, this->w()-wsize-esize, nsize).xscale(w-wsize-esize)); // n
result.blit( 0, nsize, crop( 0, nsize, wsize, this->h()-nsize-ssize).yscale(h-nsize-ssize)); // w
result.blit( wsize, h-ssize, crop( wsize, this->h()-ssize, this->w()-wsize-esize, ssize).xscale(w-wsize-esize)); // s
result.blit(w-esize, nsize, crop(this->w()-esize, nsize, esize, this->h()-nsize-ssize).yscale(h-nsize-ssize)); // e
// center
result.blit(wsize, nsize, crop(wsize, nsize, this->w()-wsize-esize, this->h()-nsize-ssize).scale(w-wsize-esize, h-nsize-ssize));
return result;
}
static float lirp(float s, float sf, float st, float df, float dt)
{
return (s-sf) / (st-sf) * (dt-df) + df;
}
private:
Array array;
const Size _w;
const Size _h;
};
};
using namespace pro;
int main(int argc, char* argv[])
{
if (argc != 2) return 1;
int frame = stoi(string{argv[1]});
using RawColor = ColorARGB::Raw;
RawColor checker_raw[] = {
0xFF888888, 0xFFAAAAAA, 0xFF888888,
0xFFAAAAAA, 0xFFFFFFFF, 0xFFAAAAAA,
0xFF888888, 0xFFAAAAAA, 0xFF888888,
};
Pixmap checker{checker_raw, 3};
checker.at(2, 2)[3] += sin(frame/10.3f) * 0.47;
checker.at(1, 0)[2] += sin(frame/9.1f+1.1f) * 0.31;
if (frame < 30) {
float x = Pixmap::lirp(frame, 0, 29, 0, 10);
float y = Pixmap::lirp(frame, 0, 29, 30, 10);
float w = Pixmap::lirp(frame, 0, 29, 20, 580);
float h = Pixmap::lirp(frame, 0, 29, 100, 580);
float a = Pixmap::lirp(frame, 0, 19, 0, 1);
if (a > 1) a = 1;
Pixmap result{600, 600};
auto m = checker.scale(30, 30).border_scale(w, h, 10, 10, 10, 10);
m.alpha(a);
result.blit(x, y, m);
cout << string(result) << endl;
return 0;
}
if (frame < 60) {
float f = Pixmap::lirp(frame, 30, 59, 30, 300);
Pixmap result{600, 600};
result.blit(10, 10,
checker
.scale(f, f)
.border_scale(580, 580, 10, 10, 10, 10));
cout << string(result) << endl;
return 0;
}
if (frame < 90) {
float f = Pixmap::lirp(frame, 60, 89, 0, 140);
Pixmap result{600, 600};
result.blit(10, 10,
checker
.scale(300, 300)
.border_scale(580, 580, f, f, f, f));
cout << string(result) << endl;
return 0;
}
if (frame < 120) {
float f = Pixmap::lirp(frame, 90, 119, 140, 0);
float a = Pixmap::lirp(frame, 90, 119, 1, 0);
Pixmap result{600, 600};
auto m = checker.scale(300, 300).border_scale(580, 580, f, f, f, f);
m.alpha(a);
result.blit(10, 10, m);
cout << string(result) << endl;
return 0;
}
}
#!/usr/bin/fish
set -l nframe 119
set -l output "./rendered"
set -l renderer "./btn-anime"
mkdir -p $output
for frame in (seq 0 $nframe)
set -l index (printf "%.4d" $frame)
set -l file "$output/$index.png"
set -l cmd "$renderer $frame | convert - $file &"
echo -e "\e[0;32mrendering \e[1m$index\e[0m..."
eval $cmd
end
ffmpeg -i "$output/%04d.png" -b:v 1M $output.webm -y
rm -rf $output
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment