summaryrefslogtreecommitdiff
path: root/dxr3interface_spu_encoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'dxr3interface_spu_encoder.c')
-rw-r--r--dxr3interface_spu_encoder.c688
1 files changed, 688 insertions, 0 deletions
diff --git a/dxr3interface_spu_encoder.c b/dxr3interface_spu_encoder.c
new file mode 100644
index 0000000..405da33
--- /dev/null
+++ b/dxr3interface_spu_encoder.c
@@ -0,0 +1,688 @@
+/*
+ * dxr3interface_spu_encoder.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "dxr3interface_spu_encoder.h"
+#include "dxr3tools.h"
+
+/*
+ ToDo:
+ - cSPUEncoder::encode_do_row: FIXME: watch this space for EOL
+*/
+
+
+#ifdef USE_XINE_SCALER
+/*=======================================================================
+ *
+ * Scaling functions taken from the xine plugin
+ *
+ */
+
+#include <math.h>
+#include <signal.h>
+#include <string>
+#include <vdr/plugin.h>
+
+namespace XineScaler
+{
+
+ template <const int yInc = 1, class T = int>
+ class cBresenham
+ {
+ const int m_dx;
+ const int m_dy;
+
+ int m_eps;
+ T m_y;
+
+ public:
+ cBresenham(const int dy, const int dx, const int eps, T const y0 = 0)
+ : m_dx(dx), m_dy(dy), m_eps(eps - m_dx), m_y(y0)
+ {
+ }
+
+ int eps() const
+ {
+ return m_eps;
+ }
+
+ T step()
+ {
+ m_eps += m_dy;
+ while (m_eps >= 0)
+ {
+ m_eps -= m_dx;
+ m_y += yInc;
+ }
+ return m_y;
+ }
+
+ T step(int n)
+ {
+ if (n <= 0)
+ return m_y;
+ while (--n > 0)
+ step();
+ return step();
+ }
+
+ T stepRelative(int n = 1)
+ {
+ T const y = m_y;
+ return step(n) - y;
+ }
+ };
+
+ static uint8_t *ScaleBitmapLQ(const uint8_t *src, uint8_t *dest, int x0,
+ int y0, int w, int h, int ws, int hs, int x1,
+ int y1, int w1, int h1,
+ const uint8_t transparentIndex)
+ {
+ uint8_t *const screen = new uint8_t[ OSDHEIGHT * OSDWIDTH ];
+ {
+ int x1 = x0 + w;
+ int y1 = y0 + h;
+ int x2 = OSDWIDTH;
+ int y2 = OSDHEIGHT;
+
+ if (x1 > x2)
+ x1 = x2;
+
+ if (y1 > y2)
+ y1 = y2;
+
+ uint8_t *dst = screen;
+
+ for (int y = 0; y < y0; y++)
+ {
+ for (int x = 0; x < x2; x++)
+ *dst++ = transparentIndex;
+ }
+
+ for (int y = y0; y < y1; y++)
+ {
+ for (int x = 0; x < x0; x++)
+ *dst++ = transparentIndex;
+
+ for (int x = x0; x < x1; x++)
+ *dst++ = *src++;
+
+ for (int x = x1; x < x2; x++)
+ *dst++ = transparentIndex;
+ }
+
+ for (int y = y1; y < y2; y++)
+ {
+ for (int x = 0; x < x2; x++)
+ *dst++ = transparentIndex;
+ }
+ }
+
+ uint8_t *scaled = dest; //new uint8_t[ hs * ws ];
+ {
+ int x2 = x1 + w1;
+ int y2 = y1 + h1;
+
+ if (x2 > ws)
+ {
+ x2 = ws;
+ w1 = x2 - x1;
+ if (w1 < 0)
+ w1 = 0;
+ }
+
+ if (y2 > hs)
+ {
+ y2 = hs;
+ h1 = y2 - y1;
+ if (h1 < 0)
+ h1 = 0;
+ }
+
+ cBresenham<OSDWIDTH, uint8_t *> yyBh(2 * OSDHEIGHT, 2 * hs, hs, screen);
+ uint8_t *screen0 = yyBh.step(y1); //(((2 * y1 + 1) * OSDHEIGHT / hs) / 2);
+
+ cBresenham<> xxBh0(2 * OSDWIDTH, 2 * ws, ws);
+ xxBh0.step(x1); //(((2 * x1 + 1) * OSDWIDTH / ws) / 2);
+
+ uint8_t *scaled0 = scaled + y1 * OSDWIDTH; //ws; ******
+
+ for (int y = y1; y < y2; y++)
+ {
+ cBresenham<> xxBh(xxBh0);
+ int xxx = xxBh.step(0); //(((2 * x1 + 1) * OSDWIDTH / ws) / 2);
+
+ uint8_t *screen00 = screen0 + xxx;
+ uint8_t *scaled00 = scaled0 + x1;
+
+ for (int x = x1; x < x2; x++)
+ {
+ *scaled00++ = *screen00;
+ screen00 += xxBh.stepRelative();
+ }
+
+ scaled0 += OSDWIDTH; //ws; *******
+ screen0 = yyBh.step();
+ }
+ }
+
+ delete [] screen;
+ return scaled;
+ }
+
+};
+
+/*=======================================================================
+ *
+ * End of scaling functions taken from the xine plugin
+ *
+ */
+#endif /* USE_XINE_SCALER */
+
+
+// ==================================
+// 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;
+}
+
+
+//========================================
+//Sets the palette indexes to use for one
+//window taking into account the global
+//palette (with colors needed by all windows)
+
+void cSPUEncoder::SetPalette(int numWindow, cPalette* commonPalette,
+ cPalette* windowPalette)
+{
+ int NumColors;
+ const tColor *Colors = windowPalette->Colors(NumColors);
+ if (Colors)
+ {
+ for (int i = 0; i < NumColors; i++)
+ {
+ int idx = commonPalette->Index(Colors[i] & 0x00FFFFFF);
+ int opacity = ((Colors[i] & 0xFF000000) >> 24) * 0xF / 0xFF;
+ bitmapcolor[numWindow][i] = (opacity<<4) | idx;
+ }
+ }
+}
+
+//========================================
+//Clears the OSD bitmap
+
+void cSPUEncoder::Clear(void)
+{
+ memset(OSD_Screen, 0, sizeof(OSD_Screen));
+}
+
+//=============================================================
+//Sets the spu palette and flushes the OSD content into the spu
+int cSPUEncoder::Flush(cPalette *Palette)
+{
+ int NumColors;
+ const tColor *Colors = Palette->Colors(NumColors);
+ if (Colors)
+ {
+ unsigned int palcolors[16];
+ for (int i = 0; i < NumColors; i++)
+ {
+ // convert AARRGGBB to AABBGGRR (the driver expected the the
+ // colors the wrong way, so does Rgb2YCrCb and friends)
+ unsigned int color = ((Colors[i] & 0x0000FF) << 16)
+ | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16);
+ palcolors[i] = Tools::Rgb2YCrCb(color);
+ }
+ cDxr3Interface::Instance().SetPalette(palcolors);
+ }
+
+ // calculate osd size (actually dead code)
+ CalculateActiveOsdArea();
+
+ m_encodeddata.count = 0;
+ EncodePixelbufRle(0, 0, OSDWIDTH, OSDHEIGHT-1, OSD_Screen, 0,
+ &m_encodeddata);
+
+ dsyslog("dxr3: cSPUEncoder::Flush: OSD data size: %d",
+ m_encodeddata.count);
+
+ if (m_encodeddata.count <= DATASIZE)
+ {
+ cDxr3Interface::Instance().WriteSpu((uint8_t*) &m_encodeddata,
+ m_encodeddata.count);
+ return 0;
+ }
+ else
+ {
+ esyslog("dxr3: spu: warning: SPU data size (%d) exceeds limit (%d)",
+ m_encodeddata.count, DATASIZE);
+ return -1;
+ }
+}
+
+// ==================================
+// stamps window content into full osd bitmap
+void cSPUEncoder::CopyBlockIntoOSD(int numWindow, int linewidth, int x0,
+ int y0, int x1, int y1, const tIndex *data)
+{
+ tIndex *cp;
+ const tIndex *sp = data;
+
+ if (x1 >= OSDWIDTH)
+ x1 = OSDWIDTH - 1;
+ if (y1 >= OSDHEIGHT)
+ y1 = OSDHEIGHT - 1;
+ cp = &OSD_Screen[y0 * OSDWIDTH + x0];
+
+ for (int y = y0; y <= y1; y++)
+ {
+ for (int x = x0; x <= x1; x++)
+ {
+ *(cp++) = bitmapcolor[numWindow][*(sp++) & 0x0f];
+ }
+ cp += OSDWIDTH - (x1 - x0 + 1);
+ sp += linewidth - (x1 - x0 + 1);
+ }
+}
+
+// ==================================
+// 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;
+
+#ifdef USE_XINE_SCALER
+ int ws = cDxr3Interface::Instance().GetHorizontalSize();
+ int hs = cDxr3Interface::Instance().GetVerticalSize();
+ if (ws < 720 || hs < 576)
+ inbuf = XineScaler::ScaleBitmapLQ(inbuf, OSD_Screen2, 0, 0, OSDWIDTH,
+ OSDHEIGHT, ws, hs, 0, 0, ws, hs,
+ 0 /* clrTransparent */);
+#else
+ if (cDxr3Interface::Instance().GetHorizontalSize() < 700)
+ {
+ double fac = (double)OSDWIDTH / (double)OSDWIDTH2;
+ ScaleOSD(fac, inbuf, 10);
+ inbuf = OSD_Screen2;
+ }
+#endif /* USE_XINE_SCALER */
+
+ 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;
+}
+
+#ifndef USE_XINE_SCALER
+// ==================================
+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)
+ {
+ dline[d] = dline[d + 1];
+ }
+
+ 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];
+ }
+ }
+ }
+}
+#endif /* not USE_XINE_SCALER */
+
+// ==================================
+// 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);
+ //check that the highlight regions data wont overflow the buffer
+ if (i + len + 2 > DATASIZE)
+ {
+ ed->count = DATASIZE + 1;
+ return;
+ }
+
+ 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;
+}
+
+// ==================================
+// Stop spu display
+void cSPUEncoder::StopSpu(void)
+{
+ uint8_t ed[10];
+ /* packet size */
+ ed[0] = 0;
+ ed[1] = 10;
+
+ /* pointer to the SP_DCSQT */
+ ed[2] = 0;
+ ed[3] = 4;
+
+ /* SP_DCSQT */
+ /* display duration... */
+ ed[4] = 0x00;
+ ed[5] = 0x00; //duration before turn on command occurs (will not be used)
+
+ /* pointer to next command block */
+ ed[6] = 0; //since this is the last command block, this
+ ed[7] = 4;//points back to itself
+
+ /* 0x02: stop displaying */
+ ed[8] = 0x02;
+
+ /* 0xFF: end sequence */
+ ed[9] = 0xFF;
+ cDxr3Interface::Instance().WriteSpu(ed, 10);
+}
+
+// ==================================
+// 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);
+ }
+ */
+}
+
+// Local variables:
+// mode: c++
+// c-file-style: "stroustrup"
+// c-file-offsets: ((inline-open . 0))
+// indent-tabs-mode: t
+// End: