package org.jcodec.codecs.mpeg12;
import static org.jcodec.codecs.mpeg12.MPEGConst.BLOCK_TO_CC;
import static org.jcodec.codecs.mpeg12.MPEGConst.SQUEEZE_X;
import static org.jcodec.codecs.mpeg12.MPEGConst.SQUEEZE_Y;
import static org.jcodec.codecs.mpeg12.MPEGConst.vlcCoeff0;
import static org.jcodec.codecs.mpeg12.MPEGConst.vlcDCSizeChroma;
import static org.jcodec.codecs.mpeg12.MPEGConst.vlcDCSizeLuma;
import static org.jcodec.codecs.mpeg12.bitstream.SequenceExtension.Chroma420;
import org.jcodec.codecs.mpeg12.bitstream.PictureHeader;
import org.jcodec.codecs.mpeg12.bitstream.SequenceHeader;
import org.jcodec.common.dct.IDCT4x4;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.VLC;
import java.util.Arrays;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* MPEG 1/2 Decoder, downscaled 4x4
*
* @author The JCodec project
*
*/
public class Mpeg2Thumb4x4 extends MPEGDecoder {
private MPEGPred localPred;
private MPEGPred oldPred;
protected void blockIntra(BitReader bits, VLC vlcCoeff, int[] block, int[] intra_dc_predictor, int blkIdx,
int[] scan, int escSize, int intra_dc_mult, int qScale, int[] qmat) {
int cc = BLOCK_TO_CC[blkIdx];
int size = (cc == 0 ? vlcDCSizeLuma : vlcDCSizeChroma).readVLC(bits);
int delta = (size != 0) ? mpegSigned(bits, size) : 0;
intra_dc_predictor[cc] = intra_dc_predictor[cc] + delta;
Arrays.fill(block, 1, 16, 0);
block[0] = intra_dc_predictor[cc] * intra_dc_mult;
int idx, readVLC = 0;
for (idx = 0; idx < 19 + (scan == scan4x4[1] ? 7 : 0);) {
readVLC = vlcCoeff.readVLC(bits);
int level;
if (readVLC == MPEGConst.CODE_END) {
break;
} else if (readVLC == MPEGConst.CODE_ESCAPE) {
idx += bits.readNBit(6) + 1;
level = twosSigned(bits, escSize) * qScale * qmat[idx];
level = level >= 0 ? (level >> 4) : -(-level >> 4);
} else {
idx += (readVLC >> 6) + 1;
level = toSigned(((readVLC & 0x3f) * qScale * qmat[idx]) >> 4, bits.read1Bit());
}
block[scan[idx]] = level;
}
if (readVLC != MPEGConst.CODE_END)
finishOff(bits, idx, vlcCoeff, escSize);
IDCT4x4.idct(block, 0);
}
private void finishOff(BitReader bits, int idx, VLC vlcCoeff, int escSize) {
for (; idx < 64;) {
int readVLC = vlcCoeff.readVLC(bits);
if (readVLC == MPEGConst.CODE_END) {
break;
} else if (readVLC == MPEGConst.CODE_ESCAPE) {
idx += bits.readNBit(6) + 1;
bits.readNBit(escSize);
} else {
bits.read1Bit();
}
}
}
protected void blockInter(BitReader bits, VLC vlcCoeff, int[] block, int[] scan, int escSize, int qScale, int[] qmat) {
Arrays.fill(block, 1, 16, 0);
int idx = -1;
if (vlcCoeff == vlcCoeff0 && bits.checkNBit(1) == 1) {
bits.read1Bit();
block[0] = toSigned(quantInter(1, qScale * qmat[0]), bits.read1Bit());
idx++;
} else {
block[0] = 0;
}
int readVLC = 0;
for (; idx < 19 + (scan == scan4x4[1] ? 7 : 0);) {
readVLC = vlcCoeff.readVLC(bits);
int ac;
if (readVLC == MPEGConst.CODE_END) {
break;
} else if (readVLC == MPEGConst.CODE_ESCAPE) {
idx += bits.readNBit(6) + 1;
ac = quantInterSigned(twosSigned(bits, escSize), qScale * qmat[idx]);
} else {
idx += (readVLC >> 6) + 1;
ac = toSigned(quantInter(readVLC & 0x3f, qScale * qmat[idx]), bits.read1Bit());
}
block[scan[idx]] = ac;
}
if (readVLC != MPEGConst.CODE_END)
finishOff(bits, idx, vlcCoeff, escSize);
IDCT4x4.idct(block, 0);
}
@Override
public int decodeMacroblock(PictureHeader ph, Context context, int prevAddr, int[] qScaleCode, byte[][] buf,
int stride, BitReader bits, int vertOff, int vertStep, MPEGPred pred) {
if (localPred == null || oldPred != pred) {
localPred = new MPEGPredDbl(pred);
oldPred = pred;
}
return super.decodeMacroblock(ph, context, prevAddr, qScaleCode, buf, stride, bits, vertOff, vertStep,
localPred);
}
public static int[] BLOCK_POS_X = new int[] { 0, 4, 0, 4, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0,
0, 4, 4, 4, 4 };
public static int[] BLOCK_POS_Y = new int[] { 0, 0, 4, 4, 0, 0, 4, 4, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
1, 0, 0, 1, 1 };
protected void mapBlock(int[] block, int[] out, int blkIdx, int dctType, int chromaFormat) {
int stepVert = chromaFormat == Chroma420 && (blkIdx == 4 || blkIdx == 5) ? 0 : dctType;
int log2stride = blkIdx < 4 ? 3 : 3 - SQUEEZE_X[chromaFormat];
int blkIdxExt = blkIdx + (dctType << 4);
int x = BLOCK_POS_X[blkIdxExt];
int y = BLOCK_POS_Y[blkIdxExt];
int off = (y << log2stride) + x, stride = 1 << (log2stride + stepVert);
for (int i = 0; i < 16; i += 4, off += stride) {
out[off] += block[i];
out[off + 1] += block[i + 1];
out[off + 2] += block[i + 2];
out[off + 3] += block[i + 3];
}
}
protected void put(int[][] mbPix, byte[][] buf, int stride, int chromaFormat, int mbX, int mbY, int width,
int height, int vertOff, int vertStep) {
int chromaStride = (stride + (1 << SQUEEZE_X[chromaFormat]) - 1) >> SQUEEZE_X[chromaFormat];
int chromaMBW = 3 - SQUEEZE_X[chromaFormat];
int chromaMBH = 3 - SQUEEZE_Y[chromaFormat];
putSub(buf[0], (mbY << 3) * (stride << vertStep) + vertOff * stride + (mbX << 3), stride << vertStep, mbPix[0],
3, 3);
putSub(buf[1], (mbY << chromaMBH) * (chromaStride << vertStep) + vertOff * chromaStride + (mbX << chromaMBW),
chromaStride << vertStep, mbPix[1], chromaMBW, chromaMBH);
putSub(buf[2], (mbY << chromaMBH) * (chromaStride << vertStep) + vertOff * chromaStride + (mbX << chromaMBW),
chromaStride << vertStep, mbPix[2], chromaMBW, chromaMBH);
}
@Override
protected void putSub(byte[] big, int off, int stride, int[] block, int mbW, int mbH) {
int blOff = 0;
if (mbW == 2) {
for (int i = 0; i < (1 << mbH); i++) {
big[off] = clipTo8Bit(block[blOff]);
big[off + 1] = clipTo8Bit(block[blOff + 1]);
big[off + 2] = clipTo8Bit(block[blOff + 2]);
big[off + 3] = clipTo8Bit(block[blOff + 3]);
blOff += 4;
off += stride;
}
} else {
for (int i = 0; i < (1 << mbH); i++) {
big[off] = clipTo8Bit(block[blOff]);
big[off + 1] = clipTo8Bit(block[blOff + 1]);
big[off + 2] = clipTo8Bit(block[blOff + 2]);
big[off + 3] = clipTo8Bit(block[blOff + 3]);
big[off + 4] = clipTo8Bit(block[blOff + 4]);
big[off + 5] = clipTo8Bit(block[blOff + 5]);
big[off + 6] = clipTo8Bit(block[blOff + 6]);
big[off + 7] = clipTo8Bit(block[blOff + 7]);
blOff += 8;
off += stride;
}
}
}
public static int[][] scan4x4 = new int[][] {
new int[] { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 16, 13, 10, 7, 16, 16, 16, 11, 14, 16, 16, 16, 16, 16, 15, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 },
new int[] { 0, 4, 8, 12, 1, 5, 2, 6, 9, 13, 16, 16, 16, 16, 16, 16, 16, 16, 14, 10, 3, 7, 16, 16, 11, 15,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 } };
protected Context initContext(SequenceHeader sh, PictureHeader ph) {
Context context = super.initContext(sh, ph);
context.codedWidth >>= 1;
context.codedHeight >>= 1;
context.picWidth >>= 1;
context.picHeight >>= 1;
context.scan = scan4x4[ph.pictureCodingExtension == null ? 0 : ph.pictureCodingExtension.alternate_scan];
return context;
}
}