blob: 5326908ca085ecfdeb7f3000d0c0b40ad589de33 [file] [log] [blame]
// Copyright 2025 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
package lowleveljpeg
// QuantizationStandardValuesType selects which table from the JPEG
// specification to use for SetToStandardValues.
type QuantizationStandardValuesType byte
const (
// QuantizationStandardValuesTypeLuma means to use table K.1 from the JPEG
// spec.
QuantizationStandardValuesTypeLuma = QuantizationStandardValuesType(0)
// QuantizationStandardValuesTypeChroma means to use table K.2 from the
// JPEG spec.
QuantizationStandardValuesTypeChroma = QuantizationStandardValuesType(1)
)
// 12-bit or 16-bit depth JPEGs can use more-than-8-bit quantization factors,
// but we only support 8-bit JPEGs and so we use 8-bit quantization factors.
// The QuantizationFactors element type is uint8.
// QuantizationFactors is an 8×8 block of uint8 values, used to quantize a
// BlockI16.
//
// It is indexed in DCT (not XY) space, like a Block16.
type QuantizationFactors [64]uint8
// String returns b in human-readable form.
func (b QuantizationFactors) String() string {
buf := [64 * 3]byte{}
for i, value := range b {
buf[(3*i)+0] = fmtHex[15&(value>>0x04)]
buf[(3*i)+1] = fmtHex[15&(value>>0x00)]
buf[(3*i)+2] = fmtSep[7&i]
}
return string(buf[:])
}
// IsValid returns whether b does not contain any zero-valued elements.
func (b *QuantizationFactors) IsValid() bool {
if b == nil {
return false
}
for _, v := range b {
if v == 0 {
return false
}
}
return true
}
const (
// MinimumQuality and MaximumQuality are the minimum and maximum
// (inclusive) values for SetToStandardValues' quality parameter.
MinimumQuality = 1
MaximumQuality = 100
// DefaultQuality is the default value used by EncoderOptions for
// SetToStandardValues' quality parameter. It matches libjpeg's default
// both in terms of numerical value and in behavior.
DefaultQuality = 75
// MinimumBaselineQuality is lowest quality level where both the cjpeg
// program (from the libjpeg C project) and this Go package will agree on
// the exact quantization factors.
//
// It's still valid to pass a SetToStandardValues quality parameter value
// below 24. But when doing so, passing the equivalent to cjpeg will print
// a JTRC_16BIT_TABLES warning: "quantization tables are too coarse for
// baseline JPEG". cjpeg will therefore produce extended (instead of
// baseline) JPEGs, using 2 bytes (instead of 1 byte) per quantization
// factor, allowing factors above 0xFF.
//
// This Go package will instead clamp such quantization factors to 0xFF.
//
// Both approaches are viable, producing valid JPEGs. This Go package, like
// the Go standard library's image/jpeg package, simply chooses to always
// produce baseline (instead of extended) JPEGs, for best compatibility
// with other JPEG decoders.
MinimumBaselineQuality = 24
)
// SetToStandardValues sets b to one of two standard quantization tables
// (Tables K.1 or K.2 in the JPEG spec, for Luma and Chroma respectively),
// scaled by a quality parameter with the same meaning as that used by
// libjpeg's encoder.
//
// The quality parameter ranges from 1 (low quality) to 100 (high quality),
// inclusive. Values outside of that range will be clamped to be in [1, 100].
//
// Passing quality = MinimumQuality will set every element of b to 0xFF.
//
// Passing quality = MaximumQuality will set every element of b to 0x01.
func (b *QuantizationFactors) SetToStandardValues(which QuantizationStandardValuesType, quality int) {
if b == nil {
return
}
// Follow the same algorithm as in libjpeg's jcparam.c.
if quality < 1 {
quality = 1
} else if quality > 100 {
quality = 100
}
q := 0
if quality < 50 {
q = 5000 / quality
} else {
q = 200 - (quality * 2)
}
std := &standardQuantizationFactors[which&1]
for i, v := range std {
scaled := ((int(v) * q) + 50) / 100
if scaled < 0x01 {
scaled = 0x01
} else if scaled > 0xFF {
scaled = 0xFF
}
b[i] = uint8(scaled)
}
}
var standardQuantizationFactors = [2]QuantizationFactors{
// Table K.1 from the JPEG spec.
QuantizationStandardValuesTypeLuma: {
16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109, 103, 77,
24, 35, 55, 64, 81, 104, 113, 92,
49, 64, 78, 87, 103, 121, 120, 101,
72, 92, 95, 98, 112, 100, 103, 99,
},
// Table K.2 from the JPEG spec.
QuantizationStandardValuesTypeChroma: {
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
},
}