Ext-intra modification/tuning

For ext-intra direcation intra modes:

1. Use neighbor block modes to modify edge filtering strength
   Coding gain (lowres/midres/hdres):
     (8 keyframes)
     PSNR: -0.19 -0.22 -0.10
     SSIM: -0.29 -0.27 -0.13

2. Use context-based cdf to code angle_delta syntax
     (8 keyframes)
     PSNR: -0.20 -0.24 -0.27
     SSIM: -0.29 -0.33 -0.37

3. Filter corner sample:
     (8 keyframes)
     PSNR: -0.01 -0.02 -0.05
     SSIM: -0.03 -0.04 -0.05

Combined Bd-rate improvement for 8 keyframes
     PSNR: -0.40 -0.47 -0.40
     SSIM: -0.57 -0.60 -0.51

Change-Id: Id47ac17b6bf91cd810b70cacfc5b457341f417f3
diff --git a/av1/common/blockd.h b/av1/common/blockd.h
index 5d2d2ca..156f44c 100644
--- a/av1/common/blockd.h
+++ b/av1/common/blockd.h
@@ -1146,8 +1146,6 @@
 }
 
 #if CONFIG_EXT_INTRA
-#define MAX_ANGLE_DELTA 3
-#define ANGLE_STEP 3
 extern const int16_t dr_intra_derivative[90];
 static const uint8_t mode_to_angle_map[] = {
   0, 90, 180, 45, 135, 111, 157, 203, 67, 0, 0,
diff --git a/av1/common/entropymode.c b/av1/common/entropymode.c
index 0d2d1ed..7d24619 100644
--- a/av1/common/entropymode.c
+++ b/av1/common/entropymode.c
@@ -6160,6 +6160,28 @@
     };
 #endif  // CONFIG_LPF_SB
 
+#if CONFIG_EXT_INTRA_MOD
+const aom_cdf_prob default_angle_delta_cdf[DIRECTIONAL_MODES][CDF_SIZE(
+    2 * MAX_ANGLE_DELTA + 1)] = {
+  { AOM_ICDF(2340), AOM_ICDF(5327), AOM_ICDF(7611), AOM_ICDF(23102),
+    AOM_ICDF(27196), AOM_ICDF(30546), AOM_ICDF(32768), 0 },
+  { AOM_ICDF(3267), AOM_ICDF(8071), AOM_ICDF(11970), AOM_ICDF(21822),
+    AOM_ICDF(25619), AOM_ICDF(30034), AOM_ICDF(32768), 0 },
+  { AOM_ICDF(3417), AOM_ICDF(9937), AOM_ICDF(12286), AOM_ICDF(16420),
+    AOM_ICDF(19941), AOM_ICDF(30669), AOM_ICDF(32768), 0 },
+  { AOM_ICDF(5167), AOM_ICDF(11735), AOM_ICDF(15254), AOM_ICDF(16662),
+    AOM_ICDF(20697), AOM_ICDF(28276), AOM_ICDF(32768), 0 },
+  { AOM_ICDF(1728), AOM_ICDF(10973), AOM_ICDF(14103), AOM_ICDF(18547),
+    AOM_ICDF(22684), AOM_ICDF(27007), AOM_ICDF(32768), 0 },
+  { AOM_ICDF(2764), AOM_ICDF(10700), AOM_ICDF(12517), AOM_ICDF(16957),
+    AOM_ICDF(20590), AOM_ICDF(30390), AOM_ICDF(32768), 0 },
+  { AOM_ICDF(2407), AOM_ICDF(12749), AOM_ICDF(16527), AOM_ICDF(20823),
+    AOM_ICDF(22781), AOM_ICDF(29642), AOM_ICDF(32768), 0 },
+  { AOM_ICDF(3068), AOM_ICDF(10132), AOM_ICDF(12079), AOM_ICDF(16542),
+    AOM_ICDF(19943), AOM_ICDF(30448), AOM_ICDF(32768), 0 }
+};
+#endif  // CONFIG_EXT_INTRA_MOD
+
 static void init_mode_probs(FRAME_CONTEXT *fc) {
   av1_copy(fc->partition_prob, default_partition_probs);
   av1_copy(fc->intra_inter_prob, default_intra_inter_p);
@@ -6169,6 +6191,9 @@
   av1_copy(fc->palette_y_color_index_cdf, default_palette_y_color_index_cdf);
   av1_copy(fc->palette_uv_color_index_cdf, default_palette_uv_color_index_cdf);
   av1_copy(fc->kf_y_cdf, default_kf_y_mode_cdf);
+#if CONFIG_EXT_INTRA_MOD
+  av1_copy(fc->angle_delta_cdf, default_angle_delta_cdf);
+#endif  // CONFIG_EXT_INTRA_MOD
 #if CONFIG_MRC_TX
   av1_copy(fc->mrc_mask_inter_cdf, default_mrc_mask_inter_cdf);
   av1_copy(fc->mrc_mask_intra_cdf, default_mrc_mask_intra_cdf);
diff --git a/av1/common/entropymode.h b/av1/common/entropymode.h
index d278238..399e22b 100644
--- a/av1/common/entropymode.h
+++ b/av1/common/entropymode.h
@@ -338,6 +338,12 @@
 #else
   aom_cdf_prob kf_y_cdf[INTRA_MODES][INTRA_MODES][CDF_SIZE(INTRA_MODES)];
 #endif
+
+#if CONFIG_EXT_INTRA_MOD
+  aom_cdf_prob angle_delta_cdf[DIRECTIONAL_MODES]
+                              [CDF_SIZE(2 * MAX_ANGLE_DELTA + 1)];
+#endif  // CONFIG_EXT_INTRA_MOD
+
   aom_cdf_prob tx_size_cdf[MAX_TX_DEPTH][TX_SIZE_CONTEXTS]
                           [CDF_SIZE(MAX_TX_DEPTH + 1)];
   aom_cdf_prob delta_q_cdf[CDF_SIZE(DELTA_Q_PROBS + 1)];
@@ -383,6 +389,7 @@
 // aggregates built solely from 'unsigned int' fields/elements
 #if CONFIG_ENTROPY_STATS
   unsigned int kf_y_mode[INTRA_MODES][INTRA_MODES][INTRA_MODES];
+  unsigned int angle_delta[DIRECTIONAL_MODES][2 * MAX_ANGLE_DELTA + 1];
   unsigned int y_mode[BLOCK_SIZE_GROUPS][INTRA_MODES];
   unsigned int uv_mode[INTRA_MODES][UV_INTRA_MODES];
 #endif  // CONFIG_ENTROPY_STATS
diff --git a/av1/common/enums.h b/av1/common/enums.h
index a114469..7f2f1c9 100644
--- a/av1/common/enums.h
+++ b/av1/common/enums.h
@@ -628,6 +628,8 @@
 
 #if CONFIG_EXT_INTRA
 #define DIRECTIONAL_MODES 8
+#define MAX_ANGLE_DELTA 3
+#define ANGLE_STEP 3
 #endif  // CONFIG_EXT_INTRA
 
 #define INTER_MODES (1 + NEWMV - NEARESTMV)
diff --git a/av1/common/reconintra.c b/av1/common/reconintra.c
index 87870e0..33893f9 100644
--- a/av1/common/reconintra.c
+++ b/av1/common/reconintra.c
@@ -40,7 +40,7 @@
 #define INTRA_EDGE_FILT 3
 #define INTRA_EDGE_TAPS 5
 #if CONFIG_INTRA_EDGE_UPSAMPLE
-#define MAX_UPSAMPLE_SZ 12
+#define MAX_UPSAMPLE_SZ 16
 #endif  // CONFIG_INTRA_EDGE_UPSAMPLE
 #endif  // CONFIG_INTRA_EDGE
 
@@ -1748,11 +1748,62 @@
 #endif  // CONFIG_FILTER_INTRA
 
 #if CONFIG_INTRA_EDGE
-static int intra_edge_filter_strength(int bsz, int delta) {
+static int is_smooth(MB_MODE_INFO *mbmi) {
+  return (mbmi->mode == SMOOTH_PRED || mbmi->mode == SMOOTH_V_PRED ||
+          mbmi->mode == SMOOTH_H_PRED);
+}
+
+static int get_filt_type(const MACROBLOCKD *xd) {
+  MB_MODE_INFO *ab = xd->up_available ? &xd->mi[-xd->mi_stride]->mbmi : 0;
+  MB_MODE_INFO *le = xd->left_available ? &xd->mi[-1]->mbmi : 0;
+
+  const int ab_sm = ab ? is_smooth(ab) : 0;
+  const int le_sm = le ? is_smooth(le) : 0;
+
+  return (ab_sm || le_sm) ? 1 : 0;
+}
+
+static int intra_edge_filter_strength(int bs0, int bs1, int delta, int type) {
   const int d = abs(delta);
   int strength = 0;
 
-  switch (bsz) {
+#if CONFIG_EXT_INTRA_MOD
+  const int blk_wh = bs0 + bs1;
+  if (type == 0) {
+    if (blk_wh <= 8) {
+      if (d >= 56) strength = 1;
+    } else if (blk_wh <= 12) {
+      if (d >= 40) strength = 1;
+    } else if (blk_wh <= 16) {
+      if (d >= 40) strength = 1;
+    } else if (blk_wh <= 24) {
+      if (d >= 8) strength = 1;
+      if (d >= 16) strength = 2;
+      if (d >= 32) strength = 3;
+    } else if (blk_wh <= 32) {
+      if (d >= 1) strength = 1;
+      if (d >= 4) strength = 2;
+      if (d >= 32) strength = 3;
+    } else {
+      if (d >= 1) strength = 3;
+    }
+  } else {
+    if (blk_wh <= 8) {
+      if (d >= 40) strength = 1;
+      if (d >= 64) strength = 2;
+    } else if (blk_wh <= 16) {
+      if (d >= 20) strength = 1;
+      if (d >= 48) strength = 2;
+    } else if (blk_wh <= 24) {
+      if (d >= 4) strength = 3;
+    } else {
+      if (d >= 1) strength = 3;
+    }
+  }
+#else
+  (void)type;
+  (void)bs1;
+  switch (bs0) {
     case 4:
       if (d < 56) {
         strength = 0;
@@ -1787,7 +1838,7 @@
       break;
     default: strength = 0; break;
   }
-
+#endif  // CONFIG_EXT_INTRA_MOD
   return strength;
 }
 
@@ -1814,6 +1865,18 @@
   }
 }
 
+#if CONFIG_EXT_INTRA_MOD
+void av1_filter_intra_edge_corner(uint8_t *p_above, uint8_t *p_left) {
+  const int kernel[3] = { 5, 6, 5 };
+
+  int s = (p_left[0] * kernel[0]) + (p_above[-1] * kernel[1]) +
+          (p_above[0] * kernel[2]);
+  s = (s + 8) >> 4;
+  p_above[-1] = s;
+  p_left[-1] = s;
+}
+#endif  // CONFIG_EXT_INTRA_MOD
+
 #if CONFIG_HIGHBITDEPTH
 void av1_filter_intra_edge_high_c(uint16_t *p, int sz, int strength) {
   if (!strength) return;
@@ -1837,12 +1900,34 @@
     p[i] = s;
   }
 }
+
+#if CONFIG_EXT_INTRA_MOD
+void av1_filter_intra_edge_corner_high(uint16_t *p_above, uint16_t *p_left) {
+  const int kernel[3] = { 5, 6, 5 };
+
+  int s = (p_left[0] * kernel[0]) + (p_above[-1] * kernel[1]) +
+          (p_above[0] * kernel[2]);
+  s = (s + 8) >> 4;
+  p_above[-1] = s;
+  p_left[-1] = s;
+}
+#endif  // CONFIG_EXT_INTRA_MOD
+
 #endif  // CONFIG_HIGHBITDEPTH
 
 #if CONFIG_INTRA_EDGE_UPSAMPLE
-static int use_intra_edge_upsample(int bsz, int delta) {
+static int use_intra_edge_upsample(int bs0, int bs1, int delta, int type) {
   const int d = abs(delta);
-  return (bsz == 4 && d > 0 && d < 56);
+
+#if CONFIG_EXT_INTRA_MOD
+  const int blk_wh = bs0 + bs1;
+  if (d <= 0 || d >= 40) return 0;
+  return type ? (blk_wh <= 8) : (blk_wh <= 16);
+#else
+  (void)type;
+  (void)bs1;
+  return (bs0 == 4 && d > 0 && d < 56);
+#endif  // CONFIG_EXT_INTRA_MOD
 }
 
 void av1_upsample_intra_edge_c(uint8_t *p, int sz) {
@@ -2087,27 +2172,37 @@
 #if CONFIG_INTRA_EDGE
     const int need_right = p_angle < 90;
     const int need_bottom = p_angle > 180;
+    const int filt_type = get_filt_type(xd);
     if (p_angle != 90 && p_angle != 180) {
       const int ab_le = need_above_left ? 1 : 0;
+#if CONFIG_EXT_INTRA_MOD
+      if (need_above && need_left && (txwpx + txhpx >= 24)) {
+        av1_filter_intra_edge_corner_high(above_row, left_col);
+      }
+#endif  // CONFIG_EXT_INTRA_MOD
       if (need_above && n_top_px > 0) {
-        const int strength = intra_edge_filter_strength(txwpx, p_angle - 90);
+        const int strength =
+            intra_edge_filter_strength(txwpx, txhpx, p_angle - 90, filt_type);
         const int n_px = n_top_px + ab_le + (need_right ? n_topright_px : 0);
         av1_filter_intra_edge_high(above_row - ab_le, n_px, strength);
       }
       if (need_left && n_left_px > 0) {
-        const int strength = intra_edge_filter_strength(txhpx, p_angle - 180);
+        const int strength =
+            intra_edge_filter_strength(txhpx, txwpx, p_angle - 180, filt_type);
         const int n_px =
             n_left_px + ab_le + (need_bottom ? n_bottomleft_px : 0);
         av1_filter_intra_edge_high(left_col - ab_le, n_px, strength);
       }
     }
 #if CONFIG_INTRA_EDGE_UPSAMPLE
-    const int upsample_above = use_intra_edge_upsample(txwpx, p_angle - 90);
+    const int upsample_above =
+        use_intra_edge_upsample(txwpx, txhpx, p_angle - 90, filt_type);
     if (need_above && upsample_above) {
       const int n_px = txwpx + (need_right ? txhpx : 0);
       av1_upsample_intra_edge_high(above_row, n_px, xd->bd);
     }
-    const int upsample_left = use_intra_edge_upsample(txhpx, p_angle - 180);
+    const int upsample_left =
+        use_intra_edge_upsample(txhpx, txwpx, p_angle - 180, filt_type);
     if (need_left && upsample_left) {
       const int n_px = txhpx + (need_bottom ? txwpx : 0);
       av1_upsample_intra_edge_high(left_col, n_px, xd->bd);
@@ -2321,27 +2416,37 @@
 #if CONFIG_INTRA_EDGE
     const int need_right = p_angle < 90;
     const int need_bottom = p_angle > 180;
+    const int filt_type = get_filt_type(xd);
     if (p_angle != 90 && p_angle != 180) {
       const int ab_le = need_above_left ? 1 : 0;
+#if CONFIG_EXT_INTRA_MOD
+      if (need_above && need_left && (txwpx + txhpx >= 24)) {
+        av1_filter_intra_edge_corner(above_row, left_col);
+      }
+#endif  // CONFIG_EXT_INTRA_MOD
       if (need_above && n_top_px > 0) {
-        const int strength = intra_edge_filter_strength(txwpx, p_angle - 90);
+        const int strength =
+            intra_edge_filter_strength(txwpx, txhpx, p_angle - 90, filt_type);
         const int n_px = n_top_px + ab_le + (need_right ? n_topright_px : 0);
         av1_filter_intra_edge(above_row - ab_le, n_px, strength);
       }
       if (need_left && n_left_px > 0) {
-        const int strength = intra_edge_filter_strength(txhpx, p_angle - 180);
+        const int strength =
+            intra_edge_filter_strength(txhpx, txwpx, p_angle - 180, filt_type);
         const int n_px =
             n_left_px + ab_le + (need_bottom ? n_bottomleft_px : 0);
         av1_filter_intra_edge(left_col - ab_le, n_px, strength);
       }
     }
 #if CONFIG_INTRA_EDGE_UPSAMPLE
-    const int upsample_above = use_intra_edge_upsample(txwpx, p_angle - 90);
+    const int upsample_above =
+        use_intra_edge_upsample(txwpx, txhpx, p_angle - 90, filt_type);
     if (need_above && upsample_above) {
       const int n_px = txwpx + (need_right ? txhpx : 0);
       av1_upsample_intra_edge(above_row, n_px);
     }
-    const int upsample_left = use_intra_edge_upsample(txhpx, p_angle - 180);
+    const int upsample_left =
+        use_intra_edge_upsample(txhpx, txwpx, p_angle - 180, filt_type);
     if (need_left && upsample_left) {
       const int n_px = txhpx + (need_bottom ? txwpx : 0);
       av1_upsample_intra_edge(left_col, n_px);
diff --git a/av1/decoder/decodemv.c b/av1/decoder/decodemv.c
index 6f127e6..ae4529f 100644
--- a/av1/decoder/decodemv.c
+++ b/av1/decoder/decodemv.c
@@ -883,21 +883,42 @@
 #endif  // CONFIG_FILTER_INTRA
 
 #if CONFIG_EXT_INTRA
+#if CONFIG_EXT_INTRA_MOD
+static int read_angle_delta(aom_reader *r, aom_cdf_prob *cdf) {
+  const int sym = aom_read_symbol(r, cdf, 2 * MAX_ANGLE_DELTA + 1, ACCT_STR);
+  return sym - MAX_ANGLE_DELTA;
+}
+#endif  // CONFIG_EXT_INTRA_MOD
+
 static void read_intra_angle_info(MACROBLOCKD *const xd, aom_reader *r) {
   MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
   const BLOCK_SIZE bsize = mbmi->sb_type;
+#if CONFIG_EXT_INTRA_MOD
+  FRAME_CONTEXT *const ec_ctx = xd->tile_ctx;
+#endif  // CONFIG_EXT_INTRA_MOD
+
   mbmi->angle_delta[0] = 0;
   mbmi->angle_delta[1] = 0;
   if (!av1_use_angle_delta(bsize)) return;
 
   if (av1_is_directional_mode(mbmi->mode, bsize)) {
+#if CONFIG_EXT_INTRA_MOD
+    mbmi->angle_delta[0] =
+        read_angle_delta(r, ec_ctx->angle_delta_cdf[mbmi->mode - V_PRED]);
+#else
     mbmi->angle_delta[0] =
         av1_read_uniform(r, 2 * MAX_ANGLE_DELTA + 1) - MAX_ANGLE_DELTA;
+#endif  // CONFIG_EXT_INTRA_MOD
   }
 
   if (av1_is_directional_mode(get_uv_mode(mbmi->uv_mode), bsize)) {
+#if CONFIG_EXT_INTRA_MOD
+    mbmi->angle_delta[1] =
+        read_angle_delta(r, ec_ctx->angle_delta_cdf[mbmi->uv_mode - V_PRED]);
+#else
     mbmi->angle_delta[1] =
         av1_read_uniform(r, 2 * MAX_ANGLE_DELTA + 1) - MAX_ANGLE_DELTA;
+#endif  // CONFIG_EXT_INTRA_MOD
   }
 }
 #endif  // CONFIG_EXT_INTRA
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index b484a6a..99a166e 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -1049,19 +1049,33 @@
 #endif  // CONFIG_FILTER_INTRA
 
 #if CONFIG_EXT_INTRA
-static void write_intra_angle_info(const MACROBLOCKD *xd, aom_writer *w) {
+static void write_intra_angle_info(const MACROBLOCKD *xd,
+                                   FRAME_CONTEXT *const ec_ctx, aom_writer *w) {
   const MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
   const BLOCK_SIZE bsize = mbmi->sb_type;
   if (!av1_use_angle_delta(bsize)) return;
 
   if (av1_is_directional_mode(mbmi->mode, bsize)) {
+#if CONFIG_EXT_INTRA_MOD
+    aom_write_symbol(w, mbmi->angle_delta[0] + MAX_ANGLE_DELTA,
+                     ec_ctx->angle_delta_cdf[mbmi->mode - V_PRED],
+                     2 * MAX_ANGLE_DELTA + 1);
+#else
+    (void)ec_ctx;
     write_uniform(w, 2 * MAX_ANGLE_DELTA + 1,
                   MAX_ANGLE_DELTA + mbmi->angle_delta[0]);
+#endif  // CONFIG_EXT_INTRA_MOD
   }
 
   if (av1_is_directional_mode(get_uv_mode(mbmi->uv_mode), bsize)) {
+#if CONFIG_EXT_INTRA_MOD
+    aom_write_symbol(w, mbmi->angle_delta[1] + MAX_ANGLE_DELTA,
+                     ec_ctx->angle_delta_cdf[mbmi->uv_mode - V_PRED],
+                     2 * MAX_ANGLE_DELTA + 1);
+#else
     write_uniform(w, 2 * MAX_ANGLE_DELTA + 1,
                   MAX_ANGLE_DELTA + mbmi->angle_delta[1]);
+#endif
   }
 }
 #endif  // CONFIG_EXT_INTRA
@@ -1594,7 +1608,7 @@
     }
 
 #if CONFIG_EXT_INTRA
-    write_intra_angle_info(xd, w);
+    write_intra_angle_info(xd, ec_ctx, w);
 #endif  // CONFIG_EXT_INTRA
     if (av1_allow_palette(cm->allow_screen_content_tools, bsize))
       write_palette_mode_info(cm, xd, mi, w);
@@ -1933,7 +1947,7 @@
   }
 
 #if CONFIG_EXT_INTRA
-  write_intra_angle_info(xd, w);
+  write_intra_angle_info(xd, ec_ctx, w);
 #endif  // CONFIG_EXT_INTRA
   if (av1_allow_palette(cm->allow_screen_content_tools, bsize))
     write_palette_mode_info(cm, xd, mi, w);
diff --git a/av1/encoder/block.h b/av1/encoder/block.h
index 049fa83..fd0e58e 100644
--- a/av1/encoder/block.h
+++ b/av1/encoder/block.h
@@ -284,6 +284,9 @@
   int intra_tx_type_costs[EXT_TX_SIZES][TX_TYPES][TX_TYPES];
   int inter_tx_type_costs[EXT_TX_SIZES][TX_TYPES];
 #endif  // CONFIG_EXT_TX
+#if CONFIG_EXT_INTRA && CONFIG_EXT_INTRA_MOD
+  int angle_delta_cost[DIRECTIONAL_MODES][2 * MAX_ANGLE_DELTA + 1];
+#endif  // CONFIG_EXT_INTRA && CONFIG_EXT_INTRA_MOD
 #if CONFIG_LOOP_RESTORATION
   int switchable_restore_cost[RESTORE_SWITCHABLE_TYPES];
   int wiener_restore_cost[2];
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index c8bae01..b866250 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -4265,10 +4265,32 @@
                FILTER_INTRA_MODES);
   }
 #endif  // CONFIG_FILTER_INTRA
+#if CONFIG_EXT_INTRA && CONFIG_EXT_INTRA_MOD
+  if (av1_is_directional_mode(mbmi->mode, bsize) &&
+      av1_use_angle_delta(bsize)) {
+#if CONFIG_ENTROPY_STATS
+    ++counts->angle_delta[mbmi->mode - V_PRED]
+                         [mbmi->angle_delta[0] + MAX_ANGLE_DELTA];
+#endif
+    update_cdf(fc->angle_delta_cdf[mbmi->mode - V_PRED],
+               mbmi->angle_delta[0] + MAX_ANGLE_DELTA, 2 * MAX_ANGLE_DELTA + 1);
+  }
+#endif  // CONFIG_EXT_INTRA && CONFIG_EXT_INTRA_MOD
 
   if (!is_chroma_reference(mi_row, mi_col, bsize, xd->plane[1].subsampling_x,
                            xd->plane[1].subsampling_y))
     return;
+#if CONFIG_EXT_INTRA && CONFIG_EXT_INTRA_MOD
+  if (av1_is_directional_mode(mbmi->uv_mode, bsize) &&
+      av1_use_angle_delta(bsize)) {
+#if CONFIG_ENTROPY_STATS
+    ++counts->angle_delta[mbmi->uv_mode - V_PRED]
+                         [mbmi->angle_delta[1] + MAX_ANGLE_DELTA];
+#endif
+    update_cdf(fc->angle_delta_cdf[mbmi->uv_mode - V_PRED],
+               mbmi->angle_delta[1] + MAX_ANGLE_DELTA, 2 * MAX_ANGLE_DELTA + 1);
+  }
+#endif  // CONFIG_EXT_INTRA && CONFIG_EXT_INTRA_MOD
 #if CONFIG_ENTROPY_STATS
   ++counts->uv_mode[y_mode][uv_mode];
 #endif  // CONFIG_ENTROPY_STATS
diff --git a/av1/encoder/rd.c b/av1/encoder/rd.c
index 9987467..5ecd86a 100644
--- a/av1/encoder/rd.c
+++ b/av1/encoder/rd.c
@@ -293,6 +293,12 @@
                              av1_ext_tx_inv);
   }
 #endif  // CONFIG_EXT_TX
+#if CONFIG_EXT_INTRA && CONFIG_EXT_INTRA_MOD
+  for (i = 0; i < DIRECTIONAL_MODES; ++i) {
+    av1_cost_tokens_from_cdf(x->angle_delta_cost[i], fc->angle_delta_cdf[i],
+                             NULL);
+  }
+#endif  // CONFIG_EXT_INTRA && CONFIG_EXT_INTRA_MOD
 #if CONFIG_LOOP_RESTORATION
   av1_cost_tokens_from_cdf(x->switchable_restore_cost,
                            fc->switchable_restore_cdf, NULL);
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index c9c766a..90cb82d 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -3024,8 +3024,13 @@
 #if CONFIG_EXT_INTRA
   if (av1_is_directional_mode(mbmi->mode, bsize) &&
       av1_use_angle_delta(bsize)) {
+#if CONFIG_EXT_INTRA_MOD
+    mode_cost += x->angle_delta_cost[mbmi->mode - V_PRED]
+                                    [MAX_ANGLE_DELTA + mbmi->angle_delta[0]];
+#else
     mode_cost += write_uniform_cost(2 * MAX_ANGLE_DELTA + 1,
                                     MAX_ANGLE_DELTA + mbmi->angle_delta[0]);
+#endif  // CONFIG_EXT_INTRA_MOD
   }
 #endif  // CONFIG_EXT_INTRA
 #if CONFIG_FILTER_INTRA
@@ -3373,8 +3378,13 @@
   if (tokenonly_rd_stats.rate == INT_MAX) return INT64_MAX;
 
   this_rate = tokenonly_rd_stats.rate + mode_cost +
+#if CONFIG_EXT_INTRA_MOD
+              x->angle_delta_cost[mbmi->mode - V_PRED]
+                                 [max_angle_delta + mbmi->angle_delta[0]];
+#else
               write_uniform_cost(2 * max_angle_delta + 1,
                                  mbmi->angle_delta[0] + max_angle_delta);
+#endif  // CONFIG_EXT_INTRA_MOD
   this_rd = RDCOST(x->rdmult, this_rate, tokenonly_rd_stats.dist);
 
   if (this_rd < *best_rd) {
@@ -3748,8 +3758,14 @@
 #if CONFIG_EXT_INTRA
     if (is_directional_mode) {
       if (av1_use_angle_delta(bsize)) {
+#if CONFIG_EXT_INTRA_MOD
+        this_rate +=
+            x->angle_delta_cost[mbmi->mode - V_PRED]
+                               [MAX_ANGLE_DELTA + mbmi->angle_delta[0]];
+#else
         this_rate += write_uniform_cost(2 * MAX_ANGLE_DELTA + 1,
                                         MAX_ANGLE_DELTA + mbmi->angle_delta[0]);
+#endif  // CONFIG_EXT_INTRA_MOD
       }
     }
 #endif  // CONFIG_EXT_INTRA
@@ -5294,6 +5310,10 @@
   if (!super_block_uvrd(cpi, x, &tokenonly_rd_stats, bsize, best_rd_in))
     return INT64_MAX;
   this_rate = tokenonly_rd_stats.rate + rate_overhead;
+#if CONFIG_EXT_INTRA_MOD
+  this_rate += x->angle_delta_cost[mbmi->uv_mode - V_PRED]
+                                  [mbmi->angle_delta[1] + MAX_ANGLE_DELTA];
+#endif  // CONFIG_EXT_INTRA_MOD
   this_rd = RDCOST(x->rdmult, this_rate, tokenonly_rd_stats.dist);
   if (this_rd < *best_rd) {
     *best_rd = this_rd;
@@ -5505,7 +5525,11 @@
     mbmi->angle_delta[1] = 0;
     if (is_directional_mode && av1_use_angle_delta(mbmi->sb_type)) {
       const int rate_overhead = x->intra_uv_mode_cost[mbmi->mode][mode] +
+#if CONFIG_EXT_INTRA_MOD
+                                0;
+#else
                                 write_uniform_cost(2 * MAX_ANGLE_DELTA + 1, 0);
+#endif  // CONFIG_EXT_INTRA_MOD
       if (!rd_pick_intra_angle_sbuv(cpi, x, bsize, rate_overhead, best_rd,
                                     &this_rate, &tokenonly_rd_stats))
         continue;
@@ -5530,8 +5554,13 @@
 #endif
 #if CONFIG_EXT_INTRA
     if (is_directional_mode && av1_use_angle_delta(mbmi->sb_type)) {
+#if CONFIG_EXT_INTRA_MOD
+      this_rate += x->angle_delta_cost[mode - V_PRED]
+                                      [mbmi->angle_delta[1] + MAX_ANGLE_DELTA];
+#else
       this_rate += write_uniform_cost(2 * MAX_ANGLE_DELTA + 1,
                                       MAX_ANGLE_DELTA + mbmi->angle_delta[1]);
+#endif  // CONFIG_EXT_INTRA_MOD
     }
 #endif  // CONFIG_EXT_INTRA
 
@@ -10125,14 +10154,24 @@
 #if CONFIG_EXT_INTRA
       if (is_directional_mode) {
         if (av1_use_angle_delta(bsize)) {
+#if CONFIG_EXT_INTRA_MOD
+          rate2 += x->angle_delta_cost[mbmi->mode - V_PRED]
+                                      [mbmi->angle_delta[0] + MAX_ANGLE_DELTA];
+#else
           rate2 += write_uniform_cost(2 * MAX_ANGLE_DELTA + 1,
                                       MAX_ANGLE_DELTA + mbmi->angle_delta[0]);
+#endif  // CONFIG_EXT_INTRA_MOD
         }
       }
       if (av1_is_directional_mode(get_uv_mode(mbmi->uv_mode), bsize) &&
           av1_use_angle_delta(bsize)) {
+#if CONFIG_EXT_INTRA_MOD
+        rate2 += x->angle_delta_cost[mbmi->uv_mode - V_PRED]
+                                    [mbmi->angle_delta[1] + MAX_ANGLE_DELTA];
+#else
         rate2 += write_uniform_cost(2 * MAX_ANGLE_DELTA + 1,
                                     MAX_ANGLE_DELTA + mbmi->angle_delta[1]);
+#endif  // CONFIG_EXT_INTRA_MOD
       }
 #endif  // CONFIG_EXT_INTRA
 #if CONFIG_FILTER_INTRA
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake
index 071a828..24513b3 100644
--- a/build/cmake/aom_config_defaults.cmake
+++ b/build/cmake/aom_config_defaults.cmake
@@ -136,6 +136,7 @@
 set(CONFIG_EXT_COMP_REFS 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_EXT_DELTA_Q 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_EXT_INTRA 1 CACHE NUMBER "AV1 experiment flag.")
+set(CONFIG_EXT_INTRA_MOD 0 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_EXT_PARTITION 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_EXT_PARTITION_TYPES 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_EXT_PARTITION_TYPES_AB 0 CACHE NUMBER "AV1 experiment flag.")
diff --git a/configure b/configure
index 5cd2fc1..fd13fe0 100755
--- a/configure
+++ b/configure
@@ -261,6 +261,7 @@
     ext_intra
     filter_intra
     intra_edge
+    ext_intra_mod
     intrabc
     interintra
     wedge
@@ -554,6 +555,7 @@
     enabled ctx1d && soft_enable lv_map
     enabled ctx1d && soft_enable ext_tx
     enabled compound_round && soft_enable convolve_round
+    enabled ext_intra_mod && enable_feature intra_edge
     enabled intra_edge && enable_feature ext_intra
     enabled ncobmc_adapt_weight && enable_feature motion_var
     enabled bgsprite && enable_feature global_motion
diff --git a/tools/aom_entropy_optimizer.c b/tools/aom_entropy_optimizer.c
index 4f156b4..750b71c 100644
--- a/tools/aom_entropy_optimizer.c
+++ b/tools/aom_entropy_optimizer.c
@@ -338,6 +338,13 @@
       "const aom_cdf_prob\n"
       "default_kf_y_mode_cdf[INTRA_MODES][INTRA_MODES][CDF_SIZE(INTRA_MODES)]");
 
+  cts_each_dim[0] = DIRECTIONAL_MODES;
+  cts_each_dim[1] = 2 * MAX_ANGLE_DELTA + 1;
+  optimize_cdf_table(&fc.angle_delta[0][0], probsfile, 2, cts_each_dim,
+                     "const aom_cdf_prob\n"
+                     "default_angle_delta_cdf"
+                     "[DIRECTIONAL_MODES][CDF_SIZE(2 * MAX_ANGLE_DELTA + 1)]");
+
   /* Intra mode (non-keyframe luma) */
   cts_each_dim[0] = BLOCK_SIZE_GROUPS;
   cts_each_dim[1] = INTRA_MODES;