Initial commit
This commit is contained in:
commit
6ac7f0e800
|
@ -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
|
|
@ -0,0 +1,4 @@
|
||||||
|
*.beam
|
||||||
|
*.ez
|
||||||
|
/build
|
||||||
|
erl_crash.dump
|
|
@ -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
|
||||||
|
```
|
|
@ -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"
|
|
@ -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" }
|
|
@ -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>>
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import gleeunit
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
gleeunit.main()
|
||||||
|
}
|
Loading…
Reference in New Issue