[xiph-commits] r15989 - in branches/theora-thusnelda/lib: . dec dec/x86

tterribe at svn.xiph.org tterribe at svn.xiph.org
Thu May 7 20:02:38 PDT 2009


Author: tterribe
Date: 2009-05-07 20:02:38 -0700 (Thu, 07 May 2009)
New Revision: 15989

Modified:
   branches/theora-thusnelda/lib/dec/decapiwrapper.c
   branches/theora-thusnelda/lib/dec/decode.c
   branches/theora-thusnelda/lib/dec/ocintrin.h
   branches/theora-thusnelda/lib/dec/state.c
   branches/theora-thusnelda/lib/dec/x86/mmxidct.c
   branches/theora-thusnelda/lib/internal.h
Log:
Fix 4:2:2 decoding and telemetry (which now works for 4:2:2 and 4:4:4).
Also includes a few other minor decoder improvements.


Modified: branches/theora-thusnelda/lib/dec/decapiwrapper.c
===================================================================
--- branches/theora-thusnelda/lib/dec/decapiwrapper.c	2009-05-08 02:20:36 UTC (rev 15988)
+++ branches/theora-thusnelda/lib/dec/decapiwrapper.c	2009-05-08 03:02:38 UTC (rev 15989)
@@ -22,10 +22,6 @@
 #include "decint.h"
 #include "theora/theoradec.h"
 
-#ifdef HAVE_CAIRO
-#include <cairo.h>
-#endif
-
 static void th_dec_api_clear(th_api_wrapper *_api){
   if(_api->setup)th_setup_free(_api->setup);
   if(_api->decode)th_decode_free(_api->decode);
@@ -180,329 +176,6 @@
   decode=(th_dec_ctx *)api->decode;
   if(!decode)return OC_FAULT;
   ret=th_decode_ycbcr_out(decode,buf);
-#ifdef HAVE_CAIRO
-  /* If telemetry ioctls are active, we need to draw to the output
-     buffer.  Stuff the plane into cairo. */
-  if(decode->telemetry){
-    /* 4:2:0 */
-    int w = buf[0].width;
-    int h = buf[0].height;
-    int x, y;
-    cairo_surface_t *cs=
-      cairo_image_surface_create(CAIRO_FORMAT_RGB24,w,h);
-
-    /* lazy data buffer init */
-    if(!decode->telemetry_frame_data)
-      decode->telemetry_frame_data = malloc(w*h*3*sizeof(*decode->telemetry_frame_data));
-
-    /* sadly, no YUV support in Cairo; convert into the RGB buffer */
-    unsigned char *data = cairo_image_surface_get_data(cs);
-    unsigned cstride = cairo_image_surface_get_stride(cs);
-    for(y=0;y<h;y+=2){
-      unsigned char *Ya = buf[0].data + y*buf[0].stride;
-      unsigned char *Yb = buf[0].data + (y+1)*buf[0].stride;
-      unsigned char *U  = buf[1].data + (y>>1)*buf[1].stride;
-      unsigned char *V  = buf[2].data + (y>>1)*buf[2].stride;
-      unsigned char *Ca = data + y*cstride; 
-      unsigned char *Cb = data + (y+1)*cstride; 
-      for(x=0;x<w*4;x+=8){
-	Ca[x+2] = OC_CLAMP255((Ya[0]*76309 + V[0]*104597 - 14609351)>>16);
-	Ca[x+1] = OC_CLAMP255((Ya[0]*76309 - U[0]*25674  - V[0]*53279 + 8885109)>>16); 
-	Ca[x+0] = OC_CLAMP255((Ya[0]*76309 + U[0]*132201 - 18142724)>>16);
-
-	Ca[x+6] = OC_CLAMP255((Ya[1]*76309 + V[0]*104597 - 14609351)>>16);
-	Ca[x+5] = OC_CLAMP255((Ya[1]*76309 - U[0]*25674  - V[0]*53279 + 8885109)>>16);
-	Ca[x+4] = OC_CLAMP255((Ya[1]*76309 + U[0]*132201 - 18142724)>>16);
-
-	Cb[x+2] = OC_CLAMP255((Yb[0]*76309 + V[0]*104597 - 14609351)>>16);
-	Cb[x+1] = OC_CLAMP255((Yb[0]*76309 - U[0]*25674  - V[0]*53279 + 8885109)>>16);
-	Cb[x+0] = OC_CLAMP255((Yb[0]*76309 + U[0]*132201 - 18142724)>>16);
-
-	Cb[x+6] = OC_CLAMP255((Yb[1]*76309 + V[0]*104597 - 14609351)>>16);
-	Cb[x+5] = OC_CLAMP255((Yb[1]*76309 - U[0]*25674  - V[0]*53279 + 8885109)>>16);
-	Cb[x+4] = OC_CLAMP255((Yb[1]*76309 + U[0]*132201 - 18142724)>>16);
-
-	Ya+=2;
-	Yb+=2;
-	U++;
-	V++;
-      }
-    }
-
-    {
-      cairo_t *c = cairo_create(cs);
-
-      /* draw coded identifier for each macroblock */
-      /* macroblocks stored in Hilbert order */
-      oc_mb *mb=decode->state.mbs;
-      oc_mb *mb_end=mb+decode->state.nmbs;
-      int row2 = 0;
-      int col2 = 0;
-
-      for(;mb<mb_end;mb++){
-	int bi;
-	float y = h - (row2 + (((col2+1)>>1)&1))*16 -16;
-	float x = (col2>>1) * 16;
-	    
-	cairo_set_line_width(c,1.);
-	if(decode->state.frame_type==OC_INTRA_FRAME){
-	  if(decode->telemetry_mbmode&0x02){
-	    cairo_set_source_rgba(c,1.,0,0,.5);
-	    cairo_rectangle(c,x+2.5,y+2.5,11,11);
-	    cairo_stroke_preserve(c);
-	    cairo_set_source_rgba(c,1.,0,0,.25);
-	    cairo_fill(c);
-	  }
-	}else{
-	  oc_fragment *coded = NULL;
-	  for(bi=0;bi<4;bi++){
-	    int fragi=mb->map[0][bi];
-	    if(fragi>=0 && decode->state.frags[fragi].coded){
-	      coded = &decode->state.frags[fragi];
-	      break;
-	    }
-	  }
-	  if(bi<4){
-	    switch(mb->mode){
-	    case OC_MODE_INTRA:
-	      if(decode->telemetry_mbmode&0x02){
-		cairo_set_source_rgba(c,1.,0,0,.5);
-		cairo_rectangle(c,x+2.5,y+2.5,11,11);
-		cairo_stroke_preserve(c);
-		cairo_set_source_rgba(c,1.,0,0,.25);
-		cairo_fill(c);
-	      }
-	      break;
-	    case OC_MODE_INTER_NOMV:
-	      if(decode->telemetry_mbmode&0x01){
-		cairo_set_source_rgba(c,0,0,1.,.5);
-		cairo_rectangle(c,x+2.5,y+2.5,11,11);
-		cairo_stroke_preserve(c);
-		cairo_set_source_rgba(c,0,0,1.,.25);
-		cairo_fill(c);
-	      }
-	      break;
-	    case OC_MODE_INTER_MV:
-	      if(decode->telemetry_mbmode&0x04){
-		cairo_rectangle(c,x+2.5,y+2.5,11,11);
-		cairo_set_source_rgba(c,0,1.,0,.5);
-		cairo_stroke(c);
-	      }
-	      if(decode->telemetry_mv&0x04){
-		cairo_move_to(c,x+8+coded->mv[0],y+8-coded->mv[1]);
-		cairo_set_source_rgba(c,1.,1.,1.,.9);
-		cairo_set_line_width(c,3.);
-		cairo_line_to(c,x+8+coded->mv[0]*.66,y+8-coded->mv[1]*.66);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,2.);
-		cairo_line_to(c,x+8+coded->mv[0]*.33,y+8-coded->mv[1]*.33);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,1.);
-		cairo_line_to(c,x+8,y+8);
-		cairo_stroke(c);
- 	      }
-	      break;
-	    case OC_MODE_INTER_MV_LAST:
-	      if(decode->telemetry_mbmode&0x08){
-		cairo_rectangle(c,x+2.5,y+2.5,11,11);
-		cairo_set_source_rgba(c,0,1.,0,.5);
-		cairo_move_to(c,x+13.5,y+2.5);
-		cairo_line_to(c,x+2.5,y+8);
-		cairo_line_to(c,x+13.5,y+13.5);
-		cairo_stroke(c);
-	      }
-	      if(decode->telemetry_mv&0x08){
-		cairo_move_to(c,x+8+coded->mv[0],y+8-coded->mv[1]);
-		cairo_set_source_rgba(c,1.,1.,1.,.9);
-		cairo_set_line_width(c,3.);
-		cairo_line_to(c,x+8+coded->mv[0]*.66,y+8-coded->mv[1]*.66);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,2.);
-		cairo_line_to(c,x+8+coded->mv[0]*.33,y+8-coded->mv[1]*.33);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,1.);
-		cairo_line_to(c,x+8,y+8);
-		cairo_stroke(c);
- 	      }
-	      break;
-	    case OC_MODE_INTER_MV_LAST2:
-	      if(decode->telemetry_mbmode&0x10){
-		cairo_rectangle(c,x+2.5,y+2.5,11,11);
-		cairo_set_source_rgba(c,0,1.,0,.5);
-		cairo_move_to(c,x+8,y+2.5);
-		cairo_line_to(c,x+2.5,y+8);
-		cairo_line_to(c,x+8,y+13.5);
-		cairo_move_to(c,x+13.5,y+2.5);
-		cairo_line_to(c,x+8,y+8);
-		cairo_line_to(c,x+13.5,y+13.5);
-		cairo_stroke(c);
-	      }
-	      if(decode->telemetry_mv&0x10){
-		cairo_move_to(c,x+8+coded->mv[0],y+8-coded->mv[1]);
-		cairo_set_source_rgba(c,1.,1.,1.,.9);
-		cairo_set_line_width(c,3.);
-		cairo_line_to(c,x+8+coded->mv[0]*.66,y+8-coded->mv[1]*.66);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,2.);
-		cairo_line_to(c,x+8+coded->mv[0]*.33,y+8-coded->mv[1]*.33);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,1.);
-		cairo_line_to(c,x+8,y+8);
-		cairo_stroke(c);
- 	      }
-	      break;
-	    case OC_MODE_GOLDEN_NOMV:
-	      if(decode->telemetry_mbmode&0x20){
-		cairo_set_source_rgba(c,1.,1.,0,.5);
-		cairo_rectangle(c,x+2.5,y+2.5,11,11);
-		cairo_stroke_preserve(c);
-		cairo_set_source_rgba(c,1.,1.,0,.25);
-		cairo_fill(c);
-	      }
-	      break;
-	    case OC_MODE_GOLDEN_MV:
-	      if(decode->telemetry_mbmode&0x40){
-		cairo_rectangle(c,x+2.5,y+2.5,11,11);
-		cairo_set_source_rgba(c,1.,1.,0,.5);
-		cairo_stroke(c);
-	      }
-	      if(decode->telemetry_mv&0x40){
-		cairo_move_to(c,x+8+coded->mv[0],y+8-coded->mv[1]);
-		cairo_set_source_rgba(c,1.,1.,1.,.9);
-		cairo_set_line_width(c,3.);
-		cairo_line_to(c,x+8+coded->mv[0]*.66,y+8-coded->mv[1]*.66);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,2.);
-		cairo_line_to(c,x+8+coded->mv[0]*.33,y+8-coded->mv[1]*.33);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,1.);
-		cairo_line_to(c,x+8,y+8);
-		cairo_stroke(c);
- 	      }
-	      break;
-	    case OC_MODE_INTER_MV_FOUR:
-	      if(decode->telemetry_mbmode&0x80){
-		cairo_rectangle(c,x+2.5,y+2.5,4,4);
-		cairo_rectangle(c,x+9.5,y+2.5,4,4);
-		cairo_rectangle(c,x+2.5,y+9.5,4,4);
-		cairo_rectangle(c,x+9.5,y+9.5,4,4);
-		cairo_set_source_rgba(c,0,1.,0,.5);
-		cairo_stroke(c);
-	      }
-	      /* 4mv is odd, coded in raster order */
-	      coded=&decode->state.frags[mb->map[0][bi]];
-	      if(coded->coded && decode->telemetry_mv&0x80){
-		cairo_move_to(c,x+4+coded->mv[0],y+12-coded->mv[1]);
-		cairo_set_source_rgba(c,1.,1.,1.,.9);
-		cairo_set_line_width(c,3.);
-		cairo_line_to(c,x+4+coded->mv[0]*.66,y+12-coded->mv[1]*.66);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,2.);
-		cairo_line_to(c,x+4+coded->mv[0]*.33,y+12-coded->mv[1]*.33);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,1.);
-		cairo_line_to(c,x+4,y+12);
-		cairo_stroke(c);
- 	      }
-	      coded=&decode->state.frags[mb->map[1][bi]];
-	      if(coded->coded && decode->telemetry_mv&0x80){
-		cairo_move_to(c,x+12+coded->mv[0],y+12-coded->mv[1]);
-		cairo_set_source_rgba(c,1.,1.,1.,.9);
-		cairo_set_line_width(c,3.);
-		cairo_line_to(c,x+12+coded->mv[0]*.66,y+12-coded->mv[1]*.66);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,2.);
-		cairo_line_to(c,x+12+coded->mv[0]*.33,y+12-coded->mv[1]*.33);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,1.);
-		cairo_line_to(c,x+12,y+12);
-		cairo_stroke(c);
- 	      }
-	      coded=&decode->state.frags[mb->map[2][bi]];
-	      if(coded->coded && decode->telemetry_mv&0x80){
-		cairo_move_to(c,x+4+coded->mv[0],y+4-coded->mv[1]);
-		cairo_set_source_rgba(c,1.,1.,1.,.9);
-		cairo_set_line_width(c,3.);
-		cairo_line_to(c,x+4+coded->mv[0]*.66,y+4-coded->mv[1]*.66);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,2.);
-		cairo_line_to(c,x+4+coded->mv[0]*.33,y+4-coded->mv[1]*.33);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,1.);
-		cairo_line_to(c,x+4,y+4);
-		cairo_stroke(c);
- 	      }
-	      coded=&decode->state.frags[mb->map[1][bi]];
-	      if(coded->coded && decode->telemetry_mv&0x80){
-		cairo_move_to(c,x+12+coded->mv[0],y+4-coded->mv[1]);
-		cairo_set_source_rgba(c,1.,1.,1.,.9);
-		cairo_set_line_width(c,3.);
-		cairo_line_to(c,x+12+coded->mv[0]*.66,y+4-coded->mv[1]*.66);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,2.);
-		cairo_line_to(c,x+12+coded->mv[0]*.33,y+4-coded->mv[1]*.33);
-		cairo_stroke_preserve(c);
-		cairo_set_line_width(c,1.);
-		cairo_line_to(c,x+12,y+4);
-		cairo_stroke(c);
- 	      }
-	      break;
-	    }
-	  }
-	}
-	col2++;
-	if((col2>>1)>=decode->state.nhmbs){
-	  col2=0;
-	  row2+=2;
-	}
-      }
-
-      cairo_destroy(c);
-    }
-
-    /* out of the Cairo plane into the telemetry YUV buffer */
-    buf[0].data = decode->telemetry_frame_data;
-    buf[1].data = decode->telemetry_frame_data+h*buf[0].stride;
-    buf[2].data = decode->telemetry_frame_data+h*buf[0].stride+h/2*buf[1].stride;
-
-    for(y=0;y<h;y+=2){
-      unsigned char *Ya = buf[0].data + y*buf[0].stride;
-      unsigned char *Yb = buf[0].data + (y+1)*buf[0].stride;
-      unsigned char *U  = buf[1].data + (y>>1)*buf[1].stride;
-      unsigned char *V  = buf[2].data + (y>>1)*buf[2].stride;
-      unsigned char *Ca = data + y*cstride; 
-      unsigned char *Cb = data + (y+1)*cstride; 
-      for(x=0;x<w*4;x+=8){
-
-	Ya[0] = ((Ca[2]*16829 + Ca[1]*33039 + Ca[0]*6416)>>16) + 16;
-	Ya[1] = ((Ca[6]*16829 + Ca[5]*33039 + Ca[4]*6416)>>16) + 16;
-	Yb[0] = ((Cb[2]*16829 + Cb[1]*33039 + Cb[0]*6416)>>16) + 16;
-	Yb[1] = ((Cb[6]*16829 + Cb[5]*33039 + Cb[4]*6416)>>16) + 16;
-
-	U[0] = ((-Ca[2]*9714 - Ca[1]*19070 + Ca[0]*28784 +
-		 -Ca[6]*9714 - Ca[5]*19070 + Ca[4]*28784 +
-		 -Cb[2]*9714 - Cb[1]*19070 + Cb[0]*28784 +
-		 -Cb[6]*9714 - Cb[5]*19070 + Cb[4]*28784)>>18) + 128;
-
-	V[0] = ((Ca[2]*28784 - Ca[1]*24103 - Ca[0]*4681 +
-		 Ca[6]*28784 - Ca[5]*24103 - Ca[4]*4681 +
-		 Cb[2]*28784 - Cb[1]*24103 - Cb[0]*4681 +
-		 Cb[6]*28784 - Cb[5]*24103 - Cb[4]*4681)>>18) + 128;
-
-	Ya+=2;
-	Yb+=2;
-	U++;
-	V++;
-	Ca+=8;
-	Cb+=8;
-      }
-    }
-
-
-    /* Finished.  Destroy the surface. */
-    cairo_surface_destroy(cs);
-  }
-#endif
   if(ret>=0){
     _yuv->y_width=buf[0].width;
     _yuv->y_height=buf[0].height;

Modified: branches/theora-thusnelda/lib/dec/decode.c
===================================================================
--- branches/theora-thusnelda/lib/dec/decode.c	2009-05-08 02:20:36 UTC (rev 15988)
+++ branches/theora-thusnelda/lib/dec/decode.c	2009-05-08 03:02:38 UTC (rev 15989)
@@ -23,7 +23,11 @@
 # include <stdio.h>
 # include "png.h"
 #endif
+#ifdef HAVE_CAIRO
+# include <cairo.h>
+#endif
 
+
 /*No post-processing.*/
 #define OC_PP_LEVEL_DISABLED  (0)
 /*Keep track of DC qi for each block only.*/
@@ -350,7 +354,7 @@
   for(sbi=0;sb_flags[sbi].coded_partially;sbi++);
   theorapackB_read1(&_dec->opb,&val);
   flag=(int)val;
-  while(sbi<nsbs){
+  do{
     int full_run;
     run_count=oc_sb_run_unpack(&_dec->opb);
     full_run=run_count>=4129;
@@ -365,6 +369,7 @@
     }
     else flag=!flag;
   }
+  while(sbi<nsbs);
   /*TODO: run_count should be 0 here.
     If it's not, we should issue a warning of some kind.*/
 }
@@ -556,13 +561,13 @@
   oc_mv_comp_unpack_func  mv_comp_unpack;
   oc_fragment            *frags;
   oc_mv                  *frag_mvs;
-  size_t                  nmbs;
-  size_t                  mbi;
   const unsigned char    *map_idxs;
-  long                    val;
   int                     map_nidxs;
   oc_mv                   last_mv[2];
   oc_mv                   cbmvs[4];
+  size_t                  nmbs;
+  size_t                  mbi;
+  long                    val;
   set_chroma_mvs=OC_SET_CHROMA_MVS_TABLE[_dec->state.info.pixel_fmt];
   theorapackB_read1(&_dec->opb,&val);
   mv_comp_unpack=val?oc_clc_mv_comp_unpack:oc_vlc_mv_comp_unpack;
@@ -612,10 +617,8 @@
             else lbmvs[bi][0]=lbmvs[bi][1]=0;
           }
           if(codedi>0){
-            last_mv[1][0]=last_mv[0][0];
-            last_mv[1][1]=last_mv[0][1];
-            last_mv[0][0]=lbmvs[coded[codedi-1]][0];
-            last_mv[0][1]=lbmvs[coded[codedi-1]][1];
+            memcpy(last_mv[1],last_mv[0],sizeof(last_mv[1]));
+            memcpy(last_mv[0],lbmvs[coded[codedi-1]],sizeof(last_mv[0]));
           }
           if(codedi<ncoded){
             (*set_chroma_mvs)(cbmvs,(const oc_mv *)lbmvs);
@@ -629,28 +632,21 @@
           }
         }break;
         case OC_MODE_INTER_MV:{
-          last_mv[1][0]=last_mv[0][0];
-          last_mv[1][1]=last_mv[0][1];
+          memcpy(last_mv[1],last_mv[0],sizeof(last_mv[1]));
           mbmv[0]=last_mv[0][0]=(signed char)(*mv_comp_unpack)(&_dec->opb);
           mbmv[1]=last_mv[0][1]=(signed char)(*mv_comp_unpack)(&_dec->opb);
         }break;
-        case OC_MODE_INTER_MV_LAST:{
-          mbmv[0]=last_mv[0][0];
-          mbmv[1]=last_mv[0][1];
-        }break;
+        case OC_MODE_INTER_MV_LAST:memcpy(mbmv,last_mv[0],sizeof(mbmv));break;
         case OC_MODE_INTER_MV_LAST2:{
-          mbmv[0]=last_mv[1][0];
-          mbmv[1]=last_mv[1][1];
-          last_mv[1][0]=last_mv[0][0];
-          last_mv[1][1]=last_mv[0][1];
-          last_mv[0][0]=mbmv[0];
-          last_mv[0][1]=mbmv[1];
+          memcpy(mbmv,last_mv[1],sizeof(mbmv));
+          memcpy(last_mv[1],last_mv[0],sizeof(last_mv[1]));
+          memcpy(last_mv[0],mbmv,sizeof(last_mv[0]));
         }break;
         case OC_MODE_GOLDEN_MV:{
           mbmv[0]=(signed char)(*mv_comp_unpack)(&_dec->opb);
           mbmv[1]=(signed char)(*mv_comp_unpack)(&_dec->opb);
         }break;
-        default:mbmv[0]=mbmv[1]=0;break;
+        default:memset(mbmv,0,sizeof(mbmv));break;
       }
       /*4MV mode fills in the fragments itself.
         For all other modes we can use this common code.*/
@@ -667,20 +663,23 @@
 }
 
 static void oc_dec_block_qis_unpack(oc_dec_ctx *_dec){
-  oc_fragment *frag;
-  ptrdiff_t   *coded_fragi;
-  ptrdiff_t   *coded_fragi_end;
-  ptrdiff_t    ncoded_fragis;
+  oc_fragment     *frags;
+  const ptrdiff_t *coded_fragis;
+  ptrdiff_t        ncoded_fragis;
+  ptrdiff_t        coded_fragii;
+  ptrdiff_t        fragi;
   ncoded_fragis=_dec->state.ncoded_fragis[0]+
    _dec->state.ncoded_fragis[1]+_dec->state.ncoded_fragis[2];
   if(ncoded_fragis<=0)return;
-  coded_fragi=_dec->state.coded_fragis;
-  coded_fragi_end=coded_fragi+ncoded_fragis;
+  frags=_dec->state.frags;
+  coded_fragis=_dec->state.coded_fragis;
   if(_dec->state.nqis==1){
+    int qi0;
     /*If this frame has only a single qi value, then just set it in all coded
        fragments.*/
-    while(coded_fragi<coded_fragi_end){
-      _dec->state.frags[*coded_fragi++].qi=_dec->state.qis[0];
+    qi0=_dec->state.qis[0];
+    for(coded_fragii=0;coded_fragii<ncoded_fragis;coded_fragii++){
+      frags[coded_fragis[coded_fragii]].qi=qi0;
     }
   }
   else{
@@ -699,16 +698,17 @@
     theorapackB_read1(&_dec->opb,&val);
     flag=(int)val;
     run_count=nqi1=0;
-    while(coded_fragi<coded_fragi_end){
+    coded_fragii=0;
+    while(coded_fragii<ncoded_fragis){
       int full_run;
       run_count=oc_sb_run_unpack(&_dec->opb);
       full_run=run_count>=4129;
       do{
-        _dec->state.frags[*coded_fragi++].qi=flag;
+        frags[coded_fragis[coded_fragii++]].qi=flag;
         nqi1+=flag;
       }
-      while(--run_count>0&&coded_fragi<coded_fragi_end);
-      if(full_run&&coded_fragi<coded_fragi_end){
+      while(--run_count>0&&coded_fragii<ncoded_fragis);
+      if(full_run&&coded_fragii<ncoded_fragis){
         theorapackB_read1(&_dec->opb,&val);
         flag=(int)val;
       }
@@ -720,35 +720,34 @@
        fragment with a non-zero qi, make the second pass.*/
     if(_dec->state.nqis==3&&nqi1>0){
       /*Skip qii==0 fragments.*/
-      for(coded_fragi=_dec->state.coded_fragis;
-       _dec->state.frags[*coded_fragi].qi==0;coded_fragi++);
+      for(coded_fragii=0;
+       frags[coded_fragis[coded_fragii]].qi==0;coded_fragii++);
       theorapackB_read1(&_dec->opb,&val);
       flag=(int)val;
-      while(coded_fragi<coded_fragi_end){
+      do{
         int full_run;
         run_count=oc_sb_run_unpack(&_dec->opb);
         full_run=run_count>=4129;
-        for(;coded_fragi<coded_fragi_end;coded_fragi++){
-          oc_fragment *frag;
-          frag=_dec->state.frags+*coded_fragi;
-          if(frag->qi==0)continue;
+        for(;coded_fragii<ncoded_fragis;coded_fragii++){
+          fragi=coded_fragis[coded_fragii];
+          if(frags[fragi].qi==0)continue;
           if(run_count--<=0)break;
-          frag->qi+=flag;
+          frags[fragi].qi+=flag;
         }
-        if(full_run&&coded_fragi<coded_fragi_end){
+        if(full_run&&coded_fragii<ncoded_fragis){
           theorapackB_read1(&_dec->opb,&val);
           flag=(int)val;
         }
         else flag=!flag;
       }
+      while(coded_fragii<ncoded_fragis);
       /*TODO: run_count should be 0 here.
         If it's not, we should issue a warning of some kind.*/
     }
     /*Finally, translate qii's to qi's.*/
-    for(coded_fragi=_dec->state.coded_fragis;coded_fragi<coded_fragi_end;
-     coded_fragi++){
-      frag=_dec->state.frags+*coded_fragi;
-      frag->qi=_dec->state.qis[frag->qi];
+    for(coded_fragii=0;coded_fragii<ncoded_fragis;coded_fragii++){
+      fragi=coded_fragis[coded_fragii];
+      frags[fragi].qi=_dec->state.qis[frags[fragi].qi];
     }
   }
 }
@@ -854,10 +853,11 @@
   Return: The length of any outstanding EOB run.*/
 static ptrdiff_t oc_dec_dc_coeff_unpack(oc_dec_ctx *_dec,int _huff_idxs[3],
  ptrdiff_t _ntoks_left[3][64]){
+  ptrdiff_t        run_counts[64];
   oc_fragment     *frags;
-  const ptrdiff_t *coded_fragi;
-  const ptrdiff_t *coded_fragi_end;
-  ptrdiff_t        run_counts[64];
+  const ptrdiff_t *coded_fragis;
+  ptrdiff_t        ncoded_fragis;
+  ptrdiff_t        coded_fragii;
   ptrdiff_t        cfi;
   ptrdiff_t        eobi;
   ptrdiff_t        eobs;
@@ -867,16 +867,17 @@
   int              pli;
   int              rli;
   frags=_dec->state.frags;
+  coded_fragis=_dec->state.coded_fragis;
   eobs=0;
   ti=ebi=0;
-  coded_fragi_end=coded_fragi=_dec->state.coded_fragis;
+  ncoded_fragis=coded_fragii=0;
   for(pli=0;pli<3;pli++){
-    coded_fragi_end+=_dec->state.ncoded_fragis[pli];
+    ncoded_fragis+=_dec->state.ncoded_fragis[pli];
     memset(run_counts,0,sizeof(run_counts));
     _dec->eob_runs[pli][0]=eobs;
     /*Continue any previous EOB run, if there was one.*/
-    for(eobi=eobs;eobi-->0&&coded_fragi<coded_fragi_end;){
-      frags[*coded_fragi++].dc=0;
+    for(eobi=eobs;eobi-->0&&coded_fragii<ncoded_fragis;){
+      frags[coded_fragis[coded_fragii++]].dc=0;
     }
     cfi=0;
     while(eobs<_ntoks_left[pli][0]-cfi){
@@ -899,13 +900,15 @@
       skip=oc_dct_token_skip(token,eb);
       if(skip<0){
         eobs=eobi=-skip;
-        while(eobi-->0&&coded_fragi<coded_fragi_end)frags[*coded_fragi++].dc=0;
+        while(eobi-->0&&coded_fragii<ncoded_fragis){
+          frags[coded_fragis[coded_fragii++]].dc=0;
+        }
       }
       else{
         run_counts[skip-1]++;
         cfi++;
         eobs=0;
-        frags[*coded_fragi++].dc=oc_dct_token_dec1val(token,eb);
+        frags[coded_fragis[coded_fragii++]].dc=oc_dct_token_dec1val(token,eb);
       }
     }
     _dec->ti0[pli][0]=ti;
@@ -1211,16 +1214,19 @@
     memset(_dec->dc_qis,_dec->state.qis[0],_dec->state.nfrags);
   }
   else{
-    ptrdiff_t     *coded_fragi;
-    ptrdiff_t     *coded_fragi_end;
-    unsigned char  qi0;
+    unsigned char   *dc_qis;
+    const ptrdiff_t *coded_fragis;
+    ptrdiff_t        ncoded_fragis;
+    ptrdiff_t        coded_fragii;
+    unsigned char    qi0;
     /*Update the DC quantization index of each coded block.*/
+    dc_qis=_dec->dc_qis;
+    coded_fragis=_dec->state.coded_fragis;
+    ncoded_fragis=_dec->state.ncoded_fragis[0]+
+     _dec->state.ncoded_fragis[1]+_dec->state.ncoded_fragis[2];
     qi0=(unsigned char)_dec->state.qis[0];
-    coded_fragi_end=_dec->state.coded_fragis+_dec->state.ncoded_fragis[0]+
-     _dec->state.ncoded_fragis[1]+_dec->state.ncoded_fragis[2];
-    for(coded_fragi=_dec->state.coded_fragis;coded_fragi<coded_fragi_end;
-     coded_fragi++){
-      _dec->dc_qis[*coded_fragi]=qi0;
+    for(coded_fragii=0;coded_fragii<ncoded_fragis;coded_fragii++){
+      dc_qis[coded_fragis[coded_fragii]]=qi0;
     }
   }
   /*pp_level 1: Stop after updating DC quantization indices.*/
@@ -1236,7 +1242,7 @@
   if(_dec->variances==NULL||
    _dec->pp_frame_has_chroma!=(_dec->pp_level>=OC_PP_LEVEL_DEBLOCKC)){
     size_t frame_sz;
-    frame_sz=_dec->state.info.frame_width*_dec->state.info.frame_height;
+    frame_sz=_dec->state.info.frame_width*(size_t)_dec->state.info.frame_height;
     if(_dec->pp_level<OC_PP_LEVEL_DEBLOCKC){
       _dec->variances=(int *)_ogg_realloc(_dec->variances,
        _dec->state.fplanes[0].nfrags*sizeof(_dec->variances[0]));
@@ -1362,9 +1368,10 @@
 static void oc_dec_dc_unpredict_mcu_plane(oc_dec_ctx *_dec,
  oc_dec_pipeline_state *_pipe,int _pli){
   const oc_fragment_plane *fplane;
-  oc_fragment             *frag;
+  oc_fragment             *frags;
   int                     *pred_last;
   ptrdiff_t                ncoded_fragis;
+  ptrdiff_t                fragi;
   int                      fragx;
   int                      fragy;
   int                      fragy0;
@@ -1376,14 +1383,15 @@
   fragy0=_pipe->fragy0[_pli];
   fragy_end=_pipe->fragy_end[_pli];
   nhfrags=fplane->nhfrags;
-  frag=_dec->state.frags+fplane->froffset+fragy0*(ptrdiff_t)nhfrags;
+  pred_last=_pipe->pred_last[_pli];
+  frags=_dec->state.frags;
   ncoded_fragis=0;
-  pred_last=_pipe->pred_last[_pli];
+  fragi=fplane->froffset+fragy0*(ptrdiff_t)nhfrags;
   for(fragy=fragy0;fragy<fragy_end;fragy++){
-    for(fragx=0;fragx<nhfrags;fragx++,frag++){
-      if(!frag->coded)continue;
-      pred_last[OC_FRAME_FOR_MODE[frag->mb_mode]]=frag->dc+=
-       oc_frag_pred_dc(frag,fplane,fragx,fragy,pred_last);
+    for(fragx=0;fragx<nhfrags;fragx++,fragi++){
+      if(!frags[fragi].coded)continue;
+      pred_last[OC_FRAME_FOR_MODE[frags[fragi].mb_mode]]=frags[fragi].dc+=
+       oc_frag_pred_dc(frags+fragi,fplane,fragx,fragy,pred_last);
       ncoded_fragis++;
     }
   }
@@ -1405,26 +1413,27 @@
 static void oc_dec_frags_recon_mcu_plane(oc_dec_ctx *_dec,
  oc_dec_pipeline_state *_pipe,int _pli){
   const oc_fragment *frags;
-  const ptrdiff_t   *coded_fragi;
-  const ptrdiff_t   *coded_fragi_end;
+  const ptrdiff_t   *coded_fragis;
+  ptrdiff_t          ncoded_fragis;
+  ptrdiff_t          coded_fragii;
   ptrdiff_t         *ti;
   ptrdiff_t         *ebi;
   ptrdiff_t         *eob_runs;
   frags=_dec->state.frags;
-  coded_fragi_end=coded_fragi=_pipe->coded_fragis[_pli];
-  coded_fragi_end+=_pipe->ncoded_fragis[_pli];
+  coded_fragis=_pipe->coded_fragis[_pli];
+  ncoded_fragis=_pipe->ncoded_fragis[_pli];
   ti=_pipe->ti[_pli];
   ebi=_pipe->ebi[_pli];
   eob_runs=_pipe->eob_runs[_pli];
-  for(;coded_fragi<coded_fragi_end;coded_fragi++){
-    oc_quant_table *quants;
+  for(coded_fragii=0;coded_fragii<ncoded_fragis;coded_fragii++){
     /*This array is made twice as large as necessary so that an invalid zero
        run cannot cause a buffer overflow.*/
-    ogg_int16_t    dct_coeffs[128];
-    ptrdiff_t      fragi;
-    int            zzi;
-    int            last_zzi;
-    fragi=*coded_fragi;
+    ogg_int16_t     dct_coeffs[128];
+    oc_quant_table *quants;
+    ptrdiff_t       fragi;
+    int             last_zzi;
+    int             zzi;
+    fragi=coded_fragis[coded_fragii];
     /*Decode the AC coefficients.*/
     for(zzi=0;zzi<64;){
       int token;
@@ -1457,7 +1466,7 @@
     oc_state_frag_recon(&_dec->state,fragi,_pli,dct_coeffs,last_zzi,zzi,
      quants[_dec->state.qis[0]][0],quants[frags[fragi].qi]);
   }
-  _pipe->coded_fragis[_pli]=coded_fragi;
+  _pipe->coded_fragis[_pli]+=ncoded_fragis;
   /*Right now the reconstructed MCU has only the coded blocks in it.*/
   /*TODO: We make the decision here to always copy the uncoded blocks into it
      from the reference frame.
@@ -2116,5 +2125,419 @@
 
 int th_decode_ycbcr_out(th_dec_ctx *_dec,th_ycbcr_buffer _ycbcr){
   oc_ycbcr_buffer_flip(_ycbcr,_dec->pp_frame_buf);
+#if defined(HAVE_CAIRO)
+  /*If telemetry ioctls are active, we need to draw to the output buffer.
+    Stuff the plane into cairo.*/
+  if(_dec->telemetry){
+    cairo_surface_t *cs;
+    unsigned char   *data;
+    unsigned char   *y_row;
+    unsigned char   *u_row;
+    unsigned char   *v_row;
+    unsigned char   *rgb_row;
+    int              cstride;
+    int              w;
+    int              h;
+    int              x;
+    int              y;
+    int              hdec;
+    int              vdec;
+    w=_ycbcr[0].width;
+    h=_ycbcr[0].height;
+    hdec=!(_dec->state.info.pixel_fmt&1);
+    vdec=!(_dec->state.info.pixel_fmt&2);
+    /*Lazy data buffer init.
+      We could try to re-use the post-processing buffer, which would save
+       memory, but complicate the allocation logic there.
+      I don't think anyone cares about memory usage when using telemetry; it is
+       not meant for embedded devices.*/
+    if(!_dec->telemetry_frame_data){
+      _dec->telemetry_frame_data=_ogg_malloc(
+       (w*h+2*(w>>hdec)*(h>>vdec))*sizeof(*_dec->telemetry_frame_data));
+    }
+    cs=cairo_image_surface_create(CAIRO_FORMAT_RGB24,w,h);
+    /*Sadly, no YUV support in Cairo (yet); convert into the RGB buffer.*/
+    data=cairo_image_surface_get_data(cs);
+    cstride=cairo_image_surface_get_stride(cs);
+    y_row=_ycbcr[0].data;
+    u_row=_ycbcr[1].data;
+    v_row=_ycbcr[2].data;
+    rgb_row=data;
+    for(y=0;y<h;y++){
+      for(x=0;x<w;x++){
+        int r;
+        int g;
+        int b;
+        r=(1904000*y_row[x]+2609823*v_row[x>>hdec]-363703744)/1635200;
+        g=(3827562*y_row[x]-1287801*u_row[x>>hdec]
+         -2672387*v_row[x>>hdec]+447306710)/3287200;
+        b=(952000*y_row[x]+1649289*u_row[x>>hdec]-225932192)/817600;
+        rgb_row[4*x+0]=OC_CLAMP255(b);
+        rgb_row[4*x+1]=OC_CLAMP255(g);
+        rgb_row[4*x+2]=OC_CLAMP255(r);
+      }
+      y_row+=_ycbcr[0].stride;
+      u_row+=_ycbcr[1].stride&-((y&1)|!vdec);
+      v_row+=_ycbcr[2].stride&-((y&1)|!vdec);
+      rgb_row+=cstride;
+    }
+    /*Draw coded identifier for each macroblock (stored in Hilbert order).*/
+    {
+      cairo_t           *c;
+      const oc_fragment *frags;
+      oc_mv             *frag_mvs;
+      const signed char *mb_modes;
+      oc_mb_map         *mb_maps;
+      size_t             nmbs;
+      size_t             mbi;
+      int                row2;
+      int                col2;
+      c=cairo_create(cs);
+      frags=_dec->state.frags;
+      frag_mvs=_dec->state.frag_mvs;
+      mb_modes=_dec->state.mb_modes;
+      mb_maps=_dec->state.mb_maps;
+      nmbs=_dec->state.nmbs;
+      row2=0;
+      col2=0;
+      for(mbi=0;mbi<nmbs;mbi++){
+        float x;
+        float y;
+        int   bi;
+        y=h-(row2+((col2+1>>1)&1))*16-16;
+        x=(col2>>1)*16;
+        cairo_set_line_width(c,1.);
+        if(_dec->state.frame_type==OC_INTRA_FRAME){
+          if(_dec->telemetry_mbmode&0x02){
+            cairo_set_source_rgba(c,1.,0,0,.5);
+            cairo_rectangle(c,x+2.5,y+2.5,11,11);
+            cairo_stroke_preserve(c);
+            cairo_set_source_rgba(c,1.,0,0,.25);
+            cairo_fill(c);
+          }
+        }
+        else{
+          const signed char *frag_mv;
+          ptrdiff_t          fragi;
+          for(bi=0;bi<4;bi++){
+            fragi=mb_maps[mbi][0][bi];
+            if(fragi>=0&&frags[fragi].coded){
+              frag_mv=frag_mvs[fragi];
+              break;
+            }
+          }
+          if(bi<4){
+            switch(mb_modes[mbi]){
+              case OC_MODE_INTRA:{
+                if(_dec->telemetry_mbmode&0x02){
+                  cairo_set_source_rgba(c,1.,0,0,.5);
+                  cairo_rectangle(c,x+2.5,y+2.5,11,11);
+                  cairo_stroke_preserve(c);
+                  cairo_set_source_rgba(c,1.,0,0,.25);
+                  cairo_fill(c);
+                }
+              }break;
+              case OC_MODE_INTER_NOMV:{
+                if(_dec->telemetry_mbmode&0x01){
+                  cairo_set_source_rgba(c,0,0,1.,.5);
+                  cairo_rectangle(c,x+2.5,y+2.5,11,11);
+                  cairo_stroke_preserve(c);
+                  cairo_set_source_rgba(c,0,0,1.,.25);
+                  cairo_fill(c);
+                }
+              }break;
+              case OC_MODE_INTER_MV:{
+                if(_dec->telemetry_mbmode&0x04){
+                  cairo_rectangle(c,x+2.5,y+2.5,11,11);
+                  cairo_set_source_rgba(c,0,1.,0,.5);
+                  cairo_stroke(c);
+                }
+                if(_dec->telemetry_mv&0x04){
+                  cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]);
+                  cairo_set_source_rgba(c,1.,1.,1.,.9);
+                  cairo_set_line_width(c,3.);
+                  cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,2.);
+                  cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,1.);
+                  cairo_line_to(c,x+8,y+8);
+                  cairo_stroke(c);
+                }
+              }break;
+              case OC_MODE_INTER_MV_LAST:{
+                if(_dec->telemetry_mbmode&0x08){
+                  cairo_rectangle(c,x+2.5,y+2.5,11,11);
+                  cairo_set_source_rgba(c,0,1.,0,.5);
+                  cairo_move_to(c,x+13.5,y+2.5);
+                  cairo_line_to(c,x+2.5,y+8);
+                  cairo_line_to(c,x+13.5,y+13.5);
+                  cairo_stroke(c);
+                }
+                if(_dec->telemetry_mv&0x08){
+                  cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]);
+                  cairo_set_source_rgba(c,1.,1.,1.,.9);
+                  cairo_set_line_width(c,3.);
+                  cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,2.);
+                  cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,1.);
+                  cairo_line_to(c,x+8,y+8);
+                  cairo_stroke(c);
+                }
+              }break;
+              case OC_MODE_INTER_MV_LAST2:{
+                if(_dec->telemetry_mbmode&0x10){
+                  cairo_rectangle(c,x+2.5,y+2.5,11,11);
+                  cairo_set_source_rgba(c,0,1.,0,.5);
+                  cairo_move_to(c,x+8,y+2.5);
+                  cairo_line_to(c,x+2.5,y+8);
+                  cairo_line_to(c,x+8,y+13.5);
+                  cairo_move_to(c,x+13.5,y+2.5);
+                  cairo_line_to(c,x+8,y+8);
+                  cairo_line_to(c,x+13.5,y+13.5);
+                  cairo_stroke(c);
+                }
+                if(_dec->telemetry_mv&0x10){
+                  cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]);
+                  cairo_set_source_rgba(c,1.,1.,1.,.9);
+                  cairo_set_line_width(c,3.);
+                  cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,2.);
+                  cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,1.);
+                  cairo_line_to(c,x+8,y+8);
+                  cairo_stroke(c);
+                }
+              }break;
+              case OC_MODE_GOLDEN_NOMV:{
+                if(_dec->telemetry_mbmode&0x20){
+                  cairo_set_source_rgba(c,1.,1.,0,.5);
+                  cairo_rectangle(c,x+2.5,y+2.5,11,11);
+                  cairo_stroke_preserve(c);
+                  cairo_set_source_rgba(c,1.,1.,0,.25);
+                  cairo_fill(c);
+                }
+              }break;
+              case OC_MODE_GOLDEN_MV:{
+                if(_dec->telemetry_mbmode&0x40){
+                  cairo_rectangle(c,x+2.5,y+2.5,11,11);
+                  cairo_set_source_rgba(c,1.,1.,0,.5);
+                  cairo_stroke(c);
+                }
+                if(_dec->telemetry_mv&0x40){
+                  cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]);
+                  cairo_set_source_rgba(c,1.,1.,1.,.9);
+                  cairo_set_line_width(c,3.);
+                  cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,2.);
+                  cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,1.);
+                  cairo_line_to(c,x+8,y+8);
+                  cairo_stroke(c);
+                }
+              }break;
+              case OC_MODE_INTER_MV_FOUR:{
+                if(_dec->telemetry_mbmode&0x80){
+                  cairo_rectangle(c,x+2.5,y+2.5,4,4);
+                  cairo_rectangle(c,x+9.5,y+2.5,4,4);
+                  cairo_rectangle(c,x+2.5,y+9.5,4,4);
+                  cairo_rectangle(c,x+9.5,y+9.5,4,4);
+                  cairo_set_source_rgba(c,0,1.,0,.5);
+                  cairo_stroke(c);
+                }
+                /*4mv is odd, coded in raster order.*/
+                fragi=mb_maps[mbi][0][0];
+                if(frags[fragi].coded&&_dec->telemetry_mv&0x80){
+                  frag_mv=frag_mvs[fragi];
+                  cairo_move_to(c,x+4+frag_mv[0],y+12-frag_mv[1]);
+                  cairo_set_source_rgba(c,1.,1.,1.,.9);
+                  cairo_set_line_width(c,3.);
+                  cairo_line_to(c,x+4+frag_mv[0]*.66,y+12-frag_mv[1]*.66);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,2.);
+                  cairo_line_to(c,x+4+frag_mv[0]*.33,y+12-frag_mv[1]*.33);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,1.);
+                  cairo_line_to(c,x+4,y+12);
+                  cairo_stroke(c);
+                }
+                fragi=mb_maps[mbi][0][1];
+                if(frags[fragi].coded&&_dec->telemetry_mv&0x80){
+                  frag_mv=frag_mvs[fragi];
+                  cairo_move_to(c,x+12+frag_mv[0],y+12-frag_mv[1]);
+                  cairo_set_source_rgba(c,1.,1.,1.,.9);
+                  cairo_set_line_width(c,3.);
+                  cairo_line_to(c,x+12+frag_mv[0]*.66,y+12-frag_mv[1]*.66);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,2.);
+                  cairo_line_to(c,x+12+frag_mv[0]*.33,y+12-frag_mv[1]*.33);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,1.);
+                  cairo_line_to(c,x+12,y+12);
+                  cairo_stroke(c);
+                }
+                fragi=mb_maps[mbi][0][2];
+                if(frags[fragi].coded&&_dec->telemetry_mv&0x80){
+                  frag_mv=frag_mvs[fragi];
+                  cairo_move_to(c,x+4+frag_mv[0],y+4-frag_mv[1]);
+                  cairo_set_source_rgba(c,1.,1.,1.,.9);
+                  cairo_set_line_width(c,3.);
+                  cairo_line_to(c,x+4+frag_mv[0]*.66,y+4-frag_mv[1]*.66);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,2.);
+                  cairo_line_to(c,x+4+frag_mv[0]*.33,y+4-frag_mv[1]*.33);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,1.);
+                  cairo_line_to(c,x+4,y+4);
+                  cairo_stroke(c);
+                }
+                fragi=mb_maps[mbi][0][3];
+                if(frags[fragi].coded&&_dec->telemetry_mv&0x80){
+                  frag_mv=frag_mvs[fragi];
+                  cairo_move_to(c,x+12+frag_mv[0],y+4-frag_mv[1]);
+                  cairo_set_source_rgba(c,1.,1.,1.,.9);
+                  cairo_set_line_width(c,3.);
+                  cairo_line_to(c,x+12+frag_mv[0]*.66,y+4-frag_mv[1]*.66);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,2.);
+                  cairo_line_to(c,x+12+frag_mv[0]*.33,y+4-frag_mv[1]*.33);
+                  cairo_stroke_preserve(c);
+                  cairo_set_line_width(c,1.);
+                  cairo_line_to(c,x+12,y+4);
+                  cairo_stroke(c);
+                }
+              }break;
+            }
+          }
+        }
+        col2++;
+        if((col2>>1)>=_dec->state.nhmbs){
+          col2=0;
+          row2+=2;
+        }
+      }
+      cairo_destroy(c);
+    }
+    /*Out of the Cairo plane into the telemetry YUV buffer.*/
+    _ycbcr[0].data=_dec->telemetry_frame_data;
+    _ycbcr[0].stride=_ycbcr[0].width;
+    _ycbcr[1].data=_ycbcr[0].data+h*_ycbcr[0].stride;
+    _ycbcr[1].stride=_ycbcr[1].width;
+    _ycbcr[2].data=_ycbcr[1].data+(h>>vdec)*_ycbcr[1].stride;
+    _ycbcr[2].stride=_ycbcr[2].width;
+    y_row=_ycbcr[0].data;
+    u_row=_ycbcr[1].data;
+    v_row=_ycbcr[2].data;
+    rgb_row=data;
+    /*This is one of the few places it's worth handling chroma on a
+       case-by-case basis.*/
+    switch(_dec->state.info.pixel_fmt){
+      case TH_PF_420:{
+        for(y=0;y<h;y+=2){
+          unsigned char *y_row2;
+          unsigned char *rgb_row2;
+          y_row2=y_row+_ycbcr[0].stride;
+          rgb_row2=rgb_row+cstride;
+          for(x=0;x<w;x+=2){
+            int y;
+            int u;
+            int v;
+            y=(65481*rgb_row[4*x+2]+128553*rgb_row[4*x+1]
+             +24966*rgb_row[4*x+0]+4207500)/255000;
+            y_row[x]=OC_CLAMP255(y);
+            y=(65481*rgb_row[4*x+6]+128553*rgb_row[4*x+5]
+             +24966*rgb_row[4*x+4]+4207500)/255000;
+            y_row[x+1]=OC_CLAMP255(y);
+            y=(65481*rgb_row2[4*x+2]+128553*rgb_row2[4*x+1]
+             +24966*rgb_row2[4*x+0]+4207500)/255000;
+            y_row2[x]=OC_CLAMP255(y);
+            y=(65481*rgb_row2[4*x+6]+128553*rgb_row2[4*x+5]
+             +24966*rgb_row2[4*x+4]+4207500)/255000;
+            y_row2[x+1]=OC_CLAMP255(y);
+            u=(-8372*(rgb_row[4*x+2]+rgb_row[4*x+6]
+             +rgb_row2[4*x+2]+rgb_row2[4*x+6])
+             -16436*(rgb_row[4*x+1]+rgb_row[4*x+5]
+             +rgb_row2[4*x+1]+rgb_row2[4*x+5])
+             +24808*(rgb_row[4*x+0]+rgb_row[4*x+4]
+             +rgb_row2[4*x+0]+rgb_row2[4*x+4])+29032005)/225930;
+            v=(39256*(rgb_row[4*x+2]+rgb_row[4*x+6]
+             +rgb_row2[4*x+2]+rgb_row2[4*x+6])
+             -32872*(rgb_row[4*x+1]+rgb_row[4*x+5]
+              +rgb_row2[4*x+1]+rgb_row2[4*x+5])
+             -6384*(rgb_row[4*x+0]+rgb_row[4*x+4]
+              +rgb_row2[4*x+0]+rgb_row2[4*x+4])+45940035)/357510;
+            u_row[x>>1]=OC_CLAMP255(u);
+            v_row[x>>1]=OC_CLAMP255(v);
+          }
+          y_row+=_ycbcr[0].stride<<1;
+          u_row+=_ycbcr[1].stride;
+          v_row+=_ycbcr[2].stride;
+          rgb_row+=cstride<<1;
+        }
+      }break;
+      case TH_PF_422:{
+        for(y=0;y<h;y++){
+          for(x=0;x<w;x+=2){
+            int y;
+            int u;
+            int v;
+            y=(65481*rgb_row[4*x+2]+128553*rgb_row[4*x+1]
+             +24966*rgb_row[4*x+0]+4207500)/255000;
+            y_row[x]=OC_CLAMP255(y);
+            y=(65481*rgb_row[4*x+6]+128553*rgb_row[4*x+5]
+             +24966*rgb_row[4*x+4]+4207500)/255000;
+            y_row[x+1]=OC_CLAMP255(y);
+            u=(-16744*(rgb_row[4*x+2]+rgb_row[4*x+6])
+             -32872*(rgb_row[4*x+1]+rgb_row[4*x+5])
+             +49616*(rgb_row[4*x+0]+rgb_row[4*x+4])+29032005)/225930;
+            v=(78512*(rgb_row[4*x+2]+rgb_row[4*x+6])
+             -65744*(rgb_row[4*x+1]+rgb_row[4*x+5])
+             -12768*(rgb_row[4*x+0]+rgb_row[4*x+4])+45940035)/357510;
+            u_row[x>>1]=OC_CLAMP255(u);
+            v_row[x>>1]=OC_CLAMP255(v);
+          }
+          y_row+=_ycbcr[0].stride;
+          u_row+=_ycbcr[1].stride;
+          v_row+=_ycbcr[2].stride;
+          rgb_row+=cstride;
+        }
+      }break;
+      /*case TH_PF_444:*/
+      default:{
+        for(y=0;y<h;y++){
+          for(x=0;x<w;x++){
+            int y;
+            int u;
+            int v;
+            y=(65481*rgb_row[4*x+2]+128553*rgb_row[4*x+1]
+             +24966*rgb_row[4*x+0]+4207500)/255000;
+            u=(-33488*rgb_row[4*x+2]-65744*rgb_row[4*x+1]
+             +99232*rgb_row[4*x+0]+29032005)/225930;
+            v=(157024*rgb_row[4*x+2]-131488*rgb_row[4*x+1]
+             -25536*rgb_row[4*x+0]+45940035)/357510;
+            y_row[x]=OC_CLAMP255(y);
+            u_row[x]=OC_CLAMP255(u);
+            v_row[x]=OC_CLAMP255(v);
+          }
+          y_row+=_ycbcr[0].stride;
+          u_row+=_ycbcr[1].stride;
+          v_row+=_ycbcr[2].stride;
+          rgb_row+=cstride;
+        }
+      }break;
+    }
+    /*Finished.
+      Destroy the surface.*/
+    cairo_surface_destroy(cs);
+  }
+#endif
   return 0;
 }

Modified: branches/theora-thusnelda/lib/dec/ocintrin.h
===================================================================
--- branches/theora-thusnelda/lib/dec/ocintrin.h	2009-05-08 02:20:36 UTC (rev 15988)
+++ branches/theora-thusnelda/lib/dec/ocintrin.h	2009-05-08 03:02:38 UTC (rev 15989)
@@ -81,9 +81,9 @@
 /*Divides _x by 16, truncating towards 0.*/
 #define OC_DIV16(_x) OC_DIV_POW2(_x,4,0xF)
 /*Right shifts _dividend by _shift, adding _rval, and subtracting one for
-   negative dividends first..
+   negative dividends first.
   When _rval is (1<<_shift-1), this is equivalent to division with rounding
-   ties towards positive infinity.*/
+   ties away from zero.*/
 #define OC_DIV_ROUND_POW2(_dividend,_shift,_rval)\
  ((_dividend)+OC_SIGNMASK(_dividend)+(_rval)>>(_shift))
 /*Divides a _x by 2, rounding towards even numbers.*/

Modified: branches/theora-thusnelda/lib/dec/state.c
===================================================================
--- branches/theora-thusnelda/lib/dec/state.c	2009-05-08 02:20:36 UTC (rev 15988)
+++ branches/theora-thusnelda/lib/dec/state.c	2009-05-08 03:02:38 UTC (rev 15989)
@@ -498,7 +498,7 @@
   info=&_state->info;
   /*Compute the image buffer parameters for each plane.*/
   hdec=!(info->pixel_fmt&1);
-  vdec=!(info->pixel_fmt&1);
+  vdec=!(info->pixel_fmt&2);
   yhstride=info->frame_width+2*OC_UMV_PADDING;
   yheight=info->frame_height+2*OC_UMV_PADDING;
   chstride=yhstride>>hdec;
@@ -630,6 +630,11 @@
    _info->pic_y+_info->pic_height>_info->frame_height||
    _info->pic_x>255||
    _info->frame_height-_info->pic_height-_info->pic_y>255||
+   /*Note: the following <0 comparisons may generate spurious warnings on
+      platforms where enums are unsigned.
+     We could cast them to unsigned and just use the following >= comparison,
+      but there are a number of compilers which will mis-optimize this.
+     It's better to live with the spurious warnings.*/
    _info->colorspace<0||_info->colorspace>=TH_CS_NSPACES||
    _info->pixel_fmt<0||_info->pixel_fmt>=TH_PF_NFORMATS){
     return TH_EINVAL;

Modified: branches/theora-thusnelda/lib/dec/x86/mmxidct.c
===================================================================
--- branches/theora-thusnelda/lib/dec/x86/mmxidct.c	2009-05-08 02:20:36 UTC (rev 15988)
+++ branches/theora-thusnelda/lib/dec/x86/mmxidct.c	2009-05-08 03:02:38 UTC (rev 15989)
@@ -336,7 +336,6 @@
     OC_COLUMN_IDCT
 #undef  OC_I
 #undef  OC_J
-    "emms\n\t"
     :
     :[y]"r"(_y),[c]"r"(OC_IDCT_CONSTS)
   );
@@ -524,7 +523,6 @@
     OC_COLUMN_IDCT_10
 #undef  OC_I
 #undef  OC_J
-    "emms\n\t"
     :
     :[y]"r"(_y),[c]"r"(OC_IDCT_CONSTS)
   );

Modified: branches/theora-thusnelda/lib/internal.h
===================================================================
--- branches/theora-thusnelda/lib/internal.h	2009-05-08 02:20:36 UTC (rev 15988)
+++ branches/theora-thusnelda/lib/internal.h	2009-05-08 03:02:38 UTC (rev 15989)
@@ -23,9 +23,6 @@
 # endif
 # include "theora/codec.h"
 # include "theora/theora.h"
-# include "dec/ocintrin.h"
-# include "dec/huffman.h"
-# include "dec/quant.h"
 
 # if defined(_MSC_VER)
 /*Thank you Microsoft, I know the order of operations.*/
@@ -33,7 +30,15 @@
 /*Disable missing EMMS warnings.*/
 #  pragma warning(disable:4799)
 # endif
+/*You, too, gcc.*/
+# if defined(__GNUC_PREREQ)&&__GNUC_PREREQ(4,2)
+#  pragma GCC diagnostic ignored "-Wparentheses"
+# endif
 
+# include "dec/ocintrin.h"
+# include "dec/huffman.h"
+# include "dec/quant.h"
+
 /*Some assembly constructs require aligned operands.*/
 # if defined(OC_X86_ASM)
 #  if defined(__GNUC__)
@@ -297,87 +302,87 @@
 /*Common state information between the encoder and decoder.*/
 struct oc_theora_state{
   /*The stream information.*/
-  th_info                     info;
+  th_info             info;
   /*Table for shared accelerated functions.*/
-  oc_base_opt_vtable          opt_vtable;
+  oc_base_opt_vtable  opt_vtable;
   /*CPU flags to detect the presence of extended instruction sets.*/
-  ogg_uint32_t                cpu_flags;
+  ogg_uint32_t        cpu_flags;
   /*The fragment plane descriptions.*/
-  oc_fragment_plane           fplanes[3];
+  oc_fragment_plane   fplanes[3];
   /*The list of fragments, indexed in image order.*/
-  oc_fragment                *frags;
+  oc_fragment        *frags;
   /*The the offset into the reference frame buffer to the upper-left pixel of
      each fragment.*/
-  ptrdiff_t                  *frag_buf_offs;
+  ptrdiff_t          *frag_buf_offs;
   /*The motion vector for each fragment.*/
-  oc_mv                      *frag_mvs;
+  oc_mv              *frag_mvs;
   /*The total number of fragments in a single frame.*/
-  ptrdiff_t                   nfrags;
+  ptrdiff_t           nfrags;
   /*The list of super block maps, indexed in image order.*/
-  oc_sb_map                  *sb_maps;
+  oc_sb_map          *sb_maps;
   /*The list of super block flags, indexed in image order.*/
-  oc_sb_flags                *sb_flags;
+  oc_sb_flags        *sb_flags;
   /*The total number of super blocks in a single frame.*/
-  unsigned                    nsbs;
+  unsigned            nsbs;
   /*The number of macro blocks in the X direction.*/
-  unsigned                    nhmbs;
+  unsigned            nhmbs;
   /*The number of macro blocks in the Y direction.*/
-  unsigned                    nvmbs;
+  unsigned            nvmbs;
   /*The total number of macro blocks.*/
-  size_t                      nmbs;
+  size_t              nmbs;
   /*The fragments from each color plane that belong to each macro block.
     Fragments are stored in image order (left to right then top to bottom).
     When chroma components are decimated, the extra fragments have an index of
      -1.*/
-  oc_mb_map                  *mb_maps;
+  oc_mb_map          *mb_maps;
   /*The list of macro block modes.
     A negative number indicates the macro block lies entirely outside the
      coded frame.*/
-  signed char                *mb_modes;
+  signed char        *mb_modes;
   /*The list of coded fragments, in coded order.*/
-  ptrdiff_t                  *coded_fragis;
+  ptrdiff_t          *coded_fragis;
   /*The number of coded fragments in each plane.*/
-  ptrdiff_t                   ncoded_fragis[3];
+  ptrdiff_t           ncoded_fragis[3];
   /*The list of uncoded fragments.
     This just past the end of the list, which is in reverse order, and
      uses the same block of allocated storage as the coded_fragis list.*/
-  ptrdiff_t                  *uncoded_fragis;
+  ptrdiff_t          *uncoded_fragis;
   /*The number of uncoded fragments in each plane.*/
-  ptrdiff_t                   nuncoded_fragis[3];
+  ptrdiff_t           nuncoded_fragis[3];
   /*The list of coded macro blocks in the Y plane, in coded order.*/
-  unsigned                   *coded_mbis;
+  unsigned           *coded_mbis;
   /*The number of coded macro blocks in the Y plane.*/
-  size_t                      ncoded_mbis;
+  size_t              ncoded_mbis;
   /*The number of unique border patterns.*/
-  int                         nborders;
+  int                 nborders;
   /*The unique border patterns for all border fragments.
     The borderi field of fragments which straddle the border indexes this
      list.*/
-  oc_border_info              borders[16];
+  oc_border_info      borders[16];
   /*The index of the buffers being used for each OC_FRAME_* reference frame.*/
-  int                         ref_frame_idx[3];
+  int                 ref_frame_idx[3];
   /*The actual buffers used for the previously decoded frames.*/
-  th_ycbcr_buffer             ref_frame_bufs[3];
+  th_ycbcr_buffer     ref_frame_bufs[3];
   /*The storage for the reference frame buffers.*/
-  unsigned char              *ref_frame_data[3];
+  unsigned char      *ref_frame_data[3];
   /*The frame number of the last keyframe.*/
-  ogg_int64_t                 keyframe_num;
+  ogg_int64_t         keyframe_num;
   /*The frame number of the current frame.*/
-  ogg_int64_t                 curframe_num;
+  ogg_int64_t         curframe_num;
   /*The granpos of the current frame.*/
-  ogg_int64_t                 granpos;
+  ogg_int64_t         granpos;
   /*The type of the current frame.*/
-  int                         frame_type;
+  int                 frame_type;
   /*The quality indices of the current frame.*/
-  unsigned char               qis[3];
+  unsigned char       qis[3];
   /*The number of quality indices used in the current frame.*/
-  unsigned char               nqis;
+  unsigned char       nqis;
   /*The dequantization tables.
     Note that these are stored in zig-zag order.*/
-  oc_quant_table             *dequant_tables[2][3];
-  oc_quant_tables OC_ALIGN16  dequant_table_data[2][3];
+  oc_quant_table     *dequant_tables[2][3];
+  oc_quant_tables     dequant_table_data[2][3]OC_ALIGN16;
   /*Loop filter strength parameters.*/
-  unsigned char               loop_filter_limits[64];
+  unsigned char       loop_filter_limits[64];
 };
 
 



More information about the commits mailing list