/*
* Java port of parts of the ffmpeg Mpeg4 base decoder.
* Copyright (c) 2003 Jonathan Hueber.
*
* Copyright (c) 2001 Fabrice Bellard.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* See Credits file and Readme for details
*/
package net.sourceforge.jffmpeg.codecs.video.mpeg4.div3;
import java.awt.Frame;
import java.awt.Image;
import java.awt.image.MemoryImageSource;
import java.awt.image.ColorModel;
import java.awt.Graphics;
import javax.media.Buffer;
/**
* This class manages a single display buffer
*/
public class DisplayOutput {
private int[] luminance;
private int[] red;
private int[] blue;
private int[] oldLuminance;
private int[] oldRed;
private int[] oldBlue;
private int screenX;
private int screenY;
private int chromX;
private int chromY;
private static final int W1 = 22725; //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5
private static final int W2 = 21407; //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5
private static final int W3 = 19266; //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5
// private static final int W4 = 16384; //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5
private static final int W4 = 16383; //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5
private static final int W5 = 12873; //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5
private static final int W6 = 8867; //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5
private static final int W7 = 4520; //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5
private static final int ROW_SHIFT = 11;
private static final int COL_SHIFT = 20; // 6
private static final void idctRowCondDC( int[] block, int offset ) {
int block0 = block[ offset ];
int block1 = block[ offset + 1 ];
int block2 = block[ offset + 2 ];
int block3 = block[ offset + 3 ];
int block4 = block[ offset + 4 ];
int block5 = block[ offset + 5 ];
int block6 = block[ offset + 6 ];
int block7 = block[ offset + 7 ];
if ( (block1|block2|block3|block4|block5|block6|block7) == 0 ) {
block0 <<= 3;
block[ offset ] = block0;
block[ offset + 1 ] = block0;
block[ offset + 2 ] = block0;
block[ offset + 3 ] = block0;
block[ offset + 4 ] = block0;
block[ offset + 5 ] = block0;
block[ offset + 6 ] = block0;
block[ offset + 7 ] = block0;
return;
}
int a0 = W4 * block0 + (1 << (ROW_SHIFT - 1));
int a1 = a0 + W6 * block2 - W4 * block4 - W2 * block6;
int a2 = a0 - W6 * block2 - W4 * block4 + W2 * block6;
int a3 = a0 - W2 * block2 + W4 * block4 - W6 * block6;
a0 += W2 * block2 + W4 * block4 + W6 * block6;
int b0 = W1 * block1 + W3 * block3 + W5 * block5 + W7 * block7;
int b1 = W3 * block1 - W7 * block3 - W1 * block5 - W5 * block7;
int b2 = W5 * block1 - W1 * block3 + W7 * block5 + W3 * block7;
int b3 = W7 * block1 - W5 * block3 + W3 * block5 - W1 * block7;
block[ offset + 0 ] = (a0 + b0) >> ROW_SHIFT;
block[ offset + 7 ] = (a0 - b0) >> ROW_SHIFT;
block[ offset + 1 ] = (a1 + b1) >> ROW_SHIFT;
block[ offset + 6 ] = (a1 - b1) >> ROW_SHIFT;
block[ offset + 2 ] = (a2 + b2) >> ROW_SHIFT;
block[ offset + 5 ] = (a2 - b2) >> ROW_SHIFT;
block[ offset + 3 ] = (a3 + b3) >> ROW_SHIFT;
block[ offset + 4 ] = (a3 - b3) >> ROW_SHIFT;
}
private static void idctSparseColAdd( int[] block, int offset, int[] destination, int destinationOffset, int destinationWidth ) {
int block0 = block[ offset ];
int block1 = block[ offset + 1 * 8 ];
int block2 = block[ offset + 2 * 8 ];
int block3 = block[ offset + 3 * 8 ];
int block4 = block[ offset + 4 * 8 ];
int block5 = block[ offset + 5 * 8 ];
int block6 = block[ offset + 6 * 8 ];
int block7 = block[ offset + 7 * 8 ];
int a0 = W4 * block0 + (1 << (COL_SHIFT - 1));
int a1 = a0 + W6 * block2 - W4 * block4 - W2 * block6;
int a2 = a0 - W6 * block2 - W4 * block4 + W2 * block6;
int a3 = a0 - W2 * block2 + W4 * block4 - W6 * block6;
a0 += W2 * block2 + W4 * block4 + W6 * block6;
int b0 = W1 * block1 + W3 * block3 + W5 * block5 + W7 * block7;
int b1 = W3 * block1 - W7 * block3 - W1 * block5 - W5 * block7;
int b2 = W5 * block1 - W1 * block3 + W7 * block5 + W3 * block7;
int b3 = W7 * block1 - W5 * block3 + W3 * block5 - W1 * block7;
destinationOffset += offset;
destination[ destinationOffset ] = crop(destination[ destinationOffset ] + ((a0 + b0) >> COL_SHIFT)); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop(destination[ destinationOffset ] + ((a1 + b1) >> COL_SHIFT)); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop(destination[ destinationOffset ] + ((a2 + b2) >> COL_SHIFT)); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop(destination[ destinationOffset ] + ((a3 + b3) >> COL_SHIFT)); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop(destination[ destinationOffset ] + ((a3 - b3) >> COL_SHIFT)); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop(destination[ destinationOffset ] + ((a2 - b2) >> COL_SHIFT)); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop(destination[ destinationOffset ] + ((a1 - b1) >> COL_SHIFT)); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop(destination[ destinationOffset ] + ((a0 - b0) >> COL_SHIFT));
}
public final void idctAdd( int[] block, int[] destination, int destinationOffset, int destinationWidth ) {
idctRowCondDC( block, 0 );
idctRowCondDC( block, 8 );
idctRowCondDC( block, 16 );
idctRowCondDC( block, 24 );
idctRowCondDC( block, 32 );
idctRowCondDC( block, 40 );
idctRowCondDC( block, 48 );
idctRowCondDC( block, 56 );
idctSparseColAdd( block, 0, destination, destinationOffset, destinationWidth );
idctSparseColAdd( block, 1, destination, destinationOffset, destinationWidth );
idctSparseColAdd( block, 2, destination, destinationOffset, destinationWidth );
idctSparseColAdd( block, 3, destination, destinationOffset, destinationWidth );
idctSparseColAdd( block, 4, destination, destinationOffset, destinationWidth );
idctSparseColAdd( block, 5, destination, destinationOffset, destinationWidth );
idctSparseColAdd( block, 6, destination, destinationOffset, destinationWidth );
idctSparseColAdd( block, 7, destination, destinationOffset, destinationWidth );
}
private static void idctSparseColPut( int[] block, int offset, int[] destination, int destinationOffset, int destinationWidth ) {
int block0 = block[ offset ];
int block1 = block[ offset + 1 * 8 ];
int block2 = block[ offset + 2 * 8 ];
int block3 = block[ offset + 3 * 8 ];
int block4 = block[ offset + 4 * 8 ];
int block5 = block[ offset + 5 * 8 ];
int block6 = block[ offset + 6 * 8 ];
int block7 = block[ offset + 7 * 8 ];
int a0 = W4 * block0 + (1 << (COL_SHIFT - 1));
int a1 = a0 + W6 * block2 - W4 * block4 - W2 * block6;
int a2 = a0 - W6 * block2 - W4 * block4 + W2 * block6;
int a3 = a0 - W2 * block2 + W4 * block4 - W6 * block6;
a0 += W2 * block2 + W4 * block4 + W6 * block6;
int b0 = W1 * block1 + W3 * block3 + W5 * block5 + W7 * block7;
int b1 = W3 * block1 - W7 * block3 - W1 * block5 - W5 * block7;
int b2 = W5 * block1 - W1 * block3 + W7 * block5 + W3 * block7;
int b3 = W7 * block1 - W5 * block3 + W3 * block5 - W1 * block7;
destinationOffset += offset;
destination[ destinationOffset ] = crop((a0 + b0) >> COL_SHIFT); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop((a1 + b1) >> COL_SHIFT); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop((a2 + b2) >> COL_SHIFT); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop((a3 + b3) >> COL_SHIFT); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop((a3 - b3) >> COL_SHIFT); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop((a2 - b2) >> COL_SHIFT); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop((a1 - b1) >> COL_SHIFT); destinationOffset += destinationWidth;
destination[ destinationOffset ] = crop((a0 - b0) >> COL_SHIFT);
}
public final void idctPut( int[] block, int[] destination, int destinationOffset, int destinationWidth ) {
idctRowCondDC( block, 0 );
idctRowCondDC( block, 8 );
idctRowCondDC( block, 16 );
idctRowCondDC( block, 24 );
idctRowCondDC( block, 32 );
idctRowCondDC( block, 40 );
idctRowCondDC( block, 48 );
idctRowCondDC( block, 56 );
idctSparseColPut( block, 0, destination, destinationOffset, destinationWidth );
idctSparseColPut( block, 1, destination, destinationOffset, destinationWidth );
idctSparseColPut( block, 2, destination, destinationOffset, destinationWidth );
idctSparseColPut( block, 3, destination, destinationOffset, destinationWidth );
idctSparseColPut( block, 4, destination, destinationOffset, destinationWidth );
idctSparseColPut( block, 5, destination, destinationOffset, destinationWidth );
idctSparseColPut( block, 6, destination, destinationOffset, destinationWidth );
idctSparseColPut( block, 7, destination, destinationOffset, destinationWidth );
}
/**
* Contructor Width and Height in MB pels
*/
private int displayX;
private int displayY;
private int[] displayArray;
/**
* Construct Display (width/height in macroblocks)
* Internally a frame of width 1 macroblock is placed around the
* the display region
*/
public DisplayOutput( int mbWidth, int mbHeight ) {
/**
* Width and height in pixels
*/
displayX = mbWidth * 16;
displayY = mbHeight * 16;
screenX = displayX + 32;
screenY = displayY + 32;
chromX = screenX / 2;
chromY = screenY / 2;
/**
* Allocate display and boarders
*/
mbWidth += 2;
mbHeight += 2;
luminance = new int[ mbWidth * 16 * mbHeight * 16 ];
red = new int[ mbWidth * 8 * mbHeight * 8 ];
blue = new int[ mbWidth * 8 * mbHeight * 8 ];
oldLuminance = new int[ mbWidth * 16 * mbHeight * 16 ];
oldRed = new int[ mbWidth * 8 * mbHeight * 8 ];
oldBlue = new int[ mbWidth * 8 * mbHeight * 8 ];
}
/**
* Move into range 0-ff
*/
private static final int crop( int x ) {
if ( (x & 0xff) == x ) return x;
if ( x < 0 ) {
x = 0;
} else if ( x > 0xff ) {
x = 0xff;
}
return x;
}
public final void putLuminanceIdct( int mbX, int mbY, int[] block ) {
idctPut( block, luminance, mbX * 8 + 16 + (mbY * 8 + 16) *screenX, screenX );
}
public final void putRedIdct( int mbX, int mbY, int[] block ) {
idctPut( block, red, mbX * 8 + 8 + (mbY * 8 + 8)*chromX, chromX);
}
public final void putBlueIdct( int mbX, int mbY, int[] block ) {
idctPut( block, blue, mbX * 8 + 8 + (mbY * 8 + 8)*chromX, chromX);
}
public final void addLuminanceIdct( int mbX, int mbY, int[] block ) {
idctAdd( block, luminance, mbX * 8 + 16 + (mbY * 8 + 16)*screenX, screenX );
}
public final void addRedIdct( int mbX, int mbY, int[] block ) {
idctAdd( block, red, mbX * 8 + 8 + (mbY * 8 + 8)*chromX, chromX);
}
public final void addBlueIdct( int mbX, int mbY, int[] block ) {
idctAdd( block, blue, mbX * 8 + 8 + (mbY * 8 + 8) * chromX, chromX );
}
/**
* Blit a block including half pixel motion
*/
private final void blitBlock( int[] source, int[] destination, int destinationWidth,
int top, int bottom, int left, int right,
int dx, int dy, int halfPixels, boolean rounding ) {
switch ( halfPixels ) {
case 0: {
/**
* This is a straight copy
*/
for ( int y = top; y < bottom; y++ ) {
for ( int x = left; x < right; x++ ) {
destination[ x + y * destinationWidth ] = source[ x + dx + (y + dy)* destinationWidth ];
}
}
break;
}
case 1: {
/**
* Half a pixel offset
*/
for ( int y = top; y < bottom; y++ ) {
int rightPixel = source[ left + dx + (y + dy) * destinationWidth ];
for ( int x = left; x < right; x++ ) {
int leftPixel = rightPixel;
rightPixel = source[ x + dx + 1 + (y + dy) * destinationWidth ];
destination[ x + y * destinationWidth ] = rounding ?
(leftPixel|rightPixel) - ((leftPixel^rightPixel) >>1)
: (leftPixel&rightPixel) + ((leftPixel^rightPixel) >>1);
}
}
break;
}
case 2: {
/**
* Half a pixel offset
*/
for ( int x = left; x < right; x++ ) {
int bottomPixel = source[ x + dx + (top + dy) * destinationWidth ];
for ( int y = top; y < bottom; y++ ) {
int topPixel = bottomPixel;
bottomPixel = source[ x + dx + (y + dy + 1) * destinationWidth ];
destination[ x + y * destinationWidth ] = rounding ?
(topPixel|bottomPixel) - ((topPixel^bottomPixel) >>1)
: (topPixel&bottomPixel) + ((topPixel^bottomPixel) >>1);
}
}
break;
}
case 3:
default: {
/**
* Half a pixel offset
*/
for ( int x = left; x < right; x++ ) {
int b = source[ x + dx + (top + dy) * destinationWidth ] + source[ x + dx + 1 + (top + dy) * destinationWidth ];
for ( int y = top; y < bottom; y++ ) {
int a = b;
b = source[ x + dx + (y + dy + 1) * destinationWidth ] + source[ x + dx + 1 + (y + dy + 1) * destinationWidth ];
destination[ x + y * destinationWidth ] = rounding ?
(a + b + 2) >>2
: (a + b + 1) >>2;
}
}
break;
}
}
}
private final int clip( int position, int offset, int maximum ) {
if ( position + offset < 0 ) {
position = - offset;
}
if ( position + offset > maximum ) {
position = maximum - offset;
}
return position;
}
/**
* Copy this macroblock from the previous frame
*/
public void move( int mbX, int mbY, int motion_x, int motion_y, boolean rounding ) {
/**
* Manage clipping
*/
int top = mbY * 16 + 16;
int left = mbX * 16 + 16;
/**
* Manage half pixel motion
*/
int halfPixels = (motion_x & 1)|((motion_y & 1)<<1);
int dx = motion_x / 2;
int dy = motion_y / 2;
if ( motion_x < 0 && ((halfPixels & 1) == 1)) {
dx--;
}
if ( motion_y < 0 && ((halfPixels & 2) == 2)) {
dy--;
}
/**
* Clipped region from the edge previous buffer
*/
left = clip( left, dx, screenX - 16 );
top = clip( top, dy, screenY - 16 );
int right = left + 16;
int bottom = top + 16;
if ( right + dx == screenX ) halfPixels &= ~1;
if ( bottom + dy == screenY ) halfPixels &= ~2;
/**
* Luminance
*/
blitBlock( oldLuminance, luminance, screenX,
top, bottom, left, right,
dx, dy, halfPixels, rounding );
/**
* Chrominance
*/
top = mbY * 8 + 8;
left = mbX * 8 + 8;
/* Round up */
if ( (motion_x & 1) != 0 ) { if (motion_x < 0) {motion_x--; } else { motion_x++; } }
if ( (motion_y & 1) != 0 ) { if (motion_y < 0) {motion_y--; } else { motion_y++; } }
halfPixels = (motion_y & 2)|((motion_x & 2)>>1);
dx = motion_x / 4;
dy = motion_y / 4;
if ( motion_x < 0 && ((halfPixels & 1) == 1)) {
dx--;
}
if ( motion_y < 0 && ((halfPixels & 2) == 2)) {
dy--;
}
/**
* Clipped region is a straight copy from previous buffer
*/
left = clip( left, dx, chromX - 8 );
top = clip( top, dy, chromY - 8 );
right = left + 8;
bottom = top + 8;
if ( right + dx == chromX ) halfPixels &= ~1;
if ( bottom + dy == chromY ) halfPixels &= ~2;
blitBlock( oldBlue, blue, chromX,
top, bottom, left, right,
dx, dy, halfPixels, rounding );
blitBlock( oldRed, red, chromX,
top, bottom, left, right,
dx, dy, halfPixels, rounding );
}
/**
* Swap this and last frames. Expand the picture into the boarders
*/
public final void endFrame() {
/** Set current as previous, etc */
int[] t;
t = oldLuminance; oldLuminance = luminance; luminance = t;
t = oldRed; oldRed = red; red = t;
t = oldBlue; oldBlue = blue; blue = t;
/**
* Expand into Boarders
*/
for ( int x = 0; x < 16; x++ ) {
for ( int y = 16; y < displayY + 16; y++ ) {
oldLuminance[ x + y * screenX ] = oldLuminance[ 16 + y * screenX ];
oldLuminance[ displayX + 32 - x - 1 + y * screenX ] = oldLuminance[ displayX + 15 + y * screenX ];
oldBlue[ x/2 + (y/2)*chromX ] = oldBlue[ 16 /2 + (y/2)*chromX ];
oldBlue[ (displayX + 32 - x)/2 - 1 + (y/2)*chromX ] = oldBlue[ (displayX + 16)/2 - 1 + (y/2)*chromX ];
oldRed[ x/2 + (y/2) * chromX ] = oldRed[ 16 /2 + (y/2)*chromX ];
oldRed[ (displayX + 32 - x)/2 - 1 + (y/2)*chromX ] = oldRed[ (displayX + 16)/2 - 1 + (y/2)*chromX ];
}
}
for ( int y = 0; y < 16; y++ ) {
for ( int x = 0; x < displayX + 32; x++ ) {
oldLuminance[ x + y * screenX ] = oldLuminance[ x + 16 * screenX ];
oldLuminance[ x + (displayY + 32 - y - 1) * screenX ] = oldLuminance[ x + (displayY + 15) * screenX ];
oldBlue[ x/2 + (y/2)*chromX ] = oldBlue[ x/2 + (16/2)*chromX ];
oldBlue[ x /2 + ((displayY + 32 - y )/2 - 1)*chromX ] = oldBlue[ x/2 + ((displayY + 16)/2 - 1)*chromX];
oldRed[ x/2 + (y/2)*chromX ] = oldRed[ x/2 + (16/2)*chromX ];
oldRed[ x /2 + ((displayY + 32 - y)/2 - 1)*chromX ] = oldRed[ x/2 + ((displayY + 16)/2 - 1)*chromX ];
}
}
}
/**
* Convert luminance and chrominance into an RGB buffer
*/
public void showScreen( Buffer buffer ) {
int[] data = (int[])buffer.getData();
if ( data == null || data.length < displayX * displayY ) {
data = new int[ displayX * displayY ];
buffer.setData( data );
}
buffer.setLength( data.length );
int h = 0;
int yExtent = displayY + 16;
int xExtent = displayX + 16;
int l1,l2,l3,l4,r,b,g,rshift,yoff,yoff1,chromoff,x2,x,scale;
for (int y = 16; y < yExtent; y += 2 ) {
yoff = screenX * y;
yoff1 = screenX * (y + 1);
chromoff = y/2 * chromX;
x2 = 8;
for ( x = 16; x < xExtent; x += 2 ) {
r = crop( red[ x2 + chromoff ] ) - 128;
b = crop( blue[ (x2++) + chromoff ] ) - 128;
g = (int)(b * ((float)13954/(1<<16)) + r * ((float)34903/(1<<16)));
r = (int)(r * ((float)117504/(1<<16)));
b = (int)(b * ((float)138452/(1<<16)));
/* l = 0.3 red + 0.59 green + 0.11 blue */
l1 = (int)((luminance[ x + yoff ]-16)*((float)76309/(1<<16)));
l3 = (int)((luminance[ x + yoff1 ]-16)*((float)76309/(1<<16)));
l2 = (int)((luminance[ x + 1 + yoff ]-16)*((float)76309/(1<<16)));
l4 = (int)((luminance[ x + 1 + yoff1 ]-16)*((float)76309/(1<<16)));
data[h ] = (crop(l1+r)<<16)|(crop(l1-g)<<8)|crop(l1+b);
data[h + 1] = (crop(l2+r)<<16)|(crop(l2-g)<<8)|crop(l2+b);
data[h + displayX ] = (crop(l3+r)<<16)|(crop(l3-g)<<8)|crop(l3+b);
data[h + displayX + 1] = (crop(l4+r)<<16)|(crop(l4-g)<<8)|crop(l4+b);
h += 2;
}
h += displayX;
}
}
}