Detect chroma subsampling more directly

In av1_copy_and_extend_frame(), detect chroma subsampling more directly
by testing src->subsampling_x and src->subsampling_y. The original tests
do not work when src->y_width or src->y_height is equal to 1.

BUG=aomedia:3113

Change-Id: I4665e2eeeeec415d73b50beaa896b66490fa97ba
(cherry picked from commit bce31a8248b3f339acbf695972384177370c4cb0)
diff --git a/av1/encoder/extend.c b/av1/encoder/extend.c
index 381aec8..46b7402 100644
--- a/av1/encoder/extend.c
+++ b/av1/encoder/extend.c
@@ -9,6 +9,8 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 
+#include <assert.h>
+
 #include "aom_dsp/aom_dsp_common.h"
 #include "aom_mem/aom_mem.h"
 #include "aom_ports/mem.h"
@@ -45,6 +47,7 @@
   dst_ptr1 = dst + dst_pitch * (-extend_top) - extend_left;
   dst_ptr2 = dst + dst_pitch * (h)-extend_left;
   linesize = extend_left + extend_right + w;
+  assert(linesize <= dst_pitch);
 
   for (i = 0; i < extend_top; i++) {
     memcpy(dst_ptr1, src_ptr1, linesize);
@@ -88,6 +91,7 @@
   dst_ptr1 = dst + dst_pitch * (-extend_top) - extend_left;
   dst_ptr2 = dst + dst_pitch * (h)-extend_left;
   linesize = extend_left + extend_right + w;
+  assert(linesize <= dst_pitch);
 
   for (i = 0; i < extend_top; i++) {
     memcpy(dst_ptr1, src_ptr1, linesize * sizeof(src_ptr1[0]));
@@ -111,8 +115,8 @@
   const int eb_y = AOMMAX(src->y_height + dst->border,
                           ALIGN_POWER_OF_TWO(src->y_height, 6)) -
                    src->y_crop_height;
-  const int uv_width_subsampling = (src->uv_width != src->y_width);
-  const int uv_height_subsampling = (src->uv_height != src->y_height);
+  const int uv_width_subsampling = src->subsampling_x;
+  const int uv_height_subsampling = src->subsampling_y;
   const int et_uv = et_y >> uv_height_subsampling;
   const int el_uv = el_y >> uv_width_subsampling;
   const int eb_uv = eb_y >> uv_height_subsampling;
diff --git a/test/encode_small_width_height_test.cc b/test/encode_small_width_height_test.cc
index 6f52fd5..9bd9471 100644
--- a/test/encode_small_width_height_test.cc
+++ b/test/encode_small_width_height_test.cc
@@ -15,10 +15,11 @@
 // (<= one AV1 superblock) with multiple threads. aom_codec_encode() should
 // not crash.
 
-#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
+#include <memory>
 
 #include "aom/aomcx.h"
 #include "aom/aom_encoder.h"
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
 
 namespace {
 
@@ -121,4 +122,54 @@
   EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
 }
 
+// A reproducer test for aomedia:3113. The test should complete without any
+// memory errors.
+TEST(EncodeSmallWidthHeight, 1x1) {
+  constexpr int kWidth = 1;
+  constexpr int kHeight = 1;
+
+  // This test cannot use aom_img_alloc() or aom_img_wrap() because they call
+  // align_image_dimension() to align img.w and img.h to the next even number
+  // (2). In this test it is important to set img.w and img.h to 1. Therefore we
+  // set up img manually.
+  aom_image_t img;
+  memset(&img, 0, sizeof(img));
+  img.fmt = AOM_IMG_FMT_I420;
+  img.bit_depth = 8;
+  img.w = kWidth;
+  img.h = kHeight;
+  img.d_w = kWidth;
+  img.d_h = kHeight;
+  img.x_chroma_shift = 1;
+  img.y_chroma_shift = 1;
+  img.bps = 12;
+  int y_stride = kWidth;
+  int uv_stride = (kWidth + 1) >> 1;
+  int y_height = kHeight;
+  int uv_height = (kHeight + 1) >> 1;
+  img.stride[AOM_PLANE_Y] = y_stride;
+  img.stride[AOM_PLANE_U] = img.stride[AOM_PLANE_V] = uv_stride;
+  std::unique_ptr<unsigned char[]> y_plane(
+      new unsigned char[y_height * y_stride]());
+  std::unique_ptr<unsigned char[]> u_plane(
+      new unsigned char[uv_height * uv_stride]());
+  std::unique_ptr<unsigned char[]> v_plane(
+      new unsigned char[uv_height * uv_stride]());
+  img.planes[AOM_PLANE_Y] = y_plane.get();
+  img.planes[AOM_PLANE_U] = u_plane.get();
+  img.planes[AOM_PLANE_V] = v_plane.get();
+
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_default(iface, &cfg, 0));
+  cfg.g_w = kWidth;
+  cfg.g_h = kHeight;
+  aom_codec_ctx_t enc;
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_control(&enc, AOME_SET_CPUUSED, 5));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, NULL, 0, 0, 0));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
+}
+
 }  // namespace