static void recover_dclp_strip(jxr_image_t image, int tx, int ty, int my)
{
    int mx;
    int use_my = my + image->tile_row_position[ty];
    int use_mx = image->tile_column_position[tx];
    int ptr = use_my*EXTENDED_WIDTH_BLOCKS(image) + use_mx;

    int format_scale = 15;
    if (image->use_clr_fmt == 2 /* YUV422 */) {
        format_scale = 7;
    } else if (image->use_clr_fmt == 1 /* YUV420 */) {
        format_scale = 3;
    }

    int ch;
    for (ch = 0 ; ch < image->num_channels ; ch += 1) {
        struct macroblock_s*mb = image->mb_row_buffer[ch] + ptr;
        int count = ch==0? 15 : format_scale;

        for (mx = 0 ; mx < (int) image->tile_column_width[tx] ; mx += 1) {
            MACROBLK_CUR_DC(image,ch,tx,mx) = mb[mx].data[0];
            DEBUG(" recover_dclp_strip: tx=%d, ty=%d, mx=%d, my=%d, ch=%d, DC=0x%0x8, LP=\n",
                tx, ty, mx, my, ch, mb[mx].data[0]);
            int idx;
            for (idx = 0 ; idx < count ; idx += 1) {
                MACROBLK_CUR_LP(image,ch,tx,mx,idx) = mb[mx].data[idx+1];
                DEBUG(" 0x%x", mb[mx].data[idx+1]);
            }
            DEBUG("\n");
            MACROBLK_CUR_LP_QUANT(image,ch,tx,mx) = mb[mx].lp_quant;
        }
    }
}
static void recover_dclphp_strip(jxr_image_t image, int tx, int ty, int my)
{
    int mx;
    int use_my = my + image->tile_row_position[ty];
    int use_mx = image->tile_column_position[tx];
    int ptr = use_my*EXTENDED_WIDTH_BLOCKS(image) + use_mx;

    int format_scale = 16;
    if (image->use_clr_fmt == 2 /* YUV422 */) {
        format_scale = 8;
    } else if (image->use_clr_fmt == 1 /* YUV420 */) {
        format_scale = 4;
    }

    int ch;
    for (ch = 0 ; ch < image->num_channels ; ch += 1) {
        struct macroblock_s*mb = image->mb_row_buffer[ch] + ptr;
        int count = ch==0? 16 : format_scale;

        if (ch == 0) {
            /* Recover also the hp_model_bits, which are
            stored only in the channel-0 blocks. */
            for (mx = 0 ; mx < (int) image->tile_column_width[tx] ; mx += 1) {
                MACROBLK_CUR(image,0,tx,mx).hp_model_bits[0] = mb[mx].hp_model_bits[0];
                MACROBLK_CUR(image,0,tx,mx).hp_model_bits[1] = mb[mx].hp_model_bits[1];
                MACROBLK_CUR(image,0,tx,mx).mbhp_pred_mode = mb[mx].mbhp_pred_mode;
            }
        }
        for (mx = 0 ; mx < (int) image->tile_column_width[tx] ; mx += 1) {
            MACROBLK_CUR_DC(image,ch,tx,mx) = mb[mx].data[0];
            DEBUG(" recover_dclphp_strip: tx=%d, ty=%d, mx=%d, my=%d, ch=%d, DC=0x%0x8, LP=\n",
                tx, ty, mx, my, ch, mb[mx].data[0]);
            int blk;
            for (blk = 1 ; blk < count ; blk += 1) {
                MACROBLK_CUR_LP(image,ch,tx,mx,blk-1) = mb[mx].data[blk];
                DEBUG(" 0x%x", mb[mx].data[blk]);
            }

            for (blk = 0 ; blk < count ; blk += 1) {
                int idx;
                for (idx = 0 ; idx < 15 ; idx += 1) {
                    int data_ptr = count+15*blk+idx;
                    MACROBLK_CUR_HP(image,ch,tx,mx,blk,idx) = mb[mx].data[data_ptr];
                }
            }
            DEBUG("\n");
            MACROBLK_CUR_LP_QUANT(image,ch,tx,mx) = mb[mx].lp_quant;
            MACROBLK_CUR_HP_QUANT(image,ch,tx,mx) = mb[mx].hp_quant;
        }
    }
}
Beispiel #3
0
/*
* Process a single spatial time. The tx/ty is the coordintes of the
* tile in units of tiles. tx=0 for the first time, tx=1 for the
* second, and so forth.
*/
int _jxr_r_TILE_SPATIAL(jxr_image_t image, struct rbitstream*str,
                        unsigned tx, unsigned ty)
{
    int rc = 0;
    DEBUG("START TILE_SPATIAL at tile=[%u %u] bitpos=%zu\n", tx, ty, _jxr_rbitstream_bitpos(str));

    /* TILE_STARTCODE == 1 */
    unsigned char s0, s1, s2, s3;
    s0 = _jxr_rbitstream_uint8(str); /* 0x00 */
    s1 = _jxr_rbitstream_uint8(str); /* 0x00 */
    s2 = _jxr_rbitstream_uint8(str); /* 0x01 */
    s3 = _jxr_rbitstream_uint8(str); /* reserved */
    DEBUG(" TILE_STARTCODE == %02x %02x %02x (reserved: %02x)\n", s0, s1, s2, s3);

    image->trim_flexbits = 0;
    if (TRIM_FLEXBITS_FLAG(image)) {
        image->trim_flexbits =_jxr_rbitstream_uint4(str);
        DEBUG(" TRIM_FLEXBITS = %u\n", image->trim_flexbits);
    }

    /* Read the tile header (which includes sub-headers for
    all the major passes). */

    _jxr_r_TILE_HEADER_DC(image, str, 0, tx, ty);
    if (image->bands_present != 3 /* DCONLY */) {
        _jxr_r_TILE_HEADER_LOWPASS(image, str, 0, tx, ty);

        if (image->bands_present != 2 /* NO_HIGHPASS */) {
            _jxr_r_TILE_HEADER_HIGHPASS(image, str, 0, tx, ty);
        }
    }

    /* If the alpha channel is present, then run another set of
    headers for the alpha channel. */
    if (ALPHACHANNEL_FLAG(image)) {
        _jxr_r_TILE_HEADER_DC(image->alpha, str, 1, tx, ty);
        if (image->bands_present != 3 /* DCONLY */) {
            _jxr_r_TILE_HEADER_LOWPASS(image->alpha, str, 1, tx, ty);

            if (image->bands_present != 2 /* NO_HIGHPASS */) {
                _jxr_r_TILE_HEADER_HIGHPASS(image->alpha, str, 1, tx, ty);
            }
        }
    }


    /* Now form and write out all the compressed data for the
    tile. This involves scanning the macroblocks, and the
    blocks within the macroblocks, generating bits as we go. */

    unsigned mb_height = EXTENDED_HEIGHT_BLOCKS(image);
    unsigned mb_width = EXTENDED_WIDTH_BLOCKS(image);

    if (TILING_FLAG(image)) {
        mb_height = image->tile_row_height[ty];
        mb_width = image->tile_column_width[tx];
    }

    unsigned mx, my, plane_idx;
    for (my = 0 ; my < mb_height ; my += 1) {
        if (ALPHACHANNEL_FLAG(image))
            _jxr_rflush_mb_strip(image->alpha, tx, ty, my);
        _jxr_rflush_mb_strip(image, tx, ty, my);

        for (mx = 0 ; mx < mb_width ; mx += 1) {
        for(plane_idx = 0U; plane_idx < (ALPHACHANNEL_FLAG(image) ? 2U : 1U); plane_idx ++){
            int ch;

            /* There is one LP_QP_INDEX per macroblock (if any)
            and that value applies to all the channels.
            Same for HP_QP_INDEX. There is no DC_QP_INDEX
            because DC QP values are per-tile, not per MB. */
            int qp_index_lp = 0;
            int qp_index_hp = 0;
            jxr_image_t plane = (plane_idx == 0 ? image : image->alpha);

            if (plane->bands_present!=3) {
                if (plane->num_lp_qps>1 && !plane->lp_use_dc_qp) {
                    qp_index_lp = _jxr_DECODE_QP_INDEX(str, plane->num_lp_qps);
                    DEBUG(" DECODE_QP_INDEX(%d) --> %u (LP)\n", plane->num_lp_qps, qp_index_lp);
                }
                qp_index_hp = 0;
                if (plane->bands_present!=2 && plane->num_hp_qps>1) {
                    if (!plane->hp_use_lp_qp) {
                        qp_index_hp = _jxr_DECODE_QP_INDEX(str, plane->num_hp_qps);
                        DEBUG(" DECODE_QP_INDEX(%d) --> %u (HP)\n", plane->num_hp_qps, qp_index_hp);
                    }
                    else {
                        qp_index_hp = qp_index_lp;
                    }
                }
            }
            for (ch = 0 ; ch < plane->num_channels ; ch += 1) {
                /* Save the LP Quant *INDEX* here. Prediction needs it. */
                MACROBLK_CUR_LP_QUANT(plane,ch,tx,mx) = qp_index_lp;
                DEBUG(" LP_QUANT INDEX for tx=%u ty=%u ch=%u MBx=%d is %d\n", tx, ty, ch, mx,
                    MACROBLK_CUR_LP_QUANT(plane,ch,tx,mx));
                MACROBLK_CUR_HP_QUANT(plane,ch,tx,mx) = plane->hp_quant_ch[ch][qp_index_hp];
                DEBUG(" HP_QUANT VALUE for tx=%u ty=%u ch=%u MBx=%d is %d\n", tx, ty, ch, mx,
                    MACROBLK_CUR_HP_QUANT(plane,ch,tx,mx));
            }

            _jxr_r_MB_DC(plane, str, plane_idx, tx, ty, mx, my);
            if (plane->bands_present != 3 /* DCONLY */) {
                _jxr_r_MB_LP(plane, str, plane_idx, tx, ty, mx, my);
                _jxr_complete_cur_dclp(plane, tx, mx, my);
                if (plane->bands_present != 2 /* NOHIGHPASS */) {
                    rc = _jxr_r_MB_CBP(plane, str, plane_idx, tx, ty, mx, my);
                    if (rc < 0) {
                        DEBUG("r_MB_CBP returned ERROR rc=%d\n", rc);
                        return rc;
                    }
                    rc = _jxr_r_MB_HP(plane, str, plane_idx, tx, ty, mx, my);
                    if (rc < 0) {
                        DEBUG("r_MB_HP returned ERROR rc=%d\n", rc);
                        return rc;
                    }
                }
            } else {
                _jxr_complete_cur_dclp(plane, tx, mx, my);
            }
        }
    }
    }

    /* Flush the remaining strips to output. */
    if (tx+1 == image->tile_columns && ty+1 == image->tile_rows) {
        DEBUG(" Cleanup flush after last tile (tx=%d, ty=%d)\n", tx, ty);
        if (ALPHACHANNEL_FLAG(image))
            _jxr_rflush_mb_strip(image->alpha, tx, ty, mb_height);
        _jxr_rflush_mb_strip(image, tx, ty, mb_height);

        if (ALPHACHANNEL_FLAG(image))
            _jxr_rflush_mb_strip(image->alpha, tx, ty, mb_height+1);
        _jxr_rflush_mb_strip(image, tx, ty, mb_height+1);

        if (ALPHACHANNEL_FLAG(image))
            _jxr_rflush_mb_strip(image->alpha, tx, ty, mb_height+2);
        _jxr_rflush_mb_strip(image, tx, ty, mb_height+2);

        if (ALPHACHANNEL_FLAG(image))
            _jxr_rflush_mb_strip(image->alpha, tx, ty, mb_height+3);
        _jxr_rflush_mb_strip(image, tx, ty, mb_height+3);
    }
    _jxr_rbitstream_syncbyte(str);
    DEBUG("END TILE_SPATIAL\n");
    return 0;
}
int _jxr_r_TILE_HP(jxr_image_t image, struct rbitstream*str,
                   unsigned tx, unsigned ty)
{
    DEBUG("START TILE_HIGHPASS at tile=[%u %u] bitpos=%zu\n", tx, ty, _jxr_rbitstream_bitpos(str));

    /* TILE_STARTCODE == 1 */
    unsigned char s0, s1, s2, s3;
    s0 = _jxr_rbitstream_uint8(str); /* 0x00 */
    s1 = _jxr_rbitstream_uint8(str); /* 0x00 */
    s2 = _jxr_rbitstream_uint8(str); /* 0x01 */
    s3 = _jxr_rbitstream_uint8(str); /* reserved */
    DEBUG(" TILE_STARTCODE == %02x %02x %02x (reserved: %02x)\n", s0, s1, s2, s3);
    if (s0 != 0x00 || s1 != 0x00 || s2 != 0x01) {
        DEBUG(" TILE_HIGHPASS ERROR: Invalid marker.\n");
        return JXR_EC_ERROR;
    }

    _jxr_r_TILE_HEADER_HIGHPASS(image, str, 0 /* alpha */, tx, ty);
    if (ALPHACHANNEL_FLAG(image))
        _jxr_r_TILE_HEADER_HIGHPASS(image->alpha, str, 1, tx, ty);

    /* Now form and write out all the compressed data for the
    tile. This involves scanning the macroblocks, and the
    blocks within the macroblocks, generating bits as we go. */

    unsigned mb_height = EXTENDED_HEIGHT_BLOCKS(image);
    unsigned mb_width = EXTENDED_WIDTH_BLOCKS(image);

    if (TILING_FLAG(image)) {
        mb_height = image->tile_row_height[ty];
        mb_width = image->tile_column_width[tx];
    }

    unsigned mx, my;
    unsigned plane_idx, num_planes = ((ALPHACHANNEL_FLAG(image)) ? 2 : 1);
    for (my = 0 ; my < mb_height ; my += 1) {
        _jxr_r_rotate_mb_strip(image);

        if (ALPHACHANNEL_FLAG(image)) {
            image->alpha->cur_my = my;
            recover_dclp_strip(image->alpha, tx, ty, my);
        }
        image->cur_my = my;
        recover_dclp_strip(image, tx, ty, my);

        for (mx = 0 ; mx < mb_width ; mx += 1) 
        for (plane_idx = 0; plane_idx < num_planes; plane_idx ++) {
            /* The qp_index_hp table goes only into channel 0 */
            int qp_index_hp = 0;
            jxr_image_t plane = (plane_idx == 0 ? image : image->alpha);
            if (plane->num_hp_qps>1) {
                if (!plane->hp_use_lp_qp)
                    qp_index_hp = _jxr_DECODE_QP_INDEX(str, plane->num_hp_qps);
                else
                    qp_index_hp = MACROBLK_CUR_LP_QUANT(plane,0,tx,mx);
            }
            DEBUG(" HP_QP_INDEX for MBx=%d is %d\n", mx, qp_index_hp);
            int ch;
            for (ch = 0 ; ch < plane->num_channels ; ch += 1) {
                MACROBLK_CUR_HP_QUANT(plane,ch,tx,mx) = plane->hp_quant_ch[ch][qp_index_hp];
                DEBUG(" HP_QUANT for MBx=%d ch=%d is %d\n", mx, ch, MACROBLK_CUR_HP_QUANT(plane,ch,tx,mx));
            }

            int rc = _jxr_r_MB_CBP(plane, str, 0, tx, ty, mx, my);
            if (rc < 0) {
                DEBUG("r_MB_CBP returned ERROR rc=%d\n", rc);
                return rc;
            }
            rc = _jxr_r_MB_HP(plane, str, 0, tx, ty, mx, my);
            if (rc < 0) {
                DEBUG("r_MB_HP returned ERROR rc=%d\n", rc);
                return rc;
            }
        }
        if (ALPHACHANNEL_FLAG(image))
            backup_hp_strip(image->alpha, tx, ty, my);
        backup_hp_strip(image, tx, ty, my);
    }

    _jxr_rbitstream_syncbyte(str);
    DEBUG("END TILE_HIGHPASS\n");
    return 0;
}
/*
** Added by thor April 2nd 2010: Process one stripe at a time.
*/
int _jxr_r_TILE_SPATIAL_stripe(jxr_image_t image, struct rbitstream*str,
			       unsigned tx, unsigned ty)
{
  int rc = 0;

  if (image->spatial_buffered_flag == 0) {
    /* Header is not yet parsed off. Do now.
     */
    DEBUG("START TILE_SPATIAL at tile=[%u %u] bitpos=%zu\n", tx, ty, _jxr_rbitstream_bitpos(str));
    
    if(INDEXTABLE_PRESENT_FLAG(image)) {
      _jxr_rbitstream_seek(str, image->tile_index_table[image->tile_columns * ty + tx]);
    }
    /* TILE_STARTCODE == 1 */
    unsigned char s0, s1, s2, s3;
    s0 = _jxr_rbitstream_uint8(str); /* 0x00 */
    s1 = _jxr_rbitstream_uint8(str); /* 0x00 */
    s2 = _jxr_rbitstream_uint8(str); /* 0x01 */
    s3 = _jxr_rbitstream_uint8(str); /* reserved */
    DEBUG(" TILE_STARTCODE == %02x %02x %02x (reserved: %02x)\n", s0, s1, s2, s3);
    if (s0 != 0x00 || s1 != 0x00 || s2 != 0x01) {
      DEBUG(" TILE_LOWPASS ERROR: Invalid marker.\n");
      return JXR_EC_ERROR;
      /* FIX THOR: Invalid TILE_STARTCODE detected */
    }
    
    image->trim_flexbits = 0;
    if (TRIM_FLEXBITS_FLAG(image)) {
      image->trim_flexbits =_jxr_rbitstream_uint4(str);
      DEBUG(" TRIM_FLEXBITS = %u\n", image->trim_flexbits);
    }
    
    /* Read the tile header (which includes sub-headers for
       all the major passes). */
    
    _jxr_r_TILE_HEADER_DC(image, str, 0, tx, ty);
    if (image->bands_present != 3 /* DCONLY */) {
      _jxr_r_TILE_HEADER_LOWPASS(image, str, 0, tx, ty);
      
      if (image->bands_present != 2 /* NO_HIGHPASS */) {
	_jxr_r_TILE_HEADER_HIGHPASS(image, str, 0, tx, ty);
      }
    }

    /* If the alpha channel is present, then run another set of
    headers for the alpha channel. */
    if (ALPHACHANNEL_FLAG(image)) {
      _jxr_r_TILE_HEADER_DC(image->alpha, str, 1, tx, ty);
      if (image->bands_present != 3 /* DCONLY */) {
	_jxr_r_TILE_HEADER_LOWPASS(image->alpha, str, 1, tx, ty);
	
	if (image->bands_present != 2 /* NO_HIGHPASS */) {
	  _jxr_r_TILE_HEADER_HIGHPASS(image->alpha, str, 1, tx, ty);
	}
      }
    }


    /* Now form and write out all the compressed data for the
    tile. This involves scanning the macroblocks, and the
    blocks within the macroblocks, generating bits as we go. */
    {
      unsigned mb_height = EXTENDED_HEIGHT_BLOCKS(image);
      unsigned mb_width = EXTENDED_WIDTH_BLOCKS(image);
      
      if (TILING_FLAG(image)) {
        mb_height = image->tile_row_height[ty];
        mb_width = image->tile_column_width[tx];
      }
      
      image->spatial_mb_height = mb_height;
      image->spatial_mb_width  = mb_width;
    }
    /*
    ** Done with the header. Initialize for the loop below.
    */
    image->spatial_buffered_flag = 1;
    image->stripe_my             = 0;
    image->cleanup_state         = 0;
    image->output_sent           = 0;
    // runs into the following.
  }

  switch(image->cleanup_state) {
  case 0:
    do {
      unsigned mx, plane_idx, my = image->stripe_my;
      unsigned mb_width = image->spatial_mb_width;
      /*
	for (my = 0 ; my < mb_height ; my += 1) {
      */
      if (ALPHACHANNEL_FLAG(image))
	_jxr_rflush_mb_strip(image->alpha, tx, ty, my);
      _jxr_rflush_mb_strip(image, tx, ty, my);
      
      for (mx = 0 ; mx < mb_width ; mx += 1) {
	for(plane_idx = 0U; plane_idx < (ALPHACHANNEL_FLAG(image) ? 2U : 1U); plane_idx ++){
	  int ch;
	  
	  /* There is one LP_QP_INDEX per macroblock (if any)
	     and that value applies to all the channels.
	     Same for HP_QP_INDEX. There is no DC_QP_INDEX
	     because DC QP values are per-tile, not per MB. */
	  int qp_index_lp = 0;
	  int qp_index_hp = 0;
	  jxr_image_t plane = (plane_idx == 0 ? image : image->alpha);
	  
	  if (plane->bands_present!=3) {
	    if (plane->num_lp_qps>1 && !plane->lp_use_dc_qp) {
	      qp_index_lp = _jxr_DECODE_QP_INDEX(str, plane->num_lp_qps);
	      DEBUG(" DECODE_QP_INDEX(%d) --> %u (LP)\n", plane->num_lp_qps, qp_index_lp);
	    }
	    qp_index_hp = 0;
	    if (plane->bands_present!=2 && plane->num_hp_qps>1) {
	      if (!plane->hp_use_lp_qp) {
		qp_index_hp = _jxr_DECODE_QP_INDEX(str, plane->num_hp_qps);
		DEBUG(" DECODE_QP_INDEX(%d) --> %u (HP)\n", plane->num_hp_qps, qp_index_hp);
	      }
	      else {
		qp_index_hp = qp_index_lp;
	      }
	    }
	  }
	  for (ch = 0 ; ch < plane->num_channels ; ch += 1) {
	    /* Save the LP Quant *INDEX* here. Prediction needs it. */
	    MACROBLK_CUR_LP_QUANT(plane,ch,tx,mx) = qp_index_lp;
	    DEBUG(" LP_QUANT INDEX for tx=%u ty=%u ch=%u MBx=%d is %d\n", tx, ty, ch, mx,
		  MACROBLK_CUR_LP_QUANT(plane,ch,tx,mx));
	    MACROBLK_CUR_HP_QUANT(plane,ch,tx,mx) = plane->hp_quant_ch[ch][qp_index_hp];
	    DEBUG(" HP_QUANT VALUE for tx=%u ty=%u ch=%u MBx=%d is %d\n", tx, ty, ch, mx,
		  MACROBLK_CUR_HP_QUANT(plane,ch,tx,mx));
	  }
	  
	  _jxr_r_MB_DC(plane, str, plane_idx, tx, ty, mx, my);
	  if (plane->bands_present != 3 /* DCONLY */) {
	    _jxr_r_MB_LP(plane, str, plane_idx, tx, ty, mx, my);
	    _jxr_complete_cur_dclp(plane, tx, mx, my);
	    if (plane->bands_present != 2 /* NOHIGHPASS */) {
	      rc = _jxr_r_MB_CBP(plane, str, plane_idx, tx, ty, mx, my);
	      if (rc < 0) {
		DEBUG("r_MB_CBP returned ERROR rc=%d\n", rc);
		return rc;
	      }
	      rc = _jxr_r_MB_HP(plane, str, plane_idx, tx, ty, mx, my);
	      if (rc < 0) {
		DEBUG("r_MB_HP returned ERROR rc=%d\n", rc);
		return rc;
	      }
	    }
	  } else {
	    _jxr_complete_cur_dclp(plane, tx, mx, my);
	  }
	}
      }
      /*
      ** Advance to the next MB row.
      */
      image->stripe_my++;
      /*
      ** The flush happens only as part of the last tile, so continue iterating
      ** until that tile is hit.
      */
    } while (image->stripe_my < image->spatial_mb_height && image->output_sent == 0);

    /*
    ** End of tile reached? If so, get the sycn marker
    */
    if (image->stripe_my == image->spatial_mb_height)
      _jxr_rbitstream_syncbyte(str);

    /*
    ** If this is an intermediate tile and not the right edge, continue with the next tile.
    */
    if (tx+1 != image->tile_columns) {
      image->spatial_buffered_flag = 0;
      image->stripe_my             = 0;
      return 1;
    }
    /*
    ** Otherwise, a stripe is complete, go to the user.
    */
    if (image->stripe_my < image->spatial_mb_height && image->output_sent) {
      image->output_sent = 0;
      return 0; /* not yet done with this strip, but return to the user. */
    }
    assert(image->stripe_my == image->spatial_mb_height);
    /*
    ** Done with this tile, go to the next. We're done in case this is not the last tile,
    ** otherwise iterate on until we get to the last one.
    */
    if (tx+1 == image->tile_columns && ty+1 == image->tile_rows) {
      // Last tile row. Continue with cleanup.
      image->cleanup_state = 1;
    } else {
      image->spatial_buffered_flag = 0;
      image->stripe_my             = 0;
      return JXR_EC_DONE;
    }
    if (image->output_sent)
      return 0;
    // runs into the following
  case 1:
    /* Flush the remaining strips to output. */
    if (tx+1 == image->tile_columns && ty+1 == image->tile_rows) {
      DEBUG(" Cleanup flush after last tile (tx=%d, ty=%d)\n", tx, ty);
      if (ALPHACHANNEL_FLAG(image))
	_jxr_rflush_mb_strip(image->alpha, tx, ty, image->spatial_mb_height);
      _jxr_rflush_mb_strip(image, tx, ty, image->spatial_mb_height);
      image->cleanup_state++;
      if (image->output_sent) {
	image->output_sent = 0;
	return 0;
      }
    } else {
      return 1;
    }
    // runs into the following
  case 2:
    if (tx+1 == image->tile_columns && ty+1 == image->tile_rows) {
      if (ALPHACHANNEL_FLAG(image))
	_jxr_rflush_mb_strip(image->alpha, tx, ty, image->spatial_mb_height+1);
      _jxr_rflush_mb_strip(image, tx, ty, image->spatial_mb_height+1);
      image->cleanup_state++;
      if (image->output_sent) {
	image->output_sent = 0;
	return 0;
      }
    } else {
      return 1;
    }
    // runs into the following
  case 3:
    if (tx+1 == image->tile_columns && ty+1 == image->tile_rows) {
      if (ALPHACHANNEL_FLAG(image))
	_jxr_rflush_mb_strip(image->alpha, tx, ty, image->spatial_mb_height+2);
      _jxr_rflush_mb_strip(image, tx, ty, image->spatial_mb_height+2);
      image->cleanup_state++;
      if (image->output_sent) {
	image->output_sent = 0;
	return 0;
      }
    } else {
      return 1;
    }
    // runs into the following
  case 4:
    if (tx+1 == image->tile_columns && ty+1 == image->tile_rows) {
      if (ALPHACHANNEL_FLAG(image))
	_jxr_rflush_mb_strip(image->alpha, tx, ty, image->spatial_mb_height+3);
      _jxr_rflush_mb_strip(image, tx, ty, image->spatial_mb_height+3);
      image->cleanup_state++;
       if (image->output_sent) {
	image->output_sent = 0;
	return 0;
      }
    } else {
      return 1;
    }
    // runs into the following
  default:
    image->spatial_buffered_flag = 0;
    image->stripe_my             = 0;
    return JXR_EC_DONE;
  }
}