summaryrefslogtreecommitdiff
path: root/dxr3interface_spu_encoder.c
diff options
context:
space:
mode:
authorscop <scop>2005-03-14 18:14:02 +0000
committerscop <scop>2005-03-14 18:14:02 +0000
commit07ac4b6a60139f18f6367832902604a2d4e3b224 (patch)
treee2e24c74b886b4cc31f592f7e3b9d469f688faeb /dxr3interface_spu_encoder.c
parentcc701f30fbffa1a6e52e3f009277a0a91475ffa2 (diff)
downloadvdr-plugin-dxr3-07ac4b6a60139f18f6367832902604a2d4e3b224.tar.gz
vdr-plugin-dxr3-07ac4b6a60139f18f6367832902604a2d4e3b224.tar.bz2
Improve OSD scaler for small resolutions (#1014339, Luca Olivetti).
Diffstat (limited to 'dxr3interface_spu_encoder.c')
-rw-r--r--dxr3interface_spu_encoder.c627
1 files changed, 627 insertions, 0 deletions
diff --git a/dxr3interface_spu_encoder.c b/dxr3interface_spu_encoder.c
new file mode 100644
index 0000000..8e49216
--- /dev/null
+++ b/dxr3interface_spu_encoder.c
@@ -0,0 +1,627 @@
+/*
+ * spuenc.c - encodes an OSD bitmap as subpicture
+ *
+ * Assimilated and adapted by
+ * Stefan Schluenss <dxr3_osd@schluenss.de>
+ * Nov. 2002
+ *
+ * Based on the subpicture encoding routines from MPlayer and
+ * the information given by
+ * Samuel Hocevar
+ * Michel Lespinasse
+ * and http://members.aol.com/mpucoder/DVD/spu.html
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "dxr3interface_spu_encoder.h"
+
+/*
+ToDo:
+ - cSPUEncoder::encode_do_row: FIXME: watch this space for EOL
+*/
+
+
+// ==================================
+// dec.
+cSpuData::~cSpuData()
+{
+ Clear();
+}
+
+// ==================================
+// free buffer and set it to 0
+void cSpuData::Clear()
+{
+ if (data)
+ {
+ free(data);
+ count = malloc_size = 0;
+ }
+}
+
+// ==================================
+// wirte a byte to spu buffer
+void cSpuData::WriteByte(uint8_t byte)
+{
+ if (count >= malloc_size)
+ {
+ data = (u_char*)realloc(data, malloc_size += 2048);
+ }
+
+ data[count++] = byte;
+}
+
+// ==================================
+void cSpuData::WriteNibble(int *higher_nibble, uint8_t nibble)
+{
+}
+
+// ==================================
+void cSpuData::WriteRle(int *higher_nibble, int length, int color)
+{
+}
+
+
+
+
+// ==================================
+cSPUEncoder::cSPUEncoder()
+{
+ // clear osd
+ memset(OSD_Screen, 0x00 ,OSDWIDTH * OSDHEIGHT);
+ memset(OSD_Screen2, 0x00 ,OSDWIDTH * OSDHEIGHT);
+ memset(OSD_Screen3, 0x00 ,OSDWIDTH * OSDHEIGHT);
+
+ // set active area to 0
+ //m_x0 = m_x1 = m_y0 = m_y1 = 0;
+}
+
+// ==================================
+// main function for the osd
+// makes life nicer :)
+int cSPUEncoder::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data)
+{
+ u_char *cp;
+ unsigned char idx = 0;
+ int opacity = 0;
+#if VDRVERSNUM >= 10307
+ const tColor *col;
+#else
+ eDvbColor *col;
+#endif
+
+ switch (cmd)
+ {
+ case OSD_SetWindow:
+ // (x0) set window with number 0<x0<8 as current
+ if (x0 < 8 && x0 > 0)
+ {
+ m_lastwindow = x0;
+ return 0;
+ }
+
+ return -1;
+ break;
+
+ case OSD_Open:
+ // (x0,y0,x1,y1,BitPerPixel[2/4/8](color&0x0F),mix[0..15](color&0xF0))
+ // Opens OSD with this size and bit depth
+ // returns 0 on success, -1 on DRAM allocation error, -2 on "already open"
+ m_windows[m_lastwindow].x0 = x0;
+ m_windows[m_lastwindow].y0 = y0;
+ m_windows[m_lastwindow].x1 = x1;
+ m_windows[m_lastwindow].y1 = y1;
+
+ return 0;
+ break;
+
+ case OSD_SetPalette:
+ // Spu->Cmd(OSD_SetPalette, 0, NumColors - 1, 0, 0, 0, Colors);
+ // (firstcolor{color},lastcolor{x0},data)
+ // Set a number of entries in the palette
+ // sets the entries "firstcolor" through "lastcolor" from the array "data"
+ // data has 4 byte for each color:
+ // R,G,B, and a opacity value: 0->transparent, 1..254->mix, 255->pixel
+
+ #if VDRVERSNUM >= 10307
+ col = (tColor*)data;
+ #else
+ eDvbColor *col;
+ col = (eDvbColor*)data;
+ #endif
+
+ m_windows[m_lastwindow].NumColors = x0;
+
+ for (int x = color, i = 0; x <= x0; x++,i++)
+ {
+ m_palManager.AddColor((int)*col & 0xFFFFFF);
+
+ idx = m_palManager.GetIndex((int)*col & 0xFFFFFF);
+ if (m_palManager.HasChanged())
+ {
+ cDxr3Interface::Instance().SetPalette(m_palManager.GetPalette());
+ }
+
+ opacity = ((*col & 0xFF000000) >> 24) * 0xF / 0xFF;
+ m_windows[m_lastwindow].colors[i] = (opacity << 4) | idx;
+ m_windows[m_lastwindow].opacity[i] = opacity;
+ col++;
+ }
+
+ return 0;
+ break;
+
+ case OSD_SetBlock:
+ // (x0,y0,x1,y1,increment{color},data)
+ // fills pixels x0,y0 through x1,y1 with the content of data[]
+ // inc contains the width of one line in the data block,
+ // inc<=0 uses blockwidth as linewidth
+ // returns 0 on success, -1 on clipping all pixel
+
+ CopyBlockIntoOSD
+ (
+ color,
+ m_windows[m_lastwindow].x0 + x0,
+ m_windows[m_lastwindow].y0 + y0,
+ m_windows[m_lastwindow].x0 + x1,
+ m_windows[m_lastwindow].y0 + y1,
+ (u_char *)data
+ );
+
+ // calculate osd size
+ CalculateActiveOsdArea();
+
+ //cLog::Instance() << "(" << m_x0 << ", " << m_x1 << ") - (" << m_y0 << ", " << m_y1 << ")";
+
+ m_encodeddata.count = 0;
+ EncodePixelbufRle(0,0, OSDWIDTH, OSDHEIGHT-1, OSD_Screen, 0, &m_encodeddata);
+
+ if (cDxr3ConfigData::Instance().GetDebug())
+ {
+ cLog::Instance() << "OSD Datasize: " << m_encodeddata.count << "\n";
+ }
+
+ if (m_encodeddata.count <= DATASIZE)
+ {
+ cDxr3Interface::Instance().WriteSpu((uint8_t*) &m_encodeddata, m_encodeddata.count);
+ return 0;
+ }
+ else
+ {
+ cLog::Instance() << "Waring: SPU data (" << m_encodeddata.count << ") size exceeds limit\n";
+ return -1;
+ }
+ break;
+
+ case OSD_Close:
+ // clear colors from plattemanager
+
+ #if VDRVERSNUM >= 10307
+ if ((col = (tColor*)m_windows[m_lastwindow].colors) != NULL)
+ #else
+ if ((col = (eDvbColor*)m_windows[m_lastwindow].colors) != NULL)
+ #endif
+ {
+ for (int i = 0; i < m_windows[m_lastwindow].NumColors; ++i)
+ {
+ m_palManager.RemoveColor((int)(col[i]) & 0xFFFFFF);
+ }
+ }
+
+ // clear osd
+ for (int i = m_windows[m_lastwindow].y0; i <= m_windows[m_lastwindow].y1; ++i)
+ {
+ cp = &OSD_Screen[i*OSDWIDTH + m_windows[m_lastwindow].x0];
+ if ((cp+m_windows[m_lastwindow].x1-m_windows[m_lastwindow].x0+1) < &OSD_Screen[OSDWIDTH * OSDHEIGHT-1])
+ {
+ for (int xx=0; xx <= (m_windows[m_lastwindow].x1-m_windows[m_lastwindow].x0); xx++)
+ {
+ *(cp+xx) = 0x00;
+ }
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ // encode rle
+ m_encodeddata.count = 0;
+ EncodePixelbufRle(0,0, OSDWIDTH, OSDHEIGHT-1, OSD_Screen, 0, &m_encodeddata);
+
+ // set windows position to 0
+ m_windows[m_lastwindow].x0 = 0;
+ m_windows[m_lastwindow].y0 = 0;
+ m_windows[m_lastwindow].x1 = 0;
+ m_windows[m_lastwindow].y1 = 0;
+
+ if (m_encodeddata.count <= DATASIZE)
+ {
+ cDxr3Interface::Instance().WriteSpu((uint8_t*) &m_encodeddata, m_encodeddata.count);
+ return 0;
+ }
+ else
+ {
+ cLog::Instance() << "Waring: SPU data (" << m_encodeddata.count << ") size exceeds limit\n";
+ return -1;
+ }
+
+ return 0;
+ break;
+
+ case OSD_Clear:
+ // Sets all pixel to color 0
+ // returns 0 on success
+
+ // This should be done in cSPUEncoder::cSPUEncoder
+
+ return 0;
+ break;
+
+ // not needed - at the moment
+ case OSD_Show:
+ case OSD_Hide:
+ case OSD_Fill:
+ case OSD_SetColor:
+ case OSD_SetTrans:
+ case OSD_SetPixel:
+ case OSD_GetPixel:
+ case OSD_SetRow:
+ case OSD_FillRow:
+ case OSD_FillBlock:
+ case OSD_Line:
+ case OSD_Query:
+ case OSD_Test:
+ case OSD_Text:
+ case OSD_MoveWindow:
+ break;
+ };
+
+ return -1;
+}
+
+// ==================================
+// stamps window content into full osd bitmap
+void cSPUEncoder::CopyBlockIntoOSD(int linewidth, int x0, int y0, int x1, int y1, u_char *data)
+{
+ int i;
+ int w;
+ u_char *cp;
+ u_char *sp = data;
+
+
+ // linewidth contains the width of one line in the data block,
+ // linewidth<=0 uses blockwidth as linewidth
+ if (linewidth <= 0)
+ {
+ w = m_windows[m_lastwindow].x1 - m_windows[m_lastwindow].x0;
+ }
+ else
+ {
+ w = linewidth;
+ }
+
+ for (i = y0; i <= y1; ++i)
+ {
+ cp = &OSD_Screen[i*OSDWIDTH + x0];
+ if ((cp+x1-x0+1) < &OSD_Screen[OSDWIDTH * OSDHEIGHT-1])
+ {
+ for (int xx=0; xx <= (x1-x0); xx++)
+ {
+ *(cp+xx) = m_windows[m_lastwindow].colors[*(sp+xx) & 0x0f];
+ }
+ }
+ else
+ {
+ continue;
+ }
+ sp += w;
+ }
+}
+
+// ==================================
+// taken from mplayer (spuenc.c)
+void cSPUEncoder::EncodePixelbufRle(int x, int y, int w, int h, u_char *inbuf, int stride, encodedata *ed)
+{
+ pixbuf pb;
+ int i, row;
+ pb.x = w;
+ pb.y = h;
+
+ if (cDxr3Interface::Instance().GetHorizontalSize() < 700)
+ {
+ double fac = (double)OSDWIDTH / (double)OSDWIDTH2;
+ ScaleOSD(fac, inbuf,10);
+ inbuf = OSD_Screen2;
+ }
+
+ m_ColorManager = new cColorManager();
+
+ // Encode colors into highlight regions
+ m_ColorManager->EncodeColors(w, h, inbuf, OSD_Screen3);
+ inbuf = OSD_Screen3;
+
+ pb.pixels = inbuf;
+ ed->count= 4;
+ ed->nibblewaiting= 0;
+
+ row= 0;
+ for (i= 0; i < pb.y; i++)
+ {
+ encode_do_row(ed, &pb, row);
+ row+= 2;
+ if (row > pb.y)
+ {
+ row= 1;
+ ed->oddstart= ed->count;
+ }
+ }
+ encode_do_control(x,y, ed, &pb);
+
+ delete m_ColorManager;
+}
+
+// ==================================
+void cSPUEncoder::ScaleOSD(double fac, unsigned char* buf, unsigned char NumColors)
+{
+ int y,x,s,d;
+ unsigned char dline[2 * OSDWIDTH + 10];
+
+ memset(OSD_Screen2, 0x00 ,OSDWIDTH * OSDHEIGHT);
+
+ if (cDxr3Interface::Instance().GetHorizontalSize() < 470)
+ {
+ for (y = 0; y < OSDHEIGHT; y++)
+ for (s = 0, d = 0; d < OSDWIDTH; s++, d += 2)
+ OSD_Screen2[y * OSDWIDTH + s] = buf[y * OSDWIDTH + d];
+ } else {
+ for (y = 0; y < OSDHEIGHT; y++)
+ {
+ memset(dline,0,2*OSDWIDTH+10);
+
+ for (s=0,d=0; s < OSDWIDTH; s++,d+=2)
+ {
+ // stretch line to double width to 1440
+ dline[d] = buf[y*OSDWIDTH + s];
+ }
+
+ for (d=1; d < (2*OSDWIDTH); d+=2)
+ {
+ #if VDRVERSNUM <= 10307
+ // 'interpolate' values
+ if ((dline[d-1] == BLACK) || (dline[d+1] == BLACK))
+ {
+ dline[d] = BLACK;
+ }
+ else if ((dline[d-1] == WHITE) || (dline[d+1] == WHITE))
+ {
+ dline[d] = WHITE;
+ }
+ else if ((dline[d-1] == CYAN) || (dline[d+1] == CYAN))
+ {
+ dline[d] = CYAN;
+ }
+ else
+ {
+ dline[d] = dline[d+1];
+ }
+ #else /*VDRVERSNUM*/
+ dline[d] = dline[d+1];
+ #endif /*VDRVERSNUM*/
+ }
+
+ for (s=0, x = 0; x < OSDWIDTH2; x++,s+=3)
+ {
+ // now take every third pixel (1440/3=480)
+ OSD_Screen2[y * OSDWIDTH + x] = dline[s];
+ }
+ }
+ }
+}
+
+// ==================================
+// taken from mplayer (spuenc.c)
+void cSPUEncoder::encode_put_nibble(encodedata* ed, u_char nibble)
+{
+ if (ed->nibblewaiting)
+ {
+ ed->data[ed->count++]|= nibble;
+ ed->nibblewaiting= 0;
+ }
+ else
+ {
+ ed->data[ed->count]= nibble<<4;
+ ed->nibblewaiting= 1;
+ }
+}
+
+// ==================================
+// taken from mplayer (spuenc.c)
+void cSPUEncoder::encode_pixels(encodedata* ed, int color, int number)
+{
+ if (number > 3)
+ {
+ if (number > 15)
+ {
+ encode_put_nibble(ed, 0);
+ if (number > 63)
+ {
+ encode_put_nibble(ed, (number & 0xC0)>>6);
+ }
+ }
+ encode_put_nibble(ed, (number & 0x3C)>>2);
+ }
+ encode_put_nibble(ed, ((number & 0xF)<<2) | color);
+}
+
+// ==================================
+// taken from mplayer (spuenc.c)
+void cSPUEncoder::encode_eol(encodedata* ed)
+{
+ if (ed->nibblewaiting)
+ {
+ ed->count++;
+ ed->nibblewaiting= 0;
+ }
+ ed->data[ed->count++]= 0x00;
+ ed->data[ed->count++]= 0x00;
+}
+
+// ==================================
+// taken from mplayer (spuenc.c)
+void cSPUEncoder::encode_do_row(encodedata* ed, pixbuf* pb, int row)
+{
+ int i= 0;
+ u_char* pix= pb->pixels + row * pb->x;
+ int color= *pix & 0x03;
+ int n= 0; /* the number of pixels of this color */
+
+ while (i++ < pb->x)
+ {
+ /* FIXME: watch this space for EOL */
+ if ((*pix & 0x03) != color || n == 255 )
+ {
+ encode_pixels( ed, color, n );
+ color= *pix & 0x03;
+ n= 1;
+ }
+ else
+ {
+ n++;
+ }
+ pix++;
+ }
+
+ /* this small optimization: (n>63) can save up to two bytes per line
+ * I wonder if this is compatible with all the hardware... */
+ if (color == 0 && n > 63)
+ {
+ encode_eol( ed );
+ }
+ else
+ {
+ encode_pixels( ed, color, n );
+ }
+
+ if (ed->nibblewaiting)
+ {
+ ed->count++;
+ ed->nibblewaiting= 0;
+ }
+}
+
+// ==================================
+// taken from mplayer (spuenc.c)
+void cSPUEncoder::encode_do_control(int x,int y, encodedata* ed, pixbuf* pb)
+{
+ int controlstart= ed->count;
+ int x1;
+ int i;
+ u_int top, left, bottom, right;
+
+ top= y; //this forces the first bit to be visible on a TV
+ left= x; //you could actually pass in x/y and do some nice
+
+ bottom= top + pb->y - 1;
+ right= left + pb->x - 1;
+
+ /* start at x0+2*/
+ i= controlstart;
+
+ x1= (i); //marker for last command block address
+
+ /* display duration... */
+ ed->data[i++]= 0x00;
+ ed->data[i++]= 0x00; //duration before turn on command occurs (will not be used)
+
+ /* x1 */
+ ed->data[i++]= x1 >> 8; //since this is the last command block, this
+ ed->data[i++]= x1 & 0xff;//points back to itself
+
+ /* 0x00: force displaying */
+ ed->data[i++]= 0x00;
+
+ /* 0x03: palette info */
+ ed->data[i++]= 0x03;
+ ed->data[i++]= 0x01;
+ ed->data[i++]= 0x23;
+
+ /* 0x04: transparency info (reversed) */
+ ed->data[i++]= 0x04; // SET_CONTR
+ ed->data[i++]= 0xFF;
+ ed->data[i++]= 0x70;
+
+ /* 0x05: coordinates */
+ ed->data[i++]= 0x05; // SET_DAREA
+ ed->data[i++]= left >> 4;
+ ed->data[i++]= ((left&0xf)<<4)+(right>>8);
+ ed->data[i++]= (right&0xff);
+ ed->data[i++]= top >> 4;
+ ed->data[i++]= ((top&0xf)<<4)+(bottom>>8);
+ ed->data[i++]= (bottom&0xff);
+
+ /* 0x06: both fields' offsets */
+ ed->data[i++]= 0x06; // SET_DSPXA
+ ed->data[i++]= 0x00;
+ ed->data[i++]= 0x04;
+ ed->data[i++]= ed->oddstart >> 8;
+ ed->data[i++]= ed->oddstart & 0xff;
+
+ int len;
+ unsigned char *spudata;
+
+ spudata = m_ColorManager->GetSpuData(len);
+
+ for (int si= 0; si < len; si++)
+ {
+ ed->data[i++] = *(spudata + si);
+ }
+
+ /* 0xFF: end sequence */
+ ed->data[i++]= 0xFF;
+ if (! i&1 )
+ {
+ ed->data[i++]= 0xff;
+ }
+
+ /* x0 */
+ ed->data[2]= (controlstart) >> 8;
+ ed->data[3]= (controlstart) & 0xff;
+
+ /* packet size */
+ ed->data[0]= i >> 8;
+ ed->data[1]= i & 0xff;
+
+ ed->count= i;
+}
+
+// ==================================
+// we _only_ write usefull data
+void cSPUEncoder::CalculateActiveOsdArea()
+{
+ // reset
+ //m_x0 = m_x1 = m_y0 = m_y1 = 0;
+
+ // calculate
+/* for (int i = 1; i < 8; i++)
+ {
+ m_x0 = max(m_x0, m_windows[i].x0);
+ m_x1 = max(m_x1, m_windows[i].y0);
+ m_y0 = max(m_y0, m_windows[i].x1);
+ m_y1 = max(m_y1, m_windows[i].y1);
+ }
+*/
+}