/** * decode palette from the input stream * * @param pic SubPicture object containing info about the current caption * @return * @throws CoreException */ private Palette decodePalette(SubPictureBD pic) throws CoreException { boolean fadeOut = false; int paletteIndex; List<PaletteInfo> paletteInfos = pic.getPalettes().get(pic.getImageObject().getPaletteID()); if (paletteInfos == null) { throw new CoreException("Palette ID out of bounds."); } Palette palette = new Palette(256, Core.usesBT601()); // by definition, index 0xff is always completely transparent // also all entries must be fully transparent after initialization try { for (PaletteInfo paletteInfo : paletteInfos) { int index = paletteInfo.getPaletteOffset(); for (int i = 0; i < paletteInfo.getPaletteSize(); i++) { // each palette entry consists of 5 bytes paletteIndex = buffer.getByte(index); int y = buffer.getByte(++index); int cr, cb; if (configuration.isSwapCrCb()) { cb = buffer.getByte(++index); cr = buffer.getByte(++index); } else { cr = buffer.getByte(++index); cb = buffer.getByte(++index); } int alpha = buffer.getByte(++index); int alphaOld = palette.getAlpha(paletteIndex); // avoid fading out if (alpha >= alphaOld) { if (alpha < configuration .getAlphaCrop()) { // to not mess with scaling algorithms, make transparent // color black y = 16; cr = 128; cb = 128; } palette.setAlpha(paletteIndex, alpha); } else { fadeOut = true; } palette.setYCbCr(paletteIndex, y, cb, cr); index++; } } if (fadeOut) { logger.warn("fade out detected -> patched palette\n"); } return palette; } catch (FileBufferException ex) { throw new CoreException(ex.getMessage()); } }
/** * Decode caption from the input stream. * * @param subPictureBD SubPicture object containing info about the caption * @param transparentColorIndex index of the transparent color * @return bitmap of the decoded caption * @throws CoreException */ private Bitmap decodeImage(SubPictureBD subPictureBD, int transparentColorIndex) throws CoreException { int width = subPictureBD.getImageWidth(); int height = subPictureBD.getImageHeight(); // always decode image obj 0, start with first entry in fragment list ImageObjectFragment imageObjectFragment = subPictureBD.getImageObject().getFragmentList().get(0); long startOfs = imageObjectFragment.getImageBufferOfs(); if (width > subPictureBD.getWidth() || height > subPictureBD.getHeight()) { throw new CoreException( "Subpicture too large: " + width + "x" + height + " at offset " + ToolBox.toHexLeftZeroPadded(startOfs, 8)); } Bitmap bm = new Bitmap(width, height, (byte) transparentColorIndex); int b; int index = 0; int ofs = 0; int size; int xpos = 0; try { // just for multi-packet support, copy all of the image data in one common buffer byte[] buffer = new byte[subPictureBD.getImageObject().getBufferSize()]; index = 0; for (ImageObjectFragment fragment : subPictureBD.getImageObject().getFragmentList()) { // copy data of all packet to one common buffer imageObjectFragment = fragment; for (int i = 0; i < imageObjectFragment.getImagePacketSize(); i++) { buffer[index + i] = (byte) this.buffer.getByte(imageObjectFragment.getImageBufferOfs() + i); } index += imageObjectFragment.getImagePacketSize(); } index = 0; do { b = buffer[index++] & 0xff; if (b == 0) { b = buffer[index++] & 0xff; if (b == 0) { // next line ofs = (ofs / width) * width; if (xpos < width) { ofs += width; } xpos = 0; } else { if ((b & 0xC0) == 0x40) { // 00 4x xx -> xxx zeroes size = ((b - 0x40) << 8) + (buffer[index++] & 0xff); for (int i = 0; i < size; i++) { bm.getInternalBuffer()[ofs++] = 0; /*(byte)b;*/ } xpos += size; } else if ((b & 0xC0) == 0x80) { // 00 8x yy -> x times value y size = (b - 0x80); b = buffer[index++] & 0xff; for (int i = 0; i < size; i++) { bm.getInternalBuffer()[ofs++] = (byte) b; } xpos += size; } else if ((b & 0xC0) != 0) { // 00 cx yy zz -> xyy times value z size = ((b - 0xC0) << 8) + (buffer[index++] & 0xff); b = buffer[index++] & 0xff; for (int i = 0; i < size; i++) { bm.getInternalBuffer()[ofs++] = (byte) b; } xpos += size; } else { // 00 xx -> xx times 0 for (int i = 0; i < b; i++) { bm.getInternalBuffer()[ofs++] = 0; } xpos += b; } } } else { bm.getInternalBuffer()[ofs++] = (byte) b; xpos++; } } while (index < buffer.length); return bm; } catch (FileBufferException ex) { throw new CoreException(ex.getMessage()); } catch (ArrayIndexOutOfBoundsException ex) { logger.warn( "Problems during RLE decoding of picture OBJ at offset " + ToolBox.toHexLeftZeroPadded(startOfs + index, 8) + "\n"); return bm; } }