+static void sub_png_paeth_prediction(uint8_t *dst, uint8_t *src, uint8_t *top,
+ int w, int bpp)
+{
+ int i;
+ for (i = 0; i < w; i++) {
+ int a, b, c, p, pa, pb, pc;
+
+ a = src[i - bpp];
+ b = top[i];
+ c = top[i - bpp];
+
+ p = b - c;
+ pc = a - c;
+
+ pa = abs(p);
+ pb = abs(pc);
+ pc = abs(p + pc);
+
+ if (pa <= pb && pa <= pc)
+ p = a;
+ else if (pb <= pc)
+ p = b;
+ else
+ p = c;
+ dst[i] = src[i] - p;
+ }
+}
+
+static void png_filter_row(PNGEncContext *c, uint8_t *dst, int filter_type,
+ uint8_t *src, uint8_t *top, int size, int bpp)
+{
+ int i;
+
+ switch (filter_type) {
+ case PNG_FILTER_VALUE_NONE:
+ memcpy(dst, src, size);
+ break;
+ case PNG_FILTER_VALUE_SUB:
+ c->hdsp.diff_bytes(dst, src, src - bpp, size);
+ memcpy(dst, src, bpp);
+ break;
+ case PNG_FILTER_VALUE_UP:
+ c->hdsp.diff_bytes(dst, src, top, size);
+ break;
+ case PNG_FILTER_VALUE_AVG:
+ for (i = 0; i < bpp; i++)
+ dst[i] = src[i] - (top[i] >> 1);
+ for (; i < size; i++)
+ dst[i] = src[i] - ((src[i - bpp] + top[i]) >> 1);
+ break;
+ case PNG_FILTER_VALUE_PAETH:
+ for (i = 0; i < bpp; i++)
+ dst[i] = src[i] - top[i];
+ sub_png_paeth_prediction(dst + i, src + i, top + i, size - i, bpp);
+ break;
+ }
+}
+
+static uint8_t *png_choose_filter(PNGEncContext *s, uint8_t *dst,
+ uint8_t *src, uint8_t *top, int size, int bpp)
+{
+ int pred = s->filter_type;
+ assert(bpp || !pred);
+ if (!top && pred)
+ pred = PNG_FILTER_VALUE_SUB;
+ if (pred == PNG_FILTER_VALUE_MIXED) {
+ int i;
+ int cost, bcost = INT_MAX;
+ uint8_t *buf1 = dst, *buf2 = dst + size + 16;
+ for (pred = 0; pred < 5; pred++) {
+ png_filter_row(s, buf1 + 1, pred, src, top, size, bpp);
+ buf1[0] = pred;
+ cost = 0;
+ for (i = 0; i <= size; i++)
+ cost += abs((int8_t) buf1[i]);
+ if (cost < bcost) {
+ bcost = cost;
+ FFSWAP(uint8_t *, buf1, buf2);
+ }
+ }
+ return buf2;
+ } else {
+ png_filter_row(s, dst + 1, pred, src, top, size, bpp);
+ dst[0] = pred;
+ return dst;
+ }
+}
+