void edge_filtering_chroma_internal(de265_image* img, bool vertical,
                                    int yStart,int yEnd,
                                    int xStart,int xEnd)
{
  //printf("chroma %d-%d %d-%d\n",xStart,xEnd,yStart,yEnd);

  const seq_parameter_set& sps = img->get_sps();

  const int SubWidthC  = sps.SubWidthC;
  const int SubHeightC = sps.SubHeightC;

  int xIncr = vertical ? 2 : 1;
  int yIncr = vertical ? 1 : 2;

  xIncr *= SubWidthC;
  yIncr *= SubHeightC;

  const int stride = img->get_image_stride(1);

  xEnd = libde265_min(xEnd,img->get_deblk_width());
  yEnd = libde265_min(yEnd,img->get_deblk_height());

  int bitDepth_C = sps.BitDepth_C;

  for (int y=yStart;y<yEnd;y+=yIncr)
    for (int x=xStart;x<xEnd;x+=xIncr) {
      int xDi = x << (3-SubWidthC);
      int yDi = y << (3-SubHeightC);

      //printf("x,y:%d,%d  xDi,yDi:%d,%d\n",x,y,xDi,yDi);

      int bS = img->get_deblk_bS(xDi*SubWidthC,yDi*SubHeightC);

      if (bS>1) {
        // 8.7.2.4.5

        for (int cplane=0;cplane<2;cplane++) {
          int cQpPicOffset = (cplane==0 ?
                              img->get_pps().pic_cb_qp_offset :
                              img->get_pps().pic_cr_qp_offset);

          pixel_t* ptr = img->get_image_plane_at_pos_NEW<pixel_t>(cplane+1, xDi,yDi);

          pixel_t p[2][4];
          pixel_t q[2][4];

          logtrace(LogDeblock,"-%s- %d %d\n",cplane==0 ? "Cb" : "Cr",xDi,yDi);

          for (int i=0;i<2;i++)
            for (int k=0;k<4;k++)
              {
                if (vertical) {
                  q[i][k] = ptr[ i  +k*stride];
                  p[i][k] = ptr[-i-1+k*stride];
                }
                else {
                  q[i][k] = ptr[k + i   *stride];
                  p[i][k] = ptr[k -(i+1)*stride];
                }
              }

#if 0
          for (int k=0;k<4;k++)
            {
              for (int i=0;i<2;i++)
                {
                  printf("%02x ", p[1-i][k]);
                }

              printf("| ");

              for (int i=0;i<2;i++)
                {
                  printf("%02x ", q[i][k]);
                }
              printf("\n");
            }
#endif

          int QP_Q = img->get_QPY(SubWidthC*xDi,SubHeightC*yDi);
          int QP_P = (vertical ?
                      img->get_QPY(SubWidthC*xDi-1,SubHeightC*yDi) :
                      img->get_QPY(SubWidthC*xDi,SubHeightC*yDi-1));
          int qP_i = ((QP_Q+QP_P+1)>>1) + cQpPicOffset;
          int QP_C;
          if (sps.ChromaArrayType == CHROMA_420) {
            QP_C = table8_22(qP_i);
          } else {
            QP_C = libde265_min(qP_i, 51);
          }


          //printf("POC=%d\n",ctx->img->PicOrderCntVal);
          logtrace(LogDeblock,"%d %d: ((%d+%d+1)>>1) + %d = qP_i=%d  (QP_C=%d)\n",
                   SubWidthC*xDi,SubHeightC*yDi, QP_Q,QP_P,cQpPicOffset,qP_i,QP_C);

          int sliceIndexQ00 = img->get_SliceHeaderIndex(SubWidthC*xDi,SubHeightC*yDi);
          int tc_offset   = img->slices[sliceIndexQ00]->slice_tc_offset;

          int Q = Clip3(0,53, QP_C + 2*(bS-1) + tc_offset);

          int tcPrime = table_8_23_tc[Q];
          int tc = tcPrime * (1<<(sps.BitDepth_C - 8));

          logtrace(LogDeblock,"tc_offset=%d Q=%d tc'=%d tc=%d\n",tc_offset,Q,tcPrime,tc);

          if (vertical) {
            bool filterP = true;
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(SubWidthC*xDi-1,SubHeightC*yDi)) filterP=false;
            if (img->get_cu_transquant_bypass(SubWidthC*xDi-1,SubHeightC*yDi)) filterP=false;

            bool filterQ = true;
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(SubWidthC*xDi,SubHeightC*yDi)) filterQ=false;
            if (img->get_cu_transquant_bypass(SubWidthC*xDi,SubHeightC*yDi)) filterQ=false;


            for (int k=0;k<4;k++) {
              int delta = Clip3(-tc,tc, ((((q[0][k]-p[0][k])*4)+p[1][k]-q[1][k]+4)>>3)); // standard says <<2 in eq. (8-356), but the value can also be negative
              logtrace(LogDeblock,"delta=%d\n",delta);
              if (filterP) { ptr[-1+k*stride] = Clip_BitDepth(p[0][k]+delta, bitDepth_C); }
              if (filterQ) { ptr[ 0+k*stride] = Clip_BitDepth(q[0][k]-delta, bitDepth_C); }
            }
          }
          else {
            bool filterP = true;
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(SubWidthC*xDi,SubHeightC*yDi-1)) filterP=false;
            if (img->get_cu_transquant_bypass(SubWidthC*xDi,SubHeightC*yDi-1)) filterP=false;

            bool filterQ = true;
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(SubWidthC*xDi,SubHeightC*yDi)) filterQ=false;
            if (img->get_cu_transquant_bypass(SubWidthC*xDi,SubHeightC*yDi)) filterQ=false;

            for (int k=0;k<4;k++) {
              int delta = Clip3(-tc,tc, ((((q[0][k]-p[0][k])*4)+p[1][k]-q[1][k]+4)>>3)); // standard says <<2, but the value can also be negative
              if (filterP) { ptr[ k-1*stride] = Clip_BitDepth(p[0][k]+delta, bitDepth_C); }
              if (filterQ) { ptr[ k+0*stride] = Clip_BitDepth(q[0][k]-delta, bitDepth_C); }
            }
          }
        }
      }
    }
}