#include #include #include #include #include #include #include #include using namespace std; deque rna; typedef int coord; typedef pair pos; pos position(0,0); pos mark(0,0); struct rgb { int r, g, b; }; struct pixel { int r, g, b, alpha; }; bool operator==(const pixel &p1, const pixel &p2) { return (p1.r == p2.r && p1.g == p2.g && p1.b == p2.b && p1.alpha == p2.alpha); } const rgb black = {0,0,0}, red = {255,0,0}, green = {0,255,0}, yellow = {255,255,0}, blue = {0,0,255}, magenta = {255,0,255}, cyan = {0,255,255}, white = {255,255,255}; const int opaque = 255, transparent = 0; typedef vector > bitmap; deque bitmaps; bitmap transparentBitmap; typedef enum rgb_or_alpha {RGB, ALPHA} rgb_or_alpha; struct color { rgb_or_alpha type; int r, g, b, alpha; color (int a) : type(ALPHA), alpha(a) { } color (rgb p) : type(RGB), r(p.r), g(p.g), b(p.b) {} }; typedef deque bucket_t; bucket_t bucket; typedef enum dir_t { DIR_N, DIR_E, DIR_S, DIR_W } dir_t; dir_t dir = DIR_E; void load(ifstream &from) { char c; while (1) { from.get(c); if (from.eof()) break; rna.push_back(c); } cout << "read " << rna.size() << " bytes" << endl; } string fetch_instr() { string ret; if (rna.size() >= 7) { for (int i = 0; i < 7; i++) { ret.push_back(rna[0]); rna.pop_front(); } } return ret; } void add_color(rgb r) { bucket.push_front(color(r)); } void add_color(int alpha) { bucket.push_front(color(alpha)); } pixel currentPixel() { int rc = 0, gc = 0, bc = 0, ac = 0, n_rgb = 0, n_trans = 0; for (int i = 0; i < bucket.size(); i++) { switch(bucket[i].type) { case RGB: rc += bucket[i].r; gc += bucket[i].g; bc += bucket[i].b; ++n_rgb; break; case ALPHA: ac += bucket[i].alpha; ++n_trans; break; default: assert(0); } } if (n_rgb > 0) { rc = rc / n_rgb; gc = gc / n_rgb; bc = bc / n_rgb; } ac = n_trans > 0 ? ac/n_trans : 255; pixel p; p.r = rc*ac/255; p.g = gc*ac/255; p.b = bc*ac/255; p.alpha = ac; return p; } pos move(pos p, dir_t dir) { int x = p.first, y = p.second; switch (dir) { case DIR_N: return pos(x, ((y-1)+2*600) % 600); case DIR_E: return pos((x+1)%600, y); case DIR_S: return pos(x, (y+1) % 600); case DIR_W: return pos(((x-1)+2*600) % 600, y); } assert(0); } pixel getPixel(pos p) { return bitmaps[0][p.second][p.first]; } pixel setPixel(pos p) { bitmaps[0][p.second][p.first] = currentPixel(); } dir_t turnCounterClockwise(dir_t d) { switch (d) { case DIR_N: return DIR_W; case DIR_E: return DIR_N; case DIR_S: return DIR_E; case DIR_W: return DIR_S; } } dir_t turnClockwise(dir_t d) { switch (d) { case DIR_N: return DIR_E; case DIR_E: return DIR_S; case DIR_S: return DIR_W; case DIR_W: return DIR_N; } } void line(pos p1, pos p2) { int x0 = p1.first, y0 = p1.second, x1 = p2.first, y1 = p2.second; int deltax = x1 - x0; int deltay = y1 - y0; int d = max(abs(deltax), abs(deltay)); int c = (deltax*deltay <= 0) ? 1 : 0; int x = x0*d + (d-c)/2, y = y0*d + (d-c)/2; for (int j = 0; j < d; j++) { setPixel(pos(x/d, y/d)); x += deltax; y += deltay; } setPixel(pos(x1, y1)); } #define PUSH(X) if (!flood_visited[X.second][X.first]) { flood_list.push_front(X); } void fill(pos start, pixel initial) { vector > flood_visited; deque flood_list; vector bline(600, false); flood_visited = vector >(600, bline); flood_list.push_back(start); while (flood_list.size()) { //cout << "flood list: " << flood_list.size() << endl; pos p = flood_list.front(); flood_list.pop_front(); int x = p.first, y = p.second; if (flood_visited[y][x]) continue; flood_visited[y][x] = true; if (getPixel(p) == initial) { setPixel(p); if (x > 0) PUSH(pos(x-1, y)); if (x < 599) PUSH(pos(x+1, y)); if (y > 0) PUSH(pos(x, y-1)); if (y < 599) PUSH(pos(x, y+1)); } } } void tryfill() { if (currentPixel() == getPixel(position)) { return; } fill(position, getPixel(position)); } void addBitmap(bitmap b) { if (bitmaps.size() < 10) { bitmaps.push_front(b); } } void compose() { if (bitmaps.size() >= 2) { for (int y = 0; y < 600; y++) { for (int x = 0; x < 600; x++) { pixel p0 = bitmaps[0][y][x], p1 = bitmaps[1][y][x], p2; p2.r = p0.r + (p1.r*(255-p0.alpha)/255); p2.g = p0.g + (p1.g*(255-p0.alpha)/255); p2.b = p0.b + (p1.b*(255-p0.alpha)/255); p2.alpha = p0.alpha + (p1.alpha*(255-p0.alpha)/255); bitmaps[1][y][x] = p2; } } bitmaps.pop_front(); } } void clip(); void build() { while (rna.size() >= 7) { string r = fetch_instr(); if (r == "PIPIIIC") { add_color(black); } else if (r == "PIPIIIP") { cout << "red" << endl; add_color(red); } else if (r == "PIPIICC") { cout << "green" << endl; add_color(green); } else if (r == "PIPIICF") { cout << "yellow" << endl; add_color(yellow); } else if (r == "PIPIICP") { cout << "blue" << endl; add_color(blue); } else if (r == "PIPIIFC") { cout << "magenta" << endl; add_color(magenta); } else if (r == "PIPIIFF") { cout << "cyan" << endl; add_color(cyan); } else if (r == "PIPIIPC") { cout << "white" << endl; add_color(white); } else if (r == "PIPIIPF") { cout << "transparent" << endl; add_color(transparent); } else if (r == "PIPIIPP") { cout << "opaque" << endl; add_color(opaque); } else if (r == "PIIPICP") { cout << "clear" << endl; bucket.clear(); } else if (r == "PIIIIIP") { cout << "move" << endl; position = move(position, dir); } else if (r == "PCCCCCP") { cout << "turn" << endl; dir = turnCounterClockwise(dir); } else if (r == "PFFFFFP") { cout << "turn" << endl; dir = turnClockwise(dir); } else if (r == "PCCIFFP") { cout << "mark" << endl; mark = position; } else if (r == "PFFICCP") { cout << "line" << endl; line(position, mark); } else if (r == "PIIPIIP") { cout << "tryfill" << endl; tryfill(); } else if (r == "PCCPFFP") { cout << "add" << endl; addBitmap(transparentBitmap); } else if (r == "PFFPCCP") { cout << "compose" << endl; compose(); } else if (r == "PFFICCF") { cout << "clip" << endl; clip(); } else { cout << "unknown instr: " << r << endl; } } } void clip() { if (bitmaps.size() >= 2) { for (int y = 0; y < 600; y++) { for (int x = 0; x < 600; x++) { pixel p0 = bitmaps[0][y][x], p1 = bitmaps[1][y][x], p2; p2.r = p1.r*p0.alpha/255; p2.g = p1.g*p0.alpha/255; p2.b = p1.b*p0.alpha/255; p2.alpha = p1.alpha*p0.alpha/255; bitmaps[1][y][x] = p2; } } bitmaps.pop_front(); } } void current_pixel_cases(); int main(int argc, char **argv) { pixel blackpixel = {0,0,0,transparent}; vector line(600, blackpixel); transparentBitmap = bitmap(600, line); bitmaps.push_front(transparentBitmap); if (argc != 3) { cout << "build " << endl; } else { ifstream from(argv[1]); load(from); from.close(); // current_pixel_cases(); build(); ofstream of(argv[2]); of << "P3" << endl << "600 600" << endl << 255 << endl; for (int y = 0; y < 600; y++) { for (int x = 0; x < 600; x++) { of << bitmaps[0][y][x].r << " " << bitmaps[0][y][x].g << " " << bitmaps[0][y][x].b << " "; } of << endl; } } return 0; } void current_pixel_cases() { bucket.clear(); add_color(opaque); add_color(opaque); add_color(transparent); pixel p = currentPixel(); cout << "(" << p.r << ", " << p.g << ", " << p.b << ", " << p.alpha << ")" << endl; bucket.clear(); add_color(cyan); add_color(yellow); add_color(black); p = currentPixel(); cout << "(" << p.r << ", " << p.g << ", " << p.b << ", " << p.alpha << ")" << endl; bucket.clear(); }