BlinkenArea - GitList
Repositories
Blog
Wiki
BlinkenLibJava
Code
Commits
Branches
Tags
Search
Tree:
2f8ee39
Branches
Tags
master
v0.1.1
v0.1.2
v0.1.3
v0.1.4
v0.1.5
BlinkenLibJava
org
blinkenarea
BlinkenLib
BlinkenMovie.java
first version of GIF write support
Stefan Schuermans
commited
2f8ee39
at 2018-09-18 20:41:57
BlinkenMovie.java
Blame
History
Raw
/* BlinkenLib * Copyright (C) 2004-2011: Stefan Schuermans <stefan@schuermans.info> * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html * a blinkenarea.org project */ package org.blinkenarea.BlinkenLib; import java.util.*; import java.util.regex.*; import java.io.*; import java.awt.*; import java.awt.image.*; import javax.imageio.*; import javax.imageio.metadata.*; import javax.imageio.stream.*; import org.w3c.dom.*; import org.blinkenarea.BlinkenLib.*; public class BlinkenMovie { private int height; private int width; private int channels; private int maxval; private int infoCnt; private String[][] infos; private int frameCnt; private BlinkenFrame[] frames; public BlinkenMovie( int height, int width, int channels, int maxval ) { if( height < BlinkenConstants.BlinkenHeightMin ) height = BlinkenConstants.BlinkenHeightMin; if( height > BlinkenConstants.BlinkenHeightMax ) height = BlinkenConstants.BlinkenHeightMax; if( width < BlinkenConstants.BlinkenWidthMin ) width = BlinkenConstants.BlinkenWidthMin; if( width > BlinkenConstants.BlinkenWidthMax ) width = BlinkenConstants.BlinkenWidthMax; if( channels < BlinkenConstants.BlinkenChannelsMin ) channels = BlinkenConstants.BlinkenChannelsMin; if( channels > BlinkenConstants.BlinkenChannelsMax ) channels = BlinkenConstants.BlinkenChannelsMax; if( maxval < BlinkenConstants.BlinkenMaxvalMin ) maxval = BlinkenConstants.BlinkenMaxvalMin; if( maxval > BlinkenConstants.BlinkenMaxvalMax ) maxval = BlinkenConstants.BlinkenMaxvalMax; this.height = height; this.width = width; this.channels = channels; this.maxval = maxval; infoCnt = 0; infos = new String[0][2]; frameCnt = 0; frames = new BlinkenFrame[0]; } public BlinkenMovie( BlinkenMovie movie ) { int i; height = movie.height; width = movie.width; channels = movie.channels; maxval = movie.maxval; infoCnt = 0; infos = new String[0][2]; frameCnt = 0; frames = new BlinkenFrame[0]; for( i = 0; i < movie.infoCnt; i++ ) appendInfo( new String( movie.infos[i][0] ), new String( movie.infos[i][1] ) ); for( i = 0; i < movie.frameCnt; i++ ) appendFrame( new BlinkenFrame( movie.frames[i] ) ); } public int getHeight( ) { return height; } public int getWidth( ) { return width; } public int getChannels( ) { return channels; } public int getMaxval( ) { return maxval; } public int getDuration( ) { int duration, i; duration = 0; for( i = 0; i < frameCnt; i++ ) duration += frames[i].getDuration( ); return duration; } public int getInfoCnt( ) { return infoCnt; } public String getInfoType( int infoNo ) { if( infoCnt < 1 ) return ""; if( infoNo < 0 ) infoNo = 0; if( infoNo >= infoCnt ) infoNo = infoCnt - 1; return infos[infoNo][0]; } public String getInfoData( int infoNo ) { if( infoCnt < 1 ) return ""; if( infoNo < 0 ) infoNo = 0; if( infoNo >= infoCnt ) infoNo = infoCnt - 1; return infos[infoNo][1]; } public void setInfo( int infoNo, String infoType, String infoData ) { if( infoNo < 0 || infoNo >= infoCnt ) return; infos[infoNo][0] = infoType; infos[infoNo][1] = infoData; } public void insertInfo( int infoNo, String infoType, String infoData ) { String[][] new_infos; int i; if( infoNo < 0 || infoNo > infoCnt ) return; new_infos = new String[infoCnt+1][2]; for( i = 0; i < infoNo; i++ ) { new_infos[i][0] = infos[i][0]; new_infos[i][1] = infos[i][1]; } new_infos[infoNo][0] = infoType; new_infos[infoNo][1] = infoData; for( i = infoNo; i < infoCnt; i++ ) { new_infos[i+1][0] = infos[i][0]; new_infos[i+1][1] = infos[i][1]; } infos = new_infos; infoCnt++; } public void appendInfo( String infoType, String infoData ) { insertInfo( infoCnt, infoType, infoData ); } public void deleteInfo( int infoNo ) { String[][] new_infos; int i; if( infoNo < 0 || infoNo >= infoCnt ) return; new_infos = new String[infoCnt-1][2]; for( i = 0; i < infoNo; i++ ) { new_infos[i][0] = infos[i][0]; new_infos[i][1] = infos[i][1]; } for( i = infoNo; i < infoCnt-1; i++ ) { new_infos[i][0] = infos[i+1][0]; new_infos[i][1] = infos[i+1][1]; } infos = new_infos; infoCnt--; } public void deleteInfos( ) { infos = new String[0][2]; infoCnt = 0; } public int getFrameCnt( ) { return frameCnt; } public BlinkenFrame getFrame( int frameNo ) { BlinkenFrame frame; if( frameCnt < 1 ) { frame = new BlinkenFrame( height, width, channels, maxval, 0 ); frame.clear( ); return frame; } if( frameNo < 0 ) frameNo = 0; if( frameNo >= frameCnt ) frameNo = frameCnt - 1; return frames[frameNo]; } public void setFrame( int frameNo, BlinkenFrame frame ) { if( frameNo < 0 || frameNo >= frameCnt ) return; frame.resize( height, width, channels, maxval ); frames[frameNo] = frame; } public void insertFrame( int frameNo, BlinkenFrame frame ) { BlinkenFrame[] new_frames; int i; if( frameNo < 0 || frameNo > frameCnt ) return; new_frames = new BlinkenFrame[frameCnt+1]; for( i = 0; i < frameNo; i++ ) new_frames[i] = frames[i]; frame.resize( height, width, channels, maxval ); new_frames[frameNo] = frame; for( i = frameNo; i < frameCnt; i++ ) new_frames[i+1] = frames[i]; frames = new_frames; frameCnt++; } public void appendFrame( BlinkenFrame frame ) { insertFrame( frameCnt, frame ); } public void deleteFrame( int frameNo ) { BlinkenFrame[] new_frames; int i; if( frameNo < 0 || frameNo >= frameCnt ) return; new_frames = new BlinkenFrame[frameCnt-1]; for( i = 0; i < frameNo; i++ ) new_frames[i] = frames[i]; for( i = frameNo; i < frameCnt-1; i++ ) new_frames[i] = frames[i+1]; frames = new_frames; frameCnt--; } public void deleteFrames( ) { frames = new BlinkenFrame[0]; frameCnt = 0; } public void resize( int height, int width, int channels, int maxval ) { int i; if( height < BlinkenConstants.BlinkenHeightMin ) height = BlinkenConstants.BlinkenHeightMin; if( height > BlinkenConstants.BlinkenHeightMax ) height = BlinkenConstants.BlinkenHeightMax; if( width < BlinkenConstants.BlinkenWidthMin ) width = BlinkenConstants.BlinkenWidthMin; if( width > BlinkenConstants.BlinkenWidthMax ) width = BlinkenConstants.BlinkenWidthMax; if( channels < BlinkenConstants.BlinkenChannelsMin ) channels = BlinkenConstants.BlinkenChannelsMin; if( channels > BlinkenConstants.BlinkenChannelsMax ) channels = BlinkenConstants.BlinkenChannelsMax; if( maxval < BlinkenConstants.BlinkenMaxvalMin ) maxval = BlinkenConstants.BlinkenMaxvalMin; if( maxval > BlinkenConstants.BlinkenMaxvalMax ) maxval = BlinkenConstants.BlinkenMaxvalMax; this.height = height; this.width = width; this.channels = channels; this.maxval = maxval; for( i = 0; i < frameCnt; i++ ) frames[i].resize( height, width, channels, maxval ); } public void scale( int height, int width ) { int i; if( height < BlinkenConstants.BlinkenHeightMin ) height = BlinkenConstants.BlinkenHeightMin; if( height > BlinkenConstants.BlinkenHeightMax ) height = BlinkenConstants.BlinkenHeightMax; if( width < BlinkenConstants.BlinkenWidthMin ) width = BlinkenConstants.BlinkenWidthMin; if( width > BlinkenConstants.BlinkenWidthMax ) width = BlinkenConstants.BlinkenWidthMax; this.height = height; this.width = width; for( i = 0; i < frameCnt; i++ ) frames[i].scale( height, width ); } public String toString( ) { String str = "BlinkenMovie " + width + "x" + height + "-" + channels + "/" + (maxval + 1) + "\n"; int i; for( i = 0; i < infoCnt; i++ ) str += infos[i][0] + " = " + infos[i][1] + "\n"; for( i = 0; i < frameCnt; i++ ) str += "frame " + i + "\n" + frames[i].toString( ); return str; } public boolean loadBlm( BufferedReader reader ) { Pattern header, infoLine, startOfFrame, dataLine; String line, pixel; Matcher matcher; int new_width, new_height, new_duration, y, x, val; BlinkenFrame frame; //initialize needed regexp patterns header = Pattern.compile( "^ *# BlinkenLights Movie ([0-9]+)x([0-9]+)" ); infoLine = Pattern.compile( "^ *# ?([A-Za-z0-9]+)(?: *= *|: *)(.*)" ); startOfFrame = Pattern.compile( "^ *@([0-9]+)" ); dataLine = Pattern.compile( " *([01])" ); //delete all frames deleteInfos( ); deleteFrames( ); resize( 0, 0, 0, 0 ); //try to read from buffered reader try { //read magic and size if( (line = reader.readLine( )) != null ) { if( (matcher = header.matcher( line )).find( ) ) { new_width = Integer.parseInt( matcher.group( 1 ) ); new_height = Integer.parseInt( matcher.group( 2 ) ); resize( new_height, new_width, 1, 1 ); //create unused dummy frame for beginning frame = new BlinkenFrame( height, width, 1, 1, 0 ); y = 0; //read frames while( (line = reader.readLine( )) != null ) { //info line if( (matcher = infoLine.matcher( line )).find( ) ) { appendInfo( matcher.group( 1 ), matcher.group( 2 ) ); } //start of frame else if( (matcher = startOfFrame.matcher( line )).find( ) ) { new_duration = Integer.parseInt( matcher.group( 1 ) ); //create new frame and append it to movie frame = new BlinkenFrame( height, width, 1, 1, new_duration ); frame.clear( ); appendFrame( frame ); y = 0; } //data line else if( (matcher = dataLine.matcher( line )).find( ) ) { x = 0; while( true ) { pixel = matcher.group( 1 ); val = Integer.parseInt( pixel ); frame.setPixel( y, x, 0, (byte)val ); //set pixel if( ! matcher.find( ) ) //get next pixel break; x++; //next pixel } y++; //next row } } //while( (line = ... } //if( matcher = header..matcher( ... } //if( (line = ... //success return true; } catch( IOException e ) { } //some error return false; } public boolean loadBlm( String filename ) { try { BufferedReader file = new BufferedReader( new FileReader( filename ) ); boolean ret = loadBlm( file ); file.close( ); return ret; } catch( IOException e ) { return false; } } public boolean loadBmm( BufferedReader reader ) { Pattern header, infoLine, startOfFrame, dataLine; String line, pixel; Matcher matcher; int new_width, new_height, new_duration, y, x, val; BlinkenFrame frame; //initialize needed regexp patterns header = Pattern.compile( "^ *# BlinkenMini Movie ([0-9]+)x([0-9]+)" ); infoLine = Pattern.compile( "^ *# ?([A-Za-z0-9]+)(?: *= *|: *)(.*)" ); startOfFrame = Pattern.compile( "^ *@([0-9]+)" ); dataLine = Pattern.compile( " *(0x[0-9A-Fa-f]+|[0-9]+)" ); //delete all frames deleteInfos( ); deleteFrames( ); resize( 0, 0, 0, 0 ); //try to read from buffered reader try { //read magic and size if( (line = reader.readLine( )) != null ) { if( (matcher = header.matcher( line )).find( ) ) { new_width = Integer.parseInt( matcher.group( 1 ) ); new_height = Integer.parseInt( matcher.group( 2 ) ); resize( new_height, new_width, 1, 255 ); //create unused dummy frame for beginning frame = new BlinkenFrame( height, width, 1, 255, 0 ); y = 0; //read frames while( (line = reader.readLine( )) != null ) { //info line if( (matcher = infoLine.matcher( line )).find( ) ) { appendInfo( matcher.group( 1 ), matcher.group( 2 ) ); } //start of frame else if( (matcher = startOfFrame.matcher( line )).find( ) ) { new_duration = Integer.parseInt( matcher.group( 1 ) ); //create new frame and append it to movie frame = new BlinkenFrame( height, width, 1, 255, new_duration ); frame.clear( ); appendFrame( frame ); y = 0; } //data line else if( (matcher = dataLine.matcher( line )).find( ) ) { x = 0; while( true ) { pixel = matcher.group( 1 ); if( pixel.length( ) >= 2 && pixel.substring( 0, 2 ).equals( "0x" ) ) val = Integer.parseInt( pixel.substring( 2 ), 0x10 ); else val = Integer.parseInt( pixel ); frame.setPixel( y, x, 0, (byte)val ); //set pixel if( ! matcher.find( ) ) //get next pixel break; x++; //next pixel } y++; //next row } } //while( (line = ... } //if( matcher = header..matcher( ... } //if( (line = ... //success return true; } catch( IOException e ) { } //some error return false; } public boolean loadBmm( String filename ) { try { BufferedReader file = new BufferedReader( new FileReader( filename ) ); boolean ret = loadBmm( file ); file.close( ); return ret; } catch( IOException e ) { return false; } } public boolean loadBml( BufferedReader reader ) { Pattern blmTag, blmHeight, blmWidth, blmChannels, blmBits; Pattern infoTitle, infoDescription, infoGeneric, infoCreator, infoAuthor, infoEmail, infoUrl; Pattern frameTag, frameDuration, rowTag, tag; String line, data, row; boolean blmTagFound; Matcher matcher, submatcher; int new_height, new_width, new_channels, bits, new_maxval, chrs, duration, y, x, c, len, i, val; BlinkenFrame frame; //initialize needed regexp patterns blmTag = Pattern.compile( "^[^<]*<blm([^>]*)>" ); blmHeight = Pattern.compile( "height=\"?([0-9]*)\"?" ); blmWidth = Pattern.compile( "width=\"?([0-9]*)\"?" ); blmChannels = Pattern.compile( "channels=\"?([0-9]*)\"?" ); blmBits = Pattern.compile( "bits=\"?([0-9]*)\"?" ); infoTitle = Pattern.compile( "^[^<]*<title>([^<]*)</title>" ); infoDescription = Pattern.compile( "[^<]*<description>([^<]*)</description>" ); infoGeneric = Pattern.compile( "^([A-Za-z0-9]+)(?: *= *|: *)(.*)" ); infoCreator = Pattern.compile( "^[^<]*<creator>([^<]*)</creator>" ); infoAuthor = Pattern.compile( "^[^<]*<author>([^<]*)</author>" ); infoEmail = Pattern.compile( "^[^<]*<email>([^<]*)</email>" ); infoUrl = Pattern.compile( "^[^<]*<url>([^<]*)</url>" ); frameTag = Pattern.compile( "^[^<]*<frame([^>]*)>" ); frameDuration = Pattern.compile( "duration=\"?([0-9]*)\"?" ); rowTag = Pattern.compile( "^[^<]*<row>([0-9A-Fa-f]*)</row>" ); tag = Pattern.compile( "^[^<]*<[^>]*>" ); //delete all frames deleteInfos( ); deleteFrames( ); resize( 0, 0, 0, 0 ); //try to read from buffered reader try { //create unused dummy frame for beginning frame = new BlinkenFrame( 0, 0, 0, 0, 0 ); y = 0; chrs = 1; //read data = ""; blmTagFound = false; while( (line = reader.readLine( )) != null ) { data += " " + line; //add new line to data //match tags while( true ) { //no blm tag yet if( ! blmTagFound ) { //blm tag if( (matcher = blmTag.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //get attributes new_width = 0; new_height = 0; new_channels = 1; bits = 4; new_maxval = 15; if( (submatcher = blmHeight.matcher( matcher.group( 1 ) )).find( ) ) //height new_height = Integer.parseInt( submatcher.group( 1 ) ); if( (submatcher = blmWidth.matcher( matcher.group( 1 ) )).find( ) ) //width new_width = Integer.parseInt( submatcher.group( 1 ) ); if( (submatcher = blmChannels.matcher( matcher.group( 1 ) )).find( ) ) //channels new_channels = Integer.parseInt( submatcher.group( 1 ) ); if( (submatcher = blmBits.matcher( matcher.group( 1 ) )).find( ) ) //bits new_maxval = (1 << (bits = Integer.parseInt( submatcher.group( 1 ) ))) - 1; //remember that blm tag was found blmTagFound = true; //set movie size resize( new_height, new_width, new_channels, new_maxval ); //get number of characters per channel chrs = (bits + 3) >> 2; } //unknown tag else if( (matcher = tag.matcher( data )).find( ) ) //remove matched part data = data.substring( matcher.end( ) ); //nothing matches else //end loop break; } //if( ! blmTagFound ) //blm tag was already found else { //title tag if( (matcher = infoTitle.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //add info to movie appendInfo( "title", matcher.group( 1 ) ); } //description tag else if( (matcher = infoDescription.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //check if generic info if( (submatcher = infoGeneric.matcher( matcher.group( 1 ) )).find( ) ) //add info to movie appendInfo( submatcher.group( 1 ), submatcher.group( 2 ) ); else //add info to movie appendInfo( "description", matcher.group( 1 ) ); } //creator tag else if( (matcher = infoCreator.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //add info to movie appendInfo( "creator", matcher.group( 1 ) ); } //author tag else if( (matcher = infoAuthor.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //add info to movie appendInfo( "author", matcher.group( 1 ) ); } //email tag else if( (matcher = infoEmail.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //add info to movie appendInfo( "email", matcher.group( 1 ) ); } //url tag else if( (matcher = infoUrl.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //add info to movie appendInfo( "url", matcher.group( 1 ) ); } //frame tag else if( (matcher = frameTag.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //get attributes duration = 0; if( (submatcher = frameDuration.matcher( matcher.group( 1 ) )).find( ) ) //duration duration = Integer.parseInt( submatcher.group( 1 ) ); //create new frame and append it to movie frame = new BlinkenFrame( height, width, channels, maxval, duration ); frame.clear( ); appendFrame( frame ); y = 0; } //row tag else if( (matcher = rowTag.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //parse row row = matcher.group( 1 ); len = row.length( ); i = 0; for( x = 0; x < this.width && i + chrs <= len; x++ ) { for( c = 0; c < this.channels && i + chrs <= len; c++, i += chrs ) { val = Integer.parseInt( row.substring( i, i + chrs ), 0x10 ); frame.setPixel( y, x, c, (byte)val ); //set pixel } } y++; //next row } //unknown tag else if( (matcher = tag.matcher( data )).find( ) ) //remove matched part data = data.substring( matcher.end( ) ); //nothing matches else //end loop break; } //if( ! blmTagFound ) ... else } //while( true ) } //while( (line = ... //success return true; } catch( IOException e ) { } //some error return false; } public boolean loadBml( String filename ) { try { BufferedReader file = new BufferedReader( new FileReader( filename ) ); boolean ret = loadBml( file ); file.close( ); return ret; } catch( IOException e ) { return false; } } public boolean loadBbm( String filename ) { RandomAccessFile file; byte[] header, subHeader, infoHeader, frameStartMarker, frameData; int headerMagic, headerHeight, headerWidth, headerChannels, headerMaxval; int headerFrameCnt, headerDuration, headerFramePtr; int subHeaderMagic, subHeaderSize; int frameStartMarkerMagic; StringTokenizer tokenizer; String infoType, infoData; int fileLength, frameLength; int duration, i, y, x, c; BlinkenFrame frame; //delete all frames deleteInfos( ); deleteFrames( ); resize( 0, 0, 0, 0 ); //try to read file try { //open file file = new RandomAccessFile( filename, "r" ); //read header header = new byte[24]; file.readFully( header ); headerMagic = ((int)header[0] & 0xFF) << 24 | ((int)header[1] & 0xFF) << 16 | ((int)header[2] & 0xFF) << 8 | ((int)header[3] & 0xFF); headerHeight = ((int)header[4] & 0xFF) << 8 | ((int)header[5] & 0xFF); headerWidth = ((int)header[6] & 0xFF) << 8 | ((int)header[7] & 0xFF); headerChannels = ((int)header[8] & 0xFF) << 8 | ((int)header[9] & 0xFF); headerMaxval = ((int)header[10] & 0xFF) << 8 | ((int)header[11] & 0xFF); headerFrameCnt = ((int)header[12] & 0xFF) << 24 | ((int)header[13] & 0xFF) << 16 | ((int)header[14] & 0xFF) << 8 | ((int)header[15] & 0xFF); headerDuration = ((int)header[16] & 0xFF) << 24 | ((int)header[17] & 0xFF) << 16 | ((int)header[18] & 0xFF) << 8 | ((int)header[19] & 0xFF); headerFramePtr = ((int)header[20] & 0xFF) << 24 | ((int)header[21] & 0xFF) << 16 | ((int)header[22] & 0xFF) << 8 | ((int)header[23] & 0xFF); //check magic if( headerMagic == 0x23542666 ) { //adapt movie format resize( headerHeight, headerWidth, headerChannels, headerMaxval ); //read subheaders subHeader = new byte[6]; while( file.getFilePointer( ) + 6 <= headerFramePtr ) { file.readFully( subHeader ); subHeaderMagic = ((int)subHeader[0] & 0xFF) << 24 | ((int)subHeader[1] & 0xFF) << 16 | ((int)subHeader[2] & 0xFF) << 8 | ((int)subHeader[3] & 0xFF); subHeaderSize = ((int)subHeader[4] & 0xFF) << 8 | ((int)subHeader[5] & 0xFF); //header fits into gap to frame start if( subHeaderSize >= 6 && file.getFilePointer( ) + subHeaderSize - 6 <= headerFramePtr ) { //info header if( subHeaderMagic == 0x696E666F ) //'i' 'n' 'f' 'o' { //read rest of info header infoHeader = new byte[subHeaderSize - 6]; file.readFully( infoHeader ); //parse information tokenizer = new StringTokenizer( new String( infoHeader ), "\000" ); if( tokenizer.countTokens( ) >= 2 ) { infoType = tokenizer.nextToken(); infoData = tokenizer.nextToken(); appendInfo( infoType, infoData ); } } //unknown subHeader else //skip file.skipBytes( subHeaderSize - 6 ); } //if( file.getFilePointer( ) ... } //while( file.getFilePointer( ) ... //seek to start of frames file.seek( headerFramePtr ); //read frame start marker frameStartMarker = new byte[4]; file.readFully( frameStartMarker ); frameStartMarkerMagic = ((int)frameStartMarker[0] & 0xFF) << 24 | ((int)frameStartMarker[1] & 0xFF) << 16 | ((int)frameStartMarker[2] & 0xFF) << 8 | ((int)frameStartMarker[3] & 0xFF); if( frameStartMarkerMagic == 0x66726D73 ) //'f' 'r' 'm' 's' { //read frames fileLength = (int)file.length( ); frameLength = 2 + headerHeight * headerWidth * headerChannels; frameData = new byte[frameLength]; while( file.getFilePointer( ) + frameLength <= fileLength ) { //read frame file.readFully( frameData ); duration = ((int)frameData[0] & 0xFF) << 8 | ((int)frameData[1] & 0xFF); //build frame and append it to movie frame = new BlinkenFrame( height, width, channels, maxval, duration ); i = 2; for( y = 0; y < headerHeight; y++ ) for( x = 0; x < headerWidth; x++ ) for( c = 0; c < headerChannels; c++, i++ ) frame.setPixel( y, x, c, frameData[i] ); appendFrame( frame ); } //while( file.getFilePointer ... } //if( frameStartMarkerMagic ... } //if( headerMagic ... //close file file.close( ); //success return true; } catch( IOException e ) { } //some error return false; } public boolean loadGif( String filename ) { //delete all frames deleteInfos( ); deleteFrames( ); resize( 0, 0, 0, 0 ); //try to read file try { //load file ImageReader reader = (ImageReader)ImageIO.getImageReadersByFormatName( "gif").next( ); ImageInputStream inStrm = ImageIO.createImageInputStream( new File( filename ) ); reader.setInput( inStrm, false ); //get global metadata IIOMetadata meta = reader.getStreamMetadata( ); Node tree = meta.getAsTree( "javax_imageio_gif_stream_1.0" ); NodeList children = tree.getChildNodes( ); //parse global metadata Color bgColor = Color.BLACK; for( int chi = 0; chi < children.getLength(); ++chi ) { Node child = children.item( chi ); if( child.getNodeName( ).equals( "GlobalColorTable" ) ) { NamedNodeMap attr = child.getAttributes(); Node bgIdxNode = attr.getNamedItem( "backgroundColorIndex" ); if( bgIdxNode != null ) { int bgIdx = Integer.valueOf( bgIdxNode.getNodeValue( ) ); NodeList entries = child.getChildNodes( ); for( int ent = 0; ent < entries.getLength( ); ++ent ) { Node entry = entries.item( ent ); if( entry.getNodeName( ).equals( "ColorTableEntry" ) ) { NamedNodeMap entryAttr = entry.getAttributes(); int index = Integer.valueOf( entryAttr.getNamedItem( "index" ).getNodeValue( ) ); if( index == bgIdx ) { int red = Integer.valueOf( entryAttr.getNamedItem( "red" ).getNodeValue( ) ); int green = Integer.valueOf( entryAttr.getNamedItem( "green" ).getNodeValue( ) ); int blue = Integer.valueOf( entryAttr.getNamedItem( "blue" ).getNodeValue( ) ); bgColor = new Color(red, green, blue); break; } //if index } //if entry } //for ent } //if bgIdxNode } //if child } //for chi //iterate through all frames int frameCnt = reader.getNumImages( true ); int movieWidth = 0; int movieHeight = 0; BufferedImage canvas = null, backup = null; for( int frame = 0; frame < frameCnt; ++frame ) { //get image BufferedImage img = reader.read( frame ); //get image metadata meta = reader.getImageMetadata( frame ); tree = meta.getAsTree( "javax_imageio_gif_image_1.0" ); children = tree.getChildNodes( ); //parse metadata for this image int left = -1, top = -1, width = -1, height = -1; int duration = 100; boolean restoreToPrevious = false; boolean restoreToBackgroundColor = false; for( int chi = 0; chi < children.getLength( ); ++chi ) { Node child = children.item( chi ); if( child.getNodeName( ).equals( "ImageDescriptor" ) ) { NamedNodeMap attr = child.getAttributes(); left = Integer.valueOf( attr.getNamedItem( "imageLeftPosition" ).getNodeValue( ) ); top = Integer.valueOf( attr.getNamedItem( "imageTopPosition" ).getNodeValue( ) ); width = Integer.valueOf( attr.getNamedItem( "imageWidth" ).getNodeValue( ) ); height = Integer.valueOf( attr.getNamedItem( "imageHeight" ).getNodeValue( ) ); } //if ImageDescriptor if( child.getNodeName( ).equals( "GraphicControlExtension" ) ) { NamedNodeMap attr = child.getAttributes(); Node disposalMethod = attr.getNamedItem( "disposalMethod" ); if( disposalMethod != null ) { String disposalMethodStr = disposalMethod.getNodeValue( ); if( disposalMethodStr.equals( "restoreToPrevious" ) ) restoreToPrevious = true; else if( disposalMethodStr.equals( "restoreToBackgroundColor" ) ) restoreToBackgroundColor = true; } Node durationNode = attr.getNamedItem( "delayTime" ); if( durationNode != null ) duration = Integer.valueOf( durationNode.getNodeValue( ) ) * 10; } //if GraphicControlExtension } //for chi //no position -> ignore this frame if (left < 0 || top < 0 || width < 0 || height < 0) continue; //first frame determines movie size if( canvas == null ) { movieWidth = width; movieHeight = height; resize( movieHeight, movieWidth, 3, 255 ); canvas = new BufferedImage( movieWidth, movieHeight, BufferedImage.TYPE_INT_ARGB ); backup = new BufferedImage( movieWidth, movieHeight, BufferedImage.TYPE_INT_ARGB ); } // keep backup if needed if( restoreToPrevious ) { Graphics g = backup.getGraphics(); g.drawImage(canvas, 0, 0, null); g.dispose(); } // draw new image to canvas Graphics g = canvas.getGraphics( ); g.drawImage( img, left, top, null ); g.dispose(); // convert canvas to new frame and append to movie BlinkenFrame newFrame = new BlinkenFrame( movieHeight, movieWidth, 3, 255, duration ); for( int y = 0; y < movieHeight; y++ ) for( int x = 0; x < movieWidth; x++ ) newFrame.setColor( y, x, new Color( canvas.getRGB( x, y ) ) ); appendFrame( newFrame ); // disposal of image on canvas if( restoreToPrevious ) { g = canvas.getGraphics(); g.drawImage(backup, 0, 0, null); g.dispose(); } if( restoreToBackgroundColor ) { Graphics2D g2d = canvas.createGraphics(); g2d.setColor(bgColor); g2d.fill(new Rectangle(left, top, width, height)); g2d.dispose(); } } //for frame //success return true; } catch( IOException e ) { } //some error return false; } public boolean load( String filename ) { if( filename.endsWith( ".blm" ) ) return loadBlm( filename ); if( filename.endsWith( ".bmm" ) ) return loadBmm( filename ); if( filename.endsWith( ".bml" ) ) return loadBml( filename ); if( filename.endsWith( ".bbm" ) ) return loadBbm( filename ); if( filename.endsWith( ".gif" ) ) return loadGif( filename ); deleteFrames( ); return false; } public boolean saveBlm( BufferedWriter writer ) { BlinkenMovie movie; int cnt, i, y, x; String line; //convert movie to suitable format movie = new BlinkenMovie( this ); movie.resize( movie.height, movie.width, 1, 1 ); try { //write header line writer.write( "# BlinkenLights Movie " + movie.width + "x" + movie.height + "\n" ); //write information lines cnt = movie.getInfoCnt( ); for( i = 0; i < cnt; i++ ) writer.write( "# " + movie.getInfoType( i ) + " = " + movie.getInfoData( i ) + "\n" ); //write frames cnt = movie.getFrameCnt( ); for( i = 0; i < cnt; i++ ) { writer.write( "\n@" + movie.frames[i].getDuration( ) + "\n" ); for( y = 0; y < movie.height; y++ ) { line = ""; for( x = 0; x < movie.width; x++ ) { if( movie.frames[i].getPixel( y, x, 0 ) != 0 ) line = line + "1"; else line = line + "0"; } writer.write( line + "\n" ); } } //success return true; } catch( IOException e ) { } //some error return false; } public boolean saveBlm( String filename ) { try { BufferedWriter file = new BufferedWriter( new FileWriter( filename ) ); boolean ret = saveBlm( file ); file.close( ); return ret; } catch( IOException e ) { return false; } } public boolean saveBmm( BufferedWriter writer ) { BlinkenMovie movie; int cnt, i, y, x, val; String line; //convert movie to suitable format movie = new BlinkenMovie( this ); movie.resize( movie.height, movie.width, 1, 255 ); try { //write header line writer.write( "# BlinkenMini Movie " + movie.width + "x" + movie.height + "\n" ); //write information lines cnt = movie.getInfoCnt( ); for( i = 0; i < cnt; i++ ) writer.write( "# " + movie.getInfoType( i ) + " = " + movie.getInfoData( i ) + "\n" ); //write frames cnt = movie.getFrameCnt( ); for( i = 0; i < cnt; i++ ) { writer.write( "\n@" + movie.frames[i].getDuration( ) + "\n" ); for( y = 0; y < movie.height; y++ ) { line = ""; for( x = 0; x < movie.width; x++ ) { val = (int)movie.frames[i].getPixel( y, x, 0 ) & 0xFF; if( val < 0x10 ) line = line + " 0x0" + Integer.toHexString( val ).toUpperCase( ); else line = line + " 0x" + Integer.toHexString( val ).toUpperCase( ); } writer.write( line.substring( 1 ) + "\n" ); } } //success return true; } catch( IOException e ) { } //some error return false; } public boolean saveBmm( String filename ) { try { BufferedWriter file = new BufferedWriter( new FileWriter( filename ) ); boolean ret = saveBmm( file ); file.close( ); return ret; } catch( IOException e ) { return false; } } public boolean saveBml( BufferedWriter writer ) { BlinkenMovie movie; int bits, cnt, i, y, x, c, val; String infoType, infoData, line; //convert movie to suitable format movie = new BlinkenMovie( this ); val = movie.maxval; //get number of bits for( bits = 0; val != 0; val >>= 1, bits++ ); movie.resize( movie.height, movie.width, movie.channels, (1 << bits) - 1 ); try { //write header line writer.write( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); //write blm start tag writer.write( "<blm width=\"" + movie.width + "\" height=\"" + movie.height + "\" bits=\"" + bits + "\" channels=\"" + movie.channels + "\">\n" ); //write information lines writer.write( "\t<header>\n" ); cnt = movie.getInfoCnt( ); for( i = 0; i < cnt; i++ ) { infoType = movie.getInfoType( i ); infoData = movie.getInfoData( i ); if( infoType.equals( "title" ) ) writer.write( "\t\t<title>" + infoData + "</title>\n" ); else if( infoType.equals( "description" ) ) writer.write( "\t\t<description>" + infoData + "</description>\n" ); else if( infoType.equals( "creator" ) ) writer.write( "\t\t<creator>" + infoData + "</creator>\n" ); else if( infoType.equals( "author" ) ) writer.write( "\t\t<author>" + infoData + "</author>\n" ); else if( infoType.equals( "email" ) ) writer.write( "\t\t<email>" + infoData + "</email>\n" ); else if( infoType.equals( "url" ) ) writer.write( "\t\t<url>" + infoData + "</url>\n" ); else writer.write( "\t\t<description>" + infoType + ": " + infoData + "</description>\n" ); } writer.write( "\t</header>\n" ); //write frames cnt = movie.getFrameCnt( ); for( i = 0; i < cnt; i++ ) { writer.write( "\n\t<frame duration=\"" + movie.frames[i].getDuration( ) + "\">\n" ); for( y = 0; y < movie.height; y++ ) { line = ""; for( x = 0; x < movie.width; x++ ) { for( c = 0; c < movie.channels; c++ ) { val = (int)movie.frames[i].getPixel( y, x, c ) & 0xFF; if( bits > 4 && val < 0x10 ) line = line + "0" + Integer.toHexString( val ).toUpperCase( ); else line = line + Integer.toHexString( val ).toUpperCase( ); } } writer.write( "\t\t<row>" + line + "</row>\n" ); } writer.write( "\t</frame>\n" ); } //write blm end tag writer.write( "</blm>\n" ); //success return true; } catch( IOException e ) { } //some error return false; } public boolean saveBml( String filename ) { try { BufferedWriter file = new BufferedWriter( new FileWriter( filename ) ); boolean ret = saveBml( file ); file.close( ); return ret; } catch( IOException e ) { return false; } } public boolean saveBbm( String filename ) { RandomAccessFile file; byte[] header, infoHeader, framePointer, frameStartMarker, frameData; int cnt, duration, i, j, len, y, x, c, val; String infoType, infoData, line; try { //open file file = new RandomAccessFile( filename, "rw" ); //truncate file file.setLength( 0 ); //write header header = new byte[24]; header[0] = 0x23; //magic header[1] = 0x54; header[2] = 0x26; header[3] = 0x66; header[4] = (byte)(this.height >> 8); header[5] = (byte)this.height; header[6] = (byte)(this.width >> 8); header[7] = (byte)this.width; header[8] = (byte)(this.channels >> 8); header[9] = (byte)this.channels; header[10] = (byte)(this.maxval >> 8); header[11] = (byte)this.maxval; cnt = this.getFrameCnt( ); header[12] = (byte)(cnt >> 24); header[13] = (byte)(cnt >> 16); header[14] = (byte)(cnt >> 8); header[15] = (byte)cnt; duration = 0; for( i = 0; i < cnt; i++ ) duration += this.frames[i].getDuration( ); header[16] = (byte)(duration >> 24); header[17] = (byte)(duration >> 16); header[18] = (byte)(duration >> 8); header[19] = (byte)duration; header[20] = 0; //frame pointer is written later header[21] = 0; header[22] = 0; header[23] = 0; file.write( header ); //write information cnt = this.getInfoCnt( ); for( i = 0; i < cnt; i++ ) { infoType = this.getInfoType( i ); if( infoType.length( ) > 32760 ) infoType = infoType.substring( 0, 32760 ); infoData = this.getInfoData( i ); if( infoData.length( ) > 32760 ) infoData = infoData.substring( 0, 32760 ); len = 8 + infoType.length( ) + infoData.length( ); infoHeader = new byte[6]; infoHeader[0] = 0x69; //'i' infoHeader[1] = 0x6E; //'n' infoHeader[2] = 0x66; //'f' infoHeader[3] = 0x6F; //'o' infoHeader[4] = (byte)(len >> 8); infoHeader[5] = (byte)len; file.write( infoHeader ); file.write( (infoType + "\000" + infoData + "\000").getBytes( ) ); } //write frame pointer framePointer = new byte[4]; val = (int)file.getFilePointer( ); framePointer[0] = (byte)(val >> 24); framePointer[1] = (byte)(val >> 16); framePointer[2] = (byte)(val >> 8); framePointer[3] = (byte)val; file.seek( 20 ); file.write( framePointer ); file.seek( val ); //write frame start marker frameStartMarker = new byte[4]; frameStartMarker[0] = 0x66; //'f' frameStartMarker[1] = 0x72; //'r' frameStartMarker[2] = 0x6D; //'m' frameStartMarker[3] = 0x73; //'s' file.write( frameStartMarker ); //write frames len = 2 + this.height * this.width * this.channels; frameData = new byte[len]; cnt = this.getFrameCnt( ); for( i = 0; i < cnt; i++ ) { val = this.frames[i].getDuration( ); frameData[0] = (byte)(val >> 8); frameData[1] = (byte)val; for( j = 2, y = 0; y < this.height; y++ ) for( x = 0; x < this.width; x++ ) for( c = 0; c < this.channels; c++, j++ ) frameData[j] = this.frames[i].getPixel( y, x, c ); file.write( frameData ); } //close file file.close( ); //success return true; } catch( IOException e ) { } //some error return false; } public boolean saveGif( String filename ) { try { //create GIF writer ImageWriter gifWr = null; { Iterator<ImageWriter> it = ImageIO.getImageWritersBySuffix( "gif" ); if ( ! it.hasNext( ) ) { return false; } gifWr = it.next(); } //begin writing image ImageOutputStream out = new FileImageOutputStream( new File( filename ) ); gifWr.setOutput( out ); gifWr.prepareWriteSequence( null ); //create buffered image with correct dimensions BufferedImage img = new BufferedImage( this.width, this.height, BufferedImage.TYPE_INT_ARGB ); //process all frames int cnt = this.getFrameCnt( ); for( int i = 0; i < cnt; i++ ) { //frame data into buffered image for( int y = 0; y < this.height; y++ ) for( int x = 0; x < this.width; x++ ) img.setRGB( x, y, this.frames[i].getColor( y, x ).getRGB( ) ); //get frame duration int duration = this.frames[i].getDuration( ); //construct metadata ImageWriteParam prm = gifWr.getDefaultWriteParam( ); ImageTypeSpecifier typeSpec = ImageTypeSpecifier.createFromBufferedImageType( img.getType( ) ); IIOMetadata meta = gifWr.getDefaultImageMetadata( typeSpec, prm ); String metaFmt = meta.getNativeMetadataFormatName( ); IIOMetadataNode metaRoot = (IIOMetadataNode)meta.getAsTree( metaFmt ); String nameGCE = "GraphicControlExtension"; IIOMetadataNode metaGCE = null; for( int n = 0; n < metaRoot.getLength(); n++ ) { Node node = metaRoot.item(n); if( node.getNodeName().compareToIgnoreCase(nameGCE) == 0 ) { metaGCE = (IIOMetadataNode)node; } } if( metaGCE == null ) { metaGCE = new IIOMetadataNode( nameGCE ); metaRoot.appendChild( metaGCE ); } metaGCE.setAttribute( "disposalMethod", "doNotDispose" ); metaGCE.setAttribute( "transparentColorFlag", "FALSE" ); metaGCE.setAttribute( "transparentColorIndex", "0" ); metaGCE.setAttribute( "delayTime", Integer.toString( (duration + 5) / 10 ) ); metaGCE.setAttribute( "userInputFlag", "FALSE" ); meta.setFromTree( metaFmt, metaRoot ); //write image gifWr.writeToSequence( new IIOImage( img, null, meta ), prm ); } //finalize image gifWr.endWriteSequence( ); out.close( ); return true; } catch( IOException e ) { } return false; } public boolean save( String filename ) { if( filename.endsWith( ".blm" ) ) return saveBlm( filename ); if( filename.endsWith( ".bmm" ) ) return saveBmm( filename ); if( filename.endsWith( ".bml" ) ) return saveBml( filename ); if( filename.endsWith( ".bbm" ) ) return saveBbm( filename ); if( filename.endsWith( ".gif" ) ) return saveGif( filename ); return false; } } //public class BlinkenMovie