trunk contents moved to root

This commit is contained in:
Jindra Petřík
2014-05-10 20:50:57 +02:00
parent 1b851e66a8
commit 199a4d0c2b
2296 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
-$A8
-$B-
-$C+
-$D+
-$E-
-$F-
-$G+
-$H+
-$I+
-$J-
-$K-
-$L+
-$M-
-$N+
-$O+
-$P+
-$Q-
-$R-
-$S-
-$T-
-$U-
-$V+
-$W-
-$X+
-$YD
-$Z1
-cg
-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
-H+
-W+
-M
-$M16384,1048576
-K$00400000
-E"..\..\lib\"
-LE"c:\program files (x86)\borland\delphi7\Projects\Bpl"
-LN"c:\program files (x86)\borland\delphi7\Projects\Bpl"
-w-UNSAFE_TYPE
-w-UNSAFE_CODE
-w-UNSAFE_CAST

View File

@@ -0,0 +1,138 @@
[FileVersion]
Version=7.0
[Compiler]
A=8
B=0
C=1
D=1
E=0
F=0
G=1
H=1
I=1
J=0
K=0
L=1
M=0
N=1
O=1
P=1
Q=0
R=0
S=0
T=0
U=0
V=1
W=0
X=1
Y=1
Z=1
ShowHints=1
ShowWarnings=1
UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
NamespacePrefix=
SymbolDeprecated=1
SymbolLibrary=1
SymbolPlatform=1
UnitLibrary=1
UnitPlatform=1
UnitDeprecated=1
HResultCompat=1
HidingMember=1
HiddenVirtual=1
Garbage=1
BoundsError=1
ZeroNilCompat=1
StringConstTruncated=1
ForLoopVarVarPar=1
TypedConstVarPar=1
AsgToTypedConst=1
CaseLabelRange=1
ForVariable=1
ConstructingAbstract=1
ComparisonFalse=1
ComparisonTrue=1
ComparingSignedUnsigned=1
CombiningSignedUnsigned=1
UnsupportedConstruct=1
FileOpen=1
FileOpenUnitSrc=1
BadGlobalSymbol=1
DuplicateConstructorDestructor=1
InvalidDirective=1
PackageNoLink=1
PackageThreadVar=1
ImplicitImport=1
HPPEMITIgnored=1
NoRetVal=1
UseBeforeDef=1
ForLoopVarUndef=1
UnitNameMismatch=1
NoCFGFileFound=1
MessageDirective=1
ImplicitVariants=1
UnicodeToLocale=1
LocaleToUnicode=1
ImagebaseMultiple=1
SuspiciousTypecast=1
PrivatePropAccessor=1
UnsafeType=0
UnsafeCode=0
UnsafeCast=0
[Linker]
MapFile=0
OutputObjs=0
ConsoleApp=1
DebugInfo=0
RemoteSymbols=0
MinStackSize=16384
MaxStackSize=1048576
ImageBase=4194304
ExeDescription=
[Directories]
OutputDir=..\..\lib\
UnitOutputDir=
PackageDLLOutputDir=
PackageDCPOutputDir=
SearchPath=
Packages=vcl;rtl;vclx;VclSmp;vclshlctrls
Conditionals=
DebugSourceDirs=
UsePackages=0
[Parameters]
RunParams=
HostApplication=
Launcher=
UseLauncher=0
DebugCWD=
[Version Info]
IncludeVerInfo=0
AutoIncBuild=0
MajorVer=1
MinorVer=0
Release=0
Build=0
Debug=0
PreRelease=0
Special=0
Private=0
DLL=0
Locale=1029
CodePage=1250
[Version Info Keys]
CompanyName=
FileDescription=
FileVersion=1.0.0.0
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=1.0.0.0
Comments=
[HistoryLists\hlUnitAliases]
Count=1
Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
[HistoryLists\hlOutputDirectorry]
Count=1
Item0=..\..\lib\

View File

@@ -0,0 +1,24 @@
program FlashPlayer;
uses
Forms,
Windows,
Dialogs,
uMain in 'uMain.pas' {frmMain};
{$R *.res}
begin
Application.Initialize;
Application.Title := 'FFDec Flash Player';
if(ParamCount<2) then
begin
ShowMessage('Wrong parameter count. This EXE is for FFDec internal use only.');
Application.Terminate;
end
else
begin
Application.CreateForm(TfrmMain, frmMain);
end;
Application.Run;
end.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,656 @@
object frmMain: TfrmMain
Left = 300
Top = 617
Width = 1381
Height = 811
Caption = 'FFDec Flash Player'
Color = clWhite
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -14
Font.Name = 'MS Sans Serif'
Font.Style = []
Icon.Data = {
0000010004000000000001000800EC310000460000003030000001000800A80E
0000323200002020000001000800A8080000DA40000010100000010008006805
00008249000089504E470D0A1A0A0000000D4948445200000100000001000806
0000005C72A866000031B34944415478DAED9D0B7C14D5F5C70F8F3C8090F008
041523521031451E159AF8A041FEB4F28F88429428A08912B51A54A80F94FEED
940AA258F081CADFA841010D1A10458A1695482B49E15F40298280881435BC02
84040328F33FBF95B50177E6DED99DDD99D9BDDFCF673EBBB0B39BBBB373CE3D
E7DC73CE6D448AA8E4FD8913F5FA8307E9686DADEF38C2CF8F1C3A4447F9F03D
E2FFF911C4B76C49F149499480473EF0989092E2FB3F1C89FC7CE0E4C98D9CFE
4E0AFB513FAA078170EFFDEC33DAC7C7DE2D5BE8787DBD23E3689C9848A95DBB
52DB6EDD28950FA524BC87FAC15CCC5B85853F0A7ADDAE5D4E0FC7122DD2D27E
540C571417ABFBCCA5A81FC645BC326488BE7DF9729F791E8DC09DE83460005D
B778B1BAEF5C82FA211CA474D8307D7B7939D557573B3D1447486CD3863A6567
53DEC285EA3E740875E123C8EB7979BE19DE6BE67CA480DB000BE1EAD252755F
460875A1C3CCCB8306E9DB962D737A189EA4F3A04174FDB265EA1E0D23EAE286
8137F2F3F58D656551EBCB471AC40EBAE7E6D255B367ABFBD566D405B5092CCD
7D3C670ED5ECD8E1D8185A356942CD1B35A284C68D299E1F7124F21177E27933
FEFF383E2F911F134E3C07C7F83872FC38D5F381E7DFF2E3515DA7637CD4F371
F4C481730EF3E381EFBF77EC3B26A7A753CFD1A3D592A34DA88B1822C59999FA
57959511FB7BCD5890539B36F51D6D59E053F9687BE27924D9C74A60DF77DFD1
5E3CF2B117CFF9F8961544A4382333930A2B2BD53D1C02EAE20501D6E7D7CD9E
4DC78F1D0BDBDFC0ACDD312E8E4E83B043D04F083D667437038BC1AF0CA01CBE
E1C79D7C9D8E85513134E6EBD42B3F5FE51B0481BA601658306A94BE61FEFCB0
083E04BE030BF8D9F1F1D4858F347E1EE77261B7C2177CCDBE3C7A94BEE0A38A
95423814021441C68811347CEEDCE8B97061465D2809E6E7E6FA827A767326DF
B0387E9690401DA34CE0CD80F0EF6425F0F99123B49D15C2D7FCDC6E10341C51
56161B173404D40532615E4E8EBE65C912DB3E0FFEFBCF1313A90B0B7C2716FC
581178115008DBD942D8CA0AE15FF5F5B6C611BAE6E4D0C8254BD48536405D98
00CC1E30C097B0630710F27359E07B356B4667C7C585FE813100DC8575DF7E4B
9B5821D8E52A20C1287FF97275BF9F82BA200D783E2B4BDF595161CB67C197EF
C1829FC133BE9AE98303C2BF812D82F5AC08103BB0838E595934A6A242FD2027
501782595450A0AF2B2909F973DAB31FDF8305BE271F498D1B3BFDB5A28ADAE3
C7E96328033E76DB1033E8555040579694C4FCFD1FF317606A9B367A28C53898
DD7BB1C0F766131F517C45F8C12AC25A7611D6B13208C5454031D284EAEA9896
8198FDF2CFF5EBA77FBD6A55D0EF87E0F765A1CF6CDE5CCDF60E01ABA0F2F061
5ACDCA201445707ABF7E74F3AA5531290B31F7A5971415E9AB67CE0CFAFD88E4
F765A187E0BB3D29275640F2914F11F011CA0A42DFA222CA993933A67ED498FA
B2D3D2D2F4604B7121F817B568E19BF55550CF9DC00A8035F0515D5DD08AA045
870E744F5555CCFCC031F1454BB2B3F52FCBCB837A2F0A6CFAF36CAFA2F9DE01
8A00F1810AB608822D5C3A2B3B9B0ACACBA3FE078FFA2F382931510FA669A65F
F0B17EAFF02EC8275811A42240D3D307EBEBA35A46A2F6CB059BBE8B593E8B05
FF223ED48C1F1DC022F88895002C82608285D19C561C955FEA892E5DF4FD5BB7
5A7E1F9277FEBB65CB8897D62A2203CA96FF72E850504945ADBB74A13BB76E8D
3A7989BA2F34292E4EB75AAD87001F041F7EBE22FA4176211481D54021AA0D1F
3C762CAA64266ABE0C3AEC6E5AB8D0F2FB10D51F80DD6F94B91F53C01578AFB6
D6B76A609573870D8B9A4EC651F1251EEFDC593FB06D9BA5F7A00C7730CFFA2A
7B2FB64156E1DB3535964B925B75EE4C776DDBE679F9F1FC17B06AF223B03798
677C15DD573404AB054BD922B012248C0697C0B3830F26CA7F3ACFF6D7A4A450
B20AF2290250F3FDF7F4DAC18396AD012FAF127872D0C1D4EBC3D787C9AF5088
587AE890E5D88057FB0D786EC0B37AF7D6ABD6AE953E1F11FEA1C9C9744E4282
D343577888CD478ED09B353596560A3AF4EE4DB7AE5DEB2999F2D460ADAEEF2B
935F110AC1B8045ECB17F0CC40A775E8A0D75555499F7F61F3E6F45F49494E0F
5B110560B970E5E1C3D2E77BA9A0C813839C9294A4CB6EB3A54C7E4538B0EA12
603BB3076A6B5D2F5FAE1FA095621EB4E41AD5AA956AD0A138097F1BF24DFCB8
95CDFAADFCFCE77CAF145A5C0A864BF00ABB04B22DC9BC504CE4EAC15959E347
1EFFD5ECEFAB8CBED8E4C0F1E3B48D8FCF5948716CE763CB89E7DFF071EA5D74
330BFFAC205685A04C4A5909C8D613B83D57C0B503D388A4C3AFDDD9DC87F02B
6283AF58D03F64015C81F6E13C1B6346AFB698D7FF5B56004F07B92C0C25B088
DD818DEC16C8A2B954D65C3928CD82F0ABF5FDE807FB047CC8B378F909A1DF6E
C3EEC4C15A000DB19A2FA0B950DE5C37202B667F768B16D49F0F4574B19167F4
0F21F438B09720CFF876138A05D09015757554CE870C6E74075C35182B01BF2B
F8C753F9FCD10384FE25FEED4BD9ACDE61C30C2FC20E0BC00FEA08DE626B4006
B705065D3310D9A53E14F3E4B2BFDF353EDEE9212B42640FCFEC10F812F4F80F
C306A166D86501F8C132E1829A1AA96222372D11BA6210B2493E58E3BFB6552B
EAA8F6D8F32C68E1BD98CDFAB93CDBBF83BDFF1C1A879D16809F9DECB2BC7AE0
8054AE805B92851C1F806C7A2F847F74EBD6AA7EDFA354B070C0C47F958F4336
EEFEDB90389E183A75EAE43BBA76ED4AEBD6ADA3952B57063CD76E0BC00FFA0B
CCD9BF5F4A09B8216DD8D13F2E5BD803B37FB49AF93DC7376CE2CF62F37E0E0B
BD1D91FB86B464E1CDCCCCA45FFDEA57949595455D5898CE3CF34C6AD4200F64
C28409F4C8238F047C7F382C003FB004E6B02520E30E385D40E4D81FB652D29B
C73EBF4AEDF50E58A79F525747B359F043D9A9A721EDDAB5F3097A7676365D7C
F1C5D4B3674F8A17C481CC1440B82C003F880920614806274B891DF9A3569A79
A868BF77B053F021F003070EA44B2EB9C427F4DDBB773F697697C1290BC08F95
D501A79A8A38A20034C9441FB5CEEF0DFC82FF3C36E90CE173E0C30F1932846E
B8E106BAECB2CB8433BC08272D003F56F2043407E431E27F5036D14765F8B91F
44F435BEB96760C38D103EA75FBF7E3EA1CFCBCBA3366DDAD8363EA72D003FB2
19834EE40844F48FC976EF55B9FDEE6711FBB877D5D6069DB4939E9E4E23478E
A4FCFC7C3AE79C73C232463758007E5E3F7850AA7620D2DD8623F68764FBF6A3
AA0F413FB52D973B415E7E11CFFA4B83D85D0791FB2BAFBCD227F403060CB0EC
D35BC52D16801FAC0CC854114672DF8188499926E1F7A39EFFA6D6AD95F0BB10
98FBD3D9D49FCC87D5001F027877DF7DB7CFC46FDEBC79C4C6EC260B00E01ACE
DEBF5FAA9F801621D98CC81F91F1FB91E8730BFB7FAA7F9FFB409EFE556CC26E
B668EE43F0274E9C48D75D775DD867FB40B8CD0200682AF2BFD5D542251AA9C2
A1B0FF01D94C3FA4F8AAFC7EF7F14A7D3D155ADC47CF69C1F7E3360BC0CF1676
0390322C2212998261FD70D9F57ED5C0D37D208B6D6C6D2D3D67A1DEFDB4D34E
A369D3A6392EF87EDC6801F8916D341AEEFC80B0FE4A9A84DF8F3DFA0AD8EF57
B80704FA86F3AC2F5BA187F5FB5B6FBD951E7AE8214A4E4E767AF83FE2560BC0
CFF3EC0AC8B41CD7C228A761FB6099DA7EE5F7BB0F2CEFDD5053235DB08335FC
679F7D96FAF4E9E3F4D07F829B2D00201D0F08637E40583EB4243B5BFFB2BC5C
789ECAF17717C8E6FBBD64D65A6BB6DA205C63C68C7185B91F08B75B0040B666
E0ACEC6C2A282FB7FD4287E597D3244C7FE5F7BB8B89EC933E2CB9F945FFFEFD
A9B4B4D4E7F3BB19B75B007E64E3015A18E4D5F60F9C9696A6D7EDDA657A0EB6
EC1A6363CAA722347EC737E00C49E1BFEFBEFBE8E1871F76EDACDF102F58007E
64E2012DD2D2E89E5DBB6CBDF0B67ED892A2227DF5CC99A6E720C9E776E5F7BB
863B0F1DA2A72422FD30F9E7CD9B4783070F767AC8D278C5020088073CCD4A40
D443A06F5111E5CC9C699BDCDAAA003409D35F95F7BA87DB59F89F95107E04FA
5E7FFD755FFEBE97F092050064CB87351BE5D6B60F7AAE5F3FFDEB55AB4CCF51
A6BF7BB8956F349935FEC2C2429AC9565DA8A5B94EE0250BC04FC9FEFDF46F41
D6ECE9AC906F5EB5CA16D9B54D016812B3FFCD2CFCAAA79FF3C8CEFCE3C68DA3
E9D3A73B3DDCA0F19A0500D053F0397605446836C9AE2D1F32B54D1BBD5E3068
55DFEF0ED09F0FEBFC22EEBFFF7E9A32658AD3C30D092F5A0040A67F40224FA6
13AAAB4396DF903F60514181BEAEA4C4F41C24FCDC959AAAAAFC1C660DCF2E97
4874AC45461F72F9BD8E172D008040E0E37BF70A7FA75E050574654949484215
B2446A12A6FFF0E464CA484CB4FB3A292C80DD73FBB0F08BBAF3C2E487E91F0D
78D502009B8E1CA1D7241284B4106538A4373F9F95A5EFACA8303D070D3ED0D2
5BE12C39070E089B783CF1C41374C71D77383D54DBF0AA05E047A68148C7AC2C
1A535111B41C87A40034C1EC0F931F81BFB66ACDDF51FE5057477F12A4F8DE76
DB6DF4F4D34F3B3D545BF1B20500F6B3B5364B2237400B418E837EA34C5F7F74
F4CD565D7D1DE56D3625AF109892175E78212DE7DFD28B4B7D6678DD0200E828
BC42A0BC43D957206805A00966FF563CEBFF96677F15F8730EF8FDDD7806D963
B2BD36F2F9FFF9CF7FBA3EAF3F18BC6E0100CCFECFF26F784010BBD18294E5A0
DE342F2747DFB26489E9392AE3CF794439FEA8E3FFF0C30F7D3BEE4423D16001
00990CC1AE39393472C912CBF21C9402D02466FF3BDAB68DE435529C02FAF85D
2058F283CF0FDF3F1A387AF428FDFDEF7FA7F2F272DAB06103AD5FBF9EB66CD9
6278BE572C003F4FEEDB17162BC0F21B64DA7C21E1A7AF9AFD1D4514F52F2828
A0175F7CD1E96186C461B66EDE7BEF3D5AB46891EFD8CF0A4F162F5900008941
4B05564030EDC32C2B004D30FB23E9E79E76ED9CB8468A132C63C1FF8D49D349
F8FB5BB76E8D688B6EBBD0D9A259BC7831BDF4D24BB46CD9323A24B9F7DEA978
CD0200D3F6EC112607691665DAD2C90B468DD2D7CF9D6B7A0E9A7C5CE8C11B2B
9AF87975357D6A525B3E977F43ECCAE32520F8A848449622CCFB50F19A0500D0
3404CD43CCE8316A140D9F3B575AAE2D2900517F7F95F2EB3CA8EDBFD36456C4
921F7C652F34F40010FC3973E6D0D4A95369E3C68DB67DAE172D00991461ABFB
09489FF85661A1BEA6B8D8F41CB5EEEF2C58F6EBB26F1F551BDC20717C7CB46A
15F5EDDBD7E9A14AF1C1071FF8BA0D9B05F382C58B160090C90BE85358485714
174BC9B6B40210CDFE98F5C7F1EC9FE89199251A1165FC7925F0575353E3AB46
7CE6996742FE2C28BDB39B34A1F39B36A55E7C9CC74737FE77778F96A5637BB1
196C051CB3C90A9096564D10FC534D3E9DA713CFFE46BBF562634E04FEDAB76F
EFF4304DF9EB5FFF4AB7DC720B6DDFBE3DA8F7A7B3700F8D8FA70B58083220F0
FCEF689B94649A886A92B22D75527166A6FE5565A5E1EB98FDC7B66D4B498D1B
3B7D6D62162CF9E59844FE911073EFBDF73A3D4C43E0EB1715150535EB63561F
9E904043F9E8E3D199DD0A3256C019999954585929946F2905A009667FD5ECC3
79AE397890CA0CF69FC7ECBF73E74E57EDDAD31098FCB9B9B9BE653D5960DA8F
4A4CA4BBD9F2F4AA391F0A324D433409F9169EF0FEC489FADF264F363D47B5FA
7216E4FA77C48C60F0BA9B7DFF6DDBB6D1E5975F2E1DE187E08FE109E76E16FE
B3E3E29C1EBE63C8B40EBB64E2441A3879B2A98C0B15C0F4F474BD66C70EC3D7
DBB3E0DFAA1A7D3ACA34F607EF33591F5EB972A52BF3FD57AF5E4D393939B467
CF1EA9F30B58E8272525D119CAD5F48152E1DD26F91EC9E9E9347EC78ED01480
2630FF07F20F72914AFC719473F7EDA3CD06C1BF1E3D7AD0279F7CE2F4107F02
847FE0C08152997C1D58E05F60F7657094952B878A4C6290269071D317DFC8CF
D73F9E3DDBF40F8C4F4D55C13F07A938768C2E32C9817763971F28A4ECEC6CA9
DC7DCCFA7FE649A695BAC77E422DBB7ED3D9F533A3677E3E5D357BB6A19C9B2A
80294949FA51130DA3DA7D39CF4D35355462B00B7333F69511FC6BE322170D3E
FFC5175F4CDF7CF38DE979C82A9DCDB3FED56AF35853446DC3E259793E505B1B
9C02D004E6BFAAF9779E54F69F8D32FFF2F2F2E8D5575F757A883F02A1472AB2
688DBF1DCFF66FA7A450DF180EF2C922D32B40339173C3175E1E3448DF66B22C
83B5FFBB55DEBFA3A0E63FC32412FCFEFBEFD3A5975EEAF4307D609D1F3EFF72
411BB99F376D4A4BD9AA54813E39900BF0982027A0F3A04174FDB2650105D550
7A35C1ECDF837DB3AB5CBAAE1C2B14B3F6BFC540FBB76BD78E76EFDEEDF4107F
64C68C19347EFC78D37390D0F321CFFCAA89AC35DE603770BD811BE8473390F5
A015C0B5ACA5BBAAA8ACA398F9FF575C7105BDF9E69B4E0FD107827E175C7001
1D33A925410A6F45EBD6749A9AF92DF3055FD7398280AA664501BC9E97A76F28
2D35FC30D5F4C31D982DFFB925F517ADBA20FC6635FCF0F92B79E68FE5C49E50
11350BC9C8CBA3AB4B4B7F22EF0115C0B4B434BD6ED72EC30F53A9BFCE83D2DF
36264B4068F6D9BF7F7FA787E933FB61FE1B81C6A4157C2FC5420E7F38111508
B5E8D081EEA9AA9253009AC0FC1FCDA69AD2D6CE6256FC03A13AC0AF39DDF20B
F5FC08FC998135FE712A912C64B6F0FDF0AA493118D002C87B500AE07E36FF55
F4DF59CC6AFFFBF4E9E3EBF5EF24D5D5D5D4BB776FDA6192463E283E9EDE5579
24B620B31AA0C92880D261C3F44D0B171A7EC8993CBB14B005A0709681ACED97
1B24808C1D3B969E7CF24947C777EDB5D752A9491C097EFFBA366D54D0CF464A
F6EFA77F9B045ACF1D368CF2162E3C49E67FA200A6B669A3D79BAC2DABC61FEE
A08549D0C7E9A69F6BD6ACA15FFCE217A6E7CC4F4951597E36236A1796C80A77
4275B5B902D094FFEF7A4409405F7EF925A5A7A73B36BEA14387D25B6FBD65F8
3AF2FB5F503924B613CC72A02505A0B2FFDCC12BF5F534AAA626E06BE8F9FFF5
D75F3B3636ACF9F7ECD9D3F0F54E4D9AD0273C89A80232FB09260E70D23F5E19
3244DFBC78B1E19B95FFEF0E26D4D6D2A3064B3EC3D8CF5BB0608163631B3E7C
382D3489217DC8F7CF25CA820C1BA238C0394386D0758B17FF28F727290051F5
9F6AFBED0ECC0280D83863E2C4898E8CEBD34F3FA58C8C0CC3D755D43FFC88E2
00A756079EA4003481FF7F236BEF8E4A7B3B8E5905E05FFEF2171A3C78B023E3
1245FED5EC1F7EACC601A41580F2FFDD017EE09F99FCC0F0FF1107883498FD7B
F5EA6598EF0FC1FF50B98F11E1619E2064E3003F3E11EDFCA39A7FB88345478E
D0B0830703BEE66405E0F5D75FEFDBC2CB88F7F9DE19A08AC72282A84948C39D
837E54002FF6EFAFEF58B1C2F04D6AFDDF1D986500C2F4870B1069D0D63B95AD
43A3D93F8B67FF8FD4EC1F31447180F4FEFDE9C6152B4E5600D33A74D0EBAAAA
0CDFA4BAFFB803E4FF2F35D0EED84E6BCA9429111F135A8EDF74D34D86AF2FE1
D95F35F48C1CA22E412DD2D2E89E5DBB4E56009A0A007A82D3F7EEA5AAE3C703
BE367FFE7CBAE69A6B223E26741D32EAF483FDF8D6B8A827612C20B367807642
F6A515C0BDEC5F46DB1E6B5E031B80A49994007FFEF9E7D4B973E7888E09C53E
679D7596E1EB4FB0E53856598E11055B873D2AD86B41B3A20054031077605602
8CEDBF0E1E3C488D22ACA41F7DF451BAEFBEFB02BE067B717B6AAA2AF8710051
8310ADA102106DFFA53200DDC1D4C38791C411F03534FF4013904873FEF9E71B
76FB51893FCE21CA08F46F1BE65300F37373F58D65658627F766136E88EA00E4
38B93CC32F34D80074DCB871347DFAF4888E4794F9F7627232E5272646744C8A
1F587CE810AD35D93CB47B6E2E8D282BFB41013CD3A387BEDBA4671B96FF2E54
5D5B1CA7F3BE7DB4DDA007E04B2FBDE45B8B8F24D8CAFBF6DB6F0FF81ADCC65D
6ACB78C7106D1BD6BE470FBA6DFDFA1F14C0A4C444FDB8495B61D501D879443D
003FFEF8639F391E49468C1841AFBDF65AC0D786252450594A4A44C7A3F80FA2
16618DD9327BB0BEFE0705A00956006E674DAE7AB53BCBDFD89FFB95410A30B6
00430FC0F8082BE9D34F3FDD708BAF67D865BCD562F41FD1EB437CF8B31C603B
A0552882895881C21DA852D1E5D8C796E2D36C319AA111C9298007DBB777FAFB
C43C33D8A4FB9D8149E7440FC0CD9B3753B76EDD0C5F47F61F76F769CEB7D861
16EA3A3E0EF081C834FE5DC3079C995A7EFC8E8F63F27F9AFCD9284DF9B3FD2A
269E9FE380D2883FF1EFF1ECB6C6720C6292202D5C935100AD78E6BF832D0085
B3E4D7D4D0CB066E5A6161213DF7DC73111D4F717131DD7CF3CD4E5F1653A624
25D184188E5D3DC916C001839811D06414C0E94D9BD21895C9E5384800DA6390
01F8F4D34FD36DB7DD16D1F1888A7FDC40AC2B80E7ABABE9EBEFBE337C5D9351
00AA0AD0792AD8FFBFC8A40418E63FDC804872F6D9670B77F9759A585700A2AA
400D0A409404D42D218146A868AEA34C64DFFF618316604EF40044E00F0140B7
13EB0A60FEC183F49941DE08403250A3254545FAEA99330D4F524940CE733E9B
72FF3230E59CF0FFE7CD9B47A3468D72FAB20889750520DA35B86F5111357A23
3F5FFF78F66CE393D43E808E22EA00841D80B113702441F20F9280DC4EAC2B80
A5870ED16A936CC09EF9F9D4489406AC1A813ACB53FC03DE6950DB8D02A0AAAA
AA88EF01880620C83BA8E7D9A596DD131C757575F42D8F15C7617657F088D771
24262652120B63C303FF87FC85047631F11CDFA5499326BED7F0FF286AC2CEC2
DFB1E5E33F8EB0398BA623FE7FE3B3274C98404B972E0D38CE585700A20D4391
0EDCE8E54183F46DCB96199E04E1EFAF148063987500C6CC0F0B2096316B441A
EB0AE02316FEF74DD2813B0F1A448D9ECFCAD2775654189E04F3BFAFAAE77684
0327EAFF8D92645E78E105BAF1C61B9D1EA6A398A523C7BA0280F9BFD4A433D0
199999D4E8E98C0C7DCF860D8627A95660CE61B6031070AA03B09B501680311B
F8FE596072FFB4CBC8A046D3D3D3F51A932D9CAF4949A173D5268E8E702DFF78
F30DA2B8FDFAF5A37FFCE31F4E0FD17194023066D39123F49A410769909C9E4E
8D44BB01ABCD409D017DDD53D9FC3F64D0D5C5C91D80DC845200C6883609C16E
C18D26C5C5E9C74D3A87DCCC277568DAD4E9EF127320F037D0A49CD389F25F37
A21480313B59AE5F3451008D7962172A00D50DD819B0F4F794C11A6EA74E9DE8
8B2FBE707A88AE4029006344DD817D0A40B900EEC4ACFBCFD8B163E9C9279F74
7A88AE40AD021823E502A820A0FB40DAEFF9264AF9DD77DFA55FFFFAD74E0FD3
15280BC018A920A068197078723265C47053052740E38F1906195CC898DBBB77
6FC4BBFFB815A5008C11ED10E45B062CCECCD4BFAAAC343C4925024516B4C54A
3389FE63E71FEC00A4F801A5008C914A0412A5020FE48B78510C5FC44853CC3F
DA2D263F1A36FFC426A08A1F50310063A4528145C5406A57E0C8D2877DFF7506
A5BF88FE6FDBB62DE2BBFFB81965011823550CA4CA81DD83A8F3CF238F3C42F7
DE7BAFD3C374154A011823550EAC1A82B887913535F4AA41EA6F5C5C9CAFF4B7
8DEACF78124A011823D51044B50473071BD9ECEFC5E6BF514AD6E8D1A3E9E597
5F767A98AE43C5008C916A0986279A6A0AEA3866B33F58B97225656565393D4C
D7A12C0063A49A829E78A2DA823B8868F67762E30FAFA0148031B6B405571B83
841FD1EC8F757FACFF2B7E8A5200C6D8B23108505B83850FCCFE192669BF3D7A
F4F055FEA9A5BFC0280560CCC37BF6F8CACA8DD0641580DA1C347CE4B09FB6D4
C44F5BB060010D1B36CCE961BA16A5000263697350D1F6E0792929748E2A08B2
9D45478ED03093620DE5FB8B510A20309BF9DE2A35B9B74EDA1EFC991E3DF4DD
EBD71B9E8C4CC00B63F442860BE4FC9FCBA6FF0E131FCD899EFF5E432980C020
03F03D9334E0F6EC5ADEB67EFD0F0A40940EAC9281EC6702FF388F9AA469AA9E
7F7228051098C5870ED15A932C40A4018F282BFB4101889281CE8C8BA382D6AD
9DFE4E518368D90F38B1E1A717510A203025FBF7D3BF4D3A7D210968E0E4C9FF
092D6B2681C0667CDA3DEDDA39FD9DA20244652F3970805699FC38D8EA1B5B7E
2BC4A84CC0C04CDBB387BE15AC00E0514A01807B590124AAA5A890316BF601D0
E77FD3A64D949C9CECF4503D81B2007E0AE24B8FB2023043B3AA005477E0D079
FBC811BAC224320BE6CE9D4B23478E74749C2B56ACF0EDBBF7FDF7DFFBF6EB6B
F8084EFDBF501EC1A99FDFF06F3424D0FB27B3EB8AF10622561580A81B30D04E
5500D3D2D2F4BA5DBB0CDFA076080A8DAF8E1FA79EFBF651B589593660C000FA
E0830F1C1D2736FE4C4D4DF56DC2E975625501885A81B5484BA37B76ED3A5901
BCD8BFBFBEC3409302B54B70F0608FBF4BD9EF5F6792978D5E7FEBD6ADA3CE9D
3B3B3A56F8D3F0ABA381585500E57575B4820F23D2FBF7A71B57AC385901BC55
58A8AF292E367C93AA0A0C8E5A16FEFE02E1076EC9F7C766A32525254E0FC316
62550188AA00FB1416D215C5C5272B00A099C401E21A35A2FBD54A802520FCFF
C53EFF2A81395DC83FC873CF3DE7F4707DB46FDF9EF60802485E211615005699
1EC38ED2122B00405A0100B54B903C88C4FE8635F1DF04C28F629FCACA4A6AEE
821B75F5EAD5BE04A468211615809500203849018876095271003920FC393CF3
2F3731C300FC7E08FF79E79DE7F4907D4C9A3489FEF0873F383D0CDB88450520
F2FFB11BD084EAEAC00AE0952143F4CD8B171BBE5965048A81E975250BFF5281
F0376BD68CDE79E71DEADFBFBFD343FE915FFEF297B46AD5AA80AFB5611730B5
7163A7876889DFB1F017C6D8CA952803F09C2143E8BAC58B032B00A009E20077
A7A6FA1E153F05C29F5B53438B4DFAB00108FFDB6FBF4D975E7AA9D343FE91DD
BB7753C78E1D0D97FFE6A7A4D0D5AA22D4D558F5FF81250500D466A181C152DF
0D870E4909FFA2458B5CB7B71F1A8EDE70C30D015FC3AFBD8B157F2B8F5900B1
86683350A08914808A035807FDFCAFE599DFACB417A0B5775959992B4B7CCD52
6A07C4C7D3FB6A09D8F588360239D5FF073F5100A5C386E99B162E34FC101507
3899297575F4473E447973107E08981BBBFB1CE69B06E6FF7E83D9E391A424BA
27C682695E44E4FF9FCBF75EDEC285E60A0068023700F900B11E07406AEFF53C
EB8B22FD00C28F1C7F3724FA04E2C9279FA43BEFBCD3F0F50D3C7374577520AE
067EFFC39205400D094A015CCBE660D718DE9E1A453D37B1BFBF87958008083F
32EB9C2EF031E2282BB06EDDBAD1F6EDDB03BEDEA94913DAA6BA42BB9E60FC7F
1050014CEBD041AFABAA32FCA058DD3014EBFBF7B19FF59449A7958640F867CD
9AE54BAF752B66C13FF03F2D5AD01F55CCC7F588F6016C5800D490800AE0F5BC
3C7D83414008C46283106CDBFD07F6F5AB24667D809D7C5F79E51557EFE6A3B3
42CBC8C8A08D1B37067C1DBFF30E36FF554768F723DA0320232F8FAE2E2D9553
004053CB813ED0B917FDFB360B22FC0D81AF5F5C5CECFAA61E6FBDF5160D1D3A
D4F0F5716CE9FD39062D3DAF11ACF90F0C15C0A4B838FDB84944B14762225DE5
F21B3C1490C30FC1AFB050178F35FE993367BADAE46F8859E61F54FBB6D4543A
43ADFDBB1ED12EC040B3AA005E1E3448DFB66C99E107466B56E0BFBEFBCEE7E7
8B52794F05453D58E6734B5EBF08341E193870A0E1EB05ACE05F8862051F2DC8
64FF751E3488AE5FB6CC9A02009AC00D88A62E4130A3FEC87E3EF6E8B3DA0B67
ECD8B1F4D8638F51BC47564610F9EFD5AB97A1EF0FD4D29F371075FF019A899C
9B2A80294949FA5193CD05A2A14908D6F11FE78B284AE10D44EBD6AD69F6ECD9
AECCEC33E381071EA0871F7ED8F0F5DC84047A2D25C5E9612A241035FF884F4A
A2076A6B8353006FE4E7EB1FF30D6EC6787603923CE62762396F0ECFF458CEFB
97A0534F20B0BC57545444BFFFFDEFA98DC7B64E5FB3660D6566669AF6FCFB3F
FE4E7DD4ECEF7AD070663A9BFF66F42A28A02B4B4A8253004013B8015EDA360C
D97B4F1D3E4C252CFC32493CA702C1CFCBCB234DD31CEFDD170C30FD2FB8E002
5A6FB20DDC109EFDDF54B3BF27106DFF0534818C0B15C0F4F474BD66C70EC3D7
DBF34C71ABCB674144F2D18BFF4D36F383ED753B78F0609A3A752A9D7FFEF94E
7F9DA079F0C107E94F7FFA93E1EB2D1B35A24D6DDBD2691EB3E8629559D5D5B4
DBC4824D4E4FA7F13B7684A60044DB8601B7EE198035FC497575C2869C66A045
16FC6537D5EE07838CE9FF627232E527263A3D548504557C4F3F6752B50BFCDB
7F999D23B586A709DC80BECD9AD160176E1E3AF4E0C1A0827BA06BD7AEBE19DF
8DD57B5641B51F84DFCCF41F141F4FEF7A3CA01B4B88527F812621DF520AA038
3353FFAAB2D2F075E4028C4B4D75DDD661B9AC00165A540068D185CAB8ABAEBA
8A1AB9ECFB0403D27D73727268E9D2A586E7C0F4FF944D7F95F4E30D10C49E21
58FB3F83157E6165A53D0A0068022BC08D0542B2160032F810DCBBEBAEBB3CED
E307E28E3BEEA0A79E7ACAF49C67D87ABB354AF239620151E30FA049CAB6B402
989498A81F37493774A315205200D888F3F6DB6FF7F5E5473FFC6863C68C1934
7EFC78D37354B71F6F2133FB378E8BA3078F1DB3570188760E026815D6DF45A5
A3660A005B704340BC92BD671551A10F80E9BF8E853F168ABAA205B4FC2E3769
FB0D1AEEFC23C2D2742D2A1042F9E85D2EAA0F3053004F3CF184CF3C8E4610F1
CFCECEA6432629A210F97758F80744A9028C4630EB3FCEB3FFB736CDFEC092A4
2E18354A5F3F77AEE9396E4A0C325300D3A74FA771E3C6393D44DBD9BC79B34F
F8BFF9E61BD3F3D4929FF79049FCE9316A140D9F3B373C0A00C858016E691662
B60A108D0A0033FF65975D26DCDB4F75F9F126D3F877359BFD816651A62D2B80
F9B9B9FAC6B232D373DC5225184B2EC08A152BE8F2CB2F3735FBC1F53CEBCF56
65BE9E036BFE4B05BF6DF7DC5C1A5156165E050034C19260AB264DE80E173492
8C150580801F9631BF1524865CC2FEE17BECF7BB2546A39047D4F20B6841C873
5077C2BC9C1C7DCB9225A6E7B8C10A888518C0BC79F3A8A0A0C034C5179CC34A
B9B2756BB5BB8F0791A9F93F75CF3F59829E0A34092BE0B76DDA383ADB44BB02
78F4D147E9BEFBEE139E87D6DEEFB3D9AF96FBBC0722FFCF56578765F607414B
E7EC0103F4EDCB979B9EE3F43662D1EA02D4D4D4F866FD85263B38F939AF6953
5F8EBF4AF3F526A2EDBE41A70103287FF9F2C82A00A009AC00CCFEBF6DDBD631
B3D36C15C0AB0AE0934F3EA1DCDC5CDAB2658BF0DC7E3CE3BF9392A2CC7E8FB2
8F677D54FC1DB339F2DF909014C0F35959FACE8A0AD3739C6C1B166D2EC08B2F
BEE8EB44240AF60124F8BCC966BFD7BA3529FE83A8DD17E8989545632A2A9C51
00401358016038DF88190E249D448B0B80725E083EB61893015D7DCAF89AAB68
BF77D9505F4F0BD8D513A18528C321DF218B0A0AF475821B130141340D8974A1
503458009F7EFAA96F89CFAC96BF21AA9DB7F79149F905A27E7F32D8229153DB
B4D1EB05DD499C2817F6B20580FE7D0F3DF490AF298968890F20BEFF47BEBE13
5C9286AD081E9972DF449E50275457872CBFB64DC99A842B10E9D6615EB5002A
2A2AE8A69B6E32EDDBDF900EECE7CF4F49F125FA28BC8D4CAB2FA0D924BBB629
0051D7207026DFA005AD5BDBF5278578AD1600CB7B68352E6AE0D11004FB4AD9
E46FA7827D5141C9FEFDF46F81C527DBED47065B9D7251A110886486A0975C00
A4F36287A11D261D984F4515F5441732197F40B3516E6D55004B8A8AF4D53367
9A9E83C8F4EDEC0A244760CB692FB800AB57AFF665F32D1724553504B33D0A7A
06AB5AFEA8A1E6FBEFE9698935FFBE4545943373A63B1500989696A6D7EDDA65
7A4EA45C01375B00DBB66DA3FBEFBF9F5E7BED354BEF43F75ED4F2ABCCBEE8E2
7916FEAF05EDEB5BA4A5D13DBB76D92AB3615997D3240282915815709B024087
DEFDECE36167A159B3664945F7FD60D67F8CAFD768D5C423EA9089FA032D0CF2
1A160550929DAD7F595E2E3CEFDA56ADA86B18CD5837B900E8D083E0DE4C7691
4435FB3FB94E2CF48FB3F0AB405FF4B199EFCF52BE4F458492EF6F46D8327344
5D8441B313B502E14A5775C32A0072F7B175786969A9A5191FA4376942CFB66C
A97CFD28057EFFFFB2E92F4AF869CC13C083F5F56191D5B0A6E66912AEC0E94D
9BD29830ED2DE8940B0053FF9D77DEA169D3A6590AEEF9C16AFE9866CDE8CF3C
EBBBA9CDBAC25E64FC7EA085514EC37A77C9B40F03E18A07445A0120671F0D3A
D06E5C3689E75490C73FB5450BEAEEC2BD1615F621EBF767E4E5D1D5A5A5DE54
00E0892E5DF4FD5BB70ACF0B473C20523180DDBB77FB7C7B04F6440D398DE8C5
02FF049BFB2A9B2FFAD972F428BD7AE080F0BCD65DBAD09D5BB786554623625F
CAC6036EB1393F20DC0A00FEFDE38F3FEEF3EF654A7403816E3D0FF18C7F9D8A
EEC704D27EBFC5FEFEC112310753938807B4E759F0A6D6AD6D2B630D870B80F5
FB575F7D95E6CF9F2F5DA11708E4EF8F63D7E72EF6F555D96E6C80249F17F6EF
A7DD0EFBFD0D89D89D573A6C98BE49A285959D0D44ECEA088425BC3973E6D082
050B68D5AA55218DE9E7ACE4C6B3E08F645F5F097EEC00E1C7729FA8C1073877
D830CA5BB830BA140078BC7367FD00CFA022BAB3705C9D9212F2DF0BC50580D0
BFF1C61BBEA0DECA952B431E0B32F8EE66C11FA496F46292D7F95EDC28B15375
ABCE9DE9AE6DDB222697119F8264E201A02F9BC6835BB60CE96F59550028C479
F7DD777D267E30CB77A782705E017F8F3BF95051FDD8051B7AAC9688118573BD
DF08476C504D221E0042DD6D58140340A79DF2F2727AEFBDF768D9B265B47DFB
765BBE1F32F66E65A12FE24365EFC53632BBF9FAD11C90474714C0EB7979FA86
D252A97343291F365300AD5BB7F6E5E5DB095A7063B647BEBE4AE051C896F782
70AFF71BE1D85D2AB3AF809FBC94143A2721C1F2DF3053007681E5CB1CF6EB61
EAAB945D851FD91C7F10AE3C7F191C9DA666F5EEAD57AD5D2B3C0FD172AC0C74
B49824132E0580515CC60A69041F57B0D0ABD6DB8A86EC3C76CCD7D25B54DB0F
3AF4EE4DB7AE5DEB981C3A6EA7CA660A62A61DCD66BB959E82762A0008FDC52C
EC7958A1E0436DB6A108047AFACD61D75294E8032291E927C2710500A675E8A0
D7555509CF8312B8D68225609607200B76D719C93E3D045F05F4146660E6478A
AF8CF0B7E8D081EEA9AA725CFE1C1F809F294949FAD1DA5AE1797007B0D1884C
4C20180BA0257F7E36CFF4D92CF8D7B0E0ABCE3B0A1990DF5FC6F79B8CD96F57
4B6F3B70C520FCC8E6080099D5011905005B229385FD372CF4E8B09BA58A7114
16B112ED7762ADDF0CD70CC48F4C67613FA23C01230580CABB5FB3B00FE403D5
776AC94E112C56D6F92355E06305570DC68F26992804CC3206FD3100E4DF0FE0
8B3FE08469AF02780A3B90CDF0F3A3B950DE5C37203F9A052560543BB0E6BBEF
E84C167615BC53D88D6C6EBF1FCDA5B2E6CA41F9B1E20EA08A100943AAC24E11
4EEA75DD27FC32557DC08D667F435C3B303F560283E827701D2B81486C3AA288
3D6A8F1FA7B9070E48D5F303B705FC02E1EAC1F991D97DD80F7205AE642510CE
76E38AD803A9BD6FD6D448ADF103372DF599E1FA01FA914D16F2E3C476E48AE8
44B681A71FB724F9C8E08941FA914D1BF6832DC89034A45C024530A07FDF029E
F545BBF536C40DE9BD56F0CC40FDC81610F9814B3054327350A1F083CCBE4507
0F4A9BFCC0E9C29E60F0D460FD582925F663478721456C60D5E4074E96F48682
E706ECC74A53113F7009AE4A495189408A80C0E47F8D677D99DD7A1AE254330F
3BF0E4A01B62255700204F60705252D05D8614D109F2F997F2CC2F53CCE3C7ED
6BFC32787AF07E64BB0D3704D6005C022BFD0514D107EAF791D26B25D00722DD
BD375C78FE0BF891DD77E054101BC072A1CA208C2D30D3C3D7B792CBEF27927D
FBC34D547C898658750900560AFE9BAD810CB53D574CB0A1BE9EFEC2B3BE9508
3FF042669F55A2EACBF8B19A2FE007F5044392935590304AD9F7FDF7BE59FFB3
20BA44796D7D5F96A8FB427E64B7263F15B80259CD9BD3457C28B7203A80B9FF
D1E1C354C18795209F9FEEB9B934A2AC2C2A6F86A8FC520DB1524CD490564D9A
D0C0162D945BE07110DD5FC1827F80677FAB44A3C97F2A51FDE5FC946467EB5F
969707F55E280258047DD5B2A1A74070AF2248C1075E4DECB14AD47FC1864C4B
4BD3EB76ED0AEABD500417B012E8ABB6F3762D30EF21F8FFC747B082EFA5421E
3B88992FEA67495191BE7AE6CCA0DF8F1583BE6C1164F2A17A09BA0334E958C5
B3FD3FF8B01AD96F48DFA222CA993933A67ED498FAB20D29CECCD4BFAAAC0CFA
FDB002600D5CDCA28552040E01C1FF7B5D9D6FD60F26B8E7E7F47EFDE8E655AB
62F2478CC92FDD102BCD46020145D02B31917AB3325059859101D97B6B59E8D7
D5D78724F85E69DA114E62FACBFB595450A0AF2B2909F973D092EC7C560638D4
7E81F682765C9FB0C0E3906DC96546AF8202BAB2A424E6EFFF98BF000D793E2B
4BDF595161CB6721A9A84742826F1951050D8303B33B5A71AD61A1976DC229A2
6356168DA9A8503FC809D485084030FD068C80F09FCB8A00D58767AB5D87A440
338E7FB1D06F62E10FC5C46F48AC2CEB59455D10135E193244DFBC78B16D9F87
1504C40ACE62EBA0132B036519FC00847CFBB163B495051EC21FEC125E20BAE6
E4D0C8254BD48536405D18099056FCD99B6F92D52223112849C6F133B6103A36
6D1A330A0102BF93FDF8CF59E0B7B3C0EF6181B76BA607A8D3EF367468D4A6EF
DA89BA401658306A94BE61FE7CDB150180F0631501B1832E7CA4459942F882AF
D9972CECF0E511C5B753E0FD40F033468CA0E173E746CF850B33EA4205C15B85
85FABA397328981A035920FC1DF9863E8D15018E56AC14DAF2FFB93DE7006BF3
30E1AB58E0F7F2E3372CEC3BF9793804DE0F04BF577E3E5D515CECEE8BE342D4
050B9150138AAC8238422A2B041C6D9B34A176FC8823D225CC28ADDDC7C20D21
C7F3BD78CE4728997856392333930A2B2BD53D1C02EAE2D9C4FB1327EA1FB355
50B36387237F1F16430B5602CDF931811FE3F9B1393F363DF15AB313FF877588
447E8E73A04CBE6381854373E4F871AAE703CF8FF2FF7D8BE7FC8815F7C3FC1C
FF87730E9F98E19D22393D9D7A8E1E4D03274F56F7AE0DA88B1806DEC8CFF7F5
22385A5BEBF450A282F8A4243AEFEAAB55E24E18501734CCBC3C6890BEBDBC3C
2C81C368067E7DA7EC6CBA7ED932758F861175712308F6328032B0B2C7612C81
525C08BD577BEC7B1175A11D04F9055F7CF00185528CE465508C03818F960EBB
5E445D781781CCC31D1F7D14B50A01029F7ED14574DDE2C5EABE7309EA877031
C837D8B7650BEDDDB4C9736E438BB4344AEDDE9DDA76EDAAD6E75D8CFA613C08
961CF77EF619EDE3632F2B887026249981A699A92CE06DBB75A3543ED4D29CF7
503F58940225517FF0201DABABA323870ED1117E8E65C9233535BE7FE3F9517E
04F12D5BFA96DA12F8312139F987E72929BE7FC7A1E3113F57C21D9DFC3FB05B
459D4B95757A0000000049454E44AE4260822800000030000000600000000100
0800000000000000000000000000000000000000000000000000000000000202
04000505070004040B0003030E0004040D0008080B000A0A0E000C0C0E000404
1200050514000C0C100006061A0006061D001111160015151A00060621000707
280007072C001B1B20001D1D22000808350008083B0022222800242429002828
2E002E2E2F0036363A0039393D003C3C3C000808470008084B0008084D000707
50000808530008085A0008085F003E3E42000909660009096A000A0A71000A0A
76000B0B79000B0B7D004343470044444700464649004A4A4C00535354005757
57005B5B5D00666667007171710077777700787878007D7D7D00000080000606
83000B0B8100090984000C0C85000404890005058F000C0C890016168B001717
8C00070790000D0D91000D0D96000606990006069D000D0D9900222291003434
9A003E3E9E000707A3000E0EA0000E0EA6000707A9000909B1000F0FB2000909
B5001010B4001010BA005050A7005757AB007272B8000E0ECE001111C2001111
C9001212D1001212D6001010DA001010DE001313E1001414E3001212E6001414
E7001414E9001515EE001414F3001515F5001616FA001515FE00808080008A8A
8A008D8D8D00929292009A9A9A009E9E9E00A1A1A100A7A7A700AAAAAA00B0B0
B000BDBDBD00C9C9C900D0D0D000D7D7D700D9D9D900DDDDDD00E2E2E200E5E5
E500EBEBEB00EDEDED00F1F1F100F5F5F500F9F9F900FEFEFE00000000002F26
000050410000705B000090740000B08E0000CFA90000F0C30000FFD21100FFD8
3100FFDD5100FFE47100FFEA9100FFF0B100FFF6D100FFFFFF00000000002F14
00005022000070300000903E0000B04D0000CF5B0000F0690000FF791100FF8A
3100FF9D5100FFAF7100FFC19100FFD2B100FFE5D100FFFFFF00000000002F03
0000500400007006000090090000B00A0000CF0C0000F00E0000FF201200FF3E
3100FF5C5100FF7A7100FF979100FFB6B100FFD4D100FFFFFF00000000002F00
0E00500017007000210090002B00B0003600CF004000F0004900FF115A00FF31
7000FF518600FF719C00FF91B200FFB1C800FFD1DF00FFFFFF00000000002F00
20005000360070004C0090006200B0007800CF008E00F000A400FF11B300FF31
BE00FF51C700FF71D100FF91DC00FFB1E500FFD1F000FFFFFF00000000002C00
2F004B0050006900700087009000A500B000C400CF00E100F000F011FF00F231
FF00F451FF00F671FF00F791FF00F9B1FF00FBD1FF00FFFFFF00000000001B00
2F002D0050003F007000520090006300B0007600CF008800F0009911FF00A631
FF00B451FF00C271FF00CF91FF00DCB1FF00EBD1FF00FFFFFF00000000000800
2F000E005000150070001B0090002100B0002600CF002C00F0003E11FF005831
FF007151FF008C71FF00A691FF00BFB1FF00DAD1FF00FFFFFF00000000000000
0000000000000000000000000056554A483B3B484A5556000000000000000000
0000000000000000000000000000000000000000000000000000543B383D4246
4F52534E4B4339383B5400000000000000000000000000000000000000000000
00000000000000003B393D505E676666666666666667665E513D383C00000000
0000000000000000000000000000000000000000000040384B5E676766666666
666666666666666666665F4D3841000000000000000000000000000000000000
000000004938475F66666666666666666666666666666666666667675E453849
0000000000000000000000000000000000000041395767676767666666666666
66666666666666666666666666665A3941000000000000000000000000000000
0000394261676767676767676666666666666666666666666666666666676761
423B0000000000000000000000000000003846652923475F6767676767676766
6666666666666666666666666666666666463800000000000000000000000000
394C6667240807011F5266676767676666666666666666666666666666666667
6766463900000000000000000000004142656767242E7E6F25000C2B63676767
6766666666666666666666666666666666666642410000000000000000004939
62666767242E8F8F8F77360E0A47666766666666666666666666666666666666
6667676139490000000000000000385767676666262E8F8F8F8F8F7C34011662
67676766666666666666666666666666666666665A3800000000000000414766
66666767242E8F8F8F8F8F8F8F7414105B676666666666666666666666666666
66666667674541000000000000385D6767666666262E8F8F8F8F8F8F8F8F7A1C
055B676767666666666666666666666666666666665F3800000000003B4D6666
66666667241A7B8F8F8F8F8F8F8F8F7E1B126567676767666666666666666666
6666666767674B3C00000000385E6767666666664D0C08347A7F8F8F8F8F8F8F
790B26676767676767676766666666666666666666665F380000005442666666
666666666666430C026D8F8F8F8F8F8F8F70005E676767676767676766666666
666666666767673D5400003B4F67676666666666666666663C02688F8F8F8F8F
8F7F0F2A67676767676767676767676666666666666666503B0000385E666666
6666666666666666674C01327F8F8F8F8F8F360C44444444444444475B666666
666666666667675C38005638676767666666666666666666666658026D8F8F8F
8F8F75080B0707080B0808004467676666666666666666673956554366666666
6666666666666666666767270F7D8F8F8F8F8F8F8F8F8F8F8F8F8F0B47666666
666666666666676742554A466767666666666666666666666666666306688F8F
8F8F8F8F8F8F8F8F8F8F8F0B446767666666666666666667464A484E67666666
6666666666666666666667673C06798F8F8F8F8F8F8F8F8F8F8F8F0B47666666
66666666666767674E483B5167676766666666666666666666666666660B698F
8F8F8F8F8F8F8F8F8F8F8F08446767666666666666666666533B3B5366666666
66666666666666666666666767222F8F8F8F8F8F8F8F8F8F8F8F8F0847666666
6666666666666767513B484E6767676666666666666666666666666666440B8F
8F8F8F8F712D2C2D2D2E2E024467666666666666666666674E484A4667676666
666666666666666666666667675A00748F8F8F8F730022262627272759666666
6666666666676767464A5542676767666666666666666666666666666667076B
8F8F8F8F7F0E2A67676766666666666666666666666666674355563966676666
666666666666666666666667676720308F8F8F8F8F370A656766666666666666
6666666666676767385600385C6767676766666666666666666666666666400E
8F8F8F8F8F79023F6767676766666666666666666666675B3800003B4F676767
66666666666666666666666666675A00738F8F8F8F8F69015267676666666666
666666666767674F3B0000543D67676767666666666666666666666666666610
687F8F8F8F8F8F330359676767676666666666666667663D54000000385E6767
66666666666666666666666666676727198F8F8F8F8F8F7F2E01285F67666666
6666666767675D38000000003C4B67676767666666666666666666666666665A
026E8F8F8F8F8F8F7F6C0E000C3C66666666666667674B3C0000000000385E67
67666666666666666666666666666767280B798F8F8F8F8F8F8F7E721D1F6666
66676767675D3800000000000040456767676767666666666666666666666666
66111D7C8F8F8F8F8F8F8F8F331E67666667676767454100000000000000385A
67676766666666666666666666666666675B0D18778F8F8F8F8F8F8F331E6767
6767676757380000000000000000493861676767676766666666666666666666
666663150E6A7E8F8F8F8F8F331E676767676761384900000000000000000041
3E656767676666666666666666666666666667662A021469768F8F8F331E6767
6767643E41000000000000000000000039466767676767676666666666666666
666666666661260C02183672311E676767674639000000000000000000000000
00384665676767676766666666666666666666666666666658281100001E6767
644638000000000000000000000000000000393E606767676767676767666666
6666666666666666666667635958676142390000000000000000000000000000
0000004138576767676767676767676767676767676767676767676767675738
41000000000000000000000000000000000000004938455D6767676767676767
6767676767676767676767675D45384900000000000000000000000000000000
00000000000040384B5D676767676767676767676767676767675D4B38400000
0000000000000000000000000000000000000000000000003B383D4F5C676767
676767676767675C4F3D383B0000000000000000000000000000000000000000
00000000000000000000543B38383E464E51514E464238383B54000000000000
000000000000000000000000000000000000000000000000000000000056554A
483B3B484A555600000000000000000000000000000000000000FFFFE007FFFF
0000FFFF0000FFFF0000FFFC00003FFF0000FFF000000FFF0000FFC0000003FF
0000FF80000001FF0000FF00000000FF0000FE000000007F0000FC000000003F
0000F8000000001F0000F0000000000F0000F0000000000F0000E00000000007
0000E000000000070000C000000000030000C000000000030000800000000001
0000800000000001000080000000000100000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000800000000001000080000000000100008000000000010000C00000000003
0000C000000000030000E000000000070000E000000000070000F0000000000F
0000F0000000000F0000F8000000001F0000FC000000003F0000FE000000007F
0000FF00000000FF0000FF80000001FF0000FFC0000003FF0000FFF000000FFF
0000FFFC00003FFF0000FFFF0000FFFF0000FFFFE007FFFF0000280000002000
0000400000000100080000000000000000000000000000000000000000000000
000000000000030305000B0B14000C0C16000E0E1D0012121F000F0F22001313
220017172300101024001919270012122D0014142F00191928001D1D29001E1E
2F001313340014143A0012123C0015153F00222231002A2A3A002C2C39003131
3D001212450010104B001616480012124E0013135200323243003A3A46001717
660012126A001414690012126E000F0F7D001111700013137E00454554004B4B
570050505E005E5E6A0063636F00676772006D6D77007373780078787F000606
8400090987000A0A88000E0E8E0012128E0015158E000D0D95000E0E9D001010
92001919910010109A0010109C002E2E9B0031319F000E0EA8001010A3001010
A6000F0FB7001010BA001010BF006262B4007A7ABE000D0DC2001212CE000F0F
D0001212D1001010DA001212E7001414E7001414E9001313EE001414EC001414
F1001414F5001616FB001616FE00808085008C8C8F00969699009E9E9F00A0A0
A100AEAEAE00B7B7B700BEBEBE00D6D6D600DADADA00E2E2E200E4E4E400E9E9
E900EEEEEE00F0F0F000F6F6F600F8F8F800FEFEFE004CB0000059CF000067F0
000078FF11008AFF31009CFF5100AEFF7100C0FF9100D2FFB100E4FFD100FFFF
FF0000000000262F0000405000005A700000749000008EB00000A9CF0000C2F0
0000D1FF1100D8FF3100DEFF5100E3FF7100E9FF9100EFFFB100F6FFD100FFFF
FF00000000002F26000050410000705B000090740000B08E0000CFA90000F0C3
0000FFD21100FFD83100FFDD5100FFE47100FFEA9100FFF0B100FFF6D100FFFF
FF00000000002F1400005022000070300000903E0000B04D0000CF5B0000F069
0000FF791100FF8A3100FF9D5100FFAF7100FFC19100FFD2B100FFE5D100FFFF
FF00000000002F030000500400007006000090090000B00A0000CF0C0000F00E
0000FF201200FF3E3100FF5C5100FF7A7100FF979100FFB6B100FFD4D100FFFF
FF00000000002F000E00500017007000210090002B00B0003600CF004000F000
4900FF115A00FF317000FF518600FF719C00FF91B200FFB1C800FFD1DF00FFFF
FF00000000002F0020005000360070004C0090006200B0007800CF008E00F000
A400FF11B300FF31BE00FF51C700FF71D100FF91DC00FFB1E500FFD1F000FFFF
FF00000000002C002F004B0050006900700087009000A500B000C400CF00E100
F000F011FF00F231FF00F451FF00F671FF00F791FF00F9B1FF00FBD1FF00FFFF
FF00000000001B002F002D0050003F007000520090006300B0007600CF008800
F0009911FF00A631FF00B451FF00C271FF00CF91FF00DCB1FF00EBD1FF00FFFF
FF000000000008002F000E005000150070001B0090002100B0002600CF002C00
F0003E11FF005831FF007151FF008C71FF00A691FF00BFB1FF00DAD1FF00FFFF
FF00000000000000000000000000433C382F2F383C4300000000000000000000
000000000000000000000043313945494A50504A494539314300000000000000
0000000000000000004431404F525252525252525252524F4031440000000000
00000000000000003B3F4F52525252525252525252525252524D3F3B00000000
0000000000000034414952525252525252525252525252525252524234000000
00000000000034474B0110324B52525252525252525252525252525247340000
00000000003B42524B0F5C2E0A1A485252525252525252525252525252423B00
00000000443F52524B0F6F6F6F5A0A3352525252525252525252525252523F44
00000000314D52524B0F6F6F6F6F6127184B5252525252525252525252524D31
00000043405252524B08626F6F6F6F6F55125252525252525252525252525240
430000314F52525250220553636F6F6F6F2B255252525252525252525252524F
300000395252525252524E2017606F6F6F610451525252525252525252525252
390043455252525252525252370E616F6F6F281B1F1F1F1F2452525252525252
45433C49525252525252525252232C6F6F6F5E59595959590352525252525252
493C384A52525252525252525250065D6F6F6F6F6F6F6F6F0352525252525252
4A382F505252525252525252525236296F6F6F6F6F6F6F6F0352525252525252
502F2F50525252525252525252524E0D6F6F6F615B5C5B5B0352525252525252
502F384A52525252525252525252520B5F6F6F5B011313131952525252525252
4A383C49525252525252525252525221586F6F6F144952525252525252525252
493C434552525252525252525252523D2A6F6F6F542452525252525252525252
4543003952525252525252525252524E0D6F6F6F63163E525252525252525252
390000304F5252525252525252525252115C6F6F6F5F0E21465252525252524F
300000434052525252525252525252523E266F6F6F6F6257151C525252525240
43000000314D52525252525252525252521B53646F6F6F6F610C525252524D31
00000000443F52525252525252525252524E1C1E5F6F6F6F610C525252523F44
00000000003B425252525252525252525252523A0756616F610C525252423B00
00000000000034475252525252525252525252524922091D2D0C525247340000
0000000000000034425252525252525252525252525252483532524234000000
00000000000000003B3F4D52525252525252525252525252524D3F3B00000000
0000000000000000004431404F525252525252525252524F4031440000000000
000000000000000000000043303945494A50504A494539304300000000000000
0000000000000000000000000000433C382F2F383C4300000000000000000000
0000FFF00FFFFF8001FFFE00007FFC00003FF800001FF000000FE0000007C000
0003C00000038000000180000001800000010000000000000000000000000000
000000000000000000000000000000000000800000018000000180000001C000
0003C0000003E0000007F000000FF800001FFC00003FFE00007FFF8001FFFFF0
0FFF280000001000000020000000010008000000000000000000000000000000
00000000000000000000000000003F3F69002A2A720044447700272784002323
8F002C2C8F003535870033338C0035358E002A2A91002D2D910023239A001F1F
A1001F1FB1001C1CBF002323A0002525A4003333BE0040408700515181005555
86005F5F80006363870062628E0066668E00696989004747B4004A4ABC001515
C3001515D4001919D6002828C5001717E5001515F3001515FA001616FE006969
C1007E7EC8009797AA00AFAFBB00BABAC700C2C2C800CDCDD400D1D1D300D5D5
D800DDDDE000E4E4E500F1F1F100F9F9F900FFFFFF0000704C000090630000B0
790000CF8F0000F0A60011FFB40031FFBE0051FFC80071FFD30091FFDC00B1FF
E500D1FFF000FFFFFF0000000000002F0E00005018000070220000902C0000B0
360000CF400000F04A0011FF5B0031FF710051FF870071FF9D0091FFB200B1FF
C900D1FFDF00FFFFFF0000000000022F00000450000006700000089000000AB0
00000BCF00000EF0000020FF12003DFF31005BFF510079FF710098FF9100B5FF
B100D4FFD100FFFFFF0000000000142F000022500000307000003D9000004CB0
000059CF000067F0000078FF11008AFF31009CFF5100AEFF7100C0FF9100D2FF
B100E4FFD100FFFFFF0000000000262F0000405000005A700000749000008EB0
0000A9CF0000C2F00000D1FF1100D8FF3100DEFF5100E3FF7100E9FF9100EFFF
B100F6FFD100FFFFFF00000000002F26000050410000705B000090740000B08E
0000CFA90000F0C30000FFD21100FFD83100FFDD5100FFE47100FFEA9100FFF0
B100FFF6D100FFFFFF00000000002F1400005022000070300000903E0000B04D
0000CF5B0000F0690000FF791100FF8A3100FF9D5100FFAF7100FFC19100FFD2
B100FFE5D100FFFFFF00000000002F030000500400007006000090090000B00A
0000CF0C0000F00E0000FF201200FF3E3100FF5C5100FF7A7100FF979100FFB6
B100FFD4D100FFFFFF00000000002F000E00500017007000210090002B00B000
3600CF004000F0004900FF115A00FF317000FF518600FF719C00FF91B200FFB1
C800FFD1DF00FFFFFF00000000002F0020005000360070004C0090006200B000
7800CF008E00F000A400FF11B300FF31BE00FF51C700FF71D100FF91DC00FFB1
E500FFD1F000FFFFFF00000000002C002F004B0050006900700087009000A500
B000C400CF00E100F000F011FF00F231FF00F451FF00F671FF00F791FF00F9B1
FF00FBD1FF00FFFFFF00000000001B002F002D0050003F007000520090006300
B0007600CF008800F0009911FF00A631FF00B451FF00C271FF00CF91FF00DCB1
FF00EBD1FF00FFFFFF000000000008002F000E005000150070001B0090002100
B0002600CF002C00F0003E11FF005831FF007151FF008C71FF00A691FF00BFB1
FF00DAD1FF00FFFFFF000000000000251C12121C250000000000000000262023
2424242423202600000000001B0D0E23242424242424211B0000002621092C17
112424242424242126000020240832322B0724242424242420002523241F032E
32280F242424242423251C24242424042D321A1515102424241C122424242424
143232323206242424121224242424240B313016180C242424121C2424242424
1D2A321324242424241C25232424242424153229052224242325002024242424
240C2E322F012424200000262124242424240A2731192421260000001B212424
2424241E0A02211B000000000026202324242424232026000000000000000025
1C12121C250000000000F81F0000E0070000C003000080010000800100000000
000000000000000000000000000000000000000000008001000080010000C003
0000E0070000F81F0000}
OldCreateOrder = False
OnActivate = FormActivate
OnCreate = FormCreate
PixelsPerInch = 120
TextHeight = 16
object tmrWatchDog: TTimer
OnTimer = tmrWatchDogTimer
Left = 16
Top = 8
end
end

View File

@@ -0,0 +1,451 @@
unit uMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, OleCtrls, ShockwaveFlashObjects_TLB, StdCtrls, ExtCtrls;
type
TfrmMain = class(TForm)
tmrWatchDog: TTimer;
procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure tmrWatchDogTimer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TMySWF = class(TShockwaveFlash)
public
procedure CreateWnd; override;
end;
type
TPipeThread = class(TThread)
protected
flashFile: string;
w: integer;
h: integer;
bgColor: TColor;
procedure Execute; override;
procedure displaySWF;
procedure freeSWF;
procedure setPos;
procedure setBGColor;
end;
TBuf = array[0..255] of byte;
var
frmMain: TfrmMain;
k: string;
t: TPipeThread;
flaPreview: TMySWF;
target: HWND = 0;
implementation
{$R *.dfm}
procedure TMySWF.CreateWnd;
begin
inherited;
end;
function arrToStr(k: TBuf; len: integer): string;
var
s: string;
i: integer;
begin
s := '';
for i := 0 to len - 1 do
begin
if k[i] = 0 then
break;
s := s + '' + chr(k[i]);
end;
Result := s;
end;
procedure ReadPipe(pipe: cardinal; var buffer: TBuf; bytesToRead: cardinal);
var
numBytesRead: DWORD;
readResult: longbool;
begin
numBytesRead := 0;
readResult := ReadFile(pipe, buffer, bytesToRead, numBytesRead, nil);
if (not readResult) or (numBytesRead <> bytesToRead) then
begin
Application.Terminate;
end;
end;
procedure WritePipe(pipe: cardinal; var buffer: TBuf; bytesToWrite: cardinal);
var
written: cardinal;
writeResult: longbool;
begin
written := 0;
writeResult := WriteFile(pipe, buffer, bytesToWrite, written, nil);
if (not writeResult) or (written <> bytesToWrite) then
begin
Application.Terminate;
end;
end;
procedure TPipeThread.freeSWF();
begin
if Assigned(flaPreview) then
begin
try
flaPreview.Stop;
flaPreview.Movie := '';
flaPreview.Free;
flaPreview := nil;
except
on E: Exception do
begin
end;
end;
end;
end;
procedure TPipeThread.displaySWF();
begin
Windows.SetParent(frmMain.Handle, target);
freeSWF();
flaPreview := TMySWF.Create(frmMain);
flaPreview.Left := 0;
flaPreview.Top := 0;
flaPreview.Width := self.w;
flaPreview.Height := self.h;
frmMain.Caption := 'set movie:' + flashFile;
flaPreview.Parent := frmMain;
flaPreview.Movie := flashFile;
flaPreview.SetFocus;
end;
procedure TPipeThread.setBGColor();
begin
frmMain.Color := self.bgColor;
end;
procedure TPipeThread.setPos();
begin
SetWindowPos(frmMain.Handle, 0, 0, 0, self.w, self.h, SWP_SHOWWINDOW);
flaPreview.Left := 0;
flaPreview.Top := 0;
flaPreview.Width := self.w;
flaPreview.Height := self.h;
flaPreview.CreateWnd;
//displaySWF();
end;
procedure TPipeThread.Execute();
var
pipe: cardinal;
buffer: TBuf;
pipename: PAnsiChar;
len: integer;
cmd: integer;
val: cardinal;
vals: string;
vars: string;
const
CMD_PLAY = 1;
CMD_RESIZE = 2;
CMD_BGCOLOR = 3;
CMD_CURRENT_FRAME = 4;
CMD_TOTAL_FRAMES = 5;
CMD_PAUSE = 6;
CMD_RESUME = 7;
CMD_PLAYING = 8;
CMD_REWIND = 9;
CMD_GOTO = 10;
CMD_CALL = 11;
CMD_GETVARIABLE = 12;
CMD_SETVARIABLE = 13;
begin
try
pipename := PAnsiChar('\\.\\pipe\ffdec_flashplayer_' + ParamStr(1));
begin
pipe := CreateFile(pipename, GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
repeat
try
ReadPipe(pipe, buffer, 1);
cmd := buffer[0];
case cmd of
CMD_PLAY:
begin
ReadPipe(pipe, buffer, 1);
len := buffer[0];
ReadPipe(pipe, buffer, len);
self.flashFile := arrToStr(buffer, len);
try
Synchronize(displaySWF);
except
on E: Exception do
begin
freeSWF();
end;
end;
end;
CMD_RESIZE:
begin
ReadPipe(pipe, buffer, 4);
self.w := buffer[0] * 256 + buffer[1];
self.h := buffer[2] * 256 + buffer[3];
Synchronize(setPos);
end;
CMD_BGCOLOR:
begin
ReadPipe(pipe, buffer, 3);
self.bgColor := RGB(buffer[0], buffer[1], buffer[2]);
Synchronize(setBGColor);
end;
CMD_CURRENT_FRAME:
begin
val := 0;
try
if flaPreview.ReadyState = 4 then
val := flaPreview.CurrentFrame
except
on E: Exception do
begin
freeSWF();
end;
end;
buffer[0] := (val shr 8) mod 256;
buffer[1] := val mod 256;
WritePipe(pipe, buffer, 2);
end;
CMD_TOTAL_FRAMES:
begin
val := 0;
try
if flaPreview.ReadyState = 4 then
val := flaPreview.TotalFrames
except
on E: Exception do
begin
freeSWF();
end;
end;
buffer[0] := (val shr 8) mod 256;
buffer[1] := val mod 256;
WritePipe(pipe, buffer, 2);
end;
CMD_PAUSE:
begin
try
flaPreview.Stop;
except
on E: Exception do
begin
freeSWF();
end;
end;
end;
CMD_RESUME:
begin
try
flaPreview.Play;
except
on E: Exception do
begin
freeSWF();
end;
end;
end;
CMD_PLAYING:
begin
buffer[0] := 0;
try
if flaPreview.ReadyState = 4 then
if flaPreview.IsPlaying then
buffer[0] := 1;
except
on E: Exception do
begin
freeSWF();
end;
end;
WritePipe(pipe, buffer, 1);
end;
CMD_REWIND:
begin
try
flaPreview.Rewind;
except
on E: Exception do
begin
freeSWF();
end;
end;
end;
CMD_GOTO:
begin
ReadPipe(pipe, buffer, 2);
val := (buffer[0] shl 8) + buffer[1];
try
flaPreview.GotoFrame(val);
except
on E: Exception do
begin
freeSWF();
end;
end;
end;
CMD_CALL:
begin
ReadPipe(pipe, buffer, 2);
val := (buffer[0] shl 8) + buffer[1];
ReadPipe(pipe, buffer, val);
SetString(vals, PChar(Addr(buffer)), val);
try
vals := flaPreview.CallFunction(vals);
except
on E: Exception do
begin
vals := '';
freeSWF();
end;
end;
val := length(vals);
buffer[0] := (val shr 8) mod 256;
buffer[1] := val mod 256;
WritePipe(pipe, buffer, 2);
Move(vals[1], buffer, val);
WritePipe(pipe, buffer, val);
end;
CMD_GETVARIABLE:
begin
ReadPipe(pipe, buffer, 2);
val := (buffer[0] shl 8) + buffer[1];
ReadPipe(pipe, buffer, val);
SetString(vals, PChar(Addr(buffer)), val);
try
vals := flaPreview.GetVariable(vals);
except
on E: Exception do
begin
vals := '';
freeSWF();
end;
end;
val := length(vals);
buffer[0] := (val shr 8) mod 256;
buffer[1] := val mod 256;
WritePipe(pipe, buffer, 2);
Move(vals[1], buffer, val);
WritePipe(pipe, buffer, val);
end;
CMD_SETVARIABLE:
begin
ReadPipe(pipe, buffer, 2);
val := (buffer[0] shl 8) + buffer[1];
ReadPipe(pipe, buffer, val);
SetString(vars, PChar(Addr(buffer)), val);
ReadPipe(pipe, buffer, 2);
val := (buffer[0] shl 8) + buffer[1];
ReadPipe(pipe, buffer, val);
SetString(vals, PChar(Addr(buffer)), val);
try
flaPreview.SetVariable(vars, vals);
except
on E: Exception do
begin
freeSWF();
end;
end;
end;
end;
except
on E: Exception do
begin
freeSWF();
end;
end;
until False;
CloseHandle(pipe);
end;
except
on E: Exception do
begin
end;
end;
end;
procedure TfrmMain.FormActivate(Sender: TObject);
begin
if (ParamCount >= 2) then
begin
flaPreview.Parent := frmMain;
ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong(Application.Handle, GWL_EXSTYLE,
getWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
ShowWindow(Application.Handle, SW_SHOW);
SetForegroundWindow(HWND(StrToInt(ParamStr(2))));
frmMain.Caption := 'FlashPlayerWindow_' + ParamStr(2);
Application.Title := 'FlashPlayerWindow_' + ParamStr(2);
target := HWND(StrToInt(ParamStr(1)));
SetWindowLong(frmMain.Handle, GWL_STYLE, 0);
ShowWindow(frmMain.Handle, SW_SHOW);
frmMain.Left := 0;
frmMain.Top := 0;
Windows.SetParent(frmMain.Handle, target);
t := TPipeThread.Create(True);
t.Resume;
end;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
if (ParamCount >= 2) then
begin
flaPreview := TMySWF.Create(frmMain);
flaPreview.AllowScriptAccess := 'always';
flaPreview.BackgroundColor := -1;
end;
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
t.Free;
end;
procedure TfrmMain.tmrWatchDogTimer(Sender: TObject);
begin
if target <> 0 then
begin
if not IsWindow(target) then
begin
Application.Terminate;
end;
end;
end;
end.

73
libsrc/LZMA/build.xml Normal file
View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="LZMA" default="default" basedir=".">
<description>Builds, tests, and runs the project LZMA.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="LZMA-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>

3
libsrc/LZMA/manifest.mf Normal file
View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
build.xml.data.CRC32=cf8fb9e4
build.xml.script.CRC32=0d82b5ac
build.xml.stylesheet.CRC32=8064a381@1.68.1.46
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=cf8fb9e4
nbproject/build-impl.xml.script.CRC32=fd6704e6
nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.74.1.48

View File

@@ -0,0 +1,71 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processor.options=
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
${run.classpath}
debug.test.classpath=\
${run.test.classpath}
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=../../lib/LZMA.jar
dist.javadoc.dir=${dist.dir}/javadoc
excludes=
includes=**
jar.compress=false
javac.classpath=
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.processorpath=\
${javac.classpath}
javac.source=1.6
javac.target=1.6
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
javac.test.processorpath=\
${javac.test.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
main.class=
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
# or test-sys-prop.name=value to set system properties for unit tests):
run.jvmargs=
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
source.encoding=UTF-8
src.dir=src
test.src.dir=test

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>LZMA</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>

View File

@@ -0,0 +1,52 @@
// SevenZip/CRC.java
package SevenZip;
public class CRC
{
static public int[] Table = new int[256];
static
{
for (int i = 0; i < 256; i++)
{
int r = i;
for (int j = 0; j < 8; j++)
if ((r & 1) != 0)
r = (r >>> 1) ^ 0xEDB88320;
else
r >>>= 1;
Table[i] = r;
}
}
int _value = -1;
public void Init()
{
_value = -1;
}
public void Update(byte[] data, int offset, int size)
{
for (int i = 0; i < size; i++)
_value = Table[(_value ^ data[offset + i]) & 0xFF] ^ (_value >>> 8);
}
public void Update(byte[] data)
{
int size = data.length;
for (int i = 0; i < size; i++)
_value = Table[(_value ^ data[i]) & 0xFF] ^ (_value >>> 8);
}
public void UpdateByte(int b)
{
_value = Table[(_value ^ b) & 0xFF] ^ (_value >>> 8);
}
public int GetDigest()
{
return _value ^ (-1);
}
}

View File

@@ -0,0 +1,382 @@
// LZ.BinTree
package SevenZip.Compression.LZ;
import java.io.IOException;
public class BinTree extends InWindow
{
int _cyclicBufferPos;
int _cyclicBufferSize = 0;
int _matchMaxLen;
int[] _son;
int[] _hash;
int _cutValue = 0xFF;
int _hashMask;
int _hashSizeSum = 0;
boolean HASH_ARRAY = true;
static final int kHash2Size = 1 << 10;
static final int kHash3Size = 1 << 16;
static final int kBT2HashSize = 1 << 16;
static final int kStartMaxLen = 1;
static final int kHash3Offset = kHash2Size;
static final int kEmptyHashValue = 0;
static final int kMaxValForNormalize = (1 << 30) - 1;
int kNumHashDirectBytes = 0;
int kMinMatchCheck = 4;
int kFixHashSize = kHash2Size + kHash3Size;
public void SetType(int numHashBytes)
{
HASH_ARRAY = (numHashBytes > 2);
if (HASH_ARRAY)
{
kNumHashDirectBytes = 0;
kMinMatchCheck = 4;
kFixHashSize = kHash2Size + kHash3Size;
}
else
{
kNumHashDirectBytes = 2;
kMinMatchCheck = 2 + 1;
kFixHashSize = 0;
}
}
public void Init() throws IOException
{
super.Init();
for (int i = 0; i < _hashSizeSum; i++)
_hash[i] = kEmptyHashValue;
_cyclicBufferPos = 0;
ReduceOffsets(-1);
}
public void MovePos() throws IOException
{
if (++_cyclicBufferPos >= _cyclicBufferSize)
_cyclicBufferPos = 0;
super.MovePos();
if (_pos == kMaxValForNormalize)
Normalize();
}
public boolean Create(int historySize, int keepAddBufferBefore,
int matchMaxLen, int keepAddBufferAfter)
{
if (historySize > kMaxValForNormalize - 256)
return false;
_cutValue = 16 + (matchMaxLen >> 1);
int windowReservSize = (historySize + keepAddBufferBefore +
matchMaxLen + keepAddBufferAfter) / 2 + 256;
super.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize);
_matchMaxLen = matchMaxLen;
int cyclicBufferSize = historySize + 1;
if (_cyclicBufferSize != cyclicBufferSize)
_son = new int[(_cyclicBufferSize = cyclicBufferSize) * 2];
int hs = kBT2HashSize;
if (HASH_ARRAY)
{
hs = historySize - 1;
hs |= (hs >> 1);
hs |= (hs >> 2);
hs |= (hs >> 4);
hs |= (hs >> 8);
hs >>= 1;
hs |= 0xFFFF;
if (hs > (1 << 24))
hs >>= 1;
_hashMask = hs;
hs++;
hs += kFixHashSize;
}
if (hs != _hashSizeSum)
_hash = new int [_hashSizeSum = hs];
return true;
}
public int GetMatches(int[] distances) throws IOException
{
int lenLimit;
if (_pos + _matchMaxLen <= _streamPos)
lenLimit = _matchMaxLen;
else
{
lenLimit = _streamPos - _pos;
if (lenLimit < kMinMatchCheck)
{
MovePos();
return 0;
}
}
int offset = 0;
int matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
int cur = _bufferOffset + _pos;
int maxLen = kStartMaxLen; // to avoid items for len < hashSize;
int hashValue, hash2Value = 0, hash3Value = 0;
if (HASH_ARRAY)
{
int temp = CrcTable[_bufferBase[cur] & 0xFF] ^ (_bufferBase[cur + 1] & 0xFF);
hash2Value = temp & (kHash2Size - 1);
temp ^= ((int)(_bufferBase[cur + 2] & 0xFF) << 8);
hash3Value = temp & (kHash3Size - 1);
hashValue = (temp ^ (CrcTable[_bufferBase[cur + 3] & 0xFF] << 5)) & _hashMask;
}
else
hashValue = ((_bufferBase[cur] & 0xFF) ^ ((int)(_bufferBase[cur + 1] & 0xFF) << 8));
int curMatch = _hash[kFixHashSize + hashValue];
if (HASH_ARRAY)
{
int curMatch2 = _hash[hash2Value];
int curMatch3 = _hash[kHash3Offset + hash3Value];
_hash[hash2Value] = _pos;
_hash[kHash3Offset + hash3Value] = _pos;
if (curMatch2 > matchMinPos)
if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur])
{
distances[offset++] = maxLen = 2;
distances[offset++] = _pos - curMatch2 - 1;
}
if (curMatch3 > matchMinPos)
if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur])
{
if (curMatch3 == curMatch2)
offset -= 2;
distances[offset++] = maxLen = 3;
distances[offset++] = _pos - curMatch3 - 1;
curMatch2 = curMatch3;
}
if (offset != 0 && curMatch2 == curMatch)
{
offset -= 2;
maxLen = kStartMaxLen;
}
}
_hash[kFixHashSize + hashValue] = _pos;
int ptr0 = (_cyclicBufferPos << 1) + 1;
int ptr1 = (_cyclicBufferPos << 1);
int len0, len1;
len0 = len1 = kNumHashDirectBytes;
if (kNumHashDirectBytes != 0)
{
if (curMatch > matchMinPos)
{
if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] !=
_bufferBase[cur + kNumHashDirectBytes])
{
distances[offset++] = maxLen = kNumHashDirectBytes;
distances[offset++] = _pos - curMatch - 1;
}
}
}
int count = _cutValue;
while (true)
{
if (curMatch <= matchMinPos || count-- == 0)
{
_son[ptr0] = _son[ptr1] = kEmptyHashValue;
break;
}
int delta = _pos - curMatch;
int cyclicPos = ((delta <= _cyclicBufferPos) ?
(_cyclicBufferPos - delta) :
(_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
int pby1 = _bufferOffset + curMatch;
int len = Math.min(len0, len1);
if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
{
while(++len != lenLimit)
if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
break;
if (maxLen < len)
{
distances[offset++] = maxLen = len;
distances[offset++] = delta - 1;
if (len == lenLimit)
{
_son[ptr1] = _son[cyclicPos];
_son[ptr0] = _son[cyclicPos + 1];
break;
}
}
}
if ((_bufferBase[pby1 + len] & 0xFF) < (_bufferBase[cur + len] & 0xFF))
{
_son[ptr1] = curMatch;
ptr1 = cyclicPos + 1;
curMatch = _son[ptr1];
len1 = len;
}
else
{
_son[ptr0] = curMatch;
ptr0 = cyclicPos;
curMatch = _son[ptr0];
len0 = len;
}
}
MovePos();
return offset;
}
public void Skip(int num) throws IOException
{
do
{
int lenLimit;
if (_pos + _matchMaxLen <= _streamPos)
lenLimit = _matchMaxLen;
else
{
lenLimit = _streamPos - _pos;
if (lenLimit < kMinMatchCheck)
{
MovePos();
continue;
}
}
int matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
int cur = _bufferOffset + _pos;
int hashValue;
if (HASH_ARRAY)
{
int temp = CrcTable[_bufferBase[cur] & 0xFF] ^ (_bufferBase[cur + 1] & 0xFF);
int hash2Value = temp & (kHash2Size - 1);
_hash[hash2Value] = _pos;
temp ^= ((int)(_bufferBase[cur + 2] & 0xFF) << 8);
int hash3Value = temp & (kHash3Size - 1);
_hash[kHash3Offset + hash3Value] = _pos;
hashValue = (temp ^ (CrcTable[_bufferBase[cur + 3] & 0xFF] << 5)) & _hashMask;
}
else
hashValue = ((_bufferBase[cur] & 0xFF) ^ ((int)(_bufferBase[cur + 1] & 0xFF) << 8));
int curMatch = _hash[kFixHashSize + hashValue];
_hash[kFixHashSize + hashValue] = _pos;
int ptr0 = (_cyclicBufferPos << 1) + 1;
int ptr1 = (_cyclicBufferPos << 1);
int len0, len1;
len0 = len1 = kNumHashDirectBytes;
int count = _cutValue;
while (true)
{
if (curMatch <= matchMinPos || count-- == 0)
{
_son[ptr0] = _son[ptr1] = kEmptyHashValue;
break;
}
int delta = _pos - curMatch;
int cyclicPos = ((delta <= _cyclicBufferPos) ?
(_cyclicBufferPos - delta) :
(_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
int pby1 = _bufferOffset + curMatch;
int len = Math.min(len0, len1);
if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
{
while (++len != lenLimit)
if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
break;
if (len == lenLimit)
{
_son[ptr1] = _son[cyclicPos];
_son[ptr0] = _son[cyclicPos + 1];
break;
}
}
if ((_bufferBase[pby1 + len] & 0xFF) < (_bufferBase[cur + len] & 0xFF))
{
_son[ptr1] = curMatch;
ptr1 = cyclicPos + 1;
curMatch = _son[ptr1];
len1 = len;
}
else
{
_son[ptr0] = curMatch;
ptr0 = cyclicPos;
curMatch = _son[ptr0];
len0 = len;
}
}
MovePos();
}
while (--num != 0);
}
void NormalizeLinks(int[] items, int numItems, int subValue)
{
for (int i = 0; i < numItems; i++)
{
int value = items[i];
if (value <= subValue)
value = kEmptyHashValue;
else
value -= subValue;
items[i] = value;
}
}
void Normalize()
{
int subValue = _pos - _cyclicBufferSize;
NormalizeLinks(_son, _cyclicBufferSize * 2, subValue);
NormalizeLinks(_hash, _hashSizeSum, subValue);
ReduceOffsets(subValue);
}
public void SetCutValue(int cutValue) { _cutValue = cutValue; }
private static final int[] CrcTable = new int[256];
static
{
for (int i = 0; i < 256; i++)
{
int r = i;
for (int j = 0; j < 8; j++)
if ((r & 1) != 0)
r = (r >>> 1) ^ 0xEDB88320;
else
r >>>= 1;
CrcTable[i] = r;
}
}
}

View File

@@ -0,0 +1,131 @@
// LZ.InWindow
package SevenZip.Compression.LZ;
import java.io.IOException;
public class InWindow
{
public byte[] _bufferBase; // pointer to buffer with data
java.io.InputStream _stream;
int _posLimit; // offset (from _buffer) of first byte when new block reading must be done
boolean _streamEndWasReached; // if (true) then _streamPos shows real end of stream
int _pointerToLastSafePosition;
public int _bufferOffset;
public int _blockSize; // Size of Allocated memory block
public int _pos; // offset (from _buffer) of curent byte
int _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos
int _keepSizeAfter; // how many BYTEs must be kept buffer after _pos
public int _streamPos; // offset (from _buffer) of first not read byte from Stream
public void MoveBlock()
{
int offset = _bufferOffset + _pos - _keepSizeBefore;
// we need one additional byte, since MovePos moves on 1 byte.
if (offset > 0)
offset--;
int numBytes = _bufferOffset + _streamPos - offset;
// check negative offset ????
for (int i = 0; i < numBytes; i++)
_bufferBase[i] = _bufferBase[offset + i];
_bufferOffset -= offset;
}
public void ReadBlock() throws IOException
{
if (_streamEndWasReached)
return;
while (true)
{
int size = (0 - _bufferOffset) + _blockSize - _streamPos;
if (size == 0)
return;
int numReadBytes = _stream.read(_bufferBase, _bufferOffset + _streamPos, size);
if (numReadBytes == -1)
{
_posLimit = _streamPos;
int pointerToPostion = _bufferOffset + _posLimit;
if (pointerToPostion > _pointerToLastSafePosition)
_posLimit = _pointerToLastSafePosition - _bufferOffset;
_streamEndWasReached = true;
return;
}
_streamPos += numReadBytes;
if (_streamPos >= _pos + _keepSizeAfter)
_posLimit = _streamPos - _keepSizeAfter;
}
}
void Free() { _bufferBase = null; }
public void Create(int keepSizeBefore, int keepSizeAfter, int keepSizeReserv)
{
_keepSizeBefore = keepSizeBefore;
_keepSizeAfter = keepSizeAfter;
int blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv;
if (_bufferBase == null || _blockSize != blockSize)
{
Free();
_blockSize = blockSize;
_bufferBase = new byte[_blockSize];
}
_pointerToLastSafePosition = _blockSize - keepSizeAfter;
}
public void SetStream(java.io.InputStream stream) { _stream = stream; }
public void ReleaseStream() { _stream = null; }
public void Init() throws IOException
{
_bufferOffset = 0;
_pos = 0;
_streamPos = 0;
_streamEndWasReached = false;
ReadBlock();
}
public void MovePos() throws IOException
{
_pos++;
if (_pos > _posLimit)
{
int pointerToPostion = _bufferOffset + _pos;
if (pointerToPostion > _pointerToLastSafePosition)
MoveBlock();
ReadBlock();
}
}
public byte GetIndexByte(int index) { return _bufferBase[_bufferOffset + _pos + index]; }
// index + limit have not to exceed _keepSizeAfter;
public int GetMatchLen(int index, int distance, int limit)
{
if (_streamEndWasReached)
if ((_pos + index) + limit > _streamPos)
limit = _streamPos - (_pos + index);
distance++;
// Byte *pby = _buffer + (size_t)_pos + index;
int pby = _bufferOffset + _pos + index;
int i;
for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++);
return i;
}
public int GetNumAvailableBytes() { return _streamPos - _pos; }
public void ReduceOffsets(int subValue)
{
_bufferOffset += subValue;
_posLimit -= subValue;
_pos -= subValue;
_streamPos -= subValue;
}
}

View File

@@ -0,0 +1,85 @@
// LZ.OutWindow
package SevenZip.Compression.LZ;
import java.io.IOException;
public class OutWindow
{
byte[] _buffer;
int _pos;
int _windowSize = 0;
int _streamPos;
java.io.OutputStream _stream;
public void Create(int windowSize)
{
if (_buffer == null || _windowSize != windowSize)
_buffer = new byte[windowSize];
_windowSize = windowSize;
_pos = 0;
_streamPos = 0;
}
public void SetStream(java.io.OutputStream stream) throws IOException
{
ReleaseStream();
_stream = stream;
}
public void ReleaseStream() throws IOException
{
Flush();
_stream = null;
}
public void Init(boolean solid)
{
if (!solid)
{
_streamPos = 0;
_pos = 0;
}
}
public void Flush() throws IOException
{
int size = _pos - _streamPos;
if (size == 0)
return;
_stream.write(_buffer, _streamPos, size);
if (_pos >= _windowSize)
_pos = 0;
_streamPos = _pos;
}
public void CopyBlock(int distance, int len) throws IOException
{
int pos = _pos - distance - 1;
if (pos < 0)
pos += _windowSize;
for (; len != 0; len--)
{
if (pos >= _windowSize)
pos = 0;
_buffer[_pos++] = _buffer[pos++];
if (_pos >= _windowSize)
Flush();
}
}
public void PutByte(byte b) throws IOException
{
_buffer[_pos++] = b;
if (_pos >= _windowSize)
Flush();
}
public byte GetByte(int distance)
{
int pos = _pos - distance - 1;
if (pos < 0)
pos += _windowSize;
return _buffer[pos];
}
}

View File

@@ -0,0 +1,88 @@
// Base.java
package SevenZip.Compression.LZMA;
public class Base
{
public static final int kNumRepDistances = 4;
public static final int kNumStates = 12;
public static final int StateInit()
{
return 0;
}
public static final int StateUpdateChar(int index)
{
if (index < 4)
return 0;
if (index < 10)
return index - 3;
return index - 6;
}
public static final int StateUpdateMatch(int index)
{
return (index < 7 ? 7 : 10);
}
public static final int StateUpdateRep(int index)
{
return (index < 7 ? 8 : 11);
}
public static final int StateUpdateShortRep(int index)
{
return (index < 7 ? 9 : 11);
}
public static final boolean StateIsCharState(int index)
{
return index < 7;
}
public static final int kNumPosSlotBits = 6;
public static final int kDicLogSizeMin = 0;
// public static final int kDicLogSizeMax = 28;
// public static final int kDistTableSizeMax = kDicLogSizeMax * 2;
public static final int kNumLenToPosStatesBits = 2; // it's for speed optimization
public static final int kNumLenToPosStates = 1 << kNumLenToPosStatesBits;
public static final int kMatchMinLen = 2;
public static final int GetLenToPosState(int len)
{
len -= kMatchMinLen;
if (len < kNumLenToPosStates)
return len;
return (int)(kNumLenToPosStates - 1);
}
public static final int kNumAlignBits = 4;
public static final int kAlignTableSize = 1 << kNumAlignBits;
public static final int kAlignMask = (kAlignTableSize - 1);
public static final int kStartPosModelIndex = 4;
public static final int kEndPosModelIndex = 14;
public static final int kNumPosModels = kEndPosModelIndex - kStartPosModelIndex;
public static final int kNumFullDistances = 1 << (kEndPosModelIndex / 2);
public static final int kNumLitPosStatesBitsEncodingMax = 4;
public static final int kNumLitContextBitsMax = 8;
public static final int kNumPosStatesBitsMax = 4;
public static final int kNumPosStatesMax = (1 << kNumPosStatesBitsMax);
public static final int kNumPosStatesBitsEncodingMax = 4;
public static final int kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax);
public static final int kNumLowLenBits = 3;
public static final int kNumMidLenBits = 3;
public static final int kNumHighLenBits = 8;
public static final int kNumLowLenSymbols = 1 << kNumLowLenBits;
public static final int kNumMidLenSymbols = 1 << kNumMidLenBits;
public static final int kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols +
(1 << kNumHighLenBits);
public static final int kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1;
}

View File

@@ -0,0 +1,329 @@
package SevenZip.Compression.LZMA;
import SevenZip.Compression.RangeCoder.BitTreeDecoder;
import SevenZip.Compression.LZMA.Base;
import SevenZip.Compression.LZ.OutWindow;
import java.io.IOException;
public class Decoder
{
class LenDecoder
{
short[] m_Choice = new short[2];
BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
BitTreeDecoder m_HighCoder = new BitTreeDecoder(Base.kNumHighLenBits);
int m_NumPosStates = 0;
public void Create(int numPosStates)
{
for (; m_NumPosStates < numPosStates; m_NumPosStates++)
{
m_LowCoder[m_NumPosStates] = new BitTreeDecoder(Base.kNumLowLenBits);
m_MidCoder[m_NumPosStates] = new BitTreeDecoder(Base.kNumMidLenBits);
}
}
public void Init()
{
SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_Choice);
for (int posState = 0; posState < m_NumPosStates; posState++)
{
m_LowCoder[posState].Init();
m_MidCoder[posState].Init();
}
m_HighCoder.Init();
}
public int Decode(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, int posState) throws IOException
{
if (rangeDecoder.DecodeBit(m_Choice, 0) == 0)
return m_LowCoder[posState].Decode(rangeDecoder);
int symbol = Base.kNumLowLenSymbols;
if (rangeDecoder.DecodeBit(m_Choice, 1) == 0)
symbol += m_MidCoder[posState].Decode(rangeDecoder);
else
symbol += Base.kNumMidLenSymbols + m_HighCoder.Decode(rangeDecoder);
return symbol;
}
}
class LiteralDecoder
{
class Decoder2
{
short[] m_Decoders = new short[0x300];
public void Init()
{
SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_Decoders);
}
public byte DecodeNormal(SevenZip.Compression.RangeCoder.Decoder rangeDecoder) throws IOException
{
int symbol = 1;
do
symbol = (symbol << 1) | rangeDecoder.DecodeBit(m_Decoders, symbol);
while (symbol < 0x100);
return (byte)symbol;
}
public byte DecodeWithMatchByte(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, byte matchByte) throws IOException
{
int symbol = 1;
do
{
int matchBit = (matchByte >> 7) & 1;
matchByte <<= 1;
int bit = rangeDecoder.DecodeBit(m_Decoders, ((1 + matchBit) << 8) + symbol);
symbol = (symbol << 1) | bit;
if (matchBit != bit)
{
while (symbol < 0x100)
symbol = (symbol << 1) | rangeDecoder.DecodeBit(m_Decoders, symbol);
break;
}
}
while (symbol < 0x100);
return (byte)symbol;
}
}
Decoder2[] m_Coders;
int m_NumPrevBits;
int m_NumPosBits;
int m_PosMask;
public void Create(int numPosBits, int numPrevBits)
{
if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits)
return;
m_NumPosBits = numPosBits;
m_PosMask = (1 << numPosBits) - 1;
m_NumPrevBits = numPrevBits;
int numStates = 1 << (m_NumPrevBits + m_NumPosBits);
m_Coders = new Decoder2[numStates];
for (int i = 0; i < numStates; i++)
m_Coders[i] = new Decoder2();
}
public void Init()
{
int numStates = 1 << (m_NumPrevBits + m_NumPosBits);
for (int i = 0; i < numStates; i++)
m_Coders[i].Init();
}
Decoder2 GetDecoder(int pos, byte prevByte)
{
return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + ((prevByte & 0xFF) >>> (8 - m_NumPrevBits))];
}
}
OutWindow m_OutWindow = new OutWindow();
SevenZip.Compression.RangeCoder.Decoder m_RangeDecoder = new SevenZip.Compression.RangeCoder.Decoder();
short[] m_IsMatchDecoders = new short[Base.kNumStates << Base.kNumPosStatesBitsMax];
short[] m_IsRepDecoders = new short[Base.kNumStates];
short[] m_IsRepG0Decoders = new short[Base.kNumStates];
short[] m_IsRepG1Decoders = new short[Base.kNumStates];
short[] m_IsRepG2Decoders = new short[Base.kNumStates];
short[] m_IsRep0LongDecoders = new short[Base.kNumStates << Base.kNumPosStatesBitsMax];
BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates];
short[] m_PosDecoders = new short[Base.kNumFullDistances - Base.kEndPosModelIndex];
BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits);
LenDecoder m_LenDecoder = new LenDecoder();
LenDecoder m_RepLenDecoder = new LenDecoder();
LiteralDecoder m_LiteralDecoder = new LiteralDecoder();
int m_DictionarySize = -1;
int m_DictionarySizeCheck = -1;
int m_PosStateMask;
public Decoder()
{
for (int i = 0; i < Base.kNumLenToPosStates; i++)
m_PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits);
}
boolean SetDictionarySize(int dictionarySize)
{
if (dictionarySize < 0)
return false;
if (m_DictionarySize != dictionarySize)
{
m_DictionarySize = dictionarySize;
m_DictionarySizeCheck = Math.max(m_DictionarySize, 1);
m_OutWindow.Create(Math.max(m_DictionarySizeCheck, (1 << 12)));
}
return true;
}
boolean SetLcLpPb(int lc, int lp, int pb)
{
if (lc > Base.kNumLitContextBitsMax || lp > 4 || pb > Base.kNumPosStatesBitsMax)
return false;
m_LiteralDecoder.Create(lp, lc);
int numPosStates = 1 << pb;
m_LenDecoder.Create(numPosStates);
m_RepLenDecoder.Create(numPosStates);
m_PosStateMask = numPosStates - 1;
return true;
}
void Init() throws IOException
{
m_OutWindow.Init(false);
SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsMatchDecoders);
SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRep0LongDecoders);
SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepDecoders);
SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepG0Decoders);
SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepG1Decoders);
SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepG2Decoders);
SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_PosDecoders);
m_LiteralDecoder.Init();
int i;
for (i = 0; i < Base.kNumLenToPosStates; i++)
m_PosSlotDecoder[i].Init();
m_LenDecoder.Init();
m_RepLenDecoder.Init();
m_PosAlignDecoder.Init();
m_RangeDecoder.Init();
}
public boolean Code(java.io.InputStream inStream, java.io.OutputStream outStream,
long outSize) throws IOException
{
m_RangeDecoder.SetStream(inStream);
m_OutWindow.SetStream(outStream);
Init();
int state = Base.StateInit();
int rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0;
long nowPos64 = 0;
byte prevByte = 0;
while (outSize < 0 || nowPos64 < outSize)
{
int posState = (int)nowPos64 & m_PosStateMask;
if (m_RangeDecoder.DecodeBit(m_IsMatchDecoders, (state << Base.kNumPosStatesBitsMax) + posState) == 0)
{
LiteralDecoder.Decoder2 decoder2 = m_LiteralDecoder.GetDecoder((int)nowPos64, prevByte);
if (!Base.StateIsCharState(state))
prevByte = decoder2.DecodeWithMatchByte(m_RangeDecoder, m_OutWindow.GetByte(rep0));
else
prevByte = decoder2.DecodeNormal(m_RangeDecoder);
m_OutWindow.PutByte(prevByte);
state = Base.StateUpdateChar(state);
nowPos64++;
}
else
{
int len;
if (m_RangeDecoder.DecodeBit(m_IsRepDecoders, state) == 1)
{
len = 0;
if (m_RangeDecoder.DecodeBit(m_IsRepG0Decoders, state) == 0)
{
if (m_RangeDecoder.DecodeBit(m_IsRep0LongDecoders, (state << Base.kNumPosStatesBitsMax) + posState) == 0)
{
state = Base.StateUpdateShortRep(state);
len = 1;
}
}
else
{
int distance;
if (m_RangeDecoder.DecodeBit(m_IsRepG1Decoders, state) == 0)
distance = rep1;
else
{
if (m_RangeDecoder.DecodeBit(m_IsRepG2Decoders, state) == 0)
distance = rep2;
else
{
distance = rep3;
rep3 = rep2;
}
rep2 = rep1;
}
rep1 = rep0;
rep0 = distance;
}
if (len == 0)
{
len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + Base.kMatchMinLen;
state = Base.StateUpdateRep(state);
}
}
else
{
rep3 = rep2;
rep2 = rep1;
rep1 = rep0;
len = Base.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState);
state = Base.StateUpdateMatch(state);
int posSlot = m_PosSlotDecoder[Base.GetLenToPosState(len)].Decode(m_RangeDecoder);
if (posSlot >= Base.kStartPosModelIndex)
{
int numDirectBits = (posSlot >> 1) - 1;
rep0 = ((2 | (posSlot & 1)) << numDirectBits);
if (posSlot < Base.kEndPosModelIndex)
rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders,
rep0 - posSlot - 1, m_RangeDecoder, numDirectBits);
else
{
rep0 += (m_RangeDecoder.DecodeDirectBits(
numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits);
rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder);
if (rep0 < 0)
{
if (rep0 == -1)
break;
return false;
}
}
}
else
rep0 = posSlot;
}
if (rep0 >= nowPos64 || rep0 >= m_DictionarySizeCheck)
{
// m_OutWindow.Flush();
return false;
}
m_OutWindow.CopyBlock(rep0, len);
nowPos64 += len;
prevByte = m_OutWindow.GetByte(0);
}
}
m_OutWindow.Flush();
m_OutWindow.ReleaseStream();
m_RangeDecoder.ReleaseStream();
return true;
}
public boolean SetDecoderProperties(byte[] properties)
{
if (properties.length < 5)
return false;
int val = properties[0] & 0xFF;
int lc = val % 9;
int remainder = val / 9;
int lp = remainder % 5;
int pb = remainder / 5;
int dictionarySize = 0;
for (int i = 0; i < 4; i++)
dictionarySize += ((int)(properties[1 + i]) & 0xFF) << (i * 8);
if (!SetLcLpPb(lc, lp, pb))
return false;
return SetDictionarySize(dictionarySize);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
package SevenZip.Compression.RangeCoder;
public class BitTreeDecoder
{
short[] Models;
int NumBitLevels;
public BitTreeDecoder(int numBitLevels)
{
NumBitLevels = numBitLevels;
Models = new short[1 << numBitLevels];
}
public void Init()
{
Decoder.InitBitModels(Models);
}
public int Decode(Decoder rangeDecoder) throws java.io.IOException
{
int m = 1;
for (int bitIndex = NumBitLevels; bitIndex != 0; bitIndex--)
m = (m << 1) + rangeDecoder.DecodeBit(Models, m);
return m - (1 << NumBitLevels);
}
public int ReverseDecode(Decoder rangeDecoder) throws java.io.IOException
{
int m = 1;
int symbol = 0;
for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
{
int bit = rangeDecoder.DecodeBit(Models, m);
m <<= 1;
m += bit;
symbol |= (bit << bitIndex);
}
return symbol;
}
public static int ReverseDecode(short[] Models, int startIndex,
Decoder rangeDecoder, int NumBitLevels) throws java.io.IOException
{
int m = 1;
int symbol = 0;
for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
{
int bit = rangeDecoder.DecodeBit(Models, startIndex + m);
m <<= 1;
m += bit;
symbol |= (bit << bitIndex);
}
return symbol;
}
}

View File

@@ -0,0 +1,99 @@
package SevenZip.Compression.RangeCoder;
import java.io.IOException;
public class BitTreeEncoder
{
short[] Models;
int NumBitLevels;
public BitTreeEncoder(int numBitLevels)
{
NumBitLevels = numBitLevels;
Models = new short[1 << numBitLevels];
}
public void Init()
{
Decoder.InitBitModels(Models);
}
public void Encode(Encoder rangeEncoder, int symbol) throws IOException
{
int m = 1;
for (int bitIndex = NumBitLevels; bitIndex != 0; )
{
bitIndex--;
int bit = (symbol >>> bitIndex) & 1;
rangeEncoder.Encode(Models, m, bit);
m = (m << 1) | bit;
}
}
public void ReverseEncode(Encoder rangeEncoder, int symbol) throws IOException
{
int m = 1;
for (int i = 0; i < NumBitLevels; i++)
{
int bit = symbol & 1;
rangeEncoder.Encode(Models, m, bit);
m = (m << 1) | bit;
symbol >>= 1;
}
}
public int GetPrice(int symbol)
{
int price = 0;
int m = 1;
for (int bitIndex = NumBitLevels; bitIndex != 0; )
{
bitIndex--;
int bit = (symbol >>> bitIndex) & 1;
price += Encoder.GetPrice(Models[m], bit);
m = (m << 1) + bit;
}
return price;
}
public int ReverseGetPrice(int symbol)
{
int price = 0;
int m = 1;
for (int i = NumBitLevels; i != 0; i--)
{
int bit = symbol & 1;
symbol >>>= 1;
price += Encoder.GetPrice(Models[m], bit);
m = (m << 1) | bit;
}
return price;
}
public static int ReverseGetPrice(short[] Models, int startIndex,
int NumBitLevels, int symbol)
{
int price = 0;
int m = 1;
for (int i = NumBitLevels; i != 0; i--)
{
int bit = symbol & 1;
symbol >>>= 1;
price += Encoder.GetPrice(Models[startIndex + m], bit);
m = (m << 1) | bit;
}
return price;
}
public static void ReverseEncode(short[] Models, int startIndex,
Encoder rangeEncoder, int NumBitLevels, int symbol) throws IOException
{
int m = 1;
for (int i = 0; i < NumBitLevels; i++)
{
int bit = symbol & 1;
rangeEncoder.Encode(Models, startIndex + m, bit);
m = (m << 1) | bit;
symbol >>= 1;
}
}
}

View File

@@ -0,0 +1,88 @@
package SevenZip.Compression.RangeCoder;
import java.io.IOException;
public class Decoder
{
static final int kTopMask = ~((1 << 24) - 1);
static final int kNumBitModelTotalBits = 11;
static final int kBitModelTotal = (1 << kNumBitModelTotalBits);
static final int kNumMoveBits = 5;
int Range;
int Code;
java.io.InputStream Stream;
public final void SetStream(java.io.InputStream stream)
{
Stream = stream;
}
public final void ReleaseStream()
{
Stream = null;
}
public final void Init() throws IOException
{
Code = 0;
Range = -1;
for (int i = 0; i < 5; i++)
Code = (Code << 8) | Stream.read();
}
public final int DecodeDirectBits(int numTotalBits) throws IOException
{
int result = 0;
for (int i = numTotalBits; i != 0; i--)
{
Range >>>= 1;
int t = ((Code - Range) >>> 31);
Code -= Range & (t - 1);
result = (result << 1) | (1 - t);
if ((Range & kTopMask) == 0)
{
Code = (Code << 8) | Stream.read();
Range <<= 8;
}
}
return result;
}
public int DecodeBit(short[] probs, int index) throws IOException
{
int prob = probs[index];
int newBound = (Range >>> kNumBitModelTotalBits) * prob;
if ((Code ^ 0x80000000) < (newBound ^ 0x80000000))
{
Range = newBound;
probs[index] = (short)(prob + ((kBitModelTotal - prob) >>> kNumMoveBits));
if ((Range & kTopMask) == 0)
{
Code = (Code << 8) | Stream.read();
Range <<= 8;
}
return 0;
}
else
{
Range -= newBound;
Code -= newBound;
probs[index] = (short)(prob - ((prob) >>> kNumMoveBits));
if ((Range & kTopMask) == 0)
{
Code = (Code << 8) | Stream.read();
Range <<= 8;
}
return 1;
}
}
public static void InitBitModels(short[] probs)
{
for (int i = 0; i < probs.length; i++)
probs[i] = (kBitModelTotal >>> 1);
}
}

View File

@@ -0,0 +1,151 @@
package SevenZip.Compression.RangeCoder;
import java.io.IOException;
public class Encoder
{
static final int kTopMask = ~((1 << 24) - 1);
static final int kNumBitModelTotalBits = 11;
static final int kBitModelTotal = (1 << kNumBitModelTotalBits);
static final int kNumMoveBits = 5;
java.io.OutputStream Stream;
long Low;
int Range;
int _cacheSize;
int _cache;
long _position;
public void SetStream(java.io.OutputStream stream)
{
Stream = stream;
}
public void ReleaseStream()
{
Stream = null;
}
public void Init()
{
_position = 0;
Low = 0;
Range = -1;
_cacheSize = 1;
_cache = 0;
}
public void FlushData() throws IOException
{
for (int i = 0; i < 5; i++)
ShiftLow();
}
public void FlushStream() throws IOException
{
Stream.flush();
}
public void ShiftLow() throws IOException
{
int LowHi = (int)(Low >>> 32);
if (LowHi != 0 || Low < 0xFF000000L)
{
_position += _cacheSize;
int temp = _cache;
do
{
Stream.write(temp + LowHi);
temp = 0xFF;
}
while(--_cacheSize != 0);
_cache = (((int)Low) >>> 24);
}
_cacheSize++;
Low = (Low & 0xFFFFFF) << 8;
}
public void EncodeDirectBits(int v, int numTotalBits) throws IOException
{
for (int i = numTotalBits - 1; i >= 0; i--)
{
Range >>>= 1;
if (((v >>> i) & 1) == 1)
Low += Range;
if ((Range & Encoder.kTopMask) == 0)
{
Range <<= 8;
ShiftLow();
}
}
}
public long GetProcessedSizeAdd()
{
return _cacheSize + _position + 4;
}
static final int kNumMoveReducingBits = 2;
public static final int kNumBitPriceShiftBits = 6;
public static void InitBitModels(short[] probs)
{
for (int i = 0; i < probs.length; i++)
probs[i] = (kBitModelTotal >>> 1);
}
public void Encode(short[] probs, int index, int symbol) throws IOException
{
int prob = probs[index];
int newBound = (Range >>> kNumBitModelTotalBits) * prob;
if (symbol == 0)
{
Range = newBound;
probs[index] = (short)(prob + ((kBitModelTotal - prob) >>> kNumMoveBits));
}
else
{
Low += (newBound & 0xFFFFFFFFL);
Range -= newBound;
probs[index] = (short)(prob - ((prob) >>> kNumMoveBits));
}
if ((Range & kTopMask) == 0)
{
Range <<= 8;
ShiftLow();
}
}
private static int[] ProbPrices = new int[kBitModelTotal >>> kNumMoveReducingBits];
static
{
int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits);
for (int i = kNumBits - 1; i >= 0; i--)
{
int start = 1 << (kNumBits - i - 1);
int end = 1 << (kNumBits - i);
for (int j = start; j < end; j++)
ProbPrices[j] = (i << kNumBitPriceShiftBits) +
(((end - j) << kNumBitPriceShiftBits) >>> (kNumBits - i - 1));
}
}
static public int GetPrice(int Prob, int symbol)
{
return ProbPrices[(((Prob - symbol) ^ ((-symbol))) & (kBitModelTotal - 1)) >>> kNumMoveReducingBits];
}
static public int GetPrice0(int Prob)
{
return ProbPrices[Prob >>> kNumMoveReducingBits];
}
static public int GetPrice1(int Prob)
{
return ProbPrices[(kBitModelTotal - Prob) >>> kNumMoveReducingBits];
}
}

View File

@@ -0,0 +1,6 @@
package SevenZip;
public interface ICodeProgress
{
public void SetProgress(long inSize, long outSize);
}

View File

@@ -0,0 +1,253 @@
package SevenZip;
public class LzmaAlone
{
static public class CommandLine
{
public static final int kEncode = 0;
public static final int kDecode = 1;
public static final int kBenchmak = 2;
public int Command = -1;
public int NumBenchmarkPasses = 10;
public int DictionarySize = 1 << 23;
public boolean DictionarySizeIsDefined = false;
public int Lc = 3;
public int Lp = 0;
public int Pb = 2;
public int Fb = 128;
public boolean FbIsDefined = false;
public boolean Eos = false;
public int Algorithm = 2;
public int MatchFinder = 1;
public String InFile;
public String OutFile;
boolean ParseSwitch(String s)
{
if (s.startsWith("d"))
{
DictionarySize = 1 << Integer.parseInt(s.substring(1));
DictionarySizeIsDefined = true;
}
else if (s.startsWith("fb"))
{
Fb = Integer.parseInt(s.substring(2));
FbIsDefined = true;
}
else if (s.startsWith("a"))
Algorithm = Integer.parseInt(s.substring(1));
else if (s.startsWith("lc"))
Lc = Integer.parseInt(s.substring(2));
else if (s.startsWith("lp"))
Lp = Integer.parseInt(s.substring(2));
else if (s.startsWith("pb"))
Pb = Integer.parseInt(s.substring(2));
else if (s.startsWith("eos"))
Eos = true;
else if (s.startsWith("mf"))
{
String mfs = s.substring(2);
if (mfs.equals("bt2"))
MatchFinder = 0;
else if (mfs.equals("bt4"))
MatchFinder = 1;
else if (mfs.equals("bt4b"))
MatchFinder = 2;
else
return false;
}
else
return false;
return true;
}
public boolean Parse(String[] args) throws Exception
{
int pos = 0;
boolean switchMode = true;
for (int i = 0; i < args.length; i++)
{
String s = args[i];
if (s.length() == 0)
return false;
if (switchMode)
{
if (s.compareTo("--") == 0)
{
switchMode = false;
continue;
}
if (s.charAt(0) == '-')
{
String sw = s.substring(1).toLowerCase();
if (sw.length() == 0)
return false;
try
{
if (!ParseSwitch(sw))
return false;
}
catch (NumberFormatException e)
{
return false;
}
continue;
}
}
if (pos == 0)
{
if (s.equalsIgnoreCase("e"))
Command = kEncode;
else if (s.equalsIgnoreCase("d"))
Command = kDecode;
else if (s.equalsIgnoreCase("b"))
Command = kBenchmak;
else
return false;
}
else if(pos == 1)
{
if (Command == kBenchmak)
{
try
{
NumBenchmarkPasses = Integer.parseInt(s);
if (NumBenchmarkPasses < 1)
return false;
}
catch (NumberFormatException e)
{
return false;
}
}
else
InFile = s;
}
else if(pos == 2)
OutFile = s;
else
return false;
pos++;
continue;
}
return true;
}
}
static void PrintHelp()
{
System.out.println(
"\nUsage: LZMA <e|d> [<switches>...] inputFile outputFile\n" +
" e: encode file\n" +
" d: decode file\n" +
" b: Benchmark\n" +
"<Switches>\n" +
// " -a{N}: set compression mode - [0, 1], default: 1 (max)\n" +
" -d{N}: set dictionary - [0,28], default: 23 (8MB)\n" +
" -fb{N}: set number of fast bytes - [5, 273], default: 128\n" +
" -lc{N}: set number of literal context bits - [0, 8], default: 3\n" +
" -lp{N}: set number of literal pos bits - [0, 4], default: 0\n" +
" -pb{N}: set number of pos bits - [0, 4], default: 2\n" +
" -mf{MF_ID}: set Match Finder: [bt2, bt4], default: bt4\n" +
" -eos: write End Of Stream marker\n"
);
}
public static void main(String[] args) throws Exception
{
System.out.println("\nLZMA (Java) 4.61 2008-11-23\n");
if (args.length < 1)
{
PrintHelp();
return;
}
CommandLine params = new CommandLine();
if (!params.Parse(args))
{
System.out.println("\nIncorrect command");
return;
}
if (params.Command == CommandLine.kBenchmak)
{
int dictionary = (1 << 21);
if (params.DictionarySizeIsDefined)
dictionary = params.DictionarySize;
if (params.MatchFinder > 1)
throw new Exception("Unsupported match finder");
SevenZip.LzmaBench.LzmaBenchmark(params.NumBenchmarkPasses, dictionary);
}
else if (params.Command == CommandLine.kEncode || params.Command == CommandLine.kDecode)
{
java.io.File inFile = new java.io.File(params.InFile);
java.io.File outFile = new java.io.File(params.OutFile);
java.io.BufferedInputStream inStream = new java.io.BufferedInputStream(new java.io.FileInputStream(inFile));
java.io.BufferedOutputStream outStream = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outFile));
boolean eos = false;
if (params.Eos)
eos = true;
if (params.Command == CommandLine.kEncode)
{
SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder();
if (!encoder.SetAlgorithm(params.Algorithm))
throw new Exception("Incorrect compression mode");
if (!encoder.SetDictionarySize(params.DictionarySize))
throw new Exception("Incorrect dictionary size");
if (!encoder.SetNumFastBytes(params.Fb))
throw new Exception("Incorrect -fb value");
if (!encoder.SetMatchFinder(params.MatchFinder))
throw new Exception("Incorrect -mf value");
if (!encoder.SetLcLpPb(params.Lc, params.Lp, params.Pb))
throw new Exception("Incorrect -lc or -lp or -pb value");
encoder.SetEndMarkerMode(eos);
encoder.WriteCoderProperties(outStream);
long fileSize;
if (eos)
fileSize = -1;
else
fileSize = inFile.length();
for (int i = 0; i < 8; i++)
outStream.write((int)(fileSize >>> (8 * i)) & 0xFF);
encoder.Code(inStream, outStream, -1, -1, null);
}
else
{
int propertiesSize = 5;
byte[] properties = new byte[propertiesSize];
if (inStream.read(properties, 0, propertiesSize) != propertiesSize)
throw new Exception("input .lzma file is too short");
SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder();
if (!decoder.SetDecoderProperties(properties))
throw new Exception("Incorrect stream properties");
long outSize = 0;
for (int i = 0; i < 8; i++)
{
int v = inStream.read();
if (v < 0)
throw new Exception("Can't read stream size");
outSize |= ((long)v) << (8 * i);
}
if (!decoder.Code(inStream, outStream, outSize))
throw new Exception("Error in data stream");
}
outStream.flush();
outStream.close();
inStream.close();
}
else
throw new Exception("Incorrect command");
return;
}
}

View File

@@ -0,0 +1,392 @@
package SevenZip;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class LzmaBench
{
static final int kAdditionalSize = (1 << 21);
static final int kCompressedAdditionalSize = (1 << 10);
static class CRandomGenerator
{
int A1;
int A2;
public CRandomGenerator() { Init(); }
public void Init() { A1 = 362436069; A2 = 521288629; }
public int GetRnd()
{
return
((A1 = 36969 * (A1 & 0xffff) + (A1 >>> 16)) << 16) ^
((A2 = 18000 * (A2 & 0xffff) + (A2 >>> 16)));
}
};
static class CBitRandomGenerator
{
CRandomGenerator RG = new CRandomGenerator();
int Value;
int NumBits;
public void Init()
{
Value = 0;
NumBits = 0;
}
public int GetRnd(int numBits)
{
int result;
if (NumBits > numBits)
{
result = Value & ((1 << numBits) - 1);
Value >>>= numBits;
NumBits -= numBits;
return result;
}
numBits -= NumBits;
result = (Value << numBits);
Value = RG.GetRnd();
result |= Value & (((int)1 << numBits) - 1);
Value >>>= numBits;
NumBits = 32 - numBits;
return result;
}
};
static class CBenchRandomGenerator
{
CBitRandomGenerator RG = new CBitRandomGenerator();
int Pos;
int Rep0;
public int BufferSize;
public byte[] Buffer = null;
public CBenchRandomGenerator() { }
public void Set(int bufferSize)
{
Buffer = new byte[bufferSize];
Pos = 0;
BufferSize = bufferSize;
}
int GetRndBit() { return RG.GetRnd(1); }
int GetLogRandBits(int numBits)
{
int len = RG.GetRnd(numBits);
return RG.GetRnd((int)len);
}
int GetOffset()
{
if (GetRndBit() == 0)
return GetLogRandBits(4);
return (GetLogRandBits(4) << 10) | RG.GetRnd(10);
}
int GetLen1() { return RG.GetRnd(1 + (int)RG.GetRnd(2)); }
int GetLen2() { return RG.GetRnd(2 + (int)RG.GetRnd(2)); }
public void Generate()
{
RG.Init();
Rep0 = 1;
while (Pos < BufferSize)
{
if (GetRndBit() == 0 || Pos < 1)
Buffer[Pos++] = (byte)(RG.GetRnd(8));
else
{
int len;
if (RG.GetRnd(3) == 0)
len = 1 + GetLen1();
else
{
do
Rep0 = GetOffset();
while (Rep0 >= Pos);
Rep0++;
len = 2 + GetLen2();
}
for (int i = 0; i < len && Pos < BufferSize; i++, Pos++)
Buffer[Pos] = Buffer[Pos - Rep0];
}
}
}
};
static class CrcOutStream extends java.io.OutputStream
{
public CRC CRC = new CRC();
public void Init()
{
CRC.Init();
}
public int GetDigest()
{
return CRC.GetDigest();
}
public void write(byte[] b)
{
CRC.Update(b);
}
public void write(byte[] b, int off, int len)
{
CRC.Update(b, off, len);
}
public void write(int b)
{
CRC.UpdateByte(b);
}
};
static class MyOutputStream extends java.io.OutputStream
{
byte[] _buffer;
int _size;
int _pos;
public MyOutputStream(byte[] buffer)
{
_buffer = buffer;
_size = _buffer.length;
}
public void reset()
{
_pos = 0;
}
public void write(int b) throws IOException
{
if (_pos >= _size)
throw new IOException("Error");
_buffer[_pos++] = (byte)b;
}
public int size()
{
return _pos;
}
};
static class MyInputStream extends java.io.InputStream
{
byte[] _buffer;
int _size;
int _pos;
public MyInputStream(byte[] buffer, int size)
{
_buffer = buffer;
_size = size;
}
public void reset()
{
_pos = 0;
}
public int read()
{
if (_pos >= _size)
return -1;
return _buffer[_pos++] & 0xFF;
}
};
static class CProgressInfo implements ICodeProgress
{
public long ApprovedStart;
public long InSize;
public long Time;
public void Init()
{ InSize = 0; }
public void SetProgress(long inSize, long outSize)
{
if (inSize >= ApprovedStart && InSize == 0)
{
Time = System.currentTimeMillis();
InSize = inSize;
}
}
}
static final int kSubBits = 8;
static int GetLogSize(int size)
{
for (int i = kSubBits; i < 32; i++)
for (int j = 0; j < (1 << kSubBits); j++)
if (size <= ((1) << i) + (j << (i - kSubBits)))
return (i << kSubBits) + j;
return (32 << kSubBits);
}
static long MyMultDiv64(long value, long elapsedTime)
{
long freq = 1000; // ms
long elTime = elapsedTime;
while (freq > 1000000)
{
freq >>>= 1;
elTime >>>= 1;
}
if (elTime == 0)
elTime = 1;
return value * freq / elTime;
}
static long GetCompressRating(int dictionarySize, long elapsedTime, long size)
{
long t = GetLogSize(dictionarySize) - (18 << kSubBits);
long numCommandsForOne = 1060 + ((t * t * 10) >> (2 * kSubBits));
long numCommands = (long)(size) * numCommandsForOne;
return MyMultDiv64(numCommands, elapsedTime);
}
static long GetDecompressRating(long elapsedTime, long outSize, long inSize)
{
long numCommands = inSize * 220 + outSize * 20;
return MyMultDiv64(numCommands, elapsedTime);
}
static long GetTotalRating(
int dictionarySize,
long elapsedTimeEn, long sizeEn,
long elapsedTimeDe,
long inSizeDe, long outSizeDe)
{
return (GetCompressRating(dictionarySize, elapsedTimeEn, sizeEn) +
GetDecompressRating(elapsedTimeDe, inSizeDe, outSizeDe)) / 2;
}
static void PrintValue(long v)
{
String s = "";
s += v;
for (int i = 0; i + s.length() < 6; i++)
System.out.print(" ");
System.out.print(s);
}
static void PrintRating(long rating)
{
PrintValue(rating / 1000000);
System.out.print(" MIPS");
}
static void PrintResults(
int dictionarySize,
long elapsedTime,
long size,
boolean decompressMode, long secondSize)
{
long speed = MyMultDiv64(size, elapsedTime);
PrintValue(speed / 1024);
System.out.print(" KB/s ");
long rating;
if (decompressMode)
rating = GetDecompressRating(elapsedTime, size, secondSize);
else
rating = GetCompressRating(dictionarySize, elapsedTime, size);
PrintRating(rating);
}
static public int LzmaBenchmark(int numIterations, int dictionarySize) throws Exception
{
if (numIterations <= 0)
return 0;
if (dictionarySize < (1 << 18))
{
System.out.println("\nError: dictionary size for benchmark must be >= 18 (256 KB)");
return 1;
}
System.out.print("\n Compressing Decompressing\n\n");
SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder();
SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder();
if (!encoder.SetDictionarySize(dictionarySize))
throw new Exception("Incorrect dictionary size");
int kBufferSize = dictionarySize + kAdditionalSize;
int kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize;
ByteArrayOutputStream propStream = new ByteArrayOutputStream();
encoder.WriteCoderProperties(propStream);
byte[] propArray = propStream.toByteArray();
decoder.SetDecoderProperties(propArray);
CBenchRandomGenerator rg = new CBenchRandomGenerator();
rg.Set(kBufferSize);
rg.Generate();
CRC crc = new CRC();
crc.Init();
crc.Update(rg.Buffer, 0, rg.BufferSize);
CProgressInfo progressInfo = new CProgressInfo();
progressInfo.ApprovedStart = dictionarySize;
long totalBenchSize = 0;
long totalEncodeTime = 0;
long totalDecodeTime = 0;
long totalCompressedSize = 0;
MyInputStream inStream = new MyInputStream(rg.Buffer, rg.BufferSize);
byte[] compressedBuffer = new byte[kCompressedBufferSize];
MyOutputStream compressedStream = new MyOutputStream(compressedBuffer);
CrcOutStream crcOutStream = new CrcOutStream();
MyInputStream inputCompressedStream = null;
int compressedSize = 0;
for (int i = 0; i < numIterations; i++)
{
progressInfo.Init();
inStream.reset();
compressedStream.reset();
encoder.Code(inStream, compressedStream, -1, -1, progressInfo);
long encodeTime = System.currentTimeMillis() - progressInfo.Time;
if (i == 0)
{
compressedSize = compressedStream.size();
inputCompressedStream = new MyInputStream(compressedBuffer, compressedSize);
}
else if (compressedSize != compressedStream.size())
throw (new Exception("Encoding error"));
if (progressInfo.InSize == 0)
throw (new Exception("Internal ERROR 1282"));
long decodeTime = 0;
for (int j = 0; j < 2; j++)
{
inputCompressedStream.reset();
crcOutStream.Init();
long outSize = kBufferSize;
long startTime = System.currentTimeMillis();
if (!decoder.Code(inputCompressedStream, crcOutStream, outSize))
throw (new Exception("Decoding Error"));;
decodeTime = System.currentTimeMillis() - startTime;
if (crcOutStream.GetDigest() != crc.GetDigest())
throw (new Exception("CRC Error"));
}
long benchSize = kBufferSize - (long)progressInfo.InSize;
PrintResults(dictionarySize, encodeTime, benchSize, false, 0);
System.out.print(" ");
PrintResults(dictionarySize, decodeTime, kBufferSize, true, compressedSize);
System.out.println();
totalBenchSize += benchSize;
totalEncodeTime += encodeTime;
totalDecodeTime += decodeTime;
totalCompressedSize += compressedSize;
}
System.out.println("---------------------------------------------------");
PrintResults(dictionarySize, totalEncodeTime, totalBenchSize, false, 0);
System.out.print(" ");
PrintResults(dictionarySize, totalDecodeTime,
kBufferSize * (long)numIterations, true, totalCompressedSize);
System.out.println(" Average");
return 0;
}
}

View File

@@ -0,0 +1,39 @@
-$A8
-$B-
-$C+
-$D+
-$E-
-$F-
-$G+
-$H+
-$I+
-$J-
-$K-
-$L+
-$M-
-$N+
-$O+
-$P+
-$Q-
-$R-
-$S-
-$T-
-$U-
-$V+
-$W-
-$X+
-$YD
-$Z1
-cg
-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
-H+
-W+
-M
-$M16384,1048576
-K$00400000
-E"."
-LE"c:\program files (x86)\borland\delphi7\Projects\Bpl"
-LN"c:\program files (x86)\borland\delphi7\Projects\Bpl"
-w-UNSAFE_TYPE
-w-UNSAFE_CODE
-w-UNSAFE_CAST

138
libsrc/Swf2Exe/Swf2Exe.dof Normal file
View File

@@ -0,0 +1,138 @@
[FileVersion]
Version=7.0
[Compiler]
A=8
B=0
C=1
D=1
E=0
F=0
G=1
H=1
I=1
J=0
K=0
L=1
M=0
N=1
O=1
P=1
Q=0
R=0
S=0
T=0
U=0
V=1
W=0
X=1
Y=1
Z=1
ShowHints=1
ShowWarnings=1
UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
NamespacePrefix=
SymbolDeprecated=1
SymbolLibrary=1
SymbolPlatform=1
UnitLibrary=1
UnitPlatform=1
UnitDeprecated=1
HResultCompat=1
HidingMember=1
HiddenVirtual=1
Garbage=1
BoundsError=1
ZeroNilCompat=1
StringConstTruncated=1
ForLoopVarVarPar=1
TypedConstVarPar=1
AsgToTypedConst=1
CaseLabelRange=1
ForVariable=1
ConstructingAbstract=1
ComparisonFalse=1
ComparisonTrue=1
ComparingSignedUnsigned=1
CombiningSignedUnsigned=1
UnsupportedConstruct=1
FileOpen=1
FileOpenUnitSrc=1
BadGlobalSymbol=1
DuplicateConstructorDestructor=1
InvalidDirective=1
PackageNoLink=1
PackageThreadVar=1
ImplicitImport=1
HPPEMITIgnored=1
NoRetVal=1
UseBeforeDef=1
ForLoopVarUndef=1
UnitNameMismatch=1
NoCFGFileFound=1
MessageDirective=1
ImplicitVariants=1
UnicodeToLocale=1
LocaleToUnicode=1
ImagebaseMultiple=1
SuspiciousTypecast=1
PrivatePropAccessor=1
UnsafeType=0
UnsafeCode=0
UnsafeCast=0
[Linker]
MapFile=0
OutputObjs=0
ConsoleApp=1
DebugInfo=0
RemoteSymbols=0
MinStackSize=16384
MaxStackSize=1048576
ImageBase=4194304
ExeDescription=
[Directories]
OutputDir=.
UnitOutputDir=
PackageDLLOutputDir=
PackageDCPOutputDir=
SearchPath=
Packages=vcl;rtl;vclx;VclSmp;vclshlctrls
Conditionals=
DebugSourceDirs=
UsePackages=0
[Parameters]
RunParams=
HostApplication=
Launcher=
UseLauncher=0
DebugCWD=
[Version Info]
IncludeVerInfo=0
AutoIncBuild=0
MajorVer=1
MinorVer=0
Release=0
Build=0
Debug=0
PreRelease=0
Special=0
Private=0
DLL=0
Locale=1029
CodePage=1250
[Version Info Keys]
CompanyName=
FileDescription=
FileVersion=1.0.0.0
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=1.0.0.0
Comments=
[HistoryLists\hlUnitAliases]
Count=1
Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
[HistoryLists\hlOutputDirectorry]
Count=1
Item0=.

View File

@@ -0,0 +1,16 @@
program Swf2Exe;
uses
Forms,
Windows,
Dialogs,
uMain in 'uMain.pas' {frmMain};
{$R *.res}
begin
Application.Initialize;
Application.Title := 'FFDec Flash Player';
Application.CreateForm(TfrmMain, frmMain);
Application.Run;
end.

BIN
libsrc/Swf2Exe/Swf2Exe.res Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

651
libsrc/Swf2Exe/uMain.dfm Normal file
View File

@@ -0,0 +1,651 @@
object frmMain: TfrmMain
Left = 323
Top = 163
Width = 1007
Height = 661
Caption = 'FFDec Flash Player'
Color = clWhite
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
Icon.Data = {
0000010004000000000001000800EC310000460000003030000001000800A80E
0000323200002020000001000800A8080000DA40000010100000010008006805
00008249000089504E470D0A1A0A0000000D4948445200000100000001000806
0000005C72A866000031B34944415478DAED9D0B7C14D5F5C70F8F3C8090F008
041523521031451E159AF8A041FEB4F28F88429428A08912B51A54A80F94FEED
940AA258F081CADFA841010D1A10458A1695482B49E15F40298280881435BC02
84040328F33FBF95B50177E6DED99DDD99D9BDDFCF673EBBB0B39BBBB373CE3D
E7DC73CE6D448AA8E4FD8913F5FA8307E9686DADEF38C2CF8F1C3A4447F9F03D
E2FFF911C4B76C49F149499480473EF0989092E2FB3F1C89FC7CE0E4C98D9CFE
4E0AFB513FAA078170EFFDEC33DAC7C7DE2D5BE8787DBD23E3689C9848A95DBB
52DB6EDD28950FA524BC87FAC15CCC5B85853F0A7ADDAE5D4E0FC7122DD2D27E
540C571417ABFBCCA5A81FC645BC326488BE7DF9729F791E8DC09DE83460005D
B778B1BAEF5C82FA211CA474D8307D7B7939D557573B3D1447486CD3863A6567
53DEC285EA3E740875E123C8EB7979BE19DE6BE67CA480DB000BE1EAD252755F
460875A1C3CCCB8306E9DB962D737A189EA4F3A04174FDB265EA1E0D23EAE286
8137F2F3F58D656551EBCB471AC40EBAE7E6D255B367ABFBD566D405B5092CCD
7D3C670ED5ECD8E1D8185A356942CD1B35A284C68D299E1F7124F21177E27933
FEFF383E2F911F134E3C07C7F83872FC38D5F381E7DFF2E3515DA7637CD4F371
F4C481730EF3E381EFBF77EC3B26A7A753CFD1A3D592A34DA88B1822C59999FA
57959511FB7BCD5890539B36F51D6D59E053F9687BE27924D9C74A60DF77DFD1
5E3CF2B117CFF9F8961544A4382333930A2B2BD53D1C02EAE20501D6E7D7CD9E
4DC78F1D0BDBDFC0ACDD312E8E4E83B043D04F083D667437038BC1AF0CA01CBE
E1C79D7C9D8E85513134E6EBD42B3F5FE51B0481BA601658306A94BE61FEFCB0
083E04BE030BF8D9F1F1D4858F347E1EE77261B7C2177CCDBE3C7A94BEE0A38A
95423814021441C68811347CEEDCE8B97061465D2809E6E7E6FA827A767326DF
B0387E9690401DA34CE0CD80F0EF6425F0F99123B49D15C2D7FCDC6E10341C51
56161B173404D40532615E4E8EBE65C912DB3E0FFEFBCF1313A90B0B7C2716FC
581178115008DBD942D8CA0AE15FF5F5B6C611BAE6E4D0C8254BD48536405D98
00CC1E30C097B0630710F27359E07B356B4667C7C585FE813100DC8575DF7E4B
9B5821D8E52A20C1287FF97275BF9F82BA200D783E2B4BDF595161CB67C197EF
C1829FC133BE9AE98303C2BF812D82F5AC08103BB0838E595934A6A242FD2027
501782595450A0AF2B2909F973DAB31FDF8305BE271F498D1B3BFDB5A28ADAE3
C7E96328033E76DB1033E8555040579694C4FCFD1FF317606A9B367A28C53898
DD7BB1C0F766131F517C45F8C12AC25A7611D6B13208C5454031D284EAEA9896
8198FDF2CFF5EBA77FBD6A55D0EF87E0F765A1CF6CDE5CCDF60E01ABA0F2F061
5ACDCA201445707ABF7E74F3AA5531290B31F7A5971415E9AB67CE0CFAFD88E4
F765A187E0BB3D29275640F2914F11F011CA0A42DFA222CA993933A67ED498FA
B2D3D2D2F4604B7121F817B568E19BF55550CF9DC00A8035F0515D5DD08AA045
870E744F5555CCFCC031F1454BB2B3F52FCBCB837A2F0A6CFAF36CAFA2F9DE01
8A00F1810AB608822D5C3A2B3B9B0ACACBA3FE078FFA2F382931510FA669A65F
F0B17EAFF02EC8275811A42240D3D307EBEBA35A46A2F6CB059BBE8B593E8B05
FF223ED48C1F1DC022F88895002C82608285D19C561C955FEA892E5DF4FD5BB7
5A7E1F9277FEBB65CB8897D62A2203CA96FF72E850504945ADBB74A13BB76E8D
3A7989BA2F34292E4EB75AAD87001F041F7EBE22FA4176211481D54021AA0D1F
3C762CAA64266ABE0C3AEC6E5AB8D0F2FB10D51F80DD6F94B91F53C01578AFB6
D6B76A609573870D8B9A4EC651F1251EEFDC593FB06D9BA5F7A00C7730CFFA2A
7B2FB64156E1DB3535964B925B75EE4C776DDBE679F9F1FC17B06AF223B03798
677C15DD573404AB054BD922B012248C0697C0B3830F26CA7F3ACFF6D7A4A450
B20AF2290250F3FDF7F4DAC18396AD012FAF127872D0C1D4EBC3D787C9AF5088
587AE890E5D88057FB0D786EC0B37AF7D6ABD6AE953E1F11FEA1C9C9744E4282
D343577888CD478ED09B353596560A3AF4EE4DB7AE5DEB2999F2D460ADAEEF2B
935F110AC1B8045ECB17F0CC40A775E8A0D75555499F7F61F3E6F45F49494E0F
5B110560B970E5E1C3D2E77BA9A0C813839C9294A4CB6EB3A54C7E4538B0EA12
603BB3076A6B5D2F5FAE1FA095621EB4E41AD5AA956AD0A138097F1BF24DFCB8
95CDFAADFCFCE77CAF145A5C0A864BF00ABB04B22DC9BC504CE4EAC15959E347
1EFFD5ECEFAB8CBED8E4C0F1E3B48D8FCF5948716CE763CB89E7DFF071EA5D74
330BFFAC205685A04C4A5909C8D613B83D57C0B503D388A4C3AFDDD9DC87F02B
6283AF58D03F64015C81F6E13C1B6346AFB698D7FF5B56004F07B92C0C25B088
DD818DEC16C8A2B954D65C3928CD82F0ABF5FDE807FB047CC8B378F909A1DF6E
C3EEC4C15A000DB19A2FA0B950DE5C37202B667F768B16D49F0F4574B19167F4
0F21F438B09720CFF876138A05D09015757554CE870C6E74075C35182B01BF2B
F8C753F9FCD10384FE25FEED4BD9ACDE61C30C2FC20E0BC00FEA08DE626B4006
B705065D3310D9A53E14F3E4B2BFDF353EDEE9212B42640FCFEC10F812F4F80F
C306A166D86501F8C132E1829A1AA96222372D11BA6210B2493E58E3BFB6552B
EAA8F6D8F32C68E1BD98CDFAB93CDBBF83BDFF1C1A879D16809F9DECB2BC7AE0
8054AE805B92851C1F806C7A2F847F74EBD6AA7EDFA354B070C0C47F958F4336
EEFEDB90389E183A75EAE43BBA76ED4AEBD6ADA3952B57063CD76E0BC00FFA0B
CCD9BF5F4A09B8216DD8D13F2E5BD803B37FB49AF93DC7376CE2CF62F37E0E0B
BD1D91FB86B464E1CDCCCCA45FFDEA57949595455D5898CE3CF34C6AD4200F64
C28409F4C8238F047C7F382C003FB004E6B02520E30E385D40E4D81FB652D29B
C73EBF4AEDF50E58A79F525747B359F043D9A9A721EDDAB5F3097A7676365D7C
F1C5D4B3674F8A17C481CC1440B82C003F880920614806274B891DF9A3569A79
A868BF77B053F021F003070EA44B2EB9C427F4DDBB773F697697C1290BC08F95
D501A79A8A38A20034C9441FB5CEEF0DFC82FF3C36E90CE173E0C30F1932846E
B8E106BAECB2CB8433BC08272D003F56F2043407E431E27F5036D14765F8B91F
44F435BEB96760C38D103EA75FBF7E3EA1CFCBCBA3366DDAD8363EA72D003FB2
19834EE40844F48FC976EF55B9FDEE6711FBB877D5D6069DB4939E9E4E23478E
A4FCFC7C3AE79C73C232463758007E5E3F7850AA7620D2DD8623F68764FBF6A3
AA0F413FB52D973B415E7E11CFFA4B83D85D0791FB2BAFBCD227F403060CB0EC
D35BC52D16801FAC0CC854114672DF8188499926E1F7A39EFFA6D6AD95F0BB10
98FBD3D9D49FCC87D5001F027877DF7DB7CFC46FDEBC79C4C6EC260B00E01ACE
DEBF5FAA9F801621D98CC81F91F1FB91E8730BFB7FAA7F9FFB409EFE556CC26E
B668EE43F0274E9C48D75D775DD867FB40B8CD0200682AF2BFD5D542251AA9C2
A1B0FF01D94C3FA4F8AAFC7EF7F14A7D3D155ADC47CF69C1F7E3360BC0CF1676
0390322C2212998261FD70D9F57ED5C0D37D208B6D6C6D2D3D67A1DEFDB4D34E
A369D3A6392EF87EDC6801F8916D341AEEFC80B0FE4A9A84DF8F3DFA0AD8EF57
B80704FA86F3AC2F5BA187F5FB5B6FBD951E7AE8214A4E4E767AF83FE2560BC0
CFF3EC0AC8B41CD7C228A761FB6099DA7EE5F7BB0F2CEFDD5053235DB08335FC
679F7D96FAF4E9E3F4D07F829B2D00201D0F08637E40583EB4243B5BFFB2BC5C
789ECAF17717C8E6FBBD64D65A6BB6DA205C63C68C7185B91F08B75B0040B666
E0ACEC6C2A282FB7FD4287E597D3244C7FE5F7BB8B89EC933E2CB9F945FFFEFD
A9B4B4D4E7F3BB19B75B007E64E3015A18E4D5F60F9C9696A6D7EDDA657A0EB6
EC1A6363CAA722347EC737E00C49E1BFEFBEFBE8E1871F76EDACDF102F58007E
64E2012DD2D2E89E5DBB6CBDF0B67ED892A2227DF5CC99A6E720C9E776E5F7BB
863B0F1DA2A72422FD30F9E7CD9B4783070F767AC8D278C5020088073CCD4A40
D443A06F5111E5CC9C699BDCDAAA003409D35F95F7BA87DB59F89F95107E04FA
5E7FFD755FFEBE97F092050064CB87351BE5D6B60F7AAE5F3FFDEB55AB4CCF51
A6BF7BB8956F349935FEC2C2429AC9565DA8A5B94EE0250BC04FC9FEFDF46F41
D6ECE9AC906F5EB5CA16D9B54D016812B3FFCD2CFCAAA79FF3C8CEFCE3C68DA3
E9D3A73B3DDCA0F19A0500D053F0397605446836C9AE2D1F32B54D1BBD5E3068
55DFEF0ED09F0FEBFC22EEBFFF7E9A32658AD3C30D092F5A0040A67F40224FA6
13AAAB4396DF903F60514181BEAEA4C4F41C24FCDC959AAAAAFC1C660DCF2E97
4874AC45461F72F9BD8E172D008040E0E37BF70A7FA75E050574654949484215
B2446A12A6FFF0E464CA484CB4FB3A292C80DD73FBB0F08BBAF3C2E487E91F0D
78D502009B8E1CA1D7241284B4106538A4373F9F95A5EFACA8303D070D3ED0D2
5BE12C39070E089B783CF1C41374C71D77383D54DBF0AA05E047A68148C7AC2C
1A535111B41C87A40034C1EC0F931F81BFB66ACDDF51FE5057477F12A4F8DE76
DB6DF4F4D34F3B3D545BF1B20500F6B3B5364B2237400B418E837EA34C5F7F74
F4CD565D7D1DE56D3625AF109892175E78212DE7DFD28B4B7D6678DD0200E828
BC42A0BC43D957206805A00966FF563CEBFF96677F15F8730EF8FDDD7806D963
B2BD36F2F9FFF9CF7FBA3EAF3F18BC6E0100CCFECFF26F784010BBD18294E5A0
DE342F2747DFB26489E9392AE3CF794439FEA8E3FFF0C30F7D3BEE4423D16001
00990CC1AE39393472C912CBF21C9402D02466FF3BDAB68DE435529C02FAF85D
2058F283CF0FDF3F1A387AF428FDFDEF7FA7F2F272DAB06103AD5FBF9EB66CD9
6278BE572C003F4FEEDB17162BC0F21B64DA7C21E1A7AF9AFD1D4514F52F2828
A0175F7CD1E96186C461B66EDE7BEF3D5AB46891EFD8CF0A4F162F5900008941
4B05564030EDC32C2B004D30FB23E9E79E76ED9CB8468A132C63C1FF8D49D349
F8FB5BB76E8D688B6EBBD0D9A259BC7831BDF4D24BB46CD9323A24B9F7DEA978
CD0200D3F6EC112607691665DAD2C90B468DD2D7CF9D6B7A0E9A7C5CE8C11B2B
9AF87975357D6A525B3E977F43ECCAE32520F8A848449622CCFB50F19A0500D0
3404CD43CCE8316A140D9F3B575AAE2D2900517F7F95F2EB3CA8EDBFD36456C4
921F7C652F34F40010FC3973E6D0D4A95369E3C68DB67DAE172D00991461ABFB
09489FF85661A1BEA6B8D8F41CB5EEEF2C58F6EBB26F1F551BDC20717C7CB46A
15F5EDDBD7E9A14AF1C1071FF8BA0D9B05F382C58B160090C90BE85358485714
174BC9B6B40210CDFE98F5C7F1EC9FE89199251A1165FC7925F0575353E3AB46
7CE6996742FE2C28BDB39B34A1F39B36A55E7C9CC74737FE77778F96A5637BB1
196C051CB3C90A9096564D10FC534D3E9DA713CFFE46BBF562634E04FEDAB76F
EFF4304DF9EB5FFF4AB7DC720B6DDFBE3DA8F7A7B3700F8D8FA70B58083220F0
FCEF689B94649A886A92B22D75527166A6FE5565A5E1EB98FDC7B66D4B498D1B
3B7D6D62162CF9E59844FE911073EFBDF73A3D4C43E0EB1715150535EB63561F
9E904043F9E8E3D199DD0A3256C019999954585929946F2905A009667FD5ECC3
79AE397890CA0CF69FC7ECBF73E74E57EDDAD31098FCB9B9B9BE653D5960DA8F
4A4CA4BBD9F2F4AA391F0A324D433409F9169EF0FEC489FADF264F363D47B5FA
7216E4FA77C48C60F0BA9B7DFF6DDBB6D1E5975F2E1DE187E08FE109E76E16FE
B3E3E29C1EBE63C8B40EBB64E2441A3879B2A98C0B15C0F4F474BD66C70EC3D7
DBB3E0DFAA1A7D3ACA34F607EF33591F5EB972A52BF3FD57AF5E4D393939B467
CF1EA9F30B58E8272525D119CAD5F48152E1DD26F91EC9E9E9347EC78ED01480
2630FF07F20F72914AFC719473F7EDA3CD06C1BF1E3D7AD0279F7CE2F4107F02
847FE0C08152997C1D58E05F60F7657094952B878A4C6290269071D317DFC8CF
D73F9E3DDBF40F8C4F4D55C13F07A938768C2E32C9817763971F28A4ECEC6CA9
DC7DCCFA7FE649A695BAC77E422DBB7ED3D9F533A3677E3E5D357BB6A19C9B2A
80294949FA51130DA3DA7D39CF4D35355462B00B7333F69511FC6BE322170D3E
FFC5175F4CDF7CF38DE979C82A9DCDB3FED56AF35853446DC3E259793E505B1B
9C02D004E6BFAAF9779E54F69F8D32FFF2F2F2E8D5575F757A883F02A1472AB2
688DBF1DCFF66FA7A450DF180EF2C922D32B40339173C3175E1E3448DF66B22C
83B5FFBB55DEBFA3A0E63FC32412FCFEFBEFD3A5975EEAF4307D609D1F3EFF72
411BB99F376D4A4BD9AA54813E39900BF0982027A0F3A04174FDB2650105D550
7A35C1ECDF837DB3AB5CBAAE1C2B14B3F6BFC540FBB76BD78E76EFDEEDF4107F
64C68C19347EFC78D37390D0F321CFFCAA89AC35DE603770BD811BE8473390F5
A015C0B5ACA5BBAAA8ACA398F9FF575C7105BDF9E69B4E0FD107827E175C7001
1D33A925410A6F45EBD6749A9AF92DF3055FD7398280AA664501BC9E97A76F28
2D35FC30D5F4C31D982DFFB925F517ADBA20FC6635FCF0F92B79E68FE5C49E50
11350BC9C8CBA3AB4B4B7F22EF0115C0B4B434BD6ED72EC30F53A9BFCE83D2DF
36264B4068F6D9BF7F7FA787E933FB61FE1B81C6A4157C2FC5420E7F38111508
B5E8D081EEA9AA9253009AC0FC1FCDA69AD2D6CE6256FC03A13AC0AF39DDF20B
F5FC08FC998135FE712A912C64B6F0FDF0AA493118D002C87B500AE07E36FF55
F4DF59CC6AFFFBF4E9E3EBF5EF24D5D5D5D4BB776FDA6192463E283E9EDE5579
24B620B31AA0C92880D261C3F44D0B171A7EC8993CBB14B005A0709681ACED97
1B24808C1D3B969E7CF24947C777EDB5D752A9491C097EFFBA366D54D0CF464A
F6EFA77F9B045ACF1D368CF2162E3C49E67FA200A6B669A3D79BAC2DABC61FEE
A08549D0C7E9A69F6BD6ACA15FFCE217A6E7CC4F4951597E36236A1796C80A77
4275B5B902D094FFEF7A4409405F7EF925A5A7A73B36BEA14387D25B6FBD65F8
3AF2FB5F503924B613CC72A02505A0B2FFDCC12BF5F534AAA626E06BE8F9FFF5
D75F3B3636ACF9F7ECD9D3F0F54E4D9AD0273C89A80232FB09260E70D23F5E19
3244DFBC78B1E19B95FFEF0E26D4D6D2A3064B3EC3D8CF5BB0608163631B3E7C
382D3489217DC8F7CF25CA820C1BA238C0394386D0758B17FF28F727290051F5
9F6AFBED0ECC0280D83863E2C4898E8CEBD34F3FA58C8C0CC3D755D43FFC88E2
00A756079EA4003481FF7F236BEF8E4A7B3B8E5905E05FFEF2171A3C78B023E3
1245FED5EC1F7EACC601A41580F2FFDD017EE09F99FCC0F0FF1107883498FD7B
F5EA6598EF0FC1FF50B98F11E1619E2064E3003F3E11EDFCA39A7FB88345478E
D0B0830703BEE66405E0F5D75FEFDBC2CB88F7F9DE19A08AC72282A84948C39D
837E54002FF6EFAFEF58B1C2F04D6AFDDF1D986500C2F4870B1069D0D63B95AD
43A3D93F8B67FF8FD4EC1F31447180F4FEFDE9C6152B4E5600D33A74D0EBAAAA
0CDFA4BAFFB803E4FF2F35D0EED84E6BCA9429111F135A8EDF74D34D86AF2FE1
D95F35F48C1CA22E412DD2D2E89E5DBB4E56009A0A007A82D3F7EEA5AAE3C703
BE367FFE7CBAE69A6B223E26741D32EAF483FDF8D6B8A827612C20B367807642
F6A515C0BDEC5F46DB1E6B5E031B80A49994007FFEF9E7D4B973E7888E09C53E
679D7596E1EB4FB0E53856598E11055B873D2AD86B41B3A20054031077605602
8CEDBF0E1E3C488D22ACA41F7DF451BAEFBEFB02BE067B717B6AAA2AF8710051
8310ADA102106DFFA53200DDC1D4C38791C411F03534FF4013904873FEF9E71B
76FB51893FCE21CA08F46F1BE65300F37373F58D65658627F766136E88EA00E4
38B93CC32F34D80074DCB871347DFAF4888E4794F9F7627232E5272646744C8A
1F587CE810AD35D93CB47B6E2E8D282BFB41013CD3A387BEDBA4671B96FF2E54
5D5B1CA7F3BE7DB4DDA007E04B2FBDE45B8B8F24D8CAFBF6DB6F0FF81ADCC65D
6ACB78C7106D1BD6BE470FBA6DFDFA1F14C0A4C444FDB8495B61D501D879443D
003FFEF8639F391E49468C1841AFBDF65AC0D786252450594A4A44C7A3F80FA2
16618DD9327BB0BEFE0705A00956006E674DAE7AB53BCBDFD89FFB95410A30B6
00430FC0F8082BE9D34F3FDD708BAF67D865BCD562F41FD1EB437CF8B31C603B
A0552882895881C21DA852D1E5D8C796E2D36C319AA111C9298007DBB777FAFB
C43C33D8A4FB9D8149E7440FC0CD9B3753B76EDD0C5F47F61F76F769CEB7D861
16EA3A3E0EF081C834FE5DC3079C995A7EFC8E8F63F27F9AFCD9284DF9B3FD2A
269E9FE380D2883FF1EFF1ECB6C6720C6292202D5C935100AD78E6BF832D0085
B3E4D7D4D0CB066E5A6161213DF7DC73111D4F717131DD7CF3CD4E5F1653A624
25D184188E5D3DC916C001839811D06414C0E94D9BD21895C9E5384800DA6390
01F8F4D34FD36DB7DD16D1F1888A7FDC40AC2B80E7ABABE9EBEFBE337C5D9351
00AA0AD0792AD8FFBFC8A40418E63FDC804872F6D9670B77F9759A585700A2AA
400D0A409404D42D218146A868AEA34C64DFFF618316604EF40044E00F0140B7
13EB0A60FEC183F49941DE08403250A3254545FAEA99330D4F524940CE733E9B
72FF3230E59CF0FFE7CD9B47A3468D72FAB20889750520DA35B86F5111357A23
3F5FFF78F66CE393D43E808E22EA00841D80B113702441F20F9280DC4EAC2B80
A5870ED16A936CC09EF9F9D4489406AC1A813ACB53FC03DE6950DB8D02A0AAAA
AA88EF01880620C83BA8E7D9A596DD131C757575F42D8F15C7617657F088D771
24262652120B63C303FF87FC85047631F11CDFA5499326BED7F0FF286AC2CEC2
DFB1E5E33F8EB0398BA623FE7FE3B3274C98404B972E0D38CE585700A20D4391
0EDCE8E54183F46DCB96199E04E1EFAF148063987500C6CC0F0B2096316B441A
EB0AE02316FEF74DD2813B0F1A448D9ECFCAD2775654189E04F3BFAFAAE77684
0327EAFF8D92645E78E105BAF1C61B9D1EA6A398A523C7BA0280F9BFD4A433D0
199999D4E8E98C0C7DCF860D8627A95660CE61B6031070AA03B09B501680311B
F8FE596072FFB4CBC8A046D3D3D3F51A932D9CAF4949A173D5268E8E702DFF78
F30DA2B8FDFAF5A37FFCE31F4E0FD17194023066D39123F49A410769909C9E4E
8D44BB01ABCD409D017DDD53D9FC3F64D0D5C5C91D80DC845200C6883609C16E
C18D26C5C5E9C74D3A87DCCC277568DAD4E9EF127320F037D0A49CD389F25F37
A21480313B59AE5F3451008D7962172A00D50DD819B0F4F794C11A6EA74E9DE8
8B2FBE707A88AE4029006344DD817D0A40B900EEC4ACFBCFD8B163E9C9279F74
7A88AE40AD021823E502A820A0FB40DAEFF9264AF9DD77DFA55FFFFAD74E0FD3
15280BC018A920A068197078723265C47053052740E38F1906195CC898DBBB77
6FC4BBFFB815A5008C11ED10E45B062CCECCD4BFAAAC343C4925024516B4C54A
3389FE63E71FEC00A4F801A5008C914A0412A5020FE48B78510C5FC44853CC3F
DA2D263F1A36FFC426A08A1F50310063A4528145C5406A57E0C8D2877DFF7506
A5BF88FE6FDBB62DE2BBFFB81965011823550CA4CA81DD83A8F3CF238F3C42F7
DE7BAFD3C374154A011823550EAC1A82B887913535F4AA41EA6F5C5C9CAFF4B7
8DEACF78124A011823D51044B50473071BD9ECEFC5E6BF514AD6E8D1A3E9E597
5F767A98AE43C5008C916A0986279A6A0AEA3866B33F58B97225656565393D4C
D7A12C0063A49A829E78A2DA823B8868F67762E30FAFA0148031B6B405571B83
841FD1EC8F757FACFF2B7E8A5200C6D8B23108505B83850FCCFE192669BF3D7A
F4F055FEA9A5BFC0280560CCC37BF6F8CACA8DD0641580DA1C347CE4B09FB6D4
C44F5BB060010D1B36CCE961BA16A5000263697350D1F6E0792929748E2A08B2
9D45478ED03093620DE5FB8B510A20309BF9DE2A35B9B74EDA1EFC991E3DF4DD
EBD71B9E8C4CC00B63F442860BE4FC9FCBA6FF0E131FCD899EFF5E432980C020
03F03D9334E0F6EC5ADEB67EFD0F0A40940EAC9281EC6702FF388F9AA469AA9E
7F7228051098C5870ED15A932C40A4018F282BFB4101889281CE8C8BA382D6AD
9DFE4E518368D90F38B1E1A717510A203025FBF7D3BF4D3A7D210968E0E4C9FF
092D6B2681C0667CDA3DEDDA39FD9DA20244652F3970805699FC38D8EA1B5B7E
2BC4A84CC0C04CDBB387BE15AC00E0514A01807B590124AAA5A890316BF601D0
E77FD3A64D949C9CECF4503D81B2007E0AE24B8FB2023043B3AA005477E0D079
FBC811BAC224320BE6CE9D4B23478E74749C2B56ACF0EDBBF7FDF7DFFBF6EB6B
F8084EFDBF501EC1A99FDFF06F3424D0FB27B3EB8AF10622561580A81B30D04E
5500D3D2D2F4BA5DBB0CDFA076080A8DAF8E1FA79EFBF651B589593660C000FA
E0830F1C1D2736FE4C4D4DF56DC2E975625501885A81B5484BA37B76ED3A5901
BCD8BFBFBEC3409302B54B70F0608FBF4BD9EF5F6792978D5E7FEBD6ADA3CE9D
3B3B3A56F8D3F0ABA381585500E57575B4820F23D2FBF7A71B57AC385901BC55
58A8AF292E367C93AA0A0C8E5A16FEFE02E1076EC9F7C766A32525254E0FC316
62550188AA00FB1416D215C5C5272B00A099C401E21A35A2FBD54A802520FCFF
C53EFF2A81395DC83FC873CF3DE7F4707DB46FDF9EF60802485E211615005699
1EC38ED2122B00405A0100B54B903C88C4FE8635F1DF04C28F629FCACA4A6AEE
821B75F5EAD5BE04A468211615809500203849018876095271003920FC393CF3
2F3731C300FC7E08FF79E79DE7F4907D4C9A3489FEF0873F383D0CDB88450520
F2FFB11BD084EAEAC00AE0952143F4CD8B171BBE5965048A81E975250BFF5281
F0376BD68CDE79E71DEADFBFBFD343FE915FFEF297B46AD5AA80AFB5611730B5
7163A7876889DFB1F017C6D8CA952803F09C2143E8BAC58B032B00A009E20077
A7A6FA1E153F05C29F5B53438B4DFAB00108FFDB6FBF4D975E7AA9D343FE91DD
BB7753C78E1D0D97FFE6A7A4D0D5AA22D4D558F5FF81250500D466A181C152DF
0D870E4909FFA2458B5CB7B71F1A8EDE70C30D015FC3AFBD8B157F2B8F5900B1
86683350A08914808A035807FDFCAFE599DFACB417A0B5775959992B4B7CCD52
6A07C4C7D3FB6A09D8F588360239D5FF073F5100A5C386E99B162E34FC101507
3899297575F4473E447973107E08981BBBFB1CE69B06E6FF7E83D9E391A424BA
27C682695E44E4FF9FCBF75EDEC285E60A0068023700F900B11E07406AEFF53C
EB8B22FD00C28F1C7F3724FA04E2C9279FA43BEFBCD3F0F50D3C7374577520AE
067EFFC39205400D094A015CCBE660D718DE9E1A453D37B1BFBF87958008083F
32EB9C2EF031E2282BB06EDDBAD1F6EDDB03BEDEA94913DAA6BA42BB9E60FC7F
1050014CEBD041AFABAA32FCA058DD3014EBFBF7B19FF59449A7958640F867CD
9AE54BAF752B66C13FF03F2D5AD01F55CCC7F588F6016C5800D490800AE0F5BC
3C7D83414008C46283106CDBFD07F6F5AB24667D809D7C5F79E51557EFE6A3B3
42CBC8C8A08D1B37067C1DBFF30E36FF554768F723DA0320232F8FAE2E2D9553
004053CB813ED0B917FDFB360B22FC0D81AF5F5C5CECFAA61E6FBDF5160D1D3A
D4F0F5716CE9FD39062D3DAF11ACF90F0C15C0A4B838FDB84944B14762225DE5
F21B3C1490C30FC1AFB050178F35FE993367BADAE46F8859E61F54FBB6D4543A
43ADFDBB1ED12EC040B3AA005E1E3448DFB66C99E107466B56E0BFBEFBCEE7E7
8B52794F05453D58E6734B5EBF08341E193870A0E1EB05ACE05F8862051F2DC8
64FF751E3488AE5FB6CC9A02009AC00D88A62E4130A3FEC87E3EF6E8B3DA0B67
ECD8B1F4D8638F51BC47564610F9EFD5AB97A1EF0FD4D29F371075FF019A899C
9B2A80294949FA5193CD05A2A14908D6F11FE78B284AE10D44EBD6AD69F6ECD9
AECCEC33E381071EA0871F7ED8F0F5DC84047A2D25C5E9612A241035FF884F4A
A2076A6B8353006FE4E7EB1FF30D6EC6787603923CE62762396F0ECFF458CEFB
97A0534F20B0BC57545444BFFFFDEFA98DC7B64E5FB3660D6566669AF6FCFB3F
FE4E7DD4ECEF7AD070663A9BFF66F42A28A02B4B4A8253004013B8015EDA360C
D97B4F1D3E4C252CFC32493CA702C1CFCBCB234DD31CEFDD170C30FD2FB8E002
5A6FB20DDC109EFDDF54B3BF27106DFF0534818C0B15C0F4F474BD66C70EC3D7
DBF34C71ABCB674144F2D18BFF4D36F383ED753B78F0609A3A752A9D7FFEF94E
7F9DA079F0C107E94F7FFA93E1EB2D1B35A24D6DDBD2691EB3E8629559D5D5B4
DBC4824D4E4FA7F13B7684A60044DB8601B7EE198035FC497575C2869C66A045
16FC6537D5EE07838CE9FF627232E527263A3D548504557C4F3F6752B50BFCDB
7F999D23B586A709DC80BECD9AD160176E1E3AF4E0C1A0827BA06BD7AEBE19DF
8DD57B5641B51F84DFCCF41F141F4FEF7A3CA01B4B88527F812621DF520AA038
3353FFAAB2D2F075E4028C4B4D75DDD661B9AC00165A540068D185CAB8ABAEBA
8A1AB9ECFB0403D27D73727268E9D2A586E7C0F4FF944D7F95F4E30D10C49E21
58FB3F83157E6165A53D0A0068022BC08D0542B2160032F810DCBBEBAEBB3CED
E307E28E3BEEA0A79E7ACAF49C67D87ABB354AF239620151E30FA049CAB6B402
989498A81F37493774A315205200D888F3F6DB6FF7F5E5473FFC6863C68C1934
7EFC78D37354B71F6F2133FB378E8BA3078F1DB3570188760E026815D6DF45A5
A3660A005B704340BC92BD671551A10F80E9BF8E853F168ABAA205B4FC2E3769
FB0D1AEEFC23C2D2742D2A1042F9E85D2EAA0F3053004F3CF184CF3C8E4610F1
CFCECEA6432629A210F97758F80744A9028C4630EB3FCEB3FFB736CDFEC092A4
2E18354A5F3F77AEE9396E4A0C325300D3A74FA771E3C6393D44DBD9BC79B34F
F8BFF9E61BD3F3D4929FF79049FCE9316A140D9F3B373C0A00C858016E691662
B60A108D0A0033FF65975D26DCDB4F75F9F126D3F877359BFD816651A62D2B80
F9B9B9FAC6B232D373DC5225184B2EC08A152BE8F2CB2F3735FBC1F53CEBCF56
65BE9E036BFE4B05BF6DF7DC5C1A5156165E050034C19260AB264DE80E173492
8C150580801F9631BF1524865CC2FEE17BECF7BB2546A39047D4F20B6841C873
5077C2BC9C1C7DCB9225A6E7B8C10A888518C0BC79F3A8A0A0C034C5179CC34A
B9B2756BB5BB8F0791A9F93F75CF3F59829E0A34092BE0B76DDA383ADB44BB02
78F4D147E9BEFBEE139E87D6DEEFB3D9AF96FBBC0722FFCF56578765F607414B
E7EC0103F4EDCB979B9EE3F43662D1EA02D4D4D4F866FD85263B38F939AF6953
5F8EBF4AF3F526A2EDBE41A70103287FF9F2C82A00A009AC00CCFEBF6DDBD631
B3D36C15C0AB0AE0934F3EA1DCDC5CDAB2658BF0DC7E3CE3BF9392A2CC7E8FB2
8F677D54FC1DB339F2DF909014C0F35959FACE8A0AD3739C6C1B166D2EC08B2F
BEE8EB44240AF60124F8BCC966BFD7BA3529FE83A8DD17E8989545632A2A9C51
00401358016038DF88190E249D448B0B80725E083EB61893015D7DCAF89AAB68
BF77D9505F4F0BD8D513A18528C321DF218B0A0AF475821B130141340D8974A1
503458009F7EFAA96F89CFAC96BF21AA9DB7F79149F905A27E7F32D8229153DB
B4D1EB05DD499C2817F6B20580FE7D0F3DF490AF298968890F20BEFF47BEBE13
5C9286AD081E9972DF449E50275457872CBFB64DC99A842B10E9D6615EB5002A
2A2AE8A69B6E32EDDBDF900EECE7CF4F49F125FA28BC8D4CAB2FA0D924BBB629
0051D7207026DFA005AD5BDBF5278578AD1600CB7B68352E6AE0D11004FB4AD9
E46FA7827D5141C9FEFDF46F81C527DBED47065B9D7251A110886486A0975C00
A4F36287A11D261D984F4515F5441732197F40B3516E6D55004B8A8AF4D53367
9A9E83C8F4EDEC0A244760CB692FB800AB57AFF665F32D1724553504B33D0A7A
06AB5AFEA8A1E6FBEFE9698935FFBE4545943373A63B1500989696A6D7EDDA65
7A4EA45C01375B00DBB66DA3FBEFBF9F5E7BED354BEF43F75ED4F2ABCCBEE8E2
7916FEAF05EDEB5BA4A5D13DBB76D92AB3615997D3240282915815709B024087
DEFDECE36167A159B3664945F7FD60D67F8CAFD768D5C423EA9089FA032D0CF2
1A160550929DAD7F595E2E3CEFDA56ADA86B18CD5837B900E8D083E0DE4C7691
4435FB3FB94E2CF48FB3F0AB405FF4B199EFCF52BE4F458492EF6F46D8327344
5D8441B313B502E14A5775C32A0072F7B175786969A9A5191FA4376942CFB66C
A97CFD28057EFFFFB2E92F4AF869CC13C083F5F56191D5B0A6E66912AEC0E94D
9BD29830ED2DE8940B0053FF9D77DEA169D3A6590AEEF9C16AFE9866CDE8CF3C
EBBBA9CDBAC25E64FC7EA085514EC37A77C9B40F03E18A07445A0120671F0D3A
D06E5C3689E75490C73FB5450BEAEEC2BD1615F621EBF767E4E5D1D5A5A5DE54
00E0892E5DF4FD5BB70ACF0B473C20523180DDBB77FB7C7B04F6440D398DE8C5
02FF049BFB2A9B2FFAD972F428BD7AE080F0BCD65DBAD09D5BB786554623625F
CAC6036EB1393F20DC0A00FEFDE38F3FEEF3EF654A7403816E3D0FF18C7F9D8A
EEC704D27EBFC5FEFEC112310753938807B4E759F0A6D6AD6D2B630D870B80F5
FB575F7D95E6CF9F2F5DA11708E4EF8F63D7E72EF6F555D96E6C80249F17F6EF
A7DD0EFBFD0D89D89D573A6C98BE49A285959D0D44ECEA088425BC3973E6D082
050B68D5AA55218DE9E7ACE4C6B3E08F645F5F097EEC00E1C7729FA8C1073877
D830CA5BB830BA140078BC7367FD00CFA022BAB3705C9D9212F2DF0BC50580D0
BFF1C61BBEA0DECA952B431E0B32F8EE66C11FA496F46292D7F95EDC28B15375
ABCE9DE9AE6DDB222697119F8264E201A02F9BC6835BB60CE96F59550028C479
F7DD777D267E30CB77A782705E017F8F3BF95051FDD8051B7AAC9688118573BD
DF08476C504D221E0042DD6D58140340A79DF2F2727AEFBDF768D9B265B47DFB
765BBE1F32F66E65A12FE24365EFC53632BBF9FAD11C90474714C0EB7979FA86
D252A97343291F365300AD5BB7F6E5E5DB095A7063B647BEBE4AE051C896F782
70AFF71BE1D85D2AB3AF809FBC94143A2721C1F2DF3053007681E5CB1CF6EB61
EAAB945D851FD91C7F10AE3C7F191C9DA666F5EEAD57AD5D2B3C0FD172AC0C74
B49824132E0580515CC60A69041F57B0D0ABD6DB8A86EC3C76CCD7D25B54DB0F
3AF4EE4DB7AE5DEB981C3A6EA7CA660A62A61DCD66BB959E82762A0008FDC52C
EC7958A1E0436DB6A108047AFACD61D75294E8032291E927C2710500A675E8A0
D7555509CF8312B8D68225609607200B76D719C93E3D045F05F4146660E6478A
AF8CF0B7E8D081EEA9AA725CFE1C1F809F294949FAD1DA5AE1797007B0D1884C
4C20180BA0257F7E36CFF4D92CF8D7B0E0ABCE3B0A1990DF5FC6F79B8CD96F57
4B6F3B70C520FCC8E6080099D5011905005B229385FD372CF4E8B09BA58A7114
16B112ED7762ADDF0CD70CC48F4C67613FA23C01230580CABB5FB3B00FE403D5
776AC94E112C56D6F92355E06305570DC68F26992804CC3206FD3100E4DF0FE0
8B3FE08469AF02780A3B90CDF0F3A3B950DE5C37203F9A052560543BB0E6BBEF
E84C167615BC53D88D6C6EBF1FCDA5B2E6CA41F9B1E20EA08A100943AAC24E11
4EEA75DD27FC32557DC08D667F435C3B303F560283E827701D2B81486C3AA288
3D6A8F1FA7B9070E48D5F303B705FC02E1EAC1F991D97DD80F7205AE642510CE
76E38AD803A9BD6FD6D448ADF103372DF599E1FA01FA914D16F2E3C476E48AE8
44B681A71FB724F9C8E08941FA914D1BF6832DC89034A45C024530A07FDF029E
F545BBF536C40DE9BD56F0CC40FDC81610F9814B3054327350A1F083CCBE4507
0F4A9BFCC0E9C29E60F0D460FD582925F663478721456C60D5E4074E96F48682
E706ECC74A53113F7009AE4A495189408A80C0E47F8D677D99DD7A1AE254330F
3BF0E4A01B62255700204F60705252D05D8614D109F2F997F2CC2F53CCE3C7ED
6BFC32787AF07E64BB0D3704D6005C022BFD0514D107EAF791D26B25D00722DD
BD375C78FE0BF891DD77E054101BC072A1CA208C2D30D3C3D7B792CBEF27927D
FBC34D547C898658750900560AFE9BAD810CB53D574CB0A1BE9EFEC2B3BE9508
3FF042669F55A2EACBF8B19A2FE007F5044392935590304AD9F7FDF7BE59FFB3
20BA44796D7D5F96A8FB427E64B7263F15B80259CD9BD3457C28B7203A80B9FF
D1E1C354C18795209F9FEEB9B934A2AC2C2A6F86A8FC520DB1524CD490564D9A
D0C0162D945BE07110DD5FC1827F80677FAB44A3C97F2A51FDE5FC946467EB5F
969707F55E280258047DD5B2A1A74070AF2248C1075E4DECB14AD47FC1864C4B
4BD3EB76ED0AEABD500417B012E8ABB6F3762D30EF21F8FFC747B082EFA5421E
3B88992FEA67495191BE7AE6CCA0DF8F1583BE6C1164F2A17A09BA0334E958C5
B3FD3FF8B01AD96F48DFA222CA993933A67ED498FAB20D29CECCD4BFAAAC0CFA
FDB002600D5CDCA28552040E01C1FF7B5D9D6FD60F26B8E7E7F47EFDE8E655AB
62F2478CC92FDD102BCD46020145D02B31917AB3325059859101D97B6B59E8D7
D5D78724F85E69DA114E62FACBFB595450A0AF2B2909F973D092EC7C560638D4
7E81F682765C9FB0C0E3906DC96546AF8202BAB2A424E6EFFF98BF000D793E2B
4BDF595161CB6721A9A84742826F1951050D8303B33B5A71AD61A1976DC229A2
6356168DA9A8503FC809D485084030FD068C80F09FCB8A00D58767AB5D87A440
338E7FB1D06F62E10FC5C46F48AC2CEB59455D10135E193244DFBC78B16D9F87
1504C40ACE62EBA0132B036519FC00847CFBB163B495051EC21FEC125E20BAE6
E4D0C8254BD48536405D18099056FCD99B6F92D52223112849C6F133B6103A36
6D1A330A0102BF93FDF8CF59E0B7B3C0EF6181B76BA607A8D3EF367468D4A6EF
DA89BA401658306A94BE61FE7CDB150180F0631501B1832E7CA4459942F882AF
D9972CECF0E511C5B753E0FD40F033468CA0E173E746CF850B33EA4205C15B85
85FABA397328981A035920FC1DF9863E8D15018E56AC14DAF2FFB93DE7006BF3
30E1AB58E0F7F2E3372CEC3BF9793804DE0F04BF577E3E5D515CECEE8BE342D4
050B9150138AAC8238422A2B041C6D9B34A176FC8823D225CC28ADDDC7C20D21
C7F3BD78CE4728997856392333930A2B2BD53D1C02EAE2D9C4FB1327EA1FB355
50B36387237F1F16430B5602CDF931811FE3F9B1393F363DF15AB313FF877588
447E8E73A04CBE6381854373E4F871AAE703CF8FF2FF7D8BE7FC8815F7C3FC1C
FF87730E9F98E19D22393D9D7A8E1E4D03274F56F7AE0DA88B1806DEC8CFF7F5
22385A5BEBF450A282F8A4243AEFEAAB55E24E18501734CCBC3C6890BEBDBC3C
2C81C368067E7DA7EC6CBA7ED932758F861175712308F6328032B0B2C7612C81
525C08BD577BEC7B1175A11D04F9055F7CF00185528CE465508C03818F960EBB
5E445D781781CCC31D1F7D14B50A01029F7ED14574DDE2C5EABE7309EA877031
C837D8B7650BEDDDB4C9736E438BB4344AEDDE9DDA76EDAAD6E75D8CFA613C08
961CF77EF619EDE3632F2B887026249981A699A92CE06DBB75A3543ED4D29CF7
503F58940225517FF0201DABABA323870ED1117E8E65C9233535BE7FE3F9517E
04F12D5BFA96DA12F8312139F987E72929BE7FC7A1E3113F57C21D9DFC3FB05B
459D4B95757A0000000049454E44AE4260822800000030000000600000000100
0800000000000000000000000000000000000000000000000000000000000202
04000505070004040B0003030E0004040D0008080B000A0A0E000C0C0E000404
1200050514000C0C100006061A0006061D001111160015151A00060621000707
280007072C001B1B20001D1D22000808350008083B0022222800242429002828
2E002E2E2F0036363A0039393D003C3C3C000808470008084B0008084D000707
50000808530008085A0008085F003E3E42000909660009096A000A0A71000A0A
76000B0B79000B0B7D004343470044444700464649004A4A4C00535354005757
57005B5B5D00666667007171710077777700787878007D7D7D00000080000606
83000B0B8100090984000C0C85000404890005058F000C0C890016168B001717
8C00070790000D0D91000D0D96000606990006069D000D0D9900222291003434
9A003E3E9E000707A3000E0EA0000E0EA6000707A9000909B1000F0FB2000909
B5001010B4001010BA005050A7005757AB007272B8000E0ECE001111C2001111
C9001212D1001212D6001010DA001010DE001313E1001414E3001212E6001414
E7001414E9001515EE001414F3001515F5001616FA001515FE00808080008A8A
8A008D8D8D00929292009A9A9A009E9E9E00A1A1A100A7A7A700AAAAAA00B0B0
B000BDBDBD00C9C9C900D0D0D000D7D7D700D9D9D900DDDDDD00E2E2E200E5E5
E500EBEBEB00EDEDED00F1F1F100F5F5F500F9F9F900FEFEFE00000000002F26
000050410000705B000090740000B08E0000CFA90000F0C30000FFD21100FFD8
3100FFDD5100FFE47100FFEA9100FFF0B100FFF6D100FFFFFF00000000002F14
00005022000070300000903E0000B04D0000CF5B0000F0690000FF791100FF8A
3100FF9D5100FFAF7100FFC19100FFD2B100FFE5D100FFFFFF00000000002F03
0000500400007006000090090000B00A0000CF0C0000F00E0000FF201200FF3E
3100FF5C5100FF7A7100FF979100FFB6B100FFD4D100FFFFFF00000000002F00
0E00500017007000210090002B00B0003600CF004000F0004900FF115A00FF31
7000FF518600FF719C00FF91B200FFB1C800FFD1DF00FFFFFF00000000002F00
20005000360070004C0090006200B0007800CF008E00F000A400FF11B300FF31
BE00FF51C700FF71D100FF91DC00FFB1E500FFD1F000FFFFFF00000000002C00
2F004B0050006900700087009000A500B000C400CF00E100F000F011FF00F231
FF00F451FF00F671FF00F791FF00F9B1FF00FBD1FF00FFFFFF00000000001B00
2F002D0050003F007000520090006300B0007600CF008800F0009911FF00A631
FF00B451FF00C271FF00CF91FF00DCB1FF00EBD1FF00FFFFFF00000000000800
2F000E005000150070001B0090002100B0002600CF002C00F0003E11FF005831
FF007151FF008C71FF00A691FF00BFB1FF00DAD1FF00FFFFFF00000000000000
0000000000000000000000000056554A483B3B484A5556000000000000000000
0000000000000000000000000000000000000000000000000000543B383D4246
4F52534E4B4339383B5400000000000000000000000000000000000000000000
00000000000000003B393D505E676666666666666667665E513D383C00000000
0000000000000000000000000000000000000000000040384B5E676766666666
666666666666666666665F4D3841000000000000000000000000000000000000
000000004938475F66666666666666666666666666666666666667675E453849
0000000000000000000000000000000000000041395767676767666666666666
66666666666666666666666666665A3941000000000000000000000000000000
0000394261676767676767676666666666666666666666666666666666676761
423B0000000000000000000000000000003846652923475F6767676767676766
6666666666666666666666666666666666463800000000000000000000000000
394C6667240807011F5266676767676666666666666666666666666666666667
6766463900000000000000000000004142656767242E7E6F25000C2B63676767
6766666666666666666666666666666666666642410000000000000000004939
62666767242E8F8F8F77360E0A47666766666666666666666666666666666666
6667676139490000000000000000385767676666262E8F8F8F8F8F7C34011662
67676766666666666666666666666666666666665A3800000000000000414766
66666767242E8F8F8F8F8F8F8F7414105B676666666666666666666666666666
66666667674541000000000000385D6767666666262E8F8F8F8F8F8F8F8F7A1C
055B676767666666666666666666666666666666665F3800000000003B4D6666
66666667241A7B8F8F8F8F8F8F8F8F7E1B126567676767666666666666666666
6666666767674B3C00000000385E6767666666664D0C08347A7F8F8F8F8F8F8F
790B26676767676767676766666666666666666666665F380000005442666666
666666666666430C026D8F8F8F8F8F8F8F70005E676767676767676766666666
666666666767673D5400003B4F67676666666666666666663C02688F8F8F8F8F
8F7F0F2A67676767676767676767676666666666666666503B0000385E666666
6666666666666666674C01327F8F8F8F8F8F360C44444444444444475B666666
666666666667675C38005638676767666666666666666666666658026D8F8F8F
8F8F75080B0707080B0808004467676666666666666666673956554366666666
6666666666666666666767270F7D8F8F8F8F8F8F8F8F8F8F8F8F8F0B47666666
666666666666676742554A466767666666666666666666666666666306688F8F
8F8F8F8F8F8F8F8F8F8F8F0B446767666666666666666667464A484E67666666
6666666666666666666667673C06798F8F8F8F8F8F8F8F8F8F8F8F0B47666666
66666666666767674E483B5167676766666666666666666666666666660B698F
8F8F8F8F8F8F8F8F8F8F8F08446767666666666666666666533B3B5366666666
66666666666666666666666767222F8F8F8F8F8F8F8F8F8F8F8F8F0847666666
6666666666666767513B484E6767676666666666666666666666666666440B8F
8F8F8F8F712D2C2D2D2E2E024467666666666666666666674E484A4667676666
666666666666666666666667675A00748F8F8F8F730022262627272759666666
6666666666676767464A5542676767666666666666666666666666666667076B
8F8F8F8F7F0E2A67676766666666666666666666666666674355563966676666
666666666666666666666667676720308F8F8F8F8F370A656766666666666666
6666666666676767385600385C6767676766666666666666666666666666400E
8F8F8F8F8F79023F6767676766666666666666666666675B3800003B4F676767
66666666666666666666666666675A00738F8F8F8F8F69015267676666666666
666666666767674F3B0000543D67676767666666666666666666666666666610
687F8F8F8F8F8F330359676767676666666666666667663D54000000385E6767
66666666666666666666666666676727198F8F8F8F8F8F7F2E01285F67666666
6666666767675D38000000003C4B67676767666666666666666666666666665A
026E8F8F8F8F8F8F7F6C0E000C3C66666666666667674B3C0000000000385E67
67666666666666666666666666666767280B798F8F8F8F8F8F8F7E721D1F6666
66676767675D3800000000000040456767676767666666666666666666666666
66111D7C8F8F8F8F8F8F8F8F331E67666667676767454100000000000000385A
67676766666666666666666666666666675B0D18778F8F8F8F8F8F8F331E6767
6767676757380000000000000000493861676767676766666666666666666666
666663150E6A7E8F8F8F8F8F331E676767676761384900000000000000000041
3E656767676666666666666666666666666667662A021469768F8F8F331E6767
6767643E41000000000000000000000039466767676767676666666666666666
666666666661260C02183672311E676767674639000000000000000000000000
00384665676767676766666666666666666666666666666658281100001E6767
644638000000000000000000000000000000393E606767676767676767666666
6666666666666666666667635958676142390000000000000000000000000000
0000004138576767676767676767676767676767676767676767676767675738
41000000000000000000000000000000000000004938455D6767676767676767
6767676767676767676767675D45384900000000000000000000000000000000
00000000000040384B5D676767676767676767676767676767675D4B38400000
0000000000000000000000000000000000000000000000003B383D4F5C676767
676767676767675C4F3D383B0000000000000000000000000000000000000000
00000000000000000000543B38383E464E51514E464238383B54000000000000
000000000000000000000000000000000000000000000000000000000056554A
483B3B484A555600000000000000000000000000000000000000FFFFE007FFFF
0000FFFF0000FFFF0000FFFC00003FFF0000FFF000000FFF0000FFC0000003FF
0000FF80000001FF0000FF00000000FF0000FE000000007F0000FC000000003F
0000F8000000001F0000F0000000000F0000F0000000000F0000E00000000007
0000E000000000070000C000000000030000C000000000030000800000000001
0000800000000001000080000000000100000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000800000000001000080000000000100008000000000010000C00000000003
0000C000000000030000E000000000070000E000000000070000F0000000000F
0000F0000000000F0000F8000000001F0000FC000000003F0000FE000000007F
0000FF00000000FF0000FF80000001FF0000FFC0000003FF0000FFF000000FFF
0000FFFC00003FFF0000FFFF0000FFFF0000FFFFE007FFFF0000280000002000
0000400000000100080000000000000000000000000000000000000000000000
000000000000030305000B0B14000C0C16000E0E1D0012121F000F0F22001313
220017172300101024001919270012122D0014142F00191928001D1D29001E1E
2F001313340014143A0012123C0015153F00222231002A2A3A002C2C39003131
3D001212450010104B001616480012124E0013135200323243003A3A46001717
660012126A001414690012126E000F0F7D001111700013137E00454554004B4B
570050505E005E5E6A0063636F00676772006D6D77007373780078787F000606
8400090987000A0A88000E0E8E0012128E0015158E000D0D95000E0E9D001010
92001919910010109A0010109C002E2E9B0031319F000E0EA8001010A3001010
A6000F0FB7001010BA001010BF006262B4007A7ABE000D0DC2001212CE000F0F
D0001212D1001010DA001212E7001414E7001414E9001313EE001414EC001414
F1001414F5001616FB001616FE00808085008C8C8F00969699009E9E9F00A0A0
A100AEAEAE00B7B7B700BEBEBE00D6D6D600DADADA00E2E2E200E4E4E400E9E9
E900EEEEEE00F0F0F000F6F6F600F8F8F800FEFEFE004CB0000059CF000067F0
000078FF11008AFF31009CFF5100AEFF7100C0FF9100D2FFB100E4FFD100FFFF
FF0000000000262F0000405000005A700000749000008EB00000A9CF0000C2F0
0000D1FF1100D8FF3100DEFF5100E3FF7100E9FF9100EFFFB100F6FFD100FFFF
FF00000000002F26000050410000705B000090740000B08E0000CFA90000F0C3
0000FFD21100FFD83100FFDD5100FFE47100FFEA9100FFF0B100FFF6D100FFFF
FF00000000002F1400005022000070300000903E0000B04D0000CF5B0000F069
0000FF791100FF8A3100FF9D5100FFAF7100FFC19100FFD2B100FFE5D100FFFF
FF00000000002F030000500400007006000090090000B00A0000CF0C0000F00E
0000FF201200FF3E3100FF5C5100FF7A7100FF979100FFB6B100FFD4D100FFFF
FF00000000002F000E00500017007000210090002B00B0003600CF004000F000
4900FF115A00FF317000FF518600FF719C00FF91B200FFB1C800FFD1DF00FFFF
FF00000000002F0020005000360070004C0090006200B0007800CF008E00F000
A400FF11B300FF31BE00FF51C700FF71D100FF91DC00FFB1E500FFD1F000FFFF
FF00000000002C002F004B0050006900700087009000A500B000C400CF00E100
F000F011FF00F231FF00F451FF00F671FF00F791FF00F9B1FF00FBD1FF00FFFF
FF00000000001B002F002D0050003F007000520090006300B0007600CF008800
F0009911FF00A631FF00B451FF00C271FF00CF91FF00DCB1FF00EBD1FF00FFFF
FF000000000008002F000E005000150070001B0090002100B0002600CF002C00
F0003E11FF005831FF007151FF008C71FF00A691FF00BFB1FF00DAD1FF00FFFF
FF00000000000000000000000000433C382F2F383C4300000000000000000000
000000000000000000000043313945494A50504A494539314300000000000000
0000000000000000004431404F525252525252525252524F4031440000000000
00000000000000003B3F4F52525252525252525252525252524D3F3B00000000
0000000000000034414952525252525252525252525252525252524234000000
00000000000034474B0110324B52525252525252525252525252525247340000
00000000003B42524B0F5C2E0A1A485252525252525252525252525252423B00
00000000443F52524B0F6F6F6F5A0A3352525252525252525252525252523F44
00000000314D52524B0F6F6F6F6F6127184B5252525252525252525252524D31
00000043405252524B08626F6F6F6F6F55125252525252525252525252525240
430000314F52525250220553636F6F6F6F2B255252525252525252525252524F
300000395252525252524E2017606F6F6F610451525252525252525252525252
390043455252525252525252370E616F6F6F281B1F1F1F1F2452525252525252
45433C49525252525252525252232C6F6F6F5E59595959590352525252525252
493C384A52525252525252525250065D6F6F6F6F6F6F6F6F0352525252525252
4A382F505252525252525252525236296F6F6F6F6F6F6F6F0352525252525252
502F2F50525252525252525252524E0D6F6F6F615B5C5B5B0352525252525252
502F384A52525252525252525252520B5F6F6F5B011313131952525252525252
4A383C49525252525252525252525221586F6F6F144952525252525252525252
493C434552525252525252525252523D2A6F6F6F542452525252525252525252
4543003952525252525252525252524E0D6F6F6F63163E525252525252525252
390000304F5252525252525252525252115C6F6F6F5F0E21465252525252524F
300000434052525252525252525252523E266F6F6F6F6257151C525252525240
43000000314D52525252525252525252521B53646F6F6F6F610C525252524D31
00000000443F52525252525252525252524E1C1E5F6F6F6F610C525252523F44
00000000003B425252525252525252525252523A0756616F610C525252423B00
00000000000034475252525252525252525252524922091D2D0C525247340000
0000000000000034425252525252525252525252525252483532524234000000
00000000000000003B3F4D52525252525252525252525252524D3F3B00000000
0000000000000000004431404F525252525252525252524F4031440000000000
000000000000000000000043303945494A50504A494539304300000000000000
0000000000000000000000000000433C382F2F383C4300000000000000000000
0000FFF00FFFFF8001FFFE00007FFC00003FF800001FF000000FE0000007C000
0003C00000038000000180000001800000010000000000000000000000000000
000000000000000000000000000000000000800000018000000180000001C000
0003C0000003E0000007F000000FF800001FFC00003FFE00007FFF8001FFFFF0
0FFF280000001000000020000000010008000000000000000000000000000000
00000000000000000000000000003F3F69002A2A720044447700272784002323
8F002C2C8F003535870033338C0035358E002A2A91002D2D910023239A001F1F
A1001F1FB1001C1CBF002323A0002525A4003333BE0040408700515181005555
86005F5F80006363870062628E0066668E00696989004747B4004A4ABC001515
C3001515D4001919D6002828C5001717E5001515F3001515FA001616FE006969
C1007E7EC8009797AA00AFAFBB00BABAC700C2C2C800CDCDD400D1D1D300D5D5
D800DDDDE000E4E4E500F1F1F100F9F9F900FFFFFF0000704C000090630000B0
790000CF8F0000F0A60011FFB40031FFBE0051FFC80071FFD30091FFDC00B1FF
E500D1FFF000FFFFFF0000000000002F0E00005018000070220000902C0000B0
360000CF400000F04A0011FF5B0031FF710051FF870071FF9D0091FFB200B1FF
C900D1FFDF00FFFFFF0000000000022F00000450000006700000089000000AB0
00000BCF00000EF0000020FF12003DFF31005BFF510079FF710098FF9100B5FF
B100D4FFD100FFFFFF0000000000142F000022500000307000003D9000004CB0
000059CF000067F0000078FF11008AFF31009CFF5100AEFF7100C0FF9100D2FF
B100E4FFD100FFFFFF0000000000262F0000405000005A700000749000008EB0
0000A9CF0000C2F00000D1FF1100D8FF3100DEFF5100E3FF7100E9FF9100EFFF
B100F6FFD100FFFFFF00000000002F26000050410000705B000090740000B08E
0000CFA90000F0C30000FFD21100FFD83100FFDD5100FFE47100FFEA9100FFF0
B100FFF6D100FFFFFF00000000002F1400005022000070300000903E0000B04D
0000CF5B0000F0690000FF791100FF8A3100FF9D5100FFAF7100FFC19100FFD2
B100FFE5D100FFFFFF00000000002F030000500400007006000090090000B00A
0000CF0C0000F00E0000FF201200FF3E3100FF5C5100FF7A7100FF979100FFB6
B100FFD4D100FFFFFF00000000002F000E00500017007000210090002B00B000
3600CF004000F0004900FF115A00FF317000FF518600FF719C00FF91B200FFB1
C800FFD1DF00FFFFFF00000000002F0020005000360070004C0090006200B000
7800CF008E00F000A400FF11B300FF31BE00FF51C700FF71D100FF91DC00FFB1
E500FFD1F000FFFFFF00000000002C002F004B0050006900700087009000A500
B000C400CF00E100F000F011FF00F231FF00F451FF00F671FF00F791FF00F9B1
FF00FBD1FF00FFFFFF00000000001B002F002D0050003F007000520090006300
B0007600CF008800F0009911FF00A631FF00B451FF00C271FF00CF91FF00DCB1
FF00EBD1FF00FFFFFF000000000008002F000E005000150070001B0090002100
B0002600CF002C00F0003E11FF005831FF007151FF008C71FF00A691FF00BFB1
FF00DAD1FF00FFFFFF000000000000251C12121C250000000000000000262023
2424242423202600000000001B0D0E23242424242424211B0000002621092C17
112424242424242126000020240832322B0724242424242420002523241F032E
32280F242424242423251C24242424042D321A1515102424241C122424242424
143232323206242424121224242424240B313016180C242424121C2424242424
1D2A321324242424241C25232424242424153229052224242325002024242424
240C2E322F012424200000262124242424240A2731192421260000001B212424
2424241E0A02211B000000000026202324242424232026000000000000000025
1C12121C250000000000F81F0000E0070000C003000080010000800100000000
000000000000000000000000000000000000000000008001000080010000C003
0000E0070000F81F0000}
OldCreateOrder = False
OnCreate = FormCreate
OnResize = FormResize
PixelsPerInch = 96
TextHeight = 13
end

94
libsrc/Swf2Exe/uMain.pas Normal file
View File

@@ -0,0 +1,94 @@
unit uMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, OleCtrls, ShockwaveFlashObjects_TLB, StdCtrls, ExtCtrls;
type
TfrmMain = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormResize(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TMySWF = class(TShockwaveFlash)
public
procedure CreateWnd; override;
end;
var
frmMain: TfrmMain;
flaPreview: TMySWF;
implementation
{$R *.dfm}
procedure TMySWF.CreateWnd;
begin
inherited;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
const
exeSize = 470016;
var
stream: TFileStream;
buffer: array of byte;
tempFile: array[0..MAX_PATH - 1] of char;
tempPath: array[0..MAX_PATH - 1] of char;
flashVarData: TVarData;
Width: integer;
Height: integer;
scaleMode: byte;
begin
flaPreview := TMySWF.Create(frmMain);
flaPreview.Parent := frmMain;
flaPreview.Anchors := [akLeft, akRight, akTop, akBottom];
flaPreview.Align := alClient;
GetTempPath(MAX_PATH, TempPath);
if GetTempFileName(TempPath, PAnsiChar('ffd'), 0, TempFile) = 0 then
raise Exception.Create('GetTempFileName API failed. ' +
SysErrorMessage(GetLastError));
try
stream := TFileStream.Create(ParamStr(0), fmOpenRead);
stream.Seek(exeSize, soBeginning);
stream.Read(Width, 4);
stream.Read(Height, 4);
stream.Read(scaleMode, 1);
SetLength(buffer, stream.Size - exeSize);
try
stream.Read(buffer[0], Length(buffer));
finally
stream.Free;
end;
except
Width := 12800;
Height := 9600;
scaleMode := 3;
end;
ClientWidth := Width div 20;
ClientHeight := Height div 20;
stream := TFileStream.Create(tempFile, fmOpenWrite);
try
stream.Write(buffer[0], Length(buffer));
finally
stream.Free;
end;
flaPreview.Movie := tempFile;
flaPreview.ScaleMode := scaleMode;
end;
procedure TfrmMain.FormResize(Sender: TObject);
begin
flaPreview.CreateWnd;
end;
end.

73
libsrc/avi/build.xml Normal file
View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="avi" default="default" basedir=".">
<description>Builds, tests, and runs the project avi.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="avi-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
build.xml.data.CRC32=505ab359
build.xml.script.CRC32=fa68b8cb
build.xml.stylesheet.CRC32=8064a381@1.68.1.46
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=505ab359
nbproject/build-impl.xml.script.CRC32=3d5c3ae5
nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.74.1.48

View File

@@ -0,0 +1,72 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processor.options=
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
${run.classpath}
debug.test.classpath=\
${run.test.classpath}
# Files in build.classes.dir which should be excluded from distribution jar
dist.archive.excludes=
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=../../lib/avi.jar
dist.javadoc.dir=${dist.dir}/javadoc
excludes=
includes=**
jar.compress=false
javac.classpath=
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.processorpath=\
${javac.classpath}
javac.source=1.7
javac.target=1.7
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
javac.test.processorpath=\
${javac.test.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
main.class=org.monte.media.math.IntMath
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=true
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project.
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
# To set system properties for unit tests define test-sys-prop.name=value:
run.jvmargs=
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
source.encoding=UTF-8
src.dir=src
test.src.dir=test

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>avi</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>

View File

@@ -0,0 +1,39 @@
/*
* @(#)AbortException.java
*
* Copyright (c) 1999-2012 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
/**
* This exception is thrown when the production of an image
* has been aborted.
*
* @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland
*
* @version $Id: AbortException.java 299 2013-01-03 07:40:18Z werner $
*/
public class AbortException extends Exception {
public static final long serialVersionUID = 1L;
/**
Creates a new exception.
*/
public AbortException() {
super();
}
/**
Creates a new exception.
*/
public AbortException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,99 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.monte.media;
import java.util.ArrayList;
/**
* {@code AbstractCodec}.
*
* @author Werner Randelshofer
* @version 1.0 2011-03-12 Created.
*/
public abstract class AbstractCodec implements Codec {
protected Format[] inputFormats;
protected Format[] outputFormats;
protected Format inputFormat;
protected Format outputFormat;
protected String name="unnamed codec";
public AbstractCodec(Format[] supportedInputFormats, Format[] supportedOutputFormats) {
this.inputFormats = supportedInputFormats;
this.outputFormats = supportedOutputFormats;
}
public AbstractCodec(Format[] supportedInputOutputFormats) {
this.inputFormats = supportedInputOutputFormats;
this.outputFormats = supportedInputOutputFormats;
}
@Override
public Format[] getInputFormats() {
return inputFormats.clone();
}
@Override
public Format[] getOutputFormats(Format input) {
ArrayList<Format>of=new ArrayList<Format>(outputFormats.length);
for (Format f:outputFormats) {
of.add(input==null?f:f.append(input));
}
return of.toArray(new Format[of.size()]);
}
@Override
public Format setInputFormat(Format f) {
if (f!=null)
for (Format sf : getInputFormats()) {
if (sf.matches(f)) {
this.inputFormat = sf.append(f);
return inputFormat;
}
}
this.inputFormat=null;
return null;
}
@Override
public Format setOutputFormat(Format f) {
for (Format sf : getOutputFormats(f)) {
if (sf.matches(f)) {
this.outputFormat = f;
return sf;
}
}
this.outputFormat=null;
return null;
}
@Override
public Format getInputFormat() {
return inputFormat;
}
@Override
public Format getOutputFormat() {
return outputFormat;
}
@Override
public String getName() {
return name;
}
/** Empty implementation of the reset method. Don't call super. */
@Override
public void reset() {
// empty
}
@Override
public String toString() {
String className=getClass().getName();
int p=className.lastIndexOf('.');
return className.substring(p+1)+"{" + "inputFormat=" + inputFormat + ", outputFormat=" + outputFormat+'}';
}
}

View File

@@ -0,0 +1,226 @@
/*
* @(#)AbstractVideoCodec.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferShort;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.WritableRaster;
import java.io.IOException;
import javax.imageio.stream.ImageOutputStream;
import static org.monte.media.VideoFormatKeys.*;
/**
* {@code AbstractVideoCodec}.
*
* @author Werner Randelshofer
* @version $Id: AbstractVideoCodec.java 299 2013-01-03 07:40:18Z werner $
*/
public abstract class AbstractVideoCodec extends AbstractCodec {
private BufferedImage imgConverter;
public AbstractVideoCodec(Format[] supportedInputFormats, Format[] supportedOutputFormats) {
super(supportedInputFormats, supportedOutputFormats);
}
/** Gets 8-bit indexed pixels from a buffer. Returns null if conversion failed. */
protected byte[] getIndexed8(Buffer buf) {
if (buf.data instanceof byte[]) {
return (byte[]) buf.data;
}
if (buf.data instanceof BufferedImage) {
BufferedImage image = (BufferedImage) buf.data;
if (image.getRaster().getDataBuffer() instanceof DataBufferByte) {
return ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
}
}
return null;
}
/** Gets 15-bit RGB pixels from a buffer. Returns null if conversion failed. */
protected short[] getRGB15(Buffer buf) {
if (buf.data instanceof int[]) {
return (short[]) buf.data;
}
if (buf.data instanceof BufferedImage) {
BufferedImage image = (BufferedImage) buf.data;
if (image.getColorModel() instanceof DirectColorModel) {
DirectColorModel dcm = (DirectColorModel) image.getColorModel();
if (image.getRaster().getDataBuffer() instanceof DataBufferShort) {
// FIXME - Implement additional checks
return ((DataBufferShort) image.getRaster().getDataBuffer()).getData();
} else if (image.getRaster().getDataBuffer() instanceof DataBufferUShort) {
// FIXME - Implement additional checks
return ((DataBufferUShort) image.getRaster().getDataBuffer()).getData();
}
}
if (imgConverter == null) {
int width = outputFormat.get(WidthKey);
int height = outputFormat.get(HeightKey);
imgConverter = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_555_RGB);
}
Graphics2D g = imgConverter.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return ((DataBufferUShort) imgConverter.getRaster().getDataBuffer()).getData();
}
return null;
}
/** Gets 16-bit RGB-5-6-5 pixels from a buffer. Returns null if conversion failed. */
protected short[] getRGB16(Buffer buf) {
if (buf.data instanceof int[]) {
return (short[]) buf.data;
}
if (buf.data instanceof BufferedImage) {
BufferedImage image = (BufferedImage) buf.data;
if (image.getColorModel() instanceof DirectColorModel) {
DirectColorModel dcm = (DirectColorModel) image.getColorModel();
if (image.getRaster().getDataBuffer() instanceof DataBufferShort) {
// FIXME - Implement additional checks
return ((DataBufferShort) image.getRaster().getDataBuffer()).getData();
} else if (image.getRaster().getDataBuffer() instanceof DataBufferUShort) {
// FIXME - Implement additional checks
return ((DataBufferUShort) image.getRaster().getDataBuffer()).getData();
}
}
if (imgConverter == null) {
int width = outputFormat.get(WidthKey);
int height = outputFormat.get(HeightKey);
imgConverter = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_565_RGB);
}
Graphics2D g = imgConverter.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return ((DataBufferUShort) imgConverter.getRaster().getDataBuffer()).getData();
}
return null;
}
/** Gets 24-bit RGB pixels from a buffer. Returns null if conversion failed. */
protected int[] getRGB24(Buffer buf) {
if (buf.data instanceof int[]) {
return (int[]) buf.data;
}
if (buf.data instanceof BufferedImage) {
BufferedImage image = (BufferedImage) buf.data;
if (image.getColorModel() instanceof DirectColorModel) {
DirectColorModel dcm = (DirectColorModel) image.getColorModel();
if (dcm.getBlueMask() == 0xff && dcm.getGreenMask() == 0xff00 && dcm.getRedMask() == 0xff0000) {
if (image.getRaster().getDataBuffer() instanceof DataBufferInt) {
return ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
}
}
}
return image.getRGB(0, 0, //
outputFormat.get(WidthKey), outputFormat.get(HeightKey), //
null, 0, outputFormat.get(WidthKey));
}
return null;
}
/** Gets 32-bit ARGB pixels from a buffer. Returns null if conversion failed. */
protected int[] getARGB32(Buffer buf) {
if (buf.data instanceof int[]) {
return (int[]) buf.data;
}
if (buf.data instanceof BufferedImage) {
BufferedImage image = (BufferedImage) buf.data;
if (image.getColorModel() instanceof DirectColorModel) {
DirectColorModel dcm = (DirectColorModel) image.getColorModel();
if (dcm.getBlueMask() == 0xff && dcm.getGreenMask() == 0xff00 && dcm.getRedMask() == 0xff0000) {
if (image.getRaster().getDataBuffer() instanceof DataBufferInt) {
return ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
}
}
}
return image.getRGB(0, 0, //
outputFormat.get(WidthKey), outputFormat.get(HeightKey), //
null, 0, outputFormat.get(WidthKey));
}
return null;
}
/** Gets a buffered image from a buffer. Returns null if conversion failed. */
protected BufferedImage getBufferedImage(Buffer buf) {
if (buf.data instanceof BufferedImage) {
return (BufferedImage) buf.data;
}
return null;
}
private byte[] byteBuf = new byte[4];
protected void writeInt24(ImageOutputStream out, int v) throws IOException {
byteBuf[0] = (byte) (v >>> 16);
byteBuf[1] = (byte) (v >>> 8);
byteBuf[2] = (byte) (v >>> 0);
out.write(byteBuf, 0, 3);
}
protected void writeInt24LE(ImageOutputStream out, int v) throws IOException {
byteBuf[2] = (byte) (v >>> 16);
byteBuf[1] = (byte) (v >>> 8);
byteBuf[0] = (byte) (v >>> 0);
out.write(byteBuf, 0, 3);
}
protected void writeInts24(ImageOutputStream out, int[] i, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > i.length || off + len < 0) {
throw new IndexOutOfBoundsException("off < 0 || len < 0 || off + len > i.length!");
}
byte[] b = new byte[len * 3];
int boff = 0;
for (int j = 0; j < len; j++) {
int v = i[off + j];
//b[boff++] = (byte)(v >>> 24);
b[boff++] = (byte) (v >>> 16);
b[boff++] = (byte) (v >>> 8);
b[boff++] = (byte) (v >>> 0);
}
out.write(b, 0, len * 3);
}
protected void writeInts24LE(ImageOutputStream out, int[] i, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > i.length || off + len < 0) {
throw new IndexOutOfBoundsException("off < 0 || len < 0 || off + len > i.length!");
}
byte[] b = new byte[len * 3];
int boff = 0;
for (int j = 0; j < len; j++) {
int v = i[off + j];
b[boff++] = (byte) (v >>> 0);
b[boff++] = (byte) (v >>> 8);
b[boff++] = (byte) (v >>> 16);
//b[boff++] = (byte)(v >>> 24);
}
out.write(b, 0, len * 3);
}
/** Copies a buffered image. */
protected static BufferedImage copyImage(BufferedImage img) {
ColorModel cm = img.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = img.copyData(null);
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
}

View File

@@ -0,0 +1,126 @@
/*
* @(#)AudioFormatKeys.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance onlyWith the
* license agreement you entered into onlyWith Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import org.monte.media.math.Rational;
import java.nio.ByteOrder;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFormat.Encoding;
/**
* Defines common format keys for audio media.
*
* @author Werner Randelshofer
* @version $Id: AudioFormatKeys.java 299 2013-01-03 07:40:18Z werner $
*/
public class AudioFormatKeys extends FormatKeys {
// Standard video EncodingKey strings for use onlyWith FormatKey.Encoding.
/**
* Specifies SignedKey, linear PCM data.
*/
public static final String ENCODING_PCM_SIGNED = javax.sound.sampled.AudioFormat.Encoding.PCM_SIGNED.toString();
/**
* Specifies unsigned, linear PCM data.
*/
public static final String ENCODING_PCM_UNSIGNED = javax.sound.sampled.AudioFormat.Encoding.PCM_UNSIGNED.toString();
/**
* Specifies u-law encoded data.
*/
public static final String ENCODING_ULAW = javax.sound.sampled.AudioFormat.Encoding.ULAW.toString();
/**
* Specifies a-law encoded data.
*/
public static final String ENCODING_ALAW = javax.sound.sampled.AudioFormat.Encoding.ALAW.toString();
/**
* AVI PCM encoding.
*/
public static final String ENCODING_AVI_PCM = "\u0000\u0000\u0000\u0001";
/**
* QuickTime 16-bit big endian signed PCM encoding.
*/
public static final String ENCODING_QUICKTIME_TWOS_PCM = "twos";
/**
* QuickTime 16-bit little endian signed PCM encoding.
*/
public static final String ENCODING_QUICKTIME_SOWT_PCM = "sowt";
/**
* QuickTime 24-bit big endian signed PCM encoding.
*/
public static final String ENCODING_QUICKTIME_IN24_PCM = "in24";
/**
* QuickTime 32-bit big endian signed PCM encoding.
*/
public static final String ENCODING_QUICKTIME_IN32_PCM = "in32";
/**
* QuickTime 8-bit unsigned PCM encoding.
*/
public static final String ENCODING_QUICKTIME_RAW_PCM = "raw ";
/**
* Specifies MP3 encoded data.
*/
public static final String ENCODING_MP3 = "MP3";
/**
* The sample size in bits.
*/
public final static FormatKey<Integer> SampleSizeInBitsKey = new FormatKey<Integer>("sampleSizeInBits", Integer.class);
/**
* The numer of ChannelsKey.
*/
public final static FormatKey<Integer> ChannelsKey = new FormatKey<Integer>("channels", Integer.class);
/**
* The size of a frame.
*/
public final static FormatKey<Integer> FrameSizeKey = new FormatKey<Integer>("frameSize", Integer.class);
/**
* The compressor name.
*/
public final static FormatKey<ByteOrder> ByteOrderKey = new FormatKey<ByteOrder>("byteOrder", ByteOrder.class);
/**
* The number of frames per second.
*/
public final static FormatKey<Rational> SampleRateKey = new FormatKey<Rational>("sampleRate", Rational.class);
/**
* Whether values are signed.
*/
public final static FormatKey<Boolean> SignedKey = new FormatKey<Boolean>("signed", Boolean.class);
/**
* Whether silence is encoded as -128 instead of 0.
*/
public final static FormatKey<Boolean> SilenceBugKey = new FormatKey<Boolean>("silenceBug", Boolean.class);
public static Format fromAudioFormat(javax.sound.sampled.AudioFormat fmt) {
return new Format(
MediaTypeKey, MediaType.AUDIO,
EncodingKey, fmt.getEncoding().toString(),
SampleRateKey, Rational.valueOf(fmt.getSampleRate()),
SampleSizeInBitsKey, fmt.getSampleSizeInBits(),
ChannelsKey, fmt.getChannels(),
FrameSizeKey, fmt.getFrameSize(),
FrameRateKey, Rational.valueOf(fmt.getFrameRate()),
ByteOrderKey, fmt.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN,
SignedKey, AudioFormat.Encoding.PCM_SIGNED.equals(fmt.getEncoding())//,
//
);
}
public static javax.sound.sampled.AudioFormat toAudioFormat(Format fmt) {
// We always use PCM_SIGNED or PCM_UNSIGNED
return new javax.sound.sampled.AudioFormat(
!fmt.containsKey(SignedKey) || fmt.get(SignedKey) ? Encoding.PCM_SIGNED : Encoding.PCM_UNSIGNED,
fmt.get(SampleRateKey).floatValue(),
fmt.get(SampleSizeInBitsKey, 16),
fmt.get(ChannelsKey, 1),
fmt.containsKey(FrameSizeKey) ? fmt.get(FrameSizeKey) : (fmt.get(SampleSizeInBitsKey, 16) + 7) / 8 * fmt.get(ChannelsKey, 1),
fmt.containsKey(FrameRateKey) ? fmt.get(FrameRateKey).floatValue() : fmt.get(SampleRateKey).floatValue(),
fmt.containsKey(ByteOrderKey) ? fmt.get(ByteOrderKey) == ByteOrder.BIG_ENDIAN : true);
}
}

View File

@@ -0,0 +1,164 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.monte.media;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.monte.media.math.Rational;
import org.monte.media.util.Methods;
/**
* A {@code Buffer} carries media data from one media processing unit to another.
*
* @author Werner Randelshofer
* @version 1.0 2011-03-12 Created.
*/
public class Buffer {
/** A flag mask that describes the boolean attributes for this buffer.
*/
public EnumSet<BufferFlag> flags = EnumSet.noneOf(BufferFlag.class);
/** Values which are not specified must have this value. */
public static final int NOT_SPECIFIED = -1;
/** The track number.
* This can be set to NOT_SPECIFIED or to a number &gt;= 0.
*/
public int track;
/** Header information, such as RTP header for this chunk. */
public Object header;
/** The media data. */
public Object data;
/** The data offset. This field is only used if {@code data} is an array. */
public int offset;
/** The data length. This field is only used if {@code data} is an array. */
public int length;
/** Duration of a sample in seconds.
* Multiply this with {@code sampleCount} to get the buffer duration.
*/
public Rational sampleDuration;
/** The time stamp of this buffer in seconds. */
public Rational timeStamp;
/** The format of the data in this buffer. */
public Format format;
/** The number of samples in the data field. */
public int sampleCount = 1;
/** Sequence number of the buffer. This can be used for debugging. */
public long sequenceNumber;
/** Sets all variables of this buffer to that buffer except for {@code data},
* {@code offset}, {@code length} and {@code header}.
*/
public void setMetaTo(Buffer that) {
this.flags = EnumSet.copyOf(that.flags);
//this.data=that.data;
//this.offset=that.offset;
//this.length=that.length;
//this.header=that.header;
this.track = that.track;
this.sampleDuration = that.sampleDuration;
this.timeStamp = that.timeStamp;
this.format = that.format;
this.sampleCount = that.sampleCount;
this.format = that.format;
this.sequenceNumber=that.sequenceNumber;
}
/** Sets {@code data}, {@code offset}, {@code length} and {@code header}
* of this buffer to that buffer.
* Note that this method creates copies of the {@code data} and
* {@code header}, so that these fields in that buffer can be discarded
* without affecting the contents of this buffer.
* <p>
* FIXME - This method does not always create a copy!!
*/
public void setDataTo(Buffer that) {
this.offset = that.offset;
this.length = that.length;
this.data = copy(that.data, this.data);
this.header = copy(that.header, this.header);
}
private Object copy(Object from, Object into) {
if (from instanceof byte[]) {
byte[] b=(byte[])from;
if (!(into instanceof byte[]) || ((byte[]) into).length < b.length) {
into = new byte[b.length];
}
System.arraycopy(b, 0, (byte[])into, 0, b.length);
} else if (from instanceof BufferedImage) {
// FIXME - Try to reuse BufferedImage in output!
BufferedImage img = (BufferedImage) from;
ColorModel cm = img.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = img.copyData(null);
into = new BufferedImage(cm, raster, isAlphaPremultiplied, null);
} else if (from instanceof Cloneable) {
try {
into=Methods.invoke(from, "clone");
} catch (NoSuchMethodException ex) {
into=from;
}
} else {
// FIXME - This is very fragile, since we do not know, if the
// input data stays valid until the output data is processed!
into = from;
}
return into;
}
/** Returns true if the specified flag is set. */
public boolean isFlag(BufferFlag flag) {
return flags.contains(flag);
}
/** Convenience method for setting a flag. */
public void setFlag(BufferFlag flag) {
setFlag(flag, true);
}
/** Convenience method for clearing a flag. */
public void clearFlag(BufferFlag flag) {
setFlag(flag, false);
}
/** Sets or clears the specified flag. */
public void setFlag(BufferFlag flag, boolean value) {
if (value) {
flags.add(flag);
} else {
flags.remove(flag);
}
}
/** Clears all flags, and then sets the specified flag. */
public void setFlagsTo(BufferFlag... flags) {
if (flags.length == 0) {
this.flags = EnumSet.noneOf(BufferFlag.class);
} else {
this.flags = EnumSet.copyOf(Arrays.asList(flags));
}
}
/** Clears all flags, and then sets the specified flag. */
public void setFlagsTo(EnumSet<BufferFlag> flags) {
if (flags == null) {
this.flags = EnumSet.noneOf(BufferFlag.class);
} else {
this.flags = EnumSet.copyOf(flags);
}
}
public void clearFlags() {
flags.clear();
}
}

View File

@@ -0,0 +1,42 @@
/*
* @(#)BufferFlag.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
/**
* {@code BufferFlag}.
*
* @author Werner Randelshofer
* @version $Id: BufferFlag.java 299 2013-01-03 07:40:18Z werner $
*/
public enum BufferFlag {
/** Indicates that the data in this buffer should be ignored. */
DISCARD,
/** Indicates that this Buffer holds an intra-coded picture, which can be
* decoded independently. */
KEYFRAME,
/** Indicates that the data in this buffer is at the end of the media. */
END_OF_MEDIA,
/** Indicates that the data in this buffer is used for initializing the
* decoding queue.
* <p>
* This flag is used when the media time of a track is set to a non-keyframe
* sample. Thus decoding must start at a keyframe at an earlier time.
* <p>
* Decoders should decode the buffer.
* Encoders and Multiplexers should discard the buffer.
*/
PREFETCH,
/** Indicates that this buffer is known to have the same data as the
* previous buffer. This may improve encoding performance.
*/
SAME_DATA;
}

View File

@@ -0,0 +1,73 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.monte.media;
/**
* A {@code Codec} processes a {@code Buffer} and stores the result in another
* {@code Buffer}.
*
* @author Werner Randelshofer
* @version 1.0 2011-03-12 Created.
*/
public interface Codec {
/** The codec successfully converted the input to output. */
public final static int CODEC_OK = 0;
/** The codec could not handle the input. */
public final static int CODEC_FAILED = 1;
/** The codec did not fully consume the input buffer.
* The codec has updated the input buffer to
* reflect the amount of data that it has processed.
* The codec must be called again with the same input buffer.
*/
public final static int CODEC_INPUT_NOT_CONSUMED = 2;
/** The codec did not fully fill the output buffer.
* The codec has updated the output buffer to
* reflect the amount of data that it has processed.
* The codec must be called again with the same output buffer.
*/
public final static int CODEC_OUTPUT_NOT_FILLED = 4;
/** Lists all of the input formats that this codec accepts. */
public Format[] getInputFormats();
/** Lists all of the output formats that this codec can generate
* with the provided input format. If the input format is null, returns
* all supported output formats.
*/
public Format[] getOutputFormats(Format input);
/** Sets the input format.
* Returns the format that was actually set. This is the closest format
* that the Codec supports. Returns null if the specified format is not
* supported and no reasonable match could be found.
*/
public Format setInputFormat(Format input);
public Format getInputFormat();
/** Sets the output format.
* Returns the format that was actually set. This is the closest format
* that the Codec supports. Returns null if the specified format is not
* supported and no reasonable match could be found.
*/
public Format setOutputFormat(Format output);
public Format getOutputFormat();
/** Performs the media processing defined by this codec.
* <p>
* Copies the data from the input buffer into the output buffer.
*
* @return A combination of processing flags.
*/
public int process(Buffer in, Buffer out);
/** Returns a human readable name of the codec. */
public String getName();
/** Resets the state of the codec. */
public void reset();
}

View File

@@ -0,0 +1,395 @@
/*
* @(#)DefaultRegistry.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance onlyWith the
* license agreement you entered into onlyWith Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import static org.monte.media.VideoFormatKeys.*;
import static org.monte.media.AudioFormatKeys.*;
/**
* {@code DefaultRegistry}.
* <p>
* FIXME - The registry should be read from a file.
*
* @author Werner Randelshofer
* @version $Id: DefaultRegistry.java 299 2013-01-03 07:40:18Z werner $
*/
public class DefaultRegistry extends Registry {
private HashMap<String, LinkedList<RegistryEntry>> codecMap;
private HashMap<String, LinkedList<RegistryEntry>> readerMap;
private HashMap<String, LinkedList<RegistryEntry>> writerMap;
private HashMap<String, Format> fileFormatMap;
@Override
public Format[] getReaderFormats() {
return getFileFormats();
}
@Override
public Format[] getWriterFormats() {
return getFileFormats();
}
@Override
public Format[] getFileFormats() {
return fileFormatMap.values().toArray(new Format[fileFormatMap.size()]);
}
private static class RegistryEntry {
Format inputFormat;
Format outputFormat;
String className;
public RegistryEntry(Format inputFormat, Format outputFormat, String className) {
this.inputFormat = inputFormat;
this.outputFormat = outputFormat;
this.className = className;
}
}
public DefaultRegistry() {
}
@Override
protected void init() {
codecMap = new HashMap<String, LinkedList<RegistryEntry>>();
readerMap = new HashMap<String, LinkedList<RegistryEntry>>();
writerMap = new HashMap<String, LinkedList<RegistryEntry>>();
fileFormatMap = new HashMap<String, Format>();
// IFF ANIM
// --------
putCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_ANIM, EncodingKey, ENCODING_BITMAP_IMAGE),
"org.monte.media.anim.BitmapCodec");
// AVI
// --------
putBidiCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI, EncodingKey, ENCODING_AVI_DIB),
"org.monte.media.avi.DIBCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI, EncodingKey, ENCODING_AVI_MJPG),
"org.monte.media.jpeg.JPEGCodec");
putCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI, EncodingKey, ENCODING_AVI_PNG),
"org.monte.media.png.PNGCodec");
putCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI, EncodingKey, ENCODING_AVI_RLE),
"org.monte.media.avi.RunLengthCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI, EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
"org.monte.media.avi.TechSmithCodec");
putCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI, EncodingKey, ENCODING_AVI_DOSBOX_SCREEN_CAPTURE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
"org.monte.media.avi.ZMBVCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_SIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_AVI, EncodingKey, ENCODING_AVI_PCM),
"org.monte.media.avi.AVIPCMAudioCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_UNSIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_AVI, EncodingKey, ENCODING_AVI_PCM),
"org.monte.media.avi.AVIPCMAudioCodec");
// QuickTime
// --------
putCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_RAW),
"org.monte.media.quicktime.RawCodec");
putCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_ANIMATION),
"org.monte.media.quicktime.AnimationCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_JPEG),
"org.monte.media.jpeg.JPEGCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI, EncodingKey, ENCODING_AVI_MJPG),
"org.monte.media.jpeg.JPEGCodec");
putCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_PNG),
"org.monte.media.png.PNGCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,
EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE),
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_BUFFERED_IMAGE),
"org.monte.media.avi.TechSmithCodec");
putCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_SIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_TWOS_PCM),
"org.monte.media.quicktime.QuickTimePCMAudioCodec");
putCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_UNSIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_TWOS_PCM),
"org.monte.media.quicktime.QuickTimePCMAudioCodec");
putCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_SIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_SOWT_PCM),
"org.monte.media.quicktime.QuickTimePCMAudioCodec");
putCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_UNSIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_SOWT_PCM),
"org.monte.media.quicktime.QuickTimePCMAudioCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_SIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_IN24_PCM),
"org.monte.media.quicktime.QuickTimePCMAudioCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_UNSIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_IN24_PCM),
"org.monte.media.quicktime.QuickTimePCMAudioCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_SIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_IN32_PCM),
"org.monte.media.quicktime.QuickTimePCMAudioCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_UNSIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_IN32_PCM),
"org.monte.media.quicktime.QuickTimePCMAudioCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_SIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_RAW_PCM),
"org.monte.media.quicktime.QuickTimePCMAudioCodec");
putBidiCodec(
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_JAVA, EncodingKey, ENCODING_PCM_UNSIGNED),
new Format(MediaTypeKey, MediaType.AUDIO, MimeTypeKey, MIME_QUICKTIME, EncodingKey, ENCODING_QUICKTIME_RAW_PCM),
"org.monte.media.quicktime.QuickTimePCMAudioCodec");
putReader(new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI), "org.monte.media.avi.AVIReader");
putReader(new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_QUICKTIME), "org.monte.media.quicktime.QuickTimeReader");
putReader(new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_ANIM), "org.monte.media.anim.ANIMReader");
putWriter(new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI), "org.monte.media.avi.AVIWriter");
putWriter(new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_QUICKTIME), "org.monte.media.quicktime.QuickTimeWriter");
putWriter(new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_ANIM), "org.monte.media.anim.ANIMWriter");
putFileFormat("avi", new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI));
putFileFormat("mov", new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_QUICKTIME));
putFileFormat("anim", new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_ANIM));
}
/**
*
* @param inputFormat Must have {@code MediaTypeKey}, {@code EncodingKey}, {@code MimeTypeKey}.
* @param outputFormat Must have {@code MediaTypeKey}, {@code EncodingKey}, {@code MimeTypeKey}.
* @param codecClass
*/
public void putBidiCodec(Format inputFormat, Format outputFormat, String codecClass) {
putCodec(inputFormat, outputFormat, codecClass);
putCodec(outputFormat, inputFormat, codecClass);
}
/**
*
* @param inputFormat Must have {@code MediaTypeKey}, {@code EncodingKey}, {@code MimeTypeKey}.
* @param outputFormat Must have {@code MediaTypeKey}, {@code EncodingKey}, {@code MimeTypeKey}.
* @param codecClass
*/
@Override
public void putCodec(Format inputFormat, Format outputFormat, String codecClass) {
RegistryEntry entry = new RegistryEntry(inputFormat, outputFormat, codecClass);
addCodecEntry(inputFormat.get(EncodingKey), entry);
addCodecEntry(outputFormat.get(EncodingKey), entry);
}
private void addCodecEntry(String key, RegistryEntry entry) {
LinkedList<RegistryEntry> list = codecMap.get(key);
if (list == null) {
list = new LinkedList<RegistryEntry>();
codecMap.put(key, list);
}
list.add(entry);
}
/**
*
* @param fileFormat Must have {@code MediaTypeKey}, {@code MimeTypeKey}.
* @param readerClass
*/
@Override
public void putReader(Format fileFormat, String readerClass) {
RegistryEntry entry = new RegistryEntry(null, fileFormat, readerClass);
String key = fileFormat.get(MimeTypeKey);
LinkedList<RegistryEntry> list = readerMap.get(key);
if (list == null) {
list = new LinkedList<RegistryEntry>();
readerMap.put(key, list);
}
list.add(entry);
}
/**
*
* @param fileFormat Must have {@code MediaTypeKey}, {@code MimeTypeKey}.
* @param writerClass
*/
@Override
public void putWriter(Format fileFormat, String writerClass) {
RegistryEntry entry = new RegistryEntry(fileFormat, null, writerClass);
String key = fileFormat.get(MimeTypeKey);
LinkedList<RegistryEntry> list = writerMap.get(key);
if (list == null) {
list = new LinkedList<RegistryEntry>();
writerMap.put(key, list);
}
list.add(entry);
}
@Override
public String[] getCodecClasses(Format inputFormat, Format outputFormat) {
HashSet<String> classNames = new HashSet<String>();
HashSet<RegistryEntry> entries = new HashSet<RegistryEntry>();
if (inputFormat != null) {
LinkedList<RegistryEntry> re;
if (inputFormat.get(EncodingKey) == null) {
re = new LinkedList<RegistryEntry>();
for (Map.Entry<String, LinkedList<RegistryEntry>> i : codecMap.entrySet()) {
for (RegistryEntry j : i.getValue()) {
if (inputFormat.matches(j.inputFormat)) {
re.add(j);
}
}
}
} else {
re = codecMap.get(inputFormat.get(EncodingKey));
}
if (re != null) {
entries.addAll(re);
}
}
if (outputFormat != null) {
LinkedList<RegistryEntry> re;
if (outputFormat.get(EncodingKey) == null) {
re = new LinkedList<RegistryEntry>();
for (Map.Entry<String, LinkedList<RegistryEntry>> i : codecMap.entrySet()) {
for (RegistryEntry j : i.getValue()) {
if (outputFormat.matches(j.outputFormat)) {
re.add(j);
}
}
}
} else {
re = codecMap.get(outputFormat.get(EncodingKey));
}
if (re != null) {
entries.addAll(re);
}
}
for (RegistryEntry e : entries) {
if ((inputFormat == null || e.inputFormat == null || inputFormat.matches(e.inputFormat))
&& (outputFormat == null || e.outputFormat == null || outputFormat.matches(e.outputFormat))) {
classNames.add(e.className);
}
}
return classNames.toArray(new String[classNames.size()]);
}
@Override
public String[] getReaderClasses(Format fileFormat) {
LinkedList<RegistryEntry> rr = readerMap.get(fileFormat.get(MimeTypeKey));
String[] names = new String[rr == null ? 0 : rr.size()];
if (rr != null) {
int i = 0;
for (RegistryEntry e : rr) {
names[i++] = e.className;
}
}
return names;
}
@Override
public Format getFileFormat(File file) {
String ext = file.getName();
int p = ext.lastIndexOf('.');
if (p != -1) {
ext = ext.substring(p + 1);
}
ext = ext.toLowerCase();
return fileFormatMap.get(ext);
}
@Override
public String[] getWriterClasses(Format fileFormat) {
LinkedList<RegistryEntry> rr = writerMap.get(fileFormat.get(MimeTypeKey));
String[] names = new String[rr == null ? 0 : rr.size()];
if (rr != null) {
int i = 0;
for (RegistryEntry e : rr) {
names[i++] = e.className;
}
}
return names;
}
@Override
public void putFileFormat(String extension, Format format) {
fileFormatMap.put(extension.toLowerCase(), format);
}
@Override
public String getExtension(Format ff) {
for (Map.Entry<String, Format> e : fileFormatMap.entrySet()) {
if (e.getValue().get(MimeTypeKey).equals(ff.get(MimeTypeKey))) {
return e.getKey();
}
}
return "";
}
@Override
public void unregisterCodec(String codecClass) {
for (Map.Entry<String, LinkedList<RegistryEntry>> i:codecMap.entrySet()) {
LinkedList<RegistryEntry> ll=i.getValue();
for (Iterator<RegistryEntry> j=ll.iterator();j.hasNext();) {
RegistryEntry e=j.next();
if (e.className.equals(codecClass)) {
j.remove();
}
}
}
}
}

View File

@@ -0,0 +1,325 @@
/*
* @(#)Format.java
*
* Copyright (c) 2011-2012 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance onlyWith the
* license agreement you entered into onlyWith Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* Specifies the format of a media, for example of audio and video.
*
* @author Werner Randelshofer
* @version $Id: Format.java 299 2013-01-03 07:40:18Z werner $
*/
public class Format {
/**
* Holds the properties of the format.
*/
private HashMap<FormatKey, Object> properties;
/**
* Creates a new format onlyWith the specified properties.
*/
public Format(Map<FormatKey, Object> properties) {
this(properties, true);
}
/**
* Creates a new format onlyWith the specified properties.
*/
private Format(Map<FormatKey, Object> properties, boolean copy) {
if (copy || ! (properties instanceof HashMap)) {
for (Map.Entry<FormatKey, Object> e : properties.entrySet()) {
if (!e.getKey().isAssignable(e.getValue())) {
throw new ClassCastException(e.getValue() + " must be of type " + e.getKey().getValueClass());
}
}
this.properties = new HashMap< FormatKey, Object>(properties);
} else {
this.properties = (HashMap< FormatKey, Object>) properties;
}
}
/**
* Creates a new format onlyWith the specified properties. The properties
* must be given as key value pairs.
*/
public Format(Object... p) {
this.properties = new HashMap< FormatKey, Object>();
for (int i = 0; i < p.length; i += 2) {
FormatKey key = (FormatKey) p[i];
if (!key.isAssignable(p[i + 1])) {
throw new ClassCastException(key + ": " + p[i + 1] + " must be of type " + key.getValueClass());
}
this.properties.put(key, p[i + 1]);
}
}
@SuppressWarnings("unchecked")
public <T> T get(FormatKey<T> key) {
return (T) properties.get(key);
}
@SuppressWarnings("unchecked")
public <T> T get(FormatKey<T> key, T defaultValue) {
return (properties.containsKey(key)) ? (T) properties.get(key) : defaultValue;
}
public boolean containsKey(FormatKey key) {
return properties.containsKey(key);
}
/**
* Gets the properties of the format as an unmodifiable map.
*/
public Map<FormatKey, Object> getProperties() {
return Collections.unmodifiableMap(properties);
}
/**
* Gets the keys of the format as an unmodifiable set.
*/
public Set<FormatKey> getKeys() {
return Collections.unmodifiableSet(properties.keySet());
}
/**
* Returns true if that format matches this format. That is iff all
* properties defined in both format objects are identical. Properties which
* are only defined in one of the format objects are not considered.
*
* @param that Another format.
* @return True if the other format matches this format.
*/
public boolean matches(Format that) {
for (Map.Entry<FormatKey, Object> e : properties.entrySet()) {
if (!e.getKey().isComment()) {
if (that.properties.containsKey(e.getKey())) {
Object a = e.getValue();
Object b = that.properties.get(e.getKey());
if (a != b && a == null || !a.equals(b)) {
return false;
}
}
}
}
return true;
}
public boolean matchesWithout(Format that, FormatKey... without) {
OuterLoop:
for (Map.Entry<FormatKey, Object> e : properties.entrySet()) {
FormatKey k = e.getKey();
if (!e.getKey().isComment()) {
if (that.properties.containsKey(k)) {
for (int i = 0; i < without.length; i++) {
if (without[i] == k) {
continue OuterLoop;
}
}
Object a = e.getValue();
Object b = that.properties.get(k);
if (a != b && a == null || !a.equals(b)) {
return false;
}
}
}
}
return true;
}
/**
* Creates a new format which contains all properties from this format and
* additional properties from that format. <p> If a property is specified in
* both formats, then the property value from this format is used. It
* overwrites that format. <p> If one of the format has more properties than
* the other, then the new format is more specific than this format.
*
* @param that
* @return That format with properties overwritten by this format.
*/
public Format append(Format that) {
HashMap<FormatKey, Object> m = new HashMap<FormatKey, Object>(this.properties);
for (Map.Entry<FormatKey, Object> e : that.properties.entrySet()) {
if (!m.containsKey(e.getKey())) {
m.put(e.getKey(), e.getValue());
}
}
return new Format(m,false);
}
/**
* Creates a new format which contains all properties from this format and
* additional properties listed. <p> If a property is specified in both
* formats, then the property value from this format is used. It overwrites
* that format. <p> If one of the format has more properties than the other,
* then the new format is more specific than this format.
*
* @param p The properties must be given as key value pairs.
* @return That format with properties overwritten by this format.
*/
public Format append(Object... p) {
HashMap<FormatKey, Object> m = new HashMap<FormatKey, Object>(this.properties);
for (int i = 0; i < p.length; i += 2) {
FormatKey key = (FormatKey) p[i];
if (!key.isAssignable(p[i + 1])) {
throw new ClassCastException(key + ": " + p[i + 1] + " must be of type " + key.getValueClass());
}
m.put(key, p[i + 1]);
}
return new Format(m,false);
}
/**
* Creates a new format which contains all properties from the specified
* format and additional properties from this format.
* <p> If a property is specified in both formats, then the property value
* from this format is used. It overwrites that format.
* <p> If one of the format has more properties than the other, then the new
* format is more specific than this format.
*
* @param that
* @return That format with properties overwritten by this format.
*/
public Format prepend(Format that) {
HashMap<FormatKey, Object> m = new HashMap<FormatKey, Object>(that.properties);
for (Map.Entry<FormatKey, Object> e : this.properties.entrySet()) {
if (!m.containsKey(e.getKey())) {
m.put(e.getKey(), e.getValue());
}
}
return new Format(m,false);
}
/**
* Creates a new format which contains all specified properties and
* additional properties from this format.
* <p> If a property is specified in both formats, then the property value
* from this format is used. It overwrites that format.
* <p> If one of the format has more properties than the other, then the new
* format is more specific than this format.
*
* @param p The properties must be given as key value pairs.
* @return That format with properties overwritten by this format.
*/
public Format prepend(Object... p) {
HashMap<FormatKey, Object> m = new HashMap<FormatKey, Object>();
for (int i = 0; i < p.length; i += 2) {
FormatKey key = (FormatKey) p[i];
if (!key.isAssignable(p[i + 1])) {
throw new ClassCastException(key + ": " + p[i + 1] + " must be of type " + key.getValueClass());
}
m.put(key, p[i + 1]);
}
for (Map.Entry<FormatKey, Object> e : this.properties.entrySet()) {
if (!m.containsKey(e.getKey())) {
m.put(e.getKey(), e.getValue());
}
}
return new Format(m,false);
}
/**
* Creates a new format which only has the specified keys (or less). <p> If
* the keys are reduced, then the new format is less specific than this
* format.
*/
public Format intersectKeys(FormatKey... keys) {
HashMap<FormatKey, Object> m = new HashMap<FormatKey, Object>();
for (FormatKey k : keys) {
if (properties.containsKey(k)) {
m.put(k, properties.get(k));
}
}
return new Format(m,false);
}
/**
* Creates a new format without the specified keys. <p> If the keys are
* reduced, then the new format is less specific than this format.
*/
public Format removeKeys(FormatKey... keys) {
boolean needsRemoval = false;
for (FormatKey k : keys) {
if (properties.containsKey(k)) {
needsRemoval = true;
break;
}
}
if (!needsRemoval) {
return this;
}
HashMap<FormatKey, Object> m = new HashMap<FormatKey, Object>(properties);
for (FormatKey k : keys) {
m.remove(k);
}
return new Format(m,false);
}
/**
* Returns true if the format has the specified keys.
*/
public Format containsKeys(FormatKey... keys) {
HashMap<FormatKey, Object> m = new HashMap<FormatKey, Object>(properties);
for (FormatKey k : keys) {
m.remove(k);
}
return new Format(m,false);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder("Format{");
boolean isFirst = true;
for (Map.Entry<FormatKey, Object> e : properties.entrySet()) {
if (isFirst) {
isFirst = false;
} else {
buf.append(',');
}
buf.append(e.getKey().toString());
buf.append(':');
appendStuffedString(e.getValue(), buf);
}
buf.append('}');
return buf.toString();
}
/**
* This method is used by #toString.
*/
private static void appendStuffedString(Object value, StringBuilder stuffed) {
if (value == null) {
stuffed.append("null");
}
value = value.toString();
if (value instanceof String) {
for (char ch : ((String) value).toCharArray()) {
if (ch >= ' ') {
stuffed.append(ch);
} else {
String hex = Integer.toHexString(ch);
stuffed.append("\\u");
for (int i = hex.length(); i < 4; i++) {
stuffed.append('0');
}
stuffed.append(hex);
}
}
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* @(#)FormatKey.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import java.io.Serializable;
/**
* A <em>FormatKey</em> provides type-safe access to an attribute of
* a {@link Format}.
* <p>
* A format key has a name, a type and a value.
*
* @author Werner Randelshofer
* @version $Id: FormatKey.java 299 2013-01-03 07:40:18Z werner $
*/
public class FormatKey<T> implements Serializable, Comparable {
public static final long serialVersionUID = 1L;
/**
* Holds a String representation of the attribute key.
*/
private String key;
/**
* Holds a pretty name. This can be null, if the value is self-explaining.
*/
private String name;
/** This variable is used as a "type token" so that we can check for
* assignability of attribute values at runtime.
*/
private Class<T> clazz;
/** Comment keys are ignored when matching two media formats with each other. */
private boolean comment;
/** Creates a new instance with the specified attribute key, type token class,
* default value null, and allowing null values. */
public FormatKey(String key, Class<T> clazz) {
this(key, key, clazz);
}
/** Creates a new instance with the specified attribute key, type token class,
* default value null, and allowing null values. */
public FormatKey(String key, String name, Class<T> clazz) {
this(key,name,clazz,false);
}
/** Creates a new instance with the specified attribute key, type token class,
* default value null, and allowing null values. */
public FormatKey(String key, String name, Class<T> clazz, boolean comment) {
this.key = key;
this.name = name;
this.clazz = clazz;
this.comment=comment;
}
/**
* Returns the key string.
* @return key string.
*/
public String getKey() {
return key;
}
/**
* Returns the pretty name string.
* @return name string.
*/
public String getName() {
return name;
}
/** Returns the key string. */
@Override
public String toString() {
return key;
}
/**
* Returns true if the specified value is assignable with this key.
*
* @param value
* @return True if assignable.
*/
public boolean isAssignable(Object value) {
return clazz.isInstance(value);
}
public boolean isComment() {
return comment;
}
public Class getValueClass() {
return clazz;
}
@Override
public int compareTo(Object o) {
return compareTo((FormatKey) o);
}
public int compareTo(FormatKey that) {
return this.key.compareTo(that.key);
}
}

View File

@@ -0,0 +1,61 @@
/*
* @(#)FormatKeys.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import org.monte.media.math.Rational;
/**
* Defines common {@code FormatKey}'s.
*
* @author Werner Randelshofer
* @version $Id: FormatKeys.java 299 2013-01-03 07:40:18Z werner $
*/
public class FormatKeys {
public static enum MediaType {
AUDIO,
VIDEO,
MIDI,
TEXT,
META,
FILE
}
/**
* The media MediaTypeKey.
*/
public final static FormatKey<MediaType> MediaTypeKey = new FormatKey<MediaType>("mediaType", MediaType.class);
/**
* The EncodingKey.
*/
public final static FormatKey<String> EncodingKey = new FormatKey<String>("encoding", String.class);
//
public final static String MIME_AVI = "video/avi";
public final static String MIME_QUICKTIME = "video/quicktime";
public final static String MIME_MP4 = "video/mp4";
public final static String MIME_JAVA = "Java";
public final static String MIME_ANIM = "x-iff/anim";
public final static String MIME_IMAGE_SEQUENCE = "ImageSequence";
/**
* The mime type.
*/
public final static FormatKey<String> MimeTypeKey = new FormatKey<String>("mimeType", String.class);
/**
* The number of frames per second.
*/
public final static FormatKey<Rational> FrameRateKey = new FormatKey<Rational>("frameRate", Rational.class);
/**
* The interval between key frames.
* If this value is not specified, most codecs will use {@code FrameRateKey}
* as a hint and try to produce one key frame per second.
*/
public final static FormatKey<Integer> KeyFrameIntervalKey = new FormatKey<Integer>("keyFrameInterval", Integer.class);
}

View File

@@ -0,0 +1,93 @@
/*
* @(#)MovieReader.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import org.monte.media.math.Rational;
import java.io.IOException;
/**
* A simple API for reading movie data (audio and video) from a file.
*
* <p>
* FIXME - MovieReader should extend Demultiplexer
*
* @author Werner Randelshofer
* @version $Id: MovieReader.java 299 2013-01-03 07:40:18Z werner $
*/
public interface MovieReader {
/** Returns the number of tracks. */
public int getTrackCount() throws IOException;
/** Finds a track with the specified format.
*
* @param fromTrack the start track number.
* @param format A format specification.
* @return The track number &gt;= fromTrack or -1 if no track has been found.
*/
public int findTrack(int fromTrack, Format format) throws IOException;
/** Returns the total duration of the movie . */
public Rational getDuration() throws IOException;
/** Returns the duration of the specified track. */
public Rational getDuration(int track) throws IOException;
/** Returns the sample number for the specified time. */
public long timeToSample(int track, Rational seconds) throws IOException;
/** Returns the time for the specified sample number. */
public Rational sampleToTime(int track, long sample) throws IOException;
/** Returns the file format. */
public Format getFileFormat() throws IOException;
/** Returns the media format of the specified track.
*
* @param track Track number.
* @return The media format of the track.
*/
public Format getFormat(int track) throws IOException;
/** Returns the number of media data chunks in the specified track.
* A chunk contains one or more samples.
*/
public long getChunkCount(int track) throws IOException;
/** Reads the next sample chunk from the specified track.
*
* @param track Track number.
* @param buffer The buffer into which to store the sample data.
*/
public void read(int track, Buffer buffer) throws IOException;
/** Reads the next sample chunk from the next track in playback sequence.
* The variable buffer.track contains the track number.
*
* @param buf The buffer into which to store the sample data.
*/
//public void read(Buffer buffer) throws IOException;
/** Returns the index of the next track in playback sequence.
*
* @return Index of next track or -1 if end of media reached.
*/
public int nextTrack() throws IOException;
public void close() throws IOException;
/** Sets the read time of all tracks to the closest sync sample before or
* at the specified time.
*
* @param newValue Time in seconds.
*/
public void setMovieReadTime(Rational newValue) throws IOException;
/** Returns the current time of the track. */
public Rational getReadTime(int track) throws IOException;
}

View File

@@ -0,0 +1,82 @@
/*
* @(#)MovieWriter.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import org.monte.media.math.Rational;
import java.io.IOException;
/**
* A simple API for writing movie data (audio and video) into a file.
*
* @author Werner Randelshofer
* @version $Id: MovieWriter.java 299 2013-01-03 07:40:18Z werner $
*/
public interface MovieWriter extends Multiplexer {
/** Returns the file format. */
public Format getFileFormat() throws IOException;
/** Adds a track to the writer for a suggested input format.
* <p>
* The format should at least specify the desired {@link FormatKeys.MediaType}.
* The actual input format is a refined version of the suggested format. For
* example, if a MovieWriter only supports fixed frame rate video, then the
* MovieWriter will extend the format with that information.
* <p>
* If the suggested input format is not compatible, then an IOException is
* thrown. For example, if a MovieWriter only supports fixed frame rate video,
* but a format with variable frame rate was requested.
*
* @param format The desired input format of the track. The actual input
* format may be a refined version of the specified format.
* @return The track number.
*/
public int addTrack(Format format) throws IOException;
/** Returns the media format of the specified track.
* This is a refined version of the format that was requested when the
* track was added. See {@link #addTrack}.
*
* @param track Track number.
* @return The media format of the track.
*/
public Format getFormat(int track);
/** Returns the number of tracks. */
public int getTrackCount();
/** Writes a sample into the specified track.
* Does nothing if the discard-flag in the buffer is set to true.
*
* @param track The track number.
* @param buf The buffer containing the sample data.
*/
@Override
public void write(int track, Buffer buf) throws IOException;
/** Closes the writer. */
@Override
public void close() throws IOException;
/** Returns true if the limit for media data has been reached.
* If this limit is reached, no more samples should be added to the movie.
* <p>
* This limit is imposed by data structures of the movie file
* which will overflow if more samples are added to the movie.
* <p>
* FIXME - Maybe replace by getCapacity():long.
*/
public boolean isDataLimitReached();
/** Returns the duration of the track in seconds. */
public Rational getDuration(int track);
/** Returns true if the specified track has no samples. */
public boolean isEmpty(int track);
}

View File

@@ -0,0 +1,34 @@
/*
* @(#)Multiplexer.java 1.0 2011-02-19
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import java.io.IOException;
/**
* A {@code Multiplexer} can write multiple media tracks into a
* single output stream.
*
* @author Werner Randelshofer
* @version 1.0 2011-02-19 Created.
*/
public interface Multiplexer {
/** Writes a sample.
* Does nothing if the discard-flag in the buffer is set to true.
*
* @param track The track number.
* @param buf The buffer containing the sample data.
*/
public void write(int track, Buffer buf) throws IOException;
/** Closes the Multiplexer. */
public void close() throws IOException;
}

View File

@@ -0,0 +1,30 @@
/*
* @(#)ParseException.java
*
* Copyright (c) 1999-2012 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
/**
* Exception thrown by IFFParse.
*
* @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland
* @version $Id: ParseException.java 299 2013-01-03 07:40:18Z werner $
*/
public class ParseException extends Exception {
public static final long serialVersionUID = 1L;
public ParseException(String message) {
super(message);
}
public ParseException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,310 @@
/*
* @(#)Registry.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import java.io.File;
import java.util.ArrayList;
import static org.monte.media.FormatKeys.*;
/**
* The {@code Registry} for audio and video codecs.
*
* @author Werner Randelshofer
* @version $Id: Registry.java 299 2013-01-03 07:40:18Z werner $
*/
public abstract class Registry {
private static Registry instance;
public static Registry getInstance() {
if (instance == null) {
instance = new DefaultRegistry();
instance.init();
}
return instance;
}
/**
* Initializes the registry.
*/
protected abstract void init();
/**
* Puts a codec into the registry.
*
* @param inputFormat The input format. Must not be null.
* @param outputFormat The output format. Must not be null.
* @param codecClass The codec class name. Must not be null.
*/
public abstract void putCodec(Format inputFormat, Format outputFormat, String codecClass);
/**
* Gets all codecs which can decode the specified format.
*
* @param format The format.
* @return An array of codec class names. If no codec was found, an empty
* array is returned.
*/
public final String[] getDecoderClasses(Format format) {
return getCodecClasses(format, null);
}
/**
* Gets all codecs which can decode the specified format.
*
* @param format The format.
* @return An array of codec class names. If no codec was found, an empty
* array is returned.
*/
public final String[] getEncoderClasses(Format format) {
return getCodecClasses(null, format);
}
/**
* Gets all codecs which can transcode from the specified input format to
* the specified output format.
*
* @param inputFormat The input format.
* @param outputFormat The output format.
* @return An array of codec class names. If no codec was found, an empty
* array is returned.
*/
public abstract String[] getCodecClasses(//
Format inputFormat,
Format outputFormat);
/**
* Gets all codecs which can decode the specified format.
*
* @param inputFormat The input format.
* @return An array of codec class names. If no codec was found, an empty
* array is returned.
*/
public final Codec[] getDecoders(Format inputFormat) {
return getCodecs(inputFormat, null);
}
/**
* Gets the first codec which can decode the specified format.
*
* @param inputFormat The output format.
* @return A codec. Returns null if no codec was found.
*/
public Codec getDecoder(Format inputFormat) {
return getCodec(inputFormat, null);
}
/**
* Gets all codecs which can encode the specified format.
*
* @param outputFormat The output format.
* @return An array of codecs. If no codec was found, an empty array is
* returned.
*/
public final Codec[] getEncoders(Format outputFormat) {
return getCodecs(null, outputFormat);
}
/**
* Gets the first codec which can encode the specified foramt.
*
* @param outputFormat The output format.
* @return A codec. Returns null if no codec was found.
*/
public Codec getEncoder(Format outputFormat) {
return getCodec(null, outputFormat);
}
/**
* Gets all codecs which can transcode from the specified input format to
* the specified output format.
*
* @param inputFormat The input format.
* @param outputFormat The output format.
* @return An array of codec class names. If no codec was found, an empty
* array is returned.
*/
public Codec[] getCodecs(Format inputFormat, Format outputFormat) {
String[] clazz = getCodecClasses(inputFormat, outputFormat);
ArrayList<Codec> codecs = new ArrayList<Codec>(clazz.length);
for (int i = 0; i < clazz.length; i++) {
try {
codecs.add((Codec) Class.forName(clazz[i]).newInstance());
} catch (Exception ex) {
//ex.printStackTrace();
System.err.println("Monte Registry. Codec class not found: " + clazz[i]);
unregisterCodec(clazz[i]);
}
}
return codecs.toArray(new Codec[codecs.size()]);
}
/**
* Gets a codec which can transcode from the specified input format to the
* specified output format.
*
* @param inputFormat The input format.
* @param outputFormat The output format.
* @return A codec or null.
*/
public Codec getCodec(Format inputFormat, Format outputFormat) {
String[] clazz = getCodecClasses(inputFormat, outputFormat);
for (int i = 0; i < clazz.length; i++) {
try {
Codec codec = ((Codec) Class.forName(clazz[i]).newInstance());
codec.setInputFormat(inputFormat);
if (outputFormat != null) {
codec.setOutputFormat(outputFormat);
}
return codec;
} catch (Exception ex) {
//ex.printStackTrace();
System.err.println("Monte Registry. Codec class not found: " + clazz[i]);
unregisterCodec(clazz[i]);
}
}
return null;
}
/**
* Puts a reader into the registry.
*
* @param fileFormat The file format, e.g."video/avi", "video/quicktime".
* Use "Java" for formats which are not tied to a file format. Must not be
* null.
* @param readerClass The reader class name. Must not be null.
*/
public abstract void putReader(Format fileFormat, String readerClass);
/**
* Puts a writer into the registry.
*
* @param fileFormat The file format, e.g."video/avi", "video/quicktime".
* Use "Java" for formats which are not tied to a file format. Must not be
* null.
* @param writerClass The writer class name. Must not be null.
*/
public abstract void putWriter(Format fileFormat, String writerClass);
/**
* Gets all reader class names from the registry for the specified file
* format.
*
* @param fileFormat The file format, e.g."AVI", "QuickTime".
* @return The reader class names.
*/
public abstract String[] getReaderClasses(Format fileFormat);
/**
* Gets all writer class names from the registry for the specified file
* format.
*
* @param fileFormat The file format, e.g."AVI", "QuickTime".
* @return The writer class names.
*/
public abstract String[] getWriterClasses(Format fileFormat);
public MovieReader getReader(Format fileFormat, File file) {
String[] clazz = getReaderClasses(fileFormat);
for (int i = 0; i < clazz.length; i++) {
try {
return ((MovieReader) Class.forName(clazz[i]).getConstructor(File.class).newInstance(file));
} catch (Exception ex) {
ex.printStackTrace();
}
}
return null;
}
public MovieWriter getWriter(File file) {
Format format = getFileFormat(file);
return format == null ? null : getWriter(format, file);
}
public MovieWriter getWriter(Format fileFormat, File file) {
String[] clazz = getWriterClasses(fileFormat);
for (int i = 0; i < clazz.length; i++) {
try {
return ((MovieWriter) Class.forName(clazz[i]).getConstructor(File.class).newInstance(file));
} catch (Exception ex) {
ex.printStackTrace();
}
}
return null;
}
public MovieReader getReader(File file) {
Format format = getFileFormat(file);
return format == null ? null : getReader(format, file);
}
public abstract void putFileFormat(String extension, Format format);
public abstract Format getFileFormat(File file);
public abstract Format[] getReaderFormats();
public abstract Format[] getWriterFormats();
public abstract Format[] getFileFormats();
public abstract String getExtension(Format ff);
/**
* Suggests output formats for the given input media format and specified
* file format.
*
* @param inputMediaFormat
* @param outputFileFormat
* @return List of output media formats.
*/
public ArrayList<Format> suggestOutputFormats(Format inputMediaFormat, Format outputFileFormat) {
ArrayList<Format> formats = new ArrayList<Format>();
Format matchFormat = new Format(//
MimeTypeKey, outputFileFormat.get(MimeTypeKey),//
MediaTypeKey, inputMediaFormat.get(MediaTypeKey));
Codec[] codecs = getEncoders(matchFormat);
int matchingCount = 0;
for (Codec c : codecs) {
for (Format mf : c.getOutputFormats(null)) {
if (mf.matches(matchFormat)) {
if (inputMediaFormat.matchesWithout(mf, MimeTypeKey)) {
// add matching formats first
formats.add(0, mf.append(inputMediaFormat));
matchingCount++;
} else if (inputMediaFormat.matchesWithout(mf, MimeTypeKey, EncodingKey)) {
// add formats which match everything but the encoding second
formats.add(matchingCount, mf.append(inputMediaFormat));
} else {
// add remaining formats last
formats.add(mf.append(inputMediaFormat));
}
}
}
}
// remove duplicates
for (int i = formats.size() - 1; i >= 0; i--) {
Format fi = formats.get(i);
for (int j = i - 1; j >= 0; j--) {
Format fj = formats.get(j);
if (fi.matches(fj)) {
formats.remove(i);
break;
}
}
}
return formats;
}
public abstract void unregisterCodec(String codecClass);
}

View File

@@ -0,0 +1,77 @@
/*
* @(#)VideoFormatKeys.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import org.monte.media.math.Rational;
/**
* Defines common format keys for video media.
*
* @author Werner Randelshofer
* @version $Id: VideoFormatKeys.java 299 2013-01-03 07:40:18Z werner $
*/
public class VideoFormatKeys extends FormatKeys {
// Standard video ENCODING strings for use with FormatKey.Encoding.
public static final String ENCODING_BUFFERED_IMAGE = "image";
/** Cinepak format. */
public static final String ENCODING_QUICKTIME_CINEPAK = "cvid";
public static final String COMPRESSOR_NAME_QUICKTIME_CINEPAK = "Cinepak";
/** JPEG format. */
public static final String ENCODING_QUICKTIME_JPEG = "jpeg";
public static final String COMPRESSOR_NAME_QUICKTIME_JPEG = "Photo - JPEG";
/** PNG format. */
public static final String ENCODING_QUICKTIME_PNG = "png ";
public static final String COMPRESSOR_NAME_QUICKTIME_PNG = "PNG";
/** Animation format. */
public static final String ENCODING_QUICKTIME_ANIMATION = "rle ";
public static final String COMPRESSOR_NAME_QUICKTIME_ANIMATION = "Animation";
/** Raw format. */
public static final String ENCODING_QUICKTIME_RAW = "raw ";
public static final String COMPRESSOR_NAME_QUICKTIME_RAW = "NONE";
// AVI Formats
/** Microsoft Device Independent Bitmap (DIB) format. */
public static final String ENCODING_AVI_DIB = "DIB ";
/** Microsoft Run Length format. */
public static final String ENCODING_AVI_RLE = "RLE ";
/** Techsmith Screen Capture format. */
public static final String ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE = "tscc";
public static final String COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE = "Techsmith Screen Capture";
/** DosBox Screen Capture format. */
public static final String ENCODING_AVI_DOSBOX_SCREEN_CAPTURE = "ZMBV";
/** JPEG format. */
public static final String ENCODING_AVI_MJPG = "MJPG";
/** PNG format. */
public static final String ENCODING_AVI_PNG = "png ";
/** Interleaved planar bitmap format. */
public static final String ENCODING_BITMAP_IMAGE = "ILBM";
//
/** The WidthKey of a video frame. */
public final static FormatKey<Integer> WidthKey = new FormatKey<Integer>("dimX","width", Integer.class);
/** The HeightKey of a video frame. */
public final static FormatKey<Integer> HeightKey = new FormatKey<Integer>("dimY","height", Integer.class);
/** The number of bits per pixel. */
public final static FormatKey<Integer> DepthKey = new FormatKey<Integer>("dimZ","depth", Integer.class);
/** The data class. */
public final static FormatKey<Class> DataClassKey = new FormatKey<Class>("dataClass", Class.class);
/** The compressor name. */
public final static FormatKey<String> CompressorNameKey = new FormatKey<String>("compressorName", "compressorName",String.class, true);
/** The pixel aspect ratio WidthKey : HeightKey;
*/
public final static FormatKey<Rational> PixelAspectRatioKey = new FormatKey<Rational>("pixelAspectRatio", Rational.class);
/** Whether the frame rate must be fixed. False means variable frame rate. */
public final static FormatKey<Boolean> FixedFrameRateKey = new FormatKey<Boolean>("fixedFrameRate", Boolean.class);
/** Whether the video is interlaced. */
public final static FormatKey<Boolean> InterlaceKey = new FormatKey<Boolean>("interlace", Boolean.class);
/** Encoding quality. Value between 0 and 1. */
public final static FormatKey<Float> QualityKey = new FormatKey<Float>("quality", Float.class);
}

View File

@@ -0,0 +1,106 @@
/*
* @(#)AVIBMPDIB.java 1.0 2009-12-30
*
* Copyright (c) 2009 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.avi;
import org.monte.media.io.ImageInputStreamAdapter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.*;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
/**
* This class defines the JPEG Huffman table, which is omitted in AVI MJPEG
* files.
* <p>
* Source:
* Microsoft Windows Bitmap Format.
* Multimedia Technical Note: JPEG DIB Format.
* (c) 1993 Microsoft Corporation. All rights reserved.
* <a href="http://www.fileformat.info/format/bmp/spec/b7c72ebab8064da48ae5ed0c053c67a4/BMPDIB.TXT">BMPDIB.txt</a>
*
* @author Werner Randelshofer
* @version 1.0 2009-12-30 Created.
*/
public class AVIBMPDIB {
/** MJPG DHT Segment */
private static byte[] MJPGDHTSeg = {
/* JPEG DHT Segment for YCrCb omitted from MJPG data */
(byte) 0xFF, (byte) 0xC4, (byte) 0x01, (byte) 0xA2,
(byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x05, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, (byte) 0x01,
(byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, (byte) 0x10, (byte) 0x00,
(byte) 0x02, (byte) 0x01, (byte) 0x03, (byte) 0x03, (byte) 0x02, (byte) 0x04, (byte) 0x03, (byte) 0x05, (byte) 0x05, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x7D,
(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x00, (byte) 0x04, (byte) 0x11, (byte) 0x05, (byte) 0x12, (byte) 0x21, (byte) 0x31, (byte) 0x41, (byte) 0x06, (byte) 0x13, (byte) 0x51, (byte) 0x61,
(byte) 0x07, (byte) 0x22, (byte) 0x71, (byte) 0x14, (byte) 0x32, (byte) 0x81, (byte) 0x91, (byte) 0xA1, (byte) 0x08, (byte) 0x23, (byte) 0x42, (byte) 0xB1, (byte) 0xC1, (byte) 0x15, (byte) 0x52,
(byte) 0xD1, (byte) 0xF0, (byte) 0x24, (byte) 0x33, (byte) 0x62, (byte) 0x72, (byte) 0x82, (byte) 0x09, (byte) 0x0A, (byte) 0x16, (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1A, (byte) 0x25,
(byte) 0x26, (byte) 0x27, (byte) 0x28, (byte) 0x29, (byte) 0x2A, (byte) 0x34, (byte) 0x35, (byte) 0x36, (byte) 0x37, (byte) 0x38, (byte) 0x39, (byte) 0x3A, (byte) 0x43, (byte) 0x44, (byte) 0x45,
(byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49, (byte) 0x4A, (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56, (byte) 0x57, (byte) 0x58, (byte) 0x59, (byte) 0x5A, (byte) 0x63, (byte) 0x64,
(byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x83,
(byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89, (byte) 0x8A, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98, (byte) 0x99,
(byte) 0x9A, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0xAA, (byte) 0xB2, (byte) 0xB3, (byte) 0xB4, (byte) 0xB5, (byte) 0xB6,
(byte) 0xB7, (byte) 0xB8, (byte) 0xB9, (byte) 0xBA, (byte) 0xC2, (byte) 0xC3, (byte) 0xC4, (byte) 0xC5, (byte) 0xC6, (byte) 0xC7, (byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xD2, (byte) 0xD3,
(byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7, (byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7, (byte) 0xE8,
(byte) 0xE9, (byte) 0xEA, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7, (byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0x11, (byte) 0x00, (byte) 0x02,
(byte) 0x01, (byte) 0x02, (byte) 0x04, (byte) 0x04, (byte) 0x03, (byte) 0x04, (byte) 0x07, (byte) 0x05, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x77, (byte) 0x00,
(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x11, (byte) 0x04, (byte) 0x05, (byte) 0x21, (byte) 0x31, (byte) 0x06, (byte) 0x12, (byte) 0x41, (byte) 0x51, (byte) 0x07, (byte) 0x61, (byte) 0x71,
(byte) 0x13, (byte) 0x22, (byte) 0x32, (byte) 0x81, (byte) 0x08, (byte) 0x14, (byte) 0x42, (byte) 0x91, (byte) 0xA1, (byte) 0xB1, (byte) 0xC1, (byte) 0x09, (byte) 0x23, (byte) 0x33, (byte) 0x52,
(byte) 0xF0, (byte) 0x15, (byte) 0x62, (byte) 0x72, (byte) 0xD1, (byte) 0x0A, (byte) 0x16, (byte) 0x24, (byte) 0x34, (byte) 0xE1, (byte) 0x25, (byte) 0xF1, (byte) 0x17, (byte) 0x18, (byte) 0x19,
(byte) 0x1A, (byte) 0x26, (byte) 0x27, (byte) 0x28, (byte) 0x29, (byte) 0x2A, (byte) 0x35, (byte) 0x36, (byte) 0x37, (byte) 0x38, (byte) 0x39, (byte) 0x3A, (byte) 0x43, (byte) 0x44, (byte) 0x45,
(byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49, (byte) 0x4A, (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56, (byte) 0x57, (byte) 0x58, (byte) 0x59, (byte) 0x5A, (byte) 0x63, (byte) 0x64,
(byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x82,
(byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89, (byte) 0x8A, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98,
(byte) 0x99, (byte) 0x9A, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0xAA, (byte) 0xB2, (byte) 0xB3, (byte) 0xB4, (byte) 0xB5,
(byte) 0xB6, (byte) 0xB7, (byte) 0xB8, (byte) 0xB9, (byte) 0xBA, (byte) 0xC2, (byte) 0xC3, (byte) 0xC4, (byte) 0xC5, (byte) 0xC6, (byte) 0xC7, (byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xD2,
(byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7, (byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7, (byte) 0xE8,
(byte) 0xE9, (byte) 0xEA, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7, (byte) 0xF8, (byte) 0xF9, (byte) 0xFA
};
/** JFIF Start of Image (SOI) segment. */
private static byte[] JFIFSOISeg = {
(byte) 0xff,
(byte) 0xd8
};
public static InputStream prependDHTSeg(byte[] jpgWithoutDHT) {
return prependDHTSeg(jpgWithoutDHT, 0, jpgWithoutDHT.length);
}
public static InputStream prependDHTSeg(byte[] jpgWithoutDHT, int offset, int length) {
// FIXME - Only add DHT Segment if none is present
Vector<InputStream> v = new Vector<InputStream>();
v.add(new ByteArrayInputStream(JFIFSOISeg));
v.add(new ByteArrayInputStream(MJPGDHTSeg));
v.add(new ByteArrayInputStream(jpgWithoutDHT,offset+JFIFSOISeg.length,length-JFIFSOISeg.length));
return new SequenceInputStream(v.elements());
}
public static ImageInputStream prependDHTSeg(ImageInputStream iisWithoutDHT) throws IOException {
Vector<InputStream> v = new Vector<InputStream>();
v.add(new ByteArrayInputStream(JFIFSOISeg));
v.add(new ByteArrayInputStream(MJPGDHTSeg));
iisWithoutDHT.seek(2);// skip JFIF SOI
v.add(new ImageInputStreamAdapter(iisWithoutDHT));
return new MemoryCacheImageInputStream(new SequenceInputStream(v.elements()));
}
public static ImageInputStream prependDHTSeg(InputStream inWithoutDHT) throws IOException {
Vector<InputStream> v = new Vector<InputStream>();
v.add(new ByteArrayInputStream(JFIFSOISeg));
v.add(new ByteArrayInputStream(MJPGDHTSeg));
inWithoutDHT.skip(2);// skip JFIF SOI
v.add(inWithoutDHT);
return new MemoryCacheImageInputStream(new SequenceInputStream(v.elements()));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,517 @@
/**
* @(#)AVIWriter.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland. All rights
* reserved.
*
* You may not use, copy or modify this file, except in compliance onlyWith the
* license agreement you entered into onlyWith Werner Randelshofer. For details
* see accompanying license terms.
*/
package org.monte.media.avi;
import java.util.EnumSet;
import org.monte.media.math.Rational;
import org.monte.media.Format;
import org.monte.media.Codec;
import org.monte.media.Buffer;
import org.monte.media.MovieWriter;
import org.monte.media.Registry;
import org.monte.media.io.ByteArrayImageOutputStream;
import org.monte.media.riff.RIFFParser;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.io.*;
import java.nio.ByteOrder;
import java.util.Arrays;
import javax.imageio.stream.*;
import static org.monte.media.AudioFormatKeys.*;
import static org.monte.media.VideoFormatKeys.*;
import org.monte.media.BufferFlag;
import static org.monte.media.BufferFlag.*;
/**
* Provides high-level support for encoding and writing audio and video samples
* into an AVI 1.0 file.
*
* @author Werner Randelshofer
* @version $Id: AVIWriter.java 306 2013-01-04 16:19:29Z werner $
*/
public class AVIWriter extends AVIOutputStream implements MovieWriter {
public final static Format AVI = new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI);
public final static Format VIDEO_RAW = new Format(
MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_DIB, CompressorNameKey, COMPRESSOR_NAME_QUICKTIME_RAW);
public final static Format VIDEO_JPEG = new Format(
MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_MJPG, CompressorNameKey, COMPRESSOR_NAME_QUICKTIME_RAW);
public final static Format VIDEO_PNG = new Format(
MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_PNG, CompressorNameKey, COMPRESSOR_NAME_QUICKTIME_RAW);
public final static Format VIDEO_RLE = new Format(
MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_RLE, CompressorNameKey, COMPRESSOR_NAME_QUICKTIME_RAW);
public final static Format VIDEO_SCREEN_CAPTURE = new Format(
MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, CompressorNameKey, COMPRESSOR_NAME_QUICKTIME_RAW);
/**
* Creates a new AVI writer.
*
* @param file the output file
*/
public AVIWriter(File file) throws IOException {
super(file);
}
/**
* Creates a new AVI writer.
*
* @param out the output stream.
*/
public AVIWriter(ImageOutputStream out) throws IOException {
super(out);
}
@Override
public Format getFileFormat() throws IOException {
return AVI;
}
@Override
public Format getFormat(int track) {
return tracks.get(track).format;
}
/**
* Returns the media duration of the track in seconds.
*/
@Override
public Rational getDuration(int track) {
Track tr = tracks.get(track);
long duration = getMediaDuration(track);
return new Rational(duration * tr.scale, tr.rate);
}
/**
* Adds a track.
*
* @param format The format of the track.
* @return The track number.
*/
@Override
public int addTrack(Format format) throws IOException {
if (format.get(MediaTypeKey) == MediaType.VIDEO) {
return addVideoTrack(format);
} else {
return addAudioTrack(format);
}
}
/**
* Adds a video track.
*
* @param format The format of the track.
* @return The track number.
*/
private int addVideoTrack(Format vf) throws IOException {
if (!vf.containsKey(EncodingKey)) {
throw new IllegalArgumentException("EncodingKey missing in " + vf);
}
if (!vf.containsKey(FrameRateKey)) {
throw new IllegalArgumentException("FrameRateKey missing in " + vf);
}
if (!vf.containsKey(WidthKey)) {
throw new IllegalArgumentException("WidthKey missing in " + vf);
}
if (!vf.containsKey(HeightKey)) {
throw new IllegalArgumentException("HeightKey missing in " + vf);
}
if (!vf.containsKey(DepthKey)) {
throw new IllegalArgumentException("DepthKey missing in " + vf);
}
int tr = addVideoTrack(vf.get(EncodingKey),
vf.get(FrameRateKey).getDenominator(), vf.get(FrameRateKey).getNumerator(),
vf.get(WidthKey), vf.get(HeightKey), vf.get(DepthKey),
vf.get(FrameRateKey).floor(1).intValue());
setCompressionQuality(tr, vf.get(QualityKey, 1.0f));
return tr;
}
/**
* Adds an audio track.
*
* @param format The format of the track.
* @return The track number.
*/
private int addAudioTrack(Format format) throws IOException {
int waveFormatTag = 0x0001; // WAVE_FORMAT_PCM
long timeScale = 1;
long sampleRate = format.get(SampleRateKey, new Rational(41000, 0)).longValue();
int numberOfChannels = format.get(ChannelsKey, 1);
int sampleSizeInBits = format.get(SampleSizeInBitsKey, 16); //
boolean isCompressed = false; // FIXME
int frameDuration = 1;
int frameSize = format.get(FrameSizeKey, (sampleSizeInBits + 7) / 8 * numberOfChannels);
String enc = format.get(EncodingKey);
if (enc == null) {
waveFormatTag = 0x0001; // WAVE_FORMAT_PCM
} else if (enc.equals(ENCODING_ALAW)) {
waveFormatTag = 0x0001; // WAVE_FORMAT_PCM
} else if (enc.equals(ENCODING_PCM_SIGNED)) {
waveFormatTag = 0x0001; // WAVE_FORMAT_PCM
} else if (enc.equals(ENCODING_PCM_UNSIGNED)) {
waveFormatTag = 0x0001; // WAVE_FORMAT_PCM
} else if (enc.equals(ENCODING_ULAW)) {
waveFormatTag = 0x0001; // WAVE_FORMAT_PCM
} else if (enc.equals(ENCODING_MP3)) {
waveFormatTag = 0x0001; // WAVE_FORMAT_PCM - FIXME
} else {
waveFormatTag = RIFFParser.stringToID(format.get(EncodingKey)) & 0xffff;
}
return addAudioTrack(waveFormatTag, //
timeScale, sampleRate, //
numberOfChannels, sampleSizeInBits, //
isCompressed, //
frameDuration, frameSize);
}
/**
* Returns the codec of the specified track.
*/
public Codec getCodec(int track) {
return tracks.get(track).codec;
}
/**
* Sets the codec for the specified track.
*/
public void setCodec(int track, Codec codec) {
tracks.get(track).codec = codec;
}
@Override
public int getTrackCount() {
return tracks.size();
}
/**
* Encodes the provided image and writes its sample data into the specified
* track.
*
* @param track The track index.
* @param image The image of the video frame.
* @param duration Duration given in media time units.
*
* @throws IndexOutofBoundsException if the track index is out of bounds.
* @throws if the duration is less than 1, or if the dimension of the frame
* does not match the dimension of the video.
* @throws UnsupportedOperationException if the {@code MovieWriter} does not
* have a built-in encoder for this video format.
* @throws IOException if writing the sample data failed.
*/
public void write(int track, BufferedImage image, long duration) throws IOException {
ensureStarted();
VideoTrack vt = (VideoTrack) tracks.get(track);
if (vt.codec == null) {
createCodec(track);
}
if (vt.codec == null) {
throw new UnsupportedOperationException("No codec for this format: " + vt.format);
}
// The dimension of the image must match the dimension of the video track
Format fmt = vt.format;
if (fmt.get(WidthKey) != image.getWidth() || fmt.get(HeightKey) != image.getHeight()) {
throw new IllegalArgumentException("Dimensions of image[" + vt.samples.size()
+ "] (width=" + image.getWidth() + ", height=" + image.getHeight()
+ ") differs from video format of track: " + fmt);
}
// Encode pixel data
{
if (vt.outputBuffer == null) {
vt.outputBuffer = new Buffer();
}
boolean isKeyframe = vt.syncInterval == 0 ? false : vt.samples.size() % vt.syncInterval == 0;
Buffer inputBuffer = new Buffer();
inputBuffer.flags = (isKeyframe) ? EnumSet.of(KEYFRAME) : EnumSet.noneOf(BufferFlag.class);
inputBuffer.data = image;
vt.codec.process(inputBuffer, vt.outputBuffer);
if (vt.outputBuffer.flags.contains(DISCARD)) {
return;
}
// Encode palette data
isKeyframe = vt.outputBuffer.flags.contains(KEYFRAME);
boolean paletteChange = writePalette(track, image, isKeyframe);
writeSample(track, (byte[]) vt.outputBuffer.data, vt.outputBuffer.offset, vt.outputBuffer.length, isKeyframe && !paletteChange);
/*
long offset = getRelativeStreamPosition();
DataChunk videoFrameChunk = new DataChunk(vt.getSampleChunkFourCC(isKeyframe));
moviChunk.add(videoFrameChunk);
videoFrameChunk.getOutputStream().write((byte[]) vt.outputBuffer.data, vt.outputBuffer.offset, vt.outputBuffer.length);
videoFrameChunk.finish();
long length = getRelativeStreamPosition() - offset;
Sample s=new Sample(videoFrameChunk.chunkType, 1, offset, length, isKeyframe&&!paletteChange);
vt.addSample(s);
idx1.add(s);
if (getRelativeStreamPosition() > 1L << 32) {
throw new IOException("AVI file is larger than 4 GB");
}*/
}
}
/**
* Encodes the data provided in the buffer and then writes it into the
* specified track. <p> Does nothing if the discard-flag in the buffer is
* set to true.
*
* @param track The track number.
* @param buf The buffer containing a data sample.
*/
@Override
public void write(int track, Buffer buf) throws IOException {
ensureStarted();
if (buf.flags.contains(DISCARD)) {
return;
}
Track tr = tracks.get(track);
boolean isKeyframe = buf.flags.contains(KEYFRAME);
if (buf.data instanceof BufferedImage) {
if (tr.syncInterval != 0) {
isKeyframe = buf.flags.contains(KEYFRAME) | (tr.samples.size() % tr.syncInterval == 0);
}
}
// Encode palette data
boolean paletteChange = false;
if (buf.data instanceof BufferedImage && tr instanceof VideoTrack) {
paletteChange = writePalette(track, (BufferedImage) buf.data, isKeyframe);
} else if (buf.header instanceof IndexColorModel) {
paletteChange = writePalette(track, (IndexColorModel) buf.header, isKeyframe);
}
// Encode sample data
{
if (buf.format == null) {
throw new IllegalArgumentException("Buffer.format must not be null");
}
if (buf.format.matchesWithout(tr.format, FrameRateKey) && buf.data instanceof byte[]) {
writeSamples(track, buf.sampleCount, (byte[]) buf.data, buf.offset, buf.length,
buf.isFlag(KEYFRAME) && !paletteChange);
return;
}
// We got here, because the buffer format does not match the track
// format. Lets see if we can create a codec which can perform the
// encoding for us.
if (tr.codec == null) {
createCodec(track);
if (tr.codec == null) {
throw new UnsupportedOperationException("No codec for this format " + tr.format);
}
}
if (tr.outputBuffer == null) {
tr.outputBuffer = new Buffer();
}
Buffer outBuf = tr.outputBuffer;
if (tr.codec.process(buf, outBuf) != Codec.CODEC_OK) {
throw new IOException("Codec failed or could not encode the sample in a single step.");
}
if (outBuf.isFlag(DISCARD)) {
return;
}
writeSamples(track, outBuf.sampleCount, (byte[]) outBuf.data, outBuf.offset, outBuf.length,
isKeyframe && !paletteChange);
}
}
private boolean writePalette(int track, BufferedImage image, boolean isKeyframe) throws IOException {
if ((image.getColorModel() instanceof IndexColorModel)) {
return writePalette(track, (IndexColorModel) image.getColorModel(), isKeyframe);
}
return false;
}
private boolean writePalette(int track, IndexColorModel imgPalette, boolean isKeyframe) throws IOException {
ensureStarted();
VideoTrack vt = (VideoTrack) tracks.get(track);
int imgDepth = vt.bitCount;
ByteArrayImageOutputStream tmp = null;
boolean paletteChange = false;
switch (imgDepth) {
case 4: {
//IndexColorModel imgPalette = (IndexColorModel) image.getColorModel();
int[] imgRGBs = new int[16];
imgPalette.getRGBs(imgRGBs);
int[] previousRGBs = new int[16];
if (vt.previousPalette == null) {
vt.previousPalette = vt.palette;
}
vt.previousPalette.getRGBs(previousRGBs);
if (isKeyframe || !Arrays.equals(imgRGBs, previousRGBs)) {
paletteChange = true;
vt.previousPalette = imgPalette;
/*
int first = imgPalette.getMapSize();
int last = -1;
for (int i = 0; i < 16; i++) {
if (previousRGBs[i] != imgRGBs[i] && i < first) {
first = i;
}
if (previousRGBs[i] != imgRGBs[i] && i > last) {
last = i;
}
}*/
int first = 0;
int last = imgPalette.getMapSize() - 1;
/*
* typedef struct {
BYTE bFirstEntry;
BYTE bNumEntries;
WORD wFlags;
PALETTEENTRY peNew[];
} AVIPALCHANGE;
*
* typedef struct tagPALETTEENTRY {
BYTE peRed;
BYTE peGreen;
BYTE peBlue;
BYTE peFlags;
} PALETTEENTRY;
*/
tmp = new ByteArrayImageOutputStream(ByteOrder.LITTLE_ENDIAN);
tmp.writeByte(first);//bFirstEntry
tmp.writeByte(last - first + 1);//bNumEntries
tmp.writeShort(0);//wFlags
for (int i = first; i <= last; i++) {
tmp.writeByte((imgRGBs[i] >>> 16) & 0xff); // red
tmp.writeByte((imgRGBs[i] >>> 8) & 0xff); // green
tmp.writeByte(imgRGBs[i] & 0xff); // blue
tmp.writeByte(0); // reserved*/
}
}
break;
}
case 8: {
//IndexColorModel imgPalette = (IndexColorModel) image.getColorModel();
int[] imgRGBs = new int[256];
imgPalette.getRGBs(imgRGBs);
int[] previousRGBs = new int[256];
if (vt.previousPalette != null) {
vt.previousPalette.getRGBs(previousRGBs);
}
if (isKeyframe || !Arrays.equals(imgRGBs, previousRGBs)) {
paletteChange = true;
vt.previousPalette = imgPalette;
/*
int first = imgPalette.getMapSize();
int last = -1;
for (int i = 0; i < 16; i++) {
if (previousRGBs[i] != imgRGBs[i] && i < first) {
first = i;
}
if (previousRGBs[i] != imgRGBs[i] && i > last) {
last = i;
}
}*/
int first = 0;
int last = imgPalette.getMapSize() - 1;
/*
* typedef struct {
BYTE bFirstEntry;
BYTE bNumEntries;
WORD wFlags;
PALETTEENTRY peNew[];
} AVIPALCHANGE;
*
* typedef struct tagPALETTEENTRY {
BYTE peRed;
BYTE peGreen;
BYTE peBlue;
BYTE peFlags;
} PALETTEENTRY;
*/
tmp = new ByteArrayImageOutputStream(ByteOrder.LITTLE_ENDIAN);
tmp.writeByte(first);//bFirstEntry
tmp.writeByte(last - first + 1);//bNumEntries
tmp.writeShort(0);//wFlags
for (int i = first; i <= last; i++) {
tmp.writeByte((imgRGBs[i] >>> 16) & 0xff); // red
tmp.writeByte((imgRGBs[i] >>> 8) & 0xff); // green
tmp.writeByte(imgRGBs[i] & 0xff); // blue
tmp.writeByte(0); // reserved*/
}
}
break;
}
}
if (tmp != null) {
tmp.close();
writePalette(track, tmp.toByteArray(), 0, (int) tmp.length(), isKeyframe);
}
return paletteChange;
}
private Codec createCodec(Format fmt) {
return Registry.getInstance().getEncoder(fmt.prepend(MimeTypeKey, MIME_AVI));
}
private void createCodec(int track) {
Track tr = tracks.get(track);
Format fmt = tr.format;
tr.codec = createCodec(fmt);
String enc = fmt.get(EncodingKey);
if (tr.codec != null) {
if (fmt.get(MediaTypeKey) == MediaType.VIDEO) {
tr.codec.setInputFormat(fmt.prepend(
EncodingKey, ENCODING_BUFFERED_IMAGE,
DataClassKey, BufferedImage.class));
if (null == tr.codec.setOutputFormat(
fmt.prepend(FixedFrameRateKey, true,
QualityKey, getCompressionQuality(track),
MimeTypeKey, MIME_AVI,
DataClassKey, byte[].class))) {
throw new UnsupportedOperationException("Track " + tr + " codec does not support format " + fmt + ". codec=" + tr.codec);
}
} else {
tr.codec.setInputFormat(null);
if (null == tr.codec.setOutputFormat(
fmt.prepend(FixedFrameRateKey, true,
QualityKey, getCompressionQuality(track),
MimeTypeKey, MIME_AVI,
DataClassKey, byte[].class))) {
throw new UnsupportedOperationException("Track " + tr + " codec " + tr.codec + " does not support format. " + fmt);
}
}
}
}
public boolean isVFRSupported() {
return false;
}
@Override
public boolean isEmpty(int track) {
return tracks.get(track).samples.isEmpty();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,343 @@
/*
* @(#)DIBCodec.java
*
* Copyright © 2011-2012 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.avi;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferByte;
import org.monte.media.AbstractVideoCodec;
import org.monte.media.Buffer;
import org.monte.media.Format;
import org.monte.media.io.SeekableByteArrayOutputStream;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.io.OutputStream;
import static org.monte.media.VideoFormatKeys.*;
import static org.monte.media.BufferFlag.*;
/**
* {@code DIBCodec} encodes a BufferedImage as a Microsoft Device Independent
* Bitmap (DIB) into a byte array.
* <p>
* The DIB codec only works with the AVI file format. Other file formats, such
* as QuickTime, use a different encoding for uncompressed video.
* <p>
* This codec currently only supports encoding from a {@code BufferedImage} into
* the file format. Decoding support may be added in the future.
* <p>
* This codec does not encode the color palette of an image. This must be done
* separately.
* <p>
* The pixels of a frame are written row by row from bottom to top and from
* the left to the right. 24-bit pixels are encoded as BGR.
* <p>
* Supported input formats:
* <ul>
* {@code Format} with {@code BufferedImage.class}, any width, any height,
* depth=4.
* </ul>
* Supported output formats:
* <ul>
* {@code Format} with {@code byte[].class}, same width and height as input
* format, depth=4.
* </ul>
*
* @author Werner Randelshofer
* @version $Id: DIBCodec.java 299 2013-01-03 07:40:18Z werner $
*/
public class DIBCodec extends AbstractVideoCodec {
public DIBCodec() {
super(new Format[]{
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA,
EncodingKey, ENCODING_BUFFERED_IMAGE, FixedFrameRateKey, true), //
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_DIB, DataClassKey, byte[].class,
FixedFrameRateKey, true, DepthKey, 4), //
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_DIB, DataClassKey, byte[].class,
FixedFrameRateKey, true, DepthKey, 8), //
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_DIB, DataClassKey, byte[].class,
FixedFrameRateKey, true, DepthKey, 24), //
},
new Format[]{
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA,
EncodingKey, ENCODING_BUFFERED_IMAGE, FixedFrameRateKey, true), //
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_DIB, DataClassKey, byte[].class,
FixedFrameRateKey, true, DepthKey, 4), //
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_DIB, DataClassKey, byte[].class,
FixedFrameRateKey, true, DepthKey, 8), //
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_DIB, DataClassKey, byte[].class,
FixedFrameRateKey, true, DepthKey, 24), //
});
}
@Override
public int process(Buffer in, Buffer out) {
if (outputFormat.get(EncodingKey) == ENCODING_BUFFERED_IMAGE) {
return decode(in, out);
} else {
return encode(in, out);
}
}
public int decode(Buffer in, Buffer out) {
out.setMetaTo(in);
out.format = outputFormat;
if (in.isFlag(DISCARD)) {
return CODEC_OK;
}
out.sampleCount = 1;
BufferedImage img = null;
int imgType;
switch (outputFormat.get(DepthKey)) {
case 4:
imgType = BufferedImage.TYPE_BYTE_INDEXED;
break;
case 8:
imgType = BufferedImage.TYPE_BYTE_INDEXED;
break;
case 24:
imgType = BufferedImage.TYPE_INT_RGB;
break;
default:
imgType = BufferedImage.TYPE_INT_RGB;
break;
}
if (out.data instanceof BufferedImage) {
img = (BufferedImage) out.data;
// Fixme: Handle sub-image
if (img.getWidth() != outputFormat.get(WidthKey)
|| img.getHeight() != outputFormat.get(HeightKey)
|| img.getType() != imgType) {
img = null;
}
}
if (img == null) {
img = new BufferedImage(outputFormat.get(WidthKey), outputFormat.get(HeightKey), imgType);
}
out.data = img;
switch (outputFormat.get(DepthKey)) {
case 4:
readKey4((byte[]) in.data, in.offset, in.length, img);
break;
case 8:
readKey8((byte[]) in.data, in.offset, in.length, img);
break;
case 24:
default:
readKey24((int[]) in.data, in.offset, in.length, img);
break;
}
return CODEC_OK;
}
public int encode(Buffer in, Buffer out) {
out.setMetaTo(in);
out.format = outputFormat;
if (in.isFlag(DISCARD)) {
return CODEC_OK;
}
SeekableByteArrayOutputStream tmp;
if (out.data instanceof byte[]) {
tmp = new SeekableByteArrayOutputStream((byte[]) out.data);
} else {
tmp = new SeekableByteArrayOutputStream();
}
// Handle sub-image
// FIXME - Scanline stride must be a multiple of four.
Rectangle r;
int scanlineStride;
if (in.data instanceof BufferedImage) {
BufferedImage image = (BufferedImage) in.data;
WritableRaster raster = image.getRaster();
scanlineStride = raster.getSampleModel().getWidth();
r = raster.getBounds();
r.x -= raster.getSampleModelTranslateX();
r.y -= raster.getSampleModelTranslateY();
out.header = image.getColorModel();
} else {
r = new Rectangle(0, 0, outputFormat.get(WidthKey), outputFormat.get(HeightKey));
scanlineStride = outputFormat.get(WidthKey);
out.header = null;
}
try {
switch (outputFormat.get(DepthKey)) {
case 4: {
byte[] pixels = getIndexed8(in);
if (pixels == null) {
out.setFlag(DISCARD);
return CODEC_OK;
}
writeKey4(tmp, pixels, r.width, r.height, r.x + r.y * scanlineStride, scanlineStride);
break;
}
case 8: {
byte[] pixels = getIndexed8(in);
if (pixels == null) {
out.setFlag(DISCARD);
return CODEC_OK;
}
writeKey8(tmp, pixels, r.width, r.height, r.x + r.y * scanlineStride, scanlineStride);
break;
}
case 24: {
int[] pixels = getRGB24(in);
if (pixels == null) {
out.setFlag(DISCARD);
return CODEC_OK;
}
writeKey24(tmp, pixels, r.width, r.height, r.x + r.y * scanlineStride, scanlineStride);
break;
}
default:
out.setFlag(DISCARD);
return CODEC_OK;
}
out.setFlag(KEYFRAME);
out.data = tmp.getBuffer();
out.sampleCount = 1;
out.offset = 0;
out.length = (int) tmp.getStreamPosition();
return CODEC_OK;
} catch (IOException ex) {
ex.printStackTrace();
out.setFlag(DISCARD);
return CODEC_FAILED;
}
}
public void readKey4(byte[] in, int offset, int length, BufferedImage img) {
DataBufferByte buf = (DataBufferByte) img.getRaster().getDataBuffer();
WritableRaster raster = img.getRaster();
int scanlineStride = raster.getSampleModel().getWidth();
Rectangle r = raster.getBounds();
r.x -= raster.getSampleModelTranslateX();
r.y -= raster.getSampleModelTranslateY();
throw new UnsupportedOperationException("readKey4 not yet implemented");
}
public void readKey8(byte[] in, int offset, int length, BufferedImage img) {
DataBufferByte buf = (DataBufferByte) img.getRaster().getDataBuffer();
WritableRaster raster = img.getRaster();
int scanlineStride = raster.getSampleModel().getWidth();
Rectangle r = raster.getBounds();
r.x -= raster.getSampleModelTranslateX();
r.y -= raster.getSampleModelTranslateY();
int h=img.getHeight();
int w=img.getWidth();
int i=offset;
int j=r.x+r.y*scanlineStride+(h-1)*scanlineStride;
byte[] out=buf.getData();
for (int y=0;y<h;y++) {
System.arraycopy(in,i,out,j,w);
i+=w;
j-=scanlineStride;
}
}
public void readKey24(int[] in, int offset, int length, BufferedImage img) {
DataBufferInt buf = (DataBufferInt) img.getRaster().getDataBuffer();
WritableRaster raster = img.getRaster();
int scanlineStride = raster.getSampleModel().getWidth();
Rectangle r = raster.getBounds();
r.x -= raster.getSampleModelTranslateX();
r.y -= raster.getSampleModelTranslateY();
int h=img.getHeight();
int w=img.getWidth();
int i=offset;
int j=r.x+r.y*scanlineStride+(h-1)*scanlineStride;
int[] out=buf.getData();
for (int y=0;y<h;y++) {
System.arraycopy(in,i,out,j,w);
i+=w;
j-=scanlineStride;
}
}
/** Encodes a 4-bit key frame.
*
* @param out The output stream.
* @param pixels The image data.
* @param offset The offset to the first pixel in the data array.
* @param width The width of the image in data elements.
* @param scanlineStride The number to append to offset to get to the next scanline.
*/
public void writeKey4(OutputStream out, byte[] pixels, int width, int height, int offset, int scanlineStride)
throws IOException {
byte[] bytes = new byte[width];
for (int y = (height - 1) * scanlineStride; y >= 0; y -= scanlineStride) { // Upside down
for (int x = offset, xx = 0, n = offset + width; x < n; x += 2, ++xx) {
bytes[xx] = (byte) (((pixels[y + x] & 0xf) << 4) | (pixels[y + x + 1] & 0xf));
}
out.write(bytes);
}
}
/** Encodes an 8-bit key frame.
*
* @param out The output stream.
* @param pixels The image data.
* @param offset The offset to the first pixel in the data array.
* @param width The width of the image in data elements.
* @param scanlineStride The number to append to offset to get to the next scanline.
*/
public void writeKey8(OutputStream out, byte[] pixels, int width, int height, int offset, int scanlineStride)
throws IOException {
for (int y = (height - 1) * scanlineStride; y >= 0; y -= scanlineStride) { // Upside down
out.write(pixels, y + offset, width);
}
}
/** Encodes a 24-bit key frame.
*
* @param out The output stream.
* @param pixels The image data.
* @param offset The offset to the first pixel in the data array.
* @param width The width of the image in data elements.
* @param scanlineStride The number to append to offset to get to the next scanline.
*/
public void writeKey24(OutputStream out, int[] pixels, int width, int height, int offset, int scanlineStride)
throws IOException {
int w3 = width * 3;
byte[] bytes = new byte[w3]; // holds a scanline of raw image data with 3 channels of 8 bit data
for (int xy = (height - 1) * scanlineStride + offset; xy >= offset; xy -= scanlineStride) { // Upside down
for (int x = 0, xp = 0; x < w3; x += 3, ++xp) {
int p = pixels[xy + xp];
bytes[x] = (byte) (p); // Blue
bytes[x + 1] = (byte) (p >> 8); // Green
bytes[x + 2] = (byte) (p >> 16); // Red
}
out.write(bytes);
}
}
}

View File

@@ -0,0 +1,213 @@
/*
* @(#)Colors.java 1.0 2011-03-13
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.color;
import java.awt.image.IndexColorModel;
import static java.lang.Math.*;
/**
* {@code Colors}.
*
* @author Werner Randelshofer
* @version 1.0 2011-03-13 Created.
*/
public class Colors {
/** Prevent instance creation. */
private Colors() {
}
/**
* The macintosh palette is arranged as follows: there are 256 colours to
* allocate, an even distribution of colors through the color cube might be
* desirable but 256 is not the cube of an integer. 6x6x6 is 216 and so the
* first 216 colors are an equal 6x6x6 sampling of the color cube.
* This leaves 40 colours to allocate, this has been done by choosing a ramp of
* 10 shades each for red, green, blue and grey.
*
* <p>
* References:<br>
* <a href="http://paulbourke.net/texture_colour/colourramp/">http://paulbourke.net/texture_colour/colourramp/</a>
*
* @return The Macintosh color palette.
*/
public static IndexColorModel createMacColors() {
byte[] r = new byte[256];
byte[] g = new byte[256];
byte[] b = new byte[256];
// Generate color cube with 216 colors
int index = 0;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
for (int k = 0; k < 6; k++) {
r[index] = (byte) (255 - 51 * i);
g[index] = (byte) (255 - 51 * j);
b[index] = (byte) (255 - 51 * k);
index++;
}
}
}
index--; // overwrite last color (black) with color ramp
// Generate red ramp
byte[] ramp = {(byte) 238, (byte) 221, (byte) 187, (byte) 170, (byte) 136, (byte) 119, 85, 68, 34, 17};
for (int i = 0; i < 10; i++) {
r[index] = ramp[i];
g[index] = (byte) (0);
b[index] = (byte) (0);
index++;
}
// Generate green ramp
for (int j = 0; j < 10; j++) {
r[index] = (byte) (0);
g[index] = ramp[j];
b[index] = (byte) (0);
index++;
}
// Generate blue ramp
for (int k = 0; k < 10; k++) {
r[index] = (byte) (0);
g[index] = (byte) (0);
b[index] = ramp[k];
index++;
}
// Generate gray ramp
for (int ijk = 0; ijk < 10; ijk++) {
r[index] = ramp[ijk];
g[index] = ramp[ijk];
b[index] = ramp[ijk];
index++;
}
// last color is black (nothing to do)
/*
for (int i=0;i<256;i++) {
if (i%6==0) System.out.println(); else System.out.print(" ");
System.out.print(Integer.toHexString(r[i]&0xff)+","+Integer.toHexString(g[i]&0xff)+","+Integer.toHexString(b[i]&0xff));
}*/
IndexColorModel icm = new IndexColorModel(8, 256, r, g, b);
return icm;
}
private static void RGBtoYCC(float[] rgb, float[] ycc) {
float R = rgb[0];
float G = rgb[1];
float B = rgb[2];
float Y = 0.3f * R + 0.6f * G + 0.1f * B;
float V = R - Y;
float U = B - Y;
float Cb = (U / 2f) + 0.5f;
float Cr = (V / 1.6f) + 0.5f;
ycc[0] = Y;
ycc[1] = Cb;
ycc[2] = Cr;
}
private static void YCCtoRGB(float[] ycc, float[] rgb) {
float Y = ycc[0];
float Cb = ycc[1];
float Cr = ycc[2];
float U = (Cb - 0.5f) * 2f;
float V = (Cr - 0.5f) * 1.6f;
float R = V + Y;
float B = U + Y;
float G = (Y - 0.3f * R - 0.1f * B) / 0.6f;
rgb[0] = R;
rgb[1] = G;
rgb[2] = B;
}
/** RGB 8-bit per channel to YCC 16-bit per channel. */
private static void RGB8toYCC16(int[] rgb, int[] ycc) {
int R = rgb[0];
int G = rgb[1];
int B = rgb[2];
int Y = 77 * R + 153 * G + 26 * B;
int V = R * 256 - Y;
int U = B * 256 - Y;
int Cb = (U / 2) + 128 * 256;
int Cr = (V * 5 / 8) + 128 * 256;
ycc[0] = Y;
ycc[1] = Cb;
ycc[2] = Cr;
}
/** RGB 8-bit per channel to YCC 16-bit per channel. */
private static void RGB8toYCC16(int rgb, int[] ycc) {
int R = (rgb & 0xff0000) >>> 16;
int G = (rgb & 0xff00) >>> 8;
int B = rgb & 0xff;
int Y = 77 * R + 153 * G + 26 * B;
int V = R * 256 - Y;
int U = B * 256 - Y;
int Cb = (U / 2) + 128 * 256;
int Cr = (V * 5 / 8) + 128 * 256;
ycc[0] = Y;
ycc[1] = Cb;
ycc[2] = Cr;
}
/** YCC 16-bit per channel to RGB 8-bit per channel. */
private static void YCC16toRGB8(int[] ycc, int[] rgb) {
int Y = ycc[0];
int Cb = ycc[1];
int Cr = ycc[2];
int U = (Cb - 128 * 256) * 2;
int V = (Cr - 128 * 256) * 8 / 5;
int R = min(255, max(0, (V + Y) / 256));
int B = min(255, max(0, (U + Y) / 256));
int G = min(255, max(0, (Y - 77 * R - 26 * B) / 153));
rgb[0] = R;
rgb[1] = G;
rgb[2] = B;
}
/** YCC 8-bit per channel to RGB 8-bit per channel.
*/
private static void YCC8toRGB8(int[] ycc, int[] rgb) {
int Y = ycc[0];
int Cb = ycc[1];
int Cr = ycc[2];
// Source: JPEG File Interchange Format Version 1.02, September 1, 1992
//RGB can be computed directly from YCbCr (256 levels) as follows:
//R = Y + 1.402 (Cr-128)
//G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
//B = Y + 1.772 (Cb-128)
int R = (1000 * Y + 1402 * (Cr - 128)) / 1000;
int G = (100000 * Y - 34414 * (Cb - 128) - 71414 * (Cr - 128)) / 100000;
int B = (1000 * Y + 1772 * (Cb - 128)) / 1000;
rgb[0] = min(255, max(0, R));
rgb[1] = min(255, max(0, G));
rgb[2] = min(255, max(0, B));
}
/** YCC 8-bit per channel to RGB 8-bit per channel.
*/
private static void RGB8toYCC8(int[] rgb, int[] ycc) {
int R = rgb[0];
int G = rgb[1];
int B = rgb[2];
// Source: JPEG File Interchange Format Version 1.02, September 1, 1992
//YCbCr (256 levels) can be computed directly from 8-bit RGB as follows:
//Y = 0.299R +0.587G +0.114B
//Cb = - 0.1687 R - 0.3313 G + 0.5 B + 128
//Cr = 0.5 R - 0.4187 G - 0.0813 B + 128
int Y = (299 * R + 587 * G + 114 * B) / 1000;
int Cb = (-1687 * R - 3313 * G + 5000 * B) / 10000 + 128;
int Cr = (5000 * R - 4187 * G - 813 * B) / 10000 + 128;
ycc[0] = min(255, max(0, Y));
ycc[1] = min(255, max(0, Cb));
ycc[2] = min(255, max(0, Cr));
}
}

View File

@@ -0,0 +1,216 @@
/*
* @(#)ByteArrayImageInputStream.java
*
* Copyright (c) 2008-2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.io;
import java.io.IOException;
import java.nio.ByteOrder;
/**
* A {@code ByteArrayImageInputStream} contains
* an internal buffer that contains bytes that
* may be read from the stream. An internal
* counter keeps track of the next byte to
* be supplied by the {@code read} method.
* <p>
* Closing a {@code ByteArrayImageInputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an {@code IOException}.
*
* @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau
* @version $Id: ByteArrayImageInputStream.java 299 2013-01-03 07:40:18Z werner $
*/
public class ByteArrayImageInputStream extends ImageInputStreamImpl2 {
/**
* An array of bytes that was provided
* by the creator of the stream. Elements <code>buf[0]</code>
* through <code>buf[count-1]</code> are the
* only bytes that can ever be read from the
* stream; element <code>buf[streamPos]</code> is
* the next byte to be read.
*/
protected byte buf[];
/**
* The index one greater than the last valid character in the input
* stream buffer.
* This value should always be nonnegative
* and not larger than the length of <code>buf</code>.
* It is one greater than the position of
* the last byte within <code>buf</code> that
* can ever be read from the input stream buffer.
*/
protected int count;
/** The offset to the start of the array. */
private final int arrayOffset;
public ByteArrayImageInputStream(byte[] buf) {
this(buf, ByteOrder.BIG_ENDIAN);
}
public ByteArrayImageInputStream(byte[] buf, ByteOrder byteOrder) {
this(buf, 0, buf.length, byteOrder);
}
public ByteArrayImageInputStream(byte[] buf, int offset, int length, ByteOrder byteOrder) {
this.buf = buf;
this.streamPos = offset;
this.count = Math.min(offset + length, buf.length);
this.arrayOffset = offset;
this.byteOrder = byteOrder;
}
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an <code>int</code> in the range
* <code>0</code> to <code>255</code>. If no byte is available
* because the end of the stream has been reached, the value
* <code>-1</code> is returned.
* <p>
* This <code>read</code> method
* cannot block.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream has been reached.
*/
@Override
public synchronized int read() {
flushBits();
return (streamPos < count) ? (buf[(int)(streamPos++)] & 0xff) : -1;
}
/**
* Reads up to <code>len</code> bytes of data into an array of bytes
* from this input stream.
* If <code>streamPos</code> equals <code>count</code>,
* then <code>-1</code> is returned to indicate
* end of file. Otherwise, the number <code>k</code>
* of bytes read is equal to the smaller of
* <code>len</code> and <code>count-streamPos</code>.
* If <code>k</code> is positive, then bytes
* <code>buf[streamPos]</code> through <code>buf[streamPos+k-1]</code>
* are copied into <code>b[off]</code> through
* <code>b[off+k-1]</code> in the manner performed
* by <code>System.arraycopy</code>. The
* value <code>k</code> is added into <code>streamPos</code>
* and <code>k</code> is returned.
* <p>
* This <code>read</code> method cannot block.
*
* @param b the buffer into which the data is read.
* @param off the start offset in the destination array <code>b</code>
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
* @exception NullPointerException If <code>b</code> is <code>null</code>.
* @exception IndexOutOfBoundsException If <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>b.length - off</code>
*/
@Override
public synchronized int read(byte b[], int off, int len) {
flushBits();
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
if (streamPos >= count) {
return -1;
}
if (streamPos + len > count) {
len = (int)(count - streamPos);
}
if (len <= 0) {
return 0;
}
System.arraycopy(buf, (int)streamPos, b, off, len);
streamPos += len;
return len;
}
/**
* Skips <code>n</code> bytes of input from this input stream. Fewer
* bytes might be skipped if the end of the input stream is reached.
* The actual number <code>k</code>
* of bytes to be skipped is equal to the smaller
* of <code>n</code> and <code>count-streamPos</code>.
* The value <code>k</code> is added into <code>streamPos</code>
* and <code>k</code> is returned.
*
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
*/
public synchronized long skip(long n) {
if (streamPos + n > count) {
n = count - streamPos;
}
if (n < 0) {
return 0;
}
streamPos += n;
return n;
}
/**
* Returns the number of remaining bytes that can be read (or skipped over)
* from this input stream.
* <p>
* The value returned is <code>count&nbsp;- streamPos</code>,
* which is the number of bytes remaining to be read from the input buffer.
*
* @return the number of remaining bytes that can be read (or skipped
* over) from this input stream without blocking.
*/
public synchronized int available() {
return (int)(count - streamPos);
}
/**
* Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an <tt>IOException</tt>.
* <p>
*/
@Override
public void close() {
// does nothing!!
}
@Override
public long getStreamPosition() throws IOException {
checkClosed();
return streamPos-arrayOffset;
}
@Override
public void seek(long pos) throws IOException {
checkClosed();
flushBits();
// This test also covers pos < 0
if (pos < flushedPos) {
throw new IndexOutOfBoundsException("pos < flushedPos!");
}
this.streamPos = pos+arrayOffset;
}
private void flushBits() {
bitOffset=0;
}
@Override
public long length() {
return count-arrayOffset;
}
}

View File

@@ -0,0 +1,336 @@
/*
* @(#)ByteArrayImageOutputStream.java 1.0.1 2011-01-23
*
* Copyright (c) 2011 Werner Randelshofer
* Staldenmattweg 2, Goldau, CH-6405, Switzerland.
* All rights reserved.
*
* The copyright of this software is owned by Werner Randelshofer.
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* Werner Randelshofer. For details see accompanying license terms.
*/
package org.monte.media.io;
import java.io.OutputStream;
import javax.imageio.stream.ImageOutputStreamImpl;
import java.io.ByteArrayOutputStream;
import javax.imageio.stream.ImageOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.nio.ByteOrder;
import static java.lang.Math.*;
/**
* This class implements an image output stream in which the data is
* written into a byte array. The buffer automatically grows as data
* is written to it.
* The data can be retrieved using {@code toByteArray()}, {@code toImageOutputStream()}
* and {@code toOutputStream()}.
* <p>
* Closing a {@code ByteArrayImageOutputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an {@code IOException}.
*
* @author Werner Randelshofer
* @version 1.0.1 2011-01-23 Implements length method.
* <br>1.0 2011-01-18 Created.
*/
public class ByteArrayImageOutputStream extends ImageOutputStreamImpl {
/**
* An array of bytes that was provided
* by the creator of the stream. Elements <code>buf[0]</code>
* through <code>buf[count-1]</code> are the
* only bytes that can ever be read from the
* stream; element <code>buf[streamPos]</code> is
* the next byte to be read.
*/
protected byte buf[];
/**
* The index one greater than the last valid character in the input
* stream buffer.
* This value should always be nonnegative
* and not larger than the length of <code>buf</code>.
* It is one greater than the position of
* the last byte within <code>buf</code> that
* can ever be read from the input stream buffer.
*/
protected int count;
/** The offset to the start of the array. */
private final int arrayOffset;
public ByteArrayImageOutputStream() {
this(16);
}
public ByteArrayImageOutputStream(int initialCapacity) {
this(new byte[initialCapacity]);
}
public ByteArrayImageOutputStream(byte[] buf) {
this(buf, ByteOrder.BIG_ENDIAN);
}
public ByteArrayImageOutputStream(byte[] buf, ByteOrder byteOrder) {
this(buf, 0, buf.length, byteOrder);
}
public ByteArrayImageOutputStream(byte[] buf, int offset, int length, ByteOrder byteOrder) {
this.buf = buf;
this.streamPos = offset;
this.count = Math.min(offset + length, buf.length);
this.arrayOffset = offset;
this.byteOrder = byteOrder;
}
public ByteArrayImageOutputStream(ByteOrder byteOrder) {
this(new byte[16],byteOrder);
}
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an <code>int</code> in the range
* <code>0</code> to <code>255</code>. If no byte is available
* because the end of the stream has been reached, the value
* <code>-1</code> is returned.
* <p>
* This <code>read</code> method
* cannot block.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream has been reached.
*/
@Override
public synchronized int read() throws IOException {
flushBits();
return (streamPos < count) ? (buf[(int) (streamPos++)] & 0xff) : -1;
}
/**
* Reads up to <code>len</code> bytes of data into an array of bytes
* from this input stream.
* If <code>streamPos</code> equals <code>count</code>,
* then <code>-1</code> is returned to indicate
* end of file. Otherwise, the number <code>k</code>
* of bytes read is equal to the smaller of
* <code>len</code> and <code>count-streamPos</code>.
* If <code>k</code> is positive, then bytes
* <code>buf[streamPos]</code> through <code>buf[streamPos+k-1]</code>
* are copied into <code>b[off]</code> through
* <code>b[off+k-1]</code> in the manner performed
* by <code>System.arraycopy</code>. The
* value <code>k</code> is added into <code>streamPos</code>
* and <code>k</code> is returned.
* <p>
* This <code>read</code> method cannot block.
*
* @param b the buffer into which the data is read.
* @param off the start offset in the destination array <code>b</code>
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
* @exception NullPointerException If <code>b</code> is <code>null</code>.
* @exception IndexOutOfBoundsException If <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>b.length - off</code>
*/
@Override
public synchronized int read(byte b[], int off, int len) throws IOException {
flushBits();
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
if (streamPos >= count) {
return -1;
}
if (streamPos + len > count) {
len = (int) (count - streamPos);
}
if (len <= 0) {
return 0;
}
System.arraycopy(buf, (int) streamPos, b, off, len);
streamPos += len;
return len;
}
/**
* Skips <code>n</code> bytes of input from this input stream. Fewer
* bytes might be skipped if the end of the input stream is reached.
* The actual number <code>k</code>
* of bytes to be skipped is equal to the smaller
* of <code>n</code> and <code>count-streamPos</code>.
* The value <code>k</code> is added into <code>streamPos</code>
* and <code>k</code> is returned.
*
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
*/
public synchronized long skip(long n) {
if (streamPos + n > count) {
n = count - streamPos;
}
if (n < 0) {
return 0;
}
streamPos += n;
return n;
}
/**
* Returns the number of remaining bytes that can be read (or skipped over)
* from this input stream.
* <p>
* The value returned is <code>count&nbsp;- streamPos</code>,
* which is the number of bytes remaining to be read from the input buffer.
*
* @return the number of remaining bytes that can be read (or skipped
* over) from this input stream without blocking.
*/
public synchronized int available() {
return (int) (count - streamPos);
}
/**
* Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an <tt>IOException</tt>.
* <p>
*/
@Override
public void close() {
// does nothing!!
}
@Override
public long getStreamPosition() throws IOException {
checkClosed();
return streamPos - arrayOffset;
}
@Override
public void seek(long pos) throws IOException {
checkClosed();
flushBits();
// This test also covers pos < 0
if (pos < flushedPos) {
throw new IndexOutOfBoundsException("pos < flushedPos!");
}
this.streamPos = pos + arrayOffset;
}
/**
* Writes the specified byte to this output stream.
*
* @param b the byte to be written.
*/
@Override
public synchronized void write(int b) throws IOException {
flushBits();
long newcount = max(streamPos + 1, count);
if (newcount> Integer.MAX_VALUE) {
throw new IndexOutOfBoundsException(newcount+" > max array size");
}
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, max(buf.length << 1, (int) newcount));
}
buf[(int) streamPos++] = (byte) b;
count = (int)newcount;
}
/**
* Writes the specified byte array to this output stream.
*
* @param b the data.
*/
@Override
public synchronized void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
/**
* Writes <code>len</code> bytes from the specified byte array
* starting at offset <code>off</code> to this output stream.
*
* @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
*/
@Override
public synchronized void write(byte b[], int off, int len) throws IOException {
flushBits();
if ((off < 0) || (off > b.length) || (len < 0)
|| ((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException("off="+off+", len="+len+", b.length="+b.length);
} else if (len == 0) {
return;
}
int newcount = max((int) streamPos + len, count);
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
}
System.arraycopy(b, off, buf, (int) streamPos, len);
streamPos += len;
count = newcount;
}
/** Writes the contents of the byte array into the specified output
* stream.
* @param out
*/
public void toOutputStream(OutputStream out) throws IOException {
out.write(buf, arrayOffset, count);
}
/** Writes the contents of the byte array into the specified image output
* stream.
* @param out
*/
public void toImageOutputStream(ImageOutputStream out) throws IOException {
out.write(buf, arrayOffset, count);
}
/**
* Creates a newly allocated byte array. Its size is the current
* size of this output stream and the valid contents of the buffer
* have been copied into it.
*
* @return the current contents of this output stream, as a byte array.
* @see java.io.ByteArrayOutputStream#size()
*/
public synchronized byte[] toByteArray() {
byte[] copy = new byte[count - arrayOffset];
System.arraycopy(buf, arrayOffset, copy, 0, count);
return copy;
}
/** Returns the internally used byte buffer. */
public byte[] getBuffer() {
return buf;
}
@Override
public long length() {
return count-arrayOffset;
}
/**
* Resets the <code>count</code> field of this byte array output
* stream to zero, so that all currently accumulated output in the
* output stream is discarded. The output stream can be used again,
* reusing the already allocated buffer space.
*
* @see java.io.ByteArrayInputStream#count
*/
public synchronized void clear() {
count = arrayOffset;
streamPos=arrayOffset;
}
}

View File

@@ -0,0 +1,189 @@
/*
* @(#)ImageInputStreamAdapter.java 1.0 2009-12-17
*
* Copyright (c) 2009 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.io;
import java.io.FilterInputStream;
import java.io.IOException;
import javax.imageio.stream.ImageInputStream;
/**
* ImageInputStreamAdapter.
*
* @author Werner Randelshofer
* @version 1.0 2009-12-17 Created.
*/
public class ImageInputStreamAdapter extends FilterInputStream {
private ImageInputStream iis;
public ImageInputStreamAdapter(ImageInputStream iis) {
super(null);
this.iis=iis;
}
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an <code>int</code> in the range
* <code>0</code> to <code>255</code>. If no byte is available
* because the end of the stream has been reached, the value
* <code>-1</code> is returned. This method blocks until input data
* is available, the end of the stream is detected, or an exception
* is thrown.
* <p>
* This method
* simply performs <code>in.read()</code> and returns the result.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @exception IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
@Override
public int read() throws IOException {
return iis.read();
}
/**
* Reads up to <code>len</code> bytes of data from this input stream
* into an array of bytes. If <code>len</code> is not zero, the method
* blocks until some input is available; otherwise, no
* bytes are read and <code>0</code> is returned.
* <p>
* This method simply performs <code>in.read(b, off, len)</code>
* and returns the result.
*
* @param b the buffer into which the data is read.
* @param off the start offset in the destination array <code>b</code>
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
* @exception NullPointerException If <code>b</code> is <code>null</code>.
* @exception IndexOutOfBoundsException If <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>b.length - off</code>
* @exception IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
@Override
public int read(byte b[], int off, int len) throws IOException {
return iis.read(b, off, len);
}
/**
* {@inheritDoc}
* <p>
* This method simply performs <code>in.skip(n)</code>.
*/
@Override
public long skip(long n) throws IOException {
return iis.skipBytes(n);
}
/**
* Returns an estimate of the number of bytes that can be read (or
* skipped over) from this input stream without blocking by the next
* caller of a method for this input stream. The next caller might be
* the same thread or another thread. A single read or skip of this
* many bytes will not block, but may read or skip fewer bytes.
* <p>
* This method returns the result of {@link #in in}.available().
*
* @return an estimate of the number of bytes that can be read (or skipped
* over) from this input stream without blocking.
* @exception IOException if an I/O error occurs.
*/
@Override
public int available() throws IOException {
return (iis.isCached()) ? //
(int)Math.min(Integer.MAX_VALUE, iis.length() - iis.getStreamPosition()) :
0;
}
/**
* Closes this input stream and releases any system resources
* associated with the stream.
* This
* method simply performs <code>in.close()</code>.
*
* @exception IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
@Override
public void close() throws IOException {
iis.close();
}
/**
* Marks the current position in this input stream. A subsequent
* call to the <code>reset</code> method repositions this stream at
* the last marked position so that subsequent reads re-read the same bytes.
* <p>
* The <code>readlimit</code> argument tells this input stream to
* allow that many bytes to be read before the mark position gets
* invalidated.
* <p>
* This method simply performs <code>in.mark(readlimit)</code>.
*
* @param readlimit the maximum limit of bytes that can be read before
* the mark position becomes invalid.
* @see java.io.FilterInputStream#in
* @see java.io.FilterInputStream#reset()
*/
@Override
public synchronized void mark(int readlimit) {
iis.mark();
}
/**
* Repositions this stream to the position at the time the
* <code>mark</code> method was last called on this input stream.
* <p>
* This method
* simply performs <code>in.reset()</code>.
* <p>
* Stream marks are intended to be used in
* situations where you need to read ahead a little to see what's in
* the stream. Often this is most easily done by invoking some
* general parser. If the stream is of the type handled by the
* parse, it just chugs along happily. If the stream is not of
* that type, the parser should toss an exception when it fails.
* If this happens within readlimit bytes, it allows the outer
* code to reset the stream and try another parser.
*
* @exception IOException if the stream has not been marked or if the
* mark has been invalidated.
* @see java.io.FilterInputStream#in
* @see java.io.FilterInputStream#mark(int)
*/
@Override
public synchronized void reset() throws IOException {
iis.reset();
}
/**
* Tests if this input stream supports the <code>mark</code>
* and <code>reset</code> methods.
* This method
* simply performs <code>in.markSupported()</code>.
*
* @return <code>true</code> if this stream type supports the
* <code>mark</code> and <code>reset</code> method;
* <code>false</code> otherwise.
* @see java.io.FilterInputStream#in
* @see java.io.InputStream#mark(int)
* @see java.io.InputStream#reset()
*/
@Override
public boolean markSupported() {
return true;
}
}

View File

@@ -0,0 +1,65 @@
/*
* @(#)ImageInputStreamImpl2.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.io;
import java.io.IOException;
import java.nio.ByteOrder;
import javax.imageio.stream.ImageInputStreamImpl;
/**
* {@code ImageInputStreamImpl2} fixes bugs in ImageInputStreamImpl.
* <p>
* ImageInputStreamImpl uses read(byte[]) instead of readFully(byte[]) inside of
* readShort. This results in corrupt data input if the underlying stream can
* not fulfill the read operation in a single step.
*
* @author Werner Randelshofer
* @version $Id: ImageInputStreamImpl2.java 299 2013-01-03 07:40:18Z werner $
*/
public abstract class ImageInputStreamImpl2 extends ImageInputStreamImpl {
// Length of the buffer used for readFully(type[], int, int)
private static final int BYTE_BUF_LENGTH = 8192;
/**
* Byte buffer used for readFully(type[], int, int). Note that this
* array is also used for bulk reads in readShort(), readInt(), etc, so
* it should be large enough to hold a primitive value (i.e. >= 8 bytes).
* Also note that this array is package protected, so that it can be
* used by ImageOutputStreamImpl in a similar manner.
*/
byte[] byteBuf = new byte[BYTE_BUF_LENGTH];
@Override
public short readShort() throws IOException {
readFully(byteBuf, 0, 2);
if (byteOrder == ByteOrder.BIG_ENDIAN) {
return (short)
(((byteBuf[0] & 0xff) << 8) | ((byteBuf[1] & 0xff) << 0));
} else {
return (short)
(((byteBuf[1] & 0xff) << 8) | ((byteBuf[0] & 0xff) << 0));
}
}
public int readInt() throws IOException {
readFully(byteBuf, 0, 4);
if (byteOrder == ByteOrder.BIG_ENDIAN) {
return
(((byteBuf[0] & 0xff) << 24) | ((byteBuf[1] & 0xff) << 16) |
((byteBuf[2] & 0xff) << 8) | ((byteBuf[3] & 0xff) << 0));
} else {
return
(((byteBuf[3] & 0xff) << 24) | ((byteBuf[2] & 0xff) << 16) |
((byteBuf[1] & 0xff) << 8) | ((byteBuf[0] & 0xff) << 0));
}
}
}

View File

@@ -0,0 +1,163 @@
/*
* @(#)SeekableByteArrayOutputStream.java
*
* Copyright © 2010-2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.io;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import static java.lang.Math.*;
/**
* {@code SeekableByteArrayOutputStream}.
*
* @author Werner Randelshofer
* @version $Id: SeekableByteArrayOutputStream.java 299 2013-01-03 07:40:18Z werner $
*/
public class SeekableByteArrayOutputStream extends ByteArrayOutputStream {
/**
* The current stream position.
*/
private int pos;
/**
* Creates a new byte array output stream. The buffer capacity is
* initially 32 bytes, though its size increases if necessary.
*/
public SeekableByteArrayOutputStream() {
this(32);
}
/**
* Creates a new byte array output stream, with a buffer capacity of
* the specified size, in bytes.
*
* @param size the initial size.
* @exception IllegalArgumentException if size is negative.
*/
public SeekableByteArrayOutputStream(int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
buf = new byte[size];
}
/**
* Creates a new byte array output stream, which reuses the supplied buffer.
*/
public SeekableByteArrayOutputStream(byte[] buf) {
this.buf = buf;
}
/**
* Writes the specified byte to this byte array output stream.
*
* @param b the byte to be written.
*/
@Override
public synchronized void write(int b) {
int newcount = max(pos + 1, count);
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
}
buf[pos++] = (byte)b;
count = newcount;
}
/**
* Writes <code>len</code> bytes from the specified byte array
* starting at offset <code>off</code> to this byte array output stream.
*
* @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
*/
@Override
public synchronized void write(byte b[], int off, int len) {
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
int newcount = max(pos+len,count);
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
}
System.arraycopy(b, off, buf, pos, len);
pos+=len;
count = newcount;
}
/**
* Resets the <code>count</code> field of this byte array output
* stream to zero, so that all currently accumulated output in the
* output stream is discarded. The output stream can be used again,
* reusing the already allocated buffer space.
*
* @see java.io.ByteArrayInputStream#count
*/
@Override
public synchronized void reset() {
count = 0;
pos=0;
}
/**
* Sets the current stream position to the desired location. The
* next read will occur at this location. The bit offset is set
* to 0.
*
* <p> An <code>IndexOutOfBoundsException</code> will be thrown if
* <code>pos</code> is smaller than the flushed position (as
* returned by <code>getflushedPosition</code>).
*
* <p> It is legal to seek past the end of the file; an
* <code>EOFException</code> will be thrown only if a read is
* performed.
*
* @param pos a <code>long</code> containing the desired file
* pointer position.
*
* @exception IndexOutOfBoundsException if <code>pos</code> is smaller
* than the flushed position.
* @exception IOException if any other I/O error occurs.
*/
public void seek(long pos) throws IOException {
this.pos = (int)pos;
}
/**
* Returns the current byte position of the stream. The next write
* will take place starting at this offset.
*
* @return a long containing the position of the stream.
*
* @exception IOException if an I/O error occurs.
*/
public long getStreamPosition() throws IOException {
return pos;
}
/** Writes the contents of the byte array into the specified output
* stream.
* @param out
*/
public void toOutputStream(OutputStream out) throws IOException {
out.write(buf, 0, count);
}
/** Returns the underlying byte buffer. */
public byte[] getBuffer() {
return buf;
}
}

View File

@@ -0,0 +1,151 @@
/*
* @(#)SubImageOutputStream.java 1.0 2011-07-20
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.io;
import java.io.IOException;
import java.nio.ByteOrder;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.ImageOutputStreamImpl;
/**
* {@code SubImageOutputStream}.
*
* @author Werner Randelshofer
* @version 1.0 2011-07-20 Created.
*/
public class SubImageOutputStream extends ImageOutputStreamImpl {
private ImageOutputStream out;
private long offset;
private long length;
/** Whether flush and close request shall be forwarded to underlying stream.*/
private boolean forwardFlushAndClose;
public SubImageOutputStream(ImageOutputStream out, ByteOrder bo,boolean forwardFlushAndClose) throws IOException {
this(out, out.getStreamPosition(),bo,forwardFlushAndClose);
}
public SubImageOutputStream(ImageOutputStream out, long offset, ByteOrder bo,boolean forwardFlushAndClose) throws IOException {
this.out = out;
this.offset = offset;
this.forwardFlushAndClose=forwardFlushAndClose;
setByteOrder(bo);
out.seek(offset);
}
private long available() throws IOException {
checkClosed();
long pos = out.getStreamPosition();
if (pos < offset) {
out.seek(offset);
pos = offset;
}
return offset + out.length() - pos;
}
@Override
public int read() throws IOException {
if (available() <= 0) {
return -1;
} else {
return out.read();
}
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
long av = available();
if (av <= 0) {
return -1;
} else {
int result = out.read(b, off, (int) Math.min(len, av));
return result;
}
}
@Override
public long getStreamPosition() throws IOException {
return out.getStreamPosition() - offset;
}
@Override
public void seek(long pos) throws IOException {
out.seek(pos + offset);
length=Math.max(pos-offset+1,length);
}
@Override
public void flush() throws IOException {
if (forwardFlushAndClose) {
out.flush();
}
}
@Override
public void close() throws IOException {
if (forwardFlushAndClose) {
super.close();
}
}
@Override
public long getFlushedPosition() {
return out.getFlushedPosition() - offset;
}
/**
* Default implementation returns false. Subclasses should
* override this if they cache data.
*/
@Override
public boolean isCached() {
return out.isCached();
}
/**
* Default implementation returns false. Subclasses should
* override this if they cache data in main memory.
*/
@Override
public boolean isCachedMemory() {
return out.isCachedMemory();
}
@Override
public boolean isCachedFile() {
return out.isCachedFile();
}
@Override
public long length() {
return length;
}
@Override
public void write(int b) throws IOException {
out.write(b);
length = Math.max(out.getStreamPosition()-offset,length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
out.write(b,off,len);
length = Math.max(out.getStreamPosition()-offset,length);
}
public void dispose() throws IOException {
if (forwardFlushAndClose) {
checkClosed();
}
out=null;
}
}

View File

@@ -0,0 +1,155 @@
/*
* @(#)JPGCodec.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.jpeg;
import org.monte.media.io.ByteArrayImageInputStream;
import javax.imageio.ImageReader;
import org.monte.media.Format;
import org.monte.media.AbstractVideoCodec;
import org.monte.media.Buffer;
import org.monte.media.io.ByteArrayImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import static org.monte.media.VideoFormatKeys.*;
import static org.monte.media.BufferFlag.*;
/**
* {@code JPEGCodec} encodes a BufferedImage as a byte[] array.
* <p>
* Supported input formats:
* <ul>
* {@code VideoFormat} with {@code BufferedImage.class}, any width, any height,
* any depth.
* </ul>
* Supported output formats:
* <ul>
* {@code VideoFormat} with {@code byte[].class}, same width and height as input
* format, depth=24.
* </ul>
*
* @author Werner Randelshofer
* @version $Id: JPEGCodec.java 299 2013-01-03 07:40:18Z werner $
*/
public class JPEGCodec extends AbstractVideoCodec {
public JPEGCodec() {
super(new Format[]{
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA,
EncodingKey, ENCODING_BUFFERED_IMAGE), //
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,
EncodingKey, ENCODING_QUICKTIME_JPEG,//
CompressorNameKey, COMPRESSOR_NAME_QUICKTIME_JPEG, //
DataClassKey, byte[].class, DepthKey, 24), //
//
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_MJPG, DataClassKey, byte[].class, DepthKey, 24), //
},
new Format[]{
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA,
EncodingKey, ENCODING_BUFFERED_IMAGE), //
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,//
EncodingKey, ENCODING_QUICKTIME_JPEG,//
CompressorNameKey, COMPRESSOR_NAME_QUICKTIME_JPEG, //
DataClassKey, byte[].class, DepthKey, 24), //
//
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_MJPG, DataClassKey, byte[].class, DepthKey, 24), //
}//
);
name = "JPEG Codec";
}
@Override
public int process(Buffer in, Buffer out) {
if (outputFormat.get(EncodingKey).equals(ENCODING_BUFFERED_IMAGE)) {
return decode(in, out);
} else {
return encode(in, out);
}
}
public int encode(Buffer in, Buffer out) {
out.setMetaTo(in);
out.format = outputFormat;
if (in.isFlag(DISCARD)) {
return CODEC_OK;
}
BufferedImage image = getBufferedImage(in);
if (image == null) {
out.setFlag(DISCARD);
return CODEC_FAILED;
}
ByteArrayImageOutputStream tmp;
if (out.data instanceof byte[]) {
tmp = new ByteArrayImageOutputStream((byte[]) out.data);
} else {
tmp = new ByteArrayImageOutputStream();
}
try {
ImageWriter iw = ImageIO.getImageWritersByMIMEType("image/jpeg").next();
ImageWriteParam iwParam = iw.getDefaultWriteParam();
iwParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
float quality = outputFormat.get(QualityKey, 1f);
iwParam.setCompressionQuality(quality);
iw.setOutput(tmp);
IIOImage img = new IIOImage(image, null, null);
iw.write(null, img, iwParam);
iw.dispose();
out.sampleCount = 1;
out.setFlag(KEYFRAME);
out.data = tmp.getBuffer();
out.offset = 0;
out.length = (int) tmp.getStreamPosition();
return CODEC_OK;
} catch (IOException ex) {
ex.printStackTrace();
out.setFlag(DISCARD);
return CODEC_FAILED;
}
}
public int decode(Buffer in, Buffer out) {
out.setMetaTo(in);
out.format = outputFormat;
if (in.isFlag(DISCARD)) {
return CODEC_OK;
}
byte[] data = (byte[]) in.data;
if (data == null) {
out.setFlag(DISCARD);
return CODEC_FAILED;
}
ByteArrayImageInputStream tmp = new ByteArrayImageInputStream(data);
try {
// ImageReader ir = (ImageReader) ImageIO.getImageReadersByMIMEType("image/jpeg").next();
ImageReader ir = new MJPGImageReader(new MJPGImageReaderSpi());
ir.setInput(tmp);
out.data = ir.read(0);
ir.dispose();
out.sampleCount = 1;
out.offset = 0;
out.length = (int) tmp.getStreamPosition();
return CODEC_OK;
} catch (IOException ex) {
ex.printStackTrace();
out.setFlag(DISCARD);
return CODEC_FAILED;
}
}
}

View File

@@ -0,0 +1,119 @@
/*
* @(#)MJPGImageReader.java
*
* Copyright (c) 2010-2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.jpeg;
import org.monte.media.avi.AVIBMPDIB;
import com.sun.imageio.plugins.jpeg.JPEGImageReader;
import java.awt.image.BufferedImage;
import java.awt.image.DirectColorModel;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.LinkedList;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
/**
* Reads an image in the Motion JPEG (MJPG) format.
* <p>.
* This class can read Motion JPEG files with omitted Huffmann table.
* <p>
* For more information see:
* Microsoft Windows Bitmap Format.
* Multimedia Technical Note: JPEG DIB Format.
* (c) 1993 Microsoft Corporation. All rights reserved.
* <a href="http://www.fileformat.info/format/bmp/spec/b7c72ebab8064da48ae5ed0c053c67a4/BMPDIB.TXT">BMPDIB.txt</a>
*
* @author Werner Randelshofer
* @version $Id: MJPGImageReader.java 299 2013-01-03 07:40:18Z werner $
*/
public class MJPGImageReader extends ImageReader {
private static DirectColorModel RGB = new DirectColorModel(24, 0xff0000, 0xff00, 0xff, 0x0);
/** When we read the header, we read the whole image. */
private BufferedImage image;
public MJPGImageReader(ImageReaderSpi originatingProvider) {
super(originatingProvider);
}
@Override
public int getNumImages(boolean allowSearch) throws IOException {
return 1;
}
@Override
public int getWidth(int imageIndex) throws IOException {
readHeader();
return image.getWidth();
}
@Override
public int getHeight(int imageIndex) throws IOException {
readHeader();
return image.getHeight();
}
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
readHeader();
LinkedList<ImageTypeSpecifier> l = new LinkedList<ImageTypeSpecifier>();
l.add(new ImageTypeSpecifier(RGB, RGB.createCompatibleSampleModel(image.getWidth(), image.getHeight())));
return l.iterator();
}
@Override
public IIOMetadata getStreamMetadata() throws IOException {
return null;
}
@Override
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
return null;
}
@Override
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
if (imageIndex > 0) {
throw new IndexOutOfBoundsException();
}
readHeader();
return image;
}
/** Reads the image header.
* Does nothing if the header has already been loaded.
*/
private void readHeader() throws IOException {
if (image == null) {
ImageReader r = new JPEGImageReader(getOriginatingProvider());
Object in = getInput();
/*if (in instanceof Buffer) {
Buffer buffer = (Buffer) in;
in=buffer.getData();
}*/
if (in instanceof byte[]) {
r.setInput(new MemoryCacheImageInputStream(AVIBMPDIB.prependDHTSeg((byte[]) in)));
} else if (in instanceof ImageInputStream) {
r.setInput(AVIBMPDIB.prependDHTSeg((ImageInputStream) in));
} else {
r.setInput(AVIBMPDIB.prependDHTSeg((InputStream) in));
}
image = r.read(0);
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* @(#)MJPGImageReaderSpi.java
*
* Copyright (c) 2010-2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.jpeg;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
/**
* ImageIO service provider interface for images in the Motion JPEG (MJPG)
* format.
* <p>
* The reader described by this class can read Motion JPEG files with omitted
* Huffmann table.
* <p>
* For more information see:
* Microsoft Windows Bitmap Format.
* Multimedia Technical Note: JPEG DIB Format.
* (c) 1993 Microsoft Corporation. All rights reserved.
* <a href="http://www.fileformat.info/format/bmp/spec/b7c72ebab8064da48ae5ed0c053c67a4/BMPDIB.TXT">BMPDIB.txt</a>
*
* @author Werner Randelshofer
* @version $Id: MJPGImageReaderSpi.java 299 2013-01-03 07:40:18Z werner $
*/
public class MJPGImageReaderSpi extends ImageReaderSpi {
public MJPGImageReaderSpi() {
super("Werner Randelshofer",//vendor name
"1.0",//version
new String[]{"MJPG"},//names
new String[]{"mjpg"},//suffixes,
new String[]{"image/mjpg"},// MIMETypes,
"org.monte.media.jmf.renderer.video.MJPGImageReader",// readerClassName,
new Class[]{ImageInputStream.class,InputStream.class,byte[].class/*,javax.media.Buffer.class*/},// inputTypes,
null,// writerSpiNames,
false,// supportsStandardStreamMetadataFormat,
null,// nativeStreamMetadataFormatName,
null,// nativeStreamMetadataFormatClassName,
null,// extraStreamMetadataFormatNames,
null,// extraStreamMetadataFormatClassNames,
false,// supportsStandardImageMetadataFormat,
null,// nativeImageMetadataFormatName,
null,// nativeImageMetadataFormatClassName,
null,// extraImageMetadataFormatNames,
null// extraImageMetadataFormatClassNames
);
}
@Override
public boolean canDecodeInput(Object source) throws IOException {
if (source instanceof ImageInputStream) {
ImageInputStream in = (ImageInputStream) source;
in.mark();
// Check if file starts with a JFIF SOI magic (0xffd8=-40)
if (in.readShort() != -40) {
in.reset();
return false;
}
in.reset();
return true;
}
return false;
}
@Override
public ImageReader createReaderInstance(Object extension) throws IOException {
return new MJPGImageReader(this);
}
@Override
public String getDescription(Locale locale) {
return "MJPG Image Reader";
}
}

View File

@@ -0,0 +1,250 @@
/*
* @(#)IntMath.java
*
* Copyright (c) 2002-2012 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.math;
import java.math.BigInteger;
/**
* Utility class for integer arithmetic.
*
* @author Werner Randelshofer
* @version $Id: IntMath.java 299 2013-01-03 07:40:18Z werner $
*/
public class IntMath {
/** Creates a new instance of IntMath */
public IntMath() {
}
/**
* Returns an int whose value is the greatest common divisor of
* <tt>abs(a)</tt> and <tt>abs(b)</tt>. Returns 0 if
* <tt>a==0 &amp;&amp; b==0</tt>.
*
* @param a value with with the GCD is to be computed.
* @param b value with with the GCD is to be computed.
* @return <tt>GCD(a, b)</tt>
*/
public static int gcd(int a, int b) {
// Quelle:
// Herrmann, D. (1992). Algorithmen Arbeitsbuch.
// Bonn, München Paris: Addison Wesley.
// ggt6, Seite 63
a = Math.abs(a);
b = Math.abs(b);
while (a > 0 && b > 0) {
a = a % b;
if (a > 0) b = b % a;
}
return a + b;
}
/**
* Returns a long whose value is the greatest common divisor of
* <tt>abs(a)</tt> and <tt>abs(b)</tt>. Returns 0 if
* <tt>a==0 &amp;&amp; b==0</tt>.
*
* @param a value with with the GCD is to be computed.
* @param b value with with the GCD is to be computed.
* @return <tt>GCD(a, b)</tt>
*/
public static long gcd(long a, long b) {
// Quelle:
// Herrmann, D. (1992). Algorithmen Arbeitsbuch.
// Bonn, München Paris: Addison Wesley.
// ggt6, Seite 63
a = Math.abs(a);
b = Math.abs(b);
while (a > 0 && b > 0) {
a = a % b;
if (a > 0) b = b % a;
}
return a + b;
}
/**
* Returns a long whose value is the greatest common divisor of
* <tt>abs(a)</tt> and <tt>abs(b)</tt>. Returns 0 if
* <tt>a==0 &amp;&amp; b==0</tt>.
*
* @param a value with with the GCD is to be computed.
* @param b value with with the GCD is to be computed.
* @return <tt>GCD(a, b)</tt>
*/
public static BigInteger gcd(BigInteger a, BigInteger b) {
// Quelle:
// Herrmann, D. (1992). Algorithmen Arbeitsbuch.
// Bonn, München Paris: Addison Wesley.
// ggt6, Seite 63
a = a.abs();
b = b.abs();
while (a.compareTo(BigInteger.ZERO) > 0 && b.compareTo(BigInteger.ZERO) > 0) {
a = a.mod(b);
if (a.compareTo(BigInteger.ZERO) > 0) b = b.mod(a);
}
return a.add(b);
}
/**
* Returns an int whose value is the smallest common multiple of
* <tt>abs(a)</tt> and <tt>abs(b)</tt>. Returns 0 if
* <tt>a==0 || b==0</tt>.
*
* @param a value with with the SCM is to be computed.
* @param b value with with the SCM is to be computed.
* @return <tt>SCM(a, b)</tt>
*/
public static int scm(int a, int b) {
// Quelle:
// Herrmann, D. (1992). Algorithmen Arbeitsbuch.
// Bonn, München Paris: Addison Wesley.
// gill, Seite 141
if (a == 0 || b == 0) return 0;
a = Math.abs(a);
b = Math.abs(b);
int u = a;
int v = b;
while (a != b) {
if (a < b) {
b -= a;
v += u;
} else {
a -= b;
u += v;
}
}
//return a; // gcd
return (u + v) / 2; // scm
}
/**
* Returns an int whose value is the smallest common multiple of
* <tt>abs(a)</tt> and <tt>abs(b)</tt>. Returns 0 if
* <tt>a==0 || b==0</tt>.
*
* @param a value with with the SCM is to be computed.
* @param b value with with the SCM is to be computed.
* @return <tt>SCM(a, b)</tt>
*/
public static long scm(long a, long b) {
// Quelle:
// Herrmann, D. (1992). Algorithmen Arbeitsbuch.
// Bonn, München Paris: Addison Wesley.
// gill, Seite 141
if (a == 0 || b == 0) return 0;
a = Math.abs(a);
b = Math.abs(b);
if (b==1)return a;
if (a==1)return b;
long u = a;
long v = b;
// FIXME - Handle overflow
while (a != b) {
if (a < b) {
b -= a;
v += u;
} else {
a -= b;
u += v;
}
}
//return a; // gcd
return (u + v) / 2; // scm
}
/**
* Returns an int whose value is the smallest common multiple of
* <tt>abs(a)</tt> and <tt>abs(b)</tt>. Returns 0 if
* <tt>a==0 || b==0</tt>.
*
* @param a value with with the SCM is to be computed.
* @param b value with with the SCM is to be computed.
* @return <tt>SCM(a, b)</tt>
*/
public static BigInteger scm(BigInteger a, BigInteger b) {
// Quelle:
// Herrmann, D. (1992). Algorithmen Arbeitsbuch.
// Bonn, München Paris: Addison Wesley.
// gill, Seite 141
if (a.compareTo(BigInteger.ZERO) == 0 || b.compareTo(BigInteger.ZERO) == 0) {
return BigInteger.ZERO;
}
a = a.abs();
b = b.abs();
if (b.compareTo(BigInteger.ONE)==0)return a;
if (a.compareTo(BigInteger.ONE)==0)return b;
BigInteger u = a;
BigInteger v = b;
// FIXME - Handle overflow
while (a.compareTo(b) != 0) {
if (a .compareTo( b)<0) {
b = b.subtract(a);
v = v.add(u);
} else {
a = a.subtract(b);
u = u.add(v);
}
}
//return a; // gcd
return (u.add(v)).divide(BigInteger.valueOf(2)); // scm
}
/**
* Reverses all 32 bits of the provided integer value.
*/
public static int reverseBits(int a) {
return reverseBits(a, 32);
}
/**
* Reverses specified number of bits of the provided integer value.
* @param a The number.
* @param numBits The number of bits (must be between 1 and 32).
*/
public static int reverseBits(int a, int numBits) {
int b = 0;
for (int i=0; i < numBits; i++) {
b <<= 1;
b |= (a & 1);
a >>>= 1;
}
return b;
}
public static void main(String[] args) {
for (int i=0; i < 8; i++) {
int a = 1<<i;
int b = reverseBits(a, 3);
System.out.println(a+" - "+b);
}
}
}

View File

@@ -0,0 +1,492 @@
/*
* @(#)Rational.java
*
* Copyright (c) 2009-2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.math;
import static java.lang.Math.*;
import java.math.BigInteger;
import static org.monte.media.math.IntMath.*;
/**
* Represents a TIFF RATIONAL number. <p> Two LONGs 32-bit (4-byte) unsigned
* integer: the first represents the numerator of a fraction; the second, the
* denominator. </p> <p> Invariants: </p> <ul> <li>denominator>=0, the
* denominator is always a positive integer</li> <li>0/1 is the unique
* representation of 0.</li> <li>1/0,-1/0 are the unique representations of
* infinity.</li> </ul>
*
* @author Werner Randelshofer
* @version $Id: Rational.java 299 2013-01-03 07:40:18Z werner $
*/
public class Rational extends Number {
public static final Rational ONE = new Rational(1, 1,false);
public static final Rational ZERO = new Rational(0, 1,false);
public static final long serialVersionUID = 1L;
private final long num;
private final long den;
public Rational(long numerator) {
this(numerator, 1);
}
public Rational(long numerator, long denominator) {
this(numerator, denominator, true);
}
private Rational(long numerator, long denominator, boolean reduceFraction) {
if (numerator == 0) {
// Invariant: 0/1 is unique representation of 0
denominator = 1;
}
if (denominator == 0) {
// Invariant: 1/0, -1/0 are unique representations of infinity
numerator = (numerator > 0) ? 1 : -1;
} else if (denominator < 0) {
// Invariant: denominator is always positive
denominator = -denominator;
numerator = -numerator;
}
if (reduceFraction) {
long g = gcd(numerator, denominator);
num = numerator / g;
den = denominator / g;
} else {
num = numerator;
den = denominator;
}
}
private Rational(BigInteger numerator, BigInteger denominator, boolean reduceFraction) {
if (numerator.equals(BigInteger.ZERO)) {
// Invariant: 0/1 is unique representation of 0
denominator = BigInteger.ONE;
}
if (denominator.equals(BigInteger.ZERO)) {
// Invariant: 1/0, -1/0 are unique representations of infinity
numerator = (numerator.compareTo(BigInteger.ZERO) > 0) ? BigInteger.ONE : BigInteger.ONE.negate();
} else if (denominator.compareTo(BigInteger.ZERO) < 0) {
// Invariant: denominator is always positive
denominator = denominator.negate();
numerator = numerator.negate();
}
BigInteger numB, denB;
if (reduceFraction) {
BigInteger g = gcd(numerator, denominator);
numB = numerator.divide(g);
denB = denominator.divide(g);
} else {
numB = numerator;
denB = denominator;
}
int bitLength = Math.max(numB.bitLength(), denB.bitLength());
if (bitLength > 63) {
numB = numB.shiftRight(bitLength - 63);
denB = denB.shiftRight(bitLength - 63);
if (numB.equals(BigInteger.ZERO)) {
// Invariant: 0/1 is unique representation of 0
denB = BigInteger.ONE;
}
if (denB.equals(BigInteger.ZERO)) {
// Invariant: 1/0, -1/0 are unique representations of infinity
numB = (numB.compareTo(BigInteger.ZERO) > 0) ? BigInteger.ONE : BigInteger.ONE.negate();
}
}
num = numB.longValue();
den = denB.longValue();
}
public Rational(Rational r) {
this(r.num, r.den);
}
public long getNumerator() {
return num;
}
public long getDenominator() {
return den;
}
public Rational add(Rational that) {
return add(that, true);
}
private Rational add(Rational that, boolean reduceFraction) {
if (this.den == that.den) {
// => same denominator: add numerators
return new Rational(this.num + that.num, this.den, reduceFraction);
}
// FIXME - handle overflow
long s = scm(this.den, that.den);
Rational result = new Rational(
this.num * (s / this.den) + that.num * (s / that.den),
s, reduceFraction);
return result;
}
/**
* Warning. Rational is supposed to be immutable. *
*
* private Rational addAssign(Rational that) { if (this.den == that.den) {
* // => same denominator: add numerators this.num += that.num; return this;
* }
*
* // FIXME - handle overflow long s = scm(this.den, that.den); this.num =
* this.num * (s / this.den) + that.num * (s / that.den); this.den = s;
*
*
* return reduceAssign(); }
*/
public Rational subtract(Rational that) {
return add(that.negate());
}
public Rational negate() {
return valueOf(-num, den);
}
public Rational inverse() {
return valueOf(den, num, false);
}
/**
* Returns the closest rational with the specified denominator which is
* smaller or equal than this number.
*/
public Rational floor(long d) {
if (d == den) {
return valueOf(num, den);
}
long s = scm(this.den, d);
if (s == d) {
return valueOf(num * s / den, d);
} else if (s == den) {
return valueOf(num * d / den, d);
} else {
return valueOf(num * d / den, d);
}
}
/**
* Returns the closest rational with the specified denominator which is
* greater or equal than this number.
*/
public Rational ceil(long d) {
if (d == den) {
return valueOf(num, den);
}
long s = scm(this.den, d);
if (s == d) {
return valueOf((num * s + den - 1) / den, d);
} else if (s == den) {
return valueOf((num * d + den - 1) / den, d);
} else {
return valueOf((num * d + den - 1) / den, d);
}
}
public Rational multiply(Rational that) {
if (abs(this.num) < Integer.MAX_VALUE
&& abs(this.den) < Integer.MAX_VALUE
&& abs(that.num) < Integer.MAX_VALUE
&& abs(that.den) < Integer.MAX_VALUE) {
return valueOf(this.num * that.num,
this.den * that.den);
} else {
return new Rational(
BigInteger.valueOf(this.num).multiply(BigInteger.valueOf(that.num)),
BigInteger.valueOf(this.den).multiply(BigInteger.valueOf(that.den)),
true);
}
}
public Rational multiply(long integer) {
if (integer==0) {
return ZERO;
} else if (this.den % integer == 0) {
return valueOf(
this.num,
this.den / integer);
} else if (abs(this.num) < Integer.MAX_VALUE
&& abs(integer) < Integer.MAX_VALUE) {
return valueOf(
this.num * integer,
this.den);
} else {
return new Rational(
BigInteger.valueOf(this.num).multiply(BigInteger.valueOf(integer)),
BigInteger.valueOf(this.den), true);
}
}
public Rational divide(Rational that) {
if (abs(this.num) < Integer.MAX_VALUE
&& abs(this.den) < Integer.MAX_VALUE
&& abs(that.num) < Integer.MAX_VALUE
&& abs(that.den) < Integer.MAX_VALUE) {
return valueOf(this.num * that.den,
this.den * that.num);
} else {
return valueOf(
BigInteger.valueOf(this.num).multiply(BigInteger.valueOf(that.den)),
BigInteger.valueOf(this.den).multiply(BigInteger.valueOf(that.num)),
true);
}
}
@Override
public String toString() {
//long gcd = IntMath.gcd(num, den);
if (num == 0) {
return "0";
} else if (den == 1) {
return Long.toString(num);
} else {
return num + "/" + den;
/*
} else {
return Float.toString((float) num / den);
*/
}
}
public String toDescriptiveString() {
long gcd = IntMath.gcd(num, den);
if (gcd == 0 || num == 0) {
return num + "/" + den + " = " + 0;
} else if (gcd == den) {
return num + "/" + den + " = " + Long.toString(num / den);
} else {
return num + "/" + den + "" + ((float) num / den);
}
}
@Override
public int intValue() {
return (int) (num / den);
}
@Override
public long longValue() {
return num / den;
}
@Override
public float floatValue() {
return (float) num / (float) den;
}
@Override
public double doubleValue() {
return (double) num / (double) den;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Rational that = (Rational) obj;
return compareTo(that) == 0;
}
/**
* return { -1, 0, +1 } if a < b, a = b, or a > b.
*/
public int compareTo(Rational that) {
// The following code avoids BigInteger allocation if the denominators
// are equal
if (this.den == that.den) {
if (this.num < that.num) {
return -1;
} else if (this.num > that.num) {
return 1;
} else {
return 0;
}
}
// Work with longs if overflow can not occur
if (abs(this.num) < Integer.MAX_VALUE
&& abs(this.den) < Integer.MAX_VALUE
&& abs(that.num) < Integer.MAX_VALUE
&& abs(that.den) < Integer.MAX_VALUE) {
long lhs = this.num * that.den;
long rhs = this.den * that.num;
if (lhs < rhs) {
return -1;
} else if (lhs > rhs) {
return 1;
} else {
return 0;
}
}
// Use big integers to avoid overflows
BigInteger lhs;
BigInteger rhs;
lhs = BigInteger.valueOf(this.num).multiply(BigInteger.valueOf(that.den));
rhs = BigInteger.valueOf(this.den).multiply(BigInteger.valueOf(that.num));
return lhs.compareTo(rhs);
}
@Override
public int hashCode() {
return (int) ((num ^ (num >>> 32))
^ (den ^ (den >>> 32)));
}
public static Rational max(Rational a, Rational b) {
return (a.compareTo(b) >= 0) ? a : b;
}
public static Rational min(Rational a, Rational b) {
return (a.compareTo(b) <= 0) ? a : b;
}
public boolean isZero() {
return num == 0;
}
public boolean isLessOrEqualZero() {
return num <= 0;
}
public static Rational valueOf(double d) {
if (d == 0) {
return valueOf(0, 1);
}
if (abs(d) > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Value " + d + " is too big.");
}
if (Double.isInfinite(d)) {
return valueOf((long) signum(d), 0);
}
if (Double.isNaN(d)) {
return valueOf(0, 1); // no way to express a NaN :-(
}
return toRational(d, Integer.MAX_VALUE, 100);
}
public static Rational valueOf(long num, long den) {
return valueOf(num, den, true);
}
private static Rational valueOf(long num, long den, boolean reduceFraction) {
if (num == den) {
return ONE;
}
if (num == 0) {
return ZERO;
}
return new Rational(num, den, reduceFraction);
}
public static Rational valueOf(BigInteger num, BigInteger den) {
return valueOf(num, den, true);
}
private static Rational valueOf(BigInteger num, BigInteger den, boolean reduceFraction) {
if (num.equals(den)) {
return ONE;
}
if (num.equals(BigInteger.ZERO)) {
return ZERO;
}
return new Rational(num, den, reduceFraction);
}
/**
* Iteratively computes rational from double. <p>Reference:<br> <a
* href="http://www2.fz-juelich.de/video/cpp/html/exercises/exercise/Rational_cpp.html">
* http://www2.fz-juelich.de/video/cpp/html/exercises/exercise/Rational_cpp.html</a>
* </p>
*/
private static Rational toRational(double x, double limit, int iterations) {
double intpart = Math.floor(x);
double fractpart = x - intpart;
double d = 1.0 / fractpart;
long left = (long) intpart;
if (d > limit || iterations == 0) {
return valueOf(left, 1, false);
} else {
return valueOf(left, 1, false).add(toRational(d, limit * 0.1, iterations - 1).inverse(), false);
}
}
public Rational round(long d) {
if (d == den) {
return valueOf(num, den);
}
Rational fl = floor(d);
Rational diffFl = subtract(fl);
if (diffFl.isZero()) {
return fl;
}
Rational cl = ceil(d);
Rational diffCl = subtract(cl);
if (diffCl.isZero()) {
return cl;
}
if (diffFl.isNegative()) {
diffFl = diffFl.negate();
}
if (diffCl.isNegative()) {
diffCl = diffCl.negate();
}
return diffFl.compareTo(diffCl) <= 0 ? fl : cl;
}
private boolean isNegative() {
return num < 0;
}
/**
* Parses a string.
*
* A rational can be represented in the following ways: <li>As a long
* number</li> <li>As a double number</li> <li>As an integer/integer
* rational number</li>
*
* @throws NumberFormatException if str can not be parsed.
*/
public static Rational valueOf(String str) {
int p = str.indexOf('/');
if (p != -1) {
return valueOf(Long.valueOf(str.substring(0, p)), Long.valueOf(str.substring(p + 1)));
}
try {
return valueOf(Long.valueOf(str));
} catch (NumberFormatException e) {
return valueOf(Double.valueOf(str));
}
}
}

View File

@@ -0,0 +1,118 @@
/*
* @(#)PNGCodec.java
*
* Copyright (c) 2011-2012 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.png;
import org.monte.media.Format;
import org.monte.media.AbstractVideoCodec;
import org.monte.media.Buffer;
import org.monte.media.io.ByteArrayImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import static org.monte.media.VideoFormatKeys.*;
import static org.monte.media.BufferFlag.*;
/**
* {@code PNGCodec} encodes a BufferedImage as a byte[] array..
* <p>
* Supported input formats:
* <ul>
* {@code VideoFormat} with {@code BufferedImage.class}, any width, any height,
* any depth.
* </ul>
* Supported output formats:
* <ul>
* {@code VideoFormat} with {@code byte[].class}, same width and height as input
* format, depth=24.
* </ul>
*
* @author Werner Randelshofer
* @version $Id: PNGCodec.java 299 2013-01-03 07:40:18Z werner $
*/
public class PNGCodec extends AbstractVideoCodec {
public PNGCodec() {
super(new Format[]{
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA,
EncodingKey, ENCODING_BUFFERED_IMAGE), //
},
new Format[]{
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,
DepthKey, 24,
EncodingKey, ENCODING_QUICKTIME_PNG, DataClassKey, byte[].class), //
//
new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
DepthKey, 24,
EncodingKey, ENCODING_AVI_PNG, DataClassKey, byte[].class), //
});
}
@Override
public Format setOutputFormat(Format f) {
String mimeType = f.get(MimeTypeKey, MIME_QUICKTIME);
if (mimeType != null && !mimeType.equals(MIME_AVI)) {
return super.setOutputFormat(
f.prepend(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,
EncodingKey, ENCODING_QUICKTIME_PNG, DataClassKey,
byte[].class, DepthKey, 24));
} else {
return super.setOutputFormat(
f.prepend(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
EncodingKey, ENCODING_AVI_PNG, DataClassKey,
byte[].class, DepthKey, 24));
}
}
@Override
public int process(Buffer in, Buffer out) {
out.setMetaTo(in);
out.format = outputFormat;
if (in.isFlag(DISCARD)) {
return CODEC_OK;
}
BufferedImage image = getBufferedImage(in);
if (image == null) {
out.setFlag(DISCARD);
return CODEC_FAILED;
}
ByteArrayImageOutputStream tmp;
if (out.data instanceof byte[]) {
tmp = new ByteArrayImageOutputStream((byte[]) out.data);
} else {
tmp = new ByteArrayImageOutputStream();
}
try {
ImageWriter iw = ImageIO.getImageWritersByMIMEType("image/png").next();
ImageWriteParam iwParam = iw.getDefaultWriteParam();
iw.setOutput(tmp);
IIOImage img = new IIOImage(image, null, null);
iw.write(null, img, iwParam);
iw.dispose();
out.setFlag(KEYFRAME);
out.header = null;
out.data = tmp.getBuffer();
out.offset = 0;
out.length = (int) tmp.getStreamPosition();
return CODEC_OK;
} catch (IOException ex) {
ex.printStackTrace();
out.setFlag(DISCARD);
return CODEC_FAILED;
}
}
}

View File

@@ -0,0 +1,188 @@
/*
* @(#)RIFFChunk.java
*
* Copyright (c) 2005-2012 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.riff;
import java.util.*;
/**
* RIFF Chunks form the building blocks of a RIFF file.
*
* @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland
* @version $Id: RIFFChunk.java 299 2013-01-03 07:40:18Z werner $
*/
public class RIFFChunk {
private int id;
private int type;
private long size;
private long scan;
private byte[] data;
private Hashtable<RIFFChunk,RIFFChunk> propertyChunks;
private ArrayList<RIFFChunk> collectionChunks;
/**
* This is used to display parser messages, when the parser encounters and
* error while parsing the chunk.
*/
private String parserMessage;
public RIFFChunk(int type, int id) {
this.id = id;
this.type = type;
size = -1;
scan = -1;
}
public RIFFChunk(int type, int id, long size, long scan) {
this.id = id;
this.type = type;
this.size = size;
this.scan = scan;
}
public RIFFChunk(int type, int id, long size, long scan, RIFFChunk propGroup) {
this.id = id;
this.type = type;
this.size = size;
this.scan = scan;
if (propGroup != null) {
if (propGroup.propertyChunks != null) {
propertyChunks = new Hashtable<RIFFChunk,RIFFChunk>(propGroup.propertyChunks);
}
if (propGroup.collectionChunks != null) {
collectionChunks = new ArrayList<RIFFChunk>(propGroup.collectionChunks);
}
}
}
/**
* @return ID of chunk.
*/
public int getID() {
return id;
}
/**
* @return Type of chunk.
*/
public int getType() {
return type;
}
/**
* @return Size of chunk.
*/
public long getSize() {
return size;
}
/**
* @return Scan position of chunk within the file.
*/
public long getScan() {
return scan;
}
public void putPropertyChunk(RIFFChunk chunk) {
if (propertyChunks == null) {
propertyChunks = new Hashtable<RIFFChunk,RIFFChunk> ();
}
propertyChunks.put(chunk,chunk);
}
public RIFFChunk getPropertyChunk(int id) {
if (propertyChunks == null) {
return null;
}
RIFFChunk chunk = new RIFFChunk(type, id);
return propertyChunks.get(chunk);
}
public Enumeration propertyChunks() {
if (propertyChunks == null) {
propertyChunks = new Hashtable<RIFFChunk,RIFFChunk> ();
}
return propertyChunks.keys();
}
public void addCollectionChunk(RIFFChunk chunk) {
if (collectionChunks == null) {
collectionChunks = new ArrayList<RIFFChunk>();
}
collectionChunks.add(chunk);
}
public RIFFChunk[] getCollectionChunks(int id) {
if (collectionChunks == null) {
return new RIFFChunk[0];
}
Iterator<RIFFChunk> enm = collectionChunks.iterator();
int i = 0;
while ( enm.hasNext() ) {
RIFFChunk chunk = enm.next();
if (chunk.id==id) {
i++;
}
}
RIFFChunk[] array = new RIFFChunk[i];
i = 0;
enm = collectionChunks.iterator();
while ( enm.hasNext() ) {
RIFFChunk chunk = enm.next();
if (chunk.id==id) {
array[i++] = chunk;
}
}
return array;
}
public Iterator collectionChunks() {
if (collectionChunks == null) {
collectionChunks = new ArrayList<RIFFChunk>();
}
return collectionChunks.iterator();
}
/**
* Sets the data.
* Note: The array will not be cloned.
*/
public void setData(byte[] data) {
this.data = data;
}
/**
* Gets the data.
* Note: The array will not be cloned.
*/
public byte[] getData() {
return data;
}
@Override
public boolean equals(Object another) {
if (another instanceof RIFFChunk) {
RIFFChunk that = (RIFFChunk) another;
return (that.id==this.id) && (that.type==this.type);
}
return false;
}
@Override
public int hashCode() {
return id;
}
public void setParserMessage(String newValue) {
this.parserMessage = newValue;
}
public String getParserMessage() {
return this.parserMessage;
}
@Override
public String toString() {
return super.toString()+"{"+RIFFParser.idToString(getType())+","+RIFFParser.idToString(getID())+"}";
}
}

View File

@@ -0,0 +1,841 @@
/*
* @(#)RIFFParser.java 1.5 2012-08-03
*
* Copyright (c) 2005-2012 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.riff;
import org.monte.media.AbortException;
import org.monte.media.ParseException;
import org.monte.media.io.ImageInputStreamAdapter;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.WeakHashMap;
import javax.imageio.stream.ImageInputStream;
/**
* Interprets Resource Interchange File Format (RIFF) streams.
*
* <p><b>Abstract</b>
* <p>
* <b>RIFF File Format</b>
* A RIFF file consists of a RIFF header followed by zero or more lists and chunks.
* <p>
* The RIFF header has the following form:
* <pre>
* 'RIFF' fileSize fileType (data)
* </pre>
* where 'RIFF' is the literal FOURCC code 'RIFF', fileSize is a 4-byte value
* giving the size of the data in the file, and fileType is a FOURCC that
* identifies the specific file type. The value of fileSize includes the size
* of the fileType FOURCC plus the size of the data that follows, but does not
* include the size of the 'RIFF' FOURCC or the size of fileSize. The file data
* consists of chunks and lists, in any order.
* <p>
* <b>FOURCCs</b><br>
* A FOURCC (four-character code) is a 32-bit unsigned integer created by
* concatenating four ASCII characters. For example, the FOURCC 'abcd' is
* represented on a Little-Endian system as 0x64636261. FOURCCs can contain
* space characters, so ' abc' is a valid FOURCC. The RIFF file format uses
* FOURCC codes to identify stream types, data chunks, index entries, and other
* information.
* <p>
* A chunk has the following form:
* <pre>
* ckID ckSize ckData
* </pre>
* where ckID is a FOURCC that identifies the data contained in the chunk,
* ckData is a 4-byte value giving the size of the data in ckData, and ckData is
* zero or more bytes of data. The data is always padded to nearest WORD boundary.
* ckSize gives the size of the valid data in the chunk; it does not include
* the padding, the size of ckID, or the size of ckSize.
* <p>
* A list has the following form:
* <pre>
* 'LIST' listSize listType listData
* </pre>
* where 'LIST' is the literal FOURCC code 'LIST', listSize is a 4-byte value
* giving the size of the list, listType is a FOURCC code, and listData consists
* of chunks or lists, in any order. The value of listSize includes the size of
* listType plus the size of listData; it does not include the 'LIST' FOURCC or
* the size of listSize.
* <p>
* For more information see:
* http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c/directx/htm/avirifffilereference.asp
* http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c/directx/htm/aboutriff.asp
* <p>
* <p><b>Grammar for RIFF streams used by this parser</b>
* <pre>
* RIFFFile ::= 'RIFF' FormGroup
* <br>
* GroupChunk ::= FormGroup | ListGroup
* FormGroup ::= size GroupType [ ChunkID LocalChunk [pad] | 'LIST ' ListGroup [pad] }
* ListGroup ::= size GroupType [ ChunkID LocalChunk [pad] | 'LIST ' ListGroup [pad] }
* <br>
* LocalChunk ::= DataChunk | CollectionChunk | PropertyChunk
* DataChunk ::= size [ struct ]
* CollectionChunk ::= size [ struct ]
* PropertyChunk ::= size [ struct ]
* <br>
* size ::= ULONG
* GroupType ::= FourCC
* ChunkID ::= FourCC
* pad ::= (BYTE)0
* struct ::= any C language struct built with primitive data types.
* </pre>
*
* <p><b>Examples</b>
*
* <p><b>Traversing the raw structure of a RIFF file</b>
* <p>To traverse the file structure you must first set up a RIFFVisitor object
* that does something useful at each call to the visit method. Then create an
* instance of a RIFFParser and invoke the #interpret method.
*
* <pre>
* class RIFFRawTraversal
* . {
* . static class Visitor
* . implements RIFFVisitor
* . {
* . ...implement the visitor interface here...
* . }
* .
* . public static void main(String[] args)
* . {
* . try {
* . Visitor visitor = new Visitor();
* . FileInputStream stream = new FileInputStream(args[0]);
* . RIFFParser p = new RIFFParser();
* . p.interpret(stream,visitor);
* . stream.close();
* . }
* . catch (IOException e) { System.out.println(e); }
* . catch (InterpreterException e) { System.out.println(e); }
* . catch (AbortedException e) { System.out.println(e); }
* . }
* . }
* </pre>
*
* <p><b>Traversing the RIFF file and interpreting its content.</b>
* <p>Since RIFF files are not completely self describing (there is no information
* that helps differentiate between data chunks, property chunks and collection
* chunks) a reader must set up the interpreter with some contextual information
* before starting the interpreter.
* <p>
* Once at least one chunk has been declared, the interpreter will only call the
* visitor for occurences of the declared group chunks and data chunks. The property
* chunks and the collection chunks can be obtained from the current group chunk
* by calling #getProperty or #getCollection.
* <br>Note: All information the visitor can obtain during interpretation is only
* valid during the actual #visit... call. Dont try to get information about properties
* or collections for chunks that the visitor is not visiting right now.
*
* <pre>
* class InterpretingAnILBMFile
* . {
* . static class Visitor
* . implements RIFFVisitor
* . {
* . ...
* . }
* .
* . public static void main(String[] args)
* . {
* . try {
* . Visitor visitor = new Visitor();
* . FileInputStream stream = new FileInputStream(args[0]);
* . RIFFParser p = new RIFFParser();
* . p.declareGroupChunk('FORM','ILBM');
* . p.declarePropertyChunk('ILBM','BMHD');
* . p.declarePropertyChunk('ILBM','CMAP');
* . p.declareCollectionChunk('ILBM','CRNG');
* . p.declareDataChunk('ILBM','BODY');
* . p.interpret(stream,visitor);
* . stream.close();
* . }
* . catch (IOException e) { System.out.println(e); }
* . catch (InterpreterException e) { System.out.println(e); }
* . catch (AbortedException e) { System.out.println(e); }
* . }
* . }
* </pre>
*
* @see RIFFVisitor
*
* @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland
* @version 1.5 2012-08-03 Adds offset property.
* <br>1.4 2011-08-26 Adds HashMap for stop chunks.
* <br>1.3 2011-01-23 Use HashMap instead of Hashtable.
* <br>1.2 2010-07-06 Use integer IDs for efficiency. Added support for
* stop chunks.
* <br>1.0 2005-01-16 Created.
*/
public class RIFFParser extends Object {
private final static boolean DEBUG =false;
/** ID for FormGroupExpression. */
public final static int RIFF_ID = stringToID("RIFF");
/** ID for ListGroupExpression. */
public final static int LIST_ID = stringToID("LIST");
/** ID for NULL chunks. */
public final static int NULL_ID = stringToID(" ");
/** ID for NULL chunks. */
public final static int NULL_NUL_ID = stringToID("\0\0\0\0");
/** ID for JUNK chunks. */
public final static int JUNK_ID = stringToID("JUNK");
/** The visitor traverses the parse tree. */
private RIFFVisitor visitor;
/** List of data chunks the visitor is interested in. */
private HashSet<RIFFChunk> dataChunks;
/** List of property chunks the visitor is interested in. */
private HashSet<RIFFChunk> propertyChunks;
/** List of collection chunks the visitor is interested in. */
private HashSet<RIFFChunk> collectionChunks;
/** List of stop chunks the visitor is interested in. */
private HashSet<Integer> stopChunkTypes;
/** List of group chunks the visitor is interested in. */
private HashSet<RIFFChunk> groupChunks;
/** Reference to the input stream. */
private RIFFPrimitivesInputStream in;
/** Reference to the image input stream. */
private ImageInputStream iin;
/** Whether we stop at all chunks. */
private boolean isStopChunks;
/** Stream offset. */
private long streamOffset;
/* ---- constructors ---- */
/**
* Constructs a new RIFF parser.
*/
public RIFFParser() {
}
public long getStreamOffset() {
return streamOffset;
}
public void setStreamOffset(long offset) {
this.streamOffset = offset;
}
/* ---- accessor methods ---- */
/* ---- action methods ---- */
/**
* Interprets the RIFFFile expression located at the
* current position of the indicated InputStream.
* Lets the visitor traverse the RIFF parse tree during
* interpretation.
*
* <p>Pre condition
* <li> Data-, property- and collection chunks must have been
* declared prior to this call.
* <li> When the client never declared chunks, then all local
* chunks will be interpreted as data chunks.
* <li> The stream must be positioned at the beginning of the
* RIFFFileExpression.
*
* <p>Post condition
* <li> When no exception was thrown then the stream is positioned
* after the RIFFFile expression.
*
* <p>Obligation
* The visitor may throw an ParseException or an
* AbortException during tree traversal.
*
* @exception ParseException
* Is thrown when an interpretation error occured.
* The stream is positioned where the error occured.
* @exception AbortException
* Is thrown when the visitor decided to abort the
* interpretation.
*/
public long parse(InputStream in, RIFFVisitor v)
throws ParseException, AbortException, IOException {
this.in = new RIFFPrimitivesInputStream(in);
visitor = v;
parseFile();
return getScan(this.in);
}
public long parse(ImageInputStream in, RIFFVisitor v)
throws ParseException, AbortException, IOException {
return parse(new ImageInputStreamAdapter(in), v);
}
/**
* Parses a RIFF file.
*
* <pre>
* RIFF = 'RIFF' FormGroup
* </pre>
*/
private void parseFile()
throws ParseException, AbortException, IOException {
int id = in.readFourCC();
if (id == RIFF_ID) {
parseFORM(null);
} else if (id == JUNK_ID) {
parseLocalChunk(null,id);
} else {
if (iin!=null) {
throw new ParseException("Invalid RIFF File ID: \"" + idToString(id) +" 0x"+Integer.toHexString(id)+" near "+iin.getStreamPosition()+" 0x"+Long.toHexString(iin.getStreamPosition()));
} else {
throw new ParseException("Invalid RIFF File ID: \"" + idToString(id) +" 0x"+Integer.toHexString(id));
}
}
}
private long getScan(RIFFPrimitivesInputStream in) {
return in.getScan()+streamOffset;
}
/**
* Parses a FORM group.
* <pre>
* FormGroup ::= size GroupType { ChunkID LocalChunk [pad]
* | 'FORM' FormGroup [pad] }
* | 'LIST' ListGroup [pad] }
* </pre>
*/
private void parseFORM(HashMap props)
throws ParseException, AbortException, IOException {
long size = in.readULONG();
long offset = getScan(in);
int type = in.readFourCC();
if (DEBUG)System.out.println("RIFFParser.parseForm "+idToString(type));
if (!isGroupType(type)) {
throw new ParseException("Invalid FORM Type: \"" + idToString(type) + "\"");
}
RIFFChunk propGroup = (props == null) ? null : (RIFFChunk) props.get(type);
RIFFChunk chunk = new RIFFChunk(type, RIFF_ID, size, offset, propGroup);
boolean visitorWantsToEnterGroup = false;
if (isGroupChunk(chunk) && (visitorWantsToEnterGroup = visitor.enteringGroup(chunk))) {
visitor.enterGroup(chunk);
}
try {
long finish = offset + size;
while (getScan(in) < finish) {
long idscan = getScan(in);
int id = in.readFourCC();
if (id == RIFF_ID) {
parseFORM(props);
} else if (id == LIST_ID) {
parseLIST(props);
} else if (isLocalChunkID(id)) {
parseLocalChunk(chunk, id);
} else {
ParseException pex = new ParseException("Invalid Chunk: \"" + id + "\" at offset:" + idscan);
chunk.setParserMessage(pex.getMessage());
throw pex;
}
in.align();
}
} catch (EOFException e) {
e.printStackTrace();
chunk.setParserMessage(
"Unexpected EOF after "
+ NumberFormat.getInstance().format(getScan(in) - offset)
+ " bytes");
} finally {
if (visitorWantsToEnterGroup) {
visitor.leaveGroup(chunk);
}
}
}
/**
* Parses a LIST group.
* <pre>
* ListGroup ::= size GroupType { ChunkID LocalChunk [pad] | 'LIST ' ListGroup [pad] }
* </pre>
*/
private void parseLIST(HashMap props)
throws ParseException, AbortException, IOException {
long size = in.readULONG();
long scan = getScan(in);
int type = in.readFourCC();
if (DEBUG)System.out.println("RIFFParser.parseLIST "+idToString(type));
if (!isGroupType(type)) {
throw new ParseException("Invalid LIST Type: \"" + type + "\"");
}
RIFFChunk propGroup = (props == null) ? null : (RIFFChunk) props.get(type);
RIFFChunk chunk = new RIFFChunk(type, LIST_ID, size, scan, propGroup);
boolean visitorWantsToEnterGroup = false;
if (isGroupChunk(chunk) && (visitorWantsToEnterGroup = visitor.enteringGroup(chunk))) {
visitor.enterGroup(chunk);
}
try {
if (visitorWantsToEnterGroup) {
long finish = scan + size;
while (getScan(in) < finish) {
long idscan = getScan(in);
int id = in.readFourCC();
if (id == LIST_ID) {
parseLIST(props);
} else if (isLocalChunkID(id)) {
parseLocalChunk(chunk, id);
} else {
parseGarbage(chunk, id, finish-getScan(in), getScan(in));
ParseException pex = new ParseException("Invalid Chunk: \"" + id + "\" at offset:" + idscan);
chunk.setParserMessage(pex.getMessage());
//throw pex;
}
in.align();
}
} else {
in.skipFully(size-4);
in.align();
}
} finally {
if (visitorWantsToEnterGroup) {
visitor.leaveGroup(chunk);
}
}
}
/**
* Parses a local chunk.
* <pre>
* LocalChunk ::= size { DataChunk | PropertyChunk | CollectionChunk }
* DataChunk = PropertyChunk = CollectionChunk ::= { byte }...*size
* </pre>
*/
private void parseLocalChunk(RIFFChunk parent, int id)
throws ParseException, AbortException, IOException {
long size = in.readULONG();
long scan = getScan(in);
if (DEBUG)System.out.println("RIFFParser.parseLocalChunk "+idToString(id));
RIFFChunk chunk = new RIFFChunk(parent==null?0:parent.getType(), id, size, scan);
if (isDataChunk(chunk)) {
byte[] data = new byte[(int) size];
in.read(data, 0, (int) size);
chunk.setData(data);
visitor.visitChunk(parent, chunk);
} else if (isPropertyChunk(chunk)) {
byte[] data = new byte[(int) size];
in.read(data, 0, (int) size);
chunk.setData(data);
parent.putPropertyChunk(chunk);
} else if (isCollectionChunk(chunk)) {
byte[] data = new byte[(int) size];
in.read(data, 0, (int) size);
chunk.setData(data);
parent.addCollectionChunk(chunk);
} else {
in.skipFully((int) size);
if (isStopChunks) {
visitor.visitChunk(parent, chunk);
}
}
}
/**
* This method is invoked when we encounter a parsing problem.
* <pre>
* LocalChunk ::= size { DataChunk | PropertyChunk | CollectionChunk }
* DataChunk = PropertyChunk = CollectionChunk ::= { byte }...*size
* </pre>
*/
private void parseGarbage(RIFFChunk parent, int id, long size, long scan)
throws ParseException, AbortException, IOException {
//long size = in.readULONG();
//long scan = getScan(in);
RIFFChunk chunk = new RIFFChunk(parent.getType(), id, size, scan);
if (isDataChunk(chunk)) {
byte[] data = new byte[(int) size];
in.read(data, 0, (int) size);
chunk.setData(data);
visitor.visitChunk(parent, chunk);
} else if (isPropertyChunk(chunk)) {
byte[] data = new byte[(int) size];
in.read(data, 0, (int) size);
chunk.setData(data);
parent.putPropertyChunk(chunk);
} else if (isCollectionChunk(chunk)) {
byte[] data = new byte[(int) size];
in.read(data, 0, (int) size);
chunk.setData(data);
parent.addCollectionChunk(chunk);
} else {
in.skipFully((int) size);
if (isStopChunk(chunk)) {
visitor.visitChunk(parent, chunk);
}
}
}
/**
* Checks whether the ID of the chunk has been declared as a
* data chunk.
*
* <p>Pre condition
* <li> Data chunks must have been declared before the
* interpretation has been started.
* <li> This method will always return true when neither
* data chunks, property chunks nor collection chunks
* have been declared,
*
* @param chunk Chunk to be verified.
* @return True when the parameter is a data chunk.
*/
protected boolean isDataChunk(RIFFChunk chunk) {
if (dataChunks == null) {
if (collectionChunks == null && propertyChunks == null && (stopChunkTypes==null||!stopChunkTypes.contains(chunk.getType()))) {
return true;
} else {
return false;
}
} else {
return dataChunks.contains(chunk);
}
}
/**
* Checks whether the ID of the chunk has been declared as
* a group chunk.
*
* <p>Pre condition
* <li> Group chunks must have been declared before the
* interpretation has been started.
* (Otherwise the response is always true).
*
* @param chunk Chunk to be verified.
* @return True when the visitor is interested in this is a group chunk.
*/
protected boolean isGroupChunk(RIFFChunk chunk) {
if (groupChunks == null) {
return true;
} else {
return groupChunks.contains(chunk);
}
}
/**
* Checks wether the ID of the chunk has been declared as a
* property chunk.
*
* <p>Pre condition
* <li> Property chunks must have been declared before the
* interpretation has been started.
* <li> This method will always return false when neither
* data chunks, property chunks nor collection chunks
* have been declared,
*/
protected boolean isPropertyChunk(RIFFChunk chunk) {
if (propertyChunks == null) {
return false;
} else {
return propertyChunks.contains(chunk);
}
}
/**
* Checks wether the ID of the chunk has been declared as a
* collection chunk.
*
* <p>Pre condition
* <li> Collection chunks must have been declared before the
* interpretation has been started.
* <li> This method will always return true when neither
* data chunks, property chunks nor collection chunks
* have been declared,
*
* @param chunk Chunk to be verified.
* @return True when the parameter is a collection chunk.
*/
protected boolean isCollectionChunk(RIFFChunk chunk) {
if (collectionChunks == null) {
return false;
} else {
return collectionChunks.contains(chunk);
}
}
/**
* Declares a data chunk.
*
* <p>Pre condition
* <li> The chunk must not have already been declared as of a
* different type.
* <li> Declarations may not be done during interpretation
* of an RIFFFileExpression.
*
* <p>Post condition
* <li> Data chunk declared
*
* @param type
* Type of the chunk. Must be formulated as a TypeID conforming
* to the method #isFormType.
* @param id
* ID of the chunk. Must be formulated as a ChunkID conforming
* to the method #isLocalChunkID.
*/
public void declareDataChunk(int type, int id) {
RIFFChunk chunk = new RIFFChunk(type, id);
if (dataChunks == null) {
dataChunks = new HashSet<RIFFChunk>();
}
dataChunks.add(chunk);
}
/**
* Declares a FORM group chunk.
*
* <p>Pre condition
* <li> The chunk must not have already been declared as of a
* different type.
* <li> Declarations may not be done during interpretation
* of an RIFFFileExpression.
*
* <p>Post condition
* <li> Group chunk declared
*
* @param type
* Type of the chunk. Must be formulated as a TypeID conforming
* to the method #isFormType.
* @param id
* ID of the chunk. Must be formulated as a ChunkID conforming
* to the method #isContentsType.
*/
public void declareGroupChunk(int type, int id) {
RIFFChunk chunk = new RIFFChunk(type, id);
if (groupChunks == null) {
groupChunks = new HashSet<RIFFChunk>();
}
groupChunks.add(chunk);
}
/**
* Declares a property chunk.
*
* <p>Pre condition
* <li> The chunk must not have already been declared as of a
* different type.
* <li> Declarations may not be done during interpretation
* of an RIFFFileExpression.
*
* <p>Post condition
* <li> Group chunk declared
*
*
* @param type
* Type of the chunk. Must be formulated as a TypeID conforming
* to the method #isFormType.
* @param id
* ID of the chunk. Must be formulated as a ChunkID conforming
* to the method #isLocalChunkID.
*/
public void declarePropertyChunk(int type, int id) {
RIFFChunk chunk = new RIFFChunk(type, id);
if (propertyChunks == null) {
propertyChunks = new HashSet<RIFFChunk>();
}
propertyChunks.add(chunk);
}
/**
* Declares a collection chunk.
*
* <p>Pre condition
* <li> The chunk must not have already been declared as of a
* different type.
* <li> Declarations may not be done during interpretation
* of an RIFFFileExpression.
*
* <p>Post condition
* <li> Collection chunk declared
*
* @param type
* Type of the chunk. Must be formulated as a TypeID conforming
* to the method #isFormType.
* @param id
* ID of the chunk. Must be formulated as a ChunkID conforming
* to the method #isLocalChunkID.
*/
public void declareCollectionChunk(int type, int id) {
RIFFChunk chunk = new RIFFChunk(type, id);
if (collectionChunks == null) {
collectionChunks = new HashSet<RIFFChunk>();
}
collectionChunks.add(chunk);
}
/**
* Declares a stop chunk.
*
* <p>Pre condition
* <li> The chunk must not have already been declared as of a
* different type.
* <li> Declarations may not be done during interpretation
* of an RIFFFileExpression.
*
* <p>Post condition
* <li> Stop chunk declared
*
* @param type
* Type of the chunk. Must be formulated as a TypeID conforming
* to the method #isFormType.
*/
public void declareStopChunkType(int type) {
if (stopChunkTypes == null) {
stopChunkTypes = new HashSet<Integer>();
}
stopChunkTypes.add(type);
}
/** Whether the parse should stop at all chunks.
* <p>
* The parser does not read the data body of stop chunks.
* <p>
* By declaring stop chunks, and not declaring any data, group or
* property chunks, the file structure of a RIFF file can be quickly
* scanned through.
*/
public void declareStopChunks() {
isStopChunks = true;
}
private boolean isStopChunk(RIFFChunk chunk) {
return isStopChunks||stopChunkTypes!=null&&stopChunkTypes.contains(chunk.getType());
}
/* ---- Class methods ---- */
/**
* Checks wether the argument represents a valid RIFF GroupID.
*
* <p>Validation
* <ul>
* <li> Group ID must be one of RIFF_ID, LIST_ID.</li>
* </ul>
*
* @param id Chunk ID to be checked.
* @return True when the chunk ID is a valid Group ID.
*/
public static boolean isGroupID(int id) {
return id == LIST_ID || id == RIFF_ID;
}
/**
* Checks wether the argument represents a valid RIFF Group Type.
*
* <p>Validation
* <ul>
* <li> Must be a valid ID.</li>
* <li> Must not be a group ID.</li>
* <li> Must not be a NULL_ID.</li>
* </ul>
*
* @param id Chunk ID to be checked.
* @return True when the chunk ID is a valid Group ID.
*/
public static boolean isGroupType(int id) {
return isID(id) && !isGroupID(id) && id != NULL_ID;
}
/**
* Checks if the argument represents a valid RIFF ID.
*
* <p>Validation
* <li> Every byte of an ID must be in the range of 0x20..0x7e
* <li> The id may not have leading spaces (unless the id is a NULL_ID).
*
* @param id Chunk ID to be checked.
* @return True when the ID is a valid IFF chunk ID.
*/
public static boolean isID(int id) {
int c0 = id >> 24;
int c1 = (id >> 16) & 0xff;
int c2 = (id >> 8) & 0xff;
int c3 = id & 0xff;
return id == NULL_NUL_ID
|| c0 >= 0x20 && c0 <= 0x7e
&& c1 >= 0x20 && c1 <= 0x7e
&& c2 >= 0x20 && c2 <= 0x7e
&& c3 >= 0x20 && c3 <= 0x7e;
}
/**
* Returns whether the argument is a valid Local Chunk ID.
*
* <p>Validation
* <ud>
* <li> Must be valid ID.</li>
* <li> Local Chunk IDs may not collide with GroupIDs.</id>
* <li> Must not be a NULL_ID.</li>
* </ud>
*
* @param id Chunk ID to be checked.
* @return True when the chunk ID is a Local Chunk ID.
*/
public static boolean isLocalChunkID(int id) {
if (isGroupID(id)) {
return false;
}
return id != NULL_ID && isID(id);
}
private WeakHashMap<String, String> ids;
/**
* Convert an integer IFF identifier to String.
*
* @param anInt ID to be converted.
* @return String representation of the ID.
*/
public static String idToString(int anInt) {
byte[] bytes = new byte[4];
bytes[0] = (byte) (anInt >>> 24);
bytes[1] = (byte) (anInt >>> 16);
bytes[2] = (byte) (anInt >>> 8);
bytes[3] = (byte) (anInt >>> 0);
try {
return new String(bytes, "ASCII");
} catch (UnsupportedEncodingException e) {
throw new InternalError(e.getMessage());
}
}
/**
* Converts the first four letters of the
* String into an IFF Identifier.
*
* @param aString String to be converted.
* @return ID representation of the String.
*/
public static int stringToID(String aString) {
byte[] bytes = aString.getBytes();
return ((int) bytes[0]) << 24
| ((int) bytes[1]) << 16
| ((int) bytes[2]) << 8
| ((int) bytes[3]) << 0;
}
}

View File

@@ -0,0 +1,265 @@
/*
* @(#)RIFFPrimitivesInputStream.java 1.0 2005-01-15
*
* Copyright (c) 2005 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.riff;
import java.io.*;
/**
* A RIFF primitives input stream lets an application read primitive data
* types in the Microsoft Resource Interfache File Format (RIFF) format from an
* underlying input stream.
*
* Reference:
* AVI RIFF File Reference
* http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c/directx/htm/avirifffilereference.asp
*
* @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland
* @version 1.0 2005-01-15 Created.
*/
public class RIFFPrimitivesInputStream extends FilterInputStream {
private long scan, mark;
/**
* Creates a new instance.
*
* @param in the input stream.
*/
public RIFFPrimitivesInputStream(InputStream in) {
super(in);
}
/**
* Read 1 byte from the input stream and interpret
* them as an 8 Bit unsigned UBYTE value.
*/
public int readUBYTE()
throws IOException {
int b0 = in.read();
if (b0 == -1) {
throw new EOFException();
}
scan += 1;
return b0 & 0xff;
}
/**
* Read 2 bytes from the input stream and interpret
* them as a 16 Bit signed WORD value.
*/
public short readWORD()
throws IOException {
int b0 = in.read();
int b1 = in.read();
if (b1 == -1) {
throw new EOFException();
}
scan += 2;
return (short) (((b0 & 0xff) << 0) | ((b1 & 0xff) << 8));
}
/**
* Read 2 bytes from the input stream and interpret
* them as a 16 Bit unsigned UWORD value.
*/
public int readUWORD()
throws IOException {
return readWORD() & 0xffff;
}
/**
* Read 4 bytes from the input stream and interpret
* them as a 32 Bit signed LONG value.
*/
public int readLONG()
throws IOException {
int b0 = in.read();
int b1 = in.read();
int b2 = in.read();
int b3 = in.read();
if (b3 == -1) {
throw new EOFException();
}
scan += 4;
return ((b0&0xff) << 0) +
((b1&0xff) << 8) +
((b2&0xff) << 16) +
((b3&0xff) << 24);
}
/**
* Read 4 bytes from the input stream and interpret
* them as a four byte character code.
*
* Cited from Referenced "AVI RIFF File Reference":
* "A FOURCC (four-character code) is a 32-bit unsigned integer created by
* concatenating four ASCII characters. For example, the FOURCC 'abcd' is
* represented on a Little-Endian system as 0x64636261. FOURCCs can contain
* space characters, so ' abc' is a valid FOURCC. The AVI file format uses
* FOURCC codes to identify stream types, data chunks, index entries, and
* other information."
*/
public int readFourCC()
throws IOException {
int b3 = in.read();
int b2 = in.read();
int b1 = in.read();
int b0 = in.read();
if (b0 == -1) {
throw new EOFException();
}
scan += 4;
return ((b0&0xff) << 0) +
((b1&0xff) << 8) +
((b2&0xff) << 16) +
((b3&0xff) << 24);
}
/**
* Read 4 bytes from the input stream and interpret
* them as a four byte character code.
*
* Cited from Referenced "AVI RIFF File Reference":
* "A FOURCC (four-character code) is a 32-bit unsigned integer created by
* concatenating four ASCII characters. For example, the FOURCC 'abcd' is
* represented on a Little-Endian system as 0x64636261. FOURCCs can contain
* space characters, so ' abc' is a valid FOURCC. The AVI file format uses
* FOURCC codes to identify stream types, data chunks, index entries, and
* other information."
*/
public String readFourCCString()
throws IOException {
byte[] buf = new byte[4];
readFully(buf, 0, 4);
//scan += 4; <- scan is updated by method readFully
return new String(buf, "ASCII");
}
/**
* Read 4 Bytes from the input Stream and interpret
* them as an unsigned Integer value of type ULONG.
*/
public long readULONG()
throws IOException {
return (long)(readLONG()) & 0x00ffffffff;
}
/**
* Align to an even byte position in the input stream.
* This will skip one byte in the stream if the current
* read position is not even.
*/
public void align()
throws IOException {
if (scan % 2 == 1) {
in.skip(1);
scan++;
}
}
/**
* Get the current read position within the file (as seen
* by this input stream filter).
*/
public long getScan()
{ return scan; }
/**
* Reads one byte.
*/
public int read()
throws IOException {
int data = in.read();
if (data != -1) scan++;
return data;
}
/**
* Reads a sequence of bytes.
*/
public int readFully(byte[] b,int offset, int length)
throws IOException {
int count = read(b, offset, length);
if (count != length) {
throw new EOFException("readFully for "+length+" bytes, unexpected EOF after "+count+" bytes.");
}
//scan += count; <- scan is already counted by read method
return count;
}
/**
* Reads a sequence of bytes.
*/
public int read(byte[] b,int offset, int length)
throws IOException {
int count = 0;
while (count < length) {
int result = in.read(b,offset+count,length-count);
if (result == -1) break;
count += result;
}
scan += count;
return count;
}
/**
* Marks the input stream.
* @param readlimit The maximum limit of bytes that can be read before
* the mark position becomes invalid.
*/
public void mark(int readlimit) {
in.mark(readlimit);
mark = scan;
}
/**
* Repositions the stream at the previously marked position.
*
* @exception IOException If the stream has not been marked or if the
* mark has been invalidated.
*/
public void reset()
throws IOException {
in.reset();
scan = mark;
}
/**
* Skips over and discards n bytes of data from this input stream. This skip
* method tries to skip the provided number of bytes.
*/
public long skip(long n)
throws IOException {
long skipped = in.skip(n);
scan += skipped;
return skipped;
}
/**
* Skips over and discards n bytes of data from this input stream. Throws
*
* @param n the number of bytes to be skipped.
* @exception EOFException if this input stream reaches the end before
* skipping all the bytes.
*/
public void skipFully(long n)
throws IOException {
if (n==0) return;
int total = 0;
int cur = 0;
while ((total<n) && ((cur = (int) in.skip(n-total)) > 0)) {
total += cur;
}
if (cur == 0) throw new EOFException();
scan += total;
}
}

View File

@@ -0,0 +1,43 @@
/*
* @(#)RIFFVIsitor.java 1.0 2005-01-09
*
* Copyright (c) 2005 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.riff;
import org.monte.media.AbortException;
import org.monte.media.ParseException;
/**
* RIFFVIsitor is notified each time the RIFFParser visits
* a data chunk and when a group is entered or leaved.
*
* @version 1.0 2005-01-09 Created.
*/
public interface RIFFVisitor {
/** This method is invoked when the parser attempts to enter a group.
* The visitor can return false, if the parse shall skip the group contents.
*
* @param group
* @return True to enter the group, false to skip over the group.
*/
public boolean enteringGroup(RIFFChunk group);
/** This method is invoked when the parser enters a group chunk.*/
public void enterGroup(RIFFChunk group)
throws ParseException, AbortException;
/** This method is invoked when the parser leaves a group chunk.*/
public void leaveGroup(RIFFChunk group)
throws ParseException, AbortException;
/** This method is invoked when the parser has read a data chunk or
* has skipped a stop chunk.*/
public void visitChunk(RIFFChunk group, RIFFChunk chunk)
throws ParseException, AbortException;
}

View File

@@ -0,0 +1,463 @@
/*
* @(#)Methods.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.util;
import java.lang.reflect.*;
/**
* Methods contains convenience methods for method invocations using
* java.lang.reflect.
*
* @author Werner Randelshofer
* @version $Id: Methods.java 299 2013-01-03 07:40:18Z werner $
*/
@SuppressWarnings("unchecked")
public class Methods {
/**
* Prevent instance creation.
*/
private Methods() {
}
/**
* Invokes the specified accessible parameterless method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
* @return The return value of the method.
* @return NoSuchMethodException if the method does not exist or is not
* accessible.
*/
public static Object invoke(Object obj, String methodName)
throws NoSuchMethodException {
try {
Method method = obj.getClass().getMethod(methodName, new Class[0]);
Object result = method.invoke(obj, new Object[0]);
return result;
} catch (IllegalAccessException e) {
throw new NoSuchMethodException(methodName+" is not accessible");
} catch (InvocationTargetException e) {
// The method is not supposed to throw exceptions
throw new InternalError(e.getMessage());
}
}
/**
* Invokes the specified accessible method with a string parameter if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
* @param stringParameter The String parameter
* @return The return value of the method or METHOD_NOT_FOUND.
* @return NoSuchMethodException if the method does not exist or is not accessible.
*/
public static Object invoke(Object obj, String methodName, String stringParameter)
throws NoSuchMethodException {
try {
Method method = obj.getClass().getMethod(methodName, new Class[] { String.class });
Object result = method.invoke(obj, new Object[] { stringParameter });
return result;
} catch (IllegalAccessException e) {
throw new NoSuchMethodException(methodName+" is not accessible");
} catch (InvocationTargetException e) {
// The method is not supposed to throw exceptions
throw new InternalError(e.getMessage());
}
}
/**
* Invokes the specified accessible parameterless method if it exists.
*
* @param clazz The class on which to invoke the method.
* @param methodName The name of the method.
* @return The return value of the method or METHOD_NOT_FOUND.
* @return NoSuchMethodException if the method does not exist or is not accessible.
*/
public static Object invokeStatic(Class clazz, String methodName)
throws NoSuchMethodException {
try {
Method method = clazz.getMethod(methodName, new Class[0]);
Object result = method.invoke(null, new Object[0]);
return result;
} catch (IllegalAccessException e) {
throw new NoSuchMethodException(methodName+" is not accessible");
} catch (InvocationTargetException e) {
// The method is not supposed to throw exceptions
throw new InternalError(e.getMessage());
}
}
/**
* Invokes the specified accessible parameterless method if it exists.
*
* @param clazz The class on which to invoke the method.
* @param methodName The name of the method.
* @return The return value of the method.
* @return NoSuchMethodException if the method does not exist or is not accessible.
*/
public static Object invokeStatic(String clazz, String methodName)
throws NoSuchMethodException {
try {
return invokeStatic(Class.forName(clazz), methodName);
} catch (ClassNotFoundException e) {
throw new NoSuchMethodException("class "+clazz+" not found");
}
}
/**
* Invokes the specified parameterless method if it exists.
*
* @param clazz The class on which to invoke the method.
* @param methodName The name of the method.
* @param type The parameter type.
* @param value The parameter value.
* @return The return value of the method.
* @return NoSuchMethodException if the method does not exist or is not accessible.
*/
public static Object invokeStatic(Class clazz, String methodName, Class type, Object value)
throws NoSuchMethodException {
return invokeStatic(clazz,methodName,new Class[]{type},new Object[]{value});
}
/**
* Invokes the specified parameterless method if it exists.
*
* @param clazz The class on which to invoke the method.
* @param methodName The name of the method.
* @param types The parameter types.
* @param values The parameter values.
* @return The return value of the method.
* @return NoSuchMethodException if the method does not exist or is not accessible.
*/
public static Object invokeStatic(Class clazz, String methodName, Class[] types, Object[] values)
throws NoSuchMethodException {
try {
Method method = clazz.getMethod(methodName, types);
Object result = method.invoke(null, values);
return result;
} catch (IllegalAccessException e) {
throw new NoSuchMethodException(methodName+" is not accessible");
} catch (InvocationTargetException e) {
// The method is not supposed to throw exceptions
throw new InternalError(e.getMessage());
}
}
/**
* Invokes the specified parameterless method if it exists.
*
* @param clazz The class on which to invoke the method.
* @param methodName The name of the method.
* @param types The parameter types.
* @param values The parameter values.
* @return The return value of the method.
* @return NoSuchMethodException if the method does not exist or is not accessible.
*/
public static Object invokeStatic(String clazz, String methodName,
Class[] types, Object[] values)
throws NoSuchMethodException {
try {
return invokeStatic(Class.forName(clazz), methodName, types, values);
} catch (ClassNotFoundException e) {
throw new NoSuchMethodException("class "+clazz+" not found");
}
}
/**
* Invokes the specified parameterless method if it exists.
*
* @param clazz The class on which to invoke the method.
* @param methodName The name of the method.
* @param types The parameter types.
* @param values The parameter values.
* @param defaultValue The default value.
* @return The return value of the method or the default value if the method
* does not exist or is not accessible.
*/
public static Object invokeStatic(String clazz, String methodName,
Class[] types, Object[] values, Object defaultValue) {
try {
return invokeStatic(Class.forName(clazz), methodName, types, values);
} catch (ClassNotFoundException e) {
return defaultValue;
} catch (NoSuchMethodException e) {
return defaultValue;
}
}
/**
* Invokes the specified getter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
* @param defaultValue This value is returned, if the method does not exist.
* @return The value returned by the getter method or the default value.
*/
public static int invokeGetter(Object obj, String methodName, int defaultValue) {
try {
Method method = obj.getClass().getMethod(methodName, new Class[0]);
Object result = method.invoke(obj, new Object[0]);
return ((Integer) result).intValue();
} catch (NoSuchMethodException e) {
return defaultValue;
} catch (IllegalAccessException e) {
return defaultValue;
} catch (InvocationTargetException e) {
return defaultValue;
}
}
/**
* Invokes the specified getter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
* @param defaultValue This value is returned, if the method does not exist.
* @return The value returned by the getter method or the default value.
*/
public static long invokeGetter(Object obj, String methodName, long defaultValue) {
try {
Method method = obj.getClass().getMethod(methodName, new Class[0]);
Object result = method.invoke(obj, new Object[0]);
return ((Long) result).longValue();
} catch (NoSuchMethodException e) {
return defaultValue;
} catch (IllegalAccessException e) {
return defaultValue;
} catch (InvocationTargetException e) {
return defaultValue;
}
}
/**
* Invokes the specified getter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
* @param defaultValue This value is returned, if the method does not exist.
* @return The value returned by the getter method or the default value.
*/
public static boolean invokeGetter(Object obj, String methodName, boolean defaultValue) {
try {
Method method = obj.getClass().getMethod(methodName, new Class[0]);
Object result = method.invoke(obj, new Object[0]);
return ((Boolean) result).booleanValue();
} catch (NoSuchMethodException e) {
return defaultValue;
} catch (IllegalAccessException e) {
return defaultValue;
} catch (InvocationTargetException e) {
return defaultValue;
}
}
/**
* Invokes the specified getter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
* @param defaultValue This value is returned, if the method does not exist.
* @return The value returned by the getter method or the default value.
*/
public static Object invokeGetter(Object obj, String methodName, Object defaultValue) {
try {
Method method = obj.getClass().getMethod(methodName, new Class[0]);
Object result = method.invoke(obj, new Object[0]);
return result;
} catch (NoSuchMethodException e) {
return defaultValue;
} catch (IllegalAccessException e) {
return defaultValue;
} catch (InvocationTargetException e) {
return defaultValue;
}
}
/**
* Invokes the specified getter method if it exists.
*
* @param clazz The object on which to invoke the method.
* @param methodName The name of the method.
* @param defaultValue This value is returned, if the method does not exist.
* @return The value returned by the getter method or the default value.
*/
public static boolean invokeStaticGetter(Class clazz, String methodName, boolean defaultValue) {
try {
Method method = clazz.getMethod(methodName, new Class[0]);
Object result = method.invoke(null, new Object[0]);
return ((Boolean) result).booleanValue();
} catch (NoSuchMethodException e) {
return defaultValue;
} catch (IllegalAccessException e) {
return defaultValue;
} catch (InvocationTargetException e) {
return defaultValue;
}
}
/**
* Invokes the specified setter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
*/
public static Object invoke(Object obj, String methodName, boolean newValue)
throws NoSuchMethodException {
try {
Method method = obj.getClass().getMethod(methodName, new Class[] { Boolean.TYPE} );
return method.invoke(obj, new Object[] { newValue});
} catch (IllegalAccessException e) {
throw new NoSuchMethodException(methodName+" is not accessible");
} catch (InvocationTargetException e) {
// The method is not supposed to throw exceptions
throw new InternalError(e.getMessage());
}
}
/**
* Invokes the specified method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
*/
public static Object invoke(Object obj, String methodName, int newValue)
throws NoSuchMethodException {
try {
Method method = obj.getClass().getMethod(methodName, new Class[] { Integer.TYPE} );
return method.invoke(obj, new Object[] { newValue});
} catch (IllegalAccessException e) {
throw new NoSuchMethodException(methodName+" is not accessible");
} catch (InvocationTargetException e) {
// The method is not supposed to throw exceptions
throw new InternalError(e.getMessage());
}
}
/**
* Invokes the specified setter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
*/
public static Object invoke(Object obj, String methodName, float newValue)
throws NoSuchMethodException {
try {
Method method = obj.getClass().getMethod(methodName, new Class[] { Float.TYPE} );
return method.invoke(obj, new Object[] { new Float(newValue)});
} catch (IllegalAccessException e) {
throw new NoSuchMethodException(methodName+" is not accessible");
} catch (InvocationTargetException e) {
// The method is not supposed to throw exceptions
throw new InternalError(e.getMessage());
}
}
/**
* Invokes the specified setter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
*/
public static Object invoke(Object obj, String methodName, Class clazz, Object newValue)
throws NoSuchMethodException {
try {
Method method = obj.getClass().getMethod(methodName, new Class[] { clazz } );
return method.invoke(obj, new Object[] { newValue});
} catch (IllegalAccessException e) {
throw new NoSuchMethodException(methodName+" is not accessible");
} catch (InvocationTargetException e) {
// The method is not supposed to throw exceptions
throw new InternalError(e.getMessage());
}
}
/**
* Invokes the specified setter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
*/
public static Object invoke(Object obj, String methodName, Class[] clazz, Object... newValue)
throws NoSuchMethodException {
try {
Method method = obj.getClass().getMethod(methodName, clazz );
return method.invoke(obj, newValue);
} catch (IllegalAccessException e) {
throw new NoSuchMethodException(methodName+" is not accessible");
} catch (InvocationTargetException e) {
// The method is not supposed to throw exceptions
InternalError error = new InternalError(e.getMessage());
error.initCause((e.getCause() != null) ? e.getCause() : e);
throw error;
}
}
/**
* Invokes the specified setter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
*/
public static void invokeIfExists(Object obj, String methodName) {
try {
invoke(obj, methodName);
} catch (NoSuchMethodException e) {
// ignore
}
}
/**
* Invokes the specified setter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
*/
public static void invokeIfExists(Object obj, String methodName, float newValue) {
try {
invoke(obj, methodName, newValue);
} catch (NoSuchMethodException e) {
// ignore
}
}
/**
* Invokes the specified method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
*/
public static void invokeIfExists(Object obj, String methodName, boolean newValue) {
try {
invoke(obj, methodName, newValue);
} catch (NoSuchMethodException e) {
// ignore
}
}
/**
* Invokes the specified setter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
*/
public static void invokeIfExists(Object obj, String methodName, Class clazz, Object newValue) {
try {
invoke(obj, methodName, clazz, newValue);
} catch (NoSuchMethodException e) {
// ignore
}
}
/**
* Invokes the specified setter method if it exists.
*
* @param obj The object on which to invoke the method.
* @param methodName The name of the method.
*/
public static void invokeIfExistsWithEnum(Object obj, String methodName, String enumClassName, String enumValueName) {
try {
Class enumClass = Class.forName(enumClassName);
Object enumValue = invokeStatic("java.lang.Enum", "valueOf", new Class[] {Class.class, String.class},
new Object[] {enumClass, enumValueName}
);
invoke(obj, methodName, enumClass, enumValue);
} catch (ClassNotFoundException e) {
// ignore
e.printStackTrace();
} catch (NoSuchMethodException e) {
// ignore
e.printStackTrace();
}
}
}

73
libsrc/gif/build.xml Normal file
View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="gif" default="default" basedir=".">
<description>Builds, tests, and runs the project gif.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="gif-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
build.xml.data.CRC32=c07cf2d0
build.xml.script.CRC32=d45952bc
build.xml.stylesheet.CRC32=8064a381@1.68.1.46
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=c07cf2d0
nbproject/build-impl.xml.script.CRC32=178a9ed1
nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.74.1.48

View File

@@ -0,0 +1,71 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processor.options=
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
${run.classpath}
debug.test.classpath=\
${run.test.classpath}
# Files in build.classes.dir which should be excluded from distribution jar
dist.archive.excludes=
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=../../lib/gif.jar
dist.javadoc.dir=${dist.dir}/javadoc
excludes=
includes=**
jar.compress=false
javac.classpath=
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.processorpath=\
${javac.classpath}
javac.source=1.7
javac.target=1.7
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
javac.test.processorpath=\
${javac.test.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=true
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project.
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
# To set system properties for unit tests define test-sys-prop.name=value:
run.jvmargs=
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
source.encoding=UTF-8
src.dir=src
test.src.dir=test

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>gif</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>

View File

@@ -0,0 +1,191 @@
package net.kroo.elliot;
/*
Created by Elliot Kroo on 2009-04-25.
This work is licensed under the Creative Commons Attribution 3.0 Unported
License. To view a copy of this license, visit
http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative
Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
*/
import javax.imageio.*;
import javax.imageio.metadata.*;
import javax.imageio.stream.*;
import java.awt.image.*;
import java.io.*;
import java.util.Iterator;
public class GifSequenceWriter {
protected ImageWriter gifWriter;
protected ImageWriteParam imageWriteParam;
protected IIOMetadata imageMetaData;
/**
* Creates a new GifSequenceWriter
*
* @param outputStream the ImageOutputStream to be written to
* @param imageType one of the imageTypes specified in BufferedImage
* @param timeBetweenFramesMS the time between frames in miliseconds
* @param loopContinuously wether the gif should loop repeatedly
* @throws IIOException if no gif ImageWriters are found
*
* @author Elliot Kroo (elliot[at]kroo[dot]net)
*/
public GifSequenceWriter(
ImageOutputStream outputStream,
int imageType,
int timeBetweenFramesMS,
boolean loopContinuously) throws IIOException, IOException {
// my method to create a writer
gifWriter = getWriter();
imageWriteParam = gifWriter.getDefaultWriteParam();
ImageTypeSpecifier imageTypeSpecifier =
ImageTypeSpecifier.createFromBufferedImageType(imageType);
imageMetaData =
gifWriter.getDefaultImageMetadata(imageTypeSpecifier,
imageWriteParam);
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
IIOMetadataNode root = (IIOMetadataNode)
imageMetaData.getAsTree(metaFormatName);
IIOMetadataNode graphicsControlExtensionNode = getNode(
root,
"GraphicControlExtension");
graphicsControlExtensionNode.setAttribute("disposalMethod", "restoreToBackgroundColor"); //JPEXS: changed none to restoreToBackgroundColor
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute(
"transparentColorFlag",
"FALSE");
graphicsControlExtensionNode.setAttribute(
"delayTime",
Integer.toString(timeBetweenFramesMS / 10));
graphicsControlExtensionNode.setAttribute(
"transparentColorIndex",
"0");
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
commentsNode.setAttribute("CommentExtension", "Created by MAH");
IIOMetadataNode appEntensionsNode = getNode(
root,
"ApplicationExtensions");
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
child.setAttribute("applicationID", "NETSCAPE");
child.setAttribute("authenticationCode", "2.0");
int loop = loopContinuously ? 0 : 1;
child.setUserObject(new byte[]{ 0x1, (byte) (loop & 0xFF), (byte)
((loop >> 8) & 0xFF)});
appEntensionsNode.appendChild(child);
imageMetaData.setFromTree(metaFormatName, root);
gifWriter.setOutput(outputStream);
gifWriter.prepareWriteSequence(null);
}
public void writeToSequence(RenderedImage img) throws IOException {
gifWriter.writeToSequence(
new IIOImage(
img,
null,
imageMetaData),
imageWriteParam);
}
/**
* Close this GifSequenceWriter object. This does not close the underlying
* stream, just finishes off the GIF.
*/
public void close() throws IOException {
gifWriter.endWriteSequence();
}
/**
* Returns the first available GIF ImageWriter using
* ImageIO.getImageWritersBySuffix("gif").
*
* @return a GIF ImageWriter object
* @throws IIOException if no GIF image writers are returned
*/
private static ImageWriter getWriter() throws IIOException {
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
if(!iter.hasNext()) {
throw new IIOException("No GIF Image Writers Exist");
} else {
return iter.next();
}
}
/**
* Returns an existing child node, or creates and returns a new child node (if
* the requested node does not exist).
*
* @param rootNode the <tt>IIOMetadataNode</tt> to search for the child node.
* @param nodeName the name of the child node.
*
* @return the child node, if found or a new node created with the given name.
*/
private static IIOMetadataNode getNode(
IIOMetadataNode rootNode,
String nodeName) {
int nNodes = rootNode.getLength();
for (int i = 0; i < nNodes; i++) {
if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName)
== 0) {
return((IIOMetadataNode) rootNode.item(i));
}
}
IIOMetadataNode node = new IIOMetadataNode(nodeName);
rootNode.appendChild(node);
return(node);
}
/**
public GifSequenceWriter(
BufferedOutputStream outputStream,
int imageType,
int timeBetweenFramesMS,
boolean loopContinuously) {
*/
public static void main(String[] args) throws Exception {
if (args.length > 1) {
// grab the output image type from the first image in the sequence
BufferedImage firstImage = ImageIO.read(new File(args[0]));
// create a new BufferedOutputStream with the last argument
ImageOutputStream output =
new FileImageOutputStream(new File(args[args.length - 1]));
// create a gif sequence with the type of the first image, 1 second
// between frames, which loops continuously
GifSequenceWriter writer =
new GifSequenceWriter(output, firstImage.getType(), 1, false);
// write out the first image to our sequence...
writer.writeToSequence(firstImage);
for(int i=1; i<args.length-1; i++) {
BufferedImage nextImage = ImageIO.read(new File(args[i]));
writer.writeToSequence(nextImage);
}
writer.close();
output.close();
} else {
System.out.println(
"Usage: java GifSequenceWriter [list of gif files] [output file]");
}
}
}

73
libsrc/jpacker/build.xml Normal file
View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="jpacker" default="default" basedir=".">
<description>Builds, tests, and runs the project jpacker.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="jpacker-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>

BIN
libsrc/jpacker/jargs.jar Normal file

Binary file not shown.

View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
build.xml.data.CRC32=811ea359
build.xml.script.CRC32=f4a6aa27
build.xml.stylesheet.CRC32=8064a381@1.74.1.48
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=811ea359
nbproject/build-impl.xml.script.CRC32=a8ed580a
nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.74.1.48

View File

@@ -0,0 +1,77 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
application.title=jpacker
application.vendor=
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
${run.classpath}
debug.test.classpath=\
${run.test.classpath}
# Files in build.classes.dir which should be excluded from distribution jar
dist.archive.excludes=
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=../../lib/jpacker.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
file.reference.jargs.jar=jargs.jar
includes=**
jar.compress=false
javac.classpath=\
${file.reference.jargs.jar}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.processorpath=\
${javac.classpath}
javac.source=1.7
javac.target=1.7
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
javac.test.processorpath=\
${javac.test.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
main.class=com.jpacker.JPacker
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=true
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project.
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
# To set system properties for unit tests define test-sys-prop.name=value:
run.jvmargs=
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
source.encoding=UTF-8
src.dir=src
test.src.dir=test

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>jpacker</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>

View File

@@ -0,0 +1,197 @@
/**
* Packer version 3.0 (final)
* Copyright 2004-2007, Dean Edwards
* Web: {@link http://dean.edwards.name/}
*
* This software is licensed under the MIT license
* Web: {@link http://www.opensource.org/licenses/mit-license}
*
* Ported to Java by Pablo Santiago based on C# version by Jesse Hansen, <twindagger2k @ msn.com>
* Web: {@link http://jpacker.googlecode.com/}
* Email: <pablo.santiago @ gmail.com>
*/
package com.jpacker;
import com.jpacker.exceptions.EmptyFileException;
import jargs.gnu.CmdLineParser;
import jargs.gnu.CmdLineParser.IllegalOptionValueException;
import jargs.gnu.CmdLineParser.Option;
import jargs.gnu.CmdLineParser.UnknownOptionException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
/**
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class JPacker {
/**
* @param args
* the command line arguments
*/
public static void main(String[] args) throws FileNotFoundException, IOException {
long startTime = System.currentTimeMillis();
CmdLineParser parser = new CmdLineParser();
Option baseOpt = parser.addIntegerOption('b', "base");
Option columnsOpt = parser.addIntegerOption('c', "column");
Option helpOpt = parser.addBooleanOption('h', "help");
Option minifyOpt = parser.addBooleanOption('m', "minify");
Option outputFilenameOpt = parser.addStringOption('o', "output");
Option quietOpt = parser.addBooleanOption('q', "quiet");
Option shrinkVariablesOpt = parser.addBooleanOption('s', "shrink-variables");
Writer out = null;
BufferedReader in = null;
try {
if (args.length == 0) {
throw new RuntimeException("No input file");
}
parser.parse(args);
Boolean help = (Boolean) parser.getOptionValue(helpOpt);
if (help != null && help.booleanValue()) {
printUsage();
System.exit(0);
}
boolean minify = parser.getOptionValue(minifyOpt) != null;
boolean shrinkVariables = parser.getOptionValue(shrinkVariablesOpt) != null;
boolean quiet = parser.getOptionValue(quietOpt) != null;
Integer base = (Integer) parser.getOptionValue(baseOpt);
Integer columns = (Integer) parser.getOptionValue(columnsOpt);
if (parser.getRemainingArgs().length == 0) {
throw new FileNotFoundException("No input file was provided");
}
String inputFilename = parser.getRemainingArgs()[0];
String outputFilename = (String) parser.getOptionValue(outputFilenameOpt);
JPackerExecuter executer;
if (base == null) {
executer = new JPackerExecuter(JPackerEncoding.NONE);
} else {
executer = new JPackerExecuter(getEncoding(baseOpt, base));
}
in = new BufferedReader(new FileReader(new File(inputFilename)));
String unpacked = buildStringFromTextFile(in);
if (unpacked.isEmpty()) {
throw new EmptyFileException("The file is empty");
}
String packed = executer.pack(unpacked, minify, shrinkVariables);
if (outputFilename == null) {
out = new OutputStreamWriter(System.out);
} else {
out = new OutputStreamWriter(new FileOutputStream(outputFilename));
}
if (columns != null) {
packed = wrapLines(packed, columns);
}
out.write(packed.replace("\n", System.getProperty("line.separator")));
out.close();
if (!quiet) {
long endTime = System.currentTimeMillis();
System.out.printf("Reduced to %.2f%% of its original size in %.4f seconds.\n",
((double) packed.length() / (double) unpacked.length()) * 100,
(double) (endTime - startTime) / 1000);
}
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getLocalizedMessage());
System.exit(1);
} catch (IllegalOptionValueException e) {
System.out.println("Illegal option: " + e.getValue() + " - " + e.getOption());
System.exit(1);
} catch (UnknownOptionException e) {
printUsage();
System.exit(1);
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
System.exit(1);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
System.out.println(e.getLocalizedMessage());
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
System.out.println(e.getLocalizedMessage());
}
}
}
}
private static void printUsage() {
System.out.println("\nUsage: java -jar jpacker-x.y.z.jar [options] [input file]\n\n"
+ "Options\n"
+ " -b, --base Encoding base. Options are: 10, 36, 52, 62 \n"
+ " and 95. Ignored if --minify option is set.\n"
+ " -c, --column <column> Insert a line break after the specified column \n"
+ " number.\n"
+ " -h, --help Displays this information.\n"
+ " -m, --minify Minify only, do not obfuscate.\n"
+ " --minify and --base values will be ignored.\n"
+ " -o <file>, --output <file> Place the output into <file>.\n"
+ " Defaults to stdout.\n"
+ " -q, --quiet Quiet mode, no message.\n"
+ " -s, --shrink-variables Shrink variables. Ignored if --minify option \n"
+ " is set.\n");
}
private static String wrapLines(String packedScript, Integer columns) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < packedScript.length(); i++) {
int end = ((i + columns) > (packedScript.length()) ? packedScript.length() : i + columns);
sb.append(packedScript.substring(i, end)).append(System.getProperty("line.separator"));
i = end + 1;
}
return sb.toString();
}
private static String buildStringFromTextFile(BufferedReader reader) throws FileNotFoundException, IOException {
StringBuilder sb = new StringBuilder();
String s;
while ((s = reader.readLine()) != null) {
sb.append(s).append(System.getProperty("line.separator"));
}
reader.close();
return sb.toString();
}
private static JPackerEncoding getEncoding(Option option, Integer base) throws IllegalOptionValueException {
switch (base) {
case 10:
return JPackerEncoding.NUMERIC;
case 36:
return JPackerEncoding.MID;
case 52:
return JPackerEncoding.BASIC;
case 62:
return JPackerEncoding.NORMAL;
case 95:
return JPackerEncoding.HIGH_ASCII;
default:
throw new IllegalOptionValueException(option, "Encoding base option not valid");
}
}
}

View File

@@ -0,0 +1,75 @@
/**
* Packer version 3.0 (final)
* Copyright 2004-2007, Dean Edwards
* Web: {@link http://dean.edwards.name/}
*
* This software is licensed under the MIT license
* Web: {@link http://www.opensource.org/licenses/mit-license}
*
* Ported to Java by Pablo Santiago based on C# version by Jesse Hansen, <twindagger2k @ msn.com>
* Web: {@link http://jpacker.googlecode.com/}
* Email: <pablo.santiago @ gmail.com>
*/
package com.jpacker;
import com.jpacker.encoders.BasicEncoder;
import com.jpacker.encoders.Encoder;
import com.jpacker.encoders.HighAsciiEncoder;
import com.jpacker.encoders.MidEncoder;
import com.jpacker.encoders.NormalEncoder;
import com.jpacker.encoders.NumericEncoder;
/**
* Enum of encoding levels
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*
*/
public enum JPackerEncoding {
/**
* No encoding
*/
NONE(0, "", null),
/**
* Base<sub>10</sub> : [0-9]
*/
NUMERIC(10, "String", new NumericEncoder()),
/**
* Base<sub>36</sub> : [0-z]
*/
MID(36, "function(c){return c.toString(a)}", new MidEncoder()),
/**
* Base<sub>52</sub> : [a-Z]
*/
BASIC(52, "function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>25?String.fromCharCode(c+39):String.fromCharCode(c+97));", new BasicEncoder()),
/**
* Base<sub>62</sub> : [0-Z]
*/
NORMAL(62, "function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))}", new NormalEncoder()),
/**
* Base<sub>95</sub> : [¡-ÿ]
*/
HIGH_ASCII(95, "function(c){return(c<a?\"\":e(c/a))String.fromCharCode(c%a+161)}", new HighAsciiEncoder());
private final int encodingBase;
private final String encode;
private Encoder encoder;
JPackerEncoding(int encodingBase, String encode, Encoder encoder) {
this.encodingBase = encodingBase;
this.encode = encode;
this.encoder = encoder;
}
public int getEncodingBase() {
return encodingBase;
}
public String getEncode() {
return encode;
}
public Encoder getEncoder() {
return encoder;
}
}

View File

@@ -0,0 +1,329 @@
/**
* Packer version 3.0 (final)
* Copyright 2004-2007, Dean Edwards
* Web: {@link http://dean.edwards.name/}
*
* This software is licensed under the MIT license
* Web: {@link http://www.opensource.org/licenses/mit-license}
*
* Ported to Java by Pablo Santiago based on C# version by Jesse Hansen, <twindagger2k @ msn.com>
* Web: {@link http://jpacker.googlecode.com/}
* Email: <pablo.santiago @ gmail.com>
*/
package com.jpacker;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.jpacker.encoders.BasicEncoder;
import com.jpacker.encoders.Encoder;
import com.jpacker.strategies.DefaultReplacementStrategy;
import com.jpacker.strategies.ReplacementStrategy;
/**
* Packer class.
*
* Main jPacker class that packs the script.
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class JPackerExecuter {
private JPackerEncoding encoding;
private static final String UNPACK = "eval(function(p,a,c,k,e,r){e=%5$s;if(!''.replace(/^/,String)){while(c--)r[%6$s]=k[c]"
+ "||%6$s;k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p."
+ "replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('%1$s',%2$s,%3$s,'%4$s'.split('|'),0,{}))";
/**
* Constructor
*
* @param encoding
* The encoding level for this instance
*/
public JPackerExecuter(JPackerEncoding encoding) {
setEncoding(encoding);
}
/**
* Packs the script
*
* @param script
* The script to pack
* @param minifyOnly
* True if script should only be minified and not encoded and/or
* its variables shrunk, false otherwise.
* @param shrinkVariables
* True if variables should be shrunk, false otherwise. If
* minifyOnly is true, this option has no side effect.
* @return The packed script
*/
public String pack(String script, boolean minifyOnly, boolean shrinkVariables) {
script += "\n";
script = minify(script);
if (!minifyOnly) {
if (shrinkVariables) {
script = shrinkVariables(script);
}
if (encoding != JPackerEncoding.NONE) {
script = encode(script);
}
}
return script;
}
// zero encoding - just removal of whitespace and comments
private String minify(String script) {
JPackerParser parser = new JPackerParser();
ReplacementStrategy defaultStrat = new DefaultReplacementStrategy();
// protect data
parser = addDataRegEx(parser);
script = parser.exec(script, defaultStrat);
// remove white-space
parser = addWhiteSpaceRegEx(parser);
script = parser.exec(script, defaultStrat);
// clean
parser = addCleanUpRegEx(parser);
script = parser.exec(script, defaultStrat);
// done
return script;
}
private JPackerParser addDataRegEx(JPackerParser parser) {
final String COMMENT1 = "(\\/\\/|;;;)[^\\n]*";
final String COMMENT2 = "\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/";
final String REGEX = "\\/(\\\\[\\/\\\\]|[^*\\/])(\\\\.|[^\\/\\n\\\\])*\\/[gim]*";
// Packer.CONTINUE
parser.remove("\\\\\\r?\\n");
parser.ignore("'(\\\\.|[^'\\\\])*'");
parser.ignore("\"(\\\\.|[^\"\\\\])*\"");
parser.ignore("\\/\\*@|@\\*\\/|\\/\\/@[^\\n]*\\n");
parser.replace("(" + COMMENT1 + ")\\n\\s*(" + REGEX + ")?", "\n$3");
parser.replace("(" + COMMENT2 + ")\\s*(" + REGEX + ")?", " $3");
parser.replace("([\\[\\(\\^=,{}:;&|!*?])\\s*(" + REGEX + ")", "$1$2");
return parser;
}
private JPackerParser addCleanUpRegEx(JPackerParser parser) {
parser.replace("\\(\\s*;\\s*;\\s*\\)", "(;;)");
parser.ignore("throw[};]+[};]"); // safari 1.3 bug
parser.replace(";+\\s*([};])", "$1");
parser.remove(";;[^\\n\\r]+[\\n\\r]");
return parser;
}
private JPackerParser addWhiteSpaceRegEx(JPackerParser parser) {
parser.replace("(\\d)\\s+(\\.\\s*[a-z\\$_\\[\\(])", "$1 $2");
parser.replace("([+\\-])\\s+([+\\-])", "$1 $2");
parser.replace("(\\b|\\$)\\s+(\\b|\\$)", "$1 $2");
parser.replace("\\b\\s+\\$\\s+\\b", " $ ");
parser.replace("\\$\\s+\\b", "$ ");
parser.replace("\\b\\s+\\$", " $");
parser.replace("\\b\\s+\\b", " ");
parser.remove("\\s+");
return parser;
}
private String shrinkVariables(String script) {
final Pattern pattern = Pattern.compile("^[^'\"]\\/");
// identify blocks, particularly identify function blocks (which define
// scope)
Pattern blockPattern = Pattern.compile("(function\\s*[\\w$]*\\s*\\(\\s*([^\\)]*)\\s*\\)\\s*)?(\\{([^{}]*)\\})");
List<String> blocks = new ArrayList<String>(); // store program blocks
// (anything between
// braces {})
final List<String> data = new ArrayList<String>(); // encoded strings
// and regular
// expressions
JPackerParser parser = new JPackerParser();
parser = addDataRegEx(parser);
script = parser.exec(script, new ReplacementStrategy() {
@Override
public String replace(List<JPackerPattern> patterns, Matcher matcher) {
String replacement = "#" + data.size();
String string = matcher.group();
if (pattern.matcher(string).find()) {
replacement = string.charAt(0) + replacement;
string = string.substring(1);
}
data.add(string);
return replacement;
}
});
do {
// put the blocks back
Matcher blockMatcher = blockPattern.matcher(script);
StringBuffer sb = new StringBuffer();
while (blockMatcher.find()) {
blockMatcher.appendReplacement(sb, encodeBlock(blockMatcher, blocks));
}
blockMatcher.appendTail(sb);
script = sb.toString();
} while (blockPattern.matcher(script).find());
while (Pattern.compile("~(\\d+)~").matcher(script).find()) {
script = decodeBlock(script, blocks);
}
// put strings and regular expressions back
Matcher storeMatcher = Pattern.compile("#(\\d+)").matcher(script);
StringBuffer sb2 = new StringBuffer();
while (storeMatcher.find()) {
int num = Integer.parseInt(storeMatcher.group(1));
storeMatcher.appendReplacement(sb2, Matcher.quoteReplacement(data.get(num)));
}
storeMatcher.appendTail(sb2);
return sb2.toString();
}
private String encode(String script) {
JPackerWords words = new JPackerWords(script, encoding);
Pattern wordsPattern = Pattern.compile("\\w+");
Matcher wordsMatcher = wordsPattern.matcher(script);
StringBuffer sb = new StringBuffer();
while (wordsMatcher.find()) {
JPackerWord tempWord = new JPackerWord(wordsMatcher.group());
wordsMatcher.appendReplacement(sb, words.find(tempWord).getEncoded());
}
wordsMatcher.appendTail(sb);
int ascii = Math.min(Math.max(words.getWords().size(), 2), encoding.getEncodingBase());
String p = escape(sb.toString());
String a = String.valueOf(ascii);
String c = String.valueOf(words.getWords().size());
String k = words.toString();
String e = getEncode(ascii);
String r = ascii > 10 ? "e(c)" : "c";
return new Formatter().format(UNPACK, p, a, c, k, e, r).toString();
}
// encoder for program blocks
private String encodeBlock(Matcher matcher, List<String> blocks) {
String block = matcher.group();
String func = matcher.group(1);
String args = matcher.group(2);
if (func != null && !func.isEmpty()) { // the block is a function block
// decode the function block (THIS IS THE IMPORTANT BIT)
// We are retrieving all sub-blocks and will re-parse them in light
// of newly shrunk variables
while (Pattern.compile("~(\\d+)~").matcher(block).find()) {
block = decodeBlock(block, blocks);
}
// create the list of variable and argument names
Pattern varNamePattern = Pattern.compile("var\\s+[\\w$]+");
Matcher varNameMatcher = varNamePattern.matcher(block);
StringBuilder sb = new StringBuilder();
while (varNameMatcher.find()) {
sb.append(varNameMatcher.group()).append(",");
}
String vars = "";
if (!sb.toString().isEmpty()) {
vars = sb.deleteCharAt(sb.length() - 1).toString().replaceAll("var\\s+", "");
}
String[] ids = concat(args.split("\\s*,\\s*"), vars.split("\\s*,\\s*"));
Set<String> idList = new LinkedHashSet<String>();
for (String s : ids) {
if (!s.isEmpty()) {
idList.add(s);
}
}
// process each identifier
int count = 0;
String shortId;
for (String id : idList) {
id = id.trim();
if (id.length() > 1) { // > 1 char
id = Matcher.quoteReplacement(id);
// find the next free short name (check everything in the
// current scope)
Encoder e = new BasicEncoder();
do {
shortId = e.encode(count++);
} while (Pattern.compile("[^\\w$.]" + shortId + "[^\\w$:]").matcher(block).find());
// replace the long name with the short name
while (Pattern.compile("([^\\w$.])" + id + "([^\\w$:])").matcher(block).find()) {
block = block.replaceAll("([^\\w$.])" + id + "([^\\w$:])", "$1" + shortId + "$2");
}
block = block.replaceAll("([^{,\\w$.])" + id + ":", "$1" + shortId + ":");
}
}
}
String replacement = "~" + blocks.size() + "~";
blocks.add(block);
return replacement;
}
private String decodeBlock(String block, List<String> blocks) {
Matcher encoded = Pattern.compile("~(\\d+)~").matcher(block);
StringBuffer sbe = new StringBuffer();
while (encoded.find()) {
int num = Integer.parseInt(encoded.group(1));
encoded.appendReplacement(sbe, Matcher.quoteReplacement(blocks.get(num)));
}
encoded.appendTail(sbe);
return sbe.toString();
}
private String[] concat(String[] a, String[] b) {
String[] c = new String[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
private String getEncode(int ascii) {
if (ascii > 96) {
return JPackerEncoding.HIGH_ASCII.getEncode();
} else if (ascii > 36) {
return JPackerEncoding.NORMAL.getEncode();
} else if (ascii > 10) {
return JPackerEncoding.MID.getEncode();
} else {
return JPackerEncoding.NUMERIC.getEncode();
}
}
private String escape(String input) {
// single quotes wrap the final string so escape them
// also escape new lines required by conditional comments
return input.replaceAll("([\\\\'])", "\\\\$1").replaceAll("[\\r\\n]+", "\\n");
}
/**
* Encoding level. Options are: {@link JPackerEncoding#NONE},
* {@link JPackerEncoding#NUMERIC}, {@link JPackerEncoding#MID},
* {@link JPackerEncoding#NORMAL} and {@link JPackerEncoding#HIGH_ASCII}.
*
* @return The current encoding level
*/
public JPackerEncoding getEncoding() {
return encoding;
}
/**
* Set the encoding level to use.
*
* @param encoding
*/
public final void setEncoding(JPackerEncoding encoding) {
this.encoding = encoding;
}
}

View File

@@ -0,0 +1,182 @@
/**
* Packer version 3.0 (final)
* Copyright 2004-2007, Dean Edwards
* Web: {@link http://dean.edwards.name/}
*
* This software is licensed under the MIT license
* Web: {@link http://www.opensource.org/licenses/mit-license}
*
* Ported to Java by Pablo Santiago based on C# version by Jesse Hansen, <twindagger2k @ msn.com>
* Web: {@link http://jpacker.googlecode.com/}
* Email: <pablo.santiago @ gmail.com>
*/
package com.jpacker;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.jpacker.evaluators.DeleteEvaluator;
import com.jpacker.evaluators.Evaluator;
import com.jpacker.evaluators.IntegerEvaluator;
import com.jpacker.evaluators.StringEvaluator;
import com.jpacker.strategies.DefaultReplacementStrategy;
import com.jpacker.strategies.ReplacementStrategy;
/**
* Parser class that matches RegGrp.js.
*
* This class parses the script using the expressions added via
* {@link #remove(String)}, {@link #ignore(String)},
* {@link #replace(String,String)} and {@link #replace(String,Evaluator)}
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class JPackerParser {
private static Pattern GROUPS = Pattern.compile("\\(");
private static Pattern SUB_REPLACE = Pattern.compile("\\$(\\d+)");
private static Pattern INDEXED = Pattern.compile("^\\$\\d+$");
private static Pattern ESCAPE = Pattern.compile("\\\\.");
private static Pattern ESCAPE_BRACKETS = Pattern.compile("\\(\\?[:=!]|\\[[^\\]]+\\]");
private static Pattern DELETED = Pattern.compile("\\x01[^\\x01]*\\x01");
private static String IGNORE = "$0";
private List<JPackerPattern> jpatterns = new ArrayList<JPackerPattern>();
/**
* Add an expression to be removed
*
* @param expression
* Regular expression {@link String}
*/
public void remove(String expression) {
replace(expression, "");
}
/**
* Add an expression to be ignored
*
* @param expression
* Regular expression {@link String}
*/
public void ignore(String expression) {
replace(expression, IGNORE);
}
/**
* Add an expression to be replaced with the replacement string
*
* @param expression
* Regular expression {@link String}
* @param replacement
* Replacement {@link String}. Use $1, $2, etc. for groups
*/
public void replace(String expression, String replacement) {
if (replacement.isEmpty()) {
replace(expression, new DeleteEvaluator());
return;
}
Evaluator evaluator;
// does the pattern deal with sub-expressions? and a simple lookup (e.g. $2)
if (SUB_REPLACE.matcher(replacement).matches() && INDEXED.matcher(replacement).matches()) {
evaluator = new IntegerEvaluator(Integer.parseInt(replacement.substring(1)));
} else {
evaluator = new StringEvaluator(replacement);
}
JPackerPattern jpattern = new JPackerPattern(expression, evaluator);
// count the number of sub-expressions
jpattern.setLength(countSubExpressions(expression));
jpatterns.add(jpattern);
}
/**
* Add an expression to be replaced using an {@link Evaluator} object
*
* @param expression
* Regular expression String
* @param evaluator
* The {@link Evaluator} object
*/
public void replace(String expression, Evaluator evaluator) {
JPackerPattern jpattern = new JPackerPattern(expression, evaluator);
// count the number of sub-expressions
jpattern.setLength(countSubExpressions(expression));
jpatterns.add(jpattern);
}
// builds the patterns into a single regular expression
private Pattern buildPatterns() {
StringBuilder rtrn = new StringBuilder();
for (JPackerPattern jpattern : jpatterns) {
rtrn.append(jpattern).append("|");
}
rtrn.deleteCharAt(rtrn.length() - 1);
return Pattern.compile(rtrn.toString());
}
/**
* Executes the parser in order to parse the script with the expressions
* added via {@link #remove(String)}, {@link #ignore(String)},
* {@link #replace(String,String)} and {@link #replace(String,Evaluator)}
*
* @param input
* The script to be parsed
* @return The parsed script
*/
public String exec(String input) {
return exec(input, new DefaultReplacementStrategy());
}
/**
* Executes the parser in order to parse the script with the expressions
* added via {@link #remove(String)}, {@link #ignore(String)},
* {@link #replace(String,String)} and {@link #replace(String,Evaluator)}.
* Using a {@link ReplacementStrategy} object, a custom replacement
* algorithm can be used.
*
* @param input
* The script to be parsed
* @param strategy
* The {@link ReplacementStrategy} object for custom replacement
* @return The parsed script
*/
public String exec(String input, ReplacementStrategy strategy) {
Matcher matcher = buildPatterns().matcher(input);
StringBuffer sb = new StringBuffer(input.length());
while (matcher.find()) {
String rep = strategy.replace(jpatterns, matcher);
if (rep != null && !rep.isEmpty()) {
rep = Matcher.quoteReplacement(rep);
}
matcher.appendReplacement(sb, rep);
}
matcher.appendTail(sb);
return DELETED.matcher(sb).replaceAll("");
}
// count the number of sub-expressions
private int countSubExpressions(String expression) {
int cont = 0;
Matcher matcher = GROUPS.matcher(internalEscape(expression));
while (matcher.find()) {
cont++;
}
// - add 1 because each group is itself a sub-expression
return cont + 1;
}
private String internalEscape(String str) {
return ESCAPE_BRACKETS.matcher(ESCAPE.matcher(str).replaceAll("")).replaceAll("");
}
/**
* The patterns added to this {@link JPackerParser} object as a {@link List}
* of {@link JPackerPattern}
*
* @return The {@link List} of {@link JPackerPattern} objects
*/
public List<JPackerPattern> getJPatterns() {
return jpatterns;
}
}

View File

@@ -0,0 +1,65 @@
/**
* Packer version 3.0 (final)
* Copyright 2004-2007, Dean Edwards
* Web: {@link http://dean.edwards.name/}
*
* This software is licensed under the MIT license
* Web: {@link http://www.opensource.org/licenses/mit-license}
*
* Ported to Java by Pablo Santiago based on C# version by Jesse Hansen, <twindagger2k @ msn.com>
* Web: {@link http://jpacker.googlecode.com/}
* Email: <pablo.santiago @ gmail.com>
*/
package com.jpacker;
import com.jpacker.evaluators.Evaluator;
/**
* Wrapper class for each pattern
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class JPackerPattern {
private String expression;
private Evaluator evaluator;
private int length;
public JPackerPattern() {
}
public JPackerPattern(String expression, Evaluator evaluator) {
this.expression = expression;
this.evaluator = evaluator;
evaluator.setJPattern(this);
}
public String getExpression() {
return expression;
}
public void setExpression(String expression) {
this.expression = expression;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public Evaluator getEvaluator() {
return evaluator;
}
public void setEvaluator(Evaluator evaluator) {
this.evaluator = evaluator;
}
@Override
public String toString() {
return "(" + expression + ")";
}
}

View File

@@ -0,0 +1,98 @@
/**
* Packer version 3.0 (final)
* Copyright 2004-2007, Dean Edwards
* Web: {@link http://dean.edwards.name/}
*
* This software is licensed under the MIT license
* Web: {@link http://www.opensource.org/licenses/mit-license}
*
* Ported to Java by Pablo Santiago based on C# version by Jesse Hansen, <twindagger2k @ msn.com>
* Web: {@link http://jpacker.googlecode.com/}
* Email: <pablo.santiago @ gmail.com>
*/
package com.jpacker;
/**
* Wrapper class for a keyword
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public class JPackerWord {
private int count = 0;
private String encoded = "";
private int index = -1;
private String word;
private String replacement;
public JPackerWord(String word) {
this.word = word;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public String getEncoded() {
return encoded;
}
public void setEncoded(String encoded) {
this.encoded = encoded;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
public String getReplacement() {
return replacement;
}
public void setReplacement(String replacement) {
this.replacement = replacement;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final JPackerWord other = (JPackerWord) obj;
if ((this.word == null) ? (other.word != null) : !this.word.equals(other.word)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 37 * hash + (this.word != null ? this.word.hashCode() : 0);
return hash;
}
@Override
public String toString() {
return word;
}
}

View File

@@ -0,0 +1,149 @@
/**
* Packer version 3.0 (final)
* Copyright 2004-2007, Dean Edwards
* Web: {@link http://dean.edwards.name/}
*
* This software is licensed under the MIT license
* Web: {@link http://www.opensource.org/licenses/mit-license}
*
* Ported to Java by Pablo Santiago based on C# version by Jesse Hansen, <twindagger2k @ msn.com>
* Web: {@link http://jpacker.googlecode.com/}
* Email: <pablo.santiago @ gmail.com>
*/
package com.jpacker;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Wrapper class for a {@link JPackerWord} list built based on script's keywords (later
* wrapped into a JPackerWord list)
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*/
public final class JPackerWords {
private JPackerEncoding encoding;
private static final Pattern WORDS = Pattern.compile("\\w+");
private List<JPackerWord> words = new ArrayList<JPackerWord>();
/**
* Constructor
*
* @param script
* The input script to look up for keywords
* @param encoding
* The encoding level to use
*/
public JPackerWords(String script, JPackerEncoding encoding) {
this.encoding = encoding;
Matcher matcher = WORDS.matcher(script);
while (matcher.find()) {
add(new JPackerWord(matcher.group()));
}
encode();
}
private void add(JPackerWord word) {
if (!words.contains(word)) {
words.add(word);
}
JPackerWord w = find(word);
w.setCount(w.getCount() + 1);
}
private void encode() {
// sort by frequency
Collections.sort(words, new Comparator<JPackerWord>() {
@Override
public int compare(JPackerWord x, JPackerWord y) {
return y.getCount() - x.getCount();
}
});
// a dictionary of encoding base -> base10
Map<String, Integer> encoded = new HashMap<String, Integer>();
for (int i = 0; i < words.size(); i++) {
encoded.put(encoding.getEncoder().encode(i), i);
}
int index = 0;
for (JPackerWord word : words) {
if (encoded.containsKey(word.getWord())) {
word.setIndex(encoded.get(word.getWord()));
word.setReplacement("");
} else {
while (words.contains(new JPackerWord(encoding.getEncoder().encode(index)))) {
index++;
}
word.setIndex(index++);
word.setReplacement(word.getWord());
}
word.setEncoded(encoding.getEncoder().encode(word.getIndex()));
}
// sort by encoding
Collections.sort(words, new Comparator<JPackerWord>() {
@Override
public int compare(JPackerWord x, JPackerWord y) {
return x.getIndex() - y.getIndex();
}
});
}
/**
* Find a word in the JPackerWord list
*
* @param word
* The JPackerWord object to find in the list
* @return The JPackerWord object if found, null otherwise
*/
public JPackerWord find(JPackerWord word) {
Iterator<JPackerWord> it = words.iterator();
while (it.hasNext() == true) {
JPackerWord pw = it.next();
if (pw.equals(word)) {
return pw;
}
}
return null;
}
/**
* Gets the list of JPackerWord objects
*
* @return The list of JPackerWord objects
*/
public List<JPackerWord> getWords() {
return words;
}
/**
* This method has been overridden to return the list of JPackerWord objects
* as a single String object separated by the '|' character
*
* @return A List of JPackerWord objects as a single String object separated
* by the '|' character
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (JPackerWord word : words) {
sb.append(word.getReplacement()).append('|');
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
}

View File

@@ -0,0 +1,27 @@
/**
* Packer version 3.0 (final)
* Copyright 2004-2007, Dean Edwards
* Web: {@link http://dean.edwards.name/}
*
* This software is licensed under the MIT license
* Web: {@link http://www.opensource.org/licenses/mit-license}
*
* Ported to Java by Pablo Santiago based on C# version by Jesse Hansen, <twindagger2k @ msn.com>
* Web: {@link http://jpacker.googlecode.com/}
* Email: <pablo.santiago @ gmail.com>
*/
package com.jpacker.encoders;
/**
* Basic (base52) encoder: [a-Z]
*
* @author Pablo Santiago <pablo.santiago @ gmail.com>
*
*/
public class BasicEncoder implements Encoder {
@Override
public String encode(int c) {
return (c < 52 ? "" : encode(c / 52)) + (((c = c % 52)) > 25 ? String.valueOf((char) (c + 39)) : String.valueOf((char) (c + 97)));
}
}

Some files were not shown because too many files have changed in this diff Show More