1 /*
2  *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <math.h>
12 #include <stdarg.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include "./tools_common.h"
18 
19 #if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER
20 #include "vpx/vp8cx.h"
21 #endif
22 
23 #if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER
24 #include "vpx/vp8dx.h"
25 #endif
26 
27 #if defined(_WIN32) || defined(__OS2__)
28 #include <io.h>
29 #include <fcntl.h>
30 
31 #ifdef __OS2__
32 #define _setmode setmode
33 #define _fileno fileno
34 #define _O_BINARY O_BINARY
35 #endif
36 #endif
37 
38 #define LOG_ERROR(label)               \
39   do {                                 \
40     const char *l = label;             \
41     va_list ap;                        \
42     va_start(ap, fmt);                 \
43     if (l) fprintf(stderr, "%s: ", l); \
44     vfprintf(stderr, fmt, ap);         \
45     fprintf(stderr, "\n");             \
46     va_end(ap);                        \
47   } while (0)
48 
49 #if CONFIG_ENCODERS
50 /* Swallow warnings about unused results of fread/fwrite */
wrap_fread(void * ptr,size_t size,size_t nmemb,FILE * stream)51 static size_t wrap_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
52   return fread(ptr, size, nmemb, stream);
53 }
54 #define fread wrap_fread
55 #endif
56 
set_binary_mode(FILE * stream)57 FILE *set_binary_mode(FILE *stream) {
58   (void)stream;
59 #if defined(_WIN32) || defined(__OS2__)
60   _setmode(_fileno(stream), _O_BINARY);
61 #endif
62   return stream;
63 }
64 
die(const char * fmt,...)65 void die(const char *fmt, ...) {
66   LOG_ERROR(NULL);
67   usage_exit();
68 }
69 
fatal(const char * fmt,...)70 void fatal(const char *fmt, ...) {
71   LOG_ERROR("Fatal");
72   exit(EXIT_FAILURE);
73 }
74 
warn(const char * fmt,...)75 void warn(const char *fmt, ...) { LOG_ERROR("Warning"); }
76 
die_codec(vpx_codec_ctx_t * ctx,const char * s)77 void die_codec(vpx_codec_ctx_t *ctx, const char *s) {
78   const char *detail = vpx_codec_error_detail(ctx);
79 
80   printf("%s: %s\n", s, vpx_codec_error(ctx));
81   if (detail) printf("    %s\n", detail);
82   exit(EXIT_FAILURE);
83 }
84 
read_yuv_frame(struct VpxInputContext * input_ctx,vpx_image_t * yuv_frame)85 int read_yuv_frame(struct VpxInputContext *input_ctx, vpx_image_t *yuv_frame) {
86   FILE *f = input_ctx->file;
87   struct FileTypeDetectionBuffer *detect = &input_ctx->detect;
88   int plane = 0;
89   int shortread = 0;
90   const int bytespp = (yuv_frame->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
91 
92   for (plane = 0; plane < 3; ++plane) {
93     uint8_t *ptr;
94     const int w = vpx_img_plane_width(yuv_frame, plane);
95     const int h = vpx_img_plane_height(yuv_frame, plane);
96     int r;
97 
98     /* Determine the correct plane based on the image format. The for-loop
99      * always counts in Y,U,V order, but this may not match the order of
100      * the data on disk.
101      */
102     switch (plane) {
103       case 1:
104         ptr =
105             yuv_frame->planes[yuv_frame->fmt == VPX_IMG_FMT_YV12 ? VPX_PLANE_V
106                                                                  : VPX_PLANE_U];
107         break;
108       case 2:
109         ptr =
110             yuv_frame->planes[yuv_frame->fmt == VPX_IMG_FMT_YV12 ? VPX_PLANE_U
111                                                                  : VPX_PLANE_V];
112         break;
113       default: ptr = yuv_frame->planes[plane];
114     }
115 
116     for (r = 0; r < h; ++r) {
117       size_t needed = w * bytespp;
118       size_t buf_position = 0;
119       const size_t left = detect->buf_read - detect->position;
120       if (left > 0) {
121         const size_t more = (left < needed) ? left : needed;
122         memcpy(ptr, detect->buf + detect->position, more);
123         buf_position = more;
124         needed -= more;
125         detect->position += more;
126       }
127       if (needed > 0) {
128         shortread |= (fread(ptr + buf_position, 1, needed, f) < needed);
129       }
130 
131       ptr += yuv_frame->stride[plane];
132     }
133   }
134 
135   return shortread;
136 }
137 
138 #if CONFIG_ENCODERS
139 
140 static const VpxInterface vpx_encoders[] = {
141 #if CONFIG_VP8_ENCODER
142   { "vp8", VP8_FOURCC, &vpx_codec_vp8_cx },
143 #endif
144 
145 #if CONFIG_VP9_ENCODER
146   { "vp9", VP9_FOURCC, &vpx_codec_vp9_cx },
147 #endif
148 };
149 
get_vpx_encoder_count(void)150 int get_vpx_encoder_count(void) {
151   return sizeof(vpx_encoders) / sizeof(vpx_encoders[0]);
152 }
153 
get_vpx_encoder_by_index(int i)154 const VpxInterface *get_vpx_encoder_by_index(int i) { return &vpx_encoders[i]; }
155 
get_vpx_encoder_by_name(const char * name)156 const VpxInterface *get_vpx_encoder_by_name(const char *name) {
157   int i;
158 
159   for (i = 0; i < get_vpx_encoder_count(); ++i) {
160     const VpxInterface *encoder = get_vpx_encoder_by_index(i);
161     if (strcmp(encoder->name, name) == 0) return encoder;
162   }
163 
164   return NULL;
165 }
166 
167 #endif  // CONFIG_ENCODERS
168 
169 #if CONFIG_DECODERS
170 
171 static const VpxInterface vpx_decoders[] = {
172 #if CONFIG_VP8_DECODER
173   { "vp8", VP8_FOURCC, &vpx_codec_vp8_dx },
174 #endif
175 
176 #if CONFIG_VP9_DECODER
177   { "vp9", VP9_FOURCC, &vpx_codec_vp9_dx },
178 #endif
179 };
180 
get_vpx_decoder_count(void)181 int get_vpx_decoder_count(void) {
182   return sizeof(vpx_decoders) / sizeof(vpx_decoders[0]);
183 }
184 
get_vpx_decoder_by_index(int i)185 const VpxInterface *get_vpx_decoder_by_index(int i) { return &vpx_decoders[i]; }
186 
get_vpx_decoder_by_name(const char * name)187 const VpxInterface *get_vpx_decoder_by_name(const char *name) {
188   int i;
189 
190   for (i = 0; i < get_vpx_decoder_count(); ++i) {
191     const VpxInterface *const decoder = get_vpx_decoder_by_index(i);
192     if (strcmp(decoder->name, name) == 0) return decoder;
193   }
194 
195   return NULL;
196 }
197 
get_vpx_decoder_by_fourcc(uint32_t fourcc)198 const VpxInterface *get_vpx_decoder_by_fourcc(uint32_t fourcc) {
199   int i;
200 
201   for (i = 0; i < get_vpx_decoder_count(); ++i) {
202     const VpxInterface *const decoder = get_vpx_decoder_by_index(i);
203     if (decoder->fourcc == fourcc) return decoder;
204   }
205 
206   return NULL;
207 }
208 
209 #endif  // CONFIG_DECODERS
210 
vpx_img_plane_width(const vpx_image_t * img,int plane)211 int vpx_img_plane_width(const vpx_image_t *img, int plane) {
212   if (plane > 0 && img->x_chroma_shift > 0)
213     return (img->d_w + 1) >> img->x_chroma_shift;
214   else
215     return img->d_w;
216 }
217 
vpx_img_plane_height(const vpx_image_t * img,int plane)218 int vpx_img_plane_height(const vpx_image_t *img, int plane) {
219   if (plane > 0 && img->y_chroma_shift > 0)
220     return (img->d_h + 1) >> img->y_chroma_shift;
221   else
222     return img->d_h;
223 }
224 
vpx_img_write(const vpx_image_t * img,FILE * file)225 void vpx_img_write(const vpx_image_t *img, FILE *file) {
226   int plane;
227 
228   for (plane = 0; plane < 3; ++plane) {
229     const unsigned char *buf = img->planes[plane];
230     const int stride = img->stride[plane];
231     const int w = vpx_img_plane_width(img, plane) *
232                   ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
233     const int h = vpx_img_plane_height(img, plane);
234     int y;
235 
236     for (y = 0; y < h; ++y) {
237       fwrite(buf, 1, w, file);
238       buf += stride;
239     }
240   }
241 }
242 
vpx_img_read(vpx_image_t * img,FILE * file)243 int vpx_img_read(vpx_image_t *img, FILE *file) {
244   int plane;
245 
246   for (plane = 0; plane < 3; ++plane) {
247     unsigned char *buf = img->planes[plane];
248     const int stride = img->stride[plane];
249     const int w = vpx_img_plane_width(img, plane) *
250                   ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
251     const int h = vpx_img_plane_height(img, plane);
252     int y;
253 
254     for (y = 0; y < h; ++y) {
255       if (fread(buf, 1, w, file) != (size_t)w) return 0;
256       buf += stride;
257     }
258   }
259 
260   return 1;
261 }
262 
263 // TODO(dkovalev) change sse_to_psnr signature: double -> int64_t
sse_to_psnr(double samples,double peak,double sse)264 double sse_to_psnr(double samples, double peak, double sse) {
265   static const double kMaxPSNR = 100.0;
266 
267   if (sse > 0.0) {
268     const double psnr = 10.0 * log10(samples * peak * peak / sse);
269     return psnr > kMaxPSNR ? kMaxPSNR : psnr;
270   } else {
271     return kMaxPSNR;
272   }
273 }
274 
275 #if CONFIG_ENCODERS
read_frame(struct VpxInputContext * input_ctx,vpx_image_t * img)276 int read_frame(struct VpxInputContext *input_ctx, vpx_image_t *img) {
277   FILE *f = input_ctx->file;
278   y4m_input *y4m = &input_ctx->y4m;
279   int shortread = 0;
280 
281   if (input_ctx->file_type == FILE_TYPE_Y4M) {
282     if (y4m_input_fetch_frame(y4m, f, img) < 1) return 0;
283   } else {
284     shortread = read_yuv_frame(input_ctx, img);
285   }
286 
287   return !shortread;
288 }
289 
file_is_y4m(const char detect[4])290 int file_is_y4m(const char detect[4]) {
291   if (memcmp(detect, "YUV4", 4) == 0) {
292     return 1;
293   }
294   return 0;
295 }
296 
fourcc_is_ivf(const char detect[4])297 int fourcc_is_ivf(const char detect[4]) {
298   if (memcmp(detect, "DKIF", 4) == 0) {
299     return 1;
300   }
301   return 0;
302 }
303 
open_input_file(struct VpxInputContext * input)304 void open_input_file(struct VpxInputContext *input) {
305   /* Parse certain options from the input file, if possible */
306   input->file = strcmp(input->filename, "-") ? fopen(input->filename, "rb")
307                                              : set_binary_mode(stdin);
308 
309   if (!input->file) fatal("Failed to open input file");
310 
311   if (!fseeko(input->file, 0, SEEK_END)) {
312     /* Input file is seekable. Figure out how long it is, so we can get
313      * progress info.
314      */
315     input->length = ftello(input->file);
316     rewind(input->file);
317   }
318 
319   /* Default to 1:1 pixel aspect ratio. */
320   input->pixel_aspect_ratio.numerator = 1;
321   input->pixel_aspect_ratio.denominator = 1;
322 
323   /* For RAW input sources, these bytes will applied on the first frame
324    *  in read_frame().
325    */
326   input->detect.buf_read = fread(input->detect.buf, 1, 4, input->file);
327   input->detect.position = 0;
328 
329   if (input->detect.buf_read == 4 && file_is_y4m(input->detect.buf)) {
330     if (y4m_input_open(&input->y4m, input->file, input->detect.buf, 4,
331                        input->only_i420) >= 0) {
332       input->file_type = FILE_TYPE_Y4M;
333       input->width = input->y4m.pic_w;
334       input->height = input->y4m.pic_h;
335       input->pixel_aspect_ratio.numerator = input->y4m.par_n;
336       input->pixel_aspect_ratio.denominator = input->y4m.par_d;
337       input->framerate.numerator = input->y4m.fps_n;
338       input->framerate.denominator = input->y4m.fps_d;
339       input->fmt = input->y4m.vpx_fmt;
340       input->bit_depth = input->y4m.bit_depth;
341     } else {
342       fatal("Unsupported Y4M stream.");
343     }
344   } else if (input->detect.buf_read == 4 && fourcc_is_ivf(input->detect.buf)) {
345     fatal("IVF is not supported as input.");
346   } else {
347     input->file_type = FILE_TYPE_RAW;
348   }
349 }
350 
close_input_file(struct VpxInputContext * input)351 void close_input_file(struct VpxInputContext *input) {
352   fclose(input->file);
353   if (input->file_type == FILE_TYPE_Y4M) y4m_input_close(&input->y4m);
354 }
355 #endif
356 
357 // TODO(debargha): Consolidate the functions below into a separate file.
358 #if CONFIG_VP9_HIGHBITDEPTH
highbd_img_upshift(vpx_image_t * dst,vpx_image_t * src,int input_shift)359 static void highbd_img_upshift(vpx_image_t *dst, vpx_image_t *src,
360                                int input_shift) {
361   // Note the offset is 1 less than half.
362   const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0;
363   int plane;
364   if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
365       dst->x_chroma_shift != src->x_chroma_shift ||
366       dst->y_chroma_shift != src->y_chroma_shift || dst->fmt != src->fmt ||
367       input_shift < 0) {
368     fatal("Unsupported image conversion");
369   }
370   switch (src->fmt) {
371     case VPX_IMG_FMT_I42016:
372     case VPX_IMG_FMT_I42216:
373     case VPX_IMG_FMT_I44416:
374     case VPX_IMG_FMT_I44016: break;
375     default: fatal("Unsupported image conversion"); break;
376   }
377   for (plane = 0; plane < 3; plane++) {
378     int w = src->d_w;
379     int h = src->d_h;
380     int x, y;
381     if (plane) {
382       w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
383       h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
384     }
385     for (y = 0; y < h; y++) {
386       uint16_t *p_src =
387           (uint16_t *)(src->planes[plane] + y * src->stride[plane]);
388       uint16_t *p_dst =
389           (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
390       for (x = 0; x < w; x++) *p_dst++ = (*p_src++ << input_shift) + offset;
391     }
392   }
393 }
394 
lowbd_img_upshift(vpx_image_t * dst,vpx_image_t * src,int input_shift)395 static void lowbd_img_upshift(vpx_image_t *dst, vpx_image_t *src,
396                               int input_shift) {
397   // Note the offset is 1 less than half.
398   const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0;
399   int plane;
400   if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
401       dst->x_chroma_shift != src->x_chroma_shift ||
402       dst->y_chroma_shift != src->y_chroma_shift ||
403       dst->fmt != src->fmt + VPX_IMG_FMT_HIGHBITDEPTH || input_shift < 0) {
404     fatal("Unsupported image conversion");
405   }
406   switch (src->fmt) {
407     case VPX_IMG_FMT_I420:
408     case VPX_IMG_FMT_I422:
409     case VPX_IMG_FMT_I444:
410     case VPX_IMG_FMT_I440: break;
411     default: fatal("Unsupported image conversion"); break;
412   }
413   for (plane = 0; plane < 3; plane++) {
414     int w = src->d_w;
415     int h = src->d_h;
416     int x, y;
417     if (plane) {
418       w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
419       h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
420     }
421     for (y = 0; y < h; y++) {
422       uint8_t *p_src = src->planes[plane] + y * src->stride[plane];
423       uint16_t *p_dst =
424           (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
425       for (x = 0; x < w; x++) {
426         *p_dst++ = (*p_src++ << input_shift) + offset;
427       }
428     }
429   }
430 }
431 
vpx_img_upshift(vpx_image_t * dst,vpx_image_t * src,int input_shift)432 void vpx_img_upshift(vpx_image_t *dst, vpx_image_t *src, int input_shift) {
433   if (src->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
434     highbd_img_upshift(dst, src, input_shift);
435   } else {
436     lowbd_img_upshift(dst, src, input_shift);
437   }
438 }
439 
vpx_img_truncate_16_to_8(vpx_image_t * dst,vpx_image_t * src)440 void vpx_img_truncate_16_to_8(vpx_image_t *dst, vpx_image_t *src) {
441   int plane;
442   if (dst->fmt + VPX_IMG_FMT_HIGHBITDEPTH != src->fmt || dst->d_w != src->d_w ||
443       dst->d_h != src->d_h || dst->x_chroma_shift != src->x_chroma_shift ||
444       dst->y_chroma_shift != src->y_chroma_shift) {
445     fatal("Unsupported image conversion");
446   }
447   switch (dst->fmt) {
448     case VPX_IMG_FMT_I420:
449     case VPX_IMG_FMT_I422:
450     case VPX_IMG_FMT_I444:
451     case VPX_IMG_FMT_I440: break;
452     default: fatal("Unsupported image conversion"); break;
453   }
454   for (plane = 0; plane < 3; plane++) {
455     int w = src->d_w;
456     int h = src->d_h;
457     int x, y;
458     if (plane) {
459       w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
460       h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
461     }
462     for (y = 0; y < h; y++) {
463       uint16_t *p_src =
464           (uint16_t *)(src->planes[plane] + y * src->stride[plane]);
465       uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
466       for (x = 0; x < w; x++) {
467         *p_dst++ = (uint8_t)(*p_src++);
468       }
469     }
470   }
471 }
472 
highbd_img_downshift(vpx_image_t * dst,vpx_image_t * src,int down_shift)473 static void highbd_img_downshift(vpx_image_t *dst, vpx_image_t *src,
474                                  int down_shift) {
475   int plane;
476   if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
477       dst->x_chroma_shift != src->x_chroma_shift ||
478       dst->y_chroma_shift != src->y_chroma_shift || dst->fmt != src->fmt ||
479       down_shift < 0) {
480     fatal("Unsupported image conversion");
481   }
482   switch (src->fmt) {
483     case VPX_IMG_FMT_I42016:
484     case VPX_IMG_FMT_I42216:
485     case VPX_IMG_FMT_I44416:
486     case VPX_IMG_FMT_I44016: break;
487     default: fatal("Unsupported image conversion"); break;
488   }
489   for (plane = 0; plane < 3; plane++) {
490     int w = src->d_w;
491     int h = src->d_h;
492     int x, y;
493     if (plane) {
494       w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
495       h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
496     }
497     for (y = 0; y < h; y++) {
498       uint16_t *p_src =
499           (uint16_t *)(src->planes[plane] + y * src->stride[plane]);
500       uint16_t *p_dst =
501           (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
502       for (x = 0; x < w; x++) *p_dst++ = *p_src++ >> down_shift;
503     }
504   }
505 }
506 
lowbd_img_downshift(vpx_image_t * dst,vpx_image_t * src,int down_shift)507 static void lowbd_img_downshift(vpx_image_t *dst, vpx_image_t *src,
508                                 int down_shift) {
509   int plane;
510   if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
511       dst->x_chroma_shift != src->x_chroma_shift ||
512       dst->y_chroma_shift != src->y_chroma_shift ||
513       src->fmt != dst->fmt + VPX_IMG_FMT_HIGHBITDEPTH || down_shift < 0) {
514     fatal("Unsupported image conversion");
515   }
516   switch (dst->fmt) {
517     case VPX_IMG_FMT_I420:
518     case VPX_IMG_FMT_I422:
519     case VPX_IMG_FMT_I444:
520     case VPX_IMG_FMT_I440: break;
521     default: fatal("Unsupported image conversion"); break;
522   }
523   for (plane = 0; plane < 3; plane++) {
524     int w = src->d_w;
525     int h = src->d_h;
526     int x, y;
527     if (plane) {
528       w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
529       h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
530     }
531     for (y = 0; y < h; y++) {
532       uint16_t *p_src =
533           (uint16_t *)(src->planes[plane] + y * src->stride[plane]);
534       uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
535       for (x = 0; x < w; x++) {
536         *p_dst++ = *p_src++ >> down_shift;
537       }
538     }
539   }
540 }
541 
vpx_img_downshift(vpx_image_t * dst,vpx_image_t * src,int down_shift)542 void vpx_img_downshift(vpx_image_t *dst, vpx_image_t *src, int down_shift) {
543   if (dst->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
544     highbd_img_downshift(dst, src, down_shift);
545   } else {
546     lowbd_img_downshift(dst, src, down_shift);
547   }
548 }
549 #endif  // CONFIG_VP9_HIGHBITDEPTH
550 
compare_img(const vpx_image_t * const img1,const vpx_image_t * const img2)551 int compare_img(const vpx_image_t *const img1, const vpx_image_t *const img2) {
552   uint32_t l_w = img1->d_w;
553   uint32_t c_w = (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
554   const uint32_t c_h =
555       (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
556   uint32_t i;
557   int match = 1;
558 
559   match &= (img1->fmt == img2->fmt);
560   match &= (img1->d_w == img2->d_w);
561   match &= (img1->d_h == img2->d_h);
562 #if CONFIG_VP9_HIGHBITDEPTH
563   if (img1->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
564     l_w *= 2;
565     c_w *= 2;
566   }
567 #endif
568 
569   for (i = 0; i < img1->d_h; ++i)
570     match &= (memcmp(img1->planes[VPX_PLANE_Y] + i * img1->stride[VPX_PLANE_Y],
571                      img2->planes[VPX_PLANE_Y] + i * img2->stride[VPX_PLANE_Y],
572                      l_w) == 0);
573 
574   for (i = 0; i < c_h; ++i)
575     match &= (memcmp(img1->planes[VPX_PLANE_U] + i * img1->stride[VPX_PLANE_U],
576                      img2->planes[VPX_PLANE_U] + i * img2->stride[VPX_PLANE_U],
577                      c_w) == 0);
578 
579   for (i = 0; i < c_h; ++i)
580     match &= (memcmp(img1->planes[VPX_PLANE_V] + i * img1->stride[VPX_PLANE_V],
581                      img2->planes[VPX_PLANE_V] + i * img2->stride[VPX_PLANE_V],
582                      c_w) == 0);
583 
584   return match;
585 }
586 
587 #define mmin(a, b) ((a) < (b) ? (a) : (b))
588 
589 #if CONFIG_VP9_HIGHBITDEPTH
find_mismatch_high(const vpx_image_t * const img1,const vpx_image_t * const img2,int yloc[4],int uloc[4],int vloc[4])590 void find_mismatch_high(const vpx_image_t *const img1,
591                         const vpx_image_t *const img2, int yloc[4], int uloc[4],
592                         int vloc[4]) {
593   uint16_t *plane1, *plane2;
594   uint32_t stride1, stride2;
595   const uint32_t bsize = 64;
596   const uint32_t bsizey = bsize >> img1->y_chroma_shift;
597   const uint32_t bsizex = bsize >> img1->x_chroma_shift;
598   const uint32_t c_w =
599       (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
600   const uint32_t c_h =
601       (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
602   int match = 1;
603   uint32_t i, j;
604   yloc[0] = yloc[1] = yloc[2] = yloc[3] = -1;
605   plane1 = (uint16_t *)img1->planes[VPX_PLANE_Y];
606   plane2 = (uint16_t *)img2->planes[VPX_PLANE_Y];
607   stride1 = img1->stride[VPX_PLANE_Y] / 2;
608   stride2 = img2->stride[VPX_PLANE_Y] / 2;
609   for (i = 0, match = 1; match && i < img1->d_h; i += bsize) {
610     for (j = 0; match && j < img1->d_w; j += bsize) {
611       int k, l;
612       const int si = mmin(i + bsize, img1->d_h) - i;
613       const int sj = mmin(j + bsize, img1->d_w) - j;
614       for (k = 0; match && k < si; ++k) {
615         for (l = 0; match && l < sj; ++l) {
616           if (*(plane1 + (i + k) * stride1 + j + l) !=
617               *(plane2 + (i + k) * stride2 + j + l)) {
618             yloc[0] = i + k;
619             yloc[1] = j + l;
620             yloc[2] = *(plane1 + (i + k) * stride1 + j + l);
621             yloc[3] = *(plane2 + (i + k) * stride2 + j + l);
622             match = 0;
623             break;
624           }
625         }
626       }
627     }
628   }
629 
630   uloc[0] = uloc[1] = uloc[2] = uloc[3] = -1;
631   plane1 = (uint16_t *)img1->planes[VPX_PLANE_U];
632   plane2 = (uint16_t *)img2->planes[VPX_PLANE_U];
633   stride1 = img1->stride[VPX_PLANE_U] / 2;
634   stride2 = img2->stride[VPX_PLANE_U] / 2;
635   for (i = 0, match = 1; match && i < c_h; i += bsizey) {
636     for (j = 0; match && j < c_w; j += bsizex) {
637       int k, l;
638       const int si = mmin(i + bsizey, c_h - i);
639       const int sj = mmin(j + bsizex, c_w - j);
640       for (k = 0; match && k < si; ++k) {
641         for (l = 0; match && l < sj; ++l) {
642           if (*(plane1 + (i + k) * stride1 + j + l) !=
643               *(plane2 + (i + k) * stride2 + j + l)) {
644             uloc[0] = i + k;
645             uloc[1] = j + l;
646             uloc[2] = *(plane1 + (i + k) * stride1 + j + l);
647             uloc[3] = *(plane2 + (i + k) * stride2 + j + l);
648             match = 0;
649             break;
650           }
651         }
652       }
653     }
654   }
655 
656   vloc[0] = vloc[1] = vloc[2] = vloc[3] = -1;
657   plane1 = (uint16_t *)img1->planes[VPX_PLANE_V];
658   plane2 = (uint16_t *)img2->planes[VPX_PLANE_V];
659   stride1 = img1->stride[VPX_PLANE_V] / 2;
660   stride2 = img2->stride[VPX_PLANE_V] / 2;
661   for (i = 0, match = 1; match && i < c_h; i += bsizey) {
662     for (j = 0; match && j < c_w; j += bsizex) {
663       int k, l;
664       const int si = mmin(i + bsizey, c_h - i);
665       const int sj = mmin(j + bsizex, c_w - j);
666       for (k = 0; match && k < si; ++k) {
667         for (l = 0; match && l < sj; ++l) {
668           if (*(plane1 + (i + k) * stride1 + j + l) !=
669               *(plane2 + (i + k) * stride2 + j + l)) {
670             vloc[0] = i + k;
671             vloc[1] = j + l;
672             vloc[2] = *(plane1 + (i + k) * stride1 + j + l);
673             vloc[3] = *(plane2 + (i + k) * stride2 + j + l);
674             match = 0;
675             break;
676           }
677         }
678       }
679     }
680   }
681 }
682 #endif  // CONFIG_VP9_HIGHBITDEPTH
683 
find_mismatch(const vpx_image_t * const img1,const vpx_image_t * const img2,int yloc[4],int uloc[4],int vloc[4])684 void find_mismatch(const vpx_image_t *const img1, const vpx_image_t *const img2,
685                    int yloc[4], int uloc[4], int vloc[4]) {
686   const uint32_t bsize = 64;
687   const uint32_t bsizey = bsize >> img1->y_chroma_shift;
688   const uint32_t bsizex = bsize >> img1->x_chroma_shift;
689   const uint32_t c_w =
690       (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
691   const uint32_t c_h =
692       (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
693   int match = 1;
694   uint32_t i, j;
695   yloc[0] = yloc[1] = yloc[2] = yloc[3] = -1;
696   for (i = 0, match = 1; match && i < img1->d_h; i += bsize) {
697     for (j = 0; match && j < img1->d_w; j += bsize) {
698       int k, l;
699       const int si = mmin(i + bsize, img1->d_h) - i;
700       const int sj = mmin(j + bsize, img1->d_w) - j;
701       for (k = 0; match && k < si; ++k) {
702         for (l = 0; match && l < sj; ++l) {
703           if (*(img1->planes[VPX_PLANE_Y] +
704                 (i + k) * img1->stride[VPX_PLANE_Y] + j + l) !=
705               *(img2->planes[VPX_PLANE_Y] +
706                 (i + k) * img2->stride[VPX_PLANE_Y] + j + l)) {
707             yloc[0] = i + k;
708             yloc[1] = j + l;
709             yloc[2] = *(img1->planes[VPX_PLANE_Y] +
710                         (i + k) * img1->stride[VPX_PLANE_Y] + j + l);
711             yloc[3] = *(img2->planes[VPX_PLANE_Y] +
712                         (i + k) * img2->stride[VPX_PLANE_Y] + j + l);
713             match = 0;
714             break;
715           }
716         }
717       }
718     }
719   }
720 
721   uloc[0] = uloc[1] = uloc[2] = uloc[3] = -1;
722   for (i = 0, match = 1; match && i < c_h; i += bsizey) {
723     for (j = 0; match && j < c_w; j += bsizex) {
724       int k, l;
725       const int si = mmin(i + bsizey, c_h - i);
726       const int sj = mmin(j + bsizex, c_w - j);
727       for (k = 0; match && k < si; ++k) {
728         for (l = 0; match && l < sj; ++l) {
729           if (*(img1->planes[VPX_PLANE_U] +
730                 (i + k) * img1->stride[VPX_PLANE_U] + j + l) !=
731               *(img2->planes[VPX_PLANE_U] +
732                 (i + k) * img2->stride[VPX_PLANE_U] + j + l)) {
733             uloc[0] = i + k;
734             uloc[1] = j + l;
735             uloc[2] = *(img1->planes[VPX_PLANE_U] +
736                         (i + k) * img1->stride[VPX_PLANE_U] + j + l);
737             uloc[3] = *(img2->planes[VPX_PLANE_U] +
738                         (i + k) * img2->stride[VPX_PLANE_U] + j + l);
739             match = 0;
740             break;
741           }
742         }
743       }
744     }
745   }
746   vloc[0] = vloc[1] = vloc[2] = vloc[3] = -1;
747   for (i = 0, match = 1; match && i < c_h; i += bsizey) {
748     for (j = 0; match && j < c_w; j += bsizex) {
749       int k, l;
750       const int si = mmin(i + bsizey, c_h - i);
751       const int sj = mmin(j + bsizex, c_w - j);
752       for (k = 0; match && k < si; ++k) {
753         for (l = 0; match && l < sj; ++l) {
754           if (*(img1->planes[VPX_PLANE_V] +
755                 (i + k) * img1->stride[VPX_PLANE_V] + j + l) !=
756               *(img2->planes[VPX_PLANE_V] +
757                 (i + k) * img2->stride[VPX_PLANE_V] + j + l)) {
758             vloc[0] = i + k;
759             vloc[1] = j + l;
760             vloc[2] = *(img1->planes[VPX_PLANE_V] +
761                         (i + k) * img1->stride[VPX_PLANE_V] + j + l);
762             vloc[3] = *(img2->planes[VPX_PLANE_V] +
763                         (i + k) * img2->stride[VPX_PLANE_V] + j + l);
764             match = 0;
765             break;
766           }
767         }
768       }
769     }
770   }
771 }
772