-module(qrkit@render@png).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/qrkit/render/png.gleam").
-export([to_bit_array/3]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(" Pure Gleam PNG renderer for qrkit QR codes.\n").
-file("src/qrkit/render/png.gleam", 56).
-spec pad_rows(list(list(boolean())), integer()) -> list(list(boolean())).
pad_rows(Rows, Margin) ->
Width = case Rows of
[First | _] ->
erlang:length(First);
[] ->
0
end,
Padding = gleam@list:repeat(false, Width + (Margin * 2)),
Body = begin
_pipe = Rows,
gleam@list:map(
_pipe,
fun(Row) ->
lists:append(
gleam@list:repeat(false, Margin),
lists:append(Row, gleam@list:repeat(false, Margin))
)
end
)
end,
lists:append(
gleam@list:repeat(Padding, Margin),
lists:append(Body, gleam@list:repeat(Padding, Margin))
).
-file("src/qrkit/render/png.gleam", 92).
-spec prepend_repeat(GFP, integer(), list(GFP)) -> list(GFP).
prepend_repeat(Value, Count, Acc) ->
case Count =< 0 of
true ->
Acc;
false ->
prepend_repeat(Value, Count - 1, [Value | Acc])
end.
-file("src/qrkit/render/png.gleam", 85).
-spec scale_row(list(boolean()), integer(), list(boolean())) -> list(boolean()).
scale_row(Row, Scale, Acc) ->
case Row of
[] ->
lists:reverse(Acc);
[Value | Rest] ->
scale_row(Rest, Scale, prepend_repeat(Value, Scale, Acc))
end.
-file("src/qrkit/render/png.gleam", 77).
-spec scale_rows(list(list(boolean())), integer()) -> list(list(boolean())).
scale_rows(Rows, Scale) ->
_pipe = Rows,
gleam@list:flat_map(
_pipe,
fun(Row) ->
Scaled = scale_row(Row, Scale, []),
gleam@list:repeat(Scaled, Scale)
end
).
-file("src/qrkit/render/png.gleam", 99).
-spec pixels_width(list(list(boolean()))) -> integer().
pixels_width(Rows) ->
case Rows of
[First | _] ->
erlang:length(First);
[] ->
0
end.
-file("src/qrkit/render/png.gleam", 140).
-spec pixel_bit(boolean()) -> integer().
pixel_bit(Pixel) ->
case Pixel of
true ->
0;
false ->
1
end.
-file("src/qrkit/render/png.gleam", 186).
-spec take_bytes(list(integer()), integer(), list(integer())) -> {list(integer()),
list(integer())}.
take_bytes(Values, Count, Acc) ->
case {Values, Count} of
{Rest, 0} ->
{lists:reverse(Acc), Rest};
{[Value | Rest@1], _} ->
take_bytes(Rest@1, Count - 1, [Value | Acc]);
{[], _} ->
{lists:reverse(Acc), []}
end.
-file("src/qrkit/render/png.gleam", 254).
-spec byte_list_to_bit_array(list(integer())) -> bitstring().
byte_list_to_bit_array(Bytes) ->
_pipe = Bytes,
_pipe@1 = gleam@list:map(_pipe, fun(Byte) -> <<Byte>> end),
gleam_stdlib:bit_array_concat(_pipe@1).
-file("src/qrkit/render/png.gleam", 260).
-spec prepend_reversed(list(GGW), list(GGW)) -> list(GGW).
prepend_reversed(Values, Acc) ->
case Values of
[] ->
Acc;
[Value | Rest] ->
prepend_reversed(Rest, [Value | Acc])
end.
-file("src/qrkit/render/png.gleam", 267).
-spec positive_or(integer(), integer()) -> integer().
positive_or(Value, Default) ->
case Value > 0 of
true ->
Value;
false ->
Default
end.
-file("src/qrkit/render/png.gleam", 274).
-spec non_negative_or(integer(), integer()) -> integer().
non_negative_or(Value, Default) ->
case Value >= 0 of
true ->
Value;
false ->
Default
end.
-file("src/qrkit/render/png.gleam", 281).
-spec u32_bytes(integer()) -> list(integer()).
u32_bytes(Value) ->
[erlang:'band'(erlang:'bsr'(Value, 24), 16#FF),
erlang:'band'(erlang:'bsr'(Value, 16), 16#FF),
erlang:'band'(erlang:'bsr'(Value, 8), 16#FF),
erlang:'band'(Value, 16#FF)].
-file("src/qrkit/render/png.gleam", 290).
-spec low_byte(integer()) -> integer().
low_byte(Value) ->
erlang:'band'(Value, 16#FF).
-file("src/qrkit/render/png.gleam", 294).
-spec high_byte(integer()) -> integer().
high_byte(Value) ->
erlang:'band'(erlang:'bsr'(Value, 8), 16#FF).
-file("src/qrkit/render/png.gleam", 162).
-spec do_deflate_store_blocks(list(integer()), list(integer())) -> list(integer()).
do_deflate_store_blocks(Data, Acc) ->
case Data of
[] ->
lists:reverse(Acc);
_ ->
{Chunk_bytes, Rest} = take_bytes(Data, 65535, []),
Final = case Rest of
[] ->
1;
_ ->
0
end,
Length = erlang:length(Chunk_bytes),
Complement = 65535 - Length,
Next = [Final,
low_byte(Length),
high_byte(Length),
low_byte(Complement),
high_byte(Complement) |
Chunk_bytes],
do_deflate_store_blocks(Rest, prepend_reversed(Next, Acc))
end.
-file("src/qrkit/render/png.gleam", 158).
-spec deflate_store_blocks(list(integer())) -> list(integer()).
deflate_store_blocks(Data) ->
do_deflate_store_blocks(Data, []).
-file("src/qrkit/render/png.gleam", 298).
-spec power_of_two(integer()) -> integer().
power_of_two(Exponent) ->
case Exponent =< 0 of
true ->
1;
false ->
2 * power_of_two(Exponent - 1)
end.
-file("src/qrkit/render/png.gleam", 147).
-spec finish_byte(integer(), integer()) -> integer().
finish_byte(Current, Bit_count) ->
Padding_bits = 8 - Bit_count,
(Current * power_of_two(Padding_bits)) + (power_of_two(Padding_bits) - 1).
-file("src/qrkit/render/png.gleam", 117).
-spec pack_bits(list(boolean()), integer(), integer(), list(integer())) -> list(integer()).
pack_bits(Pixels, Current, Bit_count, Acc) ->
case Pixels of
[] ->
case Bit_count =:= 0 of
true ->
lists:reverse(Acc);
false ->
lists:reverse([finish_byte(Current, Bit_count) | Acc])
end;
[Pixel | Rest] ->
Next = (Current * 2) + pixel_bit(Pixel),
Next_count = Bit_count + 1,
case Next_count =:= 8 of
true ->
pack_bits(Rest, 0, 0, [Next | Acc]);
false ->
pack_bits(Rest, Next, Next_count, Acc)
end
end.
-file("src/qrkit/render/png.gleam", 113).
-spec pack_scanline(list(boolean())) -> list(integer()).
pack_scanline(Row) ->
[0 | pack_bits(Row, 0, 0, [])].
-file("src/qrkit/render/png.gleam", 106).
-spec scanlines(list(list(boolean())), list(integer())) -> list(integer()).
scanlines(Rows, Acc) ->
case Rows of
[] ->
lists:reverse(Acc);
[Row | Rest] ->
scanlines(Rest, prepend_reversed(pack_scanline(Row), Acc))
end.
-file("src/qrkit/render/png.gleam", 222).
-spec crc32_byte(integer(), integer()) -> integer().
crc32_byte(Crc, Remaining) ->
case Remaining =< 0 of
true ->
Crc;
false ->
Next = case erlang:'band'(Crc, 1) =:= 1 of
true ->
erlang:'bxor'(erlang:'bsr'(Crc, 1), 16#EDB88320);
false ->
erlang:'bsr'(Crc, 1)
end,
crc32_byte(Next, Remaining - 1)
end.
-file("src/qrkit/render/png.gleam", 214).
-spec do_crc32(list(integer()), integer()) -> integer().
do_crc32(Bytes, Crc) ->
case Bytes of
[] ->
Crc;
[Byte | Rest] ->
do_crc32(Rest, crc32_byte(erlang:'bxor'(Crc, Byte), 8))
end.
-file("src/qrkit/render/png.gleam", 209).
-spec crc32(list(integer())) -> integer().
crc32(Bytes) ->
_pipe = do_crc32(Bytes, 16#FFFFFFFF),
erlang:'bxor'(_pipe, 16#FFFFFFFF).
-file("src/qrkit/render/png.gleam", 198).
-spec chunk(list(integer()), list(integer())) -> list(integer()).
chunk(Type_bytes, Data_bytes) ->
Crc = crc32(lists:append(Type_bytes, Data_bytes)),
lists:append(
u32_bytes(erlang:length(Data_bytes)),
lists:append(Type_bytes, lists:append(Data_bytes, u32_bytes(Crc)))
).
-file("src/qrkit/render/png.gleam", 243).
-spec do_adler32(list(integer()), integer(), integer()) -> integer().
do_adler32(Bytes, S1, S2) ->
case Bytes of
[] ->
(S2 * 65536) + S1;
[Byte | Rest] ->
Next_s1 = case 65521 of
0 -> 0;
Gleam@denominator -> (S1 + Byte) rem Gleam@denominator
end,
Next_s2 = case 65521 of
0 -> 0;
Gleam@denominator@1 -> (S2 + Next_s1) rem Gleam@denominator@1
end,
do_adler32(Rest, Next_s1, Next_s2)
end.
-file("src/qrkit/render/png.gleam", 239).
-spec adler32(list(integer())) -> integer().
adler32(Bytes) ->
do_adler32(Bytes, 1, 0).
-file("src/qrkit/render/png.gleam", 152).
-spec zlib_store(list(integer())) -> list(integer()).
zlib_store(Data) ->
Checksum = adler32(Data),
_pipe = lists:append([120, 1], deflate_store_blocks(Data)),
lists:append(_pipe, u32_bytes(Checksum)).
-file("src/qrkit/render/png.gleam", 18).
?DOC(
" Render a QR code as PNG bytes.\n"
"\n"
" `scale` values less than 1 are normalised to 1, and negative `margin`\n"
" values are normalised to 0.\n"
).
-spec to_bit_array(qrkit:qr_code(), integer(), integer()) -> bitstring().
to_bit_array(Qr, Scale, Margin) ->
Actual_scale = positive_or(Scale, 1),
Actual_margin = non_negative_or(Margin, 0),
Pixels = begin
_pipe = qrkit:rows(Qr),
_pipe@1 = pad_rows(_pipe, Actual_margin),
scale_rows(_pipe@1, Actual_scale)
end,
Width = pixels_width(Pixels),
Height = erlang:length(Pixels),
Image_data = scanlines(Pixels, []),
Ihdr = lists:append(
u32_bytes(Width),
lists:append(u32_bytes(Height), [1, 3, 0, 0, 0])
),
Plte = [0, 0, 0, 255, 255, 255],
Idat = zlib_store(Image_data),
_pipe@2 = lists:append(
[137, 80, 78, 71, 13, 10, 26, 10],
lists:append(
chunk([73, 72, 68, 82], Ihdr),
lists:append(
chunk([80, 76, 84, 69], Plte),
lists:append(
chunk([73, 68, 65, 84], Idat),
chunk([73, 69, 78, 68], [])
)
)
)
),
byte_list_to_bit_array(_pipe@2).