public int filterRow(byte[] currRow,
                         byte[] prevRow,
                         byte[][] scratchRows,
                         int bytesPerRow,
                         int bytesPerPixel) {
        int[] filterBadness = new int[5];
        for (int i = 0; i < 5; i++) {
            filterBadness[i] = Integer.MAX_VALUE;
        }
        
        {
            int badness = 0;
            
            for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
                int curr = currRow[i] & 0xff;
                badness += curr;
            }
            
            filterBadness[0] = badness;
        }
        
        {
            byte[] subFilteredRow = scratchRows[1];
            int badness = 0;
            
            for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
                int curr = currRow[i] & 0xff;
                int left = currRow[i - bytesPerPixel] & 0xff;
                int difference = curr - left;
                subFilteredRow[i] = (byte)difference;
                
                badness += abs(difference);
            }
            
            filterBadness[1] = badness;
        }
        
        {
            byte[] upFilteredRow = scratchRows[2];
            int badness = 0;
            
            for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
                int curr = currRow[i] & 0xff;
                int up = prevRow[i] & 0xff;
                int difference = curr - up;
                upFilteredRow[i] = (byte)difference;
                
                badness += abs(difference);
            }
            
            filterBadness[2] = badness;
        }
        
        {
            byte[] averageFilteredRow = scratchRows[3];
            int badness = 0;

            for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
                int curr = currRow[i] & 0xff;
                int left = currRow[i - bytesPerPixel] & 0xff;
                int up = prevRow[i] & 0xff;
                int difference = curr - (left + up)/2;;
                averageFilteredRow[i] = (byte)difference;
                
                badness += abs(difference);
            }
            
            filterBadness[3] = badness;
        }
        
        {
            byte[] paethFilteredRow = scratchRows[4];
            int badness = 0;
            
            for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
                int curr = currRow[i] & 0xff;
                int left = currRow[i - bytesPerPixel] & 0xff;
                int up = prevRow[i] & 0xff;
                int upleft = prevRow[i - bytesPerPixel] & 0xff;
                int predictor = paethPredictor(left, up, upleft);
                int difference = curr - predictor;
                paethFilteredRow[i] = (byte)difference;
                
                badness += abs(difference);
            }
            
            filterBadness[4] = badness;
        }
        
        int filterType = 0;
        int minBadness = filterBadness[0];
        
        for (int i = 1; i < 5; i++) {
            if (filterBadness[i] < minBadness) {
                minBadness = filterBadness[i];
                filterType = i;
            }
        }

        if (filterType == 0) {
            System.arraycopy(currRow, bytesPerPixel,
                             scratchRows[0], bytesPerPixel,
                             bytesPerRow);
        }
        
        return filterType;
    }