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