/* * dxr3spuencoder.c - encodes an OSD bitmap as subpicture * * Assimilated and adapted by * Stefan Schluenss * 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 "dxr3spuencoder.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 #include #include #include namespace XineScaler { template 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 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: