blob: 9b09c5ae4c81c1293ae19bf1f2087f0ef5de38d0 [file] [log] [blame] [edit]
// 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
// ifElse is like C's (condition ? whenTrue : whenFalse) expression.
func ifElse(condition bool, whenTrue int64, whenFalse int64) int64 {
if condition {
return whenTrue
}
return whenFalse
}
const (
fmtHex = "0123456789ABCDEF"
fmtSep = " \n" // 7 spaces and then a new line.
fmtS16 = " \n" // 15 spaces and then a new line.
)
const (
// BlockU8NeutralValue is the neutral value for a BlockU8, which holds
// unsigned uint8 values in the range [0x00, 0xFF].
BlockU8NeutralValue = 0x80
// BlockI16NeutralValue is the neutral value for a BlockI16, which holds
// signed int16 values in the range [-0x8000, +0x7FFF].
BlockI16NeutralValue = 0
)
// BlockU8 is an 8×8 block of uint8 values, such as a block of red, green, blue
// or gray pixel values.
//
// It is indexed in XY (not DCT) space. If b is a BlockU8 then b[0] is the
// top-left corner and b[8] is one pixel below that.
type BlockU8 [64]uint8
// String returns b in human-readable form.
func (b BlockU8) 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[:])
}
// SetToNeutral sets each element to BlockU8NeutralValue.
func (b *BlockU8) SetToNeutral() {
if b == nil {
return
}
for i := range b {
b[i] = BlockU8NeutralValue
}
}
// ForwardDCT returns the FDCT (Forward Discrete Cosine Transform) of b.
func (b *BlockU8) ForwardDCT() (ret BlockI16) {
ret.ForwardDCTFrom(b)
return ret
}
// ForwardDCTFrom sets *dst to src.ForwardDCT().
func (dst *BlockI16) ForwardDCTFrom(src *BlockU8) {
if dst == nil {
return
} else if src == nil {
dst.SetToNeutral()
return
}
// Convert from uint8 to int64, applying an 0x80 bias.
srcI64 := [64]int64{}
for i, v := range src {
srcI64[i] = int64(v) - 0x80
}
// Iterate in DCT space (outer) and XY space (inner).
for v := 0; v < 8; v++ {
halfAlphaV16 := ifElse(v == 0, fixedPointInv2Sqrt2, fixedPointHalf)
for u := 0; u < 8; u++ {
halfAlphaU16 := ifElse(u == 0, fixedPointInv2Sqrt2, fixedPointHalf)
// alphas32 is two 16.16 fixed point values multiplied together, as
// a 32.32 fixed point value.
alphas32 := halfAlphaV16 * halfAlphaU16
sum32 := int64(0)
for y := 0; y < 8; y++ {
for x := 0; x < 8; x++ {
// c32 is two 16.16 fixed point values multiplied together,
// as a 32.32 fixed point value.
//
// sum32 accumulates a 32.32 fixed point value.
c32 := int64(cosines[(((2*x)+1)*u)&31]) *
int64(cosines[(((2*y)+1)*v)&31])
sum32 += srcI64[(8*y)+x] * c32
}
}
// Calculate alphasSum32 as 32.32 fixed point.
alphas16 := (alphas32 + (1 << 15)) >> 16
sum16 := (sum32 + (1 << 15)) >> 16
alphasSum32 := alphas16 * sum16
// Store alphasSum32 as 16.0 fixed point.
result0 := (alphasSum32 + (1 << 31)) >> 32
dst[(8*v)+u] = int16(result0)
}
}
}
// BlockI16 is an 8×8 block of int16 values, such as a block of JPEG coefficients.
//
// It is indexed in DCT (not XY) space. If b is a BlockI16 then b[0] is the DC
// coefficient and every other element is an AC coefficient.
//
// The horizontal-only AC coefficients are b[1], b[2], ..., b[7], in order from
// low-frequency to high-frequency.
//
// The vertical-only AC coefficients are b[8], b[16], ..., b[56], in order from
// low-frequency to high-frequency.
type BlockI16 [64]int16
// String returns b in human-readable form.
func (b BlockI16) String() string {
buf := [64 * 5]byte{}
for i, value := range b {
buf[(5*i)+0] = fmtHex[15&(value>>0x0C)]
buf[(5*i)+1] = fmtHex[15&(value>>0x08)]
buf[(5*i)+2] = fmtHex[15&(value>>0x04)]
buf[(5*i)+3] = fmtHex[15&(value>>0x00)]
buf[(5*i)+4] = fmtSep[7&i]
}
return string(buf[:])
}
// Abs returns the elementwise absolute value of b.
func (b *BlockI16) Abs() (ret BlockI16) {
if b == nil {
return ret
}
for i, v := range b {
if v < 0 {
ret[i] = -v
} else {
ret[i] = +v
}
}
return ret
}
// SetToNeutral sets each element to BlockI16NeutralValue.
func (b *BlockI16) SetToNeutral() {
if b == nil {
return
}
for i := range b {
b[i] = BlockI16NeutralValue
}
}
// InverseDCT returns the IDCT (Inverse Discrete Cosine Transform) of b.
func (b *BlockI16) InverseDCT() (ret BlockU8) {
ret.InverseDCTFrom(b)
return ret
}
// InverseDCTFrom sets *dst to src.InverseDCT().
func (dst *BlockU8) InverseDCTFrom(src *BlockI16) {
if dst == nil {
return
} else if src == nil {
dst.SetToNeutral()
return
}
// Convert from int16 to int64.
srcI64 := [64]int64{}
for i, v := range src {
srcI64[i] = int64(v)
}
// Iterate in XY space (outer) and DCT space (inner).
for y := 0; y < 8; y++ {
for x := 0; x < 8; x++ {
alphasSum32 := int64(0)
for v := 0; v < 8; v++ {
halfAlphaV16 := ifElse(v == 0, fixedPointInv2Sqrt2, fixedPointHalf)
for u := 0; u < 8; u++ {
halfAlphaU16 := ifElse(u == 0, fixedPointInv2Sqrt2, fixedPointHalf)
// alphas16 is two 16.16 fixed point values multiplied
// together, as a 48.16 fixed point value.
alphas16 := ((halfAlphaV16 * halfAlphaU16) + (1 << 15)) >> 16
// c32 is two 16.16 fixed point values multiplied together,
// as a 32.32 fixed point value.
//
// c16 is c32 as a 48.16 fixed point value.
//
// alphasSum32 accumulates a 32.32 fixed point value.
c32 := int64(cosines[(((2*x)+1)*u)&31]) *
int64(cosines[(((2*y)+1)*v)&31])
c16 := (c32 + (1 << 15)) >> 16
alphasSum32 += srcI64[(8*v)+u] * (alphas16 * c16)
}
}
// Store alphasSum32, biased and clamped, as 8.0 fixed point.
result0 := (alphasSum32 + (1 << 31)) >> 32
dst[(8*y)+x] = biasAndClamp[result0&1023]
}
}
}
// IsValid returns whether b does not contain unexpectedly extreme values for
// an 8-bit-depth JPEG - one that is invalid to pass to AddN. ForwardDCT or
// ForwardDCTFrom always returns or sets a BlockI16 that IsValid.
//
// Specifically:
// - a DC element is out of range when outside [-1024, +1023].
// - an AC element is out of range when outside [-1023, +1023].
//
// A BlockI16 is valid when none of its elements are out of range.
func (b *BlockI16) IsValid() bool {
if b == nil {
return false
}
if (b[0] < -1024) || (+1023 < b[0]) {
return false
}
for _, v := range b[1:] {
if (v < -1023) || (+1023 < v) {
return false
}
}
return true
}
// QuadBlockU8 is like a BlockU8 but it is 16×16 instead of 8×8.
//
// It is indexed in XY (not DCT) space. If b is a QuadBlockU8 then b[0] is the
// top-left corner and b[16] is one pixel below that.
type QuadBlockU8 [256]uint8
// String returns b in human-readable form.
func (b QuadBlockU8) String() string {
buf := [256 * 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] = fmtS16[15&i]
}
return string(buf[:])
}
// SetToNeutral sets each element to BlockU8NeutralValue.
func (b *QuadBlockU8) SetToNeutral() {
if b == nil {
return
}
for i := range b {
b[i] = BlockU8NeutralValue
}
}
// These are "1 / 2" and "1 / (2 * √2)" as 16.16 fixed point values.
const (
fixedPointHalf = +0x8000
fixedPointInv2Sqrt2 = +0x5A82
)
// cosines is a cosine table as 16.16 fixed point values.
var cosines = [32]int32{
+0x10000, // cos(π/16 * 0)
+0x0FB14, // cos(π/16 * 1)
+0x0EC83, // cos(π/16 * 2)
+0x0D4DB, // cos(π/16 * 3)
+0x0B504, // cos(π/16 * 4)
+0x08E39, // cos(π/16 * 5)
+0x061F7, // cos(π/16 * 6)
+0x031F1, // cos(π/16 * 7)
+0x00000, // cos(π/16 * 8)
-0x031F1, // cos(π/16 * 9)
-0x061F7, // cos(π/16 * 10)
-0x08E39, // cos(π/16 * 11)
-0x0B504, // cos(π/16 * 12)
-0x0D4DB, // cos(π/16 * 13)
-0x0EC83, // cos(π/16 * 14)
-0x0FB14, // cos(π/16 * 15)
-0x10000, // cos(π/16 * 16)
-0x0FB14, // cos(π/16 * 17)
-0x0EC83, // cos(π/16 * 18)
-0x0D4DB, // cos(π/16 * 19)
-0x0B504, // cos(π/16 * 20)
-0x08E39, // cos(π/16 * 21)
-0x061F7, // cos(π/16 * 22)
-0x031F1, // cos(π/16 * 23)
+0x00000, // cos(π/16 * 24)
+0x031F1, // cos(π/16 * 25)
+0x061F7, // cos(π/16 * 26)
+0x08E39, // cos(π/16 * 27)
+0x0B504, // cos(π/16 * 28)
+0x0D4DB, // cos(π/16 * 29)
+0x0EC83, // cos(π/16 * 30)
+0x0FB14, // cos(π/16 * 31)
}
// biasAndClamp[x & 1023] is (x + 0x80), clamped to the range [0x00, 0xFF], for
// a signed integer x in the range [-512, +511].
var biasAndClamp = [1024]uint8{
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
}