/// <summary> /// Renders a teletext page to a bitmap using the designated /// default charset and second G0 charset designation. /// If either designation is -1 use the corresponding /// preselected designation. /// </summary> /// <param name="byPage">Teletext page data</param> /// <param name="mPage">Pagenumber</param> /// <param name="sPage">Subpagenumber</param> /// <returns>Rendered teletext page as bitmap</returns> public void RenderPage(ref Bitmap pageBitmap, byte[] byPage, int mPage, int sPage, bool waiting) { int col; int hold; int foreground, background, doubleheight, charset, mosaictype; byte held_mosaic; bool flag = false; bool isBoxed = false; byte[] pageChars = new byte[31 * 40]; int[] pageAttribs = new int[31 * 40]; bool row24 = false; if (pageBitmap == null) { return; } Graphics renderGraphics = Graphics.FromImage(pageBitmap); // Decode the page data (Hamming 8/4 or odd parity) for (int rowNr = 0; rowNr < MAX_ROWS; rowNr++) { if (rowNr * 42 >= byPage.Length) { break; } int packetNumber = Hamming.GetPacketNumber(rowNr * 42, ref byPage); // Only the packets 0-25 are accepted if (packetNumber < 0 || packetNumber > 25) { continue; } bool stripParity = true; // Packets 0 and 25 are hamming 8/4 encoded if (packetNumber == 25 || packetNumber == 0) { stripParity = false; } // Decode the whole row and remove the first two bytes for (col = 2; col < 42; col++) { // After pageheader in packet 0 (Bit 10) odd parity is used if (col >= 10 && packetNumber == 0) { stripParity = true; } byte kar = byPage[rowNr * 42 + col]; if (stripParity) { kar &= 0x7f; } pageChars[packetNumber * 40 + col - 2] = kar; } // Exists a packet 24 (Toptext line) if (packetNumber == 24) { row24 = true; } } int row; int txtLanguage; // language detection. Extract the bit C12-C14 from the teletext header and set the language code int languageCode; byte byte1 = Hamming.Decode[byPage[9]]; if (byte1 == 0xFF) { languageCode = 0; } else { languageCode = ((byte1 >> 3) & 0x01) | (((byte1 >> 2) & 0x01) << 1) | (((byte1 >> 1) & 0x01) << 2); } switch (languageCode) { case 0: txtLanguage = 1; break; case 1: txtLanguage = 4; break; case 2: txtLanguage = _isRegionalDKorNO ? 13 : 11; break; case 3: txtLanguage = 5; break; case 4: txtLanguage = 3; break; case 5: txtLanguage = 8; break; case 6: txtLanguage = 0; break; default: txtLanguage = 1; break; } // Detect if it's a boxed page. Boxed Page = subtitle and/or newsflash bit is set bool isSubtitlePage = Hamming.IsSubtitleBitSet(0, ref byPage); bool isNewsflash = Hamming.IsNewsflash(0, ref byPage); isBoxed = isNewsflash | isSubtitlePage; MediaPortal.GUI.Library.Log.Debug("Newsflash: " + isNewsflash); MediaPortal.GUI.Library.Log.Debug("Subtitle: " + isSubtitlePage); MediaPortal.GUI.Library.Log.Debug("Boxed: " + isBoxed); // Determine if the header or toptext line sould be displayed. bool displayHeaderAndTopText = !_fullscreenMode || !isBoxed || (isBoxed && _selectedPageText.IndexOf("-") != -1) || (isBoxed && _selectedPageText.IndexOf("-") == -1 && !_selectedPageText.Equals(Convert.ToString(mPage, 16))); // Iterate over all lines of the teletext page and prepare the rendering for (row = 0; row <= 24; row++) { // If row 24 and no toptext exists, then skip this row if (row == 24 && !row24) { continue; } // If not display the header and toptext line, then clear these two rows if ((row == 0 || row == 24) && !displayHeaderAndTopText) { for (int i = 0; i < 40; ++i) { pageChars[row * 40 + i] = 32; pageAttribs[row * 40 + i] = ((int)TextColors.Trans1 << 4) | ((int)TextColors.White); } } else { // Otherwise, analyse the information. First set the forground to white and the background to: // - Transparent, if transparent mode or boxed and fullscreen and not display the header and toptext line // - Black otherwise foreground = (int)TextColors.White; if ((isBoxed || _transparentMode) && _fullscreenMode && !displayHeaderAndTopText) { background = (int)TextColors.Trans1; } else { background = (int)TextColors.Black; } // Reset the attributes doubleheight = 0; charset = 0; mosaictype = 0; hold = 0; held_mosaic = 32; // Iterate over all columns in the row and check if a box starts for (int loop1 = 0; loop1 < 40; loop1++) { // Box starts in this row if (pageChars[(row * 40) + loop1] == (int)Attributes.StartBox) { flag = true; break; } } // If boxed page and box doesn't start in this line, than set foreground and background to black or transparent // depending on the mode (fullscreen <-> windowed) if (isBoxed && flag == false) { if (_fullscreenMode) { foreground = (int)TextColors.Trans1; background = (int)TextColors.Trans1; } else { foreground = (int)TextColors.Black; background = (int)TextColors.Black; } } // Iterate over all columns in the row again and now analyse every byte for (col = 0; col < 40; col++) { int index = row * 40 + col; // Set the attributes pageAttribs[index] = (doubleheight << 10 | charset << 8 | background << 4 | foreground); // Boxed and no flag and not row 24 than delete the characters if (isBoxed && !flag && row != 24) { pageChars[index] = 32; } // Analyse the attributes if (pageChars[index] < 32) { switch (pageChars[index]) { case (int)Attributes.AlphaBlack: foreground = (int)TextColors.Black; charset = 0; break; case (int)Attributes.AlphaRed: foreground = (int)TextColors.Red; charset = 0; break; case (int)Attributes.AlphaGreen: foreground = (int)TextColors.Green; charset = 0; break; case (int)Attributes.AlphaYellow: foreground = (int)TextColors.Yellow; charset = 0; break; case (int)Attributes.AlphaBlue: foreground = (int)TextColors.Blue; charset = 0; break; case (int)Attributes.AlphaMagenta: foreground = (int)TextColors.Magenta; charset = 0; break; case (int)Attributes.AlphaCyan: foreground = (int)TextColors.Cyan; charset = 0; break; case (int)Attributes.AlphaWhite: foreground = (int)TextColors.White; charset = 0; break; case (int)Attributes.Flash: break; case (int)Attributes.Steady: break; case (int)Attributes.EndBox: if (isBoxed) { if (_fullscreenMode) { foreground = (int)TextColors.Trans1; background = (int)TextColors.Trans1; } else { foreground = (int)TextColors.Black; background = (int)TextColors.Black; } } break; case (int)Attributes.StartBox: if (isBoxed) { // Clear everything until this position in the line if (col > 0) { for (int loop1 = 0; loop1 < col; loop1++) { pageChars[(row * 40) + loop1] = 32; } } // Clear also the page attributes for (int clear = 0; clear < col; clear++) { if (_fullscreenMode) { pageAttribs[row * 40 + clear] = doubleheight << 10 | charset << 8 | (int)TextColors.Trans1 << 4 | (int)TextColors.Trans1; } else { pageAttribs[row * 40 + clear] = doubleheight << 10 | charset << 8 | (int)TextColors.Black << 4 | (int)TextColors.Black; } } // Set the standard background color if (background == (int)TextColors.Trans1) { background = (int)TextColors.Black; } } break; case (int)Attributes.NormalSize: doubleheight = 0; pageAttribs[index] = (doubleheight << 10 | charset << 8 | background << 4 | foreground); break; case (int)Attributes.DoubleHeight: if (row < 23) { doubleheight = 1; } break; case (int)Attributes.MosaicBlack: foreground = (int)TextColors.Black; charset = 1 + mosaictype; break; case (int)Attributes.MosaicRed: foreground = (int)TextColors.Red; charset = 1 + mosaictype; break; case (int)Attributes.MosaicGreen: foreground = (int)TextColors.Green; charset = 1 + mosaictype; break; case (int)Attributes.MosaicYellow: foreground = (int)TextColors.Yellow; charset = 1 + mosaictype; break; case (int)Attributes.MosaicBlue: foreground = (int)TextColors.Blue; charset = 1 + mosaictype; break; case (int)Attributes.MosaicMagenta: foreground = (int)TextColors.Magenta; charset = 1 + mosaictype; break; case (int)Attributes.MosaicCyan: foreground = (int)TextColors.Cyan; charset = 1 + mosaictype; break; case (int)Attributes.MosaicWhite: foreground = (int)TextColors.White; charset = 1 + mosaictype; break; case (int)Attributes.Conceal: if (_hiddenMode == false) { foreground = background; pageAttribs[index] = (doubleheight << 10 | charset << 8 | background << 4 | foreground); } break; case (int)Attributes.ContiguousMosaic: mosaictype = 0; if (charset > 0) { charset = 1; pageAttribs[index] = (doubleheight << 10 | charset << 8 | background << 4 | foreground); } break; case (int)Attributes.SeparatedMosaic: mosaictype = 1; if (charset > 0) { charset = 2; pageAttribs[index] = (doubleheight << 10 | charset << 8 | background << 4 | foreground); } break; case (int)Attributes.Esc: break; case (int)Attributes.BlackBackground: background = (int)TextColors.Black; pageAttribs[index] = (doubleheight << 10 | charset << 8 | background << 4 | foreground); break; case (int)Attributes.NewBackground: background = foreground; pageAttribs[index] = (doubleheight << 10 | charset << 8 | background << 4 | foreground); break; case (int)Attributes.HoldMosaic: hold = 1; break; case (int)Attributes.ReleaseMosaic: hold = 2; break; } if (hold > 0 && charset > 0) { pageChars[index] = held_mosaic; } else { pageChars[index] = 32; } if (hold == 2) { hold = 0; } } else { if (charset > 0) { held_mosaic = pageChars[index]; } // If doubleheight is selected than delete the following line if (doubleheight > 0) { pageChars[index + 40] = 0xFF; } } } // Check, if there is double height selected in than set the attributes for the next row and skip it for (int count = (row + 1) * 40; count < ((row + 1) * 40) + 40; count++) { if (pageChars[count] == 255) { for (int loop1 = 0; loop1 < 40; loop1++) { pageAttribs[(row + 1) * 40 + loop1] = ((pageAttribs[(row * 40) + loop1] & 0xF0) | ((pageAttribs[(row * 40) + loop1] & 0xF0) >> 4)); } row++; break; } } } } //for (int rowNr = 0; rowNr < 24; rowNr++) // Generate header line, if it should be displayed if (IsDecimalPage(mPage) && displayHeaderAndTopText) { int i; string pageNumber; int lineColor; // Determine the state, of the header line. // Red=Incomplete page number // Yellow=Waiting for page // Green=Page is displayed if (_selectedPageText.IndexOf("-") == -1) { if (waiting) { lineColor = (int)TextColors.Yellow; pageNumber = _selectedPageText + (_selectedSubPageText == string.Empty ? "" : ("/" + _selectedSubPageText)); } else { lineColor = (int)TextColors.Green; pageNumber = Convert.ToString(mPage, 16) + "/" + Convert.ToString(sPage + 1, 16); } } else { lineColor = (int)TextColors.Red; pageNumber = _selectedPageText; } string headline = "MediaPortal P." + pageNumber; headline += new string((char)32, 32 - headline.Length); byte[] mpText = Encoding.ASCII.GetBytes(headline); Array.Copy(mpText, 0, pageChars, 0, mpText.Length); for (i = 0; i < 11; i++) { pageAttribs[i] = ((int)TextColors.Black << 4) | lineColor; } for (i = 12; i < 40; i++) { pageAttribs[i] = ((int)TextColors.Black << 4) | ((int)TextColors.White); } } // Now we generate the bitmap int y = 0; int x; int width = _pageRenderWidth / 40; int height = _pageRenderHeight / 25; float fntSize = height; //Math.Min(width, height); float nPercentage = ((float)_percentageOfMaximumHeight / 100); _fontTeletext = new Font("Lucida Console", fntSize, FontStyle.Regular, GraphicsUnit.Pixel); float fntHeight = _fontTeletext.GetHeight(renderGraphics); while (fntHeight > nPercentage * height) // || fntHeight > nPercentage * width) { fntSize -= 0.1f; _fontTeletext = new Font("Lucida Console", fntSize, FontStyle.Bold, GraphicsUnit.Pixel); fntHeight = _fontTeletext.GetHeight(renderGraphics); } MediaPortal.GUI.Library.Log.Debug("FONT SIZE OF TELETEXT: " + fntSize); try { // Select the brush, depending on the page and mode // Draw the base rectangle if ((isBoxed || _transparentMode) && _fullscreenMode) { renderGraphics.Clear(Transparent); } else { renderGraphics.FillRectangle(new SolidBrush(Color.Black), 0, 0, _pageRenderWidth, _pageRenderHeight); } // Fill the rectangle with the teletext page informations for (row = 0; row < 25; row++) { // If not display a toptext line than abort if (!displayHeaderAndTopText && row == 24) { break; } x = 0; // Draw a single point for (col = 0; col < 40; col++) { Render(ref renderGraphics, ref pageBitmap, pageChars[row * 40 + col], pageAttribs[row * 40 + col], ref x, ref y, width, height, txtLanguage); } y += height + (row == 23 ? 2 : 0); } } finally { _fontTeletext.Dispose(); _fontTeletext = null; } }