/** Enter a local index 'ix00' to 'indx', the index of indices */ private void writeMainIndxEntry(long ix00pointer, int dwSize, int nFrames) throws IOException { if (pointer2indxNextEntry + 16 + 8 > MAX_INDX_SIZE) { raFile.close(); throw new RuntimeException("AVI_Writer ERROR: Index Size Overflow"); } long savePosition = raFile.getFilePointer(); raFile.seek(pointer2indxNextEntry); writeLong(ix00pointer); writeInt(dwSize); writeInt(nFrames); pointer2indxNextEntry += 16; nIndxEntries++; writeString("JUNK"); // write a JUNK chunk for padding chunkSizeHere(); // size of 'JUNK' for padding goes here raFile.seek(endHeadPointer);// end of the padded range chunkEndWriteSize(); // 'JUNK' finished (nesting level 3) raFile.seek(pointer2indx+4); writeInt((int)(pointer2indxNextEntry - pointer2indx - 8)); //write new size of 'indx' raFile.seek(pointer2indxNEntriesInUse); writeInt(nIndxEntries); //write new number of 'indx' entries raFile.seek(savePosition); }
/** Write a frame as jpeg- or png-compressed image */ private void writeCompressedFrame(ImageProcessor ip) throws IOException { //IJ.log("BufferdImage Type="+bufferedImage.getType()); // 1=RGB, 13=indexed if (biCompression==JPEG_COMPRESSION) { BufferedImage bi = getBufferedImage(ip); ImageIO.write(bi, "jpeg", raOutputStream); } else { //if (biCompression==PNG_COMPRESSION) { BufferedImage bi = ip.getBufferedImage(); ImageIO.write(bi, "png", raOutputStream); } }
linePad = 4 - minLineLength%4; //uncompressed lines written must be a multiple of 4 bytes frameDataSize = (bytesPerPixel*xDim+linePad)*yDim; int microSecPerFrame = (int)Math.round((1.0/getFrameRate(imp))*1.0e6); int dwChunkId = biCompression==NO_COMPRESSION ? FOURCC_00db : FOURCC_00dc; long sizeEstimate = bytesPerPixel*xDim*yDim*(long)zDim; writeString("RIFF"); // signature chunkSizeHere(); // size of file (nesting level 0) writeString("AVI "); // RIFF type writeString("LIST"); // first LIST chunk, which contains information on data decoding chunkSizeHere(); // size of LIST (nesting level 1) writeString("hdrl"); // LIST chunk type writeString("avih"); // Write the avih sub-CHUNK writeInt(0x38); // length of the avih sub-CHUNK (38H) not including the writeInt(microSecPerFrame); // dwMicroSecPerFrame - Write the microseconds per frame writeInt(0); // dwMaxBytesPerSec (maximum data rate of the file in bytes per second) writeInt(0); // dwPaddingGranularity (for header length?), previously dwReserved1, usually set to zero. writeInt(0x10); // dwFlags - just set the bit for AVIF_HASINDEX writeInt(zDim); // dwTotalFrames - total frame number writeInt(0); // dwInitialFrames -Initial frame for interleaved files. writeInt(1); // dwStreams - number of streams in the file - here 1 video and zero audio. writeInt(0); // dwSuggestedBufferSize writeInt(xDim); // dwWidth - image width in pixels writeInt(yDim); // dwHeight - image height in pixels writeInt(0); // dwReserved[4] writeInt(0);
/** Asks for the compression type and filename; then saves as AVI file */ public void run(ImageProcessor ip) { if (!showDialog(imp)) return; //compression type dialog SaveDialog sd = new SaveDialog("Save as AVI...", imp.getTitle(), ".avi"); String fileName = sd.getFileName(); if (fileName == null) return; String fileDir = sd.getDirectory(); FileInfo fi = imp.getOriginalFileInfo(); if (fi!=null && imp.getStack().isVirtual() && fileDir.equals(fi.directory) && fileName.equals(fi.fileName)) { IJ.error("AVI Writer", "Virtual stacks cannot be saved in place."); return; } try { writeImage(imp, fileDir + fileName, COMPRESSION_TYPES[compressionIndex], jpegQuality); IJ.showStatus(""); } catch (IOException e) { IJ.error("AVI Writer", "An error occured writing the file.\n \n" + e); } IJ.showStatus(""); }
/** Reserve space to write the size of chunk and remember the position * for a later call to chunkEndWriteSize(). * Several levels of chunkSizeHere() and chunkEndWriteSize() may be nested. */ private void chunkSizeHere() throws IOException { sizePointers[stackPointer] = raFile.getFilePointer(); writeInt(0); //for now, write 0 to reserve space for "size" item stackPointer++; }
private boolean showDialog(ImagePlus imp) { String options = Macro.getOptions(); if (options!=null) { if (!options.contains("compression=")) options = "compression=JPEG "+options; options = options.replace("compression=Uncompressed", "compression=None"); Macro.setOptions(options); } double fps = getFrameRate(imp); int decimalPlaces = (int) fps == fps?0:1; GenericDialog gd = new GenericDialog("Save as AVI..."); gd.addChoice("Compression:", COMPRESSION_STRINGS, COMPRESSION_STRINGS[compressionIndex]); gd.addNumericField("Frame Rate:", fps, decimalPlaces, 3, "fps"); gd.showDialog(); // user input (or reading from macro) happens here if (gd.wasCanceled()) // dialog cancelled? return false; compressionIndex = gd.getNextChoiceIndex(); fps = gd.getNextNumber(); if (fps<=0.5) fps = 0.5; imp.getCalibration().fps = fps; return true; }
linePad = 4 - minLineLength%4; //uncompressed lines written must be a multiple of 4 bytes frameDataSize = (bytesPerPixel*xDim+linePad)*yDim; int microSecPerFrame = (int)Math.round((1.0/getFrameRate(imp))*1.0e6); int dwChunkId = biCompression==NO_COMPRESSION ? FOURCC_00db : FOURCC_00dc; long sizeEstimate = bytesPerPixel*xDim*yDim*(long)zDim; writeString("RIFF"); // signature chunkSizeHere(); // size of file (nesting level 0) writeString("AVI "); // RIFF type writeString("LIST"); // first LIST chunk, which contains information on data decoding chunkSizeHere(); // size of LIST (nesting level 1) writeString("hdrl"); // LIST chunk type writeString("avih"); // Write the avih sub-CHUNK writeInt(0x38); // length of the avih sub-CHUNK (38H) not including the writeInt(microSecPerFrame); // dwMicroSecPerFrame - Write the microseconds per frame writeInt(0); // dwMaxBytesPerSec (maximum data rate of the file in bytes per second) writeInt(0); // dwPaddingGranularity (for header length?), previously dwReserved1, usually set to zero. writeInt(0x10); // dwFlags - just set the bit for AVIF_HASINDEX writeInt(zDim); // dwTotalFrames - total frame number writeInt(0); // dwInitialFrames -Initial frame for interleaved files. writeInt(1); // dwStreams - number of streams in the file - here 1 video and zero audio. writeInt(0); // dwSuggestedBufferSize writeInt(xDim); // dwWidth - image width in pixels writeInt(yDim); // dwHeight - image height in pixels writeInt(0); // dwReserved[4] writeInt(0);
/** Asks for the compression type and filename; then saves as AVI file */ public void run(ImageProcessor ip) { if (!showDialog(imp)) return; //compression type dialog SaveDialog sd = new SaveDialog("Save as AVI...", imp.getTitle(), ".avi"); String fileName = sd.getFileName(); if (fileName == null) return; String fileDir = sd.getDirectory(); FileInfo fi = imp.getOriginalFileInfo(); if (fi!=null && imp.getStack().isVirtual() && fileDir.equals(fi.directory) && fileName.equals(fi.fileName)) { IJ.error("AVI Writer", "Virtual stacks cannot be saved in place."); return; } try { writeImage(imp, fileDir + fileName, COMPRESSION_TYPES[compressionIndex], jpegQuality); IJ.showStatus(""); } catch (IOException e) { IJ.error("AVI Writer", "An error occured writing the file.\n \n" + e); } IJ.showStatus(""); }
/** Reserve space to write the size of chunk and remember the position * for a later call to chunkEndWriteSize(). * Several levels of chunkSizeHere() and chunkEndWriteSize() may be nested. */ private void chunkSizeHere() throws IOException { sizePointers[stackPointer] = raFile.getFilePointer(); writeInt(0); //for now, write 0 to reserve space for "size" item stackPointer++; }
private boolean showDialog(ImagePlus imp) { String options = Macro.getOptions(); if (options!=null) { if (!options.contains("compression=")) options = "compression=JPEG "+options; options = options.replace("compression=Uncompressed", "compression=None"); Macro.setOptions(options); } double fps = getFrameRate(imp); int decimalPlaces = (int) fps == fps?0:1; GenericDialog gd = new GenericDialog("Save as AVI..."); gd.addChoice("Compression:", COMPRESSION_STRINGS, COMPRESSION_STRINGS[compressionIndex]); gd.addNumericField("Frame Rate:", fps, decimalPlaces, 3, "fps"); gd.showDialog(); // user input (or reading from macro) happens here if (gd.wasCanceled()) // dialog cancelled? return false; compressionIndex = gd.getNextChoiceIndex(); fps = gd.getNextNumber(); if (fps<=0.5) fps = 0.5; imp.getCalibration().fps = fps; return true; }
/** Enter a local index 'ix00' to 'indx', the index of indices */ private void writeMainIndxEntry(long ix00pointer, int dwSize, int nFrames) throws IOException { if (pointer2indxNextEntry + 16 + 8 > MAX_INDX_SIZE) { raFile.close(); throw new RuntimeException("AVI_Writer ERROR: Index Size Overflow"); } long savePosition = raFile.getFilePointer(); raFile.seek(pointer2indxNextEntry); writeLong(ix00pointer); writeInt(dwSize); writeInt(nFrames); pointer2indxNextEntry += 16; nIndxEntries++; writeString("JUNK"); // write a JUNK chunk for padding chunkSizeHere(); // size of 'JUNK' for padding goes here raFile.seek(endHeadPointer);// end of the padded range chunkEndWriteSize(); // 'JUNK' finished (nesting level 3) raFile.seek(pointer2indx+4); writeInt((int)(pointer2indxNextEntry - pointer2indx - 8)); //write new size of 'indx' raFile.seek(pointer2indxNEntriesInUse); writeInt(nIndxEntries); //write new number of 'indx' entries raFile.seek(savePosition); }
/** At the end of a chunk, calculate its size and write it to the * position remembered previously. Also pads to 2-byte boundaries. */ private void chunkEndWriteSize() throws IOException { stackPointer--; long position = raFile.getFilePointer(); raFile.seek(sizePointers[stackPointer]); writeInt((int)(position - (sizePointers[stackPointer]+4))); raFile.seek(((position+1)/2)*2); //pad to 2-byte boundary //IJ.log("chunk at 0x"+Long.toHexString(sizePointers[stackPointer]-4)+"-0x"+Long.toHexString(position)); }
/** Write a frame as jpeg- or png-compressed image */ private void writeCompressedFrame(ImageProcessor ip) throws IOException { //IJ.log("BufferdImage Type="+bufferedImage.getType()); // 1=RGB, 13=indexed if (biCompression==JPEG_COMPRESSION) { BufferedImage bi = getBufferedImage(ip); ImageIO.write(bi, "jpeg", raOutputStream); } else { //if (biCompression==PNG_COMPRESSION) { BufferedImage bi = ip.getBufferedImage(); ImageIO.write(bi, "png", raOutputStream); } }
/** At the end of a chunk, calculate its size and write it to the * position remembered previously. Also pads to 2-byte boundaries. */ private void chunkEndWriteSize() throws IOException { stackPointer--; long position = raFile.getFilePointer(); raFile.seek(sizePointers[stackPointer]); writeInt((int)(position - (sizePointers[stackPointer]+4))); raFile.seek(((position+1)/2)*2); //pad to 2-byte boundary //IJ.log("chunk at 0x"+Long.toHexString(sizePointers[stackPointer]-4)+"-0x"+Long.toHexString(position)); }