// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package vp8l // This file deals with image transforms, specified in section 3. // nTiles returns the number of tiles needed to cover size pixels, where each // tile's side is 1<> bits } const ( transformTypePredictor = 0 transformTypeCrossColor = 1 transformTypeSubtractGreen = 2 transformTypeColorIndexing = 3 nTransformTypes = 4 ) // transform holds the parameters for an invertible transform. type transform struct { // transformType is the type of the transform. transformType uint32 // oldWidth is the width of the image before transformation (or // equivalently, after inverse transformation). The color-indexing // transform can reduce the width. For example, a 50-pixel-wide // image that only needs 4 bits (half a byte) per color index can // be transformed into a 25-pixel-wide image. oldWidth int32 // bits is the log-2 size of the transform's tiles, for the predictor // and cross-color transforms. 8>>bits is the number of bits per // color index, for the color-index transform. bits uint32 // pix is the tile values, for the predictor and cross-color // transforms, and the color palette, for the color-index transform. pix []byte } var inverseTransforms = [nTransformTypes]func(*transform, []byte, int32) []byte{ transformTypePredictor: inversePredictor, transformTypeCrossColor: inverseCrossColor, transformTypeSubtractGreen: inverseSubtractGreen, transformTypeColorIndexing: inverseColorIndexing, } func inversePredictor(t *transform, pix []byte, h int32) []byte { if t.oldWidth == 0 || h == 0 { return pix } // The first pixel's predictor is mode 0 (opaque black). pix[3] += 0xff p, mask := int32(4), int32(1)<> t.bits) * tilesPerRow predictorMode := t.pix[q+1] & 0x0f q += 4 for x := int32(1); x < t.oldWidth; x++ { if x&mask == 0 { predictorMode = t.pix[q+1] & 0x0f q += 4 } switch predictorMode { case 0: // Opaque black. pix[p+3] += 0xff case 1: // L. pix[p+0] += pix[p-4] pix[p+1] += pix[p-3] pix[p+2] += pix[p-2] pix[p+3] += pix[p-1] case 2: // T. pix[p+0] += pix[top+0] pix[p+1] += pix[top+1] pix[p+2] += pix[top+2] pix[p+3] += pix[top+3] case 3: // TR. pix[p+0] += pix[top+4] pix[p+1] += pix[top+5] pix[p+2] += pix[top+6] pix[p+3] += pix[top+7] case 4: // TL. pix[p+0] += pix[top-4] pix[p+1] += pix[top-3] pix[p+2] += pix[top-2] pix[p+3] += pix[top-1] case 5: // Average2(Average2(L, TR), T). pix[p+0] += avg2(avg2(pix[p-4], pix[top+4]), pix[top+0]) pix[p+1] += avg2(avg2(pix[p-3], pix[top+5]), pix[top+1]) pix[p+2] += avg2(avg2(pix[p-2], pix[top+6]), pix[top+2]) pix[p+3] += avg2(avg2(pix[p-1], pix[top+7]), pix[top+3]) case 6: // Average2(L, TL). pix[p+0] += avg2(pix[p-4], pix[top-4]) pix[p+1] += avg2(pix[p-3], pix[top-3]) pix[p+2] += avg2(pix[p-2], pix[top-2]) pix[p+3] += avg2(pix[p-1], pix[top-1]) case 7: // Average2(L, T). pix[p+0] += avg2(pix[p-4], pix[top+0]) pix[p+1] += avg2(pix[p-3], pix[top+1]) pix[p+2] += avg2(pix[p-2], pix[top+2]) pix[p+3] += avg2(pix[p-1], pix[top+3]) case 8: // Average2(TL, T). pix[p+0] += avg2(pix[top-4], pix[top+0]) pix[p+1] += avg2(pix[top-3], pix[top+1]) pix[p+2] += avg2(pix[top-2], pix[top+2]) pix[p+3] += avg2(pix[top-1], pix[top+3]) case 9: // Average2(T, TR). pix[p+0] += avg2(pix[top+0], pix[top+4]) pix[p+1] += avg2(pix[top+1], pix[top+5]) pix[p+2] += avg2(pix[top+2], pix[top+6]) pix[p+3] += avg2(pix[top+3], pix[top+7]) case 10: // Average2(Average2(L, TL), Average2(T, TR)). pix[p+0] += avg2(avg2(pix[p-4], pix[top-4]), avg2(pix[top+0], pix[top+4])) pix[p+1] += avg2(avg2(pix[p-3], pix[top-3]), avg2(pix[top+1], pix[top+5])) pix[p+2] += avg2(avg2(pix[p-2], pix[top-2]), avg2(pix[top+2], pix[top+6])) pix[p+3] += avg2(avg2(pix[p-1], pix[top-1]), avg2(pix[top+3], pix[top+7])) case 11: // Select(L, T, TL). l0 := int32(pix[p-4]) l1 := int32(pix[p-3]) l2 := int32(pix[p-2]) l3 := int32(pix[p-1]) c0 := int32(pix[top-4]) c1 := int32(pix[top-3]) c2 := int32(pix[top-2]) c3 := int32(pix[top-1]) t0 := int32(pix[top+0]) t1 := int32(pix[top+1]) t2 := int32(pix[top+2]) t3 := int32(pix[top+3]) l := abs(c0-t0) + abs(c1-t1) + abs(c2-t2) + abs(c3-t3) t := abs(c0-l0) + abs(c1-l1) + abs(c2-l2) + abs(c3-l3) if l < t { pix[p+0] += uint8(l0) pix[p+1] += uint8(l1) pix[p+2] += uint8(l2) pix[p+3] += uint8(l3) } else { pix[p+0] += uint8(t0) pix[p+1] += uint8(t1) pix[p+2] += uint8(t2) pix[p+3] += uint8(t3) } case 12: // ClampAddSubtractFull(L, T, TL). pix[p+0] += clampAddSubtractFull(pix[p-4], pix[top+0], pix[top-4]) pix[p+1] += clampAddSubtractFull(pix[p-3], pix[top+1], pix[top-3]) pix[p+2] += clampAddSubtractFull(pix[p-2], pix[top+2], pix[top-2]) pix[p+3] += clampAddSubtractFull(pix[p-1], pix[top+3], pix[top-1]) case 13: // ClampAddSubtractHalf(Average2(L, T), TL). pix[p+0] += clampAddSubtractHalf(avg2(pix[p-4], pix[top+0]), pix[top-4]) pix[p+1] += clampAddSubtractHalf(avg2(pix[p-3], pix[top+1]), pix[top-3]) pix[p+2] += clampAddSubtractHalf(avg2(pix[p-2], pix[top+2]), pix[top-2]) pix[p+3] += clampAddSubtractHalf(avg2(pix[p-1], pix[top+3]), pix[top-1]) } p, top = p+4, top+4 } } return pix } func inverseCrossColor(t *transform, pix []byte, h int32) []byte { var greenToRed, greenToBlue, redToBlue int32 p, mask, tilesPerRow := int32(0), int32(1)<> t.bits) * tilesPerRow for x := int32(0); x < t.oldWidth; x++ { if x&mask == 0 { redToBlue = int32(int8(t.pix[q+0])) greenToBlue = int32(int8(t.pix[q+1])) greenToRed = int32(int8(t.pix[q+2])) q += 4 } red := pix[p+0] green := pix[p+1] blue := pix[p+2] red += uint8(uint32(greenToRed*int32(int8(green))) >> 5) blue += uint8(uint32(greenToBlue*int32(int8(green))) >> 5) blue += uint8(uint32(redToBlue*int32(int8(red))) >> 5) pix[p+0] = red pix[p+2] = blue p += 4 } } return pix } func inverseSubtractGreen(t *transform, pix []byte, h int32) []byte { for p := 0; p < len(pix); p += 4 { green := pix[p+1] pix[p+0] += green pix[p+2] += green } return pix } func inverseColorIndexing(t *transform, pix []byte, h int32) []byte { if t.bits == 0 { for p := 0; p < len(pix); p += 4 { i := 4 * uint32(pix[p+1]) pix[p+0] = t.pix[i+0] pix[p+1] = t.pix[i+1] pix[p+2] = t.pix[i+2] pix[p+3] = t.pix[i+3] } return pix } vMask, xMask, bitsPerPixel := uint32(0), int32(0), uint32(8>>t.bits) switch t.bits { case 1: vMask, xMask = 0x0f, 0x01 case 2: vMask, xMask = 0x03, 0x03 case 3: vMask, xMask = 0x01, 0x07 } d, p, v, dst := 0, 0, uint32(0), make([]byte, 4*t.oldWidth*h) for y := int32(0); y < h; y++ { for x := int32(0); x < t.oldWidth; x++ { if x&xMask == 0 { v = uint32(pix[p+1]) p += 4 } i := 4 * (v & vMask) dst[d+0] = t.pix[i+0] dst[d+1] = t.pix[i+1] dst[d+2] = t.pix[i+2] dst[d+3] = t.pix[i+3] d += 4 v >>= bitsPerPixel } } return dst } func abs(x int32) int32 { if x < 0 { return -x } return x } func avg2(a, b uint8) uint8 { return uint8((int32(a) + int32(b)) / 2) } func clampAddSubtractFull(a, b, c uint8) uint8 { x := int32(a) + int32(b) - int32(c) if x < 0 { return 0 } if x > 255 { return 255 } return uint8(x) } func clampAddSubtractHalf(a, b uint8) uint8 { x := int32(a) + (int32(a)-int32(b))/2 if x < 0 { return 0 } if x > 255 { return 255 } return uint8(x) }