#include #include #include #include #include Pl_PNGFilter::Pl_PNGFilter(char const* identifier, Pipeline* next, action_e action, unsigned int columns, unsigned int bytes_per_pixel) : Pipeline(identifier, next), action(action), columns(columns), cur_row(0), prev_row(0), buf1(0), buf2(0), bytes_per_pixel(bytes_per_pixel), pos(0) { if ((columns == 0) || (columns > UINT_MAX - 1)) { throw std::runtime_error( "PNGFilter created with invalid columns value"); } this->buf1 = new unsigned char[columns + 1]; this->buf2 = new unsigned char[columns + 1]; this->cur_row = buf1; // number of bytes per incoming row this->incoming = (action == a_encode ? columns : columns + 1); } Pl_PNGFilter::~Pl_PNGFilter() { delete [] buf1; delete [] buf2; } void Pl_PNGFilter::write(unsigned char* data, size_t len) { size_t left = this->incoming - this->pos; size_t offset = 0; while (len >= left) { // finish off current row memcpy(this->cur_row + this->pos, data + offset, left); offset += left; len -= left; processRow(); // Swap rows unsigned char* t = this->prev_row; this->prev_row = this->cur_row; this->cur_row = t ? t : this->buf2; memset(this->cur_row, 0, this->columns + 1); left = this->incoming; this->pos = 0; } if (len) { memcpy(this->cur_row + this->pos, data + offset, len); } this->pos += len; } void Pl_PNGFilter::processRow() { if (this->action == a_encode) { encodeRow(); } else { decodeRow(); } } void Pl_PNGFilter::decodeRow() { int filter = this->cur_row[0]; if (this->prev_row) { switch (filter) { case 0: break; case 1: this->decodeSub(); break; case 2: this->decodeUp(); break; case 3: this->decodeAverage(); break; case 4: this->decodePaeth(); break; default: // ignore break; } } getNext()->write(this->cur_row + 1, this->columns); } void Pl_PNGFilter::decodeSub() { unsigned char* buffer = this->cur_row + 1; unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1; for (unsigned int i = 0; i < this->columns; ++i) { unsigned char left = 0; if (i >= bpp) { left = buffer[i - bpp]; } buffer[i] += left; } } void Pl_PNGFilter::decodeUp() { unsigned char* buffer = this->cur_row + 1; unsigned char* above_buffer = this->prev_row + 1; for (unsigned int i = 0; i < this->columns; ++i) { unsigned char up = above_buffer[i]; buffer[i] += up; } } void Pl_PNGFilter::decodeAverage() { unsigned char* buffer = this->cur_row+1; unsigned char* above_buffer = this->prev_row+1; unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1; for (unsigned int i = 0; i < this->columns; ++i) { int left = 0, up = 0; if (i >= bpp) { left = buffer[i - bpp]; } up = above_buffer[i]; buffer[i] += floor((left+up) / 2); } } void Pl_PNGFilter::decodePaeth() { unsigned char* buffer = this->cur_row+1; unsigned char* above_buffer = this->prev_row+1; unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1; for (unsigned int i = 0; i < this->columns; ++i) { int left = 0, up = above_buffer[i], upper_left = 0; if (i >= bpp) { left = buffer[i - bpp]; upper_left = above_buffer[i - bpp]; } buffer[i] += this->PaethPredictor(left, up, upper_left); } } int Pl_PNGFilter::PaethPredictor(int a, int b, int c) { int p = a + b - c; int pa = std::abs(p - a); int pb = std::abs(p - b); int pc = std::abs(p - c); if (pa <= pb && pa <= pc) { return a; } if (pb <= pc) { return b; } return c; } void Pl_PNGFilter::encodeRow() { // For now, hard-code to using UP filter. unsigned char ch = 2; getNext()->write(&ch, 1); if (this->prev_row) { for (unsigned int i = 0; i < this->columns; ++i) { ch = this->cur_row[i] - this->prev_row[i]; getNext()->write(&ch, 1); } } else { getNext()->write(this->cur_row, this->columns); } } void Pl_PNGFilter::finish() { if (this->pos) { // write partial row processRow(); } this->prev_row = 0; this->cur_row = buf1; this->pos = 0; memset(this->cur_row, 0, this->columns + 1); getNext()->finish(); }