1%% 2%% e3d__dds.erl -- 3%% 4%% Functions for reading .dds files. 5%% 6%% Copyright (c) 2018 Dan Gudmundsson 7%% 8%% See the file "license.terms" for information on usage and redistribution 9%% of this file, and for a DISCLAIMER OF ALL WARRANTIES. 10%% 11 12-module(e3d__dds). 13 14-export([load/2, save/3]). 15-export([format_error/1]). 16-include("e3d_image.hrl"). 17 18% debug 19-export([rgb16/1,rgb32/1,rgb16to32/1, bc1/1, bc2/1]). 20 21format_error(unsupported_format) -> 22 "Unsupported format or bad DDS file"; 23format_error({unsupported_format, _Line}) -> 24 "Unsupported format or bad DDS file"; 25format_error(bad_image_specifiction) -> 26 "Bad image specification". 27 28-define(DW, 32/little-unsigned). 29-define(PF_SIZE, (4*8)). 30 31-define(IS(Flags, Flag), ((Flags band (Flag)) > 0)). 32 33-define(ERROR(What), throw({What, ?LINE})). 34 35save(#e3d_image{type=InType}=Image0, FileName, _Opts) -> 36 Image = e3d_image:convert(Image0, InType, 1, lower_left), 37 try 38 IoData = save_1(Image), 39 file:write_file(FileName, IoData) 40 catch throw:Error -> {error, Error}; 41 _:Error:ST -> 42 io:format("~p ~p ~p~n", [?MODULE, Error, ST]), 43 {error, internal_error} 44 end. 45 46save_1(#e3d_image{width=W, height=H, type=Type, bytes_pp=Bpp, image=Img}) 47 when Bpp =:= 3; Bpp =:= 4 -> 48 Pitch = W*Bpp, 49 Flags = 16#1 bor 16#2 bor 16#4 bor 16#1000, 50 PFBin = case Type of 51 r8g8b8a8 -> 52 PFFlags = 16#1 bor 16#40, 53 RM = 16#FF, GM = 16#FF00, BM = 16#FF0000, AM = 16#FF000000, 54 <<32:?DW, PFFlags:?DW, 0:?DW, (Bpp*8):?DW, RM:?DW, GM:?DW, BM:?DW, AM:?DW>>; 55 r8g8b8 -> 56 PFFlags = 16#40, 57 RM = 16#FF, GM = 16#FF00, BM = 16#FF0000, AM = 16#FF000000, 58 <<32:?DW, PFFlags:?DW, 0:?DW, (Bpp*8):?DW, RM:?DW, GM:?DW, BM:?DW, AM:?DW>>; 59 b8g8r8a8 -> 60 PFFlags = 16#1 bor 16#40, 61 RM = 16#FF0000, GM = 16#FF00, BM = 16#FF, AM = 16#FF000000, 62 <<32:?DW, PFFlags:?DW, 0:?DW, (Bpp*8):?DW, RM:?DW, GM:?DW, BM:?DW, AM:?DW>>; 63 b8g8r8 -> 64 PFFlags = 16#40, 65 RM = 16#FF0000, GM = 16#FF00, BM = 16#FF, AM = 16#FF000000, 66 <<32:?DW, PFFlags:?DW, 0:?DW, (Bpp*8):?DW, RM:?DW, GM:?DW, BM:?DW, AM:?DW>> 67 end, 68 32 = byte_size(PFBin), 69 Header = <<"DDS ", 124:?DW, Flags:?DW, 70 H:?DW, W:?DW, Pitch:?DW, 71 0:?DW, 1:?DW, 0:(11*4*8), 72 PFBin:?PF_SIZE/binary, 73 16#1000:?DW, 0:?DW, 0:?DW, 0:?DW, 0:?DW 74 >>, 75 124 = byte_size(Header) - 4, 76 [Header, Img]. 77 78 79load(FileName, _Opts) -> 80 try case file:read_file(FileName) of 81 {ok, <<"DDS ", 124:?DW, Flags:?DW, H:?DW, W:?DW, PorLS:?DW, 82 _Depth:?DW, MipMapN:?DW, _:(11*4)/binary, 83 PFBin:?PF_SIZE/binary, 84 Caps:?DW, Caps2:?DW, _:?DW, _:?DW, _:?DW, 85 Rest/binary>>} -> 86 Im0 = #{w=>W, h=>H}, 87 Im1 = parse_pitch(Flags, PorLS, Im0), 88 Im2 = parse_pf(PFBin, Im1), 89 Im3 = parse_caps(Caps, Caps2, MipMapN, Im2), 90 {Bin, Im4} = parse_dxt10(Rest, Im3), 91 get_images(Bin, Im4); 92 Error -> 93 Error 94 end 95 catch throw:Err -> Err 96 end. 97 98parse_pitch(Flags, PitchOrLs, Im0) -> 99 if ?IS(Flags, 16#8) -> Im0#{pitch=>PitchOrLs}; 100 ?IS(Flags, 16#80000) -> Im0#{linearsz=>PitchOrLs}; 101 true -> Im0 102 end. 103 104parse_pf(<<32:?DW,Flags:?DW,FCC:4/binary,BitC:?DW,RM:?DW,GM:?DW,BM:?DW,AM:?DW>>, Im0) -> 105 if ?IS(Flags,16#2) -> ?ERROR(unsupported_format); 106 ?IS(Flags,16#200) -> ?ERROR(unsupported_format); 107 ?IS(Flags,16#20000) -> ?ERROR(unsupported_format); 108 true -> ok 109 end, 110 PF0 = if ?IS(Flags,16#1) -> Im0#{a_mask=>AM}; 111 true -> Im0 112 end, 113 PF1 = if ?IS(Flags,16#04) -> PF0#{fourCC => FCC}; 114 true -> PF0 115 end, 116 PF2 = if ?IS(Flags,16#40) -> PF1#{bytes_pp=>BitC div 8, r_mask=>RM, g_mask=>GM, b_mask=>BM}; 117 true -> PF1 118 end, 119 PF2; 120parse_pf(_, _) -> ?ERROR(unsupported_format). 121 122parse_caps(Caps1, Caps2, MipMapN, Im0) -> 123 Im = if ?IS(Caps1, 16#400000) -> Im0#{mipmaps=>MipMapN}; 124 true -> Im0#{mipmaps=>1} 125 end, 126 All = 16#400 bor 16#800 bor 16#1000 bor 16#2000 bor 16#4000 bor 16#8000, 127 if ?IS(Caps2, 16#200), ?IS(Caps2, All) -> Im#{cubemap=>true}; 128 ?IS(Caps2, 16#200) -> ?ERROR(unsupported_format); 129 true -> Im#{cubemap=>false} 130 end. 131 132parse_dxt10(<<Format:?DW, Dim:?DW, Flags1:?DW, Sz:?DW, _Flags2:?DW, Rest/binary>>, 133 #{fourCC := <<"DX10">>}=Im) -> 134 if Dim =/= 3 -> ?ERROR(unsupported_format); 135 Sz =/= 1 -> ?ERROR(unsupported_format); 136 true -> ok 137 end, 138 Cubemap = ?IS(Flags1, 16#4), 139 {Rest, Im#{fourCC:=dxgi_format(Format), cubemap=>Cubemap, count=>Sz}}; 140%% Convert old fourCC to newer format 141parse_dxt10(Rest, #{fourCC := <<"DXT", Variant:8>>}=Im) -> 142 Alg = case Variant of 143 $1 -> {bc1, uint}; 144 $2 -> {bc2, uint}; 145 $3 -> {bc2, uint}; 146 $4 -> {bc3, uint}; 147 $5 -> {bc3, uint} 148 end, 149 {Rest, Im#{fourCC:=Alg}}; 150parse_dxt10(Rest, #{fourCC := <<"ATI1">>}=Im) -> 151 {Rest, Im#{fourCC:={bc4, uint}}}; 152parse_dxt10(Rest, #{fourCC := <<"BC4U">>}=Im) -> 153 {Rest, Im#{fourCC:={bc4, uint}}}; 154parse_dxt10(Rest, #{fourCC := <<"BC4S">>}=Im) -> 155 {Rest, Im#{fourCC:={bc4, sint}}}; 156parse_dxt10(Rest, #{fourCC := <<"ATI2">>}=Im) -> 157 {Rest, Im#{fourCC:={bc5, uint}}}; 158parse_dxt10(Rest, #{fourCC := <<"BC5U">>}=Im) -> 159 {Rest, Im#{fourCC:={bc5, uint}}}; 160parse_dxt10(Rest, #{fourCC := <<"BC5S">>}=Im) -> 161 {Rest, Im#{fourCC:={bc5, sint}}}; 162%% Non-spec usage 163parse_dxt10(Rest, #{fourCC := <<"RGBG">>}=Im) -> 164 {Rest, Im#{fourCC:={r8g8_b8g8,uint}}}; 165parse_dxt10(Rest, #{fourCC := <<"GRGB">>}=Im) -> 166 {Rest, Im#{fourCC:={g8r8_g8b8,uint}}}; 167parse_dxt10(Rest, #{fourCC := <<Enum:32/unsigned-little>>}=Im) -> 168 %% From https://docs.microsoft.com/en-us/windows/uwp/gaming/complete-code-for-ddstextureloader 169 Format = case Enum of 170 36 -> %% D3DFMT_A16B16G16R16 171 dxgi_format(11); %DXGI_FORMAT_R16G16B16A16_UNORM 172 110 -> %% D3DFMT_Q16W16V16U16 173 dxgi_format(13); %DXGI_FORMAT_R16G16B16A16_SNORM; 174 111 -> %% D3DFMT_R16F 175 dxgi_format(54); % DXGI_FORMAT_R16_FLOAT; 176 112 -> %% D3DFMT_G16R16F 177 dxgi_format(34); % DXGI_FORMAT_R16G16_FLOAT; 178 113 -> %% D3DFMT_A16B16G16R16F 179 dxgi_format(11); % DXGI_FORMAT_R16G16B16A16_FLOAT; 180 114 -> %% D3DFMT_R32F 181 dxgi_format(41); % DXGI_FORMAT_R32_FLOAT; 182 115 -> %% D3DFMT_G32R32F 183 dxgi_format(16); % DXGI_FORMAT_R32G32_FLOAT; 184 116 -> %% D3DFMT_A32B32G32R32F 185 dxgi_format(2); % DXGI_FORMAT_R32G32B32A32_FLOAT; 186 _ -> 187 dxgi_format(0) 188 end, 189 {Rest, Im#{fourCC:=Format}}; 190parse_dxt10(Rest, #{fourCC := _}=Im) -> 191 {Rest, Im}; 192parse_dxt10(Rest, Im) -> 193 {Rest, Im#{fourCC=>none}}. 194 195get_images(Bin, #{w:=W, h:=H, mipmaps:=MM, cubemap:=false}=Opts) -> 196 {Type, Bpp, Div, BSz, Decomp} = comp_alg(Opts), 197 {[{Image,_,_,_}|MMs],_Pad} = get_mipmaps(Bin, W, H, Div, BSz, 0, MM-1, Decomp, []), 198 #e3d_image{width=W, height=H, bytes_pp=Bpp, type=Type, 199 order=upper_left, alignment=1, image=Image, 200 extra=add_mm(MMs)}; 201 202get_images(Bin0, #{w:=W, h:=H, mipmaps:=MM, cubemap:=true} = Opts) -> 203 {Type, Bpp, Div, BSz, Decomp} = comp_alg(Opts), 204 Get = fun(Dir, Bin) -> 205 {[{Image,_,_,_}|MMs], Rest} = get_mipmaps(Bin, W, H, Div, BSz, 0, MM-1, Decomp, []), 206 {#{dir=>Dir, tx=>Image, mipmaps=>MMs}, Rest} 207 end, 208 {CubeTx, _Pad} = lists:mapfoldl(Get, Bin0, [pos_x, neg_x, pos_y, neg_y, pos_z, neg_z]), 209 [#{dir:=pos_x,tx:=Image, mipmaps:=Mipmaps}|Rest] = CubeTx, 210 #e3d_image{width=W, height=H, bytes_pp=Bpp, type=Type, 211 order=upper_left, alignment=1, image=Image, 212 extra=[{cubemaps, Rest}|add_mm(Mipmaps)]}. 213 214get_mipmaps(Bin, W, H, Div, BSz, Level, Level, Decomp, Acc) -> 215 Sz = b_size(W, H, Div, BSz), 216 <<MM:Sz/binary, Rest/binary>> = Bin, 217 Image = Decomp(MM,W,H), 218 {lists:reverse([{Image,W,H,Level}|Acc]), Rest}; 219get_mipmaps(Bin, W, H, Div, BSz, Level, Max, Decomp, Acc) when Level < Max -> 220 Sz = b_size(W, H, Div, BSz), 221 <<MM:Sz/binary, Rest/binary>> = Bin, 222 Image = Decomp(MM,W,H), 223 get_mipmaps(Rest, max(1,W div 2), max(1,H div 2), Div, BSz, Level+1, Max, Decomp, 224 [{Image,W,H,Level}|Acc]). 225 226b_size(W, H, Div, BSz) -> 227 ((W+Div-1) div Div) * ((H+Div-1) div Div) *BSz. 228 229add_mm([]) -> []; 230add_mm(MMs) -> [{mipmaps, MMs}]. 231 232type(#{b_mask:=16#FF, a_mask:=_, bytes_pp:=4}) -> b8g8r8a8; 233type(#{r_mask:=16#FF, a_mask:=_, bytes_pp:=4}) -> r8g8b8a8; 234type(#{b_mask:=16#FF, bytes_pp:=3}) -> b8g8r8; 235type(#{r_mask:=16#FF, bytes_pp:=3}) -> r8g8b8; 236type({Atom, uint}) -> Atom; 237type({Atom, undefined}) -> Atom; %% Just keep it (we don't know) 238type({Atom, sint}) -> list_to_atom(atom_to_list(Atom) ++ "s"); 239type({Atom, float}) -> list_to_atom(atom_to_list(Atom) ++ "f"); 240type(_) -> ?ERROR(unsupported_format). 241 242comp_alg(#{fourCC:={bc1, uint}}) -> 243 Expand = fun(Block) -> bc1(Block) end, 244 Decompress = fun(Compressed, MmW, MmH) -> 245 uncompress(Compressed, MmW, MmH, 8, Expand, 32) 246 end, 247 {r8g8b8a8, 4, 4, 8, Decompress}; 248comp_alg(#{fourCC:={bc2, uint}}) -> 249 Expand = fun(Block) -> bc2(Block) end, 250 Decompress = fun(Compressed, MmW, MmH) -> 251 uncompress(Compressed, MmW, MmH, 16, Expand, 32) 252 end, 253 {r8g8b8a8, 4, 4, 16, Decompress}; 254comp_alg(#{fourCC:={bc3, uint}}) -> 255 Expand = fun(Block) -> bc3(Block) end, 256 Decompress = fun(Compressed, MmW, MmH) -> 257 uncompress(Compressed, MmW, MmH, 16, Expand, 32) 258 end, 259 {r8g8b8a8, 4, 4, 16, Decompress}; 260comp_alg(#{fourCC:={bc4, Signed}}) -> 261 {Expand, Type} = case Signed of 262 uint -> {fun(Block) -> bc4(Block) end, g8}; 263 sint -> {fun(Block) -> bc4s(Block) end, g8s} 264 end, 265 Decompress = fun(Compressed, MmW, MmH) -> 266 uncompress(Compressed, MmW, MmH, 8, Expand, 8) 267 end, 268 {Type, 1, 4, 8, Decompress}; 269comp_alg(#{fourCC:={bc5, Signed}}) -> 270 {Expand, Type} = case Signed of 271 uint -> {fun(Block) -> bc5(Block) end, r8g8}; 272 sint -> {fun(Block) -> bc5s(Block) end, r8g8s} 273 end, 274 Decompress = fun(Compressed, MmW, MmH) -> 275 uncompress(Compressed, MmW, MmH, 16, Expand, 16) 276 end, 277 {Type, 2, 4, 16, Decompress}; 278 279%% Not compressed 280comp_alg(#{fourCC:={r16g16b16a16,_}=T}) -> 281 {type(T), 8, 1, 8, fun(Orig, _, _) -> Orig end}; 282comp_alg(#{fourCC:={r32g32b32a32,_}=T}) -> 283 {type(T), 16, 1, 16, fun(Orig, _, _) -> Orig end}; 284comp_alg(#{fourCC:={r32g32b32,_}=T}) -> 285 {type(T), 12, 1, 12, fun(Orig, _, _) -> Orig end}; 286comp_alg(#{fourCC:={r8g8b8a8,_}=T}) -> 287 {type(T), 4, 1, 4, fun(Orig, _, _) -> Orig end}; 288comp_alg(#{fourCC:={b8g8r8a8,_}}) -> 289 {b8g8r8a8, 4, 1, 4, fun(Orig, _, _) -> Orig end}; 290comp_alg(#{fourCC:={b8g8r8x8,_}}) -> 291 {b8g8r8a8, 4, 1, 4, fun(Orig, _, _) -> Orig end}; 292comp_alg(#{fourCC:={r8g8,_}=T}) -> 293 {type(T), 2, 1, 2, fun(Orig, _, _) -> Orig end}; 294comp_alg(#{fourCC:={r32,_}=T}) -> 295 {type(T), 4, 1, 4, fun(Orig, _, _) -> Orig end}; 296comp_alg(#{fourCC:={r16,_}=T}) -> 297 {type(T), 2, 1, 2, fun(Orig, _, _) -> Orig end}; 298comp_alg(#{fourCC:={r8,_}=T}) -> 299 {type(T), 1, 1, 1, fun(Orig, _, _) -> Orig end}; 300comp_alg(#{fourCC:={a8,_}=T}) -> 301 {type(T), 1, 1, 1, fun(Orig, _, _) -> Orig end}; 302 303comp_alg(#{fourCC:=none, bytes_pp:=Bpp} = Opts) -> 304 {type(Opts), Bpp, 1, Bpp, fun(Orig, _, _) -> Orig end}; 305 306comp_alg(Variant) -> 307 io:format("~p: unsupported_format / compression: ~p~n",[?MODULE, Variant]), 308 ?ERROR(unsupported_compression). 309 310uncompress(Compressed, W, H, BSz, Expand, BiPP) -> 311 CRowSz = ((W+3) div 4) * BSz, 312 CRows = [CRow || <<CRow:CRowSz/binary>> <= Compressed], 313 Decomp = fun(CRow, Rows) -> 314 Uncomp = [Expand(Bits) || <<Bits:BSz/binary>> <= CRow], 315 join_rows(Uncomp, Rows, BiPP) 316 end, 317 Decompressed = lists:foldl(Decomp, <<>>, CRows), 318 %% Crop image to actual size (for non multiple of 4 pixels images or mipmaps) 319 ImgSz = W*H*(BiPP div 8), 320 <<Img:ImgSz/binary, _/binary>> = Decompressed, 321 Img. 322 323join_rows(R4x4, Prev, BiPP) -> 324 [R1,R2,R3,R4] = join_block(R4x4, <<>>, <<>>, <<>>, <<>>, BiPP), 325 <<Prev/binary, R1/binary, R2/binary, R3/binary, R4/binary>>. 326 327join_block([[C00,C01,C02,C03, C10,C11,C12,C13, C20,C21,C22,C23, C30,C31,C32,C33]|Tl], 328 R1,R2,R3,R4, BiPP) -> 329 join_block(Tl, 330 <<R1/binary, C00:BiPP,C01:BiPP,C02:BiPP,C03:BiPP>>, 331 <<R2/binary, C10:BiPP,C11:BiPP,C12:BiPP,C13:BiPP>>, 332 <<R3/binary, C20:BiPP,C21:BiPP,C22:BiPP,C23:BiPP>>, 333 <<R4/binary, C30:BiPP,C31:BiPP,C32:BiPP,C33:BiPP>>, BiPP); 334join_block([], R1,R2,R3,R4, _) -> 335 [R1,R2,R3,R4]. 336 337-define(EXP5TO8R(Col16),((((Col16) bsr 8) band 16#f8) bor (((Col16) bsr 13) band 16#7))). 338-define(EXP6TO8G(Col16),((((Col16) bsr 3) band 16#fc) bor (((Col16) bsr 9) band 16#3))). 339-define(EXP5TO8B(Col16),((((Col16) bsl 3) band 16#f8) bor (((Col16) bsr 2) band 16#7))). 340 341-define(RGB(R,G,B), ((R bsl 24) bor (G bsl 16) bor (B bsl 8) bor 255)). 342-define(RGB(R,G,B,A), ((R bsl 24) bor (G bsl 16) bor (B bsl 8) bor A)). 343 344bc1(<<C0:16/unsigned-little, C1:16/unsigned-little, 345 Inds:32/unsigned-little>>) -> 346 Cs = case C0 > C1 of 347 true -> 348 C2R = (2*?EXP5TO8R(C0)+?EXP5TO8R(C1)) div 3, 349 C2G = (2*?EXP6TO8G(C0)+?EXP6TO8G(C1)) div 3, 350 C2B = (2*?EXP5TO8B(C0)+?EXP5TO8B(C1)) div 3, 351 C3R = (?EXP5TO8R(C0)+2*?EXP5TO8R(C1)) div 3, 352 C3G = (?EXP6TO8G(C0)+2*?EXP6TO8G(C1)) div 3, 353 C3B = (?EXP5TO8B(C0)+2*?EXP5TO8B(C1)) div 3, 354 {?RGB(?EXP5TO8R(C0), ?EXP6TO8G(C0), ?EXP5TO8B(C0)), 355 ?RGB(?EXP5TO8R(C1), ?EXP6TO8G(C1), ?EXP5TO8B(C1)), 356 ?RGB(C2R,C2G,C2B), 357 ?RGB(C3R,C3G,C3B)}; 358 false -> 359 C2R = (?EXP5TO8R(C0)+?EXP5TO8R(C1)) div 2, 360 C2G = (?EXP6TO8G(C0)+?EXP6TO8G(C1)) div 2, 361 C2B = (?EXP5TO8B(C0)+?EXP5TO8B(C1)) div 2, 362 {?RGB(?EXP5TO8R(C0), ?EXP6TO8G(C0), ?EXP5TO8B(C0)), 363 ?RGB(?EXP5TO8R(C1), ?EXP6TO8G(C1), ?EXP5TO8B(C1)), 364 ?RGB(C2R,C2G,C2B), 365 0} 366 end, 367 bc1(Inds, 16, Cs). 368 369bc1(I, N, Cs) when N > 0 -> 370 [element((I band 3)+1, Cs) | bc1(I bsr 2, N-1, Cs)]; 371bc1(_, 0, _) -> []. 372 373bc2(<<A:64/unsigned-little, 374 C0:16/unsigned-little, C1:16/unsigned-little, 375 Inds:32/unsigned-little>>) -> 376 C2R = (2*?EXP5TO8R(C0)+?EXP5TO8R(C1)) div 3, 377 C2G = (2*?EXP6TO8G(C0)+?EXP6TO8G(C1)) div 3, 378 C2B = (2*?EXP5TO8B(C0)+?EXP5TO8B(C1)) div 3, 379 C3R = (?EXP5TO8R(C0)+2*?EXP5TO8R(C1)) div 3, 380 C3G = (?EXP6TO8G(C0)+2*?EXP6TO8G(C1)) div 3, 381 C3B = (?EXP5TO8B(C0)+2*?EXP5TO8B(C1)) div 3, 382 Cs = {?RGB(?EXP5TO8R(C0), ?EXP6TO8G(C0), ?EXP5TO8B(C0)), 383 ?RGB(?EXP5TO8R(C1), ?EXP6TO8G(C1), ?EXP5TO8B(C1)), 384 ?RGB(C2R,C2G,C2B), 385 ?RGB(C3R,C3G,C3B)}, 386 bc2(Inds, A, 16, Cs). 387 388bc2(I, A0, N, Cs) when N > 0 -> 389 A = A0 band 15, 390 RGBA = element((I band 3)+1, Cs) band (((1 bsl 24)-1 bsl 8) bor (A bsl 4 bor A)), 391 [RGBA | bc2(I bsr 2, A0 bsr 4, N-1, Cs)]; 392bc2(_, _, 0, _) -> []. 393 394bc3(<<AC0:8, AC1:8, AInds:48/unsigned-little, 395 C0:16/unsigned-little, C1:16/unsigned-little, 396 Inds:32/unsigned-little>>) -> 397 C2R = (2*?EXP5TO8R(C0)+?EXP5TO8R(C1)) div 3, 398 C2G = (2*?EXP6TO8G(C0)+?EXP6TO8G(C1)) div 3, 399 C2B = (2*?EXP5TO8B(C0)+?EXP5TO8B(C1)) div 3, 400 C3R = (?EXP5TO8R(C0)+2*?EXP5TO8R(C1)) div 3, 401 C3G = (?EXP6TO8G(C0)+2*?EXP6TO8G(C1)) div 3, 402 C3B = (?EXP5TO8B(C0)+2*?EXP5TO8B(C1)) div 3, 403 Cs = {?RGB(?EXP5TO8R(C0), ?EXP6TO8G(C0), ?EXP5TO8B(C0)), 404 ?RGB(?EXP5TO8R(C1), ?EXP6TO8G(C1), ?EXP5TO8B(C1)), 405 ?RGB(C2R,C2G,C2B), 406 ?RGB(C3R,C3G,C3B)}, 407 As = as(AC0,AC1), 408 bc3(Inds, AInds, 16, Cs, As). 409 410bc3(I, AI, N, Cs, As) when N > 0 -> 411 A = element((AI band 7)+1, As), 412 [element((I band 3)+1, Cs) band (((1 bsl 24)-1 bsl 8) bor A) | 413 bc3(I bsr 2, AI bsr 3, N-1, Cs, As)]; 414bc3(_, _, _, _, _) -> 415 []. 416 417bc4(<<AC0:8/unsigned, AC1:8/unsigned, AInds:48/unsigned-little>>) -> 418 As = as(AC0,AC1), 419 bc4(AInds, 16, As). 420bc4s(<<AC0:8/signed, AC1:8/signed, AInds:48/unsigned-little>>) -> 421 As = as(AC0,AC1), 422 bc4(AInds, 16, As). 423 424bc4(AI, N, As) when N > 0 -> 425 A = element((AI band 7)+1, As), 426 [A | bc4(AI bsr 3, N-1, As)]; 427bc4(_, _, _) -> 428 []. 429 430bc5(<<AC0:8/unsigned, AC1:8/unsigned, AInds1:48/unsigned-little, 431 AC3:8/unsigned, AC4:8/unsigned, AInds2:48/unsigned-little>>) -> 432 As1 = as(AC0,AC1), 433 As2 = as(AC3,AC4), 434 bc5(AInds1, AInds2, 16, As1, As2). 435bc5s(<<AC0:8/signed, AC1:8/signed, AInds1:48/unsigned-little, 436 AC3:8/signed, AC4:8/signed, AInds2:48/unsigned-little>>) -> 437 As1 = as(AC0,AC1), 438 As2 = as(AC3,AC4), 439 bc5(AInds1, AInds2, 16, As1, As2). 440 441bc5(A1, B1, N, As1, As2) when N > 0 -> 442 A = element((A1 band 7)+1, As1), 443 B = element((B1 band 7)+1, As2), 444 [(A bsl 8) bor B | bc5(A1 bsr 3, B1 bsr 3, N-1, As1, As2)]; 445bc5(_, _, _, _, _) -> 446 []. 447 448as(AC0, AC1) when AC0 > AC1 -> 449 {AC0, AC1, (6*AC0+1*AC1) div 7, (5*AC0+2*AC1) div 7, 450 (4*AC0+3*AC1) div 7, (3*AC0+4*AC1) div 7, 451 (2*AC0+5*AC1) div 7, (1*AC0+6*AC1) div 7}; 452as(AC0, AC1) -> 453 {AC0, AC1, (4*AC0+1*AC1) div 5, (3*AC0+2*AC1) div 5, 454 (2*AC0+3*AC1) div 5, (1*AC0+4*AC1) div 5, 0, 255}. 455 456%% Debug 457rgb16(<<RGB:16/unsigned-little>>) -> 458 rgb16(RGB); 459rgb16(RGB) -> 460 {?EXP5TO8R(RGB), ?EXP6TO8G(RGB), ?EXP5TO8B(RGB)}. 461 462rgb32({R,G,B}) -> <<?RGB(R,G,B):32>>. 463 464rgb16to32(RGB) -> 465 rgb32(rgb16(RGB)). 466 467dxgi_format(Format) -> 468 case Format of 469 0 -> {unknown, undefined}; % DXGI_FORMAT_UNKNOWN = 0, 470 1 -> {r32g32b32a32,undefined}; % DXGI_FORMAT_R32G32B32A32_TYPELESS = 1, 471 2 -> {r32g32b32a32,float}; % DXGI_FORMAT_R32G32B32A32_FLOAT = 2, 472 3 -> {r32g32b32a32,uint}; % DXGI_FORMAT_R32G32B32A32_UINT = 3, 473 4 -> {r32g32b32a32,sint}; % DXGI_FORMAT_R32G32B32A32_SINT = 4, 474 5 -> {r32g32b32,undefined}; % DXGI_FORMAT_R32G32B32_TYPELESS = 5, 475 6 -> {r32g32b32,float}; % DXGI_FORMAT_R32G32B32_FLOAT = 6, 476 7 -> {r32g32b32,uint}; % DXGI_FORMAT_R32G32B32_UINT = 7, 477 8 -> {r32g32b32,sint}; % DXGI_FORMAT_R32G32B32_SINT = 8, 478 9 -> {r16g16b16a16,undefined}; % DXGI_FORMAT_R16G16B16A16_TYPELESS = 9, 479 10 -> {r16g16b16a16,float}; % DXGI_FORMAT_R16G16B16A16_FLOAT = 10, 480 11 -> {r16g16b16a16,uint}; % DXGI_FORMAT_R16G16B16A16_UNORM = 11, 481 12 -> {r16g16b16a16,uint}; % DXGI_FORMAT_R16G16B16A16_UINT = 12, 482 13 -> {r16g16b16a16,sint}; % DXGI_FORMAT_R16G16B16A16_SNORM = 13, 483 14 -> {r16g16b16a16,sint}; % DXGI_FORMAT_R16G16B16A16_SINT = 14, 484 15 -> {r32g32,undefined}; % DXGI_FORMAT_R32G32_TYPELESS = 15, 485 16 -> {r32g32,float}; % DXGI_FORMAT_R32G32_FLOAT = 16, 486 17 -> {r32g32,uint}; % DXGI_FORMAT_R32G32_UINT = 17, 487 18 -> {r32g32,sint}; % DXGI_FORMAT_R32G32_SINT = 18, 488 19 -> {r32g8x24,undefined}; % DXGI_FORMAT_R32G8X24_TYPELESS = 19, 489 20 -> {d32_float_s8x24,uint}; % DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20, 490 21 -> {r32_float_x8x24,undefined}; % DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, 491 22 -> {x32_undefined_g8x24,uint}; % DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22, 492 23 -> {r10g10b10a2,undefined}; % DXGI_FORMAT_R10G10B10A2_TYPELESS = 23, 493 24 -> {r10g10b10a2,uint}; % DXGI_FORMAT_R10G10B10A2_UNORM = 24, 494 25 -> {r10g10b10a2,uint}; % DXGI_FORMAT_R10G10B10A2_UINT = 25, 495 26 -> {r11g11b10,float}; % DXGI_FORMAT_R11G11B10_FLOAT = 26, 496 27 -> {r8g8b8a8,undefined}; % DXGI_FORMAT_R8G8B8A8_TYPELESS = 27, 497 28 -> {r8g8b8a8,uint}; % DXGI_FORMAT_R8G8B8A8_UNORM = 28, 498 29 -> {r8g8b8a8,uint}; % DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29, 499 30 -> {r8g8b8a8,uint}; % DXGI_FORMAT_R8G8B8A8_UINT = 30, 500 31 -> {r8g8b8a8,sint}; % DXGI_FORMAT_R8G8B8A8_SNORM = 31, 501 32 -> {r8g8b8a8,sint}; % DXGI_FORMAT_R8G8B8A8_SINT = 32, 502 33 -> {r16g16,undefined}; % DXGI_FORMAT_R16G16_TYPELESS = 33, 503 34 -> {r16g16,float}; % DXGI_FORMAT_R16G16_FLOAT = 34, 504 35 -> {r16g16,uint}; % DXGI_FORMAT_R16G16_UNORM = 35, 505 36 -> {r16g16,uint}; % DXGI_FORMAT_R16G16_UINT = 36, 506 37 -> {r16g16,sint}; % DXGI_FORMAT_R16G16_SNORM = 37, 507 38 -> {r16g16,sint}; % DXGI_FORMAT_R16G16_SINT = 38, 508 39 -> {r32,undefined}; % DXGI_FORMAT_R32_TYPELESS = 39, 509 40 -> {d32,float}; % DXGI_FORMAT_D32_FLOAT = 40, 510 41 -> {r32,float}; % DXGI_FORMAT_R32_FLOAT = 41, 511 42 -> {r32,uint}; % DXGI_FORMAT_R32_UINT = 42, 512 43 -> {r32,sint}; % DXGI_FORMAT_R32_SINT = 43, 513 44 -> {r24g8,undefined}; % DXGI_FORMAT_R24G8_TYPELESS = 44, 514 45 -> {d24_s8,uint}; % DXGI_FORMAT_D24_UNORM_S8_UINT = 45, 515 46 -> {r24_x8,undefined}; % DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46, 516 47 -> {x24_undefined_g8,uint}; % DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47, 517 48 -> {r8g8,undefined}; % DXGI_FORMAT_R8G8_TYPELESS = 48, 518 49 -> {r8g8,uint}; % DXGI_FORMAT_R8G8_UNORM = 49, 519 50 -> {r8g8,uint}; % DXGI_FORMAT_R8G8_UINT = 50, 520 51 -> {r8g8,sint}; % DXGI_FORMAT_R8G8_SNORM = 51, 521 52 -> {r8g8,sint}; % DXGI_FORMAT_R8G8_SINT = 52, 522 53 -> {r16,undefined}; % DXGI_FORMAT_R16_TYPELESS = 53, 523 54 -> {r16,float}; % DXGI_FORMAT_R16_FLOAT = 54, 524 55 -> {d16,uint}; % DXGI_FORMAT_D16_UNORM = 55, 525 56 -> {r16,uint}; % DXGI_FORMAT_R16_UNORM = 56, 526 57 -> {r16,uint}; % DXGI_FORMAT_R16_UINT = 57, 527 58 -> {r16,sint}; % DXGI_FORMAT_R16_SNORM = 58, 528 59 -> {r16,sint}; % DXGI_FORMAT_R16_SINT = 59, 529 60 -> {r8,undefined}; % DXGI_FORMAT_R8_TYPELESS = 60, 530 61 -> {r8,uint}; % DXGI_FORMAT_R8_UNORM = 61, 531 62 -> {r8,uint}; % DXGI_FORMAT_R8_UINT = 62, 532 63 -> {r8,sint}; % DXGI_FORMAT_R8_SNORM = 63, 533 64 -> {r8,sint}; % DXGI_FORMAT_R8_SINT = 64, 534 65 -> {a8,uint}; % DXGI_FORMAT_A8_UNORM = 65, 535 66 -> {r1,uint}; % DXGI_FORMAT_R1_UNORM = 66, 536 67 -> {r9g9b9e5,sharedexp}; % DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67, 537 68 -> {r8g8_b8g8,uint}; % DXGI_FORMAT_R8G8_B8G8_UNORM = 68, 538 69 -> {g8r8_g8b8,uint}; % DXGI_FORMAT_G8R8_G8B8_UNORM = 69, 539 70 -> {bc1,undefined}; % DXGI_FORMAT_BC1_TYPELESS = 70, 540 71 -> {bc1,uint}; % DXGI_FORMAT_BC1_UNORM = 71, 541 72 -> {bc1,uint}; % DXGI_FORMAT_BC1_UNORM_SRGB = 72, 542 73 -> {bc2,undefined}; % DXGI_FORMAT_BC2_TYPELESS = 73, 543 74 -> {bc2,uint}; % DXGI_FORMAT_BC2_UNORM = 74, 544 75 -> {bc2,uint}; % DXGI_FORMAT_BC2_UNORM_SRGB = 75, 545 76 -> {bc3,undefined}; % DXGI_FORMAT_BC3_TYPELESS = 76, 546 77 -> {bc3,uint}; % DXGI_FORMAT_BC3_UNORM = 77, 547 78 -> {bc3,uint}; % DXGI_FORMAT_BC3_UNORM_SRGB = 78, 548 79 -> {bc4,undefined}; % DXGI_FORMAT_BC4_TYPELESS = 79, 549 80 -> {bc4,uint}; % DXGI_FORMAT_BC4_UNORM = 80, 550 81 -> {bc4,sint}; % DXGI_FORMAT_BC4_SNORM = 81, 551 82 -> {bc5,undefined}; % DXGI_FORMAT_BC5_TYPELESS = 82, 552 83 -> {bc5,uint}; % DXGI_FORMAT_BC5_UNORM = 83, 553 84 -> {bc5,sint}; % DXGI_FORMAT_BC5_SNORM = 84, 554 85 -> {b5g6r5,uint}; % DXGI_FORMAT_B5G6R5_UNORM = 85, 555 86 -> {b5g5r5a1,uint}; % DXGI_FORMAT_B5G5R5A1_UNORM = 86, 556 87 -> {b8g8r8a8,uint}; % DXGI_FORMAT_B8G8R8A8_UNORM = 87, 557 88 -> {b8g8r8x8,uint}; % DXGI_FORMAT_B8G8R8X8_UNORM = 88, 558 89 -> {r10g10b10_xr_bias_a2,uint}; % DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, 559 90 -> {b8g8r8a8,undefined}; % DXGI_FORMAT_B8G8R8A8_TYPELESS = 90, 560 91 -> {b8g8r8a8,uint}; % DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91, 561 92 -> {b8g8r8x8,undefined}; % DXGI_FORMAT_B8G8R8X8_TYPELESS = 92, 562 93 -> {b8g8r8x8,uint}; % DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93, 563 94 -> {bc6h,undefined}; % DXGI_FORMAT_BC6H_TYPELESS = 94, 564 95 -> {bc6h,uf16}; % DXGI_FORMAT_BC6H_UF16 = 95, 565 96 -> {bc6h,sf16}; % DXGI_FORMAT_BC6H_SF16 = 96, 566 97 -> {bc7,undefing}; % DXGI_FORMAT_BC7_TYPELESS = 97, 567 98 -> {bc7,uint}; % DXGI_FORMAT_BC7_UNORM = 98, 568 99 -> {bc7,uint}; % DXGI_FORMAT_BC7_UNORM_SRGB = 99, 569 100 -> {ayuv, uint}; % DXGI_FORMAT_AYUV = 100, 570 101 -> {y410, uint}; % DXGI_FORMAT_Y410 = 101, 571 102 -> {y416, uint}; % DXGI_FORMAT_Y416 = 102, 572 103 -> {nv12, uint}; % DXGI_FORMAT_NV12 = 103, 573 104 -> {p010, uint}; % DXGI_FORMAT_P010 = 104, 574 105 -> {p016, uint}; % DXGI_FORMAT_P016 = 105, 575 106 -> {'420_opaque', uint}; % DXGI_FORMAT_420_OPAQUE = 106, 576 107 -> {yuy2, uint}; % DXGI_FORMAT_YUY2 = 107, 577 108 -> {y210, uint}; % DXGI_FORMAT_Y210 = 108, 578 109 -> {y216, uint}; % DXGI_FORMAT_Y216 = 109, 579 110 -> {nv11, uint}; % DXGI_FORMAT_NV11 = 110, 580 111 -> {ai44, uint}; % DXGI_FORMAT_AI44 = 111, 581 112 -> {ia44, uint}; % DXGI_FORMAT_IA44 = 112, 582 113 -> {p8, uint}; % DXGI_FORMAT_P8 = 113, 583 114 -> {a8p8, uint}; % DXGI_FORMAT_A8P8 = 114, 584 115 -> {b4g4r4a4, uint}; % DXGI_FORMAT_B4G4R4A4_UNORM = 115, 585 130 -> {p208, uint}; % DXGI_FORMAT_P208 = 130, 586 131 -> {v208, uint}; % DXGI_FORMAT_V208 = 131, 587 132 -> {v408, uint}; % DXGI_FORMAT_V408 = 132, 588 _ -> {unknown, undefined} 589 end. 590