%% %% e3d__tif.erl -- %% %% Functions for reading and writing TIF files. %% %% Copyright (c) 2001 Dan Gudmundsson %% %% See the file "license.terms" for information on usage and redistribution %% of this file, and for a DISCLAIMER OF ALL WARRANTIES. %% %% $Id: e3d__tif.erl,v 1.14 2004/10/07 08:32:00 dgud Exp $ %% -module(e3d__tif). -export([load/2,save/3,save_bin/2]). -export([format_error/1]). -include("e3d_image.hrl"). format_error(unsupported_format) -> "Unsupported format or bad TIFF file"; format_error({none,?MODULE,{tif_decode,_}}) -> "Decoding error"; format_error({unsupported_compression,Comp}) -> io_lib:format("Unsupported compression type (~p)", [Comp]). load(FileName, _Opts) -> case catch load1(FileName,_Opts) of {'EXIT', Reason} -> {error, {?MODULE,Reason}}; Else -> Else end. load1(FileName, _Opts) -> case file:read_file(FileName) of {ok, Orig = <<16#4949:16, 42:16/little, IFDOffset:32/little, _Rest/binary>>} -> %% io:format("Little tiff file~n", []), IFDs = getIFDs(little, IFDOffset, Orig), load_image(little, hd(IFDs), Orig); {ok, Orig = <<16#4D4D:16, 42:16, IFDOffset:32/big, _Rest/binary>>} -> IFDs = getIFDs(big, IFDOffset, Orig), % io:format("Big tiff file ~p ofs: ~p ~n ~p ~n", % [size(Orig), IFDOffset, IFDs]), load_image(big, hd(IFDs), Orig); {ok, _Bin} -> {error, {none,?MODULE,unsupported_format}}; Error -> Error end. save(Image, Filename, Opts) -> do_save(Image, Filename, Opts). save_bin(Image, Opts) -> do_save(Image, binary, Opts). do_save(Image, Name, Opts) -> Self = self(), Saver = spawn(fun() -> save(Self, Image, Name, Opts) end), receive {Saver,save,Res} -> Res end. save(Father, Image, Name, Opts) -> Father ! {self(),save,(catch save_1(Image, Name, Opts))}. save_1(Image0, Name, Opts) -> if Image0#e3d_image.bytes_pp == 4 -> Image = e3d_image:convert(Image0, r8g8b8a8, 1, upper_left); true -> Image = e3d_image:convert(Image0, r8g8b8, 1, upper_left) end, Compress = lists:member(compress, Opts), {Where,BinList} = save_image(Image, Compress, 8), Tiff = [<<16#4D4D:16,42:16,Where:32/big>>|BinList], if Name == binary -> {ok,list_to_binary(Tiff)}; true -> file:write_file(Name, Tiff) end. %% Some really ugly macros to get different formats.. %% !!OBS!! Binds variables or Matches them !!OBS!! -define(BIG_DIRENTRIES, _:IFDOffset/binary, DirEntries:16/big). -define(LITTLE_DIRENTRIES, _:IFDOffset/binary, DirEntries:16/little). -define(BIG_ENTRIESB_NEXT, EntriesB:EntSz/binary, NextIFD:32/big). -define(LITTLE_ENTRIESB_NEXT, EntriesB:EntSz/binary, NextIFD:32/little). getIFDs(_T, 0, _Orig) -> []; %% Previous was last IFD getIFDs(T, IFDOffset, Orig) -> if T == big -> <> = Orig; T == little -> <> = Orig end, EntSz = (12*DirEntries), if T == big -> <> = Temp; T == little -> <> = Temp end, Entries = getDirEntries(T, DirEntries, EntriesB), [Entries|getIFDs(T, NextIFD, Orig)]. -define(BIG_GETDIRENTRIES, Tag:16/big, IType:16/big, Count:32/big, ValOrOfBin:4/binary). -define(LITTLE_GETDIRENTRIES, Tag:16/little, IType:16/little, Count:32/little, ValOrOfBin:4/binary). getDirEntries(_, 0, <<>>) -> []; getDirEntries(T, N, Bin) -> if T == big -> <> = Bin; T == little -> <> = Bin end, if %% Ignore Private fields Tag >= 32768 -> %% io:format("Skipped private tag: ~w~n", [Tag]), getDirEntries(T, N-1, Next); true -> %% Not a private field read value or get pointer Type = type2type(IType), Length = typeSz(Type) * Count, ValOrOffset = if Length =< 4 -> Value = getdata(T, Type, Count, ValOrOfBin), % io:format("Tag ~p, ~p~n", % [{Tag,Type, Count, Value}, ValOrOfBin]), {value, Value}; true -> if T == big -> <> = ValOrOfBin; T == little -> <> = ValOrOfBin end, {offset, Offset} end, [{Tag, Type, Count, ValOrOffset} | getDirEntries(T, N-1, Next)] end. -define(NewSubFileType, 254). %%% Subfile contents %% Not Supported -define(ImageWidth, 256). %%% No of pixels per row -define(ImageLength, 257). %%% No of rows -define(BitsPerSample, 258). %%% -define(Compression, 259). %%% 1 No Comp; 2 CCITT G3 1-D Modified Huffman RLE %%% 5 Extension ; 32773 PackBits -define(DocumentName, 269). %%% Skipped -define(ImageDescription, 270). %%% Skipped -define(Predictor, 317). %%% 1 No differencing (default) %%% 2 Horizontal differencing -define(PhotoMetricInt, 262). %%% 0 WhiteisZero;1 BlackIsZero;2 RGB;3 RGB ColMap -define(StripOffsets, 273). %% Pointer to each chunk -define(SamplesPerPixel, 277). %% -define(RowsPerStrip, 278). %% No Rows in each chunk -define(StripByteCounts, 279). %% No of bytes in each strip after compression -define(XResolution, 282). %% -define(YResolution, 283). %% -define(ResolutionUnit, 296). %% 1; 2 Inch; 3 cm %% Optional -define(Orientation, 274). -define(PlanarConf, 284). -define(ColorMap, 320). -define(ExtraSamples, 338). -record(tif, {w,h,type,order = upper_left, bpp, bps, spp, rps, so, sbc, map,comp, pred}). load_image(Enc, IFDs, Orig) -> Tif = get_info(IFDs, #tif{}, Orig, Enc), %% io:format("IFD ~p ~nFileSize ~p ~n ~p", %% [IFDs, size(Orig), Tif]), Strips = get_strips(Tif#tif.so, Tif#tif.sbc, Orig, Enc, []), Size = Tif#tif.w * Tif#tif.h * (Tif#tif.bpp div 8), % io:format("Tif size ~w ~w ~w ~n", % [Size, {Tif#tif.w, Tif#tif.h,Tif#tif.bpp, % Tif#tif.order, Tif#tif.rps}, % {Tif#tif.type,Tif#tif.bps, Tif#tif.spp, length(Strips)}]), #tif{type=Type, bpp=Bpp} = Tif, case decompress(Strips, Tif#tif.comp, Tif#tif.h, Tif, []) of BinImage = <> -> {ResType,Image2} = if Bpp == 1 -> Map = if Type == bw -> {0,255}; Type == wb -> {255,0} end, Image1 = convert1(size(BinImage), BinImage, Map, []), PaddL = Tif#tif.w + e3d_image:pad_len(Tif#tif.w, 8), Image = strip(Tif#tif.h, Image1, Tif#tif.w, PaddL, []), {g8, Image}; Type == bw, Bpp == 8 -> {g8, Image0}; Type == alpha, Bpp == 8 -> {a8, Image0}; Type == maprgb -> Image = convert(size(Image0), Image0, Tif#tif.map, []), {r8g8b8, Image}; Type == rgb, Bpp > 32, Tif#tif.spp > 3 -> Image = remove_extra_samples(3,(Tif#tif.bpp-24),Image0,[]), {r8g8b8, 3, Image}; Type == rgb, Bpp > 16 -> {e3d_type(Bpp,nil), Image0}; true -> exit(unsupported_format) end, #e3d_image{width = Tif#tif.w, height = Tif#tif.h, alignment = 1,%% Correct ?? image = Image2, order = Tif#tif.order, type = ResType, bytes_pp = e3d_image:bytes_pp(ResType)}; {error,_}=Error -> Error end. remove_extra_samples(_RGBits, _DiscardBits, <<>>, Acc) -> list_to_binary(lists:reverse(Acc)); remove_extra_samples(RGBits, DiscardBits, Image, Acc) -> <> = Image, remove_extra_samples(RGBits, DiscardBits, Rest, [Keep|Acc]). e3d_type(Bpp,Alpha) -> case Bpp of 8 when Alpha == 8 -> a8; 8 -> g8; 16 when Alpha == 0 -> r8g8b8; 16 when Alpha > 0 -> r8g8b8a8; 24 -> r8g8b8; 32 -> r8g8b8a8; Exit -> exit({unsupported_format, Exit}) end. save_image(Image, Compress, Offset1) -> W = <> , H = <>, BPS = <> , BPSBin = %% Should be placed on Offset1 if Image#e3d_image.type == r8g8b8 -> <<8:16, 8:16, 8:16, 0:16>> ; %% Extra word alignment Image#e3d_image.type == r8g8b8a8 -> <<8:16, 8:16, 8:16, 8:16>> end, Offset2 = Offset1 + 8, {Comp, Strips} = if Compress == true -> {<>, lzw_compress(Image)}; Compress == false -> {<>, Image#e3d_image.image} end, Photo = <> , STO = <> , %% Strips = %% should be placed at Offset2 StripPadd = case size(Strips) rem 4 of 0 -> <<>> ; I -> PaddSz = (4-I)*8, %% word align Padd = <<0:PaddSz>> , Padd end, Offset3 = Offset2 + size(Strips) + size(StripPadd), SPP = <>, RPS = <>, STBC = <>, %% I don't know what to put here but they are requried fields so I add %% some stolen values.. XRes = <> , XResBin = %% should be placed Offset3 <<314572800:32, 262144:32>>, Offset4 = Offset3 + 8, YRes = <> , YResBin = %% should be placed Offset4 <<314572800:32, 262144:32>>, Offset5 = Offset4 + 8, ResU = <> , EndOfBlock = <<0:32>> , Bin = [BPSBin,Strips, StripPadd, XResBin, YResBin, %% Pointer Blocks <<12:16>>, %% No of IFD entries Offset5 should point here W, H, BPS, Comp, Photo, STO, SPP, RPS, STBC, XRes, YRes, ResU, EndOfBlock], {Offset5, Bin}. get_info([], Tif, _Orig,_Enc) -> Tif; get_info([{?PhotoMetricInt, _, 1, {value, Val}}| R], Tif, Orig,Enc) -> Type = case Val of 1 -> bw; 2 -> rgb; 3 -> maprgb; 4 -> alpha; _ -> exit(unsupported_format) end, get_info(R, Tif#tif{type=Type}, Orig,Enc); get_info([{?ImageWidth, _, 1, {value, W}}|R], Tif, Orig,Enc) -> get_info(R, Tif#tif{w = W}, Orig,Enc); get_info([{?ImageLength, _, 1, {value, H}}|R], Tif, Orig,Enc) -> get_info(R, Tif#tif{h = H}, Orig,Enc); get_info([{?BitsPerSample, short, 1, {value, Bpp}}|R], Tif, Orig,Enc) -> get_info(R, Tif#tif{bps = [Bpp]}, Orig,Enc); get_info([{?BitsPerSample, Type = short, Count, {offset, Off}}|R], Tif, Orig,Enc) -> Len = typeSz(Type) * Count, <<_:Off/binary, BPS:Len/binary, _/binary>> = Orig, Bpp = getdata(Enc, Type, Count, BPS), % case getdata(Enc, Type, Count, BPS) of % Bps = [8,8,8|_] -> Bps; % Err -> % io:format("~p: Unsupported BitsPerSample ~p ~n", [?MODULE, Err]), % erlang:fault({?MODULE, unsupported, bitsPerSample}) % end, get_info(R, Tif#tif{bps = Bpp}, Orig,Enc); get_info([{?SamplesPerPixel, _, 1, {value, SPP}}|R], Tif, Orig,Enc) -> SPP = length(Tif#tif.bps), %% Assert get_info(R, Tif#tif{spp = SPP, bpp = lists:sum(Tif#tif.bps)}, Orig,Enc); get_info([{?ColorMap, Type = short, Count, {offset, Off}}|R], Tif, Orig,Enc) -> Len = typeSz(Type) * Count, <<_:Off/binary, MapBin:Len/binary, _/binary>> = Orig, Map = get_map(MapBin, Enc), get_info(R, Tif#tif{map=Map}, Orig, Enc); get_info([{?ExtraSamples, Type = short, Count, {offset, Off}}|R], Tif, Orig,Enc) -> Len = typeSz(Type) * Count, <<_:Off/binary, Data:Len/binary, _/binary>> = Orig, _What = getdata(Enc, short, Count, Data), %% io:format("Tif Extra Samples ~p~n", [What]), get_info(R, Tif, Orig, Enc); get_info([{?Compression, _, 1, {value, Comp}}|R], Tif, Orig,Enc) -> %% io:format("Compression ~p ~n", [Comp]), get_info(R, Tif#tif{comp = Comp}, Orig,Enc); get_info([{?Predictor, _, 1, {value, Pred}}|R], Tif, Orig,Enc) -> %% io:format("Predictor ~p ~n", [Pred]), get_info(R, Tif#tif{pred = Pred}, Orig,Enc); get_info([{?StripOffsets, Type = long, Count, Where}|R], Tif, Orig,Enc) -> Sofs = case Where of {value, Offset} when Count == 1 -> [Offset]; {offset, Offset} -> %% Offset to Offsets Len = typeSz(Type) * Count, <<_:Offset/binary, OFSB:Len/binary, _/binary>> = Orig, getdata(Enc, Type, Count, OFSB) end, get_info(R, Tif#tif{so = Sofs}, Orig,Enc); get_info([{?RowsPerStrip, _Type, 1, {value, Rows}}|R], Tif, Orig,Enc) -> get_info(R, Tif#tif{rps = Rows}, Orig,Enc); get_info([{?StripByteCounts, Type, Count, What}|R], Tif, Orig,Enc) -> SBCS = case What of {value, Length} when Count == 1 -> [Length]; {offset, Offset} -> %% Offset to Offsets Len = typeSz(Type) * Count, <<_:Offset/binary, OFSB:Len/binary, _/binary>> = Orig, getdata(Enc, Type, Count, OFSB) end, get_info(R, Tif#tif{sbc = SBCS}, Orig,Enc); get_info([{?Orientation, _Type, 1, {value, Orient}}|R], Tif, Orig,Enc) -> Order = case Orient of 1 -> upper_left; 2 -> upper_right; 3 -> lower_rigth; 4 -> lower_left; Err -> io:format("~p: Unsupported orientation ~p ~n", [?MODULE, Err]), exit({orientation, Err}) end, get_info(R, Tif#tif{order = Order}, Orig,Enc); get_info([{?PlanarConf, _Type, 1, {value, 1}}|R], Tif, Orig,Enc) -> get_info(R, Tif, Orig,Enc); %% SKIP these get_info([{?XResolution, _, _, {offset, _W}}|R], Tif, Orig,Enc) -> %SKIP % <<_:W/binary, BPS:8/binary, _/binary>> = Orig, % What = getdata(Enc, rational, 1, BPS), % io:format("XREs ~p~n", [What]), get_info(R, Tif, Orig,Enc); get_info([{?YResolution, _, _, {offset,_W}}|R], Tif, Orig,Enc) -> %SKIP get_info(R, Tif, Orig,Enc); get_info([{?ResolutionUnit, _, _, _}|R], Tif, Orig,Enc) -> %SKIP get_info(R, Tif, Orig,Enc); get_info([{?NewSubFileType, _, _, _}|R], Tif, Orig,Enc) -> %% Skip it we don't support subfile contents anyway. get_info(R, Tif, Orig,Enc); get_info([{?DocumentName, _, _, _}|R], Tif, Orig,Enc) -> %% Skip get_info(R, Tif, Orig,Enc); get_info([{?ImageDescription, _, _, _}|R], Tif, Orig,Enc) -> %% Skip get_info(R, Tif, Orig,Enc); get_info([Err|R], Tif, Orig,Enc) -> io:format("~p: Unsupported TAG ~p ~n", [?MODULE, Err]), get_info(R, Tif, Orig,Enc). get_strips([], [], _Orig, _Enc, Acc) -> lists:reverse(Acc); get_strips([StripOff|R1], [SBC|R2], Orig, Enc, Acc) -> case Orig of <<_:StripOff/binary, Strip:SBC/binary, _/binary>> -> get_strips(R1, R2, Orig, Enc, [Strip|Acc]) end. -define(c16to8(C), round(((C) *255) / ((1 bsl 16) -1))). %% Color Map get_map(Bin,Enc) -> Sz = size(Bin) div 3, <> = Bin, get_map(Sz,R,G,B,Enc,[]). get_map(0,_,_, _, _Enc,Acc) -> list_to_tuple(Acc); get_map(I,RB,GB,BB,little,Acc) -> Prev = I-2, <<_:Prev/binary,R:16/little,_/binary>> = RB, <<_:Prev/binary,G:16/little,_/binary>> = GB, <<_:Prev/binary,B:16/little,_/binary>> = BB, get_map(Prev,RB,GB,BB,little,[[?c16to8(R),?c16to8(G),?c16to8(B)]|Acc]); get_map(I,RB,GB,BB,big,Acc) -> Prev = I-2, <<_:Prev/binary,R:16/big,_/binary>> = RB, <<_:Prev/binary,G:16/big,_/binary>> = GB, <<_:Prev/binary,B:16/big,_/binary>> = BB, get_map(Prev,RB,GB,BB,big,[[?c16to8(R),?c16to8(G),?c16to8(B)]|Acc]). convert(0,_, _, Acc) -> list_to_binary(Acc); convert(I, Image, Map, Acc) -> Prev = I-1, <<_:Prev/binary,Index:8,_/binary>> = Image, Col = element(Index+1, Map), convert(Prev,Image, Map, [Col|Acc]). convert1(0,_, _, Acc) -> list_to_binary(Acc); convert1(N, Image, Map, Acc) -> Prev = N-1, <<_:Prev/binary,I1:1,I2:1,I3:1,I4:1,I5:1,I6:1,I7:1,I8:1,_/binary>> = Image, Col1 = element(I1+1, Map), Col2 = element(I2+1, Map), Col3 = element(I3+1, Map), Col4 = element(I4+1, Map), Col5 = element(I5+1, Map), Col6 = element(I6+1, Map), Col7 = element(I7+1, Map), Col8 = element(I8+1, Map), convert1(Prev,Image, Map, [Col1,Col2,Col3,Col4,Col5,Col6,Col7,Col8|Acc]). strip(0, _Image, _RowL, _PaddL, Acc) -> list_to_binary(Acc); strip(Row, Image, RowL, PaddL, Acc) -> Skip = (Row-1)*PaddL, <<_:Skip/binary, RowBin:RowL/binary, _/binary>> = Image, strip(Row-1, Image, RowL, PaddL, [RowBin|Acc]). %% Compression -define(LZW_CLEAR, 256). -define(LZW_EOI, 257). -define(LZW_FIRST, 258). -define(LZW_STARTBITLEN, 9). -define(LZW_SWAP_9, 510). -define(LZW_SWAP_10, 1022). -define(LZW_SWAP_11, 2046). -define(LZW_MAX, 4094). -define(get_lzw(Code), get(Code)). -define(add_lzw(Code, Str), put(Code, Str)). decompress(RevStrips, 1, _, _, _) -> %% No Compression list_to_binary(RevStrips); decompress([CompStrip|Rest], Comp = 5, RowsLeft, Tif, Acc) -> %% LZW-Compression ReadCode = fun(BitLen, UsedBits) -> Shift = case ((UsedBits + BitLen) rem 8) of 0 -> 0; I -> 8 - I end, case CompStrip of <<_:UsedBits, Code:BitLen, _:Shift, _/binary>> -> %io:format("~p ",[Code]), {Code, UsedBits + BitLen}; _ -> io:format("~nEOI ~p ~p'~n",[BitLen, UsedBits]), {?LZW_EOI, size(CompStrip) * 8} end end, Decomp = lzw_decomp(0, ReadCode, 0, 258, ?LZW_STARTBITLEN, []), Differented = case Tif#tif.pred of 2 when Tif#tif.bpp == 32 -> %% Horizontal differencing undo_differencing4(0, Tif#tif.w, lists:append(lists:reverse(Decomp)), 0,0,0,0, []); 2 when Tif#tif.bpp == 24 -> %% Horizontal differencing undo_differencing3(0, Tif#tif.w, lists:append(lists:reverse(Decomp)), 0,0,0, []); _ -> %% No differencing Decomp end, %% Some pictures seem to fail to create correct size in rows per strip Rows = case RowsLeft > Tif#tif.rps of true -> Tif#tif.rps; false -> RowsLeft end, Size = Tif#tif.w * Rows * Tif#tif.bpp div 8, % io:format("Lens ~p ~p ~p ~p~n", % [Size, Rows, length(lists:flatten(Decomp)), % size(list_to_binary(Differented))]), <> = list_to_binary(lists:reverse(Differented)), decompress(Rest, Comp, RowsLeft-Rows, Tif, [StripBin|Acc]); decompress([CompStrip|Rest], Comp = 32773, RowsLeft, Tif, Acc) -> %% PackBits W = Tif#tif.w * (Tif#tif.bpp div 8), Rows = case RowsLeft > Tif#tif.rps of true -> Tif#tif.rps; false -> RowsLeft end, Bins = unpack_bits(0, W, 0, Rows, CompStrip, []), Size = Tif#tif.w * Rows * Tif#tif.bpp div 8, <> = list_to_binary(lists:reverse(Bins)), decompress(Rest, Comp, RowsLeft-Rows, Tif, [StripBin|Acc]); decompress([], _Comp, 0, _, Acc) -> list_to_binary(lists:reverse(Acc)); decompress(_RevStrips, Comp, 0, _, _Acc) -> io:format("~p: Unsupported Compression ~p ~n", [?MODULE, Comp]), {error, {none,?MODULE,{unsupported_compression,Comp}}}. undo_differencing4(W, W, Rest, _,_,_,_,Ack) -> undo_differencing4(0,W, Rest, 0,0,0,0,Ack); undo_differencing4(C, W, [R,G,B,A|Rest], AR,AG,AB,AA, Ack) -> RR = (R + AR) rem 256, RG = (G + AG) rem 256, RB = (B + AB) rem 256, RA = (A + AA) rem 256, undo_differencing4(C+1, W, Rest, RR,RG,RB,RA, [RA,RB,RG,RR|Ack]); undo_differencing4(_, _, [], _,_,_,_, Ack) -> Ack. undo_differencing3(W, W, Rest, _,_,_,Ack) -> undo_differencing3(0,W, Rest, 0,0,0,Ack); undo_differencing3(C, W, [R,G,B|Rest], AR,AG,AB, Ack) -> RR = (R + AR) rem 256, RG = (G + AG) rem 256, RB = (B + AB) rem 256, undo_differencing3(C+1, W, Rest, RR,RG,RB, [RB,RG,RR|Ack]); undo_differencing3(_, _, [], _,_,_, Ack) -> Ack. unpack_bits(0, _W, H, H, _, Acc) -> Acc; unpack_bits(WC, W, HC, H, Bin, Acc) when WC == W -> unpack_bits(0, W, HC+1, H, Bin, Acc); unpack_bits(BC, W, HC, H, <>, Acc) when BC < W -> % io:format("~p ", [{Code, BC}]), if Code == 128 -> unpack_bits(BC, W, HC, H, Rest, Acc); Code >= 0 -> Count = Code + 1, <> = Rest, unpack_bits(BC + Count, W, HC, H, Cont, [Bin|Acc]); Code < 0 -> Count = abs(Code) + 1, <> = Rest, Bin = list_to_binary(lists:duplicate(Count, Re)), unpack_bits(BC + Count, W, HC, H, Cont, [Bin|Acc]) end. %%%% Test !! % Ex = <<256:9, 7:9,258:9, 8:9,8:9,258:9,6:9, 257:9>>. % <<128,1,224,64,128,68,8,13,1>> % 99> Raw = e3d__tif:decompress([Ex], 5, []). % <<7,7,7,8,8,7,7,6>>. % e3d__tif:lzw_init_compress(Raw, size(Raw), 9, {0,[]},[]). % lzw_decomp(S, Read, PrevCode, Count, BitLen, Acc) -> case (catch Read(BitLen,S)) of {?LZW_EOI, _Where} -> % io:format("~nEOI-1 ~p ~n", [{S, PrevCode, Count, BitLen, _Where}]), Acc; {?LZW_CLEAR, NS} -> lzw_init(0), % io:format("~nClear table ~p~n", [{S, PrevCode, Count, BitLen}]), case catch Read(9, NS) of {?LZW_EOI, _} -> %% io:format("~nEOI-2 ~p ~n", [{S, PrevCode, Count, BitLen}]), Acc; {NewCode, NS2} when integer(NewCode) -> Str = ?get_lzw(NewCode), lzw_decomp(NS2, Read, NewCode, 258, 9, [Str|Acc]); Else -> io:format("~n~p: Error ~p Args: ~p ~n", [?MODULE, Else, {NS, PrevCode, Count, BitLen}]), exit({badly_compressed_data}) end; {NewCode, NS} when integer(NewCode) -> case ?get_lzw(NewCode) of undefined when Count == NewCode -> OldStr = [H|_] = ?get_lzw(PrevCode), NewStr = OldStr ++ [H], ?add_lzw(Count, NewStr), lzw_decomp(NS, Read, NewCode, Count +1, lzw_bl(Count, BitLen), [NewStr|Acc]); Str = [H|_]-> ?add_lzw(Count, ?get_lzw(PrevCode) ++ [H]), lzw_decomp(NS, Read, NewCode, Count +1, lzw_bl(Count, BitLen), [Str|Acc]); Else -> io:format("~n~p: Error Case Clause ~p ~p Args ~p ~n", [?MODULE, Else, NewCode, {S, PrevCode, Count, BitLen}]), exit({badly_compressed_data}) end; Else -> io:format("~n~p: Error ~p Args: ~p ~n", [?MODULE, Else, {S, PrevCode, Count, BitLen}]), exit({badly_compressed_data}) end. lzw_bl(?LZW_SWAP_9, Len) -> Len +1; lzw_bl(?LZW_SWAP_10, Len) -> Len +1; lzw_bl(?LZW_SWAP_11, Len) -> Len +1; lzw_bl(_, Len) -> Len. lzw_init(No) when No > 257 -> case erase(No) of undefined -> ok; _ -> lzw_init(No+1) end; lzw_init(No) -> ?add_lzw(No, [No]), lzw_init(No +1). lzw_init_comp() -> erase(), lzw_init_comp(257). lzw_init_comp(No) when No >= 0 -> ?add_lzw([No], No), lzw_init_comp(No - 1); lzw_init_comp(_) -> ok. -define(lzw_write(Aa,Ba,Ca), lzw_write(Aa,Ba,Ca)). lzw_compress(Image) -> lzw_init_compress(Image#e3d_image.image, Image#e3d_image.width * Image#e3d_image.bytes_pp, ?LZW_STARTBITLEN, {0, []}, []). lzw_init_compress(Bin, W, BitLen, Build, Acc) -> lzw_init_comp(), {NBuild, Nacc} = lzw_write({BitLen,?LZW_CLEAR}, Build, Acc), lzw_compress(Bin, 0, W, [], ?LZW_STARTBITLEN, ?LZW_FIRST, NBuild, Nacc). lzw_compress(<<>>, CC, _W, Omega, BitLen, _TabCount, Build, Acc) -> Code =?get_lzw(Omega), {NBuild, Nacc} = ?lzw_write({BitLen,Code}, Build, Acc), %% NewBL = lzw_bl(TabCount, BitLen), NewBL = BitLen, {{TotBitLen, Codes}, N2acc} = lzw_write({NewBL,?LZW_EOI}, NBuild, Nacc), PaddL = 8 - (TotBitLen rem 8), case catch lzw_buildbin(lists:reverse([{PaddL, 0}|Codes])) of Bin when binary(Bin) -> list_to_binary(lists:reverse([Bin|N2acc])); _Else -> io:format("~p:~p Error ~p ~p ~n", [?MODULE, ?LINE, {PaddL, Codes}, CC]), erlang:fault({?MODULE, decoder, {internal_error, ?LINE}}) end; lzw_compress(Bin, CC, W, Omega, BitLen, TabCount, Build, Acc) when CC == W -> Code =?get_lzw(Omega), {NBuild, Nacc} = lzw_write({BitLen,Code}, Build, Acc), lzw_init_compress(Bin, W, lzw_bl(TabCount-1,BitLen), NBuild, Nacc); lzw_compress(<>, CC, W, Omega, BitLen, TabC, Build, Acc) -> NewOmega = [Char|Omega], case ?get_lzw(NewOmega) of undefined -> Code = ?get_lzw(Omega), {NBuild, Nacc} = lzw_write({BitLen,Code}, Build, Acc), ?add_lzw(NewOmega, TabC), case TabC of ?LZW_MAX - 1 -> Code2 =?get_lzw([Char]), {NBuild2, Nacc2} = ?lzw_write({BitLen,Code2}, NBuild, Nacc), lzw_init_compress(Bin, W, lzw_bl(TabC-1,BitLen), NBuild2, Nacc2); _ -> lzw_compress(Bin, CC+1, W, [Char], lzw_bl(TabC-1, BitLen), TabC + 1, NBuild, Nacc) end; _ -> lzw_compress(Bin, CC +1, W, NewOmega, BitLen, TabC, Build, Acc) end. lzw_write({_,undefined}, _, _) -> erlang:fault({undef,value}); lzw_write({CLen, Code}, {Totlen, List}, Acc) -> NewLen = CLen + Totlen, if NewLen rem 8 == 0 -> case catch lzw_buildbin(lists:reverse([{CLen,Code}|List])) of Bin when binary(Bin) -> {{0, []}, [Bin|Acc]}; {Bin, NewList} when binary(Bin) -> Sum = lists:foldl(fun({X,_}, Sum) -> X + Sum end, 0, NewList), {{Sum, lists:reverse(NewList)}, [Bin|Acc]}; Else -> io:format("~p:~p Error ~p ~p ~n", [?MODULE, ?LINE, Else, [{CLen, Code}, {Totlen, List}]]), erlang:fault({?MODULE, decoder, {internal_error, ?LINE}}) end; NewLen > 100 -> case catch lzw_buildbin(lists:reverse([{CLen,Code}|List])) of {Bin, NewList} when binary(Bin) -> Sum = lists:foldl(fun({X,_}, Sum) -> X + Sum end, 0, NewList), {{Sum, lists:reverse(NewList)}, [Bin|Acc]}; Else -> io:format("~p:~p Error ~p ~p ~n", [?MODULE, ?LINE, Else, [{CLen, Code}, {Totlen, List}]]), erlang:fault({?MODULE, decoder, {internal_error, ?LINE}}) end; true -> {{Totlen + CLen,[{CLen,Code}|List]}, Acc} end. lzw_buildbin([{8,0}]) -> <<>>; lzw_buildbin([{L1, C1},{L2,C2}]) -> <>; lzw_buildbin([{L1,C1},{L2,C2},{L3,C3}]) -> <>; lzw_buildbin([{L1,C1},{L2,C2},{L3,C3},{L4,C4}]) -> <>; lzw_buildbin([{L1,C1},{L2,C2},{L3,C3},{L4,C4},{L5,C5}]) -> <>; lzw_buildbin([{L1,C1},{L2,C2},{L3,C3},{L4,C4},{L5,C5},{L6,C6}]) -> <>; lzw_buildbin([{L1,C1},{L2,C2},{L3,C3},{L4,C4},{L5,C5},{L6,C6},{L7,C7}]) -> <>; lzw_buildbin([{L1,C1},{L2,C2},{L3,C3},{L4,C4},{L5,C5},{L6,C6},{L7,C7},{L8,C8}]) -> <>; lzw_buildbin(_DBG = [{L1,C1},{L2,C2},{L3,C3},{L4,C4},{L5,C5},{L6,C6},{L7,C7},{L8,C8},{L9,C9} | Rest]) -> RemL = (L1+L2+L3+L4+L5+L6+L7+L8) rem 8, AddL = 8 - RemL, KeepL = L9 - AddL, SkipL = 16 - (AddL + KeepL), TempFill = 8 - (L9 rem 8), % io:format("~w~n ~p ~p ~p~n", [DBG, {AddL, KeepL}, {C9,L9}, TempFill]), <> = <>, Bin = <>, NewList = [{KeepL, Keep9}|Rest], {Bin, NewList}. %% getdata(Enc, Type, Count, Bin) getdata(Enc, Type, 1, Bin) -> [Val] = getdata2(Enc, Type, 1, Bin), Val; getdata(Enc, Type, N, Bin) -> getdata2(Enc, Type, N, Bin). getdata2(_, _, 0, _Bin) -> []; getdata2(_, byte, Count, Bin) -> <> = Bin, binary_to_list(Data); getdata2(Enc, sbyte, Count, <>) -> [V| getdata2(Enc, sbyte, Count -1, Bin)]; getdata2(_, ascii, Count, Bin) -> <> = Bin, binary_to_list(Data); getdata2(big, short, Count, <>) -> [V| getdata2(big, short, Count -1, Bin)]; getdata2(little, short, Count, <>) -> [V| getdata2(little, short, Count -1, Bin)]; getdata2(big, sshort, Count, <>) -> [V| getdata2(big, sshort, Count -1, Bin)]; getdata2(little, sshort, Count, <>) -> [V| getdata2(little, sshort, Count -1, Bin)]; getdata2(big, long, Count, <>) -> [V| getdata2(big, long, Count -1, Bin)]; getdata2(little, long, Count, <>)-> [V| getdata2(little, long, Count -1, Bin)]; getdata2(big, slong, Count, <>) -> [V| getdata2(big, slong, Count -1, Bin)]; getdata2(little, slong, Count, <>)-> [V| getdata2(little, slong, Count -1, Bin)]; getdata2(big, rational, Count, <>) -> [{V1,V2}| getdata2(big, rational, Count -1, Bin)]; getdata2(little, rational, Count, <>)-> [{V1,V2}| getdata2(little, rational, Count -1, Bin)]; getdata2(big, srational, Count, <>) -> [{V1,V2}| getdata2(big, rational, Count -1, Bin)]; getdata2(little, srational, Count, <>)-> [{V1,V2}| getdata2(little, rational, Count -1, Bin)]; getdata2(big, float, Count, <>) -> [V| getdata2(big, float, Count -1, Bin)]; getdata2(little, float, Count, <>)-> [V| getdata2(little, float, Count -1, Bin)]; getdata2(big, double, Count, <>) -> [V| getdata2(big, double, Count -1, Bin)]; getdata2(little, double, Count, <>)-> [V| getdata2(little, double, Count -1, Bin)]; getdata2(_, _, _, Bin) -> Bin. %% Conversions typeSz(Type) -> case Type of byte -> 1; ascii -> 1; short -> 2; long -> 4; rational -> 8; sbyte -> 1; undefined -> 1; sshort -> 2; slong -> 4; srational -> 8; float -> 4; double -> 4; _ -> 1 end. type2type(Id) -> case Id of 1 -> byte; 2 -> ascii; 3 -> short; 4 -> long; 5 -> rational; 6 -> sbyte; 7 -> undefined; 8 -> sshort; 9 -> slong; 10 -> srational; 11 -> float; 12 -> double; % byte -> 1; % ascii -> 2; short -> 3; long -> 4; rational -> 5; % sbyte -> 6; % undefined -> 7; % sshort -> 8; % slong -> 9; % srational -> 10; % float -> 11; % double -> 12; _ -> Id end.