Initial commit

This commit is contained in:
LilyRose2798 2024-06-04 18:44:05 +10:00
commit 6ac7f0e800
7 changed files with 208 additions and 0 deletions

23
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: test
on:
push:
branches:
- master
- main
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "26.0.2"
gleam-version: "1.2.0"
rebar3-version: "3"
# elixir-version: "1.15.4"
- run: gleam deps download
- run: gleam test
- run: gleam format --check src test

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.beam
*.ez
/build
erl_crash.dump

36
README.md Normal file
View File

@ -0,0 +1,36 @@
# pngleam
[![Package Version](https://img.shields.io/hexpm/v/pngleam)](https://hex.pm/packages/pngleam)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/pngleam/)
```sh
gleam add pngleam
```
```gleam
import pngleam
pub fn main() {
[
<<0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF>>,
<<0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF>>,
<<0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80>>,
]
|> gleam_png.from_packed(
width: 3,
height: 3,
color_info: pngleam.rgb_8bit,
compression_level: pngleam.default_compression,
)
|> simplifile.write_bits("img.png", _)
}
```
Further documentation can be found at <https://hexdocs.pm/pngleam>.
## Development
```sh
gleam run # Run the project
gleam test # Run the tests
gleam shell # Run an Erlang shell
```

16
gleam.toml Normal file
View File

@ -0,0 +1,16 @@
name = "pngleam"
version = "1.0.0"
description = "PNG image library for Gleam"
licences = ["AGPL-3.0-only"]
repository = { type = "github", user = "LilyRose2798", repo = "pngleam" }
links = [
{ title = "7Circles Git", href = "https://git.7cs.dev/lily/pngleam" },
]
[dependencies]
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
gzlib = ">= 1.0.0 and < 2.0.0"
[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"

13
manifest.toml Normal file
View File

@ -0,0 +1,13 @@
# This file was generated by Gleam
# You typically do not need to edit this file
packages = [
{ name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" },
{ name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" },
{ name = "gzlib", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gzlib", source = "hex", outer_checksum = "EC6A3FAF20B8A707B5A550E1B622785685759991C9D13CFC4AAE8FE34FDDF3B8" },
]
[requirements]
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
gzlib = { version = ">= 1.0.0 and < 2.0.0" }

111
src/pngleam.gleam Normal file
View File

@ -0,0 +1,111 @@
import gleam/bit_array
import gleam/list
import gzlib
pub const no_compression = gzlib.no_compression
pub const min_compression = gzlib.min_compression
pub const max_compression = gzlib.max_compression
pub const default_compression = gzlib.default_compression
pub const compression_level = gzlib.compression_level
pub type ColorType {
Greyscale
Color
Indexed
GreyscaleWithAlpha
ColorWithAlpha
}
fn color_type_to_int(color_type: ColorType) -> Int {
case color_type {
Greyscale -> 0b000
Color -> 0b010
Indexed -> 0b011
GreyscaleWithAlpha -> 0b100
ColorWithAlpha -> 0b110
}
}
pub opaque type ColorInfo {
ColorInfo(color_type: ColorType, bit_depth: Int)
}
pub const greyscale_8bit = ColorInfo(Greyscale, 8)
pub const rgb_8bit = ColorInfo(Color, 8)
pub const rgb_16bit = ColorInfo(Color, 16)
pub const rgba_8bit = ColorInfo(ColorWithAlpha, 8)
pub const rgba_16bit = ColorInfo(ColorWithAlpha, 16)
pub fn color_info(
color_type color_type: ColorType,
bit_depth bit_depth: Int,
) -> Result(ColorInfo, Nil) {
let color_info = Ok(ColorInfo(color_type, bit_depth))
case color_type, bit_depth {
Greyscale, 1 | Greyscale, 2 | Greyscale, 4 | Greyscale, 8 | Greyscale, 16 ->
color_info
Color, 8 | Color, 16 -> color_info
Indexed, 1 | Indexed, 2 | Indexed, 4 | Indexed, 8 -> color_info
GreyscaleWithAlpha, 8 | GreyscaleWithAlpha, 16 -> color_info
ColorWithAlpha, 8 | ColorWithAlpha, 16 -> color_info
_, _ -> Error(Nil)
}
}
fn chunk_bit_array(data: BitArray) -> List(BitArray) {
do_chunk_bit_array(data, []) |> list.reverse
}
fn do_chunk_bit_array(data: BitArray, chunks: List(BitArray)) -> List(BitArray) {
case data {
<<chunk:bytes-size(8192), rest:bytes>> ->
do_chunk_bit_array(rest, [chunk, ..chunks])
chunk -> [chunk, ..chunks]
}
}
fn get_chunk(tag: String, data: BitArray) -> BitArray {
let data_size = bit_array.byte_size(data)
let tag_bits = <<tag:utf8>>
let checksum = gzlib.continue_crc32(gzlib.crc32(tag_bits), data)
<<data_size:size(32), tag_bits:bits, data:bits, checksum:size(32)>>
}
const signature = <<137, "PNG":utf8, "\r\n":utf8, 26, "\n":utf8>>
pub fn from_packed(
row_data row_data: List(BitArray),
width width: Int,
height height: Int,
color_info color_info: ColorInfo,
compression_level compression_level: gzlib.CompressionLevel,
) -> BitArray {
let ihdr =
get_chunk("IHDR", <<
width:size(32),
height:size(32),
color_info.bit_depth:size(8),
color_type_to_int(color_info.color_type):size(8),
0:size(8),
0:size(8),
0:size(8),
>>)
let idats =
row_data
|> list.map(fn(d) { <<0, d:bits>> })
|> bit_array.concat
|> gzlib.compress_with_level(compression_level)
|> chunk_bit_array
|> list.map(get_chunk("IDAT", _))
|> bit_array.concat
let iend = get_chunk("IEND", <<>>)
<<signature:bits, ihdr:bits, idats:bits, iend:bits>>
}

5
test/pngleam_test.gleam Normal file
View File

@ -0,0 +1,5 @@
import gleeunit
pub fn main() {
gleeunit.main()
}