/** * decode given picture * * @param pic SubPicture object containing info about caption * @throws CoreException */ private void decode(SubPictureBD pic) throws CoreException { palette = decodePalette(pic); bitmap = decodeImage(pic, palette.getIndexOfMostTransparentPaletteEntry()); primaryColorIndex = bitmap.getPrimaryColorIndex( palette.getAlpha(), configuration.getAlphaThreshold(), palette.getY()); }
/** * 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()); } }
public Palette(Palette palette) { size = palette.getSize(); useBT601 = palette.usesBT601(); r = new byte[size]; g = new byte[size]; b = new byte[size]; a = new byte[size]; y = new byte[size]; cb = new byte[size]; cr = new byte[size]; for (int i = 0; i < size; i++) { a[i] = palette.a[i]; r[i] = palette.r[i]; g[i] = palette.g[i]; b[i] = palette.b[i]; y[i] = palette.y[i]; cb[i] = palette.cb[i]; cr[i] = palette.cr[i]; } }
/** * Create VobSub IDX file * * @param fname file name * @param pic a SubPicture object used to read screen width and height * @param offsets array of offsets (one for each caption) * @param timestamps array of PTS time stamps (one for each caption) * @param palette 16 color main Palette * @throws bdsup2sub.core.CoreException */ public static void writeIdx( String fname, SubPicture pic, int[] offsets, int[] timestamps, Palette palette) throws CoreException { BufferedWriter out = null; try { out = new BufferedWriter(new FileWriter(fname)); out.write("# VobSub index file, v7 (do not modify this line!)"); out.newLine(); out.write("# Created by " + Constants.APP_NAME + " " + Constants.APP_VERSION); out.newLine(); out.newLine(); out.write("# Frame size"); out.newLine(); out.write( "size: " + pic.getWidth() + "x" + (pic.getHeight() - 2 * configuration.getCropOffsetY())); out.newLine(); out.newLine(); out.write("# Origin - upper-left corner"); out.newLine(); out.write("org: 0, 0"); out.newLine(); out.newLine(); out.write("# Scaling"); out.newLine(); out.write("scale: 100%, 100%"); out.newLine(); out.newLine(); out.write("# Alpha blending"); out.newLine(); out.write("alpha: 100%"); out.newLine(); out.newLine(); out.write("# Smoothing"); out.newLine(); out.write("smooth: OFF"); out.newLine(); out.newLine(); out.write("# Fade in/out in milliseconds"); out.newLine(); out.write("fadein/out: 0, 0"); out.newLine(); out.newLine(); out.write("# Force subtitle placement relative to (org.x, org.y)"); out.newLine(); out.write("align: OFF at LEFT TOP"); out.newLine(); out.newLine(); out.write("# For correcting non-progressive desync. (in millisecs or hh:mm:ss:ms)"); out.newLine(); out.write("time offset: 0"); out.newLine(); out.newLine(); out.write("# ON: displays only forced subtitles, OFF: shows everything"); out.newLine(); out.write("forced subs: OFF"); out.newLine(); out.newLine(); out.write("# The palette of the generated file"); out.newLine(); out.write("palette: "); // Palette pal = Core.getCurrentDVDPalette(); for (int i = 0; i < palette.getSize(); i++) { int rbg[] = palette.getRGB(i); int val = (rbg[0] << 16) | (rbg[1] << 8) | rbg[2]; out.write(ToolBox.toHexLeftZeroPadded(val, 6).substring(2)); if (i != palette.getSize() - 1) { out.write(", "); } } out.newLine(); out.newLine(); out.write("# Custom colors (transp idxs and the four colors)"); out.newLine(); out.write("custom colors: OFF, tridx: 1000, colors: 000000, 444444, 888888, cccccc"); out.newLine(); out.newLine(); out.write("# Language index in use"); out.newLine(); out.write("langidx: 0"); out.newLine(); out.newLine(); out.write("# " + LANGUAGES[configuration.getLanguageIdx()][0]); out.newLine(); out.write("id: " + LANGUAGES[configuration.getLanguageIdx()][1] + ", index: 0"); out.newLine(); out.write( "# Decomment next line to activate alternative name in DirectVobSub / Windows Media Player 6.x"); out.newLine(); out.write("# alt: " + LANGUAGES[configuration.getLanguageIdx()][0]); out.newLine(); out.write("# Vob/Cell ID: 1, 1 (PTS: 0)"); out.newLine(); for (int i = 0; i < timestamps.length; i++) { out.write("timestamp: " + ptsToTimeStrIdx(timestamps[i])); out.write(", filepos: " + ToolBox.toHexLeftZeroPadded(offsets[i], 9).substring(2)); out.newLine(); } } catch (IOException ex) { throw new CoreException(ex.getMessage()); } finally { try { if (out != null) { out.close(); } } catch (IOException ex) { } } }
/* (non-Javadoc) * @see SubtitleStream#getImage(Bitmap) */ public BufferedImage getImage(Bitmap bm) { return bm.getImage(palette.getColorModel()); }
/* (non-Javadoc) * @see SubtitleStream#getImage() */ public BufferedImage getImage() { return bitmap.getImage(palette.getColorModel()); }
/* (non-Javadoc) * @see deadbeef.SupTools.SubtitleStream#decode(int) */ @Override public void decode(int index) throws CoreException { try { File f = new File(subPictures.get(index).getFileName()); if (!f.exists()) { throw new CoreException("file " + subPictures.get(index).getFileName() + " not found."); } BufferedImage img = ImageIO.read(f); int w = img.getWidth(); int h = img.getHeight(); this.palette = null; // first try to read image and palette directly from imported image if (img.getType() == BufferedImage.TYPE_BYTE_INDEXED) { IndexColorModel icm = (IndexColorModel) img.getColorModel(); if (icm.getMapSize() < 255 || (icm.hasAlpha() && icm.getAlpha(255) == 0)) { // create palette palette = new Palette(256); for (int i = 0; i < icm.getMapSize(); i++) { int alpha = (icm.getRGB(i) >> 24) & 0xff; if (alpha >= configuration.getAlphaCrop()) { palette.setARGB(i, icm.getRGB(i)); } else { palette.setARGB(i, 0); } } // copy pixels WritableRaster raster = img.getRaster(); bitmap = new Bitmap( img.getWidth(), img.getHeight(), (byte[]) raster.getDataElements(0, 0, img.getWidth(), img.getHeight(), null)); } } // if this failed, assume RGB image and quantize palette if (palette == null) { // grab int array (ARGB) int[] pixels = new int[w * h]; img.getRGB(0, 0, w, h, pixels, 0, w); // quantize image QuantizeFilter qf = new QuantizeFilter(); bitmap = new Bitmap(img.getWidth(), img.getHeight()); int ct[] = qf.quantize(pixels, bitmap.getInternalBuffer(), w, h, 255, false, false); int size = ct.length; if (size > 255) { logger.warn("Quantizer failed.\n"); size = 255; } // create palette palette = new Palette(256); for (int i = 0; i < size; i++) { int alpha = (ct[i] >> 24) & 0xff; if (alpha >= configuration.getAlphaCrop()) { palette.setARGB(i, ct[i]); } else { palette.setARGB(i, 0); } } } primaryColorIndex = bitmap.getPrimaryColorIndex( palette.getAlpha(), configuration.getAlphaThreshold(), palette.getY()); // crop BitmapBounds bounds = bitmap.getCroppingBounds(palette.getAlpha(), configuration.getAlphaCrop()); if (bounds.yMin > 0 || bounds.xMin > 0 || bounds.xMax < bitmap.getWidth() - 1 || bounds.yMax < bitmap.getHeight() - 1) { w = bounds.xMax - bounds.xMin + 1; h = bounds.yMax - bounds.yMin + 1; if (w < 2) { w = 2; } if (h < 2) { h = 2; } bitmap = bitmap.crop(bounds.xMin, bounds.yMin, w, h); // update picture SubPictureXml pic = subPictures.get(index); pic.setImageWidth(w); pic.setImageHeight(h); pic.setOfsX(pic.getOriginalXOffset() + bounds.xMin); pic.setOfsY(pic.getOriginalYOffset() + bounds.yMin); } } catch (IOException e) { throw new CoreException(e.getMessage()); } catch (OutOfMemoryError e) { JOptionPane.showMessageDialog( null, "Out of heap! Use -Xmx256m to increase heap!", "Error!", JOptionPane.WARNING_MESSAGE); throw new CoreException("Out of heap! Use -Xmx256m to increase heap!"); } }