1 // Copyright 2014 Google Inc. All Rights Reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the COPYING file in the root of the source 5 // tree. An additional intellectual property rights grant can be found 6 // in the file PATENTS. All contributing project authors may 7 // be found in the AUTHORS file in the root of the source tree. 8 // ----------------------------------------------------------------------------- 9 // 10 // Spatial prediction using various filters 11 // 12 // Author(s): Branimir Vasic (branimir.vasic (at) imgtec.com) 13 // Djordje Pesut (djordje.pesut (at) imgtec.com) 14 15 #include "./dsp.h" 16 17 #if defined(WEBP_USE_MIPS_DSP_R2) 18 19 #include "../dsp/dsp.h" 20 #include <assert.h> 21 #include <stdlib.h> 22 #include <string.h> 23 24 //------------------------------------------------------------------------------ 25 // Helpful macro. 26 27 # define SANITY_CHECK(in, out) \ 28 assert(in != NULL); \ 29 assert(out != NULL); \ 30 assert(width > 0); \ 31 assert(height > 0); \ 32 assert(stride >= width); \ 33 assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ 34 (void)height; // Silence unused warning. 35 36 // if INVERSE 37 // preds == &dst[-1] == &src[-1] 38 // else 39 // preds == &src[-1] != &dst[-1] 40 #define DO_PREDICT_LINE(SRC, DST, LENGTH, INVERSE) do { \ 41 const uint8_t* psrc = (uint8_t*)(SRC); \ 42 uint8_t* pdst = (uint8_t*)(DST); \ 43 const int ilength = (int)(LENGTH); \ 44 int temp0, temp1, temp2, temp3, temp4, temp5, temp6; \ 45 __asm__ volatile ( \ 46 ".set push \n\t" \ 47 ".set noreorder \n\t" \ 48 "srl %[temp0], %[length], 0x2 \n\t" \ 49 "beqz %[temp0], 4f \n\t" \ 50 " andi %[temp6], %[length], 0x3 \n\t" \ 51 ".if " #INVERSE " \n\t" \ 52 "lbu %[temp1], -1(%[src]) \n\t" \ 53 "1: \n\t" \ 54 "lbu %[temp2], 0(%[src]) \n\t" \ 55 "lbu %[temp3], 1(%[src]) \n\t" \ 56 "lbu %[temp4], 2(%[src]) \n\t" \ 57 "lbu %[temp5], 3(%[src]) \n\t" \ 58 "addiu %[src], %[src], 4 \n\t" \ 59 "addiu %[temp0], %[temp0], -1 \n\t" \ 60 "addu %[temp2], %[temp2], %[temp1] \n\t" \ 61 "addu %[temp3], %[temp3], %[temp2] \n\t" \ 62 "addu %[temp4], %[temp4], %[temp3] \n\t" \ 63 "addu %[temp1], %[temp5], %[temp4] \n\t" \ 64 "sb %[temp2], -4(%[src]) \n\t" \ 65 "sb %[temp3], -3(%[src]) \n\t" \ 66 "sb %[temp4], -2(%[src]) \n\t" \ 67 "bnez %[temp0], 1b \n\t" \ 68 " sb %[temp1], -1(%[src]) \n\t" \ 69 ".else \n\t" \ 70 "1: \n\t" \ 71 "ulw %[temp1], -1(%[src]) \n\t" \ 72 "ulw %[temp2], 0(%[src]) \n\t" \ 73 "addiu %[src], %[src], 4 \n\t" \ 74 "addiu %[temp0], %[temp0], -1 \n\t" \ 75 "subu.qb %[temp3], %[temp2], %[temp1] \n\t" \ 76 "usw %[temp3], 0(%[dst]) \n\t" \ 77 "bnez %[temp0], 1b \n\t" \ 78 " addiu %[dst], %[dst], 4 \n\t" \ 79 ".endif \n\t" \ 80 "4: \n\t" \ 81 "beqz %[temp6], 3f \n\t" \ 82 " nop \n\t" \ 83 "2: \n\t" \ 84 "lbu %[temp1], -1(%[src]) \n\t" \ 85 "lbu %[temp2], 0(%[src]) \n\t" \ 86 "addiu %[src], %[src], 1 \n\t" \ 87 ".if " #INVERSE " \n\t" \ 88 "addu %[temp3], %[temp1], %[temp2] \n\t" \ 89 "sb %[temp3], -1(%[src]) \n\t" \ 90 ".else \n\t" \ 91 "subu %[temp3], %[temp1], %[temp2] \n\t" \ 92 "sb %[temp3], 0(%[dst]) \n\t" \ 93 ".endif \n\t" \ 94 "addiu %[temp6], %[temp6], -1 \n\t" \ 95 "bnez %[temp6], 2b \n\t" \ 96 " addiu %[dst], %[dst], 1 \n\t" \ 97 "3: \n\t" \ 98 ".set pop \n\t" \ 99 : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \ 100 [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \ 101 [temp6]"=&r"(temp6), [dst]"+&r"(pdst), [src]"+&r"(psrc) \ 102 : [length]"r"(ilength) \ 103 : "memory" \ 104 ); \ 105 } while (0) 106 107 static WEBP_INLINE void PredictLine(const uint8_t* src, uint8_t* dst, 108 int length, int inverse) { 109 if (inverse) { 110 DO_PREDICT_LINE(src, dst, length, 1); 111 } else { 112 DO_PREDICT_LINE(src, dst, length, 0); 113 } 114 } 115 116 #define DO_PREDICT_LINE_VERTICAL(SRC, PRED, DST, LENGTH, INVERSE) do { \ 117 const uint8_t* psrc = (uint8_t*)(SRC); \ 118 const uint8_t* ppred = (uint8_t*)(PRED); \ 119 uint8_t* pdst = (uint8_t*)(DST); \ 120 const int ilength = (int)(LENGTH); \ 121 int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; \ 122 __asm__ volatile ( \ 123 ".set push \n\t" \ 124 ".set noreorder \n\t" \ 125 "srl %[temp0], %[length], 0x3 \n\t" \ 126 "beqz %[temp0], 4f \n\t" \ 127 " andi %[temp7], %[length], 0x7 \n\t" \ 128 "1: \n\t" \ 129 "ulw %[temp1], 0(%[src]) \n\t" \ 130 "ulw %[temp2], 0(%[pred]) \n\t" \ 131 "ulw %[temp3], 4(%[src]) \n\t" \ 132 "ulw %[temp4], 4(%[pred]) \n\t" \ 133 "addiu %[src], %[src], 8 \n\t" \ 134 ".if " #INVERSE " \n\t" \ 135 "addu.qb %[temp5], %[temp1], %[temp2] \n\t" \ 136 "addu.qb %[temp6], %[temp3], %[temp4] \n\t" \ 137 ".else \n\t" \ 138 "subu.qb %[temp5], %[temp1], %[temp2] \n\t" \ 139 "subu.qb %[temp6], %[temp3], %[temp4] \n\t" \ 140 ".endif \n\t" \ 141 "addiu %[pred], %[pred], 8 \n\t" \ 142 "usw %[temp5], 0(%[dst]) \n\t" \ 143 "usw %[temp6], 4(%[dst]) \n\t" \ 144 "addiu %[temp0], %[temp0], -1 \n\t" \ 145 "bnez %[temp0], 1b \n\t" \ 146 " addiu %[dst], %[dst], 8 \n\t" \ 147 "4: \n\t" \ 148 "beqz %[temp7], 3f \n\t" \ 149 " nop \n\t" \ 150 "2: \n\t" \ 151 "lbu %[temp1], 0(%[src]) \n\t" \ 152 "lbu %[temp2], 0(%[pred]) \n\t" \ 153 "addiu %[src], %[src], 1 \n\t" \ 154 "addiu %[pred], %[pred], 1 \n\t" \ 155 ".if " #INVERSE " \n\t" \ 156 "addu %[temp3], %[temp1], %[temp2] \n\t" \ 157 ".else \n\t" \ 158 "subu %[temp3], %[temp1], %[temp2] \n\t" \ 159 ".endif \n\t" \ 160 "sb %[temp3], 0(%[dst]) \n\t" \ 161 "addiu %[temp7], %[temp7], -1 \n\t" \ 162 "bnez %[temp7], 2b \n\t" \ 163 " addiu %[dst], %[dst], 1 \n\t" \ 164 "3: \n\t" \ 165 ".set pop \n\t" \ 166 : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \ 167 [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \ 168 [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [pred]"+&r"(ppred), \ 169 [dst]"+&r"(pdst), [src]"+&r"(psrc) \ 170 : [length]"r"(ilength) \ 171 : "memory" \ 172 ); \ 173 } while (0) 174 175 #define PREDICT_LINE_ONE_PASS(SRC, PRED, DST, INVERSE) do { \ 176 int temp1, temp2, temp3; \ 177 __asm__ volatile ( \ 178 "lbu %[temp1], 0(%[src]) \n\t" \ 179 "lbu %[temp2], 0(%[pred]) \n\t" \ 180 ".if " #INVERSE " \n\t" \ 181 "addu %[temp3], %[temp1], %[temp2] \n\t" \ 182 ".else \n\t" \ 183 "subu %[temp3], %[temp1], %[temp2] \n\t" \ 184 ".endif \n\t" \ 185 "sb %[temp3], 0(%[dst]) \n\t" \ 186 : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3) \ 187 : [pred]"r"((PRED)), [dst]"r"((DST)), [src]"r"((SRC)) \ 188 : "memory" \ 189 ); \ 190 } while (0) 191 192 //------------------------------------------------------------------------------ 193 // Horizontal filter. 194 195 #define FILTER_LINE_BY_LINE(INVERSE) do { \ 196 while (row < last_row) { \ 197 PREDICT_LINE_ONE_PASS(in, preds - stride, out, INVERSE); \ 198 DO_PREDICT_LINE(in + 1, out + 1, width - 1, INVERSE); \ 199 ++row; \ 200 preds += stride; \ 201 in += stride; \ 202 out += stride; \ 203 } \ 204 } while (0) 205 206 static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in, 207 int width, int height, int stride, 208 int row, int num_rows, 209 int inverse, uint8_t* out) { 210 const uint8_t* preds; 211 const size_t start_offset = row * stride; 212 const int last_row = row + num_rows; 213 SANITY_CHECK(in, out); 214 in += start_offset; 215 out += start_offset; 216 preds = inverse ? out : in; 217 218 if (row == 0) { 219 // Leftmost pixel is the same as input for topmost scanline. 220 out[0] = in[0]; 221 PredictLine(in + 1, out + 1, width - 1, inverse); 222 row = 1; 223 preds += stride; 224 in += stride; 225 out += stride; 226 } 227 228 // Filter line-by-line. 229 if (inverse) { 230 FILTER_LINE_BY_LINE(1); 231 } else { 232 FILTER_LINE_BY_LINE(0); 233 } 234 } 235 236 #undef FILTER_LINE_BY_LINE 237 238 static void HorizontalFilter(const uint8_t* data, int width, int height, 239 int stride, uint8_t* filtered_data) { 240 DoHorizontalFilter(data, width, height, stride, 0, height, 0, filtered_data); 241 } 242 243 static void HorizontalUnfilter(int width, int height, int stride, int row, 244 int num_rows, uint8_t* data) { 245 DoHorizontalFilter(data, width, height, stride, row, num_rows, 1, data); 246 } 247 248 //------------------------------------------------------------------------------ 249 // Vertical filter. 250 251 #define FILTER_LINE_BY_LINE(INVERSE) do { \ 252 while (row < last_row) { \ 253 DO_PREDICT_LINE_VERTICAL(in, preds, out, width, INVERSE); \ 254 ++row; \ 255 preds += stride; \ 256 in += stride; \ 257 out += stride; \ 258 } \ 259 } while (0) 260 261 static WEBP_INLINE void DoVerticalFilter(const uint8_t* in, 262 int width, int height, int stride, 263 int row, int num_rows, 264 int inverse, uint8_t* out) { 265 const uint8_t* preds; 266 const size_t start_offset = row * stride; 267 const int last_row = row + num_rows; 268 SANITY_CHECK(in, out); 269 in += start_offset; 270 out += start_offset; 271 preds = inverse ? out : in; 272 273 if (row == 0) { 274 // Very first top-left pixel is copied. 275 out[0] = in[0]; 276 // Rest of top scan-line is left-predicted. 277 PredictLine(in + 1, out + 1, width - 1, inverse); 278 row = 1; 279 in += stride; 280 out += stride; 281 } else { 282 // We are starting from in-between. Make sure 'preds' points to prev row. 283 preds -= stride; 284 } 285 286 // Filter line-by-line. 287 if (inverse) { 288 FILTER_LINE_BY_LINE(1); 289 } else { 290 FILTER_LINE_BY_LINE(0); 291 } 292 } 293 294 #undef FILTER_LINE_BY_LINE 295 #undef DO_PREDICT_LINE_VERTICAL 296 297 static void VerticalFilter(const uint8_t* data, int width, int height, 298 int stride, uint8_t* filtered_data) { 299 DoVerticalFilter(data, width, height, stride, 0, height, 0, filtered_data); 300 } 301 302 static void VerticalUnfilter(int width, int height, int stride, int row, 303 int num_rows, uint8_t* data) { 304 DoVerticalFilter(data, width, height, stride, row, num_rows, 1, data); 305 } 306 307 //------------------------------------------------------------------------------ 308 // Gradient filter. 309 310 static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) { 311 int temp0; 312 __asm__ volatile ( 313 "addu %[temp0], %[a], %[b] \n\t" 314 "subu %[temp0], %[temp0], %[c] \n\t" 315 "shll_s.w %[temp0], %[temp0], 23 \n\t" 316 "precrqu_s.qb.ph %[temp0], %[temp0], $zero \n\t" 317 "srl %[temp0], %[temp0], 24 \n\t" 318 : [temp0]"=&r"(temp0) 319 : [a]"r"(a),[b]"r"(b),[c]"r"(c) 320 ); 321 return temp0; 322 } 323 324 #define FILTER_LINE_BY_LINE(INVERSE, PREDS, OPERATION) do { \ 325 while (row < last_row) { \ 326 int w; \ 327 PREDICT_LINE_ONE_PASS(in, PREDS - stride, out, INVERSE); \ 328 for (w = 1; w < width; ++w) { \ 329 const int pred = GradientPredictor(PREDS[w - 1], \ 330 PREDS[w - stride], \ 331 PREDS[w - stride - 1]); \ 332 out[w] = in[w] OPERATION pred; \ 333 } \ 334 ++row; \ 335 in += stride; \ 336 out += stride; \ 337 } \ 338 } while (0) 339 340 static WEBP_INLINE void DoGradientFilter(const uint8_t* in, 341 int width, int height, int stride, 342 int row, int num_rows, 343 int inverse, uint8_t* out) { 344 const uint8_t* preds; 345 const size_t start_offset = row * stride; 346 const int last_row = row + num_rows; 347 SANITY_CHECK(in, out); 348 in += start_offset; 349 out += start_offset; 350 preds = inverse ? out : in; 351 352 // left prediction for top scan-line 353 if (row == 0) { 354 out[0] = in[0]; 355 PredictLine(in + 1, out + 1, width - 1, inverse); 356 row = 1; 357 preds += stride; 358 in += stride; 359 out += stride; 360 } 361 362 // Filter line-by-line. 363 if (inverse) { 364 FILTER_LINE_BY_LINE(1, out, +); 365 } else { 366 FILTER_LINE_BY_LINE(0, in, -); 367 } 368 } 369 370 #undef FILTER_LINE_BY_LINE 371 372 static void GradientFilter(const uint8_t* data, int width, int height, 373 int stride, uint8_t* filtered_data) { 374 DoGradientFilter(data, width, height, stride, 0, height, 0, filtered_data); 375 } 376 377 static void GradientUnfilter(int width, int height, int stride, int row, 378 int num_rows, uint8_t* data) { 379 DoGradientFilter(data, width, height, stride, row, num_rows, 1, data); 380 } 381 382 #undef PREDICT_LINE_ONE_PASS 383 #undef DO_PREDICT_LINE 384 #undef SANITY_CHECK 385 386 //------------------------------------------------------------------------------ 387 // Entry point 388 389 extern void VP8FiltersInitMIPSdspR2(void); 390 391 WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitMIPSdspR2(void) { 392 WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter; 393 WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter; 394 WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter; 395 396 WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter; 397 WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter; 398 WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter; 399 } 400 401 #else // !WEBP_USE_MIPS_DSP_R2 402 403 WEBP_DSP_INIT_STUB(VP8FiltersInitMIPSdspR2) 404 405 #endif // WEBP_USE_MIPS_DSP_R2 406