void thread_task_sao::work()
{
  state = Running;
  img->thread_run(this);

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

  const int rightCtb = sps.PicWidthInCtbsY-1;
  const int ctbSize  = (1<<sps.Log2CtbSizeY);


  // wait until also the CTB-rows below and above are ready

  img->wait_for_progress(this, rightCtb,ctb_y,  inputProgress);

  if (ctb_y>0) {
    img->wait_for_progress(this, rightCtb,ctb_y-1, inputProgress);
  }

  if (ctb_y+1<sps.PicHeightInCtbsY) {
    img->wait_for_progress(this, rightCtb,ctb_y+1, inputProgress);
  }


  // copy input image to output for this CTB-row

  outputImg->copy_lines_from(inputImg, ctb_y * ctbSize, (ctb_y+1) * ctbSize);


  // process SAO in the CTB-row

  for (int xCtb=0; xCtb<sps.PicWidthInCtbsY; xCtb++)
    {
      const slice_segment_header* shdr = img->get_SliceHeaderCtb(xCtb,ctb_y);
      if (shdr==NULL) {
        break;
      }

      if (shdr->slice_sao_luma_flag) {
        apply_sao(img, xCtb,ctb_y, shdr, 0, ctbSize, ctbSize,
                  inputImg ->get_image_plane(0), inputImg ->get_image_stride(0),
                  outputImg->get_image_plane(0), outputImg->get_image_stride(0));
      }

      if (shdr->slice_sao_chroma_flag) {
        int nSW = ctbSize / sps.SubWidthC;
        int nSH = ctbSize / sps.SubHeightC;

        apply_sao(img, xCtb,ctb_y, shdr, 1, nSW,nSH,
                  inputImg ->get_image_plane(1), inputImg ->get_image_stride(1),
                  outputImg->get_image_plane(1), outputImg->get_image_stride(1));

        apply_sao(img, xCtb,ctb_y, shdr, 2, nSW,nSH,
                  inputImg ->get_image_plane(2), inputImg ->get_image_stride(2),
                  outputImg->get_image_plane(2), outputImg->get_image_stride(2));
      }
    }


  // mark SAO progress

  for (int x=0;x<=rightCtb;x++) {
    const int CtbWidth = sps.PicWidthInCtbsY;
    img->ctb_progress[x+ctb_y*CtbWidth].set_progress(CTB_PROGRESS_SAO);
  }


  state = Finished;
  img->thread_finishes(this);
}
void thread_task_sao::work()
{
  state = Running;
  img->thread_run(this);

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

  const int rightCtb = sps.PicWidthInCtbsY-1;
  const int ctbSize  = (1<<sps.Log2CtbSizeY);


   

  img->wait_for_progress(this, rightCtb,ctb_y,  inputProgress);

  if (ctb_y>0) {
    img->wait_for_progress(this, rightCtb,ctb_y-1, inputProgress);
  }

  if (ctb_y+1<sps.PicHeightInCtbsY) {
    img->wait_for_progress(this, rightCtb,ctb_y+1, inputProgress);
  }


   

  outputImg->copy_lines_from(inputImg, ctb_y * ctbSize, (ctb_y+1) * ctbSize);


   

  for (int xCtb=0; xCtb<sps.PicWidthInCtbsY; xCtb++)
    {
      const slice_segment_header* shdr = img->get_SliceHeaderCtb(xCtb,ctb_y);
      if (shdr==NULL) {
        break;
      }

      if (shdr->slice_sao_luma_flag) {
        apply_sao(img, xCtb,ctb_y, shdr, 0, ctbSize, ctbSize,
                  inputImg ->get_image_plane(0), inputImg ->get_image_stride(0),
                  outputImg->get_image_plane(0), outputImg->get_image_stride(0));
      }

      if (shdr->slice_sao_chroma_flag) {
        int nSW = ctbSize / sps.SubWidthC;
        int nSH = ctbSize / sps.SubHeightC;

        apply_sao(img, xCtb,ctb_y, shdr, 1, nSW,nSH,
                  inputImg ->get_image_plane(1), inputImg ->get_image_stride(1),
                  outputImg->get_image_plane(1), outputImg->get_image_stride(1));

        apply_sao(img, xCtb,ctb_y, shdr, 2, nSW,nSH,
                  inputImg ->get_image_plane(2), inputImg ->get_image_stride(2),
                  outputImg->get_image_plane(2), outputImg->get_image_stride(2));
      }
    }


   

  for (int x=0;x<=rightCtb;x++) {
    const int CtbWidth = sps.PicWidthInCtbsY;
    img->ctb_progress[x+ctb_y*CtbWidth].set_progress(CTB_PROGRESS_SAO);
  }


  state = Finished;
  img->thread_finishes(this);
}