Skip to content

Commit f83938a

Browse files
committed
Initial commit
0 parents  commit f83938a

16 files changed

+1438
-0
lines changed

.github/workflows/test.yml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
on: [push, pull_request]
2+
name: Test
3+
jobs:
4+
test:
5+
strategy:
6+
matrix:
7+
go-version: [1.22.x]
8+
os: [ubuntu-latest, macos-latest, windows-latest]
9+
runs-on: ${{ matrix.os }}
10+
env:
11+
CGO_ENABLED: 0
12+
steps:
13+
- name: Install Go
14+
uses: actions/setup-go@v5
15+
with:
16+
go-version: ${{ matrix.go-version }}
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
- name: Test
20+
run: go test

LICENSE

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright (c) 2024, gen2brain
2+
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without modification,
6+
are permitted provided that the following conditions are met:
7+
8+
* Redistributions of source code must retain the above copyright notice,
9+
this list of conditions and the following disclaimer.
10+
* Redistributions in binary form must reproduce the above copyright notice,
11+
this list of conditions and the following disclaimer in the documentation
12+
and/or other materials provided with the distribution.
13+
* Neither the name of {{ project }} nor the names of its contributors
14+
may be used to endorse or promote products derived from this software
15+
without specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
## jpegli
2+
[![Status](https://github.com/gen2brain/jpegli/actions/workflows/test.yml/badge.svg)](https://github.com/gen2brain/jpegli/actions)
3+
[![Go Reference](https://pkg.go.dev/badge/github.com/gen2brain/jpegli.svg)](https://pkg.go.dev/github.com/gen2brain/jpegli)
4+
5+
Go encoder/decoder for [JPEG](https://en.wikipedia.org/wiki/JPEG).
6+
7+
Based on [jpegli](https://github.com/libjxl/libjxl/blob/main/lib/jpegli/README.md) compiled to [WASM](https://en.wikipedia.org/wiki/WebAssembly) and used with [wazero](https://wazero.io/) runtime (CGo-free).
8+
9+
### Benchmark
10+
11+
```
12+
goos: linux
13+
goarch: amd64
14+
pkg: github.com/gen2brain/jpegli
15+
cpu: 11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz
16+
17+
BenchmarkDecodeStd-8 549 2187227 ns/op 407120 B/op 7 allocs/op
18+
BenchmarkDecode-8 554 2172405 ns/op 154672 B/op 44 allocs/op
19+
20+
BenchmarkEncodeStd-8 260 4601558 ns/op 6109 B/op 7 allocs/op
21+
BenchmarkEncode-8 314 3791604 ns/op 394816 B/op 13 allocs/op
22+
23+
BenchmarkEncodeRGBAStd-8 228 5237803 ns/op 9762 B/op 8 allocs/op
24+
BenchmarkEncodeRGBA-8 261 4558648 ns/op 4862 B/op 13 allocs/op
25+
```
26+
27+
### Resources
28+
29+
* https://giannirosato.com/blog/post/jpegli/
30+
* https://cloudinary.com/blog/jpeg-xl-and-the-pareto-front
31+
* https://github.com/google-research/google-research/tree/master/mucped23

go.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/gen2brain/jpegli
2+
3+
go 1.22.1
4+
5+
require github.com/tetratelabs/wazero v1.7.0

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/tetratelabs/wazero v1.7.0 h1:jg5qPydno59wqjpGrHph81lbtHzTrWzwwtD4cD88+hQ=
2+
github.com/tetratelabs/wazero v1.7.0/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=

jpegli.go

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// Package jpegli implements an JPEG image encoder/decoder based on jpegli compiled to WASM.
2+
package jpegli
3+
4+
import "C"
5+
import (
6+
"errors"
7+
"image"
8+
"image/draw"
9+
"io"
10+
)
11+
12+
// Errors .
13+
var (
14+
ErrMemRead = errors.New("jpegli: mem read failed")
15+
ErrMemWrite = errors.New("jpegli: mem write failed")
16+
ErrDecode = errors.New("jpegli: decode failed")
17+
ErrEncode = errors.New("jpegli: encode failed")
18+
)
19+
20+
// DefaultQuality is the default quality encoding parameter.
21+
const DefaultQuality = 75
22+
23+
// DefaultDCTMethod is the default DCT algorithm method.
24+
const DefaultDCTMethod = DCTISlow
25+
26+
// DCTMethod is the DCT/IDCT method type.
27+
type DCTMethod int
28+
29+
const (
30+
// DCTISlow is slow but accurate integer algorithm
31+
DCTISlow DCTMethod = iota
32+
// DCTIFast is faster, less accurate integer method
33+
DCTIFast
34+
// DCTFloat is floating-point: accurate, fast on fast HW
35+
DCTFloat
36+
)
37+
38+
// EncodingOptions are the encoding parameters.
39+
type EncodingOptions struct {
40+
// Quality in the range [0,100]. Default is 75.
41+
Quality int
42+
// Chroma subsampling setting, 444|440|422|420.
43+
ChromaSubsampling image.YCbCrSubsampleRatio
44+
// Progressive level in the range [0,2], where level 0 is sequential, and greater level value means more progression steps.
45+
ProgressiveLevel int
46+
// Huffman code optimization.
47+
// Enabled by default.
48+
OptimizeCoding bool
49+
// Uses adaptive quantization for creating more zero coefficients.
50+
// Enabled by default.
51+
AdaptiveQuantization bool
52+
// Use standard quantization tables from Annex K of the JPEG standard.
53+
// By default, jpegli uses a different set of quantization tables and different scaling parameters for DC and AC coefficients.
54+
StandardQuantTables bool
55+
// Apply fancy downsampling.
56+
FancyDownsampling bool
57+
// DCTMethod is the DCT algorithm method.
58+
DCTMethod DCTMethod
59+
}
60+
61+
// DecodingOptions are the decoding parameters.
62+
type DecodingOptions struct {
63+
// ScaleTarget is the target size to scale image.
64+
ScaleTarget image.Rectangle
65+
// Fancy upsampling.
66+
FancyUpsampling bool
67+
// Block smoothing.
68+
BlockSmoothing bool
69+
// Use arithmetic coding instead of Huffman.
70+
ArithCode bool
71+
// DCTMethod is DCT Algorithm method.
72+
DCTMethod DCTMethod
73+
}
74+
75+
// Decode reads a JPEG image from r and returns it as an image.Image.
76+
func Decode(r io.Reader) (image.Image, error) {
77+
var err error
78+
var img image.Image
79+
80+
img, _, err = decode(r, false, false, false, false, DefaultDCTMethod, 0, 0)
81+
if err != nil {
82+
return nil, err
83+
}
84+
85+
return img, nil
86+
}
87+
88+
// DecodeWithOptions reads a JPEG image from r with decoding options.
89+
func DecodeWithOptions(r io.Reader, o *DecodingOptions) (image.Image, error) {
90+
var err error
91+
var img image.Image
92+
93+
tw := o.ScaleTarget.Dx()
94+
th := o.ScaleTarget.Dy()
95+
fancyUpsampling := o.FancyUpsampling
96+
blockSmoothing := o.BlockSmoothing
97+
arithCode := o.ArithCode
98+
dctMethod := o.DCTMethod
99+
100+
img, _, err = decode(r, false, fancyUpsampling, blockSmoothing, arithCode, dctMethod, tw, th)
101+
if err != nil {
102+
return nil, err
103+
}
104+
105+
return img, nil
106+
}
107+
108+
// DecodeConfig returns the color model and dimensions of a JPEG image without decoding the entire image.
109+
func DecodeConfig(r io.Reader) (image.Config, error) {
110+
var err error
111+
var cfg image.Config
112+
113+
_, cfg, err = decode(r, true, false, false, false, DefaultDCTMethod, 0, 0)
114+
if err != nil {
115+
return image.Config{}, err
116+
}
117+
118+
return cfg, nil
119+
}
120+
121+
// Encode writes the image m to w with the given options.
122+
func Encode(w io.Writer, m image.Image, o ...*EncodingOptions) error {
123+
quality := DefaultQuality
124+
chromaSubsampling := image.YCbCrSubsampleRatio420
125+
progressiveLevel := 0
126+
optimizeCoding := true
127+
adaptiveQuantization := true
128+
standardQuantTables := false
129+
fancyDownsampling := false
130+
dctMethod := DefaultDCTMethod
131+
132+
if o != nil && o[0] != nil {
133+
opt := o[0]
134+
quality = opt.Quality
135+
chromaSubsampling = opt.ChromaSubsampling
136+
progressiveLevel = opt.ProgressiveLevel
137+
138+
if quality <= 0 {
139+
quality = DefaultQuality
140+
} else if quality > 100 {
141+
quality = 100
142+
}
143+
144+
if progressiveLevel < 0 {
145+
progressiveLevel = 0
146+
} else if progressiveLevel > 2 {
147+
progressiveLevel = 2
148+
}
149+
150+
optimizeCoding = opt.OptimizeCoding
151+
adaptiveQuantization = opt.AdaptiveQuantization
152+
standardQuantTables = opt.StandardQuantTables
153+
fancyDownsampling = opt.FancyDownsampling
154+
dctMethod = opt.DCTMethod
155+
}
156+
157+
err := encode(w, m, quality, int(chromaSubsampling), progressiveLevel, optimizeCoding, adaptiveQuantization,
158+
standardQuantTables, fancyDownsampling, dctMethod)
159+
if err != nil {
160+
return err
161+
}
162+
163+
return nil
164+
}
165+
166+
func imageToRGBA(src image.Image) *image.RGBA {
167+
if dst, ok := src.(*image.RGBA); ok {
168+
return dst
169+
}
170+
171+
b := src.Bounds()
172+
dst := image.NewRGBA(b)
173+
draw.Draw(dst, dst.Bounds(), src, b.Min, draw.Src)
174+
175+
return dst
176+
}
177+
178+
func yCbCrSize(r image.Rectangle, subsampleRatio image.YCbCrSubsampleRatio) (w, h, cw, ch int) {
179+
w, h = r.Dx(), r.Dy()
180+
181+
switch subsampleRatio {
182+
case image.YCbCrSubsampleRatio422:
183+
cw = (r.Max.X+1)/2 - r.Min.X/2
184+
ch = h
185+
case image.YCbCrSubsampleRatio420:
186+
cw = (r.Max.X+1)/2 - r.Min.X/2
187+
ch = (r.Max.Y+1)/2 - r.Min.Y/2
188+
case image.YCbCrSubsampleRatio440:
189+
cw = w
190+
ch = (r.Max.Y+1)/2 - r.Min.Y/2
191+
case image.YCbCrSubsampleRatio411:
192+
cw = (r.Max.X+3)/4 - r.Min.X/4
193+
ch = h
194+
case image.YCbCrSubsampleRatio410:
195+
cw = (r.Max.X+3)/4 - r.Min.X/4
196+
ch = (r.Max.Y+1)/2 - r.Min.Y/2
197+
default:
198+
cw = w
199+
ch = h
200+
}
201+
202+
return
203+
}
204+
205+
func init() {
206+
image.RegisterFormat("jpeg", "\xff\xd8", Decode, DecodeConfig)
207+
}

0 commit comments

Comments
 (0)